]>
Commit | Line | Data |
---|---|---|
c295e0f8 XL |
1 | //! Unix-specific extensions to primitives in the [`std::process`] module. |
2 | //! | |
3 | //! [`std::process`]: crate::process | |
d9579d0f AL |
4 | |
5 | #![stable(feature = "rust1", since = "1.0.0")] | |
6 | ||
60c5eb7d | 7 | use crate::ffi::OsStr; |
532ac7d7 | 8 | use crate::io; |
94222f64 | 9 | use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; |
532ac7d7 | 10 | use crate::process; |
6a06907d | 11 | use crate::sealed::Sealed; |
532ac7d7 | 12 | use crate::sys; |
60c5eb7d | 13 | use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; |
d9579d0f | 14 | |
83c7162d | 15 | /// Unix-specific extensions to the [`process::Command`] builder. |
6a06907d XL |
16 | /// |
17 | /// This trait is sealed: it cannot be implemented outside the standard library. | |
18 | /// This is so that future additional methods are not breaking changes. | |
d9579d0f | 19 | #[stable(feature = "rust1", since = "1.0.0")] |
6a06907d | 20 | pub trait CommandExt: Sealed { |
9fa01778 | 21 | /// Sets the child process's user ID. This translates to a |
d9579d0f AL |
22 | /// `setuid` call in the child process. Failure in the `setuid` |
23 | /// call will cause the spawn to fail. | |
24 | #[stable(feature = "rust1", since = "1.0.0")] | |
29967ef6 XL |
25 | fn uid( |
26 | &mut self, | |
27 | #[cfg(not(target_os = "vxworks"))] id: u32, | |
28 | #[cfg(target_os = "vxworks")] id: u16, | |
29 | ) -> &mut process::Command; | |
d9579d0f | 30 | |
9fa01778 | 31 | /// Similar to `uid`, but sets the group ID of the child process. This has |
d9579d0f AL |
32 | /// the same semantics as the `uid` field. |
33 | #[stable(feature = "rust1", since = "1.0.0")] | |
29967ef6 XL |
34 | fn gid( |
35 | &mut self, | |
36 | #[cfg(not(target_os = "vxworks"))] id: u32, | |
37 | #[cfg(target_os = "vxworks")] id: u16, | |
38 | ) -> &mut process::Command; | |
e9174d1e | 39 | |
5869c6ff XL |
40 | /// Sets the supplementary group IDs for the calling process. Translates to |
41 | /// a `setgroups` call in the child process. | |
3c0e092e | 42 | #[unstable(feature = "setgroups", issue = "90747")] |
5869c6ff XL |
43 | fn groups( |
44 | &mut self, | |
45 | #[cfg(not(target_os = "vxworks"))] groups: &[u32], | |
46 | #[cfg(target_os = "vxworks")] groups: &[u16], | |
47 | ) -> &mut process::Command; | |
48 | ||
7453a54e SL |
49 | /// Schedules a closure to be run just before the `exec` function is |
50 | /// invoked. | |
51 | /// | |
52 | /// The closure is allowed to return an I/O error whose OS error code will | |
53 | /// be communicated back to the parent and returned as an error from when | |
54 | /// the spawn was requested. | |
55 | /// | |
56 | /// Multiple closures can be registered and they will be called in order of | |
57 | /// their registration. If a closure returns `Err` then no further closures | |
58 | /// will be called and the spawn operation will immediately return with a | |
59 | /// failure. | |
60 | /// | |
9fa01778 | 61 | /// # Notes and Safety |
7453a54e SL |
62 | /// |
63 | /// This closure will be run in the context of the child process after a | |
3b2f2976 | 64 | /// `fork`. This primarily means that any modifications made to memory on |
7453a54e SL |
65 | /// behalf of this closure will **not** be visible to the parent process. |
66 | /// This is often a very constrained environment where normal operations | |
6a06907d XL |
67 | /// like `malloc`, accessing environment variables through [`std::env`] |
68 | /// or acquiring a mutex are not guaranteed to work (due to | |
7453a54e SL |
69 | /// other threads perhaps still running when the `fork` was run). |
70 | /// | |
6a06907d XL |
71 | /// For further details refer to the [POSIX fork() specification] |
72 | /// and the equivalent documentation for any targeted | |
73 | /// platform, especially the requirements around *async-signal-safety*. | |
74 | /// | |
9fa01778 XL |
75 | /// This also means that all resources such as file descriptors and |
76 | /// memory-mapped regions got duplicated. It is your responsibility to make | |
77 | /// sure that the closure does not violate library invariants by making | |
78 | /// invalid use of these duplicates. | |
79 | /// | |
17df50a5 XL |
80 | /// Panicking in the closure is safe only if all the format arguments for the |
81 | /// panic message can be safely formatted; this is because although | |
82 | /// `Command` calls [`std::panic::always_abort`](crate::panic::always_abort) | |
83 | /// before calling the pre_exec hook, panic will still try to format the | |
84 | /// panic message. | |
85 | /// | |
7453a54e SL |
86 | /// When this closure is run, aspects such as the stdio file descriptors and |
87 | /// working directory have successfully been changed, so output to these | |
94222f64 | 88 | /// locations might not appear where intended. |
6a06907d XL |
89 | /// |
90 | /// [POSIX fork() specification]: | |
91 | /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html | |
92 | /// [`std::env`]: mod@crate::env | |
9fa01778 XL |
93 | #[stable(feature = "process_pre_exec", since = "1.34.0")] |
94 | unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command | |
60c5eb7d XL |
95 | where |
96 | F: FnMut() -> io::Result<()> + Send + Sync + 'static; | |
9fa01778 XL |
97 | |
98 | /// Schedules a closure to be run just before the `exec` function is | |
99 | /// invoked. | |
100 | /// | |
101 | /// This method is stable and usable, but it should be unsafe. To fix | |
102 | /// that, it got deprecated in favor of the unsafe [`pre_exec`]. | |
103 | /// | |
3dfed10e | 104 | /// [`pre_exec`]: CommandExt::pre_exec |
476ff2be | 105 | #[stable(feature = "process_exec", since = "1.15.0")] |
9fa01778 | 106 | #[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")] |
7453a54e | 107 | fn before_exec<F>(&mut self, f: F) -> &mut process::Command |
60c5eb7d XL |
108 | where |
109 | F: FnMut() -> io::Result<()> + Send + Sync + 'static, | |
9fa01778 XL |
110 | { |
111 | unsafe { self.pre_exec(f) } | |
112 | } | |
7453a54e SL |
113 | |
114 | /// Performs all the required setup by this `Command`, followed by calling | |
115 | /// the `execvp` syscall. | |
116 | /// | |
117 | /// On success this function will not return, and otherwise it will return | |
118 | /// an error indicating why the exec (or another part of the setup of the | |
119 | /// `Command`) failed. | |
120 | /// | |
8bb4bdeb XL |
121 | /// `exec` not returning has the same implications as calling |
122 | /// [`process::exit`] – no destructors on the current stack or any other | |
123 | /// thread’s stack will be run. Therefore, it is recommended to only call | |
124 | /// `exec` at a point where it is fine to not run any destructors. Note, | |
125 | /// that the `execvp` syscall independently guarantees that all memory is | |
126 | /// freed and all file descriptors with the `CLOEXEC` option (set by default | |
127 | /// on all file descriptors opened by the standard library) are closed. | |
128 | /// | |
7453a54e SL |
129 | /// This function, unlike `spawn`, will **not** `fork` the process to create |
130 | /// a new child. Like spawn, however, the default behavior for the stdio | |
131 | /// descriptors will be to inherited from the current process. | |
132 | /// | |
133 | /// # Notes | |
134 | /// | |
135 | /// The process may be in a "broken state" if this function returns in | |
136 | /// error. For example the working directory, environment variables, signal | |
137 | /// handling settings, various user/group information, or aspects of stdio | |
138 | /// file descriptors may have changed. If a "transactional spawn" is | |
139 | /// required to gracefully handle errors it is recommended to use the | |
140 | /// cross-platform `spawn` instead. | |
54a0048b | 141 | #[stable(feature = "process_exec2", since = "1.9.0")] |
7453a54e | 142 | fn exec(&mut self) -> io::Error; |
60c5eb7d XL |
143 | |
144 | /// Set executable argument | |
145 | /// | |
146 | /// Set the first process argument, `argv[0]`, to something other than the | |
147 | /// default executable path. | |
f9f354fc | 148 | #[stable(feature = "process_set_argv0", since = "1.45.0")] |
60c5eb7d XL |
149 | fn arg0<S>(&mut self, arg: S) -> &mut process::Command |
150 | where | |
151 | S: AsRef<OsStr>; | |
d9579d0f AL |
152 | } |
153 | ||
154 | #[stable(feature = "rust1", since = "1.0.0")] | |
155 | impl CommandExt for process::Command { | |
29967ef6 XL |
156 | fn uid( |
157 | &mut self, | |
158 | #[cfg(not(target_os = "vxworks"))] id: u32, | |
159 | #[cfg(target_os = "vxworks")] id: u16, | |
160 | ) -> &mut process::Command { | |
7453a54e | 161 | self.as_inner_mut().uid(id); |
d9579d0f AL |
162 | self |
163 | } | |
164 | ||
29967ef6 XL |
165 | fn gid( |
166 | &mut self, | |
167 | #[cfg(not(target_os = "vxworks"))] id: u32, | |
168 | #[cfg(target_os = "vxworks")] id: u16, | |
169 | ) -> &mut process::Command { | |
7453a54e | 170 | self.as_inner_mut().gid(id); |
d9579d0f AL |
171 | self |
172 | } | |
e9174d1e | 173 | |
5869c6ff XL |
174 | fn groups( |
175 | &mut self, | |
176 | #[cfg(not(target_os = "vxworks"))] groups: &[u32], | |
177 | #[cfg(target_os = "vxworks")] groups: &[u16], | |
178 | ) -> &mut process::Command { | |
179 | self.as_inner_mut().groups(groups); | |
180 | self | |
181 | } | |
182 | ||
9fa01778 | 183 | unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command |
60c5eb7d XL |
184 | where |
185 | F: FnMut() -> io::Result<()> + Send + Sync + 'static, | |
7453a54e | 186 | { |
9fa01778 | 187 | self.as_inner_mut().pre_exec(Box::new(f)); |
e9174d1e SL |
188 | self |
189 | } | |
7453a54e SL |
190 | |
191 | fn exec(&mut self) -> io::Error { | |
6a06907d XL |
192 | // NOTE: This may *not* be safe to call after `libc::fork`, because it |
193 | // may allocate. That may be worth fixing at some point in the future. | |
7453a54e SL |
194 | self.as_inner_mut().exec(sys::process::Stdio::Inherit) |
195 | } | |
60c5eb7d XL |
196 | |
197 | fn arg0<S>(&mut self, arg: S) -> &mut process::Command | |
198 | where | |
199 | S: AsRef<OsStr>, | |
200 | { | |
201 | self.as_inner_mut().set_arg_0(arg.as_ref()); | |
202 | self | |
203 | } | |
d9579d0f AL |
204 | } |
205 | ||
17df50a5 XL |
206 | /// Unix-specific extensions to [`process::ExitStatus`] and |
207 | /// [`ExitStatusError`](process::ExitStatusError). | |
5869c6ff | 208 | /// |
17df50a5 | 209 | /// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as |
3c0e092e | 210 | /// passed to the `_exit` system call or returned by |
17df50a5 XL |
211 | /// [`ExitStatus::code()`](crate::process::ExitStatus::code). It represents **any wait status** |
212 | /// as returned by one of the `wait` family of system | |
213 | /// calls. | |
6a06907d | 214 | /// |
17df50a5 XL |
215 | /// A Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but can also |
216 | /// represent other kinds of process event. | |
6a06907d | 217 | /// |
5869c6ff XL |
218 | /// This trait is sealed: it cannot be implemented outside the standard library. |
219 | /// This is so that future additional methods are not breaking changes. | |
d9579d0f | 220 | #[stable(feature = "rust1", since = "1.0.0")] |
6a06907d | 221 | pub trait ExitStatusExt: Sealed { |
17df50a5 XL |
222 | /// Creates a new `ExitStatus` or `ExitStatusError` from the raw underlying integer status |
223 | /// value from `wait` | |
6a06907d XL |
224 | /// |
225 | /// The value should be a **wait status, not an exit status**. | |
17df50a5 XL |
226 | /// |
227 | /// # Panics | |
228 | /// | |
229 | /// Panics on an attempt to make an `ExitStatusError` from a wait status of `0`. | |
230 | /// | |
136023e0 | 231 | /// Making an `ExitStatus` always succeeds and never panics. |
5bcae85e | 232 | #[stable(feature = "exit_status_from", since = "1.12.0")] |
a7813a04 XL |
233 | fn from_raw(raw: i32) -> Self; |
234 | ||
d9579d0f | 235 | /// If the process was terminated by a signal, returns that signal. |
5869c6ff XL |
236 | /// |
237 | /// In other words, if `WIFSIGNALED`, this returns `WTERMSIG`. | |
d9579d0f AL |
238 | #[stable(feature = "rust1", since = "1.0.0")] |
239 | fn signal(&self) -> Option<i32>; | |
5869c6ff XL |
240 | |
241 | /// If the process was terminated by a signal, says whether it dumped core. | |
3c0e092e | 242 | #[stable(feature = "unix_process_wait_more", since = "1.58.0")] |
5869c6ff XL |
243 | fn core_dumped(&self) -> bool; |
244 | ||
245 | /// If the process was stopped by a signal, returns that signal. | |
246 | /// | |
247 | /// In other words, if `WIFSTOPPED`, this returns `WSTOPSIG`. This is only possible if the status came from | |
cdc7bbd5 | 248 | /// a `wait` system call which was passed `WUNTRACED`, and was then converted into an `ExitStatus`. |
3c0e092e | 249 | #[stable(feature = "unix_process_wait_more", since = "1.58.0")] |
5869c6ff XL |
250 | fn stopped_signal(&self) -> Option<i32>; |
251 | ||
252 | /// Whether the process was continued from a stopped status. | |
253 | /// | |
254 | /// Ie, `WIFCONTINUED`. This is only possible if the status came from a `wait` system call | |
cdc7bbd5 | 255 | /// which was passed `WCONTINUED`, and was then converted into an `ExitStatus`. |
3c0e092e | 256 | #[stable(feature = "unix_process_wait_more", since = "1.58.0")] |
5869c6ff XL |
257 | fn continued(&self) -> bool; |
258 | ||
259 | /// Returns the underlying raw `wait` status. | |
6a06907d XL |
260 | /// |
261 | /// The returned integer is a **wait status, not an exit status**. | |
3c0e092e | 262 | #[stable(feature = "unix_process_wait_more", since = "1.58.0")] |
5869c6ff | 263 | fn into_raw(self) -> i32; |
d9579d0f AL |
264 | } |
265 | ||
266 | #[stable(feature = "rust1", since = "1.0.0")] | |
267 | impl ExitStatusExt for process::ExitStatus { | |
a7813a04 XL |
268 | fn from_raw(raw: i32) -> Self { |
269 | process::ExitStatus::from_inner(From::from(raw)) | |
270 | } | |
271 | ||
d9579d0f | 272 | fn signal(&self) -> Option<i32> { |
92a42be0 | 273 | self.as_inner().signal() |
d9579d0f | 274 | } |
5869c6ff XL |
275 | |
276 | fn core_dumped(&self) -> bool { | |
277 | self.as_inner().core_dumped() | |
278 | } | |
279 | ||
280 | fn stopped_signal(&self) -> Option<i32> { | |
281 | self.as_inner().stopped_signal() | |
282 | } | |
283 | ||
284 | fn continued(&self) -> bool { | |
285 | self.as_inner().continued() | |
286 | } | |
287 | ||
288 | fn into_raw(self) -> i32 { | |
289 | self.as_inner().into_raw().into() | |
290 | } | |
d9579d0f | 291 | } |
62682a34 | 292 | |
17df50a5 XL |
293 | #[unstable(feature = "exit_status_error", issue = "84908")] |
294 | impl ExitStatusExt for process::ExitStatusError { | |
295 | fn from_raw(raw: i32) -> Self { | |
296 | process::ExitStatus::from_raw(raw) | |
297 | .exit_ok() | |
298 | .expect_err("<ExitStatusError as ExitStatusExt>::from_raw(0) but zero is not an error") | |
299 | } | |
300 | ||
301 | fn signal(&self) -> Option<i32> { | |
302 | self.into_status().signal() | |
303 | } | |
304 | ||
305 | fn core_dumped(&self) -> bool { | |
306 | self.into_status().core_dumped() | |
307 | } | |
308 | ||
309 | fn stopped_signal(&self) -> Option<i32> { | |
310 | self.into_status().stopped_signal() | |
311 | } | |
312 | ||
313 | fn continued(&self) -> bool { | |
314 | self.into_status().continued() | |
315 | } | |
316 | ||
317 | fn into_raw(self) -> i32 { | |
318 | self.into_status().into_raw() | |
319 | } | |
320 | } | |
321 | ||
62682a34 SL |
322 | #[stable(feature = "process_extensions", since = "1.2.0")] |
323 | impl FromRawFd for process::Stdio { | |
cdc7bbd5 | 324 | #[inline] |
62682a34 | 325 | unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { |
94222f64 XL |
326 | let fd = sys::fd::FileDesc::from_raw_fd(fd); |
327 | let io = sys::process::Stdio::Fd(fd); | |
328 | process::Stdio::from_inner(io) | |
329 | } | |
330 | } | |
331 | ||
332 | #[unstable(feature = "io_safety", issue = "87074")] | |
333 | impl From<OwnedFd> for process::Stdio { | |
334 | #[inline] | |
335 | fn from(fd: OwnedFd) -> process::Stdio { | |
336 | let fd = sys::fd::FileDesc::from_inner(fd); | |
7453a54e SL |
337 | let io = sys::process::Stdio::Fd(fd); |
338 | process::Stdio::from_inner(io) | |
62682a34 SL |
339 | } |
340 | } | |
341 | ||
342 | #[stable(feature = "process_extensions", since = "1.2.0")] | |
343 | impl AsRawFd for process::ChildStdin { | |
cdc7bbd5 | 344 | #[inline] |
62682a34 | 345 | fn as_raw_fd(&self) -> RawFd { |
94222f64 | 346 | self.as_inner().as_raw_fd() |
62682a34 SL |
347 | } |
348 | } | |
349 | ||
350 | #[stable(feature = "process_extensions", since = "1.2.0")] | |
351 | impl AsRawFd for process::ChildStdout { | |
cdc7bbd5 | 352 | #[inline] |
62682a34 | 353 | fn as_raw_fd(&self) -> RawFd { |
94222f64 | 354 | self.as_inner().as_raw_fd() |
62682a34 SL |
355 | } |
356 | } | |
357 | ||
358 | #[stable(feature = "process_extensions", since = "1.2.0")] | |
359 | impl AsRawFd for process::ChildStderr { | |
cdc7bbd5 | 360 | #[inline] |
62682a34 | 361 | fn as_raw_fd(&self) -> RawFd { |
94222f64 | 362 | self.as_inner().as_raw_fd() |
62682a34 SL |
363 | } |
364 | } | |
c1a9b12d | 365 | |
c30ab7b3 | 366 | #[stable(feature = "into_raw_os", since = "1.4.0")] |
c1a9b12d | 367 | impl IntoRawFd for process::ChildStdin { |
cdc7bbd5 | 368 | #[inline] |
c1a9b12d | 369 | fn into_raw_fd(self) -> RawFd { |
94222f64 | 370 | self.into_inner().into_inner().into_raw_fd() |
c1a9b12d SL |
371 | } |
372 | } | |
373 | ||
c30ab7b3 | 374 | #[stable(feature = "into_raw_os", since = "1.4.0")] |
c1a9b12d | 375 | impl IntoRawFd for process::ChildStdout { |
cdc7bbd5 | 376 | #[inline] |
c1a9b12d | 377 | fn into_raw_fd(self) -> RawFd { |
94222f64 | 378 | self.into_inner().into_inner().into_raw_fd() |
c1a9b12d SL |
379 | } |
380 | } | |
381 | ||
c30ab7b3 | 382 | #[stable(feature = "into_raw_os", since = "1.4.0")] |
c1a9b12d | 383 | impl IntoRawFd for process::ChildStderr { |
cdc7bbd5 | 384 | #[inline] |
c1a9b12d | 385 | fn into_raw_fd(self) -> RawFd { |
94222f64 XL |
386 | self.into_inner().into_inner().into_raw_fd() |
387 | } | |
388 | } | |
389 | ||
390 | #[unstable(feature = "io_safety", issue = "87074")] | |
391 | impl AsFd for crate::process::ChildStdin { | |
392 | #[inline] | |
393 | fn as_fd(&self) -> BorrowedFd<'_> { | |
394 | self.as_inner().as_fd() | |
395 | } | |
396 | } | |
397 | ||
398 | #[unstable(feature = "io_safety", issue = "87074")] | |
399 | impl From<crate::process::ChildStdin> for OwnedFd { | |
400 | #[inline] | |
401 | fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd { | |
402 | child_stdin.into_inner().into_inner().into_inner() | |
403 | } | |
404 | } | |
405 | ||
406 | #[unstable(feature = "io_safety", issue = "87074")] | |
407 | impl AsFd for crate::process::ChildStdout { | |
408 | #[inline] | |
409 | fn as_fd(&self) -> BorrowedFd<'_> { | |
410 | self.as_inner().as_fd() | |
411 | } | |
412 | } | |
413 | ||
414 | #[unstable(feature = "io_safety", issue = "87074")] | |
415 | impl From<crate::process::ChildStdout> for OwnedFd { | |
416 | #[inline] | |
417 | fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd { | |
418 | child_stdout.into_inner().into_inner().into_inner() | |
419 | } | |
420 | } | |
421 | ||
422 | #[unstable(feature = "io_safety", issue = "87074")] | |
423 | impl AsFd for crate::process::ChildStderr { | |
424 | #[inline] | |
425 | fn as_fd(&self) -> BorrowedFd<'_> { | |
426 | self.as_inner().as_fd() | |
427 | } | |
428 | } | |
429 | ||
430 | #[unstable(feature = "io_safety", issue = "87074")] | |
431 | impl From<crate::process::ChildStderr> for OwnedFd { | |
432 | #[inline] | |
433 | fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd { | |
434 | child_stderr.into_inner().into_inner().into_inner() | |
c1a9b12d SL |
435 | } |
436 | } | |
ff7c6d11 XL |
437 | |
438 | /// Returns the OS-assigned process identifier associated with this process's parent. | |
3c0e092e | 439 | #[must_use] |
83c7162d | 440 | #[stable(feature = "unix_ppid", since = "1.27.0")] |
ff7c6d11 | 441 | pub fn parent_id() -> u32 { |
532ac7d7 | 442 | crate::sys::os::getppid() |
ff7c6d11 | 443 | } |