| use crate::io; |
| use crate::os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; |
| use crate::sys::fd::FileDesc; |
| use crate::sys::process::ExitStatus; |
| use crate::sys::{AsInner, FromInner, IntoInner, cvt}; |
| |
| #[cfg(test)] |
| mod tests; |
| |
| #[derive(Debug)] |
| pub(crate) struct PidFd(FileDesc); |
| |
| impl PidFd { |
| pub fn kill(&self) -> io::Result<()> { |
| self.send_signal(libc::SIGKILL) |
| } |
| |
| #[cfg(any(test, target_env = "gnu", target_env = "musl"))] |
| pub(crate) fn current_process() -> io::Result<PidFd> { |
| let pid = crate::process::id(); |
| let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, pid, 0) })?; |
| Ok(unsafe { PidFd::from_raw_fd(pidfd as RawFd) }) |
| } |
| |
| #[cfg(any(test, target_env = "gnu", target_env = "musl"))] |
| pub(crate) fn pid(&self) -> io::Result<u32> { |
| use crate::sys::weak::weak; |
| |
| // since kernel 6.13 |
| // https://lore.kernel.org/all/20241010155401.2268522-1-luca.boccassi@gmail.com/ |
| let mut pidfd_info: libc::pidfd_info = unsafe { crate::mem::zeroed() }; |
| pidfd_info.mask = libc::PIDFD_INFO_PID as u64; |
| match cvt(unsafe { libc::ioctl(self.0.as_raw_fd(), libc::PIDFD_GET_INFO, &mut pidfd_info) }) |
| { |
| Ok(_) => {} |
| Err(e) if matches!(e.raw_os_error(), Some(libc::EINVAL | libc::ENOTTY)) => { |
| // kernel doesn't support that ioctl, try the glibc helper that looks at procfs |
| weak!( |
| fn pidfd_getpid(pidfd: RawFd) -> libc::pid_t; |
| ); |
| if let Some(pidfd_getpid) = pidfd_getpid.get() { |
| let pid: libc::c_int = cvt(unsafe { pidfd_getpid(self.0.as_raw_fd()) })?; |
| return Ok(pid as u32); |
| } |
| return Err(e); |
| } |
| Err(e) => return Err(e), |
| } |
| |
| Ok(pidfd_info.pid) |
| } |
| |
| fn exit_for_reaped_child(&self) -> io::Result<ExitStatus> { |
| // since kernel 6.15 |
| // https://lore.kernel.org/linux-fsdevel/20250305-work-pidfs-kill_on_last_close-v3-0-c8c3d8361705@kernel.org/T/ |
| let mut pidfd_info: libc::pidfd_info = unsafe { crate::mem::zeroed() }; |
| pidfd_info.mask = libc::PIDFD_INFO_EXIT as u64; |
| cvt(unsafe { libc::ioctl(self.0.as_raw_fd(), libc::PIDFD_GET_INFO, &mut pidfd_info) })?; |
| Ok(ExitStatus::new(pidfd_info.exit_code)) |
| } |
| |
| fn waitid(&self, options: libc::c_int) -> io::Result<Option<ExitStatus>> { |
| let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; |
| let r = cvt(unsafe { |
| libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, options) |
| }); |
| match r { |
| Err(waitid_err) if waitid_err.raw_os_error() == Some(libc::ECHILD) => { |
| // already reaped |
| match self.exit_for_reaped_child() { |
| Ok(exit_status) => return Ok(Some(exit_status)), |
| Err(_) => return Err(waitid_err), |
| } |
| } |
| Err(e) => return Err(e), |
| Ok(_) => {} |
| } |
| if unsafe { siginfo.si_pid() } == 0 { |
| Ok(None) |
| } else { |
| Ok(Some(ExitStatus::from_waitid_siginfo(siginfo))) |
| } |
| } |
| |
| pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> { |
| cvt(unsafe { |
| libc::syscall( |
| libc::SYS_pidfd_send_signal, |
| self.0.as_raw_fd(), |
| signal, |
| crate::ptr::null::<()>(), |
| 0, |
| ) |
| }) |
| .map(drop) |
| } |
| |
| pub fn wait(&self) -> io::Result<ExitStatus> { |
| let r = self.waitid(libc::WEXITED)?; |
| match r { |
| Some(exit_status) => Ok(exit_status), |
| None => unreachable!("waitid with WEXITED should not return None"), |
| } |
| } |
| |
| pub fn try_wait(&self) -> io::Result<Option<ExitStatus>> { |
| self.waitid(libc::WEXITED | libc::WNOHANG) |
| } |
| } |
| |
| impl AsInner<FileDesc> for PidFd { |
| fn as_inner(&self) -> &FileDesc { |
| &self.0 |
| } |
| } |
| |
| impl IntoInner<FileDesc> for PidFd { |
| fn into_inner(self) -> FileDesc { |
| self.0 |
| } |
| } |
| |
| impl FromInner<FileDesc> for PidFd { |
| fn from_inner(inner: FileDesc) -> Self { |
| Self(inner) |
| } |
| } |
| |
| impl FromRawFd for PidFd { |
| unsafe fn from_raw_fd(fd: RawFd) -> Self { |
| Self(FileDesc::from_raw_fd(fd)) |
| } |
| } |
| |
| impl IntoRawFd for PidFd { |
| fn into_raw_fd(self) -> RawFd { |
| self.0.into_raw_fd() |
| } |
| } |