2 use crate::io::{self, Error, ErrorKind};
4 use crate::num::NonZeroI32;
7 use crate::sys::process::process_common::*;
8 use core::ffi::NonZero_c_int;
10 #[cfg(target_os = "linux")]
11 use crate::os::linux::process::PidFd;
13 #[cfg(target_os = "linux")]
14 use crate::sys::weak::raw_syscall;
18 target_os = "freebsd",
19 all(target_os = "linux", target_env = "gnu"),
20 all(target_os = "linux", target_env = "musl"),
22 use crate::sys::weak::weak;
24 #[cfg(target_os = "vxworks")]
25 use libc::RTP_ID as pid_t;
27 #[cfg(not(target_os = "vxworks"))]
28 use libc::{c_int, pid_t};
30 #[cfg(not(any(target_os = "vxworks", target_os = "l4re")))]
31 use libc::{gid_t, uid_t};
33 ////////////////////////////////////////////////////////////////////////////////
35 ////////////////////////////////////////////////////////////////////////////////
42 ) -> io::Result<(Process, StdioPipes)> {
43 const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX";
45 let envp = self.capture_env();
48 return Err(io::const_io_error!(
49 ErrorKind::InvalidInput,
50 "nul byte found in provided data",
54 let (ours, theirs) = self.setup_io(default, needs_stdin)?;
56 if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
57 return Ok((ret, ours));
60 let (input, output) = sys::pipe::anon_pipe()?;
62 // Whatever happens after the fork is almost for sure going to touch or
63 // look at the environment in one way or another (PATH in `execvp` or
64 // accessing the `environ` pointer ourselves). Make sure no other thread
65 // is accessing the environment when we do the fork itself.
67 // Note that as soon as we're done with the fork there's no need to hold
68 // a lock any more because the parent won't do anything and the child is
69 // in its own process. Thus the parent drops the lock guard immediately.
70 // The child calls `mem::forget` to leak the lock, which is crucial because
71 // releasing a lock is not async-signal-safe.
72 let env_lock = sys::os::env_read_lock();
73 let (pid, pidfd) = unsafe { self.do_fork()? };
76 crate::panic::always_abort();
77 mem::forget(env_lock); // avoid non-async-signal-safe unlocking
79 let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) };
80 let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
81 let errno = errno.to_be_bytes();
87 CLOEXEC_MSG_FOOTER[0],
88 CLOEXEC_MSG_FOOTER[1],
89 CLOEXEC_MSG_FOOTER[2],
90 CLOEXEC_MSG_FOOTER[3],
92 // pipe I/O up to PIPE_BUF bytes should be atomic, and then
93 // we want to be sure we *don't* run at_exit destructors as
94 // we're being torn down regardless
95 rtassert!(output.write(&bytes).is_ok());
96 unsafe { libc::_exit(1) }
102 // Safety: We obtained the pidfd from calling `clone3` with
103 // `CLONE_PIDFD` so it's valid an otherwise unowned.
104 let mut p = unsafe { Process::new(pid, pidfd) };
105 let mut bytes = [0; 8];
107 // loop to handle EINTR
109 match input.read(&mut bytes) {
110 Ok(0) => return Ok((p, ours)),
112 let (errno, footer) = bytes.split_at(4);
114 CLOEXEC_MSG_FOOTER, footer,
115 "Validation on the CLOEXEC pipe failed: {:?}",
118 let errno = i32::from_be_bytes(errno.try_into().unwrap());
119 assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
120 return Err(Error::from_raw_os_error(errno));
122 Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
124 assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
125 panic!("the CLOEXEC pipe failed: {e:?}")
128 // pipe I/O up to PIPE_BUF bytes should be atomic
129 assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
130 panic!("short read on the CLOEXEC pipe")
136 pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
137 let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
138 crate::sys_common::process::wait_with_output(proc, pipes)
141 // Attempts to fork the process. If successful, returns Ok((0, -1))
142 // in the child, and Ok((child_pid, -1)) in the parent.
143 #[cfg(not(target_os = "linux"))]
144 unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
145 cvt(libc::fork()).map(|res| (res, -1))
148 // Attempts to fork the process. If successful, returns Ok((0, -1))
149 // in the child, and Ok((child_pid, child_pidfd)) in the parent.
150 #[cfg(target_os = "linux")]
151 unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
152 use crate::sync::atomic::{AtomicBool, Ordering};
154 static HAS_CLONE3: AtomicBool = AtomicBool::new(true);
155 const CLONE_PIDFD: u64 = 0x00001000;
173 fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long
176 // Bypassing libc for `clone3` can make further libc calls unsafe,
177 // so we use it sparingly for now. See #89522 for details.
178 // Some tools (e.g. sandboxing tools) may also expect `fork`
179 // rather than `clone3`.
180 let want_clone3_pidfd = self.get_create_pidfd();
182 // If we fail to create a pidfd for any reason, this will
183 // stay as -1, which indicates an error.
184 let mut pidfd: pid_t = -1;
186 // Attempt to use the `clone3` syscall, which supports more arguments
187 // (in particular, the ability to create a pidfd). If this fails,
188 // we will fall through this block to a call to `fork()`
189 if want_clone3_pidfd && HAS_CLONE3.load(Ordering::Relaxed) {
190 let mut args = clone_args {
192 pidfd: &mut pidfd as *mut pid_t as u64,
195 exit_signal: libc::SIGCHLD as u64,
204 let args_ptr = &mut args as *mut clone_args;
205 let args_size = crate::mem::size_of::<clone_args>();
207 let res = cvt(clone3(args_ptr, args_size));
209 Ok(n) => return Ok((n as pid_t, pidfd)),
210 Err(e) => match e.raw_os_error() {
211 // Multiple threads can race to execute this store,
212 // but that's fine - that just means that multiple threads
213 // will have tried and failed to execute the same syscall,
214 // with no other side effects.
215 Some(libc::ENOSYS) => HAS_CLONE3.store(false, Ordering::Relaxed),
216 // Fallback to fork if `EPERM` is returned. (e.g. blocked by seccomp)
217 Some(libc::EPERM) => {}
223 // Generally, we just call `fork`. If we get here after wanting `clone3`,
224 // then the syscall does not exist or we do not have permission to call it.
225 cvt(libc::fork()).map(|res| (res, pidfd))
228 pub fn exec(&mut self, default: Stdio) -> io::Error {
229 let envp = self.capture_env();
232 return io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data",);
235 match self.setup_io(default, true) {
238 // Similar to when forking, we want to ensure that access to
239 // the environment is synchronized, so make sure to grab the
240 // environment lock before we try to exec.
241 let _lock = sys::os::env_read_lock();
243 let Err(e) = self.do_exec(theirs, envp.as_ref());
251 // And at this point we've reached a special time in the life of the
252 // child. The child must now be considered hamstrung and unable to
253 // do anything other than syscalls really. Consider the following
256 // 1. Thread A of process 1 grabs the malloc() mutex
257 // 2. Thread B of process 1 forks(), creating thread C
258 // 3. Thread C of process 2 then attempts to malloc()
259 // 4. The memory of process 2 is the same as the memory of
260 // process 1, so the mutex is locked.
262 // This situation looks a lot like deadlock, right? It turns out
263 // that this is what pthread_atfork() takes care of, which is
264 // presumably implemented across platforms. The first thing that
265 // threads to *before* forking is to do things like grab the malloc
266 // mutex, and then after the fork they unlock it.
268 // Despite this information, libnative's spawn has been witnessed to
269 // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
270 // all collected backtraces point at malloc/free traffic in the
271 // child spawned process.
273 // For this reason, the block of code below should contain 0
274 // invocations of either malloc of free (or their related friends).
276 // As an example of not having malloc/free traffic, we don't close
277 // this file descriptor by dropping the FileDesc (which contains an
278 // allocation). Instead we just close it manually. This will never
279 // have the drop glue anyway because this code never returns (the
280 // child will either exec() or invoke libc::exit)
284 maybe_envp: Option<&CStringArray>,
285 ) -> Result<!, io::Error> {
286 use crate::sys::{self, cvt_r};
288 if let Some(fd) = stdio.stdin.fd() {
289 cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?;
291 if let Some(fd) = stdio.stdout.fd() {
292 cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?;
294 if let Some(fd) = stdio.stderr.fd() {
295 cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?;
298 #[cfg(not(target_os = "l4re"))]
300 if let Some(_g) = self.get_groups() {
301 //FIXME: Redox kernel does not support setgroups yet
302 #[cfg(not(target_os = "redox"))]
303 cvt(libc::setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?;
305 if let Some(u) = self.get_gid() {
306 cvt(libc::setgid(u as gid_t))?;
308 if let Some(u) = self.get_uid() {
309 // When dropping privileges from root, the `setgroups` call
310 // will remove any extraneous groups. We only drop groups
311 // if the current uid is 0 and we weren't given an explicit
312 // set of groups. If we don't call this, then even though our
313 // uid has dropped, we may still have groups that enable us to
314 // do super-user things.
315 //FIXME: Redox kernel does not support setgroups yet
316 #[cfg(not(target_os = "redox"))]
317 if libc::getuid() == 0 && self.get_groups().is_none() {
318 cvt(libc::setgroups(0, crate::ptr::null()))?;
320 cvt(libc::setuid(u as uid_t))?;
323 if let Some(ref cwd) = *self.get_cwd() {
324 cvt(libc::chdir(cwd.as_ptr()))?;
327 if let Some(pgroup) = self.get_pgroup() {
328 cvt(libc::setpgid(0, pgroup))?;
331 // emscripten has no signal support.
332 #[cfg(not(target_os = "emscripten"))]
334 // Inherit the signal mask from the parent rather than resetting it (i.e. do not call
337 // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL.
338 // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility.
340 // #[unix_sigpipe] is an opportunity to change the default here.
341 if !crate::sys::unix_sigpipe_attr_specified() {
342 #[cfg(target_os = "android")] // see issue #88585
344 let mut action: libc::sigaction = mem::zeroed();
345 action.sa_sigaction = libc::SIG_DFL;
346 cvt(libc::sigaction(libc::SIGPIPE, &action, crate::ptr::null_mut()))?;
348 #[cfg(not(target_os = "android"))]
350 let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
351 if ret == libc::SIG_ERR {
352 return Err(io::Error::last_os_error());
358 for callback in self.get_closures().iter_mut() {
362 // Although we're performing an exec here we may also return with an
363 // error from this function (without actually exec'ing) in which case we
364 // want to be sure to restore the global environment back to what it
365 // once was, ensuring that our temporary override, when free'd, doesn't
366 // corrupt our process's environment.
367 let mut _reset = None;
368 if let Some(envp) = maybe_envp {
369 struct Reset(*const *const libc::c_char);
371 impl Drop for Reset {
374 *sys::os::environ() = self.0;
379 _reset = Some(Reset(*sys::os::environ()));
380 *sys::os::environ() = envp.as_ptr();
383 libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
384 Err(io::Error::last_os_error())
389 target_os = "freebsd",
390 all(target_os = "linux", target_env = "gnu"),
391 all(target_os = "linux", target_env = "musl"),
396 _: Option<&CStringArray>,
397 ) -> io::Result<Option<Process>> {
401 // Only support platforms for which posix_spawn() can return ENOENT
405 target_os = "freebsd",
406 all(target_os = "linux", target_env = "gnu"),
407 all(target_os = "linux", target_env = "musl"),
412 envp: Option<&CStringArray>,
413 ) -> io::Result<Option<Process>> {
414 use crate::mem::MaybeUninit;
415 use crate::sys::{self, cvt_nz, unix_sigpipe_attr_specified};
417 if self.get_gid().is_some()
418 || self.get_uid().is_some()
419 || (self.env_saw_path() && !self.program_is_path())
420 || !self.get_closures().is_empty()
421 || self.get_groups().is_some()
422 || self.get_create_pidfd()
427 // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
428 #[cfg(all(target_os = "linux", target_env = "gnu"))]
430 if let Some(version) = sys::os::glibc_version() {
431 if version < (2, 24) {
439 // Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory,
440 // and maybe others will gain this non-POSIX function too. We'll check
441 // for this weak symbol as soon as it's needed, so we can return early
442 // otherwise to do a manual chdir before exec.
444 fn posix_spawn_file_actions_addchdir_np(
445 *mut libc::posix_spawn_file_actions_t,
449 let addchdir = match self.get_cwd() {
451 if cfg!(target_os = "macos") {
452 // There is a bug in macOS where a relative executable
453 // path like "../myprogram" will cause `posix_spawn` to
454 // successfully launch the program, but erroneously return
455 // ENOENT when used with posix_spawn_file_actions_addchdir_np
456 // which was introduced in macOS 10.15.
457 if self.get_program_kind() == ProgramKind::Relative {
461 match posix_spawn_file_actions_addchdir_np.get() {
462 Some(f) => Some((f, cwd)),
463 None => return Ok(None),
469 let pgroup = self.get_pgroup();
471 // Safety: -1 indicates we don't have a pidfd.
472 let mut p = unsafe { Process::new(0, -1) };
474 struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit<libc::posix_spawn_file_actions_t>);
476 impl Drop for PosixSpawnFileActions<'_> {
479 libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr());
484 struct PosixSpawnattr<'a>(&'a mut MaybeUninit<libc::posix_spawnattr_t>);
486 impl Drop for PosixSpawnattr<'_> {
489 libc::posix_spawnattr_destroy(self.0.as_mut_ptr());
495 let mut attrs = MaybeUninit::uninit();
496 cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?;
497 let attrs = PosixSpawnattr(&mut attrs);
501 let mut file_actions = MaybeUninit::uninit();
502 cvt_nz(libc::posix_spawn_file_actions_init(file_actions.as_mut_ptr()))?;
503 let file_actions = PosixSpawnFileActions(&mut file_actions);
505 if let Some(fd) = stdio.stdin.fd() {
506 cvt_nz(libc::posix_spawn_file_actions_adddup2(
507 file_actions.0.as_mut_ptr(),
512 if let Some(fd) = stdio.stdout.fd() {
513 cvt_nz(libc::posix_spawn_file_actions_adddup2(
514 file_actions.0.as_mut_ptr(),
519 if let Some(fd) = stdio.stderr.fd() {
520 cvt_nz(libc::posix_spawn_file_actions_adddup2(
521 file_actions.0.as_mut_ptr(),
526 if let Some((f, cwd)) = addchdir {
527 cvt_nz(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?;
530 if let Some(pgroup) = pgroup {
531 flags |= libc::POSIX_SPAWN_SETPGROUP;
532 cvt_nz(libc::posix_spawnattr_setpgroup(attrs.0.as_mut_ptr(), pgroup))?;
535 // Inherit the signal mask from this process rather than resetting it (i.e. do not call
536 // posix_spawnattr_setsigmask).
538 // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL.
539 // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility.
541 // #[unix_sigpipe] is an opportunity to change the default here.
542 if !unix_sigpipe_attr_specified() {
543 let mut default_set = MaybeUninit::<libc::sigset_t>::uninit();
544 cvt(sigemptyset(default_set.as_mut_ptr()))?;
545 cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGPIPE))?;
546 cvt_nz(libc::posix_spawnattr_setsigdefault(
547 attrs.0.as_mut_ptr(),
548 default_set.as_ptr(),
550 flags |= libc::POSIX_SPAWN_SETSIGDEF;
553 cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
555 // Make sure we synchronize access to the global `environ` resource
556 let _env_lock = sys::os::env_read_lock();
557 let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
558 cvt_nz(libc::posix_spawnp(
560 self.get_program_cstr().as_ptr(),
561 file_actions.0.as_ptr(),
563 self.get_argv().as_ptr() as *const _,
571 ////////////////////////////////////////////////////////////////////////////////
573 ////////////////////////////////////////////////////////////////////////////////
575 /// The unique ID of the process (this should never be negative).
578 status: Option<ExitStatus>,
579 // On Linux, stores the pidfd created for this child.
580 // This is None if the user did not request pidfd creation,
581 // or if the pidfd could not be created for some reason
582 // (e.g. the `clone3` syscall was not available).
583 #[cfg(target_os = "linux")]
584 pidfd: Option<PidFd>,
588 #[cfg(target_os = "linux")]
589 unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self {
590 use crate::os::unix::io::FromRawFd;
591 use crate::sys_common::FromInner;
592 // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned.
593 let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::from_raw_fd(pidfd)));
594 Process { pid, status: None, pidfd }
597 #[cfg(not(target_os = "linux"))]
598 unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self {
599 Process { pid, status: None }
602 pub fn id(&self) -> u32 {
606 pub fn kill(&mut self) -> io::Result<()> {
607 // If we've already waited on this process then the pid can be recycled
608 // and used for another process, and we probably shouldn't be killing
609 // random processes, so just return an error.
610 if self.status.is_some() {
611 Err(io::const_io_error!(
612 ErrorKind::InvalidInput,
613 "invalid argument: can't kill an exited process",
616 cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
620 pub fn wait(&mut self) -> io::Result<ExitStatus> {
621 use crate::sys::cvt_r;
622 if let Some(status) = self.status {
625 let mut status = 0 as c_int;
626 cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
627 self.status = Some(ExitStatus::new(status));
628 Ok(ExitStatus::new(status))
631 pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
632 if let Some(status) = self.status {
633 return Ok(Some(status));
635 let mut status = 0 as c_int;
636 let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
640 self.status = Some(ExitStatus::new(status));
641 Ok(Some(ExitStatus::new(status)))
646 /// Unix exit statuses
648 // This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status".
649 // See the discussion in comments and doc comments for `std::process::ExitStatus`.
650 #[derive(PartialEq, Eq, Clone, Copy)]
651 pub struct ExitStatus(c_int);
653 impl fmt::Debug for ExitStatus {
654 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655 f.debug_tuple("unix_wait_status").field(&self.0).finish()
660 pub fn new(status: c_int) -> ExitStatus {
664 fn exited(&self) -> bool {
665 libc::WIFEXITED(self.0)
668 pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
669 // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
670 // true on all actual versions of Unix, is widely assumed, and is specified in SuS
671 // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
672 // true for a platform pretending to be Unix, the tests (our doctests, and also
673 // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
674 match NonZero_c_int::try_from(self.0) {
675 /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
676 /* was zero, couldn't convert */ Err(_) => Ok(()),
680 pub fn code(&self) -> Option<i32> {
681 self.exited().then(|| libc::WEXITSTATUS(self.0))
684 pub fn signal(&self) -> Option<i32> {
685 libc::WIFSIGNALED(self.0).then(|| libc::WTERMSIG(self.0))
688 pub fn core_dumped(&self) -> bool {
689 libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0)
692 pub fn stopped_signal(&self) -> Option<i32> {
693 libc::WIFSTOPPED(self.0).then(|| libc::WSTOPSIG(self.0))
696 pub fn continued(&self) -> bool {
697 libc::WIFCONTINUED(self.0)
700 pub fn into_raw(&self) -> c_int {
705 /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
706 impl From<c_int> for ExitStatus {
707 fn from(a: c_int) -> ExitStatus {
712 /// Convert a signal number to a readable, searchable name.
714 /// This string should be displayed right after the signal number.
715 /// If a signal is unrecognized, it returns the empty string, so that
716 /// you just get the number like "0". If it is recognized, you'll get
717 /// something like "9 (SIGKILL)".
718 fn signal_string(signal: i32) -> &'static str {
720 libc::SIGHUP => " (SIGHUP)",
721 libc::SIGINT => " (SIGINT)",
722 libc::SIGQUIT => " (SIGQUIT)",
723 libc::SIGILL => " (SIGILL)",
724 libc::SIGTRAP => " (SIGTRAP)",
725 libc::SIGABRT => " (SIGABRT)",
726 libc::SIGBUS => " (SIGBUS)",
727 libc::SIGFPE => " (SIGFPE)",
728 libc::SIGKILL => " (SIGKILL)",
729 libc::SIGUSR1 => " (SIGUSR1)",
730 libc::SIGSEGV => " (SIGSEGV)",
731 libc::SIGUSR2 => " (SIGUSR2)",
732 libc::SIGPIPE => " (SIGPIPE)",
733 libc::SIGALRM => " (SIGALRM)",
734 libc::SIGTERM => " (SIGTERM)",
735 libc::SIGCHLD => " (SIGCHLD)",
736 libc::SIGCONT => " (SIGCONT)",
737 libc::SIGSTOP => " (SIGSTOP)",
738 libc::SIGTSTP => " (SIGTSTP)",
739 libc::SIGTTIN => " (SIGTTIN)",
740 libc::SIGTTOU => " (SIGTTOU)",
741 libc::SIGURG => " (SIGURG)",
742 libc::SIGXCPU => " (SIGXCPU)",
743 libc::SIGXFSZ => " (SIGXFSZ)",
744 libc::SIGVTALRM => " (SIGVTALRM)",
745 libc::SIGPROF => " (SIGPROF)",
746 libc::SIGWINCH => " (SIGWINCH)",
747 #[cfg(not(target_os = "haiku"))]
748 libc::SIGIO => " (SIGIO)",
749 #[cfg(target_os = "haiku")]
750 libc::SIGPOLL => " (SIGPOLL)",
751 libc::SIGSYS => " (SIGSYS)",
752 // For information on Linux signals, run `man 7 signal`
756 target_arch = "x86_64",
759 target_arch = "aarch64"
762 libc::SIGSTKFLT => " (SIGSTKFLT)",
763 #[cfg(target_os = "linux")]
764 libc::SIGPWR => " (SIGPWR)",
769 target_os = "freebsd",
770 target_os = "netbsd",
771 target_os = "openbsd",
772 target_os = "dragonfly"
774 libc::SIGEMT => " (SIGEMT)",
779 target_os = "freebsd",
780 target_os = "netbsd",
781 target_os = "openbsd",
782 target_os = "dragonfly"
784 libc::SIGINFO => " (SIGINFO)",
789 impl fmt::Display for ExitStatus {
790 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
791 if let Some(code) = self.code() {
792 write!(f, "exit status: {code}")
793 } else if let Some(signal) = self.signal() {
794 let signal_string = signal_string(signal);
795 if self.core_dumped() {
796 write!(f, "signal: {signal}{signal_string} (core dumped)")
798 write!(f, "signal: {signal}{signal_string}")
800 } else if let Some(signal) = self.stopped_signal() {
801 let signal_string = signal_string(signal);
802 write!(f, "stopped (not terminated) by signal: {signal}{signal_string}")
803 } else if self.continued() {
804 write!(f, "continued (WIFCONTINUED)")
806 write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0)
811 #[derive(PartialEq, Eq, Clone, Copy)]
812 pub struct ExitStatusError(NonZero_c_int);
814 impl Into<ExitStatus> for ExitStatusError {
815 fn into(self) -> ExitStatus {
816 ExitStatus(self.0.into())
820 impl fmt::Debug for ExitStatusError {
821 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
822 f.debug_tuple("unix_wait_status").field(&self.0).finish()
826 impl ExitStatusError {
827 pub fn code(self) -> Option<NonZeroI32> {
828 ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
832 #[cfg(target_os = "linux")]
833 #[unstable(feature = "linux_pidfd", issue = "82971")]
834 impl crate::os::linux::process::ChildExt for crate::process::Child {
835 fn pidfd(&self) -> io::Result<&PidFd> {
839 .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
842 fn take_pidfd(&mut self) -> io::Result<PidFd> {
846 .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
851 #[path = "process_unix/tests.rs"]