about summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys')
-rw-r--r--library/std/src/sys/pal/unix/linux/mod.rs1
-rw-r--r--library/std/src/sys/pal/unix/linux/pidfd.rs76
-rw-r--r--library/std/src/sys/pal/unix/linux/pidfd/tests.rs88
-rw-r--r--library/std/src/sys/pal/unix/mod.rs2
-rw-r--r--library/std/src/sys/pal/unix/process/process_unix.rs80
-rw-r--r--library/std/src/sys/pal/unix/process/process_unix/tests.rs54
6 files changed, 200 insertions, 101 deletions
diff --git a/library/std/src/sys/pal/unix/linux/mod.rs b/library/std/src/sys/pal/unix/linux/mod.rs
new file mode 100644
index 00000000000..88aa1e3decc
--- /dev/null
+++ b/library/std/src/sys/pal/unix/linux/mod.rs
@@ -0,0 +1 @@
+pub mod pidfd;
diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs
new file mode 100644
index 00000000000..7474f80e94f
--- /dev/null
+++ b/library/std/src/sys/pal/unix/linux/pidfd.rs
@@ -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))
+    }
+}
diff --git a/library/std/src/sys/pal/unix/linux/pidfd/tests.rs b/library/std/src/sys/pal/unix/linux/pidfd/tests.rs
new file mode 100644
index 00000000000..42b3df62991
--- /dev/null
+++ b/library/std/src/sys/pal/unix/linux/pidfd/tests.rs
@@ -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
+    }
+}
diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs
index 735ed96bc7b..b370f06e92b 100644
--- a/library/std/src/sys/pal/unix/mod.rs
+++ b/library/std/src/sys/pal/unix/mod.rs
@@ -20,6 +20,8 @@ pub mod io;
 pub mod kernel_copy;
 #[cfg(target_os = "l4re")]
 mod l4re;
+#[cfg(target_os = "linux")]
+pub mod linux;
 #[cfg(not(target_os = "l4re"))]
 pub mod net;
 #[cfg(target_os = "l4re")]
diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs
index 72bda90a9ba..479f18876e3 100644
--- a/library/std/src/sys/pal/unix/process/process_unix.rs
+++ b/library/std/src/sys/pal/unix/process/process_unix.rs
@@ -7,9 +7,7 @@ use crate::sys::cvt;
 use crate::sys::process::process_common::*;
 
 #[cfg(target_os = "linux")]
-use crate::os::linux::process::PidFd;
-#[cfg(target_os = "linux")]
-use crate::os::unix::io::AsRawFd;
+use crate::sys::pal::unix::linux::pidfd::PidFd;
 
 #[cfg(target_os = "vxworks")]
 use libc::RTP_ID as pid_t;
@@ -815,16 +813,7 @@ impl Process {
         #[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);
+            return pid_fd.kill();
         }
         cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
     }
@@ -836,12 +825,7 @@ impl Process {
         }
         #[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);
+            let status = pid_fd.wait()?;
             self.status = Some(status);
             return Ok(status);
         }
@@ -857,22 +841,11 @@ impl Process {
         }
         #[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 = pid_fd.try_wait()?;
+            if let Some(status) = status {
+                self.status = Some(status)
             }
-            let status = ExitStatus::from_waitid_siginfo(siginfo);
-            self.status = Some(status);
-            return Ok(Some(status));
+            return Ok(status);
         }
         let mut status = 0 as c_int;
         let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
@@ -1105,20 +1078,33 @@ impl ExitStatusError {
 }
 
 #[cfg(target_os = "linux")]
-#[unstable(feature = "linux_pidfd", issue = "82971")]
-impl crate::os::linux::process::ChildExt for crate::process::Child {
-    fn pidfd(&self) -> io::Result<&PidFd> {
-        self.handle
-            .pidfd
-            .as_ref()
-            .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
-    }
+mod linux_child_ext {
+
+    use crate::io;
+    use crate::mem;
+    use crate::os::linux::process as os;
+    use crate::sys::pal::unix::linux::pidfd as imp;
+    use crate::sys::pal::unix::ErrorKind;
+    use crate::sys_common::FromInner;
+
+    #[unstable(feature = "linux_pidfd", issue = "82971")]
+    impl crate::os::linux::process::ChildExt for crate::process::Child {
+        fn pidfd(&self) -> io::Result<&os::PidFd> {
+            self.handle
+                .pidfd
+                .as_ref()
+                // SAFETY: The os type is a transparent wrapper, therefore we can transmute references
+                .map(|fd| unsafe { mem::transmute::<&imp::PidFd, &os::PidFd>(fd) })
+                .ok_or_else(|| io::Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
+        }
 
-    fn take_pidfd(&mut self) -> io::Result<PidFd> {
-        self.handle
-            .pidfd
-            .take()
-            .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
+        fn take_pidfd(&mut self) -> io::Result<os::PidFd> {
+            self.handle
+                .pidfd
+                .take()
+                .map(|fd| <os::PidFd as FromInner<imp::PidFd>>::from_inner(fd))
+                .ok_or_else(|| io::Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
+        }
     }
 }
 
diff --git a/library/std/src/sys/pal/unix/process/process_unix/tests.rs b/library/std/src/sys/pal/unix/process/process_unix/tests.rs
index 0a6c6ec19fc..e5e1f956bc3 100644
--- a/library/std/src/sys/pal/unix/process/process_unix/tests.rs
+++ b/library/std/src/sys/pal/unix/process/process_unix/tests.rs
@@ -60,57 +60,3 @@ fn test_command_fork_no_unwind() {
             || signal == libc::SIGSEGV
     );
 }
-
-#[test]
-#[cfg(target_os = "linux")] // pidfds are a linux-specific concept
-fn test_command_pidfd() {
-    use crate::assert_matches::assert_matches;
-    use crate::os::fd::{AsRawFd, RawFd};
-    use crate::os::linux::process::{ChildExt, CommandExt};
-    use crate::process::Command;
-
-    // pidfds require the pidfd_open syscall
-    let our_pid = crate::process::id();
-    let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) };
-    let pidfd_open_available = if pidfd >= 0 {
-        unsafe { libc::close(pidfd as RawFd) };
-        true
-    } else {
-        false
-    };
-
-    // 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");
-}