-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
251 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod pidfd; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
use crate::io; | ||
use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; | ||
use crate::sys::cvt; | ||
use crate::sys::pal::unix::fd::FileDesc; | ||
use crate::sys::process::ExitStatus; | ||
use crate::sys_common::{AsInner, FromInner, IntoInner}; | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
#[derive(Debug)] | ||
pub(crate) struct PidFd(FileDesc); | ||
|
||
impl PidFd { | ||
pub fn kill(&self) -> io::Result<()> { | ||
return cvt(unsafe { | ||
libc::syscall( | ||
libc::SYS_pidfd_send_signal, | ||
self.0.as_raw_fd(), | ||
libc::SIGKILL, | ||
crate::ptr::null::<()>(), | ||
0, | ||
) | ||
}) | ||
.map(drop); | ||
} | ||
|
||
pub fn wait(&self) -> io::Result<ExitStatus> { | ||
let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; | ||
cvt(unsafe { | ||
libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, libc::WEXITED) | ||
})?; | ||
return Ok(ExitStatus::from_waitid_siginfo(siginfo)); | ||
} | ||
|
||
pub fn try_wait(&self) -> io::Result<Option<ExitStatus>> { | ||
let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; | ||
|
||
cvt(unsafe { | ||
libc::waitid( | ||
libc::P_PIDFD, | ||
self.0.as_raw_fd() as u32, | ||
&mut siginfo, | ||
libc::WEXITED | libc::WNOHANG, | ||
) | ||
})?; | ||
if unsafe { siginfo.si_pid() } == 0 { | ||
return Ok(None); | ||
} | ||
return Ok(Some(ExitStatus::from_waitid_siginfo(siginfo))); | ||
} | ||
} | ||
|
||
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)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
use crate::assert_matches::assert_matches; | ||
use crate::os::fd::{AsRawFd, RawFd}; | ||
use crate::os::linux::process::{ChildExt, CommandExt}; | ||
use crate::os::unix::process::ExitStatusExt; | ||
use crate::process::Command; | ||
|
||
#[test] | ||
fn test_command_pidfd() { | ||
let pidfd_open_available = probe_pidfd_support(); | ||
|
||
// always exercise creation attempts | ||
let mut child = Command::new("false").create_pidfd(true).spawn().unwrap(); | ||
|
||
// but only check if we know that the kernel supports pidfds. | ||
// We don't assert the precise value, since the standard library | ||
// might have opened other file descriptors before our code runs. | ||
if pidfd_open_available { | ||
assert!(child.pidfd().is_ok()); | ||
} | ||
if let Ok(pidfd) = child.pidfd() { | ||
let flags = super::cvt(unsafe { libc::fcntl(pidfd.as_raw_fd(), libc::F_GETFD) }).unwrap(); | ||
assert!(flags & libc::FD_CLOEXEC != 0); | ||
} | ||
let status = child.wait().expect("error waiting on pidfd"); | ||
assert_eq!(status.code(), Some(1)); | ||
|
||
let mut child = Command::new("sleep").arg("1000").create_pidfd(true).spawn().unwrap(); | ||
assert_matches!(child.try_wait(), Ok(None)); | ||
child.kill().expect("failed to kill child"); | ||
let status = child.wait().expect("error waiting on pidfd"); | ||
assert_eq!(status.signal(), Some(libc::SIGKILL)); | ||
|
||
let _ = Command::new("echo") | ||
.create_pidfd(false) | ||
.spawn() | ||
.unwrap() | ||
.pidfd() | ||
.expect_err("pidfd should not have been created when create_pid(false) is set"); | ||
|
||
let _ = Command::new("echo") | ||
.spawn() | ||
.unwrap() | ||
.pidfd() | ||
.expect_err("pidfd should not have been created"); | ||
} | ||
|
||
#[test] | ||
fn test_pidfd() { | ||
if !probe_pidfd_support() { | ||
return; | ||
} | ||
|
||
let mut child = Command::new("sleep") | ||
.arg("1000") | ||
.create_pidfd(true) | ||
.spawn() | ||
.expect("executing 'sleep' failed"); | ||
|
||
let fd = child.take_pidfd().unwrap(); | ||
drop(child); | ||
|
||
assert_matches!(fd.try_wait(), Ok(None)); | ||
fd.kill().expect("kill failed"); | ||
fd.kill().expect("sending kill twice failed"); | ||
let status = fd.wait().expect("1st wait failed"); | ||
assert_eq!(status.signal(), Some(libc::SIGKILL)); | ||
|
||
// Trying to wait again for a reaped child is safe since there's no pid-recycling race. | ||
// But doing so will return an error. | ||
let res = fd.wait(); | ||
assert_matches!(res, Err(e) if e.raw_os_error() == Some(libc::ECHILD)); | ||
|
||
// Ditto for additional attempts to kill an already-dead child. | ||
let res = fd.kill(); | ||
assert_matches!(res, Err(e) if e.raw_os_error() == Some(libc::ESRCH)); | ||
} | ||
|
||
fn probe_pidfd_support() -> bool { | ||
// pidfds require the pidfd_open syscall | ||
let our_pid = crate::process::id(); | ||
let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) }; | ||
if pidfd >= 0 { | ||
unsafe { libc::close(pidfd as RawFd) }; | ||
true | ||
} else { | ||
false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.