//! based on https://www.man7.org/linux/man-pages/man2/pidfd_open.2.html #![cfg(target_os = "linux")] use std::{ io, mem::ManuallyDrop, ops::{Deref, DerefMut}, os::fd::{BorrowedFd, RawFd}, process::{Child, ExitStatus}, ptr, }; use libc::{SYS_pidfd_open, syscall}; use nix::poll::{PollFd, PollFlags}; pub struct ChildWaiter { fd: RawFd, child: Child, } #[derive(Debug)] struct PidFdOpenError; impl std::fmt::Display for PidFdOpenError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "pid_fd_open") } } impl std::error::Error for PidFdOpenError {} impl ChildWaiter { pub fn new(child: Child) -> io::Result { let fd = unsafe { syscall(SYS_pidfd_open, child.id(), 0) }; if fd < 0 { Err(io::Error::new(io::ErrorKind::Other, PidFdOpenError)) } else { let fd = fd as RawFd; Ok(Self { child, fd }) } } pub fn wait(&mut self, timeout_ms: u16) -> io::Result> { let mut poll_fds = [PollFd::new( unsafe { BorrowedFd::borrow_raw(self.fd) }, PollFlags::POLLIN, )]; let _ = nix::poll::poll(&mut poll_fds, timeout_ms); self.child.try_wait() } pub fn into_inner(self) -> Child { unsafe { libc::close(self.fd); } let this = ManuallyDrop::new(self); unsafe { ptr::read(&this.child) } } } impl Deref for ChildWaiter { type Target = Child; fn deref(&self) -> &Self::Target { &self.child } } impl DerefMut for ChildWaiter { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.child } } impl Drop for ChildWaiter { fn drop(&mut self) { unsafe { libc::close(self.fd); } } }