use std::{collections::HashSet, hash::BuildHasher};
use crate::{error::Result, ForkErr};
use log::{debug, info, warn};
use nix::{
sys::{
signal::{kill, Signal},
wait::{waitpid, WaitPidFlag, WaitStatus},
},
unistd::Pid,
};
pub fn wait_process<H>(pids: &mut HashSet<Pid, H>)
where
H: BuildHasher,
{
while let Ok(status) = waitpid(None, Some(WaitPidFlag::WNOHANG)) {
match status {
WaitStatus::Exited(pid, exit_code) => {
info!("Child {pid} exited with status {exit_code}");
pids.remove(&pid);
}
WaitStatus::Signaled(pid, signal, ..) => {
info!("Child {pid} killed by signal {signal}");
pids.remove(&pid);
}
WaitStatus::StillAlive => break,
_ => {
info!("waitpid got status {status:?}");
}
}
}
}
pub fn kill_all<H>(pids: HashSet<Pid, H>) -> Result<()>
where
H: BuildHasher,
{
kill_all_with(pids, Signal::SIGKILL)
}
pub fn kill_all_with<H>(pids: HashSet<Pid, H>, signal: Signal) -> Result<()>
where
H: BuildHasher,
{
let mut failures = Vec::new();
for pid in pids {
if let Err(err) = kill(pid, signal) {
warn!("Kill failed: {err}");
failures.push(pid.into());
} else {
debug!("Reaped {pid}");
}
}
if failures.is_empty() {
Ok(())
} else {
Err(ForkErr::KillAll(failures))
}
}