Skip to content

Commit 717b6c4

Browse files
authored
Rollup merge of rust-lang#81825 - voidc:pidfd, r=joshtriplett
Add Linux-specific pidfd process extensions (take 2) Continuation of rust-lang#77168. I addressed the following concerns from the original PR: - make `CommandExt` and `ChildExt` sealed traits - wrap file descriptors in `PidFd` struct representing ownership over the fd - add `take_pidfd` to take the fd out of `Child` - close fd when dropped Tracking Issue: rust-lang#82971
2 parents c28d8d2 + cfb2d72 commit 717b6c4

File tree

8 files changed

+318
-7
lines changed

8 files changed

+318
-7
lines changed

Cargo.lock

+2-2
Original file line numberDiff line numberDiff line change
@@ -1895,9 +1895,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
18951895

18961896
[[package]]
18971897
name = "libc"
1898-
version = "0.2.88"
1898+
version = "0.2.89"
18991899
source = "registry+https://github.com/rust-lang/crates.io-index"
1900-
checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a"
1900+
checksum = "538c092e5586f4cdd7dd8078c4a79220e3e168880218124dcbce860f0ea938c6"
19011901
dependencies = [
19021902
"rustc-std-workspace-core",
19031903
]

library/std/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] }
1616
panic_unwind = { path = "../panic_unwind", optional = true }
1717
panic_abort = { path = "../panic_abort" }
1818
core = { path = "../core" }
19-
libc = { version = "0.2.88", default-features = false, features = ['rustc-dep-of-std'] }
19+
libc = { version = "0.2.89", default-features = false, features = ['rustc-dep-of-std'] }
2020
compiler_builtins = { version = "0.1.39" }
2121
profiler_builtins = { path = "../profiler_builtins", optional = true }
2222
unwind = { path = "../unwind" }

library/std/src/os/linux/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
#![stable(feature = "raw_ext", since = "1.1.0")]
44

55
pub mod fs;
6+
pub mod process;
67
pub mod raw;

library/std/src/os/linux/process.rs

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//! Linux-specific extensions to primitives in the `std::process` module.
2+
3+
#![unstable(feature = "linux_pidfd", issue = "82971")]
4+
5+
use crate::io::Result;
6+
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
7+
use crate::process;
8+
use crate::sys::fd::FileDesc;
9+
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
10+
11+
/// This type represents a file descriptor that refers to a process.
12+
///
13+
/// A `PidFd` can be obtained by setting the corresponding option on [`Command`]
14+
/// with [`create_pidfd`]. Subsequently, the created pidfd can be retrieved
15+
/// from the [`Child`] by calling [`pidfd`] or [`take_pidfd`].
16+
///
17+
/// Example:
18+
/// ```no_run
19+
/// #![feature(linux_pidfd)]
20+
/// use std::os::linux::process::{CommandExt, ChildExt};
21+
/// use std::process::Command;
22+
///
23+
/// let mut child = Command::new("echo")
24+
/// .create_pidfd(true)
25+
/// .spawn()
26+
/// .expect("Failed to spawn child");
27+
///
28+
/// let pidfd = child
29+
/// .take_pidfd()
30+
/// .expect("Failed to retrieve pidfd");
31+
///
32+
/// // The file descriptor will be closed when `pidfd` is dropped.
33+
/// ```
34+
/// Refer to the man page of [`pidfd_open(2)`] for further details.
35+
///
36+
/// [`Command`]: process::Command
37+
/// [`create_pidfd`]: CommandExt::create_pidfd
38+
/// [`Child`]: process::Child
39+
/// [`pidfd`]: fn@ChildExt::pidfd
40+
/// [`take_pidfd`]: ChildExt::take_pidfd
41+
/// [`pidfd_open(2)`]: https://man7.org/linux/man-pages/man2/pidfd_open.2.html
42+
#[derive(Debug)]
43+
pub struct PidFd {
44+
inner: FileDesc,
45+
}
46+
47+
impl AsInner<FileDesc> for PidFd {
48+
fn as_inner(&self) -> &FileDesc {
49+
&self.inner
50+
}
51+
}
52+
53+
impl FromInner<FileDesc> for PidFd {
54+
fn from_inner(inner: FileDesc) -> PidFd {
55+
PidFd { inner }
56+
}
57+
}
58+
59+
impl IntoInner<FileDesc> for PidFd {
60+
fn into_inner(self) -> FileDesc {
61+
self.inner
62+
}
63+
}
64+
65+
impl AsRawFd for PidFd {
66+
fn as_raw_fd(&self) -> RawFd {
67+
self.as_inner().raw()
68+
}
69+
}
70+
71+
impl FromRawFd for PidFd {
72+
unsafe fn from_raw_fd(fd: RawFd) -> Self {
73+
Self::from_inner(FileDesc::new(fd))
74+
}
75+
}
76+
77+
impl IntoRawFd for PidFd {
78+
fn into_raw_fd(self) -> RawFd {
79+
self.into_inner().into_raw()
80+
}
81+
}
82+
83+
mod private_child_ext {
84+
pub trait Sealed {}
85+
impl Sealed for crate::process::Child {}
86+
}
87+
88+
/// Os-specific extensions for [`Child`]
89+
///
90+
/// [`Child`]: process::Child
91+
pub trait ChildExt: private_child_ext::Sealed {
92+
/// Obtains a reference to the [`PidFd`] created for this [`Child`], if available.
93+
///
94+
/// A pidfd will only be available if its creation was requested with
95+
/// [`create_pidfd`] when the corresponding [`Command`] was created.
96+
///
97+
/// Even if requested, a pidfd may not be available due to an older
98+
/// version of Linux being in use, or if some other error occurred.
99+
///
100+
/// [`Command`]: process::Command
101+
/// [`create_pidfd`]: CommandExt::create_pidfd
102+
/// [`Child`]: process::Child
103+
fn pidfd(&self) -> Result<&PidFd>;
104+
105+
/// Takes ownership of the [`PidFd`] created for this [`Child`], if available.
106+
///
107+
/// A pidfd will only be available if its creation was requested with
108+
/// [`create_pidfd`] when the corresponding [`Command`] was created.
109+
///
110+
/// Even if requested, a pidfd may not be available due to an older
111+
/// version of Linux being in use, or if some other error occurred.
112+
///
113+
/// [`Command`]: process::Command
114+
/// [`create_pidfd`]: CommandExt::create_pidfd
115+
/// [`Child`]: process::Child
116+
fn take_pidfd(&mut self) -> Result<PidFd>;
117+
}
118+
119+
mod private_command_ext {
120+
pub trait Sealed {}
121+
impl Sealed for crate::process::Command {}
122+
}
123+
124+
/// Os-specific extensions for [`Command`]
125+
///
126+
/// [`Command`]: process::Command
127+
pub trait CommandExt: private_command_ext::Sealed {
128+
/// Sets whether a [`PidFd`](struct@PidFd) should be created for the [`Child`]
129+
/// spawned by this [`Command`].
130+
/// By default, no pidfd will be created.
131+
///
132+
/// The pidfd can be retrieved from the child with [`pidfd`] or [`take_pidfd`].
133+
///
134+
/// A pidfd will only be created if it is possible to do so
135+
/// in a guaranteed race-free manner (e.g. if the `clone3` system call
136+
/// is supported). Otherwise, [`pidfd`] will return an error.
137+
///
138+
/// [`Command`]: process::Command
139+
/// [`Child`]: process::Child
140+
/// [`pidfd`]: fn@ChildExt::pidfd
141+
/// [`take_pidfd`]: ChildExt::take_pidfd
142+
fn create_pidfd(&mut self, val: bool) -> &mut process::Command;
143+
}
144+
145+
impl CommandExt for process::Command {
146+
fn create_pidfd(&mut self, val: bool) -> &mut process::Command {
147+
self.as_inner_mut().create_pidfd(val);
148+
self
149+
}
150+
}

library/std/src/process.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
165165
/// [`wait`]: Child::wait
166166
#[stable(feature = "process", since = "1.0.0")]
167167
pub struct Child {
168-
handle: imp::Process,
168+
pub(crate) handle: imp::Process,
169169

170170
/// The handle for writing to the child's standard input (stdin), if it has
171171
/// been captured. To avoid partially moving

library/std/src/sys/unix/process/process_common.rs

+6
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pub struct Command {
7979
stdin: Option<Stdio>,
8080
stdout: Option<Stdio>,
8181
stderr: Option<Stdio>,
82+
pub(crate) create_pidfd: bool,
8283
}
8384

8485
// Create a new type for argv, so that we can make it `Send` and `Sync`
@@ -141,6 +142,7 @@ impl Command {
141142
stdin: None,
142143
stdout: None,
143144
stderr: None,
145+
create_pidfd: false,
144146
}
145147
}
146148

@@ -177,6 +179,10 @@ impl Command {
177179
self.groups = Some(Box::from(groups));
178180
}
179181

182+
pub fn create_pidfd(&mut self, val: bool) {
183+
self.create_pidfd = val;
184+
}
185+
180186
pub fn saw_nul(&self) -> bool {
181187
self.saw_nul
182188
}

0 commit comments

Comments
 (0)