use std::io;
#[must_use]
pub fn live_pid_is_subprocess(pid: u32, name_key: &str, service_name: &str) -> bool {
let Ok(pid) = i32::try_from(pid) else {
return false;
};
match process_args_blob(pid) {
Ok(blob) => super::environ_from_procargs2(&blob).is_some_and(|environ| {
super::environ_identifies_child(environ, name_key, service_name)
}),
Err(e) => {
tracing::warn!(pid, error = %e, "Could not read process environ to verify child identity");
false
},
}
}
#[must_use]
#[expect(
unsafe_code,
reason = "proc_pidinfo is the only interface exposing a process state on Darwin; libc \
publishes no kinfo_proc for Apple, so there is no safe wrapper to call instead"
)]
pub fn is_zombie(pid: u32) -> bool {
let Ok(pid) = i32::try_from(pid) else {
return false;
};
let Ok(want) = libc::c_int::try_from(size_of::<libc::proc_bsdshortinfo>()) else {
return false;
};
let mut info = std::mem::MaybeUninit::<libc::proc_bsdshortinfo>::zeroed();
let written = unsafe {
libc::proc_pidinfo(
pid,
libc::PROC_PIDT_SHORTBSDINFO,
0,
info.as_mut_ptr().cast(),
want,
)
};
if written != want {
return false;
}
let info = unsafe { info.assume_init() };
info.pbsi_status == libc::SZOMB
}
#[expect(
unsafe_code,
reason = "sysctl is the only interface exposing another process's environment on Darwin; \
there is no safe wrapper for KERN_PROCARGS2 in the dependency set"
)]
fn process_args_blob(pid: libc::c_int) -> io::Result<Vec<u8>> {
let mut mib = [libc::CTL_KERN, libc::KERN_PROCARGS2, pid];
let mut buf = vec![0u8; arg_max()?];
let mut len = buf.len();
let rc = unsafe {
libc::sysctl(
mib.as_mut_ptr(),
3,
buf.as_mut_ptr().cast(),
&raw mut len,
std::ptr::null_mut(),
0,
)
};
if rc != 0 {
return Err(io::Error::last_os_error());
}
buf.truncate(len);
Ok(buf)
}
#[expect(
unsafe_code,
reason = "sizing the KERN_PROCARGS2 buffer needs KERN_ARGMAX, which is only readable through \
the same raw sysctl interface"
)]
fn arg_max() -> io::Result<usize> {
let mut mib = [libc::CTL_KERN, libc::KERN_ARGMAX];
let mut value: libc::c_int = 0;
let mut len = size_of::<libc::c_int>();
let rc = unsafe {
libc::sysctl(
mib.as_mut_ptr(),
2,
(&raw mut value).cast(),
&raw mut len,
std::ptr::null_mut(),
0,
)
};
if rc != 0 {
return Err(io::Error::last_os_error());
}
usize::try_from(value).map_err(|e| io::Error::other(format!("KERN_ARGMAX is unusable: {e}")))
}