pub fn spawn(vetto_pid: libc::pid_t, agent_pid: libc::pid_t) {
if std::env::var_os("VETTO_NO_PDEATH_WATCH").is_some() {
return;
}
if vetto_pid <= 0 || agent_pid <= 0 || vetto_pid == agent_pid {
return;
}
let pid = unsafe { libc::fork() };
if pid < 0 {
eprintln!(
"vetto: warning: parent-death watchdog fork failed: {}",
std::io::Error::last_os_error()
);
return;
}
if pid == 0 {
unsafe { run_watch(vetto_pid, agent_pid) }
}
}
unsafe fn run_watch(vetto_pid: libc::pid_t, agent_pid: libc::pid_t) -> ! {
detach_from_session_stdio();
let kq = libc::kqueue();
if kq < 0 {
poll_loop(vetto_pid, agent_pid);
}
let changes = [
libc::kevent {
ident: vetto_pid as usize,
filter: libc::EVFILT_PROC,
flags: libc::EV_ADD | libc::EV_RECEIPT,
fflags: libc::NOTE_EXIT,
data: 0,
udata: std::ptr::null_mut(),
},
libc::kevent {
ident: agent_pid as usize,
filter: libc::EVFILT_PROC,
flags: libc::EV_ADD | libc::EV_RECEIPT,
fflags: libc::NOTE_EXIT,
data: 0,
udata: std::ptr::null_mut(),
},
];
let mut receipts = [libc::kevent {
ident: 0,
filter: 0,
flags: 0,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
}; 2];
let n = libc::kevent(
kq,
changes.as_ptr(),
2,
receipts.as_mut_ptr(),
2,
std::ptr::null(),
);
if n < 0 {
libc::close(kq);
poll_loop(vetto_pid, agent_pid);
}
loop {
let mut events = [receipts[0]; 8];
let timeout = libc::timespec {
tv_sec: 1,
tv_nsec: 0,
};
let n = libc::kevent(kq, std::ptr::null(), 0, events.as_mut_ptr(), 8, &timeout);
if n < 0 {
let err = std::io::Error::last_os_error();
if err.raw_os_error() == Some(libc::EINTR) {
continue;
}
libc::close(kq);
poll_loop(vetto_pid, agent_pid);
}
for event in &events[..n.max(0) as usize] {
if event.ident == vetto_pid as usize && event.filter == libc::EVFILT_PROC {
libc::kill(agent_pid, libc::SIGKILL);
libc::close(kq);
libc::_exit(0);
}
if event.ident == agent_pid as usize && event.filter == libc::EVFILT_PROC {
libc::close(kq);
libc::_exit(0);
}
}
if libc::kill(vetto_pid, 0) == -1
&& std::io::Error::last_os_error().raw_os_error() == Some(libc::ESRCH)
{
libc::kill(agent_pid, libc::SIGKILL);
libc::close(kq);
libc::_exit(0);
}
if libc::kill(agent_pid, 0) == -1
&& std::io::Error::last_os_error().raw_os_error() == Some(libc::ESRCH)
{
libc::close(kq);
libc::_exit(0);
}
}
}
unsafe fn poll_loop(vetto_pid: libc::pid_t, agent_pid: libc::pid_t) -> ! {
loop {
libc::usleep(500_000);
if libc::kill(vetto_pid, 0) == -1
&& std::io::Error::last_os_error().raw_os_error() == Some(libc::ESRCH)
{
libc::kill(agent_pid, libc::SIGKILL);
libc::_exit(0);
}
if libc::kill(agent_pid, 0) == -1
&& std::io::Error::last_os_error().raw_os_error() == Some(libc::ESRCH)
{
libc::_exit(0);
}
}
}
unsafe fn detach_from_session_stdio() {
let _ = libc::setsid();
let devnull = b"/dev/null\0";
let fd = libc::open(devnull.as_ptr().cast(), libc::O_RDWR);
if fd >= 0 {
for target in 0..=2 {
if target != fd {
libc::dup2(fd, target);
}
}
if fd > 2 {
libc::close(fd);
}
}
let max = unsafe { libc::sysconf(libc::_SC_OPEN_MAX) } as i32;
let max = max.clamp(16, 65_536);
for candidate in 3..max {
unsafe { libc::close(candidate) };
}
}