diff --git a/src/common.rs b/src/common.rs index 1bf20a5ea..0f0bd0320 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1624,7 +1624,7 @@ impl UpdateKind { /// /// When all refresh are ruled out, a [`Process`] will still retrieve the following information: /// * Process ID ([`Pid`]) -/// * Parent process ID +/// * Parent process ID (on Windows it never changes though) /// * Process name /// * Start time /// diff --git a/src/unix/apple/macos/process.rs b/src/unix/apple/macos/process.rs index 4e235ffc6..3d2580f0f 100644 --- a/src/unix/apple/macos/process.rs +++ b/src/unix/apple/macos/process.rs @@ -335,6 +335,13 @@ unsafe fn get_bsd_info(pid: Pid) -> Option { } } +fn get_parent(info: &libc::proc_bsdinfo) -> Option { + match info.pbi_ppid as i32 { + 0 => None, + p => Some(Pid(p)), + } +} + unsafe fn create_new_process( pid: Pid, now: u64, @@ -353,10 +360,8 @@ unsafe fn create_new_process( return Err(()); } }; - let parent = match info.pbi_ppid as i32 { - 0 => None, - p => Some(Pid(p)), - }; + + let parent = get_parent(&info); let start_time = info.pbi_start_tvsec; let run_time = now.saturating_sub(start_time); @@ -654,6 +659,11 @@ pub(crate) fn update_process( // The owner of this PID changed. return create_new_process(pid, now, refresh_kind, Some(info)); } + let parent = get_parent(&info); + // Update the parent if it changed. + if p.parent != parent { + p.parent = parent; + } } if !get_process_infos(p, refresh_kind) { diff --git a/src/unix/linux/component.rs b/src/unix/linux/component.rs index 1cbed6734..fc0b2fb72 100644 --- a/src/unix/linux/component.rs +++ b/src/unix/linux/component.rs @@ -240,9 +240,16 @@ impl ComponentInner { let dir = read_dir(folder).ok()?; let mut matchings: HashMap = HashMap::with_capacity(10); for entry in dir.flatten() { + let Ok(file_type) = entry.file_type() else { + continue; + }; + if file_type.is_dir() { + continue; + } + let entry = entry.path(); let filename = entry.file_name().and_then(|x| x.to_str()).unwrap_or(""); - if entry.is_dir() || !filename.starts_with("temp") { + if !filename.starts_with("temp") { continue; } @@ -363,8 +370,11 @@ impl ComponentsInner { self.components.clear(); if let Ok(dir) = read_dir(Path::new("/sys/class/hwmon/")) { for entry in dir.flatten() { + let Ok(file_type) = entry.file_type() else { + continue; + }; let entry = entry.path(); - if !entry.is_dir() + if !file_type.is_dir() || !entry .file_name() .and_then(|x| x.to_str()) diff --git a/src/unix/linux/network.rs b/src/unix/linux/network.rs index da7040982..e2b8d1454 100644 --- a/src/unix/linux/network.rs +++ b/src/unix/linux/network.rs @@ -1,9 +1,9 @@ // Take a look at the license at the top of the repository in the LICENSE file. use std::collections::{hash_map, HashMap}; +use std::fs::File; use std::io::Read; use std::path::Path; -use std::{fs::File, u8}; use crate::common::MacAddr; use crate::network::refresh_networks_addresses; diff --git a/src/unix/linux/process.rs b/src/unix/linux/process.rs index 408916502..1e37b0891 100644 --- a/src/unix/linux/process.rs +++ b/src/unix/linux/process.rs @@ -396,12 +396,15 @@ fn refresh_user_group_ids( #[allow(clippy::too_many_arguments)] fn update_proc_info( p: &mut ProcessInner, + parent_pid: Option, refresh_kind: ProcessRefreshKind, proc_path: &mut PathHandler, parts: &[&str], uptime: u64, info: &SystemInfo, ) { + update_parent_pid(p, parent_pid, parts); + get_status(p, parts[ProcIndex::State as usize]); refresh_user_group_ids(p, proc_path, refresh_kind); @@ -430,6 +433,16 @@ fn update_proc_info( } } +fn update_parent_pid(p: &mut ProcessInner, parent_pid: Option, str_parts: &[&str]) { + p.parent = match parent_pid { + Some(parent_pid) if parent_pid.0 != 0 => Some(parent_pid), + _ => match Pid::from_str(str_parts[ProcIndex::ParentPid as usize]) { + Ok(p) if p.0 != 0 => Some(p), + _ => None, + }, + }; +} + fn retrieve_all_new_process_info( pid: Pid, parent_pid: Option, @@ -443,14 +456,6 @@ fn retrieve_all_new_process_info( let mut proc_path = PathHandler::new(path); let name = parts[ProcIndex::ShortExe as usize]; - p.parent = match parent_pid { - Some(parent_pid) if parent_pid.0 != 0 => Some(parent_pid), - _ => match Pid::from_str(parts[ProcIndex::ParentPid as usize]) { - Ok(p) if p.0 != 0 => Some(p), - _ => None, - }, - }; - p.start_time_without_boot_time = compute_start_time_without_boot_time(parts, info); p.start_time = p .start_time_without_boot_time @@ -466,7 +471,15 @@ fn retrieve_all_new_process_info( p.thread_kind = Some(ThreadKind::Userland); } - update_proc_info(&mut p, refresh_kind, &mut proc_path, parts, uptime, info); + update_proc_info( + &mut p, + parent_pid, + refresh_kind, + &mut proc_path, + parts, + uptime, + info, + ); Process { inner: p } } @@ -508,7 +521,15 @@ pub(crate) fn _get_process_data( if start_time_without_boot_time == entry.start_time_without_boot_time { let mut proc_path = PathHandler::new(path); - update_proc_info(entry, refresh_kind, &mut proc_path, &parts, uptime, info); + update_proc_info( + entry, + parent_pid, + refresh_kind, + &mut proc_path, + &parts, + uptime, + info, + ); refresh_user_group_ids(entry, &mut proc_path, refresh_kind); return Ok((None, pid)); @@ -636,6 +657,13 @@ fn get_all_pid_entries( entry: DirEntry, data: &mut Vec, ) -> Option { + let Ok(file_type) = entry.file_type() else { + return None; + }; + if !file_type.is_dir() { + return None; + } + let entry = entry.path(); let name = entry.file_name(); @@ -644,26 +672,23 @@ fn get_all_pid_entries( return None; } let name = name?; - if !entry.is_dir() { - return None; - } let pid = Pid::from(usize::from_str(&name.to_string_lossy()).ok()?); let tasks_dir = Path::join(&entry, "task"); - let tasks = if tasks_dir.is_dir() { + + let tasks = if let Ok(entries) = fs::read_dir(tasks_dir) { let mut tasks = HashSet::new(); - if let Ok(entries) = fs::read_dir(tasks_dir) { - for task in entries - .into_iter() - .filter_map(|entry| get_all_pid_entries(Some(name), Some(pid), entry.ok()?, data)) - { - tasks.insert(task); - } + for task in entries + .into_iter() + .filter_map(|entry| get_all_pid_entries(Some(name), Some(pid), entry.ok()?, data)) + { + tasks.insert(task); } Some(tasks) } else { None }; + data.push(ProcAndTasks { pid, parent_pid, diff --git a/src/windows/disk.rs b/src/windows/disk.rs index 440743b3d..16c7eafdb 100644 --- a/src/windows/disk.rs +++ b/src/windows/disk.rs @@ -1,5 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. +use crate::sys::utils::HandleWrapper; use crate::{Disk, DiskKind}; use std::ffi::{c_void, OsStr, OsString}; @@ -8,11 +9,10 @@ use std::os::windows::ffi::OsStringExt; use std::path::Path; use windows::core::{Error, HRESULT, PCWSTR}; -use windows::Win32::Foundation::{CloseHandle, HANDLE, MAX_PATH}; +use windows::Win32::Foundation::MAX_PATH; use windows::Win32::Storage::FileSystem::{ - CreateFileW, FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, GetDiskFreeSpaceExW, - GetDriveTypeW, GetVolumeInformationW, GetVolumePathNamesForVolumeNameW, FILE_ACCESS_RIGHTS, - FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING, + FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, GetDiskFreeSpaceExW, GetDriveTypeW, + GetVolumeInformationW, GetVolumePathNamesForVolumeNameW, }; use windows::Win32::System::Ioctl::{ PropertyStandardQuery, StorageDeviceSeekPenaltyProperty, DEVICE_SEEK_PENALTY_DESCRIPTOR, @@ -205,31 +205,6 @@ impl DisksInner { } } -struct HandleWrapper(HANDLE); - -impl HandleWrapper { - unsafe fn new(drive_name: &[u16], open_rights: FILE_ACCESS_RIGHTS) -> Option { - let lpfilename = PCWSTR::from_raw(drive_name.as_ptr()); - let handle = CreateFileW( - lpfilename, - open_rights.0, - FILE_SHARE_READ | FILE_SHARE_WRITE, - None, - OPEN_EXISTING, - Default::default(), - HANDLE::default(), - ) - .ok()?; - Some(Self(handle)) - } -} - -impl Drop for HandleWrapper { - fn drop(&mut self) { - let _err = unsafe { CloseHandle(self.0) }; - } -} - unsafe fn get_drive_size(mount_point: &[u16]) -> Option<(u64, u64)> { let mut total_size = 0; let mut available_space = 0; @@ -292,7 +267,8 @@ pub(crate) unsafe fn get_list() -> Vec { .copied() .chain([0]) .collect::>(); - let Some(handle) = HandleWrapper::new(&device_path[..], Default::default()) else { + let Some(handle) = HandleWrapper::new_from_file(&device_path[..], Default::default()) + else { return Vec::new(); }; let Some((total_space, available_space)) = get_drive_size(&mount_paths[0][..]) else { diff --git a/src/windows/process.rs b/src/windows/process.rs index 3178b9bc4..3933302f1 100644 --- a/src/windows/process.rs +++ b/src/windows/process.rs @@ -1,6 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::sys::system::is_proc_running; +use crate::sys::utils::HandleWrapper; use crate::windows::Sid; use crate::{DiskUsage, Gid, Pid, ProcessRefreshKind, ProcessStatus, Signal, Uid}; @@ -9,7 +10,6 @@ use std::fmt; #[cfg(feature = "debug")] use std::io; use std::mem::{size_of, zeroed, MaybeUninit}; -use std::ops::Deref; use std::os::windows::ffi::OsStringExt; use std::os::windows::process::CommandExt; use std::path::{Path, PathBuf}; @@ -31,9 +31,8 @@ use windows::Wdk::System::Threading::{ ProcessWow64Information, PROCESSINFOCLASS, }; use windows::Win32::Foundation::{ - CloseHandle, LocalFree, ERROR_INSUFFICIENT_BUFFER, FILETIME, HANDLE, HINSTANCE, HLOCAL, - MAX_PATH, STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH, - UNICODE_STRING, + LocalFree, ERROR_INSUFFICIENT_BUFFER, FILETIME, HANDLE, HINSTANCE, HLOCAL, MAX_PATH, + STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH, UNICODE_STRING, }; use windows::Win32::Security::{GetTokenInformation, TokenUser, TOKEN_QUERY, TOKEN_USER}; use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory; @@ -168,32 +167,6 @@ unsafe fn get_process_user_id(process: &mut ProcessInner, refresh_kind: ProcessR } } -struct HandleWrapper(HANDLE); - -impl HandleWrapper { - fn new(handle: HANDLE) -> Option { - if handle.is_invalid() { - None - } else { - Some(Self(handle)) - } - } -} - -impl Deref for HandleWrapper { - type Target = HANDLE; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Drop for HandleWrapper { - fn drop(&mut self) { - let _err = unsafe { CloseHandle(self.0) }; - } -} - #[allow(clippy::non_send_fields_in_send_ty)] unsafe impl Send for HandleWrapper {} unsafe impl Sync for HandleWrapper {} @@ -209,7 +182,7 @@ pub(crate) struct ProcessInner { root: Option, pub(crate) memory: u64, pub(crate) virtual_memory: u64, - parent: Option, + pub(crate) parent: Option, status: ProcessStatus, handle: Option>, cpu_calc_values: CPUsageCalculationValues, @@ -353,48 +326,20 @@ unsafe fn get_exe(process_handler: &HandleWrapper) -> Option { } impl ProcessInner { - pub(crate) fn new_from_pid( - pid: Pid, - now: u64, - refresh_kind: ProcessRefreshKind, - ) -> Option { + pub(crate) fn new_from_pid(pid: Pid, now: u64) -> Option { unsafe { let process_handler = get_process_handler(pid)?; - let mut info: MaybeUninit = MaybeUninit::uninit(); - if NtQueryInformationProcess( - process_handler.0, - ProcessBasicInformation, - info.as_mut_ptr().cast(), - size_of::() as _, - null_mut(), - ) - .is_err() - { - return None; - } - let info = info.assume_init(); - let name = get_process_name(pid).unwrap_or_default(); - let exe = if refresh_kind.exe().needs_update(|| true) { - get_exe(&process_handler) - } else { - None - }; let (start_time, run_time) = get_start_and_run_time(*process_handler, now); - let parent = if info.InheritedFromUniqueProcessId != 0 { - Some(Pid(info.InheritedFromUniqueProcessId as _)) - } else { - None - }; - let mut p = Self { + Some(Self { handle: Some(Arc::new(process_handler)), name, pid, - parent, + parent: None, user_id: None, cmd: Vec::new(), environ: Vec::new(), - exe, + exe: None, cwd: None, root: None, status: ProcessStatus::Run, @@ -409,10 +354,7 @@ impl ProcessInner { old_written_bytes: 0, read_bytes: 0, written_bytes: 0, - }; - get_process_user_id(&mut p, refresh_kind); - get_process_params(&mut p, refresh_kind); - Some(p) + }) } } @@ -423,75 +365,36 @@ impl ProcessInner { virtual_memory: u64, name: String, now: u64, - refresh_kind: ProcessRefreshKind, ) -> Self { - if let Some(handle) = get_process_handler(pid) { - unsafe { - let exe = if refresh_kind.exe().needs_update(|| true) { - get_exe(&handle) - } else { - None - }; - let (start_time, run_time) = get_start_and_run_time(*handle, now); - let mut p = Self { - handle: Some(Arc::new(handle)), - name, - pid, - user_id: None, - parent, - cmd: Vec::new(), - environ: Vec::new(), - exe, - cwd: None, - root: None, - status: ProcessStatus::Run, - memory, - virtual_memory, - cpu_usage: 0., - cpu_calc_values: CPUsageCalculationValues::new(), - start_time, - run_time, - updated: true, - old_read_bytes: 0, - old_written_bytes: 0, - read_bytes: 0, - written_bytes: 0, - }; - - get_process_user_id(&mut p, refresh_kind); - get_process_params(&mut p, refresh_kind); - p - } + let (handle, start_time, run_time) = if let Some(handle) = get_process_handler(pid) { + let (start_time, run_time) = get_start_and_run_time(*handle, now); + (Some(Arc::new(handle)), start_time, run_time) } else { - let exe = if refresh_kind.exe().needs_update(|| true) { - get_executable_path(pid) - } else { - None - }; - Self { - handle: None, - name, - pid, - user_id: None, - parent, - cmd: Vec::new(), - environ: Vec::new(), - exe, - cwd: None, - root: None, - status: ProcessStatus::Run, - memory, - virtual_memory, - cpu_usage: 0., - cpu_calc_values: CPUsageCalculationValues::new(), - start_time: 0, - run_time: 0, - updated: true, - old_read_bytes: 0, - old_written_bytes: 0, - read_bytes: 0, - written_bytes: 0, - } + (None, 0, 0) + }; + Self { + handle, + name, + pid, + user_id: None, + parent, + cmd: Vec::new(), + environ: Vec::new(), + exe: None, + cwd: None, + root: None, + status: ProcessStatus::Run, + memory, + virtual_memory, + cpu_usage: 0., + cpu_calc_values: CPUsageCalculationValues::new(), + start_time, + run_time, + updated: true, + old_read_bytes: 0, + old_written_bytes: 0, + read_bytes: 0, + written_bytes: 0, } } @@ -500,6 +403,7 @@ impl ProcessInner { refresh_kind: crate::ProcessRefreshKind, nb_cpus: u64, now: u64, + refresh_parent: bool, ) { if refresh_kind.cpu() { compute_cpu_usage(self, nb_cpus); @@ -512,7 +416,7 @@ impl ProcessInner { } unsafe { get_process_user_id(self, refresh_kind); - get_process_params(self, refresh_kind); + get_process_params(self, refresh_kind, refresh_parent); } if refresh_kind.exe().needs_update(|| self.exe.is_none()) { unsafe { @@ -857,16 +761,25 @@ macro_rules! impl_RtlUserProcessParameters { impl_RtlUserProcessParameters!(RTL_USER_PROCESS_PARAMETERS32); impl_RtlUserProcessParameters!(RTL_USER_PROCESS_PARAMETERS); -unsafe fn get_process_params(process: &mut ProcessInner, refresh_kind: ProcessRefreshKind) { - if !(refresh_kind.cmd().needs_update(|| process.cmd.is_empty()) +fn has_anything_to_update(process: &ProcessInner, refresh_kind: ProcessRefreshKind) -> bool { + refresh_kind.cmd().needs_update(|| process.cmd.is_empty()) || refresh_kind .environ() .needs_update(|| process.environ.is_empty()) || refresh_kind.cwd().needs_update(|| process.cwd.is_none()) - || refresh_kind.root().needs_update(|| process.root.is_none())) - { + || refresh_kind.root().needs_update(|| process.root.is_none()) +} + +unsafe fn get_process_params( + process: &mut ProcessInner, + refresh_kind: ProcessRefreshKind, + refresh_parent: bool, +) { + let has_anything_to_update = has_anything_to_update(process, refresh_kind); + if !refresh_parent && !has_anything_to_update { return; } + let handle = match process.handle.as_ref().map(|handle| handle.0) { Some(h) => h, None => return, @@ -906,6 +819,18 @@ unsafe fn get_process_params(process: &mut ProcessInner, refresh_kind: ProcessRe } let pinfo = pbasicinfo.assume_init(); + let ppid: usize = pinfo.InheritedFromUniqueProcessId as _; + let parent = if ppid != 0 { + Some(Pid(pinfo.InheritedFromUniqueProcessId as _)) + } else { + None + }; + process.parent = parent; + + if !has_anything_to_update { + return; + } + let mut peb = MaybeUninit::::uninit(); if ReadProcessMemory( handle, @@ -949,6 +874,10 @@ unsafe fn get_process_params(process: &mut ProcessInner, refresh_kind: ProcessRe } // target is a 32 bit process in wow64 mode + if !has_anything_to_update { + return; + } + let mut peb32 = MaybeUninit::::uninit(); if ReadProcessMemory( handle, diff --git a/src/windows/system.rs b/src/windows/system.rs index ff631ee58..75df62b05 100644 --- a/src/windows/system.rs +++ b/src/windows/system.rs @@ -182,8 +182,8 @@ impl SystemInner { } // We need to re-make the process because the PID owner changed. } - if let Some(mut p) = ProcessInner::new_from_pid(pid, now, refresh_kind) { - p.update(refresh_kind, nb_cpus, now); + if let Some(mut p) = ProcessInner::new_from_pid(pid, now) { + p.update(refresh_kind, nb_cpus, now, true); p.updated = false; self.process_list.insert(pid, Process { inner: p }); true @@ -307,6 +307,12 @@ impl SystemInner { // as above, read_unaligned is necessary let pi = ptr::read_unaligned(pi.0); let pid = Pid(pi.UniqueProcessId as _); + let ppid: usize = pi.InheritedFromUniqueProcessId as _; + let parent = if ppid != 0 { + Some(Pid(pi.InheritedFromUniqueProcessId as _)) + } else { + None + }; if let Some(proc_) = (*process_list.0.get()).get_mut(&pid) { let proc_ = &mut proc_.inner; if proc_ @@ -318,7 +324,9 @@ impl SystemInner { proc_.memory = pi.WorkingSetSize as _; proc_.virtual_memory = pi.VirtualSize as _; } - proc_.update(refresh_kind, nb_cpus, now); + proc_.update(refresh_kind, nb_cpus, now, false); + // Update the parent in case it changed. + proc_.parent = parent; return None; } // If the PID owner changed, we need to recompute the whole process. @@ -330,20 +338,9 @@ impl SystemInner { } else { (0, 0) }; - let mut p = ProcessInner::new_full( - pid, - if pi.InheritedFromUniqueProcessId as usize != 0 { - Some(Pid(pi.InheritedFromUniqueProcessId as _)) - } else { - None - }, - memory, - virtual_memory, - name, - now, - refresh_kind, - ); - p.update(refresh_kind.without_memory(), nb_cpus, now); + let mut p = + ProcessInner::new_full(pid, parent, memory, virtual_memory, name, now); + p.update(refresh_kind.without_memory(), nb_cpus, now, false); Some(Process { inner: p }) }) .collect::>(); @@ -528,7 +525,7 @@ fn refresh_existing_process( } else { return Some(false); } - proc_.update(refresh_kind, nb_cpus, now); + proc_.update(refresh_kind, nb_cpus, now, false); proc_.updated = false; Some(true) } diff --git a/src/windows/utils.rs b/src/windows/utils.rs index 56deb796d..237ae3a44 100644 --- a/src/windows/utils.rs +++ b/src/windows/utils.rs @@ -1,12 +1,16 @@ // Take a look at the license at the top of the repository in the LICENSE file. use windows::core::{PCWSTR, PWSTR}; -use windows::Win32::Foundation::{self, FILETIME}; +use windows::Win32::Foundation::{self, CloseHandle, FILETIME, HANDLE}; +use windows::Win32::Storage::FileSystem::{ + CreateFileW, FILE_ACCESS_RIGHTS, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING, +}; use windows::Win32::System::Registry::{ RegCloseKey, RegOpenKeyExW, RegQueryValueExW, HKEY, KEY_READ, REG_NONE, }; use std::ffi::OsStr; +use std::ops::Deref; use std::os::windows::ffi::OsStrExt; use std::time::SystemTime; @@ -132,3 +136,47 @@ pub(crate) fn get_reg_value_u32(hkey: HKEY, path: &str, field_name: &str) -> Opt .ok() } } + +pub(crate) struct HandleWrapper(pub(crate) HANDLE); + +impl HandleWrapper { + pub(crate) fn new(handle: HANDLE) -> Option { + if handle.is_invalid() { + None + } else { + Some(Self(handle)) + } + } + + pub(crate) unsafe fn new_from_file( + drive_name: &[u16], + open_rights: FILE_ACCESS_RIGHTS, + ) -> Option { + let lpfilename = PCWSTR::from_raw(drive_name.as_ptr()); + let handle = CreateFileW( + lpfilename, + open_rights.0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + None, + OPEN_EXISTING, + Default::default(), + HANDLE::default(), + ) + .ok()?; + Some(Self(handle)) + } +} + +impl Deref for HandleWrapper { + type Target = HANDLE; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Drop for HandleWrapper { + fn drop(&mut self) { + let _err = unsafe { CloseHandle(self.0) }; + } +} diff --git a/test_bin/main.rs b/test_bin/main.rs index d15425064..8957b0037 100644 --- a/test_bin/main.rs +++ b/test_bin/main.rs @@ -1,5 +1,25 @@ // Does nothing and just exits after waiting for 30 seconds. +use std::process::{Child, Command}; + +fn maybe_start_child(last: String, args: &[String]) -> Option { + if last == "1" { + let mut cmd = Command::new(&args[0]); + for arg in &args[1..] { + cmd.arg(arg); + } + Some(cmd.spawn().expect("failed to run command")) + } else { + None + } +} + fn main() { - std::thread::sleep(std::time::Duration::from_secs(30)); + let mut args: Vec = std::env::args().collect(); + let child = args.pop().and_then(|last| maybe_start_child(last, &args)); + if child.is_some() { + std::thread::sleep(std::time::Duration::from_secs(3)); + } else { + std::thread::sleep(std::time::Duration::from_secs(30)); + } } diff --git a/tests/code_checkers/utils.rs b/tests/code_checkers/utils.rs index 159b30d36..3ca359b23 100644 --- a/tests/code_checkers/utils.rs +++ b/tests/code_checkers/utils.rs @@ -25,8 +25,9 @@ pub fn read_dirs, F: FnMut(&Path, &str)>(dirs: &[P], callback: &m fn read_dir, F: FnMut(&Path, &str)>(dir: P, callback: &mut F) { for entry in fs::read_dir(dir).expect("read_dir failed") { let entry = entry.expect("entry failed"); + let file_type = entry.file_type().expect("file_type failed"); let path = entry.path(); - if path.is_dir() { + if file_type.is_dir() { read_dir(path, callback); } else if path .extension() diff --git a/tests/process.rs b/tests/process.rs index 8f04a17f9..9c4b2db85 100644 --- a/tests/process.rs +++ b/tests/process.rs @@ -80,11 +80,11 @@ fn test_cmd() { } } -fn build_test_binary() { +fn build_test_binary(file_name: &str) { std::process::Command::new("rustc") .arg("test_bin/main.rs") .arg("-o") - .arg("target/test_binary") + .arg(file_name) .stdout(std::process::Stdio::null()) .spawn() .unwrap() @@ -97,8 +97,9 @@ fn test_environ() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } - build_test_binary(); - let mut p = std::process::Command::new("./target/test_binary") + let file_name = "target/test_binary"; + build_test_binary(file_name); + let mut p = std::process::Command::new(format!("./{file_name}")) .env("FOO", "BAR") .env("OTHER", "VALUE") .spawn() @@ -794,3 +795,52 @@ fn test_process_run_time() { run_time ); } + +// Test that if the parent of a process is removed, then the child PID will be +// updated as well. +#[test] +fn test_parent_change() { + if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") || cfg!(windows) { + // Windows never updates its parent PID so no need to check anything. + return; + } + + let file_name = "target/test_binary2"; + build_test_binary(file_name); + let mut p = std::process::Command::new(format!("./{file_name}")) + .arg("1") + .spawn() + .unwrap(); + + std::thread::sleep(std::time::Duration::from_secs(1)); + + let pid = Pid::from_u32(p.id() as _); + let mut s = System::new(); + s.refresh_processes(); + + assert_eq!( + s.process(pid).expect("process was not created").parent(), + sysinfo::get_current_pid().ok(), + ); + + let child_pid = s + .processes() + .iter() + .find(|(_, proc_)| proc_.parent() == Some(pid)) + .map(|(pid, _)| *pid) + .expect("failed to get child process"); + + // Waiting for the parent process to stop. + p.wait().expect("wait failed"); + + s.refresh_processes(); + // Parent should not be around anymore. + assert!(s.process(pid).is_none()); + + let child = s.process(child_pid).expect("child is dead"); + // Child should have a different parent now. + assert_ne!(child.parent(), Some(pid)); + + // We kill the child to clean up. + child.kill(); +}