summaryrefslogtreecommitdiffstats
path: root/vendor/sysinfo/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/sysinfo/src
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.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/component.rs26
-rw-r--r--vendor/sysinfo/src/apple/app_store/mod.rs4
-rw-r--r--vendor/sysinfo/src/apple/app_store/process.rs82
-rw-r--r--vendor/sysinfo/src/apple/component.rs3
-rw-r--r--vendor/sysinfo/src/apple/cpu.rs331
-rw-r--r--vendor/sysinfo/src/apple/disk.rs66
-rw-r--r--vendor/sysinfo/src/apple/ffi.rs28
-rw-r--r--vendor/sysinfo/src/apple/ios.rs5
-rw-r--r--vendor/sysinfo/src/apple/macos/component.rs221
-rw-r--r--vendor/sysinfo/src/apple/macos/disk.rs199
-rw-r--r--vendor/sysinfo/src/apple/macos/ffi.rs156
-rw-r--r--vendor/sysinfo/src/apple/macos/mod.rs17
-rw-r--r--vendor/sysinfo/src/apple/macos/process.rs643
-rw-r--r--vendor/sysinfo/src/apple/macos/system.rs136
-rw-r--r--vendor/sysinfo/src/apple/mod.rs31
-rw-r--r--vendor/sysinfo/src/apple/network.rs239
-rw-r--r--vendor/sysinfo/src/apple/process.rs83
-rw-r--r--vendor/sysinfo/src/apple/system.rs763
-rw-r--r--vendor/sysinfo/src/apple/users.rs178
-rw-r--r--vendor/sysinfo/src/apple/utils.rs39
-rw-r--r--vendor/sysinfo/src/c_interface.rs468
-rw-r--r--vendor/sysinfo/src/common.rs965
-rw-r--r--vendor/sysinfo/src/debug.rs132
-rw-r--r--vendor/sysinfo/src/freebsd/component.rs71
-rw-r--r--vendor/sysinfo/src/freebsd/cpu.rs44
-rw-r--r--vendor/sysinfo/src/freebsd/disk.rs143
-rw-r--r--vendor/sysinfo/src/freebsd/mod.rs16
-rw-r--r--vendor/sysinfo/src/freebsd/network.rs199
-rw-r--r--vendor/sysinfo/src/freebsd/process.rs254
-rw-r--r--vendor/sysinfo/src/freebsd/system.rs803
-rw-r--r--vendor/sysinfo/src/freebsd/utils.rs296
-rw-r--r--vendor/sysinfo/src/lib.rs447
-rw-r--r--vendor/sysinfo/src/linux/component.rs189
-rw-r--r--vendor/sysinfo/src/linux/cpu.rs357
-rw-r--r--vendor/sysinfo/src/linux/disk.rs292
-rw-r--r--vendor/sysinfo/src/linux/mod.rs16
-rw-r--r--vendor/sysinfo/src/linux/network.rs314
-rw-r--r--vendor/sysinfo/src/linux/process.rs700
-rw-r--r--vendor/sysinfo/src/linux/system.rs852
-rw-r--r--vendor/sysinfo/src/linux/utils.rs55
-rw-r--r--vendor/sysinfo/src/macros.rs58
-rw-r--r--vendor/sysinfo/src/sysinfo.h45
-rw-r--r--vendor/sysinfo/src/system.rs117
-rw-r--r--vendor/sysinfo/src/traits.rs1582
-rw-r--r--vendor/sysinfo/src/unknown/component.rs26
-rw-r--r--vendor/sysinfo/src/unknown/cpu.rs34
-rw-r--r--vendor/sysinfo/src/unknown/disk.rs42
-rw-r--r--vendor/sysinfo/src/unknown/mod.rs15
-rw-r--r--vendor/sysinfo/src/unknown/network.rs81
-rw-r--r--vendor/sysinfo/src/unknown/process.rs92
-rw-r--r--vendor/sysinfo/src/unknown/system.rs171
-rw-r--r--vendor/sysinfo/src/users.rs97
-rw-r--r--vendor/sysinfo/src/utils.rs61
-rw-r--r--vendor/sysinfo/src/windows/component.rs374
-rw-r--r--vendor/sysinfo/src/windows/cpu.rs548
-rw-r--r--vendor/sysinfo/src/windows/disk.rs249
-rw-r--r--vendor/sysinfo/src/windows/macros.rs16
-rw-r--r--vendor/sysinfo/src/windows/mod.rs20
-rw-r--r--vendor/sysinfo/src/windows/network.rs249
-rw-r--r--vendor/sysinfo/src/windows/process.rs1019
-rw-r--r--vendor/sysinfo/src/windows/system.rs623
-rw-r--r--vendor/sysinfo/src/windows/tools.rs55
-rw-r--r--vendor/sysinfo/src/windows/users.rs181
-rw-r--r--vendor/sysinfo/src/windows/utils.rs36
64 files changed, 15654 insertions, 0 deletions
diff --git a/vendor/sysinfo/src/apple/app_store/component.rs b/vendor/sysinfo/src/apple/app_store/component.rs
new file mode 100644
index 000000000..914fc9406
--- /dev/null
+++ b/vendor/sysinfo/src/apple/app_store/component.rs
@@ -0,0 +1,26 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::ComponentExt;
+
+#[doc = include_str!("../../../md_doc/component.md")]
+pub struct Component {}
+
+impl ComponentExt for Component {
+ fn temperature(&self) -> f32 {
+ 0.0
+ }
+
+ fn max(&self) -> f32 {
+ 0.0
+ }
+
+ fn critical(&self) -> Option<f32> {
+ None
+ }
+
+ fn label(&self) -> &str {
+ ""
+ }
+
+ fn refresh(&mut self) {}
+}
diff --git a/vendor/sysinfo/src/apple/app_store/mod.rs b/vendor/sysinfo/src/apple/app_store/mod.rs
new file mode 100644
index 000000000..0df24cabf
--- /dev/null
+++ b/vendor/sysinfo/src/apple/app_store/mod.rs
@@ -0,0 +1,4 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+pub mod component;
+pub mod process;
diff --git a/vendor/sysinfo/src/apple/app_store/process.rs b/vendor/sysinfo/src/apple/app_store/process.rs
new file mode 100644
index 000000000..8c3348ee9
--- /dev/null
+++ b/vendor/sysinfo/src/apple/app_store/process.rs
@@ -0,0 +1,82 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use std::path::Path;
+
+use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessStatus, Signal, Uid};
+
+#[doc = include_str!("../../../md_doc/process.md")]
+pub struct Process;
+
+impl ProcessExt for Process {
+ fn kill_with(&self, _signal: Signal) -> Option<bool> {
+ None
+ }
+
+ fn name(&self) -> &str {
+ ""
+ }
+
+ fn cmd(&self) -> &[String] {
+ &[]
+ }
+
+ fn exe(&self) -> &Path {
+ Path::new("/")
+ }
+
+ fn pid(&self) -> Pid {
+ Pid(0)
+ }
+
+ fn environ(&self) -> &[String] {
+ &[]
+ }
+
+ fn cwd(&self) -> &Path {
+ Path::new("/")
+ }
+
+ fn root(&self) -> &Path {
+ Path::new("/")
+ }
+
+ fn memory(&self) -> u64 {
+ 0
+ }
+
+ fn virtual_memory(&self) -> u64 {
+ 0
+ }
+
+ fn parent(&self) -> Option<Pid> {
+ None
+ }
+
+ fn status(&self) -> ProcessStatus {
+ ProcessStatus::Unknown(0)
+ }
+
+ fn start_time(&self) -> u64 {
+ 0
+ }
+
+ fn run_time(&self) -> u64 {
+ 0
+ }
+
+ fn cpu_usage(&self) -> f32 {
+ 0.0
+ }
+
+ fn disk_usage(&self) -> DiskUsage {
+ DiskUsage::default()
+ }
+
+ fn user_id(&self) -> Option<&Uid> {
+ None
+ }
+
+ fn group_id(&self) -> Option<Gid> {
+ None
+ }
+}
diff --git a/vendor/sysinfo/src/apple/component.rs b/vendor/sysinfo/src/apple/component.rs
new file mode 100644
index 000000000..7c4196e72
--- /dev/null
+++ b/vendor/sysinfo/src/apple/component.rs
@@ -0,0 +1,3 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+pub use crate::sys::inner::component::*;
diff --git a/vendor/sysinfo/src/apple/cpu.rs b/vendor/sysinfo/src/apple/cpu.rs
new file mode 100644
index 000000000..b1068e971
--- /dev/null
+++ b/vendor/sysinfo/src/apple/cpu.rs
@@ -0,0 +1,331 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::system::get_sys_value;
+
+use crate::{CpuExt, CpuRefreshKind};
+
+use libc::{c_char, host_processor_info, mach_task_self};
+use std::mem;
+use std::ops::Deref;
+use std::sync::Arc;
+
+pub(crate) struct UnsafePtr<T>(*mut T);
+
+unsafe impl<T> Send for UnsafePtr<T> {}
+unsafe impl<T> Sync for UnsafePtr<T> {}
+
+impl<T> Deref for UnsafePtr<T> {
+ type Target = *mut T;
+
+ fn deref(&self) -> &*mut T {
+ &self.0
+ }
+}
+
+pub(crate) struct CpuData {
+ pub cpu_info: UnsafePtr<i32>,
+ pub num_cpu_info: u32,
+}
+
+impl CpuData {
+ pub fn new(cpu_info: *mut i32, num_cpu_info: u32) -> CpuData {
+ CpuData {
+ cpu_info: UnsafePtr(cpu_info),
+ num_cpu_info,
+ }
+ }
+}
+
+impl Drop for CpuData {
+ fn drop(&mut self) {
+ if !self.cpu_info.0.is_null() {
+ let prev_cpu_info_size = std::mem::size_of::<i32>() as u32 * self.num_cpu_info;
+ unsafe {
+ libc::vm_deallocate(
+ mach_task_self(),
+ self.cpu_info.0 as _,
+ prev_cpu_info_size as _,
+ );
+ }
+ self.cpu_info.0 = std::ptr::null_mut();
+ }
+ }
+}
+
+#[doc = include_str!("../../md_doc/cpu.md")]
+pub struct Cpu {
+ name: String,
+ cpu_usage: f32,
+ cpu_data: Arc<CpuData>,
+ frequency: u64,
+ vendor_id: String,
+ brand: String,
+}
+
+impl Cpu {
+ pub(crate) fn new(
+ name: String,
+ cpu_data: Arc<CpuData>,
+ frequency: u64,
+ vendor_id: String,
+ brand: String,
+ ) -> Cpu {
+ Cpu {
+ name,
+ cpu_usage: 0f32,
+ cpu_data,
+ frequency,
+ vendor_id,
+ brand,
+ }
+ }
+
+ pub(crate) fn set_cpu_usage(&mut self, cpu_usage: f32) {
+ self.cpu_usage = cpu_usage;
+ }
+
+ pub(crate) fn update(&mut self, cpu_usage: f32, cpu_data: Arc<CpuData>) {
+ self.cpu_usage = cpu_usage;
+ self.cpu_data = cpu_data;
+ }
+
+ pub(crate) fn data(&self) -> Arc<CpuData> {
+ Arc::clone(&self.cpu_data)
+ }
+
+ pub(crate) fn set_frequency(&mut self, frequency: u64) {
+ self.frequency = frequency;
+ }
+}
+
+impl CpuExt for Cpu {
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ /// Returns the CPU frequency in MHz.
+ fn frequency(&self) -> u64 {
+ self.frequency
+ }
+
+ fn vendor_id(&self) -> &str {
+ &self.vendor_id
+ }
+
+ fn brand(&self) -> &str {
+ &self.brand
+ }
+}
+
+pub(crate) fn get_cpu_frequency() -> u64 {
+ let mut speed: u64 = 0;
+ let mut len = std::mem::size_of::<u64>();
+ unsafe {
+ libc::sysctlbyname(
+ b"hw.cpufrequency\0".as_ptr() as *const c_char,
+ &mut speed as *mut _ as _,
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ );
+ speed / 1_000_000
+ }
+}
+
+#[inline]
+fn get_in_use(cpu_info: *mut i32, offset: isize) -> i32 {
+ 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)
+ }
+}
+
+#[inline]
+fn get_idle(cpu_info: *mut i32, offset: isize) -> i32 {
+ unsafe { *cpu_info.offset(offset + libc::CPU_STATE_IDLE as isize) }
+}
+
+pub(crate) fn compute_usage_of_cpu(proc_: &Cpu, cpu_info: *mut i32, offset: isize) -> f32 {
+ let old_cpu_info = proc_.data().cpu_info.0;
+ let in_use;
+ let total;
+
+ // 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);
+ } 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 as f32 / total as f32 * 100.
+}
+
+pub(crate) fn update_cpu_usage<F: FnOnce(Arc<CpuData>, *mut i32) -> (f32, usize)>(
+ port: libc::mach_port_t,
+ global_cpu: &mut Cpu,
+ f: F,
+) {
+ let mut num_cpu_u = 0u32;
+ let mut cpu_info: *mut i32 = std::ptr::null_mut();
+ let mut num_cpu_info = 0u32;
+
+ let mut total_cpu_usage = 0f32;
+
+ unsafe {
+ if host_processor_info(
+ port,
+ libc::PROCESSOR_CPU_LOAD_INFO,
+ &mut num_cpu_u as *mut u32,
+ &mut cpu_info as *mut *mut i32,
+ &mut num_cpu_info as *mut u32,
+ ) == libc::KERN_SUCCESS
+ {
+ let (total_percentage, len) =
+ f(Arc::new(CpuData::new(cpu_info, num_cpu_info)), cpu_info);
+ total_cpu_usage = total_percentage / len as f32;
+ }
+ global_cpu.set_cpu_usage(total_cpu_usage);
+ }
+}
+
+pub(crate) fn init_cpus(
+ port: libc::mach_port_t,
+ cpus: &mut Vec<Cpu>,
+ global_cpu: &mut Cpu,
+ refresh_kind: CpuRefreshKind,
+) {
+ let mut num_cpu = 0;
+ let mut mib = [0, 0];
+
+ let (vendor_id, brand) = get_vendor_id_and_brand();
+ let frequency = if refresh_kind.frequency() {
+ get_cpu_frequency()
+ } else {
+ 0
+ };
+
+ unsafe {
+ if !get_sys_value(
+ libc::CTL_HW as _,
+ libc::HW_NCPU as _,
+ mem::size_of::<u32>(),
+ &mut num_cpu as *mut _ as *mut _,
+ &mut mib,
+ ) {
+ num_cpu = 1;
+ }
+ }
+ update_cpu_usage(port, global_cpu, |proc_data, cpu_info| {
+ let mut percentage = 0f32;
+ let mut offset = 0;
+ for i in 0..num_cpu {
+ let mut p = Cpu::new(
+ format!("{}", i + 1),
+ Arc::clone(&proc_data),
+ frequency,
+ vendor_id.clone(),
+ brand.clone(),
+ );
+ if refresh_kind.cpu_usage() {
+ let cpu_usage = compute_usage_of_cpu(&p, cpu_info, offset);
+ p.set_cpu_usage(cpu_usage);
+ percentage += p.cpu_usage();
+ }
+ cpus.push(p);
+
+ offset += libc::CPU_STATE_MAX as isize;
+ }
+ (percentage, cpus.len())
+ });
+
+ // We didn't set them above to avoid cloning them unnecessarily.
+ global_cpu.brand = brand;
+ global_cpu.vendor_id = vendor_id;
+ global_cpu.frequency = frequency;
+}
+
+fn get_sysctl_str(s: &[u8]) -> String {
+ let mut len = 0;
+
+ unsafe {
+ libc::sysctlbyname(
+ s.as_ptr() as *const c_char,
+ std::ptr::null_mut(),
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ );
+ if len < 1 {
+ return String::new();
+ }
+
+ let mut buf = Vec::with_capacity(len);
+ libc::sysctlbyname(
+ s.as_ptr() as *const c_char,
+ buf.as_mut_ptr() as _,
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ );
+ if len > 0 {
+ buf.set_len(len);
+ while buf.last() == Some(&b'\0') {
+ buf.pop();
+ }
+ String::from_utf8(buf).unwrap_or_else(|_| String::new())
+ } else {
+ String::new()
+ }
+ }
+}
+
+pub(crate) fn get_vendor_id_and_brand() -> (String, String) {
+ // On apple M1, `sysctl machdep.cpu.vendor` returns "", so fallback to "Apple" if the result
+ // is empty.
+ let mut vendor = get_sysctl_str(b"machdep.cpu.vendor\0");
+ if vendor.is_empty() {
+ vendor = "Apple".to_string();
+ }
+
+ (vendor, get_sysctl_str(b"machdep.cpu.brand_string\0"))
+}
+
+#[cfg(test)]
+mod test {
+ use crate::*;
+ use std::process::Command;
+
+ #[test]
+ fn check_vendor_and_brand() {
+ let child = Command::new("sysctl")
+ .arg("-a")
+ .output()
+ .expect("Failed to start command...");
+
+ assert!(child.status.success());
+ let stdout = String::from_utf8(child.stdout).expect("Not valid UTF8");
+
+ let sys = System::new_with_specifics(
+ crate::RefreshKind::new().with_cpu(CpuRefreshKind::new().with_cpu_usage()),
+ );
+ let cpus = sys.cpus();
+ assert!(!cpus.is_empty(), "no CPU found");
+ if let Some(line) = stdout.lines().find(|l| l.contains("machdep.cpu.vendor")) {
+ let sysctl_value = line.split(":").skip(1).next().unwrap();
+ assert_eq!(cpus[0].vendor_id(), sysctl_value.trim());
+ }
+ if let Some(line) = stdout
+ .lines()
+ .find(|l| l.contains("machdep.cpu.brand_string"))
+ {
+ let sysctl_value = line.split(":").skip(1).next().unwrap();
+ assert_eq!(cpus[0].brand(), sysctl_value.trim());
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/apple/disk.rs b/vendor/sysinfo/src/apple/disk.rs
new file mode 100644
index 000000000..9f0b4a3a3
--- /dev/null
+++ b/vendor/sysinfo/src/apple/disk.rs
@@ -0,0 +1,66 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::utils::to_cpath;
+use crate::{DiskExt, DiskType};
+
+#[cfg(target_os = "macos")]
+pub(crate) use crate::sys::inner::disk::*;
+
+use libc::statfs;
+use std::ffi::{OsStr, OsString};
+use std::mem;
+use std::path::{Path, PathBuf};
+
+#[doc = include_str!("../../md_doc/disk.md")]
+pub struct Disk {
+ pub(crate) type_: DiskType,
+ pub(crate) name: OsString,
+ pub(crate) file_system: Vec<u8>,
+ pub(crate) mount_point: PathBuf,
+ pub(crate) total_space: u64,
+ pub(crate) available_space: u64,
+ pub(crate) is_removable: bool,
+}
+
+impl DiskExt for Disk {
+ fn type_(&self) -> DiskType {
+ self.type_
+ }
+
+ fn name(&self) -> &OsStr {
+ &self.name
+ }
+
+ fn file_system(&self) -> &[u8] {
+ &self.file_system
+ }
+
+ fn mount_point(&self) -> &Path {
+ &self.mount_point
+ }
+
+ fn total_space(&self) -> u64 {
+ self.total_space
+ }
+
+ fn available_space(&self) -> u64 {
+ self.available_space
+ }
+
+ fn is_removable(&self) -> bool {
+ self.is_removable
+ }
+
+ fn refresh(&mut self) -> bool {
+ unsafe {
+ let mut stat: statfs = mem::zeroed();
+ let mount_point_cpath = to_cpath(&self.mount_point);
+ if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 {
+ self.available_space = u64::from(stat.f_bsize).saturating_mul(stat.f_bavail);
+ true
+ } else {
+ false
+ }
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/apple/ffi.rs b/vendor/sysinfo/src/apple/ffi.rs
new file mode 100644
index 000000000..7a8248537
--- /dev/null
+++ b/vendor/sysinfo/src/apple/ffi.rs
@@ -0,0 +1,28 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use libc::c_void;
+
+// Reexport items defined in either macos or ios ffi module.
+pub use crate::sys::inner::ffi::*;
+
+#[repr(C)]
+pub struct __DADisk(c_void);
+#[repr(C)]
+pub struct __DASession(c_void);
+
+// #[allow(non_camel_case_types)]
+// pub type io_name_t = [u8; 128];
+// #[allow(non_camel_case_types)]
+// pub type io_registry_entry_t = io_object_t;
+
+// pub type IOOptionBits = u32;
+
+#[cfg_attr(feature = "debug", derive(Eq, Hash, PartialEq))]
+#[derive(Clone)]
+#[repr(C)]
+pub struct Val_t {
+ pub key: [i8; 5],
+ pub data_size: u32,
+ pub data_type: [i8; 5], // UInt32Char_t
+ pub bytes: [i8; 32], // SMCBytes_t
+}
diff --git a/vendor/sysinfo/src/apple/ios.rs b/vendor/sysinfo/src/apple/ios.rs
new file mode 100644
index 000000000..0393c5ec6
--- /dev/null
+++ b/vendor/sysinfo/src/apple/ios.rs
@@ -0,0 +1,5 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+pub mod ffi {}
+pub use crate::sys::app_store::component;
+pub use crate::sys::app_store::process;
diff --git a/vendor/sysinfo/src/apple/macos/component.rs b/vendor/sysinfo/src/apple/macos/component.rs
new file mode 100644
index 000000000..384efb950
--- /dev/null
+++ b/vendor/sysinfo/src/apple/macos/component.rs
@@ -0,0 +1,221 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::ffi;
+use crate::ComponentExt;
+
+use libc::{c_char, c_int, c_void};
+
+use std::mem;
+
+pub(crate) const COMPONENTS_TEMPERATURE_IDS: &[(&str, &[i8])] = &[
+ ("PECI CPU", &['T' as i8, 'C' as i8, 'X' as i8, 'C' as i8]), // PECI CPU "TCXC"
+ ("PECI CPU", &['T' as i8, 'C' as i8, 'X' as i8, 'c' as i8]), // PECI CPU "TCXc"
+ (
+ "CPU Proximity",
+ &['T' as i8, 'C' as i8, '0' as i8, 'P' as i8],
+ ), // CPU Proximity (heat spreader) "TC0P"
+ ("GPU", &['T' as i8, 'G' as i8, '0' as i8, 'P' as i8]), // GPU "TG0P"
+ ("Battery", &['T' as i8, 'B' as i8, '0' as i8, 'T' as i8]), // Battery "TB0T"
+];
+
+pub(crate) struct ComponentFFI {
+ input_structure: ffi::KeyData_t,
+ val: ffi::Val_t,
+}
+
+impl ComponentFFI {
+ fn new(key: &[i8], con: ffi::io_connect_t) -> Option<ComponentFFI> {
+ unsafe {
+ get_key_size(con, key)
+ .ok()
+ .map(|(input_structure, val)| ComponentFFI {
+ input_structure,
+ val,
+ })
+ }
+ }
+
+ fn temperature(&self, con: ffi::io_connect_t) -> Option<f32> {
+ get_temperature_inner(con, &self.input_structure, &self.val)
+ }
+}
+
+#[doc = include_str!("../../../md_doc/component.md")]
+pub struct Component {
+ temperature: f32,
+ max: f32,
+ critical: Option<f32>,
+ label: String,
+ ffi_part: ComponentFFI,
+ connection: ffi::io_connect_t,
+}
+
+impl Component {
+ /// Creates a new `Component` with the given information.
+ pub(crate) fn new(
+ label: String,
+ max: Option<f32>,
+ critical: Option<f32>,
+ key: &[i8],
+ connection: ffi::io_connect_t,
+ ) -> Option<Component> {
+ let ffi_part = ComponentFFI::new(key, connection)?;
+ ffi_part
+ .temperature(connection)
+ .map(|temperature| Component {
+ temperature,
+ label,
+ max: max.unwrap_or(0.0),
+ critical,
+ ffi_part,
+ connection,
+ })
+ }
+}
+
+impl ComponentExt for Component {
+ fn temperature(&self) -> f32 {
+ self.temperature
+ }
+
+ fn max(&self) -> f32 {
+ self.max
+ }
+
+ fn critical(&self) -> Option<f32> {
+ self.critical
+ }
+
+ fn label(&self) -> &str {
+ &self.label
+ }
+
+ fn refresh(&mut self) {
+ if let Some(temp) = self.ffi_part.temperature(self.connection) {
+ self.temperature = temp;
+ if self.temperature > self.max {
+ self.max = self.temperature;
+ }
+ }
+ }
+}
+
+unsafe fn perform_call(
+ conn: ffi::io_connect_t,
+ index: c_int,
+ input_structure: *const ffi::KeyData_t,
+ output_structure: *mut ffi::KeyData_t,
+) -> i32 {
+ let mut structure_output_size = mem::size_of::<ffi::KeyData_t>();
+
+ ffi::IOConnectCallStructMethod(
+ conn,
+ index as u32,
+ input_structure,
+ mem::size_of::<ffi::KeyData_t>(),
+ output_structure,
+ &mut structure_output_size,
+ )
+}
+
+// Adapted from https://github.com/lavoiesl/osx-cpu-temp/blob/master/smc.c#L28
+#[inline]
+fn strtoul(s: &[i8]) -> u32 {
+ unsafe {
+ ((*s.get_unchecked(0) as u32) << (3u32 << 3))
+ + ((*s.get_unchecked(1) as u32) << (2u32 << 3))
+ + ((*s.get_unchecked(2) as u32) << (1u32 << 3))
+ + (*s.get_unchecked(3) as u32)
+ }
+}
+
+#[inline]
+unsafe fn ultostr(s: *mut c_char, val: u32) {
+ *s.offset(0) = ((val >> 24) % 128) as i8;
+ *s.offset(1) = ((val >> 16) % 128) as i8;
+ *s.offset(2) = ((val >> 8) % 128) as i8;
+ *s.offset(3) = (val % 128) as i8;
+ *s.offset(4) = 0;
+}
+
+unsafe fn get_key_size(
+ con: ffi::io_connect_t,
+ key: &[i8],
+) -> Result<(ffi::KeyData_t, ffi::Val_t), i32> {
+ let mut input_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();
+ let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();
+ let mut val: ffi::Val_t = mem::zeroed::<ffi::Val_t>();
+
+ input_structure.key = strtoul(key);
+ input_structure.data8 = ffi::SMC_CMD_READ_KEYINFO;
+
+ let result = perform_call(
+ con,
+ ffi::KERNEL_INDEX_SMC,
+ &input_structure,
+ &mut output_structure,
+ );
+ if result != ffi::KIO_RETURN_SUCCESS {
+ return Err(result);
+ }
+
+ val.data_size = output_structure.key_info.data_size;
+ ultostr(
+ val.data_type.as_mut_ptr(),
+ output_structure.key_info.data_type,
+ );
+ input_structure.key_info.data_size = val.data_size;
+ input_structure.data8 = ffi::SMC_CMD_READ_BYTES;
+ Ok((input_structure, val))
+}
+
+unsafe fn read_key(
+ con: ffi::io_connect_t,
+ input_structure: &ffi::KeyData_t,
+ mut val: ffi::Val_t,
+) -> Result<ffi::Val_t, i32> {
+ let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();
+
+ match perform_call(
+ con,
+ ffi::KERNEL_INDEX_SMC,
+ input_structure,
+ &mut output_structure,
+ ) {
+ ffi::KIO_RETURN_SUCCESS => {
+ libc::memcpy(
+ val.bytes.as_mut_ptr() as *mut c_void,
+ output_structure.bytes.as_mut_ptr() as *mut c_void,
+ mem::size_of::<[u8; 32]>(),
+ );
+ Ok(val)
+ }
+ result => Err(result),
+ }
+}
+
+fn get_temperature_inner(
+ con: ffi::io_connect_t,
+ input_structure: &ffi::KeyData_t,
+ original_val: &ffi::Val_t,
+) -> Option<f32> {
+ unsafe {
+ if let Ok(val) = read_key(con, input_structure, (*original_val).clone()) {
+ if val.data_size > 0
+ && libc::strcmp(val.data_type.as_ptr(), b"sp78\0".as_ptr() as *const i8) == 0
+ {
+ // convert sp78 value to temperature
+ let x = (i32::from(val.bytes[0]) << 6) + (i32::from(val.bytes[1]) >> 2);
+ return Some(x as f32 / 64f32);
+ }
+ }
+ }
+ None
+}
+
+pub(crate) fn get_temperature(con: ffi::io_connect_t, key: &[i8]) -> Option<f32> {
+ unsafe {
+ let (input_structure, val) = get_key_size(con, key).ok()?;
+ get_temperature_inner(con, &input_structure, &val)
+ }
+}
diff --git a/vendor/sysinfo/src/apple/macos/disk.rs b/vendor/sysinfo/src/apple/macos/disk.rs
new file mode 100644
index 000000000..7cc5a83ce
--- /dev/null
+++ b/vendor/sysinfo/src/apple/macos/disk.rs
@@ -0,0 +1,199 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::{ffi, utils};
+use crate::utils::to_cpath;
+use crate::{Disk, DiskType};
+
+use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull, CFRelease};
+use core_foundation_sys::dictionary::{CFDictionaryGetValueIfPresent, CFDictionaryRef};
+use core_foundation_sys::number::{kCFBooleanTrue, CFBooleanRef};
+use core_foundation_sys::string as cfs;
+
+use libc::{c_char, c_int, c_void, statfs};
+
+use std::ffi::{OsStr, OsString};
+use std::mem;
+use std::os::unix::ffi::OsStrExt;
+use std::path::PathBuf;
+use std::ptr;
+
+fn to_path(mount_path: &[c_char]) -> Option<PathBuf> {
+ let mut tmp = Vec::with_capacity(mount_path.len());
+ for &c in mount_path {
+ if c == 0 {
+ break;
+ }
+ tmp.push(c as u8);
+ }
+ if tmp.is_empty() {
+ None
+ } else {
+ let path = OsStr::from_bytes(&tmp);
+ Some(PathBuf::from(path))
+ }
+}
+
+pub(crate) fn get_disks(session: ffi::DASessionRef) -> Vec<Disk> {
+ if session.is_null() {
+ return Vec::new();
+ }
+ unsafe {
+ let count = libc::getfsstat(ptr::null_mut(), 0, libc::MNT_NOWAIT);
+ if count < 1 {
+ return Vec::new();
+ }
+ let bufsize = count * mem::size_of::<libc::statfs>() as c_int;
+ let mut disks = Vec::with_capacity(count as _);
+ let count = libc::getfsstat(disks.as_mut_ptr(), bufsize, libc::MNT_NOWAIT);
+ if count < 1 {
+ return Vec::new();
+ }
+ disks.set_len(count as _);
+ disks
+ .into_iter()
+ .filter_map(|c_disk| {
+ let mount_point = to_path(&c_disk.f_mntonname)?;
+ let disk = ffi::DADiskCreateFromBSDName(
+ kCFAllocatorDefault as _,
+ session,
+ c_disk.f_mntfromname.as_ptr(),
+ );
+ let dict = ffi::DADiskCopyDescription(disk);
+ if dict.is_null() {
+ return None;
+ }
+ // Keeping this around in case one might want the list of the available
+ // keys in "dict".
+ // core_foundation_sys::base::CFShow(dict as _);
+ let name = match get_str_value(dict, b"DAMediaName\0").map(OsString::from) {
+ Some(n) => n,
+ None => return None,
+ };
+ let removable = get_bool_value(dict, b"DAMediaRemovable\0").unwrap_or(false);
+ let ejectable = get_bool_value(dict, b"DAMediaEjectable\0").unwrap_or(false);
+ // This is very hackish but still better than nothing...
+ let type_ = if let Some(model) = get_str_value(dict, b"DADeviceModel\0") {
+ if model.contains("SSD") {
+ DiskType::SSD
+ } else {
+ // We just assume by default that this is a HDD
+ DiskType::HDD
+ }
+ } else {
+ DiskType::Unknown(-1)
+ };
+
+ CFRelease(dict as _);
+ new_disk(name, mount_point, type_, removable || ejectable)
+ })
+ .collect::<Vec<_>>()
+ }
+}
+
+unsafe fn get_dict_value<T, F: FnOnce(*const c_void) -> Option<T>>(
+ dict: CFDictionaryRef,
+ key: &[u8],
+ callback: F,
+) -> Option<T> {
+ let key = ffi::CFStringCreateWithCStringNoCopy(
+ ptr::null_mut(),
+ key.as_ptr() as *const c_char,
+ cfs::kCFStringEncodingUTF8,
+ kCFAllocatorNull as _,
+ );
+ let mut value = std::ptr::null();
+ let ret = if CFDictionaryGetValueIfPresent(dict, key as _, &mut value) != 0 {
+ callback(value)
+ } else {
+ None
+ };
+ CFRelease(key as _);
+ ret
+}
+
+unsafe fn get_str_value(dict: CFDictionaryRef, key: &[u8]) -> Option<String> {
+ get_dict_value(dict, key, |v| {
+ let v = v as cfs::CFStringRef;
+
+ let len_utf16 = cfs::CFStringGetLength(v);
+ let len_bytes = len_utf16 as usize * 2; // Two bytes per UTF-16 codepoint.
+
+ let v_ptr = cfs::CFStringGetCStringPtr(v, cfs::kCFStringEncodingUTF8);
+ if v_ptr.is_null() {
+ // Fallback on CFStringGetString to read the underlying bytes from the CFString.
+ let mut buf = vec![0; len_bytes];
+ let success = cfs::CFStringGetCString(
+ v,
+ buf.as_mut_ptr(),
+ len_bytes as _,
+ cfs::kCFStringEncodingUTF8,
+ );
+
+ if success != 0 {
+ utils::vec_to_rust(buf)
+ } else {
+ None
+ }
+ } else {
+ utils::cstr_to_rust_with_size(v_ptr, Some(len_bytes))
+ }
+ })
+}
+
+unsafe fn get_bool_value(dict: CFDictionaryRef, key: &[u8]) -> Option<bool> {
+ get_dict_value(dict, key, |v| Some(v as CFBooleanRef == kCFBooleanTrue))
+}
+
+fn new_disk(
+ name: OsString,
+ mount_point: PathBuf,
+ type_: DiskType,
+ is_removable: bool,
+) -> Option<Disk> {
+ let mount_point_cpath = to_cpath(&mount_point);
+ let mut total_space = 0;
+ let mut available_space = 0;
+ let mut file_system = None;
+ unsafe {
+ let mut stat: statfs = mem::zeroed();
+ if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 {
+ // APFS is "special" because its a snapshot-based filesystem, and modern
+ // macOS devices take full advantage of this.
+ //
+ // By default, listing volumes with `statfs` can return both the root-level
+ // "data" partition and any snapshots that exist. However, other than some flags and
+ // reserved(undocumented) bytes, there is no difference between the OS boot snapshot
+ // and the "data" partition.
+ //
+ // To avoid duplicating the number of disks (and therefore available space, etc), only return
+ // a disk (which is really a partition with APFS) if it is the root of the filesystem.
+ let is_root = stat.f_flags & libc::MNT_ROOTFS as u32 == 0;
+ if !is_root {
+ return None;
+ }
+
+ total_space = u64::from(stat.f_bsize).saturating_mul(stat.f_blocks);
+ available_space = u64::from(stat.f_bsize).saturating_mul(stat.f_bavail);
+ let mut vec = Vec::with_capacity(stat.f_fstypename.len());
+ for x in &stat.f_fstypename {
+ if *x == 0 {
+ break;
+ }
+ vec.push(*x as u8);
+ }
+ file_system = Some(vec);
+ }
+ if total_space == 0 {
+ return None;
+ }
+ Some(Disk {
+ type_,
+ name,
+ file_system: file_system.unwrap_or_else(|| b"<Unknown>".to_vec()),
+ mount_point,
+ total_space,
+ available_space,
+ is_removable,
+ })
+ }
+}
diff --git a/vendor/sysinfo/src/apple/macos/ffi.rs b/vendor/sysinfo/src/apple/macos/ffi.rs
new file mode 100644
index 000000000..f884701d9
--- /dev/null
+++ b/vendor/sysinfo/src/apple/macos/ffi.rs
@@ -0,0 +1,156 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use core_foundation_sys::base::CFAllocatorRef;
+use core_foundation_sys::dictionary::CFMutableDictionaryRef;
+use core_foundation_sys::string::{CFStringEncoding, CFStringRef};
+
+use libc::{c_char, c_void};
+#[cfg(not(feature = "apple-sandbox"))]
+use libc::{mach_port_t, size_t};
+
+pub(crate) use crate::sys::ffi::*;
+
+#[cfg(not(feature = "apple-sandbox"))]
+extern "C" {
+ // The proc_* PID functions are internal Apple APIs which are not
+ // allowed in App store releases as Apple blocks any binary using them.
+
+ // IOKit is only available on MacOS: https://developer.apple.com/documentation/iokit, and when not running inside
+ // of the default macOS sandbox.
+ pub fn IOMasterPort(a: i32, b: *mut mach_port_t) -> i32;
+
+ pub fn IOServiceMatching(a: *const c_char) -> *mut c_void;
+
+ pub fn IOServiceGetMatchingServices(
+ a: mach_port_t,
+ b: *mut c_void,
+ c: *mut io_iterator_t,
+ ) -> i32;
+
+ pub fn IOIteratorNext(iterator: io_iterator_t) -> io_object_t;
+
+ pub fn IOObjectRelease(obj: io_object_t) -> i32;
+
+ pub fn IOServiceOpen(device: io_object_t, a: u32, t: u32, x: *mut io_connect_t) -> i32;
+
+ pub fn IOServiceClose(a: io_connect_t) -> i32;
+
+ pub fn IOConnectCallStructMethod(
+ connection: mach_port_t,
+ selector: u32,
+ inputStruct: *const KeyData_t,
+ inputStructCnt: size_t,
+ outputStruct: *mut KeyData_t,
+ outputStructCnt: *mut size_t,
+ ) -> i32;
+ // pub fn IORegistryEntryCreateCFProperties(
+ // entry: io_registry_entry_t,
+ // properties: *mut CFMutableDictionaryRef,
+ // allocator: CFAllocatorRef,
+ // options: IOOptionBits,
+ // ) -> kern_return_t;
+ // pub fn IORegistryEntryGetName(entry: io_registry_entry_t, name: *mut c_char) -> kern_return_t;
+}
+
+extern "C" {
+ pub fn CFStringCreateWithCStringNoCopy(
+ alloc: *mut c_void,
+ cStr: *const c_char,
+ encoding: CFStringEncoding,
+ contentsDeallocator: *mut c_void,
+ ) -> CFStringRef;
+
+ // Disk information functions are non-operational on iOS because of the sandboxing
+ // restrictions of apps, so they don't can't filesystem information. This results in
+ // mountedVolumeURLs and similar returning `nil`. Hence, they are MacOS specific here.
+
+ pub fn DASessionCreate(allocator: CFAllocatorRef) -> DASessionRef;
+
+ // pub fn DADiskCreateFromVolumePath(
+ // allocator: CFAllocatorRef,
+ // session: DASessionRef,
+ // path: CFURLRef,
+ // ) -> DADiskRef;
+ pub fn DADiskCreateFromBSDName(
+ allocator: CFAllocatorRef,
+ session: DASessionRef,
+ path: *const c_char,
+ ) -> DADiskRef;
+ // pub fn DADiskGetBSDName(disk: DADiskRef) -> *const c_char;
+
+ pub fn DADiskCopyDescription(disk: DADiskRef) -> CFMutableDictionaryRef;
+}
+
+pub type DADiskRef = *const __DADisk;
+pub type DASessionRef = *const __DASession;
+
+// We need to wrap `DASessionRef` to be sure `System` remains Send+Sync.
+pub struct SessionWrap(pub DASessionRef);
+
+unsafe impl Send for SessionWrap {}
+unsafe impl Sync for SessionWrap {}
+
+#[cfg(not(feature = "apple-sandbox"))]
+mod io_service {
+ use super::mach_port_t;
+
+ #[allow(non_camel_case_types)]
+ pub type io_object_t = mach_port_t;
+ #[allow(non_camel_case_types)]
+ pub type io_connect_t = io_object_t;
+ #[allow(non_camel_case_types)]
+ pub type io_iterator_t = io_object_t;
+
+ #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))]
+ #[repr(C)]
+ pub struct KeyData_vers_t {
+ pub major: u8,
+ pub minor: u8,
+ pub build: u8,
+ pub reserved: [u8; 1],
+ pub release: u16,
+ }
+
+ #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))]
+ #[repr(C)]
+ pub struct KeyData_pLimitData_t {
+ pub version: u16,
+ pub length: u16,
+ pub cpu_plimit: u32,
+ pub gpu_plimit: u32,
+ pub mem_plimit: u32,
+ }
+
+ #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))]
+ #[repr(C)]
+ pub struct KeyData_keyInfo_t {
+ pub data_size: u32,
+ pub data_type: u32,
+ pub data_attributes: u8,
+ }
+
+ #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))]
+ #[repr(C)]
+ pub struct KeyData_t {
+ pub key: u32,
+ pub vers: KeyData_vers_t,
+ pub p_limit_data: KeyData_pLimitData_t,
+ pub key_info: KeyData_keyInfo_t,
+ pub result: u8,
+ pub status: u8,
+ pub data8: u8,
+ pub data32: u32,
+ pub bytes: [i8; 32], // SMCBytes_t
+ }
+
+ pub const KERNEL_INDEX_SMC: i32 = 2;
+ pub const SMC_CMD_READ_KEYINFO: u8 = 9;
+ pub const SMC_CMD_READ_BYTES: u8 = 5;
+
+ pub const KIO_RETURN_SUCCESS: i32 = 0;
+}
+
+#[cfg(feature = "apple-sandbox")]
+mod io_service {}
+
+pub use io_service::*;
diff --git a/vendor/sysinfo/src/apple/macos/mod.rs b/vendor/sysinfo/src/apple/macos/mod.rs
new file mode 100644
index 000000000..172bbfddc
--- /dev/null
+++ b/vendor/sysinfo/src/apple/macos/mod.rs
@@ -0,0 +1,17 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+pub mod disk;
+pub mod ffi;
+
+#[cfg(not(feature = "apple-sandbox"))]
+pub mod system;
+
+#[cfg(not(feature = "apple-sandbox"))]
+pub mod component;
+#[cfg(not(feature = "apple-sandbox"))]
+pub mod process;
+
+#[cfg(feature = "apple-sandbox")]
+pub use crate::sys::app_store::component;
+#[cfg(feature = "apple-sandbox")]
+pub use crate::sys::app_store::process;
diff --git a/vendor/sysinfo/src/apple/macos/process.rs b/vendor/sysinfo/src/apple/macos/process.rs
new file mode 100644
index 000000000..f146126cf
--- /dev/null
+++ b/vendor/sysinfo/src/apple/macos/process.rs
@@ -0,0 +1,643 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use std::ffi::CStr;
+use std::mem::{self, MaybeUninit};
+use std::ops::Deref;
+use std::path::{Path, PathBuf};
+
+use std::borrow::Borrow;
+
+use libc::{c_int, c_void, kill, size_t};
+
+use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid};
+
+use crate::sys::process::ThreadStatus;
+use crate::sys::system::Wrap;
+
+#[doc = include_str!("../../../md_doc/process.md")]
+pub struct Process {
+ pub(crate) name: String,
+ pub(crate) cmd: Vec<String>,
+ pub(crate) exe: PathBuf,
+ pid: Pid,
+ parent: Option<Pid>,
+ pub(crate) environ: Vec<String>,
+ cwd: PathBuf,
+ pub(crate) root: PathBuf,
+ pub(crate) memory: u64,
+ pub(crate) virtual_memory: u64,
+ old_utime: u64,
+ old_stime: u64,
+ start_time: u64,
+ run_time: u64,
+ updated: bool,
+ cpu_usage: f32,
+ user_id: Option<Uid>,
+ 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) old_read_bytes: u64,
+ pub(crate) old_written_bytes: u64,
+ pub(crate) read_bytes: u64,
+ pub(crate) written_bytes: u64,
+}
+
+impl Process {
+ pub(crate) fn new_empty(pid: Pid, exe: PathBuf, name: String, cwd: PathBuf) -> Process {
+ Process {
+ name,
+ pid,
+ parent: None,
+ cmd: Vec::new(),
+ environ: Vec::new(),
+ exe,
+ cwd,
+ root: PathBuf::new(),
+ memory: 0,
+ virtual_memory: 0,
+ cpu_usage: 0.,
+ old_utime: 0,
+ old_stime: 0,
+ updated: true,
+ start_time: 0,
+ run_time: 0,
+ user_id: None,
+ group_id: None,
+ process_status: ProcessStatus::Unknown(0),
+ status: None,
+ old_read_bytes: 0,
+ old_written_bytes: 0,
+ read_bytes: 0,
+ written_bytes: 0,
+ }
+ }
+
+ pub(crate) fn new(pid: Pid, parent: Option<Pid>, start_time: u64, run_time: u64) -> Process {
+ Process {
+ name: String::new(),
+ pid,
+ parent,
+ cmd: Vec::new(),
+ environ: Vec::new(),
+ exe: PathBuf::new(),
+ cwd: PathBuf::new(),
+ root: PathBuf::new(),
+ memory: 0,
+ virtual_memory: 0,
+ cpu_usage: 0.,
+ old_utime: 0,
+ old_stime: 0,
+ updated: true,
+ start_time,
+ run_time,
+ user_id: None,
+ group_id: None,
+ process_status: ProcessStatus::Unknown(0),
+ status: None,
+ old_read_bytes: 0,
+ old_written_bytes: 0,
+ read_bytes: 0,
+ written_bytes: 0,
+ }
+ }
+}
+
+impl ProcessExt for Process {
+ fn kill_with(&self, signal: Signal) -> Option<bool> {
+ let c_signal = crate::sys::system::convert_signal(signal)?;
+ unsafe { Some(kill(self.pid.0, c_signal) == 0) }
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ fn cmd(&self) -> &[String] {
+ &self.cmd
+ }
+
+ fn exe(&self) -> &Path {
+ self.exe.as_path()
+ }
+
+ fn pid(&self) -> Pid {
+ self.pid
+ }
+
+ fn environ(&self) -> &[String] {
+ &self.environ
+ }
+
+ fn cwd(&self) -> &Path {
+ self.cwd.as_path()
+ }
+
+ fn root(&self) -> &Path {
+ self.root.as_path()
+ }
+
+ fn memory(&self) -> u64 {
+ self.memory
+ }
+
+ fn virtual_memory(&self) -> u64 {
+ self.virtual_memory
+ }
+
+ fn parent(&self) -> Option<Pid> {
+ self.parent
+ }
+
+ fn status(&self) -> ProcessStatus {
+ self.process_status
+ }
+
+ fn start_time(&self) -> u64 {
+ self.start_time
+ }
+
+ fn run_time(&self) -> u64 {
+ self.run_time
+ }
+
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn disk_usage(&self) -> DiskUsage {
+ DiskUsage {
+ read_bytes: self.read_bytes - self.old_read_bytes,
+ total_read_bytes: self.read_bytes,
+ written_bytes: self.written_bytes - self.old_written_bytes,
+ total_written_bytes: self.written_bytes,
+ }
+ }
+
+ fn user_id(&self) -> Option<&Uid> {
+ self.user_id.as_ref()
+ }
+
+ fn group_id(&self) -> Option<Gid> {
+ self.group_id
+ }
+}
+
+#[allow(deprecated)] // Because of libc::mach_absolute_time.
+pub(crate) fn compute_cpu_usage(
+ p: &mut Process,
+ task_info: libc::proc_taskinfo,
+ system_time: u64,
+ user_time: u64,
+ time_interval: Option<f64>,
+) {
+ if let Some(time_interval) = time_interval {
+ let total_existing_time = p.old_stime.saturating_add(p.old_utime);
+ if time_interval > 0.000001 && total_existing_time > 0 {
+ let total_current_time = task_info
+ .pti_total_system
+ .saturating_add(task_info.pti_total_user);
+
+ let total_time_diff = total_current_time.saturating_sub(total_existing_time);
+ p.cpu_usage = (total_time_diff as f64 / time_interval * 100.) as f32;
+ } else {
+ p.cpu_usage = 0.;
+ }
+ p.old_stime = task_info.pti_total_system;
+ p.old_utime = task_info.pti_total_user;
+ } else {
+ unsafe {
+ // This is the "backup way" of CPU computation.
+ let time = libc::mach_absolute_time();
+ let task_time = user_time
+ .saturating_add(system_time)
+ .saturating_add(task_info.pti_total_user)
+ .saturating_add(task_info.pti_total_system);
+
+ let system_time_delta = if task_time < p.old_utime {
+ task_time
+ } else {
+ task_time.saturating_sub(p.old_utime)
+ };
+ let time_delta = if time < p.old_stime {
+ time
+ } else {
+ time.saturating_sub(p.old_stime)
+ };
+ p.old_utime = task_time;
+ p.old_stime = time;
+ p.cpu_usage = if time_delta == 0 {
+ 0f32
+ } else {
+ (system_time_delta as f64 * 100f64 / time_delta as f64) as f32
+ };
+ }
+ }
+ p.updated = true;
+}
+
+/*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;
+}*/
+
+#[inline]
+pub(crate) fn has_been_updated(p: &mut Process) -> bool {
+ let old = p.updated;
+ p.updated = false;
+ old
+}
+
+#[inline]
+pub(crate) fn force_update(p: &mut Process) {
+ 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
+ // so it's "fine".
+ libc::proc_pidinfo(
+ pid.0,
+ libc::PROC_PIDTASKINFO,
+ 0,
+ &mut task_info as *mut libc::proc_taskinfo as *mut c_void,
+ mem::size_of::<libc::proc_taskinfo>() as _,
+ );
+ task_info
+}
+
+#[inline]
+fn check_if_pid_is_alive(pid: Pid) -> bool {
+ unsafe { kill(pid.0, 0) == 0 }
+ // For the full complete check, it'd need to be (but that seems unneeded):
+ // unsafe {
+ // *libc::__errno_location() == libc::ESRCH
+ // }
+}
+
+#[inline]
+fn do_not_get_env_path(_: &str, _: &mut PathBuf, _: &mut bool) {}
+
+#[inline]
+fn do_get_env_path(env: &str, root: &mut PathBuf, check: &mut bool) {
+ if *check && env.starts_with("PATH=") {
+ *check = false;
+ *root = Path::new(&env[5..]).to_path_buf();
+ }
+}
+
+pub(crate) fn update_process(
+ wrap: &Wrap,
+ pid: Pid,
+ mut size: size_t,
+ time_interval: Option<f64>,
+ now: u64,
+ refresh_kind: ProcessRefreshKind,
+) -> Result<Option<Process>, ()> {
+ let mut proc_args = Vec::with_capacity(size as usize);
+
+ unsafe {
+ if let Some(ref mut p) = (*wrap.0.get()).get_mut(&pid) {
+ if p.memory == 0 {
+ // We don't have access to this process' information.
+ force_update(p);
+ return if check_if_pid_is_alive(pid) {
+ Ok(None)
+ } else {
+ Err(())
+ };
+ }
+ let task_info = get_task_info(pid);
+ let mut thread_info = mem::zeroed::<libc::proc_threadinfo>();
+ let (user_time, system_time, thread_status) = if libc::proc_pidinfo(
+ pid.0,
+ libc::PROC_PIDTHREADINFO,
+ 0,
+ &mut thread_info as *mut libc::proc_threadinfo as *mut c_void,
+ mem::size_of::<libc::proc_threadinfo>() as _,
+ ) != 0
+ {
+ (
+ thread_info.pth_user_time,
+ thread_info.pth_system_time,
+ Some(ThreadStatus::from(thread_info.pth_run_state)),
+ )
+ } else {
+ // It very likely means that the process is dead...
+ return if check_if_pid_is_alive(pid) {
+ Ok(None)
+ } else {
+ Err(())
+ };
+ };
+ p.status = thread_status;
+ if refresh_kind.cpu() {
+ compute_cpu_usage(p, task_info, system_time, user_time, time_interval);
+ }
+
+ p.memory = task_info.pti_resident_size / 1_000;
+ p.virtual_memory = task_info.pti_virtual_size / 1_000;
+ if refresh_kind.disk_usage() {
+ update_proc_disk_activity(p);
+ }
+ return Ok(None);
+ }
+
+ let mut vnodepathinfo = mem::zeroed::<libc::proc_vnodepathinfo>();
+ let result = libc::proc_pidinfo(
+ pid.0,
+ libc::PROC_PIDVNODEPATHINFO,
+ 0,
+ &mut vnodepathinfo as *mut _ as *mut _,
+ mem::size_of::<libc::proc_vnodepathinfo>() as _,
+ );
+ let cwd = if result > 0 {
+ let buffer = vnodepathinfo.pvi_cdir.vip_path;
+ let buffer = CStr::from_ptr(buffer.as_ptr() as _);
+ buffer
+ .to_str()
+ .map(PathBuf::from)
+ .unwrap_or_else(|_| PathBuf::new())
+ } else {
+ PathBuf::new()
+ };
+
+ let mut info = mem::zeroed::<libc::proc_bsdinfo>();
+ if libc::proc_pidinfo(
+ pid.0,
+ libc::PROC_PIDTBSDINFO,
+ 0,
+ &mut info as *mut _ as *mut _,
+ mem::size_of::<libc::proc_bsdinfo>() as _,
+ ) != mem::size_of::<libc::proc_bsdinfo>() as _
+ {
+ let mut buffer: Vec<u8> = Vec::with_capacity(libc::PROC_PIDPATHINFO_MAXSIZE as _);
+ match libc::proc_pidpath(
+ pid.0,
+ buffer.as_mut_ptr() as *mut _,
+ libc::PROC_PIDPATHINFO_MAXSIZE as _,
+ ) {
+ x if x > 0 => {
+ buffer.set_len(x as _);
+ let tmp = String::from_utf8_unchecked(buffer);
+ let exe = PathBuf::from(tmp);
+ let name = exe
+ .file_name()
+ .and_then(|x| x.to_str())
+ .unwrap_or("")
+ .to_owned();
+ return Ok(Some(Process::new_empty(pid, exe, name, cwd)));
+ }
+ _ => {}
+ }
+ return Err(());
+ }
+ let parent = match info.pbi_ppid as i32 {
+ 0 => None,
+ p => Some(Pid(p)),
+ };
+
+ let ptr: *mut u8 = proc_args.as_mut_slice().as_mut_ptr();
+ let mut mib = [libc::CTL_KERN, libc::KERN_PROCARGS2, pid.0 as _];
+ /*
+ * /---------------\ 0x00000000
+ * | ::::::::::::: |
+ * |---------------| <-- Beginning of data returned by sysctl() is here.
+ * | argc |
+ * |---------------|
+ * | exec_path |
+ * |---------------|
+ * | 0 |
+ * |---------------|
+ * | arg[0] |
+ * |---------------|
+ * | 0 |
+ * |---------------|
+ * | arg[n] |
+ * |---------------|
+ * | 0 |
+ * |---------------|
+ * | env[0] |
+ * |---------------|
+ * | 0 |
+ * |---------------|
+ * | env[n] |
+ * |---------------|
+ * | ::::::::::::: |
+ * |---------------| <-- Top of stack.
+ * : :
+ * : :
+ * \---------------/ 0xffffffff
+ */
+ if libc::sysctl(
+ mib.as_mut_ptr(),
+ mib.len() as _,
+ ptr as *mut c_void,
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) == -1
+ {
+ return Err(()); // not enough rights I assume?
+ }
+ let mut n_args: c_int = 0;
+ libc::memcpy(
+ (&mut n_args) as *mut c_int as *mut c_void,
+ ptr as *const c_void,
+ mem::size_of::<c_int>(),
+ );
+
+ let mut cp = ptr.add(mem::size_of::<c_int>());
+ let mut start = cp;
+
+ let start_time = info.pbi_start_tvsec;
+ let run_time = now.saturating_sub(start_time);
+
+ let mut p = if cp < ptr.add(size) {
+ while cp < ptr.add(size) && *cp != 0 {
+ cp = cp.offset(1);
+ }
+ let exe = Path::new(get_unchecked_str(cp, start).as_str()).to_path_buf();
+ let name = exe
+ .file_name()
+ .and_then(|x| x.to_str())
+ .unwrap_or("")
+ .to_owned();
+ while cp < ptr.add(size) && *cp == 0 {
+ cp = cp.offset(1);
+ }
+ start = cp;
+ let mut c = 0;
+ let mut cmd = Vec::with_capacity(n_args as usize);
+ while c < n_args && cp < ptr.add(size) {
+ if *cp == 0 {
+ c += 1;
+ cmd.push(get_unchecked_str(cp, start));
+ start = cp.offset(1);
+ }
+ cp = cp.offset(1);
+ }
+
+ #[inline]
+ unsafe fn get_environ<F: Fn(&str, &mut PathBuf, &mut bool)>(
+ ptr: *mut u8,
+ mut cp: *mut u8,
+ size: size_t,
+ mut root: PathBuf,
+ callback: F,
+ ) -> (Vec<String>, PathBuf) {
+ let mut environ = Vec::with_capacity(10);
+ let mut start = cp;
+ let mut check = true;
+ while cp < ptr.add(size) {
+ if *cp == 0 {
+ if cp == start {
+ break;
+ }
+ let e = get_unchecked_str(cp, start);
+ callback(&e, &mut root, &mut check);
+ environ.push(e);
+ start = cp.offset(1);
+ }
+ cp = cp.offset(1);
+ }
+ (environ, root)
+ }
+
+ let (environ, root) = if exe.is_absolute() {
+ if let Some(parent_path) = exe.parent() {
+ get_environ(
+ ptr,
+ cp,
+ size,
+ parent_path.to_path_buf(),
+ do_not_get_env_path,
+ )
+ } else {
+ get_environ(ptr, cp, size, PathBuf::new(), do_get_env_path)
+ }
+ } else {
+ get_environ(ptr, cp, size, PathBuf::new(), do_get_env_path)
+ };
+ let mut p = Process::new(pid, parent, start_time, run_time);
+
+ p.exe = exe;
+ p.name = name;
+ p.cwd = cwd;
+ p.cmd = parse_command_line(&cmd);
+ p.environ = environ;
+ p.root = root;
+ p
+ } else {
+ Process::new(pid, parent, start_time, run_time)
+ };
+
+ let task_info = get_task_info(pid);
+
+ p.memory = task_info.pti_resident_size / 1_000;
+ p.virtual_memory = task_info.pti_virtual_size / 1_000;
+
+ p.user_id = Some(Uid(info.pbi_uid));
+ p.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);
+ }
+ Ok(Some(p))
+ }
+}
+
+fn update_proc_disk_activity(p: &mut Process) {
+ p.old_read_bytes = p.read_bytes;
+ p.old_written_bytes = p.written_bytes;
+
+ let mut pidrusage = MaybeUninit::<libc::rusage_info_v2>::uninit();
+
+ unsafe {
+ let retval = libc::proc_pid_rusage(
+ p.pid().0 as _,
+ libc::RUSAGE_INFO_V2,
+ pidrusage.as_mut_ptr() as _,
+ );
+
+ if retval < 0 {
+ sysinfo_debug!("proc_pid_rusage failed: {:?}", retval);
+ } else {
+ let pidrusage = pidrusage.assume_init();
+ p.read_bytes = pidrusage.ri_diskio_bytesread;
+ p.written_bytes = pidrusage.ri_diskio_byteswritten;
+ }
+ }
+}
+
+#[allow(clippy::uninit_vec)]
+pub(crate) fn get_proc_list() -> Option<Vec<Pid>> {
+ unsafe {
+ let count = libc::proc_listallpids(::std::ptr::null_mut(), 0);
+ if count < 1 {
+ return None;
+ }
+ let mut pids: Vec<Pid> = Vec::with_capacity(count as usize);
+ pids.set_len(count as usize);
+ let count = count * mem::size_of::<Pid>() as i32;
+ let x = libc::proc_listallpids(pids.as_mut_ptr() as *mut c_void, count);
+
+ if x < 1 || x as usize >= pids.len() {
+ None
+ } else {
+ pids.set_len(x as usize);
+ Some(pids)
+ }
+ }
+}
+
+unsafe fn get_unchecked_str(cp: *mut u8, start: *mut u8) -> String {
+ let len = cp as usize - start as usize;
+ let part = Vec::from_raw_parts(start, len, len);
+ let tmp = String::from_utf8_unchecked(part.clone());
+ mem::forget(part);
+ tmp
+}
+
+fn parse_command_line<T: Deref<Target = str> + Borrow<str>>(cmd: &[T]) -> Vec<String> {
+ let mut x = 0;
+ let mut command = Vec::with_capacity(cmd.len());
+ while x < cmd.len() {
+ let mut y = x;
+ if cmd[y].starts_with('\'') || cmd[y].starts_with('"') {
+ let c = if cmd[y].starts_with('\'') { '\'' } else { '"' };
+ while y < cmd.len() && !cmd[y].ends_with(c) {
+ y += 1;
+ }
+ command.push(cmd[x..y].join(" "));
+ x = y;
+ } else {
+ command.push(cmd[x].to_owned());
+ }
+ x += 1;
+ }
+ command
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_get_path() {
+ let mut path = PathBuf::new();
+ let mut check = true;
+
+ do_get_env_path("PATH=tadam", &mut path, &mut check);
+
+ assert!(!check);
+ assert_eq!(path, PathBuf::from("tadam"));
+ }
+}
diff --git a/vendor/sysinfo/src/apple/macos/system.rs b/vendor/sysinfo/src/apple/macos/system.rs
new file mode 100644
index 000000000..949532234
--- /dev/null
+++ b/vendor/sysinfo/src/apple/macos/system.rs
@@ -0,0 +1,136 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+#[allow(deprecated)]
+use libc::{mach_timebase_info, mach_timebase_info_data_t};
+
+use libc::{
+ host_processor_info, mach_port_t, munmap, natural_t, processor_cpu_load_info,
+ processor_cpu_load_info_t, sysconf, vm_page_size, PROCESSOR_CPU_LOAD_INFO, _SC_CLK_TCK,
+};
+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();
+ }
+}
+
+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,
+}
+
+unsafe impl Send for SystemTimeInfo {}
+unsafe impl Sync for SystemTimeInfo {}
+
+impl SystemTimeInfo {
+ #[allow(deprecated)] // Everything related to mach_timebase_info_data_t
+ pub fn new(port: mach_port_t) -> Option<Self> {
+ unsafe {
+ let clock_ticks_per_sec = sysconf(_SC_CLK_TCK);
+
+ // FIXME: Maybe check errno here? Problem is that if errno is not 0 before this call,
+ // we will get an error which isn't related...
+ // if let Some(er) = std::io::Error::last_os_error().raw_os_error() {
+ // if err != 0 {
+ // println!("==> {:?}", er);
+ // sysinfo_debug!("Failed to get _SC_CLK_TCK value, using old CPU tick measure system");
+ // return None;
+ // }
+ // }
+
+ let mut info = mach_timebase_info_data_t { numer: 0, denom: 0 };
+ if mach_timebase_info(&mut info) != libc::KERN_SUCCESS {
+ sysinfo_debug!("mach_timebase_info failed, using default value of 1");
+ info.numer = 1;
+ 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,
+ None => {
+ sysinfo_debug!("host_processor_info failed, using old CPU tick measure system");
+ return None;
+ }
+ };
+
+ let nano_per_seconds = 1_000_000_000.;
+ sysinfo_debug!("");
+ 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,
+ })
+ }
+ }
+
+ 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,
+ None => return 0.,
+ };
+ let cpu_count = std::cmp::min(self.old_cpu_count, new_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 _);
+ for (new, old) in new_load.cpu_ticks.iter().zip(old_load.cpu_ticks.iter()) {
+ if new > old {
+ total += new - old;
+ }
+ }
+ }
+
+ free_cpu_load_info(&mut self.old_cpu_load);
+ self.old_cpu_load = new_cpu_load;
+ self.old_cpu_count = new_cpu_count;
+
+ // Now we convert the ticks to nanoseconds:
+ total as f64 / self.timebase_to_ns * self.clock_per_sec / cpu_count as f64
+ }
+ }
+}
+
+impl Drop for SystemTimeInfo {
+ fn drop(&mut self) {
+ unsafe {
+ free_cpu_load_info(&mut self.old_cpu_load);
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/apple/mod.rs b/vendor/sysinfo/src/apple/mod.rs
new file mode 100644
index 000000000..daff800a2
--- /dev/null
+++ b/vendor/sysinfo/src/apple/mod.rs
@@ -0,0 +1,31 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+#[cfg(target_os = "macos")]
+pub(crate) mod macos;
+#[cfg(target_os = "macos")]
+pub(crate) use self::macos as inner;
+
+#[cfg(target_os = "ios")]
+pub(crate) mod ios;
+#[cfg(target_os = "ios")]
+pub(crate) use self::ios as inner;
+
+#[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
+pub(crate) mod app_store;
+
+pub mod component;
+pub mod cpu;
+pub mod disk;
+mod ffi;
+pub mod network;
+pub mod process;
+pub mod system;
+pub mod users;
+mod utils;
+
+pub use self::component::Component;
+pub use self::cpu::Cpu;
+pub use self::disk::Disk;
+pub use self::network::{NetworkData, Networks};
+pub use self::process::Process;
+pub use self::system::System;
diff --git a/vendor/sysinfo/src/apple/network.rs b/vendor/sysinfo/src/apple/network.rs
new file mode 100644
index 000000000..f1316920e
--- /dev/null
+++ b/vendor/sysinfo/src/apple/network.rs
@@ -0,0 +1,239 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use libc::{self, c_char, if_msghdr2, CTL_NET, NET_RT_IFLIST2, PF_ROUTE, RTM_IFINFO2};
+
+use std::collections::{hash_map, HashMap};
+use std::ptr::null_mut;
+
+use crate::{NetworkExt, NetworksExt, NetworksIter};
+
+macro_rules! old_and_new {
+ ($ty_:expr, $name:ident, $old:ident, $new_val:expr) => {{
+ $ty_.$old = $ty_.$name;
+ $ty_.$name = $new_val;
+ }};
+}
+
+#[doc = include_str!("../../md_doc/networks.md")]
+pub struct Networks {
+ interfaces: HashMap<String, NetworkData>,
+}
+
+impl Networks {
+ pub(crate) fn new() -> Self {
+ Networks {
+ interfaces: HashMap::new(),
+ }
+ }
+
+ #[allow(clippy::cast_ptr_alignment)]
+ #[allow(clippy::uninit_vec)]
+ fn update_networks(&mut self) {
+ let mib = &mut [CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0];
+ let mut len = 0;
+ unsafe {
+ if libc::sysctl(
+ mib.as_mut_ptr(),
+ mib.len() as _,
+ null_mut(),
+ &mut len,
+ null_mut(),
+ 0,
+ ) < 0
+ {
+ // TODO: might be nice to put an error in here...
+ return;
+ }
+ let mut buf = Vec::with_capacity(len);
+ buf.set_len(len);
+ if libc::sysctl(
+ mib.as_mut_ptr(),
+ mib.len() as _,
+ buf.as_mut_ptr(),
+ &mut len,
+ null_mut(),
+ 0,
+ ) < 0
+ {
+ // TODO: might be nice to put an error in here...
+ return;
+ }
+ let buf = buf.as_ptr() as *const c_char;
+ let lim = buf.add(len);
+ let mut next = buf;
+ while next < lim {
+ let ifm = next as *const libc::if_msghdr;
+ next = next.offset((*ifm).ifm_msglen as isize);
+ if (*ifm).ifm_type == RTM_IFINFO2 as u8 {
+ // The interface (line description) name stored at ifname will be returned in
+ // the default coded character set identifier (CCSID) currently in effect for
+ // the job. If this is not a single byte CCSID, then storage greater than
+ // IFNAMSIZ (16) bytes may be needed. 22 bytes is large enough for all CCSIDs.
+ let mut name = vec![0u8; libc::IFNAMSIZ + 6];
+
+ let if2m: *const if_msghdr2 = ifm as *const if_msghdr2;
+ let pname =
+ libc::if_indextoname((*if2m).ifm_index as _, name.as_mut_ptr() as _);
+ if pname.is_null() {
+ continue;
+ }
+ name.set_len(libc::strlen(pname));
+ let name = String::from_utf8_unchecked(name);
+ match self.interfaces.entry(name) {
+ hash_map::Entry::Occupied(mut e) => {
+ let mut interface = e.get_mut();
+ old_and_new!(
+ interface,
+ current_out,
+ old_out,
+ (*if2m).ifm_data.ifi_obytes
+ );
+ old_and_new!(
+ interface,
+ current_in,
+ old_in,
+ (*if2m).ifm_data.ifi_ibytes
+ );
+ old_and_new!(
+ interface,
+ packets_in,
+ old_packets_in,
+ (*if2m).ifm_data.ifi_ipackets
+ );
+ old_and_new!(
+ interface,
+ packets_out,
+ old_packets_out,
+ (*if2m).ifm_data.ifi_opackets
+ );
+ old_and_new!(
+ interface,
+ errors_in,
+ old_errors_in,
+ (*if2m).ifm_data.ifi_ierrors
+ );
+ old_and_new!(
+ interface,
+ errors_out,
+ old_errors_out,
+ (*if2m).ifm_data.ifi_oerrors
+ );
+ interface.updated = true;
+ }
+ hash_map::Entry::Vacant(e) => {
+ let current_in = (*if2m).ifm_data.ifi_ibytes;
+ let current_out = (*if2m).ifm_data.ifi_obytes;
+ let packets_in = (*if2m).ifm_data.ifi_ipackets;
+ let packets_out = (*if2m).ifm_data.ifi_opackets;
+ let errors_in = (*if2m).ifm_data.ifi_ierrors;
+ let errors_out = (*if2m).ifm_data.ifi_oerrors;
+
+ e.insert(NetworkData {
+ current_in,
+ old_in: current_in,
+ current_out,
+ old_out: current_out,
+ packets_in,
+ old_packets_in: packets_in,
+ packets_out,
+ old_packets_out: packets_out,
+ errors_in,
+ old_errors_in: errors_in,
+ errors_out,
+ old_errors_out: errors_out,
+ updated: true,
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+impl NetworksExt for Networks {
+ #[allow(clippy::needless_lifetimes)]
+ fn iter<'a>(&'a self) -> NetworksIter<'a> {
+ NetworksIter::new(self.interfaces.iter())
+ }
+
+ fn refresh_networks_list(&mut self) {
+ for (_, data) in self.interfaces.iter_mut() {
+ data.updated = false;
+ }
+ self.update_networks();
+ self.interfaces.retain(|_, data| data.updated);
+ }
+
+ fn refresh(&mut self) {
+ self.update_networks();
+ }
+}
+
+#[doc = include_str!("../../md_doc/network_data.md")]
+#[derive(PartialEq, Eq)]
+pub struct NetworkData {
+ current_in: u64,
+ old_in: u64,
+ current_out: u64,
+ old_out: u64,
+ packets_in: u64,
+ old_packets_in: u64,
+ packets_out: u64,
+ old_packets_out: u64,
+ errors_in: u64,
+ old_errors_in: u64,
+ errors_out: u64,
+ old_errors_out: u64,
+ updated: bool,
+}
+
+impl NetworkExt for NetworkData {
+ fn received(&self) -> u64 {
+ self.current_in.saturating_sub(self.old_in)
+ }
+
+ fn total_received(&self) -> u64 {
+ self.current_in
+ }
+
+ fn transmitted(&self) -> u64 {
+ self.current_out.saturating_sub(self.old_out)
+ }
+
+ fn total_transmitted(&self) -> u64 {
+ self.current_out
+ }
+
+ fn packets_received(&self) -> u64 {
+ self.packets_in.saturating_sub(self.old_packets_in)
+ }
+
+ fn total_packets_received(&self) -> u64 {
+ self.packets_in
+ }
+
+ fn packets_transmitted(&self) -> u64 {
+ self.packets_out.saturating_sub(self.old_packets_out)
+ }
+
+ fn total_packets_transmitted(&self) -> u64 {
+ self.packets_out
+ }
+
+ fn errors_on_received(&self) -> u64 {
+ self.errors_in.saturating_sub(self.old_errors_in)
+ }
+
+ fn total_errors_on_received(&self) -> u64 {
+ self.errors_in
+ }
+
+ fn errors_on_transmitted(&self) -> u64 {
+ self.errors_out.saturating_sub(self.old_errors_out)
+ }
+
+ fn total_errors_on_transmitted(&self) -> u64 {
+ self.errors_out
+ }
+}
diff --git a/vendor/sysinfo/src/apple/process.rs b/vendor/sysinfo/src/apple/process.rs
new file mode 100644
index 000000000..e0f005bdc
--- /dev/null
+++ b/vendor/sysinfo/src/apple/process.rs
@@ -0,0 +1,83 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use std::fmt;
+
+pub use crate::sys::inner::process::*;
+use crate::ProcessStatus;
+
+#[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),
+ }
+ }
+}
+
+impl fmt::Display for ProcessStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(match *self {
+ ProcessStatus::Idle => "Idle",
+ ProcessStatus::Run => "Runnable",
+ ProcessStatus::Sleep => "Sleeping",
+ ProcessStatus::Stop => "Stopped",
+ ProcessStatus::Zombie => "Zombie",
+ _ => "Unknown",
+ })
+ }
+}
+
+/// Enum describing the different status of a thread.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum ThreadStatus {
+ /// Thread is running normally.
+ Running,
+ /// Thread is stopped.
+ Stopped,
+ /// Thread is waiting normally.
+ Waiting,
+ /// Thread is in an uninterruptible wait
+ Uninterruptible,
+ /// Thread is halted at a clean point.
+ Halted,
+ /// Unknown.
+ Unknown(i32),
+}
+
+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,
+ 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
new file mode 100644
index 000000000..abe617dee
--- /dev/null
+++ b/vendor/sysinfo/src/apple/system.rs
@@ -0,0 +1,763 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::component::Component;
+use crate::sys::cpu::*;
+use crate::sys::disk::*;
+#[cfg(target_os = "macos")]
+use crate::sys::ffi;
+use crate::sys::network::Networks;
+use crate::sys::process::*;
+#[cfg(target_os = "macos")]
+use core_foundation_sys::base::{kCFAllocatorDefault, CFRelease};
+
+use crate::{
+ CpuExt, CpuRefreshKind, LoadAvg, Pid, ProcessRefreshKind, RefreshKind, SystemExt, User,
+};
+
+#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+use crate::ProcessExt;
+
+use std::cell::UnsafeCell;
+use std::collections::HashMap;
+use std::mem;
+use std::sync::Arc;
+#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+use std::time::SystemTime;
+
+#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+use libc::size_t;
+
+use libc::{
+ c_char, c_int, c_void, host_statistics64, mach_port_t, sysconf, sysctl, sysctlbyname, timeval,
+ vm_statistics64, _SC_PAGESIZE,
+};
+
+#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+declare_signals! {
+ c_int,
+ Signal::Hangup => libc::SIGHUP,
+ Signal::Interrupt => libc::SIGINT,
+ Signal::Quit => libc::SIGQUIT,
+ Signal::Illegal => libc::SIGILL,
+ Signal::Trap => libc::SIGTRAP,
+ Signal::Abort => libc::SIGABRT,
+ Signal::IOT => libc::SIGIOT,
+ Signal::Bus => libc::SIGBUS,
+ Signal::FloatingPointException => libc::SIGFPE,
+ Signal::Kill => libc::SIGKILL,
+ Signal::User1 => libc::SIGUSR1,
+ Signal::Segv => libc::SIGSEGV,
+ Signal::User2 => libc::SIGUSR2,
+ Signal::Pipe => libc::SIGPIPE,
+ Signal::Alarm => libc::SIGALRM,
+ Signal::Term => libc::SIGTERM,
+ Signal::Child => libc::SIGCHLD,
+ Signal::Continue => libc::SIGCONT,
+ Signal::Stop => libc::SIGSTOP,
+ Signal::TSTP => libc::SIGTSTP,
+ Signal::TTIN => libc::SIGTTIN,
+ Signal::TTOU => libc::SIGTTOU,
+ Signal::Urgent => libc::SIGURG,
+ Signal::XCPU => libc::SIGXCPU,
+ Signal::XFSZ => libc::SIGXFSZ,
+ Signal::VirtualAlarm => libc::SIGVTALRM,
+ Signal::Profiling => libc::SIGPROF,
+ Signal::Winch => libc::SIGWINCH,
+ Signal::IO => libc::SIGIO,
+ // SIGPOLL doesn't exist on apple targets but since it's an equivalent of SIGIO on unix,
+ // we simply use the SIGIO constant.
+ Signal::Poll => libc::SIGIO,
+ Signal::Sys => libc::SIGSYS,
+ _ => None,
+}
+#[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
+declare_signals! {
+ c_int,
+ _ => None,
+}
+
+#[doc = include_str!("../../md_doc/system.md")]
+pub struct System {
+ process_list: HashMap<Pid, Process>,
+ mem_total: u64,
+ mem_free: u64,
+ mem_available: u64,
+ swap_total: u64,
+ swap_free: u64,
+ global_cpu: Cpu,
+ cpus: Vec<Cpu>,
+ page_size_kb: u64,
+ components: Vec<Component>,
+ // Used to get CPU information, not supported on iOS, or inside the default macOS sandbox.
+ #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+ connection: Option<ffi::io_connect_t>,
+ disks: Vec<Disk>,
+ networks: Networks,
+ port: mach_port_t,
+ users: Vec<User>,
+ boot_time: u64,
+ // Used to get disk information, to be more specific, it's needed by the
+ // DADiskCreateFromVolumePath function. Not supported on iOS.
+ #[cfg(target_os = "macos")]
+ session: ffi::SessionWrap,
+ #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+ clock_info: Option<crate::sys::macos::system::SystemTimeInfo>,
+ got_cpu_frequency: bool,
+}
+
+impl Drop for System {
+ fn drop(&mut self) {
+ #[cfg(target_os = "macos")]
+ unsafe {
+ #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+ if let Some(conn) = self.connection {
+ ffi::IOServiceClose(conn);
+ }
+
+ if !self.session.0.is_null() {
+ CFRelease(self.session.0 as _);
+ }
+ }
+ }
+}
+
+pub(crate) struct Wrap<'a>(pub UnsafeCell<&'a mut HashMap<Pid, Process>>);
+
+unsafe impl<'a> Send for Wrap<'a> {}
+unsafe impl<'a> Sync for Wrap<'a> {}
+
+#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+impl System {
+ fn clear_procs(&mut self) {
+ use crate::sys::macos::process;
+
+ self.process_list
+ .retain(|_, proc_| process::has_been_updated(proc_));
+ }
+}
+
+fn boot_time() -> u64 {
+ let mut boot_time = timeval {
+ tv_sec: 0,
+ tv_usec: 0,
+ };
+ let mut len = std::mem::size_of::<timeval>();
+ let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME];
+
+ unsafe {
+ if sysctl(
+ mib.as_mut_ptr(),
+ mib.len() as _,
+ &mut boot_time as *mut timeval as *mut _,
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ ) < 0
+ {
+ 0
+ } else {
+ boot_time.tv_sec as _
+ }
+ }
+}
+
+#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+fn get_now() -> u64 {
+ SystemTime::now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .map(|n| n.as_secs())
+ .unwrap_or(0)
+}
+
+impl SystemExt for System {
+ const IS_SUPPORTED: bool = true;
+ const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals();
+
+ fn new_with_specifics(refreshes: RefreshKind) -> System {
+ unsafe {
+ let port = libc::mach_host_self();
+
+ let mut s = System {
+ process_list: HashMap::with_capacity(200),
+ mem_total: 0,
+ mem_free: 0,
+ mem_available: 0,
+ swap_total: 0,
+ swap_free: 0,
+ global_cpu: Cpu::new(
+ "0".to_owned(),
+ Arc::new(CpuData::new(std::ptr::null_mut(), 0)),
+ 0,
+ String::new(),
+ String::new(),
+ ),
+ cpus: Vec::new(),
+ page_size_kb: sysconf(_SC_PAGESIZE) as u64 / 1_000,
+ components: Vec::with_capacity(2),
+ #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+ connection: get_io_service_connection(),
+ disks: Vec::with_capacity(1),
+ networks: Networks::new(),
+ port,
+ users: Vec::new(),
+ boot_time: boot_time(),
+ #[cfg(target_os = "macos")]
+ session: ffi::SessionWrap(::std::ptr::null_mut()),
+ #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+ clock_info: crate::sys::macos::system::SystemTimeInfo::new(port),
+ got_cpu_frequency: false,
+ };
+ s.refresh_specifics(refreshes);
+ s
+ }
+ }
+
+ fn refresh_memory(&mut self) {
+ let mut mib = [0, 0];
+
+ unsafe {
+ // get system values
+ // get swap info
+ let mut xs: libc::xsw_usage = mem::zeroed::<libc::xsw_usage>();
+ if get_sys_value(
+ libc::CTL_VM as _,
+ libc::VM_SWAPUSAGE as _,
+ mem::size_of::<libc::xsw_usage>(),
+ &mut xs as *mut _ as *mut c_void,
+ &mut mib,
+ ) {
+ self.swap_total = xs.xsu_total / 1_000;
+ self.swap_free = xs.xsu_avail / 1_000;
+ }
+ // get ram info
+ if self.mem_total < 1 {
+ get_sys_value(
+ libc::CTL_HW as _,
+ libc::HW_MEMSIZE as _,
+ mem::size_of::<u64>(),
+ &mut self.mem_total as *mut u64 as *mut c_void,
+ &mut mib,
+ );
+ self.mem_total /= 1_000;
+ }
+ let mut count: u32 = libc::HOST_VM_INFO64_COUNT as _;
+ let mut stat = mem::zeroed::<vm_statistics64>();
+ if host_statistics64(
+ self.port,
+ libc::HOST_VM_INFO64,
+ &mut stat as *mut vm_statistics64 as *mut _,
+ &mut count,
+ ) == libc::KERN_SUCCESS
+ {
+ // From the apple documentation:
+ //
+ // /*
+ // * NB: speculative pages are already accounted for in "free_count",
+ // * so "speculative_count" is the number of "free" pages that are
+ // * 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);
+ }
+ }
+ }
+
+ #[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
+ fn refresh_components_list(&mut self) {}
+
+ #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+ fn refresh_components_list(&mut self) {
+ if let Some(con) = self.connection {
+ self.components.clear();
+ // getting CPU critical temperature
+ let critical_temp = crate::apple::component::get_temperature(
+ con,
+ &['T' as i8, 'C' as i8, '0' as i8, 'D' as i8, 0],
+ );
+
+ for (id, v) in crate::apple::component::COMPONENTS_TEMPERATURE_IDS.iter() {
+ if let Some(c) = Component::new((*id).to_owned(), None, critical_temp, v, con) {
+ self.components.push(c);
+ }
+ }
+ }
+ }
+
+ fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind) {
+ let cpus = &mut self.cpus;
+ if cpus.is_empty() {
+ init_cpus(self.port, cpus, &mut self.global_cpu, refresh_kind);
+ self.got_cpu_frequency = refresh_kind.frequency();
+ return;
+ }
+ if refresh_kind.frequency() && !self.got_cpu_frequency {
+ let frequency = get_cpu_frequency();
+ for proc_ in cpus.iter_mut() {
+ proc_.set_frequency(frequency);
+ }
+ self.got_cpu_frequency = true;
+ }
+ if refresh_kind.cpu_usage() {
+ update_cpu_usage(self.port, &mut self.global_cpu, |proc_data, cpu_info| {
+ let mut percentage = 0f32;
+ let mut offset = 0;
+ for proc_ in cpus.iter_mut() {
+ let cpu_usage = compute_usage_of_cpu(proc_, cpu_info, offset);
+ proc_.update(cpu_usage, Arc::clone(&proc_data));
+ percentage += proc_.cpu_usage();
+
+ offset += libc::CPU_STATE_MAX as isize;
+ }
+ (percentage, cpus.len())
+ });
+ }
+ }
+
+ #[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
+ fn refresh_processes_specifics(&mut self, _refresh_kind: ProcessRefreshKind) {}
+
+ #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+ fn refresh_processes_specifics(&mut self, refresh_kind: ProcessRefreshKind) {
+ use crate::utils::into_iter;
+
+ unsafe {
+ let count = libc::proc_listallpids(::std::ptr::null_mut(), 0);
+ if count < 1 {
+ return;
+ }
+ }
+ if let Some(pids) = get_proc_list() {
+ let now = get_now();
+ let arg_max = get_arg_max();
+ let port = self.port;
+ let time_interval = self.clock_info.as_mut().map(|c| c.get_time_interval(port));
+ let entries: Vec<Process> = {
+ let wrap = &Wrap(UnsafeCell::new(&mut self.process_list));
+
+ #[cfg(feature = "multithread")]
+ use rayon::iter::ParallelIterator;
+
+ into_iter(pids)
+ .flat_map(|pid| {
+ match update_process(
+ wrap,
+ pid,
+ arg_max as size_t,
+ time_interval,
+ now,
+ refresh_kind,
+ ) {
+ Ok(x) => x,
+ _ => None,
+ }
+ })
+ .collect()
+ };
+ entries.into_iter().for_each(|entry| {
+ self.process_list.insert(entry.pid(), entry);
+ });
+ self.clear_procs();
+ }
+ }
+
+ #[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
+ fn refresh_process_specifics(&mut self, _pid: Pid, _refresh_kind: ProcessRefreshKind) -> bool {
+ false
+ }
+
+ #[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 arg_max = get_arg_max();
+ let port = self.port;
+ let 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(
+ &wrap,
+ pid,
+ arg_max as size_t,
+ time_interval,
+ now,
+ refresh_kind,
+ )
+ } {
+ Ok(Some(p)) => {
+ self.process_list.insert(p.pid(), p);
+ true
+ }
+ Ok(_) => true,
+ Err(_) => false,
+ }
+ }
+
+ #[cfg(target_os = "ios")]
+ fn refresh_disks_list(&mut self) {}
+
+ #[cfg(target_os = "macos")]
+ fn refresh_disks_list(&mut self) {
+ unsafe {
+ if self.session.0.is_null() {
+ self.session.0 = ffi::DASessionCreate(kCFAllocatorDefault as _);
+ }
+ self.disks = get_disks(self.session.0);
+ }
+ }
+
+ fn refresh_users_list(&mut self) {
+ self.users = crate::apple::users::get_users_list();
+ }
+
+ // COMMON PART
+ //
+ // Need to be moved into a "common" file to avoid duplication.
+
+ fn processes(&self) -> &HashMap<Pid, Process> {
+ &self.process_list
+ }
+
+ fn process(&self, pid: Pid) -> Option<&Process> {
+ self.process_list.get(&pid)
+ }
+
+ fn global_cpu_info(&self) -> &Cpu {
+ &self.global_cpu
+ }
+
+ fn cpus(&self) -> &[Cpu] {
+ &self.cpus
+ }
+
+ fn physical_core_count(&self) -> Option<usize> {
+ let mut physical_core_count = 0;
+
+ unsafe {
+ if get_sys_value_by_name(
+ b"hw.physicalcpu\0",
+ &mut mem::size_of::<u32>(),
+ &mut physical_core_count as *mut usize as *mut c_void,
+ ) {
+ Some(physical_core_count)
+ } else {
+ None
+ }
+ }
+ }
+
+ fn networks(&self) -> &Networks {
+ &self.networks
+ }
+
+ fn networks_mut(&mut self) -> &mut Networks {
+ &mut self.networks
+ }
+
+ fn total_memory(&self) -> u64 {
+ self.mem_total
+ }
+
+ fn free_memory(&self) -> u64 {
+ self.mem_free
+ }
+
+ fn available_memory(&self) -> u64 {
+ self.mem_available
+ }
+
+ fn used_memory(&self) -> u64 {
+ self.mem_total - self.mem_free
+ }
+
+ fn total_swap(&self) -> u64 {
+ self.swap_total
+ }
+
+ fn free_swap(&self) -> u64 {
+ self.swap_free
+ }
+
+ // TODO: need to be checked
+ fn used_swap(&self) -> u64 {
+ self.swap_total - self.swap_free
+ }
+
+ fn components(&self) -> &[Component] {
+ &self.components
+ }
+
+ fn components_mut(&mut self) -> &mut [Component] {
+ &mut self.components
+ }
+
+ fn disks(&self) -> &[Disk] {
+ &self.disks
+ }
+
+ fn disks_mut(&mut self) -> &mut [Disk] {
+ &mut self.disks
+ }
+
+ fn uptime(&self) -> u64 {
+ unsafe {
+ let csec = libc::time(::std::ptr::null_mut());
+
+ libc::difftime(csec, self.boot_time as _) as u64
+ }
+ }
+
+ fn load_average(&self) -> LoadAvg {
+ let mut loads = vec![0f64; 3];
+
+ unsafe {
+ libc::getloadavg(loads.as_mut_ptr(), 3);
+ LoadAvg {
+ one: loads[0],
+ five: loads[1],
+ fifteen: loads[2],
+ }
+ }
+ }
+
+ fn users(&self) -> &[User] {
+ &self.users
+ }
+
+ fn boot_time(&self) -> u64 {
+ self.boot_time
+ }
+
+ fn name(&self) -> Option<String> {
+ get_system_info(libc::KERN_OSTYPE, Some("Darwin"))
+ }
+
+ fn long_os_version(&self) -> Option<String> {
+ #[cfg(target_os = "macos")]
+ let friendly_name = match self.os_version().unwrap_or_default() {
+ f_n if f_n.starts_with("10.16")
+ | f_n.starts_with("11.0")
+ | f_n.starts_with("11.1")
+ | f_n.starts_with("11.2") =>
+ {
+ "Big Sur"
+ }
+ f_n if f_n.starts_with("10.15") => "Catalina",
+ f_n if f_n.starts_with("10.14") => "Mojave",
+ f_n if f_n.starts_with("10.13") => "High Sierra",
+ f_n if f_n.starts_with("10.12") => "Sierra",
+ f_n if f_n.starts_with("10.11") => "El Capitan",
+ f_n if f_n.starts_with("10.10") => "Yosemite",
+ f_n if f_n.starts_with("10.9") => "Mavericks",
+ f_n if f_n.starts_with("10.8") => "Mountain Lion",
+ f_n if f_n.starts_with("10.7") => "Lion",
+ f_n if f_n.starts_with("10.6") => "Snow Leopard",
+ f_n if f_n.starts_with("10.5") => "Leopard",
+ f_n if f_n.starts_with("10.4") => "Tiger",
+ f_n if f_n.starts_with("10.3") => "Panther",
+ f_n if f_n.starts_with("10.2") => "Jaguar",
+ f_n if f_n.starts_with("10.1") => "Puma",
+ f_n if f_n.starts_with("10.0") => "Cheetah",
+ _ => "",
+ };
+
+ #[cfg(target_os = "macos")]
+ let long_name = Some(format!(
+ "MacOS {} {}",
+ self.os_version().unwrap_or_default(),
+ friendly_name
+ ));
+
+ #[cfg(target_os = "ios")]
+ let long_name = Some(format!("iOS {}", self.os_version().unwrap_or_default()));
+
+ long_name
+ }
+
+ fn host_name(&self) -> Option<String> {
+ get_system_info(libc::KERN_HOSTNAME, None)
+ }
+
+ fn kernel_version(&self) -> Option<String> {
+ get_system_info(libc::KERN_OSRELEASE, None)
+ }
+
+ fn os_version(&self) -> Option<String> {
+ unsafe {
+ // get the size for the buffer first
+ let mut size = 0;
+ if get_sys_value_by_name(b"kern.osproductversion\0", &mut size, std::ptr::null_mut())
+ && size > 0
+ {
+ // now create a buffer with the size and get the real value
+ let mut buf = vec![0_u8; size as usize];
+
+ if get_sys_value_by_name(
+ b"kern.osproductversion\0",
+ &mut size,
+ buf.as_mut_ptr() as *mut c_void,
+ ) {
+ if let Some(pos) = buf.iter().position(|x| *x == 0) {
+ // Shrink buffer to terminate the null bytes
+ buf.resize(pos, 0);
+ }
+
+ String::from_utf8(buf).ok()
+ } else {
+ // getting the system value failed
+ None
+ }
+ } else {
+ // getting the system value failed, or did not return a buffer size
+ None
+ }
+ }
+ }
+}
+
+impl Default for System {
+ fn default() -> System {
+ System::new()
+ }
+}
+
+// code from https://github.com/Chris911/iStats
+// Not supported on iOS, or in the default macOS
+#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+fn get_io_service_connection() -> Option<ffi::io_connect_t> {
+ let mut master_port: mach_port_t = 0;
+ let mut iterator: ffi::io_iterator_t = 0;
+
+ unsafe {
+ ffi::IOMasterPort(libc::MACH_PORT_NULL, &mut master_port);
+
+ let matching_dictionary = ffi::IOServiceMatching(b"AppleSMC\0".as_ptr() as *const i8);
+ let result =
+ ffi::IOServiceGetMatchingServices(master_port, matching_dictionary, &mut iterator);
+ if result != ffi::KIO_RETURN_SUCCESS {
+ sysinfo_debug!("Error: IOServiceGetMatchingServices() = {}", result);
+ return None;
+ }
+
+ let device = ffi::IOIteratorNext(iterator);
+ ffi::IOObjectRelease(iterator);
+ if device == 0 {
+ sysinfo_debug!("Error: no SMC found");
+ return None;
+ }
+
+ let mut conn = 0;
+ let result = ffi::IOServiceOpen(device, libc::mach_task_self(), 0, &mut conn);
+ ffi::IOObjectRelease(device);
+ if result != ffi::KIO_RETURN_SUCCESS {
+ sysinfo_debug!("Error: IOServiceOpen() = {}", result);
+ return None;
+ }
+
+ Some(conn)
+ }
+}
+
+#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+fn get_arg_max() -> usize {
+ let mut mib = [libc::CTL_KERN, libc::KERN_ARGMAX];
+ let mut arg_max = 0i32;
+ let mut size = mem::size_of::<c_int>();
+ unsafe {
+ if sysctl(
+ mib.as_mut_ptr(),
+ mib.len() as _,
+ (&mut arg_max) as *mut i32 as *mut c_void,
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) == -1
+ {
+ 4096 // We default to this value
+ } else {
+ arg_max as usize
+ }
+ }
+}
+
+pub(crate) unsafe fn get_sys_value(
+ high: u32,
+ low: u32,
+ mut len: usize,
+ value: *mut c_void,
+ mib: &mut [i32; 2],
+) -> bool {
+ mib[0] = high as i32;
+ mib[1] = low as i32;
+ sysctl(
+ mib.as_mut_ptr(),
+ mib.len() as _,
+ value,
+ &mut len as *mut usize,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+}
+
+unsafe fn get_sys_value_by_name(name: &[u8], len: &mut usize, value: *mut c_void) -> bool {
+ sysctlbyname(
+ name.as_ptr() as *const c_char,
+ value,
+ len,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+}
+
+fn get_system_info(value: c_int, default: Option<&str>) -> Option<String> {
+ let mut mib: [c_int; 2] = [libc::CTL_KERN, value];
+ let mut size = 0;
+
+ unsafe {
+ // Call first to get size
+ sysctl(
+ mib.as_mut_ptr(),
+ mib.len() as _,
+ std::ptr::null_mut(),
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ );
+
+ // exit early if we did not update the size
+ if size == 0 {
+ default.map(|s| s.to_owned())
+ } else {
+ // set the buffer to the correct size
+ let mut buf = vec![0_u8; size as usize];
+
+ if sysctl(
+ mib.as_mut_ptr(),
+ mib.len() as _,
+ buf.as_mut_ptr() as _,
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) == -1
+ {
+ // If command fails return default
+ default.map(|s| s.to_owned())
+ } else {
+ if let Some(pos) = buf.iter().position(|x| *x == 0) {
+ // Shrink buffer to terminate the null bytes
+ buf.resize(pos, 0);
+ }
+
+ String::from_utf8(buf).ok()
+ }
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/apple/users.rs b/vendor/sysinfo/src/apple/users.rs
new file mode 100644
index 000000000..690aceee1
--- /dev/null
+++ b/vendor/sysinfo/src/apple/users.rs
@@ -0,0 +1,178 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{
+ common::{Gid, Uid},
+ User,
+};
+
+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();
+ }
+ }
+}
+
+fn endswith(s1: *const c_char, s2: &[u8]) -> bool {
+ if s1.is_null() {
+ return false;
+ }
+ unsafe {
+ let mut len = strlen(s1) as isize - 1;
+ let mut i = s2.len() as isize - 1;
+ while len >= 0 && i >= 0 && *s1.offset(len) == s2[i as usize] as _ {
+ i -= 1;
+ len -= 1;
+ }
+ i == -1
+ }
+}
+
+fn users_list<F>(filter: F) -> Vec<User>
+where
+ F: Fn(*const c_char, u32) -> bool,
+{
+ let mut users = Vec::new();
+
+ unsafe {
+ setpwent();
+ loop {
+ let pw = getpwent();
+ if pw.is_null() {
+ break;
+ }
+
+ if !filter((*pw).pw_shell, (*pw).pw_uid) {
+ // 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,
+ });
+ }
+ }
+ endpwent();
+ }
+ users.sort_unstable_by(|x, y| x.name.partial_cmp(&y.name).unwrap());
+ users.dedup_by(|a, b| a.name == b.name);
+ users
+}
+
+pub(crate) fn get_users_list() -> Vec<User> {
+ users_list(|shell, uid| {
+ !endswith(shell, b"/false") && !endswith(shell, b"/uucico") && uid < 65536
+ })
+}
+
+// This was the OSX-based solution. It provides enough information, but what a mess!
+// pub fn get_users_list() -> Vec<User> {
+// let mut users = Vec::new();
+// let node_name = b"/Local/Default\0";
+
+// unsafe {
+// let node_name = ffi::CFStringCreateWithCStringNoCopy(
+// std::ptr::null_mut(),
+// node_name.as_ptr() as *const c_char,
+// ffi::kCFStringEncodingMacRoman,
+// ffi::kCFAllocatorNull as *mut c_void,
+// );
+// let node_ref = ffi::ODNodeCreateWithName(
+// ffi::kCFAllocatorDefault,
+// ffi::kODSessionDefault,
+// node_name,
+// std::ptr::null_mut(),
+// );
+// let query = ffi::ODQueryCreateWithNode(
+// ffi::kCFAllocatorDefault,
+// node_ref,
+// ffi::kODRecordTypeUsers as _, // kODRecordTypeGroups
+// std::ptr::null(),
+// 0,
+// std::ptr::null(),
+// std::ptr::null(),
+// 0,
+// std::ptr::null_mut(),
+// );
+// if query.is_null() {
+// return users;
+// }
+// let results = ffi::ODQueryCopyResults(
+// query,
+// false as _,
+// std::ptr::null_mut(),
+// );
+// let len = ffi::CFArrayGetCount(results);
+// for i in 0..len {
+// let name = match get_user_name(ffi::CFArrayGetValueAtIndex(results, i)) {
+// Some(n) => n,
+// None => continue,
+// };
+// let groups = get_user_groups(&name);
+// users.push(User { name });
+// }
+
+// ffi::CFRelease(results as *const c_void);
+// ffi::CFRelease(query as *const c_void);
+// ffi::CFRelease(node_ref as *const c_void);
+// ffi::CFRelease(node_name as *const c_void);
+// }
+// users.sort_unstable_by(|x, y| x.name.partial_cmp(&y.name).unwrap());
+// return users;
+// }
+
+// fn get_user_name(result: *const c_void) -> Option<String> {
+// let user_name = ffi::ODRecordGetRecordName(result as _);
+// let ptr = ffi::CFStringGetCharactersPtr(user_name);
+// String::from_utf16(&if ptr.is_null() {
+// let len = ffi::CFStringGetLength(user_name); // It returns the len in UTF-16 code pairs.
+// if len == 0 {
+// continue;
+// }
+// let mut v = Vec::with_capacity(len as _);
+// for x in 0..len {
+// v.push(ffi::CFStringGetCharacterAtIndex(user_name, x));
+// }
+// v
+// } else {
+// let mut v: Vec<u16> = Vec::new();
+// let mut x = 0;
+// loop {
+// let letter = *ptr.offset(x);
+// if letter == 0 {
+// break;
+// }
+// v.push(letter);
+// x += 1;
+// }
+// v
+// }.ok()
+// }
diff --git a/vendor/sysinfo/src/apple/utils.rs b/vendor/sysinfo/src/apple/utils.rs
new file mode 100644
index 000000000..019295b95
--- /dev/null
+++ b/vendor/sysinfo/src/apple/utils.rs
@@ -0,0 +1,39 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use libc::c_char;
+
+pub(crate) fn cstr_to_rust(c: *const c_char) -> Option<String> {
+ cstr_to_rust_with_size(c, None)
+}
+
+pub(crate) fn cstr_to_rust_with_size(c: *const c_char, size: Option<usize>) -> Option<String> {
+ if c.is_null() {
+ return None;
+ }
+ let mut s = match size {
+ Some(len) => Vec::with_capacity(len),
+ None => Vec::new(),
+ };
+ let mut i = 0;
+ unsafe {
+ loop {
+ let value = *c.offset(i) as u8;
+ if value == 0 {
+ break;
+ }
+ s.push(value);
+ i += 1;
+ }
+ String::from_utf8(s).ok()
+ }
+}
+
+#[cfg(target_os = "macos")]
+pub(crate) fn vec_to_rust(buf: Vec<i8>) -> Option<String> {
+ String::from_utf8(
+ buf.into_iter()
+ .flat_map(|b| if b > 0 { Some(b as u8) } else { None })
+ .collect(),
+ )
+ .ok()
+}
diff --git a/vendor/sysinfo/src/c_interface.rs b/vendor/sysinfo/src/c_interface.rs
new file mode 100644
index 000000000..e7cc5359b
--- /dev/null
+++ b/vendor/sysinfo/src/c_interface.rs
@@ -0,0 +1,468 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{CpuExt, NetworkExt, NetworksExt, Pid, Process, ProcessExt, System, SystemExt};
+use libc::{self, c_char, c_float, c_uint, c_void, pid_t, size_t};
+use std::borrow::BorrowMut;
+use std::ffi::CString;
+
+/// Equivalent of [`System`][crate::System] struct.
+pub type CSystem = *mut c_void;
+/// Equivalent of [`Process`][crate::Process] struct.
+pub type CProcess = *const c_void;
+/// C string returned from `CString::into_raw`.
+pub type RString = *const c_char;
+/// Callback used by [`processes`][crate::System#method.processes].
+pub type ProcessLoop = extern "C" fn(pid: pid_t, process: CProcess, data: *mut c_void) -> bool;
+
+/// Equivalent of [`System::new()`][crate::System#method.new].
+#[no_mangle]
+pub extern "C" fn sysinfo_init() -> CSystem {
+ let system = Box::new(System::new());
+ Box::into_raw(system) as CSystem
+}
+
+/// Equivalent of `System::drop()`. Important in C to cleanup memory.
+#[no_mangle]
+pub extern "C" fn sysinfo_destroy(system: CSystem) {
+ assert!(!system.is_null());
+ unsafe {
+ Box::from_raw(system as *mut System);
+ }
+}
+
+/// Equivalent of [`System::refresh_system()`][crate::System#method.refresh_system].
+#[no_mangle]
+pub extern "C" fn sysinfo_refresh_system(system: CSystem) {
+ assert!(!system.is_null());
+ unsafe {
+ let mut system: Box<System> = Box::from_raw(system as *mut System);
+ {
+ let system: &mut System = system.borrow_mut();
+ system.refresh_system();
+ }
+ Box::into_raw(system);
+ }
+}
+
+/// Equivalent of [`System::refresh_memory()`][crate::System#method.refresh_memory].
+#[no_mangle]
+pub extern "C" fn sysinfo_refresh_memory(system: CSystem) {
+ assert!(!system.is_null());
+ unsafe {
+ let mut system: Box<System> = Box::from_raw(system as *mut System);
+ {
+ let system: &mut System = system.borrow_mut();
+ system.refresh_memory();
+ }
+ Box::into_raw(system);
+ }
+}
+
+/// Equivalent of [`System::refresh_cpu()`][crate::System#method.refresh_cpu].
+#[no_mangle]
+pub extern "C" fn sysinfo_refresh_cpu(system: CSystem) {
+ assert!(!system.is_null());
+ unsafe {
+ let mut system: Box<System> = Box::from_raw(system as *mut System);
+ {
+ let system: &mut System = system.borrow_mut();
+ system.refresh_cpu();
+ }
+ Box::into_raw(system);
+ }
+}
+
+/// Equivalent of [`System::refresh_components()`][crate::System#method.refresh_temperatures].
+#[no_mangle]
+pub extern "C" fn sysinfo_refresh_components(system: CSystem) {
+ assert!(!system.is_null());
+ unsafe {
+ let mut system: Box<System> = Box::from_raw(system as *mut System);
+ {
+ let system: &mut System = system.borrow_mut();
+ system.refresh_components();
+ }
+ Box::into_raw(system);
+ }
+}
+
+/// Equivalent of [`System::refresh_all()`][crate::System#method.refresh_all].
+#[no_mangle]
+pub extern "C" fn sysinfo_refresh_all(system: CSystem) {
+ assert!(!system.is_null());
+ unsafe {
+ let mut system: Box<System> = Box::from_raw(system as *mut System);
+ {
+ let system: &mut System = system.borrow_mut();
+ system.refresh_all();
+ }
+ Box::into_raw(system);
+ }
+}
+
+/// Equivalent of [`System::refresh_processes()`][crate::System#method.refresh_processes].
+#[no_mangle]
+pub extern "C" fn sysinfo_refresh_processes(system: CSystem) {
+ assert!(!system.is_null());
+ unsafe {
+ let mut system: Box<System> = Box::from_raw(system as *mut System);
+ {
+ let system: &mut System = system.borrow_mut();
+ system.refresh_processes();
+ }
+ Box::into_raw(system);
+ }
+}
+
+/// Equivalent of [`System::refresh_process()`][crate::System#method.refresh_process].
+#[cfg(target_os = "linux")]
+#[no_mangle]
+pub extern "C" fn sysinfo_refresh_process(system: CSystem, pid: pid_t) {
+ assert!(!system.is_null());
+ unsafe {
+ let mut system: Box<System> = Box::from_raw(system as *mut System);
+ {
+ let system: &mut System = system.borrow_mut();
+ system.refresh_process(Pid(pid));
+ }
+ Box::into_raw(system);
+ }
+}
+
+/// Equivalent of [`System::refresh_disks()`][crate::System#method.refresh_disks].
+#[no_mangle]
+pub extern "C" fn sysinfo_refresh_disks(system: CSystem) {
+ assert!(!system.is_null());
+ unsafe {
+ let mut system: Box<System> = Box::from_raw(system as *mut System);
+ {
+ let system: &mut System = system.borrow_mut();
+ system.refresh_disks();
+ }
+ Box::into_raw(system);
+ }
+}
+
+/// Equivalent of [`System::refresh_disks_list()`][crate::System#method.refresh_disks_list].
+#[no_mangle]
+pub extern "C" fn sysinfo_refresh_disks_list(system: CSystem) {
+ assert!(!system.is_null());
+ unsafe {
+ let mut system: Box<System> = Box::from_raw(system as *mut System);
+ {
+ let system: &mut System = system.borrow_mut();
+ system.refresh_disks_list();
+ }
+ Box::into_raw(system);
+ }
+}
+
+/// Equivalent of [`System::total_memory()`][crate::System#method.total_memory].
+#[no_mangle]
+pub extern "C" fn sysinfo_total_memory(system: CSystem) -> size_t {
+ assert!(!system.is_null());
+ unsafe {
+ let system: Box<System> = Box::from_raw(system as *mut System);
+ let ret = system.total_memory() as size_t;
+ Box::into_raw(system);
+ ret
+ }
+}
+
+/// Equivalent of [`System::free_memory()`][crate::System#method.free_memory].
+#[no_mangle]
+pub extern "C" fn sysinfo_free_memory(system: CSystem) -> size_t {
+ assert!(!system.is_null());
+ unsafe {
+ let system: Box<System> = Box::from_raw(system as *mut System);
+ let ret = system.free_memory() as size_t;
+ Box::into_raw(system);
+ ret
+ }
+}
+
+/// Equivalent of [`System::used_memory()`][crate::System#method.used_memory].
+#[no_mangle]
+pub extern "C" fn sysinfo_used_memory(system: CSystem) -> size_t {
+ assert!(!system.is_null());
+ let system: Box<System> = unsafe { Box::from_raw(system as *mut System) };
+ let ret = system.used_memory() as size_t;
+ Box::into_raw(system);
+ ret
+}
+
+/// Equivalent of [`System::total_swap()`][crate::System#method.total_swap].
+#[no_mangle]
+pub extern "C" fn sysinfo_total_swap(system: CSystem) -> size_t {
+ assert!(!system.is_null());
+ unsafe {
+ let system: Box<System> = Box::from_raw(system as *mut System);
+ let ret = system.total_swap() as size_t;
+ Box::into_raw(system);
+ ret
+ }
+}
+
+/// Equivalent of [`System::free_swap()`][crate::System#method.free_swap].
+#[no_mangle]
+pub extern "C" fn sysinfo_free_swap(system: CSystem) -> size_t {
+ assert!(!system.is_null());
+ unsafe {
+ let system: Box<System> = Box::from_raw(system as *mut System);
+ let ret = system.free_swap() as size_t;
+ Box::into_raw(system);
+ ret
+ }
+}
+
+/// Equivalent of [`System::used_swap()`][crate::System#method.used_swap].
+#[no_mangle]
+pub extern "C" fn sysinfo_used_swap(system: CSystem) -> size_t {
+ assert!(!system.is_null());
+ unsafe {
+ let system: Box<System> = Box::from_raw(system as *mut System);
+ let ret = system.used_swap() as size_t;
+ Box::into_raw(system);
+ ret
+ }
+}
+
+/// Equivalent of
+/// `system::networks().iter().fold(0, |acc, (_, data)| acc + data.received() as size_t)`.
+#[no_mangle]
+pub extern "C" fn sysinfo_networks_received(system: CSystem) -> size_t {
+ assert!(!system.is_null());
+ unsafe {
+ let system: Box<System> = Box::from_raw(system as *mut System);
+ let ret = system.networks().iter().fold(0, |acc: size_t, (_, data)| {
+ acc.saturating_add(data.received() as size_t)
+ });
+ Box::into_raw(system);
+ ret
+ }
+}
+
+/// Equivalent of
+/// `system::networks().iter().fold(0, |acc, (_, data)| acc + data.transmitted() as size_t)`.
+#[no_mangle]
+pub extern "C" fn sysinfo_networks_transmitted(system: CSystem) -> size_t {
+ assert!(!system.is_null());
+ unsafe {
+ let system: Box<System> = Box::from_raw(system as *mut System);
+ let ret = system.networks().iter().fold(0, |acc: size_t, (_, data)| {
+ acc.saturating_add(data.transmitted() as size_t)
+ });
+ Box::into_raw(system);
+ ret
+ }
+}
+
+/// 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.
+#[no_mangle]
+pub extern "C" fn sysinfo_cpus_usage(
+ system: CSystem,
+ length: *mut c_uint,
+ procs: *mut *mut c_float,
+) {
+ assert!(!system.is_null());
+ if procs.is_null() || length.is_null() {
+ return;
+ }
+ unsafe {
+ let system: Box<System> = Box::from_raw(system as *mut System);
+ {
+ let cpus = system.cpus();
+ if (*procs).is_null() {
+ (*procs) =
+ libc::malloc(::std::mem::size_of::<c_float>() * cpus.len()) as *mut c_float;
+ }
+ for (pos, cpu) in cpus.iter().skip(1).enumerate() {
+ (*(*procs).offset(pos as isize)) = cpu.cpu_usage();
+ }
+ *length = cpus.len() as c_uint - 1;
+ }
+ Box::into_raw(system);
+ }
+}
+
+/// Equivalent of [`System::processes()`][crate::System#method.processes]. Returns an
+/// array ended by a null pointer. Must be freed.
+///
+/// # /!\ WARNING /!\
+///
+/// While having this method returned processes, you should *never* call any refresh method!
+#[no_mangle]
+pub extern "C" fn sysinfo_processes(
+ system: CSystem,
+ fn_pointer: Option<ProcessLoop>,
+ data: *mut c_void,
+) -> size_t {
+ assert!(!system.is_null());
+ if let Some(fn_pointer) = fn_pointer {
+ unsafe {
+ let system: Box<System> = Box::from_raw(system as *mut System);
+ let len = {
+ let entries = system.processes();
+ for (pid, process) in entries {
+ if !fn_pointer(pid.0, process as *const Process as CProcess, data) {
+ break;
+ }
+ }
+ entries.len() as size_t
+ };
+ Box::into_raw(system);
+ len
+ }
+ } else {
+ 0
+ }
+}
+
+/// Equivalent of [`System::process()`][crate::System#method.process].
+///
+/// # /!\ WARNING /!\
+///
+/// While having this method returned process, you should *never* call any
+/// refresh method!
+#[no_mangle]
+pub extern "C" fn sysinfo_process_by_pid(system: CSystem, pid: pid_t) -> CProcess {
+ assert!(!system.is_null());
+ unsafe {
+ let system: Box<System> = Box::from_raw(system as *mut System);
+ let ret = if let Some(process) = system.process(Pid(pid)) {
+ process as *const Process as CProcess
+ } else {
+ std::ptr::null()
+ };
+ Box::into_raw(system);
+ ret
+ }
+}
+
+/// Equivalent of iterating over [`Process::tasks()`][crate::Process#method.tasks].
+///
+/// # /!\ WARNING /!\
+///
+/// While having this method processes, you should *never* call any refresh method!
+#[cfg(target_os = "linux")]
+#[no_mangle]
+pub extern "C" fn sysinfo_process_tasks(
+ process: CProcess,
+ fn_pointer: Option<ProcessLoop>,
+ data: *mut c_void,
+) -> size_t {
+ assert!(!process.is_null());
+ if let Some(fn_pointer) = fn_pointer {
+ unsafe {
+ let process = process as *const Process;
+ for (pid, process) in (*process).tasks.iter() {
+ if !fn_pointer(pid.0, process as *const Process as CProcess, data) {
+ break;
+ }
+ }
+ (*process).tasks.len() as size_t
+ }
+ } else {
+ 0
+ }
+}
+
+/// Equivalent of [`Process::pid()`][crate::Process#method.pid].
+#[no_mangle]
+pub extern "C" fn sysinfo_process_pid(process: CProcess) -> pid_t {
+ assert!(!process.is_null());
+ let process = process as *const Process;
+ unsafe { (*process).pid().0 }
+}
+
+/// Equivalent of [`Process::parent()`][crate::Process#method.parent].
+///
+/// In case there is no known parent, it returns `0`.
+#[no_mangle]
+pub extern "C" fn sysinfo_process_parent_pid(process: CProcess) -> pid_t {
+ assert!(!process.is_null());
+ let process = process as *const Process;
+ unsafe { (*process).parent().unwrap_or(Pid(0)).0 }
+}
+
+/// Equivalent of [`Process::cpu_usage()`][crate::Process#method.cpu_usage].
+#[no_mangle]
+pub extern "C" fn sysinfo_process_cpu_usage(process: CProcess) -> c_float {
+ assert!(!process.is_null());
+ let process = process as *const Process;
+ unsafe { (*process).cpu_usage() }
+}
+
+/// Equivalent of [`Process::memory()`][crate::Process#method.memory].
+#[no_mangle]
+pub extern "C" fn sysinfo_process_memory(process: CProcess) -> size_t {
+ assert!(!process.is_null());
+ let process = process as *const Process;
+ unsafe { (*process).memory() as usize }
+}
+
+/// Equivalent of [`Process::virtual_memory()`][crate::Process#method.virtual_memory].
+#[no_mangle]
+pub extern "C" fn sysinfo_process_virtual_memory(process: CProcess) -> size_t {
+ assert!(!process.is_null());
+ let process = process as *const Process;
+ unsafe { (*process).virtual_memory() as usize }
+}
+
+/// Equivalent of [`Process::exe()`][crate::Process#method.exe].
+#[no_mangle]
+pub extern "C" fn sysinfo_process_executable_path(process: CProcess) -> RString {
+ assert!(!process.is_null());
+ let process = process as *const Process;
+ unsafe {
+ if let Some(p) = (*process).exe().to_str() {
+ if let Ok(c) = CString::new(p) {
+ return c.into_raw() as _;
+ }
+ }
+ std::ptr::null()
+ }
+}
+
+/// Equivalent of [`Process::root()`][crate::Process#method.root].
+#[no_mangle]
+pub extern "C" fn sysinfo_process_root_directory(process: CProcess) -> RString {
+ assert!(!process.is_null());
+ let process = process as *const Process;
+ unsafe {
+ if let Some(p) = (*process).root().to_str() {
+ if let Ok(c) = CString::new(p) {
+ return c.into_raw() as _;
+ }
+ }
+ std::ptr::null()
+ }
+}
+
+/// Equivalent of [`Process::cwd()`][crate::Process#method.cwd].
+#[no_mangle]
+pub extern "C" fn sysinfo_process_current_directory(process: CProcess) -> RString {
+ assert!(!process.is_null());
+ let process = process as *const Process;
+ unsafe {
+ if let Some(p) = (*process).cwd().to_str() {
+ if let Ok(c) = CString::new(p) {
+ return c.into_raw() as _;
+ }
+ }
+ std::ptr::null()
+ }
+}
+
+/// Frees a C string created with `CString::into_raw()`.
+#[no_mangle]
+pub extern "C" fn sysinfo_rstring_free(s: RString) {
+ if !s.is_null() {
+ unsafe {
+ let _ = CString::from_raw(s as usize as *mut i8);
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/common.rs b/vendor/sysinfo/src/common.rs
new file mode 100644
index 000000000..15cbe87be
--- /dev/null
+++ b/vendor/sysinfo/src/common.rs
@@ -0,0 +1,965 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{NetworkData, Networks, NetworksExt, UserExt};
+
+use std::convert::From;
+use std::fmt;
+use std::str::FromStr;
+
+/// Trait to have a common conversions for the [`Pid`][crate::Pid] type.
+///
+/// ```
+/// use sysinfo::{Pid, PidExt};
+///
+/// let p = Pid::from_u32(0);
+/// let value: u32 = p.as_u32();
+/// ```
+pub trait PidExt<T>: Copy + From<T> + FromStr + fmt::Display {
+ /// Allows to convert [`Pid`][crate::Pid] into [`u32`].
+ ///
+ /// ```
+ /// use sysinfo::{Pid, PidExt};
+ ///
+ /// let p = Pid::from_u32(0);
+ /// let value: u32 = p.as_u32();
+ /// ```
+ fn as_u32(self) -> u32;
+ /// Allows to convert a [`u32`] into [`Pid`][crate::Pid].
+ ///
+ /// ```
+ /// use sysinfo::{Pid, PidExt};
+ ///
+ /// let p = Pid::from_u32(0);
+ /// ```
+ fn from_u32(v: u32) -> Self;
+}
+
+macro_rules! pid_decl {
+ ($typ:ty) => {
+ #[doc = include_str!("../md_doc/pid.md")]
+ #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
+ #[repr(transparent)]
+ pub struct Pid(pub(crate) $typ);
+
+ impl From<$typ> for Pid {
+ fn from(v: $typ) -> Self {
+ Self(v)
+ }
+ }
+ impl From<Pid> for $typ {
+ fn from(v: Pid) -> Self {
+ v.0
+ }
+ }
+ impl PidExt<$typ> for Pid {
+ fn as_u32(self) -> u32 {
+ self.0 as u32
+ }
+ fn from_u32(v: u32) -> Self {
+ Self(v as _)
+ }
+ }
+ impl FromStr for Pid {
+ type Err = <$typ as FromStr>::Err;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(Self(<$typ>::from_str(s)?))
+ }
+ }
+ impl fmt::Display for Pid {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.0)
+ }
+ }
+ };
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(all(
+ not(feature = "unknown-ci"),
+ any(
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "macos",
+ target_os = "ios",
+ )
+ ))] {
+ use libc::pid_t;
+
+ pid_decl!(pid_t);
+ } else {
+ pid_decl!(usize);
+ }
+}
+
+macro_rules! impl_get_set {
+ ($ty_name:ident, $name:ident, $with:ident, $without:ident $(, $extra_doc:literal)? $(,)?) => {
+ #[doc = concat!("Returns the value of the \"", stringify!($name), "\" refresh kind.")]
+ $(#[doc = concat!("
+", $extra_doc, "
+")])?
+ #[doc = concat!("
+```
+use sysinfo::", stringify!($ty_name), ";
+
+let r = ", stringify!($ty_name), "::new();
+assert_eq!(r.", stringify!($name), "(), false);
+
+let r = r.with_", stringify!($name), "();
+assert_eq!(r.", stringify!($name), "(), true);
+
+let r = r.without_", stringify!($name), "();
+assert_eq!(r.", stringify!($name), "(), false);
+```")]
+ pub fn $name(&self) -> bool {
+ self.$name
+ }
+
+ #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `true`.
+
+```
+use sysinfo::", stringify!($ty_name), ";
+
+let r = ", stringify!($ty_name), "::new();
+assert_eq!(r.", stringify!($name), "(), false);
+
+let r = r.with_", stringify!($name), "();
+assert_eq!(r.", stringify!($name), "(), true);
+```")]
+ #[must_use]
+ pub fn $with(mut self) -> Self {
+ self.$name = true;
+ self
+ }
+
+ #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `false`.
+
+```
+use sysinfo::", stringify!($ty_name), ";
+
+let r = ", stringify!($ty_name), "::everything();
+assert_eq!(r.", stringify!($name), "(), true);
+
+let r = r.without_", stringify!($name), "();
+assert_eq!(r.", stringify!($name), "(), false);
+```")]
+ #[must_use]
+ pub fn $without(mut self) -> Self {
+ self.$name = false;
+ self
+ }
+ };
+
+ ($ty_name:ident, $name:ident, $with:ident, $without:ident, $typ:ty $(,)?) => {
+ #[doc = concat!("Returns the value of the \"", stringify!($name), "\" refresh kind.
+
+```
+use sysinfo::{", stringify!($ty_name), ", ", stringify!($typ), "};
+
+let r = ", stringify!($ty_name), "::new();
+assert_eq!(r.", stringify!($name), "().is_some(), false);
+
+let r = r.with_", stringify!($name), "(", stringify!($typ), "::everything());
+assert_eq!(r.", stringify!($name), "().is_some(), true);
+
+let r = r.without_", stringify!($name), "();
+assert_eq!(r.", stringify!($name), "().is_some(), false);
+```")]
+ pub fn $name(&self) -> Option<$typ> {
+ self.$name
+ }
+
+ #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `true`.
+
+```
+use sysinfo::{", stringify!($ty_name), ", ", stringify!($typ), "};
+
+let r = ", stringify!($ty_name), "::new();
+assert_eq!(r.", stringify!($name), "().is_some(), false);
+
+let r = r.with_", stringify!($name), "(", stringify!($typ), "::everything());
+assert_eq!(r.", stringify!($name), "().is_some(), true);
+```")]
+ #[must_use]
+ pub fn $with(mut self, kind: $typ) -> Self {
+ self.$name = Some(kind);
+ self
+ }
+
+ #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `false`.
+
+```
+use sysinfo::", stringify!($ty_name), ";
+
+let r = ", stringify!($ty_name), "::everything();
+assert_eq!(r.", stringify!($name), "().is_some(), true);
+
+let r = r.without_", stringify!($name), "();
+assert_eq!(r.", stringify!($name), "().is_some(), false);
+```")]
+ #[must_use]
+ pub fn $without(mut self) -> Self {
+ self.$name = None;
+ self
+ }
+ };
+}
+
+/// Used to determine what you want to refresh specifically on the [`Process`] type.
+///
+/// ⚠️ Just like all other refresh types, ruling out a refresh doesn't assure you that
+/// the information won't be retrieved if the information is accessible without needing
+/// extra computation.
+///
+/// ```
+/// use sysinfo::{ProcessExt, ProcessRefreshKind, System, SystemExt};
+///
+/// let mut system = System::new();
+///
+/// // We don't want to update the CPU information.
+/// system.refresh_processes_specifics(ProcessRefreshKind::everything().without_cpu());
+///
+/// for (_, proc_) in system.processes() {
+/// // We use a `==` comparison on float only because we know it's set to 0 here.
+/// assert_eq!(proc_.cpu_usage(), 0.);
+/// }
+/// ```
+///
+/// [`Process`]: crate::Process
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub struct ProcessRefreshKind {
+ cpu: bool,
+ disk_usage: bool,
+ user: bool,
+}
+
+impl ProcessRefreshKind {
+ /// Creates a new `ProcessRefreshKind` with every refresh set to `false`.
+ ///
+ /// ```
+ /// use sysinfo::ProcessRefreshKind;
+ ///
+ /// let r = ProcessRefreshKind::new();
+ ///
+ /// assert_eq!(r.cpu(), false);
+ /// assert_eq!(r.disk_usage(), false);
+ /// ```
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Creates a new `ProcessRefreshKind` with every refresh set to `true`.
+ ///
+ /// ```
+ /// use sysinfo::ProcessRefreshKind;
+ ///
+ /// let r = ProcessRefreshKind::everything();
+ ///
+ /// assert_eq!(r.cpu(), true);
+ /// assert_eq!(r.disk_usage(), true);
+ /// ```
+ pub fn everything() -> Self {
+ Self {
+ cpu: true,
+ disk_usage: true,
+ user: true,
+ }
+ }
+
+ impl_get_set!(ProcessRefreshKind, cpu, with_cpu, without_cpu);
+ impl_get_set!(
+ ProcessRefreshKind,
+ disk_usage,
+ with_disk_usage,
+ without_disk_usage
+ );
+ impl_get_set!(
+ ProcessRefreshKind,
+ user,
+ with_user,
+ without_user,
+ r#"This refresh is about `user_id` and `group_id`. Please note that it has an effect mostly
+on Windows as other platforms get this information alongside the Process information directly."#,
+ );
+}
+
+/// Used to determine what you want to refresh specifically on the [`Process`] type.
+///
+/// ⚠️ Just like all other refresh types, ruling out a refresh doesn't assure you that
+/// the information won't be retrieved if the information is accessible without needing
+/// extra computation.
+///
+/// ```
+/// use sysinfo::{CpuExt, CpuRefreshKind, System, SystemExt};
+///
+/// let mut system = System::new();
+///
+/// // We don't want to update all the CPU information.
+/// system.refresh_cpu_specifics(CpuRefreshKind::everything().without_frequency());
+///
+/// for cpu in system.cpus() {
+/// assert_eq!(cpu.frequency(), 0);
+/// }
+/// ```
+///
+/// [`Process`]: crate::Process
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub struct CpuRefreshKind {
+ cpu_usage: bool,
+ frequency: bool,
+}
+
+impl CpuRefreshKind {
+ /// Creates a new `CpuRefreshKind` with every refresh set to `false`.
+ ///
+ /// ```
+ /// use sysinfo::CpuRefreshKind;
+ ///
+ /// let r = CpuRefreshKind::new();
+ ///
+ /// assert_eq!(r.frequency(), false);
+ /// ```
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Creates a new `CpuRefreshKind` with every refresh set to `true`.
+ ///
+ /// ```
+ /// use sysinfo::CpuRefreshKind;
+ ///
+ /// let r = CpuRefreshKind::everything();
+ ///
+ /// assert_eq!(r.frequency(), true);
+ /// ```
+ pub fn everything() -> Self {
+ Self {
+ cpu_usage: true,
+ frequency: true,
+ }
+ }
+
+ impl_get_set!(CpuRefreshKind, cpu_usage, with_cpu_usage, without_cpu_usage);
+ impl_get_set!(CpuRefreshKind, frequency, with_frequency, without_frequency);
+}
+
+/// Used to determine what you want to refresh specifically on the [`System`] type.
+///
+/// ⚠️ Just like all other refresh types, ruling out a refresh doesn't assure you that
+/// the information won't be retrieved if the information is accessible without needing
+/// extra computation.
+///
+/// ```
+/// use sysinfo::{RefreshKind, System, SystemExt};
+///
+/// // We want everything except disks.
+/// let mut system = System::new_with_specifics(RefreshKind::everything().without_disks_list());
+///
+/// assert_eq!(system.disks().len(), 0);
+/// # if System::IS_SUPPORTED && !cfg!(feature = "apple-sandbox") {
+/// assert!(system.processes().len() > 0);
+/// # }
+/// ```
+///
+/// [`System`]: crate::System
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub struct RefreshKind {
+ networks: bool,
+ networks_list: bool,
+ processes: Option<ProcessRefreshKind>,
+ disks_list: bool,
+ disks: bool,
+ memory: bool,
+ cpu: Option<CpuRefreshKind>,
+ components: bool,
+ components_list: bool,
+ users_list: bool,
+}
+
+impl RefreshKind {
+ /// Creates a new `RefreshKind` with every refresh set to `false`/`None`.
+ ///
+ /// ```
+ /// use sysinfo::RefreshKind;
+ ///
+ /// let r = RefreshKind::new();
+ ///
+ /// assert_eq!(r.networks(), false);
+ /// assert_eq!(r.networks_list(), false);
+ /// assert_eq!(r.processes().is_some(), false);
+ /// assert_eq!(r.disks_list(), false);
+ /// assert_eq!(r.disks(), false);
+ /// assert_eq!(r.memory(), false);
+ /// assert_eq!(r.cpu().is_some(), false);
+ /// assert_eq!(r.components(), false);
+ /// assert_eq!(r.components_list(), false);
+ /// assert_eq!(r.users_list(), false);
+ /// ```
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Creates a new `RefreshKind` with every refresh set to `true`/`Some(...)`.
+ ///
+ /// ```
+ /// use sysinfo::RefreshKind;
+ ///
+ /// let r = RefreshKind::everything();
+ ///
+ /// assert_eq!(r.networks(), true);
+ /// assert_eq!(r.networks_list(), true);
+ /// assert_eq!(r.processes().is_some(), true);
+ /// assert_eq!(r.disks_list(), true);
+ /// assert_eq!(r.disks(), true);
+ /// assert_eq!(r.memory(), true);
+ /// assert_eq!(r.cpu().is_some(), true);
+ /// assert_eq!(r.components(), true);
+ /// assert_eq!(r.components_list(), true);
+ /// assert_eq!(r.users_list(), true);
+ /// ```
+ pub fn everything() -> Self {
+ Self {
+ networks: true,
+ networks_list: true,
+ processes: Some(ProcessRefreshKind::everything()),
+ disks: true,
+ disks_list: true,
+ memory: true,
+ cpu: Some(CpuRefreshKind::everything()),
+ components: true,
+ components_list: true,
+ users_list: true,
+ }
+ }
+
+ impl_get_set!(
+ RefreshKind,
+ processes,
+ with_processes,
+ without_processes,
+ ProcessRefreshKind
+ );
+ impl_get_set!(RefreshKind, networks, with_networks, without_networks);
+ impl_get_set!(
+ RefreshKind,
+ networks_list,
+ with_networks_list,
+ without_networks_list
+ );
+ impl_get_set!(RefreshKind, disks, with_disks, without_disks);
+ impl_get_set!(RefreshKind, disks_list, with_disks_list, without_disks_list);
+ impl_get_set!(RefreshKind, memory, with_memory, without_memory);
+ impl_get_set!(RefreshKind, cpu, with_cpu, without_cpu, CpuRefreshKind);
+ impl_get_set!(RefreshKind, components, with_components, without_components);
+ impl_get_set!(
+ RefreshKind,
+ components_list,
+ with_components_list,
+ without_components_list
+ );
+ impl_get_set!(RefreshKind, users_list, with_users_list, without_users_list);
+}
+
+/// Iterator over network interfaces.
+///
+/// It is returned by [`Networks::iter`][crate::Networks#method.iter].
+///
+/// ```no_run
+/// use sysinfo::{System, SystemExt, NetworksExt};
+///
+/// let system = System::new_all();
+/// let networks_iter = system.networks().iter();
+/// ```
+pub struct NetworksIter<'a> {
+ inner: std::collections::hash_map::Iter<'a, String, NetworkData>,
+}
+
+impl<'a> NetworksIter<'a> {
+ pub(crate) fn new(v: std::collections::hash_map::Iter<'a, String, NetworkData>) -> Self {
+ NetworksIter { inner: v }
+ }
+}
+
+impl<'a> Iterator for NetworksIter<'a> {
+ type Item = (&'a String, &'a NetworkData);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.inner.next()
+ }
+}
+
+impl<'a> IntoIterator for &'a Networks {
+ type Item = (&'a String, &'a NetworkData);
+ type IntoIter = NetworksIter<'a>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+/// Enum containing the different supported disks types.
+///
+/// This type is returned by [`Disk::get_type`][crate::Disk#method.type].
+///
+/// ```no_run
+/// use sysinfo::{System, SystemExt, DiskExt};
+///
+/// let system = System::new_all();
+/// for disk in system.disks() {
+/// println!("{:?}: {:?}", disk.name(), disk.type_());
+/// }
+/// ```
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum DiskType {
+ /// HDD type.
+ HDD,
+ /// SSD type.
+ SSD,
+ /// Unknown type.
+ Unknown(isize),
+}
+
+/// An enum representing signals on UNIX-like systems.
+///
+/// On non-unix systems, this enum is mostly useless and is only there to keep coherency between
+/// the different OSes.
+///
+/// If you want the list of the supported signals on the current system, use
+/// [`SystemExt::SUPPORTED_SIGNALS`][crate::SystemExt::SUPPORTED_SIGNALS].
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Debug)]
+pub enum Signal {
+ /// Hangup detected on controlling terminal or death of controlling process.
+ Hangup,
+ /// Interrupt from keyboard.
+ Interrupt,
+ /// Quit from keyboard.
+ Quit,
+ /// Illegal instruction.
+ Illegal,
+ /// Trace/breakpoint trap.
+ Trap,
+ /// Abort signal from C abort function.
+ Abort,
+ /// IOT trap. A synonym for SIGABRT.
+ IOT,
+ /// Bus error (bad memory access).
+ Bus,
+ /// Floating point exception.
+ FloatingPointException,
+ /// Kill signal.
+ Kill,
+ /// User-defined signal 1.
+ User1,
+ /// Invalid memory reference.
+ Segv,
+ /// User-defined signal 2.
+ User2,
+ /// Broken pipe: write to pipe with no readers.
+ Pipe,
+ /// Timer signal from C alarm function.
+ Alarm,
+ /// Termination signal.
+ Term,
+ /// Child stopped or terminated.
+ Child,
+ /// Continue if stopped.
+ Continue,
+ /// Stop process.
+ Stop,
+ /// Stop typed at terminal.
+ TSTP,
+ /// Terminal input for background process.
+ TTIN,
+ /// Terminal output for background process.
+ TTOU,
+ /// Urgent condition on socket.
+ Urgent,
+ /// CPU time limit exceeded.
+ XCPU,
+ /// File size limit exceeded.
+ XFSZ,
+ /// Virtual alarm clock.
+ VirtualAlarm,
+ /// Profiling time expired.
+ Profiling,
+ /// Windows resize signal.
+ Winch,
+ /// I/O now possible.
+ IO,
+ /// Pollable event (Sys V). Synonym for IO
+ Poll,
+ /// Power failure (System V).
+ ///
+ /// Doesn't exist on apple systems so will be ignored.
+ Power,
+ /// Bad argument to routine (SVr4).
+ Sys,
+}
+
+impl std::fmt::Display for Signal {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let s = match *self {
+ Self::Hangup => "Hangup",
+ Self::Interrupt => "Interrupt",
+ Self::Quit => "Quit",
+ Self::Illegal => "Illegal",
+ Self::Trap => "Trap",
+ Self::Abort => "Abort",
+ Self::IOT => "IOT",
+ Self::Bus => "Bus",
+ Self::FloatingPointException => "FloatingPointException",
+ Self::Kill => "Kill",
+ Self::User1 => "User1",
+ Self::Segv => "Segv",
+ Self::User2 => "User2",
+ Self::Pipe => "Pipe",
+ Self::Alarm => "Alarm",
+ Self::Term => "Term",
+ Self::Child => "Child",
+ Self::Continue => "Continue",
+ Self::Stop => "Stop",
+ Self::TSTP => "TSTP",
+ Self::TTIN => "TTIN",
+ Self::TTOU => "TTOU",
+ Self::Urgent => "Urgent",
+ Self::XCPU => "XCPU",
+ Self::XFSZ => "XFSZ",
+ Self::VirtualAlarm => "VirtualAlarm",
+ Self::Profiling => "Profiling",
+ Self::Winch => "Winch",
+ Self::IO => "IO",
+ Self::Poll => "Poll",
+ Self::Power => "Power",
+ Self::Sys => "Sys",
+ };
+ f.write_str(s)
+ }
+}
+
+/// A struct representing system load average value.
+///
+/// It is returned by [`SystemExt::load_average`][crate::SystemExt::load_average].
+///
+/// ```no_run
+/// use sysinfo::{System, SystemExt};
+///
+/// let s = System::new_all();
+/// let load_avg = s.load_average();
+/// println!(
+/// "one minute: {}%, five minutes: {}%, fifteen minutes: {}%",
+/// load_avg.one,
+/// load_avg.five,
+/// load_avg.fifteen,
+/// );
+/// ```
+#[repr(C)]
+#[derive(Default, Debug, Clone)]
+pub struct LoadAvg {
+ /// Average load within one minute.
+ pub one: f64,
+ /// Average load within five minutes.
+ pub five: f64,
+ /// Average load within fifteen minutes.
+ pub fifteen: f64,
+}
+
+macro_rules! xid {
+ ($(#[$outer:meta])+ $name:ident, $type:ty) => {
+ $(#[$outer])+
+ #[repr(transparent)]
+ #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+ pub struct $name(pub(crate) $type);
+
+ impl std::ops::Deref for $name {
+ type Target = $type;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+ };
+}
+
+macro_rules! uid {
+ ($(#[$outer:meta])+ $type:ty) => {
+ xid!($(#[$outer])+ Uid, $type);
+ };
+}
+
+macro_rules! gid {
+ ($(#[$outer:meta])+ $type:ty) => {
+ xid!($(#[$outer])+ #[derive(Copy)] Gid, $type);
+ };
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(all(
+ not(feature = "unknown-ci"),
+ any(
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "macos",
+ 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
+ );
+ } 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
+ );
+ } else {
+ uid!(
+ /// A user id wrapping a platform specific type.
+ u32
+ );
+ gid!(
+ /// A group id wrapping a platform specific type.
+ u32
+ );
+
+ }
+}
+
+/// Type containing user information.
+///
+/// It is returned by [`SystemExt::users`][crate::SystemExt::users].
+///
+/// ```no_run
+/// use sysinfo::{System, SystemExt};
+///
+/// let s = System::new_all();
+/// println!("users: {:?}", s.users());
+/// ```
+#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub struct User {
+ pub(crate) uid: Uid,
+ pub(crate) gid: Gid,
+ pub(crate) name: String,
+ pub(crate) groups: Vec<String>,
+}
+
+impl UserExt for User {
+ fn id(&self) -> &Uid {
+ &self.uid
+ }
+
+ fn group_id(&self) -> Gid {
+ self.gid
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ fn groups(&self) -> &[String] {
+ &self.groups
+ }
+}
+
+/// Type containing read and written bytes.
+///
+/// It is returned by [`ProcessExt::disk_usage`][crate::ProcessExt::disk_usage].
+///
+/// ```no_run
+/// use sysinfo::{ProcessExt, System, SystemExt};
+///
+/// let s = System::new_all();
+/// for (pid, process) in s.processes() {
+/// let disk_usage = process.disk_usage();
+/// println!("[{}] read bytes : new/total => {}/{} B",
+/// pid,
+/// disk_usage.read_bytes,
+/// disk_usage.total_read_bytes,
+/// );
+/// println!("[{}] written bytes: new/total => {}/{} B",
+/// pid,
+/// disk_usage.written_bytes,
+/// disk_usage.total_written_bytes,
+/// );
+/// }
+/// ```
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd)]
+pub struct DiskUsage {
+ /// Total number of written bytes.
+ pub total_written_bytes: u64,
+ /// Number of written bytes since the last refresh.
+ pub written_bytes: u64,
+ /// Total number of read bytes.
+ pub total_read_bytes: u64,
+ /// Number of read bytes since the last refresh.
+ pub read_bytes: u64,
+}
+
+/// Enum describing the different status of a process.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum ProcessStatus {
+ /// ## Linux/FreeBSD
+ ///
+ /// Waiting in uninterruptible disk sleep.
+ ///
+ /// ## macOs
+ ///
+ /// Process being created by fork.
+ ///
+ /// ## Other OS
+ ///
+ /// Not available.
+ Idle,
+ /// Running.
+ Run,
+ /// ## Linux/FreeBSD
+ ///
+ /// Sleeping in an interruptible waiting.
+ ///
+ /// ## macOS
+ ///
+ /// Sleeping on an address.
+ ///
+ /// ## Other OS
+ ///
+ /// Not available.
+ Sleep,
+ /// ## Linux/FreeBSD
+ ///
+ /// Stopped (on a signal) or (before Linux 2.6.33) trace stopped.
+ ///
+ /// ## macOS
+ ///
+ /// Process debugging or suspension.
+ ///
+ /// ## Other OS
+ ///
+ /// Not available.
+ Stop,
+ /// ## Linux/FreeBSD/macOS
+ ///
+ /// Zombie process. Terminated but not reaped by its parent.
+ ///
+ /// ## Other OS
+ ///
+ /// Not available.
+ Zombie,
+ /// ## Linux
+ ///
+ /// Tracing stop (Linux 2.6.33 onward). Stopped by debugger during the tracing.
+ ///
+ /// ## Other OS
+ ///
+ /// Not available.
+ Tracing,
+ /// ## Linux/FreeBSD
+ ///
+ /// Dead/uninterruptible sleep (usually IO).
+ ///
+ /// ## Other OS
+ ///
+ /// Not available.
+ Dead,
+ /// ## Linux
+ ///
+ /// Wakekill (Linux 2.6.33 to 3.13 only).
+ ///
+ /// ## Other OS
+ ///
+ /// Not available.
+ Wakekill,
+ /// ## Linux
+ ///
+ /// Waking (Linux 2.6.33 to 3.13 only).
+ ///
+ /// ## Other OS
+ ///
+ /// Not available.
+ Waking,
+ /// ## Linux
+ ///
+ /// Parked (Linux 3.9 to 3.13 only).
+ ///
+ /// ## Other OS
+ ///
+ /// Not available.
+ Parked,
+ /// ## FreeBSD
+ ///
+ /// Blocked on a lock.
+ ///
+ /// ## Other OS
+ ///
+ /// Not available.
+ LockBlocked,
+ /// Unknown.
+ Unknown(u32),
+}
+
+/// Returns the pid for the current process.
+///
+/// `Err` is returned in case the platform isn't supported.
+///
+/// ```no_run
+/// use sysinfo::get_current_pid;
+///
+/// match get_current_pid() {
+/// Ok(pid) => {
+/// println!("current pid: {}", pid);
+/// }
+/// Err(e) => {
+/// eprintln!("failed to get current pid: {}", e);
+/// }
+/// }
+/// ```
+#[allow(clippy::unnecessary_wraps)]
+pub fn get_current_pid() -> Result<Pid, &'static str> {
+ cfg_if::cfg_if! {
+ if #[cfg(feature = "unknown-ci")] {
+ fn inner() -> Result<Pid, &'static str> {
+ Err("Unknown platform (CI)")
+ }
+ } else if #[cfg(any(
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "macos",
+ target_os = "ios",
+ ))] {
+ fn inner() -> Result<Pid, &'static str> {
+ unsafe { Ok(Pid(libc::getpid())) }
+ }
+ } else if #[cfg(windows)] {
+ fn inner() -> Result<Pid, &'static str> {
+ use winapi::um::processthreadsapi::GetCurrentProcessId;
+
+ unsafe { Ok(Pid(GetCurrentProcessId() as _)) }
+ }
+ } else {
+ fn inner() -> Result<Pid, &'static str> {
+ Err("Unknown platform")
+ }
+ }
+ }
+ inner()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::ProcessStatus;
+
+ // This test only exists to ensure that the `Display` trait is implemented on the
+ // `ProcessStatus` enum on all targets.
+ #[test]
+ fn check_display_impl_process_status() {
+ println!("{} {:?}", ProcessStatus::Parked, ProcessStatus::Idle);
+ }
+}
diff --git a/vendor/sysinfo/src/debug.rs b/vendor/sysinfo/src/debug.rs
new file mode 100644
index 000000000..e2f4361d9
--- /dev/null
+++ b/vendor/sysinfo/src/debug.rs
@@ -0,0 +1,132 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{
+ Component, ComponentExt, Cpu, CpuExt, Disk, DiskExt, NetworkData, NetworkExt, Networks,
+ NetworksExt, Process, ProcessExt, System, SystemExt,
+};
+
+use std::fmt;
+
+impl fmt::Debug for Cpu {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Cpu")
+ .field("name", &self.name())
+ .field("CPU usage", &self.cpu_usage())
+ .field("frequency", &self.frequency())
+ .field("vendor ID", &self.vendor_id())
+ .field("brand", &self.brand())
+ .finish()
+ }
+}
+
+impl fmt::Debug for System {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("System")
+ .field("global CPU usage", &self.global_cpu_info().cpu_usage())
+ .field("load average", &self.load_average())
+ .field("total memory", &self.total_memory())
+ .field("free memory", &self.free_memory())
+ .field("total swap", &self.total_swap())
+ .field("free swap", &self.free_swap())
+ .field("nb CPUs", &self.cpus().len())
+ .field("nb network interfaces", &self.networks().iter().count())
+ .field("nb processes", &self.processes().len())
+ .field("nb disks", &self.disks().len())
+ .field("nb components", &self.components().len())
+ .finish()
+ }
+}
+
+impl fmt::Debug for Disk {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ fmt,
+ "Disk({:?})[FS: {:?}][Type: {:?}][removable: {}] mounted on {:?}: {}/{} B",
+ self.name(),
+ self.file_system()
+ .iter()
+ .map(|c| *c as char)
+ .collect::<Vec<_>>(),
+ self.type_(),
+ if self.is_removable() { "yes" } else { "no" },
+ self.mount_point(),
+ self.available_space(),
+ self.total_space(),
+ )
+ }
+}
+
+impl fmt::Debug for Process {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Process")
+ .field("pid", &self.pid())
+ .field("parent", &self.parent())
+ .field("name", &self.name())
+ .field("environ", &self.environ())
+ .field("command", &self.cmd())
+ .field("executable path", &self.exe())
+ .field("current working directory", &self.cwd())
+ .field("memory usage", &self.memory())
+ .field("virtual memory usage", &self.virtual_memory())
+ .field("CPU usage", &self.cpu_usage())
+ .field("status", &self.status())
+ .field("root", &self.root())
+ .field("disk_usage", &self.disk_usage())
+ .finish()
+ }
+}
+
+impl fmt::Debug for Component {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if let Some(critical) = self.critical() {
+ write!(
+ f,
+ "{}: {}°C (max: {}°C / critical: {}°C)",
+ self.label(),
+ self.temperature(),
+ self.max(),
+ critical
+ )
+ } else {
+ write!(
+ f,
+ "{}: {}°C (max: {}°C)",
+ self.label(),
+ self.temperature(),
+ self.max()
+ )
+ }
+ }
+}
+
+impl fmt::Debug for Networks {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "Networks {{ {} }}",
+ self.iter()
+ .map(|x| format!("{:?}", x))
+ .collect::<Vec<_>>()
+ .join(", ")
+ )
+ }
+}
+
+impl fmt::Debug for NetworkData {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("NetworkData")
+ .field("income", &self.received())
+ .field("total income", &self.total_received())
+ .field("outcome", &self.transmitted())
+ .field("total outcome", &self.total_transmitted())
+ .field("packets income", &self.packets_received())
+ .field("total packets income", &self.total_packets_received())
+ .field("packets outcome", &self.packets_transmitted())
+ .field("total packets outcome", &self.total_packets_transmitted())
+ .field("errors income", &self.errors_on_received())
+ .field("total errors income", &self.total_errors_on_received())
+ .field("errors outcome", &self.errors_on_transmitted())
+ .field("total errors outcome", &self.total_errors_on_transmitted())
+ .finish()
+ }
+}
diff --git a/vendor/sysinfo/src/freebsd/component.rs b/vendor/sysinfo/src/freebsd/component.rs
new file mode 100644
index 000000000..6529be73c
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/component.rs
@@ -0,0 +1,71 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use super::utils::get_sys_value_by_name;
+use crate::ComponentExt;
+
+#[doc = include_str!("../../md_doc/component.md")]
+pub struct Component {
+ id: Vec<u8>,
+ label: String,
+ temperature: f32,
+ max: f32,
+}
+
+impl ComponentExt for Component {
+ fn temperature(&self) -> f32 {
+ self.temperature
+ }
+
+ fn max(&self) -> f32 {
+ self.max
+ }
+
+ fn critical(&self) -> Option<f32> {
+ None
+ }
+
+ fn label(&self) -> &str {
+ &self.label
+ }
+
+ fn refresh(&mut self) {
+ unsafe {
+ if let Some(temperature) = refresh_component(&self.id) {
+ self.temperature = temperature;
+ if self.temperature > self.max {
+ self.max = self.temperature;
+ }
+ }
+ }
+ }
+}
+
+unsafe fn refresh_component(id: &[u8]) -> Option<f32> {
+ let mut temperature: libc::c_int = 0;
+ if !get_sys_value_by_name(id, &mut temperature) {
+ None
+ } else {
+ // convert from Kelvin (x 10 -> 273.2 x 10) to Celsius
+ Some((temperature - 2732) as f32 / 10.)
+ }
+}
+
+pub unsafe fn get_components(nb_cpus: usize) -> Vec<Component> {
+ // For now, we only have temperature for CPUs...
+ let mut components = Vec::with_capacity(nb_cpus);
+
+ for core in 0..nb_cpus {
+ let id = format!("dev.cpu.{}.temperature\0", core)
+ .as_bytes()
+ .to_vec();
+ if let Some(temperature) = refresh_component(&id) {
+ components.push(Component {
+ id,
+ label: format!("CPU {}", core + 1),
+ temperature,
+ max: temperature,
+ });
+ }
+ }
+ components
+}
diff --git a/vendor/sysinfo/src/freebsd/cpu.rs b/vendor/sysinfo/src/freebsd/cpu.rs
new file mode 100644
index 000000000..9d6c8e506
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/cpu.rs
@@ -0,0 +1,44 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::CpuExt;
+
+#[doc = include_str!("../../md_doc/cpu.md")]
+pub struct Cpu {
+ pub(crate) cpu_usage: f32,
+ name: String,
+ pub(crate) vendor_id: String,
+ pub(crate) frequency: u64,
+}
+
+impl Cpu {
+ pub(crate) fn new(name: String, vendor_id: String, frequency: u64) -> Cpu {
+ Cpu {
+ cpu_usage: 0.,
+ name,
+ vendor_id,
+ frequency,
+ }
+ }
+}
+
+impl CpuExt for Cpu {
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ fn frequency(&self) -> u64 {
+ self.frequency
+ }
+
+ fn vendor_id(&self) -> &str {
+ &self.vendor_id
+ }
+
+ fn brand(&self) -> &str {
+ ""
+ }
+}
diff --git a/vendor/sysinfo/src/freebsd/disk.rs b/vendor/sysinfo/src/freebsd/disk.rs
new file mode 100644
index 000000000..5156f1215
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/disk.rs
@@ -0,0 +1,143 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{DiskExt, DiskType};
+
+use std::ffi::{OsStr, OsString};
+use std::path::{Path, PathBuf};
+
+use super::utils::c_buf_to_str;
+
+#[doc = include_str!("../../md_doc/disk.md")]
+pub struct Disk {
+ name: OsString,
+ c_mount_point: Vec<libc::c_char>,
+ mount_point: PathBuf,
+ total_space: u64,
+ available_space: u64,
+ file_system: Vec<u8>,
+ is_removable: bool,
+}
+
+impl DiskExt for Disk {
+ fn type_(&self) -> DiskType {
+ DiskType::Unknown(-1)
+ }
+
+ fn name(&self) -> &OsStr {
+ &self.name
+ }
+
+ fn file_system(&self) -> &[u8] {
+ &self.file_system
+ }
+
+ fn mount_point(&self) -> &Path {
+ &self.mount_point
+ }
+
+ fn total_space(&self) -> u64 {
+ self.total_space
+ }
+
+ fn available_space(&self) -> u64 {
+ self.available_space
+ }
+
+ fn is_removable(&self) -> bool {
+ self.is_removable
+ }
+
+ fn refresh(&mut self) -> bool {
+ unsafe {
+ let mut vfs: libc::statvfs = std::mem::zeroed();
+ refresh_disk(self, &mut vfs)
+ }
+ }
+}
+
+// FIXME: if you want to get disk I/O usage:
+// statfs.[f_syncwrites, f_asyncwrites, f_syncreads, f_asyncreads]
+
+unsafe fn refresh_disk(disk: &mut Disk, vfs: &mut libc::statvfs) -> bool {
+ if libc::statvfs(disk.c_mount_point.as_ptr() as *const _, vfs) < 0 {
+ return false;
+ }
+ let f_frsize: u64 = vfs.f_frsize as _;
+
+ disk.total_space = vfs.f_blocks.saturating_mul(f_frsize);
+ disk.available_space = vfs.f_favail.saturating_mul(f_frsize);
+ true
+}
+
+pub unsafe fn get_all_disks() -> Vec<Disk> {
+ let mut fs_infos: *mut libc::statfs = std::ptr::null_mut();
+
+ let count = libc::getmntinfo(&mut fs_infos, libc::MNT_WAIT);
+
+ if count < 1 {
+ return Vec::new();
+ }
+ let mut vfs: libc::statvfs = std::mem::zeroed();
+ let fs_infos: &[libc::statfs] = std::slice::from_raw_parts(fs_infos as _, count as _);
+ let mut disks = Vec::new();
+
+ for fs_info in fs_infos {
+ if fs_info.f_mntfromname[0] == 0 || fs_info.f_mntonname[0] == 0 {
+ // If we have missing information, no need to look any further...
+ continue;
+ }
+ let fs_type: &[libc::c_char] =
+ if let Some(pos) = fs_info.f_fstypename.iter().position(|x| *x == 0) {
+ &fs_info.f_fstypename[..pos]
+ } else {
+ &fs_info.f_fstypename
+ };
+ let fs_type: &[u8] = std::slice::from_raw_parts(fs_type.as_ptr() as _, fs_type.len());
+ match fs_type {
+ b"autofs" | b"devfs" | b"linprocfs" | b"procfs" | b"fdesckfs" | b"tmpfs"
+ | b"linsysfs" => {
+ sysinfo_debug!(
+ "Memory filesystem `{:?}`, ignoring it.",
+ c_buf_to_str(&fs_info.f_fstypename).unwrap(),
+ );
+ continue;
+ }
+ _ => {}
+ }
+
+ if libc::statvfs(fs_info.f_mntonname.as_ptr(), &mut vfs) != 0 {
+ continue;
+ }
+
+ let mount_point = match c_buf_to_str(&fs_info.f_mntonname) {
+ Some(m) => m,
+ None => {
+ sysinfo_debug!("Cannot get disk mount point, ignoring it.");
+ continue;
+ }
+ };
+
+ let name = if mount_point == "/" {
+ OsString::from("root")
+ } else {
+ OsString::from(mount_point)
+ };
+
+ // USB keys and CDs are removable.
+ let is_removable =
+ [b"USB", b"usb"].iter().any(|b| b == &fs_type) || fs_type.starts_with(b"/dev/cd");
+
+ let f_frsize: u64 = vfs.f_frsize as _;
+
+ disks.push(Disk {
+ name,
+ c_mount_point: fs_info.f_mntonname.to_vec(),
+ mount_point: PathBuf::from(mount_point),
+ total_space: vfs.f_blocks.saturating_mul(f_frsize),
+ available_space: vfs.f_favail.saturating_mul(f_frsize),
+ file_system: fs_type.to_vec(),
+ is_removable,
+ });
+ }
+ disks
+}
diff --git a/vendor/sysinfo/src/freebsd/mod.rs b/vendor/sysinfo/src/freebsd/mod.rs
new file mode 100644
index 000000000..8a8726812
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/mod.rs
@@ -0,0 +1,16 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+pub mod component;
+pub mod cpu;
+pub mod disk;
+pub mod network;
+pub mod process;
+pub mod system;
+mod utils;
+
+pub use self::component::Component;
+pub use self::cpu::Cpu;
+pub use self::disk::Disk;
+pub use self::network::{NetworkData, Networks};
+pub use self::process::Process;
+pub use self::system::System;
diff --git a/vendor/sysinfo/src/freebsd/network.rs b/vendor/sysinfo/src/freebsd/network.rs
new file mode 100644
index 000000000..e58ad823a
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/network.rs
@@ -0,0 +1,199 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use std::collections::{hash_map, HashMap};
+use std::mem::MaybeUninit;
+
+use super::utils;
+use crate::{NetworkExt, NetworksExt, NetworksIter};
+
+macro_rules! old_and_new {
+ ($ty_:expr, $name:ident, $old:ident, $data:expr) => {{
+ $ty_.$old = $ty_.$name;
+ $ty_.$name = $data.$name;
+ }};
+}
+
+#[doc = include_str!("../../md_doc/networks.md")]
+pub struct Networks {
+ interfaces: HashMap<String, NetworkData>,
+}
+
+impl Networks {
+ pub(crate) fn new() -> Networks {
+ Networks {
+ interfaces: HashMap::new(),
+ }
+ }
+}
+
+impl NetworksExt for Networks {
+ fn iter(&self) -> NetworksIter {
+ NetworksIter::new(self.interfaces.iter())
+ }
+
+ fn refresh_networks_list(&mut self) {
+ unsafe {
+ self.refresh_interfaces(true);
+ }
+ // Remove interfaces which are gone.
+ self.interfaces.retain(|_, n| n.updated);
+ }
+
+ fn refresh(&mut self) {
+ unsafe {
+ self.refresh_interfaces(false);
+ }
+ }
+}
+
+impl Networks {
+ unsafe fn refresh_interfaces(&mut self, refresh_all: bool) {
+ let mut nb_interfaces: libc::c_int = 0;
+ if !utils::get_sys_value(
+ &[
+ libc::CTL_NET,
+ libc::PF_LINK,
+ libc::NETLINK_GENERIC,
+ libc::IFMIB_SYSTEM,
+ libc::IFMIB_IFCOUNT,
+ ],
+ &mut nb_interfaces,
+ ) {
+ return;
+ }
+ if refresh_all {
+ // We don't need to update this value if we're not updating all interfaces.
+ for interface in self.interfaces.values_mut() {
+ interface.updated = false;
+ }
+ }
+ let mut data: libc::ifmibdata = MaybeUninit::zeroed().assume_init();
+ for row in 1..nb_interfaces {
+ let mib = [
+ libc::CTL_NET,
+ libc::PF_LINK,
+ libc::NETLINK_GENERIC,
+ libc::IFMIB_IFDATA,
+ row,
+ libc::IFDATA_GENERAL,
+ ];
+
+ if !utils::get_sys_value(&mib, &mut data) {
+ continue;
+ }
+ if let Some(name) = utils::c_buf_to_string(&data.ifmd_name) {
+ let data = &data.ifmd_data;
+ match self.interfaces.entry(name) {
+ hash_map::Entry::Occupied(mut e) => {
+ let mut interface = e.get_mut();
+
+ old_and_new!(interface, ifi_ibytes, old_ifi_ibytes, data);
+ old_and_new!(interface, ifi_obytes, old_ifi_obytes, data);
+ old_and_new!(interface, ifi_ipackets, old_ifi_ipackets, data);
+ old_and_new!(interface, ifi_opackets, old_ifi_opackets, data);
+ old_and_new!(interface, ifi_ierrors, old_ifi_ierrors, data);
+ old_and_new!(interface, ifi_oerrors, old_ifi_oerrors, data);
+ interface.updated = true;
+ }
+ hash_map::Entry::Vacant(e) => {
+ if !refresh_all {
+ // This is simply a refresh, we don't want to add new interfaces!
+ continue;
+ }
+ e.insert(NetworkData {
+ ifi_ibytes: data.ifi_ibytes,
+ old_ifi_ibytes: 0,
+ ifi_obytes: data.ifi_obytes,
+ old_ifi_obytes: 0,
+ ifi_ipackets: data.ifi_ipackets,
+ old_ifi_ipackets: 0,
+ ifi_opackets: data.ifi_opackets,
+ old_ifi_opackets: 0,
+ ifi_ierrors: data.ifi_ierrors,
+ old_ifi_ierrors: 0,
+ ifi_oerrors: data.ifi_oerrors,
+ old_ifi_oerrors: 0,
+ updated: true,
+ });
+ }
+ }
+ }
+ }
+ }
+}
+
+#[doc = include_str!("../../md_doc/network_data.md")]
+pub struct NetworkData {
+ /// Total number of bytes received over interface.
+ ifi_ibytes: u64,
+ old_ifi_ibytes: u64,
+ /// Total number of bytes transmitted over interface.
+ ifi_obytes: u64,
+ old_ifi_obytes: u64,
+ /// Total number of packets received.
+ ifi_ipackets: u64,
+ old_ifi_ipackets: u64,
+ /// Total number of packets transmitted.
+ ifi_opackets: u64,
+ old_ifi_opackets: u64,
+ /// Shows the total number of packets received with error. This includes
+ /// too-long-frames errors, ring-buffer overflow errors, CRC errors,
+ /// frame alignment errors, fifo overruns, and missed packets.
+ ifi_ierrors: u64,
+ old_ifi_ierrors: u64,
+ /// similar to `ifi_ierrors`
+ ifi_oerrors: u64,
+ old_ifi_oerrors: u64,
+ /// Whether or not the above data has been updated during refresh
+ updated: bool,
+}
+
+impl NetworkExt for NetworkData {
+ fn received(&self) -> u64 {
+ self.ifi_ibytes.saturating_sub(self.old_ifi_ibytes)
+ }
+
+ fn total_received(&self) -> u64 {
+ self.ifi_ibytes
+ }
+
+ fn transmitted(&self) -> u64 {
+ self.ifi_obytes.saturating_sub(self.old_ifi_obytes)
+ }
+
+ fn total_transmitted(&self) -> u64 {
+ self.ifi_obytes
+ }
+
+ fn packets_received(&self) -> u64 {
+ self.ifi_ipackets.saturating_sub(self.old_ifi_ipackets)
+ }
+
+ fn total_packets_received(&self) -> u64 {
+ self.ifi_ipackets
+ }
+
+ fn packets_transmitted(&self) -> u64 {
+ self.ifi_opackets.saturating_sub(self.old_ifi_opackets)
+ }
+
+ fn total_packets_transmitted(&self) -> u64 {
+ self.ifi_opackets
+ }
+
+ fn errors_on_received(&self) -> u64 {
+ self.ifi_ierrors.saturating_sub(self.old_ifi_ierrors)
+ }
+
+ fn total_errors_on_received(&self) -> u64 {
+ self.ifi_ierrors
+ }
+
+ fn errors_on_transmitted(&self) -> u64 {
+ self.ifi_oerrors.saturating_sub(self.old_ifi_oerrors)
+ }
+
+ fn total_errors_on_transmitted(&self) -> u64 {
+ self.ifi_oerrors
+ }
+}
diff --git a/vendor/sysinfo/src/freebsd/process.rs b/vendor/sysinfo/src/freebsd/process.rs
new file mode 100644
index 000000000..b3302edbe
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/process.rs
@@ -0,0 +1,254 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid};
+
+use std::fmt;
+use std::path::{Path, PathBuf};
+
+use super::utils::{get_sys_value_str, WrapMap};
+
+#[doc(hidden)]
+impl From<libc::c_char> for ProcessStatus {
+ fn from(status: libc::c_char) -> ProcessStatus {
+ match status {
+ libc::SIDL => ProcessStatus::Idle,
+ libc::SRUN => ProcessStatus::Run,
+ libc::SSLEEP => ProcessStatus::Sleep,
+ libc::SSTOP => ProcessStatus::Stop,
+ libc::SZOMB => ProcessStatus::Zombie,
+ libc::SWAIT => ProcessStatus::Dead,
+ libc::SLOCK => ProcessStatus::LockBlocked,
+ x => ProcessStatus::Unknown(x as _),
+ }
+ }
+}
+
+impl fmt::Display for ProcessStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(match *self {
+ ProcessStatus::Idle => "Idle",
+ ProcessStatus::Run => "Runnable",
+ ProcessStatus::Sleep => "Sleeping",
+ ProcessStatus::Stop => "Stopped",
+ ProcessStatus::Zombie => "Zombie",
+ ProcessStatus::Dead => "Dead",
+ ProcessStatus::LockBlocked => "LockBlocked",
+ _ => "Unknown",
+ })
+ }
+}
+
+#[doc = include_str!("../../md_doc/process.md")]
+pub struct Process {
+ pub(crate) name: String,
+ pub(crate) cmd: Vec<String>,
+ pub(crate) exe: PathBuf,
+ pub(crate) pid: Pid,
+ parent: Option<Pid>,
+ pub(crate) environ: Vec<String>,
+ pub(crate) cwd: PathBuf,
+ pub(crate) root: PathBuf,
+ pub(crate) memory: u64,
+ pub(crate) virtual_memory: u64,
+ pub(crate) updated: bool,
+ cpu_usage: f32,
+ start_time: u64,
+ run_time: u64,
+ pub(crate) status: ProcessStatus,
+ user_id: Uid,
+ group_id: Gid,
+ read_bytes: u64,
+ old_read_bytes: u64,
+ written_bytes: u64,
+ old_written_bytes: u64,
+}
+
+impl ProcessExt for Process {
+ fn kill_with(&self, signal: Signal) -> Option<bool> {
+ let c_signal = super::system::convert_signal(signal)?;
+ unsafe { Some(libc::kill(self.pid.0, c_signal) == 0) }
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ fn cmd(&self) -> &[String] {
+ &self.cmd
+ }
+
+ fn exe(&self) -> &Path {
+ self.exe.as_path()
+ }
+
+ fn pid(&self) -> Pid {
+ self.pid
+ }
+
+ fn environ(&self) -> &[String] {
+ &self.environ
+ }
+
+ fn cwd(&self) -> &Path {
+ self.cwd.as_path()
+ }
+
+ fn root(&self) -> &Path {
+ self.root.as_path()
+ }
+
+ fn memory(&self) -> u64 {
+ self.memory
+ }
+
+ fn virtual_memory(&self) -> u64 {
+ self.virtual_memory
+ }
+
+ fn parent(&self) -> Option<Pid> {
+ self.parent
+ }
+
+ fn status(&self) -> ProcessStatus {
+ self.status
+ }
+
+ fn start_time(&self) -> u64 {
+ self.start_time
+ }
+
+ fn run_time(&self) -> u64 {
+ self.run_time
+ }
+
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn disk_usage(&self) -> DiskUsage {
+ DiskUsage {
+ written_bytes: self.written_bytes.saturating_sub(self.old_written_bytes),
+ total_written_bytes: self.written_bytes,
+ read_bytes: self.read_bytes.saturating_sub(self.old_read_bytes),
+ total_read_bytes: self.read_bytes,
+ }
+ }
+
+ fn user_id(&self) -> Option<&Uid> {
+ Some(&self.user_id)
+ }
+
+ fn group_id(&self) -> Option<Gid> {
+ Some(self.group_id)
+ }
+}
+
+pub(crate) unsafe fn get_process_data(
+ kproc: &libc::kinfo_proc,
+ wrap: &WrapMap,
+ page_size: isize,
+ fscale: f32,
+ now: u64,
+ refresh_kind: ProcessRefreshKind,
+) -> Result<Option<Process>, ()> {
+ if kproc.ki_pid != 1 && (kproc.ki_flag as libc::c_int & libc::P_SYSTEM) != 0 {
+ // We filter out the kernel threads.
+ return Err(());
+ }
+
+ // We now get the values needed for both new and existing process.
+ let cpu_usage = if refresh_kind.cpu() {
+ (100 * kproc.ki_pctcpu) as f32 / fscale
+ } else {
+ 0.
+ };
+ // Processes can be reparented apparently?
+ let parent = if kproc.ki_ppid != 0 {
+ Some(Pid(kproc.ki_ppid))
+ } else {
+ None
+ };
+ let status = ProcessStatus::from(kproc.ki_stat);
+
+ // from FreeBSD source /src/usr.bin/top/machine.c
+ let virtual_memory = (kproc.ki_size / 1_000) as u64;
+ let memory = (kproc.ki_rssize as u64).saturating_mul(page_size as _) / 1_000;
+ // FIXME: This is to get the "real" run time (in micro-seconds).
+ // let run_time = (kproc.ki_runtime + 5_000) / 10_000;
+
+ if let Some(proc_) = (*wrap.0.get()).get_mut(&Pid(kproc.ki_pid)) {
+ proc_.cpu_usage = cpu_usage;
+ proc_.parent = parent;
+ proc_.status = status;
+ proc_.virtual_memory = virtual_memory;
+ proc_.memory = memory;
+ proc_.run_time = now.saturating_sub(proc_.start_time);
+ proc_.updated = true;
+
+ if refresh_kind.disk_usage() {
+ proc_.old_read_bytes = proc_.read_bytes;
+ proc_.read_bytes = kproc.ki_rusage.ru_inblock as _;
+ proc_.old_written_bytes = proc_.written_bytes;
+ proc_.written_bytes = kproc.ki_rusage.ru_oublock as _;
+ }
+
+ return Ok(None);
+ }
+
+ // This is a new process, we need to get more information!
+ let mut buffer = [0; libc::PATH_MAX as usize + 1];
+
+ let exe = get_sys_value_str(
+ &[
+ libc::CTL_KERN,
+ libc::KERN_PROC,
+ libc::KERN_PROC_PATHNAME,
+ kproc.ki_pid,
+ ],
+ &mut buffer,
+ )
+ .unwrap_or_default();
+ // For some reason, it can return completely invalid path like `p\u{5}`. So we need to use
+ // procstat to get around this problem.
+ // let cwd = get_sys_value_str(
+ // &[
+ // libc::CTL_KERN,
+ // libc::KERN_PROC,
+ // libc::KERN_PROC_CWD,
+ // kproc.ki_pid,
+ // ],
+ // &mut buffer,
+ // )
+ // .map(|s| s.into())
+ // .unwrap_or_else(PathBuf::new);
+
+ let start_time = kproc.ki_start.tv_sec as u64;
+ Ok(Some(Process {
+ pid: Pid(kproc.ki_pid),
+ parent,
+ user_id: Uid(kproc.ki_ruid),
+ group_id: Gid(kproc.ki_rgid),
+ start_time,
+ run_time: now.saturating_sub(start_time),
+ cpu_usage,
+ virtual_memory,
+ memory,
+ // procstat_getfiles
+ cwd: PathBuf::new(),
+ exe: exe.into(),
+ // kvm_getargv isn't thread-safe so we get it in the main thread.
+ name: String::new(),
+ // kvm_getargv isn't thread-safe so we get it in the main thread.
+ cmd: Vec::new(),
+ // kvm_getargv isn't thread-safe so we get it in the main thread.
+ root: PathBuf::new(),
+ // kvm_getenvv isn't thread-safe so we get it in the main thread.
+ environ: Vec::new(),
+ status,
+ read_bytes: kproc.ki_rusage.ru_inblock as _,
+ old_read_bytes: 0,
+ written_bytes: kproc.ki_rusage.ru_oublock as _,
+ old_written_bytes: 0,
+ updated: true,
+ }))
+}
diff --git a/vendor/sysinfo/src/freebsd/system.rs b/vendor/sysinfo/src/freebsd/system.rs
new file mode 100644
index 000000000..16da2de50
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/system.rs
@@ -0,0 +1,803 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{
+ sys::{component::Component, Cpu, Disk, Networks, Process},
+ CpuRefreshKind, LoadAvg, Pid, ProcessRefreshKind, RefreshKind, SystemExt, User,
+};
+
+use std::cell::UnsafeCell;
+use std::collections::HashMap;
+use std::ffi::CStr;
+use std::mem::MaybeUninit;
+use std::path::{Path, PathBuf};
+use std::ptr::NonNull;
+
+use super::utils::{
+ self, boot_time, c_buf_to_string, from_cstr_array, get_frequency_for_cpu, get_sys_value,
+ get_sys_value_array, get_sys_value_by_name, get_sys_value_str_by_name, get_system_info,
+ init_mib,
+};
+
+use libc::c_int;
+
+declare_signals! {
+ c_int,
+ Signal::Hangup => libc::SIGHUP,
+ Signal::Interrupt => libc::SIGINT,
+ Signal::Quit => libc::SIGQUIT,
+ Signal::Illegal => libc::SIGILL,
+ Signal::Trap => libc::SIGTRAP,
+ Signal::Abort => libc::SIGABRT,
+ Signal::IOT => libc::SIGIOT,
+ Signal::Bus => libc::SIGBUS,
+ Signal::FloatingPointException => libc::SIGFPE,
+ Signal::Kill => libc::SIGKILL,
+ Signal::User1 => libc::SIGUSR1,
+ Signal::Segv => libc::SIGSEGV,
+ Signal::User2 => libc::SIGUSR2,
+ Signal::Pipe => libc::SIGPIPE,
+ Signal::Alarm => libc::SIGALRM,
+ Signal::Term => libc::SIGTERM,
+ Signal::Child => libc::SIGCHLD,
+ Signal::Continue => libc::SIGCONT,
+ Signal::Stop => libc::SIGSTOP,
+ Signal::TSTP => libc::SIGTSTP,
+ Signal::TTIN => libc::SIGTTIN,
+ Signal::TTOU => libc::SIGTTOU,
+ Signal::Urgent => libc::SIGURG,
+ Signal::XCPU => libc::SIGXCPU,
+ Signal::XFSZ => libc::SIGXFSZ,
+ Signal::VirtualAlarm => libc::SIGVTALRM,
+ Signal::Profiling => libc::SIGPROF,
+ Signal::Winch => libc::SIGWINCH,
+ Signal::IO => libc::SIGIO,
+ Signal::Sys => libc::SIGSYS,
+ _ => None,
+}
+
+#[doc = include_str!("../../md_doc/system.md")]
+pub struct System {
+ process_list: HashMap<Pid, Process>,
+ mem_total: u64,
+ mem_free: u64,
+ mem_used: u64,
+ swap_total: u64,
+ swap_used: u64,
+ global_cpu: Cpu,
+ cpus: Vec<Cpu>,
+ components: Vec<Component>,
+ disks: Vec<Disk>,
+ networks: Networks,
+ users: Vec<User>,
+ boot_time: u64,
+ system_info: SystemInfo,
+ got_cpu_frequency: bool,
+}
+
+impl SystemExt for System {
+ const IS_SUPPORTED: bool = true;
+ const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals();
+
+ fn new_with_specifics(refreshes: RefreshKind) -> System {
+ let system_info = SystemInfo::new();
+
+ let mut s = System {
+ process_list: HashMap::with_capacity(200),
+ mem_total: 0,
+ mem_free: 0,
+ mem_used: 0,
+ swap_total: 0,
+ swap_used: 0,
+ global_cpu: Cpu::new(String::new(), String::new(), 0),
+ cpus: Vec::with_capacity(system_info.nb_cpus as _),
+ components: Vec::with_capacity(2),
+ disks: Vec::with_capacity(1),
+ networks: Networks::new(),
+ users: Vec::new(),
+ boot_time: boot_time(),
+ system_info,
+ got_cpu_frequency: false,
+ };
+ s.refresh_specifics(refreshes);
+ s
+ }
+
+ fn refresh_memory(&mut self) {
+ if self.mem_total == 0 {
+ self.mem_total = self.system_info.get_total_memory();
+ }
+ self.mem_used = self.system_info.get_used_memory();
+ self.mem_free = self.system_info.get_free_memory();
+ let (swap_used, swap_total) = self.system_info.get_swap_info();
+ self.swap_total = swap_total;
+ self.swap_used = swap_used;
+ }
+
+ fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind) {
+ if self.cpus.is_empty() {
+ let mut frequency = 0;
+
+ // We get the CPU vendor ID in here.
+ let vendor_id =
+ get_sys_value_str_by_name(b"hw.model\0").unwrap_or_else(|| "<unknown>".to_owned());
+ for pos in 0..self.system_info.nb_cpus {
+ if refresh_kind.frequency() {
+ unsafe {
+ frequency = get_frequency_for_cpu(pos);
+ }
+ }
+ self.cpus.push(Cpu::new(
+ format!("cpu {}", pos),
+ vendor_id.clone(),
+ frequency,
+ ));
+ }
+ self.global_cpu.vendor_id = vendor_id;
+ self.got_cpu_frequency = refresh_kind.frequency();
+ } else if refresh_kind.frequency() && !self.got_cpu_frequency {
+ for (pos, proc_) in self.cpus.iter_mut().enumerate() {
+ unsafe {
+ proc_.frequency = get_frequency_for_cpu(pos as _);
+ }
+ }
+ self.got_cpu_frequency = true;
+ }
+ if refresh_kind.cpu_usage() {
+ self.system_info
+ .get_cpu_usage(&mut self.global_cpu, &mut self.cpus);
+ }
+ }
+
+ fn refresh_components_list(&mut self) {
+ if self.cpus.is_empty() {
+ self.refresh_cpu();
+ }
+ self.components = unsafe { super::component::get_components(self.cpus.len()) };
+ }
+
+ fn refresh_processes_specifics(&mut self, refresh_kind: ProcessRefreshKind) {
+ unsafe { self.refresh_procs(refresh_kind) }
+ }
+
+ fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool {
+ unsafe {
+ let kd = self.system_info.kd.as_ptr();
+ let mut count = 0;
+ let procs = libc::kvm_getprocs(kd, libc::KERN_PROC_PROC, 0, &mut count);
+ if count < 1 {
+ sysinfo_debug!("kvm_getprocs returned nothing...");
+ return false;
+ }
+ let now = super::utils::get_now();
+
+ let fscale = self.system_info.fscale;
+ let page_size = self.system_info.page_size as isize;
+ let proc_list = utils::WrapMap(UnsafeCell::new(&mut self.process_list));
+ let procs: &mut [utils::KInfoProc] =
+ std::slice::from_raw_parts_mut(procs as _, count as _);
+
+ #[cfg(feature = "multithread")]
+ use rayon::iter::ParallelIterator;
+
+ macro_rules! multi_iter {
+ ($name:ident, $($iter:tt)+) => {
+ $name = crate::utils::into_iter(procs).$($iter)+;
+ }
+ }
+
+ let ret;
+ #[cfg(not(feature = "multithread"))]
+ multi_iter!(ret, find(|kproc| kproc.ki_pid == pid.0));
+ #[cfg(feature = "multithread")]
+ multi_iter!(ret, find_any(|kproc| kproc.ki_pid == pid.0));
+
+ let kproc = if let Some(kproc) = ret {
+ kproc
+ } else {
+ return false;
+ };
+ match super::process::get_process_data(
+ kproc,
+ &proc_list,
+ page_size,
+ fscale,
+ now,
+ refresh_kind,
+ ) {
+ Ok(Some(proc_)) => {
+ self.add_missing_proc_info(self.system_info.kd.as_ptr(), kproc, proc_);
+ true
+ }
+ Ok(None) => true,
+ Err(_) => false,
+ }
+ }
+ }
+
+ fn refresh_disks_list(&mut self) {
+ self.disks = unsafe { super::disk::get_all_disks() };
+ }
+
+ fn refresh_users_list(&mut self) {
+ self.users = crate::users::get_users_list();
+ }
+
+ // COMMON PART
+ //
+ // Need to be moved into a "common" file to avoid duplication.
+
+ fn processes(&self) -> &HashMap<Pid, Process> {
+ &self.process_list
+ }
+
+ fn process(&self, pid: Pid) -> Option<&Process> {
+ self.process_list.get(&pid)
+ }
+
+ fn networks(&self) -> &Networks {
+ &self.networks
+ }
+
+ fn networks_mut(&mut self) -> &mut Networks {
+ &mut self.networks
+ }
+
+ fn global_cpu_info(&self) -> &Cpu {
+ &self.global_cpu
+ }
+
+ fn cpus(&self) -> &[Cpu] {
+ &self.cpus
+ }
+
+ fn physical_core_count(&self) -> Option<usize> {
+ let mut physical_core_count: u32 = 0;
+
+ unsafe {
+ if get_sys_value_by_name(b"hw.ncpu\0", &mut physical_core_count) {
+ Some(physical_core_count as _)
+ } else {
+ None
+ }
+ }
+ }
+
+ fn total_memory(&self) -> u64 {
+ self.mem_total
+ }
+
+ fn free_memory(&self) -> u64 {
+ self.mem_free
+ }
+
+ fn available_memory(&self) -> u64 {
+ self.mem_free
+ }
+
+ fn used_memory(&self) -> u64 {
+ self.mem_used
+ }
+
+ fn total_swap(&self) -> u64 {
+ self.swap_total
+ }
+
+ fn free_swap(&self) -> u64 {
+ self.swap_total - self.swap_used
+ }
+
+ // TODO: need to be checked
+ fn used_swap(&self) -> u64 {
+ self.swap_used
+ }
+
+ fn components(&self) -> &[Component] {
+ &self.components
+ }
+
+ fn components_mut(&mut self) -> &mut [Component] {
+ &mut self.components
+ }
+
+ fn disks(&self) -> &[Disk] {
+ &self.disks
+ }
+
+ fn disks_mut(&mut self) -> &mut [Disk] {
+ &mut self.disks
+ }
+
+ fn uptime(&self) -> u64 {
+ unsafe {
+ let csec = libc::time(::std::ptr::null_mut());
+
+ libc::difftime(csec, self.boot_time as _) as u64
+ }
+ }
+
+ fn boot_time(&self) -> u64 {
+ self.boot_time
+ }
+
+ fn load_average(&self) -> LoadAvg {
+ let mut loads = vec![0f64; 3];
+ unsafe {
+ libc::getloadavg(loads.as_mut_ptr(), 3);
+ LoadAvg {
+ one: loads[0],
+ five: loads[1],
+ fifteen: loads[2],
+ }
+ }
+ }
+
+ fn users(&self) -> &[User] {
+ &self.users
+ }
+
+ fn name(&self) -> Option<String> {
+ self.system_info.get_os_name()
+ }
+
+ fn long_os_version(&self) -> Option<String> {
+ self.system_info.get_os_release_long()
+ }
+
+ fn host_name(&self) -> Option<String> {
+ self.system_info.get_hostname()
+ }
+
+ fn kernel_version(&self) -> Option<String> {
+ self.system_info.get_kernel_version()
+ }
+
+ fn os_version(&self) -> Option<String> {
+ self.system_info.get_os_release()
+ }
+}
+
+impl Default for System {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl System {
+ unsafe fn refresh_procs(&mut self, refresh_kind: ProcessRefreshKind) {
+ let kd = self.system_info.kd.as_ptr();
+ let procs = {
+ let mut count = 0;
+ let procs = libc::kvm_getprocs(kd, libc::KERN_PROC_PROC, 0, &mut count);
+ if count < 1 {
+ sysinfo_debug!("kvm_getprocs returned nothing...");
+ return;
+ }
+ #[cfg(feature = "multithread")]
+ use rayon::iter::{ParallelIterator, ParallelIterator as IterTrait};
+ #[cfg(not(feature = "multithread"))]
+ use std::iter::Iterator as IterTrait;
+
+ crate::utils::into_iter(&mut self.process_list).for_each(|(_, proc_)| {
+ proc_.updated = false;
+ });
+
+ let fscale = self.system_info.fscale;
+ let page_size = self.system_info.page_size as isize;
+ let now = super::utils::get_now();
+ let proc_list = utils::WrapMap(UnsafeCell::new(&mut self.process_list));
+ let procs: &mut [utils::KInfoProc] =
+ std::slice::from_raw_parts_mut(procs as _, count as _);
+
+ IterTrait::filter_map(crate::utils::into_iter(procs), |kproc| {
+ super::process::get_process_data(
+ kproc,
+ &proc_list,
+ page_size,
+ fscale,
+ now,
+ refresh_kind,
+ )
+ .ok()
+ .and_then(|p| p.map(|p| (kproc, p)))
+ })
+ .collect::<Vec<_>>()
+ };
+
+ // We remove all processes that don't exist anymore.
+ self.process_list.retain(|_, v| v.updated);
+
+ for (kproc, proc_) in procs {
+ self.add_missing_proc_info(kd, kproc, proc_);
+ }
+ }
+
+ unsafe fn add_missing_proc_info(
+ &mut self,
+ kd: *mut libc::kvm_t,
+ kproc: &libc::kinfo_proc,
+ mut proc_: Process,
+ ) {
+ proc_.cmd = from_cstr_array(libc::kvm_getargv(kd, kproc, 0) as _);
+ self.system_info.get_proc_missing_info(kproc, &mut proc_);
+ if !proc_.cmd.is_empty() {
+ // First, we try to retrieve the name from the command line.
+ let p = Path::new(&proc_.cmd[0]);
+ if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
+ proc_.name = name.to_owned();
+ }
+ if proc_.root.as_os_str().is_empty() {
+ if let Some(parent) = p.parent() {
+ proc_.root = parent.to_path_buf();
+ }
+ }
+ }
+ if proc_.name.is_empty() {
+ // The name can be cut short because the `ki_comm` field size is limited,
+ // which is why we prefer to get the name from the command line as much as
+ // possible.
+ proc_.name = c_buf_to_string(&kproc.ki_comm).unwrap_or_default();
+ }
+ proc_.environ = from_cstr_array(libc::kvm_getenvv(kd, kproc, 0) as _);
+ self.process_list.insert(proc_.pid, proc_);
+ }
+}
+
+#[derive(Debug)]
+struct Zfs {
+ enabled: bool,
+ mib_arcstats_size: [c_int; 5],
+}
+
+impl Zfs {
+ fn new() -> Self {
+ let mut zfs = Self {
+ enabled: false,
+ mib_arcstats_size: Default::default(),
+ };
+ unsafe {
+ init_mib(
+ b"kstat.zfs.misc.arcstats.size\0",
+ &mut zfs.mib_arcstats_size,
+ );
+ let mut arc_size: u64 = 0;
+ if get_sys_value(&zfs.mib_arcstats_size, &mut arc_size) {
+ zfs.enabled = arc_size != 0;
+ }
+ }
+ zfs
+ }
+
+ fn arc_size(&self) -> Option<u64> {
+ if self.enabled {
+ let mut arc_size: u64 = 0;
+ unsafe {
+ get_sys_value(&self.mib_arcstats_size, &mut arc_size);
+ Some(arc_size)
+ }
+ } else {
+ None
+ }
+ }
+}
+
+/// This struct is used to get system information more easily.
+#[derive(Debug)]
+struct SystemInfo {
+ hw_physical_memory: [c_int; 2],
+ page_size: c_int,
+ virtual_page_count: [c_int; 4],
+ virtual_wire_count: [c_int; 4],
+ virtual_active_count: [c_int; 4],
+ virtual_cache_count: [c_int; 4],
+ virtual_inactive_count: [c_int; 4],
+ virtual_free_count: [c_int; 4],
+ os_type: [c_int; 2],
+ os_release: [c_int; 2],
+ kern_version: [c_int; 2],
+ hostname: [c_int; 2],
+ buf_space: [c_int; 2],
+ nb_cpus: c_int,
+ kd: NonNull<libc::kvm_t>,
+ // For these two fields, we could use `kvm_getcptime` but the function isn't very efficient...
+ mib_cp_time: [c_int; 2],
+ mib_cp_times: [c_int; 2],
+ // For the global CPU usage.
+ cp_time: utils::VecSwitcher<libc::c_ulong>,
+ // For each CPU usage.
+ cp_times: utils::VecSwitcher<libc::c_ulong>,
+ /// From FreeBSD manual: "The kernel fixed-point scale factor". It's used when computing
+ /// processes' CPU usage.
+ fscale: f32,
+ procstat: *mut libc::procstat,
+ zfs: Zfs,
+}
+
+// This is needed because `kd: *mut libc::kvm_t` isn't thread-safe.
+unsafe impl Send for SystemInfo {}
+unsafe impl Sync for SystemInfo {}
+
+impl SystemInfo {
+ fn new() -> Self {
+ unsafe {
+ let mut errbuf =
+ MaybeUninit::<[libc::c_char; libc::_POSIX2_LINE_MAX as usize]>::uninit();
+ let kd = NonNull::new(libc::kvm_openfiles(
+ std::ptr::null(),
+ b"/dev/null\0".as_ptr() as *const _,
+ std::ptr::null(),
+ 0,
+ errbuf.as_mut_ptr() as *mut _,
+ ))
+ .expect("kvm_openfiles failed");
+
+ let mut smp: c_int = 0;
+ let mut nb_cpus: c_int = 1;
+ if !get_sys_value_by_name(b"kern.smp.active\0", &mut smp) {
+ smp = 0;
+ }
+ #[allow(clippy::collapsible_if)] // I keep as is for readability reasons.
+ if smp != 0 {
+ if !get_sys_value_by_name(b"kern.smp.cpus\0", &mut nb_cpus) || nb_cpus < 1 {
+ nb_cpus = 1;
+ }
+ }
+
+ let mut si = SystemInfo {
+ hw_physical_memory: Default::default(),
+ page_size: 0,
+ virtual_page_count: Default::default(),
+ virtual_wire_count: Default::default(),
+ virtual_active_count: Default::default(),
+ virtual_cache_count: Default::default(),
+ virtual_inactive_count: Default::default(),
+ virtual_free_count: Default::default(),
+ buf_space: Default::default(),
+ os_type: Default::default(),
+ os_release: Default::default(),
+ kern_version: Default::default(),
+ hostname: Default::default(),
+ nb_cpus,
+ kd,
+ mib_cp_time: Default::default(),
+ mib_cp_times: Default::default(),
+ cp_time: utils::VecSwitcher::new(vec![0; libc::CPUSTATES as usize]),
+ cp_times: utils::VecSwitcher::new(vec![
+ 0;
+ nb_cpus as usize * libc::CPUSTATES as usize
+ ]),
+ fscale: 0.,
+ procstat: std::ptr::null_mut(),
+ zfs: Zfs::new(),
+ };
+ let mut fscale: c_int = 0;
+ if !get_sys_value_by_name(b"kern.fscale\0", &mut fscale) {
+ // Default value used in htop.
+ fscale = 2048;
+ }
+ si.fscale = fscale as f32;
+
+ if !get_sys_value_by_name(b"vm.stats.vm.v_page_size\0", &mut si.page_size) {
+ panic!("cannot get page size...");
+ }
+
+ init_mib(b"hw.physmem\0", &mut si.hw_physical_memory);
+ init_mib(b"vm.stats.vm.v_page_count\0", &mut si.virtual_page_count);
+ init_mib(b"vm.stats.vm.v_wire_count\0", &mut si.virtual_wire_count);
+ init_mib(
+ b"vm.stats.vm.v_active_count\0",
+ &mut si.virtual_active_count,
+ );
+ init_mib(b"vm.stats.vm.v_cache_count\0", &mut si.virtual_cache_count);
+ init_mib(
+ b"vm.stats.vm.v_inactive_count\0",
+ &mut si.virtual_inactive_count,
+ );
+ init_mib(b"vm.stats.vm.v_free_count\0", &mut si.virtual_free_count);
+ init_mib(b"vfs.bufspace\0", &mut si.buf_space);
+
+ init_mib(b"kern.ostype\0", &mut si.os_type);
+ init_mib(b"kern.osrelease\0", &mut si.os_release);
+ init_mib(b"kern.version\0", &mut si.kern_version);
+ init_mib(b"kern.hostname\0", &mut si.hostname);
+
+ init_mib(b"kern.cp_time\0", &mut si.mib_cp_time);
+ init_mib(b"kern.cp_times\0", &mut si.mib_cp_times);
+
+ si
+ }
+ }
+
+ fn get_os_name(&self) -> Option<String> {
+ get_system_info(&[self.os_type[0], self.os_type[1]], Some("FreeBSD"))
+ }
+
+ fn get_kernel_version(&self) -> Option<String> {
+ get_system_info(&[self.kern_version[0], self.kern_version[1]], None)
+ }
+
+ fn get_os_release_long(&self) -> Option<String> {
+ get_system_info(&[self.os_release[0], self.os_release[1]], None)
+ }
+
+ fn get_os_release(&self) -> Option<String> {
+ // It returns something like "13.0-RELEASE". We want to keep everything until the "-".
+ get_system_info(&[self.os_release[0], self.os_release[1]], None)
+ .and_then(|s| s.split('-').next().map(|s| s.to_owned()))
+ }
+
+ fn get_hostname(&self) -> Option<String> {
+ get_system_info(&[self.hostname[0], self.hostname[1]], Some(""))
+ }
+
+ /// Returns (used, total).
+ fn get_swap_info(&self) -> (u64, u64) {
+ // Magic number used in htop. Cannot find how they got it when reading `kvm_getswapinfo`
+ // source code so here we go...
+ const LEN: usize = 16;
+ let mut swap = MaybeUninit::<[libc::kvm_swap; LEN]>::uninit();
+ unsafe {
+ let nswap =
+ libc::kvm_getswapinfo(self.kd.as_ptr(), swap.as_mut_ptr() as *mut _, LEN as _, 0)
+ as usize;
+ if nswap < 1 {
+ return (0, 0);
+ }
+ let swap =
+ std::slice::from_raw_parts(swap.as_ptr() as *mut libc::kvm_swap, nswap.min(LEN));
+ let (used, total) = swap.iter().fold((0, 0), |(used, total): (u64, u64), swap| {
+ (
+ used.saturating_add(swap.ksw_used as _),
+ total.saturating_add(swap.ksw_total as _),
+ )
+ });
+ (
+ used.saturating_mul(self.page_size as _) / 1_000,
+ total.saturating_mul(self.page_size as _) / 1_000,
+ )
+ }
+ }
+
+ fn get_total_memory(&self) -> u64 {
+ let mut nb_pages: u64 = 0;
+ unsafe {
+ if get_sys_value(&self.virtual_page_count, &mut nb_pages) {
+ return nb_pages.saturating_mul(self.page_size as _) / 1_000;
+ }
+
+ // This is a fallback. It includes all the available memory, not just the one available for
+ // the users.
+ let mut total_memory: u64 = 0;
+ get_sys_value(&self.hw_physical_memory, &mut total_memory);
+ total_memory / 1_000
+ }
+ }
+
+ fn get_used_memory(&self) -> u64 {
+ let mut mem_active: u64 = 0;
+ let mut mem_wire: u64 = 0;
+
+ unsafe {
+ get_sys_value(&self.virtual_active_count, &mut mem_active);
+ get_sys_value(&self.virtual_wire_count, &mut mem_wire);
+
+ let mut mem_wire = mem_wire.saturating_mul(self.page_size as _);
+ // We need to subtract "ZFS ARC" from the "wired memory" because it should belongs to cache
+ // but the kernel reports it as "wired memory" instead...
+ if let Some(arc_size) = self.zfs.arc_size() {
+ mem_wire -= arc_size;
+ }
+ let used = mem_active
+ .saturating_mul(self.page_size as _)
+ .saturating_add(mem_wire);
+ used / 1_000
+ }
+ }
+
+ fn get_free_memory(&self) -> u64 {
+ let mut buffers_mem: u64 = 0;
+ let mut inactive_mem: u64 = 0;
+ let mut cached_mem: u64 = 0;
+ let mut free_mem: u64 = 0;
+
+ unsafe {
+ get_sys_value(&self.buf_space, &mut buffers_mem);
+ get_sys_value(&self.virtual_inactive_count, &mut inactive_mem);
+ get_sys_value(&self.virtual_cache_count, &mut cached_mem);
+ get_sys_value(&self.virtual_free_count, &mut free_mem);
+ // For whatever reason, buffers_mem is already the right value...
+ let free = buffers_mem
+ .saturating_add(inactive_mem.saturating_mul(self.page_size as u64))
+ .saturating_add(cached_mem.saturating_mul(self.page_size as u64))
+ .saturating_add(free_mem.saturating_mul(self.page_size as u64));
+ free / 1_000
+ }
+ }
+
+ fn get_cpu_usage(&mut self, global: &mut Cpu, cpus: &mut [Cpu]) {
+ unsafe {
+ get_sys_value_array(&self.mib_cp_time, self.cp_time.get_mut());
+ get_sys_value_array(&self.mib_cp_times, self.cp_times.get_mut());
+ }
+
+ fn fill_cpu(proc_: &mut Cpu, new_cp_time: &[libc::c_ulong], old_cp_time: &[libc::c_ulong]) {
+ let mut total_new: u64 = 0;
+ let mut total_old: u64 = 0;
+ let mut cp_diff: libc::c_ulong = 0;
+
+ for i in 0..(libc::CPUSTATES as usize) {
+ // We obviously don't want to get the idle part of the CPU usage, otherwise
+ // we would always be at 100%...
+ if i != libc::CP_IDLE as usize {
+ cp_diff += new_cp_time[i] - old_cp_time[i];
+ }
+ total_new += new_cp_time[i] as u64;
+ total_old += old_cp_time[i] as u64;
+ }
+
+ let total_diff = total_new - total_old;
+ if total_diff < 1 {
+ proc_.cpu_usage = 0.;
+ } else {
+ proc_.cpu_usage = cp_diff as f32 / total_diff as f32 * 100.;
+ }
+ }
+
+ fill_cpu(global, self.cp_time.get_new(), self.cp_time.get_old());
+ let old_cp_times = self.cp_times.get_old();
+ let new_cp_times = self.cp_times.get_new();
+ for (pos, proc_) in cpus.iter_mut().enumerate() {
+ let index = pos * libc::CPUSTATES as usize;
+
+ fill_cpu(proc_, &new_cp_times[index..], &old_cp_times[index..]);
+ }
+ }
+
+ #[allow(clippy::collapsible_if)] // I keep as is for readability reasons.
+ unsafe fn get_proc_missing_info(&mut self, kproc: &libc::kinfo_proc, proc_: &mut Process) {
+ if self.procstat.is_null() {
+ self.procstat = libc::procstat_open_sysctl();
+ }
+ if self.procstat.is_null() {
+ return;
+ }
+ let head = libc::procstat_getfiles(self.procstat, kproc as *const _ as usize as *mut _, 0);
+ if head.is_null() {
+ return;
+ }
+ let mut entry = (*head).stqh_first;
+ let mut done = 0;
+ while !entry.is_null() && done < 2 {
+ {
+ let tmp = &*entry;
+ if tmp.fs_uflags & libc::PS_FST_UFLAG_CDIR != 0 {
+ if !tmp.fs_path.is_null() {
+ if let Ok(p) = CStr::from_ptr(tmp.fs_path).to_str() {
+ proc_.cwd = PathBuf::from(p);
+ done += 1;
+ }
+ }
+ } else if tmp.fs_uflags & libc::PS_FST_UFLAG_RDIR != 0 {
+ if !tmp.fs_path.is_null() {
+ if let Ok(p) = CStr::from_ptr(tmp.fs_path).to_str() {
+ proc_.root = PathBuf::from(p);
+ done += 1;
+ }
+ }
+ }
+ }
+ entry = (*entry).next.stqe_next;
+ }
+ libc::procstat_freefiles(self.procstat, head);
+ }
+}
+
+impl Drop for SystemInfo {
+ fn drop(&mut self) {
+ unsafe {
+ libc::kvm_close(self.kd.as_ptr());
+ if !self.procstat.is_null() {
+ libc::procstat_close(self.procstat);
+ }
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/freebsd/utils.rs b/vendor/sysinfo/src/freebsd/utils.rs
new file mode 100644
index 000000000..00de3e9d9
--- /dev/null
+++ b/vendor/sysinfo/src/freebsd/utils.rs
@@ -0,0 +1,296 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{Pid, Process};
+use libc::{c_char, c_int, timeval};
+use std::cell::UnsafeCell;
+use std::collections::HashMap;
+use std::ffi::CStr;
+use std::mem;
+use std::time::SystemTime;
+
+/// This struct is used to switch between the "old" and "new" every time you use "get_mut".
+#[derive(Debug)]
+pub(crate) struct VecSwitcher<T> {
+ v1: Vec<T>,
+ v2: Vec<T>,
+ first: bool,
+}
+
+impl<T: Clone> VecSwitcher<T> {
+ pub fn new(v1: Vec<T>) -> Self {
+ let v2 = v1.clone();
+
+ Self {
+ v1,
+ v2,
+ first: true,
+ }
+ }
+
+ pub fn get_mut(&mut self) -> &mut [T] {
+ self.first = !self.first;
+ if self.first {
+ // It means that `v2` will be the "new".
+ &mut self.v2
+ } else {
+ // It means that `v1` will be the "new".
+ &mut self.v1
+ }
+ }
+
+ pub fn get_old(&self) -> &[T] {
+ if self.first {
+ &self.v1
+ } else {
+ &self.v2
+ }
+ }
+
+ pub fn get_new(&self) -> &[T] {
+ if self.first {
+ &self.v2
+ } else {
+ &self.v1
+ }
+ }
+}
+
+#[inline]
+pub unsafe fn init_mib(name: &[u8], mib: &mut [c_int]) {
+ let mut len = mib.len();
+ libc::sysctlnametomib(name.as_ptr() as _, mib.as_mut_ptr(), &mut len);
+}
+
+pub(crate) fn boot_time() -> u64 {
+ let mut boot_time = timeval {
+ tv_sec: 0,
+ tv_usec: 0,
+ };
+ let mut len = std::mem::size_of::<timeval>();
+ let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME];
+ unsafe {
+ if libc::sysctl(
+ mib.as_mut_ptr(),
+ mib.len() as _,
+ &mut boot_time as *mut timeval as *mut _,
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ ) < 0
+ {
+ 0
+ } else {
+ boot_time.tv_sec as _
+ }
+ }
+}
+
+pub(crate) unsafe fn get_sys_value<T: Sized>(mib: &[c_int], value: &mut T) -> bool {
+ let mut len = mem::size_of::<T>() as libc::size_t;
+ libc::sysctl(
+ mib.as_ptr(),
+ mib.len() as _,
+ value as *mut _ as *mut _,
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+}
+
+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;
+ libc::sysctl(
+ mib.as_ptr(),
+ mib.len() as _,
+ value.as_mut_ptr() as *mut _,
+ &mut len as *mut _,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+}
+
+pub(crate) fn c_buf_to_str(buf: &[libc::c_char]) -> Option<&str> {
+ unsafe {
+ let buf: &[u8] = std::slice::from_raw_parts(buf.as_ptr() as _, buf.len());
+ if let Some(pos) = buf.iter().position(|x| *x == 0) {
+ // Shrink buffer to terminate the null bytes
+ std::str::from_utf8(&buf[..pos]).ok()
+ } else {
+ std::str::from_utf8(buf).ok()
+ }
+ }
+}
+
+pub(crate) fn c_buf_to_string(buf: &[libc::c_char]) -> Option<String> {
+ c_buf_to_str(buf).map(|s| s.to_owned())
+}
+
+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;
+ if libc::sysctl(
+ mib.as_ptr(),
+ mib.len() as _,
+ buf.as_mut_ptr() as *mut _,
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ ) != 0
+ {
+ return None;
+ }
+ c_buf_to_string(&buf[..len / mem::size_of::<libc::c_char>()])
+}
+
+pub(crate) unsafe fn get_sys_value_by_name<T: Sized>(name: &[u8], value: &mut T) -> bool {
+ let mut len = mem::size_of::<T>() as libc::size_t;
+ let original = len;
+
+ libc::sysctlbyname(
+ name.as_ptr() as *const c_char,
+ value as *mut _ as *mut _,
+ &mut len,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+ && original == len
+}
+
+pub(crate) fn get_sys_value_str_by_name(name: &[u8]) -> Option<String> {
+ let mut size = 0;
+
+ unsafe {
+ if libc::sysctlbyname(
+ name.as_ptr() as *const c_char,
+ std::ptr::null_mut(),
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+ && size > 0
+ {
+ // now create a buffer with the size and get the real value
+ let mut buf: Vec<libc::c_char> = vec![0; size as usize];
+
+ if libc::sysctlbyname(
+ name.as_ptr() as *const c_char,
+ buf.as_mut_ptr() as *mut _,
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) == 0
+ && size > 0
+ {
+ c_buf_to_string(&buf)
+ } else {
+ // getting the system value failed
+ None
+ }
+ } else {
+ None
+ }
+ }
+}
+
+pub(crate) fn get_system_info(mib: &[c_int], default: Option<&str>) -> Option<String> {
+ let mut size = 0;
+
+ unsafe {
+ // Call first to get size
+ libc::sysctl(
+ mib.as_ptr(),
+ mib.len() as _,
+ std::ptr::null_mut(),
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ );
+
+ // exit early if we did not update the size
+ if size == 0 {
+ default.map(|s| s.to_owned())
+ } else {
+ // set the buffer to the correct size
+ let mut buf: Vec<libc::c_char> = vec![0; size as usize];
+
+ if libc::sysctl(
+ mib.as_ptr(),
+ mib.len() as _,
+ buf.as_mut_ptr() as _,
+ &mut size,
+ std::ptr::null_mut(),
+ 0,
+ ) == -1
+ {
+ // If command fails return default
+ default.map(|s| s.to_owned())
+ } else {
+ c_buf_to_string(&buf)
+ }
+ }
+ }
+}
+
+pub(crate) unsafe fn from_cstr_array(ptr: *const *const c_char) -> Vec<String> {
+ if ptr.is_null() {
+ return Vec::new();
+ }
+ let mut max = 0;
+ loop {
+ let ptr = ptr.add(max);
+ if (*ptr).is_null() {
+ break;
+ }
+ max += 1;
+ }
+ if max == 0 {
+ return Vec::new();
+ }
+ let mut ret = Vec::with_capacity(max);
+
+ for pos in 0..max {
+ let p = ptr.add(pos);
+ if let Ok(s) = CStr::from_ptr(*p).to_str() {
+ ret.push(s.to_owned());
+ }
+ }
+ ret
+}
+
+pub(crate) fn get_now() -> u64 {
+ SystemTime::now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .map(|n| n.as_secs())
+ .unwrap_or(0)
+}
+
+// All this is needed because `kinfo_proc` doesn't implement `Send` (because it contains pointers).
+pub(crate) struct WrapMap<'a>(pub UnsafeCell<&'a mut HashMap<Pid, Process>>);
+
+unsafe impl<'a> Send for WrapMap<'a> {}
+unsafe impl<'a> Sync for WrapMap<'a> {}
+
+#[repr(transparent)]
+pub(crate) struct KInfoProc(libc::kinfo_proc);
+unsafe impl Send for KInfoProc {}
+unsafe impl Sync for KInfoProc {}
+
+impl std::ops::Deref for KInfoProc {
+ type Target = libc::kinfo_proc;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+pub(crate) unsafe fn get_frequency_for_cpu(cpu_nb: c_int) -> u64 {
+ let mut frequency = 0;
+
+ // The information can be missing if it's running inside a VM.
+ if !get_sys_value_by_name(
+ format!("dev.cpu.{}.freq\0", cpu_nb).as_bytes(),
+ &mut frequency,
+ ) {
+ frequency = 0;
+ }
+ frequency as _
+}
diff --git a/vendor/sysinfo/src/lib.rs b/vendor/sysinfo/src/lib.rs
new file mode 100644
index 000000000..977de23a3
--- /dev/null
+++ b/vendor/sysinfo/src/lib.rs
@@ -0,0 +1,447 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+#![doc = include_str!("../README.md")]
+#![allow(unknown_lints)]
+#![deny(missing_docs)]
+#![deny(rustdoc::broken_intra_doc_links)]
+#![allow(clippy::upper_case_acronyms)]
+#![allow(clippy::non_send_fields_in_send_ty)]
+#![allow(renamed_and_removed_lints)]
+#![allow(unknown_lints)]
+
+#[macro_use]
+mod macros;
+
+cfg_if::cfg_if! {
+ if #[cfg(feature = "unknown-ci")] {
+ // This is used in CI to check that the build for unknown targets is compiling fine.
+ mod unknown;
+ use unknown as sys;
+
+ #[cfg(test)]
+ pub(crate) const MIN_USERS: usize = 0;
+ } else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
+ mod apple;
+ use apple as sys;
+ extern crate core_foundation_sys;
+
+ #[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;
+
+ #[cfg(test)]
+ pub(crate) const MIN_USERS: usize = 1;
+ } else if #[cfg(any(target_os = "linux", target_os = "android"))] {
+ mod linux;
+ use linux as sys;
+ pub(crate) mod users;
+
+ #[cfg(test)]
+ pub(crate) const MIN_USERS: usize = 1;
+ } else if #[cfg(target_os = "freebsd")] {
+ mod freebsd;
+ use freebsd as sys;
+ pub(crate) mod users;
+
+ #[cfg(test)]
+ pub(crate) const MIN_USERS: usize = 1;
+ } else {
+ mod unknown;
+ use unknown as sys;
+
+ #[cfg(test)]
+ pub(crate) const MIN_USERS: usize = 0;
+ }
+}
+
+pub use common::{
+ get_current_pid, CpuRefreshKind, DiskType, DiskUsage, Gid, LoadAvg, NetworksIter, Pid, PidExt,
+ ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, Uid, User,
+};
+pub use sys::{Component, Cpu, Disk, NetworkData, Networks, Process, System};
+pub use traits::{
+ ComponentExt, CpuExt, DiskExt, NetworkExt, NetworksExt, ProcessExt, SystemExt, UserExt,
+};
+
+#[cfg(feature = "c-interface")]
+pub use c_interface::*;
+
+#[cfg(feature = "c-interface")]
+mod c_interface;
+mod common;
+mod debug;
+mod system;
+mod traits;
+mod utils;
+
+/// 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
+/// 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
+/// allow them to change this limit.
+///
+/// Note that if you set a limit bigger than the system limit, the system limit will be set.
+///
+/// Returns `true` if the new value has been set.
+///
+/// ```no_run
+/// use sysinfo::{System, SystemExt, set_open_files_limit};
+///
+/// // We call the function before any call to the processes update.
+/// if !set_open_files_limit(10) {
+/// // It'll always return false on non-linux targets.
+/// eprintln!("failed to update the open files limit...");
+/// }
+/// let s = System::new_all();
+/// ```
+pub fn set_open_files_limit(mut _new_limit: isize) -> bool {
+ cfg_if::cfg_if! {
+ if #[cfg(all(not(feature = "unknown-ci"), any(target_os = "linux", target_os = "android")))]
+ {
+ if _new_limit < 0 {
+ _new_limit = 0;
+ }
+ let max = sys::system::get_max_nb_fds();
+ if _new_limit > max {
+ _new_limit = max;
+ }
+ unsafe {
+ if let Ok(ref mut x) = sys::system::REMAINING_FILES.lock() {
+ // If files are already open, to be sure that the number won't be bigger when those
+ // files are closed, we subtract the current number of opened files to the new
+ // limit.
+ let diff = max.saturating_sub(**x);
+ **x = _new_limit.saturating_sub(diff);
+ true
+ } else {
+ false
+ }
+ }
+ } else {
+ false
+ }
+ }
+}
+
+// FIXME: Can be removed once negative trait bounds are supported.
+#[cfg(doctest)]
+mod doctest {
+ /// Check that `Process` doesn't implement `Clone`.
+ ///
+ /// First we check that the "basic" code works:
+ ///
+ /// ```no_run
+ /// use sysinfo::{Process, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// let p: &Process = s.processes().values().next().unwrap();
+ /// ```
+ ///
+ /// And now we check if it fails when we try to clone it:
+ ///
+ /// ```compile_fail
+ /// use sysinfo::{Process, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// let p: &Process = s.processes().values().next().unwrap();
+ /// let p = (*p).clone();
+ /// ```
+ mod process_clone {}
+
+ /// Check that `System` doesn't implement `Clone`.
+ ///
+ /// First we check that the "basic" code works:
+ ///
+ /// ```no_run
+ /// use sysinfo::{Process, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// ```
+ ///
+ /// And now we check if it fails when we try to clone it:
+ ///
+ /// ```compile_fail
+ /// use sysinfo::{Process, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// let s = s.clone();
+ /// ```
+ mod system_clone {}
+}
+
+#[cfg(test)]
+mod test {
+ use crate::*;
+
+ #[cfg(feature = "unknown-ci")]
+ #[test]
+ fn check_unknown_ci_feature() {
+ assert!(!System::IS_SUPPORTED);
+ }
+
+ #[test]
+ fn check_process_memory_usage() {
+ let mut s = System::new();
+ s.refresh_all();
+
+ if System::IS_SUPPORTED {
+ // No process should have 0 as memory usage.
+ #[cfg(not(feature = "apple-sandbox"))]
+ assert!(!s.processes().iter().all(|(_, proc_)| proc_.memory() == 0));
+ } else {
+ // There should be no process, but if there is one, its memory usage should be 0.
+ assert!(s.processes().iter().all(|(_, proc_)| proc_.memory() == 0));
+ }
+ }
+
+ #[test]
+ fn check_memory_usage() {
+ let mut s = System::new();
+
+ assert_eq!(s.total_memory(), 0);
+ assert_eq!(s.free_memory(), 0);
+ assert_eq!(s.available_memory(), 0);
+ assert_eq!(s.used_memory(), 0);
+ assert_eq!(s.total_swap(), 0);
+ assert_eq!(s.free_swap(), 0);
+ assert_eq!(s.used_swap(), 0);
+
+ s.refresh_memory();
+ if System::IS_SUPPORTED {
+ assert!(s.total_memory() > 0);
+ assert!(s.used_memory() > 0);
+ if s.total_swap() > 0 {
+ // I think it's pretty safe to assume that there is still some swap left...
+ assert!(s.free_swap() > 0);
+ }
+ } else {
+ assert_eq!(s.total_memory(), 0);
+ assert_eq!(s.used_memory(), 0);
+ assert_eq!(s.total_swap(), 0);
+ assert_eq!(s.free_swap(), 0);
+ }
+ }
+
+ #[cfg(target_os = "linux")]
+ #[test]
+ fn check_processes_cpu_usage() {
+ if !System::IS_SUPPORTED {
+ return;
+ }
+ let mut s = System::new();
+
+ s.refresh_processes();
+ // All CPU usage will start at zero until the second refresh
+ assert!(s
+ .processes()
+ .iter()
+ .all(|(_, proc_)| proc_.cpu_usage() == 0.0));
+
+ // Wait a bit to update CPU usage values
+ std::thread::sleep(std::time::Duration::from_millis(100));
+ s.refresh_processes();
+ assert!(s
+ .processes()
+ .iter()
+ .all(|(_, proc_)| proc_.cpu_usage() >= 0.0
+ && proc_.cpu_usage() <= (s.cpus().len() as f32) * 100.0));
+ assert!(s
+ .processes()
+ .iter()
+ .any(|(_, proc_)| proc_.cpu_usage() > 0.0));
+ }
+
+ #[test]
+ fn check_cpu_usage() {
+ if !System::IS_SUPPORTED {
+ return;
+ }
+ let mut s = System::new();
+ for _ in 0..10 {
+ s.refresh_cpu();
+ // Wait a bit to update CPU usage values
+ std::thread::sleep(std::time::Duration::from_millis(100));
+ if s.cpus().iter().any(|c| c.cpu_usage() > 0.0) {
+ // All good!
+ return;
+ }
+ }
+ panic!("CPU usage is always zero...");
+ }
+
+ #[test]
+ fn check_users() {
+ let mut s = System::new();
+ assert!(s.users().is_empty());
+ s.refresh_users_list();
+ assert!(s.users().len() >= MIN_USERS);
+
+ let mut s = System::new();
+ assert!(s.users().is_empty());
+ s.refresh_all();
+ assert!(s.users().is_empty());
+
+ let s = System::new_all();
+ assert!(s.users().len() >= MIN_USERS);
+ }
+
+ #[test]
+ fn check_uid_gid() {
+ let mut s = System::new();
+ assert!(s.users().is_empty());
+ s.refresh_users_list();
+ let users = s.users();
+ assert!(users.len() >= MIN_USERS);
+
+ if System::IS_SUPPORTED {
+ #[cfg(not(target_os = "windows"))]
+ {
+ let user = users
+ .iter()
+ .find(|u| u.name() == "root")
+ .expect("no root user");
+ assert_eq!(**user.id(), 0);
+ assert_eq!(*user.group_id(), 0);
+ if let Some(user) = users.iter().find(|u| *u.group_id() > 0) {
+ assert!(**user.id() > 0);
+ assert!(*user.group_id() > 0);
+ }
+ assert!(users.iter().filter(|u| **u.id() > 0).count() > 0);
+ }
+
+ // And now check that our `get_user_by_id` method works.
+ s.refresh_processes();
+ assert!(s
+ .processes()
+ .iter()
+ .filter_map(|(_, p)| p.user_id())
+ .any(|uid| s.get_user_by_id(uid).is_some()));
+ }
+ }
+
+ #[test]
+ fn check_system_info() {
+ // We don't want to test on unsupported systems.
+ if System::IS_SUPPORTED {
+ let s = System::new();
+ assert!(!s.name().expect("Failed to get system name").is_empty());
+
+ assert!(!s
+ .kernel_version()
+ .expect("Failed to get kernel version")
+ .is_empty());
+
+ assert!(!s.os_version().expect("Failed to get os version").is_empty());
+
+ assert!(!s
+ .long_os_version()
+ .expect("Failed to get long OS version")
+ .is_empty());
+ }
+ }
+
+ #[test]
+ fn check_host_name() {
+ // We don't want to test on unsupported systems.
+ if System::IS_SUPPORTED {
+ let s = System::new();
+ assert!(s.host_name().is_some());
+ }
+ }
+
+ #[test]
+ fn check_refresh_process_return_value() {
+ // We don't want to test on unsupported systems.
+ if System::IS_SUPPORTED {
+ let _pid = get_current_pid().expect("Failed to get current PID");
+
+ #[cfg(not(feature = "apple-sandbox"))]
+ {
+ let mut s = System::new();
+ // First check what happens in case the process isn't already in our process list.
+ assert!(s.refresh_process(_pid));
+ // Then check that it still returns true if the process is already in our process list.
+ assert!(s.refresh_process(_pid));
+ }
+ }
+ }
+
+ #[test]
+ fn ensure_is_supported_is_set_correctly() {
+ if MIN_USERS > 0 {
+ assert!(System::IS_SUPPORTED);
+ } else {
+ assert!(!System::IS_SUPPORTED);
+ }
+ }
+
+ #[test]
+ fn check_cpus_number() {
+ let mut s = System::new();
+
+ // This information isn't retrieved by default.
+ assert!(s.cpus().is_empty());
+ if System::IS_SUPPORTED {
+ // The physical cores count is recomputed every time the function is called, so the
+ // information must be relevant even with nothing initialized.
+ let physical_cores_count = s
+ .physical_core_count()
+ .expect("failed to get number of physical cores");
+
+ s.refresh_cpu();
+ // The cpus shouldn't be empty anymore.
+ assert!(!s.cpus().is_empty());
+
+ // In case we are running inside a VM, it's possible to not have a physical core, only
+ // logical ones, which is why we don't test `physical_cores_count > 0`.
+ let physical_cores_count2 = s
+ .physical_core_count()
+ .expect("failed to get number of physical cores");
+ assert!(physical_cores_count2 <= s.cpus().len());
+ assert_eq!(physical_cores_count, physical_cores_count2);
+ } else {
+ assert_eq!(s.physical_core_count(), None);
+ }
+ assert!(s.physical_core_count().unwrap_or(0) <= s.cpus().len());
+ }
+
+ #[test]
+ fn check_nb_supported_signals() {
+ if System::IS_SUPPORTED {
+ assert!(
+ !System::SUPPORTED_SIGNALS.is_empty(),
+ "SUPPORTED_SIGNALS shoudn't be empty on supported systems!"
+ );
+ } else {
+ assert!(
+ System::SUPPORTED_SIGNALS.is_empty(),
+ "SUPPORTED_SIGNALS should be empty on not support systems!"
+ );
+ }
+ }
+
+ // Ensure that the CPUs frequency isn't retrieved until we ask for it.
+ #[test]
+ #[cfg(not(target_os = "freebsd"))] // In a VM, it'll fail.
+ fn check_cpu_frequency() {
+ if !System::IS_SUPPORTED {
+ return;
+ }
+ let mut s = System::new();
+ s.refresh_processes();
+ for proc_ in s.cpus() {
+ assert_eq!(proc_.frequency(), 0);
+ }
+ s.refresh_cpu();
+ for proc_ in s.cpus() {
+ assert_ne!(proc_.frequency(), 0);
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/linux/component.rs b/vendor/sysinfo/src/linux/component.rs
new file mode 100644
index 000000000..3a10588e5
--- /dev/null
+++ b/vendor/sysinfo/src/linux/component.rs
@@ -0,0 +1,189 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::ComponentExt;
+
+use std::collections::HashMap;
+use std::fs::{metadata, read_dir, File};
+use std::io::Read;
+use std::path::{Path, PathBuf};
+
+#[doc = include_str!("../../md_doc/component.md")]
+pub struct Component {
+ temperature: f32,
+ max: f32,
+ critical: Option<f32>,
+ label: String,
+ input_file: PathBuf,
+}
+
+fn get_file_line(file: &Path, capacity: usize) -> Option<String> {
+ let mut reader = String::with_capacity(capacity);
+ if let Ok(mut f) = File::open(file) {
+ if f.read_to_string(&mut reader).is_ok() {
+ Some(reader)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+}
+
+fn is_file<T: AsRef<Path>>(path: T) -> bool {
+ metadata(path).ok().map(|m| m.is_file()).unwrap_or(false)
+}
+
+fn append_files(components: &mut Vec<Component>, folder: &Path) {
+ let mut matchings: HashMap<u32, Vec<String>> = HashMap::with_capacity(10);
+
+ if let Ok(dir) = read_dir(folder) {
+ for entry in dir.flatten() {
+ let entry = entry.path();
+ if entry.is_dir()
+ || !entry
+ .file_name()
+ .and_then(|x| x.to_str())
+ .unwrap_or("")
+ .starts_with("temp")
+ {
+ continue;
+ }
+ if let Some(entry) = entry.file_name() {
+ if let Some(entry) = entry.to_str() {
+ let mut parts = entry.split('_');
+ if let Some(Some(id)) = parts.next().map(|s| s[4..].parse::<u32>().ok()) {
+ matchings
+ .entry(id)
+ .or_insert_with(|| Vec::with_capacity(5))
+ .push(
+ parts
+ .next()
+ .map(|s| format!("_{}", s))
+ .unwrap_or_else(String::new),
+ );
+ }
+ }
+ }
+ }
+ for (key, val) in &matchings {
+ let mut found_input = None;
+ let mut found_label = None;
+ for (pos, v) in val.iter().enumerate() {
+ match v.as_str() {
+ // raspberry has empty string for temperature input
+ "_input" | "" => {
+ found_input = Some(pos);
+ }
+ "_label" => {
+ found_label = Some(pos);
+ }
+ _ => {}
+ }
+ }
+ if let (Some(_), Some(found_input)) = (found_label, found_input) {
+ let mut p_label = folder.to_path_buf();
+ let mut p_input = folder.to_path_buf();
+ let mut p_crit = folder.to_path_buf();
+ let mut p_max = folder.to_path_buf();
+
+ p_label.push(&format!("temp{}_label", key));
+ p_input.push(&format!("temp{}{}", key, val[found_input]));
+ p_max.push(&format!("temp{}_max", key));
+ p_crit.push(&format!("temp{}_crit", key));
+ if is_file(&p_input) {
+ let label = get_file_line(p_label.as_path(), 10)
+ .unwrap_or_else(|| format!("Component {}", key)) // needed for raspberry pi
+ .replace('\n', "");
+ let max = get_file_line(p_max.as_path(), 10).map(|max| {
+ max.replace('\n', "").parse::<f32>().unwrap_or(100_000f32) / 1000f32
+ });
+ let crit = get_file_line(p_crit.as_path(), 10).map(|crit| {
+ crit.replace('\n', "").parse::<f32>().unwrap_or(100_000f32) / 1000f32
+ });
+ components.push(Component::new(label, p_input.as_path(), max, crit));
+ }
+ }
+ }
+ }
+}
+
+impl Component {
+ /// Creates a new component with the given information.
+ pub(crate) fn new(
+ label: String,
+ input_path: &Path,
+ max: Option<f32>,
+ critical: Option<f32>,
+ ) -> Component {
+ let mut c = Component {
+ temperature: 0f32,
+ label,
+ input_file: input_path.to_path_buf(),
+ max: max.unwrap_or(0.0),
+ critical,
+ };
+ c.refresh();
+ c
+ }
+}
+
+impl ComponentExt for Component {
+ fn temperature(&self) -> f32 {
+ self.temperature
+ }
+
+ fn max(&self) -> f32 {
+ self.max
+ }
+
+ fn critical(&self) -> Option<f32> {
+ self.critical
+ }
+
+ fn label(&self) -> &str {
+ &self.label
+ }
+
+ fn refresh(&mut self) {
+ if let Some(content) = get_file_line(self.input_file.as_path(), 10) {
+ self.temperature = content
+ .replace('\n', "")
+ .parse::<f32>()
+ .unwrap_or(100_000f32)
+ / 1000f32;
+ if self.temperature > self.max {
+ self.max = self.temperature;
+ }
+ }
+ }
+}
+
+pub(crate) fn get_components() -> Vec<Component> {
+ let mut components = Vec::with_capacity(10);
+ if let Ok(dir) = read_dir(&Path::new("/sys/class/hwmon/")) {
+ for entry in dir.flatten() {
+ let entry = entry.path();
+ if !entry.is_dir()
+ || !entry
+ .file_name()
+ .and_then(|x| x.to_str())
+ .unwrap_or("")
+ .starts_with("hwmon")
+ {
+ continue;
+ }
+ append_files(&mut components, &entry);
+ }
+ components.sort_by(|c1, c2| c1.label.to_lowercase().cmp(&c2.label.to_lowercase()));
+ }
+ if is_file("/sys/class/thermal/thermal_zone0/temp") {
+ // Specfic to raspberry pi.
+ components.push(Component::new(
+ "CPU".to_owned(),
+ Path::new("/sys/class/thermal/thermal_zone0/temp"),
+ None,
+ None,
+ ));
+ }
+ components
+}
diff --git a/vendor/sysinfo/src/linux/cpu.rs b/vendor/sysinfo/src/linux/cpu.rs
new file mode 100644
index 000000000..f3970c318
--- /dev/null
+++ b/vendor/sysinfo/src/linux/cpu.rs
@@ -0,0 +1,357 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+#![allow(clippy::too_many_arguments)]
+
+use std::collections::HashSet;
+use std::fs::File;
+use std::io::Read;
+
+use crate::CpuExt;
+
+/// Struct containing values to compute a CPU usage.
+#[derive(Clone, Copy)]
+pub(crate) struct CpuValues {
+ user: u64,
+ nice: u64,
+ system: u64,
+ idle: u64,
+ iowait: u64,
+ irq: u64,
+ softirq: u64,
+ steal: u64,
+ _guest: u64,
+ _guest_nice: u64,
+}
+
+impl CpuValues {
+ /// Creates a new instance of `CpuValues` with everything set to `0`.
+ pub fn new() -> CpuValues {
+ CpuValues {
+ user: 0,
+ nice: 0,
+ system: 0,
+ idle: 0,
+ iowait: 0,
+ 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,
+ }
+ }
+
+ /*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,
+ user: u64,
+ nice: u64,
+ system: u64,
+ idle: u64,
+ iowait: u64,
+ irq: u64,
+ softirq: u64,
+ steal: u64,
+ guest: u64,
+ guest_nice: u64,
+ ) {
+ self.user = user;
+ self.nice = 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;
+ }
+
+ /// Returns work time.
+ pub fn work_time(&self) -> u64 {
+ self.user
+ .saturating_add(self.nice)
+ .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)
+ }
+}
+
+#[doc = include_str!("../../md_doc/cpu.md")]
+pub struct Cpu {
+ old_values: CpuValues,
+ new_values: CpuValues,
+ pub(crate) name: String,
+ cpu_usage: f32,
+ total_time: u64,
+ old_total_time: u64,
+ pub(crate) frequency: u64,
+ pub(crate) vendor_id: String,
+ pub(crate) brand: String,
+}
+
+impl Cpu {
+ pub(crate) fn new_with_values(
+ name: &str,
+ user: u64,
+ nice: u64,
+ system: u64,
+ idle: u64,
+ iowait: u64,
+ irq: u64,
+ softirq: u64,
+ steal: u64,
+ guest: u64,
+ guest_nice: u64,
+ frequency: u64,
+ vendor_id: String,
+ brand: String,
+ ) -> Cpu {
+ 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,
+ ),
+ cpu_usage: 0f32,
+ total_time: 0,
+ old_total_time: 0,
+ frequency,
+ vendor_id,
+ brand,
+ }
+ }
+
+ pub(crate) fn set(
+ &mut self,
+ user: u64,
+ nice: u64,
+ system: u64,
+ idle: u64,
+ iowait: u64,
+ irq: u64,
+ softirq: u64,
+ steal: u64,
+ guest: u64,
+ guest_nice: u64,
+ ) {
+ macro_rules! min {
+ ($a:expr, $b:expr) => {
+ if $a > $b {
+ ($a - $b) as f32
+ } else {
+ 1.
+ }
+ };
+ }
+ self.old_values = self.new_values;
+ self.new_values.set(
+ user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice,
+ );
+ 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)
+ * 100.;
+ if self.cpu_usage > 100. {
+ self.cpu_usage = 100.; // to prevent the percentage to go above 100%
+ }
+ }
+}
+
+impl CpuExt for Cpu {
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ /// Returns the CPU frequency in MHz.
+ fn frequency(&self) -> u64 {
+ self.frequency
+ }
+
+ fn vendor_id(&self) -> &str {
+ &self.vendor_id
+ }
+
+ fn brand(&self) -> &str {
+ &self.brand
+ }
+}
+
+pub(crate) fn get_raw_times(p: &Cpu) -> (u64, u64) {
+ (p.total_time, p.old_total_time)
+}
+
+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
+ ))
+ .and_then(|mut f| f.read_to_string(&mut s))
+ .is_ok()
+ {
+ let freq_option = s.trim().split('\n').next();
+ if let Some(freq_string) = freq_option {
+ if let Ok(freq) = freq_string.parse::<u64>() {
+ return freq / 1000;
+ }
+ }
+ }
+ s.clear();
+ if File::open("/proc/cpuinfo")
+ .and_then(|mut f| f.read_to_string(&mut s))
+ .is_err()
+ {
+ return 0;
+ }
+ let find_cpu_mhz = s.split('\n').find(|line| {
+ line.starts_with("cpu MHz\t")
+ || line.starts_with("BogoMIPS")
+ || line.starts_with("clock\t")
+ || line.starts_with("bogomips per cpu")
+ });
+ find_cpu_mhz
+ .and_then(|line| line.split(':').last())
+ .and_then(|val| val.replace("MHz", "").trim().parse::<f64>().ok())
+ .map(|speed| speed as u64)
+ .unwrap_or_default()
+}
+
+#[allow(unused_assignments)]
+pub(crate) fn get_physical_core_count() -> Option<usize> {
+ let mut s = String::new();
+ if let Err(_e) = File::open("/proc/cpuinfo").and_then(|mut f| f.read_to_string(&mut s)) {
+ sysinfo_debug!("Cannot read `/proc/cpuinfo` file: {:?}", _e);
+ return None;
+ }
+
+ macro_rules! add_core {
+ ($core_ids_and_physical_ids:ident, $core_id:ident, $physical_id:ident, $cpu:ident) => {{
+ if !$core_id.is_empty() && !$physical_id.is_empty() {
+ $core_ids_and_physical_ids.insert(format!("{} {}", $core_id, $physical_id));
+ } else if !$cpu.is_empty() {
+ // On systems with only physical cores like raspberry, there is no "core id" or
+ // "physical id" fields. So if one of them is missing, we simply use the "CPU"
+ // info and count it as a physical core.
+ $core_ids_and_physical_ids.insert($cpu.to_owned());
+ }
+ $core_id = "";
+ $physical_id = "";
+ $cpu = "";
+ }};
+ }
+
+ let mut core_ids_and_physical_ids: HashSet<String> = HashSet::new();
+ let mut core_id = "";
+ let mut physical_id = "";
+ let mut cpu = "";
+
+ for line in s.lines() {
+ if line.is_empty() {
+ add_core!(core_ids_and_physical_ids, core_id, physical_id, cpu);
+ } else if line.starts_with("processor") {
+ cpu = line
+ .splitn(2, ':')
+ .last()
+ .map(|x| x.trim())
+ .unwrap_or_default();
+ } else if line.starts_with("core id") {
+ core_id = line
+ .splitn(2, ':')
+ .last()
+ .map(|x| x.trim())
+ .unwrap_or_default();
+ } else if line.starts_with("physical id") {
+ physical_id = line
+ .splitn(2, ':')
+ .last()
+ .map(|x| x.trim())
+ .unwrap_or_default();
+ }
+ }
+ add_core!(core_ids_and_physical_ids, core_id, physical_id, cpu);
+
+ Some(core_ids_and_physical_ids.len())
+}
+
+/// 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();
+ if File::open("/proc/cpuinfo")
+ .and_then(|mut f| f.read_to_string(&mut s))
+ .is_err()
+ {
+ return (String::new(), String::new());
+ }
+
+ fn get_value(s: &str) -> String {
+ s.split(':')
+ .last()
+ .map(|x| x.trim().to_owned())
+ .unwrap_or_default()
+ }
+
+ let mut vendor_id = None;
+ let mut brand = 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 {
+ continue;
+ }
+ if brand.is_some() && vendor_id.is_some() {
+ break;
+ }
+ }
+ (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
new file mode 100644
index 000000000..5a313fd27
--- /dev/null
+++ b/vendor/sysinfo/src/linux/disk.rs
@@ -0,0 +1,292 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::utils::get_all_data;
+use crate::{utils, DiskExt, DiskType};
+
+use libc::statvfs;
+use std::ffi::{OsStr, OsString};
+use std::fs;
+use std::mem;
+use std::os::unix::ffi::OsStrExt;
+use std::path::{Path, PathBuf};
+
+macro_rules! cast {
+ ($x:expr) => {
+ u64::from($x)
+ };
+}
+
+#[doc = include_str!("../../md_doc/disk.md")]
+#[derive(PartialEq, Eq)]
+pub struct Disk {
+ type_: DiskType,
+ device_name: OsString,
+ file_system: Vec<u8>,
+ mount_point: PathBuf,
+ total_space: u64,
+ available_space: u64,
+ is_removable: bool,
+}
+
+impl DiskExt for Disk {
+ fn type_(&self) -> DiskType {
+ self.type_
+ }
+
+ fn name(&self) -> &OsStr {
+ &self.device_name
+ }
+
+ fn file_system(&self) -> &[u8] {
+ &self.file_system
+ }
+
+ fn mount_point(&self) -> &Path {
+ &self.mount_point
+ }
+
+ fn total_space(&self) -> u64 {
+ self.total_space
+ }
+
+ fn available_space(&self) -> u64 {
+ self.available_space
+ }
+
+ fn is_removable(&self) -> bool {
+ self.is_removable
+ }
+
+ fn refresh(&mut self) -> bool {
+ unsafe {
+ let mut stat: statvfs = mem::zeroed();
+ let mount_point_cpath = utils::to_cpath(&self.mount_point);
+ if 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
+ } else {
+ false
+ }
+ }
+ }
+}
+
+fn new_disk(
+ device_name: &OsStr,
+ mount_point: &Path,
+ file_system: &[u8],
+ removable_entries: &[PathBuf],
+) -> Option<Disk> {
+ let mount_point_cpath = utils::to_cpath(mount_point);
+ let type_ = find_type_for_device_name(device_name);
+ let mut total = 0;
+ let mut available = 0;
+ unsafe {
+ let mut stat: statvfs = mem::zeroed();
+ if 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);
+ total = bsize.saturating_mul(blocks);
+ available = bsize.saturating_mul(bavail);
+ }
+ if total == 0 {
+ return None;
+ }
+ let mount_point = mount_point.to_owned();
+ let is_removable = removable_entries
+ .iter()
+ .any(|e| e.as_os_str() == device_name);
+ Some(Disk {
+ type_,
+ device_name: device_name.to_owned(),
+ file_system: file_system.to_owned(),
+ mount_point,
+ total_space: cast!(total),
+ available_space: cast!(available),
+ is_removable,
+ })
+ }
+}
+
+#[allow(clippy::manual_range_contains)]
+fn find_type_for_device_name(device_name: &OsStr) -> DiskType {
+ // 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
+ // /sys/block/
+ // - In the case of /dev/sd, the format is /dev/sd[a-z][1-9],
+ // corresponding to /sys/block/sd[a-z]
+ // - In the case of /dev/nvme, the format is /dev/nvme[0-9]n[0-9]p[0-9],
+ // corresponding to /sys/block/nvme[0-9]n[0-9]
+ // - In the case of /dev/mmcblk, the format is /dev/mmcblk[0-9]p[0-9],
+ // corresponding to /sys/block/mmcblk[0-9]
+ let device_name_path = device_name.to_str().unwrap_or_default();
+ let real_path = fs::canonicalize(device_name).unwrap_or_else(|_| PathBuf::from(device_name));
+ let mut real_path = real_path.to_str().unwrap_or_default();
+ if device_name_path.starts_with("/dev/mapper/") {
+ // Recursively solve, for example /dev/dm-0
+ if real_path != device_name_path {
+ return find_type_for_device_name(OsStr::new(&real_path));
+ }
+ } else if device_name_path.starts_with("/dev/sd") || device_name_path.starts_with("/dev/vd") {
+ // Turn "sda1" into "sda" or "vda1" into "vda"
+ real_path = real_path.trim_start_matches("/dev/");
+ real_path = real_path.trim_end_matches(|c| c >= '0' && c <= '9');
+ } else if device_name_path.starts_with("/dev/nvme") {
+ // Turn "nvme0n1p1" into "nvme0n1"
+ real_path = real_path.trim_start_matches("/dev/");
+ real_path = real_path.trim_end_matches(|c| c >= '0' && c <= '9');
+ real_path = real_path.trim_end_matches(|c| c == 'p');
+ } else if device_name_path.starts_with("/dev/root") {
+ // Recursively solve, for example /dev/mmcblk0p1
+ if real_path != device_name_path {
+ return find_type_for_device_name(OsStr::new(&real_path));
+ }
+ } else if device_name_path.starts_with("/dev/mmcblk") {
+ // Turn "mmcblk0p1" into "mmcblk0"
+ real_path = real_path.trim_start_matches("/dev/");
+ real_path = real_path.trim_end_matches(|c| c >= '0' && c <= '9');
+ real_path = real_path.trim_end_matches(|c| c == 'p');
+ } else {
+ // Default case: remove /dev/ and expects the name presents under /sys/block/
+ // For example, /dev/dm-0 to dm-0
+ real_path = real_path.trim_start_matches("/dev/");
+ }
+
+ let trimmed: &OsStr = OsStrExt::from_bytes(real_path.as_bytes());
+
+ let path = Path::new("/sys/block/")
+ .to_owned()
+ .join(trimmed)
+ .join("queue/rotational");
+ // Normally, this file only contains '0' or '1' but just in case, we get 8 bytes...
+ match get_all_data(path, 8)
+ .unwrap_or_default()
+ .trim()
+ .parse()
+ .ok()
+ {
+ // The disk is marked as rotational so it's a HDD.
+ Some(1) => DiskType::HDD,
+ // The disk is marked as non-rotational so it's very likely a SSD.
+ Some(0) => DiskType::SSD,
+ // Normally it shouldn't happen but welcome to the wonderful world of IT! :D
+ Some(x) => DiskType::Unknown(x),
+ // The information isn't available...
+ None => DiskType::Unknown(-1),
+ }
+}
+
+fn get_all_disks_inner(content: &str) -> Vec<Disk> {
+ // The goal of this array is to list all removable devices (the ones whose name starts with
+ // "usb-"). Then we check if
+ let removable_entries = match fs::read_dir("/dev/disk/by-id/") {
+ Ok(r) => r
+ .filter_map(|res| Some(res.ok()?.path()))
+ .filter_map(|e| {
+ if e.file_name()
+ .and_then(|x| Some(x.to_str()?.starts_with("usb-")))
+ .unwrap_or_default()
+ {
+ e.canonicalize().ok()
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<PathBuf>>(),
+ _ => Vec::new(),
+ };
+
+ content
+ .lines()
+ .map(|line| {
+ let line = line.trim_start();
+ // mounts format
+ // http://man7.org/linux/man-pages/man5/fstab.5.html
+ // fs_spec<tab>fs_file<tab>fs_vfstype<tab>other fields
+ let mut fields = line.split_whitespace();
+ let fs_spec = fields.next().unwrap_or("");
+ let fs_file = fields
+ .next()
+ .unwrap_or("")
+ .replace("\\134", "\\")
+ .replace("\\040", " ")
+ .replace("\\011", "\t")
+ .replace("\\012", "\n");
+ let fs_vfstype = fields.next().unwrap_or("");
+ (fs_spec, fs_file, fs_vfstype)
+ })
+ .filter(|(fs_spec, fs_file, fs_vfstype)| {
+ // Check if fs_vfstype is one of our 'ignored' file systems.
+ let filtered = matches!(
+ *fs_vfstype,
+ "rootfs" | // https://www.kernel.org/doc/Documentation/filesystems/ramfs-rootfs-initramfs.txt
+ "sysfs" | // pseudo file system for kernel objects
+ "proc" | // another pseudo file system
+ "tmpfs" |
+ "devtmpfs" |
+ "cgroup" |
+ "cgroup2" |
+ "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
+ );
+
+ !(filtered ||
+ fs_file.starts_with("/sys") || // check if fs_file is an 'ignored' mount point
+ fs_file.starts_with("/proc") ||
+ (fs_file.starts_with("/run") && !fs_file.starts_with("/run/media")) ||
+ fs_spec.starts_with("sunrpc"))
+ })
+ .filter_map(|(fs_spec, fs_file, fs_vfstype)| {
+ new_disk(
+ fs_spec.as_ref(),
+ Path::new(&fs_file),
+ fs_vfstype.as_bytes(),
+ &removable_entries,
+ )
+ })
+ .collect()
+}
+
+pub(crate) fn get_all_disks() -> Vec<Disk> {
+ get_all_disks_inner(&get_all_data("/proc/mounts", 16_385).unwrap_or_default())
+}
+
+// #[test]
+// fn check_all_disks() {
+// let disks = get_all_disks_inner(
+// r#"tmpfs /proc tmpfs rw,seclabel,relatime 0 0
+// proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
+// systemd-1 /proc/sys/fs/binfmt_misc autofs rw,relatime,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=17771 0 0
+// tmpfs /sys tmpfs rw,seclabel,relatime 0 0
+// sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
+// securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0
+// cgroup2 /sys/fs/cgroup cgroup2 rw,seclabel,nosuid,nodev,noexec,relatime,nsdelegate 0 0
+// pstore /sys/fs/pstore pstore rw,seclabel,nosuid,nodev,noexec,relatime 0 0
+// none /sys/fs/bpf bpf rw,nosuid,nodev,noexec,relatime,mode=700 0 0
+// configfs /sys/kernel/config configfs rw,nosuid,nodev,noexec,relatime 0 0
+// selinuxfs /sys/fs/selinux selinuxfs rw,relatime 0 0
+// debugfs /sys/kernel/debug debugfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
+// tmpfs /dev/shm tmpfs rw,seclabel,relatime 0 0
+// devpts /dev/pts devpts rw,seclabel,relatime,gid=5,mode=620,ptmxmode=666 0 0
+// tmpfs /sys/fs/selinux tmpfs rw,seclabel,relatime 0 0
+// /dev/vda2 /proc/filesystems xfs rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota 0 0
+// "#,
+// );
+// assert_eq!(disks.len(), 1);
+// assert_eq!(
+// disks[0],
+// Disk {
+// type_: DiskType::Unknown(-1),
+// name: OsString::from("devpts"),
+// file_system: vec![100, 101, 118, 112, 116, 115],
+// mount_point: PathBuf::from("/dev/pts"),
+// total_space: 0,
+// available_space: 0,
+// }
+// );
+// }
diff --git a/vendor/sysinfo/src/linux/mod.rs b/vendor/sysinfo/src/linux/mod.rs
new file mode 100644
index 000000000..9cff98907
--- /dev/null
+++ b/vendor/sysinfo/src/linux/mod.rs
@@ -0,0 +1,16 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+pub mod component;
+pub mod cpu;
+pub mod disk;
+pub mod network;
+pub mod process;
+pub mod system;
+pub(crate) mod utils;
+
+pub use self::component::Component;
+pub use self::cpu::Cpu;
+pub use self::disk::Disk;
+pub use self::network::{NetworkData, Networks};
+pub use self::process::Process;
+pub use self::system::System;
diff --git a/vendor/sysinfo/src/linux/network.rs b/vendor/sysinfo/src/linux/network.rs
new file mode 100644
index 000000000..0153c151a
--- /dev/null
+++ b/vendor/sysinfo/src/linux/network.rs
@@ -0,0 +1,314 @@
+// 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 crate::{NetworkExt, NetworksExt, NetworksIter};
+use std::collections::{hash_map, HashMap};
+
+#[doc = include_str!("../../md_doc/networks.md")]
+pub struct Networks {
+ interfaces: HashMap<String, NetworkData>,
+}
+
+macro_rules! old_and_new {
+ ($ty_:expr, $name:ident, $old:ident) => {{
+ $ty_.$old = $ty_.$name;
+ $ty_.$name = $name;
+ }};
+ ($ty_:expr, $name:ident, $old:ident, $path:expr) => {{
+ let _tmp = $path;
+ $ty_.$old = $ty_.$name;
+ $ty_.$name = _tmp;
+ }};
+}
+
+#[allow(clippy::ptr_arg)]
+fn read<P: AsRef<Path>>(parent: P, path: &str, data: &mut Vec<u8>) -> u64 {
+ if let Ok(mut f) = File::open(parent.as_ref().join(path)) {
+ if let Ok(size) = f.read(data) {
+ let mut i = 0;
+ let mut ret = 0;
+
+ while i < size && i < data.len() && data[i] >= b'0' && data[i] <= b'9' {
+ ret *= 10;
+ ret += (data[i] - b'0') as u64;
+ i += 1;
+ }
+ return ret;
+ }
+ }
+ 0
+}
+
+impl Networks {
+ pub(crate) fn new() -> Self {
+ Networks {
+ interfaces: HashMap::new(),
+ }
+ }
+}
+
+fn refresh_networks_list_from_sysfs(
+ interfaces: &mut HashMap<String, NetworkData>,
+ sysfs_net: &Path,
+) {
+ if let Ok(dir) = std::fs::read_dir(sysfs_net) {
+ let mut data = vec![0; 30];
+
+ for stats in interfaces.values_mut() {
+ stats.updated = false;
+ }
+
+ for entry in dir.flatten() {
+ let parent = &entry.path().join("statistics");
+ let entry = match entry.file_name().into_string() {
+ Ok(entry) => entry,
+ Err(_) => continue,
+ };
+ let rx_bytes = read(parent, "rx_bytes", &mut data);
+ let tx_bytes = read(parent, "tx_bytes", &mut data);
+ let rx_packets = read(parent, "rx_packets", &mut data);
+ let tx_packets = read(parent, "tx_packets", &mut data);
+ let rx_errors = read(parent, "rx_errors", &mut data);
+ let tx_errors = read(parent, "tx_errors", &mut data);
+ // let rx_compressed = read(parent, "rx_compressed", &mut data);
+ // 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();
+ 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);
+ old_and_new!(interface, tx_packets, old_tx_packets);
+ old_and_new!(interface, rx_errors, old_rx_errors);
+ old_and_new!(interface, tx_errors, old_tx_errors);
+ // old_and_new!(e, rx_compressed, old_rx_compressed);
+ // old_and_new!(e, tx_compressed, old_tx_compressed);
+ interface.updated = true;
+ }
+ hash_map::Entry::Vacant(e) => {
+ e.insert(NetworkData {
+ rx_bytes,
+ old_rx_bytes: rx_bytes,
+ tx_bytes,
+ old_tx_bytes: tx_bytes,
+ rx_packets,
+ old_rx_packets: rx_packets,
+ tx_packets,
+ old_tx_packets: tx_packets,
+ rx_errors,
+ old_rx_errors: rx_errors,
+ tx_errors,
+ old_tx_errors: tx_errors,
+ // rx_compressed,
+ // old_rx_compressed: rx_compressed,
+ // tx_compressed,
+ // old_tx_compressed: tx_compressed,
+ updated: true,
+ });
+ }
+ };
+ }
+
+ // Remove interfaces which are gone.
+ interfaces.retain(|_, d| d.updated);
+ }
+}
+
+impl NetworksExt for Networks {
+ fn iter(&self) -> NetworksIter {
+ NetworksIter::new(self.interfaces.iter())
+ }
+
+ fn refresh(&mut self) {
+ let mut v = vec![0; 30];
+
+ for (interface_name, data) in self.interfaces.iter_mut() {
+ data.update(interface_name, &mut v);
+ }
+ }
+
+ fn refresh_networks_list(&mut self) {
+ refresh_networks_list_from_sysfs(&mut self.interfaces, Path::new("/sys/class/net/"));
+ }
+}
+
+#[doc = include_str!("../../md_doc/network_data.md")]
+pub struct NetworkData {
+ /// Total number of bytes received over interface.
+ rx_bytes: u64,
+ old_rx_bytes: u64,
+ /// Total number of bytes transmitted over interface.
+ tx_bytes: u64,
+ old_tx_bytes: u64,
+ /// Total number of packets received.
+ rx_packets: u64,
+ old_rx_packets: u64,
+ /// Total number of packets transmitted.
+ tx_packets: u64,
+ old_tx_packets: u64,
+ /// Shows the total number of packets received with error. This includes
+ /// too-long-frames errors, ring-buffer overflow errors, CRC errors,
+ /// frame alignment errors, fifo overruns, and missed packets.
+ rx_errors: u64,
+ old_rx_errors: u64,
+ /// similar to `rx_errors`
+ tx_errors: u64,
+ old_tx_errors: u64,
+ // /// 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).
+ // rx_compressed: usize,
+ // old_rx_compressed: usize,
+ // /// Indicates the number of transmitted compressed packets. Note
+ // /// this might only be relevant for devices that support
+ // /// compression (e.g: PPP).
+ // tx_compressed: usize,
+ // old_tx_compressed: usize,
+ /// Whether or not the above data has been updated during refresh
+ updated: bool,
+}
+
+impl NetworkData {
+ fn update(&mut self, path: &str, data: &mut Vec<u8>) {
+ let path = &Path::new("/sys/class/net/").join(path).join("statistics");
+ old_and_new!(self, rx_bytes, old_rx_bytes, read(path, "rx_bytes", data));
+ old_and_new!(self, tx_bytes, old_tx_bytes, read(path, "tx_bytes", data));
+ old_and_new!(
+ self,
+ rx_packets,
+ old_rx_packets,
+ read(path, "rx_packets", data)
+ );
+ old_and_new!(
+ self,
+ tx_packets,
+ old_tx_packets,
+ read(path, "tx_packets", data)
+ );
+ old_and_new!(
+ self,
+ rx_errors,
+ old_rx_errors,
+ read(path, "rx_errors", data)
+ );
+ old_and_new!(
+ self,
+ tx_errors,
+ old_tx_errors,
+ read(path, "tx_errors", data)
+ );
+ // old_and_new!(
+ // self,
+ // rx_compressed,
+ // old_rx_compressed,
+ // read(path, "rx_compressed", data)
+ // );
+ // old_and_new!(
+ // self,
+ // tx_compressed,
+ // old_tx_compressed,
+ // read(path, "tx_compressed", data)
+ // );
+ }
+}
+
+impl NetworkExt for NetworkData {
+ fn received(&self) -> u64 {
+ self.rx_bytes.saturating_sub(self.old_rx_bytes)
+ }
+
+ fn total_received(&self) -> u64 {
+ self.rx_bytes
+ }
+
+ fn transmitted(&self) -> u64 {
+ self.tx_bytes.saturating_sub(self.old_tx_bytes)
+ }
+
+ fn total_transmitted(&self) -> u64 {
+ self.tx_bytes
+ }
+
+ fn packets_received(&self) -> u64 {
+ self.rx_packets.saturating_sub(self.old_rx_packets)
+ }
+
+ fn total_packets_received(&self) -> u64 {
+ self.rx_packets
+ }
+
+ fn packets_transmitted(&self) -> u64 {
+ self.tx_packets.saturating_sub(self.old_tx_packets)
+ }
+
+ fn total_packets_transmitted(&self) -> u64 {
+ self.tx_packets
+ }
+
+ fn errors_on_received(&self) -> u64 {
+ self.rx_errors.saturating_sub(self.old_rx_errors)
+ }
+
+ fn total_errors_on_received(&self) -> u64 {
+ self.rx_errors
+ }
+
+ fn errors_on_transmitted(&self) -> u64 {
+ self.tx_errors.saturating_sub(self.old_tx_errors)
+ }
+
+ fn total_errors_on_transmitted(&self) -> u64 {
+ self.tx_errors
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::refresh_networks_list_from_sysfs;
+ use std::collections::HashMap;
+ use std::fs;
+
+ #[test]
+ fn refresh_networks_list_add_interface() {
+ let sys_net_dir = tempfile::tempdir().expect("failed to create temporary directory");
+
+ fs::create_dir(sys_net_dir.path().join("itf1")).expect("failed to create subdirectory");
+
+ let mut interfaces = HashMap::new();
+
+ refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
+ assert_eq!(interfaces.keys().collect::<Vec<_>>(), ["itf1"]);
+
+ fs::create_dir(sys_net_dir.path().join("itf2")).expect("failed to create subdirectory");
+
+ refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
+ let mut itf_names: Vec<String> = interfaces.keys().map(|n| n.to_owned()).collect();
+ itf_names.sort();
+ assert_eq!(itf_names, ["itf1", "itf2"]);
+ }
+
+ #[test]
+ fn refresh_networks_list_remove_interface() {
+ let sys_net_dir = tempfile::tempdir().expect("failed to create temporary directory");
+
+ let itf1_dir = sys_net_dir.path().join("itf1");
+ let itf2_dir = sys_net_dir.path().join("itf2");
+ fs::create_dir(&itf1_dir).expect("failed to create subdirectory");
+ fs::create_dir(&itf2_dir).expect("failed to create subdirectory");
+
+ let mut interfaces = HashMap::new();
+
+ refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
+ let mut itf_names: Vec<String> = interfaces.keys().map(|n| n.to_owned()).collect();
+ itf_names.sort();
+ assert_eq!(itf_names, ["itf1", "itf2"]);
+
+ fs::remove_dir(&itf1_dir).expect("failed to remove subdirectory");
+
+ refresh_networks_list_from_sysfs(&mut interfaces, sys_net_dir.path());
+ assert_eq!(interfaces.keys().collect::<Vec<_>>(), ["itf2"]);
+ }
+}
diff --git a/vendor/sysinfo/src/linux/process.rs b/vendor/sysinfo/src/linux/process.rs
new file mode 100644
index 000000000..0b06e26f1
--- /dev/null
+++ b/vendor/sysinfo/src/linux/process.rs
@@ -0,0 +1,700 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use std::cell::UnsafeCell;
+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;
+
+use libc::{gid_t, kill, uid_t};
+
+use crate::sys::system::{SystemInfo, REMAINING_FILES};
+use crate::sys::utils::{get_all_data, get_all_data_from_file, realpath};
+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,
+ 'Z' => ProcessStatus::Zombie,
+ 'T' => ProcessStatus::Stop,
+ 't' => ProcessStatus::Tracing,
+ 'X' | 'x' => ProcessStatus::Dead,
+ 'K' => ProcessStatus::Wakekill,
+ 'W' => ProcessStatus::Waking,
+ 'P' => ProcessStatus::Parked,
+ x => ProcessStatus::Unknown(x as u32),
+ }
+ }
+}
+
+impl fmt::Display for ProcessStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(match *self {
+ ProcessStatus::Idle => "Idle",
+ ProcessStatus::Run => "Runnable",
+ ProcessStatus::Sleep => "Sleeping",
+ ProcessStatus::Stop => "Stopped",
+ ProcessStatus::Zombie => "Zombie",
+ ProcessStatus::Tracing => "Tracing",
+ ProcessStatus::Dead => "Dead",
+ ProcessStatus::Wakekill => "Wakekill",
+ ProcessStatus::Waking => "Waking",
+ ProcessStatus::Parked => "Parked",
+ _ => "Unknown",
+ })
+ }
+}
+
+#[doc = include_str!("../../md_doc/process.md")]
+pub struct Process {
+ pub(crate) name: String,
+ pub(crate) cmd: Vec<String>,
+ pub(crate) exe: PathBuf,
+ pub(crate) pid: Pid,
+ parent: Option<Pid>,
+ pub(crate) environ: Vec<String>,
+ pub(crate) cwd: PathBuf,
+ pub(crate) root: PathBuf,
+ pub(crate) memory: u64,
+ pub(crate) virtual_memory: u64,
+ utime: u64,
+ stime: u64,
+ old_utime: u64,
+ old_stime: u64,
+ start_time_without_boot_time: u64,
+ start_time: u64,
+ run_time: u64,
+ pub(crate) updated: bool,
+ cpu_usage: f32,
+ user_id: Option<Uid>,
+ group_id: Option<Gid>,
+ pub(crate) status: ProcessStatus,
+ /// Tasks run by this process.
+ pub tasks: HashMap<Pid, Process>,
+ pub(crate) stat_file: Option<File>,
+ old_read_bytes: u64,
+ old_written_bytes: u64,
+ read_bytes: u64,
+ written_bytes: u64,
+}
+
+impl Process {
+ pub(crate) fn new(
+ pid: Pid,
+ parent: Option<Pid>,
+ start_time_without_boot_time: u64,
+ info: &SystemInfo,
+ ) -> Process {
+ Process {
+ name: String::with_capacity(20),
+ pid,
+ parent,
+ cmd: Vec::with_capacity(2),
+ environ: Vec::with_capacity(10),
+ exe: PathBuf::new(),
+ cwd: PathBuf::new(),
+ root: PathBuf::new(),
+ memory: 0,
+ virtual_memory: 0,
+ cpu_usage: 0.,
+ utime: 0,
+ stime: 0,
+ old_utime: 0,
+ old_stime: 0,
+ updated: true,
+ start_time_without_boot_time,
+ start_time: start_time_without_boot_time.saturating_add(info.boot_time),
+ run_time: 0,
+ user_id: None,
+ group_id: None,
+ status: ProcessStatus::Unknown(0),
+ tasks: if pid.0 == 0 {
+ HashMap::with_capacity(1000)
+ } else {
+ HashMap::new()
+ },
+ stat_file: None,
+ old_read_bytes: 0,
+ old_written_bytes: 0,
+ read_bytes: 0,
+ written_bytes: 0,
+ }
+ }
+}
+
+impl ProcessExt for Process {
+ fn kill_with(&self, signal: Signal) -> Option<bool> {
+ let c_signal = super::system::convert_signal(signal)?;
+ unsafe { Some(kill(self.pid.0, c_signal) == 0) }
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ fn cmd(&self) -> &[String] {
+ &self.cmd
+ }
+
+ fn exe(&self) -> &Path {
+ self.exe.as_path()
+ }
+
+ fn pid(&self) -> Pid {
+ self.pid
+ }
+
+ fn environ(&self) -> &[String] {
+ &self.environ
+ }
+
+ fn cwd(&self) -> &Path {
+ self.cwd.as_path()
+ }
+
+ fn root(&self) -> &Path {
+ self.root.as_path()
+ }
+
+ fn memory(&self) -> u64 {
+ self.memory
+ }
+
+ fn virtual_memory(&self) -> u64 {
+ self.virtual_memory
+ }
+
+ fn parent(&self) -> Option<Pid> {
+ self.parent
+ }
+
+ fn status(&self) -> ProcessStatus {
+ self.status
+ }
+
+ fn start_time(&self) -> u64 {
+ self.start_time
+ }
+
+ fn run_time(&self) -> u64 {
+ self.run_time
+ }
+
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn disk_usage(&self) -> DiskUsage {
+ DiskUsage {
+ written_bytes: self.written_bytes.saturating_sub(self.old_written_bytes),
+ total_written_bytes: self.written_bytes,
+ read_bytes: self.read_bytes.saturating_sub(self.old_read_bytes),
+ total_read_bytes: self.read_bytes,
+ }
+ }
+
+ fn user_id(&self) -> Option<&Uid> {
+ self.user_id.as_ref()
+ }
+
+ fn group_id(&self) -> Option<Gid> {
+ self.group_id
+ }
+}
+
+impl Drop for Process {
+ fn drop(&mut self) {
+ if self.stat_file.is_some() {
+ unsafe {
+ if let Ok(ref mut x) = crate::sys::system::REMAINING_FILES.lock() {
+ **x += 1;
+ }
+ }
+ }
+ }
+}
+
+pub(crate) fn compute_cpu_usage(p: &mut Process, total_time: f32, max_value: f32) {
+ // First time updating the values without reference, wait for a second cycle to update cpu_usage
+ if p.old_utime == 0 && p.old_stime == 0 {
+ return;
+ }
+
+ // We use `max_value` to ensure that the process CPU usage will never get bigger than:
+ // `"number of CPUs" * 100.`
+ p.cpu_usage = ((p.utime.saturating_sub(p.old_utime) + p.stime.saturating_sub(p.old_stime))
+ as f32
+ / total_time
+ * 100.)
+ .min(max_value);
+}
+
+pub(crate) 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;
+}
+
+pub(crate) fn update_process_disk_activity(p: &mut Process, path: &Path) {
+ let mut path = PathBuf::from(path);
+ path.push("io");
+ let data = match get_all_data(&path, 16_384) {
+ Ok(d) => d,
+ Err(_) => return,
+ };
+ let mut done = 0;
+ for line in data.split('\n') {
+ let mut parts = line.split(": ");
+ match parts.next() {
+ Some("read_bytes") => {
+ p.old_read_bytes = p.read_bytes;
+ p.read_bytes = parts
+ .next()
+ .and_then(|x| x.parse::<u64>().ok())
+ .unwrap_or(p.old_read_bytes);
+ }
+ Some("write_bytes") => {
+ p.old_written_bytes = p.written_bytes;
+ p.written_bytes = parts
+ .next()
+ .and_then(|x| x.parse::<u64>().ok())
+ .unwrap_or(p.old_written_bytes);
+ }
+ _ => continue,
+ }
+ done += 1;
+ if done > 1 {
+ // No need to continue the reading.
+ break;
+ }
+ }
+}
+
+struct Wrap<'a, T>(UnsafeCell<&'a mut T>);
+
+impl<'a, T> Wrap<'a, T> {
+ fn get(&self) -> &'a mut T {
+ unsafe { *(self.0.get()) }
+ }
+}
+
+#[allow(clippy::non_send_fields_in_send_ty)]
+unsafe impl<'a, T> Send for Wrap<'a, T> {}
+unsafe impl<'a, T> Sync for Wrap<'a, T> {}
+
+pub(crate) fn _get_process_data(
+ path: &Path,
+ proc_list: &mut Process,
+ pid: Pid,
+ uptime: u64,
+ info: &SystemInfo,
+ refresh_kind: ProcessRefreshKind,
+) -> Result<(Option<Process>, Pid), ()> {
+ let pid = match path.file_name().and_then(|x| x.to_str()).map(Pid::from_str) {
+ Some(Ok(nb)) if nb != pid => nb,
+ _ => return Err(()),
+ };
+
+ let get_status = |p: &mut Process, part: &str| {
+ p.status = part
+ .chars()
+ .next()
+ .map(ProcessStatus::from)
+ .unwrap_or_else(|| ProcessStatus::Unknown(0));
+ };
+ let parent_memory = proc_list.memory;
+ let parent_virtual_memory = proc_list.virtual_memory;
+ if let Some(ref mut entry) = proc_list.tasks.get_mut(&pid) {
+ let data = if let Some(ref mut f) = entry.stat_file {
+ get_all_data_from_file(f, 1024).map_err(|_| ())?
+ } else {
+ let mut tmp = PathBuf::from(path);
+ tmp.push("stat");
+ let mut file = File::open(tmp).map_err(|_| ())?;
+ let data = get_all_data_from_file(&mut file, 1024).map_err(|_| ())?;
+ entry.stat_file = check_nb_open_files(file);
+ data
+ };
+ let parts = parse_stat_file(&data)?;
+ get_status(entry, parts[2]);
+ update_time_and_memory(
+ path,
+ entry,
+ &parts,
+ parent_memory,
+ parent_virtual_memory,
+ uptime,
+ info,
+ refresh_kind,
+ );
+ if refresh_kind.disk_usage() {
+ update_process_disk_activity(entry, path);
+ }
+ if refresh_kind.user() && entry.user_id.is_none() {
+ let mut tmp = PathBuf::from(path);
+ tmp.push("status");
+ if let Some((user_id, group_id)) = get_uid_and_gid(&tmp) {
+ entry.user_id = Some(Uid(user_id));
+ entry.group_id = Some(Gid(group_id));
+ }
+ }
+ return Ok((None, pid));
+ }
+
+ let mut tmp = PathBuf::from(path);
+
+ tmp.push("stat");
+ let mut file = std::fs::File::open(&tmp).map_err(|_| ())?;
+ let data = get_all_data_from_file(&mut file, 1024).map_err(|_| ())?;
+ let stat_file = check_nb_open_files(file);
+ let parts = parse_stat_file(&data)?;
+ let name = parts[1];
+
+ let parent_pid = if proc_list.pid.0 != 0 {
+ Some(proc_list.pid)
+ } else {
+ match Pid::from_str(parts[3]) {
+ Ok(p) if p.0 != 0 => Some(p),
+ _ => None,
+ }
+ };
+
+ // To be noted that the start time is invalid here, it still needs
+ let start_time = u64::from_str(parts[21]).unwrap_or(0) / info.clock_cycle;
+ let mut p = Process::new(pid, parent_pid, start_time, info);
+
+ p.stat_file = stat_file;
+ get_status(&mut p, parts[2]);
+
+ if refresh_kind.user() {
+ tmp.pop();
+ tmp.push("status");
+ if let Some((user_id, group_id)) = get_uid_and_gid(&tmp) {
+ p.user_id = Some(Uid(user_id));
+ p.group_id = Some(Gid(group_id));
+ }
+ }
+
+ 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();
+ tmp.pop();
+ tmp.push("cmdline");
+ p.cmd = copy_from_file(&tmp);
+ tmp.pop();
+ tmp.push("exe");
+ match tmp.read_link() {
+ Ok(exe_path) => {
+ p.exe = exe_path;
+ }
+ Err(_) => {
+ p.exe = if let Some(cmd) = p.cmd.get(0) {
+ PathBuf::from(cmd)
+ } else {
+ PathBuf::new()
+ };
+ }
+ }
+ tmp.pop();
+ tmp.push("environ");
+ p.environ = copy_from_file(&tmp);
+ tmp.pop();
+ tmp.push("cwd");
+ p.cwd = realpath(&tmp);
+ tmp.pop();
+ tmp.push("root");
+ p.root = realpath(&tmp);
+ }
+
+ update_time_and_memory(
+ path,
+ &mut p,
+ &parts,
+ proc_list.memory,
+ proc_list.virtual_memory,
+ uptime,
+ info,
+ refresh_kind,
+ );
+ if refresh_kind.disk_usage() {
+ update_process_disk_activity(&mut p, path);
+ }
+ Ok((Some(p), pid))
+}
+
+#[allow(clippy::too_many_arguments)]
+fn update_time_and_memory(
+ path: &Path,
+ entry: &mut Process,
+ parts: &[&str],
+ parent_memory: u64,
+ parent_virtual_memory: u64,
+ uptime: u64,
+ info: &SystemInfo,
+ refresh_kind: ProcessRefreshKind,
+) {
+ {
+ // rss
+ entry.memory = u64::from_str(parts[23])
+ .unwrap_or(0)
+ .saturating_mul(info.page_size_kb);
+ if entry.memory >= parent_memory {
+ entry.memory -= parent_memory;
+ }
+ // vsz correspond to the Virtual memory size in bytes. Divising by 1_000 gives us kb.
+ // see: https://man7.org/linux/man-pages/man5/proc.5.html
+ entry.virtual_memory = u64::from_str(parts[22]).unwrap_or(0) / 1_000;
+ if entry.virtual_memory >= parent_virtual_memory {
+ entry.virtual_memory -= parent_virtual_memory;
+ }
+ set_time(
+ entry,
+ u64::from_str(parts[13]).unwrap_or(0),
+ u64::from_str(parts[14]).unwrap_or(0),
+ );
+ entry.run_time = uptime.saturating_sub(entry.start_time_without_boot_time);
+ }
+ refresh_procs(
+ entry,
+ &path.join("task"),
+ entry.pid,
+ uptime,
+ info,
+ refresh_kind,
+ );
+}
+
+pub(crate) fn refresh_procs(
+ proc_list: &mut Process,
+ path: &Path,
+ pid: Pid,
+ uptime: u64,
+ info: &SystemInfo,
+ refresh_kind: ProcessRefreshKind,
+) -> bool {
+ if let Ok(d) = fs::read_dir(path) {
+ let folders = d
+ .filter_map(|entry| {
+ if let Ok(entry) = entry {
+ let entry = entry.path();
+
+ if entry.is_dir() {
+ Some(entry)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ if pid.0 == 0 {
+ let proc_list = Wrap(UnsafeCell::new(proc_list));
+
+ #[cfg(feature = "multithread")]
+ use rayon::iter::ParallelIterator;
+
+ into_iter(folders)
+ .filter_map(|e| {
+ if let Ok((p, _)) = _get_process_data(
+ e.as_path(),
+ proc_list.get(),
+ pid,
+ uptime,
+ info,
+ refresh_kind,
+ ) {
+ p
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>()
+ } else {
+ let mut updated_pids = Vec::with_capacity(folders.len());
+ let new_tasks = folders
+ .iter()
+ .filter_map(|e| {
+ if let Ok((p, pid)) =
+ _get_process_data(e.as_path(), proc_list, pid, uptime, info, refresh_kind)
+ {
+ updated_pids.push(pid);
+ p
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ // Sub-tasks are not cleaned up outside so we do it here directly.
+ proc_list
+ .tasks
+ .retain(|&pid, _| updated_pids.iter().any(|&x| x == pid));
+ new_tasks
+ }
+ .into_iter()
+ .for_each(|e| {
+ proc_list.tasks.insert(e.pid(), e);
+ });
+ true
+ } else {
+ false
+ }
+}
+
+fn copy_from_file(entry: &Path) -> Vec<String> {
+ match File::open(entry) {
+ Ok(mut f) => {
+ let mut data = vec![0; 16_384];
+
+ if let Ok(size) = f.read(&mut data) {
+ data.truncate(size);
+ let mut out = Vec::with_capacity(20);
+ let mut start = 0;
+ for (pos, x) in data.iter().enumerate() {
+ if *x == 0 {
+ if pos - start >= 1 {
+ if let Ok(s) =
+ std::str::from_utf8(&data[start..pos]).map(|x| x.trim().to_owned())
+ {
+ out.push(s);
+ }
+ }
+ start = pos + 1; // to keeping prevent '\0'
+ }
+ }
+ 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));
+ }
+ }
+
+ 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.
+
+ let f = |h: &str, n: &str| -> Option<uid_t> {
+ if h.starts_with(n) {
+ h.split_whitespace().nth(2).unwrap_or("0").parse().ok()
+ } else {
+ None
+ }
+ };
+ let mut uid = None;
+ let mut 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);
+ } else {
+ continue;
+ }
+ if uid.is_some() && gid.is_some() {
+ break;
+ }
+ }
+ match (uid, gid) {
+ (Some(u), Some(g)) => Some((u, g)),
+ _ => None,
+ }
+}
+
+fn check_nb_open_files(f: File) -> Option<File> {
+ unsafe {
+ if let Ok(ref mut x) = REMAINING_FILES.lock() {
+ if **x > 0 {
+ **x -= 1;
+ return Some(f);
+ }
+ }
+ }
+ // Something bad happened...
+ None
+}
+
+macro_rules! unwrap_or_return {
+ ($data:expr) => {{
+ match $data {
+ Some(x) => x,
+ None => return Err(()),
+ }
+ }};
+}
+
+fn parse_stat_file(data: &str) -> Result<Vec<&str>, ()> {
+ // The stat file is "interesting" to parse, because spaces cannot
+ // be used as delimiters. The second field stores the command name
+ // surrounded by parentheses. Unfortunately, whitespace and
+ // parentheses are legal parts of the command, so parsing has to
+ // proceed like this: The first field is delimited by the first
+ // whitespace, the second field is everything until the last ')'
+ // in the entire string. All other fields are delimited by
+ // whitespace.
+
+ let mut parts = Vec::with_capacity(52);
+ let mut data_it = data.splitn(2, ' ');
+ parts.push(unwrap_or_return!(data_it.next()));
+ let mut data_it = unwrap_or_return!(data_it.next()).rsplitn(2, ')');
+ let data = unwrap_or_return!(data_it.next());
+ parts.push(unwrap_or_return!(data_it.next()));
+ parts.extend(data.split_whitespace());
+ // Remove command name '('
+ if let Some(name) = parts[1].strip_prefix('(') {
+ parts[1] = name;
+ }
+ Ok(parts)
+}
diff --git a/vendor/sysinfo/src/linux/system.rs b/vendor/sysinfo/src/linux/system.rs
new file mode 100644
index 000000000..7a8d0a010
--- /dev/null
+++ b/vendor/sysinfo/src/linux/system.rs
@@ -0,0 +1,852 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::component::{self, Component};
+use crate::sys::cpu::*;
+use crate::sys::disk;
+use crate::sys::process::*;
+use crate::sys::utils::get_all_data;
+use crate::{
+ CpuRefreshKind, Disk, LoadAvg, Networks, Pid, ProcessRefreshKind, RefreshKind, SystemExt, User,
+};
+
+use libc::{self, c_char, c_int, sysconf, _SC_CLK_TCK, _SC_HOST_NAME_MAX, _SC_PAGESIZE};
+use std::collections::HashMap;
+use std::fs::File;
+use std::io::{BufRead, BufReader, Read};
+use std::path::Path;
+use std::str::FromStr;
+use std::sync::{Arc, Mutex};
+
+// 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.
+#[allow(clippy::mutex_atomic)]
+pub(crate) static mut REMAINING_FILES: once_cell::sync::Lazy<Arc<Mutex<isize>>> =
+ once_cell::sync::Lazy::new(|| {
+ unsafe {
+ let mut limits = libc::rlimit {
+ rlim_cur: 0,
+ rlim_max: 0,
+ };
+ if libc::getrlimit(libc::RLIMIT_NOFILE, &mut limits) != 0 {
+ // Most linux system now defaults to 1024.
+ return Arc::new(Mutex::new(1024 / 2));
+ }
+ // We save the value in case the update fails.
+ let current = limits.rlim_cur;
+
+ // The set the soft limit to the hard one.
+ limits.rlim_cur = limits.rlim_max;
+ // In this part, we leave minimum 50% of the available file descriptors to the process
+ // using sysinfo.
+ Arc::new(Mutex::new(
+ if libc::setrlimit(libc::RLIMIT_NOFILE, &limits) == 0 {
+ limits.rlim_cur / 2
+ } else {
+ current / 2
+ } as _,
+ ))
+ }
+ });
+
+pub(crate) fn get_max_nb_fds() -> isize {
+ unsafe {
+ let mut limits = libc::rlimit {
+ rlim_cur: 0,
+ rlim_max: 0,
+ };
+ if libc::getrlimit(libc::RLIMIT_NOFILE, &mut limits) != 0 {
+ // Most linux system now defaults to 1024.
+ 1024 / 2
+ } else {
+ limits.rlim_max as isize / 2
+ }
+ }
+}
+
+macro_rules! to_str {
+ ($e:expr) => {
+ unsafe { std::str::from_utf8_unchecked($e) }
+ };
+}
+
+fn boot_time() -> u64 {
+ if let Ok(f) = File::open("/proc/stat") {
+ let buf = BufReader::new(f);
+ let line = buf
+ .split(b'\n')
+ .filter_map(|r| r.ok())
+ .find(|l| l.starts_with(b"btime"));
+
+ if let Some(line) = line {
+ return line
+ .split(|x| *x == b' ')
+ .filter(|s| !s.is_empty())
+ .nth(1)
+ .map(to_u64)
+ .unwrap_or(0);
+ }
+ }
+ // 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 {
+ if libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut up) == 0 {
+ up.tv_sec as u64
+ } else {
+ sysinfo_debug!("clock_gettime failed: boot time cannot be retrieve...");
+ 0
+ }
+ }
+}
+
+pub(crate) struct SystemInfo {
+ pub(crate) page_size_kb: u64,
+ pub(crate) clock_cycle: u64,
+ pub(crate) boot_time: u64,
+}
+
+impl SystemInfo {
+ fn new() -> Self {
+ unsafe {
+ Self {
+ page_size_kb: (sysconf(_SC_PAGESIZE) / 1024) as _,
+ clock_cycle: sysconf(_SC_CLK_TCK) as _,
+ boot_time: boot_time(),
+ }
+ }
+ }
+}
+
+declare_signals! {
+ c_int,
+ Signal::Hangup => libc::SIGHUP,
+ Signal::Interrupt => libc::SIGINT,
+ Signal::Quit => libc::SIGQUIT,
+ Signal::Illegal => libc::SIGILL,
+ Signal::Trap => libc::SIGTRAP,
+ Signal::Abort => libc::SIGABRT,
+ Signal::IOT => libc::SIGIOT,
+ Signal::Bus => libc::SIGBUS,
+ Signal::FloatingPointException => libc::SIGFPE,
+ Signal::Kill => libc::SIGKILL,
+ Signal::User1 => libc::SIGUSR1,
+ Signal::Segv => libc::SIGSEGV,
+ Signal::User2 => libc::SIGUSR2,
+ Signal::Pipe => libc::SIGPIPE,
+ Signal::Alarm => libc::SIGALRM,
+ Signal::Term => libc::SIGTERM,
+ Signal::Child => libc::SIGCHLD,
+ Signal::Continue => libc::SIGCONT,
+ Signal::Stop => libc::SIGSTOP,
+ Signal::TSTP => libc::SIGTSTP,
+ Signal::TTIN => libc::SIGTTIN,
+ Signal::TTOU => libc::SIGTTOU,
+ Signal::Urgent => libc::SIGURG,
+ Signal::XCPU => libc::SIGXCPU,
+ Signal::XFSZ => libc::SIGXFSZ,
+ Signal::VirtualAlarm => libc::SIGVTALRM,
+ Signal::Profiling => libc::SIGPROF,
+ Signal::Winch => libc::SIGWINCH,
+ Signal::IO => libc::SIGIO,
+ Signal::Poll => libc::SIGPOLL,
+ Signal::Power => libc::SIGPWR,
+ Signal::Sys => libc::SIGSYS,
+}
+
+#[doc = include_str!("../../md_doc/system.md")]
+pub struct System {
+ process_list: Process,
+ mem_total: u64,
+ mem_free: u64,
+ mem_available: u64,
+ mem_buffers: u64,
+ mem_page_cache: u64,
+ mem_slab_reclaimable: u64,
+ swap_total: u64,
+ swap_free: u64,
+ global_cpu: Cpu,
+ cpus: Vec<Cpu>,
+ components: Vec<Component>,
+ disks: Vec<Disk>,
+ networks: Networks,
+ users: Vec<User>,
+ /// Field set to `false` in `update_cpus` and to `true` in `refresh_processes_specifics`.
+ ///
+ /// The reason behind this is to avoid calling the `update_cpus` more than necessary.
+ /// For example when running `refresh_all` or `refresh_specifics`.
+ need_cpus_update: bool,
+ info: SystemInfo,
+ got_cpu_frequency: bool,
+}
+
+impl System {
+ /// It is sometime possible that a CPU usage computation is bigger than
+ /// `"number of CPUs" * 100`.
+ ///
+ /// To prevent that, we compute ahead of time this maximum value and ensure that processes'
+ /// CPU usage don't go over it.
+ fn get_max_process_cpu_usage(&self) -> f32 {
+ self.cpus.len() as f32 * 100.
+ }
+
+ fn clear_procs(&mut self, refresh_kind: ProcessRefreshKind) {
+ let (total_time, compute_cpu, max_value) = if refresh_kind.cpu() {
+ if self.need_cpus_update {
+ self.refresh_cpus(true, CpuRefreshKind::new().with_cpu_usage());
+ }
+
+ if self.cpus.is_empty() {
+ sysinfo_debug!("cannot compute processes CPU usage: no CPU found...");
+ (0., false, 0.)
+ } else {
+ let (new, old) = get_raw_times(&self.global_cpu);
+ let total_time = if old > new { 1 } else { new - old };
+ (
+ total_time as f32 / self.cpus.len() as f32,
+ true,
+ self.get_max_process_cpu_usage(),
+ )
+ }
+ } else {
+ (0., false, 0.)
+ };
+
+ self.process_list.tasks.retain(|_, proc_| {
+ if !proc_.updated {
+ return false;
+ }
+ if compute_cpu {
+ compute_cpu_usage(proc_, total_time, max_value);
+ }
+ proc_.updated = false;
+ true
+ });
+ }
+
+ fn refresh_cpus(&mut self, only_update_global_cpu: bool, refresh_kind: CpuRefreshKind) {
+ if let Ok(f) = File::open("/proc/stat") {
+ self.need_cpus_update = false;
+
+ let buf = BufReader::new(f);
+ 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 " {
+ 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 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;
+ }
+ }
+ }
+
+ if refresh_kind.frequency() && !self.got_cpu_frequency {
+ #[cfg(feature = "multithread")]
+ use rayon::iter::{
+ IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator,
+ };
+
+ #[cfg(feature = "multithread")]
+ // This function is voluntarily made generic in case we want to generalize it.
+ fn iter_mut<'a, T>(
+ val: &'a mut T,
+ ) -> <&'a mut T as rayon::iter::IntoParallelIterator>::Iter
+ where
+ &'a mut T: rayon::iter::IntoParallelIterator,
+ {
+ val.par_iter_mut()
+ }
+
+ #[cfg(not(feature = "multithread"))]
+ fn iter_mut<'a>(val: &'a mut Vec<Cpu>) -> std::slice::IterMut<'a, Cpu> {
+ val.iter_mut()
+ }
+
+ // `get_cpu_frequency` is very slow, so better run it in parallel.
+ self.global_cpu.frequency = iter_mut(&mut self.cpus)
+ .enumerate()
+ .map(|(pos, proc_)| {
+ proc_.frequency = get_cpu_frequency(pos);
+ proc_.frequency
+ })
+ .max()
+ .unwrap_or(0);
+
+ self.got_cpu_frequency = true;
+ }
+
+ if first {
+ self.global_cpu.vendor_id = vendor_id;
+ self.global_cpu.brand = brand;
+ }
+ }
+ }
+}
+
+impl SystemExt for System {
+ const IS_SUPPORTED: bool = true;
+ const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals();
+
+ fn new_with_specifics(refreshes: RefreshKind) -> System {
+ let info = SystemInfo::new();
+ let process_list = Process::new(Pid(0), None, 0, &info);
+ let mut s = System {
+ process_list,
+ mem_total: 0,
+ mem_free: 0,
+ mem_available: 0,
+ mem_buffers: 0,
+ mem_page_cache: 0,
+ mem_slab_reclaimable: 0,
+ swap_total: 0,
+ swap_free: 0,
+ global_cpu: Cpu::new_with_values(
+ "",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ String::new(),
+ String::new(),
+ ),
+ cpus: Vec::with_capacity(4),
+ components: Vec::new(),
+ disks: Vec::with_capacity(2),
+ networks: Networks::new(),
+ users: Vec::new(),
+ need_cpus_update: true,
+ info,
+ got_cpu_frequency: false,
+ };
+ s.refresh_specifics(refreshes);
+ s
+ }
+
+ fn refresh_components_list(&mut self) {
+ self.components = component::get_components();
+ }
+
+ fn refresh_memory(&mut self) {
+ if let Ok(data) = get_all_data("/proc/meminfo", 16_385) {
+ for line in data.split('\n') {
+ let mut iter = line.split(':');
+ let field = match iter.next() {
+ Some("MemTotal") => &mut self.mem_total,
+ Some("MemFree") => &mut self.mem_free,
+ Some("MemAvailable") => &mut self.mem_available,
+ Some("Buffers") => &mut self.mem_buffers,
+ Some("Cached") => &mut self.mem_page_cache,
+ Some("SReclaimable") => &mut self.mem_slab_reclaimable,
+ Some("SwapTotal") => &mut self.swap_total,
+ Some("SwapFree") => &mut self.swap_free,
+ _ => continue,
+ };
+ if let Some(val_str) = iter.next().and_then(|s| s.trim_start().split(' ').next()) {
+ if let Ok(value) = u64::from_str(val_str) {
+ // /proc/meminfo reports KiB, though it says "kB". Convert it.
+ *field = value.saturating_mul(128) / 125;
+ }
+ }
+ }
+ }
+ }
+
+ fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind) {
+ self.refresh_cpus(false, refresh_kind);
+ }
+
+ fn refresh_processes_specifics(&mut self, refresh_kind: ProcessRefreshKind) {
+ let uptime = self.uptime();
+ refresh_procs(
+ &mut self.process_list,
+ Path::new("/proc"),
+ Pid(0),
+ uptime,
+ &self.info,
+ refresh_kind,
+ );
+ self.clear_procs(refresh_kind);
+ self.need_cpus_update = true;
+ }
+
+ fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool {
+ let uptime = self.uptime();
+ let found = match _get_process_data(
+ &Path::new("/proc/").join(pid.to_string()),
+ &mut self.process_list,
+ Pid(0),
+ uptime,
+ &self.info,
+ refresh_kind,
+ ) {
+ Ok((Some(p), pid)) => {
+ self.process_list.tasks.insert(pid, p);
+ true
+ }
+ Ok(_) => true,
+ Err(_) => false,
+ };
+ if found {
+ 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) = get_raw_times(&self.global_cpu);
+ 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;
+ }
+ }
+ found
+ }
+
+ fn refresh_disks_list(&mut self) {
+ self.disks = disk::get_all_disks();
+ }
+
+ fn refresh_users_list(&mut self) {
+ self.users = crate::users::get_users_list();
+ }
+
+ // COMMON PART
+ //
+ // Need to be moved into a "common" file to avoid duplication.
+
+ fn processes(&self) -> &HashMap<Pid, Process> {
+ &self.process_list.tasks
+ }
+
+ fn process(&self, pid: Pid) -> Option<&Process> {
+ self.process_list.tasks.get(&pid)
+ }
+
+ fn networks(&self) -> &Networks {
+ &self.networks
+ }
+
+ fn networks_mut(&mut self) -> &mut Networks {
+ &mut self.networks
+ }
+
+ fn global_cpu_info(&self) -> &Cpu {
+ &self.global_cpu
+ }
+
+ fn cpus(&self) -> &[Cpu] {
+ &self.cpus
+ }
+
+ fn physical_core_count(&self) -> Option<usize> {
+ get_physical_core_count()
+ }
+
+ fn total_memory(&self) -> u64 {
+ self.mem_total
+ }
+
+ fn free_memory(&self) -> u64 {
+ self.mem_free
+ }
+
+ fn available_memory(&self) -> u64 {
+ self.mem_available
+ }
+
+ fn used_memory(&self) -> u64 {
+ self.mem_total
+ - self.mem_free
+ - self.mem_buffers
+ - self.mem_page_cache
+ - self.mem_slab_reclaimable
+ }
+
+ fn total_swap(&self) -> u64 {
+ self.swap_total
+ }
+
+ fn free_swap(&self) -> u64 {
+ self.swap_free
+ }
+
+ // need to be checked
+ fn used_swap(&self) -> u64 {
+ self.swap_total - self.swap_free
+ }
+
+ fn components(&self) -> &[Component] {
+ &self.components
+ }
+
+ fn components_mut(&mut self) -> &mut [Component] {
+ &mut self.components
+ }
+
+ fn disks(&self) -> &[Disk] {
+ &self.disks
+ }
+
+ fn disks_mut(&mut self) -> &mut [Disk] {
+ &mut self.disks
+ }
+
+ fn uptime(&self) -> u64 {
+ let content = get_all_data("/proc/uptime", 50).unwrap_or_default();
+ content
+ .split('.')
+ .next()
+ .and_then(|t| t.parse().ok())
+ .unwrap_or_default()
+ }
+
+ fn boot_time(&self) -> u64 {
+ self.info.boot_time
+ }
+
+ fn load_average(&self) -> LoadAvg {
+ let mut s = String::new();
+ if File::open("/proc/loadavg")
+ .and_then(|mut f| f.read_to_string(&mut s))
+ .is_err()
+ {
+ return LoadAvg::default();
+ }
+ let loads = s
+ .trim()
+ .split(' ')
+ .take(3)
+ .map(|val| val.parse::<f64>().unwrap())
+ .collect::<Vec<f64>>();
+ LoadAvg {
+ one: loads[0],
+ five: loads[1],
+ fifteen: loads[2],
+ }
+ }
+
+ fn users(&self) -> &[User] {
+ &self.users
+ }
+
+ #[cfg(not(target_os = "android"))]
+ fn name(&self) -> Option<String> {
+ get_system_info_linux(
+ InfoType::Name,
+ Path::new("/etc/os-release"),
+ Path::new("/etc/lsb-release"),
+ )
+ }
+
+ #[cfg(target_os = "android")]
+ fn name(&self) -> Option<String> {
+ get_system_info_android(InfoType::Name)
+ }
+
+ fn long_os_version(&self) -> Option<String> {
+ #[cfg(target_os = "android")]
+ let system_name = "Android";
+
+ #[cfg(not(target_os = "android"))]
+ let system_name = "Linux";
+
+ Some(format!(
+ "{} {} {}",
+ system_name,
+ self.os_version().unwrap_or_default(),
+ self.name().unwrap_or_default()
+ ))
+ }
+
+ fn host_name(&self) -> Option<String> {
+ unsafe {
+ let hostname_max = sysconf(_SC_HOST_NAME_MAX);
+ let mut buffer = vec![0_u8; hostname_max as usize];
+ if libc::gethostname(buffer.as_mut_ptr() as *mut c_char, buffer.len()) == 0 {
+ if let Some(pos) = buffer.iter().position(|x| *x == 0) {
+ // Shrink buffer to terminate the null bytes
+ buffer.resize(pos, 0);
+ }
+ String::from_utf8(buffer).ok()
+ } else {
+ sysinfo_debug!("gethostname failed: hostname cannot be retrieved...");
+ None
+ }
+ }
+ }
+
+ fn kernel_version(&self) -> Option<String> {
+ let mut raw = std::mem::MaybeUninit::<libc::utsname>::zeroed();
+
+ unsafe {
+ if libc::uname(raw.as_mut_ptr()) == 0 {
+ let info = raw.assume_init();
+
+ let release = info
+ .release
+ .iter()
+ .filter(|c| **c != 0)
+ .map(|c| *c as u8 as char)
+ .collect::<String>();
+
+ Some(release)
+ } else {
+ None
+ }
+ }
+ }
+
+ #[cfg(not(target_os = "android"))]
+ fn os_version(&self) -> Option<String> {
+ get_system_info_linux(
+ InfoType::OsVersion,
+ Path::new("/etc/os-release"),
+ Path::new("/etc/lsb-release"),
+ )
+ }
+
+ #[cfg(target_os = "android")]
+ fn os_version(&self) -> Option<String> {
+ get_system_info_android(InfoType::OsVersion)
+ }
+}
+
+impl Default for System {
+ fn default() -> System {
+ System::new()
+ }
+}
+
+fn to_u64(v: &[u8]) -> u64 {
+ let mut x = 0;
+
+ for c in v {
+ x *= 10;
+ x += u64::from(c - b'0');
+ }
+ x
+}
+
+#[derive(PartialEq, Eq)]
+enum InfoType {
+ /// The end-user friendly name of:
+ /// - Android: The device model
+ /// - Linux: The distributions name
+ Name,
+ OsVersion,
+}
+
+#[cfg(not(target_os = "android"))]
+fn get_system_info_linux(info: InfoType, path: &Path, fallback_path: &Path) -> Option<String> {
+ if let Ok(f) = File::open(path) {
+ let reader = BufReader::new(f);
+
+ let info_str = match info {
+ InfoType::Name => "NAME=",
+ InfoType::OsVersion => "VERSION_ID=",
+ };
+
+ for line in reader.lines().flatten() {
+ if let Some(stripped) = line.strip_prefix(info_str) {
+ return Some(stripped.replace('"', ""));
+ }
+ }
+ }
+
+ // Fallback to `/etc/lsb-release` file for systems where VERSION_ID is not included.
+ // VERSION_ID is not required in the `/etc/os-release` file
+ // per https://www.linux.org/docs/man5/os-release.html
+ // If this fails for some reason, fallback to None
+ let reader = BufReader::new(File::open(fallback_path).ok()?);
+
+ let info_str = match info {
+ InfoType::OsVersion => "DISTRIB_RELEASE=",
+ InfoType::Name => "DISTRIB_ID=",
+ };
+ for line in reader.lines().flatten() {
+ if let Some(stripped) = line.strip_prefix(info_str) {
+ return Some(stripped.replace('"', ""));
+ }
+ }
+ None
+}
+
+#[cfg(target_os = "android")]
+fn get_system_info_android(info: InfoType) -> Option<String> {
+ // https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/os/Build.java#58
+ let name: &'static [u8] = match info {
+ InfoType::Name => b"ro.product.model\0",
+ InfoType::OsVersion => b"ro.build.version.release\0",
+ };
+
+ let mut value_buffer = vec![0u8; libc::PROP_VALUE_MAX as usize];
+ unsafe {
+ let len = libc::__system_property_get(
+ name.as_ptr() as *const c_char,
+ value_buffer.as_mut_ptr() as *mut c_char,
+ );
+
+ if len != 0 {
+ if let Some(pos) = value_buffer.iter().position(|c| *c == 0) {
+ value_buffer.resize(pos, 0);
+ }
+ String::from_utf8(value_buffer).ok()
+ } else {
+ None
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ #[cfg(target_os = "android")]
+ use super::get_system_info_android;
+ #[cfg(not(target_os = "android"))]
+ use super::get_system_info_linux;
+ use super::InfoType;
+
+ #[test]
+ #[cfg(target_os = "android")]
+ fn lsb_release_fallback_android() {
+ assert!(get_system_info_android(InfoType::OsVersion).is_some());
+ assert!(get_system_info_android(InfoType::Name).is_some());
+ }
+
+ #[test]
+ #[cfg(not(target_os = "android"))]
+ fn lsb_release_fallback_not_android() {
+ use std::path::Path;
+
+ let dir = tempfile::tempdir().expect("failed to create temporary directory");
+ let tmp1 = dir.path().join("tmp1");
+ let tmp2 = dir.path().join("tmp2");
+
+ // /etc/os-release
+ std::fs::write(
+ &tmp1,
+ r#"NAME="Ubuntu"
+VERSION="20.10 (Groovy Gorilla)"
+ID=ubuntu
+ID_LIKE=debian
+PRETTY_NAME="Ubuntu 20.10"
+VERSION_ID="20.10"
+VERSION_CODENAME=groovy
+UBUNTU_CODENAME=groovy
+"#,
+ )
+ .expect("Failed to create tmp1");
+
+ // /etc/lsb-release
+ std::fs::write(
+ &tmp2,
+ r#"DISTRIB_ID=Ubuntu
+DISTRIB_RELEASE=20.10
+DISTRIB_CODENAME=groovy
+DISTRIB_DESCRIPTION="Ubuntu 20.10"
+"#,
+ )
+ .expect("Failed to create tmp2");
+
+ // Check for the "normal" path: "/etc/os-release"
+ assert_eq!(
+ get_system_info_linux(InfoType::OsVersion, &tmp1, Path::new("")),
+ Some("20.10".to_owned())
+ );
+ assert_eq!(
+ get_system_info_linux(InfoType::Name, &tmp1, Path::new("")),
+ Some("Ubuntu".to_owned())
+ );
+
+ // Check for the "fallback" path: "/etc/lsb-release"
+ assert_eq!(
+ get_system_info_linux(InfoType::OsVersion, Path::new(""), &tmp2),
+ Some("20.10".to_owned())
+ );
+ assert_eq!(
+ get_system_info_linux(InfoType::Name, Path::new(""), &tmp2),
+ Some("Ubuntu".to_owned())
+ );
+ }
+}
diff --git a/vendor/sysinfo/src/linux/utils.rs b/vendor/sysinfo/src/linux/utils.rs
new file mode 100644
index 000000000..cd881a3d1
--- /dev/null
+++ b/vendor/sysinfo/src/linux/utils.rs
@@ -0,0 +1,55 @@
+// 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::path::Path;
+
+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.read_to_string(&mut buf)?;
+ Ok(buf)
+}
+
+pub(crate) fn get_all_data<P: AsRef<Path>>(file_path: P, size: usize) -> io::Result<String> {
+ let mut file = File::open(file_path.as_ref())?;
+ get_all_data_from_file(&mut file, size)
+}
+
+#[allow(clippy::useless_conversion)]
+pub(crate) fn realpath(original: &Path) -> std::path::PathBuf {
+ use libc::{lstat, stat, S_IFLNK, S_IFMT};
+ use std::fs;
+ use std::mem::MaybeUninit;
+ use std::path::PathBuf;
+
+ fn and(x: u32, y: u32) -> u32 {
+ x & y
+ }
+
+ // let ori = Path::new(original.to_str().unwrap());
+ // Right now lstat on windows doesn't work quite well
+ // if cfg!(windows) {
+ // return PathBuf::from(ori);
+ // }
+ let result = PathBuf::from(original);
+ let mut result_s = result.to_str().unwrap_or("").as_bytes().to_vec();
+ result_s.push(0);
+ let mut buf = MaybeUninit::<stat>::uninit();
+ unsafe {
+ let res = lstat(result_s.as_ptr() as *const _, buf.as_mut_ptr());
+ if res < 0 {
+ PathBuf::new()
+ } else {
+ let buf = buf.assume_init();
+ if and(buf.st_mode.into(), S_IFMT.into()) != S_IFLNK.into() {
+ PathBuf::new()
+ } else {
+ match fs::read_link(&result) {
+ Ok(f) => f,
+ Err(_) => PathBuf::new(),
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/macros.rs b/vendor/sysinfo/src/macros.rs
new file mode 100644
index 000000000..3bb4defa1
--- /dev/null
+++ b/vendor/sysinfo/src/macros.rs
@@ -0,0 +1,58 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+#[cfg(feature = "debug")]
+#[doc(hidden)]
+#[allow(unused)]
+macro_rules! sysinfo_debug {
+ ($($x:tt)*) => {{
+ eprintln!($($x)*);
+ }}
+}
+
+#[cfg(not(feature = "debug"))]
+#[doc(hidden)]
+#[allow(unused)]
+macro_rules! sysinfo_debug {
+ ($($x:tt)*) => {{}};
+}
+
+macro_rules! declare_signals {
+ ($kind:ty, _ => None,) => (
+ use crate::Signal;
+
+ pub(crate) const fn supported_signals() -> &'static [Signal] {
+ &[]
+ }
+ );
+
+ ($kind:ty, $(Signal::$signal:ident => $map:expr,)+ _ => None,) => (
+ use crate::Signal;
+
+ pub(crate) const fn supported_signals() -> &'static [Signal] {
+ &[$(Signal::$signal,)*]
+ }
+
+ #[inline]
+ pub(crate) fn convert_signal(s: Signal) -> Option<$kind> {
+ match s {
+ $(Signal::$signal => Some($map),)*
+ _ => None,
+ }
+ }
+ );
+
+ ($kind:ty, $(Signal::$signal:ident => $map:expr,)+) => (
+ use crate::Signal;
+
+ pub(crate) const fn supported_signals() -> &'static [Signal] {
+ &[$(Signal::$signal,)*]
+ }
+
+ #[inline]
+ pub(crate) fn convert_signal(s: Signal) -> Option<$kind> {
+ match s {
+ $(Signal::$signal => Some($map),)*
+ }
+ }
+ )
+}
diff --git a/vendor/sysinfo/src/sysinfo.h b/vendor/sysinfo/src/sysinfo.h
new file mode 100644
index 000000000..13c039128
--- /dev/null
+++ b/vendor/sysinfo/src/sysinfo.h
@@ -0,0 +1,45 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+#pragma once
+
+#include <sys/types.h>
+#include <stdbool.h>
+
+typedef void* CSystem;
+typedef const void* CProcess;
+typedef const char* RString;
+
+CSystem *sysinfo_init();
+void sysinfo_destroy(CSystem system);
+void sysinfo_refresh_system(CSystem system);
+void sysinfo_refresh_all(CSystem system);
+void sysinfo_refresh_processes(CSystem system);
+#ifdef __linux__
+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);
+#ifdef __linux__
+size_t sysinfo_process_get_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);
+void sysinfo_rstring_free(RString str);
diff --git a/vendor/sysinfo/src/system.rs b/vendor/sysinfo/src/system.rs
new file mode 100644
index 000000000..63a70651f
--- /dev/null
+++ b/vendor/sysinfo/src/system.rs
@@ -0,0 +1,117 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+// Once https://github.com/rust-lang/rfcs/blob/master/text/1422-pub-restricted.md
+// feature gets stabilized, we can move common parts in here.
+
+#[cfg(test)]
+mod tests {
+ use crate::{ProcessExt, System, SystemExt};
+
+ #[test]
+ fn test_refresh_system() {
+ let mut sys = System::new();
+ sys.refresh_system();
+ // We don't want to test on unsupported systems.
+ if System::IS_SUPPORTED {
+ assert!(sys.total_memory() != 0);
+ assert!(sys.free_memory() != 0);
+ }
+ assert!(sys.total_memory() >= sys.free_memory());
+ assert!(sys.total_swap() >= sys.free_swap());
+ }
+
+ #[test]
+ fn test_refresh_process() {
+ let mut sys = System::new();
+ assert!(sys.processes().is_empty(), "no process should be listed!");
+ // We don't want to test on unsupported systems.
+
+ #[cfg(not(feature = "apple-sandbox"))]
+ if System::IS_SUPPORTED {
+ assert!(
+ sys.refresh_process(crate::get_current_pid().expect("failed to get current pid")),
+ "process not listed",
+ );
+ // Ensure that the process was really added to the list!
+ assert!(sys
+ .process(crate::get_current_pid().expect("failed to get current pid"))
+ .is_some());
+ }
+ }
+
+ #[test]
+ fn test_get_process() {
+ let mut sys = System::new();
+ sys.refresh_processes();
+ let current_pid = match crate::get_current_pid() {
+ Ok(pid) => pid,
+ _ => {
+ if !System::IS_SUPPORTED {
+ return;
+ }
+ panic!("get_current_pid should work!");
+ }
+ };
+ if let Some(p) = sys.process(current_pid) {
+ assert!(p.memory() > 0);
+ } else {
+ #[cfg(not(feature = "apple-sandbox"))]
+ assert!(!System::IS_SUPPORTED);
+ }
+ }
+
+ #[test]
+ fn check_if_send_and_sync() {
+ trait Foo {
+ fn foo(&self) {}
+ }
+ impl<T> Foo for T where T: Send {}
+
+ trait Bar {
+ fn bar(&self) {}
+ }
+
+ impl<T> Bar for T where T: Sync {}
+
+ let mut sys = System::new();
+ sys.refresh_processes();
+ let current_pid = match crate::get_current_pid() {
+ Ok(pid) => pid,
+ _ => {
+ if !System::IS_SUPPORTED {
+ return;
+ }
+ panic!("get_current_pid should work!");
+ }
+ };
+ if let Some(p) = sys.process(current_pid) {
+ p.foo(); // If this doesn't compile, it'll simply mean that the Process type
+ // doesn't implement the Send trait.
+ p.bar(); // If this doesn't compile, it'll simply mean that the Process type
+ // doesn't implement the Sync trait.
+ } else {
+ #[cfg(not(feature = "apple-sandbox"))]
+ assert!(!System::IS_SUPPORTED);
+ }
+ }
+
+ #[test]
+ fn check_hostname_has_no_nuls() {
+ let sys = System::new();
+
+ if let Some(hostname) = sys.host_name() {
+ assert!(!hostname.contains('\u{0}'))
+ }
+ }
+
+ #[test]
+ fn check_uptime() {
+ let sys = System::new();
+ let uptime = sys.uptime();
+ if System::IS_SUPPORTED {
+ std::thread::sleep(std::time::Duration::from_millis(1000));
+ let new_uptime = sys.uptime();
+ assert!(uptime < new_uptime);
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/traits.rs b/vendor/sysinfo/src/traits.rs
new file mode 100644
index 000000000..3d5eafaa8
--- /dev/null
+++ b/vendor/sysinfo/src/traits.rs
@@ -0,0 +1,1582 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{
+ common::{Gid, Uid},
+ sys::{Component, Cpu, Disk, Networks, Process},
+};
+use crate::{
+ CpuRefreshKind, DiskType, DiskUsage, LoadAvg, NetworksIter, Pid, ProcessRefreshKind,
+ ProcessStatus, RefreshKind, Signal, User,
+};
+
+use std::collections::HashMap;
+use std::ffi::OsStr;
+use std::fmt::Debug;
+use std::path::Path;
+
+/// Contains all the methods of the [`Disk`][crate::Disk] struct.
+///
+/// ```no_run
+/// use sysinfo::{DiskExt, System, SystemExt};
+///
+/// let s = System::new();
+/// for disk in s.disks() {
+/// println!("{:?}: {:?}", disk.name(), disk.type_());
+/// }
+/// ```
+pub trait DiskExt: Debug {
+ /// Returns the disk type.
+ ///
+ /// ```no_run
+ /// use sysinfo::{DiskExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for disk in s.disks() {
+ /// println!("{:?}", disk.type_());
+ /// }
+ /// ```
+ fn type_(&self) -> DiskType;
+
+ /// Returns the disk name.
+ ///
+ /// ```no_run
+ /// use sysinfo::{DiskExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for disk in s.disks() {
+ /// println!("{:?}", disk.name());
+ /// }
+ /// ```
+ fn name(&self) -> &OsStr;
+
+ /// Returns the file system used on this disk (so for example: `EXT4`, `NTFS`, etc...).
+ ///
+ /// ```no_run
+ /// use sysinfo::{DiskExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for disk in s.disks() {
+ /// println!("{:?}", disk.file_system());
+ /// }
+ /// ```
+ fn file_system(&self) -> &[u8];
+
+ /// Returns the mount point of the disk (`/` for example).
+ ///
+ /// ```no_run
+ /// use sysinfo::{DiskExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for disk in s.disks() {
+ /// println!("{:?}", disk.mount_point());
+ /// }
+ /// ```
+ fn mount_point(&self) -> &Path;
+
+ /// Returns the total disk size, in bytes.
+ ///
+ /// ```no_run
+ /// use sysinfo::{DiskExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for disk in s.disks() {
+ /// println!("{}", disk.total_space());
+ /// }
+ /// ```
+ fn total_space(&self) -> u64;
+
+ /// Returns the available disk size, in bytes.
+ ///
+ /// ```no_run
+ /// use sysinfo::{DiskExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for disk in s.disks() {
+ /// println!("{}", disk.available_space());
+ /// }
+ /// ```
+ fn available_space(&self) -> u64;
+
+ /// Returns `true` if the disk is removable.
+ ///
+ /// ```no_run
+ /// use sysinfo::{DiskExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for disk in s.disks() {
+ /// println!("{}", disk.is_removable());
+ /// }
+ /// ```
+ fn is_removable(&self) -> bool;
+
+ /// Updates the disk' information.
+ ///
+ /// ```no_run
+ /// use sysinfo::{DiskExt, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// for disk in s.disks_mut() {
+ /// disk.refresh();
+ /// }
+ /// ```
+ fn refresh(&mut self) -> bool;
+}
+
+/// Contains all the methods of the [`Process`][crate::Process] struct.
+pub trait ProcessExt: Debug {
+ /// Sends [`Signal::Kill`] to the process (which is the only signal supported on all supported
+ /// platforms by this crate).
+ ///
+ /// If you want to send another signal, take a look at [`ProcessExt::kill_with`].
+ ///
+ /// To get the list of the supported signals on this system, use
+ /// [`SystemExt::SUPPORTED_SIGNALS`].
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// process.kill();
+ /// }
+ /// ```
+ fn kill(&self) -> bool {
+ self.kill_with(Signal::Kill).unwrap_or(false)
+ }
+
+ /// Sends the given `signal` to the process. If the signal doesn't exist on this platform,
+ /// it'll do nothing and will return `None`. Otherwise it'll return if the signal was sent
+ /// successfully.
+ ///
+ /// If you just want to kill the process, use [`ProcessExt::kill`] directly.
+ ///
+ /// To get the list of the supported signals on this system, use
+ /// [`SystemExt::SUPPORTED_SIGNALS`].
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, Signal, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// if process.kill_with(Signal::Kill).is_none() {
+ /// eprintln!("This signal isn't supported on this platform");
+ /// }
+ /// }
+ /// ```
+ fn kill_with(&self, signal: Signal) -> Option<bool>;
+
+ /// Returns the name of the process.
+ ///
+ /// **⚠️ Important ⚠️**
+ ///
+ /// 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.
+ ///
+ /// If you are looking for a specific process, unless you know what you are doing, in most
+ /// cases it's better to use [`ProcessExt::exe`] instead (which can be empty sometimes!).
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{}", process.name());
+ /// }
+ /// ```
+ fn name(&self) -> &str;
+
+ /// Returns the command line.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{:?}", process.cmd());
+ /// }
+ /// ```
+ fn cmd(&self) -> &[String];
+
+ /// Returns the path to the process.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{}", process.exe().display());
+ /// }
+ /// ```
+ fn exe(&self) -> &Path;
+
+ /// Returns the pid of the process.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{}", process.pid());
+ /// }
+ /// ```
+ fn pid(&self) -> Pid;
+
+ /// Returns the environment variables of the process.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{:?}", process.environ());
+ /// }
+ /// ```
+ fn environ(&self) -> &[String];
+
+ /// Returns the current working directory.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{}", process.cwd().display());
+ /// }
+ /// ```
+ fn cwd(&self) -> &Path;
+
+ /// Returns the path of the root directory.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{}", process.root().display());
+ /// }
+ /// ```
+ fn root(&self) -> &Path;
+
+ /// Returns the memory usage (in KB).
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{} KB", process.memory());
+ /// }
+ /// ```
+ fn memory(&self) -> u64;
+
+ /// Returns the virtual memory usage (in KB).
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{} KB", process.virtual_memory());
+ /// }
+ /// ```
+ fn virtual_memory(&self) -> u64;
+
+ /// Returns the parent pid.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{:?}", process.parent());
+ /// }
+ /// ```
+ fn parent(&self) -> Option<Pid>;
+
+ /// Returns the status of the processus.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{:?}", process.status());
+ /// }
+ /// ```
+ fn status(&self) -> ProcessStatus;
+
+ /// Returns the time where the process was started (in seconds) from epoch.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("Started at {} seconds", process.start_time());
+ /// }
+ /// ```
+ fn start_time(&self) -> u64;
+
+ /// Returns for how much time the process has been running (in seconds).
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("Running since {} seconds", process.run_time());
+ /// }
+ /// ```
+ 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.
+ ///
+ /// If you want a value between 0% and 100%, divide the returned value by the number of CPU
+ /// 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).
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{}%", process.cpu_usage());
+ /// }
+ /// ```
+ fn cpu_usage(&self) -> f32;
+
+ /// Returns number of bytes read and written to disk.
+ ///
+ /// ⚠️ On Windows and FreeBSD, this method actually returns **ALL** I/O read and written bytes.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// let disk_usage = process.disk_usage();
+ /// println!("read bytes : new/total => {}/{}",
+ /// disk_usage.read_bytes,
+ /// disk_usage.total_read_bytes,
+ /// );
+ /// println!("written bytes: new/total => {}/{}",
+ /// disk_usage.written_bytes,
+ /// disk_usage.total_written_bytes,
+ /// );
+ /// }
+ /// ```
+ fn disk_usage(&self) -> DiskUsage;
+
+ /// Returns the ID of the owner user 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`].
+ ///
+ /// ```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.user_id());
+ /// }
+ /// ```
+ fn user_id(&self) -> Option<&Uid>;
+
+ /// Returns the process group ID of the process.
+ ///
+ /// ⚠️ 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!("Group id for process 1337: {:?}", process.group_id());
+ /// }
+ /// ```
+ fn group_id(&self) -> Option<Gid>;
+}
+
+/// Contains all the methods of the [`Cpu`][crate::Cpu] struct.
+pub trait CpuExt: Debug {
+ /// Returns this CPU's usage.
+ ///
+ /// Note: You'll need to refresh it at least twice (diff between the first and the second is
+ /// how CPU usage is computed) at first if you want to have a non-zero value.
+ ///
+ /// ```no_run
+ /// use sysinfo::{CpuExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for cpu in s.cpus() {
+ /// println!("{}%", cpu.cpu_usage());
+ /// }
+ /// ```
+ fn cpu_usage(&self) -> f32;
+
+ /// Returns this CPU's name.
+ ///
+ /// ```no_run
+ /// use sysinfo::{CpuExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for cpu in s.cpus() {
+ /// println!("{}", cpu.name());
+ /// }
+ /// ```
+ fn name(&self) -> &str;
+
+ /// Returns the CPU's vendor id.
+ ///
+ /// ```no_run
+ /// use sysinfo::{CpuExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for cpu in s.cpus() {
+ /// println!("{}", cpu.vendor_id());
+ /// }
+ /// ```
+ fn vendor_id(&self) -> &str;
+
+ /// Returns the CPU's brand.
+ ///
+ /// ```no_run
+ /// use sysinfo::{CpuExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for cpu in s.cpus() {
+ /// println!("{}", cpu.brand());
+ /// }
+ /// ```
+ fn brand(&self) -> &str;
+
+ /// Returns the CPU's frequency.
+ ///
+ /// ```no_run
+ /// use sysinfo::{CpuExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// for cpu in s.cpus() {
+ /// println!("{}", cpu.frequency());
+ /// }
+ /// ```
+ fn frequency(&self) -> u64;
+}
+
+/// Contains all the methods of the [`System`][crate::System] type.
+pub trait SystemExt: Sized + Debug + Default + Send + Sync {
+ /// Returns `true` if this OS is supported. Please refer to the
+ /// [crate-level documentation](index.html) to get the list of supported OSes.
+ ///
+ /// ```
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// if System::IS_SUPPORTED {
+ /// println!("This OS is supported!");
+ /// } else {
+ /// println!("This OS isn't supported (yet?).");
+ /// }
+ /// ```
+ const IS_SUPPORTED: bool;
+
+ /// Returns the list of the supported signals on this system (used by
+ /// [`ProcessExt::kill_with`]).
+ ///
+ /// ```
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// println!("supported signals: {:?}", System::SUPPORTED_SIGNALS);
+ /// ```
+ 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.
+ ///
+ /// Use the [`refresh_all`] method to update its internal information (or any of the `refresh_`
+ /// method).
+ ///
+ /// [`System`]: crate::System
+ /// [`refresh_all`]: #method.refresh_all
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// ```
+ fn new() -> Self {
+ Self::new_with_specifics(RefreshKind::new())
+ }
+
+ /// Creates a new [`System`] instance with everything loaded.
+ ///
+ /// It is an equivalent of [`SystemExt::new_with_specifics`]`(`[`RefreshKind::everything`]`())`.
+ ///
+ /// [`System`]: crate::System
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// ```
+ fn new_all() -> Self {
+ Self::new_with_specifics(RefreshKind::everything())
+ }
+
+ /// Creates a new [`System`] instance and refresh the data corresponding to the
+ /// given [`RefreshKind`].
+ ///
+ /// [`System`]: crate::System
+ ///
+ /// ```
+ /// use sysinfo::{RefreshKind, System, SystemExt};
+ ///
+ /// // We want everything except disks.
+ /// let mut system = System::new_with_specifics(RefreshKind::everything().without_disks_list());
+ ///
+ /// assert_eq!(system.disks().len(), 0);
+ /// # if System::IS_SUPPORTED && !cfg!(feature = "apple-sandbox") {
+ /// assert!(system.processes().len() > 0);
+ /// # }
+ ///
+ /// // If you want the disks list afterwards, just call the corresponding
+ /// // "refresh_disks_list":
+ /// system.refresh_disks_list();
+ /// let disks = system.disks();
+ /// ```
+ fn new_with_specifics(refreshes: RefreshKind) -> Self;
+
+ /// Refreshes according to the given [`RefreshKind`]. It calls the corresponding
+ /// "refresh_" methods.
+ ///
+ /// ```
+ /// use sysinfo::{ProcessRefreshKind, RefreshKind, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ ///
+ /// // Let's just update networks and processes:
+ /// s.refresh_specifics(
+ /// RefreshKind::new().with_networks().with_processes(ProcessRefreshKind::everything()),
+ /// );
+ /// ```
+ fn refresh_specifics(&mut self, refreshes: RefreshKind) {
+ if refreshes.memory() {
+ self.refresh_memory();
+ }
+ if let Some(kind) = refreshes.cpu() {
+ self.refresh_cpu_specifics(kind);
+ }
+ if refreshes.components_list() {
+ self.refresh_components_list();
+ } else if refreshes.components() {
+ self.refresh_components();
+ }
+ if refreshes.networks_list() {
+ self.refresh_networks_list();
+ } else if refreshes.networks() {
+ self.refresh_networks();
+ }
+ if let Some(kind) = refreshes.processes() {
+ self.refresh_processes_specifics(kind);
+ }
+ if refreshes.disks_list() {
+ self.refresh_disks_list();
+ } else if refreshes.disks() {
+ self.refresh_disks();
+ }
+ if refreshes.users_list() {
+ self.refresh_users_list();
+ }
+ }
+
+ /// Refreshes all system, processes, disks and network interfaces information.
+ ///
+ /// Please note that it doesn't recompute disks list, components list, network interfaces
+ /// list nor users list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_all();
+ /// ```
+ fn refresh_all(&mut self) {
+ self.refresh_system();
+ self.refresh_processes();
+ self.refresh_disks();
+ self.refresh_networks();
+ }
+
+ /// Refreshes system information (RAM, swap, CPU usage and components' temperature).
+ ///
+ /// If you want some more specific refreshes, you might be interested into looking at
+ /// [`refresh_memory`], [`refresh_cpu`] and [`refresh_components`].
+ ///
+ /// [`refresh_memory`]: SystemExt::refresh_memory
+ /// [`refresh_cpu`]: SystemExt::refresh_memory
+ /// [`refresh_components`]: SystemExt::refresh_components
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_system();
+ /// ```
+ fn refresh_system(&mut self) {
+ self.refresh_memory();
+ self.refresh_cpu();
+ self.refresh_components();
+ }
+
+ /// Refreshes RAM and SWAP usage.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_memory();
+ /// ```
+ fn refresh_memory(&mut self);
+
+ /// Refreshes CPUs information.
+ ///
+ /// ⚠️ 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.
+ ///
+ /// Calling this method is the same as calling
+ /// `refresh_cpu_specifics(CpuRefreshKind::everything())`.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_cpu();
+ /// ```
+ fn refresh_cpu(&mut self) {
+ self.refresh_cpu_specifics(CpuRefreshKind::everything())
+ }
+
+ /// Refreshes CPUs specific information.
+ ///
+ /// Please note that it doesn't recompute disks list, components list, network interfaces
+ /// list nor users list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_all();
+ /// ```
+ fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind);
+
+ /// Refreshes components' temperature.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_components();
+ /// ```
+ fn refresh_components(&mut self) {
+ for component in self.components_mut() {
+ component.refresh();
+ }
+ }
+
+ /// Refreshes components list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new();
+ /// s.refresh_components_list();
+ /// ```
+ fn refresh_components_list(&mut self);
+
+ /// Gets all processes and updates their information.
+ ///
+ /// It does the same as `system.refresh_processes_specifics(ProcessRefreshKind::everything())`.
+ ///
+ /// ⚠️ On Linux, `sysinfo` keeps the `stat` files open by default. You can change this behaviour
+ /// by using [`set_open_files_limit`][crate::set_open_files_limit].
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_processes();
+ /// ```
+ fn refresh_processes(&mut self) {
+ self.refresh_processes_specifics(ProcessRefreshKind::everything());
+ }
+
+ /// Gets all processes and updates the specified information.
+ ///
+ /// ⚠️ On Linux, `sysinfo` keeps the `stat` files open by default. You can change this behaviour
+ /// by using [`set_open_files_limit`][crate::set_open_files_limit].
+ ///
+ /// ```no_run
+ /// use sysinfo::{ProcessRefreshKind, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_processes_specifics(ProcessRefreshKind::new());
+ /// ```
+ fn refresh_processes_specifics(&mut self, refresh_kind: ProcessRefreshKind);
+
+ /// Refreshes *only* the process corresponding to `pid`. Returns `false` if the process doesn't
+ /// exist (it will **NOT** be removed from the processes if it doesn't exist anymore). If it
+ /// isn't listed yet, it'll be added.
+ ///
+ /// It is the same as calling
+ /// `sys.refresh_process_specifics(pid, ProcessRefreshKind::everything())`.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_process(Pid::from(1337));
+ /// ```
+ fn refresh_process(&mut self, pid: Pid) -> bool {
+ self.refresh_process_specifics(pid, ProcessRefreshKind::everything())
+ }
+
+ /// Refreshes *only* the process corresponding to `pid`. Returns `false` if the process doesn't
+ /// exist (it will **NOT** be removed from the processes if it doesn't exist anymore). If it
+ /// isn't listed yet, it'll be added.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessRefreshKind, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_process_specifics(Pid::from(1337), ProcessRefreshKind::new());
+ /// ```
+ fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool;
+
+ /// Refreshes the listed disks' information.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_disks();
+ /// ```
+ fn refresh_disks(&mut self) {
+ for disk in self.disks_mut() {
+ disk.refresh();
+ }
+ }
+
+ /// The disk list will be emptied then completely recomputed.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_disks_list();
+ /// ```
+ fn refresh_disks_list(&mut self);
+
+ /// Refreshes users list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_users_list();
+ /// ```
+ fn refresh_users_list(&mut self);
+
+ /// Refreshes networks data.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_networks();
+ /// ```
+ ///
+ /// It is a shortcut for:
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworksExt, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// let networks = s.networks_mut();
+ /// networks.refresh();
+ /// ```
+ fn refresh_networks(&mut self) {
+ self.networks_mut().refresh();
+ }
+
+ /// The network list will be updated: removing not existing anymore interfaces and adding new
+ /// ones.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// s.refresh_networks_list();
+ /// ```
+ ///
+ /// This is a shortcut for:
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworksExt, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// let networks = s.networks_mut();
+ /// networks.refresh_networks_list();
+ /// ```
+ fn refresh_networks_list(&mut self) {
+ self.networks_mut().refresh_networks_list();
+ }
+
+ /// Returns the process list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// for (pid, process) in s.processes() {
+ /// println!("{} {}", pid, process.name());
+ /// }
+ /// ```
+ fn processes(&self) -> &HashMap<Pid, Process>;
+
+ /// Returns the process corresponding to the given pid or `None` if no such process exists.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// println!("{}", process.name());
+ /// }
+ /// ```
+ fn process(&self, pid: Pid) -> Option<&Process>;
+
+ /// Returns an iterator of process containing the given `name`.
+ ///
+ /// If you want only the processes with exactly the given `name`, take a look at
+ /// [`SystemExt::processes_by_exact_name`].
+ ///
+ /// ```no_run
+ /// use sysinfo::{ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// for process in s.processes_by_name("htop") {
+ /// println!("{} {}", process.pid(), process.name());
+ /// }
+ /// ```
+ // FIXME: replace the returned type with `impl Iterator<Item = &Process>` when it's supported!
+ fn processes_by_name<'a>(
+ &'a self,
+ name: &'a str,
+ ) -> Box<dyn Iterator<Item = &'a Process> + 'a> {
+ Box::new(
+ self.processes()
+ .values()
+ .filter(move |val: &&Process| val.name().contains(name)),
+ )
+ }
+
+ /// Returns an iterator of processes with exactly the given `name`.
+ ///
+ /// If you instead want the processes containing `name`, take a look at
+ /// [`SystemExt::processes_by_name`].
+ ///
+ /// ```no_run
+ /// use sysinfo::{ProcessExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// for process in s.processes_by_exact_name("htop") {
+ /// println!("{} {}", process.pid(), process.name());
+ /// }
+ /// ```
+ // FIXME: replace the returned type with `impl Iterator<Item = &Process>` when it's supported!
+ fn processes_by_exact_name<'a>(
+ &'a self,
+ name: &'a str,
+ ) -> Box<dyn Iterator<Item = &'a Process> + 'a> {
+ Box::new(
+ self.processes()
+ .values()
+ .filter(move |val: &&Process| val.name() == name),
+ )
+ }
+
+ /// 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.
+ ///
+ /// ```no_run
+ /// use sysinfo::{CpuRefreshKind, CpuExt, RefreshKind, System, SystemExt};
+ ///
+ /// let s = System::new_with_specifics(
+ /// RefreshKind::new().with_cpu(CpuRefreshKind::everything()),
+ /// );
+ /// println!("{}%", s.global_cpu_info().cpu_usage());
+ /// ```
+ fn global_cpu_info(&self) -> &Cpu;
+
+ /// Returns the list of the CPUs.
+ ///
+ /// By default, the list of cpus is empty until you call [`SystemExt::refresh_cpu`] or
+ /// [`SystemExt::refresh_specifics`] with `cpu` enabled.
+ ///
+ /// ```no_run
+ /// use sysinfo::{CpuRefreshKind, CpuExt, RefreshKind, System, SystemExt};
+ ///
+ /// let s = System::new_with_specifics(
+ /// RefreshKind::new().with_cpu(CpuRefreshKind::everything()),
+ /// );
+ /// for cpu in s.cpus() {
+ /// println!("{}%", cpu.cpu_usage());
+ /// }
+ /// ```
+ fn cpus(&self) -> &[Cpu];
+
+ /// Returns the number of physical cores on the CPU or `None` if it couldn't get it.
+ ///
+ /// In case there are multiple CPUs, it will combine the physical core count of all the CPUs.
+ ///
+ /// **Important**: this information is computed every time this function is called.
+ ///
+ /// ```no_run
+ /// use sysinfo::{CpuExt, System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// println!("{:?}", s.physical_core_count());
+ /// ```
+ fn physical_core_count(&self) -> Option<usize>;
+
+ /// Returns the RAM size in KB.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// println!("{} KB", s.total_memory());
+ /// ```
+ fn total_memory(&self) -> u64;
+
+ /// Returns the amount of free RAM in KB.
+ ///
+ /// Generally, "free" memory refers to unallocated memory whereas "available" memory refers to
+ /// memory that is available for (re)use.
+ ///
+ /// Side note: Windows doesn't report "free" memory so this method returns the same value
+ /// as [`get_available_memory`](#tymethod.available_memory).
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// println!("{} KB", s.free_memory());
+ /// ```
+ fn free_memory(&self) -> u64;
+
+ /// Returns the amount of available RAM in KB.
+ ///
+ /// Generally, "free" memory refers to unallocated memory whereas "available" memory refers to
+ /// memory that is available for (re)use.
+ ///
+ /// ⚠️ Windows and FreeBSD don't report "available" memory so [`SystemExt::free_memory`]
+ /// returns the same value as this method.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// println!("{} KB", s.available_memory());
+ /// ```
+ fn available_memory(&self) -> u64;
+
+ /// Returns the amount of used RAM in KB.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// println!("{} KB", s.used_memory());
+ /// ```
+ fn used_memory(&self) -> u64;
+
+ /// Returns the SWAP size in KB.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// println!("{} KB", s.total_swap());
+ /// ```
+ fn total_swap(&self) -> u64;
+
+ /// Returns the amount of free SWAP in KB.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// println!("{} KB", s.free_swap());
+ /// ```
+ fn free_swap(&self) -> u64;
+
+ /// Returns the amount of used SWAP in KB.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// println!("{} KB", s.used_swap());
+ /// ```
+ fn used_swap(&self) -> u64;
+
+ /// Returns the components list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{ComponentExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// for component in s.components() {
+ /// println!("{}: {}°C", component.label(), component.temperature());
+ /// }
+ /// ```
+ fn components(&self) -> &[Component];
+
+ /// Returns a mutable components list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{ComponentExt, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// for component in s.components_mut() {
+ /// component.refresh();
+ /// }
+ /// ```
+ fn components_mut(&mut self) -> &mut [Component];
+
+ /// Returns the disks list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{DiskExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// for disk in s.disks() {
+ /// println!("{:?}", disk.name());
+ /// }
+ /// ```
+ fn disks(&self) -> &[Disk];
+
+ /// Returns the users list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt, UserExt};
+ ///
+ /// let mut s = System::new_all();
+ /// for user in s.users() {
+ /// println!("{} is in {} groups", user.name(), user.groups().len());
+ /// }
+ /// ```
+ fn users(&self) -> &[User];
+
+ /// Returns the disks list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{DiskExt, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// for disk in s.disks_mut() {
+ /// disk.refresh();
+ /// }
+ /// ```
+ fn disks_mut(&mut self) -> &mut [Disk];
+
+ /// Returns the network interfaces object.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, data) in networks {
+ /// println!(
+ /// "[{}] in: {}, out: {}",
+ /// interface_name,
+ /// data.received(),
+ /// data.transmitted(),
+ /// );
+ /// }
+ /// ```
+ fn networks(&self) -> &Networks;
+
+ /// Returns a mutable access to network interfaces.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// let networks = s.networks_mut();
+ /// networks.refresh_networks_list();
+ /// ```
+ fn networks_mut(&mut self) -> &mut Networks;
+
+ /// Returns system uptime (in seconds).
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// println!("System running since {} seconds", s.uptime());
+ /// ```
+ fn uptime(&self) -> u64;
+
+ /// Returns the time (in seconds) when the system booted since UNIX epoch.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// println!("System booted at {} seconds", s.boot_time());
+ /// ```
+ fn boot_time(&self) -> u64;
+
+ /// Returns the system load average value.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let load_avg = s.load_average();
+ /// println!(
+ /// "one minute: {}%, five minutes: {}%, fifteen minutes: {}%",
+ /// load_avg.one,
+ /// load_avg.five,
+ /// load_avg.fifteen,
+ /// );
+ /// ```
+ fn load_average(&self) -> LoadAvg;
+
+ /// Returns the system name.
+ ///
+ /// **Important**: this information is computed every time this function is called.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// println!("OS: {:?}", s.name());
+ /// ```
+ fn name(&self) -> Option<String>;
+
+ /// Returns the system's kernel version.
+ ///
+ /// **Important**: this information is computed every time this function is called.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// println!("kernel version: {:?}", s.kernel_version());
+ /// ```
+ fn kernel_version(&self) -> Option<String>;
+
+ /// Returns the system version (e.g. for MacOS this will return 11.1 rather than the kernel version).
+ ///
+ /// **Important**: this information is computed every time this function is called.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// println!("OS version: {:?}", s.os_version());
+ /// ```
+ fn os_version(&self) -> Option<String>;
+
+ /// Returns the system long os version (e.g "MacOS 11.2 BigSur").
+ ///
+ /// **Important**: this information is computed every time this function is called.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// println!("Long OS Version: {:?}", s.long_os_version());
+ /// ```
+ fn long_os_version(&self) -> Option<String>;
+
+ /// Returns the system hostname based off DNS
+ ///
+ /// **Important**: this information is computed every time this function is called.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// println!("Hostname: {:?}", s.host_name());
+ /// ```
+ fn host_name(&self) -> Option<String>;
+
+ /// Returns the [`User`] matching the given `user_id`.
+ ///
+ /// **Important**: The user list must be filled before using this method, otherwise it will
+ /// always return `None` (through the `refresh_*` methods).
+ ///
+ /// It is a shorthand for:
+ ///
+ /// ```ignore
+ /// let s = System::new_all();
+ /// s.users().find(|user| user.id() == user_id);
+ /// ```
+ ///
+ /// Full example:
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ ///
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// if let Some(user_id) = process.user_id() {
+ /// eprintln!("User for process 1337: {:?}", s.get_user_by_id(user_id));
+ /// }
+ /// }
+ /// ```
+ fn get_user_by_id(&self, user_id: &Uid) -> Option<&User> {
+ self.users().iter().find(|user| user.id() == user_id)
+ }
+}
+
+/// Getting volume of received and transmitted data.
+pub trait NetworkExt: Debug {
+ /// Returns the number of received bytes since the last refresh.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("in: {} B", network.received());
+ /// }
+ /// ```
+ fn received(&self) -> u64;
+
+ /// Returns the total number of received bytes.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("in: {} B", network.total_received());
+ /// }
+ /// ```
+ fn total_received(&self) -> u64;
+
+ /// Returns the number of transmitted bytes since the last refresh.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("out: {} B", network.transmitted());
+ /// }
+ /// ```
+ fn transmitted(&self) -> u64;
+
+ /// Returns the total number of transmitted bytes.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("out: {} B", network.total_transmitted());
+ /// }
+ /// ```
+ fn total_transmitted(&self) -> u64;
+
+ /// Returns the number of incoming packets since the last refresh.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("in: {}", network.packets_received());
+ /// }
+ /// ```
+ fn packets_received(&self) -> u64;
+
+ /// Returns the total number of incoming packets.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("in: {}", network.total_packets_received());
+ /// }
+ /// ```
+ fn total_packets_received(&self) -> u64;
+
+ /// Returns the number of outcoming packets since the last refresh.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("out: {}", network.packets_transmitted());
+ /// }
+ /// ```
+ fn packets_transmitted(&self) -> u64;
+
+ /// Returns the total number of outcoming packets.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("out: {}", network.total_packets_transmitted());
+ /// }
+ /// ```
+ fn total_packets_transmitted(&self) -> u64;
+
+ /// Returns the number of incoming errors since the last refresh.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("in: {}", network.errors_on_received());
+ /// }
+ /// ```
+ fn errors_on_received(&self) -> u64;
+
+ /// Returns the total number of incoming errors.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("in: {}", network.total_errors_on_received());
+ /// }
+ /// ```
+ fn total_errors_on_received(&self) -> u64;
+
+ /// Returns the number of outcoming errors since the last refresh.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("out: {}", network.errors_on_transmitted());
+ /// }
+ /// ```
+ fn errors_on_transmitted(&self) -> u64;
+
+ /// Returns the total number of outcoming errors.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("out: {}", network.total_errors_on_transmitted());
+ /// }
+ /// ```
+ fn total_errors_on_transmitted(&self) -> u64;
+}
+
+/// Interacting with network interfaces.
+pub trait NetworksExt: Debug {
+ /// Returns an iterator over the network interfaces.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// let networks = s.networks();
+ /// for (interface_name, network) in networks {
+ /// println!("in: {} B", network.received());
+ /// }
+ /// ```
+ fn iter(&self) -> NetworksIter;
+
+ /// Refreshes the network interfaces list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworksExt, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// let networks = s.networks_mut();
+ /// networks.refresh_networks_list();
+ /// ```
+ fn refresh_networks_list(&mut self);
+
+ /// Refreshes the network interfaces' content.
+ ///
+ /// ```no_run
+ /// use sysinfo::{NetworksExt, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// let networks = s.networks_mut();
+ /// networks.refresh();
+ /// ```
+ fn refresh(&mut self);
+}
+
+/// Getting a component temperature information.
+pub trait ComponentExt: Debug {
+ /// Returns the temperature of the component (in celsius degree).
+ ///
+ /// ```no_run
+ /// use sysinfo::{ComponentExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// for component in s.components() {
+ /// println!("{}°C", component.temperature());
+ /// }
+ /// ```
+ fn temperature(&self) -> f32;
+
+ /// Returns the maximum temperature of the component (in celsius degree).
+ ///
+ /// ```no_run
+ /// use sysinfo::{ComponentExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// for component in s.components() {
+ /// println!("{}°C", component.max());
+ /// }
+ /// ```
+ fn max(&self) -> f32;
+
+ /// Returns the highest temperature before the component halts (in celsius degree).
+ ///
+ /// ```no_run
+ /// use sysinfo::{ComponentExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// for component in s.components() {
+ /// println!("{:?}°C", component.critical());
+ /// }
+ /// ```
+ fn critical(&self) -> Option<f32>;
+
+ /// Returns the label of the component.
+ ///
+ /// ```no_run
+ /// use sysinfo::{ComponentExt, System, SystemExt};
+ ///
+ /// let s = System::new_all();
+ /// for component in s.components() {
+ /// println!("{}", component.label());
+ /// }
+ /// ```
+ fn label(&self) -> &str;
+
+ /// Refreshes component.
+ ///
+ /// ```no_run
+ /// use sysinfo::{ComponentExt, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ /// for component in s.components_mut() {
+ /// component.refresh();
+ /// }
+ /// ```
+ fn refresh(&mut self);
+}
+
+/// Getting information for a user.
+///
+/// It is returned from [`SystemExt::users`].
+///
+/// ```no_run
+/// use sysinfo::{System, SystemExt, UserExt};
+///
+/// let mut s = System::new_all();
+/// for user in s.users() {
+/// println!("{} is in {} groups", user.name(), user.groups().len());
+/// }
+/// ```
+pub trait UserExt: Debug {
+ /// Return the user id of the user.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt, UserExt};
+ ///
+ /// let mut s = System::new_all();
+ /// for user in s.users() {
+ /// println!("{:?}", *user.id());
+ /// }
+ /// ```
+ fn id(&self) -> &Uid;
+
+ /// Return the group id of the user.
+ ///
+ /// *NOTE* - On Windows, this value defaults to 0. Windows doesn't have a `username` specific group assigned to the user.
+ /// They do however have unique [Security Identifiers](https://docs.microsoft.com/en-us/windows/win32/secauthz/security-identifiers)
+ /// made up of various [Components](https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-components).
+ /// Pieces of the SID may be a candidate for this field, but it doesn't map well to a single group id.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt, UserExt};
+ ///
+ /// let mut s = System::new_all();
+ /// for user in s.users() {
+ /// println!("{}", *user.group_id());
+ /// }
+ /// ```
+ fn group_id(&self) -> Gid;
+
+ /// Returns the name of the user.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt, UserExt};
+ ///
+ /// let mut s = System::new_all();
+ /// for user in s.users() {
+ /// println!("{}", user.name());
+ /// }
+ /// ```
+ fn name(&self) -> &str;
+
+ /// Returns the groups of the user.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt, UserExt};
+ ///
+ /// let mut s = System::new_all();
+ /// for user in s.users() {
+ /// println!("{} is in {:?}", user.name(), user.groups());
+ /// }
+ /// ```
+ fn groups(&self) -> &[String];
+}
diff --git a/vendor/sysinfo/src/unknown/component.rs b/vendor/sysinfo/src/unknown/component.rs
new file mode 100644
index 000000000..ec4a8e87a
--- /dev/null
+++ b/vendor/sysinfo/src/unknown/component.rs
@@ -0,0 +1,26 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::ComponentExt;
+
+#[doc = include_str!("../../md_doc/component.md")]
+pub struct Component {}
+
+impl ComponentExt for Component {
+ fn temperature(&self) -> f32 {
+ 0.0
+ }
+
+ fn max(&self) -> f32 {
+ 0.0
+ }
+
+ fn critical(&self) -> Option<f32> {
+ None
+ }
+
+ fn label(&self) -> &str {
+ ""
+ }
+
+ fn refresh(&mut self) {}
+}
diff --git a/vendor/sysinfo/src/unknown/cpu.rs b/vendor/sysinfo/src/unknown/cpu.rs
new file mode 100644
index 000000000..72b9be447
--- /dev/null
+++ b/vendor/sysinfo/src/unknown/cpu.rs
@@ -0,0 +1,34 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::CpuExt;
+
+#[doc = include_str!("../../md_doc/cpu.md")]
+pub struct Cpu {}
+
+impl Cpu {
+ pub(crate) fn new() -> Cpu {
+ Cpu {}
+ }
+}
+
+impl CpuExt for Cpu {
+ fn cpu_usage(&self) -> f32 {
+ 0.0
+ }
+
+ fn name(&self) -> &str {
+ ""
+ }
+
+ fn frequency(&self) -> u64 {
+ 0
+ }
+
+ fn vendor_id(&self) -> &str {
+ ""
+ }
+
+ fn brand(&self) -> &str {
+ ""
+ }
+}
diff --git a/vendor/sysinfo/src/unknown/disk.rs b/vendor/sysinfo/src/unknown/disk.rs
new file mode 100644
index 000000000..8956da9f5
--- /dev/null
+++ b/vendor/sysinfo/src/unknown/disk.rs
@@ -0,0 +1,42 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{DiskExt, DiskType};
+
+use std::{ffi::OsStr, path::Path};
+
+#[doc = include_str!("../../md_doc/disk.md")]
+pub struct Disk {}
+
+impl DiskExt for Disk {
+ fn type_(&self) -> DiskType {
+ unreachable!()
+ }
+
+ fn name(&self) -> &OsStr {
+ unreachable!()
+ }
+
+ fn file_system(&self) -> &[u8] {
+ &[]
+ }
+
+ fn mount_point(&self) -> &Path {
+ Path::new("")
+ }
+
+ fn total_space(&self) -> u64 {
+ 0
+ }
+
+ fn available_space(&self) -> u64 {
+ 0
+ }
+
+ fn is_removable(&self) -> bool {
+ false
+ }
+
+ fn refresh(&mut self) -> bool {
+ true
+ }
+}
diff --git a/vendor/sysinfo/src/unknown/mod.rs b/vendor/sysinfo/src/unknown/mod.rs
new file mode 100644
index 000000000..e62712607
--- /dev/null
+++ b/vendor/sysinfo/src/unknown/mod.rs
@@ -0,0 +1,15 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+pub mod component;
+pub mod cpu;
+pub mod disk;
+pub mod network;
+pub mod process;
+pub mod system;
+
+pub use self::component::Component;
+pub use self::cpu::Cpu;
+pub use self::disk::Disk;
+pub use self::network::{NetworkData, Networks};
+pub use self::process::Process;
+pub use self::system::System;
diff --git a/vendor/sysinfo/src/unknown/network.rs b/vendor/sysinfo/src/unknown/network.rs
new file mode 100644
index 000000000..4a8ff4831
--- /dev/null
+++ b/vendor/sysinfo/src/unknown/network.rs
@@ -0,0 +1,81 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use std::collections::HashMap;
+
+use crate::{NetworkExt, NetworksExt, NetworksIter};
+
+#[doc = include_str!("../../md_doc/networks.md")]
+pub struct Networks {
+ interfaces: HashMap<String, NetworkData>,
+}
+
+impl Networks {
+ pub(crate) fn new() -> Networks {
+ Networks {
+ interfaces: HashMap::new(),
+ }
+ }
+}
+
+impl NetworksExt for Networks {
+ fn iter(&self) -> NetworksIter {
+ NetworksIter::new(self.interfaces.iter())
+ }
+
+ fn refresh_networks_list(&mut self) {}
+
+ fn refresh(&mut self) {}
+}
+
+#[doc = include_str!("../../md_doc/network_data.md")]
+pub struct NetworkData;
+
+impl NetworkExt for NetworkData {
+ fn received(&self) -> u64 {
+ 0
+ }
+
+ fn total_received(&self) -> u64 {
+ 0
+ }
+
+ fn transmitted(&self) -> u64 {
+ 0
+ }
+
+ fn total_transmitted(&self) -> u64 {
+ 0
+ }
+
+ fn packets_received(&self) -> u64 {
+ 0
+ }
+
+ fn total_packets_received(&self) -> u64 {
+ 0
+ }
+
+ fn packets_transmitted(&self) -> u64 {
+ 0
+ }
+
+ fn total_packets_transmitted(&self) -> u64 {
+ 0
+ }
+
+ fn errors_on_received(&self) -> u64 {
+ 0
+ }
+
+ fn total_errors_on_received(&self) -> u64 {
+ 0
+ }
+
+ fn errors_on_transmitted(&self) -> u64 {
+ 0
+ }
+
+ fn total_errors_on_transmitted(&self) -> u64 {
+ 0
+ }
+}
diff --git a/vendor/sysinfo/src/unknown/process.rs b/vendor/sysinfo/src/unknown/process.rs
new file mode 100644
index 000000000..c4656d3f5
--- /dev/null
+++ b/vendor/sysinfo/src/unknown/process.rs
@@ -0,0 +1,92 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessStatus, Signal, Uid};
+
+use std::fmt;
+use std::path::Path;
+
+impl fmt::Display for ProcessStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str("Unknown")
+ }
+}
+
+#[doc = include_str!("../../md_doc/process.md")]
+pub struct Process {
+ pid: Pid,
+ parent: Option<Pid>,
+}
+
+impl ProcessExt for Process {
+ fn kill_with(&self, _signal: Signal) -> Option<bool> {
+ None
+ }
+
+ fn name(&self) -> &str {
+ ""
+ }
+
+ fn cmd(&self) -> &[String] {
+ &[]
+ }
+
+ fn exe(&self) -> &Path {
+ Path::new("")
+ }
+
+ fn pid(&self) -> Pid {
+ self.pid
+ }
+
+ fn environ(&self) -> &[String] {
+ &[]
+ }
+
+ fn cwd(&self) -> &Path {
+ Path::new("")
+ }
+
+ fn root(&self) -> &Path {
+ Path::new("")
+ }
+
+ fn memory(&self) -> u64 {
+ 0
+ }
+
+ fn virtual_memory(&self) -> u64 {
+ 0
+ }
+
+ fn parent(&self) -> Option<Pid> {
+ self.parent
+ }
+
+ fn status(&self) -> ProcessStatus {
+ ProcessStatus::Unknown(0)
+ }
+
+ fn start_time(&self) -> u64 {
+ 0
+ }
+
+ fn run_time(&self) -> u64 {
+ 0
+ }
+
+ fn cpu_usage(&self) -> f32 {
+ 0.0
+ }
+
+ fn disk_usage(&self) -> DiskUsage {
+ DiskUsage::default()
+ }
+
+ fn user_id(&self) -> Option<&Uid> {
+ None
+ }
+
+ fn group_id(&self) -> Option<Gid> {
+ None
+ }
+}
diff --git a/vendor/sysinfo/src/unknown/system.rs b/vendor/sysinfo/src/unknown/system.rs
new file mode 100644
index 000000000..4603ae737
--- /dev/null
+++ b/vendor/sysinfo/src/unknown/system.rs
@@ -0,0 +1,171 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{
+ sys::{component::Component, Cpu, Disk, Networks, Process},
+ CpuRefreshKind, LoadAvg, Pid, ProcessRefreshKind, RefreshKind, SystemExt, User,
+};
+
+use std::collections::HashMap;
+
+declare_signals! {
+ (),
+ _ => None,
+}
+
+#[doc = include_str!("../../md_doc/system.md")]
+pub struct System {
+ processes_list: HashMap<Pid, Process>,
+ networks: Networks,
+ global_cpu: Cpu,
+}
+
+impl SystemExt for System {
+ const IS_SUPPORTED: bool = false;
+ const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals();
+
+ fn new_with_specifics(_: RefreshKind) -> System {
+ System {
+ processes_list: Default::default(),
+ networks: Networks::new(),
+ global_cpu: Cpu::new(),
+ }
+ }
+
+ fn refresh_memory(&mut self) {}
+
+ fn refresh_cpu_specifics(&mut self, _refresh_kind: CpuRefreshKind) {}
+
+ fn refresh_components_list(&mut self) {}
+
+ fn refresh_processes_specifics(&mut self, _refresh_kind: ProcessRefreshKind) {}
+
+ fn refresh_process_specifics(&mut self, _pid: Pid, _refresh_kind: ProcessRefreshKind) -> bool {
+ false
+ }
+
+ fn refresh_disks_list(&mut self) {}
+
+ fn refresh_users_list(&mut self) {}
+
+ // COMMON PART
+ //
+ // Need to be moved into a "common" file to avoid duplication.
+
+ fn processes(&self) -> &HashMap<Pid, Process> {
+ &self.processes_list
+ }
+
+ fn process(&self, _pid: Pid) -> Option<&Process> {
+ None
+ }
+
+ fn networks(&self) -> &Networks {
+ &self.networks
+ }
+
+ fn networks_mut(&mut self) -> &mut Networks {
+ &mut self.networks
+ }
+
+ fn global_cpu_info(&self) -> &Cpu {
+ &self.global_cpu
+ }
+
+ fn cpus(&self) -> &[Cpu] {
+ &[]
+ }
+
+ fn physical_core_count(&self) -> Option<usize> {
+ None
+ }
+
+ fn total_memory(&self) -> u64 {
+ 0
+ }
+
+ fn free_memory(&self) -> u64 {
+ 0
+ }
+
+ fn available_memory(&self) -> u64 {
+ 0
+ }
+
+ fn used_memory(&self) -> u64 {
+ 0
+ }
+
+ fn total_swap(&self) -> u64 {
+ 0
+ }
+
+ fn free_swap(&self) -> u64 {
+ 0
+ }
+
+ fn used_swap(&self) -> u64 {
+ 0
+ }
+
+ fn components(&self) -> &[Component] {
+ &[]
+ }
+
+ fn components_mut(&mut self) -> &mut [Component] {
+ &mut []
+ }
+
+ fn disks(&self) -> &[Disk] {
+ &[]
+ }
+
+ fn disks_mut(&mut self) -> &mut [Disk] {
+ &mut []
+ }
+
+ fn uptime(&self) -> u64 {
+ 0
+ }
+
+ fn boot_time(&self) -> u64 {
+ 0
+ }
+
+ fn load_average(&self) -> LoadAvg {
+ LoadAvg {
+ one: 0.,
+ five: 0.,
+ fifteen: 0.,
+ }
+ }
+
+ fn users(&self) -> &[User] {
+ &[]
+ }
+
+ fn name(&self) -> Option<String> {
+ None
+ }
+
+ fn long_os_version(&self) -> Option<String> {
+ None
+ }
+
+ fn kernel_version(&self) -> Option<String> {
+ None
+ }
+
+ fn os_version(&self) -> Option<String> {
+ None
+ }
+
+ fn host_name(&self) -> Option<String> {
+ None
+ }
+}
+
+impl Default for System {
+ fn default() -> System {
+ System::new()
+ }
+}
diff --git a/vendor/sysinfo/src/users.rs b/vendor/sysinfo/src/users.rs
new file mode 100644
index 000000000..1891ec7cc
--- /dev/null
+++ b/vendor/sysinfo/src/users.rs
@@ -0,0 +1,97 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{
+ common::{Gid, Uid},
+ User,
+};
+
+use libc::{getgrgid, getgrouplist};
+use std::fs::File;
+use std::io::Read;
+
+pub fn get_users_list() -> Vec<User> {
+ let mut s = String::new();
+ let mut ngroups = 100;
+ let mut groups = vec![0; ngroups as usize];
+
+ let _ = File::open("/etc/passwd").and_then(|mut f| f.read_to_string(&mut s));
+ s.lines()
+ .filter_map(|line| {
+ let mut parts = line.split(':');
+ if let Some(username) = parts.next() {
+ let mut parts = parts.skip(1);
+ // 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(),
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+ None
+ })
+ .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
new file mode 100644
index 000000000..578ab61c6
--- /dev/null
+++ b/vendor/sysinfo/src/utils.rs
@@ -0,0 +1,61 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+/* convert a path to a NUL-terminated Vec<u8> suitable for use with C functions */
+#[cfg(all(
+ not(feature = "unknown-ci"),
+ any(target_os = "linux", target_os = "android", target_vendor = "apple")
+))]
+pub(crate) fn to_cpath(path: &std::path::Path) -> Vec<u8> {
+ use std::{ffi::OsStr, os::unix::ffi::OsStrExt};
+
+ let path_os: &OsStr = path.as_ref();
+ let mut cpath = path_os.as_bytes().to_vec();
+ cpath.push(0);
+ cpath
+}
+
+/// Converts the value into a parallel iterator (if the multithread feature is enabled)
+/// Uses the rayon::iter::IntoParallelIterator trait
+#[cfg(all(
+ all(
+ any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "macos",
+ target_os = "windows",
+ target_os = "freebsd",
+ ),
+ feature = "multithread"
+ ),
+ not(feature = "apple-sandbox"),
+ not(feature = "unknown-ci")
+))]
+pub(crate) fn into_iter<T>(val: T) -> T::Iter
+where
+ T: rayon::iter::IntoParallelIterator,
+{
+ val.into_par_iter()
+}
+
+/// Converts the value into a sequential iterator (if the multithread feature is disabled)
+/// Uses the std::iter::IntoIterator trait
+#[cfg(all(
+ all(
+ any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "macos",
+ target_os = "windows",
+ target_os = "freebsd",
+ ),
+ not(feature = "multithread")
+ ),
+ not(feature = "unknown-ci"),
+ not(feature = "apple-sandbox")
+))]
+pub(crate) fn into_iter<T>(val: T) -> T::IntoIter
+where
+ T: IntoIterator,
+{
+ val.into_iter()
+}
diff --git a/vendor/sysinfo/src/windows/component.rs b/vendor/sysinfo/src/windows/component.rs
new file mode 100644
index 000000000..502594e38
--- /dev/null
+++ b/vendor/sysinfo/src/windows/component.rs
@@ -0,0 +1,374 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::ComponentExt;
+
+use std::ptr::null_mut;
+
+use winapi::shared::rpcdce::{
+ RPC_C_AUTHN_LEVEL_CALL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+};
+use winapi::shared::winerror::{FAILED, SUCCEEDED, S_FALSE, S_OK};
+use winapi::shared::wtypesbase::CLSCTX_INPROC_SERVER;
+use winapi::um::combaseapi::{
+ CoCreateInstance, CoInitializeEx, CoInitializeSecurity, CoSetProxyBlanket, CoUninitialize,
+};
+use winapi::um::oaidl::VARIANT;
+use winapi::um::objidl::EOAC_NONE;
+use winapi::um::oleauto::{SysAllocString, SysFreeString, VariantClear};
+use winapi::um::wbemcli::{
+ CLSID_WbemLocator, IEnumWbemClassObject, IID_IWbemLocator, IWbemClassObject, IWbemLocator,
+ IWbemServices, WBEM_FLAG_FORWARD_ONLY, WBEM_FLAG_NONSYSTEM_ONLY, WBEM_FLAG_RETURN_IMMEDIATELY,
+};
+
+#[doc = include_str!("../../md_doc/component.md")]
+pub struct Component {
+ temperature: f32,
+ max: f32,
+ critical: Option<f32>,
+ label: String,
+ connection: Option<Connection>,
+}
+
+impl Component {
+ /// Creates a new `Component` with the given information.
+ fn new() -> Option<Component> {
+ let mut c = Connection::new()
+ .and_then(|x| x.initialize_security())
+ .and_then(|x| x.create_instance())
+ .and_then(|x| x.connect_server())
+ .and_then(|x| x.set_proxy_blanket())
+ .and_then(|x| x.exec_query())?;
+
+ c.temperature(true)
+ .map(|(temperature, critical)| Component {
+ temperature,
+ label: "Computer".to_owned(),
+ max: temperature,
+ critical,
+ connection: Some(c),
+ })
+ }
+}
+
+impl ComponentExt for Component {
+ fn temperature(&self) -> f32 {
+ self.temperature
+ }
+
+ fn max(&self) -> f32 {
+ self.max
+ }
+
+ fn critical(&self) -> Option<f32> {
+ self.critical
+ }
+
+ fn label(&self) -> &str {
+ &self.label
+ }
+
+ fn refresh(&mut self) {
+ if self.connection.is_none() {
+ self.connection = Connection::new()
+ .and_then(|x| x.initialize_security())
+ .and_then(|x| x.create_instance())
+ .and_then(|x| x.connect_server())
+ .and_then(|x| x.set_proxy_blanket());
+ }
+ self.connection = if let Some(x) = self.connection.take() {
+ x.exec_query()
+ } else {
+ None
+ };
+ if let Some(ref mut connection) = self.connection {
+ if let Some((temperature, _)) = connection.temperature(false) {
+ self.temperature = temperature;
+ if self.temperature > self.max {
+ self.max = self.temperature;
+ }
+ }
+ }
+ }
+}
+
+pub(crate) fn get_components() -> Vec<Component> {
+ match Component::new() {
+ Some(c) => vec![c],
+ None => Vec::new(),
+ }
+}
+
+struct Instance(*mut IWbemLocator);
+
+impl Drop for Instance {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe {
+ (*self.0).Release();
+ }
+ }
+ }
+}
+
+struct ServerConnection(*mut IWbemServices);
+
+impl Drop for ServerConnection {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe {
+ (*self.0).Release();
+ }
+ }
+ }
+}
+
+struct Enumerator(*mut IEnumWbemClassObject);
+
+impl Drop for Enumerator {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe {
+ (*self.0).Release();
+ }
+ }
+ }
+}
+
+macro_rules! bstr {
+ ($($x:expr),*) => {{
+ let x: &[u16] = &[$($x as u16),*, 0];
+ SysAllocString(x.as_ptr())
+ }}
+}
+
+struct Connection {
+ instance: Option<Instance>,
+ server_connection: Option<ServerConnection>,
+ enumerator: Option<Enumerator>,
+ initialized: bool,
+}
+
+#[allow(clippy::non_send_fields_in_send_ty)]
+unsafe impl Send for Connection {}
+unsafe impl Sync for Connection {}
+
+impl Connection {
+ #[allow(clippy::unnecessary_wraps)]
+ fn new() -> Option<Connection> {
+ unsafe {
+ let val = CoInitializeEx(null_mut(), 0);
+ Some(Connection {
+ instance: None,
+ server_connection: None,
+ enumerator: None,
+ initialized: val == S_OK || val == S_FALSE,
+ })
+ }
+ }
+
+ fn initialize_security(self) -> Option<Connection> {
+ unsafe {
+ if FAILED(CoInitializeSecurity(
+ null_mut(),
+ -1,
+ null_mut(),
+ null_mut(),
+ RPC_C_AUTHN_LEVEL_DEFAULT,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ null_mut(),
+ EOAC_NONE,
+ null_mut(),
+ )) {
+ None
+ } else {
+ Some(self)
+ }
+ }
+ }
+
+ fn create_instance(mut self) -> Option<Connection> {
+ let mut p_loc = null_mut();
+
+ unsafe {
+ if FAILED(CoCreateInstance(
+ &CLSID_WbemLocator as *const _,
+ null_mut(),
+ CLSCTX_INPROC_SERVER,
+ &IID_IWbemLocator as *const _,
+ &mut p_loc as *mut _ as *mut _,
+ )) {
+ None
+ } else {
+ self.instance = Some(Instance(p_loc));
+ Some(self)
+ }
+ }
+ }
+
+ fn connect_server(mut self) -> Option<Connection> {
+ let mut p_svc = null_mut();
+
+ if let Some(ref instance) = self.instance {
+ unsafe {
+ // "root\WMI"
+ let s = bstr!('r', 'o', 'o', 't', '\\', 'W', 'M', 'I');
+ let res = (*instance.0).ConnectServer(
+ s,
+ null_mut(),
+ null_mut(),
+ null_mut(),
+ 0,
+ null_mut(),
+ null_mut(),
+ &mut p_svc as *mut _,
+ );
+ SysFreeString(s);
+ if FAILED(res) {
+ return None;
+ }
+ }
+ } else {
+ return None;
+ }
+ self.server_connection = Some(ServerConnection(p_svc));
+ Some(self)
+ }
+
+ fn set_proxy_blanket(self) -> Option<Connection> {
+ if let Some(ref server_connection) = self.server_connection {
+ unsafe {
+ if FAILED(CoSetProxyBlanket(
+ server_connection.0 as *mut _,
+ RPC_C_AUTHN_WINNT,
+ RPC_C_AUTHZ_NONE,
+ null_mut(),
+ RPC_C_AUTHN_LEVEL_CALL,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ null_mut(),
+ EOAC_NONE,
+ )) {
+ return None;
+ }
+ }
+ } else {
+ return None;
+ }
+ Some(self)
+ }
+
+ fn exec_query(mut self) -> Option<Connection> {
+ let mut p_enumerator = null_mut();
+
+ if let Some(ref server_connection) = self.server_connection {
+ unsafe {
+ // "WQL"
+ let s = bstr!('W', 'Q', 'L'); // query kind
+ // "SELECT * FROM MSAcpi_ThermalZoneTemperature"
+ let query = bstr!(
+ 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'M', 'S',
+ 'A', 'c', 'p', 'i', '_', 'T', 'h', 'e', 'r', 'm', 'a', 'l', 'Z', 'o', 'n', 'e',
+ 'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u', 'r', 'e'
+ );
+ let hres = (*server_connection.0).ExecQuery(
+ s,
+ query,
+ (WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY) as _,
+ null_mut(),
+ &mut p_enumerator as *mut _,
+ );
+ SysFreeString(s);
+ SysFreeString(query);
+ if FAILED(hres) {
+ return None;
+ }
+ }
+ } else {
+ return None;
+ }
+ self.enumerator = Some(Enumerator(p_enumerator));
+ Some(self)
+ }
+
+ fn temperature(&mut self, get_critical: bool) -> Option<(f32, Option<f32>)> {
+ use winapi::um::wbemcli::WBEM_INFINITE;
+
+ let p_enum = match self.enumerator.take() {
+ Some(x) => x,
+ None => {
+ return None;
+ }
+ };
+ let mut p_obj: *mut IWbemClassObject = null_mut();
+ let mut nb_returned = 0;
+
+ unsafe {
+ (*p_enum.0).Next(
+ WBEM_INFINITE as _, // Time out
+ 1, // One object
+ &mut p_obj as *mut _,
+ &mut nb_returned,
+ );
+
+ if nb_returned == 0 {
+ return None; // not enough rights I suppose...
+ }
+
+ (*p_obj).BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY as _);
+
+ let mut p_val = std::mem::MaybeUninit::<VARIANT>::uninit();
+ // "CurrentTemperature"
+ let temp = bstr!(
+ 'C', 'u', 'r', 'r', 'e', 'n', 't', 'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u',
+ 'r', 'e'
+ );
+ let res = (*p_obj).Get(temp, 0, p_val.as_mut_ptr(), null_mut(), null_mut());
+ let mut p_val = p_val.assume_init();
+
+ SysFreeString(temp);
+ VariantClear(&mut p_val as *mut _ as *mut _);
+
+ let temp = if SUCCEEDED(res) {
+ // temperature is given in tenth of degrees Kelvin
+ (p_val.n1.decVal().Lo64 / 10) as f32 - 273.15
+ } else {
+ (*p_obj).Release();
+ return None;
+ };
+
+ let mut critical = None;
+ if get_critical {
+ // "CriticalPoint"
+ let crit = bstr!(
+ 'C', 'r', 'i', 't', 'i', 'c', 'a', 'l', 'T', 'r', 'i', 'p', 'P', 'o', 'i', 'n',
+ 't'
+ );
+ let res = (*p_obj).Get(crit, 0, &mut p_val, null_mut(), null_mut());
+
+ SysFreeString(crit);
+ VariantClear(&mut p_val as *mut _ as *mut _);
+
+ if SUCCEEDED(res) {
+ // temperature is given in tenth of degrees Kelvin
+ critical = Some((p_val.n1.decVal().Lo64 / 10) as f32 - 273.15);
+ }
+ }
+ (*p_obj).Release();
+ Some((temp, critical))
+ }
+ }
+}
+
+impl Drop for Connection {
+ fn drop(&mut self) {
+ // Those three calls are here to enforce that they get dropped in the good order.
+ self.enumerator.take();
+ self.server_connection.take();
+ self.instance.take();
+ if self.initialized {
+ unsafe {
+ CoUninitialize();
+ }
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/windows/cpu.rs b/vendor/sysinfo/src/windows/cpu.rs
new file mode 100644
index 000000000..bbaa27ad7
--- /dev/null
+++ b/vendor/sysinfo/src/windows/cpu.rs
@@ -0,0 +1,548 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::tools::KeyHandler;
+use crate::{CpuExt, CpuRefreshKind, LoadAvg};
+
+use std::collections::HashMap;
+use std::io::Error;
+use std::mem;
+use std::ops::DerefMut;
+use std::ptr::null_mut;
+use std::sync::Mutex;
+
+use ntapi::ntpoapi::PROCESSOR_POWER_INFORMATION;
+
+use winapi::shared::minwindef::FALSE;
+use winapi::shared::winerror::{ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS};
+use winapi::um::handleapi::CloseHandle;
+use winapi::um::pdh::{
+ PdhAddEnglishCounterA, PdhAddEnglishCounterW, PdhCloseQuery, PdhCollectQueryData,
+ PdhCollectQueryDataEx, PdhGetFormattedCounterValue, PdhOpenQueryA, PdhRemoveCounter,
+ PDH_FMT_COUNTERVALUE, PDH_FMT_DOUBLE, PDH_HCOUNTER, PDH_HQUERY,
+};
+use winapi::um::powerbase::CallNtPowerInformation;
+use winapi::um::synchapi::CreateEventA;
+use winapi::um::sysinfoapi::GetLogicalProcessorInformationEx;
+use winapi::um::sysinfoapi::SYSTEM_INFO;
+use winapi::um::winbase::{RegisterWaitForSingleObject, INFINITE};
+use winapi::um::winnt::{
+ ProcessorInformation, RelationAll, RelationProcessorCore, BOOLEAN, HANDLE,
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PVOID, WT_EXECUTEDEFAULT,
+};
+
+// 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;
+#[allow(clippy::excessive_precision)]
+const LOADAVG_FACTOR_5F: f64 = 0.9834714538216174894737477501;
+#[allow(clippy::excessive_precision)]
+const LOADAVG_FACTOR_15F: f64 = 0.9944598480048967508795473394;
+// The time interval in seconds between taking load counts, same as Linux
+const SAMPLING_INTERVAL: usize = 5;
+
+// maybe use a read/write lock instead?
+static LOAD_AVG: once_cell::sync::Lazy<Mutex<Option<LoadAvg>>> =
+ once_cell::sync::Lazy::new(|| unsafe { init_load_avg() });
+
+pub(crate) fn get_load_average() -> LoadAvg {
+ if let Ok(avg) = LOAD_AVG.lock() {
+ if let Some(avg) = &*avg {
+ return avg.clone();
+ }
+ }
+ LoadAvg::default()
+}
+
+unsafe extern "system" fn load_avg_callback(counter: PVOID, _: BOOLEAN) {
+ let mut display_value = mem::MaybeUninit::<PDH_FMT_COUNTERVALUE>::uninit();
+
+ if PdhGetFormattedCounterValue(
+ counter as _,
+ PDH_FMT_DOUBLE,
+ null_mut(),
+ display_value.as_mut_ptr(),
+ ) != ERROR_SUCCESS as _
+ {
+ return;
+ }
+ let display_value = display_value.assume_init();
+ if let Ok(mut avg) = LOAD_AVG.lock() {
+ if let Some(avg) = avg.deref_mut() {
+ let current_load = display_value.u.doubleValue();
+
+ avg.one = avg.one * LOADAVG_FACTOR_1F + current_load * (1.0 - LOADAVG_FACTOR_1F);
+ avg.five = avg.five * LOADAVG_FACTOR_5F + current_load * (1.0 - LOADAVG_FACTOR_5F);
+ avg.fifteen =
+ avg.fifteen * LOADAVG_FACTOR_15F + current_load * (1.0 - LOADAVG_FACTOR_15F);
+ }
+ }
+}
+
+unsafe fn init_load_avg() -> Mutex<Option<LoadAvg>> {
+ // You can see the original implementation here: https://github.com/giampaolo/psutil
+ let mut query = null_mut();
+
+ if PdhOpenQueryA(null_mut(), 0, &mut query) != ERROR_SUCCESS as _ {
+ sysinfo_debug!("init_load_avg: PdhOpenQueryA failed");
+ return Mutex::new(None);
+ }
+
+ let mut counter: PDH_HCOUNTER = mem::zeroed();
+ if PdhAddEnglishCounterA(
+ query,
+ b"\\System\\Cpu Queue Length\0".as_ptr() as _,
+ 0,
+ &mut counter,
+ ) != ERROR_SUCCESS as _
+ {
+ PdhCloseQuery(query);
+ sysinfo_debug!("init_load_avg: failed to get CPU queue length");
+ return Mutex::new(None);
+ }
+
+ let event = CreateEventA(null_mut(), FALSE, FALSE, b"LoadUpdateEvent\0".as_ptr() as _);
+ if event.is_null() {
+ PdhCloseQuery(query);
+ sysinfo_debug!("init_load_avg: failed to create event `LoadUpdateEvent`");
+ return Mutex::new(None);
+ }
+
+ if PdhCollectQueryDataEx(query, SAMPLING_INTERVAL as _, event) != ERROR_SUCCESS as _ {
+ PdhCloseQuery(query);
+ sysinfo_debug!("init_load_avg: PdhCollectQueryDataEx failed");
+ return Mutex::new(None);
+ }
+
+ let mut wait_handle = null_mut();
+ if RegisterWaitForSingleObject(
+ &mut wait_handle,
+ event,
+ Some(load_avg_callback),
+ counter as _,
+ INFINITE,
+ WT_EXECUTEDEFAULT,
+ ) == 0
+ {
+ PdhRemoveCounter(counter);
+ PdhCloseQuery(query);
+ sysinfo_debug!("init_load_avg: RegisterWaitForSingleObject failed");
+ Mutex::new(None)
+ } else {
+ Mutex::new(Some(LoadAvg::default()))
+ }
+}
+
+struct InternalQuery {
+ query: PDH_HQUERY,
+ event: HANDLE,
+ data: HashMap<String, PDH_HCOUNTER>,
+}
+
+unsafe impl Send for InternalQuery {}
+unsafe impl Sync for InternalQuery {}
+
+impl Drop for InternalQuery {
+ fn drop(&mut self) {
+ unsafe {
+ for (_, counter) in self.data.iter() {
+ PdhRemoveCounter(*counter);
+ }
+
+ if !self.event.is_null() {
+ CloseHandle(self.event);
+ }
+
+ if !self.query.is_null() {
+ PdhCloseQuery(self.query);
+ }
+ }
+ }
+}
+
+pub(crate) struct Query {
+ internal: InternalQuery,
+}
+
+impl Query {
+ pub fn new() -> Option<Query> {
+ let mut query = null_mut();
+ unsafe {
+ if PdhOpenQueryA(null_mut(), 0, &mut query) == ERROR_SUCCESS as i32 {
+ let q = InternalQuery {
+ query,
+ event: null_mut(),
+ data: HashMap::new(),
+ };
+ Some(Query { internal: q })
+ } else {
+ sysinfo_debug!("Query::new: PdhOpenQueryA failed");
+ None
+ }
+ }
+ }
+
+ #[allow(clippy::ptr_arg)]
+ pub fn get(&self, name: &String) -> Option<f32> {
+ if let Some(counter) = self.internal.data.get(name) {
+ unsafe {
+ let mut display_value = mem::MaybeUninit::<PDH_FMT_COUNTERVALUE>::uninit();
+ let counter: PDH_HCOUNTER = *counter;
+
+ let ret = PdhGetFormattedCounterValue(
+ counter,
+ PDH_FMT_DOUBLE,
+ null_mut(),
+ display_value.as_mut_ptr(),
+ ) as u32;
+ let display_value = display_value.assume_init();
+ return if ret == ERROR_SUCCESS as _ {
+ let data = *display_value.u.doubleValue();
+ Some(data as f32)
+ } else {
+ sysinfo_debug!("Query::get: PdhGetFormattedCounterValue failed");
+ Some(0.)
+ };
+ }
+ }
+ None
+ }
+
+ #[allow(clippy::ptr_arg)]
+ pub fn add_english_counter(&mut self, name: &String, getter: Vec<u16>) -> bool {
+ if self.internal.data.contains_key(name) {
+ sysinfo_debug!("Query::add_english_counter: doesn't have key `{:?}`", name);
+ return false;
+ }
+ unsafe {
+ let mut counter: PDH_HCOUNTER = std::mem::zeroed();
+ let ret = PdhAddEnglishCounterW(self.internal.query, getter.as_ptr(), 0, &mut counter);
+ if ret == ERROR_SUCCESS as _ {
+ self.internal.data.insert(name.clone(), counter);
+ } else {
+ sysinfo_debug!(
+ "Query::add_english_counter: failed to add counter '{}': {:x}...",
+ name,
+ ret,
+ );
+ return false;
+ }
+ }
+ true
+ }
+
+ pub fn refresh(&self) {
+ unsafe {
+ if PdhCollectQueryData(self.internal.query) != ERROR_SUCCESS as _ {
+ sysinfo_debug!("failed to refresh CPU data");
+ }
+ }
+ }
+}
+
+pub(crate) struct CpusWrapper {
+ global: Cpu,
+ cpus: Vec<Cpu>,
+ got_cpu_frequency: bool,
+}
+
+impl CpusWrapper {
+ pub fn new() -> Self {
+ Self {
+ global: Cpu::new_with_values("Total CPU".to_owned(), String::new(), String::new(), 0),
+ cpus: Vec::new(),
+ got_cpu_frequency: false,
+ }
+ }
+
+ pub fn global_cpu(&self) -> &Cpu {
+ &self.global
+ }
+
+ pub fn global_cpu_mut(&mut self) -> &mut Cpu {
+ &mut self.global
+ }
+
+ pub fn cpus(&self) -> &[Cpu] {
+ &self.cpus
+ }
+
+ fn init_if_needed(&mut self, refresh_kind: CpuRefreshKind) {
+ if self.cpus.is_empty() {
+ let (cpus, vendor_id, brand) = super::tools::init_cpus(refresh_kind);
+ self.cpus = cpus;
+ self.global.vendor_id = vendor_id;
+ self.global.brand = brand;
+ self.got_cpu_frequency = refresh_kind.frequency();
+ }
+ }
+
+ pub fn len(&mut self) -> usize {
+ self.init_if_needed(CpuRefreshKind::new());
+ self.cpus.len()
+ }
+
+ pub fn iter_mut(&mut self, refresh_kind: CpuRefreshKind) -> impl Iterator<Item = &mut Cpu> {
+ self.init_if_needed(refresh_kind);
+ self.cpus.iter_mut()
+ }
+
+ pub fn get_frequencies(&mut self) {
+ if self.got_cpu_frequency {
+ return;
+ }
+ let frequencies = get_frequencies(self.cpus.len());
+
+ for (cpu, frequency) in self.cpus.iter_mut().zip(frequencies) {
+ cpu.set_frequency(frequency);
+ }
+ self.got_cpu_frequency = true;
+ }
+}
+
+#[doc = include_str!("../../md_doc/cpu.md")]
+pub struct Cpu {
+ name: String,
+ cpu_usage: f32,
+ key_used: Option<KeyHandler>,
+ vendor_id: String,
+ brand: String,
+ frequency: u64,
+}
+
+impl CpuExt for Cpu {
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ fn frequency(&self) -> u64 {
+ self.frequency
+ }
+
+ fn vendor_id(&self) -> &str {
+ &self.vendor_id
+ }
+
+ fn brand(&self) -> &str {
+ &self.brand
+ }
+}
+
+impl Cpu {
+ pub(crate) fn new_with_values(
+ name: String,
+ vendor_id: String,
+ brand: String,
+ frequency: u64,
+ ) -> Cpu {
+ Cpu {
+ name,
+ cpu_usage: 0f32,
+ key_used: None,
+ vendor_id,
+ brand,
+ frequency,
+ }
+ }
+
+ pub(crate) fn set_cpu_usage(&mut self, value: f32) {
+ self.cpu_usage = value;
+ }
+
+ pub(crate) fn set_frequency(&mut self, value: u64) {
+ self.frequency = value;
+ }
+}
+
+fn get_vendor_id_not_great(info: &SYSTEM_INFO) -> String {
+ use winapi::um::winnt;
+ // https://docs.microsoft.com/fr-fr/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
+ unsafe {
+ match info.u.s().wProcessorArchitecture {
+ winnt::PROCESSOR_ARCHITECTURE_INTEL => "Intel x86",
+ winnt::PROCESSOR_ARCHITECTURE_MIPS => "MIPS",
+ winnt::PROCESSOR_ARCHITECTURE_ALPHA => "RISC Alpha",
+ winnt::PROCESSOR_ARCHITECTURE_PPC => "PPC",
+ winnt::PROCESSOR_ARCHITECTURE_SHX => "SHX",
+ winnt::PROCESSOR_ARCHITECTURE_ARM => "ARM",
+ winnt::PROCESSOR_ARCHITECTURE_IA64 => "Intel Itanium-based x64",
+ winnt::PROCESSOR_ARCHITECTURE_ALPHA64 => "RISC Alpha x64",
+ winnt::PROCESSOR_ARCHITECTURE_MSIL => "MSIL",
+ winnt::PROCESSOR_ARCHITECTURE_AMD64 => "(Intel or AMD) x64",
+ winnt::PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 => "Intel Itanium-based x86",
+ winnt::PROCESSOR_ARCHITECTURE_NEUTRAL => "unknown",
+ winnt::PROCESSOR_ARCHITECTURE_ARM64 => "ARM x64",
+ winnt::PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 => "ARM",
+ winnt::PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 => "Intel Itanium-based x86",
+ _ => "unknown",
+ }
+ .to_owned()
+ }
+}
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub(crate) fn get_vendor_id_and_brand(info: &SYSTEM_INFO) -> (String, String) {
+ #[cfg(target_arch = "x86")]
+ use std::arch::x86::__cpuid;
+ #[cfg(target_arch = "x86_64")]
+ use std::arch::x86_64::__cpuid;
+
+ unsafe fn add_u32(v: &mut Vec<u8>, i: u32) {
+ let i = &i as *const u32 as *const u8;
+ v.push(*i);
+ v.push(*i.offset(1));
+ v.push(*i.offset(2));
+ v.push(*i.offset(3));
+ }
+
+ unsafe {
+ // First, we try to get the complete name.
+ let res = __cpuid(0x80000000);
+ let n_ex_ids = res.eax;
+ let brand = if n_ex_ids >= 0x80000004 {
+ let mut extdata = Vec::with_capacity(5);
+
+ for i in 0x80000000..=n_ex_ids {
+ extdata.push(__cpuid(i));
+ }
+
+ // 4 * u32 * nb_entries
+ let mut out = Vec::with_capacity(4 * std::mem::size_of::<u32>() * 3);
+ for data in extdata.iter().take(5).skip(2) {
+ add_u32(&mut out, data.eax);
+ add_u32(&mut out, data.ebx);
+ add_u32(&mut out, data.ecx);
+ add_u32(&mut out, data.edx);
+ }
+ let mut pos = 0;
+ for e in out.iter() {
+ if *e == 0 {
+ break;
+ }
+ pos += 1;
+ }
+ match std::str::from_utf8(&out[..pos]) {
+ Ok(s) => s.to_owned(),
+ _ => String::new(),
+ }
+ } else {
+ String::new()
+ };
+
+ // Failed to get full name, let's retry for the short version!
+ let res = __cpuid(0);
+ let mut x = Vec::with_capacity(3 * std::mem::size_of::<u32>());
+ add_u32(&mut x, res.ebx);
+ add_u32(&mut x, res.edx);
+ add_u32(&mut x, res.ecx);
+ let mut pos = 0;
+ for e in x.iter() {
+ if *e == 0 {
+ break;
+ }
+ pos += 1;
+ }
+ let vendor_id = match std::str::from_utf8(&x[..pos]) {
+ Ok(s) => s.to_owned(),
+ Err(_) => get_vendor_id_not_great(info),
+ };
+ (vendor_id, brand)
+ }
+}
+
+#[cfg(all(not(target_arch = "x86_64"), not(target_arch = "x86")))]
+pub(crate) fn get_vendor_id_and_brand(info: &SYSTEM_INFO) -> (String, String) {
+ (get_vendor_id_not_great(info), String::new())
+}
+
+pub(crate) fn get_key_used(p: &mut Cpu) -> &mut Option<KeyHandler> {
+ &mut p.key_used
+}
+
+// From https://stackoverflow.com/a/43813138:
+//
+// If your PC has 64 or fewer logical cpus installed, the above code will work fine. However,
+// if your PC has more than 64 logical cpus installed, use GetActiveCpuCount() or
+// GetLogicalCpuInformation() to determine the total number of logical cpus installed.
+pub(crate) fn get_frequencies(nb_cpus: usize) -> Vec<u64> {
+ let size = nb_cpus * mem::size_of::<PROCESSOR_POWER_INFORMATION>();
+ let mut infos: Vec<PROCESSOR_POWER_INFORMATION> = Vec::with_capacity(nb_cpus);
+
+ unsafe {
+ if CallNtPowerInformation(
+ ProcessorInformation,
+ null_mut(),
+ 0,
+ infos.as_mut_ptr() as _,
+ size as _,
+ ) == 0
+ {
+ infos.set_len(nb_cpus);
+ // infos.Number
+ return infos
+ .into_iter()
+ .map(|i| i.CurrentMhz as u64)
+ .collect::<Vec<_>>();
+ }
+ }
+ sysinfo_debug!("get_frequencies: CallNtPowerInformation failed");
+ vec![0; nb_cpus]
+}
+
+pub(crate) fn get_physical_core_count() -> Option<usize> {
+ // we cannot use the number of cpus here to pre calculate the buf size
+ // GetLogicalCpuInformationEx with RelationProcessorCore passed to it not only returns
+ // the logical cores but also numa nodes
+ //
+ // GetLogicalCpuInformationEx: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
+
+ let mut needed_size = 0;
+ unsafe {
+ GetLogicalProcessorInformationEx(RelationAll, null_mut(), &mut needed_size);
+
+ let mut buf: Vec<u8> = Vec::with_capacity(needed_size as _);
+
+ loop {
+ if GetLogicalProcessorInformationEx(
+ RelationAll,
+ buf.as_mut_ptr() as *mut _,
+ &mut needed_size,
+ ) == FALSE
+ {
+ let e = Error::last_os_error();
+ // For some reasons, the function might return a size not big enough...
+ match e.raw_os_error() {
+ Some(value) if value == ERROR_INSUFFICIENT_BUFFER as _ => {}
+ _ => {
+ sysinfo_debug!(
+ "get_physical_core_count: GetLogicalCpuInformationEx failed"
+ );
+ return None;
+ }
+ }
+ } else {
+ break;
+ }
+ buf.reserve(needed_size as usize - buf.capacity());
+ }
+
+ buf.set_len(needed_size as _);
+
+ let mut i = 0;
+ let raw_buf = buf.as_ptr();
+ let mut count = 0;
+ while i < buf.len() {
+ let p = &*(raw_buf.add(i) as PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX);
+ i += p.Size as usize;
+ if p.Relationship == RelationProcessorCore {
+ // Only count the physical cores.
+ count += 1;
+ }
+ }
+ Some(count)
+ }
+}
diff --git a/vendor/sysinfo/src/windows/disk.rs b/vendor/sysinfo/src/windows/disk.rs
new file mode 100644
index 000000000..ae393afb2
--- /dev/null
+++ b/vendor/sysinfo/src/windows/disk.rs
@@ -0,0 +1,249 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{DiskExt, DiskType};
+
+use std::ffi::{OsStr, OsString};
+use std::mem::size_of;
+use std::path::Path;
+
+use winapi::ctypes::c_void;
+use winapi::shared::minwindef::{DWORD, MAX_PATH};
+use winapi::um::fileapi::{
+ CreateFileW, GetDiskFreeSpaceExW, GetDriveTypeW, GetLogicalDrives, GetVolumeInformationW,
+ OPEN_EXISTING,
+};
+use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
+use winapi::um::ioapiset::DeviceIoControl;
+use winapi::um::winbase::{DRIVE_FIXED, DRIVE_REMOVABLE};
+use winapi::um::winioctl::{
+ DEVICE_TRIM_DESCRIPTOR, IOCTL_STORAGE_QUERY_PROPERTY, STORAGE_PROPERTY_QUERY,
+};
+use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, HANDLE, ULARGE_INTEGER};
+
+#[doc = include_str!("../../md_doc/disk.md")]
+pub struct Disk {
+ type_: DiskType,
+ name: OsString,
+ file_system: Vec<u8>,
+ mount_point: Vec<u16>,
+ s_mount_point: String,
+ total_space: u64,
+ available_space: u64,
+ is_removable: bool,
+}
+
+impl DiskExt for Disk {
+ fn type_(&self) -> DiskType {
+ self.type_
+ }
+
+ fn name(&self) -> &OsStr {
+ &self.name
+ }
+
+ fn file_system(&self) -> &[u8] {
+ &self.file_system
+ }
+
+ fn mount_point(&self) -> &Path {
+ Path::new(&self.s_mount_point)
+ }
+
+ fn total_space(&self) -> u64 {
+ self.total_space
+ }
+
+ fn available_space(&self) -> u64 {
+ self.available_space
+ }
+
+ fn is_removable(&self) -> bool {
+ self.is_removable
+ }
+
+ fn refresh(&mut self) -> bool {
+ if self.total_space != 0 {
+ unsafe {
+ let mut tmp: ULARGE_INTEGER = std::mem::zeroed();
+ if GetDiskFreeSpaceExW(
+ self.mount_point.as_ptr(),
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ &mut tmp,
+ ) != 0
+ {
+ self.available_space = *tmp.QuadPart();
+ return true;
+ }
+ }
+ }
+ false
+ }
+}
+
+struct HandleWrapper(HANDLE);
+
+impl HandleWrapper {
+ unsafe fn new(drive_name: &[u16], open_rights: DWORD) -> Option<Self> {
+ let handle = CreateFileW(
+ drive_name.as_ptr(),
+ open_rights,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ std::ptr::null_mut(),
+ OPEN_EXISTING,
+ 0,
+ std::ptr::null_mut(),
+ );
+ if handle == INVALID_HANDLE_VALUE {
+ CloseHandle(handle);
+ None
+ } else {
+ Some(Self(handle))
+ }
+ }
+}
+
+impl Drop for HandleWrapper {
+ fn drop(&mut self) {
+ unsafe {
+ CloseHandle(self.0);
+ }
+ }
+}
+
+unsafe fn get_drive_size(mount_point: &[u16]) -> Option<(u64, u64)> {
+ let mut total_size: ULARGE_INTEGER = std::mem::zeroed();
+ let mut available_space: ULARGE_INTEGER = std::mem::zeroed();
+ if GetDiskFreeSpaceExW(
+ mount_point.as_ptr(),
+ std::ptr::null_mut(),
+ &mut total_size,
+ &mut available_space,
+ ) != 0
+ {
+ Some((
+ *total_size.QuadPart() as u64,
+ *available_space.QuadPart() as u64,
+ ))
+ } else {
+ None
+ }
+}
+
+pub(crate) unsafe fn get_disks() -> Vec<Disk> {
+ let drives = GetLogicalDrives();
+ if drives == 0 {
+ return Vec::new();
+ }
+
+ #[cfg(feature = "multithread")]
+ use rayon::iter::ParallelIterator;
+
+ crate::utils::into_iter(0..DWORD::BITS)
+ .filter_map(|x| {
+ if (drives >> x) & 1 == 0 {
+ return None;
+ }
+ let mount_point = [b'A' as u16 + x as u16, b':' as u16, b'\\' as u16, 0];
+
+ let drive_type = GetDriveTypeW(mount_point.as_ptr());
+
+ let is_removable = drive_type == DRIVE_REMOVABLE;
+
+ if drive_type != DRIVE_FIXED && drive_type != DRIVE_REMOVABLE {
+ return None;
+ }
+ let mut name = [0u16; MAX_PATH + 1];
+ let mut file_system = [0u16; 32];
+ if GetVolumeInformationW(
+ mount_point.as_ptr(),
+ name.as_mut_ptr(),
+ name.len() as DWORD,
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ file_system.as_mut_ptr(),
+ file_system.len() as DWORD,
+ ) == 0
+ {
+ return None;
+ }
+ let mut pos = 0;
+ for x in name.iter() {
+ if *x == 0 {
+ break;
+ }
+ pos += 1;
+ }
+ let name = String::from_utf16_lossy(&name[..pos]);
+ let name = OsStr::new(&name);
+
+ pos = 0;
+ for x in file_system.iter() {
+ if *x == 0 {
+ break;
+ }
+ pos += 1;
+ }
+ let file_system: Vec<u8> = file_system[..pos].iter().map(|x| *x as u8).collect();
+
+ let drive_name = [
+ b'\\' as u16,
+ b'\\' as u16,
+ b'.' as u16,
+ b'\\' as u16,
+ b'A' as u16 + x as u16,
+ b':' as u16,
+ 0,
+ ];
+ let handle = HandleWrapper::new(&drive_name, 0)?;
+ let (total_space, available_space) = get_drive_size(&mount_point)?;
+ if total_space == 0 {
+ return None;
+ }
+ /*let mut spq_trim: STORAGE_PROPERTY_QUERY = std::mem::zeroed();
+ spq_trim.PropertyId = StorageDeviceTrimProperty;
+ spq_trim.QueryType = PropertyStandardQuery;
+ let mut dtd: DEVICE_TRIM_DESCRIPTOR = std::mem::zeroed();*/
+ let mut spq_trim = STORAGE_PROPERTY_QUERY {
+ PropertyId: 8,
+ QueryType: 0,
+ AdditionalParameters: [0],
+ };
+ let mut dtd: DEVICE_TRIM_DESCRIPTOR = std::mem::zeroed();
+
+ let mut dw_size = 0;
+ let type_ = if DeviceIoControl(
+ handle.0,
+ IOCTL_STORAGE_QUERY_PROPERTY,
+ &mut spq_trim as *mut STORAGE_PROPERTY_QUERY as *mut c_void,
+ size_of::<STORAGE_PROPERTY_QUERY>() as DWORD,
+ &mut dtd as *mut DEVICE_TRIM_DESCRIPTOR as *mut c_void,
+ size_of::<DEVICE_TRIM_DESCRIPTOR>() as DWORD,
+ &mut dw_size,
+ std::ptr::null_mut(),
+ ) == 0
+ || dw_size != size_of::<DEVICE_TRIM_DESCRIPTOR>() as DWORD
+ {
+ DiskType::Unknown(-1)
+ } else {
+ let is_ssd = dtd.TrimEnabled != 0;
+ if is_ssd {
+ DiskType::SSD
+ } else {
+ DiskType::HDD
+ }
+ };
+ Some(Disk {
+ type_,
+ name: name.to_owned(),
+ file_system: file_system.to_vec(),
+ mount_point: mount_point.to_vec(),
+ s_mount_point: String::from_utf16_lossy(&mount_point[..mount_point.len() - 1]),
+ total_space,
+ available_space,
+ is_removable,
+ })
+ })
+ .collect::<Vec<_>>()
+}
diff --git a/vendor/sysinfo/src/windows/macros.rs b/vendor/sysinfo/src/windows/macros.rs
new file mode 100644
index 000000000..b0c024c7c
--- /dev/null
+++ b/vendor/sysinfo/src/windows/macros.rs
@@ -0,0 +1,16 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+/// Allows to cast only when needed.
+#[macro_export]
+macro_rules! auto_cast {
+ ($t:expr, $cast:ty) => {{
+ #[cfg(target_pointer_width = "32")]
+ {
+ $t as $cast
+ }
+ #[cfg(not(target_pointer_width = "32"))]
+ {
+ $t
+ }
+ }};
+}
diff --git a/vendor/sysinfo/src/windows/mod.rs b/vendor/sysinfo/src/windows/mod.rs
new file mode 100644
index 000000000..fa5d66beb
--- /dev/null
+++ b/vendor/sysinfo/src/windows/mod.rs
@@ -0,0 +1,20 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+mod component;
+mod cpu;
+mod disk;
+#[macro_use]
+mod macros;
+mod network;
+mod process;
+mod system;
+mod tools;
+mod users;
+mod utils;
+
+pub use self::component::Component;
+pub use self::cpu::Cpu;
+pub use self::disk::Disk;
+pub use self::network::{NetworkData, Networks};
+pub use self::process::Process;
+pub use self::system::System;
diff --git a/vendor/sysinfo/src/windows/network.rs b/vendor/sysinfo/src/windows/network.rs
new file mode 100644
index 000000000..6a09a0490
--- /dev/null
+++ b/vendor/sysinfo/src/windows/network.rs
@@ -0,0 +1,249 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{NetworkExt, NetworksExt, NetworksIter};
+
+use std::collections::{hash_map, HashMap};
+
+use winapi::shared::ifdef::{MediaConnectStateDisconnected, NET_LUID};
+use winapi::shared::netioapi::{
+ FreeMibTable, GetIfEntry2, GetIfTable2, MIB_IF_ROW2, PMIB_IF_TABLE2,
+};
+use winapi::shared::winerror::NO_ERROR;
+
+macro_rules! old_and_new {
+ ($ty_:expr, $name:ident, $old:ident, $new_val:expr) => {{
+ $ty_.$old = $ty_.$name;
+ $ty_.$name = $new_val;
+ }};
+}
+
+#[doc = include_str!("../../md_doc/networks.md")]
+pub struct Networks {
+ interfaces: HashMap<String, NetworkData>,
+}
+
+impl Networks {
+ pub(crate) fn new() -> Networks {
+ Networks {
+ interfaces: HashMap::new(),
+ }
+ }
+}
+
+impl NetworksExt for Networks {
+ #[allow(clippy::needless_lifetimes)]
+ fn iter<'a>(&'a self) -> NetworksIter<'a> {
+ NetworksIter::new(self.interfaces.iter())
+ }
+
+ fn refresh_networks_list(&mut self) {
+ let mut table: PMIB_IF_TABLE2 = std::ptr::null_mut();
+
+ unsafe {
+ if GetIfTable2(&mut table) != NO_ERROR {
+ return;
+ }
+
+ for (_, data) in self.interfaces.iter_mut() {
+ data.updated = false;
+ }
+
+ // In here, this is tricky: we have to filter out the software interfaces to only keep
+ // the hardware ones. To do so, we first check the connection potential speed (if 0, not
+ // interesting), then we check its state: if not open, not interesting either. And finally,
+ // we count the members of a same group: if there is more than 1, then it's software level.
+ let mut groups = HashMap::new();
+ let mut indexes = Vec::new();
+ let ptr = (*table).Table.as_ptr();
+ for i in 0..(*table).NumEntries {
+ let ptr = &*ptr.offset(i as _);
+ if (ptr.TransmitLinkSpeed == 0 && ptr.ReceiveLinkSpeed == 0)
+ || ptr.MediaConnectState == MediaConnectStateDisconnected
+ || ptr.PhysicalAddressLength == 0
+ {
+ continue;
+ }
+ let id = vec![
+ ptr.InterfaceGuid.Data2,
+ ptr.InterfaceGuid.Data3,
+ ptr.InterfaceGuid.Data4[0] as _,
+ ptr.InterfaceGuid.Data4[1] as _,
+ ptr.InterfaceGuid.Data4[2] as _,
+ ptr.InterfaceGuid.Data4[3] as _,
+ ptr.InterfaceGuid.Data4[4] as _,
+ ptr.InterfaceGuid.Data4[5] as _,
+ ptr.InterfaceGuid.Data4[6] as _,
+ ptr.InterfaceGuid.Data4[7] as _,
+ ];
+ let entry = groups.entry(id.clone()).or_insert(0);
+ *entry += 1;
+ if *entry > 1 {
+ continue;
+ }
+ indexes.push((i, id));
+ }
+ for (i, id) in indexes {
+ let ptr = &*ptr.offset(i as _);
+ if *groups.get(&id).unwrap_or(&0) > 1 {
+ continue;
+ }
+ let mut pos = 0;
+ for x in ptr.Alias.iter() {
+ if *x == 0 {
+ break;
+ }
+ pos += 1;
+ }
+ let interface_name = match String::from_utf16(&ptr.Alias[..pos]) {
+ Ok(s) => s,
+ _ => continue,
+ };
+ match self.interfaces.entry(interface_name) {
+ hash_map::Entry::Occupied(mut e) => {
+ let mut 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!(
+ interface,
+ packets_in,
+ old_packets_in,
+ ptr.InUcastPkts.saturating_add(ptr.InNUcastPkts)
+ );
+ old_and_new!(
+ interface,
+ packets_out,
+ old_packets_out,
+ ptr.OutUcastPkts.saturating_add(ptr.OutNUcastPkts)
+ );
+ old_and_new!(interface, errors_in, old_errors_in, ptr.InErrors);
+ old_and_new!(interface, errors_out, old_errors_out, ptr.OutErrors);
+ interface.updated = true;
+ }
+ hash_map::Entry::Vacant(e) => {
+ let packets_in = ptr.InUcastPkts.saturating_add(ptr.InNUcastPkts);
+ let packets_out = ptr.OutUcastPkts.saturating_add(ptr.OutNUcastPkts);
+
+ e.insert(NetworkData {
+ id: ptr.InterfaceLuid,
+ current_out: ptr.OutOctets,
+ old_out: ptr.OutOctets,
+ current_in: ptr.InOctets,
+ old_in: ptr.InOctets,
+ packets_in,
+ old_packets_in: packets_in,
+ packets_out,
+ old_packets_out: packets_out,
+ errors_in: ptr.InErrors,
+ old_errors_in: ptr.InErrors,
+ errors_out: ptr.OutErrors,
+ old_errors_out: ptr.OutErrors,
+ updated: true,
+ });
+ }
+ }
+ }
+ FreeMibTable(table as _);
+ }
+ // Remove interfaces which are gone.
+ self.interfaces.retain(|_, d| d.updated);
+ }
+
+ fn refresh(&mut self) {
+ let entry = std::mem::MaybeUninit::<MIB_IF_ROW2>::zeroed();
+
+ unsafe {
+ let mut entry = entry.assume_init();
+ for (_, interface) in self.interfaces.iter_mut() {
+ entry.InterfaceLuid = interface.id;
+ entry.InterfaceIndex = 0; // to prevent the function to pick this one as index
+ if GetIfEntry2(&mut entry) != NO_ERROR {
+ continue;
+ }
+ old_and_new!(interface, current_out, old_out, entry.OutOctets);
+ old_and_new!(interface, current_in, old_in, entry.InOctets);
+ old_and_new!(
+ interface,
+ packets_in,
+ old_packets_in,
+ entry.InUcastPkts.saturating_add(entry.InNUcastPkts)
+ );
+ old_and_new!(
+ interface,
+ packets_out,
+ old_packets_out,
+ entry.OutUcastPkts.saturating_add(entry.OutNUcastPkts)
+ );
+ old_and_new!(interface, errors_in, old_errors_in, entry.InErrors);
+ old_and_new!(interface, errors_out, old_errors_out, entry.OutErrors);
+ }
+ }
+ }
+}
+
+#[doc = include_str!("../../md_doc/network_data.md")]
+pub struct NetworkData {
+ id: NET_LUID,
+ current_out: u64,
+ old_out: u64,
+ current_in: u64,
+ old_in: u64,
+ packets_in: u64,
+ old_packets_in: u64,
+ packets_out: u64,
+ old_packets_out: u64,
+ errors_in: u64,
+ old_errors_in: u64,
+ errors_out: u64,
+ old_errors_out: u64,
+ updated: bool,
+}
+
+impl NetworkExt for NetworkData {
+ fn received(&self) -> u64 {
+ self.current_in.saturating_sub(self.old_in)
+ }
+
+ fn total_received(&self) -> u64 {
+ self.current_in
+ }
+
+ fn transmitted(&self) -> u64 {
+ self.current_out.saturating_sub(self.old_out)
+ }
+
+ fn total_transmitted(&self) -> u64 {
+ self.current_out
+ }
+
+ fn packets_received(&self) -> u64 {
+ self.packets_in.saturating_sub(self.old_packets_in)
+ }
+
+ fn total_packets_received(&self) -> u64 {
+ self.packets_in
+ }
+
+ fn packets_transmitted(&self) -> u64 {
+ self.packets_out.saturating_sub(self.old_packets_out)
+ }
+
+ fn total_packets_transmitted(&self) -> u64 {
+ self.packets_out
+ }
+
+ fn errors_on_received(&self) -> u64 {
+ self.errors_in.saturating_sub(self.old_errors_in)
+ }
+
+ fn total_errors_on_received(&self) -> u64 {
+ self.errors_in
+ }
+
+ fn errors_on_transmitted(&self) -> u64 {
+ self.errors_out.saturating_sub(self.old_errors_out)
+ }
+
+ fn total_errors_on_transmitted(&self) -> u64 {
+ self.errors_out
+ }
+}
diff --git a/vendor/sysinfo/src/windows/process.rs b/vendor/sysinfo/src/windows/process.rs
new file mode 100644
index 000000000..bdc35c53e
--- /dev/null
+++ b/vendor/sysinfo/src/windows/process.rs
@@ -0,0 +1,1019 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::utils::to_str;
+use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid};
+
+use std::ffi::OsString;
+use std::fmt;
+use std::mem::{size_of, zeroed, MaybeUninit};
+use std::ops::Deref;
+use std::os::windows::ffi::OsStringExt;
+use std::os::windows::process::CommandExt;
+use std::path::{Path, PathBuf};
+use std::process;
+use std::ptr::null_mut;
+use std::str;
+use std::sync::Arc;
+
+use libc::{c_void, memcpy};
+
+use ntapi::ntpebteb::PEB;
+use ntapi::ntwow64::{PEB32, PRTL_USER_PROCESS_PARAMETERS32, RTL_USER_PROCESS_PARAMETERS32};
+use once_cell::sync::Lazy;
+
+use ntapi::ntpsapi::{
+ NtQueryInformationProcess, ProcessBasicInformation, ProcessCommandLineInformation,
+ ProcessWow64Information, PROCESSINFOCLASS, PROCESS_BASIC_INFORMATION,
+};
+use ntapi::ntrtl::{RtlGetVersion, PRTL_USER_PROCESS_PARAMETERS, RTL_USER_PROCESS_PARAMETERS};
+use winapi::shared::basetsd::SIZE_T;
+use winapi::shared::minwindef::{DWORD, FALSE, FILETIME, LPVOID, MAX_PATH, TRUE, ULONG};
+use winapi::shared::ntdef::{NT_SUCCESS, UNICODE_STRING};
+use winapi::shared::ntstatus::{
+ STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH,
+};
+use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
+use winapi::um::errhandlingapi::GetLastError;
+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,
+};
+use winapi::um::psapi::{
+ EnumProcessModulesEx, GetModuleBaseNameW, GetModuleFileNameExW, GetProcessMemoryInfo,
+ LIST_MODULES_ALL, PROCESS_MEMORY_COUNTERS, PROCESS_MEMORY_COUNTERS_EX,
+};
+use winapi::um::securitybaseapi::GetTokenInformation;
+use winapi::um::winbase::{GetProcessIoCounters, CREATE_NO_WINDOW};
+use winapi::um::winnt::{
+ TokenUser, HANDLE, HEAP_ZERO_MEMORY, IO_COUNTERS, MEMORY_BASIC_INFORMATION,
+ PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, RTL_OSVERSIONINFOEXW, TOKEN_QUERY, TOKEN_USER,
+ ULARGE_INTEGER,
+};
+
+impl fmt::Display for ProcessStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(match *self {
+ ProcessStatus::Run => "Runnable",
+ _ => "Unknown",
+ })
+ }
+}
+
+fn get_process_handler(pid: Pid) -> Option<HandleWrapper> {
+ if pid.0 == 0 {
+ return None;
+ }
+ let options = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
+
+ unsafe { HandleWrapper::new(OpenProcess(options, FALSE, pid.0 as DWORD)) }
+}
+
+unsafe fn get_process_user_id(
+ handle: &HandleWrapper,
+ refresh_kind: ProcessRefreshKind,
+) -> Option<Uid> {
+ struct HeapWrap<T>(*mut T);
+
+ impl<T> HeapWrap<T> {
+ unsafe fn new(size: DWORD) -> Option<Self> {
+ let ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size as _) as *mut T;
+ if ptr.is_null() {
+ sysinfo_debug!("HeapAlloc failed");
+ None
+ } else {
+ Some(Self(ptr))
+ }
+ }
+ }
+
+ impl<T> Drop for HeapWrap<T> {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe {
+ HeapFree(GetProcessHeap(), 0, self.0 as *mut _);
+ }
+ }
+ }
+ }
+
+ if !refresh_kind.user() {
+ return None;
+ }
+
+ let mut token = null_mut();
+
+ if OpenProcessToken(**handle, TOKEN_QUERY, &mut token) == 0 {
+ sysinfo_debug!("OpenProcessToken failed");
+ return None;
+ }
+
+ let token = HandleWrapper::new(token)?;
+
+ let mut size = 0;
+
+ if GetTokenInformation(*token, TokenUser, null_mut(), 0, &mut size) == 0 {
+ let err = GetLastError();
+ if err != ERROR_INSUFFICIENT_BUFFER {
+ sysinfo_debug!("GetTokenInformation failed, error: {:?}", err);
+ return None;
+ }
+ }
+
+ let ptu: HeapWrap<TOKEN_USER> = HeapWrap::new(size)?;
+
+ if GetTokenInformation(*token, TokenUser, ptu.0 as *mut _, size, &mut size) == 0 {
+ sysinfo_debug!("GetTokenInformation failed, error: {:?}", GetLastError());
+ 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()))
+ }
+}
+
+struct HandleWrapper(HANDLE);
+
+impl HandleWrapper {
+ fn new(handle: HANDLE) -> Option<Self> {
+ if handle.is_null() {
+ None
+ } else {
+ Some(Self(handle))
+ }
+ }
+}
+
+impl Deref for HandleWrapper {
+ type Target = HANDLE;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl Drop for HandleWrapper {
+ fn drop(&mut self) {
+ unsafe {
+ CloseHandle(self.0);
+ }
+ }
+}
+
+#[allow(clippy::non_send_fields_in_send_ty)]
+unsafe impl Send for HandleWrapper {}
+unsafe impl Sync for HandleWrapper {}
+
+#[doc = include_str!("../../md_doc/process.md")]
+pub struct Process {
+ name: String,
+ cmd: Vec<String>,
+ exe: PathBuf,
+ pid: Pid,
+ user_id: Option<Uid>,
+ environ: Vec<String>,
+ cwd: PathBuf,
+ root: PathBuf,
+ pub(crate) memory: u64,
+ pub(crate) virtual_memory: u64,
+ parent: Option<Pid>,
+ status: ProcessStatus,
+ handle: Option<Arc<HandleWrapper>>,
+ cpu_calc_values: CPUsageCalculationValues,
+ start_time: u64,
+ pub(crate) run_time: u64,
+ cpu_usage: f32,
+ pub(crate) updated: bool,
+ old_read_bytes: u64,
+ old_written_bytes: u64,
+ read_bytes: u64,
+ written_bytes: u64,
+}
+
+struct CPUsageCalculationValues {
+ old_process_sys_cpu: u64,
+ old_process_user_cpu: u64,
+ old_system_sys_cpu: u64,
+ old_system_user_cpu: u64,
+}
+
+impl CPUsageCalculationValues {
+ fn new() -> Self {
+ CPUsageCalculationValues {
+ old_process_sys_cpu: 0,
+ old_process_user_cpu: 0,
+ old_system_sys_cpu: 0,
+ old_system_user_cpu: 0,
+ }
+ }
+}
+static WINDOWS_8_1_OR_NEWER: Lazy<bool> = Lazy::new(|| unsafe {
+ let mut version_info: RTL_OSVERSIONINFOEXW = MaybeUninit::zeroed().assume_init();
+
+ version_info.dwOSVersionInfoSize = std::mem::size_of::<RTL_OSVERSIONINFOEXW>() as u32;
+ if !NT_SUCCESS(RtlGetVersion(
+ &mut version_info as *mut RTL_OSVERSIONINFOEXW as *mut _,
+ )) {
+ return true;
+ }
+
+ // Windows 8.1 is 6.3
+ version_info.dwMajorVersion > 6
+ || version_info.dwMajorVersion == 6 && version_info.dwMinorVersion >= 3
+});
+
+unsafe fn get_process_name(process_handler: &HandleWrapper, h_mod: *mut c_void) -> String {
+ let mut process_name = [0u16; MAX_PATH + 1];
+
+ GetModuleBaseNameW(
+ **process_handler,
+ h_mod as _,
+ process_name.as_mut_ptr(),
+ MAX_PATH as DWORD + 1,
+ );
+ null_terminated_wchar_to_string(&process_name)
+}
+
+unsafe fn get_h_mod(process_handler: &HandleWrapper, h_mod: &mut *mut c_void) -> bool {
+ let mut cb_needed = 0;
+ EnumProcessModulesEx(
+ **process_handler,
+ h_mod as *mut *mut c_void as _,
+ size_of::<DWORD>() as DWORD,
+ &mut cb_needed,
+ LIST_MODULES_ALL,
+ ) != 0
+}
+
+unsafe fn get_exe(process_handler: &HandleWrapper, h_mod: *mut c_void) -> PathBuf {
+ let mut exe_buf = [0u16; MAX_PATH + 1];
+ GetModuleFileNameExW(
+ **process_handler,
+ h_mod as _,
+ exe_buf.as_mut_ptr(),
+ MAX_PATH as DWORD + 1,
+ );
+
+ PathBuf::from(null_terminated_wchar_to_string(&exe_buf))
+}
+
+impl Process {
+ pub(crate) fn new_from_pid(
+ pid: Pid,
+ now: u64,
+ refresh_kind: ProcessRefreshKind,
+ ) -> Option<Process> {
+ unsafe {
+ let process_handler = get_process_handler(pid)?;
+ let mut info: MaybeUninit<PROCESS_BASIC_INFORMATION> = MaybeUninit::uninit();
+ if NtQueryInformationProcess(
+ *process_handler,
+ ProcessBasicInformation,
+ info.as_mut_ptr() as *mut _,
+ size_of::<PROCESS_BASIC_INFORMATION>() as _,
+ null_mut(),
+ ) != 0
+ {
+ return None;
+ }
+ let info = info.assume_init();
+ let mut h_mod = null_mut();
+
+ let name = if get_h_mod(&process_handler, &mut h_mod) {
+ get_process_name(&process_handler, h_mod)
+ } else {
+ String::new()
+ };
+
+ let exe = get_exe(&process_handler, h_mod);
+ let mut root = exe.clone();
+ root.pop();
+ let (cmd, environ, cwd) = match get_process_params(&process_handler) {
+ Ok(args) => args,
+ Err(_e) => {
+ sysinfo_debug!("Failed to get process parameters: {}", _e);
+ (Vec::new(), Vec::new(), PathBuf::new())
+ }
+ };
+ let (start_time, run_time) = get_start_and_run_time(&process_handler, now);
+ let parent = if info.InheritedFromUniqueProcessId as usize != 0 {
+ Some(Pid(info.InheritedFromUniqueProcessId as _))
+ } else {
+ None
+ };
+ let user_id = get_process_user_id(&process_handler, refresh_kind);
+ Some(Process {
+ handle: Some(Arc::new(process_handler)),
+ name,
+ pid,
+ parent,
+ user_id,
+ cmd,
+ environ,
+ exe,
+ cwd,
+ root,
+ status: ProcessStatus::Run,
+ memory: 0,
+ virtual_memory: 0,
+ cpu_usage: 0.,
+ cpu_calc_values: CPUsageCalculationValues::new(),
+ start_time,
+ run_time,
+ updated: true,
+ old_read_bytes: 0,
+ old_written_bytes: 0,
+ read_bytes: 0,
+ written_bytes: 0,
+ })
+ }
+ }
+
+ pub(crate) fn new_full(
+ pid: Pid,
+ parent: Option<Pid>,
+ memory: u64,
+ virtual_memory: u64,
+ name: String,
+ now: u64,
+ refresh_kind: ProcessRefreshKind,
+ ) -> Process {
+ if let Some(handle) = get_process_handler(pid) {
+ let mut h_mod = null_mut();
+
+ unsafe {
+ let exe = if get_h_mod(&handle, &mut h_mod) {
+ get_exe(&handle, h_mod)
+ } else {
+ PathBuf::new()
+ };
+ let mut root = exe.clone();
+ root.pop();
+ let (cmd, environ, cwd) = match get_process_params(&handle) {
+ Ok(args) => args,
+ Err(_e) => {
+ sysinfo_debug!("Failed to get process parameters: {}", _e);
+ (Vec::new(), Vec::new(), PathBuf::new())
+ }
+ };
+ let (start_time, run_time) = get_start_and_run_time(&handle, now);
+ let user_id = get_process_user_id(&handle, refresh_kind);
+ Process {
+ handle: Some(Arc::new(handle)),
+ name,
+ pid,
+ user_id,
+ parent,
+ cmd,
+ environ,
+ exe,
+ cwd,
+ root,
+ status: ProcessStatus::Run,
+ memory,
+ virtual_memory,
+ cpu_usage: 0.,
+ cpu_calc_values: CPUsageCalculationValues::new(),
+ start_time,
+ run_time,
+ updated: true,
+ old_read_bytes: 0,
+ old_written_bytes: 0,
+ read_bytes: 0,
+ written_bytes: 0,
+ }
+ }
+ } else {
+ Process {
+ handle: None,
+ name,
+ pid,
+ user_id: None,
+ parent,
+ cmd: Vec::new(),
+ environ: Vec::new(),
+ exe: get_executable_path(pid),
+ cwd: PathBuf::new(),
+ root: PathBuf::new(),
+ status: ProcessStatus::Run,
+ memory,
+ virtual_memory,
+ cpu_usage: 0.,
+ cpu_calc_values: CPUsageCalculationValues::new(),
+ start_time: 0,
+ run_time: 0,
+ updated: true,
+ old_read_bytes: 0,
+ old_written_bytes: 0,
+ read_bytes: 0,
+ written_bytes: 0,
+ }
+ }
+ }
+
+ pub(crate) fn update(
+ &mut self,
+ refresh_kind: crate::ProcessRefreshKind,
+ nb_cpus: u64,
+ now: u64,
+ ) {
+ if refresh_kind.cpu() {
+ compute_cpu_usage(self, nb_cpus);
+ }
+ if refresh_kind.disk_usage() {
+ update_disk_usage(self);
+ }
+ self.run_time = now.saturating_sub(self.start_time());
+ self.updated = true;
+ }
+
+ pub(crate) fn get_handle(&self) -> Option<HANDLE> {
+ self.handle.as_ref().map(|h| ***h)
+ }
+}
+
+impl ProcessExt for Process {
+ fn kill_with(&self, signal: Signal) -> Option<bool> {
+ super::system::convert_signal(signal)?;
+ let mut kill = process::Command::new("taskkill.exe");
+ kill.arg("/PID").arg(self.pid.to_string()).arg("/F");
+ kill.creation_flags(CREATE_NO_WINDOW);
+ match kill.output() {
+ Ok(o) => Some(o.status.success()),
+ Err(_) => Some(false),
+ }
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ fn cmd(&self) -> &[String] {
+ &self.cmd
+ }
+
+ fn exe(&self) -> &Path {
+ self.exe.as_path()
+ }
+
+ fn pid(&self) -> Pid {
+ self.pid
+ }
+
+ fn environ(&self) -> &[String] {
+ &self.environ
+ }
+
+ fn cwd(&self) -> &Path {
+ self.cwd.as_path()
+ }
+
+ fn root(&self) -> &Path {
+ self.root.as_path()
+ }
+
+ fn memory(&self) -> u64 {
+ self.memory
+ }
+
+ fn virtual_memory(&self) -> u64 {
+ self.virtual_memory
+ }
+
+ fn parent(&self) -> Option<Pid> {
+ self.parent
+ }
+
+ fn status(&self) -> ProcessStatus {
+ self.status
+ }
+
+ fn start_time(&self) -> u64 {
+ self.start_time
+ }
+
+ fn run_time(&self) -> u64 {
+ self.run_time
+ }
+
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn disk_usage(&self) -> DiskUsage {
+ DiskUsage {
+ written_bytes: self.written_bytes - self.old_written_bytes,
+ total_written_bytes: self.written_bytes,
+ read_bytes: self.read_bytes - self.old_read_bytes,
+ total_read_bytes: self.read_bytes,
+ }
+ }
+
+ fn user_id(&self) -> Option<&Uid> {
+ self.user_id.as_ref()
+ }
+
+ fn group_id(&self) -> Option<Gid> {
+ None
+ }
+}
+
+unsafe fn get_start_and_run_time(handle: &HandleWrapper, now: u64) -> (u64, u64) {
+ let mut fstart: FILETIME = zeroed();
+ let mut x = zeroed();
+
+ GetProcessTimes(
+ **handle,
+ &mut fstart as *mut FILETIME,
+ &mut x as *mut FILETIME,
+ &mut x as *mut FILETIME,
+ &mut x as *mut FILETIME,
+ );
+ let tmp = super::utils::filetime_to_u64(fstart);
+ // 11_644_473_600 is the number of seconds between the Windows epoch (1601-01-01) and
+ // the linux epoch (1970-01-01).
+ let start = tmp / 10_000_000 - 11_644_473_600;
+ let run_time = check_sub(now, start);
+ (start, run_time)
+}
+
+#[allow(clippy::uninit_vec)]
+unsafe fn ph_query_process_variable_size(
+ process_handle: &HandleWrapper,
+ process_information_class: PROCESSINFOCLASS,
+) -> Option<Vec<u16>> {
+ let mut return_length = MaybeUninit::<ULONG>::uninit();
+
+ let mut status = NtQueryInformationProcess(
+ **process_handle,
+ process_information_class,
+ null_mut(),
+ 0,
+ return_length.as_mut_ptr() as *mut _,
+ );
+
+ if status != STATUS_BUFFER_OVERFLOW
+ && status != STATUS_BUFFER_TOO_SMALL
+ && status != STATUS_INFO_LENGTH_MISMATCH
+ {
+ return None;
+ }
+
+ 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,
+ buffer.as_mut_ptr() as *mut _,
+ return_length,
+ &mut return_length as *mut _,
+ );
+ if !NT_SUCCESS(status) {
+ return None;
+ }
+ buffer.push(0);
+ Some(buffer)
+}
+
+unsafe fn get_cmdline_from_buffer(buffer: *const u16) -> Vec<String> {
+ // Get argc and argv from the command line
+ let mut argc = MaybeUninit::<i32>::uninit();
+ let argv_p = winapi::um::shellapi::CommandLineToArgvW(buffer, argc.as_mut_ptr());
+ if argv_p.is_null() {
+ return Vec::new();
+ }
+ let argc = argc.assume_init();
+ let argv = std::slice::from_raw_parts(argv_p, argc as usize);
+
+ let mut res = Vec::new();
+ for arg in argv {
+ let len = libc::wcslen(*arg);
+ let str_slice = std::slice::from_raw_parts(*arg, len);
+ res.push(String::from_utf16_lossy(str_slice));
+ }
+
+ winapi::um::winbase::LocalFree(argv_p as *mut _);
+
+ res
+}
+
+unsafe fn get_region_size(handle: &HandleWrapper, ptr: LPVOID) -> Result<usize, &'static str> {
+ let mut meminfo = MaybeUninit::<MEMORY_BASIC_INFORMATION>::uninit();
+ if VirtualQueryEx(
+ **handle,
+ ptr,
+ meminfo.as_mut_ptr() as *mut _,
+ size_of::<MEMORY_BASIC_INFORMATION>(),
+ ) == 0
+ {
+ return Err("Unable to read process memory information");
+ }
+ let meminfo = meminfo.assume_init();
+ 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);
+ if ReadProcessMemory(
+ **handle,
+ ptr as *mut _,
+ buffer.as_mut_ptr() as *mut _,
+ size,
+ null_mut(),
+ ) != TRUE
+ {
+ return Err("Unable to read process data");
+ }
+ Ok(buffer)
+}
+
+trait RtlUserProcessParameters {
+ fn get_cmdline(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str>;
+ fn get_cwd(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str>;
+ fn get_environ(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str>;
+}
+
+macro_rules! impl_RtlUserProcessParameters {
+ ($t:ty) => {
+ impl RtlUserProcessParameters for $t {
+ fn get_cmdline(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str> {
+ let ptr = self.CommandLine.Buffer;
+ let size = self.CommandLine.Length;
+ unsafe { get_process_data(handle, ptr as _, size as _) }
+ }
+ fn get_cwd(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str> {
+ let ptr = self.CurrentDirectory.DosPath.Buffer;
+ let size = self.CurrentDirectory.DosPath.Length;
+ unsafe { get_process_data(handle, ptr as _, size as _) }
+ }
+ fn get_environ(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str> {
+ let ptr = self.Environment;
+ unsafe {
+ let size = get_region_size(handle, ptr as LPVOID)?;
+ get_process_data(handle, ptr as _, size as _)
+ }
+ }
+ }
+ };
+}
+
+impl_RtlUserProcessParameters!(RTL_USER_PROCESS_PARAMETERS32);
+impl_RtlUserProcessParameters!(RTL_USER_PROCESS_PARAMETERS);
+
+unsafe fn get_process_params(
+ handle: &HandleWrapper,
+) -> Result<(Vec<String>, Vec<String>, PathBuf), &'static str> {
+ if !cfg!(target_pointer_width = "64") {
+ return Err("Non 64 bit targets are not supported");
+ }
+
+ // First check if target process is running in wow64 compatibility emulator
+ let mut pwow32info = MaybeUninit::<LPVOID>::uninit();
+ let result = NtQueryInformationProcess(
+ **handle,
+ ProcessWow64Information,
+ pwow32info.as_mut_ptr() as *mut _,
+ size_of::<LPVOID>() as u32,
+ null_mut(),
+ );
+ if !NT_SUCCESS(result) {
+ return Err("Unable to check WOW64 information about the process");
+ }
+ let pwow32info = pwow32info.assume_init();
+
+ if pwow32info.is_null() {
+ // target is a 64 bit process
+
+ let mut pbasicinfo = MaybeUninit::<PROCESS_BASIC_INFORMATION>::uninit();
+ let result = NtQueryInformationProcess(
+ **handle,
+ ProcessBasicInformation,
+ pbasicinfo.as_mut_ptr() as *mut _,
+ size_of::<PROCESS_BASIC_INFORMATION>() as u32,
+ null_mut(),
+ );
+ if !NT_SUCCESS(result) {
+ return Err("Unable to get basic process information");
+ }
+ let pinfo = pbasicinfo.assume_init();
+
+ let mut peb = MaybeUninit::<PEB>::uninit();
+ if ReadProcessMemory(
+ **handle,
+ pinfo.PebBaseAddress as *mut _,
+ peb.as_mut_ptr() as *mut _,
+ size_of::<PEB>() as SIZE_T,
+ null_mut(),
+ ) != TRUE
+ {
+ return Err("Unable to read process PEB");
+ }
+
+ let peb = peb.assume_init();
+
+ let mut proc_params = MaybeUninit::<RTL_USER_PROCESS_PARAMETERS>::uninit();
+ if ReadProcessMemory(
+ **handle,
+ peb.ProcessParameters as *mut PRTL_USER_PROCESS_PARAMETERS as *mut _,
+ proc_params.as_mut_ptr() as *mut _,
+ size_of::<RTL_USER_PROCESS_PARAMETERS>() as SIZE_T,
+ null_mut(),
+ ) != TRUE
+ {
+ return Err("Unable to read process parameters");
+ }
+
+ let proc_params = proc_params.assume_init();
+ return Ok((
+ get_cmd_line(&proc_params, handle),
+ get_proc_env(&proc_params, handle),
+ get_cwd(&proc_params, handle),
+ ));
+ }
+ // target is a 32 bit process in wow64 mode
+
+ let mut peb32 = MaybeUninit::<PEB32>::uninit();
+ if ReadProcessMemory(
+ **handle,
+ pwow32info,
+ peb32.as_mut_ptr() as *mut _,
+ size_of::<PEB32>() as SIZE_T,
+ null_mut(),
+ ) != TRUE
+ {
+ return Err("Unable to read PEB32");
+ }
+ let peb32 = peb32.assume_init();
+
+ let mut proc_params = MaybeUninit::<RTL_USER_PROCESS_PARAMETERS32>::uninit();
+ if ReadProcessMemory(
+ **handle,
+ peb32.ProcessParameters as *mut PRTL_USER_PROCESS_PARAMETERS32 as *mut _,
+ proc_params.as_mut_ptr() as *mut _,
+ size_of::<RTL_USER_PROCESS_PARAMETERS32>() as SIZE_T,
+ null_mut(),
+ ) != TRUE
+ {
+ return Err("Unable to read 32 bit process parameters");
+ }
+ let proc_params = proc_params.assume_init();
+ Ok((
+ get_cmd_line(&proc_params, handle),
+ get_proc_env(&proc_params, handle),
+ get_cwd(&proc_params, handle),
+ ))
+}
+
+fn get_cwd<T: RtlUserProcessParameters>(params: &T, handle: &HandleWrapper) -> PathBuf {
+ match params.get_cwd(handle) {
+ Ok(buffer) => unsafe { PathBuf::from(null_terminated_wchar_to_string(buffer.as_slice())) },
+ Err(_e) => {
+ sysinfo_debug!("get_cwd failed to get data: {}", _e);
+ PathBuf::new()
+ }
+ }
+}
+
+unsafe fn null_terminated_wchar_to_string(slice: &[u16]) -> String {
+ match slice.iter().position(|&x| x == 0) {
+ Some(pos) => OsString::from_wide(&slice[..pos])
+ .to_string_lossy()
+ .into_owned(),
+ None => OsString::from_wide(slice).to_string_lossy().into_owned(),
+ }
+}
+
+fn get_cmd_line_old<T: RtlUserProcessParameters>(
+ params: &T,
+ handle: &HandleWrapper,
+) -> Vec<String> {
+ match params.get_cmdline(handle) {
+ Ok(buffer) => unsafe { get_cmdline_from_buffer(buffer.as_ptr()) },
+ Err(_e) => {
+ sysinfo_debug!("get_cmd_line_old failed to get data: {}", _e);
+ Vec::new()
+ }
+ }
+}
+
+#[allow(clippy::cast_ptr_alignment)]
+fn get_cmd_line_new(handle: &HandleWrapper) -> Vec<String> {
+ unsafe {
+ if let Some(buffer) = ph_query_process_variable_size(handle, ProcessCommandLineInformation)
+ {
+ let buffer = (*(buffer.as_ptr() as *const UNICODE_STRING)).Buffer;
+
+ get_cmdline_from_buffer(buffer)
+ } else {
+ vec![]
+ }
+ }
+}
+
+fn get_cmd_line<T: RtlUserProcessParameters>(params: &T, handle: &HandleWrapper) -> Vec<String> {
+ if *WINDOWS_8_1_OR_NEWER {
+ get_cmd_line_new(handle)
+ } else {
+ get_cmd_line_old(params, handle)
+ }
+}
+
+fn get_proc_env<T: RtlUserProcessParameters>(params: &T, handle: &HandleWrapper) -> Vec<String> {
+ match params.get_environ(handle) {
+ Ok(buffer) => {
+ let equals = "=".encode_utf16().next().unwrap();
+ let raw_env = buffer;
+ let mut result = Vec::new();
+ let mut begin = 0;
+ while let Some(offset) = raw_env[begin..].iter().position(|&c| c == 0) {
+ let end = begin + offset;
+ if raw_env[begin..end].iter().any(|&c| c == equals) {
+ result.push(
+ OsString::from_wide(&raw_env[begin..end])
+ .to_string_lossy()
+ .into_owned(),
+ );
+ begin = end + 1;
+ } else {
+ break;
+ }
+ }
+ result
+ }
+ Err(_e) => {
+ sysinfo_debug!("get_proc_env failed to get data: {}", _e);
+ Vec::new()
+ }
+ }
+}
+
+pub(crate) fn get_executable_path(_pid: Pid) -> PathBuf {
+ /*let where_req = format!("ProcessId={}", pid);
+
+ if let Some(ret) = run_wmi(&["process", "where", &where_req, "get", "ExecutablePath"]) {
+ for line in ret.lines() {
+ if line.is_empty() || line == "ExecutablePath" {
+ continue
+ }
+ return line.to_owned();
+ }
+ }*/
+ PathBuf::new()
+}
+
+#[inline]
+fn check_sub(a: u64, b: u64) -> u64 {
+ if a < b {
+ a
+ } else {
+ a - b
+ }
+}
+
+/// Before changing this function, you must consider the following:
+/// 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();
+ let mut fsys: FILETIME = zeroed();
+ let mut fuser: FILETIME = zeroed();
+ let mut fglobal_idle_time: FILETIME = zeroed();
+ let mut fglobal_kernel_time: FILETIME = zeroed(); // notice that it includes idle time
+ let mut fglobal_user_time: FILETIME = zeroed();
+
+ if let Some(handle) = p.get_handle() {
+ GetProcessTimes(
+ handle,
+ &mut ftime as *mut FILETIME,
+ &mut ftime as *mut FILETIME,
+ &mut fsys as *mut FILETIME,
+ &mut fuser as *mut FILETIME,
+ );
+ }
+ GetSystemTimes(
+ &mut fglobal_idle_time as *mut FILETIME,
+ &mut fglobal_kernel_time as *mut FILETIME,
+ &mut fglobal_user_time as *mut FILETIME,
+ );
+
+ let mut sys: ULARGE_INTEGER = std::mem::zeroed();
+ memcpy(
+ &mut sys as *mut ULARGE_INTEGER as *mut c_void,
+ &mut fsys as *mut FILETIME as *mut c_void,
+ size_of::<FILETIME>(),
+ );
+ let mut user: ULARGE_INTEGER = std::mem::zeroed();
+ memcpy(
+ &mut user as *mut ULARGE_INTEGER as *mut c_void,
+ &mut fuser as *mut FILETIME as *mut c_void,
+ size_of::<FILETIME>(),
+ );
+ let mut global_kernel_time: ULARGE_INTEGER = std::mem::zeroed();
+ memcpy(
+ &mut global_kernel_time as *mut ULARGE_INTEGER as *mut c_void,
+ &mut fglobal_kernel_time as *mut FILETIME as *mut c_void,
+ size_of::<FILETIME>(),
+ );
+ let mut global_user_time: ULARGE_INTEGER = std::mem::zeroed();
+ memcpy(
+ &mut global_user_time as *mut ULARGE_INTEGER as *mut c_void,
+ &mut fglobal_user_time as *mut FILETIME as *mut c_void,
+ size_of::<FILETIME>(),
+ );
+
+ let sys = *sys.QuadPart();
+ let user = *user.QuadPart();
+ let global_kernel_time = *global_kernel_time.QuadPart();
+ let global_user_time = *global_user_time.QuadPart();
+
+ let delta_global_kernel_time =
+ check_sub(global_kernel_time, p.cpu_calc_values.old_system_sys_cpu);
+ let delta_global_user_time =
+ check_sub(global_user_time, p.cpu_calc_values.old_system_user_cpu);
+ let delta_user_time = check_sub(user, p.cpu_calc_values.old_process_user_cpu);
+ let delta_sys_time = check_sub(sys, p.cpu_calc_values.old_process_sys_cpu);
+
+ p.cpu_calc_values.old_process_user_cpu = user;
+ p.cpu_calc_values.old_process_sys_cpu = sys;
+ p.cpu_calc_values.old_system_user_cpu = global_user_time;
+ p.cpu_calc_values.old_system_sys_cpu = global_kernel_time;
+
+ let denominator = delta_global_user_time.saturating_add(delta_global_kernel_time) as f32;
+
+ if denominator < 0.00001 {
+ p.cpu_usage = 0.;
+ return;
+ }
+
+ p.cpu_usage = 100.0
+ * (delta_user_time.saturating_add(delta_sys_time) as f32 / denominator as f32)
+ * nb_cpus as f32;
+ }
+}
+
+pub(crate) fn update_disk_usage(p: &mut Process) {
+ let mut counters = MaybeUninit::<IO_COUNTERS>::uninit();
+
+ if let Some(handle) = p.get_handle() {
+ unsafe {
+ let ret = GetProcessIoCounters(handle, counters.as_mut_ptr());
+ if ret == 0 {
+ sysinfo_debug!("GetProcessIoCounters call failed on process {}", p.pid());
+ } else {
+ let counters = counters.assume_init();
+ p.old_read_bytes = p.read_bytes;
+ p.old_written_bytes = p.written_bytes;
+ p.read_bytes = counters.ReadTransferCount;
+ p.written_bytes = counters.WriteTransferCount;
+ }
+ }
+ }
+}
+
+pub(crate) fn update_memory(p: &mut Process) {
+ if let Some(handle) = p.get_handle() {
+ unsafe {
+ let mut pmc: PROCESS_MEMORY_COUNTERS_EX = zeroed();
+ if GetProcessMemoryInfo(
+ handle,
+ &mut pmc as *mut PROCESS_MEMORY_COUNTERS_EX as *mut c_void
+ as *mut PROCESS_MEMORY_COUNTERS,
+ size_of::<PROCESS_MEMORY_COUNTERS_EX>() as DWORD,
+ ) != 0
+ {
+ p.memory = (pmc.WorkingSetSize as u64) / 1_000;
+ p.virtual_memory = (pmc.PrivateUsage as u64) / 1_000;
+ }
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/windows/system.rs b/vendor/sysinfo/src/windows/system.rs
new file mode 100644
index 000000000..6abd30db5
--- /dev/null
+++ b/vendor/sysinfo/src/windows/system.rs
@@ -0,0 +1,623 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{
+ CpuRefreshKind, LoadAvg, Networks, Pid, ProcessExt, ProcessRefreshKind, RefreshKind, SystemExt,
+ User,
+};
+use winapi::um::winreg::HKEY_LOCAL_MACHINE;
+
+use crate::sys::component::{self, Component};
+use crate::sys::cpu::*;
+use crate::sys::disk::{get_disks, Disk};
+use crate::sys::process::{update_memory, Process};
+use crate::sys::tools::*;
+use crate::sys::users::get_users;
+use crate::sys::utils::get_now;
+
+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 ntapi::ntexapi::{
+ NtQuerySystemInformation, SystemProcessInformation, SYSTEM_PROCESS_INFORMATION,
+};
+use winapi::ctypes::wchar_t;
+use winapi::shared::minwindef::{DWORD, FALSE, HKEY, LPBYTE, 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};
+use winapi::um::sysinfoapi::{
+ ComputerNamePhysicalDnsHostname, GetComputerNameExW, GetTickCount64, GlobalMemoryStatusEx,
+ MEMORYSTATUSEX,
+};
+use winapi::um::winnt::{HANDLE, KEY_READ};
+use winapi::um::winreg::{RegOpenKeyExW, RegQueryValueExW};
+
+declare_signals! {
+ (),
+ Signal::Kill => (),
+ _ => None,
+}
+
+#[doc = include_str!("../../md_doc/system.md")]
+pub struct System {
+ process_list: HashMap<Pid, Process>,
+ mem_total: u64,
+ mem_available: u64,
+ swap_total: u64,
+ swap_used: u64,
+ cpus: CpusWrapper,
+ components: Vec<Component>,
+ disks: Vec<Disk>,
+ query: Option<Query>,
+ networks: Networks,
+ boot_time: u64,
+ users: Vec<User>,
+}
+
+// Useful for parallel iterations.
+struct Wrap<T>(T);
+
+#[allow(clippy::non_send_fields_in_send_ty)]
+unsafe impl<T> Send for Wrap<T> {}
+unsafe impl<T> Sync for Wrap<T> {}
+
+unsafe fn boot_time() -> u64 {
+ match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
+ Ok(n) => n.as_secs().saturating_sub(GetTickCount64()) / 1000,
+ Err(_e) => {
+ sysinfo_debug!("Failed to compute boot time: {:?}", _e);
+ 0
+ }
+ }
+}
+
+impl SystemExt for System {
+ const IS_SUPPORTED: bool = true;
+ const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals();
+
+ #[allow(non_snake_case)]
+ fn new_with_specifics(refreshes: RefreshKind) -> System {
+ let mut s = System {
+ process_list: HashMap::with_capacity(500),
+ mem_total: 0,
+ mem_available: 0,
+ swap_total: 0,
+ swap_used: 0,
+ cpus: CpusWrapper::new(),
+ components: Vec::new(),
+ disks: Vec::with_capacity(2),
+ query: None,
+ networks: Networks::new(),
+ boot_time: unsafe { boot_time() },
+ users: Vec::new(),
+ };
+ s.refresh_specifics(refreshes);
+ s
+ }
+
+ fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind) {
+ if self.query.is_none() {
+ self.query = Query::new();
+ if let Some(ref mut query) = self.query {
+ add_english_counter(
+ r"\Processor(_Total)\% Processor 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({})\% Processor Time", pos),
+ query,
+ get_key_used(proc_),
+ format!("{}_0", pos),
+ );
+ }
+ }
+ }
+ if let Some(ref mut query) = self.query {
+ query.refresh();
+ let mut used_time = None;
+ if let Some(ref key_used) = *get_key_used(self.cpus.global_cpu_mut()) {
+ used_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);
+ }
+ for p in self.cpus.iter_mut(refresh_kind) {
+ let mut used_time = None;
+ if let Some(ref key_used) = *get_key_used(p) {
+ used_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 refresh_kind.frequency() {
+ self.cpus.get_frequencies();
+ }
+ }
+ }
+
+ fn refresh_memory(&mut self) {
+ unsafe {
+ let mut mem_info: MEMORYSTATUSEX = zeroed();
+ mem_info.dwLength = size_of::<MEMORYSTATUSEX>() as u32;
+ GlobalMemoryStatusEx(&mut mem_info);
+ self.mem_total = auto_cast!(mem_info.ullTotalPhys, u64) / 1_000;
+ self.mem_available = auto_cast!(mem_info.ullAvailPhys, u64) / 1_000;
+ let mut perf_info: PERFORMANCE_INFORMATION = zeroed();
+ if GetPerformanceInfo(&mut perf_info, size_of::<PERFORMANCE_INFORMATION>() as u32)
+ == TRUE
+ {
+ let swap_total = perf_info.PageSize.saturating_mul(
+ perf_info
+ .CommitLimit
+ .saturating_sub(perf_info.PhysicalTotal),
+ );
+ let swap_used = perf_info.PageSize.saturating_mul(
+ perf_info
+ .CommitTotal
+ .saturating_sub(perf_info.PhysicalTotal),
+ );
+ self.swap_total = (swap_total / 1000) as u64;
+ self.swap_used = (swap_used / 1000) as u64;
+ }
+ }
+ }
+
+ fn refresh_components_list(&mut self) {
+ self.components = component::get_components();
+ }
+
+ #[allow(clippy::map_entry)]
+ fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool {
+ if self.process_list.contains_key(&pid) {
+ return refresh_existing_process(self, pid, refresh_kind);
+ }
+ let now = get_now();
+ if let Some(mut p) = Process::new_from_pid(pid, now, refresh_kind) {
+ p.update(refresh_kind, self.cpus.len() as u64, now);
+ p.updated = false;
+ self.process_list.insert(pid, p);
+ true
+ } else {
+ false
+ }
+ }
+
+ #[allow(clippy::cast_ptr_alignment)]
+ fn refresh_processes_specifics(&mut self, refresh_kind: ProcessRefreshKind) {
+ // Windows 10 notebook requires at least 512KiB of memory to make it in one go
+ let mut buffer_size: usize = 512 * 1024;
+ let now = get_now();
+
+ loop {
+ let mut process_information: Vec<u8> = Vec::with_capacity(buffer_size);
+ let mut cb_needed = 0;
+
+ unsafe {
+ process_information.set_len(buffer_size);
+ let ntstatus = NtQuerySystemInformation(
+ SystemProcessInformation,
+ process_information.as_mut_ptr() as PVOID,
+ buffer_size as ULONG,
+ &mut cb_needed,
+ );
+
+ if ntstatus != STATUS_INFO_LENGTH_MISMATCH {
+ if ntstatus < 0 {
+ sysinfo_debug!(
+ "Couldn't get process infos: NtQuerySystemInformation returned {}",
+ ntstatus
+ );
+ }
+
+ // Parse the data block to get process information
+ let mut process_ids = Vec::with_capacity(500);
+ let mut process_information_offset = 0;
+ loop {
+ let p = process_information
+ .as_ptr()
+ .offset(process_information_offset)
+ as *const SYSTEM_PROCESS_INFORMATION;
+ let pi = &*p;
+
+ process_ids.push(Wrap(p));
+
+ if pi.NextEntryOffset == 0 {
+ break;
+ }
+
+ process_information_offset += pi.NextEntryOffset as isize;
+ }
+ let process_list = Wrap(UnsafeCell::new(&mut self.process_list));
+ let nb_cpus = if refresh_kind.cpu() {
+ self.cpus.len() as u64
+ } else {
+ 0
+ };
+
+ #[cfg(feature = "multithread")]
+ use rayon::iter::ParallelIterator;
+
+ // TODO: instead of using parallel iterator only here, would be better to be
+ // able to run it over `process_information` directly!
+ let processes = into_iter(process_ids)
+ .filter_map(|pi| {
+ let pi = *pi.0;
+ let pid = Pid(pi.UniqueProcessId as _);
+ if let Some(proc_) = (*process_list.0.get()).get_mut(&pid) {
+ proc_.memory = (pi.WorkingSetSize as u64) / 1_000;
+ proc_.virtual_memory = (pi.VirtualSize as u64) / 1_000;
+ proc_.update(refresh_kind, nb_cpus, now);
+ return None;
+ }
+ let name = get_process_name(&pi, pid);
+ let mut p = Process::new_full(
+ pid,
+ if pi.InheritedFromUniqueProcessId as usize != 0 {
+ Some(Pid(pi.InheritedFromUniqueProcessId as _))
+ } else {
+ None
+ },
+ (pi.WorkingSetSize as u64) / 1_000,
+ (pi.VirtualSize as u64) / 1_000,
+ name,
+ now,
+ refresh_kind,
+ );
+ p.update(refresh_kind, nb_cpus, now);
+ Some(p)
+ })
+ .collect::<Vec<_>>();
+ for p in processes.into_iter() {
+ self.process_list.insert(p.pid(), p);
+ }
+ self.process_list.retain(|_, v| {
+ let x = v.updated;
+ v.updated = false;
+ x
+ });
+
+ break;
+ }
+
+ // GetNewBufferSize
+ if cb_needed == 0 {
+ buffer_size *= 2;
+ continue;
+ }
+ // allocating a few more kilo bytes just in case there are some new process
+ // kicked in since new call to NtQuerySystemInformation
+ buffer_size = (cb_needed + (1024 * 10)) as usize;
+ }
+ }
+ }
+
+ fn refresh_disks_list(&mut self) {
+ self.disks = unsafe { get_disks() };
+ }
+
+ fn refresh_users_list(&mut self) {
+ self.users = unsafe { get_users() };
+ }
+
+ fn processes(&self) -> &HashMap<Pid, Process> {
+ &self.process_list
+ }
+
+ fn process(&self, pid: Pid) -> Option<&Process> {
+ self.process_list.get(&pid)
+ }
+
+ fn global_cpu_info(&self) -> &Cpu {
+ self.cpus.global_cpu()
+ }
+
+ fn cpus(&self) -> &[Cpu] {
+ self.cpus.cpus()
+ }
+
+ fn physical_core_count(&self) -> Option<usize> {
+ get_physical_core_count()
+ }
+
+ fn total_memory(&self) -> u64 {
+ self.mem_total
+ }
+
+ fn free_memory(&self) -> u64 {
+ // MEMORYSTATUSEX doesn't report free memory
+ self.mem_available
+ }
+
+ fn available_memory(&self) -> u64 {
+ self.mem_available
+ }
+
+ fn used_memory(&self) -> u64 {
+ self.mem_total - self.mem_available
+ }
+
+ fn total_swap(&self) -> u64 {
+ self.swap_total
+ }
+
+ fn free_swap(&self) -> u64 {
+ self.swap_total - self.swap_used
+ }
+
+ fn used_swap(&self) -> u64 {
+ self.swap_used
+ }
+
+ fn components(&self) -> &[Component] {
+ &self.components
+ }
+
+ fn components_mut(&mut self) -> &mut [Component] {
+ &mut self.components
+ }
+
+ fn disks(&self) -> &[Disk] {
+ &self.disks
+ }
+
+ fn disks_mut(&mut self) -> &mut [Disk] {
+ &mut self.disks
+ }
+
+ fn users(&self) -> &[User] {
+ &self.users
+ }
+
+ fn networks(&self) -> &Networks {
+ &self.networks
+ }
+
+ fn networks_mut(&mut self) -> &mut Networks {
+ &mut self.networks
+ }
+
+ fn uptime(&self) -> u64 {
+ unsafe { GetTickCount64() / 1000 }
+ }
+
+ fn boot_time(&self) -> u64 {
+ self.boot_time
+ }
+
+ fn load_average(&self) -> LoadAvg {
+ get_load_average()
+ }
+
+ fn name(&self) -> Option<String> {
+ Some("Windows".to_owned())
+ }
+
+ fn long_os_version(&self) -> Option<String> {
+ get_reg_string_value(
+ HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ "ProductName",
+ )
+ }
+
+ fn host_name(&self) -> Option<String> {
+ get_dns_hostname()
+ }
+
+ fn kernel_version(&self) -> Option<String> {
+ get_reg_string_value(
+ HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ "CurrentBuildNumber",
+ )
+ }
+
+ fn os_version(&self) -> Option<String> {
+ let major = get_reg_value_u32(
+ HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ "CurrentMajorVersionNumber",
+ );
+
+ let build_number = get_reg_string_value(
+ HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ "CurrentBuildNumber",
+ );
+
+ Some(format!(
+ "{} ({})",
+ u32::from_le_bytes(major.unwrap_or_default()),
+ build_number.unwrap_or_default()
+ ))
+ }
+}
+
+impl Default for System {
+ fn default() -> System {
+ System::new()
+ }
+}
+
+fn is_proc_running(handle: HANDLE) -> bool {
+ let mut exit_code = 0;
+ unsafe {
+ let ret = GetExitCodeProcess(handle, &mut exit_code);
+ !(ret == FALSE || exit_code != STILL_ACTIVE)
+ }
+}
+
+fn refresh_existing_process(s: &mut System, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool {
+ if let Some(ref mut entry) = s.process_list.get_mut(&pid) {
+ if let Some(handle) = entry.get_handle() {
+ if !is_proc_running(handle) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ update_memory(entry);
+ entry.update(refresh_kind, s.cpus.len() as u64, get_now());
+ entry.updated = false;
+ true
+ } else {
+ false
+ }
+}
+
+#[allow(clippy::size_of_in_element_count)]
+//^ needed for "name.Length as usize / std::mem::size_of::<u16>()"
+pub(crate) fn get_process_name(process: &SYSTEM_PROCESS_INFORMATION, process_id: Pid) -> String {
+ let name = &process.ImageName;
+ if name.Buffer.is_null() {
+ match process_id.0 {
+ 0 => "Idle".to_owned(),
+ 4 => "System".to_owned(),
+ _ => format!("<no name> Process {}", process_id),
+ }
+ } else {
+ unsafe {
+ let slice = std::slice::from_raw_parts(
+ name.Buffer,
+ // The length is in bytes, not the length of string
+ name.Length as usize / std::mem::size_of::<u16>(),
+ );
+
+ String::from_utf16_lossy(slice)
+ }
+ }
+}
+
+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
+ // setting the `lpBuffer` to null will return the buffer size
+ // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw
+ unsafe {
+ GetComputerNameExW(
+ ComputerNamePhysicalDnsHostname,
+ std::ptr::null_mut(),
+ &mut buffer_size,
+ );
+
+ // Setting the buffer with the new length
+ let mut buffer = vec![0_u16; buffer_size as usize];
+
+ // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ne-sysinfoapi-computer_name_format
+ if GetComputerNameExW(
+ ComputerNamePhysicalDnsHostname,
+ buffer.as_mut_ptr() as *mut wchar_t,
+ &mut buffer_size,
+ ) == TRUE
+ {
+ if let Some(pos) = buffer.iter().position(|c| *c == 0) {
+ buffer.resize(pos, 0);
+ }
+
+ return String::from_utf16(&buffer).ok();
+ }
+ }
+
+ sysinfo_debug!("Failed to get computer hostname");
+ None
+}
diff --git a/vendor/sysinfo/src/windows/tools.rs b/vendor/sysinfo/src/windows/tools.rs
new file mode 100644
index 000000000..a0c334010
--- /dev/null
+++ b/vendor/sysinfo/src/windows/tools.rs
@@ -0,0 +1,55 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::cpu::{self, Cpu, Query};
+use crate::CpuRefreshKind;
+
+use std::mem::zeroed;
+
+use winapi::um::sysinfoapi::{GetSystemInfo, SYSTEM_INFO};
+
+pub(crate) struct KeyHandler {
+ pub unique_id: String,
+}
+
+impl KeyHandler {
+ pub fn new(unique_id: String) -> KeyHandler {
+ KeyHandler { unique_id }
+ }
+}
+
+pub(crate) fn init_cpus(refresh_kind: CpuRefreshKind) -> (Vec<Cpu>, String, String) {
+ unsafe {
+ let mut sys_info: SYSTEM_INFO = zeroed();
+ GetSystemInfo(&mut sys_info);
+ let (vendor_id, brand) = cpu::get_vendor_id_and_brand(&sys_info);
+ let nb_cpus = sys_info.dwNumberOfProcessors as usize;
+ let frequencies = if refresh_kind.frequency() {
+ cpu::get_frequencies(nb_cpus)
+ } else {
+ vec![0; nb_cpus]
+ };
+ let mut ret = Vec::with_capacity(nb_cpus + 1);
+ for (nb, frequency) in frequencies.iter().enumerate() {
+ ret.push(Cpu::new_with_values(
+ format!("CPU {}", nb + 1),
+ vendor_id.clone(),
+ brand.clone(),
+ *frequency,
+ ));
+ }
+ (ret, vendor_id, brand)
+ }
+}
+
+pub(crate) fn add_english_counter(
+ s: String,
+ query: &mut Query,
+ keys: &mut Option<KeyHandler>,
+ counter_name: String,
+) {
+ let mut full = s.encode_utf16().collect::<Vec<_>>();
+ full.push(0);
+ if query.add_english_counter(&counter_name, full) {
+ *keys = Some(KeyHandler::new(counter_name));
+ }
+}
diff --git a/vendor/sysinfo/src/windows/users.rs b/vendor/sysinfo/src/windows/users.rs
new file mode 100644
index 000000000..2fef05c3f
--- /dev/null
+++ b/vendor/sysinfo/src/windows/users.rs
@@ -0,0 +1,181 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::utils::to_str;
+use crate::{
+ common::{Gid, Uid},
+ User,
+};
+
+use std::ptr::null_mut;
+use winapi::shared::lmcons::{MAX_PREFERRED_LENGTH, NET_API_STATUS};
+use winapi::shared::minwindef::DWORD;
+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,
+};
+use winapi::um::lmapibuf::NetApiBufferFree;
+use winapi::um::ntlsa::{
+ LsaEnumerateLogonSessions, LsaFreeReturnBuffer, LsaGetLogonSessionData,
+ PSECURITY_LOGON_SESSION_DATA,
+};
+use winapi::um::winnt::{LPWSTR, PLUID};
+
+// FIXME: once this is mreged in winapi, it can be removed.
+#[allow(non_upper_case_globals)]
+const NERR_Success: NET_API_STATUS = 0;
+
+unsafe fn get_groups_for_user(username: LPWSTR) -> Vec<String> {
+ let mut buf: LPLOCALGROUP_USERS_INFO_0 = null_mut();
+ let mut nb_entries = 0;
+ let mut total_entries = 0;
+ let mut groups;
+
+ let status = NetUserGetLocalGroups(
+ [0u16].as_ptr(),
+ username,
+ 0,
+ LG_INCLUDE_INDIRECT,
+ &mut buf as *mut _ as _,
+ MAX_PREFERRED_LENGTH,
+ &mut nb_entries,
+ &mut total_entries,
+ );
+
+ 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));
+ }
+ }
+ } 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;
+
+ loop {
+ let status = NetUserEnum(
+ null_mut(),
+ 0,
+ FILTER_NORMAL_ACCOUNT,
+ &mut buffer as *mut _ as *mut _,
+ MAX_PREFERRED_LENGTH,
+ &mut nb_read,
+ &mut total,
+ &mut resume_handle as *mut _ as *mut _,
+ );
+ if status == NERR_Success || status == ERROR_MORE_DATA {
+ let entries: &[USER_INFO_0] = std::slice::from_raw_parts(buffer, 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,
+ });
+ }
+ } else {
+ sysinfo_debug!(
+ "NetUserEnum error: {}",
+ if status == winapi::shared::winerror::ERROR_ACCESS_DENIED {
+ "access denied"
+ } else if status == winapi::shared::winerror::ERROR_INVALID_LEVEL {
+ "invalid level"
+ } else {
+ "unknown error"
+ }
+ );
+ }
+ if !buffer.is_null() {
+ NetApiBufferFree(buffer as *mut _);
+ buffer = null_mut();
+ }
+ if status != ERROR_MORE_DATA {
+ break;
+ }
+ }
+
+ // 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 {
+ 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;
+ if data.LogonType == winapi::um::ntlsa::Network {
+ continue;
+ }
+ let name = to_str(data.UserName.Buffer);
+ if users.iter().any(|u| u.name == name) {
+ continue;
+ }
+ users.push(User {
+ uid: Uid(name.clone().into_boxed_str()),
+ 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 _);
+ }
+ }
+ }
+
+ users
+}
diff --git a/vendor/sysinfo/src/windows/utils.rs b/vendor/sysinfo/src/windows/utils.rs
new file mode 100644
index 000000000..419ee195c
--- /dev/null
+++ b/vendor/sysinfo/src/windows/utils.rs
@@ -0,0 +1,36 @@
+// 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 std::time::SystemTime;
+
+#[inline]
+pub(crate) fn filetime_to_u64(f: FILETIME) -> u64 {
+ (f.dwHighDateTime as u64) << 32 | (f.dwLowDateTime as u64)
+}
+
+#[inline]
+pub(crate) fn get_now() -> u64 {
+ SystemTime::now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .map(|n| n.as_secs())
+ .unwrap_or(0)
+}
+
+pub(crate) unsafe fn to_str(p: LPWSTR) -> String {
+ let mut i = 0;
+
+ loop {
+ let c = *p.offset(i);
+ if c == 0 {
+ break;
+ }
+ i += 1;
+ }
+ let s = std::slice::from_raw_parts(p, i as _);
+ String::from_utf16(s).unwrap_or_else(|_e| {
+ sysinfo_debug!("Failed to convert to UTF-16 string: {}", _e);
+ String::new()
+ })
+}