heim_process/sys/macos/bindings/
process.rs

1use std::convert::TryFrom;
2use std::mem;
3use std::ptr;
4
5use mach::{boolean, vm_types};
6
7use heim_common::prelude::Error;
8
9use crate::{Pid, ProcessError, Status};
10
11// Process status values, declared at `bsd/sys/proc.h`
12// ex. http://fxr.watson.org/fxr/source/bsd/sys/proc.h?v=xnu-792.6.70#L149
13// Used in `extern_proc.p_stat` field
14
15/// Process being created by fork.
16pub const SIDL: libc::c_char = 1;
17/// Currently runnable.
18pub const SRUN: libc::c_char = 2;
19/// Sleeping on an address.
20pub const SSLEEP: libc::c_char = 3;
21/// Process debugging or suspension.
22pub const SSTOP: libc::c_char = 4;
23/// Awaiting collection by parent.
24pub const SZOMB: libc::c_char = 5;
25
26impl TryFrom<libc::c_char> for Status {
27    type Error = Error;
28
29    fn try_from(value: libc::c_char) -> Result<Status, Self::Error> {
30        match value {
31            SIDL => Ok(Status::Idle),
32            SRUN => Ok(Status::Running),
33            SSLEEP => Ok(Status::Sleeping),
34            SSTOP => Ok(Status::Stopped),
35            SZOMB => Ok(Status::Zombie),
36            other => Err(Error::incompatible(format!(
37                "Unnknown process p_stat {:?}",
38                other
39            ))),
40        }
41    }
42}
43
44#[allow(non_camel_case_types)]
45type caddr_t = *const libc::c_char;
46#[allow(non_camel_case_types)]
47type segsz_t = i32;
48
49#[repr(C)]
50#[derive(Debug, Copy, Clone)]
51pub struct vmspace {
52    pub dummy: i32,
53    pub dummy2: caddr_t,
54    pub dummy3: [i32; 5],
55    pub dummy4: [caddr_t; 3],
56}
57
58#[repr(C)]
59#[derive(Copy, Clone)]
60pub struct pcred {
61    pub pc_lock: [libc::c_char; 72],
62    pub pc_ucred: *mut libc::xucred,
63    pub p_ruid: libc::uid_t,
64    pub p_svuid: libc::uid_t,
65    pub p_rgid: libc::gid_t,
66    pub p_svgid: libc::gid_t,
67    pub p_refcnt: libc::c_int,
68}
69
70#[repr(C)]
71#[derive(Copy, Clone)]
72pub struct kinfo_proc {
73    pub kp_proc: extern_proc,
74    pub kp_eproc: kinfo_proc_eproc,
75}
76
77#[repr(C)]
78#[derive(Copy, Clone)]
79pub struct run_sleep_queue {
80    p_forw: vm_types::user_addr_t,
81    p_back: vm_types::user_addr_t,
82}
83
84#[repr(C)]
85#[derive(Copy, Clone)]
86pub union p_un {
87    pub p_st1: run_sleep_queue,
88    pub p_starttime: libc::timeval,
89}
90
91#[repr(C)]
92#[derive(Copy, Clone)]
93pub struct extern_proc {
94    pub p_un: p_un,
95    pub p_vmspace: vm_types::user_addr_t,
96    pub p_sigacts: vm_types::user_addr_t,
97
98    pub p_flag: libc::c_int,
99    pub p_stat: libc::c_char,
100    pub p_pid: libc::pid_t,
101    pub p_oppid: libc::pid_t,
102    pub p_dupfd: libc::c_int,
103    pub user_stack: caddr_t,
104    pub exit_thread: *mut libc::c_void,
105    pub p_debugger: libc::c_int,
106    pub sigwait: boolean::boolean_t,
107    pub p_estcpu: libc::c_uint,
108    pub p_cpticks: libc::c_int,
109    pub p_pctcpu: u32,
110    pub p_wchan: *mut libc::c_void,
111    pub p_wmesg: *mut libc::c_char,
112    pub p_swtime: libc::c_uint,
113    pub p_slptime: libc::c_uint,
114    pub p_realtimer: libc::itimerval,
115    pub p_rtime: libc::timeval,
116    pub p_uticks: u64,
117    pub p_sticks: u64,
118    pub p_iticks: u64,
119    pub p_traceflag: libc::c_int,
120    pub p_tracep: *mut libc::c_void,
121    pub p_siglist: libc::c_int,
122    // TODO: It was a pointer to `struct vnode`
123    pub p_textvp: *mut libc::c_void,
124    pub p_holdcnt: libc::c_int,
125    pub p_sigmask: libc::sigset_t,
126    pub p_sigignore: libc::sigset_t,
127    pub p_sigcatch: libc::sigset_t,
128    pub p_priority: libc::c_uchar,
129    pub p_usrpri: libc::c_uchar,
130    pub p_nice: libc::c_char,
131    pub p_comm: [libc::c_char; 17],
132    // TODO: It was a pointer to `struct proc`, declared at `bsd/sys/proc.h`
133    pub p_pgrp: *mut libc::c_void,
134    // TODO: It was a pointer to `struct user`, declared at `bsd/sys/user.h`
135    // but it is not used anymore and we do not need it too
136    pub p_addr: *mut libc::c_void,
137    pub p_xstat: libc::c_ushort,
138    pub p_acflag: libc::c_ushort,
139    pub p_ru: *mut libc::rusage,
140}
141
142#[repr(C)]
143#[derive(Copy, Clone)]
144pub struct kinfo_proc_eproc {
145    // TODO: It should be a pointer to `struct proc`
146    pub e_paddr: *mut libc::c_void,
147    // TODO: It should be a pointer to `struct session`
148    // but since we are not using it and it's declaration kinda big,
149    // it was skipped. Same goes to `e_tsess` field below.
150    pub e_sess: *mut libc::c_void,
151    pub e_pcred: pcred,
152    pub e_ucred: libc::xucred,
153    pub e_vm: vmspace,
154    pub e_ppid: libc::pid_t,
155    pub e_pgid: libc::pid_t,
156    pub e_jobc: libc::c_short,
157    pub e_tdev: libc::dev_t,
158    pub e_tpgid: libc::pid_t,
159    pub e_tsess: *mut libc::c_void, // TODO: See `TODO` comment from above
160    pub e_wmesg: [libc::c_char; 8],
161    pub e_xsize: segsz_t,
162    pub e_xrssize: libc::c_short,
163    pub e_xccount: libc::c_short,
164    pub e_xswrss: libc::c_short,
165    pub e_flag: i32,
166    pub e_login: [libc::c_char; 12],
167    pub e_spare: [i32; 4],
168}
169
170pub fn processes() -> Result<Vec<kinfo_proc>, Error> {
171    let mut name: [i32; 3] = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_ALL];
172    let mut size: libc::size_t = 0;
173    let mut processes: Vec<kinfo_proc> = vec![];
174
175    loop {
176        let result = unsafe {
177            libc::sysctl(
178                name.as_mut_ptr(),
179                3,
180                ptr::null_mut(),
181                &mut size,
182                ptr::null_mut(),
183                0,
184            )
185        };
186        if result < 0 {
187            return Err(Error::last_os_error());
188        }
189
190        processes.reserve(size);
191
192        let result = unsafe {
193            libc::sysctl(
194                name.as_mut_ptr(),
195                3,
196                processes.as_mut_ptr() as *mut libc::c_void,
197                &mut size,
198                ptr::null_mut(),
199                0,
200            )
201        };
202        match result {
203            libc::ENOMEM => continue,
204            code if code < 0 => return Err(Error::last_os_error()),
205            _ => {
206                let length = size / mem::size_of::<kinfo_proc>();
207                unsafe {
208                    processes.set_len(length);
209                }
210                debug_assert!(!processes.is_empty());
211
212                return Ok(processes);
213            }
214        }
215    }
216}
217
218pub fn process(pid: Pid) -> Result<kinfo_proc, ProcessError> {
219    let mut name: [i32; 4] = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_PID, pid];
220    let mut size: libc::size_t = mem::size_of::<kinfo_proc>();
221    let mut info = mem::MaybeUninit::<kinfo_proc>::uninit();
222
223    let result = unsafe {
224        libc::sysctl(
225            name.as_mut_ptr(),
226            4,
227            info.as_mut_ptr() as *mut libc::c_void,
228            &mut size,
229            ptr::null_mut(),
230            0,
231        )
232    };
233
234    if result < 0 {
235        return Err(Error::last_os_error().into());
236    }
237
238    // sysctl succeeds but size is zero, happens when process has gone away
239    if size == 0 {
240        return Err(ProcessError::NoSuchProcess(pid));
241    }
242
243    unsafe { Ok(info.assume_init()) }
244}
245
246#[cfg(test)]
247mod tests {
248    use std::mem;
249
250    use super::{kinfo_proc, kinfo_proc_eproc, pcred, vmspace};
251
252    #[test]
253    fn test_layout() {
254        assert_eq!(mem::size_of::<vmspace>(), 64);
255        assert_eq!(mem::align_of::<vmspace>(), 8);
256
257        assert_eq!(mem::size_of::<pcred>(), 104);
258        assert_eq!(mem::align_of::<pcred>(), 8);
259
260        assert_eq!(mem::size_of::<kinfo_proc>(), 648);
261        assert_eq!(mem::align_of::<kinfo_proc>(), 8);
262
263        assert_eq!(mem::size_of::<kinfo_proc_eproc>(), 352);
264        assert_eq!(mem::align_of::<kinfo_proc_eproc>(), 8);
265    }
266}