1 | //! Unix-specific extensions to primitives in the [`std::process`] module. |
2 | //! |
3 | //! [`std::process`]: crate::process |
4 | |
5 | #![stable (feature = "rust1" , since = "1.0.0" )] |
6 | |
7 | use cfg_if::cfg_if; |
8 | |
9 | use crate::ffi::OsStr; |
10 | use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; |
11 | use crate::path::Path; |
12 | use crate::sealed::Sealed; |
13 | use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; |
14 | use crate::{io, process, sys}; |
15 | |
16 | cfg_if! { |
17 | if #[cfg(any(target_os = "vxworks" , target_os = "espidf" , target_os = "horizon" , target_os = "vita" ))] { |
18 | type UserId = u16; |
19 | type GroupId = u16; |
20 | } else if #[cfg(target_os = "nto" )] { |
21 | // Both IDs are signed, see `sys/target_nto.h` of the QNX Neutrino SDP. |
22 | // Only positive values should be used, see e.g. |
23 | // https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/s/setuid.html |
24 | type UserId = i32; |
25 | type GroupId = i32; |
26 | } else { |
27 | type UserId = u32; |
28 | type GroupId = u32; |
29 | } |
30 | } |
31 | |
32 | /// Unix-specific extensions to the [`process::Command`] builder. |
33 | /// |
34 | /// This trait is sealed: it cannot be implemented outside the standard library. |
35 | /// This is so that future additional methods are not breaking changes. |
36 | #[stable (feature = "rust1" , since = "1.0.0" )] |
37 | pub trait CommandExt: Sealed { |
38 | /// Sets the child process's user ID. This translates to a |
39 | /// `setuid` call in the child process. Failure in the `setuid` |
40 | /// call will cause the spawn to fail. |
41 | /// |
42 | /// # Notes |
43 | /// |
44 | /// This will also trigger a call to `setgroups(0, NULL)` in the child |
45 | /// process if no groups have been specified. |
46 | /// This removes supplementary groups that might have given the child |
47 | /// unwanted permissions. |
48 | #[stable (feature = "rust1" , since = "1.0.0" )] |
49 | fn uid(&mut self, id: UserId) -> &mut process::Command; |
50 | |
51 | /// Similar to `uid`, but sets the group ID of the child process. This has |
52 | /// the same semantics as the `uid` field. |
53 | #[stable (feature = "rust1" , since = "1.0.0" )] |
54 | fn gid(&mut self, id: GroupId) -> &mut process::Command; |
55 | |
56 | /// Sets the supplementary group IDs for the calling process. Translates to |
57 | /// a `setgroups` call in the child process. |
58 | #[unstable (feature = "setgroups" , issue = "90747" )] |
59 | fn groups(&mut self, groups: &[GroupId]) -> &mut process::Command; |
60 | |
61 | /// Schedules a closure to be run just before the `exec` function is |
62 | /// invoked. |
63 | /// |
64 | /// The closure is allowed to return an I/O error whose OS error code will |
65 | /// be communicated back to the parent and returned as an error from when |
66 | /// the spawn was requested. |
67 | /// |
68 | /// Multiple closures can be registered and they will be called in order of |
69 | /// their registration. If a closure returns `Err` then no further closures |
70 | /// will be called and the spawn operation will immediately return with a |
71 | /// failure. |
72 | /// |
73 | /// # Notes and Safety |
74 | /// |
75 | /// This closure will be run in the context of the child process after a |
76 | /// `fork`. This primarily means that any modifications made to memory on |
77 | /// behalf of this closure will **not** be visible to the parent process. |
78 | /// This is often a very constrained environment where normal operations |
79 | /// like `malloc`, accessing environment variables through [`std::env`] |
80 | /// or acquiring a mutex are not guaranteed to work (due to |
81 | /// other threads perhaps still running when the `fork` was run). |
82 | /// |
83 | /// For further details refer to the [POSIX fork() specification] |
84 | /// and the equivalent documentation for any targeted |
85 | /// platform, especially the requirements around *async-signal-safety*. |
86 | /// |
87 | /// This also means that all resources such as file descriptors and |
88 | /// memory-mapped regions got duplicated. It is your responsibility to make |
89 | /// sure that the closure does not violate library invariants by making |
90 | /// invalid use of these duplicates. |
91 | /// |
92 | /// Panicking in the closure is safe only if all the format arguments for the |
93 | /// panic message can be safely formatted; this is because although |
94 | /// `Command` calls [`std::panic::always_abort`](crate::panic::always_abort) |
95 | /// before calling the pre_exec hook, panic will still try to format the |
96 | /// panic message. |
97 | /// |
98 | /// When this closure is run, aspects such as the stdio file descriptors and |
99 | /// working directory have successfully been changed, so output to these |
100 | /// locations might not appear where intended. |
101 | /// |
102 | /// [POSIX fork() specification]: |
103 | /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html |
104 | /// [`std::env`]: mod@crate::env |
105 | #[stable (feature = "process_pre_exec" , since = "1.34.0" )] |
106 | unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command |
107 | where |
108 | F: FnMut() -> io::Result<()> + Send + Sync + 'static; |
109 | |
110 | /// Schedules a closure to be run just before the `exec` function is |
111 | /// invoked. |
112 | /// |
113 | /// `before_exec` used to be a safe method, but it needs to be unsafe since the closure may only |
114 | /// perform operations that are *async-signal-safe*. Hence it got deprecated in favor of the |
115 | /// unsafe [`pre_exec`]. Meanwhile, Rust gained the ability to make an existing safe method |
116 | /// fully unsafe in a new edition, which is how `before_exec` became `unsafe`. It still also |
117 | /// remains deprecated; `pre_exec` should be used instead. |
118 | /// |
119 | /// [`pre_exec`]: CommandExt::pre_exec |
120 | #[stable (feature = "process_exec" , since = "1.15.0" )] |
121 | #[deprecated (since = "1.37.0" , note = "should be unsafe, use `pre_exec` instead" )] |
122 | #[rustc_deprecated_safe_2024 (audit_that = "the closure is async-signal-safe" )] |
123 | unsafe fn before_exec<F>(&mut self, f: F) -> &mut process::Command |
124 | where |
125 | F: FnMut() -> io::Result<()> + Send + Sync + 'static, |
126 | { |
127 | unsafe { self.pre_exec(f) } |
128 | } |
129 | |
130 | /// Performs all the required setup by this `Command`, followed by calling |
131 | /// the `execvp` syscall. |
132 | /// |
133 | /// On success this function will not return, and otherwise it will return |
134 | /// an error indicating why the exec (or another part of the setup of the |
135 | /// `Command`) failed. |
136 | /// |
137 | /// `exec` not returning has the same implications as calling |
138 | /// [`process::exit`] – no destructors on the current stack or any other |
139 | /// thread’s stack will be run. Therefore, it is recommended to only call |
140 | /// `exec` at a point where it is fine to not run any destructors. Note, |
141 | /// that the `execvp` syscall independently guarantees that all memory is |
142 | /// freed and all file descriptors with the `CLOEXEC` option (set by default |
143 | /// on all file descriptors opened by the standard library) are closed. |
144 | /// |
145 | /// This function, unlike `spawn`, will **not** `fork` the process to create |
146 | /// a new child. Like spawn, however, the default behavior for the stdio |
147 | /// descriptors will be to inherit them from the current process. |
148 | /// |
149 | /// # Notes |
150 | /// |
151 | /// The process may be in a "broken state" if this function returns in |
152 | /// error. For example the working directory, environment variables, signal |
153 | /// handling settings, various user/group information, or aspects of stdio |
154 | /// file descriptors may have changed. If a "transactional spawn" is |
155 | /// required to gracefully handle errors it is recommended to use the |
156 | /// cross-platform `spawn` instead. |
157 | #[stable (feature = "process_exec2" , since = "1.9.0" )] |
158 | #[must_use ] |
159 | fn exec(&mut self) -> io::Error; |
160 | |
161 | /// Set executable argument |
162 | /// |
163 | /// Set the first process argument, `argv[0]`, to something other than the |
164 | /// default executable path. |
165 | #[stable (feature = "process_set_argv0" , since = "1.45.0" )] |
166 | fn arg0<S>(&mut self, arg: S) -> &mut process::Command |
167 | where |
168 | S: AsRef<OsStr>; |
169 | |
170 | /// Sets the process group ID (PGID) of the child process. Equivalent to a |
171 | /// `setpgid` call in the child process, but may be more efficient. |
172 | /// |
173 | /// Process groups determine which processes receive signals. |
174 | /// |
175 | /// # Examples |
176 | /// |
177 | /// Pressing Ctrl-C in a terminal will send SIGINT to all processes in |
178 | /// the current foreground process group. By spawning the `sleep` |
179 | /// subprocess in a new process group, it will not receive SIGINT from the |
180 | /// terminal. |
181 | /// |
182 | /// The parent process could install a signal handler and manage the |
183 | /// subprocess on its own terms. |
184 | /// |
185 | /// A process group ID of 0 will use the process ID as the PGID. |
186 | /// |
187 | /// ```no_run |
188 | /// use std::process::Command; |
189 | /// use std::os::unix::process::CommandExt; |
190 | /// |
191 | /// Command::new("sleep" ) |
192 | /// .arg("10" ) |
193 | /// .process_group(0) |
194 | /// .spawn()? |
195 | /// .wait()?; |
196 | /// # |
197 | /// # Ok::<_, Box<dyn std::error::Error>>(()) |
198 | /// ``` |
199 | #[stable (feature = "process_set_process_group" , since = "1.64.0" )] |
200 | fn process_group(&mut self, pgroup: i32) -> &mut process::Command; |
201 | |
202 | /// Set the root of the child process. This calls `chroot` in the child process before executing |
203 | /// the command. |
204 | /// |
205 | /// This happens before changing to the directory specified with |
206 | /// [`process::Command::current_dir`], and that directory will be relative to the new root. |
207 | /// |
208 | /// If no directory has been specified with [`process::Command::current_dir`], this will set the |
209 | /// directory to `/`, to avoid leaving the current directory outside the chroot. (This is an |
210 | /// intentional difference from the underlying `chroot` system call.) |
211 | #[unstable (feature = "process_chroot" , issue = "141298" )] |
212 | fn chroot<P: AsRef<Path>>(&mut self, dir: P) -> &mut process::Command; |
213 | } |
214 | |
215 | #[stable (feature = "rust1" , since = "1.0.0" )] |
216 | impl CommandExt for process::Command { |
217 | fn uid(&mut self, id: UserId) -> &mut process::Command { |
218 | self.as_inner_mut().uid(id); |
219 | self |
220 | } |
221 | |
222 | fn gid(&mut self, id: GroupId) -> &mut process::Command { |
223 | self.as_inner_mut().gid(id); |
224 | self |
225 | } |
226 | |
227 | fn groups(&mut self, groups: &[GroupId]) -> &mut process::Command { |
228 | self.as_inner_mut().groups(groups); |
229 | self |
230 | } |
231 | |
232 | unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command |
233 | where |
234 | F: FnMut() -> io::Result<()> + Send + Sync + 'static, |
235 | { |
236 | self.as_inner_mut().pre_exec(Box::new(f)); |
237 | self |
238 | } |
239 | |
240 | fn exec(&mut self) -> io::Error { |
241 | // NOTE: This may *not* be safe to call after `libc::fork`, because it |
242 | // may allocate. That may be worth fixing at some point in the future. |
243 | self.as_inner_mut().exec(sys::process::Stdio::Inherit) |
244 | } |
245 | |
246 | fn arg0<S>(&mut self, arg: S) -> &mut process::Command |
247 | where |
248 | S: AsRef<OsStr>, |
249 | { |
250 | self.as_inner_mut().set_arg_0(arg.as_ref()); |
251 | self |
252 | } |
253 | |
254 | fn process_group(&mut self, pgroup: i32) -> &mut process::Command { |
255 | self.as_inner_mut().pgroup(pgroup); |
256 | self |
257 | } |
258 | |
259 | fn chroot<P: AsRef<Path>>(&mut self, dir: P) -> &mut process::Command { |
260 | self.as_inner_mut().chroot(dir.as_ref()); |
261 | self |
262 | } |
263 | } |
264 | |
265 | /// Unix-specific extensions to [`process::ExitStatus`] and |
266 | /// [`ExitStatusError`](process::ExitStatusError). |
267 | /// |
268 | /// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as |
269 | /// passed to the `_exit` system call or returned by |
270 | /// [`ExitStatus::code()`](crate::process::ExitStatus::code). It represents **any wait status** |
271 | /// as returned by one of the `wait` family of system |
272 | /// calls. |
273 | /// |
274 | /// A Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but can also |
275 | /// represent other kinds of process event. |
276 | /// |
277 | /// This trait is sealed: it cannot be implemented outside the standard library. |
278 | /// This is so that future additional methods are not breaking changes. |
279 | #[stable (feature = "rust1" , since = "1.0.0" )] |
280 | pub trait ExitStatusExt: Sealed { |
281 | /// Creates a new `ExitStatus` or `ExitStatusError` from the raw underlying integer status |
282 | /// value from `wait` |
283 | /// |
284 | /// The value should be a **wait status, not an exit status**. |
285 | /// |
286 | /// # Panics |
287 | /// |
288 | /// Panics on an attempt to make an `ExitStatusError` from a wait status of `0`. |
289 | /// |
290 | /// Making an `ExitStatus` always succeeds and never panics. |
291 | #[stable (feature = "exit_status_from" , since = "1.12.0" )] |
292 | fn from_raw(raw: i32) -> Self; |
293 | |
294 | /// If the process was terminated by a signal, returns that signal. |
295 | /// |
296 | /// In other words, if `WIFSIGNALED`, this returns `WTERMSIG`. |
297 | #[stable (feature = "rust1" , since = "1.0.0" )] |
298 | fn signal(&self) -> Option<i32>; |
299 | |
300 | /// If the process was terminated by a signal, says whether it dumped core. |
301 | #[stable (feature = "unix_process_wait_more" , since = "1.58.0" )] |
302 | fn core_dumped(&self) -> bool; |
303 | |
304 | /// If the process was stopped by a signal, returns that signal. |
305 | /// |
306 | /// In other words, if `WIFSTOPPED`, this returns `WSTOPSIG`. This is only possible if the status came from |
307 | /// a `wait` system call which was passed `WUNTRACED`, and was then converted into an `ExitStatus`. |
308 | #[stable (feature = "unix_process_wait_more" , since = "1.58.0" )] |
309 | fn stopped_signal(&self) -> Option<i32>; |
310 | |
311 | /// Whether the process was continued from a stopped status. |
312 | /// |
313 | /// Ie, `WIFCONTINUED`. This is only possible if the status came from a `wait` system call |
314 | /// which was passed `WCONTINUED`, and was then converted into an `ExitStatus`. |
315 | #[stable (feature = "unix_process_wait_more" , since = "1.58.0" )] |
316 | fn continued(&self) -> bool; |
317 | |
318 | /// Returns the underlying raw `wait` status. |
319 | /// |
320 | /// The returned integer is a **wait status, not an exit status**. |
321 | #[stable (feature = "unix_process_wait_more" , since = "1.58.0" )] |
322 | fn into_raw(self) -> i32; |
323 | } |
324 | |
325 | #[stable (feature = "rust1" , since = "1.0.0" )] |
326 | impl ExitStatusExt for process::ExitStatus { |
327 | fn from_raw(raw: i32) -> Self { |
328 | process::ExitStatus::from_inner(From::from(raw)) |
329 | } |
330 | |
331 | fn signal(&self) -> Option<i32> { |
332 | self.as_inner().signal() |
333 | } |
334 | |
335 | fn core_dumped(&self) -> bool { |
336 | self.as_inner().core_dumped() |
337 | } |
338 | |
339 | fn stopped_signal(&self) -> Option<i32> { |
340 | self.as_inner().stopped_signal() |
341 | } |
342 | |
343 | fn continued(&self) -> bool { |
344 | self.as_inner().continued() |
345 | } |
346 | |
347 | fn into_raw(self) -> i32 { |
348 | self.as_inner().into_raw().into() |
349 | } |
350 | } |
351 | |
352 | #[unstable (feature = "exit_status_error" , issue = "84908" )] |
353 | impl ExitStatusExt for process::ExitStatusError { |
354 | fn from_raw(raw: i32) -> Self { |
355 | process::ExitStatus::from_raw(raw) |
356 | .exit_ok() |
357 | .expect_err("<ExitStatusError as ExitStatusExt>::from_raw(0) but zero is not an error" ) |
358 | } |
359 | |
360 | fn signal(&self) -> Option<i32> { |
361 | self.into_status().signal() |
362 | } |
363 | |
364 | fn core_dumped(&self) -> bool { |
365 | self.into_status().core_dumped() |
366 | } |
367 | |
368 | fn stopped_signal(&self) -> Option<i32> { |
369 | self.into_status().stopped_signal() |
370 | } |
371 | |
372 | fn continued(&self) -> bool { |
373 | self.into_status().continued() |
374 | } |
375 | |
376 | fn into_raw(self) -> i32 { |
377 | self.into_status().into_raw() |
378 | } |
379 | } |
380 | |
381 | #[stable (feature = "process_extensions" , since = "1.2.0" )] |
382 | impl FromRawFd for process::Stdio { |
383 | #[inline ] |
384 | unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { |
385 | let fd: FileDesc = sys::fd::FileDesc::from_raw_fd(fd); |
386 | let io: Stdio = sys::process::Stdio::Fd(fd); |
387 | process::Stdio::from_inner(io) |
388 | } |
389 | } |
390 | |
391 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
392 | impl From<OwnedFd> for process::Stdio { |
393 | /// Takes ownership of a file descriptor and returns a [`Stdio`](process::Stdio) |
394 | /// that can attach a stream to it. |
395 | #[inline ] |
396 | fn from(fd: OwnedFd) -> process::Stdio { |
397 | let fd: FileDesc = sys::fd::FileDesc::from_inner(fd); |
398 | let io: Stdio = sys::process::Stdio::Fd(fd); |
399 | process::Stdio::from_inner(io) |
400 | } |
401 | } |
402 | |
403 | #[stable (feature = "process_extensions" , since = "1.2.0" )] |
404 | impl AsRawFd for process::ChildStdin { |
405 | #[inline ] |
406 | fn as_raw_fd(&self) -> RawFd { |
407 | self.as_inner().as_raw_fd() |
408 | } |
409 | } |
410 | |
411 | #[stable (feature = "process_extensions" , since = "1.2.0" )] |
412 | impl AsRawFd for process::ChildStdout { |
413 | #[inline ] |
414 | fn as_raw_fd(&self) -> RawFd { |
415 | self.as_inner().as_raw_fd() |
416 | } |
417 | } |
418 | |
419 | #[stable (feature = "process_extensions" , since = "1.2.0" )] |
420 | impl AsRawFd for process::ChildStderr { |
421 | #[inline ] |
422 | fn as_raw_fd(&self) -> RawFd { |
423 | self.as_inner().as_raw_fd() |
424 | } |
425 | } |
426 | |
427 | #[stable (feature = "into_raw_os" , since = "1.4.0" )] |
428 | impl IntoRawFd for process::ChildStdin { |
429 | #[inline ] |
430 | fn into_raw_fd(self) -> RawFd { |
431 | self.into_inner().into_inner().into_raw_fd() |
432 | } |
433 | } |
434 | |
435 | #[stable (feature = "into_raw_os" , since = "1.4.0" )] |
436 | impl IntoRawFd for process::ChildStdout { |
437 | #[inline ] |
438 | fn into_raw_fd(self) -> RawFd { |
439 | self.into_inner().into_inner().into_raw_fd() |
440 | } |
441 | } |
442 | |
443 | #[stable (feature = "into_raw_os" , since = "1.4.0" )] |
444 | impl IntoRawFd for process::ChildStderr { |
445 | #[inline ] |
446 | fn into_raw_fd(self) -> RawFd { |
447 | self.into_inner().into_inner().into_raw_fd() |
448 | } |
449 | } |
450 | |
451 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
452 | impl AsFd for crate::process::ChildStdin { |
453 | #[inline ] |
454 | fn as_fd(&self) -> BorrowedFd<'_> { |
455 | self.as_inner().as_fd() |
456 | } |
457 | } |
458 | |
459 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
460 | impl From<crate::process::ChildStdin> for OwnedFd { |
461 | /// Takes ownership of a [`ChildStdin`](crate::process::ChildStdin)'s file descriptor. |
462 | #[inline ] |
463 | fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd { |
464 | child_stdin.into_inner().into_inner().into_inner() |
465 | } |
466 | } |
467 | |
468 | /// Creates a `ChildStdin` from the provided `OwnedFd`. |
469 | /// |
470 | /// The provided file descriptor must point to a pipe |
471 | /// with the `CLOEXEC` flag set. |
472 | #[stable (feature = "child_stream_from_fd" , since = "1.74.0" )] |
473 | impl From<OwnedFd> for process::ChildStdin { |
474 | #[inline ] |
475 | fn from(fd: OwnedFd) -> process::ChildStdin { |
476 | let fd: FileDesc = sys::fd::FileDesc::from_inner(fd); |
477 | let pipe: AnonPipe = sys::pipe::AnonPipe::from_inner(fd); |
478 | process::ChildStdin::from_inner(pipe) |
479 | } |
480 | } |
481 | |
482 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
483 | impl AsFd for crate::process::ChildStdout { |
484 | #[inline ] |
485 | fn as_fd(&self) -> BorrowedFd<'_> { |
486 | self.as_inner().as_fd() |
487 | } |
488 | } |
489 | |
490 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
491 | impl From<crate::process::ChildStdout> for OwnedFd { |
492 | /// Takes ownership of a [`ChildStdout`](crate::process::ChildStdout)'s file descriptor. |
493 | #[inline ] |
494 | fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd { |
495 | child_stdout.into_inner().into_inner().into_inner() |
496 | } |
497 | } |
498 | |
499 | /// Creates a `ChildStdout` from the provided `OwnedFd`. |
500 | /// |
501 | /// The provided file descriptor must point to a pipe |
502 | /// with the `CLOEXEC` flag set. |
503 | #[stable (feature = "child_stream_from_fd" , since = "1.74.0" )] |
504 | impl From<OwnedFd> for process::ChildStdout { |
505 | #[inline ] |
506 | fn from(fd: OwnedFd) -> process::ChildStdout { |
507 | let fd: FileDesc = sys::fd::FileDesc::from_inner(fd); |
508 | let pipe: AnonPipe = sys::pipe::AnonPipe::from_inner(fd); |
509 | process::ChildStdout::from_inner(pipe) |
510 | } |
511 | } |
512 | |
513 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
514 | impl AsFd for crate::process::ChildStderr { |
515 | #[inline ] |
516 | fn as_fd(&self) -> BorrowedFd<'_> { |
517 | self.as_inner().as_fd() |
518 | } |
519 | } |
520 | |
521 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
522 | impl From<crate::process::ChildStderr> for OwnedFd { |
523 | /// Takes ownership of a [`ChildStderr`](crate::process::ChildStderr)'s file descriptor. |
524 | #[inline ] |
525 | fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd { |
526 | child_stderr.into_inner().into_inner().into_inner() |
527 | } |
528 | } |
529 | |
530 | /// Creates a `ChildStderr` from the provided `OwnedFd`. |
531 | /// |
532 | /// The provided file descriptor must point to a pipe |
533 | /// with the `CLOEXEC` flag set. |
534 | #[stable (feature = "child_stream_from_fd" , since = "1.74.0" )] |
535 | impl From<OwnedFd> for process::ChildStderr { |
536 | #[inline ] |
537 | fn from(fd: OwnedFd) -> process::ChildStderr { |
538 | let fd: FileDesc = sys::fd::FileDesc::from_inner(fd); |
539 | let pipe: AnonPipe = sys::pipe::AnonPipe::from_inner(fd); |
540 | process::ChildStderr::from_inner(pipe) |
541 | } |
542 | } |
543 | |
544 | /// Returns the OS-assigned process identifier associated with this process's parent. |
545 | #[must_use ] |
546 | #[stable (feature = "unix_ppid" , since = "1.27.0" )] |
547 | pub fn parent_id() -> u32 { |
548 | crate::sys::os::getppid() |
549 | } |
550 | |