diff options
Diffstat (limited to 'vendor/sysinfo/src/apple/macos')
-rw-r--r-- | vendor/sysinfo/src/apple/macos/component/arm.rs | 179 | ||||
-rw-r--r-- | vendor/sysinfo/src/apple/macos/component/mod.rs | 13 | ||||
-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.rs | 281 | ||||
-rw-r--r-- | vendor/sysinfo/src/apple/macos/ffi.rs | 295 | ||||
-rw-r--r-- | vendor/sysinfo/src/apple/macos/mod.rs | 3 | ||||
-rw-r--r-- | vendor/sysinfo/src/apple/macos/process.rs | 511 | ||||
-rw-r--r-- | vendor/sysinfo/src/apple/macos/utils.rs | 30 |
8 files changed, 947 insertions, 510 deletions
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 _) }; + } +} |