// Take a look at the license at the top of the repository in the LICENSE file. use crate::{Pid, Process}; use libc::{c_char, c_int, timeval}; use std::cell::UnsafeCell; use std::collections::HashMap; use std::ffi::CStr; use std::mem; use std::time::SystemTime; /// This struct is used to switch between the "old" and "new" every time you use "get_mut". #[derive(Debug)] pub(crate) struct VecSwitcher { v1: Vec, v2: Vec, first: bool, } impl VecSwitcher { pub fn new(v1: Vec) -> Self { let v2 = v1.clone(); Self { v1, v2, first: true, } } pub fn get_mut(&mut self) -> &mut [T] { self.first = !self.first; if self.first { // It means that `v2` will be the "new". &mut self.v2 } else { // It means that `v1` will be the "new". &mut self.v1 } } pub fn get_old(&self) -> &[T] { if self.first { &self.v1 } else { &self.v2 } } pub fn get_new(&self) -> &[T] { if self.first { &self.v2 } else { &self.v1 } } } #[inline] pub unsafe fn init_mib(name: &[u8], mib: &mut [c_int]) { let mut len = mib.len(); libc::sysctlnametomib(name.as_ptr() as _, mib.as_mut_ptr(), &mut len); } pub(crate) fn boot_time() -> u64 { let mut boot_time = timeval { tv_sec: 0, tv_usec: 0, }; let mut len = std::mem::size_of::(); let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME]; unsafe { if libc::sysctl( mib.as_mut_ptr(), mib.len() as _, &mut boot_time as *mut timeval as *mut _, &mut len, std::ptr::null_mut(), 0, ) < 0 { 0 } else { boot_time.tv_sec as _ } } } pub(crate) unsafe fn get_sys_value(mib: &[c_int], value: &mut T) -> bool { let mut len = mem::size_of::() as libc::size_t; libc::sysctl( mib.as_ptr(), mib.len() as _, value as *mut _ as *mut _, &mut len, std::ptr::null_mut(), 0, ) == 0 } pub(crate) unsafe fn get_sys_value_array(mib: &[c_int], value: &mut [T]) -> bool { let mut len = (mem::size_of::() * value.len()) as libc::size_t; libc::sysctl( mib.as_ptr(), mib.len() as _, value.as_mut_ptr() as *mut _, &mut len as *mut _, std::ptr::null_mut(), 0, ) == 0 } pub(crate) fn c_buf_to_str(buf: &[libc::c_char]) -> Option<&str> { unsafe { let buf: &[u8] = std::slice::from_raw_parts(buf.as_ptr() as _, buf.len()); if let Some(pos) = buf.iter().position(|x| *x == 0) { // Shrink buffer to terminate the null bytes std::str::from_utf8(&buf[..pos]).ok() } else { std::str::from_utf8(buf).ok() } } } pub(crate) fn c_buf_to_string(buf: &[libc::c_char]) -> Option { c_buf_to_str(buf).map(|s| s.to_owned()) } pub(crate) unsafe fn get_sys_value_str(mib: &[c_int], buf: &mut [libc::c_char]) -> Option { let mut len = (mem::size_of::() * buf.len()) as libc::size_t; if libc::sysctl( mib.as_ptr(), mib.len() as _, buf.as_mut_ptr() as *mut _, &mut len, std::ptr::null_mut(), 0, ) != 0 { return None; } c_buf_to_string(&buf[..len / mem::size_of::()]) } pub(crate) unsafe fn get_sys_value_by_name(name: &[u8], value: &mut T) -> bool { let mut len = mem::size_of::() as libc::size_t; let original = len; libc::sysctlbyname( name.as_ptr() as *const c_char, value as *mut _ as *mut _, &mut len, std::ptr::null_mut(), 0, ) == 0 && original == len } pub(crate) fn get_sys_value_str_by_name(name: &[u8]) -> Option { let mut size = 0; unsafe { if libc::sysctlbyname( name.as_ptr() as *const c_char, std::ptr::null_mut(), &mut size, std::ptr::null_mut(), 0, ) == 0 && size > 0 { // now create a buffer with the size and get the real value let mut buf: Vec = vec![0; size as _]; if libc::sysctlbyname( name.as_ptr() as *const c_char, buf.as_mut_ptr() as *mut _, &mut size, std::ptr::null_mut(), 0, ) == 0 && size > 0 { c_buf_to_string(&buf) } else { // getting the system value failed None } } else { None } } } pub(crate) fn get_system_info(mib: &[c_int], default: Option<&str>) -> Option { let mut size = 0; unsafe { // Call first to get size libc::sysctl( mib.as_ptr(), mib.len() as _, std::ptr::null_mut(), &mut size, std::ptr::null_mut(), 0, ); // exit early if we did not update the size if size == 0 { default.map(|s| s.to_owned()) } else { // set the buffer to the correct size let mut buf: Vec = vec![0; size as _]; if libc::sysctl( mib.as_ptr(), mib.len() as _, buf.as_mut_ptr() as _, &mut size, std::ptr::null_mut(), 0, ) == -1 { // If command fails return default default.map(|s| s.to_owned()) } else { c_buf_to_string(&buf) } } } } pub(crate) unsafe fn from_cstr_array(ptr: *const *const c_char) -> Vec { if ptr.is_null() { return Vec::new(); } let mut max = 0; loop { let ptr = ptr.add(max); if (*ptr).is_null() { break; } max += 1; } if max == 0 { return Vec::new(); } let mut ret = Vec::with_capacity(max); for pos in 0..max { let p = ptr.add(pos); if let Ok(s) = CStr::from_ptr(*p).to_str() { ret.push(s.to_owned()); } } ret } pub(crate) fn get_now() -> u64 { SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .map(|n| n.as_secs()) .unwrap_or(0) } // All this is needed because `kinfo_proc` doesn't implement `Send` (because it contains pointers). pub(crate) struct WrapMap<'a>(pub UnsafeCell<&'a mut HashMap>); unsafe impl<'a> Send for WrapMap<'a> {} unsafe impl<'a> Sync for WrapMap<'a> {} #[repr(transparent)] pub(crate) struct KInfoProc(libc::kinfo_proc); unsafe impl Send for KInfoProc {} unsafe impl Sync for KInfoProc {} impl std::ops::Deref for KInfoProc { type Target = libc::kinfo_proc; fn deref(&self) -> &Self::Target { &self.0 } } pub(crate) unsafe fn get_frequency_for_cpu(cpu_nb: c_int) -> u64 { let mut frequency = 0; // The information can be missing if it's running inside a VM. if !get_sys_value_by_name( format!("dev.cpu.{cpu_nb}.freq\0").as_bytes(), &mut frequency, ) { frequency = 0; } frequency as _ }