if available use a Child's pidfd for kill/wait

This commit is contained in:
The 8472
2023-11-16 01:41:02 +01:00
parent 3ffbb4899e
commit 12efa53b19
3 changed files with 78 additions and 4 deletions

View File

@@ -9,6 +9,8 @@ use core::ffi::NonZero_c_int;
#[cfg(target_os = "linux")]
use crate::os::linux::process::PidFd;
#[cfg(target_os = "linux")]
use crate::os::unix::io::AsRawFd;
#[cfg(any(
target_os = "macos",
@@ -816,10 +818,23 @@ impl Process {
// and used for another process, and we probably shouldn't be killing
// random processes, so return Ok because the process has exited already.
if self.status.is_some() {
Ok(())
} else {
cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
return Ok(());
}
#[cfg(target_os = "linux")]
if let Some(pid_fd) = self.pidfd.as_ref() {
// pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too
return cvt(unsafe {
libc::syscall(
libc::SYS_pidfd_send_signal,
pid_fd.as_raw_fd(),
libc::SIGKILL,
crate::ptr::null::<()>(),
0,
)
})
.map(drop);
}
cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
@@ -827,6 +842,17 @@ impl Process {
if let Some(status) = self.status {
return Ok(status);
}
#[cfg(target_os = "linux")]
if let Some(pid_fd) = self.pidfd.as_ref() {
let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() };
cvt_r(|| unsafe {
libc::waitid(libc::P_PIDFD, pid_fd.as_raw_fd() as u32, &mut siginfo, libc::WEXITED)
})?;
let status = ExitStatus::from_waitid_siginfo(siginfo);
self.status = Some(status);
return Ok(status);
}
let mut status = 0 as c_int;
cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
self.status = Some(ExitStatus::new(status));
@@ -837,6 +863,25 @@ impl Process {
if let Some(status) = self.status {
return Ok(Some(status));
}
#[cfg(target_os = "linux")]
if let Some(pid_fd) = self.pidfd.as_ref() {
let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() };
cvt(unsafe {
libc::waitid(
libc::P_PIDFD,
pid_fd.as_raw_fd() as u32,
&mut siginfo,
libc::WEXITED | libc::WNOHANG,
)
})?;
if unsafe { siginfo.si_pid() } == 0 {
return Ok(None);
}
let status = ExitStatus::from_waitid_siginfo(siginfo);
self.status = Some(status);
return Ok(Some(status));
}
let mut status = 0 as c_int;
let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
if pid == 0 {
@@ -866,6 +911,20 @@ impl ExitStatus {
ExitStatus(status)
}
#[cfg(target_os = "linux")]
pub fn from_waitid_siginfo(siginfo: libc::siginfo_t) -> ExitStatus {
let status = unsafe { siginfo.si_status() };
match siginfo.si_code {
libc::CLD_EXITED => ExitStatus((status & 0xff) << 8),
libc::CLD_KILLED => ExitStatus(status),
libc::CLD_DUMPED => ExitStatus(status | 0x80),
libc::CLD_CONTINUED => ExitStatus(0xffff),
libc::CLD_STOPPED | libc::CLD_TRAPPED => ExitStatus(((status & 0xff) << 8) | 0x7f),
_ => unreachable!("waitid() should only return the above codes"),
}
}
fn exited(&self) -> bool {
libc::WIFEXITED(self.0)
}