diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
commit | d1b2d29528b7794b41e66fc2136e395a02f8529b (patch) | |
tree | a4a17504b260206dec3cf55b2dca82929a348ac2 /vendor/sysinfo/src | |
parent | Releasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip |
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/sysinfo/src')
50 files changed, 2446 insertions, 848 deletions
diff --git a/vendor/sysinfo/src/apple/app_store/process.rs b/vendor/sysinfo/src/apple/app_store/process.rs index 7c43697a6..d7df72afb 100644 --- a/vendor/sysinfo/src/apple/app_store/process.rs +++ b/vendor/sysinfo/src/apple/app_store/process.rs @@ -76,9 +76,21 @@ impl ProcessExt for Process { None } + fn effective_user_id(&self) -> Option<&Uid> { + None + } + fn group_id(&self) -> Option<Gid> { None } + fn effective_group_id(&self) -> Option<Gid> { + None + } + fn wait(&self) {} + + fn session_id(&self) -> Option<Pid> { + None + } } diff --git a/vendor/sysinfo/src/apple/cpu.rs b/vendor/sysinfo/src/apple/cpu.rs index e613bdd2c..9eda48d16 100644 --- a/vendor/sysinfo/src/apple/cpu.rs +++ b/vendor/sysinfo/src/apple/cpu.rs @@ -137,11 +137,13 @@ pub(crate) fn get_cpu_frequency() -> u64 { } #[inline] -fn get_in_use(cpu_info: *mut i32, offset: isize) -> i32 { +fn get_in_use(cpu_info: *mut i32, offset: isize) -> i64 { unsafe { - *cpu_info.offset(offset + libc::CPU_STATE_USER as isize) - + *cpu_info.offset(offset + libc::CPU_STATE_SYSTEM as isize) - + *cpu_info.offset(offset + libc::CPU_STATE_NICE as isize) + let user = *cpu_info.offset(offset + libc::CPU_STATE_USER as isize) as i64; + let system = *cpu_info.offset(offset + libc::CPU_STATE_SYSTEM as isize) as i64; + let nice = *cpu_info.offset(offset + libc::CPU_STATE_NICE as isize) as i64; + + user.saturating_add(system).saturating_add(nice) } } @@ -158,10 +160,12 @@ pub(crate) fn compute_usage_of_cpu(proc_: &Cpu, cpu_info: *mut i32, offset: isiz // In case we are initializing cpus, there is no "old value" yet. if old_cpu_info == cpu_info { in_use = get_in_use(cpu_info, offset); - total = in_use + get_idle(cpu_info, offset); + total = in_use.saturating_add(get_idle(cpu_info, offset) as _); } else { - in_use = get_in_use(cpu_info, offset) - get_in_use(old_cpu_info, offset); - total = in_use + (get_idle(cpu_info, offset) - get_idle(old_cpu_info, offset)); + in_use = get_in_use(cpu_info, offset).saturating_sub(get_in_use(old_cpu_info, offset)); + total = in_use.saturating_add( + get_idle(cpu_info, offset).saturating_sub(get_idle(old_cpu_info, offset)) as _, + ); } in_use as f32 / total as f32 * 100. } @@ -207,7 +211,7 @@ pub(crate) fn init_cpus( let frequency = if refresh_kind.frequency() { get_cpu_frequency() } else { - 0 + global_cpu.frequency }; unsafe { diff --git a/vendor/sysinfo/src/apple/disk.rs b/vendor/sysinfo/src/apple/disk.rs index d866d5bba..0ea492dd7 100644 --- a/vendor/sysinfo/src/apple/disk.rs +++ b/vendor/sysinfo/src/apple/disk.rs @@ -4,7 +4,7 @@ use crate::sys::{ ffi, utils::{self, CFReleaser}, }; -use crate::{DiskExt, DiskType}; +use crate::{DiskExt, DiskKind}; use core_foundation_sys::array::CFArrayCreate; use core_foundation_sys::base::kCFAllocatorDefault; @@ -21,7 +21,7 @@ use std::ptr; #[doc = include_str!("../../md_doc/disk.md")] pub struct Disk { - pub(crate) type_: DiskType, + pub(crate) type_: DiskKind, pub(crate) name: OsString, pub(crate) file_system: Vec<u8>, pub(crate) mount_point: PathBuf, @@ -32,7 +32,7 @@ pub struct Disk { } impl DiskExt for Disk { - fn type_(&self) -> DiskType { + fn kind(&self) -> DiskKind { self.type_ } @@ -328,9 +328,9 @@ unsafe fn new_disk( // so we just assume the disk type is an SSD until Rust has a way to conditionally link to // IOKit in more recent deployment versions. #[cfg(target_os = "macos")] - let type_ = crate::sys::inner::disk::get_disk_type(&c_disk).unwrap_or(DiskType::Unknown(-1)); + let type_ = crate::sys::inner::disk::get_disk_type(&c_disk).unwrap_or(DiskKind::Unknown(-1)); #[cfg(not(target_os = "macos"))] - let type_ = DiskType::SSD; + let type_ = DiskKind::SSD; // Note: Since we requested these properties from the system, we don't expect // these property retrievals to fail. diff --git a/vendor/sysinfo/src/apple/macos/disk.rs b/vendor/sysinfo/src/apple/macos/disk.rs index 3a4372a2f..91631a263 100644 --- a/vendor/sysinfo/src/apple/macos/disk.rs +++ b/vendor/sysinfo/src/apple/macos/disk.rs @@ -6,14 +6,14 @@ use crate::sys::{ macos::utils::IOReleaser, utils::CFReleaser, }; -use crate::DiskType; +use crate::DiskKind; use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull}; use core_foundation_sys::string as cfs; use std::ffi::CStr; -pub(crate) fn get_disk_type(disk: &libc::statfs) -> Option<DiskType> { +pub(crate) fn get_disk_type(disk: &libc::statfs) -> Option<DiskKind> { let characteristics_string = unsafe { CFReleaser::new(cfs::CFStringCreateWithBytesNoCopy( kCFAllocatorDefault, @@ -106,8 +106,8 @@ pub(crate) fn get_disk_type(disk: &libc::statfs) -> Option<DiskType> { }; if let Some(disk_type) = disk_type.and_then(|medium| match medium.as_str() { - _ if medium == ffi::kIOPropertyMediumTypeSolidStateKey => Some(DiskType::SSD), - _ if medium == ffi::kIOPropertyMediumTypeRotationalKey => Some(DiskType::HDD), + _ if medium == ffi::kIOPropertyMediumTypeSolidStateKey => Some(DiskKind::SSD), + _ if medium == ffi::kIOPropertyMediumTypeRotationalKey => Some(DiskKind::HDD), _ => None, }) { return Some(disk_type); @@ -116,7 +116,7 @@ pub(crate) fn get_disk_type(disk: &libc::statfs) -> Option<DiskType> { // // In these cases, assuming that there were _any_ properties about them registered, we fallback // to `HDD` when no storage medium is provided by the device instead of `Unknown`. - return Some(DiskType::HDD); + return Some(DiskKind::HDD); } } } diff --git a/vendor/sysinfo/src/apple/macos/ffi.rs b/vendor/sysinfo/src/apple/macos/ffi.rs index 0b9c82cfa..577ecd38e 100644 --- a/vendor/sysinfo/src/apple/macos/ffi.rs +++ b/vendor/sysinfo/src/apple/macos/ffi.rs @@ -31,6 +31,11 @@ pub const kIOPropertyMediumTypeSolidStateKey: &str = "Solid State"; #[allow(non_upper_case_globals)] pub const kIOPropertyMediumTypeRotationalKey: &str = "Rotational"; +// Based on https://github.com/libusb/libusb/blob/bed8d3034eac74a6e1ba123b5c270ea63cb6cf1a/libusb/os/darwin_usb.c#L54-L55, +// we can simply set it to 0 (and is the same value as its replacement `kIOMainPortDefault`). +#[allow(non_upper_case_globals)] +pub const kIOMasterPortDefault: mach_port_t = 0; + // Note: Obtaining information about disks using IOKIt is allowed inside the default macOS App Sandbox. #[link(name = "IOKit", kind = "framework")] extern "C" { @@ -61,9 +66,6 @@ extern "C" { options: u32, bsdName: *const c_char, ) -> CFMutableDictionaryRef; - - // This is deprecated as of macOS 12.0, but Rust doesn't have a good way to only use the replacement on 12+. - pub static kIOMasterPortDefault: mach_port_t; } #[cfg(all( diff --git a/vendor/sysinfo/src/apple/macos/process.rs b/vendor/sysinfo/src/apple/macos/process.rs index fff9c1f71..116138650 100644 --- a/vendor/sysinfo/src/apple/macos/process.rs +++ b/vendor/sysinfo/src/apple/macos/process.rs @@ -33,13 +33,15 @@ pub struct Process { pub(crate) updated: bool, cpu_usage: f32, user_id: Option<Uid>, + effective_user_id: Option<Uid>, group_id: Option<Gid>, + effective_group_id: Option<Gid>, pub(crate) process_status: ProcessStatus, /// Status of process (running, stopped, waiting, etc). `None` means `sysinfo` doesn't have /// enough rights to get this information. /// /// This is very likely this one that you want instead of `process_status`. - pub status: Option<ThreadStatus>, + pub(crate) status: Option<ThreadStatus>, pub(crate) old_read_bytes: u64, pub(crate) old_written_bytes: u64, pub(crate) read_bytes: u64, @@ -66,7 +68,9 @@ impl Process { start_time: 0, run_time: 0, user_id: None, + effective_user_id: None, group_id: None, + effective_group_id: None, process_status: ProcessStatus::Unknown(0), status: None, old_read_bytes: 0, @@ -95,7 +99,9 @@ impl Process { start_time, run_time, user_id: None, + effective_user_id: None, group_id: None, + effective_group_id: None, process_status: ProcessStatus::Unknown(0), status: None, old_read_bytes: 0, @@ -153,6 +159,13 @@ impl ProcessExt for Process { } fn status(&self) -> ProcessStatus { + // If the status is `Run`, then it's very likely wrong so we instead + // return a `ProcessStatus` converted from the `ThreadStatus`. + if self.process_status == ProcessStatus::Run { + if let Some(thread_status) = self.status { + return ProcessStatus::from(thread_status); + } + } self.process_status } @@ -181,15 +194,23 @@ impl ProcessExt for Process { self.user_id.as_ref() } + fn effective_user_id(&self) -> Option<&Uid> { + self.effective_user_id.as_ref() + } + fn group_id(&self) -> Option<Gid> { self.group_id } + fn effective_group_id(&self) -> Option<Gid> { + self.effective_group_id + } + fn wait(&self) { let mut status = 0; // attempt waiting unsafe { - if libc::waitpid(self.pid.0, &mut status, 0) < 0 { + if retry_eintr!(libc::waitpid(self.pid.0, &mut status, 0)) < 0 { // attempt failed (non-child process) so loop until process ends let duration = std::time::Duration::from_millis(10); while kill(self.pid.0, 0) == 0 { @@ -198,6 +219,17 @@ impl ProcessExt for Process { } } } + + fn session_id(&self) -> Option<Pid> { + unsafe { + let session_id = libc::getsid(self.pid.0); + if session_id < 0 { + None + } else { + Some(Pid(session_id)) + } + } + } } #[allow(deprecated)] // Because of libc::mach_absolute_time. @@ -210,6 +242,7 @@ pub(crate) fn compute_cpu_usage( ) { if let Some(time_interval) = time_interval { let total_existing_time = p.old_stime.saturating_add(p.old_utime); + let mut updated_cpu_usage = false; if time_interval > 0.000001 && total_existing_time > 0 { let total_current_time = task_info .pti_total_system @@ -218,8 +251,10 @@ pub(crate) fn compute_cpu_usage( let total_time_diff = total_current_time.saturating_sub(total_existing_time); if total_time_diff > 0 { p.cpu_usage = (total_time_diff as f64 / time_interval * 100.) as f32; + updated_cpu_usage = true; } - } else { + } + if !updated_cpu_usage { p.cpu_usage = 0.; } p.old_stime = task_info.pti_total_system; @@ -254,14 +289,6 @@ pub(crate) fn compute_cpu_usage( } } -/*pub fn set_time(p: &mut Process, utime: u64, stime: u64) { - p.old_utime = p.utime; - p.old_stime = p.stime; - p.utime = utime; - p.stime = stime; - p.updated = true; -}*/ - unsafe fn get_task_info(pid: Pid) -> libc::proc_taskinfo { let mut task_info = mem::zeroed::<libc::proc_taskinfo>(); // If it doesn't work, we just don't have memory information for this process @@ -289,7 +316,7 @@ fn check_if_pid_is_alive(pid: Pid, check_if_alive: bool) -> bool { return true; } // `kill` failed but it might not be because the process is dead. - let errno = libc::__error(); + let errno = crate::libc_errno(); // If errno is equal to ESCHR, it means the process is dead. !errno.is_null() && *errno != libc::ESRCH } @@ -520,8 +547,10 @@ unsafe fn create_new_process( p.memory = task_info.pti_resident_size; p.virtual_memory = task_info.pti_virtual_size; - p.user_id = Some(Uid(info.pbi_uid)); - p.group_id = Some(Gid(info.pbi_gid)); + p.user_id = Some(Uid(info.pbi_ruid)); + p.effective_user_id = Some(Uid(info.pbi_uid)); + p.group_id = Some(Gid(info.pbi_rgid)); + p.effective_group_id = Some(Gid(info.pbi_gid)); p.process_status = ProcessStatus::from(info.pbi_status); if refresh_kind.disk_usage() { update_proc_disk_activity(&mut p); diff --git a/vendor/sysinfo/src/apple/macos/system.rs b/vendor/sysinfo/src/apple/macos/system.rs index 949532234..7c8b957a5 100644 --- a/vendor/sysinfo/src/apple/macos/system.rs +++ b/vendor/sysinfo/src/apple/macos/system.rs @@ -1,5 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. +use crate::SystemExt; + #[allow(deprecated)] use libc::{mach_timebase_info, mach_timebase_info_data_t}; @@ -9,18 +11,52 @@ use libc::{ }; use std::ptr::null_mut; -unsafe fn free_cpu_load_info(cpu_load: &mut processor_cpu_load_info_t) { - if !cpu_load.is_null() { - munmap(*cpu_load as _, vm_page_size); - *cpu_load = null_mut(); +struct ProcessorCpuLoadInfo { + cpu_load: processor_cpu_load_info_t, + cpu_count: natural_t, +} + +impl ProcessorCpuLoadInfo { + fn new(port: mach_port_t) -> Option<Self> { + let mut info_size = std::mem::size_of::<processor_cpu_load_info_t>() as _; + let mut cpu_count = 0; + let mut cpu_load: processor_cpu_load_info_t = null_mut(); + + unsafe { + if host_processor_info( + port, + PROCESSOR_CPU_LOAD_INFO, + &mut cpu_count, + &mut cpu_load as *mut _ as *mut _, + &mut info_size, + ) != 0 + { + sysinfo_debug!("host_processor_info failed, not updating CPU ticks usage..."); + None + } else if cpu_count < 1 || cpu_load.is_null() { + None + } else { + Some(Self { + cpu_load, + cpu_count, + }) + } + } + } +} + +impl Drop for ProcessorCpuLoadInfo { + fn drop(&mut self) { + unsafe { + munmap(self.cpu_load as _, vm_page_size); + } } } pub(crate) struct SystemTimeInfo { timebase_to_ns: f64, clock_per_sec: f64, - old_cpu_load: processor_cpu_load_info_t, - old_cpu_count: natural_t, + old_cpu_info: ProcessorCpuLoadInfo, } unsafe impl Send for SystemTimeInfo {} @@ -49,9 +85,8 @@ impl SystemTimeInfo { info.denom = 1; } - let mut old_cpu_load = null_mut(); - let old_cpu_count = match Self::update_ticks(port, &mut old_cpu_load) { - Some(c) => c, + let old_cpu_info = match ProcessorCpuLoadInfo::new(port) { + Some(cpu_info) => cpu_info, None => { sysinfo_debug!("host_processor_info failed, using old CPU tick measure system"); return None; @@ -63,53 +98,23 @@ impl SystemTimeInfo { Some(Self { timebase_to_ns: info.numer as f64 / info.denom as f64, clock_per_sec: nano_per_seconds / clock_ticks_per_sec as f64, - old_cpu_load, - old_cpu_count, + old_cpu_info, }) } } - fn update_ticks( - port: mach_port_t, - cpu_load: &mut processor_cpu_load_info_t, - ) -> Option<natural_t> { - let mut info_size = std::mem::size_of::<processor_cpu_load_info_t>() as _; - let mut cpu_count = 0; - - unsafe { - free_cpu_load_info(cpu_load); - - if host_processor_info( - port, - PROCESSOR_CPU_LOAD_INFO, - &mut cpu_count, - cpu_load as *mut _ as *mut _, - &mut info_size, - ) != 0 - { - sysinfo_debug!("host_processor_info failed, not updating CPU ticks usage..."); - None - } else if cpu_count < 1 || cpu_load.is_null() { - None - } else { - Some(cpu_count) - } - } - } - pub fn get_time_interval(&mut self, port: mach_port_t) -> f64 { let mut total = 0; - let mut new_cpu_load = null_mut(); - - let new_cpu_count = match Self::update_ticks(port, &mut new_cpu_load) { - Some(c) => c, + let new_cpu_info = match ProcessorCpuLoadInfo::new(port) { + Some(cpu_info) => cpu_info, None => return 0., }; - let cpu_count = std::cmp::min(self.old_cpu_count, new_cpu_count); + let cpu_count = std::cmp::min(self.old_cpu_info.cpu_count, new_cpu_info.cpu_count); unsafe { for i in 0..cpu_count { - let new_load: &processor_cpu_load_info = &*new_cpu_load.offset(i as _); - let old_load: &processor_cpu_load_info = &*self.old_cpu_load.offset(i as _); + let new_load: &processor_cpu_load_info = &*new_cpu_info.cpu_load.offset(i as _); + let old_load: &processor_cpu_load_info = + &*self.old_cpu_info.cpu_load.offset(i as _); for (new, old) in new_load.cpu_ticks.iter().zip(old_load.cpu_ticks.iter()) { if new > old { total += new - old; @@ -117,20 +122,44 @@ impl SystemTimeInfo { } } - free_cpu_load_info(&mut self.old_cpu_load); - self.old_cpu_load = new_cpu_load; - self.old_cpu_count = new_cpu_count; + self.old_cpu_info = new_cpu_info; - // Now we convert the ticks to nanoseconds: - total as f64 / self.timebase_to_ns * self.clock_per_sec / cpu_count as f64 + // Now we convert the ticks to nanoseconds (if the interval is less than + // `MINIMUM_CPU_UPDATE_INTERVAL`, we replace it with it instead): + let base_interval = total as f64 / cpu_count as f64 * self.clock_per_sec; + let smallest = + crate::System::MINIMUM_CPU_UPDATE_INTERVAL.as_secs_f64() * 1_000_000_000.0; + if base_interval < smallest { + smallest + } else { + base_interval / self.timebase_to_ns + } } } } -impl Drop for SystemTimeInfo { - fn drop(&mut self) { - unsafe { - free_cpu_load_info(&mut self.old_cpu_load); +#[cfg(test)] +mod test { + + use super::*; + + /// Regression test for <https://github.com/GuillaumeGomez/sysinfo/issues/956>. + #[test] + fn test_getting_time_interval() { + if !crate::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") { + return; } + + let port = unsafe { libc::mach_host_self() }; + let mut info = SystemTimeInfo::new(port).unwrap(); + info.get_time_interval(port); + + std::thread::sleep(crate::System::MINIMUM_CPU_UPDATE_INTERVAL.saturating_mul(5)); + + let val = info.get_time_interval(port); + assert_ne!( + val, + crate::System::MINIMUM_CPU_UPDATE_INTERVAL.as_secs_f64() * 1_000_000_000.0 + ); } } diff --git a/vendor/sysinfo/src/apple/network.rs b/vendor/sysinfo/src/apple/network.rs index 3c4918155..26fe16379 100644 --- a/vendor/sysinfo/src/apple/network.rs +++ b/vendor/sysinfo/src/apple/network.rs @@ -5,6 +5,8 @@ use libc::{self, c_char, if_msghdr2, CTL_NET, NET_RT_IFLIST2, PF_ROUTE, RTM_IFIN use std::collections::{hash_map, HashMap}; use std::ptr::null_mut; +use crate::common::MacAddr; +use crate::network::refresh_networks_addresses; use crate::{NetworkExt, NetworksExt, NetworksIter}; macro_rules! old_and_new { @@ -82,7 +84,7 @@ impl Networks { let name = String::from_utf8_unchecked(name); match self.interfaces.entry(name) { hash_map::Entry::Occupied(mut e) => { - let mut interface = e.get_mut(); + let interface = e.get_mut(); old_and_new!( interface, current_out, @@ -143,6 +145,7 @@ impl Networks { errors_out, old_errors_out: errors_out, updated: true, + mac_addr: MacAddr::UNSPECIFIED, }); } } @@ -164,6 +167,7 @@ impl NetworksExt for Networks { } self.update_networks(); self.interfaces.retain(|_, data| data.updated); + refresh_networks_addresses(&mut self.interfaces); } fn refresh(&mut self) { @@ -187,6 +191,8 @@ pub struct NetworkData { errors_out: u64, old_errors_out: u64, updated: bool, + /// MAC address + pub(crate) mac_addr: MacAddr, } impl NetworkExt for NetworkData { @@ -237,4 +243,8 @@ impl NetworkExt for NetworkData { fn total_errors_on_transmitted(&self) -> u64 { self.errors_out } + + fn mac_address(&self) -> MacAddr { + self.mac_addr + } } diff --git a/vendor/sysinfo/src/apple/process.rs b/vendor/sysinfo/src/apple/process.rs index e0f005bdc..ba2912bd3 100644 --- a/vendor/sysinfo/src/apple/process.rs +++ b/vendor/sysinfo/src/apple/process.rs @@ -9,16 +9,30 @@ use crate::ProcessStatus; impl From<u32> for ProcessStatus { fn from(status: u32) -> ProcessStatus { match status { - 1 => ProcessStatus::Idle, - 2 => ProcessStatus::Run, - 3 => ProcessStatus::Sleep, - 4 => ProcessStatus::Stop, - 5 => ProcessStatus::Zombie, + libc::SIDL => ProcessStatus::Idle, + libc::SRUN => ProcessStatus::Run, + libc::SSLEEP => ProcessStatus::Sleep, + libc::SSTOP => ProcessStatus::Stop, + libc::SZOMB => ProcessStatus::Zombie, x => ProcessStatus::Unknown(x), } } } +#[doc(hidden)] +impl From<ThreadStatus> for ProcessStatus { + fn from(status: ThreadStatus) -> ProcessStatus { + match status { + ThreadStatus::Running => ProcessStatus::Run, + ThreadStatus::Stopped => ProcessStatus::Stop, + ThreadStatus::Waiting => ProcessStatus::Sleep, + ThreadStatus::Uninterruptible => ProcessStatus::Dead, + ThreadStatus::Halted => ProcessStatus::Parked, + ThreadStatus::Unknown(x) => ProcessStatus::Unknown(x as _), + } + } +} + impl fmt::Display for ProcessStatus { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(match *self { @@ -33,8 +47,8 @@ impl fmt::Display for ProcessStatus { } /// Enum describing the different status of a thread. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum ThreadStatus { +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum ThreadStatus { /// Thread is running normally. Running, /// Thread is stopped. @@ -52,32 +66,12 @@ pub enum ThreadStatus { impl From<i32> for ThreadStatus { fn from(status: i32) -> ThreadStatus { match status { - 1 => ThreadStatus::Running, - 2 => ThreadStatus::Stopped, - 3 => ThreadStatus::Waiting, - 4 => ThreadStatus::Uninterruptible, - 5 => ThreadStatus::Halted, + libc::TH_STATE_RUNNING => ThreadStatus::Running, + libc::TH_STATE_STOPPED => ThreadStatus::Stopped, + libc::TH_STATE_WAITING => ThreadStatus::Waiting, + libc::TH_STATE_UNINTERRUPTIBLE => ThreadStatus::Uninterruptible, + libc::TH_STATE_HALTED => ThreadStatus::Halted, x => ThreadStatus::Unknown(x), } } } - -impl ThreadStatus { - /// Used to display `ThreadStatus`. - pub fn to_string(&self) -> &str { - match *self { - ThreadStatus::Running => "Running", - ThreadStatus::Stopped => "Stopped", - ThreadStatus::Waiting => "Waiting", - ThreadStatus::Uninterruptible => "Uninterruptible", - ThreadStatus::Halted => "Halted", - ThreadStatus::Unknown(_) => "Unknown", - } - } -} - -impl fmt::Display for ThreadStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_string()) - } -} diff --git a/vendor/sysinfo/src/apple/system.rs b/vendor/sysinfo/src/apple/system.rs index 12abbd23b..0691a23d2 100644 --- a/vendor/sysinfo/src/apple/system.rs +++ b/vendor/sysinfo/src/apple/system.rs @@ -17,6 +17,7 @@ use std::cell::UnsafeCell; use std::collections::HashMap; use std::mem; use std::sync::Arc; +use std::time::Duration; #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] use std::time::SystemTime; @@ -80,6 +81,7 @@ pub struct System { process_list: HashMap<Pid, Process>, mem_total: u64, mem_free: u64, + mem_used: u64, mem_available: u64, swap_total: u64, swap_free: u64, @@ -139,6 +141,7 @@ fn get_now() -> u64 { impl SystemExt for System { const IS_SUPPORTED: bool = true; const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals(); + const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(200); fn new_with_specifics(refreshes: RefreshKind) -> System { unsafe { @@ -149,6 +152,7 @@ impl SystemExt for System { mem_total: 0, mem_free: 0, mem_available: 0, + mem_used: 0, swap_total: 0, swap_free: 0, global_cpu: Cpu::new( @@ -220,15 +224,19 @@ impl SystemExt for System { // * used to hold data that was read speculatively from disk but // * haven't actually been used by anyone so far. // */ - self.mem_available = self.mem_total.saturating_sub( - u64::from(stat.active_count) - .saturating_add(u64::from(stat.inactive_count)) - .saturating_add(u64::from(stat.wire_count)) - .saturating_add(u64::from(stat.speculative_count)) - .saturating_sub(u64::from(stat.purgeable_count)) - .saturating_mul(self.page_size_kb), - ); - self.mem_free = u64::from(stat.free_count).saturating_mul(self.page_size_kb); + self.mem_available = u64::from(stat.free_count) + .saturating_add(u64::from(stat.inactive_count)) + .saturating_add(u64::from(stat.purgeable_count)) + .saturating_sub(u64::from(stat.compressor_page_count)) + .saturating_mul(self.page_size_kb); + self.mem_used = u64::from(stat.active_count) + .saturating_add(u64::from(stat.wire_count)) + .saturating_add(u64::from(stat.compressor_page_count)) + .saturating_add(u64::from(stat.speculative_count)) + .saturating_mul(self.page_size_kb); + self.mem_free = u64::from(stat.free_count) + .saturating_sub(u64::from(stat.speculative_count)) + .saturating_mul(self.page_size_kb); } } } @@ -327,10 +335,14 @@ impl SystemExt for System { #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool { - let now = get_now(); + let mut time_interval = None; 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 now = get_now(); + + if refresh_kind.cpu() { + let port = self.port; + time_interval = self.clock_info.as_mut().map(|c| c.get_time_interval(port)); + } match { let wrap = Wrap(UnsafeCell::new(&mut self.process_list)); update_process( @@ -417,7 +429,7 @@ impl SystemExt for System { } fn used_memory(&self) -> u64 { - self.mem_total - self.mem_free + self.mem_used } fn total_swap(&self) -> u64 { diff --git a/vendor/sysinfo/src/apple/users.rs b/vendor/sysinfo/src/apple/users.rs index 690aceee1..e57c7a141 100644 --- a/vendor/sysinfo/src/apple/users.rs +++ b/vendor/sysinfo/src/apple/users.rs @@ -6,33 +6,8 @@ use crate::{ }; use crate::sys::utils; -use libc::{c_char, endpwent, getgrgid, getgrouplist, getpwent, gid_t, setpwent, strlen}; - -fn get_user_groups(name: *const c_char, group_id: gid_t) -> Vec<String> { - let mut add = 0; - - loop { - let mut nb_groups = 256 + add; - let mut groups = Vec::with_capacity(nb_groups as _); - unsafe { - if getgrouplist(name, group_id as _, groups.as_mut_ptr(), &mut nb_groups) == -1 { - add += 100; - continue; - } - groups.set_len(nb_groups as _); - return groups - .into_iter() - .filter_map(|g| { - let group = getgrgid(g as _); - if group.is_null() { - return None; - } - utils::cstr_to_rust((*group).gr_name) - }) - .collect(); - } - } -} +use libc::{c_char, endpwent, getpwent, setpwent, strlen}; +use std::collections::HashMap; fn endswith(s1: *const c_char, s2: &[u8]) -> bool { if s1.is_null() { @@ -53,13 +28,19 @@ fn users_list<F>(filter: F) -> Vec<User> where F: Fn(*const c_char, u32) -> bool, { - let mut users = Vec::new(); + let mut users = HashMap::with_capacity(10); + let mut buffer = Vec::with_capacity(2048); + let mut groups = Vec::with_capacity(256); unsafe { setpwent(); loop { let pw = getpwent(); if pw.is_null() { + // The call was interrupted by a signal, retrying. + if std::io::Error::last_os_error().kind() == std::io::ErrorKind::Interrupted { + continue; + } break; } @@ -67,24 +48,33 @@ where // This is not a "real" or "local" user. continue; } - - let groups = get_user_groups((*pw).pw_name, (*pw).pw_gid); - let uid = (*pw).pw_uid; - let gid = (*pw).pw_gid; if let Some(name) = utils::cstr_to_rust((*pw).pw_name) { - users.push(User { - uid: Uid(uid), - gid: Gid(gid), - name, - groups, - }); + if users.contains_key(&name) { + continue; + } + + let groups = crate::users::get_user_groups( + (*pw).pw_name, + (*pw).pw_gid, + &mut groups, + &mut buffer, + ); + let uid = (*pw).pw_uid; + let gid = (*pw).pw_gid; + users.insert(name, (Uid(uid), Gid(gid), groups)); } } endpwent(); } - users.sort_unstable_by(|x, y| x.name.partial_cmp(&y.name).unwrap()); - users.dedup_by(|a, b| a.name == b.name); users + .into_iter() + .map(|(name, (uid, gid, groups))| User { + uid, + gid, + name, + groups, + }) + .collect() } pub(crate) fn get_users_list() -> Vec<User> { diff --git a/vendor/sysinfo/src/c_interface.rs b/vendor/sysinfo/src/c_interface.rs index 0914a8135..f39e5bedd 100644 --- a/vendor/sysinfo/src/c_interface.rs +++ b/vendor/sysinfo/src/c_interface.rs @@ -26,7 +26,7 @@ pub extern "C" fn sysinfo_init() -> CSystem { pub extern "C" fn sysinfo_destroy(system: CSystem) { assert!(!system.is_null()); unsafe { - Box::from_raw(system as *mut System); + drop(Box::from_raw(system as *mut System)); } } @@ -259,8 +259,8 @@ pub extern "C" fn sysinfo_networks_transmitted(system: CSystem) -> size_t { /// Equivalent of [`System::cpus_usage()`][crate::System#method.cpus_usage]. /// -/// * `length` will contain the number of cpu usage added into `procs`. -/// * `procs` will be allocated if it's null and will contain of cpu usage. +/// * `length` will contain the number of CPU usage added into `procs`. +/// * `procs` will be allocated if it's null and will contain of CPU usage. #[no_mangle] pub extern "C" fn sysinfo_cpus_usage( system: CSystem, @@ -291,7 +291,7 @@ pub extern "C" fn sysinfo_cpus_usage( /// Equivalent of [`System::processes()`][crate::System#method.processes]. Returns an /// array ended by a null pointer. Must be freed. /// -/// # /!\ WARNING /!\ +/// # ⚠️ WARNING ⚠️ /// /// While having this method returned processes, you should *never* call any refresh method! #[no_mangle] @@ -323,7 +323,7 @@ pub extern "C" fn sysinfo_processes( /// Equivalent of [`System::process()`][crate::System#method.process]. /// -/// # /!\ WARNING /!\ +/// # ⚠️ WARNING ⚠️ /// /// While having this method returned process, you should *never* call any /// refresh method! @@ -344,7 +344,7 @@ pub extern "C" fn sysinfo_process_by_pid(system: CSystem, pid: pid_t) -> CProces /// Equivalent of iterating over [`Process::tasks()`][crate::Process#method.tasks]. /// -/// # /!\ WARNING /!\ +/// # ⚠️ WARNING ⚠️ /// /// While having this method processes, you should *never* call any refresh method! #[cfg(target_os = "linux")] diff --git a/vendor/sysinfo/src/common.rs b/vendor/sysinfo/src/common.rs index 2710204f5..77de714f6 100644 --- a/vendor/sysinfo/src/common.rs +++ b/vendor/sysinfo/src/common.rs @@ -2,7 +2,7 @@ use crate::{NetworkData, Networks, NetworksExt, UserExt}; -use std::convert::From; +use std::convert::{From, TryFrom}; use std::fmt; use std::str::FromStr; @@ -14,7 +14,7 @@ use std::str::FromStr; /// let p = Pid::from_u32(0); /// let value: u32 = p.as_u32(); /// ``` -pub trait PidExt<T>: Copy + From<T> + FromStr + fmt::Display { +pub trait PidExt: Copy + From<usize> + FromStr + fmt::Display { /// Allows to convert [`Pid`][crate::Pid] into [`u32`]. /// /// ``` @@ -41,19 +41,19 @@ macro_rules! pid_decl { #[repr(transparent)] pub struct Pid(pub(crate) $typ); - impl From<$typ> for Pid { - fn from(v: $typ) -> Self { - Self(v) + impl From<usize> for Pid { + fn from(v: usize) -> Self { + Self(v as _) } } - impl From<Pid> for $typ { + impl From<Pid> for usize { fn from(v: Pid) -> Self { - v.0 + v.0 as _ } } - impl PidExt<$typ> for Pid { + impl PidExt for Pid { fn as_u32(self) -> u32 { - self.0 as u32 + self.0 as _ } fn from_u32(v: u32) -> Self { Self(v as _) @@ -497,20 +497,20 @@ impl<'a> IntoIterator for &'a Networks { } } -/// Enum containing the different supported disks types. +/// Enum containing the different supported kinds of disks. /// -/// This type is returned by [`Disk::get_type`][crate::Disk#method.type]. +/// This type is returned by [`DiskExt::kind`](`crate::DiskExt::kind`). /// /// ```no_run /// use sysinfo::{System, SystemExt, DiskExt}; /// /// let system = System::new_all(); /// for disk in system.disks() { -/// println!("{:?}: {:?}", disk.name(), disk.type_()); +/// println!("{:?}: {:?}", disk.name(), disk.kind()); /// } /// ``` #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum DiskType { +pub enum DiskKind { /// HDD type. HDD, /// SSD type. @@ -664,7 +664,7 @@ pub struct LoadAvg { } macro_rules! xid { - ($(#[$outer:meta])+ $name:ident, $type:ty) => { + ($(#[$outer:meta])+ $name:ident, $type:ty $(, $trait:ty)?) => { $(#[$outer])+ #[repr(transparent)] #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] @@ -677,18 +677,47 @@ macro_rules! xid { &self.0 } } + + $( + impl TryFrom<usize> for $name { + type Error = <$type as TryFrom<usize>>::Error; + + fn try_from(t: usize) -> Result<Self, <$type as TryFrom<usize>>::Error> { + Ok(Self(<$type>::try_from(t)?)) + } + } + + impl $trait for $name { + type Err = <$type as FromStr>::Err; + + fn from_str(t: &str) -> Result<Self, <$type as FromStr>::Err> { + Ok(Self(<$type>::from_str(t)?)) + } + } + )? }; } macro_rules! uid { - ($(#[$outer:meta])+ $type:ty) => { - xid!($(#[$outer])+ Uid, $type); + ($type:ty$(, $trait:ty)?) => { + xid!( + /// A user id wrapping a platform specific type. + Uid, + $type + $(, $trait)? + ); }; } macro_rules! gid { - ($(#[$outer:meta])+ $type:ty) => { - xid!($(#[$outer])+ #[derive(Copy)] Gid, $type); + ($type:ty) => { + xid!( + /// A group id wrapping a platform specific type. + #[derive(Copy)] + Gid, + $type, + FromStr + ); }; } @@ -703,33 +732,22 @@ cfg_if::cfg_if! { target_os = "ios", ) ))] { - uid!( - /// A user id wrapping a platform specific type. - libc::uid_t - ); - gid!( - /// A group id wrapping a platform specific type. - libc::gid_t - ); + uid!(libc::uid_t, FromStr); + gid!(libc::gid_t); } else if #[cfg(windows)] { - uid!( - /// A user id wrapping a platform specific type. - Box<str> - ); - gid!( - /// A group id wrapping a platform specific type. - u32 - ); + uid!(crate::windows::Sid); + gid!(u32); + // Manual implementation outside of the macro... + impl FromStr for Uid { + type Err = <crate::windows::Sid as FromStr>::Err; + + fn from_str(t: &str) -> Result<Self, Self::Err> { + Ok(Self(t.parse()?)) + } + } } else { - uid!( - /// A user id wrapping a platform specific type. - u32 - ); - gid!( - /// A group id wrapping a platform specific type. - u32 - ); - + uid!(u32, FromStr); + gid!(u32); } } @@ -806,11 +824,11 @@ pub struct DiskUsage { /// Enum describing the different status of a process. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ProcessStatus { - /// ## Linux/FreeBSD + /// ## Linux /// - /// Waiting in uninterruptible disk sleep. + /// Idle kernel thread. /// - /// ## macOs + /// ## macOs/FreeBSD /// /// Process being created by fork. /// @@ -820,11 +838,11 @@ pub enum ProcessStatus { Idle, /// Running. Run, - /// ## Linux/FreeBSD + /// ## Linux /// /// Sleeping in an interruptible waiting. /// - /// ## macOS + /// ## macOS/FreeBSD /// /// Sleeping on an address. /// @@ -832,11 +850,11 @@ pub enum ProcessStatus { /// /// Not available. Sleep, - /// ## Linux/FreeBSD + /// ## Linux /// /// Stopped (on a signal) or (before Linux 2.6.33) trace stopped. /// - /// ## macOS + /// ## macOS/FreeBSD /// /// Process debugging or suspension. /// @@ -860,10 +878,14 @@ pub enum ProcessStatus { /// /// Not available. Tracing, - /// ## Linux/FreeBSD + /// ## Linux /// /// Dead/uninterruptible sleep (usually IO). /// + /// ## FreeBSD + /// + /// A process should never end up in this state. + /// /// ## Other OS /// /// Not available. @@ -888,6 +910,10 @@ pub enum ProcessStatus { /// /// Parked (Linux 3.9 to 3.13 only). /// + /// ## macOS + /// + /// Halted at a clean point. + /// /// ## Other OS /// /// Not available. @@ -900,6 +926,14 @@ pub enum ProcessStatus { /// /// Not available. LockBlocked, + /// ## Linux + /// + /// Waiting in uninterruptible disk sleep. + /// + /// ## Other OS + /// + /// Not available. + UninterruptibleDiskSleep, /// Unknown. Unknown(u32), } @@ -952,14 +986,89 @@ pub fn get_current_pid() -> Result<Pid, &'static str> { inner() } +/// MAC address for network interface. +/// +/// It is returned by [`NetworkExt::mac_address`][crate::NetworkExt::mac_address]. +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +pub struct MacAddr(pub [u8; 6]); + +impl MacAddr { + /// A `MacAddr` with all bytes set to `0`. + pub const UNSPECIFIED: Self = MacAddr([0; 6]); + + /// Checks if this `MacAddr` has all bytes equal to `0`. + pub fn is_unspecified(&self) -> bool { + self == &MacAddr::UNSPECIFIED + } +} + +impl fmt::Display for MacAddr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let data = &self.0; + write!( + f, + "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + data[0], data[1], data[2], data[3], data[4], data[5], + ) + } +} + #[cfg(test)] mod tests { - use super::ProcessStatus; + use super::{MacAddr, ProcessStatus}; - // This test only exists to ensure that the `Display` trait is implemented on the + // This test only exists to ensure that the `Display` and `Debug` traits are implemented on the // `ProcessStatus` enum on all targets. #[test] fn check_display_impl_process_status() { println!("{} {:?}", ProcessStatus::Parked, ProcessStatus::Idle); } + + // Ensure that the `Display` and `Debug` traits are implemented on the `MacAddr` struct + #[test] + fn check_display_impl_mac_address() { + println!( + "{} {:?}", + MacAddr([0x1, 0x2, 0x3, 0x4, 0x5, 0x6]), + MacAddr([0xa, 0xb, 0xc, 0xd, 0xe, 0xf]) + ); + } + + #[test] + fn check_mac_address_is_unspecified_true() { + assert!(MacAddr::UNSPECIFIED.is_unspecified()); + assert!(MacAddr([0; 6]).is_unspecified()); + } + + #[test] + fn check_mac_address_is_unspecified_false() { + assert!(!MacAddr([1, 2, 3, 4, 5, 6]).is_unspecified()); + } + + // This test exists to ensure that the `TryFrom<usize>` and `FromStr` traits are implemented + // on `Uid`, `Gid` and `Pid`. + #[test] + fn check_uid_gid_from_impls() { + use std::convert::TryFrom; + use std::str::FromStr; + + #[cfg(not(windows))] + { + assert!(crate::Uid::try_from(0usize).is_ok()); + assert!(crate::Uid::from_str("0").is_ok()); + } + #[cfg(windows)] + { + assert!(crate::Uid::from_str("S-1-5-18").is_ok()); // SECURITY_LOCAL_SYSTEM_RID + assert!(crate::Uid::from_str("0").is_err()); + } + + assert!(crate::Gid::try_from(0usize).is_ok()); + assert!(crate::Gid::from_str("0").is_ok()); + + assert!(crate::Pid::try_from(0usize).is_ok()); + // If it doesn't panic, it's fine. + let _ = crate::Pid::from(0); + assert!(crate::Pid::from_str("0").is_ok()); + } } diff --git a/vendor/sysinfo/src/debug.rs b/vendor/sysinfo/src/debug.rs index ef460eb49..634e2da58 100644 --- a/vendor/sysinfo/src/debug.rs +++ b/vendor/sysinfo/src/debug.rs @@ -47,7 +47,7 @@ impl fmt::Debug for Disk { .iter() .map(|c| *c as char) .collect::<Vec<_>>(), - self.type_(), + self.kind(), if self.is_removable() { "yes" } else { "no" }, self.mount_point(), self.available_space(), diff --git a/vendor/sysinfo/src/freebsd/disk.rs b/vendor/sysinfo/src/freebsd/disk.rs index 5156f1215..c9f84eeae 100644 --- a/vendor/sysinfo/src/freebsd/disk.rs +++ b/vendor/sysinfo/src/freebsd/disk.rs @@ -1,6 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use crate::{DiskExt, DiskType}; +use crate::{DiskExt, DiskKind}; use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; @@ -19,8 +19,8 @@ pub struct Disk { } impl DiskExt for Disk { - fn type_(&self) -> DiskType { - DiskType::Unknown(-1) + fn kind(&self) -> DiskKind { + DiskKind::Unknown(-1) } fn name(&self) -> &OsStr { diff --git a/vendor/sysinfo/src/freebsd/network.rs b/vendor/sysinfo/src/freebsd/network.rs index e58ad823a..bad2f32d3 100644 --- a/vendor/sysinfo/src/freebsd/network.rs +++ b/vendor/sysinfo/src/freebsd/network.rs @@ -4,6 +4,8 @@ use std::collections::{hash_map, HashMap}; use std::mem::MaybeUninit; use super::utils; +use crate::common::MacAddr; +use crate::network::refresh_networks_addresses; use crate::{NetworkExt, NetworksExt, NetworksIter}; macro_rules! old_and_new { @@ -37,6 +39,7 @@ impl NetworksExt for Networks { } // Remove interfaces which are gone. self.interfaces.retain(|_, n| n.updated); + refresh_networks_addresses(&mut self.interfaces); } fn refresh(&mut self) { @@ -85,7 +88,7 @@ impl Networks { let data = &data.ifmd_data; match self.interfaces.entry(name) { hash_map::Entry::Occupied(mut e) => { - let mut interface = e.get_mut(); + let interface = e.get_mut(); old_and_new!(interface, ifi_ibytes, old_ifi_ibytes, data); old_and_new!(interface, ifi_obytes, old_ifi_obytes, data); @@ -114,6 +117,7 @@ impl Networks { ifi_oerrors: data.ifi_oerrors, old_ifi_oerrors: 0, updated: true, + mac_addr: MacAddr::UNSPECIFIED, }); } } @@ -146,6 +150,8 @@ pub struct NetworkData { old_ifi_oerrors: u64, /// Whether or not the above data has been updated during refresh updated: bool, + /// MAC address + pub(crate) mac_addr: MacAddr, } impl NetworkExt for NetworkData { @@ -196,4 +202,8 @@ impl NetworkExt for NetworkData { fn total_errors_on_transmitted(&self) -> u64 { self.ifi_oerrors } + + fn mac_address(&self) -> MacAddr { + self.mac_addr + } } diff --git a/vendor/sysinfo/src/freebsd/process.rs b/vendor/sysinfo/src/freebsd/process.rs index b0dda0f76..52064e179 100644 --- a/vendor/sysinfo/src/freebsd/process.rs +++ b/vendor/sysinfo/src/freebsd/process.rs @@ -58,7 +58,9 @@ pub struct Process { run_time: u64, pub(crate) status: ProcessStatus, user_id: Uid, + effective_user_id: Uid, group_id: Gid, + effective_group_id: Gid, read_bytes: u64, old_read_bytes: u64, written_bytes: u64, @@ -140,15 +142,23 @@ impl ProcessExt for Process { Some(&self.user_id) } + fn effective_user_id(&self) -> Option<&Uid> { + Some(&self.effective_user_id) + } + fn group_id(&self) -> Option<Gid> { Some(self.group_id) } + fn effective_group_id(&self) -> Option<Gid> { + Some(self.effective_group_id) + } + fn wait(&self) { let mut status = 0; // attempt waiting unsafe { - if libc::waitpid(self.pid.0, &mut status, 0) < 0 { + if retry_eintr!(libc::waitpid(self.pid.0, &mut status, 0)) < 0 { // attempt failed (non-child process) so loop until process ends let duration = std::time::Duration::from_millis(10); while kill(self.pid.0, 0) == 0 { @@ -157,6 +167,17 @@ impl ProcessExt for Process { } } } + + fn session_id(&self) -> Option<Pid> { + unsafe { + let session_id = libc::getsid(self.pid.0); + if session_id < 0 { + None + } else { + Some(Pid(session_id)) + } + } + } } pub(crate) unsafe fn get_process_data( @@ -248,7 +269,9 @@ pub(crate) unsafe fn get_process_data( pid: Pid(kproc.ki_pid), parent, user_id: Uid(kproc.ki_ruid), + effective_user_id: Uid(kproc.ki_uid), group_id: Gid(kproc.ki_rgid), + effective_group_id: Gid(kproc.ki_svgid), start_time, run_time: now.saturating_sub(start_time), cpu_usage, diff --git a/vendor/sysinfo/src/freebsd/system.rs b/vendor/sysinfo/src/freebsd/system.rs index 12298bbcf..a59173720 100644 --- a/vendor/sysinfo/src/freebsd/system.rs +++ b/vendor/sysinfo/src/freebsd/system.rs @@ -11,6 +11,7 @@ use std::ffi::CStr; use std::mem::MaybeUninit; use std::path::{Path, PathBuf}; use std::ptr::NonNull; +use std::time::Duration; use super::utils::{ self, boot_time, c_buf_to_string, from_cstr_array, get_frequency_for_cpu, get_sys_value, @@ -77,6 +78,7 @@ pub struct System { impl SystemExt for System { const IS_SUPPORTED: bool = true; const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals(); + const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(100); fn new_with_specifics(refreshes: RefreshKind) -> System { let system_info = SystemInfo::new(); @@ -143,6 +145,9 @@ impl SystemExt for System { self.system_info .get_cpu_usage(&mut self.global_cpu, &mut self.cpus); } + if refresh_kind.frequency() { + self.global_cpu.frequency = self.cpus.get(0).map(|cpu| cpu.frequency).unwrap_or(0); + } } fn refresh_components_list(&mut self) { diff --git a/vendor/sysinfo/src/freebsd/utils.rs b/vendor/sysinfo/src/freebsd/utils.rs index 5745a37f6..072d2cff0 100644 --- a/vendor/sysinfo/src/freebsd/utils.rs +++ b/vendor/sysinfo/src/freebsd/utils.rs @@ -98,7 +98,7 @@ pub(crate) unsafe fn get_sys_value<T: Sized>(mib: &[c_int], value: &mut T) -> bo } pub(crate) unsafe fn get_sys_value_array<T: Sized>(mib: &[c_int], value: &mut [T]) -> bool { - let mut len = (mem::size_of::<T>() * value.len()) as libc::size_t; + let mut len = mem::size_of_val(value) as libc::size_t; libc::sysctl( mib.as_ptr(), mib.len() as _, @@ -126,7 +126,7 @@ pub(crate) fn c_buf_to_string(buf: &[libc::c_char]) -> Option<String> { } pub(crate) unsafe fn get_sys_value_str(mib: &[c_int], buf: &mut [libc::c_char]) -> Option<String> { - let mut len = (mem::size_of::<libc::c_char>() * buf.len()) as libc::size_t; + let mut len = mem::size_of_val(buf) as libc::size_t; if libc::sysctl( mib.as_ptr(), mib.len() as _, diff --git a/vendor/sysinfo/src/lib.rs b/vendor/sysinfo/src/lib.rs index 0800d2562..6d538a6a8 100644 --- a/vendor/sysinfo/src/lib.rs +++ b/vendor/sysinfo/src/lib.rs @@ -1,6 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. #![doc = include_str!("../README.md")] +#![cfg_attr(feature = "serde", doc = include_str!("../md_doc/serde.md"))] #![allow(unknown_lints)] #![deny(missing_docs)] #![deny(rustdoc::broken_intra_doc_links)] @@ -24,15 +25,23 @@ cfg_if::cfg_if! { } else if #[cfg(any(target_os = "macos", target_os = "ios"))] { mod apple; use apple as sys; - extern crate core_foundation_sys; + pub(crate) mod users; + mod network_helper_nix; + use network_helper_nix as network_helper; + mod network; + + // This is needed because macos uses `int*` for `getgrouplist`... + pub(crate) type GroupId = libc::c_int; + pub(crate) use libc::__error as libc_errno; #[cfg(test)] pub(crate) const MIN_USERS: usize = 1; } else if #[cfg(windows)] { mod windows; use windows as sys; - extern crate winapi; - extern crate ntapi; + mod network_helper_win; + use network_helper_win as network_helper; + mod network; #[cfg(test)] pub(crate) const MIN_USERS: usize = 1; @@ -40,6 +49,16 @@ cfg_if::cfg_if! { mod linux; use linux as sys; pub(crate) mod users; + mod network_helper_nix; + use network_helper_nix as network_helper; + mod network; + + // This is needed because macos uses `int*` for `getgrouplist`... + pub(crate) type GroupId = libc::gid_t; + #[cfg(target_os = "linux")] + pub(crate) use libc::__errno_location as libc_errno; + #[cfg(target_os = "android")] + pub(crate) use libc::__errno as libc_errno; #[cfg(test)] pub(crate) const MIN_USERS: usize = 1; @@ -47,6 +66,13 @@ cfg_if::cfg_if! { mod freebsd; use freebsd as sys; pub(crate) mod users; + mod network_helper_nix; + use network_helper_nix as network_helper; + mod network; + + // This is needed because macos uses `int*` for `getgrouplist`... + pub(crate) type GroupId = libc::gid_t; + pub(crate) use libc::__error as libc_errno; #[cfg(test)] pub(crate) const MIN_USERS: usize = 1; @@ -60,8 +86,8 @@ cfg_if::cfg_if! { } pub use common::{ - get_current_pid, CpuRefreshKind, DiskType, DiskUsage, Gid, LoadAvg, NetworksIter, Pid, PidExt, - ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, Uid, User, + get_current_pid, CpuRefreshKind, DiskKind, DiskUsage, Gid, LoadAvg, MacAddr, NetworksIter, Pid, + PidExt, ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, Uid, User, }; pub use sys::{Component, Cpu, Disk, NetworkData, Networks, Process, System}; pub use traits::{ @@ -75,14 +101,16 @@ pub use c_interface::*; mod c_interface; mod common; mod debug; +#[cfg(feature = "serde")] +mod serde; mod system; mod traits; mod utils; -/// This function is only used on linux targets, on the other platforms it does nothing and returns +/// This function is only used on Linux targets, on the other platforms it does nothing and returns /// `false`. /// -/// On linux, to improve performance, we keep a `/proc` file open for each process we index with +/// On Linux, to improve performance, we keep a `/proc` file open for each process we index with /// a maximum number of files open equivalent to half of the system limit. /// /// The problem is that some users might need all the available file descriptors so we need to @@ -328,6 +356,25 @@ mod test { } #[test] + fn check_all_process_uids_resolvable() { + if System::IS_SUPPORTED { + let s = System::new_with_specifics( + RefreshKind::new() + .with_processes(ProcessRefreshKind::new().with_user()) + .with_users_list(), + ); + + // For every process where we can get a user ID, we should also be able + // to find that user ID in the global user list + for process in s.processes().values() { + if let Some(uid) = process.user_id() { + assert!(s.get_user_by_id(uid).is_some(), "No UID {:?} found", uid); + } + } + } + } + + #[test] fn check_system_info() { let s = System::new(); diff --git a/vendor/sysinfo/src/linux/component.rs b/vendor/sysinfo/src/linux/component.rs index 7815103b4..3286829f4 100644 --- a/vendor/sysinfo/src/linux/component.rs +++ b/vendor/sysinfo/src/linux/component.rs @@ -31,7 +31,7 @@ pub struct Component { /// - Read in: `temp[1-*]_input`. /// - Unit: read as millidegree Celsius converted to Celsius. temperature: Option<f32>, - /// Maximum value computed by sysinfo + /// Maximum value computed by `sysinfo`. max: Option<f32>, /// Max threshold provided by the chip/kernel /// - Read in:`temp[1-*]_max` @@ -60,7 +60,7 @@ pub struct Component { sensor_type: Option<TermalSensorType>, /// Component Label /// - /// For formating detail see `Component::label` function docstring. + /// For formatting detail see `Component::label` function docstring. /// /// ## Linux implementation details /// @@ -89,7 +89,7 @@ pub struct Component { /// File to read current temperature shall be `temp[1-*]_input` /// It may be absent but we don't continue if absent. input_file: Option<PathBuf>, - /// `temp[1-*]_highest file` to read if disponnible highest value. + /// `temp[1-*]_highest file` to read if available highest value. highest_file: Option<PathBuf>, } @@ -132,14 +132,14 @@ fn get_temperature_from_file(file: &Path) -> Option<f32> { convert_temp_celsius(temp) } -/// Takes a raw temperature in mili-celsius and convert it to celsius +/// Takes a raw temperature in mili-celsius and convert it to celsius. #[inline] fn convert_temp_celsius(temp: Option<i32>) -> Option<f32> { temp.map(|n| (n as f32) / 1000f32) } /// Information about thermal sensor. It may be unavailable as it's -/// kernel module and chip dependant. +/// kernel module and chip dependent. enum TermalSensorType { /// 1: CPU embedded diode CPUEmbeddedDiode, @@ -228,7 +228,7 @@ impl Component { /// - Optional: max threshold value defined in `tempN_max` /// - Optional: critical threshold value defined in `tempN_crit` /// - /// Where `N` is a u32 associated to a sensor like `temp1_max`, `temp1_input`. + /// Where `N` is a `u32` associated to a sensor like `temp1_max`, `temp1_input`. /// /// ## Doc to Linux kernel API. /// diff --git a/vendor/sysinfo/src/linux/cpu.rs b/vendor/sysinfo/src/linux/cpu.rs index 103f5362a..d99b325c1 100644 --- a/vendor/sysinfo/src/linux/cpu.rs +++ b/vendor/sysinfo/src/linux/cpu.rs @@ -5,9 +5,10 @@ use std::collections::HashSet; use std::fs::File; use std::io::{BufRead, BufReader, Read}; +use std::time::Instant; use crate::sys::utils::to_u64; -use crate::{CpuExt, CpuRefreshKind}; +use crate::{CpuExt, CpuRefreshKind, SystemExt}; macro_rules! to_str { ($e:expr) => { @@ -24,6 +25,8 @@ pub(crate) struct CpusWrapper { /// For example when running `refresh_all` or `refresh_specifics`. need_cpus_update: bool, got_cpu_frequency: bool, + /// This field is needed to prevent updating when not enough time passed since last update. + last_update: Option<Instant>, } impl CpusWrapper { @@ -48,6 +51,7 @@ impl CpusWrapper { cpus: Vec::with_capacity(4), need_cpus_update: true, got_cpu_frequency: false, + last_update: None, } } @@ -62,90 +66,101 @@ impl CpusWrapper { } pub(crate) fn refresh(&mut self, only_update_global_cpu: bool, refresh_kind: CpuRefreshKind) { - let f = match File::open("/proc/stat") { - Ok(f) => f, - Err(_e) => { - sysinfo_debug!("failed to retrieve CPU information: {:?}", _e); - return; - } - }; - let buf = BufReader::new(f); + let need_cpu_usage_update = self + .last_update + .map(|last_update| last_update.elapsed() > crate::System::MINIMUM_CPU_UPDATE_INTERVAL) + .unwrap_or(true); - self.need_cpus_update = false; - let mut i: usize = 0; let first = self.cpus.is_empty(); - let mut it = buf.split(b'\n'); let (vendor_id, brand) = if first { get_vendor_id_and_brand() } else { (String::new(), String::new()) }; - if first || refresh_kind.cpu_usage() { - if let Some(Ok(line)) = it.next() { - if &line[..4] != b"cpu " { + // If the last CPU usage update is too close (less than `MINIMUM_CPU_UPDATE_INTERVAL`), + // we don't want to update CPUs times. + if need_cpu_usage_update { + self.last_update = Some(Instant::now()); + let f = match File::open("/proc/stat") { + Ok(f) => f, + Err(_e) => { + sysinfo_debug!("failed to retrieve CPU information: {:?}", _e); return; } - let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty()); - if first { - self.global_cpu.name = to_str!(parts.next().unwrap_or(&[])).to_owned(); - } else { - parts.next(); - } - self.global_cpu.set( - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - ); - } - if first || !only_update_global_cpu { - while let Some(Ok(line)) = it.next() { - if &line[..3] != b"cpu" { - break; - } + }; + let buf = BufReader::new(f); + + self.need_cpus_update = false; + let mut i: usize = 0; + let mut it = buf.split(b'\n'); + if first || refresh_kind.cpu_usage() { + if let Some(Ok(line)) = it.next() { + if &line[..4] != b"cpu " { + return; + } let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty()); if first { - self.cpus.push(Cpu::new_with_values( - to_str!(parts.next().unwrap_or(&[])), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - 0, - vendor_id.clone(), - brand.clone(), - )); + self.global_cpu.name = to_str!(parts.next().unwrap_or(&[])).to_owned(); } else { - parts.next(); // we don't want the name again - self.cpus[i].set( - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - parts.next().map(to_u64).unwrap_or(0), - ); + parts.next(); + } + self.global_cpu.set( + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + ); + } + if first || !only_update_global_cpu { + while let Some(Ok(line)) = it.next() { + if &line[..3] != b"cpu" { + break; + } + + let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty()); + if first { + self.cpus.push(Cpu::new_with_values( + to_str!(parts.next().unwrap_or(&[])), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + 0, + vendor_id.clone(), + brand.clone(), + )); + } else { + parts.next(); // we don't want the name again + self.cpus[i].set( + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + parts.next().map(to_u64).unwrap_or(0), + ); + } + + i += 1; } - - i += 1; } } } @@ -219,8 +234,8 @@ pub(crate) struct CpuValues { irq: u64, softirq: u64, steal: u64, - _guest: u64, - _guest_nice: u64, + guest: u64, + guest_nice: u64, } impl CpuValues { @@ -235,44 +250,11 @@ impl CpuValues { irq: 0, softirq: 0, steal: 0, - _guest: 0, - _guest_nice: 0, - } - } - - /// Creates a new instance of `CpuValues` with everything set to the corresponding argument. - pub fn new_with_values( - user: u64, - nice: u64, - system: u64, - idle: u64, - iowait: u64, - irq: u64, - softirq: u64, - steal: u64, - guest: u64, - guest_nice: u64, - ) -> CpuValues { - CpuValues { - user, - nice, - system, - idle, - iowait, - irq, - softirq, - steal, - _guest: guest, - _guest_nice: guest_nice, + guest: 0, + guest_nice: 0, } } - /*pub fn is_zero(&self) -> bool { - self.user == 0 && self.nice == 0 && self.system == 0 && self.idle == 0 && - self.iowait == 0 && self.irq == 0 && self.softirq == 0 && self.steal == 0 && - self.guest == 0 && self.guest_nice == 0 - }*/ - /// Sets the given argument to the corresponding fields. pub fn set( &mut self, @@ -287,16 +269,18 @@ impl CpuValues { guest: u64, guest_nice: u64, ) { - self.user = user; - self.nice = nice; + // `guest` is already accounted in `user`. + self.user = user.saturating_sub(guest); + // `guest_nice` is already accounted in `nice`. + self.nice = nice.saturating_sub(guest_nice); self.system = system; self.idle = idle; self.iowait = iowait; self.irq = irq; self.softirq = softirq; self.steal = steal; - self._guest = guest; - self._guest_nice = guest_nice; + self.guest = guest; + self.guest_nice = guest_nice; } /// Returns work time. @@ -306,16 +290,18 @@ impl CpuValues { .saturating_add(self.system) .saturating_add(self.irq) .saturating_add(self.softirq) - .saturating_add(self.steal) } /// Returns total time. pub fn total_time(&self) -> u64 { - // `guest` is already included in `user` - // `guest_nice` is already included in `nice` self.work_time() .saturating_add(self.idle) .saturating_add(self.iowait) + // `steal`, `guest` and `guest_nice` are only used if we want to account the "guest" + // into the computation. + .saturating_add(self.guest) + .saturating_add(self.guest_nice) + .saturating_add(self.steal) } } @@ -349,12 +335,14 @@ impl Cpu { vendor_id: String, brand: String, ) -> Cpu { + let mut new_values = CpuValues::new(); + new_values.set( + user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice, + ); Cpu { name: name.to_owned(), old_values: CpuValues::new(), - new_values: CpuValues::new_with_values( - user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice, - ), + new_values, cpu_usage: 0f32, total_time: 0, old_total_time: 0, @@ -378,11 +366,11 @@ impl Cpu { guest_nice: u64, ) { macro_rules! min { - ($a:expr, $b:expr) => { + ($a:expr, $b:expr, $def:expr) => { if $a > $b { ($a - $b) as f32 } else { - 1. + $def } }; } @@ -392,8 +380,8 @@ impl Cpu { ); self.total_time = self.new_values.total_time(); self.old_total_time = self.old_values.total_time(); - self.cpu_usage = min!(self.new_values.work_time(), self.old_values.work_time()) - / min!(self.total_time, self.old_total_time) + self.cpu_usage = min!(self.new_values.work_time(), self.old_values.work_time(), 0.) + / min!(self.total_time, self.old_total_time, 1.) * 100.; if self.cpu_usage > 100. { self.cpu_usage = 100.; // to prevent the percentage to go above 100% @@ -427,8 +415,7 @@ impl CpuExt for Cpu { pub(crate) fn get_cpu_frequency(cpu_core_index: usize) -> u64 { let mut s = String::new(); if File::open(format!( - "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq", - cpu_core_index + "/sys/devices/system/cpu/cpu{cpu_core_index}/cpufreq/scaling_cur_freq", )) .and_then(|mut f| f.read_to_string(&mut s)) .is_ok() @@ -517,6 +504,216 @@ pub(crate) fn get_physical_core_count() -> Option<usize> { Some(core_ids_and_physical_ids.len()) } +/// Obtain the implementer of this CPU core. +/// +/// This has been obtained from util-linux's lscpu implementation, see +/// https://github.com/util-linux/util-linux/blob/7076703b529d255600631306419cca1b48ab850a/sys-utils/lscpu-arm.c#L240 +/// +/// This list will have to be updated every time a new vendor appears, please keep it synchronized +/// with util-linux and update the link above with the commit you have used. +fn get_arm_implementer(implementer: u32) -> Option<&'static str> { + Some(match implementer { + 0x41 => "ARM", + 0x42 => "Broadcom", + 0x43 => "Cavium", + 0x44 => "DEC", + 0x46 => "FUJITSU", + 0x48 => "HiSilicon", + 0x49 => "Infineon", + 0x4d => "Motorola/Freescale", + 0x4e => "NVIDIA", + 0x50 => "APM", + 0x51 => "Qualcomm", + 0x53 => "Samsung", + 0x56 => "Marvell", + 0x61 => "Apple", + 0x66 => "Faraday", + 0x69 => "Intel", + 0x70 => "Phytium", + 0xc0 => "Ampere", + _ => return None, + }) +} + +/// Obtain the part of this CPU core. +/// +/// This has been obtained from util-linux's lscpu implementation, see +/// https://github.com/util-linux/util-linux/blob/7076703b529d255600631306419cca1b48ab850a/sys-utils/lscpu-arm.c#L34 +/// +/// This list will have to be updated every time a new core appears, please keep it synchronized +/// with util-linux and update the link above with the commit you have used. +fn get_arm_part(implementer: u32, part: u32) -> Option<&'static str> { + Some(match (implementer, part) { + // ARM + (0x41, 0x810) => "ARM810", + (0x41, 0x920) => "ARM920", + (0x41, 0x922) => "ARM922", + (0x41, 0x926) => "ARM926", + (0x41, 0x940) => "ARM940", + (0x41, 0x946) => "ARM946", + (0x41, 0x966) => "ARM966", + (0x41, 0xa20) => "ARM1020", + (0x41, 0xa22) => "ARM1022", + (0x41, 0xa26) => "ARM1026", + (0x41, 0xb02) => "ARM11 MPCore", + (0x41, 0xb36) => "ARM1136", + (0x41, 0xb56) => "ARM1156", + (0x41, 0xb76) => "ARM1176", + (0x41, 0xc05) => "Cortex-A5", + (0x41, 0xc07) => "Cortex-A7", + (0x41, 0xc08) => "Cortex-A8", + (0x41, 0xc09) => "Cortex-A9", + (0x41, 0xc0d) => "Cortex-A17", // Originally A12 + (0x41, 0xc0f) => "Cortex-A15", + (0x41, 0xc0e) => "Cortex-A17", + (0x41, 0xc14) => "Cortex-R4", + (0x41, 0xc15) => "Cortex-R5", + (0x41, 0xc17) => "Cortex-R7", + (0x41, 0xc18) => "Cortex-R8", + (0x41, 0xc20) => "Cortex-M0", + (0x41, 0xc21) => "Cortex-M1", + (0x41, 0xc23) => "Cortex-M3", + (0x41, 0xc24) => "Cortex-M4", + (0x41, 0xc27) => "Cortex-M7", + (0x41, 0xc60) => "Cortex-M0+", + (0x41, 0xd01) => "Cortex-A32", + (0x41, 0xd02) => "Cortex-A34", + (0x41, 0xd03) => "Cortex-A53", + (0x41, 0xd04) => "Cortex-A35", + (0x41, 0xd05) => "Cortex-A55", + (0x41, 0xd06) => "Cortex-A65", + (0x41, 0xd07) => "Cortex-A57", + (0x41, 0xd08) => "Cortex-A72", + (0x41, 0xd09) => "Cortex-A73", + (0x41, 0xd0a) => "Cortex-A75", + (0x41, 0xd0b) => "Cortex-A76", + (0x41, 0xd0c) => "Neoverse-N1", + (0x41, 0xd0d) => "Cortex-A77", + (0x41, 0xd0e) => "Cortex-A76AE", + (0x41, 0xd13) => "Cortex-R52", + (0x41, 0xd20) => "Cortex-M23", + (0x41, 0xd21) => "Cortex-M33", + (0x41, 0xd40) => "Neoverse-V1", + (0x41, 0xd41) => "Cortex-A78", + (0x41, 0xd42) => "Cortex-A78AE", + (0x41, 0xd43) => "Cortex-A65AE", + (0x41, 0xd44) => "Cortex-X1", + (0x41, 0xd46) => "Cortex-A510", + (0x41, 0xd47) => "Cortex-A710", + (0x41, 0xd48) => "Cortex-X2", + (0x41, 0xd49) => "Neoverse-N2", + (0x41, 0xd4a) => "Neoverse-E1", + (0x41, 0xd4b) => "Cortex-A78C", + (0x41, 0xd4c) => "Cortex-X1C", + (0x41, 0xd4d) => "Cortex-A715", + (0x41, 0xd4e) => "Cortex-X3", + + // Broadcom + (0x42, 0x00f) => "Brahma-B15", + (0x42, 0x100) => "Brahma-B53", + (0x42, 0x516) => "ThunderX2", + + // Cavium + (0x43, 0x0a0) => "ThunderX", + (0x43, 0x0a1) => "ThunderX-88XX", + (0x43, 0x0a2) => "ThunderX-81XX", + (0x43, 0x0a3) => "ThunderX-83XX", + (0x43, 0x0af) => "ThunderX2-99xx", + + // DEC + (0x44, 0xa10) => "SA110", + (0x44, 0xa11) => "SA1100", + + // Fujitsu + (0x46, 0x001) => "A64FX", + + // HiSilicon + (0x48, 0xd01) => "Kunpeng-920", // aka tsv110 + + // NVIDIA + (0x4e, 0x000) => "Denver", + (0x4e, 0x003) => "Denver 2", + (0x4e, 0x004) => "Carmel", + + // APM + (0x50, 0x000) => "X-Gene", + + // Qualcomm + (0x51, 0x00f) => "Scorpion", + (0x51, 0x02d) => "Scorpion", + (0x51, 0x04d) => "Krait", + (0x51, 0x06f) => "Krait", + (0x51, 0x201) => "Kryo", + (0x51, 0x205) => "Kryo", + (0x51, 0x211) => "Kryo", + (0x51, 0x800) => "Falkor-V1/Kryo", + (0x51, 0x801) => "Kryo-V2", + (0x51, 0x802) => "Kryo-3XX-Gold", + (0x51, 0x803) => "Kryo-3XX-Silver", + (0x51, 0x804) => "Kryo-4XX-Gold", + (0x51, 0x805) => "Kryo-4XX-Silver", + (0x51, 0xc00) => "Falkor", + (0x51, 0xc01) => "Saphira", + + // Samsung + (0x53, 0x001) => "exynos-m1", + + // Marvell + (0x56, 0x131) => "Feroceon-88FR131", + (0x56, 0x581) => "PJ4/PJ4b", + (0x56, 0x584) => "PJ4B-MP", + + // Apple + (0x61, 0x020) => "Icestorm-A14", + (0x61, 0x021) => "Firestorm-A14", + (0x61, 0x022) => "Icestorm-M1", + (0x61, 0x023) => "Firestorm-M1", + (0x61, 0x024) => "Icestorm-M1-Pro", + (0x61, 0x025) => "Firestorm-M1-Pro", + (0x61, 0x028) => "Icestorm-M1-Max", + (0x61, 0x029) => "Firestorm-M1-Max", + (0x61, 0x030) => "Blizzard-A15", + (0x61, 0x031) => "Avalanche-A15", + (0x61, 0x032) => "Blizzard-M2", + (0x61, 0x033) => "Avalanche-M2", + + // Faraday + (0x66, 0x526) => "FA526", + (0x66, 0x626) => "FA626", + + // Intel + (0x69, 0x200) => "i80200", + (0x69, 0x210) => "PXA250A", + (0x69, 0x212) => "PXA210A", + (0x69, 0x242) => "i80321-400", + (0x69, 0x243) => "i80321-600", + (0x69, 0x290) => "PXA250B/PXA26x", + (0x69, 0x292) => "PXA210B", + (0x69, 0x2c2) => "i80321-400-B0", + (0x69, 0x2c3) => "i80321-600-B0", + (0x69, 0x2d0) => "PXA250C/PXA255/PXA26x", + (0x69, 0x2d2) => "PXA210C", + (0x69, 0x411) => "PXA27x", + (0x69, 0x41c) => "IPX425-533", + (0x69, 0x41d) => "IPX425-400", + (0x69, 0x41f) => "IPX425-266", + (0x69, 0x682) => "PXA32x", + (0x69, 0x683) => "PXA930/PXA935", + (0x69, 0x688) => "PXA30x", + (0x69, 0x689) => "PXA31x", + (0x69, 0xb11) => "SA1110", + (0x69, 0xc12) => "IPX1200", + + // Phytium + (0x70, 0x660) => "FTC660", + (0x70, 0x661) => "FTC661", + (0x70, 0x662) => "FTC662", + (0x70, 0x663) => "FTC663", + + _ => return None, + }) +} + /// Returns the brand/vendor string for the first CPU (which should be the same for all CPUs). pub(crate) fn get_vendor_id_and_brand() -> (String, String) { let mut s = String::new(); @@ -534,20 +731,49 @@ pub(crate) fn get_vendor_id_and_brand() -> (String, String) { .unwrap_or_default() } + fn get_hex_value(s: &str) -> u32 { + s.split(':') + .last() + .map(|x| x.trim()) + .filter(|x| x.starts_with("0x")) + .map(|x| u32::from_str_radix(&x[2..], 16).unwrap()) + .unwrap_or_default() + } + let mut vendor_id = None; let mut brand = None; + let mut implementer = None; + let mut part = None; for it in s.split('\n') { if it.starts_with("vendor_id\t") { vendor_id = Some(get_value(it)); } else if it.starts_with("model name\t") { brand = Some(get_value(it)); + } else if it.starts_with("CPU implementer\t") { + implementer = Some(get_hex_value(it)); + } else if it.starts_with("CPU part\t") { + part = Some(get_hex_value(it)); } else { continue; } - if brand.is_some() && vendor_id.is_some() { + if (brand.is_some() && vendor_id.is_some()) || (implementer.is_some() && part.is_some()) { break; } } + if let (Some(implementer), Some(part)) = (implementer, part) { + vendor_id = get_arm_implementer(implementer).map(String::from); + // It's possible to "model name" even with an ARM CPU, so just in case we can't retrieve + // the brand from "CPU part", we will then use the value from "model name". + // + // Example from raspberry pi 3B+: + // + // ``` + // model name : ARMv7 Processor rev 4 (v7l) + // CPU implementer : 0x41 + // CPU part : 0xd03 + // ``` + brand = get_arm_part(implementer, part).map(String::from).or(brand); + } (vendor_id.unwrap_or_default(), brand.unwrap_or_default()) } diff --git a/vendor/sysinfo/src/linux/disk.rs b/vendor/sysinfo/src/linux/disk.rs index 6d7fc083c..23871d57f 100644 --- a/vendor/sysinfo/src/linux/disk.rs +++ b/vendor/sysinfo/src/linux/disk.rs @@ -1,7 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::sys::utils::{get_all_data, to_cpath}; -use crate::{DiskExt, DiskType}; +use crate::{DiskExt, DiskKind}; use libc::statvfs; use std::ffi::{OsStr, OsString}; @@ -19,7 +19,7 @@ macro_rules! cast { #[doc = include_str!("../../md_doc/disk.md")] #[derive(PartialEq, Eq)] pub struct Disk { - type_: DiskType, + type_: DiskKind, device_name: OsString, file_system: Vec<u8>, mount_point: PathBuf, @@ -29,7 +29,7 @@ pub struct Disk { } impl DiskExt for Disk { - fn type_(&self) -> DiskType { + fn kind(&self) -> DiskKind { self.type_ } @@ -61,7 +61,7 @@ impl DiskExt for Disk { unsafe { let mut stat: statvfs = mem::zeroed(); let mount_point_cpath = to_cpath(&self.mount_point); - if statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat) == 0 { + if retry_eintr!(statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat)) == 0 { let tmp = cast!(stat.f_bsize).saturating_mul(cast!(stat.f_bavail)); self.available_space = cast!(tmp); true @@ -84,7 +84,7 @@ fn new_disk( let mut available = 0; unsafe { let mut stat: statvfs = mem::zeroed(); - if statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat) == 0 { + if retry_eintr!(statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat)) == 0 { let bsize = cast!(stat.f_bsize); let blocks = cast!(stat.f_blocks); let bavail = cast!(stat.f_bavail); @@ -111,7 +111,7 @@ fn new_disk( } #[allow(clippy::manual_range_contains)] -fn find_type_for_device_name(device_name: &OsStr) -> DiskType { +fn find_type_for_device_name(device_name: &OsStr) -> DiskKind { // The format of devices are as follows: // - device_name is symbolic link in the case of /dev/mapper/ // and /dev/root, and the target is corresponding device under @@ -171,13 +171,13 @@ fn find_type_for_device_name(device_name: &OsStr) -> DiskType { .ok() { // The disk is marked as rotational so it's a HDD. - Some(1) => DiskType::HDD, + Some(1) => DiskKind::HDD, // The disk is marked as non-rotational so it's very likely a SSD. - Some(0) => DiskType::SSD, + Some(0) => DiskKind::SSD, // Normally it shouldn't happen but welcome to the wonderful world of IT! :D - Some(x) => DiskType::Unknown(x), + Some(x) => DiskKind::Unknown(x), // The information isn't available... - None => DiskType::Unknown(-1), + None => DiskKind::Unknown(-1), } } @@ -234,7 +234,9 @@ fn get_all_disks_inner(content: &str) -> Vec<Disk> { "pstore" | // https://www.kernel.org/doc/Documentation/ABI/testing/pstore "squashfs" | // squashfs is a compressed read-only file system (for snaps) "rpc_pipefs" | // The pipefs pseudo file system service - "iso9660" // optical media + "iso9660" | // optical media + "nfs4" | // calling statvfs on a mounted NFS may hang + "nfs" // nfs2 or nfs3 ); !(filtered || diff --git a/vendor/sysinfo/src/linux/network.rs b/vendor/sysinfo/src/linux/network.rs index c8da2bcb3..3b89fdc57 100644 --- a/vendor/sysinfo/src/linux/network.rs +++ b/vendor/sysinfo/src/linux/network.rs @@ -1,9 +1,11 @@ // Take a look at the license at the top of the repository in the LICENSE file. -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; use crate::{NetworkExt, NetworksExt, NetworksIter}; use std::collections::{hash_map, HashMap}; @@ -77,7 +79,7 @@ fn refresh_networks_list_from_sysfs( // let tx_compressed = read(parent, "tx_compressed", &mut data); match interfaces.entry(entry) { hash_map::Entry::Occupied(mut e) => { - let mut interface = e.get_mut(); + let interface = e.get_mut(); old_and_new!(interface, rx_bytes, old_rx_bytes); old_and_new!(interface, tx_bytes, old_tx_bytes); old_and_new!(interface, rx_packets, old_rx_packets); @@ -102,6 +104,7 @@ fn refresh_networks_list_from_sysfs( old_rx_errors: rx_errors, tx_errors, old_tx_errors: tx_errors, + mac_addr: MacAddr::UNSPECIFIED, // rx_compressed, // old_rx_compressed: rx_compressed, // tx_compressed, @@ -132,6 +135,7 @@ impl NetworksExt for Networks { fn refresh_networks_list(&mut self) { refresh_networks_list_from_sysfs(&mut self.interfaces, Path::new("/sys/class/net/")); + refresh_networks_addresses(&mut self.interfaces); } } @@ -157,6 +161,8 @@ pub struct NetworkData { /// similar to `rx_errors` tx_errors: u64, old_tx_errors: u64, + /// MAC address + pub(crate) mac_addr: MacAddr, // /// Indicates the number of compressed packets received by this // /// network device. This value might only be relevant for interfaces // /// that support packet compression (e.g: PPP). @@ -263,6 +269,10 @@ impl NetworkExt for NetworkData { fn total_errors_on_transmitted(&self) -> u64 { self.tx_errors } + + fn mac_address(&self) -> MacAddr { + self.mac_addr + } } #[cfg(test)] diff --git a/vendor/sysinfo/src/linux/process.rs b/vendor/sysinfo/src/linux/process.rs index d7d61b5bc..55893ce8f 100644 --- a/vendor/sysinfo/src/linux/process.rs +++ b/vendor/sysinfo/src/linux/process.rs @@ -5,7 +5,6 @@ use std::collections::HashMap; use std::fmt; use std::fs::{self, File}; use std::io::Read; -use std::mem::MaybeUninit; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -19,26 +18,13 @@ use crate::utils::into_iter; use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid}; #[doc(hidden)] -impl From<u32> for ProcessStatus { - fn from(status: u32) -> ProcessStatus { - match status { - 1 => ProcessStatus::Idle, - 2 => ProcessStatus::Run, - 3 => ProcessStatus::Sleep, - 4 => ProcessStatus::Stop, - 5 => ProcessStatus::Zombie, - x => ProcessStatus::Unknown(x), - } - } -} - -#[doc(hidden)] impl From<char> for ProcessStatus { fn from(status: char) -> ProcessStatus { match status { 'R' => ProcessStatus::Run, 'S' => ProcessStatus::Sleep, - 'D' => ProcessStatus::Idle, + 'I' => ProcessStatus::Idle, + 'D' => ProcessStatus::UninterruptibleDiskSleep, 'Z' => ProcessStatus::Zombie, 'T' => ProcessStatus::Stop, 't' => ProcessStatus::Tracing, @@ -64,6 +50,7 @@ impl fmt::Display for ProcessStatus { ProcessStatus::Wakekill => "Wakekill", ProcessStatus::Waking => "Waking", ProcessStatus::Parked => "Parked", + ProcessStatus::UninterruptibleDiskSleep => "UninterruptibleDiskSleep", _ => "Unknown", }) } @@ -91,7 +78,9 @@ pub struct Process { pub(crate) updated: bool, cpu_usage: f32, user_id: Option<Uid>, + effective_user_id: Option<Uid>, group_id: Option<Gid>, + effective_group_id: Option<Gid>, pub(crate) status: ProcessStatus, /// Tasks run by this process. pub tasks: HashMap<Pid, Process>, @@ -125,7 +114,9 @@ impl Process { start_time: 0, run_time: 0, user_id: None, + effective_user_id: None, group_id: None, + effective_group_id: None, status: ProcessStatus::Unknown(0), tasks: if pid.0 == 0 { HashMap::with_capacity(1000) @@ -216,15 +207,23 @@ impl ProcessExt for Process { self.user_id.as_ref() } + fn effective_user_id(&self) -> Option<&Uid> { + self.effective_user_id.as_ref() + } + fn group_id(&self) -> Option<Gid> { self.group_id } + fn effective_group_id(&self) -> Option<Gid> { + self.effective_group_id + } + fn wait(&self) { let mut status = 0; // attempt waiting unsafe { - if libc::waitpid(self.pid.0, &mut status, 0) < 0 { + if retry_eintr!(libc::waitpid(self.pid.0, &mut status, 0)) < 0 { // attempt failed (non-child process) so loop until process ends let duration = std::time::Duration::from_millis(10); while kill(self.pid.0, 0) == 0 { @@ -233,6 +232,17 @@ impl ProcessExt for Process { } } } + + fn session_id(&self) -> Option<Pid> { + unsafe { + let session_id = libc::getsid(self.pid.0); + if session_id < 0 { + None + } else { + Some(Pid(session_id)) + } + } + } } pub(crate) fn compute_cpu_usage(p: &mut Process, total_time: f32, max_value: f32) { @@ -248,6 +258,17 @@ pub(crate) fn compute_cpu_usage(p: &mut Process, total_time: f32, max_value: f32 / total_time * 100.) .min(max_value); + + for task in p.tasks.values_mut() { + compute_cpu_usage(task, total_time, max_value); + } +} + +pub(crate) fn unset_updated(p: &mut Process) { + p.updated = false; + for task in p.tasks.values_mut() { + unset_updated(task); + } } pub(crate) fn set_time(p: &mut Process, utime: u64, stime: u64) { @@ -327,9 +348,13 @@ fn get_status(p: &mut Process, part: &str) { } fn refresh_user_group_ids<P: PathPush>(p: &mut Process, path: &mut P) { - if let Some((user_id, group_id)) = get_uid_and_gid(path.join("status")) { + if let Some(((user_id, effective_user_id), (group_id, effective_group_id))) = + get_uid_and_gid(path.join("status")) + { p.user_id = Some(Uid(user_id)); + p.effective_user_id = Some(Uid(effective_user_id)); p.group_id = Some(Gid(group_id)); + p.effective_group_id = Some(Gid(effective_group_id)); } } @@ -366,35 +391,24 @@ fn retrieve_all_new_process_info( refresh_user_group_ids(&mut p, &mut tmp); } - if proc_list.pid.0 != 0 { - // If we're getting information for a child, no need to get those info since we - // already have them... - p.cmd = proc_list.cmd.clone(); - p.name = proc_list.name.clone(); - p.environ = proc_list.environ.clone(); - p.exe = proc_list.exe.clone(); - p.cwd = proc_list.cwd.clone(); - p.root = proc_list.root.clone(); - } else { - p.name = name.into(); + p.name = name.into(); - match tmp.join("exe").read_link() { - Ok(exe_path) => { - p.exe = exe_path; - } - Err(_) => { - // Do not use cmd[0] because it is not the same thing. - // See https://github.com/GuillaumeGomez/sysinfo/issues/697. - p.exe = PathBuf::new() - } + match tmp.join("exe").read_link() { + Ok(exe_path) => { + p.exe = exe_path; + } + Err(_) => { + // Do not use cmd[0] because it is not the same thing. + // See https://github.com/GuillaumeGomez/sysinfo/issues/697. + p.exe = PathBuf::new() } - - p.cmd = copy_from_file(tmp.join("cmdline")); - p.environ = copy_from_file(tmp.join("environ")); - p.cwd = realpath(tmp.join("cwd")); - p.root = realpath(tmp.join("root")); } + p.cmd = copy_from_file(tmp.join("cmdline")); + p.environ = copy_from_file(tmp.join("environ")); + p.cwd = realpath(tmp.join("cwd")); + p.root = realpath(tmp.join("root")); + update_time_and_memory( path, &mut p, @@ -420,6 +434,11 @@ pub(crate) fn _get_process_data( refresh_kind: ProcessRefreshKind, ) -> Result<(Option<Process>, Pid), ()> { let pid = match path.file_name().and_then(|x| x.to_str()).map(Pid::from_str) { + // If `pid` and `nb` are the same, it means the file is linking to itself so we skip it. + // + // It's because when reading `/proc/[PID]` folder, we then go through the folders inside it. + // Then, if we encounter a sub-folder with the same PID as the parent, then it's a link to + // the current folder we already did read so no need to do anything. Some(Ok(nb)) if nb != pid => nb, _ => return Err(()), }; @@ -609,10 +628,12 @@ pub(crate) fn refresh_procs( fn copy_from_file(entry: &Path) -> Vec<String> { match File::open(entry) { Ok(mut f) => { - let mut data = vec![0; 16_384]; + let mut data = Vec::with_capacity(16_384); - if let Ok(size) = f.read(&mut data) { - data.truncate(size); + if let Err(_e) = f.read_to_end(&mut data) { + sysinfo_debug!("Failed to read file in `copy_from_file`: {:?}", _e); + Vec::new() + } else { let mut out = Vec::with_capacity(20); let mut start = 0; for (pos, x) in data.iter().enumerate() { @@ -628,51 +649,47 @@ fn copy_from_file(entry: &Path) -> Vec<String> { } } out - } else { - Vec::new() } } - Err(_) => Vec::new(), - } -} - -fn get_uid_and_gid(file_path: &Path) -> Option<(uid_t, gid_t)> { - use std::os::unix::ffi::OsStrExt; - - unsafe { - let mut sstat: MaybeUninit<libc::stat> = MaybeUninit::uninit(); - - let mut file_path: Vec<u8> = file_path.as_os_str().as_bytes().to_vec(); - file_path.push(0); - if libc::stat(file_path.as_ptr() as *const _, sstat.as_mut_ptr()) == 0 { - let sstat = sstat.assume_init(); - - return Some((sstat.st_uid, sstat.st_gid)); + Err(_e) => { + sysinfo_debug!("Failed to open file in `copy_from_file`: {:?}", _e); + Vec::new() } } +} +// Fetch tuples of real and effective UID and GID. +fn get_uid_and_gid(file_path: &Path) -> Option<((uid_t, uid_t), (gid_t, gid_t))> { let status_data = get_all_data(file_path, 16_385).ok()?; // We're only interested in the lines starting with Uid: and Gid: - // here. From these lines, we're looking at the second entry to get - // the effective u/gid. + // here. From these lines, we're looking at the first and second entries to get + // the real u/gid. - let f = |h: &str, n: &str| -> Option<uid_t> { + let f = |h: &str, n: &str| -> (Option<uid_t>, Option<uid_t>) { if h.starts_with(n) { - h.split_whitespace().nth(2).unwrap_or("0").parse().ok() + let mut ids = h.split_whitespace(); + let real = ids.nth(1).unwrap_or("0").parse().ok(); + let effective = ids.next().unwrap_or("0").parse().ok(); + + (real, effective) } else { - None + (None, None) } }; let mut uid = None; + let mut effective_uid = None; let mut gid = None; + let mut effective_gid = None; for line in status_data.lines() { - if let Some(u) = f(line, "Uid:") { - assert!(uid.is_none()); - uid = Some(u); - } else if let Some(g) = f(line, "Gid:") { - assert!(gid.is_none()); - gid = Some(g); + if let (Some(real), Some(effective)) = f(line, "Uid:") { + debug_assert!(uid.is_none() && effective_uid.is_none()); + uid = Some(real); + effective_uid = Some(effective); + } else if let (Some(real), Some(effective)) = f(line, "Gid:") { + debug_assert!(gid.is_none() && effective_gid.is_none()); + gid = Some(real); + effective_gid = Some(effective); } else { continue; } @@ -680,8 +697,10 @@ fn get_uid_and_gid(file_path: &Path) -> Option<(uid_t, gid_t)> { break; } } - match (uid, gid) { - (Some(u), Some(g)) => Some((u, g)), + match (uid, effective_uid, gid, effective_gid) { + (Some(uid), Some(effective_uid), Some(gid), Some(effective_gid)) => { + Some(((uid, effective_uid), (gid, effective_gid))) + } _ => None, } } diff --git a/vendor/sysinfo/src/linux/system.rs b/vendor/sysinfo/src/linux/system.rs index 3c4fce345..bbb6b24c4 100644 --- a/vendor/sysinfo/src/linux/system.rs +++ b/vendor/sysinfo/src/linux/system.rs @@ -16,6 +16,7 @@ use std::io::{BufRead, BufReader, Read}; use std::path::Path; use std::str::FromStr; use std::sync::{Arc, Mutex}; +use std::time::Duration; // This whole thing is to prevent having too many files open at once. It could be problematic // for processes using a lot of files and using sysinfo at the same time. @@ -28,7 +29,7 @@ pub(crate) static mut REMAINING_FILES: once_cell::sync::Lazy<Arc<Mutex<isize>>> rlim_max: 0, }; if libc::getrlimit(libc::RLIMIT_NOFILE, &mut limits) != 0 { - // Most linux system now defaults to 1024. + // Most Linux system now defaults to 1024. return Arc::new(Mutex::new(1024 / 2)); } // We save the value in case the update fails. @@ -55,7 +56,7 @@ pub(crate) fn get_max_nb_fds() -> isize { rlim_max: 0, }; if libc::getrlimit(libc::RLIMIT_NOFILE, &mut limits) != 0 { - // Most linux system now defaults to 1024. + // Most Linux system now defaults to 1024. 1024 / 2 } else { limits.rlim_max as isize / 2 @@ -81,11 +82,8 @@ fn boot_time() -> u64 { } } // Either we didn't find "btime" or "/proc/stat" wasn't available for some reason... - let mut up = libc::timespec { - tv_sec: 0, - tv_nsec: 0, - }; unsafe { + let mut up: libc::timespec = std::mem::zeroed(); if libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut up) == 0 { up.tv_sec as u64 } else { @@ -207,7 +205,7 @@ impl System { if compute_cpu { compute_cpu_usage(proc_, total_time, max_value); } - proc_.updated = false; + unset_updated(proc_); true }); } @@ -220,6 +218,7 @@ impl System { impl SystemExt for System { const IS_SUPPORTED: bool = true; const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals(); + const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(200); fn new_with_specifics(refreshes: RefreshKind) -> System { let process_list = Process::new(Pid(0)); @@ -311,7 +310,7 @@ impl SystemExt for System { fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool { let uptime = self.uptime(); - let found = match _get_process_data( + match _get_process_data( &Path::new("/proc/").join(pid.to_string()), &mut self.process_list, Pid(0), @@ -321,32 +320,33 @@ impl SystemExt for System { ) { Ok((Some(p), pid)) => { self.process_list.tasks.insert(pid, p); - true } - Ok(_) => true, - Err(_) => false, + Ok(_) => {} + Err(_e) => { + sysinfo_debug!("Cannot get information for PID {:?}: {:?}", pid, _e); + return false; + } }; - if found { - if refresh_kind.cpu() { - self.refresh_cpus(true, CpuRefreshKind::new().with_cpu_usage()); + if refresh_kind.cpu() { + self.refresh_cpus(true, CpuRefreshKind::new().with_cpu_usage()); - if self.cpus.is_empty() { - sysinfo_debug!("Cannot compute process CPU usage: no cpus found..."); - return found; - } - let (new, old) = self.cpus.get_global_raw_times(); - let total_time = (if old >= new { 1 } else { new - old }) as f32; - - let max_cpu_usage = self.get_max_process_cpu_usage(); - if let Some(p) = self.process_list.tasks.get_mut(&pid) { - compute_cpu_usage(p, total_time / self.cpus.len() as f32, max_cpu_usage); - p.updated = false; - } - } else if let Some(p) = self.process_list.tasks.get_mut(&pid) { - p.updated = false; + if self.cpus.is_empty() { + eprintln!("Cannot compute process CPU usage: no cpus found..."); + return true; + } + let (new, old) = self.cpus.get_global_raw_times(); + let total_time = (if old >= new { 1 } else { new - old }) as f32; + let total_time = total_time / self.cpus.len() as f32; + + let max_cpu_usage = self.get_max_process_cpu_usage(); + if let Some(p) = self.process_list.tasks.get_mut(&pid) { + compute_cpu_usage(p, total_time, max_cpu_usage); + unset_updated(p); } + } else if let Some(p) = self.process_list.tasks.get_mut(&pid) { + unset_updated(p); } - found + true } fn refresh_disks_list(&mut self) { diff --git a/vendor/sysinfo/src/linux/utils.rs b/vendor/sysinfo/src/linux/utils.rs index 60a9aa2b0..486682210 100644 --- a/vendor/sysinfo/src/linux/utils.rs +++ b/vendor/sysinfo/src/linux/utils.rs @@ -1,14 +1,14 @@ // Take a look at the license at the top of the repository in the LICENSE file. use std::fs::File; -use std::io::{self, Read, Seek, SeekFrom}; +use std::io::{self, Read, Seek}; use std::path::{Path, PathBuf}; use crate::sys::system::REMAINING_FILES; pub(crate) fn get_all_data_from_file(file: &mut File, size: usize) -> io::Result<String> { let mut buf = String::with_capacity(size); - file.seek(SeekFrom::Start(0))?; + file.rewind()?; file.read_to_string(&mut buf)?; Ok(buf) } diff --git a/vendor/sysinfo/src/macros.rs b/vendor/sysinfo/src/macros.rs index 3bb4defa1..84d651cf5 100644 --- a/vendor/sysinfo/src/macros.rs +++ b/vendor/sysinfo/src/macros.rs @@ -56,3 +56,36 @@ macro_rules! declare_signals { } ) } + +#[cfg(all(unix, not(feature = "unknown-ci")))] +macro_rules! retry_eintr { + (set_to_0 => $($t:tt)+) => {{ + let errno = crate::libc_errno(); + if !errno.is_null() { + *errno = 0; + } + retry_eintr!($($t)+) + }}; + ($errno_value:ident => $($t:tt)+) => {{ + loop { + let ret = $($t)+; + if ret < 0 { + let tmp = std::io::Error::last_os_error(); + if tmp.kind() == std::io::ErrorKind::Interrupted { + continue; + } + $errno_value = tmp.raw_os_error().unwrap_or(0); + } + break ret; + } + }}; + ($($t:tt)+) => {{ + loop { + let ret = $($t)+; + if ret < 0 && std::io::Error::last_os_error().kind() == std::io::ErrorKind::Interrupted { + continue; + } + break ret; + } + }}; +} diff --git a/vendor/sysinfo/src/network.rs b/vendor/sysinfo/src/network.rs new file mode 100644 index 000000000..8cb9477d5 --- /dev/null +++ b/vendor/sysinfo/src/network.rs @@ -0,0 +1,17 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use std::collections::HashMap; + +use crate::network_helper::get_interface_address; +use crate::NetworkData; + +/// Interface addresses are OS-independent +pub(crate) fn refresh_networks_addresses(interfaces: &mut HashMap<String, NetworkData>) { + if let Ok(ifa_iterator) = get_interface_address() { + for (name, ifa) in ifa_iterator { + if let Some(interface) = interfaces.get_mut(&name) { + interface.mac_addr = ifa; + } + } + } +} diff --git a/vendor/sysinfo/src/network_helper_nix.rs b/vendor/sysinfo/src/network_helper_nix.rs new file mode 100644 index 000000000..30352d486 --- /dev/null +++ b/vendor/sysinfo/src/network_helper_nix.rs @@ -0,0 +1,114 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::common::MacAddr; +use std::ptr::null_mut; + +/// This iterator yields an interface name and address. +pub(crate) struct InterfaceAddressIterator { + /// Pointer to the current `ifaddrs` struct. + ifap: *mut libc::ifaddrs, + /// Pointer to the first element in linked list. + buf: *mut libc::ifaddrs, +} + +impl Iterator for InterfaceAddressIterator { + type Item = (String, MacAddr); + + fn next(&mut self) -> Option<Self::Item> { + unsafe { + while !self.ifap.is_null() { + // advance the pointer until a MAC address is found + let ifap = self.ifap; + self.ifap = (*ifap).ifa_next; + + if let Some(addr) = parse_interface_address(ifap) { + // libc::IFNAMSIZ + 6 + // This size refers to ./apple/network.rs:75 + let mut name = vec![0u8; libc::IFNAMSIZ + 6]; + libc::strcpy(name.as_mut_ptr() as _, (*ifap).ifa_name); + name.set_len(libc::strlen((*ifap).ifa_name)); + let name = String::from_utf8_unchecked(name); + + return Some((name, addr)); + } + } + None + } + } +} + +impl Drop for InterfaceAddressIterator { + fn drop(&mut self) { + unsafe { + libc::freeifaddrs(self.buf); + } + } +} + +#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "ios"))] +impl From<&libc::sockaddr_dl> for MacAddr { + fn from(value: &libc::sockaddr_dl) -> Self { + let sdl_data = value.sdl_data; + // interface name length, NO trailing 0 + let sdl_nlen = value.sdl_nlen as usize; + // make sure that it is never out of bound + if sdl_nlen + 5 < 12 { + MacAddr([ + sdl_data[sdl_nlen] as u8, + sdl_data[sdl_nlen + 1] as u8, + sdl_data[sdl_nlen + 2] as u8, + sdl_data[sdl_nlen + 3] as u8, + sdl_data[sdl_nlen + 4] as u8, + sdl_data[sdl_nlen + 5] as u8, + ]) + } else { + MacAddr::UNSPECIFIED + } + } +} + +#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "ios"))] +unsafe fn parse_interface_address(ifap: *const libc::ifaddrs) -> Option<MacAddr> { + let sock_addr = (*ifap).ifa_addr; + if sock_addr.is_null() { + return None; + } + match (*sock_addr).sa_family as libc::c_int { + libc::AF_LINK => { + let addr = sock_addr as *const libc::sockaddr_dl; + Some(MacAddr::from(&*addr)) + } + _ => None, + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +unsafe fn parse_interface_address(ifap: *const libc::ifaddrs) -> Option<MacAddr> { + use libc::sockaddr_ll; + + let sock_addr = (*ifap).ifa_addr; + if sock_addr.is_null() { + return None; + } + match (*sock_addr).sa_family as libc::c_int { + libc::AF_PACKET => { + let addr = sock_addr as *const sockaddr_ll; + // Take the first 6 bytes + let [addr @ .., _, _] = (*addr).sll_addr; + Some(MacAddr(addr)) + } + _ => None, + } +} + +/// Return an iterator on (interface_name, address) pairs +pub(crate) fn get_interface_address() -> Result<InterfaceAddressIterator, String> { + let mut ifap = null_mut(); + unsafe { + if retry_eintr!(libc::getifaddrs(&mut ifap)) == 0 && !ifap.is_null() { + Ok(InterfaceAddressIterator { ifap, buf: ifap }) + } else { + Err("failed to call getifaddrs()".to_string()) + } + } +} diff --git a/vendor/sysinfo/src/network_helper_win.rs b/vendor/sysinfo/src/network_helper_win.rs new file mode 100644 index 000000000..1d7470804 --- /dev/null +++ b/vendor/sysinfo/src/network_helper_win.rs @@ -0,0 +1,104 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use std::ffi::OsString; +use std::os::windows::prelude::OsStringExt; +use std::ptr::null_mut; + +use winapi::shared::winerror::{ERROR_BUFFER_OVERFLOW, ERROR_SUCCESS}; +use winapi::shared::ws2def::AF_UNSPEC; +use winapi::um::iphlpapi::GetAdaptersAddresses; +use winapi::um::iptypes::{ + GAA_FLAG_SKIP_ANYCAST, GAA_FLAG_SKIP_DNS_SERVER, GAA_FLAG_SKIP_MULTICAST, PIP_ADAPTER_ADDRESSES, +}; + +use crate::common::MacAddr; + +/// this iterator yields an interface name and address +pub(crate) struct InterfaceAddressIterator { + /// The first item in the linked list + buf: PIP_ADAPTER_ADDRESSES, + /// The current adapter + adapter: PIP_ADAPTER_ADDRESSES, +} + +// Need a function to convert u16 pointer into String +// https://stackoverflow.com/a/48587463/8706476 +unsafe fn u16_ptr_to_string(ptr: *const u16) -> OsString { + let len = (0..).take_while(|&i| *ptr.offset(i) != 0).count(); + let slice = std::slice::from_raw_parts(ptr, len); + + OsString::from_wide(slice) +} + +impl Iterator for InterfaceAddressIterator { + type Item = (String, MacAddr); + + fn next(&mut self) -> Option<Self::Item> { + if self.adapter.is_null() { + return None; + } + unsafe { + let adapter = self.adapter; + // Move to the next adapter + self.adapter = (*adapter).Next; + if let Ok(interface_name) = u16_ptr_to_string((*adapter).FriendlyName).into_string() { + // take the first 6 bytes and return the MAC address instead + let [mac @ .., _, _] = (*adapter).PhysicalAddress; + Some((interface_name, MacAddr(mac))) + } else { + // Not sure whether error can occur when parsing adapter name. + self.next() + } + } + } +} + +impl Drop for InterfaceAddressIterator { + fn drop(&mut self) { + unsafe { + libc::free(self.buf as _); + } + } +} + +pub(crate) fn get_interface_address() -> Result<InterfaceAddressIterator, String> { + unsafe { + // https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#remarks + // A 15k buffer is recommended + let mut size: u32 = 15 * 1024; + let mut ret = ERROR_SUCCESS; + + // https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#examples + // Try to retrieve adapter information up to 3 times + for _ in 0..3 { + let buf = libc::malloc(size as _) as PIP_ADAPTER_ADDRESSES; + // free memory on drop + let iterator = InterfaceAddressIterator { buf, adapter: buf }; + if buf.is_null() { + // insufficient memory available + // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/malloc?view=msvc-170#return-value + // malloc is not documented to set the last-error code + return Err("failed to allocate memory for IP_ADAPTER_ADDRESSES".to_string()); + } + + ret = GetAdaptersAddresses( + AF_UNSPEC as u32, + GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER, + null_mut(), + buf, + &mut size, + ); + if ret == ERROR_SUCCESS { + return Ok(iterator); + } else if ret != ERROR_BUFFER_OVERFLOW { + break; + } + // if the given memory size is too small to hold the adapter information, + // the SizePointer returned will point to the required size of the buffer, + // and we should continue. + // Otherwise, break the loop and check the return code again + } + + Err(format!("GetAdaptersAddresses() failed with code {ret}")) + } +} diff --git a/vendor/sysinfo/src/serde.rs b/vendor/sysinfo/src/serde.rs new file mode 100644 index 000000000..cf4d24986 --- /dev/null +++ b/vendor/sysinfo/src/serde.rs @@ -0,0 +1,346 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::common::PidExt; +use crate::{ + ComponentExt, CpuExt, DiskExt, DiskKind, DiskUsage, MacAddr, NetworkExt, NetworksExt, + ProcessExt, ProcessStatus, Signal, SystemExt, UserExt, +}; +use serde::{ser::SerializeStruct, Serialize, Serializer}; +use std::ops::Deref; + +impl Serialize for crate::Disk { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Disk", 7)?; + + state.serialize_field("DiskKind", &self.kind())?; + if let Some(s) = self.name().to_str() { + state.serialize_field("name", s)?; + } + state.serialize_field("file_system", &self.file_system())?; + state.serialize_field("mount_point", &self.mount_point())?; + state.serialize_field("total_space", &self.total_space())?; + state.serialize_field("available_space", &self.available_space())?; + state.serialize_field("is_removable", &self.is_removable())?; + + state.end() + } +} + +impl Serialize for crate::Process { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Process", 18)?; + + state.serialize_field("name", &self.name())?; + state.serialize_field("cmd", &self.cmd())?; + state.serialize_field("exe", &self.exe())?; + state.serialize_field("pid", &self.pid().as_u32())?; + state.serialize_field("environ", &self.environ())?; + state.serialize_field("cwd", &self.cwd())?; + state.serialize_field("root", &self.root())?; + state.serialize_field("memory", &self.memory())?; + state.serialize_field("virtual_memory", &self.virtual_memory())?; + if let Some(pid) = self.parent() { + state.serialize_field("parent", &pid.as_u32())?; + } + state.serialize_field("status", &self.status())?; + state.serialize_field("start_time", &self.start_time())?; + state.serialize_field("run_time", &self.run_time())?; + state.serialize_field("cpu_usage", &self.cpu_usage())?; + state.serialize_field("disk_usage", &self.disk_usage())?; + if let Some(uid) = self.user_id() { + state.serialize_field("user_id", &uid.to_string())?; + } + if let Some(gid) = self.group_id() { + let gid = *gid.deref(); + state.serialize_field("group_id", &gid)?; + } + if let Some(pid) = self.session_id() { + state.serialize_field("session_id", &pid.as_u32())?; + } + + state.end() + } +} + +impl Serialize for crate::Cpu { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Cpu", 1)?; + + state.serialize_field("cpu_usage", &self.cpu_usage())?; + state.serialize_field("name", &self.name())?; + state.serialize_field("vendor_id", &self.vendor_id())?; + state.serialize_field("brand", &self.brand())?; + state.serialize_field("frequency", &self.frequency())?; + + state.end() + } +} + +impl serde::Serialize for crate::System { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("System", 27)?; + + state.serialize_field("IS_SUPPORTED", &<Self as SystemExt>::IS_SUPPORTED)?; + state.serialize_field("SUPPORTED_SIGNALS", <Self as SystemExt>::SUPPORTED_SIGNALS)?; + state.serialize_field( + "MINIMUM_CPU_UPDATE_INTERVAL", + &<Self as SystemExt>::MINIMUM_CPU_UPDATE_INTERVAL, + )?; + + state.serialize_field("global_cpu_info", &self.global_cpu_info())?; + state.serialize_field("cpus", &self.cpus())?; + + state.serialize_field("physical_core_count", &self.physical_core_count())?; + state.serialize_field("total_memory", &self.total_memory())?; + state.serialize_field("free_memory", &self.free_memory())?; + state.serialize_field("available_memory", &self.available_memory())?; + state.serialize_field("used_memory", &self.used_memory())?; + state.serialize_field("total_swap", &self.total_swap())?; + state.serialize_field("free_swap", &self.free_swap())?; + state.serialize_field("used_swap", &self.used_swap())?; + + state.serialize_field("components", &self.components())?; + state.serialize_field("users", &self.users())?; + state.serialize_field("disks", &self.disks())?; + state.serialize_field("networks", &self.networks())?; + + state.serialize_field("uptime", &self.uptime())?; + state.serialize_field("boot_time", &self.boot_time())?; + state.serialize_field("load_average", &self.load_average())?; + state.serialize_field("name", &self.name())?; + state.serialize_field("kernel_version", &self.kernel_version())?; + state.serialize_field("os_version", &self.os_version())?; + state.serialize_field("long_os_version", &self.long_os_version())?; + state.serialize_field("distribution_id", &self.distribution_id())?; + state.serialize_field("host_name", &self.host_name())?; + + state.end() + } +} + +impl Serialize for crate::Networks { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + serializer.collect_seq(self.iter()) + } +} + +impl Serialize for Signal { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let (index, variant) = match *self { + Signal::Hangup => (0, "Hangup"), + Signal::Interrupt => (1, "Interrupt"), + Signal::Quit => (2, "Quit"), + Signal::Illegal => (3, "Illegal"), + Signal::Trap => (4, "Trap"), + Signal::Abort => (5, "Abort"), + Signal::IOT => (6, "IOT"), + Signal::Bus => (7, "Bus"), + Signal::FloatingPointException => (8, "FloatingPointException"), + Signal::Kill => (9, "Kill"), + Signal::User1 => (10, "User1"), + Signal::Segv => (11, "Segv"), + Signal::User2 => (12, "User2"), + Signal::Pipe => (13, "Pipe"), + Signal::Alarm => (14, "Alarm"), + Signal::Term => (15, "Term"), + Signal::Child => (16, "Child"), + Signal::Continue => (17, "Continue"), + Signal::Stop => (18, "Stop"), + Signal::TSTP => (19, "TSTP"), + Signal::TTIN => (20, "TTIN"), + Signal::TTOU => (21, "TTOU"), + Signal::Urgent => (22, "Urgent"), + Signal::XCPU => (23, "XCPU"), + Signal::XFSZ => (24, "XFSZ"), + Signal::VirtualAlarm => (25, "VirtualAlarm"), + Signal::Profiling => (26, "Profiling"), + Signal::Winch => (27, "Winch"), + Signal::IO => (28, "IO"), + Signal::Poll => (29, "Poll"), + Signal::Power => (30, "Power"), + Signal::Sys => (31, "Sys"), + }; + + serializer.serialize_unit_variant("Signal", index, variant) + } +} + +impl Serialize for crate::LoadAvg { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut state = serializer.serialize_struct("LoadAvg", 1)?; + + state.serialize_field("one", &self.one)?; + state.serialize_field("five", &self.five)?; + state.serialize_field("fifteen", &self.fifteen)?; + state.end() + } +} + +// impl Serialize for crate::NetworkData { +// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> +// where +// S: Serializer, +// { +// let mut state = serializer.serialize_struct("NetworkData", 1)?; +// } +// } + +impl Serialize for crate::NetworkData { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut state = serializer.serialize_struct("NetworkData", 1)?; + + state.serialize_field("received", &self.received())?; + state.serialize_field("total_received", &self.total_received())?; + state.serialize_field("transmitted", &self.transmitted())?; + state.serialize_field("total_transmitted", &self.total_transmitted())?; + state.serialize_field("packets_received", &self.packets_received())?; + state.serialize_field("total_packets_received", &self.total_packets_received())?; + state.serialize_field("packets_transmitted", &self.packets_transmitted())?; + state.serialize_field( + "total_packets_transmitted", + &self.total_packets_transmitted(), + )?; + state.serialize_field("errors_on_received", &self.errors_on_received())?; + state.serialize_field("total_errors_on_received", &self.total_errors_on_received())?; + state.serialize_field("errors_on_transmitted", &self.errors_on_transmitted())?; + state.serialize_field( + "total_errors_on_transmitted", + &self.total_errors_on_transmitted(), + )?; + state.serialize_field("mac_address", &self.mac_address())?; + + state.end() + } +} + +impl Serialize for crate::Component { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Component", 4)?; + + state.serialize_field("temperature", &self.temperature())?; + state.serialize_field("max", &self.max())?; + state.serialize_field("critical", &self.critical())?; + state.serialize_field("label", &self.label())?; + + state.end() + } +} + +impl Serialize for crate::User { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut state = serializer.serialize_struct("User", 1)?; + + state.serialize_field("id", &self.id().to_string())?; + + let gid = *self.group_id().deref(); + state.serialize_field("group_id", &gid)?; + + state.serialize_field("name", &self.name())?; + state.serialize_field("groups", &self.groups())?; + + state.end() + } +} + +impl Serialize for DiskKind { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let (index, variant, maybe_value) = match *self { + DiskKind::HDD => (0, "HDD", None), + DiskKind::SSD => (1, "SSD", None), + DiskKind::Unknown(ref s) => (2, "Unknown", Some(s)), + }; + + if let Some(ref value) = maybe_value { + serializer.serialize_newtype_variant("DiskKind", index, variant, value) + } else { + serializer.serialize_unit_variant("DiskKind", index, variant) + } + } +} + +impl Serialize for ProcessStatus { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let (index, variant, maybe_value) = match *self { + ProcessStatus::Idle => (0, "Idle", None), + ProcessStatus::Run => (1, "Run", None), + ProcessStatus::Sleep => (2, "Sleep", None), + ProcessStatus::Stop => (3, "Stop", None), + ProcessStatus::Zombie => (4, "Zombie", None), + ProcessStatus::Tracing => (5, "Tracing", None), + ProcessStatus::Dead => (6, "Dead", None), + ProcessStatus::Wakekill => (7, "Wakekill", None), + ProcessStatus::Waking => (8, "Waking", None), + ProcessStatus::Parked => (9, "Parked", None), + ProcessStatus::LockBlocked => (10, "LockBlocked", None), + ProcessStatus::UninterruptibleDiskSleep => (11, "UninterruptibleDiskSleep", None), + ProcessStatus::Unknown(n) => (12, "Unknown", Some(n)), + }; + + if let Some(ref value) = maybe_value { + serializer.serialize_newtype_variant("ProcessStatus", index, variant, value) + } else { + serializer.serialize_unit_variant("ProcessStatus", index, variant) + } + } +} + +impl Serialize for DiskUsage { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut state = serializer.serialize_struct("DiskUsage", 4)?; + + state.serialize_field("total_written_bytes", &self.total_written_bytes)?; + state.serialize_field("written_bytes", &self.written_bytes)?; + state.serialize_field("total_read_bytes", &self.total_read_bytes)?; + state.serialize_field("read_bytes", &self.read_bytes)?; + + state.end() + } +} + +impl Serialize for MacAddr { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + serializer.serialize_newtype_struct("MacAddr", &self.0) + } +} diff --git a/vendor/sysinfo/src/sysinfo.h b/vendor/sysinfo/src/sysinfo.h index 13c039128..1b85b2f32 100644 --- a/vendor/sysinfo/src/sysinfo.h +++ b/vendor/sysinfo/src/sysinfo.h @@ -11,7 +11,11 @@ typedef const char* RString; CSystem *sysinfo_init(); void sysinfo_destroy(CSystem system); + void sysinfo_refresh_system(CSystem system); +void sysinfo_refresh_memory(CSystem system); +void sysinfo_refresh_cpu(CSystem system); +void sysinfo_refresh_components(CSystem system); void sysinfo_refresh_all(CSystem system); void sysinfo_refresh_processes(CSystem system); #ifdef __linux__ @@ -19,27 +23,33 @@ void sysinfo_refresh_process(CSystem system, pid_t pid); #endif void sysinfo_refresh_disks(CSystem system); void sysinfo_refresh_disk_list(CSystem system); -size_t sysinfo_get_total_memory(CSystem system); -size_t sysinfo_get_free_memory(CSystem system); -size_t sysinfo_get_used_memory(CSystem system); -size_t sysinfo_get_total_swap(CSystem system); -size_t sysinfo_get_free_swap(CSystem system); -size_t sysinfo_get_used_swap(CSystem system); -size_t sysinfo_get_network_income(CSystem system); -size_t sysinfo_get_network_outcome(CSystem system); -void sysinfo_get_cpus_usage(CSystem system, unsigned int *length, float **cpus); -size_t sysinfo_get_processes(CSystem system, bool (*fn_pointer)(pid_t, CProcess, void*), - void *data); + +size_t sysinfo_total_memory(CSystem system); +size_t sysinfo_free_memory(CSystem system); +size_t sysinfo_used_memory(CSystem system); +size_t sysinfo_total_swap(CSystem system); +size_t sysinfo_free_swap(CSystem system); +size_t sysinfo_used_swap(CSystem system); + +size_t sysinfo_networks_received(CSystem system); +size_t sysinfo_networks_transmitted(CSystem system); + +void sysinfo_cpus_usage(CSystem system, unsigned int *length, float **cpus); + +size_t sysinfo_processes(CSystem system, bool (*fn_pointer)(pid_t, CProcess, void*), + void *data); #ifdef __linux__ -size_t sysinfo_process_get_tasks(CProcess process, bool (*fn_pointer)(pid_t, CProcess, void*), - void *data); +size_t sysinfo_process_tasks(CProcess process, bool (*fn_pointer)(pid_t, CProcess, void*), + void *data); #endif -CProcess sysinfo_get_process_by_pid(CSystem system, pid_t pid); -pid_t sysinfo_process_get_pid(CProcess process); -pid_t sysinfo_process_get_parent_pid(CProcess process); -float sysinfo_process_get_cpu_usage(CProcess process); -size_t sysinfo_process_get_memory(CProcess process); -RString sysinfo_process_get_executable_path(CProcess process); -RString sysinfo_process_get_root_directory(CProcess process); -RString sysinfo_process_get_current_directory(CProcess process); +CProcess sysinfo_process_by_pid(CSystem system, pid_t pid); +pid_t sysinfo_process_pid(CProcess process); +pid_t sysinfo_process_parent_pid(CProcess process); +float sysinfo_process_cpu_usage(CProcess process); +size_t sysinfo_process_memory(CProcess process); +size_t sysinfo_process_virtual_memory(CProcess process); +RString sysinfo_process_executable_path(CProcess process); +RString sysinfo_process_root_directory(CProcess process); +RString sysinfo_process_current_directory(CProcess process); + void sysinfo_rstring_free(RString str); diff --git a/vendor/sysinfo/src/system.rs b/vendor/sysinfo/src/system.rs index 63a70651f..1c4254e23 100644 --- a/vendor/sysinfo/src/system.rs +++ b/vendor/sysinfo/src/system.rs @@ -114,4 +114,69 @@ mod tests { assert!(uptime < new_uptime); } } + + // This test is used to ensure that the CPU usage computation isn't completely going off + // when refreshing it too frequently (ie, multiple times in a row in a very small interval). + #[test] + #[ignore] // This test MUST be run on its own to prevent wrong CPU usage measurements. + fn test_consecutive_cpu_usage_update() { + use crate::{PidExt, ProcessExt, ProcessRefreshKind, System, SystemExt}; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; + use std::time::Duration; + + if !System::IS_SUPPORTED { + return; + } + + let mut sys = System::new_all(); + assert!(!sys.cpus().is_empty()); + sys.refresh_processes_specifics(ProcessRefreshKind::new().with_cpu()); + + let stop = Arc::new(AtomicBool::new(false)); + // Spawning a few threads to ensure that it will actually have an impact on the CPU usage. + for it in 0..sys.cpus().len() / 2 + 1 { + let stop_c = Arc::clone(&stop); + std::thread::spawn(move || { + while !stop_c.load(Ordering::Relaxed) { + if it != 0 { + // The first thread runs at 100% to be sure it'll be noticeable. + std::thread::sleep(Duration::from_millis(1)); + } + } + }); + } + + let mut pids = sys + .processes() + .iter() + .map(|(pid, _)| *pid) + .take(2) + .collect::<Vec<_>>(); + let pid = std::process::id(); + pids.push(PidExt::from_u32(pid)); + assert_eq!(pids.len(), 3); + + for it in 0..3 { + std::thread::sleep( + crate::System::MINIMUM_CPU_UPDATE_INTERVAL + Duration::from_millis(1), + ); + for pid in &pids { + sys.refresh_process_specifics(*pid, ProcessRefreshKind::new().with_cpu()); + } + // To ensure that Linux doesn't give too high numbers. + assert!( + sys.process(pids[2]).unwrap().cpu_usage() < sys.cpus().len() as f32 * 100., + "using ALL CPU: failed at iteration {}", + it + ); + // To ensure it's not 0 either. + assert!( + sys.process(pids[2]).unwrap().cpu_usage() > 0., + "using NO CPU: failed at iteration {}", + it + ); + } + stop.store(false, Ordering::Relaxed); + } } diff --git a/vendor/sysinfo/src/traits.rs b/vendor/sysinfo/src/traits.rs index 7b442990c..314e61b50 100644 --- a/vendor/sysinfo/src/traits.rs +++ b/vendor/sysinfo/src/traits.rs @@ -1,11 +1,11 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::{ - common::{Gid, Uid}, + common::{Gid, MacAddr, Uid}, sys::{Component, Cpu, Disk, Networks, Process}, }; use crate::{ - CpuRefreshKind, DiskType, DiskUsage, LoadAvg, NetworksIter, Pid, ProcessRefreshKind, + CpuRefreshKind, DiskKind, DiskUsage, LoadAvg, NetworksIter, Pid, ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, User, }; @@ -13,6 +13,7 @@ use std::collections::HashMap; use std::ffi::OsStr; use std::fmt::Debug; use std::path::Path; +use std::time::Duration; /// Contains all the methods of the [`Disk`][crate::Disk] struct. /// @@ -21,21 +22,21 @@ use std::path::Path; /// /// let s = System::new(); /// for disk in s.disks() { -/// println!("{:?}: {:?}", disk.name(), disk.type_()); +/// println!("{:?}: {:?}", disk.name(), disk.kind()); /// } /// ``` pub trait DiskExt: Debug { - /// Returns the disk type. + /// Returns the kind of disk. /// /// ```no_run /// use sysinfo::{DiskExt, System, SystemExt}; /// /// let s = System::new(); /// for disk in s.disks() { - /// println!("{:?}", disk.type_()); + /// println!("{:?}", disk.kind()); /// } /// ``` - fn type_(&self) -> DiskType; + fn kind(&self) -> DiskKind; /// Returns the disk name. /// @@ -169,7 +170,7 @@ pub trait ProcessExt: Debug { /// /// **⚠️ Important ⚠️** /// - /// On **linux**, there are two things to know about processes' name: + /// On **Linux**, there are two things to know about processes' name: /// 1. It is limited to 15 characters. /// 2. It is not always the exe name. /// @@ -222,7 +223,7 @@ pub trait ProcessExt: Debug { /// freely, making this an untrustworthy source of information. fn exe(&self) -> &Path; - /// Returns the pid of the process. + /// Returns the PID of the process. /// /// ```no_run /// use sysinfo::{Pid, ProcessExt, System, SystemExt}; @@ -294,7 +295,7 @@ pub trait ProcessExt: Debug { /// ``` fn virtual_memory(&self) -> u64; - /// Returns the parent pid. + /// Returns the parent PID. /// /// ```no_run /// use sysinfo::{Pid, ProcessExt, System, SystemExt}; @@ -306,7 +307,7 @@ pub trait ProcessExt: Debug { /// ``` fn parent(&self) -> Option<Pid>; - /// Returns the status of the processus. + /// Returns the status of the process. /// /// ```no_run /// use sysinfo::{Pid, ProcessExt, System, SystemExt}; @@ -343,13 +344,17 @@ pub trait ProcessExt: Debug { fn run_time(&self) -> u64; /// Returns the total CPU usage (in %). Notice that it might be bigger than 100 if run on a - /// multicore machine. + /// multi-core machine. /// - /// If you want a value between 0% and 100%, divide the returned value by the number of CPU - /// CPUs. + /// If you want a value between 0% and 100%, divide the returned value by the number of CPUs. /// - /// **Warning**: If you want accurate CPU usage number, better leave a bit of time - /// between two calls of this method (200 ms for example). + /// ⚠️ To start to have accurate CPU usage, a process needs to be refreshed **twice** because + /// CPU usage computation is based on time diff (process time on a given time period divided by + /// total system time on the same time period). + /// + /// ⚠️ If you want accurate CPU usage number, better leave a bit of time + /// between two calls of this method (take a look at + /// [`SystemExt::MINIMUM_CPU_UPDATE_INTERVAL`] for more information). /// /// ```no_run /// use sysinfo::{Pid, ProcessExt, System, SystemExt}; @@ -398,6 +403,26 @@ pub trait ProcessExt: Debug { /// ``` fn user_id(&self) -> Option<&Uid>; + /// Returns the user ID of the effective owner of this process or `None` if this information + /// couldn't be retrieved. If you want to get the [`User`] from it, take a look at + /// [`SystemExt::get_user_by_id`]. + /// + /// If you run something with `sudo`, the real user ID of the launched process will be the ID of + /// the user you are logged in as but effective user ID will be `0` (i-e root). + /// + /// ⚠️ It always returns `None` on Windows. + /// + /// ```no_run + /// use sysinfo::{Pid, ProcessExt, System, SystemExt}; + /// + /// let mut s = System::new_all(); + /// + /// if let Some(process) = s.process(Pid::from(1337)) { + /// eprintln!("User id for process 1337: {:?}", process.effective_user_id()); + /// } + /// ``` + fn effective_user_id(&self) -> Option<&Uid>; + /// Returns the process group ID of the process. /// /// ⚠️ It always returns `None` on Windows. @@ -408,11 +433,29 @@ pub trait ProcessExt: Debug { /// let mut s = System::new_all(); /// /// if let Some(process) = s.process(Pid::from(1337)) { - /// eprintln!("Group id for process 1337: {:?}", process.group_id()); + /// eprintln!("Group ID for process 1337: {:?}", process.group_id()); /// } /// ``` fn group_id(&self) -> Option<Gid>; + /// Returns the effective group ID of the process. + /// + /// If you run something with `sudo`, the real group ID of the launched process will be the + /// primary group ID you are logged in as but effective group ID will be `0` (i-e root). + /// + /// ⚠️ It always returns `None` on Windows. + /// + /// ```no_run + /// use sysinfo::{Pid, ProcessExt, System, SystemExt}; + /// + /// let mut s = System::new_all(); + /// + /// if let Some(process) = s.process(Pid::from(1337)) { + /// eprintln!("User id for process 1337: {:?}", process.effective_group_id()); + /// } + /// ``` + fn effective_group_id(&self) -> Option<Gid>; + /// Wait for process termination. /// /// ```no_run @@ -427,6 +470,21 @@ pub trait ProcessExt: Debug { /// } /// ``` fn wait(&self); + + /// Returns the session ID for the current process or `None` if it couldn't be retrieved. + /// + /// ⚠️ This information is computed every time this method is called. + /// + /// ```no_run + /// use sysinfo::{Pid, ProcessExt, System, SystemExt}; + /// + /// let mut s = System::new_all(); + /// + /// if let Some(process) = s.process(Pid::from(1337)) { + /// eprintln!("Session ID for process 1337: {:?}", process.session_id()); + /// } + /// ``` + fn session_id(&self) -> Option<Pid>; } /// Contains all the methods of the [`Cpu`][crate::Cpu] struct. @@ -521,9 +579,20 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync { /// ``` const SUPPORTED_SIGNALS: &'static [Signal]; - /// Creates a new [`System`] instance with nothing loaded except the cpus list. If you - /// want to load components, network interfaces or the disks, you'll have to use the - /// `refresh_*_list` methods. [`SystemExt::refresh_networks_list`] for example. + /// This is the minimum interval time used internally by `sysinfo` to refresh the CPU time. + /// + /// ⚠️ This value differs from one OS to another. + /// + /// Why is this constant even needed? + /// + /// If refreshed too often, the CPU usage of processes will be `0` whereas on Linux it'll + /// always be the maximum value (`number of CPUs * 100`). + const MINIMUM_CPU_UPDATE_INTERVAL: Duration; + + /// Creates a new [`System`] instance with nothing loaded. If you want to + /// load components, network interfaces or the disks, you'll have to use the + /// `refresh_*_list` methods. [`SystemExt::refresh_networks_list`] for + /// example. /// /// Use the [`refresh_all`] method to update its internal information (or any of the `refresh_` /// method). @@ -674,7 +743,8 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync { /// /// ⚠️ Please note that the result will very likely be inaccurate at the first call. /// You need to call this method at least twice (with a bit of time between each call, like - /// 200ms) to get accurate values as it uses previous results to compute the next value. + /// 200 ms, take a look at [`SystemExt::MINIMUM_CPU_UPDATE_INTERVAL`] for more information) + /// to get accurate value as it uses previous results to compute the next value. /// /// Calling this method is the same as calling /// `refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage())`. @@ -801,6 +871,16 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync { /// The disk list will be emptied then completely recomputed. /// + /// ## Linux + /// + /// ⚠️ On Linux, the [NFS](https://en.wikipedia.org/wiki/Network_File_System) file + /// systems are ignored and the information of a mounted NFS **cannot** be obtained + /// via [`SystemExt::refresh_disks_list`]. This is due to the fact that I/O function + /// `statvfs` used by [`SystemExt::refresh_disks_list`] is blocking and + /// [may hang](https://github.com/GuillaumeGomez/sysinfo/pull/876) in some cases, + /// requiring to call `systemctl stop` to terminate the NFS service from the remote + /// server in some cases. + /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// @@ -876,7 +956,7 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync { /// ``` fn processes(&self) -> &HashMap<Pid, Process>; - /// Returns the process corresponding to the given pid or `None` if no such process exists. + /// Returns the process corresponding to the given `pid` or `None` if no such process exists. /// /// ```no_run /// use sysinfo::{Pid, ProcessExt, System, SystemExt}; @@ -895,7 +975,7 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync { /// /// **⚠️ Important ⚠️** /// - /// On **linux**, there are two things to know about processes' name: + /// On **Linux**, there are two things to know about processes' name: /// 1. It is limited to 15 characters. /// 2. It is not always the exe name. /// @@ -908,10 +988,10 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync { /// } /// ``` // FIXME: replace the returned type with `impl Iterator<Item = &Process>` when it's supported! - fn processes_by_name<'a>( + fn processes_by_name<'a: 'b, 'b>( &'a self, - name: &'a str, - ) -> Box<dyn Iterator<Item = &'a Process> + 'a> { + name: &'b str, + ) -> Box<dyn Iterator<Item = &'a Process> + 'b> { Box::new( self.processes() .values() @@ -926,7 +1006,7 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync { /// /// **⚠️ Important ⚠️** /// - /// On **linux**, there are two things to know about processes' name: + /// On **Linux**, there are two things to know about processes' name: /// 1. It is limited to 15 characters. /// 2. It is not always the exe name. /// @@ -939,10 +1019,10 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync { /// } /// ``` // FIXME: replace the returned type with `impl Iterator<Item = &Process>` when it's supported! - fn processes_by_exact_name<'a>( + fn processes_by_exact_name<'a: 'b, 'b>( &'a self, - name: &'a str, - ) -> Box<dyn Iterator<Item = &'a Process> + 'a> { + name: &'b str, + ) -> Box<dyn Iterator<Item = &'a Process> + 'b> { Box::new( self.processes() .values() @@ -950,7 +1030,7 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync { ) } - /// Returns "global" cpus information (aka the addition of all the CPUs). + /// Returns "global" CPUs information (aka the addition of all the CPUs). /// /// To have up-to-date information, you need to call [`SystemExt::refresh_cpu`] or /// [`SystemExt::refresh_specifics`] with `cpu` enabled. @@ -967,7 +1047,7 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync { /// Returns the list of the CPUs. /// - /// By default, the list of cpus is empty until you call [`SystemExt::refresh_cpu`] or + /// By default, the list of CPUs is empty until you call [`SystemExt::refresh_cpu`] or /// [`SystemExt::refresh_specifics`] with `cpu` enabled. /// /// ```no_run @@ -1478,6 +1558,19 @@ pub trait NetworkExt: Debug { /// } /// ``` fn total_errors_on_transmitted(&self) -> u64; + + /// Returns the MAC address associated to current interface. + /// + /// ```no_run + /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; + /// + /// let s = System::new_all(); + /// let networks = s.networks(); + /// for (interface_name, network) in networks { + /// println!("MAC address: {}", network.mac_address()); + /// } + /// ``` + fn mac_address(&self) -> MacAddr; } /// Interacting with network interfaces. @@ -1552,7 +1645,7 @@ pub trait ComponentExt: Debug { /// /// ## Linux /// - /// May be computed by sysinfo from kernel. + /// May be computed by `sysinfo` from kernel. /// Returns `f32::NAN` if it failed to retrieve it. fn max(&self) -> f32; @@ -1585,7 +1678,7 @@ pub trait ComponentExt: Debug { /// /// ## Linux /// - /// Since components informations are retrieved thanks to `hwmon`, + /// Since components information is retrieved thanks to `hwmon`, /// the labels are generated as follows. /// Note: it may change and it was inspired by `sensors` own formatting. /// diff --git a/vendor/sysinfo/src/unknown/disk.rs b/vendor/sysinfo/src/unknown/disk.rs index 8956da9f5..0d70dbd15 100644 --- a/vendor/sysinfo/src/unknown/disk.rs +++ b/vendor/sysinfo/src/unknown/disk.rs @@ -1,6 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use crate::{DiskExt, DiskType}; +use crate::{DiskExt, DiskKind}; use std::{ffi::OsStr, path::Path}; @@ -8,7 +8,7 @@ use std::{ffi::OsStr, path::Path}; pub struct Disk {} impl DiskExt for Disk { - fn type_(&self) -> DiskType { + fn kind(&self) -> DiskKind { unreachable!() } diff --git a/vendor/sysinfo/src/unknown/network.rs b/vendor/sysinfo/src/unknown/network.rs index 4a8ff4831..de8459e31 100644 --- a/vendor/sysinfo/src/unknown/network.rs +++ b/vendor/sysinfo/src/unknown/network.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; +use crate::common::MacAddr; use crate::{NetworkExt, NetworksExt, NetworksIter}; #[doc = include_str!("../../md_doc/networks.md")] @@ -78,4 +79,8 @@ impl NetworkExt for NetworkData { fn total_errors_on_transmitted(&self) -> u64 { 0 } + + fn mac_address(&self) -> MacAddr { + MacAddr::UNSPECIFIED + } } diff --git a/vendor/sysinfo/src/unknown/process.rs b/vendor/sysinfo/src/unknown/process.rs index 4a12ec829..4e116d7df 100644 --- a/vendor/sysinfo/src/unknown/process.rs +++ b/vendor/sysinfo/src/unknown/process.rs @@ -86,9 +86,21 @@ impl ProcessExt for Process { None } + fn effective_user_id(&self) -> Option<&Uid> { + None + } + fn group_id(&self) -> Option<Gid> { None } + fn effective_group_id(&self) -> Option<Gid> { + None + } + fn wait(&self) {} + + fn session_id(&self) -> Option<Pid> { + None + } } diff --git a/vendor/sysinfo/src/unknown/system.rs b/vendor/sysinfo/src/unknown/system.rs index c205cdf96..b7b2d47e6 100644 --- a/vendor/sysinfo/src/unknown/system.rs +++ b/vendor/sysinfo/src/unknown/system.rs @@ -6,6 +6,7 @@ use crate::{ }; use std::collections::HashMap; +use std::time::Duration; declare_signals! { (), @@ -22,6 +23,7 @@ pub struct System { impl SystemExt for System { const IS_SUPPORTED: bool = false; const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals(); + const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(0); fn new_with_specifics(_: RefreshKind) -> System { System { diff --git a/vendor/sysinfo/src/users.rs b/vendor/sysinfo/src/users.rs index 1891ec7cc..6d3343bd7 100644 --- a/vendor/sysinfo/src/users.rs +++ b/vendor/sysinfo/src/users.rs @@ -5,14 +5,87 @@ use crate::{ User, }; -use libc::{getgrgid, getgrouplist}; +use libc::{getgrgid_r, getgrouplist}; use std::fs::File; use std::io::Read; -pub fn get_users_list() -> Vec<User> { +pub(crate) unsafe fn get_group_name( + id: libc::gid_t, + buffer: &mut Vec<libc::c_char>, +) -> Option<String> { + let mut g = std::mem::MaybeUninit::<libc::group>::uninit(); + let mut tmp_ptr = std::ptr::null_mut(); + let mut last_errno = 0; + loop { + if retry_eintr!(set_to_0 => last_errno => getgrgid_r( + id as _, + g.as_mut_ptr() as _, + buffer.as_mut_ptr(), + buffer.capacity() as _, + &mut tmp_ptr as _ + )) != 0 + { + // If there was not enough memory, we give it more. + if last_errno == libc::ERANGE as _ { + buffer.reserve(2048); + continue; + } + return None; + } + break; + } + let g = g.assume_init(); + let mut group_name = Vec::new(); + let c_group_name = g.gr_name; + let mut x = 0; + loop { + let c = *c_group_name.offset(x); + if c == 0 { + break; + } + group_name.push(c as u8); + x += 1; + } + String::from_utf8(group_name).ok() +} + +pub(crate) unsafe fn get_user_groups( + name: *const libc::c_char, + group_id: libc::gid_t, + groups: &mut Vec<crate::GroupId>, + buffer: &mut Vec<libc::c_char>, +) -> Vec<String> { + loop { + let mut nb_groups = groups.capacity(); + if getgrouplist( + name, + group_id as _, + groups.as_mut_ptr(), + &mut nb_groups as *mut _ as *mut _, + ) == -1 + { + groups.reserve(256); + continue; + } + groups.set_len(nb_groups as _); + return groups + .iter() + .filter_map(|group_id| crate::users::get_group_name(*group_id as _, buffer)) + .collect(); + } +} + +// Not used by mac. +#[allow(unused)] +pub(crate) fn get_users_list() -> Vec<User> { + #[inline] + fn parse_id(id: &str) -> Option<u32> { + id.parse::<u32>().ok() + } + let mut s = String::new(); - let mut ngroups = 100; - let mut groups = vec![0; ngroups as usize]; + let mut buffer = Vec::with_capacity(2048); + let mut groups = Vec::with_capacity(256); let _ = File::open("/etc/passwd").and_then(|mut f| f.read_to_string(&mut s)); s.lines() @@ -23,66 +96,22 @@ pub fn get_users_list() -> Vec<User> { // Skip the user if the uid cannot be parsed correctly if let Some(uid) = parts.next().and_then(parse_id) { if let Some(group_id) = parts.next().and_then(parse_id) { - if let Some(command) = parts.last() { - if command.is_empty() - || command.ends_with("/false") - || command.ends_with("/nologin") - { - // We don't want "fake" users so in case the user command is "bad", we - // ignore this user. - return None; - } - let mut c_user = username.as_bytes().to_vec(); - c_user.push(0); - loop { - let mut current = ngroups; - - unsafe { - if getgrouplist( - c_user.as_ptr() as *const _, - group_id, - groups.as_mut_ptr(), - &mut current, - ) == -1 - { - if current > ngroups { - groups.resize(current as _, 0); - ngroups = current; - continue; - } - // It really failed, let's move on... - return None; - } - // Let's get all the group names! - return Some(User { - uid: Uid(uid), - gid: Gid(group_id), - name: username.to_owned(), - groups: groups[..current as usize] - .iter() - .filter_map(|id| { - let g = getgrgid(*id as _); - if g.is_null() { - return None; - } - let mut group_name = Vec::new(); - let c_group_name = (*g).gr_name; - let mut x = 0; - loop { - let c = *c_group_name.offset(x); - if c == 0 { - break; - } - group_name.push(c as u8); - x += 1; - } - String::from_utf8(group_name).ok() - }) - .collect(), - }); - } - } - } + let mut c_user = username.as_bytes().to_vec(); + c_user.push(0); + // Let's get all the group names! + return Some(User { + uid: Uid(uid), + gid: Gid(group_id), + name: username.to_owned(), + groups: unsafe { + get_user_groups( + c_user.as_ptr() as *const _, + group_id, + &mut groups, + &mut buffer, + ) + }, + }); } } } @@ -90,8 +119,3 @@ pub fn get_users_list() -> Vec<User> { }) .collect() } - -#[inline] -fn parse_id(id: &str) -> Option<u32> { - id.parse::<u32>().ok() -} diff --git a/vendor/sysinfo/src/utils.rs b/vendor/sysinfo/src/utils.rs index 70d96d9fa..af61a8200 100644 --- a/vendor/sysinfo/src/utils.rs +++ b/vendor/sysinfo/src/utils.rs @@ -1,7 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. -/// Converts the value into a parallel iterator (if the multithread feature is enabled) -/// Uses the rayon::iter::IntoParallelIterator trait +/// Converts the value into a parallel iterator (if the multi-thread feature is enabled). +/// Uses the `rayon::iter::IntoParallelIterator` trait. #[cfg(all( all( any( @@ -23,8 +23,8 @@ where val.into_par_iter() } -/// Converts the value into a sequential iterator (if the multithread feature is disabled) -/// Uses the std::iter::IntoIterator trait +/// Converts the value into a sequential iterator (if the multithread feature is disabled). +/// Uses the `std::iter::IntoIterator` trait. #[cfg(all( all( any( diff --git a/vendor/sysinfo/src/windows/cpu.rs b/vendor/sysinfo/src/windows/cpu.rs index bbaa27ad7..f32ddf832 100644 --- a/vendor/sysinfo/src/windows/cpu.rs +++ b/vendor/sysinfo/src/windows/cpu.rs @@ -30,7 +30,7 @@ use winapi::um::winnt::{ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PVOID, WT_EXECUTEDEFAULT, }; -// This formula comes from linux's include/linux/sched/loadavg.h +// This formula comes from Linux's include/linux/sched/loadavg.h // https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23 #[allow(clippy::excessive_precision)] const LOADAVG_FACTOR_1F: f64 = 0.9200444146293232478931553241; @@ -296,6 +296,8 @@ impl CpusWrapper { for (cpu, frequency) in self.cpus.iter_mut().zip(frequencies) { cpu.set_frequency(frequency); } + self.global + .set_frequency(self.cpus.get(0).map(|cpu| cpu.frequency()).unwrap_or(0)); self.got_cpu_frequency = true; } } diff --git a/vendor/sysinfo/src/windows/disk.rs b/vendor/sysinfo/src/windows/disk.rs index 215fb8c58..fc7b8af01 100644 --- a/vendor/sysinfo/src/windows/disk.rs +++ b/vendor/sysinfo/src/windows/disk.rs @@ -1,6 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use crate::{DiskExt, DiskType}; +use crate::{DiskExt, DiskKind}; use std::ffi::{OsStr, OsString}; use std::mem::size_of; @@ -23,7 +23,7 @@ use winapi::um::winnt::{BOOLEAN, FILE_SHARE_READ, FILE_SHARE_WRITE, HANDLE, ULAR #[doc = include_str!("../../md_doc/disk.md")] pub struct Disk { - type_: DiskType, + type_: DiskKind, name: OsString, file_system: Vec<u8>, mount_point: Vec<u16>, @@ -34,7 +34,7 @@ pub struct Disk { } impl DiskExt for Disk { - fn type_(&self) -> DiskType { + fn kind(&self) -> DiskKind { self.type_ } @@ -231,13 +231,13 @@ pub(crate) unsafe fn get_disks() -> Vec<Disk> { ) == 0 || dw_size != size_of::<DEVICE_SEEK_PENALTY_DESCRIPTOR>() as DWORD { - DiskType::Unknown(-1) + DiskKind::Unknown(-1) } else { let is_ssd = result.IncursSeekPenalty == 0; if is_ssd { - DiskType::SSD + DiskKind::SSD } else { - DiskType::HDD + DiskKind::HDD } }; Some(Disk { diff --git a/vendor/sysinfo/src/windows/mod.rs b/vendor/sysinfo/src/windows/mod.rs index 805e85269..adba16b65 100644 --- a/vendor/sysinfo/src/windows/mod.rs +++ b/vendor/sysinfo/src/windows/mod.rs @@ -5,6 +5,7 @@ mod cpu; mod disk; mod network; mod process; +mod sid; mod system; mod tools; mod users; @@ -15,4 +16,5 @@ pub use self::cpu::Cpu; pub use self::disk::Disk; pub use self::network::{NetworkData, Networks}; pub use self::process::Process; +pub use self::sid::Sid; pub use self::system::System; diff --git a/vendor/sysinfo/src/windows/network.rs b/vendor/sysinfo/src/windows/network.rs index 6a09a0490..9ad551f82 100644 --- a/vendor/sysinfo/src/windows/network.rs +++ b/vendor/sysinfo/src/windows/network.rs @@ -1,5 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. +use crate::common::MacAddr; +use crate::network::refresh_networks_addresses; use crate::{NetworkExt, NetworksExt, NetworksIter}; use std::collections::{hash_map, HashMap}; @@ -100,7 +102,7 @@ impl NetworksExt for Networks { }; match self.interfaces.entry(interface_name) { hash_map::Entry::Occupied(mut e) => { - let mut interface = e.get_mut(); + let interface = e.get_mut(); old_and_new!(interface, current_out, old_out, ptr.OutOctets); old_and_new!(interface, current_in, old_in, ptr.InOctets); old_and_new!( @@ -137,6 +139,7 @@ impl NetworksExt for Networks { old_errors_in: ptr.InErrors, errors_out: ptr.OutErrors, old_errors_out: ptr.OutErrors, + mac_addr: MacAddr::UNSPECIFIED, updated: true, }); } @@ -146,6 +149,8 @@ impl NetworksExt for Networks { } // Remove interfaces which are gone. self.interfaces.retain(|_, d| d.updated); + // Refresh all interfaces' addresses. + refresh_networks_addresses(&mut self.interfaces); } fn refresh(&mut self) { @@ -196,6 +201,7 @@ pub struct NetworkData { errors_out: u64, old_errors_out: u64, updated: bool, + pub(crate) mac_addr: MacAddr, } impl NetworkExt for NetworkData { @@ -246,4 +252,8 @@ impl NetworkExt for NetworkData { fn total_errors_on_transmitted(&self) -> u64 { self.errors_out } + + fn mac_address(&self) -> MacAddr { + self.mac_addr + } } diff --git a/vendor/sysinfo/src/windows/process.rs b/vendor/sysinfo/src/windows/process.rs index 7561c658f..6ec1d4bb0 100644 --- a/vendor/sysinfo/src/windows/process.rs +++ b/vendor/sysinfo/src/windows/process.rs @@ -1,8 +1,10 @@ // 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::to_str; -use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid}; +use crate::windows::Sid; +use crate::{ + DiskUsage, Gid, Pid, PidExt, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid, +}; use std::ffi::OsString; use std::fmt; @@ -39,7 +41,7 @@ use winapi::um::handleapi::CloseHandle; use winapi::um::heapapi::{GetProcessHeap, HeapAlloc, HeapFree}; use winapi::um::memoryapi::{ReadProcessMemory, VirtualQueryEx}; use winapi::um::processthreadsapi::{ - GetProcessTimes, GetSystemTimes, OpenProcess, OpenProcessToken, + GetProcessTimes, GetSystemTimes, OpenProcess, OpenProcessToken, ProcessIdToSessionId, }; use winapi::um::psapi::{ EnumProcessModulesEx, GetModuleBaseNameW, GetModuleFileNameExW, GetProcessMemoryInfo, @@ -141,29 +143,7 @@ unsafe fn get_process_user_id( return None; } - let mut name_use = 0; - let mut name = [0u16; 256]; - let mut domain_name = [0u16; 256]; - let mut size = 256; - - if winapi::um::winbase::LookupAccountSidW( - std::ptr::null_mut(), - (*ptu.0).User.Sid, - name.as_mut_ptr(), - &mut size, - domain_name.as_mut_ptr(), - &mut size, - &mut name_use, - ) == 0 - { - sysinfo_debug!( - "LookupAccountSidW failed: {:?}", - winapi::um::errhandlingapi::GetLastError(), - ); - None - } else { - Some(Uid(to_str(name.as_mut_ptr()).into_boxed_str())) - } + Sid::from_psid((*ptu.0).User.Sid).map(Uid) } struct HandleWrapper(HANDLE); @@ -545,10 +525,18 @@ impl ProcessExt for Process { self.user_id.as_ref() } + fn effective_user_id(&self) -> Option<&Uid> { + None + } + fn group_id(&self) -> Option<Gid> { None } + fn effective_group_id(&self) -> Option<Gid> { + None + } + fn wait(&self) { if let Some(handle) = self.get_handle() { while is_proc_running(handle) { @@ -563,6 +551,17 @@ impl ProcessExt for Process { sysinfo_debug!("can't wait on this process so returning"); } } + + fn session_id(&self) -> Option<Pid> { + unsafe { + let mut out = 0; + if ProcessIdToSessionId(self.pid.as_u32(), &mut out) != 0 { + return Some(Pid(out as _)); + } + sysinfo_debug!("ProcessIdToSessionId failed, error: {:?}", GetLastError()); + None + } + } } #[inline] @@ -583,7 +582,7 @@ unsafe fn get_process_times(handle: HANDLE) -> u64 { #[inline] fn compute_start(process_times: u64) -> u64 { // 11_644_473_600 is the number of seconds between the Windows epoch (1601-01-01) and - // the linux epoch (1970-01-01). + // the Linux epoch (1970-01-01). process_times / 10_000_000 - 11_644_473_600 } @@ -604,7 +603,6 @@ pub(crate) fn get_start_time(handle: HANDLE) -> u64 { } } -#[allow(clippy::uninit_vec)] unsafe fn ph_query_process_variable_size( process_handle: &HandleWrapper, process_information_class: PROCESSINFOCLASS, @@ -629,8 +627,6 @@ unsafe fn ph_query_process_variable_size( let mut return_length = return_length.assume_init(); let buf_len = (return_length as usize) / 2; let mut buffer: Vec<u16> = Vec::with_capacity(buf_len + 1); - buffer.set_len(buf_len); - status = NtQueryInformationProcess( **process_handle, process_information_class, @@ -641,6 +637,7 @@ unsafe fn ph_query_process_variable_size( if !NT_SUCCESS(status) { return None; } + buffer.set_len(buf_len); buffer.push(0); Some(buffer) } @@ -682,24 +679,33 @@ unsafe fn get_region_size(handle: &HandleWrapper, ptr: LPVOID) -> Result<usize, Ok((meminfo.RegionSize as isize - ptr.offset_from(meminfo.BaseAddress)) as usize) } -#[allow(clippy::uninit_vec)] unsafe fn get_process_data( handle: &HandleWrapper, ptr: LPVOID, size: usize, ) -> Result<Vec<u16>, &'static str> { let mut buffer: Vec<u16> = Vec::with_capacity(size / 2 + 1); - buffer.set_len(size / 2); + let mut bytes_read = 0; + if ReadProcessMemory( **handle, ptr as *mut _, buffer.as_mut_ptr() as *mut _, size, - null_mut(), - ) != TRUE + &mut bytes_read, + ) == FALSE { return Err("Unable to read process data"); } + + // Documentation states that the function fails if not all data is accessible. + if bytes_read != size { + return Err("ReadProcessMemory returned unexpected number of bytes read"); + } + + buffer.set_len(size / 2); + buffer.push(0); + Ok(buffer) } @@ -947,7 +953,7 @@ fn check_sub(a: u64, b: u64) -> u64 { } /// Before changing this function, you must consider the following: -/// https://github.com/GuillaumeGomez/sysinfo/issues/459 +/// <https://github.com/GuillaumeGomez/sysinfo/issues/459> pub(crate) fn compute_cpu_usage(p: &mut Process, nb_cpus: u64) { unsafe { let mut ftime: FILETIME = zeroed(); @@ -966,6 +972,8 @@ pub(crate) fn compute_cpu_usage(p: &mut Process, nb_cpus: u64) { &mut fuser as *mut FILETIME, ); } + // FIXME: should these values be stored in one place to make use of + // `MINIMUM_CPU_UPDATE_INTERVAL`? GetSystemTimes( &mut fglobal_idle_time as *mut FILETIME, &mut fglobal_kernel_time as *mut FILETIME, diff --git a/vendor/sysinfo/src/windows/sid.rs b/vendor/sysinfo/src/windows/sid.rs new file mode 100644 index 000000000..44d94f2f5 --- /dev/null +++ b/vendor/sysinfo/src/windows/sid.rs @@ -0,0 +1,157 @@ +// Take a look at the license at the top of the repository in the LICENSE file.
+
+use std::{fmt::Display, str::FromStr};
+
+use winapi::{
+ shared::{
+ sddl::{ConvertSidToStringSidW, ConvertStringSidToSidW},
+ winerror::ERROR_INSUFFICIENT_BUFFER,
+ },
+ um::{
+ errhandlingapi::GetLastError,
+ securitybaseapi::{CopySid, GetLengthSid, IsValidSid},
+ winbase::{LocalFree, LookupAccountSidW},
+ winnt::{SidTypeUnknown, LPWSTR, PSID},
+ },
+};
+
+use crate::sys::utils::to_str;
+
+#[doc = include_str!("../../md_doc/sid.md")]
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Sid {
+ sid: Vec<u8>,
+}
+
+impl Sid {
+ /// Creates an `Sid` by making a copy of the given raw SID.
+ pub(crate) unsafe fn from_psid(psid: PSID) -> Option<Self> {
+ if psid.is_null() {
+ return None;
+ }
+
+ if IsValidSid(psid) == 0 {
+ return None;
+ }
+
+ let length = GetLengthSid(psid);
+
+ let mut sid = vec![0; length as usize];
+
+ if CopySid(length, sid.as_mut_ptr() as *mut _, psid) == 0 {
+ sysinfo_debug!("CopySid failed: {:?}", GetLastError());
+ return None;
+ }
+
+ // We are making assumptions about the SID internal structure,
+ // and these only hold if the revision is 1
+ // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid
+ // Namely:
+ // 1. SIDs can be compared directly (memcmp).
+ // 2. Following from this, to hash a SID we can just hash its bytes.
+ // These are the basis for deriving PartialEq, Eq, and Hash.
+ // And since we also need PartialOrd and Ord, we might as well derive them
+ // too. The default implementation will be consistent with Eq,
+ // and we don't care about the actual order, just that there is one.
+ // So it should all work out.
+ // Why bother with this? Because it makes the implementation that
+ // much simpler :)
+ assert_eq!(sid[0], 1, "Expected SID revision to be 1");
+
+ Some(Self { sid })
+ }
+
+ /// Retrieves the account name of this SID.
+ pub(crate) fn account_name(&self) -> Option<String> {
+ unsafe {
+ let mut name_len = 0;
+ let mut domain_len = 0;
+ let mut name_use = SidTypeUnknown;
+
+ if LookupAccountSidW(
+ std::ptr::null_mut(),
+ self.sid.as_ptr() as *mut _,
+ std::ptr::null_mut(),
+ &mut name_len,
+ std::ptr::null_mut(),
+ &mut domain_len,
+ &mut name_use,
+ ) == 0
+ {
+ let error = GetLastError();
+ if error != ERROR_INSUFFICIENT_BUFFER {
+ sysinfo_debug!("LookupAccountSidW failed: {:?}", error);
+ return None;
+ }
+ }
+
+ let mut name = vec![0; name_len as usize];
+
+ // Reset length to 0 since we're still passing a NULL pointer
+ // for the domain.
+ domain_len = 0;
+
+ if LookupAccountSidW(
+ std::ptr::null_mut(),
+ self.sid.as_ptr() as *mut _,
+ name.as_mut_ptr(),
+ &mut name_len,
+ std::ptr::null_mut(),
+ &mut domain_len,
+ &mut name_use,
+ ) == 0
+ {
+ sysinfo_debug!("LookupAccountSidW failed: {:?}", GetLastError());
+ return None;
+ }
+
+ Some(to_str(name.as_mut_ptr()))
+ }
+ }
+}
+
+impl Display for Sid {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ unsafe fn convert_sid_to_string_sid(sid: PSID) -> Option<String> {
+ let mut string_sid: LPWSTR = std::ptr::null_mut();
+ if ConvertSidToStringSidW(sid, &mut string_sid) == 0 {
+ sysinfo_debug!("ConvertSidToStringSidW failed: {:?}", GetLastError());
+ return None;
+ }
+ let result = to_str(string_sid);
+ LocalFree(string_sid as *mut _);
+ Some(result)
+ }
+
+ let string_sid = unsafe { convert_sid_to_string_sid(self.sid.as_ptr() as *mut _) };
+ let string_sid = string_sid.ok_or(std::fmt::Error)?;
+
+ write!(f, "{string_sid}")
+ }
+}
+
+impl FromStr for Sid {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ unsafe {
+ let mut string_sid: Vec<u16> = s.encode_utf16().collect();
+ string_sid.push(0);
+
+ let mut psid: PSID = std::ptr::null_mut();
+ if ConvertStringSidToSidW(string_sid.as_ptr(), &mut psid) == 0 {
+ return Err(format!(
+ "ConvertStringSidToSidW failed: {:?}",
+ GetLastError()
+ ));
+ }
+ let sid = Self::from_psid(psid);
+ LocalFree(psid as *mut _);
+
+ // Unwrapping because ConvertStringSidToSidW should've performed
+ // all the necessary validations. If it returned an invalid SID,
+ // we better fail fast.
+ Ok(sid.unwrap())
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/windows/system.rs b/vendor/sysinfo/src/windows/system.rs index 643a7b4bc..4864d2295 100644 --- a/vendor/sysinfo/src/windows/system.rs +++ b/vendor/sysinfo/src/windows/system.rs @@ -12,26 +12,22 @@ use crate::sys::disk::{get_disks, Disk}; use crate::sys::process::{get_start_time, update_memory, Process}; use crate::sys::tools::*; use crate::sys::users::get_users; -use crate::sys::utils::get_now; +use crate::sys::utils::{get_now, get_reg_string_value, get_reg_value_u32}; use crate::utils::into_iter; use std::cell::UnsafeCell; use std::collections::HashMap; -use std::ffi::OsStr; use std::mem::{size_of, zeroed}; -use std::os::windows::ffi::OsStrExt; -use std::slice::from_raw_parts; -use std::time::SystemTime; +use std::time::{Duration, SystemTime}; use ntapi::ntexapi::{ NtQuerySystemInformation, SystemProcessInformation, SYSTEM_PROCESS_INFORMATION, }; use winapi::ctypes::wchar_t; -use winapi::shared::minwindef::{DWORD, FALSE, HKEY, LPBYTE, TRUE}; +use winapi::shared::minwindef::{FALSE, TRUE}; use winapi::shared::ntdef::{PVOID, ULONG}; use winapi::shared::ntstatus::STATUS_INFO_LENGTH_MISMATCH; -use winapi::shared::winerror; use winapi::um::minwinbase::STILL_ACTIVE; use winapi::um::processthreadsapi::GetExitCodeProcess; use winapi::um::psapi::{GetPerformanceInfo, PERFORMANCE_INFORMATION}; @@ -39,8 +35,7 @@ use winapi::um::sysinfoapi::{ ComputerNamePhysicalDnsHostname, GetComputerNameExW, GetTickCount64, GlobalMemoryStatusEx, MEMORYSTATUSEX, }; -use winapi::um::winnt::{HANDLE, KEY_READ}; -use winapi::um::winreg::{RegOpenKeyExW, RegQueryValueExW}; +use winapi::um::winnt::HANDLE; declare_signals! { (), @@ -97,6 +92,7 @@ unsafe fn boot_time() -> u64 { impl SystemExt for System { const IS_SUPPORTED: bool = true; const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals(); + const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(200); #[allow(non_snake_case)] fn new_with_specifics(refreshes: RefreshKind) -> System { @@ -123,14 +119,14 @@ impl SystemExt for System { self.query = Query::new(); if let Some(ref mut query) = self.query { add_english_counter( - r"\Processor(_Total)\% Processor Time".to_string(), + r"\Processor(_Total)\% Idle Time".to_string(), query, get_key_used(self.cpus.global_cpu_mut()), "tot_0".to_owned(), ); for (pos, proc_) in self.cpus.iter_mut(refresh_kind).enumerate() { add_english_counter( - format!(r"\Processor({pos})\% Processor Time"), + format!(r"\Processor({pos})\% Idle Time"), query, get_key_used(proc_), format!("{pos}_0"), @@ -140,28 +136,30 @@ impl SystemExt for System { } if let Some(ref mut query) = self.query { query.refresh(); - let mut used_time = None; + let mut total_idle_time = None; if let Some(ref key_used) = *get_key_used(self.cpus.global_cpu_mut()) { - used_time = Some( + total_idle_time = Some( query .get(&key_used.unique_id) .expect("global_key_idle disappeared"), ); } - if let Some(used_time) = used_time { - self.cpus.global_cpu_mut().set_cpu_usage(used_time); + if let Some(total_idle_time) = total_idle_time { + self.cpus + .global_cpu_mut() + .set_cpu_usage(100.0 - total_idle_time); } for p in self.cpus.iter_mut(refresh_kind) { - let mut used_time = None; + let mut idle_time = None; if let Some(ref key_used) = *get_key_used(p) { - used_time = Some( + idle_time = Some( query .get(&key_used.unique_id) .expect("key_used disappeared"), ); } - if let Some(used_time) = used_time { - p.set_cpu_usage(used_time); + if let Some(idle_time) = idle_time { + p.set_cpu_usage(100.0 - idle_time); } } if refresh_kind.frequency() { @@ -565,84 +563,6 @@ pub(crate) fn get_process_name(process: &SYSTEM_PROCESS_INFORMATION, process_id: } } -fn utf16_str<S: AsRef<OsStr> + ?Sized>(text: &S) -> Vec<u16> { - OsStr::new(text) - .encode_wide() - .chain(Some(0).into_iter()) - .collect::<Vec<_>>() -} - -fn get_reg_string_value(hkey: HKEY, path: &str, field_name: &str) -> Option<String> { - let c_path = utf16_str(path); - let c_field_name = utf16_str(field_name); - - let mut new_hkey: HKEY = std::ptr::null_mut(); - unsafe { - if RegOpenKeyExW(hkey, c_path.as_ptr(), 0, KEY_READ, &mut new_hkey) != 0 { - return None; - } - - let mut buf_len: DWORD = 2048; - let mut buf_type: DWORD = 0; - let mut buf: Vec<u8> = Vec::with_capacity(buf_len as usize); - loop { - match RegQueryValueExW( - new_hkey, - c_field_name.as_ptr(), - std::ptr::null_mut(), - &mut buf_type, - buf.as_mut_ptr() as LPBYTE, - &mut buf_len, - ) as DWORD - { - 0 => break, - winerror::ERROR_MORE_DATA => { - buf.reserve(buf_len as _); - } - _ => return None, - } - } - - buf.set_len(buf_len as _); - - let words = from_raw_parts(buf.as_ptr() as *const u16, buf.len() / 2); - let mut s = String::from_utf16_lossy(words); - while s.ends_with('\u{0}') { - s.pop(); - } - Some(s) - } -} - -fn get_reg_value_u32(hkey: HKEY, path: &str, field_name: &str) -> Option<[u8; 4]> { - let c_path = utf16_str(path); - let c_field_name = utf16_str(field_name); - - let mut new_hkey: HKEY = std::ptr::null_mut(); - unsafe { - if RegOpenKeyExW(hkey, c_path.as_ptr(), 0, KEY_READ, &mut new_hkey) != 0 { - return None; - } - - let mut buf_len: DWORD = 4; - let mut buf_type: DWORD = 0; - let mut buf = [0u8; 4]; - - match RegQueryValueExW( - new_hkey, - c_field_name.as_ptr(), - std::ptr::null_mut(), - &mut buf_type, - buf.as_mut_ptr() as LPBYTE, - &mut buf_len, - ) as DWORD - { - 0 => Some(buf), - _ => None, - } - } -} - fn get_dns_hostname() -> Option<String> { let mut buffer_size = 0; // Running this first to get the buffer size since the DNS name can be longer than MAX_COMPUTERNAME_LENGTH diff --git a/vendor/sysinfo/src/windows/users.rs b/vendor/sysinfo/src/windows/users.rs index 2fef05c3f..fa979e731 100644 --- a/vendor/sysinfo/src/windows/users.rs +++ b/vendor/sysinfo/src/windows/users.rs @@ -3,31 +3,88 @@ use crate::sys::utils::to_str; use crate::{ common::{Gid, Uid}, + windows::sid::Sid, User, }; use std::ptr::null_mut; use winapi::shared::lmcons::{MAX_PREFERRED_LENGTH, NET_API_STATUS}; -use winapi::shared::minwindef::DWORD; +use winapi::shared::minwindef::{DWORD, LPBYTE}; use winapi::shared::ntstatus::STATUS_SUCCESS; use winapi::shared::winerror::ERROR_MORE_DATA; -use winapi::um::lmaccess::{NetUserEnum, NetUserGetLocalGroups}; use winapi::um::lmaccess::{ - FILTER_NORMAL_ACCOUNT, LG_INCLUDE_INDIRECT, LPLOCALGROUP_USERS_INFO_0, USER_INFO_0, + NetUserEnum, NetUserGetInfo, NetUserGetLocalGroups, LOCALGROUP_USERS_INFO_0, USER_INFO_23, }; +use winapi::um::lmaccess::{FILTER_NORMAL_ACCOUNT, LG_INCLUDE_INDIRECT, USER_INFO_0}; use winapi::um::lmapibuf::NetApiBufferFree; use winapi::um::ntlsa::{ LsaEnumerateLogonSessions, LsaFreeReturnBuffer, LsaGetLogonSessionData, - PSECURITY_LOGON_SESSION_DATA, + SECURITY_LOGON_SESSION_DATA, }; -use winapi::um::winnt::{LPWSTR, PLUID}; +use winapi::um::winnt::{LPWSTR, LUID}; -// FIXME: once this is mreged in winapi, it can be removed. +// FIXME: Can be removed once merged in winapi. #[allow(non_upper_case_globals)] const NERR_Success: NET_API_STATUS = 0; +struct NetApiBuffer<T>(*mut T); + +impl<T> Drop for NetApiBuffer<T> { + fn drop(&mut self) { + unsafe { + if !self.0.is_null() { + NetApiBufferFree(self.0 as *mut _); + } + } + } +} + +impl<T> Default for NetApiBuffer<T> { + fn default() -> Self { + Self(null_mut()) + } +} + +impl<T> NetApiBuffer<T> { + pub fn inner_mut(&mut self) -> &mut *mut T { + assert!(self.0.is_null()); + &mut self.0 + } + + pub unsafe fn inner_mut_as_bytes(&mut self) -> &mut LPBYTE { + // https://doc.rust-lang.org/std/mem/fn.transmute.html + // Turning an &mut T into an &mut U: + &mut *(self.inner_mut() as *mut *mut T as *mut LPBYTE) + } +} + +struct LsaBuffer<T>(*mut T); + +impl<T> Drop for LsaBuffer<T> { + fn drop(&mut self) { + unsafe { + if !self.0.is_null() { + LsaFreeReturnBuffer(self.0 as *mut _); + } + } + } +} + +impl<T> Default for LsaBuffer<T> { + fn default() -> Self { + Self(null_mut()) + } +} + +impl<T> LsaBuffer<T> { + pub fn inner_mut(&mut self) -> &mut *mut T { + assert!(self.0.is_null()); + &mut self.0 + } +} + unsafe fn get_groups_for_user(username: LPWSTR) -> Vec<String> { - let mut buf: LPLOCALGROUP_USERS_INFO_0 = null_mut(); + let mut buf: NetApiBuffer<LOCALGROUP_USERS_INFO_0> = Default::default(); let mut nb_entries = 0; let mut total_entries = 0; let mut groups; @@ -37,7 +94,7 @@ unsafe fn get_groups_for_user(username: LPWSTR) -> Vec<String> { username, 0, LG_INCLUDE_INDIRECT, - &mut buf as *mut _ as _, + buf.inner_mut_as_bytes(), MAX_PREFERRED_LENGTH, &mut nb_entries, &mut total_entries, @@ -45,83 +102,62 @@ unsafe fn get_groups_for_user(username: LPWSTR) -> Vec<String> { if status == NERR_Success { groups = Vec::with_capacity(nb_entries as _); - - if !buf.is_null() { - for i in 0..nb_entries { - let tmp = buf.offset(i as _); - if tmp.is_null() { - break; - } - groups.push(to_str((*tmp).lgrui0_name)); - } + if !buf.0.is_null() { + let entries = std::slice::from_raw_parts(buf.0, nb_entries as _); + groups.extend(entries.iter().map(|entry| to_str(entry.lgrui0_name))); } } else { groups = Vec::new(); sysinfo_debug!("NetUserGetLocalGroups failed with ret code {}", status); } - if !buf.is_null() { - NetApiBufferFree(buf as *mut _); - } groups } -// FIXME: For now, the Uid is the user name, which is quite bad. Normally, there is `PSID` for -// that. But when getting the `PSID` from the processes, it doesn't match the ones we have for -// the users (`EqualSid`). Anyway, until I have time and motivation to fix this. It'll remain -// like that... pub unsafe fn get_users() -> Vec<User> { let mut users = Vec::new(); - let mut buffer: *mut USER_INFO_0 = null_mut(); - let mut nb_read = 0; - let mut total = 0; - let mut resume_handle: DWORD = 0; + let mut resume_handle: DWORD = 0; loop { + let mut buffer: NetApiBuffer<USER_INFO_0> = Default::default(); + let mut nb_read = 0; + let mut total = 0; let status = NetUserEnum( null_mut(), 0, FILTER_NORMAL_ACCOUNT, - &mut buffer as *mut _ as *mut _, + buffer.inner_mut_as_bytes(), MAX_PREFERRED_LENGTH, &mut nb_read, &mut total, - &mut resume_handle as *mut _ as *mut _, + &mut resume_handle, ); if status == NERR_Success || status == ERROR_MORE_DATA { - let entries: &[USER_INFO_0] = std::slice::from_raw_parts(buffer, nb_read as _); + let entries = std::slice::from_raw_parts(buffer.0, nb_read as _); for entry in entries { if entry.usri0_name.is_null() { continue; } - // let mut user: *mut USER_INFO_23 = null_mut(); - - // if NetUserGetInfo( - // null_mut(), - // entry.usri0_name, - // 23, - // &mut user as *mut _ as *mut _, - // ) == NERR_Success - // { - // let groups = get_groups_for_user((*user).usri23_name); - // users.push(User { - // uid: Uid(name.clone().into_boxed_str()), - // gid: Gid(0), - // name: to_str((*user).usri23_name), - // groups, - // }); - // } - // if !user.is_null() { - // NetApiBufferFree(user as *mut _); - // } - let groups = get_groups_for_user(entry.usri0_name); - let name = to_str(entry.usri0_name); - users.push(User { - uid: Uid(name.clone().into_boxed_str()), - gid: Gid(0), - name, - groups, - }); + + let mut user: NetApiBuffer<USER_INFO_23> = Default::default(); + if NetUserGetInfo(null_mut(), entry.usri0_name, 23, user.inner_mut_as_bytes()) + == NERR_Success + { + if let Some(sid) = Sid::from_psid((*user.0).usri23_user_sid) { + // Get the account name from the SID (because it's usually + // a better name), but fall back to the name we were given + // if this fails. + let name = sid + .account_name() + .unwrap_or_else(|| to_str(entry.usri0_name)); + users.push(User { + uid: Uid(sid), + gid: Gid(0), + name, + groups: get_groups_for_user(entry.usri0_name), + }); + } + } } } else { sysinfo_debug!( @@ -135,10 +171,6 @@ pub unsafe fn get_users() -> Vec<User> { } ); } - if !buffer.is_null() { - NetApiBufferFree(buffer as *mut _); - buffer = null_mut(); - } if status != ERROR_MORE_DATA { break; } @@ -146,34 +178,52 @@ pub unsafe fn get_users() -> Vec<User> { // First part done. Second part now! let mut nb_sessions = 0; - let mut uids: PLUID = null_mut(); - if LsaEnumerateLogonSessions(&mut nb_sessions, &mut uids) != STATUS_SUCCESS { + let mut uids: LsaBuffer<LUID> = Default::default(); + if LsaEnumerateLogonSessions(&mut nb_sessions, uids.inner_mut()) != STATUS_SUCCESS { sysinfo_debug!("LsaEnumerateLogonSessions failed"); } else { - for offset in 0..nb_sessions { - let entry = uids.add(offset as _); - let mut data: PSECURITY_LOGON_SESSION_DATA = null_mut(); - - if LsaGetLogonSessionData(entry, &mut data) == STATUS_SUCCESS && !data.is_null() { - let data = *data; + let entries = std::slice::from_raw_parts_mut(uids.0, nb_sessions as _); + for entry in entries { + let mut data: LsaBuffer<SECURITY_LOGON_SESSION_DATA> = Default::default(); + if LsaGetLogonSessionData(entry, data.inner_mut()) == STATUS_SUCCESS + && !data.0.is_null() + { + let data = *data.0; if data.LogonType == winapi::um::ntlsa::Network { continue; } - let name = to_str(data.UserName.Buffer); - if users.iter().any(|u| u.name == name) { + + let sid = match Sid::from_psid(data.Sid) { + Some(sid) => sid, + None => continue, + }; + + if users.iter().any(|u| u.uid.0 == sid) { continue; } + + // Get the account name from the SID (because it's usually + // a better name), but fall back to the name we were given + // if this fails. + let name = sid.account_name().unwrap_or_else(|| { + String::from_utf16(std::slice::from_raw_parts( + data.UserName.Buffer, + data.UserName.Length as usize / std::mem::size_of::<u16>(), + )) + .unwrap_or_else(|_err| { + sysinfo_debug!("Failed to convert from UTF-16 string: {}", _err); + String::new() + }) + }); + users.push(User { - uid: Uid(name.clone().into_boxed_str()), + uid: Uid(sid), gid: Gid(0), name, // There is no local groups for a non-local user. groups: Vec::new(), }); } - if !data.is_null() { - LsaFreeReturnBuffer(data as *mut _); - } } } diff --git a/vendor/sysinfo/src/windows/utils.rs b/vendor/sysinfo/src/windows/utils.rs index 419ee195c..96132117e 100644 --- a/vendor/sysinfo/src/windows/utils.rs +++ b/vendor/sysinfo/src/windows/utils.rs @@ -1,8 +1,13 @@ // Take a look at the license at the top of the repository in the LICENSE file. use winapi::shared::minwindef::FILETIME; -use winapi::um::winnt::LPWSTR; +use winapi::shared::minwindef::{DWORD, HKEY}; +use winapi::shared::winerror; +use winapi::um::winnt::{KEY_READ, LPWSTR}; +use winapi::um::winreg::{RegCloseKey, RegOpenKeyExW, RegQueryValueExW}; +use std::ffi::OsStr; +use std::os::windows::ffi::OsStrExt; use std::time::SystemTime; #[inline] @@ -34,3 +39,89 @@ pub(crate) unsafe fn to_str(p: LPWSTR) -> String { String::new() }) } + +fn utf16_str<S: AsRef<OsStr> + ?Sized>(text: &S) -> Vec<u16> { + OsStr::new(text) + .encode_wide() + .chain(Some(0).into_iter()) + .collect::<Vec<_>>() +} + +struct RegKey(HKEY); + +impl RegKey { + unsafe fn open(hkey: HKEY, path: &[u16]) -> Option<Self> { + let mut new_hkey: HKEY = std::ptr::null_mut(); + if RegOpenKeyExW(hkey, path.as_ptr(), 0, KEY_READ, &mut new_hkey) != 0 { + return None; + } + Some(Self(new_hkey)) + } + + unsafe fn get_value(&self, field_name: &[u16], buf: &mut [u8], buf_len: &mut DWORD) -> DWORD { + let mut buf_type: DWORD = 0; + + RegQueryValueExW( + self.0, + field_name.as_ptr(), + std::ptr::null_mut(), + &mut buf_type, + buf.as_mut_ptr() as _, + buf_len, + ) as DWORD + } +} + +impl Drop for RegKey { + fn drop(&mut self) { + unsafe { + RegCloseKey(self.0); + } + } +} + +pub(crate) fn get_reg_string_value(hkey: HKEY, path: &str, field_name: &str) -> Option<String> { + let c_path = utf16_str(path); + let c_field_name = utf16_str(field_name); + + unsafe { + let new_key = RegKey::open(hkey, &c_path)?; + let mut buf_len: DWORD = 2048; + let mut buf: Vec<u8> = Vec::with_capacity(buf_len as usize); + + loop { + match new_key.get_value(&c_field_name, &mut buf, &mut buf_len) { + winerror::ERROR_SUCCESS => break, + winerror::ERROR_MORE_DATA => { + buf.reserve(buf_len as _); + } + _ => return None, + } + } + + buf.set_len(buf_len as _); + + let words = std::slice::from_raw_parts(buf.as_ptr() as *const u16, buf.len() / 2); + let mut s = String::from_utf16_lossy(words); + while s.ends_with('\u{0}') { + s.pop(); + } + Some(s) + } +} + +pub(crate) fn get_reg_value_u32(hkey: HKEY, path: &str, field_name: &str) -> Option<[u8; 4]> { + let c_path = utf16_str(path); + let c_field_name = utf16_str(field_name); + + unsafe { + let new_key = RegKey::open(hkey, &c_path)?; + let mut buf_len: DWORD = 4; + let mut buf = [0u8; 4]; + + match new_key.get_value(&c_field_name, &mut buf, &mut buf_len) { + winerror::ERROR_SUCCESS => Some(buf), + _ => None, + } + } +} |