diff options
Diffstat (limited to 'vendor/sysinfo/src/apple/system.rs')
-rw-r--r-- | vendor/sysinfo/src/apple/system.rs | 763 |
1 files changed, 763 insertions, 0 deletions
diff --git a/vendor/sysinfo/src/apple/system.rs b/vendor/sysinfo/src/apple/system.rs new file mode 100644 index 000000000..abe617dee --- /dev/null +++ b/vendor/sysinfo/src/apple/system.rs @@ -0,0 +1,763 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::sys::component::Component; +use crate::sys::cpu::*; +use crate::sys::disk::*; +#[cfg(target_os = "macos")] +use crate::sys::ffi; +use crate::sys::network::Networks; +use crate::sys::process::*; +#[cfg(target_os = "macos")] +use core_foundation_sys::base::{kCFAllocatorDefault, CFRelease}; + +use crate::{ + CpuExt, CpuRefreshKind, LoadAvg, Pid, ProcessRefreshKind, RefreshKind, SystemExt, User, +}; + +#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] +use crate::ProcessExt; + +use std::cell::UnsafeCell; +use std::collections::HashMap; +use std::mem; +use std::sync::Arc; +#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] +use std::time::SystemTime; + +#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] +use libc::size_t; + +use libc::{ + c_char, c_int, c_void, host_statistics64, mach_port_t, sysconf, sysctl, sysctlbyname, timeval, + vm_statistics64, _SC_PAGESIZE, +}; + +#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] +declare_signals! { + c_int, + Signal::Hangup => libc::SIGHUP, + Signal::Interrupt => libc::SIGINT, + Signal::Quit => libc::SIGQUIT, + Signal::Illegal => libc::SIGILL, + Signal::Trap => libc::SIGTRAP, + Signal::Abort => libc::SIGABRT, + Signal::IOT => libc::SIGIOT, + Signal::Bus => libc::SIGBUS, + Signal::FloatingPointException => libc::SIGFPE, + Signal::Kill => libc::SIGKILL, + Signal::User1 => libc::SIGUSR1, + Signal::Segv => libc::SIGSEGV, + Signal::User2 => libc::SIGUSR2, + Signal::Pipe => libc::SIGPIPE, + Signal::Alarm => libc::SIGALRM, + Signal::Term => libc::SIGTERM, + Signal::Child => libc::SIGCHLD, + Signal::Continue => libc::SIGCONT, + Signal::Stop => libc::SIGSTOP, + Signal::TSTP => libc::SIGTSTP, + Signal::TTIN => libc::SIGTTIN, + Signal::TTOU => libc::SIGTTOU, + Signal::Urgent => libc::SIGURG, + Signal::XCPU => libc::SIGXCPU, + Signal::XFSZ => libc::SIGXFSZ, + Signal::VirtualAlarm => libc::SIGVTALRM, + Signal::Profiling => libc::SIGPROF, + Signal::Winch => libc::SIGWINCH, + Signal::IO => libc::SIGIO, + // SIGPOLL doesn't exist on apple targets but since it's an equivalent of SIGIO on unix, + // we simply use the SIGIO constant. + Signal::Poll => libc::SIGIO, + Signal::Sys => libc::SIGSYS, + _ => None, +} +#[cfg(any(target_os = "ios", feature = "apple-sandbox"))] +declare_signals! { + c_int, + _ => None, +} + +#[doc = include_str!("../../md_doc/system.md")] +pub struct System { + process_list: HashMap<Pid, Process>, + mem_total: u64, + mem_free: u64, + mem_available: u64, + swap_total: u64, + swap_free: u64, + global_cpu: Cpu, + cpus: Vec<Cpu>, + page_size_kb: u64, + components: Vec<Component>, + // Used to get CPU information, not supported on iOS, or inside the default macOS sandbox. + #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] + connection: Option<ffi::io_connect_t>, + disks: Vec<Disk>, + networks: Networks, + port: mach_port_t, + users: Vec<User>, + boot_time: u64, + // Used to get disk information, to be more specific, it's needed by the + // DADiskCreateFromVolumePath function. Not supported on iOS. + #[cfg(target_os = "macos")] + session: ffi::SessionWrap, + #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] + clock_info: Option<crate::sys::macos::system::SystemTimeInfo>, + got_cpu_frequency: bool, +} + +impl Drop for System { + fn drop(&mut self) { + #[cfg(target_os = "macos")] + unsafe { + #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] + if let Some(conn) = self.connection { + ffi::IOServiceClose(conn); + } + + if !self.session.0.is_null() { + CFRelease(self.session.0 as _); + } + } + } +} + +pub(crate) struct Wrap<'a>(pub UnsafeCell<&'a mut HashMap<Pid, Process>>); + +unsafe impl<'a> Send for Wrap<'a> {} +unsafe impl<'a> Sync for Wrap<'a> {} + +#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] +impl System { + fn clear_procs(&mut self) { + use crate::sys::macos::process; + + self.process_list + .retain(|_, proc_| process::has_been_updated(proc_)); + } +} + +fn boot_time() -> u64 { + let mut boot_time = timeval { + tv_sec: 0, + tv_usec: 0, + }; + let mut len = std::mem::size_of::<timeval>(); + let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME]; + + unsafe { + if sysctl( + mib.as_mut_ptr(), + mib.len() as _, + &mut boot_time as *mut timeval as *mut _, + &mut len, + std::ptr::null_mut(), + 0, + ) < 0 + { + 0 + } else { + boot_time.tv_sec as _ + } + } +} + +#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] +fn get_now() -> u64 { + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .map(|n| n.as_secs()) + .unwrap_or(0) +} + +impl SystemExt for System { + const IS_SUPPORTED: bool = true; + const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals(); + + fn new_with_specifics(refreshes: RefreshKind) -> System { + unsafe { + let port = libc::mach_host_self(); + + let mut s = System { + process_list: HashMap::with_capacity(200), + mem_total: 0, + mem_free: 0, + mem_available: 0, + swap_total: 0, + swap_free: 0, + global_cpu: Cpu::new( + "0".to_owned(), + Arc::new(CpuData::new(std::ptr::null_mut(), 0)), + 0, + String::new(), + String::new(), + ), + cpus: Vec::new(), + page_size_kb: sysconf(_SC_PAGESIZE) as u64 / 1_000, + components: Vec::with_capacity(2), + #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] + connection: get_io_service_connection(), + disks: Vec::with_capacity(1), + networks: Networks::new(), + port, + users: Vec::new(), + boot_time: boot_time(), + #[cfg(target_os = "macos")] + session: ffi::SessionWrap(::std::ptr::null_mut()), + #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] + clock_info: crate::sys::macos::system::SystemTimeInfo::new(port), + got_cpu_frequency: false, + }; + s.refresh_specifics(refreshes); + s + } + } + + fn refresh_memory(&mut self) { + let mut mib = [0, 0]; + + unsafe { + // get system values + // get swap info + let mut xs: libc::xsw_usage = mem::zeroed::<libc::xsw_usage>(); + if get_sys_value( + libc::CTL_VM as _, + libc::VM_SWAPUSAGE as _, + mem::size_of::<libc::xsw_usage>(), + &mut xs as *mut _ as *mut c_void, + &mut mib, + ) { + self.swap_total = xs.xsu_total / 1_000; + self.swap_free = xs.xsu_avail / 1_000; + } + // get ram info + if self.mem_total < 1 { + get_sys_value( + libc::CTL_HW as _, + libc::HW_MEMSIZE as _, + mem::size_of::<u64>(), + &mut self.mem_total as *mut u64 as *mut c_void, + &mut mib, + ); + self.mem_total /= 1_000; + } + let mut count: u32 = libc::HOST_VM_INFO64_COUNT as _; + let mut stat = mem::zeroed::<vm_statistics64>(); + if host_statistics64( + self.port, + libc::HOST_VM_INFO64, + &mut stat as *mut vm_statistics64 as *mut _, + &mut count, + ) == libc::KERN_SUCCESS + { + // From the apple documentation: + // + // /* + // * NB: speculative pages are already accounted for in "free_count", + // * so "speculative_count" is the number of "free" pages that are + // * used to hold data that was read speculatively from disk but + // * haven't actually been used by anyone so far. + // */ + self.mem_available = self + .mem_total + .saturating_sub( + u64::from(stat.active_count) + .saturating_add(u64::from(stat.inactive_count)) + .saturating_add(u64::from(stat.wire_count)) + .saturating_add(u64::from(stat.speculative_count)) + .saturating_sub(u64::from(stat.purgeable_count)), + ) + .saturating_mul(self.page_size_kb); + self.mem_free = u64::from(stat.free_count).saturating_mul(self.page_size_kb); + } + } + } + + #[cfg(any(target_os = "ios", feature = "apple-sandbox"))] + fn refresh_components_list(&mut self) {} + + #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] + fn refresh_components_list(&mut self) { + if let Some(con) = self.connection { + self.components.clear(); + // getting CPU critical temperature + let critical_temp = crate::apple::component::get_temperature( + con, + &['T' as i8, 'C' as i8, '0' as i8, 'D' as i8, 0], + ); + + for (id, v) in crate::apple::component::COMPONENTS_TEMPERATURE_IDS.iter() { + if let Some(c) = Component::new((*id).to_owned(), None, critical_temp, v, con) { + self.components.push(c); + } + } + } + } + + fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind) { + let cpus = &mut self.cpus; + if cpus.is_empty() { + init_cpus(self.port, cpus, &mut self.global_cpu, refresh_kind); + self.got_cpu_frequency = refresh_kind.frequency(); + return; + } + if refresh_kind.frequency() && !self.got_cpu_frequency { + let frequency = get_cpu_frequency(); + for proc_ in cpus.iter_mut() { + proc_.set_frequency(frequency); + } + self.got_cpu_frequency = true; + } + if refresh_kind.cpu_usage() { + update_cpu_usage(self.port, &mut self.global_cpu, |proc_data, cpu_info| { + let mut percentage = 0f32; + let mut offset = 0; + for proc_ in cpus.iter_mut() { + let cpu_usage = compute_usage_of_cpu(proc_, cpu_info, offset); + proc_.update(cpu_usage, Arc::clone(&proc_data)); + percentage += proc_.cpu_usage(); + + offset += libc::CPU_STATE_MAX as isize; + } + (percentage, cpus.len()) + }); + } + } + + #[cfg(any(target_os = "ios", feature = "apple-sandbox"))] + fn refresh_processes_specifics(&mut self, _refresh_kind: ProcessRefreshKind) {} + + #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] + fn refresh_processes_specifics(&mut self, refresh_kind: ProcessRefreshKind) { + use crate::utils::into_iter; + + unsafe { + let count = libc::proc_listallpids(::std::ptr::null_mut(), 0); + if count < 1 { + return; + } + } + if let Some(pids) = get_proc_list() { + let now = get_now(); + let arg_max = get_arg_max(); + let port = self.port; + let time_interval = self.clock_info.as_mut().map(|c| c.get_time_interval(port)); + let entries: Vec<Process> = { + let wrap = &Wrap(UnsafeCell::new(&mut self.process_list)); + + #[cfg(feature = "multithread")] + use rayon::iter::ParallelIterator; + + into_iter(pids) + .flat_map(|pid| { + match update_process( + wrap, + pid, + arg_max as size_t, + time_interval, + now, + refresh_kind, + ) { + Ok(x) => x, + _ => None, + } + }) + .collect() + }; + entries.into_iter().for_each(|entry| { + self.process_list.insert(entry.pid(), entry); + }); + self.clear_procs(); + } + } + + #[cfg(any(target_os = "ios", feature = "apple-sandbox"))] + fn refresh_process_specifics(&mut self, _pid: Pid, _refresh_kind: ProcessRefreshKind) -> bool { + false + } + + #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] + fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool { + let now = get_now(); + let arg_max = get_arg_max(); + let port = self.port; + let time_interval = self.clock_info.as_mut().map(|c| c.get_time_interval(port)); + match { + let wrap = Wrap(UnsafeCell::new(&mut self.process_list)); + update_process( + &wrap, + pid, + arg_max as size_t, + time_interval, + now, + refresh_kind, + ) + } { + Ok(Some(p)) => { + self.process_list.insert(p.pid(), p); + true + } + Ok(_) => true, + Err(_) => false, + } + } + + #[cfg(target_os = "ios")] + fn refresh_disks_list(&mut self) {} + + #[cfg(target_os = "macos")] + fn refresh_disks_list(&mut self) { + unsafe { + if self.session.0.is_null() { + self.session.0 = ffi::DASessionCreate(kCFAllocatorDefault as _); + } + self.disks = get_disks(self.session.0); + } + } + + fn refresh_users_list(&mut self) { + self.users = crate::apple::users::get_users_list(); + } + + // COMMON PART + // + // Need to be moved into a "common" file to avoid duplication. + + fn processes(&self) -> &HashMap<Pid, Process> { + &self.process_list + } + + fn process(&self, pid: Pid) -> Option<&Process> { + self.process_list.get(&pid) + } + + fn global_cpu_info(&self) -> &Cpu { + &self.global_cpu + } + + fn cpus(&self) -> &[Cpu] { + &self.cpus + } + + fn physical_core_count(&self) -> Option<usize> { + let mut physical_core_count = 0; + + unsafe { + if get_sys_value_by_name( + b"hw.physicalcpu\0", + &mut mem::size_of::<u32>(), + &mut physical_core_count as *mut usize as *mut c_void, + ) { + Some(physical_core_count) + } else { + None + } + } + } + + fn networks(&self) -> &Networks { + &self.networks + } + + fn networks_mut(&mut self) -> &mut Networks { + &mut self.networks + } + + fn total_memory(&self) -> u64 { + self.mem_total + } + + fn free_memory(&self) -> u64 { + self.mem_free + } + + fn available_memory(&self) -> u64 { + self.mem_available + } + + fn used_memory(&self) -> u64 { + self.mem_total - self.mem_free + } + + fn total_swap(&self) -> u64 { + self.swap_total + } + + fn free_swap(&self) -> u64 { + self.swap_free + } + + // TODO: need to be checked + fn used_swap(&self) -> u64 { + self.swap_total - self.swap_free + } + + fn components(&self) -> &[Component] { + &self.components + } + + fn components_mut(&mut self) -> &mut [Component] { + &mut self.components + } + + fn disks(&self) -> &[Disk] { + &self.disks + } + + fn disks_mut(&mut self) -> &mut [Disk] { + &mut self.disks + } + + fn uptime(&self) -> u64 { + unsafe { + let csec = libc::time(::std::ptr::null_mut()); + + libc::difftime(csec, self.boot_time as _) as u64 + } + } + + fn load_average(&self) -> LoadAvg { + let mut loads = vec![0f64; 3]; + + unsafe { + libc::getloadavg(loads.as_mut_ptr(), 3); + LoadAvg { + one: loads[0], + five: loads[1], + fifteen: loads[2], + } + } + } + + fn users(&self) -> &[User] { + &self.users + } + + fn boot_time(&self) -> u64 { + self.boot_time + } + + fn name(&self) -> Option<String> { + get_system_info(libc::KERN_OSTYPE, Some("Darwin")) + } + + fn long_os_version(&self) -> Option<String> { + #[cfg(target_os = "macos")] + let friendly_name = match self.os_version().unwrap_or_default() { + f_n if f_n.starts_with("10.16") + | f_n.starts_with("11.0") + | f_n.starts_with("11.1") + | f_n.starts_with("11.2") => + { + "Big Sur" + } + f_n if f_n.starts_with("10.15") => "Catalina", + f_n if f_n.starts_with("10.14") => "Mojave", + f_n if f_n.starts_with("10.13") => "High Sierra", + f_n if f_n.starts_with("10.12") => "Sierra", + f_n if f_n.starts_with("10.11") => "El Capitan", + f_n if f_n.starts_with("10.10") => "Yosemite", + f_n if f_n.starts_with("10.9") => "Mavericks", + f_n if f_n.starts_with("10.8") => "Mountain Lion", + f_n if f_n.starts_with("10.7") => "Lion", + f_n if f_n.starts_with("10.6") => "Snow Leopard", + f_n if f_n.starts_with("10.5") => "Leopard", + f_n if f_n.starts_with("10.4") => "Tiger", + f_n if f_n.starts_with("10.3") => "Panther", + f_n if f_n.starts_with("10.2") => "Jaguar", + f_n if f_n.starts_with("10.1") => "Puma", + f_n if f_n.starts_with("10.0") => "Cheetah", + _ => "", + }; + + #[cfg(target_os = "macos")] + let long_name = Some(format!( + "MacOS {} {}", + self.os_version().unwrap_or_default(), + friendly_name + )); + + #[cfg(target_os = "ios")] + let long_name = Some(format!("iOS {}", self.os_version().unwrap_or_default())); + + long_name + } + + fn host_name(&self) -> Option<String> { + get_system_info(libc::KERN_HOSTNAME, None) + } + + fn kernel_version(&self) -> Option<String> { + get_system_info(libc::KERN_OSRELEASE, None) + } + + fn os_version(&self) -> Option<String> { + unsafe { + // get the size for the buffer first + let mut size = 0; + if get_sys_value_by_name(b"kern.osproductversion\0", &mut size, std::ptr::null_mut()) + && size > 0 + { + // now create a buffer with the size and get the real value + let mut buf = vec![0_u8; size as usize]; + + if get_sys_value_by_name( + b"kern.osproductversion\0", + &mut size, + buf.as_mut_ptr() as *mut c_void, + ) { + if let Some(pos) = buf.iter().position(|x| *x == 0) { + // Shrink buffer to terminate the null bytes + buf.resize(pos, 0); + } + + String::from_utf8(buf).ok() + } else { + // getting the system value failed + None + } + } else { + // getting the system value failed, or did not return a buffer size + None + } + } + } +} + +impl Default for System { + fn default() -> System { + System::new() + } +} + +// code from https://github.com/Chris911/iStats +// Not supported on iOS, or in the default macOS +#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] +fn get_io_service_connection() -> Option<ffi::io_connect_t> { + let mut master_port: mach_port_t = 0; + let mut iterator: ffi::io_iterator_t = 0; + + unsafe { + ffi::IOMasterPort(libc::MACH_PORT_NULL, &mut master_port); + + let matching_dictionary = ffi::IOServiceMatching(b"AppleSMC\0".as_ptr() as *const i8); + let result = + ffi::IOServiceGetMatchingServices(master_port, matching_dictionary, &mut iterator); + if result != ffi::KIO_RETURN_SUCCESS { + sysinfo_debug!("Error: IOServiceGetMatchingServices() = {}", result); + return None; + } + + let device = ffi::IOIteratorNext(iterator); + ffi::IOObjectRelease(iterator); + if device == 0 { + sysinfo_debug!("Error: no SMC found"); + return None; + } + + let mut conn = 0; + let result = ffi::IOServiceOpen(device, libc::mach_task_self(), 0, &mut conn); + ffi::IOObjectRelease(device); + if result != ffi::KIO_RETURN_SUCCESS { + sysinfo_debug!("Error: IOServiceOpen() = {}", result); + return None; + } + + Some(conn) + } +} + +#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] +fn get_arg_max() -> usize { + let mut mib = [libc::CTL_KERN, libc::KERN_ARGMAX]; + let mut arg_max = 0i32; + let mut size = mem::size_of::<c_int>(); + unsafe { + if sysctl( + mib.as_mut_ptr(), + mib.len() as _, + (&mut arg_max) as *mut i32 as *mut c_void, + &mut size, + std::ptr::null_mut(), + 0, + ) == -1 + { + 4096 // We default to this value + } else { + arg_max as usize + } + } +} + +pub(crate) unsafe fn get_sys_value( + high: u32, + low: u32, + mut len: usize, + value: *mut c_void, + mib: &mut [i32; 2], +) -> bool { + mib[0] = high as i32; + mib[1] = low as i32; + sysctl( + mib.as_mut_ptr(), + mib.len() as _, + value, + &mut len as *mut usize, + std::ptr::null_mut(), + 0, + ) == 0 +} + +unsafe fn get_sys_value_by_name(name: &[u8], len: &mut usize, value: *mut c_void) -> bool { + sysctlbyname( + name.as_ptr() as *const c_char, + value, + len, + std::ptr::null_mut(), + 0, + ) == 0 +} + +fn get_system_info(value: c_int, default: Option<&str>) -> Option<String> { + let mut mib: [c_int; 2] = [libc::CTL_KERN, value]; + let mut size = 0; + + unsafe { + // Call first to get size + sysctl( + mib.as_mut_ptr(), + mib.len() as _, + std::ptr::null_mut(), + &mut size, + std::ptr::null_mut(), + 0, + ); + + // exit early if we did not update the size + if size == 0 { + default.map(|s| s.to_owned()) + } else { + // set the buffer to the correct size + let mut buf = vec![0_u8; size as usize]; + + if sysctl( + mib.as_mut_ptr(), + mib.len() as _, + buf.as_mut_ptr() as _, + &mut size, + std::ptr::null_mut(), + 0, + ) == -1 + { + // If command fails return default + default.map(|s| s.to_owned()) + } else { + if let Some(pos) = buf.iter().position(|x| *x == 0) { + // Shrink buffer to terminate the null bytes + buf.resize(pos, 0); + } + + String::from_utf8(buf).ok() + } + } + } +} |