diff --git a/src/common.rs b/src/common.rs index 589f2c1db..aeb5c8652 100644 --- a/src/common.rs +++ b/src/common.rs @@ -3427,6 +3427,7 @@ mod tests { // This test exists to ensure that the `TryFrom` and `FromStr` traits are implemented // on `Uid`, `Gid` and `Pid`. + #[allow(clippy::unnecessary_fallible_conversions)] #[test] fn check_uid_gid_from_impls() { use std::convert::TryFrom; diff --git a/src/lib.rs b/src/lib.rs index acc1a726a..9ca605831 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,6 @@ #![allow(clippy::non_send_fields_in_send_ty)] #![allow(renamed_and_removed_lints)] #![allow(clippy::assertions_on_constants)] -#![allow(unknown_lints)] #[macro_use] mod macros; diff --git a/src/unix/apple/cpu.rs b/src/unix/apple/cpu.rs index ae8bfe1ba..5ccb0bf90 100644 --- a/src/unix/apple/cpu.rs +++ b/src/unix/apple/cpu.rs @@ -279,7 +279,7 @@ pub(crate) fn init_cpus( refresh_kind: CpuRefreshKind, ) { let mut num_cpu = 0; - let mut mib = [0, 0]; + let mut mib = [libc::CTL_HW as _, libc::HW_NCPU as _]; let (vendor_id, brand) = get_vendor_id_and_brand(); let frequency = if refresh_kind.frequency() { @@ -290,8 +290,6 @@ pub(crate) fn init_cpus( unsafe { if !get_sys_value( - libc::CTL_HW as _, - libc::HW_NCPU as _, mem::size_of::(), &mut num_cpu as *mut _ as *mut _, &mut mib, diff --git a/src/unix/apple/macos/process.rs b/src/unix/apple/macos/process.rs index b69004950..7bd997ed4 100644 --- a/src/unix/apple/macos/process.rs +++ b/src/unix/apple/macos/process.rs @@ -6,7 +6,7 @@ use std::path::{Path, PathBuf}; use std::borrow::Borrow; -use libc::{c_int, c_void, kill, size_t}; +use libc::{c_int, c_void, kill}; use crate::{DiskUsage, Gid, Pid, Process, ProcessRefreshKind, ProcessStatus, Signal, Uid}; @@ -363,7 +363,6 @@ unsafe fn convert_node_path_info(node: &libc::vnode_info_path) -> PathBuf { unsafe fn create_new_process( pid: Pid, - mut size: size_t, now: u64, refresh_kind: ProcessRefreshKind, info: Option, @@ -417,9 +416,6 @@ unsafe fn create_new_process( p => Some(Pid(p)), }; - let mut proc_args = Vec::with_capacity(size as _); - let ptr: *mut u8 = proc_args.as_mut_slice().as_mut_ptr(); - let mut mib = [libc::CTL_KERN, libc::KERN_PROCARGS2, pid.0 as _]; /* * /---------------\ 0x00000000 * | ::::::::::::: | @@ -450,74 +446,66 @@ unsafe fn create_new_process( * : : * \---------------/ 0xffffffff */ + let mut mib = [libc::CTL_KERN, libc::KERN_PROCARGS2, pid.0 as _]; + let mut arg_max = 0; + // First we retrieve the size we will need for our data (in `arg_max`). if libc::sysctl( mib.as_mut_ptr(), mib.len() as _, - ptr as *mut c_void, - &mut size, + std::ptr::null_mut(), + &mut arg_max, std::ptr::null_mut(), 0, ) == -1 { + sysinfo_debug!( + "couldn't get arguments and environment size for PID {}", + pid.0 + ); return Err(()); // not enough rights I assume? } - let mut n_args: c_int = 0; - libc::memcpy( - (&mut n_args) as *mut c_int as *mut c_void, - ptr as *const c_void, - mem::size_of::(), - ); - let mut cp = ptr.add(mem::size_of::()); - let mut start = cp; + let mut proc_args: Vec = Vec::with_capacity(arg_max as _); + if libc::sysctl( + mib.as_mut_ptr(), + mib.len() as _, + proc_args.as_mut_slice().as_mut_ptr() as *mut _, + &mut arg_max, + std::ptr::null_mut(), + 0, + ) == -1 + { + sysinfo_debug!("couldn't get arguments and environment for PID {}", pid.0); + return Err(()); // What changed since the previous call? Dark magic! + } + + proc_args.set_len(arg_max); let start_time = info.pbi_start_tvsec; let run_time = now.saturating_sub(start_time); - let mut p = if cp < ptr.add(size) { - while cp < ptr.add(size) && *cp != 0 { - cp = cp.offset(1); - } - let exe = Path::new(get_unchecked_str(cp, start).as_str()).to_path_buf(); + let mut p = if !proc_args.is_empty() { + // We copy the number of arguments (`argc`) to `n_args`. + let mut n_args: c_int = 0; + libc::memcpy( + &mut n_args as *mut _ as *mut _, + proc_args.as_slice().as_ptr() as *const _, + mem::size_of::(), + ); + + // We skip `argc`. + let proc_args = &proc_args[mem::size_of::()..]; + + let (exe, proc_args) = get_exe(proc_args); let name = exe .file_name() .and_then(|x| x.to_str()) .unwrap_or("") .to_owned(); - while cp < ptr.add(size) && *cp == 0 { - cp = cp.offset(1); - } - start = cp; - let mut c = 0; - let mut cmd = Vec::with_capacity(n_args as usize); - while c < n_args && cp < ptr.add(size) { - if *cp == 0 { - c += 1; - cmd.push(get_unchecked_str(cp, start)); - start = cp.offset(1); - } - cp = cp.offset(1); - } - #[inline] - unsafe fn get_environ(ptr: *mut u8, mut cp: *mut u8, size: size_t) -> Vec { - let mut environ = Vec::with_capacity(10); - let mut start = cp; - while cp < ptr.add(size) { - if *cp == 0 { - if cp == start { - break; - } - let e = get_unchecked_str(cp, start); - environ.push(e); - start = cp.offset(1); - } - cp = cp.offset(1); - } - environ - } + let (cmd, proc_args) = get_arguments(proc_args, n_args); - let environ = get_environ(ptr, cp, size); + let environ = get_environ(proc_args); let mut p = ProcessInner::new(pid, parent, start_time, run_time, cwd, root); p.exe = exe; @@ -545,10 +533,67 @@ unsafe fn create_new_process( Ok(Some(Process { inner: p })) } +fn get_exe(data: &[u8]) -> (PathBuf, &[u8]) { + let pos = data.iter().position(|c| *c == 0).unwrap_or(data.len()); + unsafe { + ( + Path::new(std::str::from_utf8_unchecked(&data[..pos])).to_path_buf(), + &data[pos..], + ) + } +} + +fn get_arguments(mut data: &[u8], mut n_args: c_int) -> (Vec, &[u8]) { + if n_args < 1 { + return (Vec::new(), data); + } + while data.first() == Some(&0) { + data = &data[1..]; + } + let mut cmd = Vec::with_capacity(n_args as _); + + unsafe { + while n_args > 0 && !data.is_empty() { + let pos = data.iter().position(|c| *c == 0).unwrap_or(data.len()); + let arg = std::str::from_utf8_unchecked(&data[..pos]); + if !arg.is_empty() { + cmd.push(arg.to_string()); + } + data = &data[pos..]; + while data.first() == Some(&0) { + data = &data[1..]; + } + n_args -= 1; + } + (cmd, data) + } +} + +fn get_environ(mut data: &[u8]) -> Vec { + while data.first() == Some(&0) { + data = &data[1..]; + } + let mut environ = Vec::new(); + unsafe { + while !data.is_empty() { + let pos = data.iter().position(|c| *c == 0).unwrap_or(data.len()); + let arg = std::str::from_utf8_unchecked(&data[..pos]); + if arg.is_empty() { + return environ; + } + environ.push(arg.to_string()); + data = &data[pos..]; + while data.first() == Some(&0) { + data = &data[1..]; + } + } + environ + } +} + pub(crate) fn update_process( wrap: &Wrap, pid: Pid, - size: size_t, time_interval: Option, now: u64, refresh_kind: ProcessRefreshKind, @@ -571,7 +616,7 @@ pub(crate) fn update_process( // We don't it to be removed, just replaced. p.updated = true; // The owner of this PID changed. - return create_new_process(pid, size, now, refresh_kind, Some(info)); + return create_new_process(pid, now, refresh_kind, Some(info)); } } let task_info = get_task_info(pid); @@ -611,7 +656,7 @@ pub(crate) fn update_process( p.updated = true; return Ok(None); } - create_new_process(pid, size, now, refresh_kind, get_bsd_info(pid)) + create_new_process(pid, now, refresh_kind, get_bsd_info(pid)) } } @@ -638,7 +683,6 @@ fn update_proc_disk_activity(p: &mut ProcessInner) { } } -#[allow(unknown_lints)] #[allow(clippy::uninit_vec)] pub(crate) fn get_proc_list() -> Option> { unsafe { @@ -660,14 +704,6 @@ pub(crate) fn get_proc_list() -> Option> { } } -unsafe fn get_unchecked_str(cp: *mut u8, start: *mut u8) -> String { - let len = cp as usize - start as usize; - let part = Vec::from_raw_parts(start, len, len); - let tmp = String::from_utf8_unchecked(part.clone()); - mem::forget(part); - tmp -} - fn parse_command_line + Borrow>(cmd: &[T]) -> Vec { let mut x = 0; let mut command = Vec::with_capacity(cmd.len()); diff --git a/src/unix/apple/network.rs b/src/unix/apple/network.rs index b55436501..78fcf50b7 100644 --- a/src/unix/apple/network.rs +++ b/src/unix/apple/network.rs @@ -44,7 +44,6 @@ impl NetworksInner { self.update_networks(false); } - #[allow(unknown_lints)] #[allow(clippy::cast_ptr_alignment)] #[allow(clippy::uninit_vec)] fn update_networks(&mut self, insert: bool) { diff --git a/src/unix/apple/system.rs b/src/unix/apple/system.rs index c89b4c642..cf4275c8d 100644 --- a/src/unix/apple/system.rs +++ b/src/unix/apple/system.rs @@ -13,9 +13,6 @@ use std::mem; #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] use std::time::SystemTime; -#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] -use libc::size_t; - use libc::{ c_int, c_void, host_statistics64, mach_port_t, sysconf, sysctl, timeval, vm_statistics64, _SC_PAGESIZE, @@ -99,15 +96,13 @@ impl SystemInner { } pub(crate) fn refresh_memory(&mut self) { - let mut mib = [0, 0]; + let mut mib = [libc::CTL_VM as _, libc::VM_SWAPUSAGE as _]; unsafe { // get system values // get swap info let mut xs: libc::xsw_usage = mem::zeroed::(); if get_sys_value( - libc::CTL_VM as _, - libc::VM_SWAPUSAGE as _, mem::size_of::(), &mut xs as *mut _ as *mut c_void, &mut mib, @@ -115,11 +110,11 @@ impl SystemInner { self.swap_total = xs.xsu_total; self.swap_free = xs.xsu_avail; } + mib[0] = libc::CTL_HW as _; + mib[1] = libc::HW_MEMSIZE as _; // get ram info if self.mem_total < 1 { get_sys_value( - libc::CTL_HW as _, - libc::HW_MEMSIZE as _, mem::size_of::(), &mut self.mem_total as *mut u64 as *mut c_void, &mut mib, @@ -182,7 +177,6 @@ impl SystemInner { } if let Some(pids) = get_proc_list() { let now = get_now(); - let arg_max = get_arg_max(); let port = self.port; let time_interval = self.clock_info.as_mut().map(|c| c.get_time_interval(port)); let entries: Vec = { @@ -193,15 +187,7 @@ impl SystemInner { into_iter(pids) .flat_map(|pid| { - match update_process( - wrap, - pid, - arg_max as size_t, - time_interval, - now, - refresh_kind, - false, - ) { + match update_process(wrap, pid, time_interval, now, refresh_kind, false) { Ok(x) => x, _ => None, } @@ -232,7 +218,6 @@ impl SystemInner { refresh_kind: ProcessRefreshKind, ) -> bool { let mut time_interval = None; - let arg_max = get_arg_max(); let now = get_now(); if refresh_kind.cpu() { @@ -241,15 +226,7 @@ impl SystemInner { } match { let wrap = Wrap(UnsafeCell::new(&mut self.process_list)); - update_process( - &wrap, - pid, - arg_max as size_t, - time_interval, - now, - refresh_kind, - true, - ) + update_process(&wrap, pid, time_interval, now, refresh_kind, true) } { Ok(Some(p)) => { self.process_list.insert(p.pid(), p); @@ -430,28 +407,6 @@ impl SystemInner { } } -#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] -fn get_arg_max() -> usize { - let mut mib = [libc::CTL_KERN, libc::KERN_ARGMAX]; - let mut arg_max = 0i32; - let mut size = mem::size_of::(); - unsafe { - if sysctl( - mib.as_mut_ptr(), - mib.len() as _, - (&mut arg_max) as *mut i32 as *mut c_void, - &mut size, - std::ptr::null_mut(), - 0, - ) == -1 - { - 4096 // We default to this value - } else { - arg_max as usize - } - } -} - fn get_system_info(value: c_int, default: Option<&str>) -> Option { let mut mib: [c_int; 2] = [libc::CTL_KERN, value]; let mut size = 0; diff --git a/src/unix/apple/utils.rs b/src/unix/apple/utils.rs index e87a01c09..c784c0d5f 100644 --- a/src/unix/apple/utils.rs +++ b/src/unix/apple/utils.rs @@ -1,7 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. use core_foundation_sys::base::CFRelease; -use libc::{c_char, c_void, sysctl, sysctlbyname}; +use libc::{c_void, sysctl, sysctlbyname}; use std::ptr::NonNull; // A helper using to auto release the resource got from CoreFoundation. @@ -44,20 +44,12 @@ pub(crate) fn vec_to_rust(buf: Vec) -> Option { .ok() } -pub(crate) unsafe fn get_sys_value( - high: u32, - low: u32, - mut len: usize, - value: *mut c_void, - mib: &mut [i32; 2], -) -> bool { - mib[0] = high as i32; - mib[1] = low as i32; +pub(crate) unsafe fn get_sys_value(mut len: usize, value: *mut c_void, mib: &mut [i32]) -> bool { sysctl( mib.as_mut_ptr(), mib.len() as _, value, - &mut len as *mut usize, + &mut len as *mut _, std::ptr::null_mut(), 0, ) == 0 @@ -69,7 +61,7 @@ pub(crate) unsafe fn get_sys_value_by_name( value: *mut c_void, ) -> bool { sysctlbyname( - name.as_ptr() as *const c_char, + name.as_ptr() as *const _, value, len, std::ptr::null_mut(), diff --git a/test_bin/main.rs b/test_bin/main.rs new file mode 100644 index 000000000..d15425064 --- /dev/null +++ b/test_bin/main.rs @@ -0,0 +1,5 @@ +// Does nothing and just exits after waiting for 30 seconds. + +fn main() { + std::thread::sleep(std::time::Duration::from_secs(30)); +} diff --git a/tests/process.rs b/tests/process.rs index 91078958c..47d95acde 100644 --- a/tests/process.rs +++ b/tests/process.rs @@ -95,98 +95,74 @@ fn test_cmd() { } } +fn build_test_binary() { + std::process::Command::new("rustc") + .arg("test_bin/main.rs") + .arg("-o") + .arg("target/test_binary") + .stdout(std::process::Stdio::null()) + .spawn() + .unwrap() + .wait() + .unwrap(); +} + #[test] fn test_environ() { if !sysinfo::IS_SUPPORTED || cfg!(feature = "apple-sandbox") { return; } - let mut p = if cfg!(target_os = "windows") { - std::process::Command::new("waitfor") - .arg("/t") - .arg("3") - .arg("EnvironSignal") - .stdout(std::process::Stdio::null()) - .env("FOO", "BAR") - .env("OTHER", "VALUE") - .spawn() - .unwrap() - } else { - std::process::Command::new("sleep") - .arg("3") - .stdout(std::process::Stdio::null()) - .env("FOO", "BAR") - .env("OTHER", "VALUE") - .spawn() - .unwrap() - }; + build_test_binary(); + let mut p = std::process::Command::new("./target/test_binary") + .env("FOO", "BAR") + .env("OTHER", "VALUE") + .spawn() + .unwrap(); - let pid = Pid::from_u32(p.id() as _); 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(); + + s.refresh_process_specifics(pid, sysinfo::ProcessRefreshKind::everything()); p.kill().expect("Unable to kill process."); let processes = s.processes(); - let p = processes.get(&pid); + let proc_ = processes.get(&pid); - if let Some(p) = p { - assert_eq!(p.pid(), pid); - // FIXME: instead of ignoring the test on CI, try to find out what's wrong... - if std::env::var("APPLE_CI").is_err() { - assert!(p.environ().iter().any(|e| e == "FOO=BAR")); - assert!(p.environ().iter().any(|e| e == "OTHER=VALUE")); - } + if let Some(proc_) = proc_ { + assert_eq!(proc_.pid(), pid); + assert!(proc_.environ().iter().any(|e| e == "FOO=BAR")); + assert!(proc_.environ().iter().any(|e| e == "OTHER=VALUE")); } else { panic!("Process not found!"); } -} -// Test to ensure that a process with a lot of environment variables doesn't get truncated. -// More information in . -#[test] -fn test_big_environ() { - if !sysinfo::IS_SUPPORTED || cfg!(feature = "apple-sandbox") { - return; - } + // Test to ensure that a process with a lot of environment variables doesn't get truncated. + // More information in . const SIZE: usize = 30_000; let mut big_env = String::with_capacity(SIZE); for _ in 0..SIZE { big_env.push('a'); } - let mut p = if cfg!(target_os = "windows") { - std::process::Command::new("waitfor") - .arg("/t") - .arg("3") - .arg("EnvironSignal") - .stdout(std::process::Stdio::null()) - .env("FOO", &big_env) - .spawn() - .unwrap() - } else { - std::process::Command::new("sleep") - .arg("3") - .stdout(std::process::Stdio::null()) - .env("FOO", &big_env) - .spawn() - .unwrap() - }; + let mut p = std::process::Command::new("./target/test_binary") + .env("FOO", &big_env) + .spawn() + .unwrap(); - let pid = Pid::from_u32(p.id() as _); 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(); - p.kill().expect("Unable to kill process."); let processes = s.processes(); - let p = processes.get(&pid); + let proc_ = processes.get(&pid); - if let Some(p) = p { - assert_eq!(p.pid(), pid); - // FIXME: instead of ignoring the test on CI, try to find out what's wrong... - if std::env::var("APPLE_CI").is_err() { - let env = format!("FOO={big_env}"); - assert!(p.environ().iter().any(|e| *e == env)); - } + if let Some(proc_) = proc_ { + p.kill().expect("Unable to kill process."); + assert_eq!(proc_.pid(), pid); + let env = format!("FOO={big_env}"); + assert!(proc_.environ().iter().any(|e| *e == env)); } else { panic!("Process not found!"); }