#![allow(unsafe_code)]
#[cfg(target_os = "macos")]
pub fn end_what_is_left(mark: &str) -> usize {
use std::ffi::CString;
use std::time::Duration;
let (Ok(name), Ok(other)) = (CString::new(mark), CString::new(format!("{mark}.other"))) else {
return 0;
};
let mut left = 0;
for _ in 0..ROUNDS {
let found: Vec<libc::pid_t> = all_pids()
.into_iter()
.filter(|&pid| in_sandbox_of(pid, &name, &other))
.collect();
left = found.len();
if left == 0 {
break;
}
for pid in found {
unsafe { libc::kill(pid, libc::SIGKILL) };
}
std::thread::sleep(Duration::from_millis(10));
}
left
}
#[cfg(target_os = "macos")]
const ROUNDS: usize = 200;
#[cfg(target_os = "macos")]
unsafe extern "C" {
fn sandbox_check(pid: libc::pid_t, operation: *const libc::c_char, kind: i32, ...) -> i32;
}
#[cfg(target_os = "macos")]
const BY_NAME: i32 = 2 | 0x4000_0000;
#[cfg(target_os = "macos")]
fn in_sandbox_of(pid: libc::pid_t, mark: &std::ffi::CStr, other: &std::ffi::CStr) -> bool {
let lookup = c"mach-lookup".as_ptr();
let marked = unsafe {
sandbox_check(pid, std::ptr::null(), 0) == 1
&& sandbox_check(pid, lookup, BY_NAME, mark.as_ptr()) == 0
&& sandbox_check(pid, lookup, BY_NAME, other.as_ptr()) == 1
};
marked && !is_zombie(pid)
}
#[cfg(target_os = "macos")]
fn is_zombie(pid: libc::pid_t) -> bool {
let size = std::mem::size_of::<libc::proc_bsdinfo>() as libc::c_int;
let (got, info) = unsafe {
let mut info: libc::proc_bsdinfo = std::mem::zeroed();
let buffer = (&raw mut info).cast();
let got = libc::proc_pidinfo(pid, libc::PROC_PIDTBSDINFO, 0, buffer, size);
(got, info)
};
got == size && info.pbi_status == libc::SZOMB
}
#[cfg(target_os = "macos")]
fn all_pids() -> Vec<libc::pid_t> {
let size = std::mem::size_of::<libc::pid_t>();
let count = unsafe { libc::proc_listallpids(std::ptr::null_mut(), 0) };
let mut pids = vec![0; count.max(0) as usize + 256];
let bytes = (pids.len() * size) as libc::c_int;
let got = unsafe { libc::proc_listallpids(pids.as_mut_ptr().cast(), bytes) };
pids.truncate(got.max(0) as usize);
pids
}
#[cfg(not(target_os = "macos"))]
pub fn end_what_is_left(_: &str) -> usize {
0
}
pub fn what_would_not_end(mark: &str) -> Option<(String, String)> {
let left = end_what_is_left(mark);
(left > 0).then(|| ("left_running".into(), left.to_string()))
}