Skip to content

Commit 4e21ef2

Browse files
committed
Auto 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 2e9c870 + 4a832d3 commit 4e21ef2

File tree

8 files changed

+410
-11
lines changed

8 files changed

+410
-11
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
#![doc(cfg(target_os = "linux"))]
55

66
pub mod fs;
7+
pub mod process;
78
pub mod raw;

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

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

library/std/src/process.rs

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

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

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

+41
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ pub struct Command {
7979
stdin: Option<Stdio>,
8080
stdout: Option<Stdio>,
8181
stderr: Option<Stdio>,
82+
#[cfg(target_os = "linux")]
83+
create_pidfd: bool,
8284
}
8385

8486
// Create a new type for argv, so that we can make it `Send` and `Sync`
@@ -124,6 +126,7 @@ pub enum Stdio {
124126
}
125127

126128
impl Command {
129+
#[cfg(not(target_os = "linux"))]
127130
pub fn new(program: &OsStr) -> Command {
128131
let mut saw_nul = false;
129132
let program = os2c(program, &mut saw_nul);
@@ -144,6 +147,28 @@ impl Command {
144147
}
145148
}
146149

150+
#[cfg(target_os = "linux")]
151+
pub fn new(program: &OsStr) -> Command {
152+
let mut saw_nul = false;
153+
let program = os2c(program, &mut saw_nul);
154+
Command {
155+
argv: Argv(vec![program.as_ptr(), ptr::null()]),
156+
args: vec![program.clone()],
157+
program,
158+
env: Default::default(),
159+
cwd: None,
160+
uid: None,
161+
gid: None,
162+
saw_nul,
163+
closures: Vec::new(),
164+
groups: None,
165+
stdin: None,
166+
stdout: None,
167+
stderr: None,
168+
create_pidfd: false,
169+
}
170+
}
171+
147172
pub fn set_arg_0(&mut self, arg: &OsStr) {
148173
// Set a new arg0
149174
let arg = os2c(arg, &mut self.saw_nul);
@@ -177,6 +202,22 @@ impl Command {
177202
self.groups = Some(Box::from(groups));
178203
}
179204

205+
#[cfg(target_os = "linux")]
206+
pub fn create_pidfd(&mut self, val: bool) {
207+
self.create_pidfd = val;
208+
}
209+
210+
#[cfg(not(target_os = "linux"))]
211+
#[allow(dead_code)]
212+
pub fn get_create_pidfd(&self) -> bool {
213+
false
214+
}
215+
216+
#[cfg(target_os = "linux")]
217+
pub fn get_create_pidfd(&self) -> bool {
218+
self.create_pidfd
219+
}
220+
180221
pub fn saw_nul(&self) -> bool {
181222
self.saw_nul
182223
}

0 commit comments

Comments
 (0)