use std::mem;
use anyhow::{Result, bail};
use libc::{
CTL_KERN, KERN_PROC, KERN_PROC_PID, MAXCOMLEN, boolean_t, c_char, c_long, c_short, c_uchar,
c_ushort, c_void, dev_t, gid_t, itimerval, pid_t, rusage, sigset_t, timeval, uid_t, xucred,
};
use mach2::vm_types::user_addr_t;
use crate::collection::Pid;
#[repr(C)]
pub(crate) struct kinfo_proc {
pub kp_proc: extern_proc,
pub kp_eproc: eproc,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct p_st1 {
p_forw: user_addr_t,
p_back: user_addr_t,
}
#[repr(C)]
pub union p_un {
pub p_st1: p_st1,
pub p_starttime: timeval,
}
#[repr(C)]
pub(crate) struct extern_proc {
pub p_un: p_un,
pub p_vmspace: *mut vmspace,
pub p_sigacts: user_addr_t,
pub p_flag: i32,
pub p_stat: c_char,
pub p_pid: pid_t,
pub p_oppid: pid_t,
pub p_dupfd: i32,
pub user_stack: caddr_t,
pub exit_thread: *mut c_void,
pub p_debugger: i32,
pub sigwait: boolean_t,
pub p_estcpu: u32,
pub p_cpticks: i32,
pub p_pctcpu: fixpt_t,
pub p_wchan: *mut c_void,
pub p_wmesg: *mut c_char,
pub p_swtime: u32,
pub p_slptime: u32,
pub p_realtimer: itimerval,
pub p_rtime: timeval,
pub p_uticks: u64,
pub p_sticks: u64,
pub p_iticks: u64,
pub p_traceflag: i32,
pub p_tracep: *mut c_void,
pub p_siglist: i32,
pub p_textvp: *mut c_void,
pub p_holdcnt: i32,
pub p_sigmask: sigset_t,
pub p_sigignore: sigset_t,
pub p_sigcatch: 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 rusage,
}
const WMESGLEN: usize = 7;
const COMAPT_MAXLOGNAME: usize = 12;
#[expect(non_camel_case_types)]
type caddr_t = *const libc::c_char;
#[expect(non_camel_case_types)]
type segsz_t = i32;
#[expect(non_camel_case_types)]
type fixpt_t = u32;
#[repr(C)]
pub(crate) struct pcred {
pub pc_lock: [c_char; 72],
pub pc_ucred: *mut xucred,
pub p_ruid: uid_t,
pub p_svuid: uid_t,
pub p_rgid: gid_t,
pub p_svgid: gid_t,
pub p_refcnt: i32,
}
#[repr(C)]
pub(crate) struct vmspace {
pub dummy: i32,
pub dummy2: caddr_t,
pub dummy3: [i32; 5],
pub dummy4: [caddr_t; 3],
}
#[repr(C)]
pub(crate) struct eproc {
pub e_paddr: *mut c_void,
pub e_sess: *mut c_void,
pub e_pcred: pcred,
pub e_ucred: xucred,
pub e_vm: vmspace,
pub e_ppid: pid_t,
pub e_pgid: pid_t,
pub e_jobc: c_short,
pub e_tdev: dev_t,
pub e_tpgid: pid_t,
pub e_tsess: *mut c_void,
pub e_wmesg: [c_char; WMESGLEN + 1],
pub e_xsize: segsz_t,
pub e_xrssize: c_short,
pub e_xccount: c_short,
pub e_xswrss: c_short,
pub e_flag: c_long,
pub e_login: [c_char; COMAPT_MAXLOGNAME],
pub e_spare: [c_long; 4],
}
pub(crate) fn kinfo_process(pid: Pid) -> Result<kinfo_proc> {
let mut name: [i32; 4] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid];
let mut size = mem::size_of::<kinfo_proc>();
let mut info = mem::MaybeUninit::<kinfo_proc>::uninit();
let result = unsafe {
libc::sysctl(
name.as_mut_ptr(),
4,
info.as_mut_ptr() as *mut libc::c_void,
&mut size,
std::ptr::null_mut(),
0,
)
};
if result < 0 {
bail!("failed to get process for pid {pid}");
}
if size == 0 {
bail!("failed to get process for pid {pid}");
}
unsafe { Ok(info.assume_init()) }
}
#[cfg(test)]
mod test {
use std::mem;
use super::*;
#[test]
fn test_struct_sizes() {
assert_eq!(mem::size_of::<p_st1>(), 16);
assert_eq!(mem::align_of::<p_st1>(), 8);
assert_eq!(mem::size_of::<pcred>(), 104);
assert_eq!(mem::align_of::<pcred>(), 8);
assert_eq!(mem::size_of::<vmspace>(), 64);
assert_eq!(mem::align_of::<vmspace>(), 8);
assert_eq!(mem::size_of::<extern_proc>(), 296);
assert_eq!(mem::align_of::<extern_proc>(), 8);
assert_eq!(mem::size_of::<eproc>(), 376);
assert_eq!(mem::align_of::<eproc>(), 8);
assert_eq!(mem::size_of::<kinfo_proc>(), 672);
assert_eq!(mem::align_of::<kinfo_proc>(), 8);
}
}