use std::{
error::Error,
io, thread,
time::{Duration, Instant},
};
use fork::{CheckedDaemon, DaemonOptions, ProcessGroupGuard};
use super::{BOUND, POLL};
pub(super) fn run(mode: &str) -> Result<(), Box<dyn Error>> {
if mode == "waitid-guard" {
return ProcessGroupGuard::new()?.disarm(BOUND).map_err(Into::into);
}
let result = match fork::checked_daemon(DaemonOptions::new(), BOUND) {
Ok(CheckedDaemon::Daemon(notifier)) => {
let ready = notifier.notify_ready().is_ok();
if mode == "waitid-failure" {
thread::sleep(Duration::from_secs(4));
}
unsafe { libc::_exit(i32::from(!ready)) }
}
other => other,
};
let check = if mode == "waitid-failure" {
match result {
Err(fork::DaemonError::InvalidHandshake { cleanup_pending })
if cleanup_pending.process_group().is_some()
&& cleanup_pending.intermediate().is_none() =>
{
Ok(())
}
other => Err(io::Error::other(format!(
"missing failed-intermediate cleanup obligation: {other:?}"
))),
}
} else {
match result {
Ok(CheckedDaemon::Parent(_)) => Ok(()),
other => Err(io::Error::other(format!(
"denied waitid broke successful daemon: {other:?}"
))),
}
};
let deadline = Instant::now() + BOUND;
loop {
match fork::wait_any_event_nohang() {
Err(error) if error.raw_os_error() == Some(libc::ECHILD) => break,
Err(error) => return Err(error.into()),
Ok(_) => {}
}
if Instant::now() >= deadline {
return Err("daemon descendants did not finish".into());
}
thread::sleep(POLL);
}
check.map_err(Into::into)
}