use std::sync::atomic::{AtomicI32, Ordering};
const EXIT_WRAPPER_ERROR: i32 = 127;
static FORWARD_PID: AtomicI32 = AtomicI32::new(0);
extern "C" fn forward_signal(sig: libc::c_int) {
let pid = FORWARD_PID.load(Ordering::SeqCst);
if pid > 0 {
#[expect(
unsafe_code,
reason = "kill(2) is async-signal-safe; positive pid targets a single process"
)]
unsafe {
libc::kill(pid, sig);
}
}
}
#[must_use]
pub fn run(argv: &[String]) -> i32 {
let _ = tracing_subscriber::fmt()
.with_writer(std::io::stderr)
.with_target(false)
.without_time()
.try_init();
let Some((program, rest)) = argv.split_first() else {
tracing::error!("cuenv __supervise: missing command");
return EXIT_WRAPPER_ERROR;
};
let program = program.clone();
let child_args = rest.to_vec();
#[expect(
unsafe_code,
reason = "getppid(2) has no preconditions and cannot fail"
)]
let parent_pid = unsafe { libc::getppid() };
let mut cmd = std::process::Command::new(&program);
cmd.args(&child_args);
let mut child = match cmd.spawn() {
Ok(c) => c,
Err(e) => {
tracing::error!("cuenv __supervise: failed to spawn {program}: {e}");
return EXIT_WRAPPER_ERROR;
}
};
let child_pid: libc::pid_t = child.id().cast_signed();
spawn_parent_watcher(parent_pid, child_pid);
spawn_signal_forwarder(child_pid);
match child.wait() {
Ok(status) => status.code().unwrap_or(EXIT_WRAPPER_ERROR),
Err(_) => EXIT_WRAPPER_ERROR,
}
}
fn kill_group() {
#[expect(
unsafe_code,
reason = "getpid/kill on our own pgid are safe; SIGKILL terminates the whole group"
)]
unsafe {
let pgid = libc::getpid();
libc::kill(-pgid, libc::SIGKILL);
}
}
#[cfg(target_os = "macos")]
fn spawn_parent_watcher(parent_pid: libc::pid_t, _child_pid: libc::pid_t) {
std::thread::spawn(move || {
#[expect(
unsafe_code,
reason = "kqueue()/kevent()/close() on a locally-owned fd; pointers live for the call"
)]
unsafe {
let kq = libc::kqueue();
if kq < 0 {
tracing::warn!(
"cuenv __supervise: kqueue() failed (errno {}); killing group",
std::io::Error::last_os_error()
);
kill_group();
return;
}
let mut change: libc::kevent = std::mem::zeroed();
change.ident = usize::try_from(parent_pid).expect("pid fits in usize");
change.filter = libc::EVFILT_PROC;
change.flags = libc::EV_ADD | libc::EV_ENABLE | libc::EV_ONESHOT;
change.fflags = libc::NOTE_EXIT;
let mut event: libc::kevent = std::mem::zeroed();
let n = libc::kevent(
kq,
&raw const change,
1,
&raw mut event,
1,
std::ptr::null(),
);
if n < 0 {
tracing::warn!(
"cuenv __supervise: kevent() failed (errno {}); killing group",
std::io::Error::last_os_error()
);
}
libc::close(kq);
kill_group();
}
});
}
#[cfg(target_os = "linux")]
fn spawn_parent_watcher(parent_pid: libc::pid_t, _child_pid: libc::pid_t) {
std::thread::spawn(move || {
loop {
#[expect(
unsafe_code,
reason = "kill(pid, 0) is the standard POSIX probe for process liveness"
)]
let alive = unsafe { libc::kill(parent_pid, 0) } == 0;
if !alive {
kill_group();
return;
}
std::thread::sleep(std::time::Duration::from_millis(500));
}
});
}
#[cfg(all(unix, not(target_os = "linux"), not(target_os = "macos")))]
fn spawn_parent_watcher(parent_pid: libc::pid_t, _child_pid: libc::pid_t) {
std::thread::spawn(move || {
loop {
#[expect(
unsafe_code,
reason = "kill(pid, 0) is the standard POSIX probe for process liveness"
)]
let alive = unsafe { libc::kill(parent_pid, 0) } == 0;
if !alive {
kill_group();
return;
}
std::thread::sleep(std::time::Duration::from_millis(500));
}
});
}
fn spawn_signal_forwarder(child_pid: libc::pid_t) {
FORWARD_PID.store(child_pid, Ordering::SeqCst);
for sig in [libc::SIGTERM, libc::SIGINT, libc::SIGHUP, libc::SIGQUIT] {
#[expect(
unsafe_code,
reason = "installing a signal handler at wrapper startup; function pointer cast via *const ()"
)]
unsafe {
libc::signal(sig, forward_signal as *const () as libc::sighandler_t);
}
}
}