use nix::sys::signal::{
kill, sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal,
};
use nix::unistd::{fork, getpid, write, ForkResult, Pid};
use std::thread;
use std::time::Duration;
use super::params::Params;
pub fn os_params_hook(params: &Params) -> Option<Pid> {
(params.timeout != Duration::ZERO).then(|| install_timeout(params.timeout))
}
pub fn os_exit_hook(pid: Option<Pid>) {
if let Some(pid) = pid {
tracing::debug!("Killing child {pid} used for timeout");
let _ = kill(pid, Signal::SIGTERM);
}
}
fn install_timeout(timeout: Duration) -> Pid {
let alarm_action = SigAction::new(
SigHandler::Handler(sigalrm_handler),
SaFlags::empty(),
SigSet::empty(),
);
unsafe {
let _ = sigaction(Signal::SIGALRM, &alarm_action);
}
let parent = getpid();
let child = match unsafe { fork() } {
Ok(ForkResult::Parent { child, .. }) => child,
Ok(ForkResult::Child) => {
thread::sleep(timeout);
let _ = kill(parent, Signal::SIGALRM);
unsafe {
libc::_exit(0);
}
}
Err(error) => panic!("Could not fork child for timeout: {error}"),
};
tracing::debug!("Forked child {child} to send SIGALRM after {timeout:?}");
child
}
extern "C" fn sigalrm_handler(_: nix::libc::c_int) {
let _ = write(
std::io::stdout(),
b"\nrepo_state=Error\nrepo_error='Timed out'\n",
);
unsafe {
libc::_exit(2);
}
}