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