From 0624cb5222a63c24905e119c15c846f4e7a8530d Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Tue, 21 May 2024 23:52:18 +1200 Subject: [PATCH 1/6] fix: update windows for new changes Looks like a fair bit of unix-specific code has gotten tangled up in the main files Have separated the unix specific process logic into the unix.rs module Created a wrapper for the Signal type since its not available on non unix platforms, non unix platforms just handle it as a uppercase string (No special handling at this stage, but could be implemented at a later stage if we make our own signal enum that maps to the unix enum) Added a new WindowsProcess type for windows processes, Wired up the code to collect and kill in separate places Added process name lookup for windows processes (To match the other impls) if a process has a bad name or fails to obtain its name None is used instead Docker container killing is working, however it doesn't work properly if you don't explicitly specify the container mode (I think its trying to kill the wrong one causing it to fail?) Made the type for a killable into an enum as its possible values are constant? Renamed the killport_tests.rs to killport_unix_tests.rs and gated it to unix only since its using unix specific features, Windows tests will need to be added Added a build and release target for windows to the github workflows --- .github/workflows/build.yaml | 1 + .github/workflows/release.yml | 2 +- src/cli.rs | 18 +-- src/docker.rs | 10 +- src/killport.rs | 90 ++++-------- src/lib.rs | 5 + src/linux.rs | 11 +- src/macos.rs | 14 +- src/main.rs | 2 +- src/signal.rs | 35 +++++ src/unix.rs | 59 ++++++++ src/windows.rs | 134 +++++++++++++++--- ...llport_tests.rs => killport_unix_tests.rs} | 69 +++++---- tests/utils.rs | 2 +- 14 files changed, 300 insertions(+), 152 deletions(-) create mode 100644 src/signal.rs create mode 100644 src/unix.rs rename tests/{killport_tests.rs => killport_unix_tests.rs} (60%) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4fc3665..1d4a7c6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -19,6 +19,7 @@ jobs: - s390x-unknown-linux-gnu - aarch64-apple-darwin - x86_64-apple-darwin + - x86_64-pc-windows-gnu runs-on: ${{ (matrix.target == 'aarch64-apple-darwin' || matrix.target == 'x86_64-apple-darwin') && 'macos-latest' || 'ubuntu-latest' }} steps: - name: Checkout repository diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3862952..9a8932f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,7 +64,7 @@ jobs: - s390x-unknown-linux-gnu - aarch64-apple-darwin - x86_64-apple-darwin - # - x86_64-pc-windows-gnu + - x86_64-pc-windows-gnu runs-on: ${{ (matrix.target == 'aarch64-apple-darwin' || matrix.target == 'x86_64-apple-darwin') && 'macos-latest' || 'ubuntu-latest' }} steps: - name: Checkout repository diff --git a/src/cli.rs b/src/cli.rs index 7e01424..c9eb880 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,8 +1,8 @@ use clap::{Parser, ValueEnum}; use clap_verbosity_flag::{Verbosity, WarnLevel}; use core::fmt; -use nix::sys::signal::Signal; -use std::str::FromStr; + +use crate::signal::KillportSignal; /// Modes of operation for killport. #[derive(Debug, Clone, Copy, PartialEq, ValueEnum)] @@ -67,7 +67,7 @@ pub struct KillPortArgs { default_value = "sigkill", value_parser = parse_signal )] - pub signal: Signal, + pub signal: KillportSignal, /// A verbosity flag to control the level of logging output. #[command(flatten)] @@ -81,14 +81,6 @@ pub struct KillPortArgs { pub dry_run: bool, } -fn parse_signal(arg: &str) -> Result { - let str_arg = arg.parse::(); - match str_arg { - Ok(str_arg) => { - let signal_str = str_arg.to_uppercase(); - let signal = Signal::from_str(signal_str.as_str())?; - return Ok(signal); - } - Err(e) => Err(std::io::Error::new(std::io::ErrorKind::Other, e)), - } +fn parse_signal(arg: &str) -> Result { + arg.to_uppercase().parse() } diff --git a/src/docker.rs b/src/docker.rs index 1a4f3f1..b53011c 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -1,7 +1,7 @@ +use crate::signal::KillportSignal; use bollard::container::{KillContainerOptions, ListContainersOptions}; use bollard::Docker; use log::debug; -use nix::sys::signal::Signal; use std::collections::HashMap; use std::io::Error; use tokio::runtime::Runtime; @@ -17,7 +17,7 @@ impl DockerContainer { /// /// * `name` - A container name. /// * `signal` - A enum value representing the signal type. - pub fn kill_container(name: &String, signal: Signal) -> Result<(), Error> { + pub fn kill_container(name: &str, signal: KillportSignal) -> Result<(), Error> { let rt = Runtime::new()?; rt.block_on(async { let docker = Docker::connect_with_socket_defaults() @@ -63,11 +63,7 @@ impl DockerContainer { .as_ref()? .first() .map(|name| DockerContainer { - name: if name.starts_with('/') { - name[1..].to_string() - } else { - name.clone() - }, + name: name.strip_prefix('/').unwrap_or(name).to_string(), }) }) .collect()) diff --git a/src/killport.rs b/src/killport.rs index a17e7c8..b630be2 100644 --- a/src/killport.rs +++ b/src/killport.rs @@ -1,64 +1,32 @@ -use crate::cli::Mode; use crate::docker::DockerContainer; #[cfg(target_os = "linux")] use crate::linux::find_target_processes; #[cfg(target_os = "macos")] use crate::macos::find_target_processes; -use log::info; -use nix::sys::signal::{kill, Signal}; -use nix::unistd::Pid; -use std::io::Error; - -#[derive(Debug)] -pub struct NativeProcess { - /// System native process ID. - pub pid: Pid, - pub name: String, -} +#[cfg(target_os = "windows")] +use crate::windows::find_target_processes; +use crate::{cli::Mode, signal::KillportSignal}; +use std::{fmt::Display, io::Error}; /// Interface for killable targets such as native process and docker container. pub trait Killable { - fn kill(&self, signal: Signal) -> Result; - fn get_type(&self) -> String; + fn kill(&self, signal: KillportSignal) -> Result; + fn get_type(&self) -> KillableType; fn get_name(&self) -> String; } -impl Killable for NativeProcess { - /// Entry point to kill the linux native process. - /// - /// # Arguments - /// - /// * `signal` - A enum value representing the signal type. - fn kill(&self, signal: Signal) -> Result { - info!("Killing process '{}' with PID {}", self.name, self.pid); - - kill(self.pid, signal).map(|_| true).map_err(|e| { - Error::new( - std::io::ErrorKind::Other, - format!( - "Failed to kill process '{}' with PID {}: {}", - self.name, self.pid, e - ), - ) - }) - } - - /// Returns the type of the killable target. - /// - /// This method is used to identify the type of the target (either a native process or a Docker container) - /// that is being handled. This information can be useful for logging, error handling, or other needs - /// where type of the target is relevant. - /// - /// # Returns - /// - /// * `String` - A string that describes the type of the killable target. For a `NativeProcess` it will return "process", - /// and for a `DockerContainer` it will return "container". - fn get_type(&self) -> String { - "process".to_string() - } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum KillableType { + Process, + Container, +} - fn get_name(&self) -> String { - self.name.to_string() +impl Display for KillableType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + KillableType::Process => "process", + KillableType::Container => "container", + }) } } @@ -68,10 +36,8 @@ impl Killable for DockerContainer { /// # Arguments /// /// * `signal` - A enum value representing the signal type. - fn kill(&self, signal: Signal) -> Result { - if let Err(err) = Self::kill_container(&self.name, signal) { - return Err(err); - } + fn kill(&self, signal: KillportSignal) -> Result { + Self::kill_container(&self.name, signal)?; Ok(true) } @@ -84,10 +50,10 @@ impl Killable for DockerContainer { /// /// # Returns /// - /// * `String` - A string that describes the type of the killable target. For a `NativeProcess` it will return "process", + /// * `String` - A string that describes the type of the killable target. For a `UnixProcess` it will return "process", /// and for a `DockerContainer` it will return "container". - fn get_type(&self) -> String { - "container".to_string() + fn get_type(&self) -> KillableType { + KillableType::Container } fn get_name(&self) -> String { @@ -104,10 +70,10 @@ pub trait KillportOperations { fn kill_service_by_port( &self, port: u16, - signal: Signal, + signal: KillportSignal, mode: Mode, dry_run: bool, - ) -> Result, Error>; + ) -> Result, Error>; } pub struct Killport; @@ -133,7 +99,7 @@ impl KillportOperations for Killport { for process in target_processes { // Check if the process name contains 'docker' and skip if in docker mode - if docker_present && process.name.to_lowercase().contains("docker") { + if docker_present && process.get_name().to_lowercase().contains("docker") { continue; } target_killables.push(Box::new(process)); @@ -166,10 +132,10 @@ impl KillportOperations for Killport { fn kill_service_by_port( &self, port: u16, - signal: Signal, + signal: KillportSignal, mode: Mode, dry_run: bool, - ) -> Result, Error> { + ) -> Result, Error> { let mut results = Vec::new(); let target_killables = self.find_target_killables(port, mode)?; // Use the existing function to find targets @@ -179,7 +145,7 @@ impl KillportOperations for Killport { results.push((killable.get_type(), killable.get_name())); } else { // In actual mode, attempt to kill the entity and collect its information if successful - if killable.kill(signal)? { + if killable.kill(signal.clone())? { results.push((killable.get_type(), killable.get_name())); } } diff --git a/src/lib.rs b/src/lib.rs index 97f84b9..49c452d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,11 @@ pub mod cli; pub mod docker; pub mod killport; +pub mod signal; + +#[cfg(unix)] +pub mod unix; + #[cfg(target_os = "linux")] pub mod linux; #[cfg(target_os = "macos")] diff --git a/src/linux.rs b/src/linux.rs index de0151a..d9d5d7d 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -1,4 +1,4 @@ -use crate::killport::NativeProcess; +use crate::unix::UnixProcess; use log::debug; use nix::unistd::Pid; @@ -75,8 +75,8 @@ fn find_target_inodes(port: u16) -> Vec { /// # Arguments /// /// * `inodes` - Target inodes -pub fn find_target_processes(port: u16) -> Result, Error> { - let mut target_pids: Vec = vec![]; +pub fn find_target_processes(port: u16) -> Result, Error> { + let mut target_pids: Vec = vec![]; let inodes = find_target_inodes(port); for inode in inodes { @@ -96,10 +96,7 @@ pub fn find_target_processes(port: u16) -> Result, Error> { .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))? .join(" "); debug!("Found process '{}' with PID {}", name, process.pid()); - target_pids.push(NativeProcess { - pid: Pid::from_raw(process.pid), - name: name, - }); + target_pids.push(UnixProcess::new(Pid::from_raw(process.pid), name)); } } } diff --git a/src/macos.rs b/src/macos.rs index bdcbd1a..b7114f9 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -1,4 +1,4 @@ -use crate::killport::NativeProcess; +use crate::unix::UnixProcess; use libproc::libproc::file_info::pidfdinfo; use libproc::libproc::file_info::{ListFDs, ProcFDType}; @@ -16,8 +16,8 @@ use std::io; /// # Arguments /// /// * `port` - Target port number -pub fn find_target_processes(port: u16) -> Result, io::Error> { - let mut target_pids: Vec = vec![]; +pub fn find_target_processes(port: u16) -> Result, io::Error> { + let mut target_pids: Vec = vec![]; if let Ok(procs) = pids_by_type(ProcFilter::All) { for p in procs { @@ -56,10 +56,10 @@ pub fn find_target_processes(port: u16) -> Result, io::Error> "Found process '{}' with PID {} listening on port {}", process_name, pid, port ); - target_pids.push(NativeProcess { - pid: Pid::from_raw(pid), - name: process_name, - }); + target_pids.push(UnixProcess::new( + Pid::from_raw(pid), + process_name, + )); } } _ => (), diff --git a/src/main.rs b/src/main.rs index a4dedc0..8895564 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,7 +52,7 @@ fn main() { // Attempt to kill processes listening on specified ports for port in args.ports { - match killport.kill_service_by_port(port, args.signal, args.mode, args.dry_run) { + match killport.kill_service_by_port(port, args.signal.clone(), args.mode, args.dry_run) { Ok(killed_services) => { if killed_services.is_empty() { println!("No {} found using port {}", service_type_singular, port); diff --git a/src/signal.rs b/src/signal.rs new file mode 100644 index 0000000..68b5127 --- /dev/null +++ b/src/signal.rs @@ -0,0 +1,35 @@ +//! Wrapper around signals for platforms that they are not supported on + +use std::{fmt::Display, str::FromStr}; + +#[cfg(unix)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct KillportSignal(pub nix::sys::signal::Signal); + +/// On a platform where we don't have the proper signals enum +#[cfg(not(unix))] +#[derive(Debug, Clone)] +pub struct KillportSignal(pub String); + +impl Display for KillportSignal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl FromStr for KillportSignal { + type Err = std::io::Error; + + fn from_str(value: &str) -> Result { + #[cfg(unix)] + { + let signal = nix::sys::signal::Signal::from_str(value)?; + Ok(KillportSignal(signal)) + } + + #[cfg(not(unix))] + { + Ok(KillportSignal(value.to_string())) + } + } +} diff --git a/src/unix.rs b/src/unix.rs new file mode 100644 index 0000000..8b13cda --- /dev/null +++ b/src/unix.rs @@ -0,0 +1,59 @@ +use crate::killport::{Killable, KillableType}; +use crate::signal::KillportSignal; +use log::info; +use nix::sys::signal::kill; +use nix::unistd::Pid; +use std::io::Error; + +/// Process type shared amongst unix-like operating systems +#[derive(Debug)] +pub struct UnixProcess { + /// System native process ID. + pid: Pid, + name: String, +} + +impl UnixProcess { + pub fn new(pid: Pid, name: String) -> Self { + Self { pid, name } + } +} + +impl Killable for UnixProcess { + /// Entry point to kill the linux native process. + /// + /// # Arguments + /// + /// * `signal` - A enum value representing the signal type. + fn kill(&self, signal: KillportSignal) -> Result { + info!("Killing process '{}' with PID {}", self.name, self.pid); + + kill(self.pid, signal.0).map(|_| true).map_err(|e| { + Error::new( + std::io::ErrorKind::Other, + format!( + "Failed to kill process '{}' with PID {}: {}", + self.name, self.pid, e + ), + ) + }) + } + + /// Returns the type of the killable target. + /// + /// This method is used to identify the type of the target (either a native process or a Docker container) + /// that is being handled. This information can be useful for logging, error handling, or other needs + /// where type of the target is relevant. + /// + /// # Returns + /// + /// * `String` - A string that describes the type of the killable target. For a `UnixProcess` it will return "process", + /// and for a `DockerContainer` it will return "container". + fn get_type(&self) -> KillableType { + KillableType::Process + } + + fn get_name(&self) -> String { + self.name.to_string() + } +} diff --git a/src/windows.rs b/src/windows.rs index 6090e43..00a2950 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,4 +1,4 @@ -use crate::KillPortSignalOptions; +use crate::killport::{Killable, KillableType}; use log::info; use std::{ alloc::{alloc, dealloc, Layout}, @@ -29,24 +29,24 @@ use windows_sys::Win32::{ }, }; -/// Attempts to kill processes listening on the specified `port`. -/// -/// # Arguments +/// Represents a windows native process +#[derive(Debug)] +pub struct WindowsProcess { + pid: u32, + name: Option, +} + +/// Finds the processes associated with the specified `port`. /// -/// * `port` - A u16 value representing the port number. +/// Returns a `Vec` of native processes. /// -/// # Returns +/// # Arguments /// -/// A `Result` containing a tuple. The first element is a boolean indicating if -/// at least one process was killed (true if yes, false otherwise). The second -/// element is a string indicating the type of the killed entity. An `Error` is -/// returned if the operation failed or the platform is unsupported. -pub fn kill_processes_by_port( - port: u16, - _: KillPortSignalOptions, -) -> Result<(bool, String), Error> { +/// * `port` - Target port number +pub fn find_target_processes(port: u16) -> Result> { let mut pids: HashSet = HashSet::new(); - unsafe { + + let processes = unsafe { // Find processes in the TCP IPv4 table use_extended_table::(port, &mut pids)?; @@ -59,21 +59,109 @@ pub fn kill_processes_by_port( // Find processes in the UDP IPv6 table use_extended_table::(port, &mut pids)?; - // Nothing was found + // Collect parents of the PIDs + collect_parents(&mut pids)?; + + // Collect the processes + let mut processes: Vec = pids + .into_iter() + .map(|pid| WindowsProcess { pid, name: None }) + .collect(); + + lookup_proccess_names(&mut processes)?; + + processes + }; + + Ok(processes) +} + +impl Killable for WindowsProcess { + fn kill(&self, _signal: crate::signal::KillportSignal) -> Result { + let mut pids: HashSet = HashSet::new(); + pids.insert(self.pid); + if pids.is_empty() { - return Ok((false, "None".to_string())); + return Ok(false); } - // Collect parents of the PIDs - collect_parents(&mut pids)?; + unsafe { + collect_parents(&mut pids)?; + + for pid in pids { + kill_process(pid)?; + } + }; + + Ok(true) + } - for pid in pids { - kill_process(pid)?; + fn get_type(&self) -> KillableType { + KillableType::Process + } + + fn get_name(&self) -> String { + match self.name.as_ref() { + Some(value) => value.to_string(), + None => "Unknown".to_string(), } + } +} + +/// Collects the names for the processes in the provided collection of +/// processes. If name resolving fails that process is just "Unknown" +/// +/// # Arguments +/// +/// * `processes` - The set of processes to resolve the names of +unsafe fn lookup_proccess_names(processes: &mut [WindowsProcess]) -> Result<()> { + // Request a snapshot handle + let handle: HANDLE = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + // Ensure we got a valid handle + if handle == INVALID_HANDLE_VALUE { + let error: WIN32_ERROR = GetLastError(); + return Err(Error::new( + ErrorKind::Other, + format!("Failed to get handle to processes: {:#x}", error), + )); + } + + // Allocate the memory to use for the entries + let mut entry: PROCESSENTRY32 = std::mem::zeroed(); + entry.dwSize = std::mem::size_of::() as u32; - // Something had to have been killed to reach here - Ok((true, "process".to_string())) + // Process the first item + if Process32First(handle, &mut entry) != FALSE { + loop { + let target_process = processes + .iter_mut() + .find(|proc| proc.pid == entry.th32ProcessID); + if let Some(target_process) = target_process { + let name_chars = entry + .szExeFile + .iter() + .copied() + .take_while(|value| *value != 0) + .collect(); + + let name = String::from_utf8(name_chars); + if let Ok(name) = name { + target_process.name = Some(name) + } + } + + // Process the next entry + if Process32Next(handle, &mut entry) == FALSE { + break; + } + } } + + // Close the handle now that its no longer needed + CloseHandle(handle); + + Ok(()) } /// Collects all the parent processes for the PIDs in diff --git a/tests/killport_tests.rs b/tests/killport_unix_tests.rs similarity index 60% rename from tests/killport_tests.rs rename to tests/killport_unix_tests.rs index d668e80..f2f0af7 100644 --- a/tests/killport_tests.rs +++ b/tests/killport_unix_tests.rs @@ -1,50 +1,55 @@ +#![cfg(unix)] + use killport::cli::Mode; use killport::docker::DockerContainer; -use killport::killport::KillportOperations; -use killport::killport::{Killable, NativeProcess}; +use killport::killport::{Killable, KillableType, KillportOperations}; +use killport::signal::KillportSignal; +use killport::unix::UnixProcess; use mockall::*; use nix::sys::signal::Signal; use nix::unistd::Pid; use std::io::Error; -use std::sync::{Arc, Mutex}; // Setup Mocks mock! { DockerContainer {} impl Killable for DockerContainer { - fn kill(&self, signal: Signal) -> Result; - fn get_type(&self) -> String; + fn kill(&self, signal: KillportSignal) -> Result; + fn get_type(&self) -> KillableType; fn get_name(&self) -> String; } } mock! { - NativeProcess {} + UnixProcess {} - impl Killable for NativeProcess { - fn kill(&self, signal: Signal) -> Result; - fn get_type(&self) -> String; + impl Killable for UnixProcess { + fn kill(&self, signal: KillportSignal) -> Result; + fn get_type(&self) -> KillableType; fn get_name(&self) -> String; } } mock! { KillportOperations { fn find_target_killables(&self, port: u16, mode: Mode) -> Result>, Error>; - fn kill_service_by_port(&self, port: u16, signal: Signal, mode: Mode, dry_run: bool) -> Result, Error>; + fn kill_service_by_port(&self, port: u16, signal: KillportSignal, mode: Mode, dry_run: bool) -> Result, Error>; } } #[test] fn native_process_kill_succeeds() { - let mut mock_process = MockNativeProcess::new(); + let mut mock_process = MockUnixProcess::new(); // Setup the expectation for the mock mock_process .expect_kill() - .with(mockall::predicate::eq(Signal::SIGKILL)) + .with(mockall::predicate::eq(KillportSignal(Signal::SIGKILL))) .times(1) // Ensure the kill method is called exactly once .returning(|_| Ok(true)); // Simulate successful kill - assert_eq!(mock_process.kill(Signal::SIGKILL).unwrap(), true); + assert_eq!( + mock_process.kill(KillportSignal(Signal::SIGKILL)).unwrap(), + true + ); } #[test] @@ -52,11 +57,16 @@ fn docker_container_kill_succeeds() { let mut mock_container = MockDockerContainer::new(); mock_container .expect_kill() - .with(mockall::predicate::eq(Signal::SIGKILL)) + .with(mockall::predicate::eq(KillportSignal(Signal::SIGKILL))) .times(1) .returning(|_| Ok(true)); - assert_eq!(mock_container.kill(Signal::SIGKILL).unwrap(), true); + assert_eq!( + mock_container + .kill(KillportSignal(Signal::SIGKILL)) + .unwrap(), + true + ); } #[test] @@ -67,10 +77,10 @@ fn find_killables_processes_only() { .expect_find_target_killables() .withf(|&port, &mode| port == 8080 && mode == Mode::Process) .returning(|_, _| { - let mut mock_process = MockNativeProcess::new(); + let mut mock_process = MockUnixProcess::new(); mock_process .expect_get_type() - .return_const("process".to_string()); + .return_const(KillableType::Process); mock_process .expect_get_name() .return_const("mock_process".to_string()); @@ -80,47 +90,46 @@ fn find_killables_processes_only() { let port = 8080; let mode = Mode::Process; let found_killables = mock_killport.find_target_killables(port, mode).unwrap(); - assert!(found_killables.iter().all(|k| k.get_type() == "process")); + assert!(found_killables + .iter() + .all(|k| k.get_type() == KillableType::Process)); } #[test] fn kill_service_by_port_dry_run() { let mut mock_killport = MockKillportOperations::new(); - let mut mock_process = MockNativeProcess::new(); + let mut mock_process = MockUnixProcess::new(); mock_process.expect_kill().never(); mock_process .expect_get_type() - .return_const("process".to_string()); + .return_const(KillableType::Process); mock_process .expect_get_name() .return_const("mock_process".to_string()); mock_killport .expect_kill_service_by_port() - .returning(|_, _, _, _| Ok(vec![("process".to_string(), "mock_process".to_string())])); + .returning(|_, _, _, _| Ok(vec![(KillableType::Process, "mock_process".to_string())])); let port = 8080; let mode = Mode::Process; let dry_run = true; - let signal = Signal::SIGKILL; + let signal = KillportSignal(Signal::SIGKILL); let results = mock_killport .kill_service_by_port(port, signal, mode, dry_run) .unwrap(); assert_eq!(results.len(), 1); - assert_eq!(results[0].0, "process"); + assert_eq!(results[0].0, KillableType::Process); assert_eq!(results[0].1, "mock_process"); } #[test] fn check_process_type_and_name() { - let process = NativeProcess { - pid: Pid::from_raw(1234), - name: "unique_process".to_string(), - }; + let process = UnixProcess::new(Pid::from_raw(1234), "unique_process".to_string()); - assert_eq!(process.get_type(), "process"); + assert_eq!(process.get_type(), KillableType::Process); assert_eq!(process.get_name(), "unique_process"); } @@ -130,12 +139,12 @@ fn check_docker_container_type_and_name() { mock_container .expect_get_type() .times(1) - .returning(|| "container".to_string()); + .returning(|| KillableType::Container); mock_container .expect_get_name() .times(1) .returning(|| "docker_container".to_string()); - assert_eq!(mock_container.get_type(), "container"); + assert_eq!(mock_container.get_type(), KillableType::Container); assert_eq!(mock_container.get_name(), "docker_container"); } diff --git a/tests/utils.rs b/tests/utils.rs index 7feb689..eb17a36 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -34,7 +34,7 @@ pub fn start_listener_process(tempdir_path: &Path, port: u16) -> Child { .expect("Failed to write mock process code"); let status = SystemCommand::new("rustc") - .args(&[ + .args([ mock_process_path.to_str().unwrap(), "--out-dir", tempdir_path.to_str().unwrap(), From 3cf01efd8485da8a248ffd509b7ffb881d25433b Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Wed, 22 May 2024 19:05:25 +1200 Subject: [PATCH 2/6] feat: windows process constructor, windows tests --- src/signal.rs | 2 +- src/windows.rs | 8 +- tests/killport_windows_tests.rs | 149 ++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 tests/killport_windows_tests.rs diff --git a/src/signal.rs b/src/signal.rs index 68b5127..7f16e9e 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -8,7 +8,7 @@ pub struct KillportSignal(pub nix::sys::signal::Signal); /// On a platform where we don't have the proper signals enum #[cfg(not(unix))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct KillportSignal(pub String); impl Display for KillportSignal { diff --git a/src/windows.rs b/src/windows.rs index 00a2950..05f6745 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -36,6 +36,12 @@ pub struct WindowsProcess { name: Option, } +impl WindowsProcess { + pub fn new(pid: u32, name: Option) -> Self { + Self { pid, name } + } +} + /// Finds the processes associated with the specified `port`. /// /// Returns a `Vec` of native processes. @@ -65,7 +71,7 @@ pub fn find_target_processes(port: u16) -> Result> { // Collect the processes let mut processes: Vec = pids .into_iter() - .map(|pid| WindowsProcess { pid, name: None }) + .map(|pid| WindowsProcess::new(pid, None)) .collect(); lookup_proccess_names(&mut processes)?; diff --git a/tests/killport_windows_tests.rs b/tests/killport_windows_tests.rs new file mode 100644 index 0000000..6d19668 --- /dev/null +++ b/tests/killport_windows_tests.rs @@ -0,0 +1,149 @@ +#![cfg(windows)] + +use killport::cli::Mode; +use killport::killport::{Killable, KillableType}; +use killport::signal::KillportSignal; +use killport::windows::WindowsProcess; +use mockall::*; + +use std::io::Error; + +// Setup Mocks +mock! { + DockerContainer {} + + impl Killable for DockerContainer { + fn kill(&self, signal: KillportSignal) -> Result; + fn get_type(&self) -> KillableType; + fn get_name(&self) -> String; + } +} + +mock! { + WindowsProcess {} + + impl Killable for WindowsProcess { + fn kill(&self, signal: KillportSignal) -> Result; + fn get_type(&self) -> KillableType; + fn get_name(&self) -> String; + } +} +mock! { + KillportOperations { + fn find_target_killables(&self, port: u16, mode: Mode) -> Result>, Error>; + fn kill_service_by_port(&self, port: u16, signal: KillportSignal, mode: Mode, dry_run: bool) -> Result, Error>; + } +} + +#[test] +fn native_process_kill_succeeds() { + let mut mock_process = MockWindowsProcess::new(); + // Setup the expectation for the mock + mock_process + .expect_kill() + .with(mockall::predicate::eq(KillportSignal( + "SIGKILL".to_string(), + ))) + .times(1) // Ensure the kill method is called exactly once + .returning(|_| Ok(true)); // Simulate successful kill + + assert!(mock_process + .kill(KillportSignal("SIGKILL".to_string())) + .unwrap()); +} + +#[test] +fn docker_container_kill_succeeds() { + let mut mock_container = MockDockerContainer::new(); + mock_container + .expect_kill() + .with(mockall::predicate::eq(KillportSignal( + "SIGKILL".to_string(), + ))) + .times(1) + .returning(|_| Ok(true)); + + assert!(mock_container + .kill(KillportSignal("SIGKILL".to_string())) + .unwrap()); +} + +#[test] +fn find_killables_processes_only() { + let mut mock_killport = MockKillportOperations::new(); + + mock_killport + .expect_find_target_killables() + .withf(|&port, &mode| port == 8080 && mode == Mode::Process) + .returning(|_, _| { + let mut mock_process = MockWindowsProcess::new(); + mock_process + .expect_get_type() + .return_const(KillableType::Process); + mock_process + .expect_get_name() + .return_const("mock_process".to_string()); + Ok(vec![Box::new(mock_process)]) + }); + + let port = 8080; + let mode = Mode::Process; + let found_killables = mock_killport.find_target_killables(port, mode).unwrap(); + assert!(found_killables + .iter() + .all(|k| k.get_type() == KillableType::Process)); +} + +#[test] +fn kill_service_by_port_dry_run() { + let mut mock_killport = MockKillportOperations::new(); + let mut mock_process = MockWindowsProcess::new(); + + mock_process.expect_kill().never(); + mock_process + .expect_get_type() + .return_const(KillableType::Process); + mock_process + .expect_get_name() + .return_const("mock_process".to_string()); + + mock_killport + .expect_kill_service_by_port() + .returning(|_, _, _, _| Ok(vec![(KillableType::Process, "mock_process".to_string())])); + + let port = 8080; + let mode = Mode::Process; + let dry_run = true; + let signal = KillportSignal("SIGKILL".to_string()); + + let results = mock_killport + .kill_service_by_port(port, signal, mode, dry_run) + .unwrap(); + assert_eq!(results.len(), 1); + assert_eq!(results[0].0, KillableType::Process); + assert_eq!(results[0].1, "mock_process"); +} + +#[test] +fn check_process_type_and_name() { + let process = WindowsProcess::new(1234, Some("unique_process".to_string())); + + assert_eq!(process.get_type(), KillableType::Process); + assert_eq!(process.get_name(), "unique_process"); +} + +#[test] +fn check_docker_container_type_and_name() { + let mut mock_container = MockDockerContainer::new(); + mock_container + .expect_get_type() + .times(1) + .returning(|| KillableType::Container); + mock_container + .expect_get_name() + .times(1) + .returning(|| "docker_container".to_string()); + + assert_eq!(mock_container.get_type(), KillableType::Container); + assert_eq!(mock_container.get_name(), "docker_container"); +} From 41ae804373034426b0490fd82aafd41a7f91568a Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Wed, 22 May 2024 20:54:23 +1200 Subject: [PATCH 3/6] feat: always resolve windows process names, resolve parents properly, lookup tables --- src/killport.rs | 3 + src/windows.rs | 326 +++++++++++++++++++++----------- tests/killport_windows_tests.rs | 2 +- 3 files changed, 218 insertions(+), 113 deletions(-) diff --git a/src/killport.rs b/src/killport.rs index b630be2..97f0857 100644 --- a/src/killport.rs +++ b/src/killport.rs @@ -11,7 +11,9 @@ use std::{fmt::Display, io::Error}; /// Interface for killable targets such as native process and docker container. pub trait Killable { fn kill(&self, signal: KillportSignal) -> Result; + fn get_type(&self) -> KillableType; + fn get_name(&self) -> String; } @@ -102,6 +104,7 @@ impl KillportOperations for Killport { if docker_present && process.get_name().to_lowercase().contains("docker") { continue; } + target_killables.push(Box::new(process)); } } diff --git a/src/windows.rs b/src/windows.rs index 05f6745..19070b6 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -2,7 +2,7 @@ use crate::killport::{Killable, KillableType}; use log::info; use std::{ alloc::{alloc, dealloc, Layout}, - collections::HashSet, + collections::{HashMap, HashSet}, ffi::c_void, io::{Error, ErrorKind, Result}, ptr::addr_of, @@ -33,12 +33,17 @@ use windows_sys::Win32::{ #[derive(Debug)] pub struct WindowsProcess { pid: u32, - name: Option, + name: String, + parent: Option>, } impl WindowsProcess { - pub fn new(pid: u32, name: Option) -> Self { - Self { pid, name } + pub fn new(pid: u32, name: String) -> Self { + Self { + pid, + name, + parent: None, + } } } @@ -50,6 +55,7 @@ impl WindowsProcess { /// /// * `port` - Target port number pub fn find_target_processes(port: u16) -> Result> { + let lookup_table: ProcessLookupTable = ProcessLookupTable::create()?; let mut pids: HashSet = HashSet::new(); let processes = unsafe { @@ -65,16 +71,22 @@ pub fn find_target_processes(port: u16) -> Result> { // Find processes in the UDP IPv6 table use_extended_table::(port, &mut pids)?; - // Collect parents of the PIDs - collect_parents(&mut pids)?; + let mut processes: Vec = Vec::with_capacity(pids.len()); + + for pid in pids { + let process_name = lookup_table + .process_names + .get(&pid) + .cloned() + .unwrap_or_else(|| "Unknown".to_string()); - // Collect the processes - let mut processes: Vec = pids - .into_iter() - .map(|pid| WindowsProcess::new(pid, None)) - .collect(); + let mut process = WindowsProcess::new(pid, process_name); - lookup_proccess_names(&mut processes)?; + // Resolve the process parents + lookup_process_parents(&lookup_table, &mut process)?; + + processes.push(process); + } processes }; @@ -84,22 +96,18 @@ pub fn find_target_processes(port: u16) -> Result> { impl Killable for WindowsProcess { fn kill(&self, _signal: crate::signal::KillportSignal) -> Result { - let mut pids: HashSet = HashSet::new(); - pids.insert(self.pid); + let mut killed = false; + let mut next = Some(self); + while let Some(current) = next { + unsafe { + kill_process(current)?; + } - if pids.is_empty() { - return Ok(false); + killed = true; + next = current.parent.as_ref().map(|value| value.as_ref()); } - unsafe { - collect_parents(&mut pids)?; - - for pid in pids { - kill_process(pid)?; - } - }; - - Ok(true) + Ok(killed) } fn get_type(&self) -> KillableType { @@ -107,133 +115,222 @@ impl Killable for WindowsProcess { } fn get_name(&self) -> String { - match self.name.as_ref() { - Some(value) => value.to_string(), - None => "Unknown".to_string(), - } + self.name.to_string() } } -/// Collects the names for the processes in the provided collection of -/// processes. If name resolving fails that process is just "Unknown" +/// Checks if there is a running process with the provided pid /// /// # Arguments /// -/// * `processes` - The set of processes to resolve the names of -unsafe fn lookup_proccess_names(processes: &mut [WindowsProcess]) -> Result<()> { - // Request a snapshot handle - let handle: HANDLE = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); +/// * `pid` - The process ID to search for +fn is_process_running(pid: u32) -> Result { + let mut snapshot = WindowsProcessesSnapshot::create()?; + let is_running = snapshot.any(|entry| entry.th32ProcessID == pid); + Ok(is_running) +} - // Ensure we got a valid handle - if handle == INVALID_HANDLE_VALUE { - let error: WIN32_ERROR = GetLastError(); - return Err(Error::new( - ErrorKind::Other, - format!("Failed to get handle to processes: {:#x}", error), - )); - } +/// Lookup table for finding the names and parents for +/// a process using its pid +pub struct ProcessLookupTable { + /// Mapping from pid to name + process_names: HashMap, + /// Mapping from pid to parent pid + process_parents: HashMap, +} - // Allocate the memory to use for the entries - let mut entry: PROCESSENTRY32 = std::mem::zeroed(); - entry.dwSize = std::mem::size_of::() as u32; - - // Process the first item - if Process32First(handle, &mut entry) != FALSE { - loop { - let target_process = processes - .iter_mut() - .find(|proc| proc.pid == entry.th32ProcessID); - if let Some(target_process) = target_process { - let name_chars = entry - .szExeFile - .iter() - .copied() - .take_while(|value| *value != 0) - .collect(); - - let name = String::from_utf8(name_chars); - if let Ok(name) = name { - target_process.name = Some(name) - } - } +impl ProcessLookupTable { + pub fn create() -> Result { + let mut process_names: HashMap = HashMap::new(); + let mut process_parents: HashMap = HashMap::new(); - // Process the next entry - if Process32Next(handle, &mut entry) == FALSE { - break; - } - } + WindowsProcessesSnapshot::create()?.for_each(|entry| { + process_names.insert(entry.th32ProcessID, get_process_entry_name(&entry)); + process_parents.insert(entry.th32ProcessID, entry.th32ParentProcessID); + }); + + Ok(Self { + process_names, + process_parents, + }) } +} - // Close the handle now that its no longer needed - CloseHandle(handle); +/// Finds any parent processes of the provided process, adding +/// the process to the list of parents +/// +/// WARNING - This worked in the previous versions because the implementation +/// was flawwed and didn't properly look up the tree of parents, trying to kill +/// all of the parents causes problems since you'll end up killing explorer.exe +/// or some other windows sys process. So I've limited the depth to a single process deep +/// +/// # Arguments +/// +/// * `process` - The process to collect parents for +fn lookup_process_parents( + lookup_table: &ProcessLookupTable, + process: &mut WindowsProcess, +) -> Result<()> { + const MAX_PARENT_DEPTH: u8 = 1; + + let mut current_procces = process; + let mut depth = 0; + + while let Some(&parent_pid) = lookup_table.process_parents.get(¤t_procces.pid) { + if depth == MAX_PARENT_DEPTH { + break; + } + + let process_name = lookup_table + .process_names + .get(&parent_pid) + .cloned() + .unwrap_or_else(|| "Unknown".to_string()); + + // Add the new parent process + let parent = current_procces + .parent + .insert(Box::new(WindowsProcess::new(parent_pid, process_name))); + + current_procces = parent; + depth += 1 + } Ok(()) } -/// Collects all the parent processes for the PIDs in -/// the provided set +/// Parses the name from a process entry, falls back to "Unknown" +/// for invalid names /// /// # Arguments /// -/// * `pids` - The set to match PIDs from and insert PIDs into -unsafe fn collect_parents(pids: &mut HashSet) -> Result<()> { - // Request a snapshot handle - let handle: HANDLE = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); +/// * `entry` - The process entry +fn get_process_entry_name(entry: &PROCESSENTRY32) -> String { + let name_chars = entry + .szExeFile + .iter() + .copied() + .take_while(|value| *value != 0) + .collect(); + + let name = String::from_utf8(name_chars); + name.unwrap_or_else(|_| "Unknown".to_string()) +} - // Ensure we got a valid handle - if handle == INVALID_HANDLE_VALUE { - let error: WIN32_ERROR = GetLastError(); - return Err(Error::new( - ErrorKind::Other, - format!("Failed to get handle to processes: {:#x}", error), - )); +/// Snapshot of the running windows processes that can be iterated to find +/// information about various processes such as parent processes and +/// process names +/// +/// This is a safe abstraction +pub struct WindowsProcessesSnapshot { + /// Handle to the snapshot + handle: HANDLE, + /// The memory for reading process entries + entry: PROCESSENTRY32, + /// State of reading + state: SnapshotState, +} + +/// State for the snapshot iterator +pub enum SnapshotState { + /// Can read the first entry + First, + /// Can read the next entry + Next, + /// Reached the end, cannot iterate further always give [None] + End, +} + +impl WindowsProcessesSnapshot { + /// Creates a new process snapshot to iterate + pub fn create() -> Result { + // Request a snapshot handle + let handle: HANDLE = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }; + + // Ensure we got a valid handle + if handle == INVALID_HANDLE_VALUE { + let error: WIN32_ERROR = unsafe { GetLastError() }; + return Err(Error::new( + ErrorKind::Other, + format!("Failed to get handle to processes: {:#x}", error), + )); + } + + // Allocate the memory to use for the entries + let mut entry: PROCESSENTRY32 = unsafe { std::mem::zeroed() }; + entry.dwSize = std::mem::size_of::() as u32; + + Ok(Self { + handle, + entry, + state: SnapshotState::First, + }) } +} - // Allocate the memory to use for the entries - let mut entry: PROCESSENTRY32 = std::mem::zeroed(); - entry.dwSize = std::mem::size_of::() as u32; +impl Iterator for WindowsProcessesSnapshot { + type Item = PROCESSENTRY32; - // Process the first item - if Process32First(handle, &mut entry) != FALSE { - let mut count = 0; + fn next(&mut self) -> Option { + match self.state { + SnapshotState::First => { + // Process the first entry + if unsafe { Process32First(self.handle, &mut self.entry) } == FALSE { + self.state = SnapshotState::End; + return None; + } + self.state = SnapshotState::Next; - loop { - // Add matching processes to the output - if pids.contains(&entry.th32ProcessID) { - pids.insert(entry.th32ParentProcessID); - count += 1; + Some(self.entry) } + SnapshotState::Next => { + // Process the next entry + if unsafe { Process32Next(self.handle, &mut self.entry) } == FALSE { + self.state = SnapshotState::End; + return None; + } - // Process the next entry - if Process32Next(handle, &mut entry) == FALSE { - break; + Some(self.entry) } + SnapshotState::End => None, } - - info!("Collected {} parent processes", count); } +} - // Close the handle now that its no longer needed - CloseHandle(handle); - - Ok(()) +impl Drop for WindowsProcessesSnapshot { + fn drop(&mut self) { + unsafe { + // Close the handle now that its no longer needed + CloseHandle(self.handle); + } + } } /// Kills a process with the provided process ID /// /// # Arguments /// -/// * `pid` - The process ID -unsafe fn kill_process(pid: u32) -> Result<()> { - info!("Killing process with PID {}", pid); +/// * `process` - The process +unsafe fn kill_process(process: &WindowsProcess) -> Result<()> { + info!("Killing process {}:{}", process.get_name(), process.pid); // Open the process handle with intent to terminate - let handle: HANDLE = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + let handle: HANDLE = OpenProcess(PROCESS_TERMINATE, FALSE, process.pid); if handle == 0 { + // If the process just isn't running we can ignore the error + if !is_process_running(process.pid)? { + return Ok(()); + } + let error: WIN32_ERROR = GetLastError(); return Err(Error::new( ErrorKind::Other, - format!("Failed to obtain handle to process {}: {:#x}", pid, error), + format!( + "Failed to obtain handle to process {}:{}: {:#x}", + process.get_name(), + process.pid, + error + ), )); } @@ -247,7 +344,12 @@ unsafe fn kill_process(pid: u32) -> Result<()> { let error: WIN32_ERROR = GetLastError(); return Err(Error::new( ErrorKind::Other, - format!("Failed to terminate process {}: {:#x}", pid, error), + format!( + "Failed to terminate process {}:{}: {:#x}", + process.get_name(), + process.pid, + error + ), )); } diff --git a/tests/killport_windows_tests.rs b/tests/killport_windows_tests.rs index 6d19668..dd36b56 100644 --- a/tests/killport_windows_tests.rs +++ b/tests/killport_windows_tests.rs @@ -126,7 +126,7 @@ fn kill_service_by_port_dry_run() { #[test] fn check_process_type_and_name() { - let process = WindowsProcess::new(1234, Some("unique_process".to_string())); + let process = WindowsProcess::new(1234, "unique_process".to_string()); assert_eq!(process.get_type(), KillableType::Process); assert_eq!(process.get_name(), "unique_process"); From 4fdb1c1e7d4493ad753283e2346ee62a0ac5a55d Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Wed, 22 May 2024 21:04:21 +1200 Subject: [PATCH 4/6] fix: mock process name on windows --- tests/integration_test.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 6ee506d..10a49b8 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -4,6 +4,11 @@ use utils::start_listener_process; use assert_cmd::Command; use tempfile::tempdir; +#[cfg(unix)] +const MOCK_PROCESS_NAME: &str = "mock_process"; +#[cfg(windows)] +const MOCK_PROCESS_NAME: &str = "mock_process.exe"; + #[test] fn test_basic_kill_no_process() { let mut cmd = Command::cargo_bin("killport").unwrap(); @@ -21,10 +26,9 @@ fn test_basic_kill_process() { let mut child = start_listener_process(tempdir_path, 8080); let mut cmd = Command::cargo_bin("killport").unwrap(); - cmd.args(&["8080"]) - .assert() - .success() - .stdout("Successfully killed process 'mock_process' listening on port 8080\n"); + cmd.args(&["8080"]).assert().success().stdout(format!( + "Successfully killed process '{MOCK_PROCESS_NAME}' listening on port 8080\n" + )); // Clean up let _ = child.kill(); @@ -66,7 +70,7 @@ fn test_mode_option() { .assert() .success() .stdout(format!( - "Successfully killed process 'mock_process' listening on port 8082\n" + "Successfully killed process '{MOCK_PROCESS_NAME}' listening on port 8082\n" )); // Clean up let _ = child.kill(); @@ -103,7 +107,9 @@ fn test_dry_run_option() { cmd.args(&["8083", "--dry-run"]) .assert() .success() - .stdout("Would kill process 'mock_process' listening on port 8083\n"); + .stdout(format!( + "Would kill process '{MOCK_PROCESS_NAME}' listening on port 8083\n" + )); // Clean up let _ = child.kill(); From 0689a0781d5cb726065c4959da147d7779312723 Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Wed, 22 May 2024 21:07:07 +1200 Subject: [PATCH 5/6] fix: mock process name on windows Missed one --- tests/integration_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 10a49b8..bbe4ef0 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -48,7 +48,7 @@ fn test_signal_handling() { .assert() .success() .stdout(format!( - "Successfully killed process 'mock_process' listening on port 8081\n" + "Successfully killed process '{MOCK_PROCESS_NAME}' listening on port 8081\n" )); // Clean up From 64d2e5a3d61403eca1ff7ac0f83af653e484196c Mon Sep 17 00:00:00 2001 From: Jacobtread Date: Thu, 23 May 2024 18:38:17 +1200 Subject: [PATCH 6/6] fix: disable killing nested processes Depth to zero for now to prevent killing parent processes --- src/windows.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index 19070b6..1fab695 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -162,7 +162,10 @@ impl ProcessLookupTable { /// WARNING - This worked in the previous versions because the implementation /// was flawwed and didn't properly look up the tree of parents, trying to kill /// all of the parents causes problems since you'll end up killing explorer.exe -/// or some other windows sys process. So I've limited the depth to a single process deep +/// or some other windows sys process. This has been disabled (Depth of 0) but +/// may be enabled in a future release +/// +/// /// /// # Arguments /// @@ -171,7 +174,7 @@ fn lookup_process_parents( lookup_table: &ProcessLookupTable, process: &mut WindowsProcess, ) -> Result<()> { - const MAX_PARENT_DEPTH: u8 = 1; + const MAX_PARENT_DEPTH: u8 = 0; let mut current_procces = process; let mut depth = 0;