1 | //! Linux-specific extensions to primitives in the [`std::process`] module. |
2 | //! |
3 | //! [`std::process`]: crate::process |
4 | |
5 | #![unstable (feature = "linux_pidfd" , issue = "82971" )] |
6 | |
7 | use crate::io::Result; |
8 | use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; |
9 | use crate::process::{self, ExitStatus}; |
10 | use crate::sealed::Sealed; |
11 | #[cfg (not(doc))] |
12 | use crate::sys::{fd::FileDesc, linux::pidfd::PidFd as InnerPidFd}; |
13 | use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; |
14 | |
15 | #[cfg (doc)] |
16 | struct InnerPidFd; |
17 | |
18 | /// This type represents a file descriptor that refers to a process. |
19 | /// |
20 | /// A `PidFd` can be obtained by setting the corresponding option on [`Command`] |
21 | /// with [`create_pidfd`]. Subsequently, the created pidfd can be retrieved |
22 | /// from the [`Child`] by calling [`pidfd`] or [`into_pidfd`]. |
23 | /// |
24 | /// Example: |
25 | /// ```no_run |
26 | /// #![feature(linux_pidfd)] |
27 | /// use std::os::linux::process::{CommandExt, ChildExt}; |
28 | /// use std::process::Command; |
29 | /// |
30 | /// let mut child = Command::new("echo" ) |
31 | /// .create_pidfd(true) |
32 | /// .spawn() |
33 | /// .expect("Failed to spawn child" ); |
34 | /// |
35 | /// let pidfd = child |
36 | /// .into_pidfd() |
37 | /// .expect("Failed to retrieve pidfd" ); |
38 | /// |
39 | /// // The file descriptor will be closed when `pidfd` is dropped. |
40 | /// ``` |
41 | /// Refer to the man page of [`pidfd_open(2)`] for further details. |
42 | /// |
43 | /// [`Command`]: process::Command |
44 | /// [`create_pidfd`]: CommandExt::create_pidfd |
45 | /// [`Child`]: process::Child |
46 | /// [`pidfd`]: fn@ChildExt::pidfd |
47 | /// [`into_pidfd`]: ChildExt::into_pidfd |
48 | /// [`pidfd_open(2)`]: https://man7.org/linux/man-pages/man2/pidfd_open.2.html |
49 | #[derive (Debug)] |
50 | #[repr (transparent)] |
51 | pub struct PidFd { |
52 | inner: InnerPidFd, |
53 | } |
54 | |
55 | impl PidFd { |
56 | /// Forces the child process to exit. |
57 | /// |
58 | /// Unlike [`Child::kill`] it is possible to attempt to kill |
59 | /// reaped children since PidFd does not suffer from pid recycling |
60 | /// races. But doing so will return an Error. |
61 | /// |
62 | /// [`Child::kill`]: process::Child::kill |
63 | pub fn kill(&self) -> Result<()> { |
64 | self.inner.kill() |
65 | } |
66 | |
67 | /// Waits for the child to exit completely, returning the status that it exited with. |
68 | /// |
69 | /// Unlike [`Child::wait`] it does not ensure that the stdin handle is closed. |
70 | /// Additionally it will not return an `ExitStatus` if the child |
71 | /// has already been reaped. Instead an error will be returned. |
72 | /// |
73 | /// [`Child::wait`]: process::Child::wait |
74 | pub fn wait(&self) -> Result<ExitStatus> { |
75 | self.inner.wait().map(FromInner::from_inner) |
76 | } |
77 | |
78 | /// Attempts to collect the exit status of the child if it has already exited. |
79 | /// |
80 | /// Unlike [`Child::try_wait`] this method will return an Error |
81 | /// if the child has already been reaped. |
82 | /// |
83 | /// [`Child::try_wait`]: process::Child::try_wait |
84 | pub fn try_wait(&self) -> Result<Option<ExitStatus>> { |
85 | Ok(self.inner.try_wait()?.map(FromInner::from_inner)) |
86 | } |
87 | } |
88 | |
89 | impl AsInner<InnerPidFd> for PidFd { |
90 | #[inline ] |
91 | fn as_inner(&self) -> &InnerPidFd { |
92 | &self.inner |
93 | } |
94 | } |
95 | |
96 | impl FromInner<InnerPidFd> for PidFd { |
97 | fn from_inner(inner: InnerPidFd) -> PidFd { |
98 | PidFd { inner } |
99 | } |
100 | } |
101 | |
102 | impl IntoInner<InnerPidFd> for PidFd { |
103 | fn into_inner(self) -> InnerPidFd { |
104 | self.inner |
105 | } |
106 | } |
107 | |
108 | impl AsRawFd for PidFd { |
109 | #[inline ] |
110 | fn as_raw_fd(&self) -> RawFd { |
111 | self.as_inner().as_inner().as_raw_fd() |
112 | } |
113 | } |
114 | |
115 | impl FromRawFd for PidFd { |
116 | unsafe fn from_raw_fd(fd: RawFd) -> Self { |
117 | Self::from_inner(InnerPidFd::from_raw_fd(fd)) |
118 | } |
119 | } |
120 | |
121 | impl IntoRawFd for PidFd { |
122 | fn into_raw_fd(self) -> RawFd { |
123 | self.into_inner().into_inner().into_raw_fd() |
124 | } |
125 | } |
126 | |
127 | impl AsFd for PidFd { |
128 | fn as_fd(&self) -> BorrowedFd<'_> { |
129 | self.as_inner().as_inner().as_fd() |
130 | } |
131 | } |
132 | |
133 | impl From<OwnedFd> for PidFd { |
134 | fn from(fd: OwnedFd) -> Self { |
135 | Self::from_inner(InnerPidFd::from_inner(FileDesc::from_inner(fd))) |
136 | } |
137 | } |
138 | |
139 | impl From<PidFd> for OwnedFd { |
140 | fn from(pid_fd: PidFd) -> Self { |
141 | pid_fd.into_inner().into_inner().into_inner() |
142 | } |
143 | } |
144 | |
145 | /// Os-specific extensions for [`Child`] |
146 | /// |
147 | /// [`Child`]: process::Child |
148 | pub trait ChildExt: Sealed { |
149 | /// Obtains a reference to the [`PidFd`] created for this [`Child`], if available. |
150 | /// |
151 | /// A pidfd will only be available if its creation was requested with |
152 | /// [`create_pidfd`] when the corresponding [`Command`] was created. |
153 | /// |
154 | /// Even if requested, a pidfd may not be available due to an older |
155 | /// version of Linux being in use, or if some other error occurred. |
156 | /// |
157 | /// [`Command`]: process::Command |
158 | /// [`create_pidfd`]: CommandExt::create_pidfd |
159 | /// [`Child`]: process::Child |
160 | fn pidfd(&self) -> Result<&PidFd>; |
161 | |
162 | /// Returns the [`PidFd`] created for this [`Child`], if available. |
163 | /// Otherwise self is returned. |
164 | /// |
165 | /// A pidfd will only be available if its creation was requested with |
166 | /// [`create_pidfd`] when the corresponding [`Command`] was created. |
167 | /// |
168 | /// Taking ownership of the PidFd consumes the Child to avoid pid reuse |
169 | /// races. Use [`pidfd`] and [`BorrowedFd::try_clone_to_owned`] if |
170 | /// you don't want to disassemble the Child yet. |
171 | /// |
172 | /// Even if requested, a pidfd may not be available due to an older |
173 | /// version of Linux being in use, or if some other error occurred. |
174 | /// |
175 | /// [`Command`]: process::Command |
176 | /// [`create_pidfd`]: CommandExt::create_pidfd |
177 | /// [`pidfd`]: ChildExt::pidfd |
178 | /// [`Child`]: process::Child |
179 | fn into_pidfd(self) -> crate::result::Result<PidFd, Self> |
180 | where |
181 | Self: Sized; |
182 | } |
183 | |
184 | /// Os-specific extensions for [`Command`] |
185 | /// |
186 | /// [`Command`]: process::Command |
187 | pub trait CommandExt: Sealed { |
188 | /// Sets whether a [`PidFd`](struct@PidFd) should be created for the [`Child`] |
189 | /// spawned by this [`Command`]. |
190 | /// By default, no pidfd will be created. |
191 | /// |
192 | /// The pidfd can be retrieved from the child with [`pidfd`] or [`into_pidfd`]. |
193 | /// |
194 | /// A pidfd will only be created if it is possible to do so |
195 | /// in a guaranteed race-free manner. Otherwise, [`pidfd`] will return an error. |
196 | /// |
197 | /// If a pidfd has been successfully created and not been taken from the `Child` |
198 | /// then calls to `kill()`, `wait()` and `try_wait()` will use the pidfd |
199 | /// instead of the pid. This can prevent pid recycling races, e.g. |
200 | /// those caused by rogue libraries in the same process prematurely reaping |
201 | /// zombie children via `waitpid(-1, ...)` calls. |
202 | /// |
203 | /// [`Command`]: process::Command |
204 | /// [`Child`]: process::Child |
205 | /// [`pidfd`]: fn@ChildExt::pidfd |
206 | /// [`into_pidfd`]: ChildExt::into_pidfd |
207 | fn create_pidfd(&mut self, val: bool) -> &mut process::Command; |
208 | } |
209 | |
210 | impl CommandExt for process::Command { |
211 | fn create_pidfd(&mut self, val: bool) -> &mut process::Command { |
212 | self.as_inner_mut().create_pidfd(val); |
213 | self |
214 | } |
215 | } |
216 | |