summaryrefslogtreecommitdiffstats
path: root/vendor/sysinfo/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
commit4547b622d8d29df964fa2914213088b148c498fc (patch)
tree9fc6b25f3c3add6b745be9a2400a6e96140046e9 /vendor/sysinfo/src
parentReleasing progress-linux version 1.66.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-4547b622d8d29df964fa2914213088b148c498fc.tar.xz
rustc-4547b622d8d29df964fa2914213088b148c498fc.zip
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/sysinfo/src')
-rw-r--r--vendor/sysinfo/src/apple/app_store/process.rs2
-rw-r--r--vendor/sysinfo/src/apple/cpu.rs4
-rw-r--r--vendor/sysinfo/src/apple/disk.rs351
-rw-r--r--vendor/sysinfo/src/apple/ffi.rs32
-rw-r--r--vendor/sysinfo/src/apple/macos/component/arm.rs179
-rw-r--r--vendor/sysinfo/src/apple/macos/component/mod.rs13
-rw-r--r--vendor/sysinfo/src/apple/macos/component/x86.rs (renamed from vendor/sysinfo/src/apple/macos/component.rs)145
-rw-r--r--vendor/sysinfo/src/apple/macos/disk.rs281
-rw-r--r--vendor/sysinfo/src/apple/macos/ffi.rs295
-rw-r--r--vendor/sysinfo/src/apple/macos/mod.rs3
-rw-r--r--vendor/sysinfo/src/apple/macos/process.rs511
-rw-r--r--vendor/sysinfo/src/apple/macos/utils.rs30
-rw-r--r--vendor/sysinfo/src/apple/mod.rs1
-rw-r--r--vendor/sysinfo/src/apple/network.rs1
-rw-r--r--vendor/sysinfo/src/apple/system.rs176
-rw-r--r--vendor/sysinfo/src/apple/utils.rs34
-rw-r--r--vendor/sysinfo/src/c_interface.rs2
-rw-r--r--vendor/sysinfo/src/common.rs6
-rw-r--r--vendor/sysinfo/src/debug.rs2
-rw-r--r--vendor/sysinfo/src/freebsd/component.rs4
-rw-r--r--vendor/sysinfo/src/freebsd/process.rs57
-rw-r--r--vendor/sysinfo/src/freebsd/system.rs53
-rw-r--r--vendor/sysinfo/src/freebsd/utils.rs6
-rw-r--r--vendor/sysinfo/src/lib.rs50
-rw-r--r--vendor/sysinfo/src/linux/component.rs405
-rw-r--r--vendor/sysinfo/src/linux/cpu.rs208
-rw-r--r--vendor/sysinfo/src/linux/disk.rs22
-rw-r--r--vendor/sysinfo/src/linux/network.rs2
-rw-r--r--vendor/sysinfo/src/linux/process.rs439
-rw-r--r--vendor/sysinfo/src/linux/system.rs268
-rw-r--r--vendor/sysinfo/src/linux/utils.rs130
-rw-r--r--vendor/sysinfo/src/traits.rs164
-rw-r--r--vendor/sysinfo/src/unknown/process.rs2
-rw-r--r--vendor/sysinfo/src/unknown/system.rs11
-rw-r--r--vendor/sysinfo/src/utils.rs18
-rw-r--r--vendor/sysinfo/src/windows/disk.rs36
-rw-r--r--vendor/sysinfo/src/windows/macros.rs16
-rw-r--r--vendor/sysinfo/src/windows/mod.rs2
-rw-r--r--vendor/sysinfo/src/windows/process.rs94
-rw-r--r--vendor/sysinfo/src/windows/system.rs147
40 files changed, 2766 insertions, 1436 deletions
diff --git a/vendor/sysinfo/src/apple/app_store/process.rs b/vendor/sysinfo/src/apple/app_store/process.rs
index 8c3348ee9..7c43697a6 100644
--- a/vendor/sysinfo/src/apple/app_store/process.rs
+++ b/vendor/sysinfo/src/apple/app_store/process.rs
@@ -79,4 +79,6 @@ impl ProcessExt for Process {
fn group_id(&self) -> Option<Gid> {
None
}
+
+ fn wait(&self) {}
}
diff --git a/vendor/sysinfo/src/apple/cpu.rs b/vendor/sysinfo/src/apple/cpu.rs
index b1068e971..e613bdd2c 100644
--- a/vendor/sysinfo/src/apple/cpu.rs
+++ b/vendor/sysinfo/src/apple/cpu.rs
@@ -317,14 +317,14 @@ mod test {
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();
+ let sysctl_value = line.split(':').nth(1).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();
+ let sysctl_value = line.split(':').nth(1).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
index 9f0b4a3a3..d866d5bba 100644
--- a/vendor/sysinfo/src/apple/disk.rs
+++ b/vendor/sysinfo/src/apple/disk.rs
@@ -1,15 +1,23 @@
// Take a look at the license at the top of the repository in the LICENSE file.
-use crate::utils::to_cpath;
+use crate::sys::{
+ ffi,
+ utils::{self, CFReleaser},
+};
use crate::{DiskExt, DiskType};
-#[cfg(target_os = "macos")]
-pub(crate) use crate::sys::inner::disk::*;
+use core_foundation_sys::array::CFArrayCreate;
+use core_foundation_sys::base::kCFAllocatorDefault;
+use core_foundation_sys::dictionary::{CFDictionaryGetValueIfPresent, CFDictionaryRef};
+use core_foundation_sys::number::{kCFBooleanTrue, CFBooleanRef, CFNumberGetValue};
+use core_foundation_sys::string::{self as cfs, CFStringRef};
-use libc::statfs;
-use std::ffi::{OsStr, OsString};
-use std::mem;
+use libc::c_void;
+
+use std::ffi::{CStr, OsStr, OsString};
+use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
+use std::ptr;
#[doc = include_str!("../../md_doc/disk.md")]
pub struct Disk {
@@ -17,6 +25,7 @@ pub struct Disk {
pub(crate) name: OsString,
pub(crate) file_system: Vec<u8>,
pub(crate) mount_point: PathBuf,
+ volume_url: RetainedCFURL,
pub(crate) total_space: u64,
pub(crate) available_space: u64,
pub(crate) is_removable: bool,
@@ -53,14 +62,334 @@ impl DiskExt for Disk {
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
+ if let Some(requested_properties) = build_requested_properties(&[
+ ffi::kCFURLVolumeAvailableCapacityKey,
+ ffi::kCFURLVolumeAvailableCapacityForImportantUsageKey,
+ ]) {
+ match get_disk_properties(&self.volume_url, &requested_properties) {
+ Some(disk_props) => {
+ self.available_space = get_available_volume_space(&disk_props);
+ true
+ }
+ None => false,
+ }
} else {
+ sysinfo_debug!("failed to create volume key list, skipping refresh");
false
}
}
}
}
+
+pub(super) unsafe fn get_disks() -> Vec<Disk> {
+ let raw_disks = {
+ let count = libc::getfsstat(ptr::null_mut(), 0, libc::MNT_NOWAIT);
+ if count < 1 {
+ return Vec::new();
+ }
+ let bufsize = count * std::mem::size_of::<libc::statfs>() as libc::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 usize);
+
+ disks
+ };
+
+ // Create a list of properties about the disk that we want to fetch.
+ let requested_properties = match build_requested_properties(&[
+ ffi::kCFURLVolumeIsEjectableKey,
+ ffi::kCFURLVolumeIsRemovableKey,
+ ffi::kCFURLVolumeIsInternalKey,
+ ffi::kCFURLVolumeTotalCapacityKey,
+ ffi::kCFURLVolumeAvailableCapacityForImportantUsageKey,
+ ffi::kCFURLVolumeAvailableCapacityKey,
+ ffi::kCFURLVolumeNameKey,
+ ffi::kCFURLVolumeIsBrowsableKey,
+ ffi::kCFURLVolumeIsLocalKey,
+ ]) {
+ Some(properties) => properties,
+ None => {
+ sysinfo_debug!("failed to create volume key list");
+ return Vec::new();
+ }
+ };
+
+ let mut disks = Vec::with_capacity(raw_disks.len());
+ for c_disk in raw_disks {
+ let volume_url = match CFReleaser::new(
+ core_foundation_sys::url::CFURLCreateFromFileSystemRepresentation(
+ kCFAllocatorDefault,
+ c_disk.f_mntonname.as_ptr() as *const _,
+ c_disk.f_mntonname.len() as _,
+ false as _,
+ ),
+ ) {
+ Some(url) => url,
+ None => {
+ sysinfo_debug!("getfsstat returned incompatible paths");
+ continue;
+ }
+ };
+
+ let prop_dict = match get_disk_properties(&volume_url, &requested_properties) {
+ Some(props) => props,
+ None => continue,
+ };
+
+ // Future note: There is a difference between `kCFURLVolumeIsBrowsableKey` and the
+ // `kCFURLEnumeratorSkipInvisibles` option of `CFURLEnumeratorOptions`. Specifically,
+ // the first one considers the writable `Data`(`/System/Volumes/Data`) partition to be
+ // browsable, while it is classified as "invisible" by CoreFoundation's volume emumerator.
+ let browsable = get_bool_value(
+ prop_dict.inner(),
+ DictKey::Extern(ffi::kCFURLVolumeIsBrowsableKey),
+ )
+ .unwrap_or_default();
+
+ // Do not return invisible "disks". Most of the time, these are APFS snapshots, hidden
+ // system volumes, etc. Browsable is defined to be visible in the system's UI like Finder,
+ // disk utility, system information, etc.
+ //
+ // To avoid seemingly duplicating many disks and creating an inaccurate view of the system's resources,
+ // these are skipped entirely.
+ if !browsable {
+ continue;
+ }
+
+ let local_only = get_bool_value(
+ prop_dict.inner(),
+ DictKey::Extern(ffi::kCFURLVolumeIsLocalKey),
+ )
+ .unwrap_or(true);
+
+ // Skip any drive that is not locally attached to the system.
+ //
+ // This includes items like SMB mounts, and matches the other platform's behavior.
+ if !local_only {
+ continue;
+ }
+
+ let mount_point = PathBuf::from(OsStr::from_bytes(
+ CStr::from_ptr(c_disk.f_mntonname.as_ptr()).to_bytes(),
+ ));
+
+ disks.extend(new_disk(mount_point, volume_url, c_disk, &prop_dict))
+ }
+
+ disks
+}
+
+type RetainedCFArray = CFReleaser<core_foundation_sys::array::__CFArray>;
+type RetainedCFDictionary = CFReleaser<core_foundation_sys::dictionary::__CFDictionary>;
+type RetainedCFURL = CFReleaser<core_foundation_sys::url::__CFURL>;
+
+unsafe fn build_requested_properties(properties: &[CFStringRef]) -> Option<RetainedCFArray> {
+ CFReleaser::new(CFArrayCreate(
+ ptr::null_mut(),
+ properties.as_ptr() as *const *const c_void,
+ properties.len() as _,
+ &core_foundation_sys::array::kCFTypeArrayCallBacks,
+ ))
+}
+
+fn get_disk_properties(
+ volume_url: &RetainedCFURL,
+ requested_properties: &RetainedCFArray,
+) -> Option<RetainedCFDictionary> {
+ CFReleaser::new(unsafe {
+ ffi::CFURLCopyResourcePropertiesForKeys(
+ volume_url.inner(),
+ requested_properties.inner(),
+ ptr::null_mut(),
+ )
+ })
+}
+
+fn get_available_volume_space(disk_props: &RetainedCFDictionary) -> u64 {
+ // We prefer `AvailableCapacityForImportantUsage` over `AvailableCapacity` because
+ // it takes more of the system's properties into account, like the trash, system-managed caches,
+ // etc. It generally also returns higher values too, because of the above, so it's a more accurate
+ // representation of what the system _could_ still use.
+ unsafe {
+ get_int_value(
+ disk_props.inner(),
+ DictKey::Extern(ffi::kCFURLVolumeAvailableCapacityForImportantUsageKey),
+ )
+ .filter(|bytes| *bytes != 0)
+ .or_else(|| {
+ get_int_value(
+ disk_props.inner(),
+ DictKey::Extern(ffi::kCFURLVolumeAvailableCapacityKey),
+ )
+ })
+ }
+ .unwrap_or_default() as u64
+}
+
+pub(super) enum DictKey {
+ Extern(CFStringRef),
+ #[cfg(target_os = "macos")]
+ Defined(&'static str),
+}
+
+unsafe fn get_dict_value<T, F: FnOnce(*const c_void) -> Option<T>>(
+ dict: CFDictionaryRef,
+ key: DictKey,
+ callback: F,
+) -> Option<T> {
+ #[cfg(target_os = "macos")]
+ let _defined;
+ let key = match key {
+ DictKey::Extern(val) => val,
+ #[cfg(target_os = "macos")]
+ DictKey::Defined(val) => {
+ _defined = CFReleaser::new(cfs::CFStringCreateWithBytesNoCopy(
+ kCFAllocatorDefault,
+ val.as_ptr(),
+ val.len() as _,
+ cfs::kCFStringEncodingUTF8,
+ false as _,
+ core_foundation_sys::base::kCFAllocatorNull,
+ ))?;
+
+ _defined.inner()
+ }
+ };
+
+ let mut value = std::ptr::null();
+ if CFDictionaryGetValueIfPresent(dict, key.cast(), &mut value) != 0 {
+ callback(value)
+ } else {
+ None
+ }
+}
+
+pub(super) unsafe fn get_str_value(dict: CFDictionaryRef, key: DictKey) -> Option<String> {
+ get_dict_value(dict, key, |v| {
+ let v = v as cfs::CFStringRef;
+
+ let len_utf16 = cfs::CFStringGetLength(v) as usize;
+ let len_bytes = len_utf16 * 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: DictKey) -> Option<bool> {
+ get_dict_value(dict, key, |v| Some(v as CFBooleanRef == kCFBooleanTrue))
+}
+
+unsafe fn get_int_value(dict: CFDictionaryRef, key: DictKey) -> Option<i64> {
+ get_dict_value(dict, key, |v| {
+ let mut val: i64 = 0;
+ if CFNumberGetValue(
+ v.cast(),
+ core_foundation_sys::number::kCFNumberSInt64Type,
+ &mut val as *mut i64 as *mut c_void,
+ ) {
+ Some(val)
+ } else {
+ None
+ }
+ })
+}
+
+unsafe fn new_disk(
+ mount_point: PathBuf,
+ volume_url: RetainedCFURL,
+ c_disk: libc::statfs,
+ disk_props: &RetainedCFDictionary,
+) -> Option<Disk> {
+ // IOKit is not available on any but the most recent (16+) iOS and iPadOS versions.
+ // Due to this, we can't query the medium type. All iOS devices use flash-based storage
+ // so we just assume the disk type is an SSD until Rust has a way to conditionally link to
+ // IOKit in more recent deployment versions.
+ #[cfg(target_os = "macos")]
+ let type_ = crate::sys::inner::disk::get_disk_type(&c_disk).unwrap_or(DiskType::Unknown(-1));
+ #[cfg(not(target_os = "macos"))]
+ let type_ = DiskType::SSD;
+
+ // Note: Since we requested these properties from the system, we don't expect
+ // these property retrievals to fail.
+
+ let name = get_str_value(
+ disk_props.inner(),
+ DictKey::Extern(ffi::kCFURLVolumeNameKey),
+ )
+ .map(OsString::from)?;
+
+ let is_removable = {
+ let ejectable = get_bool_value(
+ disk_props.inner(),
+ DictKey::Extern(ffi::kCFURLVolumeIsEjectableKey),
+ )
+ .unwrap_or_default();
+
+ let removable = get_bool_value(
+ disk_props.inner(),
+ DictKey::Extern(ffi::kCFURLVolumeIsRemovableKey),
+ )
+ .unwrap_or_default();
+
+ let is_removable = ejectable || removable;
+
+ if is_removable {
+ is_removable
+ } else {
+ // If neither `ejectable` or `removable` return `true`, fallback to checking
+ // if the disk is attached to the internal system.
+ let internal = get_bool_value(
+ disk_props.inner(),
+ DictKey::Extern(ffi::kCFURLVolumeIsInternalKey),
+ )
+ .unwrap_or_default();
+
+ !internal
+ }
+ };
+
+ let total_space = get_int_value(
+ disk_props.inner(),
+ DictKey::Extern(ffi::kCFURLVolumeTotalCapacityKey),
+ )? as u64;
+
+ let available_space = get_available_volume_space(disk_props);
+
+ let file_system = IntoIterator::into_iter(c_disk.f_fstypename)
+ .filter_map(|b| if b != 0 { Some(b as u8) } else { None })
+ .collect();
+
+ Some(Disk {
+ type_,
+ name,
+ file_system,
+ mount_point,
+ volume_url,
+ total_space,
+ available_space,
+ is_removable,
+ })
+}
diff --git a/vendor/sysinfo/src/apple/ffi.rs b/vendor/sysinfo/src/apple/ffi.rs
index 7a8248537..72822202f 100644
--- a/vendor/sysinfo/src/apple/ffi.rs
+++ b/vendor/sysinfo/src/apple/ffi.rs
@@ -1,21 +1,31 @@
// Take a look at the license at the top of the repository in the LICENSE file.
-use libc::c_void;
+use core_foundation_sys::{
+ array::CFArrayRef, dictionary::CFDictionaryRef, error::CFErrorRef, string::CFStringRef,
+ url::CFURLRef,
+};
// 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);
+#[link(name = "CoreFoundation", kind = "framework")]
+extern "C" {
+ pub fn CFURLCopyResourcePropertiesForKeys(
+ url: CFURLRef,
+ keys: CFArrayRef,
+ error: *mut CFErrorRef,
+ ) -> CFDictionaryRef;
-// #[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;
+ pub static kCFURLVolumeIsEjectableKey: CFStringRef;
+ pub static kCFURLVolumeIsRemovableKey: CFStringRef;
+ pub static kCFURLVolumeAvailableCapacityKey: CFStringRef;
+ pub static kCFURLVolumeAvailableCapacityForImportantUsageKey: CFStringRef;
+ pub static kCFURLVolumeTotalCapacityKey: CFStringRef;
+ pub static kCFURLVolumeNameKey: CFStringRef;
+ pub static kCFURLVolumeIsLocalKey: CFStringRef;
+ pub static kCFURLVolumeIsInternalKey: CFStringRef;
+ pub static kCFURLVolumeIsBrowsableKey: CFStringRef;
+}
#[cfg_attr(feature = "debug", derive(Eq, Hash, PartialEq))]
#[derive(Clone)]
diff --git a/vendor/sysinfo/src/apple/macos/component/arm.rs b/vendor/sysinfo/src/apple/macos/component/arm.rs
new file mode 100644
index 000000000..328ffebfa
--- /dev/null
+++ b/vendor/sysinfo/src/apple/macos/component/arm.rs
@@ -0,0 +1,179 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use std::ffi::CStr;
+
+use core_foundation_sys::array::{CFArrayGetCount, CFArrayGetValueAtIndex};
+use core_foundation_sys::base::{kCFAllocatorDefault, CFRetain};
+use core_foundation_sys::string::{
+ kCFStringEncodingUTF8, CFStringCreateWithBytes, CFStringGetCStringPtr,
+};
+
+use crate::apple::inner::ffi::{
+ kHIDPage_AppleVendor, kHIDUsage_AppleVendor_TemperatureSensor, kIOHIDEventTypeTemperature,
+ matching, IOHIDEventFieldBase, IOHIDEventGetFloatValue, IOHIDEventSystemClientCopyServices,
+ IOHIDEventSystemClientCreate, IOHIDEventSystemClientSetMatching, IOHIDServiceClientCopyEvent,
+ IOHIDServiceClientCopyProperty, __IOHIDEventSystemClient, __IOHIDServiceClient,
+ HID_DEVICE_PROPERTY_PRODUCT,
+};
+use crate::sys::utils::CFReleaser;
+use crate::ComponentExt;
+
+pub(crate) struct Components {
+ pub inner: Vec<Component>,
+ client: Option<CFReleaser<__IOHIDEventSystemClient>>,
+}
+
+impl Components {
+ pub(crate) fn new() -> Self {
+ Self {
+ inner: vec![],
+ client: None,
+ }
+ }
+
+ pub(crate) fn refresh(&mut self) {
+ self.inner.clear();
+
+ unsafe {
+ let matches = match CFReleaser::new(matching(
+ kHIDPage_AppleVendor,
+ kHIDUsage_AppleVendor_TemperatureSensor,
+ )) {
+ Some(m) => m,
+ None => return,
+ };
+
+ if self.client.is_none() {
+ let client =
+ match CFReleaser::new(IOHIDEventSystemClientCreate(kCFAllocatorDefault)) {
+ Some(c) => c,
+ None => return,
+ };
+ // Without this call, client is freed during the execution of the program. It must be kept!
+ CFRetain(client.inner() as _);
+ self.client = Some(client);
+ }
+
+ let client = self.client.as_ref().unwrap();
+
+ let _ = IOHIDEventSystemClientSetMatching(client.inner(), matches.inner());
+
+ let services = match CFReleaser::new(IOHIDEventSystemClientCopyServices(client.inner()))
+ {
+ Some(s) => s,
+ None => return,
+ };
+
+ let key_ref = match CFReleaser::new(CFStringCreateWithBytes(
+ kCFAllocatorDefault,
+ HID_DEVICE_PROPERTY_PRODUCT.as_ptr(),
+ HID_DEVICE_PROPERTY_PRODUCT.len() as _,
+ kCFStringEncodingUTF8,
+ false as _,
+ )) {
+ Some(r) => r,
+ None => return,
+ };
+
+ let count = CFArrayGetCount(services.inner());
+
+ for i in 0..count {
+ let service = match CFReleaser::new(
+ CFArrayGetValueAtIndex(services.inner(), i) as *const _
+ ) {
+ Some(s) => s,
+ None => continue,
+ };
+
+ let name = match CFReleaser::new(IOHIDServiceClientCopyProperty(
+ service.inner(),
+ key_ref.inner(),
+ )) {
+ Some(n) => n,
+ None => continue,
+ };
+
+ let name_ptr =
+ CFStringGetCStringPtr(name.inner() as *const _, kCFStringEncodingUTF8);
+ let name_str = CStr::from_ptr(name_ptr).to_string_lossy().to_string();
+
+ let mut component = Component::new(name_str, None, None, service);
+ component.refresh();
+
+ self.inner.push(component);
+ }
+ }
+ }
+}
+
+unsafe impl Send for Components {}
+unsafe impl Sync for Components {}
+
+#[doc = include_str!("../../../../md_doc/component.md")]
+pub struct Component {
+ service: CFReleaser<__IOHIDServiceClient>,
+ temperature: f32,
+ label: String,
+ max: f32,
+ critical: Option<f32>,
+}
+
+impl Component {
+ pub(crate) fn new(
+ label: String,
+ max: Option<f32>,
+ critical: Option<f32>,
+ service: CFReleaser<__IOHIDServiceClient>,
+ ) -> Self {
+ Self {
+ service,
+ label,
+ max: max.unwrap_or(0.),
+ critical,
+ temperature: 0.,
+ }
+ }
+}
+
+unsafe impl Send for Component {}
+unsafe impl Sync for Component {}
+
+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) {
+ unsafe {
+ let event = match CFReleaser::new(IOHIDServiceClientCopyEvent(
+ self.service.inner() as *const _,
+ kIOHIDEventTypeTemperature,
+ 0,
+ 0,
+ )) {
+ Some(e) => e,
+ None => return,
+ };
+
+ self.temperature = IOHIDEventGetFloatValue(
+ event.inner(),
+ IOHIDEventFieldBase(kIOHIDEventTypeTemperature),
+ ) as _;
+ if self.temperature > self.max {
+ self.max = self.temperature;
+ }
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/apple/macos/component/mod.rs b/vendor/sysinfo/src/apple/macos/component/mod.rs
new file mode 100644
index 000000000..50b359e61
--- /dev/null
+++ b/vendor/sysinfo/src/apple/macos/component/mod.rs
@@ -0,0 +1,13 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub(crate) mod x86;
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub use self::x86::*;
+
+#[cfg(target_arch = "aarch64")]
+pub(crate) mod arm;
+
+#[cfg(target_arch = "aarch64")]
+pub use self::arm::*;
diff --git a/vendor/sysinfo/src/apple/macos/component.rs b/vendor/sysinfo/src/apple/macos/component/x86.rs
index 384efb950..415f90455 100644
--- a/vendor/sysinfo/src/apple/macos/component.rs
+++ b/vendor/sysinfo/src/apple/macos/component/x86.rs
@@ -1,13 +1,13 @@
// Take a look at the license at the top of the repository in the LICENSE file.
-use crate::sys::ffi;
+use crate::sys::{ffi, macos::utils::IOReleaser};
use crate::ComponentExt;
use libc::{c_char, c_int, c_void};
use std::mem;
-pub(crate) const COMPONENTS_TEMPERATURE_IDS: &[(&str, &[i8])] = &[
+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"
(
@@ -21,33 +21,69 @@ pub(crate) const COMPONENTS_TEMPERATURE_IDS: &[(&str, &[i8])] = &[
pub(crate) struct ComponentFFI {
input_structure: ffi::KeyData_t,
val: ffi::Val_t,
+ /// It is the `System::connection`. We need it to not require an extra argument
+ /// in `ComponentExt::refresh`.
+ connection: ffi::io_connect_t,
}
impl ComponentFFI {
- fn new(key: &[i8], con: ffi::io_connect_t) -> Option<ComponentFFI> {
+ fn new(key: &[i8], connection: ffi::io_connect_t) -> Option<ComponentFFI> {
unsafe {
- get_key_size(con, key)
+ get_key_size(connection, key)
.ok()
.map(|(input_structure, val)| ComponentFFI {
input_structure,
val,
+ connection,
})
}
}
- fn temperature(&self, con: ffi::io_connect_t) -> Option<f32> {
- get_temperature_inner(con, &self.input_structure, &self.val)
+ fn temperature(&self) -> Option<f32> {
+ get_temperature_inner(self.connection, &self.input_structure, &self.val)
+ }
+}
+
+/// Used to get CPU information, not supported on iOS, or inside the default macOS sandbox.
+pub(crate) struct Components {
+ pub inner: Vec<Component>,
+ connection: Option<IoService>,
+}
+
+impl Components {
+ pub(crate) fn new() -> Self {
+ Self {
+ inner: Vec::with_capacity(2),
+ connection: IoService::new_connection(),
+ }
+ }
+
+ pub(crate) fn refresh(&mut self) {
+ if let Some(ref connection) = self.connection {
+ let connection = connection.inner();
+ self.inner.clear();
+ // getting CPU critical temperature
+ let critical_temp =
+ get_temperature(connection, &['T' as i8, 'C' as i8, '0' as i8, 'D' as i8, 0]);
+
+ for (id, v) in COMPONENTS_TEMPERATURE_IDS.iter() {
+ if let Some(c) =
+ Component::new((*id).to_owned(), None, critical_temp, v, connection)
+ {
+ self.inner.push(c);
+ }
+ }
+ }
}
}
-#[doc = include_str!("../../../md_doc/component.md")]
+#[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 {
@@ -60,16 +96,13 @@ impl Component {
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,
- })
+ ffi_part.temperature().map(|temperature| Component {
+ temperature,
+ label,
+ max: max.unwrap_or(temperature),
+ critical,
+ ffi_part,
+ })
}
}
@@ -91,7 +124,7 @@ impl ComponentExt for Component {
}
fn refresh(&mut self) {
- if let Some(temp) = self.ffi_part.temperature(self.connection) {
+ if let Some(temp) = self.ffi_part.temperature() {
self.temperature = temp;
if self.temperature > self.max {
self.max = self.temperature;
@@ -213,9 +246,81 @@ fn get_temperature_inner(
None
}
-pub(crate) fn get_temperature(con: ffi::io_connect_t, key: &[i8]) -> Option<f32> {
+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)
}
}
+
+pub(crate) struct IoService(ffi::io_connect_t);
+
+impl IoService {
+ fn new(obj: ffi::io_connect_t) -> Option<Self> {
+ if obj == 0 {
+ None
+ } else {
+ Some(Self(obj))
+ }
+ }
+
+ pub(crate) fn inner(&self) -> ffi::io_connect_t {
+ self.0
+ }
+
+ // code from https://github.com/Chris911/iStats
+ // Not supported on iOS, or in the default macOS
+ pub(crate) fn new_connection() -> Option<Self> {
+ let mut iterator: ffi::io_iterator_t = 0;
+
+ unsafe {
+ let matching_dictionary = ffi::IOServiceMatching(b"AppleSMC\0".as_ptr() as *const i8);
+ let result = ffi::IOServiceGetMatchingServices(
+ ffi::kIOMasterPortDefault,
+ matching_dictionary,
+ &mut iterator,
+ );
+ if result != ffi::KIO_RETURN_SUCCESS {
+ sysinfo_debug!("Error: IOServiceGetMatchingServices() = {}", result);
+ return None;
+ }
+ let iterator = match IOReleaser::new(iterator) {
+ Some(i) => i,
+ None => {
+ sysinfo_debug!("Error: IOServiceGetMatchingServices() succeeded but returned invalid descriptor");
+ return None;
+ }
+ };
+
+ let device = match IOReleaser::new(ffi::IOIteratorNext(iterator.inner())) {
+ Some(d) => d,
+ None => {
+ sysinfo_debug!("Error: no SMC found");
+ return None;
+ }
+ };
+
+ let mut conn = 0;
+ let result = ffi::IOServiceOpen(device.inner(), libc::mach_task_self(), 0, &mut conn);
+ if result != ffi::KIO_RETURN_SUCCESS {
+ sysinfo_debug!("Error: IOServiceOpen() = {}", result);
+ return None;
+ }
+ let conn = IoService::new(conn);
+ if conn.is_none() {
+ sysinfo_debug!(
+ "Error: IOServiceOpen() succeeded but returned invalid descriptor..."
+ );
+ }
+ conn
+ }
+ }
+}
+
+impl Drop for IoService {
+ fn drop(&mut self) {
+ unsafe {
+ ffi::IOServiceClose(self.0);
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/apple/macos/disk.rs b/vendor/sysinfo/src/apple/macos/disk.rs
index 7cc5a83ce..3a4372a2f 100644
--- a/vendor/sysinfo/src/apple/macos/disk.rs
+++ b/vendor/sysinfo/src/apple/macos/disk.rs
@@ -1,199 +1,126 @@
// 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 crate::sys::ffi;
+use crate::sys::{
+ disk::{get_str_value, DictKey},
+ macos::utils::IOReleaser,
+ utils::CFReleaser,
+};
+use crate::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::base::{kCFAllocatorDefault, kCFAllocatorNull};
use core_foundation_sys::string as cfs;
-use libc::{c_char, c_int, c_void, statfs};
+use std::ffi::CStr;
-use std::ffi::{OsStr, OsString};
-use std::mem;
-use std::os::unix::ffi::OsStrExt;
-use std::path::PathBuf;
-use std::ptr;
+pub(crate) fn get_disk_type(disk: &libc::statfs) -> Option<DiskType> {
+ let characteristics_string = unsafe {
+ CFReleaser::new(cfs::CFStringCreateWithBytesNoCopy(
+ kCFAllocatorDefault,
+ ffi::kIOPropertyDeviceCharacteristicsKey.as_ptr(),
+ ffi::kIOPropertyDeviceCharacteristicsKey.len() as _,
+ cfs::kCFStringEncodingUTF8,
+ false as _,
+ kCFAllocatorNull,
+ ))?
+ };
-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))
- }
-}
+ // Removes `/dev/` from the value.
+ let bsd_name = unsafe {
+ CStr::from_ptr(disk.f_mntfromname.as_ptr())
+ .to_bytes()
+ .strip_prefix(b"/dev/")
+ .or_else(|| {
+ sysinfo_debug!("unknown disk mount path format");
+ None
+ })?
+ };
+
+ // We don't need to wrap this in an auto-releaser because the following call to `IOServiceGetMatchingServices`
+ // will take ownership of one retain reference.
+ let matching =
+ unsafe { ffi::IOBSDNameMatching(ffi::kIOMasterPortDefault, 0, bsd_name.as_ptr().cast()) };
-pub(crate) fn get_disks(session: ffi::DASessionRef) -> Vec<Disk> {
- if session.is_null() {
- return Vec::new();
+ if matching.is_null() {
+ return None;
}
- 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<_>>()
+ let mut service_iterator: ffi::io_iterator_t = 0;
+
+ if unsafe {
+ ffi::IOServiceGetMatchingServices(
+ ffi::kIOMasterPortDefault,
+ matching.cast(),
+ &mut service_iterator,
+ )
+ } != libc::KERN_SUCCESS
+ {
+ return None;
}
-}
-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
-}
+ // Safety: We checked for success, so there is always a valid iterator, even if its empty.
+ let service_iterator = unsafe { IOReleaser::new_unchecked(service_iterator) };
-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
+ let mut parent_entry: ffi::io_registry_entry_t = 0;
+
+ while let Some(mut current_service_entry) =
+ IOReleaser::new(unsafe { ffi::IOIteratorNext(service_iterator.inner()) })
+ {
+ // Note: This loop is required in a non-obvious way. Due to device properties existing as a tree
+ // in IOKit, we may need an arbitrary number of calls to `IORegistryEntryCreateCFProperty` in order to find
+ // the values we are looking for. The function may return nothing if we aren't deep enough into the registry
+ // tree, so we need to continue going from child->parent node until its found.
+ loop {
+ if unsafe {
+ ffi::IORegistryEntryGetParentEntry(
+ current_service_entry.inner(),
+ ffi::kIOServicePlane.as_ptr().cast(),
+ &mut parent_entry,
+ )
+ } != libc::KERN_SUCCESS
+ {
+ break;
}
- } 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))
-}
+ current_service_entry = match IOReleaser::new(parent_entry) {
+ Some(service) => service,
+ // There were no more parents left
+ None => break,
+ };
-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;
- }
+ let properties_result = unsafe {
+ CFReleaser::new(ffi::IORegistryEntryCreateCFProperty(
+ current_service_entry.inner(),
+ characteristics_string.inner(),
+ kCFAllocatorDefault,
+ 0,
+ ))
+ };
+
+ if let Some(device_properties) = properties_result {
+ let disk_type = unsafe {
+ super::disk::get_str_value(
+ device_properties.inner(),
+ DictKey::Defined(ffi::kIOPropertyMediumTypeKey),
+ )
+ };
- 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;
+ if let Some(disk_type) = disk_type.and_then(|medium| match medium.as_str() {
+ _ if medium == ffi::kIOPropertyMediumTypeSolidStateKey => Some(DiskType::SSD),
+ _ if medium == ffi::kIOPropertyMediumTypeRotationalKey => Some(DiskType::HDD),
+ _ => None,
+ }) {
+ return Some(disk_type);
+ } else {
+ // Many external drive vendors do not advertise their device's storage medium.
+ //
+ // In these cases, assuming that there were _any_ properties about them registered, we fallback
+ // to `HDD` when no storage medium is provided by the device instead of `Unknown`.
+ return Some(DiskType::HDD);
}
- 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,
- })
}
+
+ None
}
diff --git a/vendor/sysinfo/src/apple/macos/ffi.rs b/vendor/sysinfo/src/apple/macos/ffi.rs
index f884701d9..0b9c82cfa 100644
--- a/vendor/sysinfo/src/apple/macos/ffi.rs
+++ b/vendor/sysinfo/src/apple/macos/ffi.rs
@@ -1,105 +1,111 @@
// 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 core_foundation_sys::base::{mach_port_t, CFAllocatorRef};
+use core_foundation_sys::dictionary::{CFDictionaryRef, CFMutableDictionaryRef};
+use core_foundation_sys::string::CFStringRef;
-use libc::{c_char, c_void};
-#[cfg(not(feature = "apple-sandbox"))]
-use libc::{mach_port_t, size_t};
+use libc::{c_char, kern_return_t};
-pub(crate) use crate::sys::ffi::*;
+// Note: IOKit is only available on MacOS up until very recent iOS versions: https://developer.apple.com/documentation/iokit
-#[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.
+#[allow(non_camel_case_types)]
+pub type io_object_t = mach_port_t;
+
+#[allow(non_camel_case_types)]
+pub type io_iterator_t = io_object_t;
+#[allow(non_camel_case_types)]
+pub type io_registry_entry_t = io_object_t;
+#[allow(non_camel_case_types)]
+pub type io_name_t = *const c_char;
- // 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 type IOOptionBits = u32;
- pub fn IOServiceMatching(a: *const c_char) -> *mut c_void;
+#[allow(non_upper_case_globals)]
+pub const kIOServicePlane: &str = "IOService\0";
+#[allow(non_upper_case_globals)]
+pub const kIOPropertyDeviceCharacteristicsKey: &str = "Device Characteristics";
+#[allow(non_upper_case_globals)]
+pub const kIOPropertyMediumTypeKey: &str = "Medium Type";
+#[allow(non_upper_case_globals)]
+pub const kIOPropertyMediumTypeSolidStateKey: &str = "Solid State";
+#[allow(non_upper_case_globals)]
+pub const kIOPropertyMediumTypeRotationalKey: &str = "Rotational";
+// Note: Obtaining information about disks using IOKIt is allowed inside the default macOS App Sandbox.
+#[link(name = "IOKit", kind = "framework")]
+extern "C" {
pub fn IOServiceGetMatchingServices(
- a: mach_port_t,
- b: *mut c_void,
- c: *mut io_iterator_t,
- ) -> i32;
+ mainPort: mach_port_t,
+ matching: CFMutableDictionaryRef,
+ existing: *mut io_iterator_t,
+ ) -> kern_return_t;
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;
-}
+ pub fn IOObjectRelease(obj: io_object_t) -> 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(
+ pub fn IORegistryEntryCreateCFProperty(
+ entry: io_registry_entry_t,
+ key: CFStringRef,
allocator: CFAllocatorRef,
- session: DASessionRef,
- path: *const c_char,
- ) -> DADiskRef;
- // pub fn DADiskGetBSDName(disk: DADiskRef) -> *const c_char;
-
- pub fn DADiskCopyDescription(disk: DADiskRef) -> CFMutableDictionaryRef;
-}
+ options: IOOptionBits,
+ ) -> CFDictionaryRef;
+ pub fn IORegistryEntryGetParentEntry(
+ entry: io_registry_entry_t,
+ plane: io_name_t,
+ parent: *mut io_registry_entry_t,
+ ) -> kern_return_t;
-pub type DADiskRef = *const __DADisk;
-pub type DASessionRef = *const __DASession;
+ pub fn IOBSDNameMatching(
+ mainPort: mach_port_t,
+ options: u32,
+ bsdName: *const c_char,
+ ) -> CFMutableDictionaryRef;
-// 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 {}
+ // This is deprecated as of macOS 12.0, but Rust doesn't have a good way to only use the replacement on 12+.
+ pub static kIOMasterPortDefault: mach_port_t;
+}
-#[cfg(not(feature = "apple-sandbox"))]
+#[cfg(all(
+ not(feature = "apple-sandbox"),
+ any(target_arch = "x86", target_arch = "x86_64")
+))]
mod io_service {
- use super::mach_port_t;
+ use super::{io_object_t, mach_port_t};
+ use core_foundation_sys::dictionary::CFMutableDictionaryRef;
+ use libc::{c_char, kern_return_t, size_t, task_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;
+ pub type io_service_t = io_object_t;
+
+ #[allow(non_camel_case_types)]
+ pub type task_port_t = task_t;
+
+ extern "C" {
+ pub fn IOServiceMatching(a: *const c_char) -> CFMutableDictionaryRef;
+
+ pub fn IOServiceOpen(
+ device: io_service_t,
+ owning_task: task_port_t,
+ type_: u32,
+ connect: *mut io_connect_t,
+ ) -> kern_return_t;
+
+ pub fn IOServiceClose(a: io_connect_t) -> kern_return_t;
+
+ #[allow(dead_code)]
+ pub fn IOConnectCallStructMethod(
+ connection: mach_port_t,
+ selector: u32,
+ inputStruct: *const KeyData_t,
+ inputStructCnt: size_t,
+ outputStruct: *mut KeyData_t,
+ outputStructCnt: *mut size_t,
+ ) -> kern_return_t;
+ }
#[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))]
#[repr(C)]
@@ -143,8 +149,13 @@ mod io_service {
pub bytes: [i8; 32], // SMCBytes_t
}
+ #[allow(dead_code)]
pub const KERNEL_INDEX_SMC: i32 = 2;
+
+ #[allow(dead_code)]
pub const SMC_CMD_READ_KEYINFO: u8 = 9;
+
+ #[allow(dead_code)]
pub const SMC_CMD_READ_BYTES: u8 = 5;
pub const KIO_RETURN_SUCCESS: i32 = 0;
@@ -153,4 +164,128 @@ mod io_service {
#[cfg(feature = "apple-sandbox")]
mod io_service {}
+#[cfg(all(
+ not(feature = "apple-sandbox"),
+ any(target_arch = "x86", target_arch = "x86_64")
+))]
+pub use io_service::*;
+
+#[cfg(all(not(feature = "apple-sandbox"), target_arch = "aarch64"))]
+mod io_service {
+ use std::ptr::null;
+
+ use core_foundation_sys::array::CFArrayRef;
+ use core_foundation_sys::base::{CFAllocatorRef, CFRelease};
+ use core_foundation_sys::dictionary::{
+ kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryValueCallBacks, CFDictionaryCreate,
+ CFDictionaryRef,
+ };
+ use core_foundation_sys::number::{kCFNumberSInt32Type, CFNumberCreate};
+ use core_foundation_sys::string::{CFStringCreateWithCString, CFStringRef};
+
+ #[repr(C)]
+ pub struct __IOHIDServiceClient(libc::c_void);
+
+ pub type IOHIDServiceClientRef = *const __IOHIDServiceClient;
+
+ #[repr(C)]
+ pub struct __IOHIDEventSystemClient(libc::c_void);
+
+ pub type IOHIDEventSystemClientRef = *const __IOHIDEventSystemClient;
+
+ #[repr(C)]
+ pub struct __IOHIDEvent(libc::c_void);
+
+ pub type IOHIDEventRef = *const __IOHIDEvent;
+
+ #[allow(non_upper_case_globals)]
+ pub const kIOHIDEventTypeTemperature: i64 = 15;
+
+ #[inline]
+ #[allow(non_snake_case)]
+ pub fn IOHIDEventFieldBase(event_type: i64) -> i64 {
+ event_type << 16
+ }
+
+ #[cfg(not(feature = "apple-sandbox"))]
+ extern "C" {
+ pub fn IOHIDEventSystemClientCreate(allocator: CFAllocatorRef)
+ -> IOHIDEventSystemClientRef;
+
+ pub fn IOHIDEventSystemClientSetMatching(
+ client: IOHIDEventSystemClientRef,
+ matches: CFDictionaryRef,
+ ) -> i32;
+
+ pub fn IOHIDEventSystemClientCopyServices(client: IOHIDEventSystemClientRef) -> CFArrayRef;
+
+ pub fn IOHIDServiceClientCopyProperty(
+ service: IOHIDServiceClientRef,
+ key: CFStringRef,
+ ) -> CFStringRef;
+
+ pub fn IOHIDServiceClientCopyEvent(
+ service: IOHIDServiceClientRef,
+ v0: i64,
+ v1: i32,
+ v2: i64,
+ ) -> IOHIDEventRef;
+
+ pub fn IOHIDEventGetFloatValue(event: IOHIDEventRef, field: i64) -> f64;
+ }
+
+ pub(crate) const HID_DEVICE_PROPERTY_PRODUCT: &[u8] = b"Product\0";
+
+ pub(crate) const HID_DEVICE_PROPERTY_PRIMARY_USAGE: &[u8] = b"PrimaryUsage\0";
+ pub(crate) const HID_DEVICE_PROPERTY_PRIMARY_USAGE_PAGE: &[u8] = b"PrimaryUsagePage\0";
+
+ #[allow(non_upper_case_globals)]
+ pub(crate) const kHIDPage_AppleVendor: i32 = 0xff00;
+
+ #[allow(non_upper_case_globals)]
+ pub(crate) const kHIDUsage_AppleVendor_TemperatureSensor: i32 = 0x0005;
+
+ pub(crate) fn matching(page: i32, usage: i32) -> CFDictionaryRef {
+ unsafe {
+ let keys = [
+ CFStringCreateWithCString(
+ null() as *const _,
+ HID_DEVICE_PROPERTY_PRIMARY_USAGE_PAGE.as_ptr() as *const _,
+ 0,
+ ),
+ CFStringCreateWithCString(
+ null() as *const _,
+ HID_DEVICE_PROPERTY_PRIMARY_USAGE.as_ptr() as *const _,
+ 0,
+ ),
+ ];
+
+ let nums = [
+ CFNumberCreate(null(), kCFNumberSInt32Type, &page as *const _ as *const _),
+ CFNumberCreate(null(), kCFNumberSInt32Type, &usage as *const _ as *const _),
+ ];
+
+ let dict = CFDictionaryCreate(
+ null(),
+ &keys as *const _ as *const _,
+ &nums as *const _ as *const _,
+ 2,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks,
+ );
+
+ for key in keys {
+ CFRelease(key as _);
+ }
+
+ for num in nums {
+ CFRelease(num as _);
+ }
+
+ dict
+ }
+ }
+}
+
+#[cfg(all(not(feature = "apple-sandbox"), target_arch = "aarch64"))]
pub use io_service::*;
diff --git a/vendor/sysinfo/src/apple/macos/mod.rs b/vendor/sysinfo/src/apple/macos/mod.rs
index 172bbfddc..856c5931d 100644
--- a/vendor/sysinfo/src/apple/macos/mod.rs
+++ b/vendor/sysinfo/src/apple/macos/mod.rs
@@ -2,16 +2,19 @@
pub mod disk;
pub mod ffi;
+pub(crate) mod utils;
#[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
index f146126cf..fff9c1f71 100644
--- a/vendor/sysinfo/src/apple/macos/process.rs
+++ b/vendor/sysinfo/src/apple/macos/process.rs
@@ -30,7 +30,7 @@ pub struct Process {
old_stime: u64,
start_time: u64,
run_time: u64,
- updated: bool,
+ pub(crate) updated: bool,
cpu_usage: f32,
user_id: Option<Uid>,
group_id: Option<Gid>,
@@ -184,6 +184,20 @@ impl ProcessExt for Process {
fn group_id(&self) -> Option<Gid> {
self.group_id
}
+
+ fn wait(&self) {
+ let mut status = 0;
+ // attempt waiting
+ unsafe {
+ if libc::waitpid(self.pid.0, &mut status, 0) < 0 {
+ // attempt failed (non-child process) so loop until process ends
+ let duration = std::time::Duration::from_millis(10);
+ while kill(self.pid.0, 0) == 0 {
+ std::thread::sleep(duration);
+ }
+ }
+ }
+ }
}
#[allow(deprecated)] // Because of libc::mach_absolute_time.
@@ -202,7 +216,9 @@ pub(crate) fn compute_cpu_usage(
.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;
+ if total_time_diff > 0 {
+ p.cpu_usage = (total_time_diff as f64 / time_interval * 100.) as f32;
+ }
} else {
p.cpu_usage = 0.;
}
@@ -236,7 +252,6 @@ pub(crate) fn compute_cpu_usage(
};
}
}
- p.updated = true;
}
/*pub fn set_time(p: &mut Process, utime: u64, stime: u64) {
@@ -247,18 +262,6 @@ pub(crate) fn compute_cpu_usage(
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
@@ -274,12 +277,22 @@ unsafe fn get_task_info(pid: Pid) -> libc::proc_taskinfo {
}
#[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
- // }
+fn check_if_pid_is_alive(pid: Pid, check_if_alive: bool) -> bool {
+ // In case we are iterating all pids we got from `proc_listallpids`, then
+ // there is no point checking if the process is alive since it was returned
+ // from this function.
+ if !check_if_alive {
+ return true;
+ }
+ unsafe {
+ if kill(pid.0, 0) == 0 {
+ return true;
+ }
+ // `kill` failed but it might not be because the process is dead.
+ let errno = libc::__error();
+ // If errno is equal to ESCHR, it means the process is dead.
+ !errno.is_null() && *errno != libc::ESRCH
+ }
}
#[inline]
@@ -293,91 +306,52 @@ fn do_get_env_path(env: &str, root: &mut PathBuf, check: &mut bool) {
}
}
-pub(crate) fn update_process(
- wrap: &Wrap,
+unsafe fn get_bsd_info(pid: Pid) -> Option<libc::proc_bsdinfo> {
+ 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 _
+ {
+ None
+ } else {
+ Some(info)
+ }
+}
+
+unsafe fn create_new_process(
pid: Pid,
mut size: size_t,
- time_interval: Option<f64>,
now: u64,
refresh_kind: ProcessRefreshKind,
+ info: Option<libc::proc_bsdinfo>,
) -> 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 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 info = match info {
+ Some(info) => info,
+ None => {
let mut buffer: Vec<u8> = Vec::with_capacity(libc::PROC_PIDPATHINFO_MAXSIZE as _);
match libc::proc_pidpath(
pid.0,
@@ -399,158 +373,228 @@ pub(crate) fn update_process(
}
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 parent = match info.pbi_ppid as i32 {
+ 0 => None,
+ p => Some(Pid(p)),
+ };
+
+ let mut proc_args = Vec::with_capacity(size as _);
+ let ptr: *mut u8 = proc_args.as_mut_slice().as_mut_ptr();
+ let mut mib = [libc::CTL_KERN, libc::KERN_PROCARGS2, pid.0 as _];
+ /*
+ * /---------------\ 0x00000000
+ * | ::::::::::::: |
+ * |---------------| <-- 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 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 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);
+ 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);
}
- start = cp;
- let mut c = 0;
- let mut cmd = Vec::with_capacity(n_args as usize);
- while c < n_args && cp < ptr.add(size) {
+ 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 {
- c += 1;
- cmd.push(get_unchecked_str(cp, start));
+ 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)
+ }
- #[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;
+ p.virtual_memory = task_info.pti_virtual_size;
- 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,
- )
+ 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))
+}
+
+pub(crate) fn update_process(
+ wrap: &Wrap,
+ pid: Pid,
+ size: size_t,
+ time_interval: Option<f64>,
+ now: u64,
+ refresh_kind: ProcessRefreshKind,
+ check_if_alive: bool,
+) -> Result<Option<Process>, ()> {
+ 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.
+ return if check_if_pid_is_alive(pid, check_if_alive) {
+ p.updated = true;
+ Ok(None)
} else {
- get_environ(ptr, cp, size, PathBuf::new(), do_get_env_path)
+ Err(())
+ };
+ }
+ if let Some(info) = get_bsd_info(pid) {
+ if info.pbi_start_tvsec != p.start_time {
+ // We don't it to be removed, just replaced.
+ p.updated = true;
+ // The owner of this PID changed.
+ return create_new_process(pid, size, now, refresh_kind, Some(info));
}
+ }
+ 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 {
- get_environ(ptr, cp, size, PathBuf::new(), do_get_env_path)
+ // It very likely means that the process is dead...
+ if check_if_pid_is_alive(pid, check_if_alive) {
+ (0, 0, Some(ThreadStatus::Running))
+ } else {
+ return Err(());
+ }
};
- 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.status = thread_status;
- p.memory = task_info.pti_resident_size / 1_000;
- p.virtual_memory = task_info.pti_virtual_size / 1_000;
+ if refresh_kind.cpu() {
+ compute_cpu_usage(p, task_info, system_time, user_time, time_interval);
+ }
- 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);
+ p.memory = task_info.pti_resident_size;
+ p.virtual_memory = task_info.pti_virtual_size;
+ if refresh_kind.disk_usage() {
+ update_proc_disk_activity(p);
+ }
+ p.updated = true;
+ return Ok(None);
}
- Ok(Some(p))
+ create_new_process(pid, size, now, refresh_kind, get_bsd_info(pid))
}
}
@@ -577,6 +621,7 @@ fn update_proc_disk_activity(p: &mut Process) {
}
}
+#[allow(unknown_lints)]
#[allow(clippy::uninit_vec)]
pub(crate) fn get_proc_list() -> Option<Vec<Pid>> {
unsafe {
diff --git a/vendor/sysinfo/src/apple/macos/utils.rs b/vendor/sysinfo/src/apple/macos/utils.rs
new file mode 100644
index 000000000..ff870db55
--- /dev/null
+++ b/vendor/sysinfo/src/apple/macos/utils.rs
@@ -0,0 +1,30 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use std::num::NonZeroU32;
+
+type IoObject = NonZeroU32;
+
+pub(crate) struct IOReleaser(IoObject);
+
+impl IOReleaser {
+ pub(crate) fn new(obj: u32) -> Option<Self> {
+ IoObject::new(obj).map(Self)
+ }
+
+ pub(crate) unsafe fn new_unchecked(obj: u32) -> Self {
+ // Chance at catching in-development mistakes
+ debug_assert_ne!(obj, 0);
+ Self(IoObject::new_unchecked(obj))
+ }
+
+ #[inline]
+ pub(crate) fn inner(&self) -> u32 {
+ self.0.get()
+ }
+}
+
+impl Drop for IOReleaser {
+ fn drop(&mut self) {
+ unsafe { super::ffi::IOObjectRelease(self.0.get() as _) };
+ }
+}
diff --git a/vendor/sysinfo/src/apple/mod.rs b/vendor/sysinfo/src/apple/mod.rs
index daff800a2..fd6fdec2c 100644
--- a/vendor/sysinfo/src/apple/mod.rs
+++ b/vendor/sysinfo/src/apple/mod.rs
@@ -2,6 +2,7 @@
#[cfg(target_os = "macos")]
pub(crate) mod macos;
+
#[cfg(target_os = "macos")]
pub(crate) use self::macos as inner;
diff --git a/vendor/sysinfo/src/apple/network.rs b/vendor/sysinfo/src/apple/network.rs
index f1316920e..3c4918155 100644
--- a/vendor/sysinfo/src/apple/network.rs
+++ b/vendor/sysinfo/src/apple/network.rs
@@ -26,6 +26,7 @@ impl Networks {
}
}
+ #[allow(unknown_lints)]
#[allow(clippy::cast_ptr_alignment)]
#[allow(clippy::uninit_vec)]
fn update_networks(&mut self) {
diff --git a/vendor/sysinfo/src/apple/system.rs b/vendor/sysinfo/src/apple/system.rs
index abe617dee..12abbd23b 100644
--- a/vendor/sysinfo/src/apple/system.rs
+++ b/vendor/sysinfo/src/apple/system.rs
@@ -3,12 +3,8 @@
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,
@@ -32,6 +28,9 @@ use libc::{
vm_statistics64, _SC_PAGESIZE,
};
+#[cfg(not(any(target_os = "ios", feature = "apple-sandbox")))]
+use super::inner::component::Components;
+
#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
declare_signals! {
c_int,
@@ -87,55 +86,23 @@ pub struct System {
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>,
+ #[cfg(not(any(target_os = "ios", feature = "apple-sandbox")))]
+ components: Components,
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,
@@ -192,17 +159,14 @@ impl SystemExt for System {
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(),
+ page_size_kb: sysconf(_SC_PAGESIZE) as _,
+ #[cfg(not(any(target_os = "ios", feature = "apple-sandbox")))]
+ components: Components::new(),
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,
@@ -226,8 +190,8 @@ impl SystemExt for System {
&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;
+ self.swap_total = xs.xsu_total;
+ self.swap_free = xs.xsu_avail;
}
// get ram info
if self.mem_total < 1 {
@@ -238,7 +202,6 @@ impl SystemExt for System {
&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>();
@@ -257,16 +220,14 @@ impl SystemExt for System {
// * used to hold data that was read speculatively from disk but
// * haven't actually been used by anyone so far.
// */
- self.mem_available = self
- .mem_total
- .saturating_sub(
- u64::from(stat.active_count)
- .saturating_add(u64::from(stat.inactive_count))
- .saturating_add(u64::from(stat.wire_count))
- .saturating_add(u64::from(stat.speculative_count))
- .saturating_sub(u64::from(stat.purgeable_count)),
- )
- .saturating_mul(self.page_size_kb);
+ self.mem_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);
}
}
@@ -275,22 +236,9 @@ impl SystemExt for System {
#[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
fn refresh_components_list(&mut self) {}
- #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+ #[cfg(not(any(target_os = "ios", 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);
- }
- }
- }
+ self.components.refresh();
}
fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind) {
@@ -356,6 +304,7 @@ impl SystemExt for System {
time_interval,
now,
refresh_kind,
+ false,
) {
Ok(x) => x,
_ => None,
@@ -366,7 +315,8 @@ impl SystemExt for System {
entries.into_iter().for_each(|entry| {
self.process_list.insert(entry.pid(), entry);
});
- self.clear_procs();
+ self.process_list
+ .retain(|_, proc_| std::mem::replace(&mut proc_.updated, false));
}
}
@@ -390,6 +340,7 @@ impl SystemExt for System {
time_interval,
now,
refresh_kind,
+ true,
)
} {
Ok(Some(p)) => {
@@ -401,17 +352,8 @@ impl SystemExt for System {
}
}
- #[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);
- }
+ self.disks = unsafe { get_disks() };
}
fn refresh_users_list(&mut self) {
@@ -491,12 +433,24 @@ impl SystemExt for System {
self.swap_total - self.swap_free
}
+ #[cfg(not(any(target_os = "ios", feature = "apple-sandbox")))]
fn components(&self) -> &[Component] {
- &self.components
+ &self.components.inner
}
+ #[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
+ fn components(&self) -> &[Component] {
+ &[]
+ }
+
+ #[cfg(not(any(target_os = "ios", feature = "apple-sandbox")))]
fn components_mut(&mut self) -> &mut [Component] {
- &mut self.components
+ &mut self.components.inner
+ }
+
+ #[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
+ fn components_mut(&mut self) -> &mut [Component] {
+ &mut []
}
fn disks(&self) -> &[Disk] {
@@ -507,6 +461,13 @@ impl SystemExt for System {
&mut self.disks
}
+ fn sort_disks_by<F>(&mut self, compare: F)
+ where
+ F: FnMut(&Disk, &Disk) -> std::cmp::Ordering,
+ {
+ self.disks.sort_unstable_by(compare);
+ }
+
fn uptime(&self) -> u64 {
unsafe {
let csec = libc::time(::std::ptr::null_mut());
@@ -598,7 +559,7 @@ impl SystemExt for System {
&& size > 0
{
// now create a buffer with the size and get the real value
- let mut buf = vec![0_u8; size as usize];
+ let mut buf = vec![0_u8; size as _];
if get_sys_value_by_name(
b"kern.osproductversion\0",
@@ -621,6 +582,10 @@ impl SystemExt for System {
}
}
}
+
+ fn distribution_id(&self) -> String {
+ std::env::consts::OS.to_owned()
+ }
}
impl Default for System {
@@ -629,43 +594,6 @@ impl Default for System {
}
}
-// 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];
@@ -737,7 +665,7 @@ fn get_system_info(value: c_int, default: Option<&str>) -> Option<String> {
default.map(|s| s.to_owned())
} else {
// set the buffer to the correct size
- let mut buf = vec![0_u8; size as usize];
+ let mut buf = vec![0_u8; size as _];
if sysctl(
mib.as_mut_ptr(),
diff --git a/vendor/sysinfo/src/apple/utils.rs b/vendor/sysinfo/src/apple/utils.rs
index 019295b95..408c02c31 100644
--- a/vendor/sysinfo/src/apple/utils.rs
+++ b/vendor/sysinfo/src/apple/utils.rs
@@ -1,6 +1,39 @@
// Take a look at the license at the top of the repository in the LICENSE file.
+use core_foundation_sys::base::CFRelease;
use libc::c_char;
+use std::ptr::NonNull;
+
+// A helper using to auto release the resource got from CoreFoundation.
+// More information about the ownership policy for CoreFoundation pelease refer the link below:
+// https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html#//apple_ref/doc/uid/20001148-CJBEJBHH
+#[repr(transparent)]
+pub(crate) struct CFReleaser<T>(NonNull<T>);
+
+impl<T> CFReleaser<T> {
+ pub(crate) fn new(ptr: *const T) -> Option<Self> {
+ // This cast is OK because `NonNull` is a transparent wrapper
+ // over a `*const T`. Additionally, mutability doesn't matter with
+ // pointers here.
+ NonNull::new(ptr as *mut T).map(Self)
+ }
+
+ pub(crate) fn inner(&self) -> *const T {
+ self.0.as_ptr().cast()
+ }
+}
+
+impl<T> Drop for CFReleaser<T> {
+ fn drop(&mut self) {
+ unsafe { CFRelease(self.0.as_ptr().cast()) }
+ }
+}
+
+// Safety: These are safe to implement because we only wrap non-mutable
+// CoreFoundation types, which are generally threadsafe unless noted
+// otherwise.
+unsafe impl<T> Send for CFReleaser<T> {}
+unsafe impl<T> Sync for CFReleaser<T> {}
pub(crate) fn cstr_to_rust(c: *const c_char) -> Option<String> {
cstr_to_rust_with_size(c, None)
@@ -28,7 +61,6 @@ pub(crate) fn cstr_to_rust_with_size(c: *const c_char, size: Option<usize>) -> O
}
}
-#[cfg(target_os = "macos")]
pub(crate) fn vec_to_rust(buf: Vec<i8>) -> Option<String> {
String::from_utf8(
buf.into_iter()
diff --git a/vendor/sysinfo/src/c_interface.rs b/vendor/sysinfo/src/c_interface.rs
index e7cc5359b..0914a8135 100644
--- a/vendor/sysinfo/src/c_interface.rs
+++ b/vendor/sysinfo/src/c_interface.rs
@@ -462,7 +462,7 @@ pub extern "C" fn sysinfo_process_current_directory(process: CProcess) -> RStrin
pub extern "C" fn sysinfo_rstring_free(s: RString) {
if !s.is_null() {
unsafe {
- let _ = CString::from_raw(s as usize as *mut i8);
+ let _ = CString::from_raw(s as usize as *mut _);
}
}
}
diff --git a/vendor/sysinfo/src/common.rs b/vendor/sysinfo/src/common.rs
index 15cbe87be..2710204f5 100644
--- a/vendor/sysinfo/src/common.rs
+++ b/vendor/sysinfo/src/common.rs
@@ -283,7 +283,7 @@ on Windows as other platforms get this information alongside the Process informa
);
}
-/// Used to determine what you want to refresh specifically on the [`Process`] type.
+/// Used to determine what you want to refresh specifically on the [`Cpu`] 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
@@ -302,7 +302,7 @@ on Windows as other platforms get this information alongside the Process informa
/// }
/// ```
///
-/// [`Process`]: crate::Process
+/// [`Cpu`]: crate::Cpu
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct CpuRefreshKind {
cpu_usage: bool,
@@ -667,7 +667,7 @@ macro_rules! xid {
($(#[$outer:meta])+ $name:ident, $type:ty) => {
$(#[$outer])+
#[repr(transparent)]
- #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+ #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct $name(pub(crate) $type);
impl std::ops::Deref for $name {
diff --git a/vendor/sysinfo/src/debug.rs b/vendor/sysinfo/src/debug.rs
index e2f4361d9..ef460eb49 100644
--- a/vendor/sysinfo/src/debug.rs
+++ b/vendor/sysinfo/src/debug.rs
@@ -105,7 +105,7 @@ impl fmt::Debug for Networks {
f,
"Networks {{ {} }}",
self.iter()
- .map(|x| format!("{:?}", x))
+ .map(|x| format!("{x:?}"))
.collect::<Vec<_>>()
.join(", ")
)
diff --git a/vendor/sysinfo/src/freebsd/component.rs b/vendor/sysinfo/src/freebsd/component.rs
index 6529be73c..c4e84fe63 100644
--- a/vendor/sysinfo/src/freebsd/component.rs
+++ b/vendor/sysinfo/src/freebsd/component.rs
@@ -55,9 +55,7 @@ pub unsafe fn get_components(nb_cpus: usize) -> Vec<Component> {
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();
+ let id = format!("dev.cpu.{core}.temperature\0").as_bytes().to_vec();
if let Some(temperature) = refresh_component(&id) {
components.push(Component {
id,
diff --git a/vendor/sysinfo/src/freebsd/process.rs b/vendor/sysinfo/src/freebsd/process.rs
index b3302edbe..b0dda0f76 100644
--- a/vendor/sysinfo/src/freebsd/process.rs
+++ b/vendor/sysinfo/src/freebsd/process.rs
@@ -5,6 +5,8 @@ use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus,
use std::fmt;
use std::path::{Path, PathBuf};
+use libc::kill;
+
use super::utils::{get_sys_value_str, WrapMap};
#[doc(hidden)]
@@ -141,6 +143,20 @@ impl ProcessExt for Process {
fn group_id(&self) -> Option<Gid> {
Some(self.group_id)
}
+
+ fn wait(&self) {
+ let mut status = 0;
+ // attempt waiting
+ unsafe {
+ if libc::waitpid(self.pid.0, &mut status, 0) < 0 {
+ // attempt failed (non-child process) so loop until process ends
+ let duration = std::time::Duration::from_millis(10);
+ while kill(self.pid.0, 0) == 0 {
+ std::thread::sleep(duration);
+ }
+ }
+ }
+ }
}
pub(crate) unsafe fn get_process_data(
@@ -171,28 +187,34 @@ pub(crate) unsafe fn get_process_data(
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;
+ let virtual_memory = kproc.ki_size as _;
+ let memory = (kproc.ki_rssize as u64).saturating_mul(page_size as _);
// FIXME: This is to get the "real" run time (in micro-seconds).
// let run_time = (kproc.ki_runtime + 5_000) / 10_000;
+ let start_time = kproc.ki_start.tv_sec as u64;
+
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 _;
+ // If the `start_time` we just got is different from the one stored, it means it's not the
+ // same process.
+ if proc_.start_time == start_time {
+ 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);
+
+ 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);
}
-
- return Ok(None);
}
// This is a new process, we need to get more information!
@@ -222,7 +244,6 @@ pub(crate) unsafe fn get_process_data(
// .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,
@@ -249,6 +270,6 @@ pub(crate) unsafe fn get_process_data(
old_read_bytes: 0,
written_bytes: kproc.ki_rusage.ru_oublock as _,
old_written_bytes: 0,
- updated: true,
+ updated: false,
}))
}
diff --git a/vendor/sysinfo/src/freebsd/system.rs b/vendor/sysinfo/src/freebsd/system.rs
index 16da2de50..12298bbcf 100644
--- a/vendor/sysinfo/src/freebsd/system.rs
+++ b/vendor/sysinfo/src/freebsd/system.rs
@@ -126,11 +126,8 @@ impl SystemExt for System {
frequency = get_frequency_for_cpu(pos);
}
}
- self.cpus.push(Cpu::new(
- format!("cpu {}", pos),
- vendor_id.clone(),
- frequency,
- ));
+ 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();
@@ -307,6 +304,13 @@ impl SystemExt for System {
&mut self.disks
}
+ fn sort_disks_by<F>(&mut self, compare: F)
+ where
+ F: FnMut(&Disk, &Disk) -> std::cmp::Ordering,
+ {
+ self.disks.sort_unstable_by(compare);
+ }
+
fn uptime(&self) -> u64 {
unsafe {
let csec = libc::time(::std::ptr::null_mut());
@@ -354,6 +358,10 @@ impl SystemExt for System {
fn os_version(&self) -> Option<String> {
self.system_info.get_os_release()
}
+
+ fn distribution_id(&self) -> String {
+ std::env::consts::OS.to_owned()
+ }
}
impl Default for System {
@@ -377,10 +385,6 @@ impl System {
#[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();
@@ -404,7 +408,8 @@ impl System {
};
// We remove all processes that don't exist anymore.
- self.process_list.retain(|_, v| v.updated);
+ self.process_list
+ .retain(|_, v| std::mem::replace(&mut v.updated, false));
for (kproc, proc_) in procs {
self.add_missing_proc_info(kd, kproc, proc_);
@@ -651,8 +656,8 @@ impl SystemInfo {
)
});
(
- used.saturating_mul(self.page_size as _) / 1_000,
- total.saturating_mul(self.page_size as _) / 1_000,
+ used.saturating_mul(self.page_size as _),
+ total.saturating_mul(self.page_size as _),
)
}
}
@@ -661,14 +666,14 @@ impl SystemInfo {
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;
+ return nb_pages.saturating_mul(self.page_size as _);
}
// 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
+ total_memory
}
}
@@ -686,10 +691,9 @@ impl SystemInfo {
if let Some(arc_size) = self.zfs.arc_size() {
mem_wire -= arc_size;
}
- let used = mem_active
+ mem_active
.saturating_mul(self.page_size as _)
- .saturating_add(mem_wire);
- used / 1_000
+ .saturating_add(mem_wire)
}
}
@@ -705,11 +709,10 @@ impl SystemInfo {
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
+ buffers_mem
+ .saturating_add(inactive_mem.saturating_mul(self.page_size as _))
+ .saturating_add(cached_mem.saturating_mul(self.page_size as _))
+ .saturating_add(free_mem.saturating_mul(self.page_size as _))
}
}
@@ -730,8 +733,10 @@ impl SystemInfo {
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 mut tmp: u64 = new_cp_time[i] as _;
+ total_new += tmp;
+ tmp = old_cp_time[i] as _;
+ total_old += tmp;
}
let total_diff = total_new - total_old;
diff --git a/vendor/sysinfo/src/freebsd/utils.rs b/vendor/sysinfo/src/freebsd/utils.rs
index 00de3e9d9..5745a37f6 100644
--- a/vendor/sysinfo/src/freebsd/utils.rs
+++ b/vendor/sysinfo/src/freebsd/utils.rs
@@ -169,7 +169,7 @@ pub(crate) fn get_sys_value_str_by_name(name: &[u8]) -> Option<String> {
&& 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];
+ let mut buf: Vec<libc::c_char> = vec![0; size as _];
if libc::sysctlbyname(
name.as_ptr() as *const c_char,
@@ -210,7 +210,7 @@ pub(crate) fn get_system_info(mib: &[c_int], default: Option<&str>) -> Option<St
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];
+ let mut buf: Vec<libc::c_char> = vec![0; size as _];
if libc::sysctl(
mib.as_ptr(),
@@ -287,7 +287,7 @@ pub(crate) unsafe fn get_frequency_for_cpu(cpu_nb: c_int) -> u64 {
// 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(),
+ format!("dev.cpu.{cpu_nb}.freq\0").as_bytes(),
&mut frequency,
) {
frequency = 0;
diff --git a/vendor/sysinfo/src/lib.rs b/vendor/sysinfo/src/lib.rs
index 977de23a3..0800d2562 100644
--- a/vendor/sysinfo/src/lib.rs
+++ b/vendor/sysinfo/src/lib.rs
@@ -7,6 +7,7 @@
#![allow(clippy::upper_case_acronyms)]
#![allow(clippy::non_send_fields_in_send_ty)]
#![allow(renamed_and_removed_lints)]
+#![allow(clippy::assertions_on_constants)]
#![allow(unknown_lints)]
#[macro_use]
@@ -328,9 +329,10 @@ mod test {
#[test]
fn check_system_info() {
+ let s = System::new();
+
// 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
@@ -345,6 +347,8 @@ mod test {
.expect("Failed to get long OS version")
.is_empty());
}
+
+ assert!(!s.distribution_id().is_empty());
}
#[test]
@@ -429,7 +433,6 @@ mod test {
// 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;
@@ -441,7 +444,48 @@ mod test {
}
s.refresh_cpu();
for proc_ in s.cpus() {
- assert_ne!(proc_.frequency(), 0);
+ assert_eq!(proc_.frequency(), 0);
+ }
+ // In a VM, it'll fail.
+ if std::env::var("APPLE_CI").is_err() && std::env::var("FREEBSD_CI").is_err() {
+ s.refresh_cpu_specifics(CpuRefreshKind::everything());
+ for proc_ in s.cpus() {
+ assert_ne!(proc_.frequency(), 0);
+ }
+ }
+ }
+
+ // In case `Process::updated` is misused, `System::refresh_processes` might remove them
+ // so this test ensures that it doesn't happen.
+ #[test]
+ fn check_refresh_process_update() {
+ if !System::IS_SUPPORTED {
+ return;
}
+ let mut s = System::new_all();
+ let total = s.processes().len() as isize;
+ s.refresh_processes();
+ let new_total = s.processes().len() as isize;
+ // There should be almost no difference in the processes count.
+ assert!(
+ (new_total - total).abs() <= 5,
+ "{} <= 5",
+ (new_total - total).abs()
+ );
+ }
+
+ // We ensure that the `Process` cmd information is retrieved as expected.
+ #[test]
+ fn check_cmd_line() {
+ if !System::IS_SUPPORTED {
+ return;
+ }
+ let mut sys = System::new();
+ sys.refresh_processes_specifics(ProcessRefreshKind::new());
+
+ assert!(sys
+ .processes()
+ .iter()
+ .any(|(_, process)| !process.cmd().is_empty()));
}
}
diff --git a/vendor/sysinfo/src/linux/component.rs b/vendor/sysinfo/src/linux/component.rs
index 3a10588e5..7815103b4 100644
--- a/vendor/sysinfo/src/linux/component.rs
+++ b/vendor/sysinfo/src/linux/component.rs
@@ -1,143 +1,309 @@
// Take a look at the license at the top of the repository in the LICENSE file.
+// Information about values readable from `hwmon` sysfs.
+//
+// Values in /sys/class/hwmonN are `c_long` or `c_ulong`
+// transposed to rust we only read `u32` or `i32` values.
use crate::ComponentExt;
use std::collections::HashMap;
-use std::fs::{metadata, read_dir, File};
+use std::fs::{read_dir, File};
use std::io::Read;
use std::path::{Path, PathBuf};
#[doc = include_str!("../../md_doc/component.md")]
+#[derive(Default)]
pub struct Component {
- temperature: f32,
- max: f32,
- critical: Option<f32>,
+ /// Optional associated device of a `Component`.
+ device_model: Option<String>,
+ /// The chip name.
+ ///
+ /// Kernel documentation extract:
+ /// ```txt
+ /// This should be a short, lowercase string, not containing
+ /// whitespace, dashes, or the wildcard character '*'.
+ /// This attribute represents the chip name. It is the only
+ /// mandatory attribute.
+ /// I2C devices get this attribute created automatically.
+ /// ```
+ name: String,
+ /// Temperature current value
+ /// - Read in: `temp[1-*]_input`.
+ /// - Unit: read as millidegree Celsius converted to Celsius.
+ temperature: Option<f32>,
+ /// Maximum value computed by sysinfo
+ max: Option<f32>,
+ /// Max threshold provided by the chip/kernel
+ /// - Read in:`temp[1-*]_max`
+ /// - Unit: read as millidegree Celsius converted to Celsius.
+ threshold_max: Option<f32>,
+ /// Min threshold provided by the chip/kernel.
+ /// - Read in:`temp[1-*]_min`
+ /// - Unit: read as millidegree Celsius converted to Celsius.
+ threshold_min: Option<f32>,
+ /// Critical threshold provided by the chip/kernel previous user write.
+ /// Read in `temp[1-*]_crit`:
+ /// Typically greater than corresponding temp_max values.
+ /// - Unit: read as millidegree Celsius converted to Celsius.
+ threshold_critical: Option<f32>,
+ /// Sensor type, not common but can exist!
+ ///
+ /// Read in: `temp[1-*]_type` Sensor type selection.
+ /// Values integer:
+ /// - 1: CPU embedded diode
+ /// - 2: 3904 transistor
+ /// - 3: thermal diode
+ /// - 4: thermistor
+ /// - 5: AMD AMDSI
+ /// - 6: Intel PECI
+ /// Not all types are supported by all chips
+ sensor_type: Option<TermalSensorType>,
+ /// Component Label
+ ///
+ /// For formating detail see `Component::label` function docstring.
+ ///
+ /// ## Linux implementation details
+ ///
+ /// read n: `temp[1-*]_label` Suggested temperature channel label.
+ /// Value: Text string
+ ///
+ /// Should only be created if the driver has hints about what
+ /// this temperature channel is being used for, and user-space
+ /// doesn't. In all other cases, the label is provided by user-space.
label: String,
- input_file: PathBuf,
+ // TODO: not used now.
+ // Historical minimum temperature
+ // - Read in:`temp[1-*]_lowest
+ // - Unit: millidegree Celsius
+ //
+ // Temperature critical min value, typically lower than
+ // corresponding temp_min values.
+ // - Read in:`temp[1-*]_lcrit`
+ // - Unit: millidegree Celsius
+ //
+ // Temperature emergency max value, for chips supporting more than
+ // two upper temperature limits. Must be equal or greater than
+ // corresponding temp_crit values.
+ // - temp[1-*]_emergency
+ // - Unit: millidegree Celsius
+ /// File to read current temperature shall be `temp[1-*]_input`
+ /// It may be absent but we don't continue if absent.
+ input_file: Option<PathBuf>,
+ /// `temp[1-*]_highest file` to read if disponnible highest value.
+ highest_file: Option<PathBuf>,
}
+// Read arbitrary data from sysfs.
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
+ let mut f = File::open(file).ok()?;
+ f.read_to_string(&mut reader).ok()?;
+ reader.truncate(reader.trim_end().len());
+ Some(reader)
+}
+
+/// Designed at first for reading an `i32` or `u32` aka `c_long`
+/// from a `/sys/class/hwmon` sysfs file.
+fn read_number_from_file<N>(file: &Path) -> Option<N>
+where
+ N: std::str::FromStr,
+{
+ let mut reader = [0u8; 32];
+ let mut f = File::open(file).ok()?;
+ let n = f.read(&mut reader).ok()?;
+ // parse and trim would complain about `\0`.
+ let number = &reader[..n];
+ let number = std::str::from_utf8(number).ok()?;
+ let number = number.trim();
+ // Assert that we cleaned a little bit that string.
+ if cfg!(feature = "debug") {
+ assert!(!number.contains('\n') && !number.contains('\0'));
}
+ number.parse().ok()
}
-fn is_file<T: AsRef<Path>>(path: T) -> bool {
- metadata(path).ok().map(|m| m.is_file()).unwrap_or(false)
+// Read a temperature from a `tempN_item` sensor form the sysfs.
+// number returned will be in mili-celsius.
+//
+// Don't call it on `label`, `name` or `type` file.
+#[inline]
+fn get_temperature_from_file(file: &Path) -> Option<f32> {
+ let temp = read_number_from_file(file);
+ convert_temp_celsius(temp)
}
-fn append_files(components: &mut Vec<Component>, folder: &Path) {
- let mut matchings: HashMap<u32, Vec<String>> = HashMap::with_capacity(10);
+/// Takes a raw temperature in mili-celsius and convert it to celsius
+#[inline]
+fn convert_temp_celsius(temp: Option<i32>) -> Option<f32> {
+ temp.map(|n| (n as f32) / 1000f32)
+}
- 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),
- );
- }
- }
- }
+/// Information about thermal sensor. It may be unavailable as it's
+/// kernel module and chip dependant.
+enum TermalSensorType {
+ /// 1: CPU embedded diode
+ CPUEmbeddedDiode,
+ /// 2: 3904 transistor
+ Transistor3904,
+ /// 3: thermal diode
+ ThermalDiode,
+ /// 4: thermistor
+ Thermistor,
+ /// 5: AMD AMDSI
+ AMDAMDSI,
+ /// 6: Intel PECI
+ IntelPECI,
+ /// Not all types are supported by all chips so we keep space for
+ /// unknown sensors.
+ Unknown(u8),
+}
+
+impl From<u8> for TermalSensorType {
+ fn from(input: u8) -> Self {
+ match input {
+ 0 => Self::CPUEmbeddedDiode,
+ 1 => Self::Transistor3904,
+ 3 => Self::ThermalDiode,
+ 4 => Self::Thermistor,
+ 5 => Self::AMDAMDSI,
+ 6 => Self::IntelPECI,
+ n => Self::Unknown(n),
}
- 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));
- }
+ }
+}
+
+/// Check given `item` dispatch to read the right `file` with the right parsing and store data in
+/// given `component`. `id` is provided for `label` creation.
+fn fill_component(component: &mut Component, item: &str, folder: &Path, file: &str) {
+ let hwmon_file = folder.join(file);
+ match item {
+ "type" => {
+ component.sensor_type =
+ read_number_from_file::<u8>(&hwmon_file).map(TermalSensorType::from)
+ }
+ "input" => {
+ let temperature = get_temperature_from_file(&hwmon_file);
+ component.input_file = Some(hwmon_file);
+ component.temperature = temperature;
+ // Maximum know try to get it from `highest` if not available
+ // use current temperature
+ if component.max.is_none() {
+ component.max = temperature;
}
}
+ "label" => component.label = get_file_line(&hwmon_file, 10).unwrap_or_default(),
+ "highest" => {
+ component.max = get_temperature_from_file(&hwmon_file).or(component.temperature);
+ component.highest_file = Some(hwmon_file);
+ }
+ "max" => component.threshold_max = get_temperature_from_file(&hwmon_file),
+ "min" => component.threshold_min = get_temperature_from_file(&hwmon_file),
+ "crit" => component.threshold_critical = get_temperature_from_file(&hwmon_file),
+ _ => {
+ sysinfo_debug!(
+ "This hwmon-temp file is still not supported! Contributions are appreciated.;) {:?}",
+ hwmon_file,
+ );
+ }
}
}
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,
+ /// Read out `hwmon` info (hardware monitor) from `folder`
+ /// to get values' path to be used on refresh as well as files containing `max`,
+ /// `critical value` and `label`. Then we store everything into `components`.
+ ///
+ /// Note that a thermal [Component] must have a way to read its temperature.
+ /// If not, it will be ignored and not added into `components`.
+ ///
+ /// ## What is read:
+ ///
+ /// - Mandatory: `name` the name of the `hwmon`.
+ /// - Mandatory: `tempN_input` Drop [Component] if missing
+ /// - Optional: sensor `label`, in the general case content of `tempN_label`
+ /// see below for special cases
+ /// - Optional: `label`
+ /// - Optional: `/device/model`
+ /// - Optional: hightest historic value in `tempN_hightest`.
+ /// - Optional: max threshold value defined in `tempN_max`
+ /// - Optional: critical threshold value defined in `tempN_crit`
+ ///
+ /// Where `N` is a u32 associated to a sensor like `temp1_max`, `temp1_input`.
+ ///
+ /// ## Doc to Linux kernel API.
+ ///
+ /// Kernel hwmon API: https://www.kernel.org/doc/html/latest/hwmon/hwmon-kernel-api.html
+ /// DriveTemp kernel API: https://docs.kernel.org/gpu/amdgpu/thermal.html#hwmon-interfaces
+ /// Amdgpu hwmon interface: https://www.kernel.org/doc/html/latest/hwmon/drivetemp.html
+ fn from_hwmon(components: &mut Vec<Component>, folder: &Path) -> Option<()> {
+ let dir = read_dir(folder).ok()?;
+ let mut matchings: HashMap<u32, Component> = HashMap::with_capacity(10);
+ for entry in dir.flatten() {
+ let entry = entry.path();
+ let filename = entry.file_name().and_then(|x| x.to_str()).unwrap_or("");
+ if entry.is_dir() || !filename.starts_with("temp") {
+ continue;
+ }
+
+ let (id, item) = filename.split_once('_')?;
+ let id = id.get(4..)?.parse::<u32>().ok()?;
+
+ let component = matchings.entry(id).or_insert_with(Component::default);
+ let name = get_file_line(&folder.join("name"), 16);
+ component.name = name.unwrap_or_default();
+ let device_model = get_file_line(&folder.join("device/model"), 16);
+ component.device_model = device_model;
+ fill_component(component, item, folder, filename);
+ }
+ let compo = matchings
+ .into_iter()
+ .map(|(id, mut c)| {
+ // sysinfo expose a generic interface with a `label`.
+ // Problem: a lot of sensors don't have a label or a device model! ¯\_(ツ)_/¯
+ // So let's pretend we have a unique label!
+ // See the table in `Component::label` documentation for the table detail.
+ c.label = c.format_label("temp", id);
+ c
+ })
+ // Remove components without `tempN_input` file termal. `Component` doesn't support this kind of sensors yet
+ .filter(|c| c.input_file.is_some());
+
+ components.extend(compo);
+ Some(())
+ }
+
+ /// Compute a label out of available information.
+ /// See the table in `Component::label`'s documentation.
+ fn format_label(&self, class: &str, id: u32) -> String {
+ let Component {
+ device_model,
+ name,
label,
- input_file: input_path.to_path_buf(),
- max: max.unwrap_or(0.0),
- critical,
- };
- c.refresh();
- c
+ ..
+ } = self;
+ let has_label = !label.is_empty();
+ match (has_label, device_model) {
+ (true, Some(device_model)) => {
+ format!("{name} {label} {device_model} {class}{id}")
+ }
+ (true, None) => format!("{name} {label}"),
+ (false, Some(device_model)) => format!("{name} {device_model}"),
+ (false, None) => format!("{name} {class}{id}"),
+ }
}
}
impl ComponentExt for Component {
fn temperature(&self) -> f32 {
- self.temperature
+ self.temperature.unwrap_or(f32::NAN)
}
fn max(&self) -> f32 {
- self.max
+ self.max.unwrap_or(f32::NAN)
}
fn critical(&self) -> Option<f32> {
- self.critical
+ self.threshold_critical
}
fn label(&self) -> &str {
@@ -145,22 +311,28 @@ impl ComponentExt for Component {
}
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;
- }
- }
+ let current = self
+ .input_file
+ .as_ref()
+ .and_then(|file| get_temperature_from_file(file.as_path()));
+ // tries to read out kernel highest if not compute something from temperature.
+ let max = self
+ .highest_file
+ .as_ref()
+ .and_then(|file| get_temperature_from_file(file.as_path()))
+ .or_else(|| {
+ let last = self.temperature?;
+ let current = current?;
+ Some(last.max(current))
+ });
+ self.max = max;
+ self.temperature = current;
}
}
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/")) {
+ if let Ok(dir) = read_dir(Path::new("/sys/class/hwmon/")) {
for entry in dir.flatten() {
let entry = entry.path();
if !entry.is_dir()
@@ -172,18 +344,9 @@ pub(crate) fn get_components() -> Vec<Component> {
{
continue;
}
- append_files(&mut components, &entry);
+ Component::from_hwmon(&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
index f3970c318..103f5362a 100644
--- a/vendor/sysinfo/src/linux/cpu.rs
+++ b/vendor/sysinfo/src/linux/cpu.rs
@@ -4,9 +4,209 @@
use std::collections::HashSet;
use std::fs::File;
-use std::io::Read;
+use std::io::{BufRead, BufReader, Read};
-use crate::CpuExt;
+use crate::sys::utils::to_u64;
+use crate::{CpuExt, CpuRefreshKind};
+
+macro_rules! to_str {
+ ($e:expr) => {
+ unsafe { std::str::from_utf8_unchecked($e) }
+ };
+}
+
+pub(crate) struct CpusWrapper {
+ pub(crate) global_cpu: Cpu,
+ pub(crate) cpus: Vec<Cpu>,
+ /// 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,
+ got_cpu_frequency: bool,
+}
+
+impl CpusWrapper {
+ pub(crate) fn new() -> Self {
+ Self {
+ 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),
+ need_cpus_update: true,
+ got_cpu_frequency: false,
+ }
+ }
+
+ pub(crate) fn refresh_if_needed(
+ &mut self,
+ only_update_global_cpu: bool,
+ refresh_kind: CpuRefreshKind,
+ ) {
+ if self.need_cpus_update {
+ self.refresh(only_update_global_cpu, refresh_kind);
+ }
+ }
+
+ pub(crate) fn refresh(&mut self, only_update_global_cpu: bool, refresh_kind: CpuRefreshKind) {
+ let f = match File::open("/proc/stat") {
+ Ok(f) => f,
+ Err(_e) => {
+ sysinfo_debug!("failed to retrieve CPU information: {:?}", _e);
+ return;
+ }
+ };
+ let buf = BufReader::new(f);
+
+ self.need_cpus_update = false;
+ let mut i: usize = 0;
+ let first = self.cpus.is_empty();
+ let mut it = buf.split(b'\n');
+ let (vendor_id, brand) = if first {
+ get_vendor_id_and_brand()
+ } else {
+ (String::new(), String::new())
+ };
+
+ if first || refresh_kind.cpu_usage() {
+ if let Some(Ok(line)) = it.next() {
+ if &line[..4] != b"cpu " {
+ 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() {
+ #[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;
+ }
+ }
+
+ pub(crate) fn get_global_raw_times(&self) -> (u64, u64) {
+ (self.global_cpu.total_time, self.global_cpu.old_total_time)
+ }
+
+ pub(crate) fn len(&self) -> usize {
+ self.cpus.len()
+ }
+
+ pub(crate) fn is_empty(&self) -> bool {
+ self.cpus.is_empty()
+ }
+
+ pub(crate) fn set_need_cpus_update(&mut self) {
+ self.need_cpus_update = true;
+ }
+}
/// Struct containing values to compute a CPU usage.
#[derive(Clone, Copy)]
@@ -224,10 +424,6 @@ impl CpuExt for Cpu {
}
}
-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!(
diff --git a/vendor/sysinfo/src/linux/disk.rs b/vendor/sysinfo/src/linux/disk.rs
index 5a313fd27..6d7fc083c 100644
--- a/vendor/sysinfo/src/linux/disk.rs
+++ b/vendor/sysinfo/src/linux/disk.rs
@@ -1,7 +1,7 @@
// Take a look at the license at the top of the repository in the LICENSE file.
-use crate::sys::utils::get_all_data;
-use crate::{utils, DiskExt, DiskType};
+use crate::sys::utils::{get_all_data, to_cpath};
+use crate::{DiskExt, DiskType};
use libc::statvfs;
use std::ffi::{OsStr, OsString};
@@ -60,7 +60,7 @@ impl DiskExt for Disk {
fn refresh(&mut self) -> bool {
unsafe {
let mut stat: statvfs = mem::zeroed();
- let mount_point_cpath = utils::to_cpath(&self.mount_point);
+ let mount_point_cpath = 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);
@@ -78,7 +78,7 @@ fn new_disk(
file_system: &[u8],
removable_entries: &[PathBuf],
) -> Option<Disk> {
- let mount_point_cpath = utils::to_cpath(mount_point);
+ let mount_point_cpath = to_cpath(mount_point);
let type_ = find_type_for_device_name(device_name);
let mut total = 0;
let mut available = 0;
@@ -136,9 +136,10 @@ fn find_type_for_device_name(device_name: &OsStr) -> DiskType {
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');
+ real_path = match real_path.find('p') {
+ Some(idx) => &real_path["/dev/".len()..idx],
+ None => &real_path["/dev/".len()..],
+ };
} else if device_name_path.starts_with("/dev/root") {
// Recursively solve, for example /dev/mmcblk0p1
if real_path != device_name_path {
@@ -146,9 +147,10 @@ fn find_type_for_device_name(device_name: &OsStr) -> DiskType {
}
} 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');
+ real_path = match real_path.find('p') {
+ Some(idx) => &real_path["/dev/".len()..idx],
+ None => &real_path["/dev/".len()..],
+ };
} else {
// Default case: remove /dev/ and expects the name presents under /sys/block/
// For example, /dev/dm-0 to dm-0
diff --git a/vendor/sysinfo/src/linux/network.rs b/vendor/sysinfo/src/linux/network.rs
index 0153c151a..c8da2bcb3 100644
--- a/vendor/sysinfo/src/linux/network.rs
+++ b/vendor/sysinfo/src/linux/network.rs
@@ -297,7 +297,7 @@ mod test {
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");
+ fs::create_dir(itf2_dir).expect("failed to create subdirectory");
let mut interfaces = HashMap::new();
diff --git a/vendor/sysinfo/src/linux/process.rs b/vendor/sysinfo/src/linux/process.rs
index 0b06e26f1..d7d61b5bc 100644
--- a/vendor/sysinfo/src/linux/process.rs
+++ b/vendor/sysinfo/src/linux/process.rs
@@ -11,8 +11,10 @@ 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::sys::system::SystemInfo;
+use crate::sys::utils::{
+ get_all_data, get_all_data_from_file, realpath, FileCounter, PathHandler, PathPush,
+};
use crate::utils::into_iter;
use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid};
@@ -93,7 +95,7 @@ pub struct Process {
pub(crate) status: ProcessStatus,
/// Tasks run by this process.
pub tasks: HashMap<Pid, Process>,
- pub(crate) stat_file: Option<File>,
+ pub(crate) stat_file: Option<FileCounter>,
old_read_bytes: u64,
old_written_bytes: u64,
read_bytes: u64,
@@ -101,16 +103,11 @@ pub struct Process {
}
impl Process {
- pub(crate) fn new(
- pid: Pid,
- parent: Option<Pid>,
- start_time_without_boot_time: u64,
- info: &SystemInfo,
- ) -> Process {
+ pub(crate) fn new(pid: Pid) -> Process {
Process {
name: String::with_capacity(20),
pid,
- parent,
+ parent: None,
cmd: Vec::with_capacity(2),
environ: Vec::with_capacity(10),
exe: PathBuf::new(),
@@ -124,8 +121,8 @@ impl Process {
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),
+ start_time_without_boot_time: 0,
+ start_time: 0,
run_time: 0,
user_id: None,
group_id: None,
@@ -222,14 +219,16 @@ impl ProcessExt for Process {
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;
+ fn wait(&self) {
+ let mut status = 0;
+ // attempt waiting
+ unsafe {
+ if libc::waitpid(self.pid.0, &mut status, 0) < 0 {
+ // attempt failed (non-child process) so loop until process ends
+ let duration = std::time::Duration::from_millis(10);
+ while kill(self.pid.0, 0) == 0 {
+ std::thread::sleep(duration);
}
}
}
@@ -260,9 +259,7 @@ pub(crate) fn set_time(p: &mut Process, utime: u64, stime: u64) {
}
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) {
+ let data = match get_all_data(path.join("io"), 16_384) {
Ok(d) => d,
Err(_) => return,
};
@@ -306,75 +303,50 @@ impl<'a, T> Wrap<'a, T> {
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(()),
- };
+#[inline(always)]
+fn compute_start_time_without_boot_time(parts: &[&str], info: &SystemInfo) -> u64 {
+ // To be noted that the start time is invalid here, it still needs to be converted into
+ // "real" time.
+ u64::from_str(parts[21]).unwrap_or(0) / info.clock_cycle
+}
- 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));
- }
+fn _get_stat_data(path: &Path, stat_file: &mut Option<FileCounter>) -> Result<String, ()> {
+ let mut file = File::open(path.join("stat")).map_err(|_| ())?;
+ let data = get_all_data_from_file(&mut file, 1024).map_err(|_| ())?;
+ *stat_file = FileCounter::new(file);
+ Ok(data)
+}
- let mut tmp = PathBuf::from(path);
+#[inline(always)]
+fn get_status(p: &mut Process, part: &str) {
+ p.status = part
+ .chars()
+ .next()
+ .map(ProcessStatus::from)
+ .unwrap_or_else(|| ProcessStatus::Unknown(0));
+}
- 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)?;
+fn refresh_user_group_ids<P: PathPush>(p: &mut Process, path: &mut P) {
+ if let Some((user_id, group_id)) = get_uid_and_gid(path.join("status")) {
+ p.user_id = Some(Uid(user_id));
+ p.group_id = Some(Gid(group_id));
+ }
+}
+
+fn retrieve_all_new_process_info(
+ pid: Pid,
+ proc_list: &Process,
+ parts: &[&str],
+ path: &Path,
+ info: &SystemInfo,
+ refresh_kind: ProcessRefreshKind,
+ uptime: u64,
+) -> Process {
+ let mut p = Process::new(pid);
+ let mut tmp = PathHandler::new(path);
let name = parts[1];
- let parent_pid = if proc_list.pid.0 != 0 {
+ p.parent = if proc_list.pid.0 != 0 {
Some(proc_list.pid)
} else {
match Pid::from_str(parts[3]) {
@@ -383,20 +355,15 @@ pub(crate) fn _get_process_data(
}
};
- // 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.start_time_without_boot_time = compute_start_time_without_boot_time(parts, info);
+ p.start_time = p
+ .start_time_without_boot_time
+ .saturating_add(info.boot_time);
- 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));
- }
+ refresh_user_group_ids(&mut p, &mut tmp);
}
if proc_list.pid.0 != 0 {
@@ -410,38 +377,28 @@ pub(crate) fn _get_process_data(
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() {
+
+ match tmp.join("exe").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()
- };
+ // Do not use cmd[0] because it is not the same thing.
+ // See https://github.com/GuillaumeGomez/sysinfo/issues/697.
+ p.exe = 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);
+
+ p.cmd = copy_from_file(tmp.join("cmdline"));
+ p.environ = copy_from_file(tmp.join("environ"));
+ p.cwd = realpath(tmp.join("cwd"));
+ p.root = realpath(tmp.join("root"));
}
update_time_and_memory(
path,
&mut p,
- &parts,
+ parts,
proc_list.memory,
proc_list.virtual_memory,
uptime,
@@ -451,7 +408,91 @@ pub(crate) fn _get_process_data(
if refresh_kind.disk_usage() {
update_process_disk_activity(&mut p, path);
}
- Ok((Some(p), pid))
+ p
+}
+
+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 parent_memory = proc_list.memory;
+ let parent_virtual_memory = proc_list.virtual_memory;
+
+ let data;
+ let parts = if let Some(ref mut entry) = proc_list.tasks.get_mut(&pid) {
+ data = if let Some(mut f) = entry.stat_file.take() {
+ match get_all_data_from_file(&mut f, 1024) {
+ Ok(data) => {
+ // Everything went fine, we put back the file descriptor.
+ entry.stat_file = Some(f);
+ data
+ }
+ Err(_) => {
+ // It's possible that the file descriptor is no longer valid in case the
+ // original process was terminated and another one took its place.
+ _get_stat_data(path, &mut entry.stat_file)?
+ }
+ }
+ } else {
+ _get_stat_data(path, &mut entry.stat_file)?
+ };
+ let parts = parse_stat_file(&data).ok_or(())?;
+ let start_time_without_boot_time = compute_start_time_without_boot_time(&parts, info);
+
+ // It's possible that a new process took this same PID when the "original one" terminated.
+ // If the start time differs, then it means it's not the same process anymore and that we
+ // need to get all its information, hence why we check it here.
+ if start_time_without_boot_time == entry.start_time_without_boot_time {
+ 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() {
+ refresh_user_group_ids(entry, &mut PathBuf::from(path));
+ }
+ return Ok((None, pid));
+ }
+ parts
+ } else {
+ let mut stat_file = None;
+ let data = _get_stat_data(path, &mut stat_file)?;
+ let parts = parse_stat_file(&data).ok_or(())?;
+
+ let mut p =
+ retrieve_all_new_process_info(pid, proc_list, &parts, path, info, refresh_kind, uptime);
+ p.stat_file = stat_file;
+ return Ok((Some(p), pid));
+ };
+
+ // If we're here, it means that the PID still exists but it's a different process.
+ let p = retrieve_all_new_process_info(pid, proc_list, &parts, path, info, refresh_kind, uptime);
+ match proc_list.tasks.get_mut(&pid) {
+ Some(ref mut entry) => **entry = p,
+ // If it ever enters this case, it means that the process was removed from the HashMap
+ // in-between with the usage of dark magic.
+ None => unreachable!(),
+ }
+ // Since this PID is already in the HashMap, no need to add it again.
+ Ok((None, pid))
}
#[allow(clippy::too_many_arguments)]
@@ -473,9 +514,9 @@ fn update_time_and_memory(
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.
+ // vsz correspond to the Virtual memory size in bytes.
// see: https://man7.org/linux/man-pages/man5/proc.5.html
- entry.virtual_memory = u64::from_str(parts[22]).unwrap_or(0) / 1_000;
+ entry.virtual_memory = u64::from_str(parts[22]).unwrap_or(0);
if entry.virtual_memory >= parent_virtual_memory {
entry.virtual_memory -= parent_virtual_memory;
}
@@ -504,73 +545,65 @@ pub(crate) fn refresh_procs(
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
- }
+ let d = match fs::read_dir(path) {
+ Ok(d) => d,
+ Err(_) => return false,
+ };
+ let folders = d
+ .filter_map(|entry| {
+ let entry = entry.ok()?;
+ let entry = entry.path();
+
+ if entry.is_dir() {
+ Some(entry)
+ } 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| {
+ let (p, _) = _get_process_data(
+ e.as_path(),
+ proc_list.get(),
+ pid,
+ uptime,
+ info,
+ refresh_kind,
+ )
+ .ok()?;
+ p
})
- .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
+ .collect::<Vec<_>>()
} else {
- false
- }
+ let mut updated_pids = Vec::with_capacity(folders.len());
+ let new_tasks = folders
+ .iter()
+ .filter_map(|e| {
+ let (p, pid) =
+ _get_process_data(e.as_path(), proc_list, pid, uptime, info, refresh_kind)
+ .ok()?;
+ updated_pids.push(pid);
+ p
+ })
+ .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
}
fn copy_from_file(entry: &Path) -> Vec<String> {
@@ -618,7 +651,7 @@ fn get_uid_and_gid(file_path: &Path) -> Option<(uid_t, gid_t)> {
}
}
- let status_data = get_all_data(&file_path, 16_385).ok()?;
+ 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
@@ -653,29 +686,7 @@ fn get_uid_and_gid(file_path: &Path) -> Option<(uid_t, gid_t)> {
}
}
-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>, ()> {
+fn parse_stat_file(data: &str) -> Option<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
@@ -687,14 +698,14 @@ fn parse_stat_file(data: &str) -> Result<Vec<&str>, ()> {
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.push(data_it.next()?);
+ let mut data_it = data_it.next()?.rsplitn(2, ')');
+ let data = data_it.next()?;
+ parts.push(data_it.next()?);
parts.extend(data.split_whitespace());
// Remove command name '('
if let Some(name) = parts[1].strip_prefix('(') {
parts[1] = name;
}
- Ok(parts)
+ Some(parts)
}
diff --git a/vendor/sysinfo/src/linux/system.rs b/vendor/sysinfo/src/linux/system.rs
index 7a8d0a010..3c4fce345 100644
--- a/vendor/sysinfo/src/linux/system.rs
+++ b/vendor/sysinfo/src/linux/system.rs
@@ -4,7 +4,7 @@ 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::sys::utils::{get_all_data, to_u64};
use crate::{
CpuRefreshKind, Disk, LoadAvg, Networks, Pid, ProcessRefreshKind, RefreshKind, SystemExt, User,
};
@@ -63,12 +63,6 @@ pub(crate) fn get_max_nb_fds() -> isize {
}
}
-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);
@@ -111,7 +105,7 @@ impl SystemInfo {
fn new() -> Self {
unsafe {
Self {
- page_size_kb: (sysconf(_SC_PAGESIZE) / 1024) as _,
+ page_size_kb: sysconf(_SC_PAGESIZE) as _,
clock_cycle: sysconf(_SC_CLK_TCK) as _,
boot_time: boot_time(),
}
@@ -163,22 +157,16 @@ pub struct System {
mem_available: u64,
mem_buffers: u64,
mem_page_cache: u64,
+ mem_shmem: 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,
+ cpus: CpusWrapper,
}
impl System {
@@ -193,15 +181,14 @@ impl System {
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());
- }
+ self.cpus
+ .refresh_if_needed(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 (new, old) = self.cpus.get_global_raw_times();
let total_time = if old > new { 1 } else { new - old };
(
total_time as f32 / self.cpus.len() as f32,
@@ -226,128 +213,7 @@ impl System {
}
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;
- }
- }
+ self.cpus.refresh(only_update_global_cpu, refresh_kind);
}
}
@@ -356,8 +222,7 @@ impl SystemExt for System {
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 process_list = Process::new(Pid(0));
let mut s = System {
process_list,
mem_total: 0,
@@ -365,33 +230,16 @@ impl SystemExt for System {
mem_available: 0,
mem_buffers: 0,
mem_page_cache: 0,
+ mem_shmem: 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),
+ cpus: CpusWrapper::new(),
components: Vec::new(),
disks: Vec::with_capacity(2),
networks: Networks::new(),
users: Vec::new(),
- need_cpus_update: true,
- info,
- got_cpu_frequency: false,
+ info: SystemInfo::new(),
};
s.refresh_specifics(refreshes);
s
@@ -403,14 +251,20 @@ impl SystemExt for System {
fn refresh_memory(&mut self) {
if let Ok(data) = get_all_data("/proc/meminfo", 16_385) {
+ let mut mem_available_found = false;
+
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("MemAvailable") => {
+ mem_available_found = true;
+ &mut self.mem_available
+ }
Some("Buffers") => &mut self.mem_buffers,
Some("Cached") => &mut self.mem_page_cache,
+ Some("Shmem") => &mut self.mem_shmem,
Some("SReclaimable") => &mut self.mem_slab_reclaimable,
Some("SwapTotal") => &mut self.swap_total,
Some("SwapFree") => &mut self.swap_free,
@@ -419,10 +273,21 @@ impl SystemExt for System {
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;
+ *field = value.saturating_mul(1_024);
}
}
}
+
+ // Linux < 3.14 may not have MemAvailable in /proc/meminfo
+ // So it should fallback to the old way of estimating available memory
+ // https://github.com/KittyKatt/screenFetch/issues/386#issuecomment-249312716
+ if !mem_available_found {
+ self.mem_available = self.mem_free
+ + self.mem_buffers
+ + self.mem_page_cache
+ + self.mem_slab_reclaimable
+ - self.mem_shmem;
+ }
}
}
@@ -441,7 +306,7 @@ impl SystemExt for System {
refresh_kind,
);
self.clear_procs(refresh_kind);
- self.need_cpus_update = true;
+ self.cpus.set_need_cpus_update();
}
fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool {
@@ -469,7 +334,7 @@ impl SystemExt for System {
sysinfo_debug!("Cannot compute process CPU usage: no cpus found...");
return found;
}
- let (new, old) = get_raw_times(&self.global_cpu);
+ let (new, old) = self.cpus.get_global_raw_times();
let total_time = (if old >= new { 1 } else { new - old }) as f32;
let max_cpu_usage = self.get_max_process_cpu_usage();
@@ -513,11 +378,11 @@ impl SystemExt for System {
}
fn global_cpu_info(&self) -> &Cpu {
- &self.global_cpu
+ &self.cpus.global_cpu
}
fn cpus(&self) -> &[Cpu] {
- &self.cpus
+ &self.cpus.cpus
}
fn physical_core_count(&self) -> Option<usize> {
@@ -537,11 +402,7 @@ impl SystemExt for System {
}
fn used_memory(&self) -> u64 {
- self.mem_total
- - self.mem_free
- - self.mem_buffers
- - self.mem_page_cache
- - self.mem_slab_reclaimable
+ self.mem_total - self.mem_available
}
fn total_swap(&self) -> u64 {
@@ -573,6 +434,13 @@ impl SystemExt for System {
&mut self.disks
}
+ fn sort_disks_by<F>(&mut self, compare: F)
+ where
+ F: FnMut(&Disk, &Disk) -> std::cmp::Ordering,
+ {
+ self.disks.sort_unstable_by(compare);
+ }
+
fn uptime(&self) -> u64 {
let content = get_all_data("/proc/uptime", 50).unwrap_or_default();
content
@@ -691,6 +559,25 @@ impl SystemExt for System {
fn os_version(&self) -> Option<String> {
get_system_info_android(InfoType::OsVersion)
}
+
+ #[cfg(not(target_os = "android"))]
+ fn distribution_id(&self) -> String {
+ get_system_info_linux(
+ InfoType::DistributionID,
+ Path::new("/etc/os-release"),
+ Path::new(""),
+ )
+ .unwrap_or_else(|| std::env::consts::OS.to_owned())
+ }
+
+ #[cfg(target_os = "android")]
+ fn distribution_id(&self) -> String {
+ // Currently get_system_info_android doesn't support InfoType::DistributionID and always
+ // returns None. This call is done anyway for consistency with non-Android implementation
+ // and to suppress dead-code warning for DistributionID on Android.
+ get_system_info_android(InfoType::DistributionID)
+ .unwrap_or_else(|| std::env::consts::OS.to_owned())
+ }
}
impl Default for System {
@@ -699,16 +586,6 @@ impl Default for System {
}
}
-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:
@@ -716,6 +593,9 @@ enum InfoType {
/// - Linux: The distributions name
Name,
OsVersion,
+ /// Machine-parseable ID of a distribution, see
+ /// https://www.freedesktop.org/software/systemd/man/os-release.html#ID=
+ DistributionID,
}
#[cfg(not(target_os = "android"))]
@@ -726,6 +606,7 @@ fn get_system_info_linux(info: InfoType, path: &Path, fallback_path: &Path) -> O
let info_str = match info {
InfoType::Name => "NAME=",
InfoType::OsVersion => "VERSION_ID=",
+ InfoType::DistributionID => "ID=",
};
for line in reader.lines().flatten() {
@@ -744,6 +625,10 @@ fn get_system_info_linux(info: InfoType, path: &Path, fallback_path: &Path) -> O
let info_str = match info {
InfoType::OsVersion => "DISTRIB_RELEASE=",
InfoType::Name => "DISTRIB_ID=",
+ InfoType::DistributionID => {
+ // lsb-release is inconsistent with os-release and unsupported.
+ return None;
+ }
};
for line in reader.lines().flatten() {
if let Some(stripped) = line.strip_prefix(info_str) {
@@ -759,6 +644,10 @@ fn get_system_info_android(info: InfoType) -> Option<String> {
let name: &'static [u8] = match info {
InfoType::Name => b"ro.product.model\0",
InfoType::OsVersion => b"ro.build.version.release\0",
+ InfoType::DistributionID => {
+ // Not supported.
+ return None;
+ }
};
let mut value_buffer = vec![0u8; libc::PROP_VALUE_MAX as usize];
@@ -792,6 +681,7 @@ mod test {
fn lsb_release_fallback_android() {
assert!(get_system_info_android(InfoType::OsVersion).is_some());
assert!(get_system_info_android(InfoType::Name).is_some());
+ assert!(get_system_info_android(InfoType::DistributionID).is_none());
}
#[test]
@@ -838,6 +728,10 @@ DISTRIB_DESCRIPTION="Ubuntu 20.10"
get_system_info_linux(InfoType::Name, &tmp1, Path::new("")),
Some("Ubuntu".to_owned())
);
+ assert_eq!(
+ get_system_info_linux(InfoType::DistributionID, &tmp1, Path::new("")),
+ Some("ubuntu".to_owned())
+ );
// Check for the "fallback" path: "/etc/lsb-release"
assert_eq!(
@@ -848,5 +742,9 @@ DISTRIB_DESCRIPTION="Ubuntu 20.10"
get_system_info_linux(InfoType::Name, Path::new(""), &tmp2),
Some("Ubuntu".to_owned())
);
+ assert_eq!(
+ get_system_info_linux(InfoType::DistributionID, Path::new(""), &tmp2),
+ None
+ );
}
}
diff --git a/vendor/sysinfo/src/linux/utils.rs b/vendor/sysinfo/src/linux/utils.rs
index cd881a3d1..60a9aa2b0 100644
--- a/vendor/sysinfo/src/linux/utils.rs
+++ b/vendor/sysinfo/src/linux/utils.rs
@@ -2,7 +2,9 @@
use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom};
-use std::path::Path;
+use std::path::{Path, PathBuf};
+
+use crate::sys::system::REMAINING_FILES;
pub(crate) fn get_all_data_from_file(file: &mut File, size: usize) -> io::Result<String> {
let mut buf = String::with_capacity(size);
@@ -17,39 +19,105 @@ pub(crate) fn get_all_data<P: AsRef<Path>>(file_path: P, size: usize) -> io::Res
}
#[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 {
+pub(crate) fn realpath(path: &Path) -> std::path::PathBuf {
+ match std::fs::read_link(path) {
+ Ok(f) => f,
+ Err(_e) => {
+ sysinfo_debug!("failed to get real path for {:?}: {:?}", path, _e);
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(),
+ }
+ }
+}
+
+/// Type used to correctly handle the `REMAINING_FILES` global.
+pub(crate) struct FileCounter(File);
+
+impl FileCounter {
+ pub(crate) fn new(f: File) -> Option<Self> {
+ unsafe {
+ if let Ok(ref mut x) = REMAINING_FILES.lock() {
+ if **x > 0 {
+ **x -= 1;
+ return Some(Self(f));
}
+ // All file descriptors we were allowed are being used.
+ }
+ }
+ None
+ }
+}
+
+impl std::ops::Deref for FileCounter {
+ type Target = File;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+impl std::ops::DerefMut for FileCounter {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl Drop for FileCounter {
+ fn drop(&mut self) {
+ unsafe {
+ if let Ok(ref mut x) = crate::sys::system::REMAINING_FILES.lock() {
+ **x += 1;
}
}
}
}
+
+/// This type is used in `retrieve_all_new_process_info` because we have a "parent" path and
+/// from it, we `pop`/`join` every time because it's more memory efficient than using `Path::join`.
+pub(crate) struct PathHandler(PathBuf);
+
+impl PathHandler {
+ pub(crate) fn new(path: &Path) -> Self {
+ // `path` is the "parent" for all paths which will follow so we add a fake element at
+ // the end since every `PathHandler::join` call will first call `pop` internally.
+ Self(path.join("a"))
+ }
+}
+
+pub(crate) trait PathPush {
+ fn join(&mut self, p: &str) -> &Path;
+}
+
+impl PathPush for PathHandler {
+ fn join(&mut self, p: &str) -> &Path {
+ self.0.pop();
+ self.0.push(p);
+ self.0.as_path()
+ }
+}
+
+// This implementation allows to skip one allocation that is done in `PathHandler`.
+impl PathPush for PathBuf {
+ fn join(&mut self, p: &str) -> &Path {
+ self.push(p);
+ self.as_path()
+ }
+}
+
+pub(crate) fn to_u64(v: &[u8]) -> u64 {
+ let mut x = 0;
+
+ for c in v {
+ x *= 10;
+ x += u64::from(c - b'0');
+ }
+ x
+}
+
+/// Converts a path to a NUL-terminated `Vec<u8>` suitable for use with C functions.
+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
+}
diff --git a/vendor/sysinfo/src/traits.rs b/vendor/sysinfo/src/traits.rs
index 3d5eafaa8..7b442990c 100644
--- a/vendor/sysinfo/src/traits.rs
+++ b/vendor/sysinfo/src/traits.rs
@@ -208,6 +208,18 @@ pub trait ProcessExt: Debug {
/// println!("{}", process.exe().display());
/// }
/// ```
+ ///
+ /// ### Implementation notes
+ ///
+ /// On Linux, this method will return an empty path if there
+ /// was an error trying to read `/proc/<pid>/exe`. This can
+ /// happen, for example, if the permission levels or UID namespaces
+ /// between the caller and target processes are different.
+ ///
+ /// It is also the case that `cmd[0]` is _not_ usually a correct
+ /// replacement for this.
+ /// A process [may change its `cmd[0]` value](https://man7.org/linux/man-pages/man5/proc.5.html)
+ /// freely, making this an untrustworthy source of information.
fn exe(&self) -> &Path;
/// Returns the pid of the process.
@@ -258,26 +270,26 @@ pub trait ProcessExt: Debug {
/// ```
fn root(&self) -> &Path;
- /// Returns the memory usage (in KB).
+ /// Returns the memory usage (in bytes).
///
/// ```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());
+ /// println!("{} bytes", process.memory());
/// }
/// ```
fn memory(&self) -> u64;
- /// Returns the virtual memory usage (in KB).
+ /// Returns the virtual memory usage (in bytes).
///
/// ```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());
+ /// println!("{} bytes", process.virtual_memory());
/// }
/// ```
fn virtual_memory(&self) -> u64;
@@ -400,6 +412,21 @@ pub trait ProcessExt: Debug {
/// }
/// ```
fn group_id(&self) -> Option<Gid>;
+
+ /// Wait for process termination.
+ ///
+ /// ```no_run
+ /// use sysinfo::{Pid, ProcessExt, System, SystemExt};
+ ///
+ /// let mut s = System::new_all();
+ ///
+ /// if let Some(process) = s.process(Pid::from(1337)) {
+ /// eprintln!("Waiting for pid 1337");
+ /// process.wait();
+ /// eprintln!("Pid 1337 exited");
+ /// }
+ /// ```
+ fn wait(&self);
}
/// Contains all the methods of the [`Cpu`][crate::Cpu] struct.
@@ -650,7 +677,7 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// 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())`.
+ /// `refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage())`.
///
/// ```no_run
/// use sysinfo::{System, SystemExt};
@@ -659,7 +686,7 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// s.refresh_cpu();
/// ```
fn refresh_cpu(&mut self) {
- self.refresh_cpu_specifics(CpuRefreshKind::everything())
+ self.refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage())
}
/// Refreshes CPUs specific information.
@@ -668,10 +695,10 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// list nor users list.
///
/// ```no_run
- /// use sysinfo::{System, SystemExt};
+ /// use sysinfo::{System, SystemExt, CpuRefreshKind};
///
/// let mut s = System::new_all();
- /// s.refresh_all();
+ /// s.refresh_cpu_specifics(CpuRefreshKind::everything());
/// ```
fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind);
@@ -866,6 +893,12 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// If you want only the processes with exactly the given `name`, take a look at
/// [`SystemExt::processes_by_exact_name`].
///
+ /// **⚠️ 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.
+ ///
/// ```no_run
/// use sysinfo::{ProcessExt, System, SystemExt};
///
@@ -891,6 +924,12 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// If you instead want the processes containing `name`, take a look at
/// [`SystemExt::processes_by_name`].
///
+ /// **⚠️ 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.
+ ///
/// ```no_run
/// use sysinfo::{ProcessExt, System, SystemExt};
///
@@ -957,17 +996,17 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// ```
fn physical_core_count(&self) -> Option<usize>;
- /// Returns the RAM size in KB.
+ /// Returns the RAM size in bytes.
///
/// ```no_run
/// use sysinfo::{System, SystemExt};
///
/// let s = System::new_all();
- /// println!("{} KB", s.total_memory());
+ /// println!("{} bytes", s.total_memory());
/// ```
fn total_memory(&self) -> u64;
- /// Returns the amount of free RAM in KB.
+ /// Returns the amount of free RAM in bytes.
///
/// Generally, "free" memory refers to unallocated memory whereas "available" memory refers to
/// memory that is available for (re)use.
@@ -979,11 +1018,11 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// use sysinfo::{System, SystemExt};
///
/// let s = System::new_all();
- /// println!("{} KB", s.free_memory());
+ /// println!("{} bytes", s.free_memory());
/// ```
fn free_memory(&self) -> u64;
- /// Returns the amount of available RAM in KB.
+ /// Returns the amount of available RAM in bytes.
///
/// Generally, "free" memory refers to unallocated memory whereas "available" memory refers to
/// memory that is available for (re)use.
@@ -995,47 +1034,47 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// use sysinfo::{System, SystemExt};
///
/// let s = System::new_all();
- /// println!("{} KB", s.available_memory());
+ /// println!("{} bytes", s.available_memory());
/// ```
fn available_memory(&self) -> u64;
- /// Returns the amount of used RAM in KB.
+ /// Returns the amount of used RAM in bytes.
///
/// ```no_run
/// use sysinfo::{System, SystemExt};
///
/// let s = System::new_all();
- /// println!("{} KB", s.used_memory());
+ /// println!("{} bytes", s.used_memory());
/// ```
fn used_memory(&self) -> u64;
- /// Returns the SWAP size in KB.
+ /// Returns the SWAP size in bytes.
///
/// ```no_run
/// use sysinfo::{System, SystemExt};
///
/// let s = System::new_all();
- /// println!("{} KB", s.total_swap());
+ /// println!("{} bytes", s.total_swap());
/// ```
fn total_swap(&self) -> u64;
- /// Returns the amount of free SWAP in KB.
+ /// Returns the amount of free SWAP in bytes.
///
/// ```no_run
/// use sysinfo::{System, SystemExt};
///
/// let s = System::new_all();
- /// println!("{} KB", s.free_swap());
+ /// println!("{} bytes", s.free_swap());
/// ```
fn free_swap(&self) -> u64;
- /// Returns the amount of used SWAP in KB.
+ /// Returns the amount of used SWAP in bytes.
///
/// ```no_run
/// use sysinfo::{System, SystemExt};
///
/// let s = System::new_all();
- /// println!("{} KB", s.used_swap());
+ /// println!("{} bytes", s.used_swap());
/// ```
fn used_swap(&self) -> u64;
@@ -1063,18 +1102,6 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// ```
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
@@ -1092,6 +1119,18 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// ```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 disks list.
+ ///
+ /// ```no_run
+ /// use sysinfo::{DiskExt, System, SystemExt};
+ ///
/// let mut s = System::new_all();
/// for disk in s.disks_mut() {
/// disk.refresh();
@@ -1099,6 +1138,17 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// ```
fn disks_mut(&mut self) -> &mut [Disk];
+ /// Sort the disk list with the provided callback.
+ ///
+ /// Internally, it is using the [`slice::sort_unstable_by`] function, so please refer to it
+ /// for implementation details.
+ ///
+ /// ⚠️ If you use [`SystemExt::refresh_disks_list`], you need to use this method before using
+ /// [`SystemExt::disks`] or [`SystemExt::disks_mut`] if you want them to be sorted.
+ fn sort_disks_by<F>(&mut self, compare: F)
+ where
+ F: FnMut(&Disk, &Disk) -> std::cmp::Ordering;
+
/// Returns the network interfaces object.
///
/// ```no_run
@@ -1212,6 +1262,23 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync {
/// ```
fn long_os_version(&self) -> Option<String>;
+ /// Returns the distribution id as defined by os-release,
+ /// or [`std::env::consts::OS`].
+ ///
+ /// See also
+ /// - <https://www.freedesktop.org/software/systemd/man/os-release.html#ID=>
+ /// - <https://doc.rust-lang.org/std/env/consts/constant.OS.html>
+ ///
+ /// **Important**: this information is computed every time this function is called.
+ ///
+ /// ```no_run
+ /// use sysinfo::{System, SystemExt};
+ ///
+ /// let s = System::new();
+ /// println!("Distribution ID: {:?}", s.distribution_id());
+ /// ```
+ fn distribution_id(&self) -> String;
+
/// Returns the system hostname based off DNS
///
/// **Important**: this information is computed every time this function is called.
@@ -1463,10 +1530,17 @@ pub trait ComponentExt: Debug {
/// println!("{}°C", component.temperature());
/// }
/// ```
+ ///
+ /// ## Linux
+ ///
+ /// Returns `f32::NAN` if it failed to retrieve it.
fn temperature(&self) -> f32;
/// Returns the maximum temperature of the component (in celsius degree).
///
+ /// Note: if `temperature` is higher than the current `max`,
+ /// `max` value will be updated on refresh.
+ ///
/// ```no_run
/// use sysinfo::{ComponentExt, System, SystemExt};
///
@@ -1475,6 +1549,11 @@ pub trait ComponentExt: Debug {
/// println!("{}°C", component.max());
/// }
/// ```
+ ///
+ /// ## Linux
+ ///
+ /// May be computed by sysinfo from kernel.
+ /// Returns `f32::NAN` if it failed to retrieve it.
fn max(&self) -> f32;
/// Returns the highest temperature before the component halts (in celsius degree).
@@ -1487,6 +1566,10 @@ pub trait ComponentExt: Debug {
/// println!("{:?}°C", component.critical());
/// }
/// ```
+ ///
+ /// ## Linux
+ ///
+ /// Critical threshold defined by chip or kernel.
fn critical(&self) -> Option<f32>;
/// Returns the label of the component.
@@ -1499,6 +1582,19 @@ pub trait ComponentExt: Debug {
/// println!("{}", component.label());
/// }
/// ```
+ ///
+ /// ## Linux
+ ///
+ /// Since components informations are retrieved thanks to `hwmon`,
+ /// the labels are generated as follows.
+ /// Note: it may change and it was inspired by `sensors` own formatting.
+ ///
+ /// | name | label | device_model | id_sensor | Computed label by `sysinfo` |
+ /// |---------|--------|------------|----------|----------------------|
+ /// | ✓ | ✓ | ✓ | ✓ | `"{name} {label} {device_model} temp{id}"` |
+ /// | ✓ | ✓ | ✗ | ✓ | `"{name} {label} {id}"` |
+ /// | ✓ | ✗ | ✓ | ✓ | `"{name} {device_model}"` |
+ /// | ✓ | ✗ | ✗ | ✓ | `"{name} temp{id}"` |
fn label(&self) -> &str;
/// Refreshes component.
diff --git a/vendor/sysinfo/src/unknown/process.rs b/vendor/sysinfo/src/unknown/process.rs
index c4656d3f5..4a12ec829 100644
--- a/vendor/sysinfo/src/unknown/process.rs
+++ b/vendor/sysinfo/src/unknown/process.rs
@@ -89,4 +89,6 @@ impl ProcessExt for Process {
fn group_id(&self) -> Option<Gid> {
None
}
+
+ fn wait(&self) {}
}
diff --git a/vendor/sysinfo/src/unknown/system.rs b/vendor/sysinfo/src/unknown/system.rs
index 4603ae737..c205cdf96 100644
--- a/vendor/sysinfo/src/unknown/system.rs
+++ b/vendor/sysinfo/src/unknown/system.rs
@@ -123,6 +123,13 @@ impl SystemExt for System {
&mut []
}
+ fn sort_disks_by<F>(&mut self, _compare: F)
+ where
+ F: FnMut(&Disk, &Disk) -> std::cmp::Ordering,
+ {
+ // does nothing.
+ }
+
fn uptime(&self) -> u64 {
0
}
@@ -159,6 +166,10 @@ impl SystemExt for System {
None
}
+ fn distribution_id(&self) -> String {
+ std::env::consts::OS.to_owned()
+ }
+
fn host_name(&self) -> Option<String> {
None
}
diff --git a/vendor/sysinfo/src/utils.rs b/vendor/sysinfo/src/utils.rs
index 578ab61c6..70d96d9fa 100644
--- a/vendor/sysinfo/src/utils.rs
+++ b/vendor/sysinfo/src/utils.rs
@@ -1,19 +1,5 @@
// 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(
@@ -27,7 +13,7 @@ pub(crate) fn to_cpath(path: &std::path::Path) -> Vec<u8> {
),
feature = "multithread"
),
- not(feature = "apple-sandbox"),
+ not(all(target_os = "macos", feature = "apple-sandbox")),
not(feature = "unknown-ci")
))]
pub(crate) fn into_iter<T>(val: T) -> T::Iter
@@ -51,7 +37,7 @@ where
not(feature = "multithread")
),
not(feature = "unknown-ci"),
- not(feature = "apple-sandbox")
+ not(all(target_os = "macos", feature = "apple-sandbox"))
))]
pub(crate) fn into_iter<T>(val: T) -> T::IntoIter
where
diff --git a/vendor/sysinfo/src/windows/disk.rs b/vendor/sysinfo/src/windows/disk.rs
index ae393afb2..215fb8c58 100644
--- a/vendor/sysinfo/src/windows/disk.rs
+++ b/vendor/sysinfo/src/windows/disk.rs
@@ -16,9 +16,10 @@ 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,
+ PropertyStandardQuery, StorageDeviceSeekPenaltyProperty, IOCTL_STORAGE_QUERY_PROPERTY,
+ STORAGE_PROPERTY_QUERY,
};
-use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, HANDLE, ULARGE_INTEGER};
+use winapi::um::winnt::{BOOLEAN, FILE_SHARE_READ, FILE_SHARE_WRITE, HANDLE, ULARGE_INTEGER};
#[doc = include_str!("../../md_doc/disk.md")]
pub struct Disk {
@@ -122,14 +123,23 @@ unsafe fn get_drive_size(mount_point: &[u16]) -> Option<(u64, u64)> {
) != 0
{
Some((
- *total_size.QuadPart() as u64,
- *available_space.QuadPart() as u64,
+ *total_size.QuadPart() as _,
+ *available_space.QuadPart() as _,
))
} else {
None
}
}
+// FIXME: To be removed once <https://github.com/retep998/winapi-rs/pull/1028> has been merged.
+#[allow(non_snake_case)]
+#[repr(C)]
+struct DEVICE_SEEK_PENALTY_DESCRIPTOR {
+ Version: DWORD,
+ Size: DWORD,
+ IncursSeekPenalty: BOOLEAN,
+}
+
pub(crate) unsafe fn get_disks() -> Vec<Disk> {
let drives = GetLogicalDrives();
if drives == 0 {
@@ -201,16 +211,12 @@ pub(crate) unsafe fn get_disks() -> Vec<Disk> {
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,
+ PropertyId: StorageDeviceSeekPenaltyProperty,
+ QueryType: PropertyStandardQuery,
AdditionalParameters: [0],
};
- let mut dtd: DEVICE_TRIM_DESCRIPTOR = std::mem::zeroed();
+ let mut result: DEVICE_SEEK_PENALTY_DESCRIPTOR = std::mem::zeroed();
let mut dw_size = 0;
let type_ = if DeviceIoControl(
@@ -218,16 +224,16 @@ pub(crate) unsafe fn get_disks() -> Vec<Disk> {
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 result as *mut DEVICE_SEEK_PENALTY_DESCRIPTOR as *mut c_void,
+ size_of::<DEVICE_SEEK_PENALTY_DESCRIPTOR>() as DWORD,
&mut dw_size,
std::ptr::null_mut(),
) == 0
- || dw_size != size_of::<DEVICE_TRIM_DESCRIPTOR>() as DWORD
+ || dw_size != size_of::<DEVICE_SEEK_PENALTY_DESCRIPTOR>() as DWORD
{
DiskType::Unknown(-1)
} else {
- let is_ssd = dtd.TrimEnabled != 0;
+ let is_ssd = result.IncursSeekPenalty == 0;
if is_ssd {
DiskType::SSD
} else {
diff --git a/vendor/sysinfo/src/windows/macros.rs b/vendor/sysinfo/src/windows/macros.rs
deleted file mode 100644
index b0c024c7c..000000000
--- a/vendor/sysinfo/src/windows/macros.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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
index fa5d66beb..805e85269 100644
--- a/vendor/sysinfo/src/windows/mod.rs
+++ b/vendor/sysinfo/src/windows/mod.rs
@@ -3,8 +3,6 @@
mod component;
mod cpu;
mod disk;
-#[macro_use]
-mod macros;
mod network;
mod process;
mod system;
diff --git a/vendor/sysinfo/src/windows/process.rs b/vendor/sysinfo/src/windows/process.rs
index bdc35c53e..7561c658f 100644
--- a/vendor/sysinfo/src/windows/process.rs
+++ b/vendor/sysinfo/src/windows/process.rs
@@ -1,5 +1,6 @@
// Take a look at the license at the top of the repository in the LICENSE file.
+use crate::sys::system::is_proc_running;
use crate::sys::utils::to_str;
use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid};
@@ -48,8 +49,8 @@ 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,
+ PROCESS_QUERY_INFORMATION, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ,
+ RTL_OSVERSIONINFOEXW, TOKEN_QUERY, TOKEN_USER, ULARGE_INTEGER,
};
impl fmt::Display for ProcessStatus {
@@ -67,7 +68,19 @@ fn get_process_handler(pid: Pid) -> Option<HandleWrapper> {
}
let options = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
- unsafe { HandleWrapper::new(OpenProcess(options, FALSE, pid.0 as DWORD)) }
+ HandleWrapper::new(unsafe { OpenProcess(options, FALSE, pid.0 as DWORD) })
+ .or_else(|| {
+ sysinfo_debug!("OpenProcess failed, error: {:?}", unsafe { GetLastError() });
+ HandleWrapper::new(unsafe {
+ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid.0 as DWORD)
+ })
+ })
+ .or_else(|| {
+ sysinfo_debug!("OpenProcess limited failed, error: {:?}", unsafe {
+ GetLastError()
+ });
+ None
+ })
}
unsafe fn get_process_user_id(
@@ -266,11 +279,11 @@ unsafe fn get_h_mod(process_handler: &HandleWrapper, h_mod: &mut *mut c_void) ->
) != 0
}
-unsafe fn get_exe(process_handler: &HandleWrapper, h_mod: *mut c_void) -> PathBuf {
+unsafe fn get_exe(process_handler: &HandleWrapper) -> PathBuf {
let mut exe_buf = [0u16; MAX_PATH + 1];
GetModuleFileNameExW(
**process_handler,
- h_mod as _,
+ std::ptr::null_mut(),
exe_buf.as_mut_ptr(),
MAX_PATH as DWORD + 1,
);
@@ -306,7 +319,7 @@ impl Process {
String::new()
};
- let exe = get_exe(&process_handler, h_mod);
+ let exe = get_exe(&process_handler);
let mut root = exe.clone();
root.pop();
let (cmd, environ, cwd) = match get_process_params(&process_handler) {
@@ -316,7 +329,7 @@ impl Process {
(Vec::new(), Vec::new(), PathBuf::new())
}
};
- let (start_time, run_time) = get_start_and_run_time(&process_handler, now);
+ 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 {
@@ -360,14 +373,8 @@ impl Process {
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 exe = get_exe(&handle);
let mut root = exe.clone();
root.pop();
let (cmd, environ, cwd) = match get_process_params(&handle) {
@@ -377,7 +384,7 @@ impl Process {
(Vec::new(), Vec::new(), PathBuf::new())
}
};
- let (start_time, run_time) = get_start_and_run_time(&handle, now);
+ 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)),
@@ -451,6 +458,10 @@ impl Process {
pub(crate) fn get_handle(&self) -> Option<HANDLE> {
self.handle.as_ref().map(|h| ***h)
}
+
+ pub(crate) fn get_start_time(&self) -> Option<u64> {
+ self.handle.as_ref().map(|handle| get_start_time(***handle))
+ }
}
impl ProcessExt for Process {
@@ -537,25 +548,60 @@ impl ProcessExt for Process {
fn group_id(&self) -> Option<Gid> {
None
}
+
+ fn wait(&self) {
+ if let Some(handle) = self.get_handle() {
+ while is_proc_running(handle) {
+ if get_start_time(handle) != self.start_time() {
+ // PID owner changed so the previous process was finished!
+ return;
+ }
+ std::thread::sleep(std::time::Duration::from_millis(10));
+ }
+ } else {
+ // In this case, we can't do anything so we just return.
+ sysinfo_debug!("can't wait on this process so returning");
+ }
+ }
}
-unsafe fn get_start_and_run_time(handle: &HandleWrapper, now: u64) -> (u64, u64) {
+#[inline]
+unsafe fn get_process_times(handle: HANDLE) -> u64 {
let mut fstart: FILETIME = zeroed();
let mut x = zeroed();
GetProcessTimes(
- **handle,
+ 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);
+ super::utils::filetime_to_u64(fstart)
+}
+
+#[inline]
+fn compute_start(process_times: u64) -> u64 {
// 11_644_473_600 is the number of seconds between the Windows epoch (1601-01-01) and
// the linux epoch (1970-01-01).
- let start = tmp / 10_000_000 - 11_644_473_600;
- let run_time = check_sub(now, start);
- (start, run_time)
+ process_times / 10_000_000 - 11_644_473_600
+}
+
+fn get_start_and_run_time(handle: HANDLE, now: u64) -> (u64, u64) {
+ unsafe {
+ let process_times = get_process_times(handle);
+ let start = compute_start(process_times);
+ let run_time = check_sub(now, start);
+ (start, run_time)
+ }
+}
+
+#[inline]
+pub(crate) fn get_start_time(handle: HANDLE) -> u64 {
+ unsafe {
+ let process_times = get_process_times(handle);
+ compute_start(process_times)
+ }
}
#[allow(clippy::uninit_vec)]
@@ -976,7 +1022,7 @@ pub(crate) fn compute_cpu_usage(p: &mut Process, nb_cpus: u64) {
}
p.cpu_usage = 100.0
- * (delta_user_time.saturating_add(delta_sys_time) as f32 / denominator as f32)
+ * (delta_user_time.saturating_add(delta_sys_time) as f32 / denominator)
* nb_cpus as f32;
}
}
@@ -1011,8 +1057,8 @@ pub(crate) fn update_memory(p: &mut Process) {
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;
+ p.memory = pmc.WorkingSetSize as _;
+ p.virtual_memory = pmc.PrivateUsage as _;
}
}
}
diff --git a/vendor/sysinfo/src/windows/system.rs b/vendor/sysinfo/src/windows/system.rs
index 6abd30db5..643a7b4bc 100644
--- a/vendor/sysinfo/src/windows/system.rs
+++ b/vendor/sysinfo/src/windows/system.rs
@@ -9,7 +9,7 @@ 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::process::{get_start_time, update_memory, Process};
use crate::sys::tools::*;
use crate::sys::users::get_users;
use crate::sys::utils::get_now;
@@ -64,6 +64,19 @@ pub struct System {
users: Vec<User>,
}
+static WINDOWS_ELEVEN_BUILD_NUMBER: u32 = 22000;
+
+impl System {
+ fn is_windows_eleven(&self) -> bool {
+ WINDOWS_ELEVEN_BUILD_NUMBER
+ <= self
+ .kernel_version()
+ .unwrap_or_default()
+ .parse()
+ .unwrap_or(0)
+ }
+}
+
// Useful for parallel iterations.
struct Wrap<T>(T);
@@ -73,7 +86,7 @@ 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,
+ Ok(n) => n.as_secs().saturating_sub(GetTickCount64() / 1_000),
Err(_e) => {
sysinfo_debug!("Failed to compute boot time: {:?}", _e);
0
@@ -117,10 +130,10 @@ impl SystemExt for System {
);
for (pos, proc_) in self.cpus.iter_mut(refresh_kind).enumerate() {
add_english_counter(
- format!(r"\Processor({})\% Processor Time", pos),
+ format!(r"\Processor({pos})\% Processor Time"),
query,
get_key_used(proc_),
- format!("{}_0", pos),
+ format!("{pos}_0"),
);
}
}
@@ -162,8 +175,8 @@ impl SystemExt for System {
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;
+ self.mem_total = mem_info.ullTotalPhys as _;
+ self.mem_available = mem_info.ullAvailPhys as _;
let mut perf_info: PERFORMANCE_INFORMATION = zeroed();
if GetPerformanceInfo(&mut perf_info, size_of::<PERFORMANCE_INFORMATION>() as u32)
== TRUE
@@ -178,8 +191,8 @@ impl SystemExt for System {
.CommitTotal
.saturating_sub(perf_info.PhysicalTotal),
);
- self.swap_total = (swap_total / 1000) as u64;
- self.swap_used = (swap_used / 1000) as u64;
+ self.swap_total = swap_total as _;
+ self.swap_used = swap_used as _;
}
}
}
@@ -190,12 +203,17 @@ impl SystemExt for System {
#[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();
+ let nb_cpus = self.cpus.len() as u64;
+
+ if let Some(proc_) = self.process_list.get_mut(&pid) {
+ if let Some(ret) = refresh_existing_process(proc_, nb_cpus, now, refresh_kind) {
+ return ret;
+ }
+ // We need to re-make the process because the PID owner changed.
+ }
if let Some(mut p) = Process::new_from_pid(pid, now, refresh_kind) {
- p.update(refresh_kind, self.cpus.len() as u64, now);
+ p.update(refresh_kind, nb_cpus, now);
p.updated = false;
self.process_list.insert(pid, p);
true
@@ -266,10 +284,18 @@ impl SystemExt for System {
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;
+ if proc_
+ .get_start_time()
+ .map(|start| start == proc_.start_time())
+ .unwrap_or(true)
+ {
+ proc_.memory = pi.WorkingSetSize as _;
+ proc_.virtual_memory = pi.VirtualSize as _;
+ proc_.update(refresh_kind, nb_cpus, now);
+ return None;
+ }
+ // If the PID owner changed, we need to recompute the whole process.
+ sysinfo_debug!("owner changed for PID {}", proc_.pid());
}
let name = get_process_name(&pi, pid);
let mut p = Process::new_full(
@@ -279,8 +305,8 @@ impl SystemExt for System {
} else {
None
},
- (pi.WorkingSetSize as u64) / 1_000,
- (pi.VirtualSize as u64) / 1_000,
+ pi.WorkingSetSize as _,
+ pi.VirtualSize as _,
name,
now,
refresh_kind,
@@ -386,6 +412,13 @@ impl SystemExt for System {
&mut self.disks
}
+ fn sort_disks_by<F>(&mut self, compare: F)
+ where
+ F: FnMut(&Disk, &Disk) -> std::cmp::Ordering,
+ {
+ self.disks.sort_unstable_by(compare);
+ }
+
fn users(&self) -> &[User] {
&self.users
}
@@ -399,7 +432,7 @@ impl SystemExt for System {
}
fn uptime(&self) -> u64 {
- unsafe { GetTickCount64() / 1000 }
+ unsafe { GetTickCount64() / 1_000 }
}
fn boot_time(&self) -> u64 {
@@ -415,6 +448,14 @@ impl SystemExt for System {
}
fn long_os_version(&self) -> Option<String> {
+ if self.is_windows_eleven() {
+ return get_reg_string_value(
+ HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ "ProductName",
+ )
+ .map(|product_name| product_name.replace("Windows 10 ", "Windows 11 "));
+ }
get_reg_string_value(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
@@ -435,23 +476,29 @@ impl SystemExt for System {
}
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",
- );
+ )
+ .unwrap_or_default();
+ let major = if self.is_windows_eleven() {
+ 11u32
+ } else {
+ u32::from_le_bytes(
+ get_reg_value_u32(
+ HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ "CurrentMajorVersionNumber",
+ )
+ .unwrap_or_default(),
+ )
+ };
+ Some(format!("{major} ({build_number})"))
+ }
- Some(format!(
- "{} ({})",
- u32::from_le_bytes(major.unwrap_or_default()),
- build_number.unwrap_or_default()
- ))
+ fn distribution_id(&self) -> String {
+ std::env::consts::OS.to_owned()
}
}
@@ -461,7 +508,7 @@ impl Default for System {
}
}
-fn is_proc_running(handle: HANDLE) -> bool {
+pub(crate) fn is_proc_running(handle: HANDLE) -> bool {
let mut exit_code = 0;
unsafe {
let ret = GetExitCodeProcess(handle, &mut exit_code);
@@ -469,22 +516,30 @@ fn is_proc_running(handle: HANDLE) -> bool {
}
}
-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;
+/// If it returns `None`, it means that the PID owner changed and that the `Process` must be
+/// completely recomputed.
+fn refresh_existing_process(
+ proc_: &mut Process,
+ nb_cpus: u64,
+ now: u64,
+ refresh_kind: ProcessRefreshKind,
+) -> Option<bool> {
+ if let Some(handle) = proc_.get_handle() {
+ if get_start_time(handle) != proc_.start_time() {
+ sysinfo_debug!("owner changed for PID {}", proc_.pid());
+ // PID owner changed!
+ return None;
+ }
+ if !is_proc_running(handle) {
+ return Some(false);
}
- update_memory(entry);
- entry.update(refresh_kind, s.cpus.len() as u64, get_now());
- entry.updated = false;
- true
} else {
- false
+ return Some(false);
}
+ update_memory(proc_);
+ proc_.update(refresh_kind, nb_cpus, now);
+ proc_.updated = false;
+ Some(true)
}
#[allow(clippy::size_of_in_element_count)]
@@ -495,7 +550,7 @@ pub(crate) fn get_process_name(process: &SYSTEM_PROCESS_INFORMATION, process_id:
match process_id.0 {
0 => "Idle".to_owned(),
4 => "System".to_owned(),
- _ => format!("<no name> Process {}", process_id),
+ _ => format!("<no name> Process {process_id}"),
}
} else {
unsafe {