summaryrefslogtreecommitdiffstats
path: root/vendor/sysinfo/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
commitd1b2d29528b7794b41e66fc2136e395a02f8529b (patch)
treea4a17504b260206dec3cf55b2dca82929a348ac2 /vendor/sysinfo/src
parentReleasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-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')
-rw-r--r--vendor/sysinfo/src/apple/app_store/process.rs12
-rw-r--r--vendor/sysinfo/src/apple/cpu.rs20
-rw-r--r--vendor/sysinfo/src/apple/disk.rs10
-rw-r--r--vendor/sysinfo/src/apple/macos/disk.rs10
-rw-r--r--vendor/sysinfo/src/apple/macos/ffi.rs8
-rw-r--r--vendor/sysinfo/src/apple/macos/process.rs57
-rw-r--r--vendor/sysinfo/src/apple/macos/system.rs139
-rw-r--r--vendor/sysinfo/src/apple/network.rs12
-rw-r--r--vendor/sysinfo/src/apple/process.rs58
-rw-r--r--vendor/sysinfo/src/apple/system.rs38
-rw-r--r--vendor/sysinfo/src/apple/users.rs70
-rw-r--r--vendor/sysinfo/src/c_interface.rs12
-rw-r--r--vendor/sysinfo/src/common.rs215
-rw-r--r--vendor/sysinfo/src/debug.rs2
-rw-r--r--vendor/sysinfo/src/freebsd/disk.rs6
-rw-r--r--vendor/sysinfo/src/freebsd/network.rs12
-rw-r--r--vendor/sysinfo/src/freebsd/process.rs25
-rw-r--r--vendor/sysinfo/src/freebsd/system.rs5
-rw-r--r--vendor/sysinfo/src/freebsd/utils.rs4
-rw-r--r--vendor/sysinfo/src/lib.rs61
-rw-r--r--vendor/sysinfo/src/linux/component.rs12
-rw-r--r--vendor/sysinfo/src/linux/cpu.rs474
-rw-r--r--vendor/sysinfo/src/linux/disk.rs24
-rw-r--r--vendor/sysinfo/src/linux/network.rs14
-rw-r--r--vendor/sysinfo/src/linux/process.rs173
-rw-r--r--vendor/sysinfo/src/linux/system.rs58
-rw-r--r--vendor/sysinfo/src/linux/utils.rs4
-rw-r--r--vendor/sysinfo/src/macros.rs33
-rw-r--r--vendor/sysinfo/src/network.rs17
-rw-r--r--vendor/sysinfo/src/network_helper_nix.rs114
-rw-r--r--vendor/sysinfo/src/network_helper_win.rs104
-rw-r--r--vendor/sysinfo/src/serde.rs346
-rw-r--r--vendor/sysinfo/src/sysinfo.h52
-rw-r--r--vendor/sysinfo/src/system.rs65
-rw-r--r--vendor/sysinfo/src/traits.rs159
-rw-r--r--vendor/sysinfo/src/unknown/disk.rs4
-rw-r--r--vendor/sysinfo/src/unknown/network.rs5
-rw-r--r--vendor/sysinfo/src/unknown/process.rs12
-rw-r--r--vendor/sysinfo/src/unknown/system.rs2
-rw-r--r--vendor/sysinfo/src/users.rs162
-rw-r--r--vendor/sysinfo/src/utils.rs8
-rw-r--r--vendor/sysinfo/src/windows/cpu.rs4
-rw-r--r--vendor/sysinfo/src/windows/disk.rs12
-rw-r--r--vendor/sysinfo/src/windows/mod.rs2
-rw-r--r--vendor/sysinfo/src/windows/network.rs12
-rw-r--r--vendor/sysinfo/src/windows/process.rs78
-rw-r--r--vendor/sysinfo/src/windows/sid.rs157
-rw-r--r--vendor/sysinfo/src/windows/system.rs114
-rw-r--r--vendor/sysinfo/src/windows/users.rs204
-rw-r--r--vendor/sysinfo/src/windows/utils.rs93
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,
+ }
+ }
+}