diff --git a/Cargo.toml b/Cargo.toml index de0e81aa6..e35caa41b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,7 +107,17 @@ windows = { version = ">=0.54, <=0.57", optional = true } libc = "^0.2.164" [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] -core-foundation-sys = "0.8.7" +objc2-core-foundation = { version = "0.3.0", default-features = false, features = [ + "std", + "CFArray", + "CFBase", + "CFData", + "CFDictionary", + "CFError", + "CFNumber", + "CFString", + "CFURL", +] } [target.'cfg(all(target_os = "linux", not(target_os = "android")))'.dev-dependencies] tempfile = "3.9" diff --git a/src/unix/apple/disk.rs b/src/unix/apple/disk.rs index c5b24b4f0..2219494f9 100644 --- a/src/unix/apple/disk.rs +++ b/src/unix/apple/disk.rs @@ -1,19 +1,16 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use crate::{ - sys::{ - ffi, - utils::{self, CFReleaser}, - }, - DiskUsage, -}; +use crate::{sys::ffi, DiskUsage}; use crate::{Disk, DiskKind, DiskRefreshKind}; -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 objc2_core_foundation::{ + kCFAllocatorDefault, kCFTypeArrayCallBacks, kCFURLVolumeAvailableCapacityForImportantUsageKey, + kCFURLVolumeAvailableCapacityKey, kCFURLVolumeIsBrowsableKey, kCFURLVolumeIsEjectableKey, + kCFURLVolumeIsInternalKey, kCFURLVolumeIsLocalKey, kCFURLVolumeIsRemovableKey, + kCFURLVolumeNameKey, kCFURLVolumeTotalCapacityKey, CFArray, CFArrayCreate, CFBoolean, + CFDictionary, CFDictionaryGetValueIfPresent, CFNumber, CFRetained, CFString, + CFURLCopyResourcePropertiesForKeys, CFURLCreateFromFileSystemRepresentation, CFURL, +}; use libc::c_void; @@ -29,7 +26,7 @@ pub(crate) struct DiskInner { bsd_name: Option>, pub(crate) file_system: OsString, pub(crate) mount_point: PathBuf, - volume_url: RetainedCFURL, + volume_url: CFRetained, pub(crate) total_space: u64, pub(crate) available_space: u64, pub(crate) is_removable: bool, @@ -81,16 +78,13 @@ impl DiskInner { if refresh_kind.storage() { unsafe { if let Some(requested_properties) = build_requested_properties(&[ - ffi::kCFURLVolumeTotalCapacityKey, - ffi::kCFURLVolumeAvailableCapacityKey, - ffi::kCFURLVolumeAvailableCapacityForImportantUsageKey, + kCFURLVolumeTotalCapacityKey, + kCFURLVolumeAvailableCapacityKey, + kCFURLVolumeAvailableCapacityForImportantUsageKey, ]) { match get_disk_properties(&self.volume_url, &requested_properties) { Some(disk_props) => { - match get_int_value( - disk_props.inner(), - DictKey::Extern(ffi::kCFURLVolumeTotalCapacityKey), - ) { + match get_int_value(&disk_props, kCFURLVolumeTotalCapacityKey) { Some(total_space) => self.total_space = total_space, None => { sysinfo_debug!("Failed to get disk total space"); @@ -238,19 +232,19 @@ unsafe fn get_list(container: &mut Vec, refresh_kind: DiskRefreshKind) { // Currently we query maximum 9 properties. let mut properties = Vec::with_capacity(9); // "mandatory" information - properties.push(ffi::kCFURLVolumeNameKey); - properties.push(ffi::kCFURLVolumeIsBrowsableKey); - properties.push(ffi::kCFURLVolumeIsLocalKey); + properties.push(kCFURLVolumeNameKey); + properties.push(kCFURLVolumeIsBrowsableKey); + properties.push(kCFURLVolumeIsLocalKey); // is_removable - properties.push(ffi::kCFURLVolumeIsEjectableKey); - properties.push(ffi::kCFURLVolumeIsRemovableKey); - properties.push(ffi::kCFURLVolumeIsInternalKey); + properties.push(kCFURLVolumeIsEjectableKey); + properties.push(kCFURLVolumeIsRemovableKey); + properties.push(kCFURLVolumeIsInternalKey); if refresh_kind.storage() { - properties.push(ffi::kCFURLVolumeTotalCapacityKey); - properties.push(ffi::kCFURLVolumeAvailableCapacityForImportantUsageKey); - properties.push(ffi::kCFURLVolumeAvailableCapacityKey); + properties.push(kCFURLVolumeTotalCapacityKey); + properties.push(kCFURLVolumeAvailableCapacityForImportantUsageKey); + properties.push(kCFURLVolumeAvailableCapacityKey); } // Create a list of properties about the disk that we want to fetch. @@ -263,13 +257,11 @@ unsafe fn get_list(container: &mut Vec, refresh_kind: DiskRefreshKind) { }; 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 _, - ), + let volume_url = match CFURLCreateFromFileSystemRepresentation( + kCFAllocatorDefault, + c_disk.f_mntonname.as_ptr() as *const _, + c_disk.f_mntonname.len() as _, + false as _, ) { Some(url) => url, None => { @@ -287,11 +279,7 @@ unsafe fn get_list(container: &mut Vec, refresh_kind: DiskRefreshKind) { // `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(); + let browsable = get_bool_value(&prop_dict, 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, @@ -303,11 +291,7 @@ unsafe fn get_list(container: &mut Vec, refresh_kind: DiskRefreshKind) { continue; } - let local_only = get_bool_value( - prop_dict.inner(), - DictKey::Extern(ffi::kCFURLVolumeIsLocalKey), - ) - .unwrap_or(true); + let local_only = get_bool_value(&prop_dict, kCFURLVolumeIsLocalKey).unwrap_or(true); // Skip any drive that is not locally attached to the system. // @@ -336,155 +320,87 @@ unsafe fn get_list(container: &mut Vec, refresh_kind: DiskRefreshKind) { } } -type RetainedCFArray = CFReleaser; -pub(crate) type RetainedCFDictionary = CFReleaser; -type RetainedCFURL = CFReleaser; -#[cfg(target_os = "macos")] -pub(crate) type RetainedCFString = CFReleaser; - -unsafe fn build_requested_properties(properties: &[CFStringRef]) -> Option { - CFReleaser::new(CFArrayCreate( - ptr::null_mut(), - properties.as_ptr() as *const *const c_void, +unsafe fn build_requested_properties( + properties: &[Option<&CFString>], +) -> Option> { + CFArrayCreate( + None, + properties.as_ptr() as *mut *const c_void, properties.len() as _, - &core_foundation_sys::array::kCFTypeArrayCallBacks, - )) + &kCFTypeArrayCallBacks, + ) } fn get_disk_properties( - volume_url: &RetainedCFURL, - requested_properties: &RetainedCFArray, -) -> Option { - CFReleaser::new(unsafe { - ffi::CFURLCopyResourcePropertiesForKeys( - volume_url.inner(), - requested_properties.inner(), - ptr::null_mut(), - ) - }) + volume_url: &CFURL, + requested_properties: &CFArray, +) -> Option> { + unsafe { + CFURLCopyResourcePropertiesForKeys(volume_url, Some(requested_properties), ptr::null_mut()) + } } -fn get_available_volume_space(disk_props: &RetainedCFDictionary) -> Option { +fn get_available_volume_space(disk_props: &CFDictionary) -> Option { // 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), + disk_props, + kCFURLVolumeAvailableCapacityForImportantUsageKey, ) .filter(|bytes| *bytes != 0) - .or_else(|| { - get_int_value( - disk_props.inner(), - DictKey::Extern(ffi::kCFURLVolumeAvailableCapacityKey), - ) - }) + .or_else(|| get_int_value(disk_props, kCFURLVolumeAvailableCapacityKey)) } } -pub(super) enum DictKey { - Extern(CFStringRef), - #[cfg(target_os = "macos")] - Defined(&'static str), -} - unsafe fn get_dict_value Option>( - dict: CFDictionaryRef, - key: DictKey, + dict: &CFDictionary, + key: Option<&CFString>, callback: F, ) -> Option { - #[cfg(target_os = "macos")] - let _defined; - #[allow(clippy::infallible_destructuring_match)] - 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 { + let key: *const CFString = key.map(|key| key as *const CFString).unwrap_or(ptr::null()); + if CFDictionaryGetValueIfPresent(dict, key.cast(), &mut value) { callback(value) } else { None } } -pub(super) unsafe fn get_str_value(dict: CFDictionaryRef, key: DictKey) -> Option { +pub(super) unsafe fn get_str_value(dict: &CFDictionary, key: Option<&CFString>) -> Option { 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 { - crate::unix::utils::cstr_to_rust_with_size(v_ptr, Some(len_bytes)) - } + let v = unsafe { &*v.cast::() }; + Some(v.to_string()) }) } -unsafe fn get_bool_value(dict: CFDictionaryRef, key: DictKey) -> Option { - get_dict_value(dict, key, |v| Some(v as CFBooleanRef == kCFBooleanTrue)) +unsafe fn get_bool_value(dict: &CFDictionary, key: Option<&CFString>) -> Option { + get_dict_value(dict, key, |v| { + let v = unsafe { &*v.cast::() }; + Some(v.as_bool()) + }) } -pub(super) unsafe fn get_int_value(dict: CFDictionaryRef, key: DictKey) -> Option { +pub(super) unsafe fn get_int_value(dict: &CFDictionary, key: Option<&CFString>) -> Option { get_dict_value(dict, key, |v| { - let mut val: i64 = 0; - if CFNumberGetValue( - v.cast(), - core_foundation_sys::number::kCFNumberSInt64Type, - &mut val as *mut _ as *mut _, - ) { - Some(val as _) - } else { - None - } + let v = unsafe { &*v.cast::() }; + Some(v.as_i64()? as u64) }) } unsafe fn new_disk( disk: Option<&mut Disk>, mount_point: PathBuf, - volume_url: RetainedCFURL, + volume_url: CFRetained, c_disk: libc::statfs, - disk_props: &RetainedCFDictionary, + disk_props: &CFDictionary, refresh_kind: DiskRefreshKind, ) -> Option { let (total_space, available_space) = if refresh_kind.storage() { ( - get_int_value( - disk_props.inner(), - DictKey::Extern(ffi::kCFURLVolumeTotalCapacityKey), - ), + get_int_value(disk_props, kCFURLVolumeTotalCapacityKey), get_available_volume_space(disk_props), ) } else { @@ -509,11 +425,7 @@ unsafe fn new_disk( // 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 name = get_str_value(disk_props, kCFURLVolumeNameKey).map(OsString::from)?; let file_system = { let len = c_disk @@ -537,28 +449,16 @@ unsafe fn new_disk( // so we just assume the disk type is an SSD and set disk i/o stats to 0 until Rust has a way to conditionally link to // IOKit in more recent deployment versions. - let ejectable = get_bool_value( - disk_props.inner(), - DictKey::Extern(ffi::kCFURLVolumeIsEjectableKey), - ) - .unwrap_or(false); + let ejectable = get_bool_value(disk_props, kCFURLVolumeIsEjectableKey).unwrap_or(false); - let removable = get_bool_value( - disk_props.inner(), - DictKey::Extern(ffi::kCFURLVolumeIsRemovableKey), - ) - .unwrap_or(false); + let removable = get_bool_value(disk_props, kCFURLVolumeIsRemovableKey).unwrap_or(false); let is_removable = if ejectable || removable { true } 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(); + let internal = get_bool_value(disk_props, kCFURLVolumeIsInternalKey).unwrap_or_default(); !internal }; diff --git a/src/unix/apple/ffi.rs b/src/unix/apple/ffi.rs index 75ae0e1ec..d30e5649b 100644 --- a/src/unix/apple/ffi.rs +++ b/src/unix/apple/ffi.rs @@ -9,31 +9,8 @@ pub use crate::sys::inner::ffi::*; cfg_if! { if #[cfg(feature = "disk")] { - use core_foundation_sys::{ - array::CFArrayRef, dictionary::CFDictionaryRef, error::CFErrorRef, string::CFStringRef, - url::CFURLRef, - }; use std::ffi::c_void; - #[link(name = "CoreFoundation", kind = "framework")] - extern "C" { - pub fn CFURLCopyResourcePropertiesForKeys( - url: CFURLRef, - keys: CFArrayRef, - error: *mut CFErrorRef, - ) -> CFDictionaryRef; - - 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; - } - #[link(name = "objc", kind = "dylib")] extern "C" { pub fn objc_autoreleasePoolPop(pool: *mut c_void); diff --git a/src/unix/apple/macos/component/arm.rs b/src/unix/apple/macos/component/arm.rs index 6577fd15d..05ed5ef14 100644 --- a/src/unix/apple/macos/component/arm.rs +++ b/src/unix/apple/macos/component/arm.rs @@ -1,26 +1,23 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use std::ffi::CStr; +use std::ptr::NonNull; -use core_foundation_sys::array::{CFArrayGetCount, CFArrayGetValueAtIndex}; -use core_foundation_sys::base::kCFAllocatorDefault; -use core_foundation_sys::string::{ - kCFStringEncodingUTF8, CFStringCreateWithBytes, CFStringGetCStringPtr, +use objc2_core_foundation::{ + kCFAllocatorDefault, CFArrayGetCount, CFArrayGetValueAtIndex, CFRetained, CFString, }; use crate::sys::inner::ffi::{ kHIDPage_AppleVendor, kHIDUsage_AppleVendor_TemperatureSensor, kIOHIDEventTypeTemperature, matching, IOHIDEventFieldBase, IOHIDEventGetFloatValue, IOHIDEventSystemClientCopyServices, IOHIDEventSystemClientCreate, IOHIDEventSystemClientSetMatching, IOHIDServiceClientCopyEvent, - IOHIDServiceClientCopyProperty, __IOHIDEventSystemClient, __IOHIDServiceClient, - HID_DEVICE_PROPERTY_PRODUCT, + IOHIDServiceClientCopyProperty, HID_DEVICE_PROPERTY_PRODUCT, }; -use crate::sys::utils::CFReleaser; +use crate::unix::apple::ffi::{IOHIDEventSystemClient, IOHIDServiceClient}; use crate::Component; pub(crate) struct ComponentsInner { pub(crate) components: Vec, - client: Option>, + client: Option>, } impl ComponentsInner { @@ -53,68 +50,49 @@ impl ComponentsInner { #[allow(unreachable_code)] pub(crate) fn refresh(&mut self) { unsafe { - let matches = match CFReleaser::new(matching( + let matches = match 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, - }; + let client = match IOHIDEventSystemClientCreate(kCFAllocatorDefault) { + Some(c) => CFRetained::from_raw(c), + None => return, + }; self.client = Some(client); } let client = self.client.as_ref().unwrap(); - let _ = IOHIDEventSystemClientSetMatching(client.inner(), matches.inner()); + let _ = IOHIDEventSystemClientSetMatching(client, &matches); - let services = match CFReleaser::new(IOHIDEventSystemClientCopyServices(client.inner())) - { - Some(s) => s, + let services = match IOHIDEventSystemClientCopyServices(client) { + Some(s) => CFRetained::from_raw(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 key = CFString::from_static_str(HID_DEVICE_PROPERTY_PRODUCT); - let count = CFArrayGetCount(services.inner()); + let count = CFArrayGetCount(&services); for i in 0..count { - // The 'service' should never be freed since it is returned by a 'Get' call. - // See issue https://github.com/GuillaumeGomez/sysinfo/issues/1279 - let service = CFArrayGetValueAtIndex(services.inner(), i); + let service = CFArrayGetValueAtIndex(&services, i).cast::(); if service.is_null() { continue; } + // The 'service' should never be freed since it is returned by a 'Get' call. + // See issue https://github.com/GuillaumeGomez/sysinfo/issues/1279 + let service = CFRetained::retain(NonNull::from(&*service)); - let Some(name) = CFReleaser::new(IOHIDServiceClientCopyProperty( - service as *const _, - key_ref.inner(), - )) else { + let Some(name) = IOHIDServiceClientCopyProperty(&service, &key) else { continue; }; - - let name_ptr = - CFStringGetCStringPtr(name.inner() as *const _, kCFStringEncodingUTF8); - if name_ptr.is_null() { - continue; - } - - let name_str = CStr::from_ptr(name_ptr).to_string_lossy().to_string(); + let name = CFRetained::from_raw(name); + let name_str = name.to_string(); if let Some(c) = self .components @@ -126,7 +104,7 @@ impl ComponentsInner { continue; } - let mut component = ComponentInner::new(name_str, None, None, service as *mut _); + let mut component = ComponentInner::new(name_str, None, None, service); component.refresh(); self.components.push(Component { inner: component }); @@ -136,7 +114,7 @@ impl ComponentsInner { } pub(crate) struct ComponentInner { - service: *mut __IOHIDServiceClient, + service: CFRetained, temperature: Option, label: String, max: f32, @@ -152,7 +130,7 @@ impl ComponentInner { label: String, max: Option, critical: Option, - service: *mut __IOHIDServiceClient, + service: CFRetained, ) -> Self { Self { service, @@ -182,20 +160,17 @@ impl ComponentInner { pub(crate) fn refresh(&mut self) { unsafe { - let Some(event) = CFReleaser::new(IOHIDServiceClientCopyEvent( - self.service as *const _, - kIOHIDEventTypeTemperature, - 0, - 0, - )) else { + let Some(event) = + IOHIDServiceClientCopyEvent(&self.service, kIOHIDEventTypeTemperature, 0, 0) + else { self.temperature = None; return; }; + let event = CFRetained::from_raw(event); - let temperature = IOHIDEventGetFloatValue( - event.inner(), - IOHIDEventFieldBase(kIOHIDEventTypeTemperature), - ) as _; + let temperature = + IOHIDEventGetFloatValue(&event, IOHIDEventFieldBase(kIOHIDEventTypeTemperature)) + as _; self.temperature = Some(temperature); if temperature > self.max { self.max = temperature; diff --git a/src/unix/apple/macos/component/x86.rs b/src/unix/apple/macos/component/x86.rs index a921179bf..f92690a55 100644 --- a/src/unix/apple/macos/component/x86.rs +++ b/src/unix/apple/macos/component/x86.rs @@ -296,11 +296,12 @@ impl IoService { let mut iterator: ffi::io_iterator_t = 0; unsafe { - let matching_dictionary = ffi::IOServiceMatching(b"AppleSMC\0".as_ptr() as *const i8); - if matching_dictionary.is_null() { + let Some(matching_dictionary) = + ffi::IOServiceMatching(b"AppleSMC\0".as_ptr() as *const i8) + else { sysinfo_debug!("IOServiceMatching call failed, `AppleSMC` not found"); return None; - } + }; let result = ffi::IOServiceGetMatchingServices( ffi::kIOMasterPortDefault, matching_dictionary, diff --git a/src/unix/apple/macos/cpu.rs b/src/unix/apple/macos/cpu.rs index 974f4d0b7..59f2b5379 100644 --- a/src/unix/apple/macos/cpu.rs +++ b/src/unix/apple/macos/cpu.rs @@ -9,14 +9,14 @@ pub(crate) unsafe fn get_cpu_frequency() -> u64 { pub(crate) unsafe fn get_cpu_frequency() -> u64 { use crate::sys::ffi; use crate::sys::macos::utils::IOReleaser; - use crate::sys::utils::CFReleaser; - use core_foundation_sys::string::CFStringCreateWithCStringNoCopy; + use objc2_core_foundation::{ + kCFAllocatorDefault, CFData, CFDataGetBytes, CFDataGetLength, CFRange, CFRetained, CFString, + }; - let matching = ffi::IOServiceMatching(b"AppleARMIODevice\0".as_ptr() as *const _); - if matching.is_null() { + let Some(matching) = ffi::IOServiceMatching(b"AppleARMIODevice\0".as_ptr() as *const _) else { sysinfo_debug!("IOServiceMatching call failed, `AppleARMIODevice` not found"); return 0; - } + }; // Starting from mac M1, the above call returns nothing for the CPU frequency // so we try to get it from another source. This code comes from @@ -55,41 +55,39 @@ pub(crate) unsafe fn get_cpu_frequency() -> u64 { } }; - let node_name = match CFReleaser::new(CFStringCreateWithCStringNoCopy( - std::ptr::null(), - b"voltage-states5-sram\0".as_ptr() as *const _, - core_foundation_sys::string::kCFStringEncodingUTF8, - core_foundation_sys::base::kCFAllocatorNull as *mut _, - )) { - Some(n) => n, - None => { - sysinfo_debug!("CFStringCreateWithCStringNoCopy failed"); - return 0; - } - }; + let node_name = CFString::from_static_str("voltage-states5-sram"); - let core_ref = match CFReleaser::new(ffi::IORegistryEntryCreateCFProperty( + let core_ref = match ffi::IORegistryEntryCreateCFProperty( entry.inner(), - node_name.inner(), - core_foundation_sys::base::kCFAllocatorDefault, + &node_name, + kCFAllocatorDefault, 0, - )) { + ) { Some(c) => c, None => { sysinfo_debug!("`voltage-states5-sram` property not found"); return 0; } }; + let core_ref = CFRetained::from_raw(core_ref); + + let Ok(core_ref) = core_ref.downcast::() else { + sysinfo_debug!("`voltage-states5-sram` property was not CFData"); + return 0; + }; - let core_length = core_foundation_sys::data::CFDataGetLength(core_ref.inner() as *const _); + let core_length = CFDataGetLength(&core_ref); if core_length < 8 { sysinfo_debug!("expected `voltage-states5-sram` buffer to have at least size 8"); return 0; } let mut max: u64 = 0; - core_foundation_sys::data::CFDataGetBytes( - core_ref.inner() as *const _, - core_foundation_sys::base::CFRange::init(core_length - 8, 4), + CFDataGetBytes( + &core_ref, + CFRange { + location: core_length - 8, + length: 4, + }, &mut max as *mut _ as *mut _, ); max / 1_000_000 diff --git a/src/unix/apple/macos/disk.rs b/src/unix/apple/macos/disk.rs index 3522b599a..22f1a14c3 100644 --- a/src/unix/apple/macos/disk.rs +++ b/src/unix/apple/macos/disk.rs @@ -2,35 +2,28 @@ use crate::sys::ffi; use crate::sys::{ - disk::{get_int_value, get_str_value, DictKey}, + disk::{get_int_value, get_str_value}, macos::utils::IOReleaser, - utils::CFReleaser, }; -use crate::unix::apple::disk::{RetainedCFDictionary, RetainedCFString}; use crate::DiskKind; -use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull}; -use core_foundation_sys::string::{self as cfs}; +use objc2_core_foundation::{kCFAllocatorDefault, CFDictionary, CFRetained, CFString}; -fn iterate_service_tree(bsd_name: &[u8], key: RetainedCFString, eval: F) -> Option +fn iterate_service_tree(bsd_name: &[u8], key: &CFString, eval: F) -> Option where - F: Fn(ffi::io_registry_entry_t, &RetainedCFDictionary) -> Option, + F: Fn(ffi::io_registry_entry_t, &CFDictionary) -> Option, { - // We don't need to wrap this in an auto-releaser because the following call to `IOServiceGetMatchingServices` - // will take ownership of one retain reference. + // We don't need to wrap this in CFRetained 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()) }; - - if matching.is_null() { - return None; - } + unsafe { ffi::IOBSDNameMatching(ffi::kIOMasterPortDefault, 0, bsd_name.as_ptr().cast()) }?; let mut service_iterator: ffi::io_iterator_t = 0; if unsafe { ffi::IOServiceGetMatchingServices( ffi::kIOMasterPortDefault, - matching.cast(), + matching, &mut service_iterator, ) } != libc::KERN_SUCCESS @@ -69,18 +62,21 @@ where }; let properties_result = unsafe { - CFReleaser::new(ffi::IORegistryEntryCreateCFProperty( + ffi::IORegistryEntryCreateCFProperty( current_service_entry.inner(), - key.inner(), + key, kCFAllocatorDefault, 0, - )) + ) }; - if let Some(result) = - properties_result.and_then(|properties| eval(parent_entry, &properties)) - { - return Some(result); + if let Some(properties) = properties_result { + let properties = unsafe { CFRetained::from_raw(properties) }; + if let Ok(properties) = properties.downcast::() { + if let Some(result) = eval(parent_entry, &properties) { + return Some(result); + } + } } } } @@ -89,22 +85,14 @@ where } pub(crate) fn get_disk_type(bsd_name: &[u8]) -> Option { - let characteristics_string = unsafe { - CFReleaser::new(cfs::CFStringCreateWithBytesNoCopy( - kCFAllocatorDefault, - ffi::kIOPropertyDeviceCharacteristicsKey.as_ptr(), - ffi::kIOPropertyDeviceCharacteristicsKey.len() as _, - cfs::kCFStringEncodingUTF8, - false as _, - kCFAllocatorNull, - ))? - }; - - iterate_service_tree(bsd_name, characteristics_string, |_, properties| { + let characteristics_string = + CFString::from_static_str(ffi::kIOPropertyDeviceCharacteristicsKey); + + iterate_service_tree(bsd_name, &characteristics_string, |_, properties| { let medium = unsafe { super::disk::get_str_value( - properties.inner(), - DictKey::Defined(ffi::kIOPropertyMediumTypeKey), + properties, + Some(&CFString::from_static_str(ffi::kIOPropertyMediumTypeKey)), ) }?; @@ -118,18 +106,9 @@ pub(crate) fn get_disk_type(bsd_name: &[u8]) -> Option { /// Returns a tuple consisting of the total number of bytes read and written by the specified disk pub(crate) fn get_disk_io(bsd_name: &[u8]) -> Option<(u64, u64)> { - let stat_string = unsafe { - CFReleaser::new(cfs::CFStringCreateWithBytesNoCopy( - kCFAllocatorDefault, - ffi::kIOBlockStorageDriverStatisticsKey.as_ptr(), - ffi::kIOBlockStorageDriverStatisticsKey.len() as _, - cfs::kCFStringEncodingUTF8, - false as _, - kCFAllocatorNull, - ))? - }; - - iterate_service_tree(bsd_name, stat_string, |parent_entry, properties| { + let stat_string = CFString::from_static_str(ffi::kIOBlockStorageDriverStatisticsKey); + + iterate_service_tree(bsd_name, &stat_string, |parent_entry, properties| { if unsafe { ffi::IOObjectConformsTo(parent_entry, b"IOBlockStorageDriver\0".as_ptr() as *const _) } == 0 @@ -139,12 +118,16 @@ pub(crate) fn get_disk_io(bsd_name: &[u8]) -> Option<(u64, u64)> { unsafe { let read_bytes = super::disk::get_int_value( - properties.inner(), - DictKey::Defined(ffi::kIOBlockStorageDriverStatisticsBytesReadKey), + properties, + Some(&CFString::from_static_str( + ffi::kIOBlockStorageDriverStatisticsBytesReadKey, + )), )?; let written_bytes = super::disk::get_int_value( - properties.inner(), - DictKey::Defined(ffi::kIOBlockStorageDriverStatisticsBytesWrittenKey), + properties, + Some(&CFString::from_static_str( + ffi::kIOBlockStorageDriverStatisticsBytesWrittenKey, + )), )?; Some((read_bytes, written_bytes)) diff --git a/src/unix/apple/macos/ffi.rs b/src/unix/apple/macos/ffi.rs index 0bc21c83d..8e14eaa0d 100644 --- a/src/unix/apple/macos/ffi.rs +++ b/src/unix/apple/macos/ffi.rs @@ -8,11 +8,9 @@ any(target_arch = "x86", target_arch = "x86_64") ), ))] -use core_foundation_sys::base::mach_port_t; +use libc::mach_port_t; #[cfg(any(feature = "system", feature = "disk"))] -use core_foundation_sys::base::CFAllocatorRef; -#[cfg(any(feature = "system", feature = "disk"))] -use core_foundation_sys::dictionary::CFDictionaryRef; +use objc2_core_foundation::CFAllocator; #[cfg(any( feature = "system", feature = "disk", @@ -21,9 +19,12 @@ use core_foundation_sys::dictionary::CFDictionaryRef; any(target_arch = "x86", target_arch = "x86_64") ), ))] -use core_foundation_sys::dictionary::CFMutableDictionaryRef; +use objc2_core_foundation::CFMutableDictionary; +#[cfg(any(feature = "system", feature = "disk"))] +use objc2_core_foundation::CFString; #[cfg(any(feature = "system", feature = "disk"))] -use core_foundation_sys::string::CFStringRef; +use objc2_core_foundation::CFType; +use std::ptr::NonNull; use libc::c_char; #[cfg(any( @@ -120,7 +121,7 @@ pub const kIOMasterPortDefault: mach_port_t = 0; extern "C" { pub fn IOServiceGetMatchingServices( mainPort: mach_port_t, - matching: CFMutableDictionaryRef, + matching: NonNull, // CF_RELEASES_ARGUMENT existing: *mut io_iterator_t, ) -> kern_return_t; #[cfg(all( @@ -133,7 +134,7 @@ extern "C" { ), ), ))] - pub fn IOServiceMatching(a: *const c_char) -> CFMutableDictionaryRef; + pub fn IOServiceMatching(a: *const c_char) -> Option>; // CF_RETURNS_RETAINED pub fn IOIteratorNext(iterator: io_iterator_t) -> io_object_t; @@ -142,10 +143,10 @@ extern "C" { #[cfg(any(feature = "system", feature = "disk"))] pub fn IORegistryEntryCreateCFProperty( entry: io_registry_entry_t, - key: CFStringRef, - allocator: CFAllocatorRef, + key: &CFString, + allocator: Option<&CFAllocator>, options: IOOptionBits, - ) -> CFDictionaryRef; + ) -> Option>; #[cfg(feature = "disk")] pub fn IORegistryEntryGetParentEntry( entry: io_registry_entry_t, @@ -157,7 +158,7 @@ extern "C" { mainPort: mach_port_t, options: u32, bsdName: *const c_char, - ) -> CFMutableDictionaryRef; + ) -> Option>; // CF_RETURNS_RETAINED #[cfg(all(feature = "system", not(feature = "apple-sandbox")))] pub fn IORegistryEntryGetName(entry: io_registry_entry_t, name: io_name_t) -> kern_return_t; #[cfg(feature = "disk")] @@ -285,31 +286,36 @@ pub use io_service::*; target_arch = "aarch64" ))] mod io_service { - use std::ptr::null; + use std::ptr::NonNull; - use core_foundation_sys::array::CFArrayRef; - use core_foundation_sys::base::{CFAllocatorRef, CFRelease}; - use core_foundation_sys::dictionary::{ - kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryValueCallBacks, CFDictionaryCreate, - CFDictionaryRef, + use objc2_core_foundation::{ + cf_type, kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryValueCallBacks, CFAllocator, + CFArray, CFDictionary, CFDictionaryCreate, CFNumber, CFRetained, CFString, }; - use core_foundation_sys::number::{kCFNumberSInt32Type, CFNumberCreate}; - use core_foundation_sys::string::{CFStringCreateWithCStringNoCopy, CFStringRef}; #[repr(C)] - pub struct __IOHIDServiceClient(libc::c_void); + pub struct IOHIDServiceClient(libc::c_void); - pub type IOHIDServiceClientRef = *const __IOHIDServiceClient; + cf_type!( + #[encoding_name = "__IOHIDServiceClient"] + unsafe impl IOHIDServiceClient {} + ); #[repr(C)] - pub struct __IOHIDEventSystemClient(libc::c_void); + pub struct IOHIDEventSystemClient(libc::c_void); - pub type IOHIDEventSystemClientRef = *const __IOHIDEventSystemClient; + cf_type!( + #[encoding_name = "__IOHIDEventSystemClient"] + unsafe impl IOHIDEventSystemClient {} + ); #[repr(C)] - pub struct __IOHIDEvent(libc::c_void); + pub struct IOHIDEvent(libc::c_void); - pub type IOHIDEventRef = *const __IOHIDEvent; + cf_type!( + #[encoding_name = "__IOHIDEvent"] + unsafe impl IOHIDEvent {} + ); #[allow(non_upper_case_globals)] pub const kIOHIDEventTypeTemperature: i64 = 15; @@ -322,35 +328,38 @@ mod io_service { #[cfg(not(feature = "apple-sandbox"))] extern "C" { - pub fn IOHIDEventSystemClientCreate(allocator: CFAllocatorRef) - -> IOHIDEventSystemClientRef; + pub fn IOHIDEventSystemClientCreate( + allocator: Option<&CFAllocator>, + ) -> Option>; pub fn IOHIDEventSystemClientSetMatching( - client: IOHIDEventSystemClientRef, - matches: CFDictionaryRef, + client: &IOHIDEventSystemClient, + matches: &CFDictionary, ) -> i32; - pub fn IOHIDEventSystemClientCopyServices(client: IOHIDEventSystemClientRef) -> CFArrayRef; + pub fn IOHIDEventSystemClientCopyServices( + client: &IOHIDEventSystemClient, + ) -> Option>; pub fn IOHIDServiceClientCopyProperty( - service: IOHIDServiceClientRef, - key: CFStringRef, - ) -> CFStringRef; + service: &IOHIDServiceClient, + key: &CFString, + ) -> Option>; pub fn IOHIDServiceClientCopyEvent( - service: IOHIDServiceClientRef, + service: &IOHIDServiceClient, v0: i64, v1: i32, v2: i64, - ) -> IOHIDEventRef; + ) -> Option>; - pub fn IOHIDEventGetFloatValue(event: IOHIDEventRef, field: i64) -> f64; + pub fn IOHIDEventGetFloatValue(event: &IOHIDEvent, field: i64) -> f64; } - pub(crate) const HID_DEVICE_PROPERTY_PRODUCT: &[u8] = b"Product\0"; + pub(crate) const HID_DEVICE_PROPERTY_PRODUCT: &str = "Product"; - 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"; + pub(crate) const HID_DEVICE_PROPERTY_PRIMARY_USAGE: &str = "PrimaryUsage"; + pub(crate) const HID_DEVICE_PROPERTY_PRIMARY_USAGE_PAGE: &str = "PrimaryUsagePage"; #[allow(non_upper_case_globals)] pub(crate) const kHIDPage_AppleVendor: i32 = 0xff00; @@ -358,46 +367,23 @@ mod io_service { #[allow(non_upper_case_globals)] pub(crate) const kHIDUsage_AppleVendor_TemperatureSensor: i32 = 0x0005; - pub(crate) fn matching(page: i32, usage: i32) -> CFDictionaryRef { + pub(crate) fn matching(page: i32, usage: i32) -> Option> { unsafe { let keys = [ - CFStringCreateWithCStringNoCopy( - null() as *const _, - HID_DEVICE_PROPERTY_PRIMARY_USAGE_PAGE.as_ptr() as *const _, - core_foundation_sys::string::kCFStringEncodingUTF8, - core_foundation_sys::base::kCFAllocatorNull as *mut _, - ), - CFStringCreateWithCStringNoCopy( - null() as *const _, - HID_DEVICE_PROPERTY_PRIMARY_USAGE.as_ptr() as *const _, - core_foundation_sys::string::kCFStringEncodingUTF8, - core_foundation_sys::base::kCFAllocatorNull as *mut _, - ), + CFString::from_static_str(HID_DEVICE_PROPERTY_PRIMARY_USAGE_PAGE), + CFString::from_static_str(HID_DEVICE_PROPERTY_PRIMARY_USAGE), ]; - let nums = [ - CFNumberCreate(null(), kCFNumberSInt32Type, &page as *const _ as *const _), - CFNumberCreate(null(), kCFNumberSInt32Type, &usage as *const _ as *const _), - ]; + let nums = [CFNumber::new_i32(page), CFNumber::new_i32(usage)]; - let dict = CFDictionaryCreate( - null(), - &keys as *const _ as *const _, - &nums as *const _ as *const _, + CFDictionaryCreate( + None, + &keys as *const _ as *mut _, + &nums as *const _ as *mut _, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks, - ); - - for key in keys { - CFRelease(key as _); - } - - for num in nums { - CFRelease(num as _); - } - - dict + ) } } } diff --git a/src/unix/apple/utils.rs b/src/unix/apple/utils.rs index dc7b28bd7..ed527ab9c 100644 --- a/src/unix/apple/utils.rs +++ b/src/unix/apple/utils.rs @@ -1,51 +1,5 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use core_foundation_sys::base::CFRelease; -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)] -#[allow(dead_code)] -pub(crate) struct CFReleaser(NonNull); - -#[allow(dead_code)] -impl CFReleaser { - pub(crate) fn new(ptr: *const T) -> Option { - // 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 Drop for CFReleaser { - 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 Send for CFReleaser {} -unsafe impl Sync for CFReleaser {} - -#[cfg(feature = "disk")] -pub(crate) fn vec_to_rust(buf: Vec) -> Option { - String::from_utf8( - buf.into_iter() - .flat_map(|b| if b > 0 { Some(b as u8) } else { None }) - .collect(), - ) - .ok() -} - #[cfg(feature = "system")] pub(crate) unsafe fn get_sys_value( mut len: usize,