use crate::verify_ng::sandbox_backend::HostVerification;
pub fn verify_child_host(pid: u32) -> HostVerification {
let mut out = HostVerification::none();
if pid == 0 {
return out;
}
let pgid = unsafe { libc::getpgid(pid as libc::pid_t) };
out.pgroup_separate = pgid == pid as libc::pid_t && pgid > 0;
out
}
pub fn sweep_tree(pid: u32) -> bool {
if pid == 0 {
return false;
}
let pgid = unsafe { libc::getpgid(pid as libc::pid_t) };
unsafe {
if pgid > 0 {
libc::kill(-pgid, libc::SIGKILL);
}
libc::kill(pid as libc::pid_t, libc::SIGKILL);
}
for _ in 0..20 {
if tree_gone(pid, pgid) {
return true;
}
std::thread::sleep(std::time::Duration::from_millis(100));
}
tree_gone(pid, pgid)
}
fn tree_gone(pid: u32, pgid: libc::pid_t) -> bool {
let group_gone = if pgid > 0 {
unsafe { libc::kill(-pgid, 0) != 0 }
} else {
true
};
let mut status = 0i32;
let r = unsafe { libc::waitpid(pid as libc::pid_t, &mut status, libc::WNOHANG) };
let leader_reaped_or_gone = r == pid as libc::pid_t || (r < 0 && is_esrch_or_echild());
let leader_dead = leader_reaped_or_gone || unsafe { libc::kill(pid as libc::pid_t, 0) != 0 };
group_gone && leader_dead
}
fn is_esrch_or_echild() -> bool {
matches!(
std::io::Error::last_os_error().raw_os_error(),
Some(code) if code == libc::ESRCH || code == libc::ECHILD
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_pid_is_never_verified_or_clean() {
let v = verify_child_host(0);
assert!(!v.pgroup_separate);
assert!(!v.all_observed());
assert!(!sweep_tree(0));
}
#[test]
fn foreign_pid_is_not_our_group() {
let me = std::process::id();
let v = verify_child_host(me);
let pgid = unsafe { libc::getpgid(me as libc::pid_t) };
assert_eq!(v.pgroup_separate, pgid == me as libc::pid_t && pgid > 0);
}
}