#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)] #![allow(dead_code)]
use std::io;
use std::mem::size_of;
use std::os::raw::{c_char, c_int, c_short, c_uchar, c_uint, c_ushort, c_void};
use std::ptr;
use crate::ported::crt::CRT_fatalError;
use crate::ported::darwin::darwinmachine::DarwinMachine;
use crate::ported::darwin::darwinprocess::{
DarwinProcess, DarwinProcess_new, DarwinProcess_scanThreads, DarwinProcess_setFromKInfoProc,
DarwinProcess_setFromLibprocPidinfo,
};
use crate::ported::darwin::platform::Platform_schedulerTicksToNanoseconds;
use crate::ported::darwin::platformhelpers::{KernelVersion, Platform_KernelVersionIsBetween};
use crate::ported::machine::Machine;
use crate::ported::object::Object;
use crate::ported::process::ProcessState;
use crate::ported::processtable::{
ProcessTable, ProcessTable_cleanupEntries, ProcessTable_getProcess, ProcessTable_init,
ProcessTable_prepareEntries,
};
use crate::ported::table::{Table, TableClass};
const MAXCOMLEN: usize = 16;
const WMESGLEN: usize = 7;
const COMAPT_MAXLOGNAME: usize = 12;
const NGROUPS: usize = 16;
#[repr(C)]
pub struct extern_proc {
pub p_starttime: libc::timeval,
pub p_vmspace: *mut c_void,
pub p_sigacts: *mut c_void,
pub p_flag: c_int,
pub p_stat: c_char,
pub p_pid: libc::pid_t,
pub p_oppid: libc::pid_t,
pub p_dupfd: c_int,
pub user_stack: *mut c_char,
pub exit_thread: *mut c_void,
pub p_debugger: c_int,
pub sigwait: c_int,
pub p_estcpu: c_uint,
pub p_cpticks: c_int,
pub p_pctcpu: c_uint,
pub p_wchan: *mut c_void,
pub p_wmesg: *mut c_char,
pub p_swtime: c_uint,
pub p_slptime: c_uint,
pub p_realtimer: libc::itimerval,
pub p_rtime: libc::timeval,
pub p_uticks: u64,
pub p_sticks: u64,
pub p_iticks: u64,
pub p_traceflag: c_int,
pub p_tracep: *mut c_void,
pub p_siglist: c_int,
pub p_textvp: *mut c_void,
pub p_holdcnt: c_int,
pub p_sigmask: libc::sigset_t,
pub p_sigignore: libc::sigset_t,
pub p_sigcatch: libc::sigset_t,
pub p_priority: c_uchar,
pub p_usrpri: c_uchar,
pub p_nice: c_char,
pub p_comm: [c_char; MAXCOMLEN + 1],
pub p_pgrp: *mut c_void,
pub p_addr: *mut c_void,
pub p_xstat: c_ushort,
pub p_acflag: c_ushort,
pub p_ru: *mut c_void,
}
#[repr(C)]
pub struct _pcred {
pub pc_lock: [c_char; 72],
pub pc_ucred: *mut c_void,
pub p_ruid: libc::uid_t,
pub p_svuid: libc::uid_t,
pub p_rgid: libc::gid_t,
pub p_svgid: libc::gid_t,
pub p_refcnt: c_int,
}
#[repr(C)]
pub struct _ucred {
pub cr_ref: i32,
pub cr_uid: libc::uid_t,
pub cr_ngroups: c_short,
pub cr_groups: [libc::gid_t; NGROUPS],
}
#[repr(C)]
pub struct vmspace {
pub dummy: i32,
pub dummy2: *mut c_char,
pub dummy3: [i32; 5],
pub dummy4: [*mut c_char; 3],
}
#[repr(C)]
pub struct eproc {
pub e_paddr: *mut c_void,
pub e_sess: *mut c_void,
pub e_pcred: _pcred,
pub e_ucred: _ucred,
pub e_vm: vmspace,
pub e_ppid: libc::pid_t,
pub e_pgid: libc::pid_t,
pub e_jobc: c_short,
pub e_tdev: libc::dev_t,
pub e_tpgid: libc::pid_t,
pub e_tsess: *mut c_void,
pub e_wmesg: [c_char; WMESGLEN + 1],
pub e_xsize: i32,
pub e_xrssize: c_short,
pub e_xccount: c_short,
pub e_xswrss: c_short,
pub e_flag: i32,
pub e_login: [c_char; COMAPT_MAXLOGNAME],
pub e_spare: [i32; 4],
}
#[repr(C)]
pub struct kinfo_proc {
pub kp_proc: extern_proc,
pub kp_eproc: eproc,
}
#[repr(C)]
pub struct DarwinProcessTable {
pub super_: ProcessTable,
pub global_diff: u64,
}
pub fn ProcessTable_getKInfoProcs() -> Vec<kinfo_proc> {
let mut mib: [c_int; 4] = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_ALL, 0];
for retry in 0..4usize {
let mut size: usize = 0;
if unsafe {
libc::sysctl(
mib.as_mut_ptr(),
4,
ptr::null_mut(),
&mut size,
ptr::null_mut(),
0,
)
} < 0
|| size == 0
{
CRT_fatalError("Unable to get size of kproc_infos");
}
size += 16 * retry * retry * size_of::<kinfo_proc>();
let cap = size / size_of::<kinfo_proc>();
let mut procs: Vec<kinfo_proc> = Vec::with_capacity(cap);
let mut got = size;
let rc = unsafe {
libc::sysctl(
mib.as_mut_ptr(),
4,
procs.as_mut_ptr() as *mut c_void,
&mut got,
ptr::null_mut(),
0,
)
};
if rc == 0 {
let count = got / size_of::<kinfo_proc>();
unsafe { procs.set_len(count) };
return procs;
}
if io::Error::last_os_error().raw_os_error() != Some(libc::ENOMEM) {
break;
}
}
CRT_fatalError("Unable to get kinfo_procs");
}
impl DarwinProcessTable {
fn scan_prepare(super_: *mut Table) {
let this = super_ as *mut DarwinProcessTable;
ProcessTable_prepareEntries(unsafe { &mut (*this).super_ });
}
fn scan_iterate(super_: *mut Table) {
let this = super_ as *mut DarwinProcessTable;
ProcessTable_goThroughEntries(unsafe { &mut *this });
}
fn scan_cleanup(super_: *mut Table) {
let this = super_ as *mut DarwinProcessTable;
ProcessTable_cleanupEntries(unsafe { &mut (*this).super_ });
}
}
pub static DarwinProcessTable_class: TableClass = TableClass {
prepare: Some(DarwinProcessTable::scan_prepare),
iterate: Some(DarwinProcessTable::scan_iterate),
cleanup: Some(DarwinProcessTable::scan_cleanup),
};
pub fn ProcessTable_new(
host: *const Machine,
pidMatchList: Option<usize>,
) -> Box<DarwinProcessTable> {
let mut this = Box::new(DarwinProcessTable {
super_: ProcessTable::empty(),
global_diff: 0,
});
ProcessTable_init(&mut this.super_, host, pidMatchList);
this.super_.super_.klass = &DarwinProcessTable_class as *const TableClass;
this
}
pub fn ProcessTable_delete() {
todo!("port of DarwinProcessTable.c:66")
}
pub fn ProcessTable_goThroughEntries(dpt: &mut DarwinProcessTable) {
let host = dpt.super_.super_.host;
let dhost = host as *const DarwinMachine;
dpt.global_diff = 0;
let (existing_cpus, active_cpus) = unsafe { ((*host).existingCPUs, (*host).activeCPUs) };
unsafe {
let curr = (*dhost).curr_load;
let prev = (*dhost).prev_load;
if !curr.is_null() && !prev.is_null() {
for i in 0..existing_cpus as usize {
let c = &*curr.add(i);
let p = &*prev.add(i);
for j in 0..libc::CPU_STATE_MAX as usize {
dpt.global_diff += (c.cpu_ticks[j] as u64).wrapping_sub(p.cpu_ticks[j] as u64);
}
}
}
}
let ticks_ns = Platform_schedulerTicksToNanoseconds(dpt.global_diff as f64);
let time_interval_ns = if active_cpus > 0 {
ticks_ns / active_cpus as f64
} else {
ticks_ns
};
let procs = ProcessTable_getKInfoProcs();
for kp in &procs {
let pid = kp.kp_proc.p_pid;
let (pre_existing, idx) = ProcessTable_getProcess(&mut dpt.super_, pid, |h| {
DarwinProcess_new(h) as Box<dyn Object>
});
let dproc: *mut DarwinProcess = {
let obj: &mut dyn Object = dpt.super_.super_.rows[idx].as_mut().unwrap().as_mut();
let any: &mut dyn core::any::Any = obj;
any.downcast_mut::<DarwinProcess>().unwrap()
};
let dpt_ptr = dpt as *mut DarwinProcessTable;
unsafe {
DarwinProcess_setFromKInfoProc(&mut (*dproc).super_, kp, pre_existing);
DarwinProcess_setFromLibprocPidinfo(&mut *dproc, &mut *dpt_ptr, time_interval_ns);
let p_stat = kp.kp_proc.p_stat as u32;
if p_stat == libc::SZOMB {
(*dproc).super_.state = ProcessState::ZOMBIE;
} else if p_stat == libc::SSTOP {
(*dproc).super_.state = ProcessState::STOPPED;
}
let uid = kp.kp_eproc.e_ucred.cr_uid;
if (*dproc).super_.st_uid != uid {
(*dproc).super_.st_uid = uid;
}
let isScanThreadSupported = !Platform_KernelVersionIsBetween(
KernelVersion {
major: 17,
minor: 0,
patch: 0,
},
KernelVersion {
major: 17,
minor: 5,
patch: 0,
},
);
if isScanThreadSupported {
DarwinProcess_scanThreads(dproc, dpt_ptr);
}
(*dpt_ptr).super_.totalTasks += 1;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_inits_base_table_with_host_and_filter() {
let host = 0xF00D as *const Machine;
let filter = Some(0xBEEF_usize);
let pt = ProcessTable_new(host, filter);
assert_eq!(pt.global_diff, 0);
assert_eq!(pt.super_.pidMatchList.map(|p| p as usize), filter);
assert_eq!(pt.super_.super_.host, host);
assert!(pt.super_.super_.rows.is_empty());
assert_eq!(pt.super_.totalTasks, 0);
assert_eq!(pt.super_.runningTasks, 0);
}
#[test]
fn kinfo_proc_layout_matches_kernel_for_own_pid() {
let pid = unsafe { libc::getpid() };
let ppid = unsafe { libc::getppid() };
let uid = unsafe { libc::getuid() };
let mut mib: [c_int; 4] = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_PID, pid];
let mut kp: kinfo_proc = unsafe { core::mem::zeroed() };
let mut size = size_of::<kinfo_proc>();
let rc = unsafe {
libc::sysctl(
mib.as_mut_ptr(),
4,
&mut kp as *mut kinfo_proc as *mut c_void,
&mut size,
ptr::null_mut(),
0,
)
};
assert_eq!(rc, 0, "sysctl(KERN_PROC_PID) failed");
assert_eq!(size, size_of::<kinfo_proc>());
assert_eq!(kp.kp_proc.p_pid, pid);
assert_eq!(kp.kp_eproc.e_ppid, ppid);
assert_eq!(kp.kp_eproc.e_ucred.cr_uid, uid);
}
#[test]
fn getKInfoProcs_snapshot_contains_our_pid() {
let procs = ProcessTable_getKInfoProcs();
assert!(!procs.is_empty());
let me = unsafe { libc::getpid() };
assert!(procs.iter().any(|p| p.kp_proc.p_pid == me));
}
#[test]
fn goThroughEntries_enumerates_live_processes_including_self() {
use crate::ported::darwin::darwinmachine::{
host_basic_info_data_t, DarwinMachine, DarwinMachine_freeCPULoadInfo,
DarwinMachine_getHostInfo, Machine_scan,
};
use crate::ported::darwin::platform::Platform_init;
use crate::ported::linux::linuxmachine::ZfsArcStats;
use crate::ported::machine::{ScreenSettings, Settings};
use crate::ported::process::Process_getPid;
Platform_init();
let mut dm = Box::new(DarwinMachine {
super_: Machine::default(),
host_info: host_basic_info_data_t::default(),
vm_stats: unsafe { core::mem::zeroed() },
prev_load: ptr::null_mut(),
curr_load: ptr::null_mut(),
GPUService: 0,
zfs: ZfsArcStats::default(),
});
dm.super_.activeCPUs = 1;
dm.super_.existingCPUs = 1;
dm.super_.settings = Some(Settings {
screens: vec![ScreenSettings::default()],
ssIndex: 0,
..Default::default()
});
DarwinMachine_getHostInfo(&mut dm.host_info);
Machine_scan(&mut dm);
std::thread::sleep(std::time::Duration::from_millis(50));
Machine_scan(&mut dm);
let mut dpt = ProcessTable_new(&dm.super_ as *const Machine, None);
ProcessTable_goThroughEntries(&mut dpt);
let me = unsafe { libc::getpid() };
let found = dpt
.super_
.super_
.rows
.iter()
.flatten()
.any(|o| Process_getPid(o.as_process().unwrap()) == me);
assert!(found, "own pid not found in the scan");
assert!(dpt.super_.super_.rows.len() > 1, "expected many processes");
assert!(dpt.super_.totalTasks > 0);
DarwinMachine_freeCPULoadInfo(&mut dm.curr_load);
}
}