blob: 671046949aa16077249e952bcf30a6dad9a5f41d [file] [log] [blame] [edit]
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()
}
}