Skip to main content

std/sys/process/unix/
unix.rs

1#[cfg(target_os = "vxworks")]
2use libc::RTP_ID as pid_t;
3#[cfg(not(target_os = "vxworks"))]
4use libc::{c_int, pid_t};
5#[cfg(not(any(
6    target_os = "vxworks",
7    target_os = "l4re",
8    target_os = "tvos",
9    target_os = "watchos",
10)))]
11use libc::{gid_t, uid_t};
12
13use super::common::*;
14use crate::io::{self, Error, ErrorKind};
15use crate::num::NonZero;
16use crate::process::StdioPipes;
17use crate::sys::cvt;
18#[cfg(target_os = "linux")]
19use crate::sys::process::PidFd;
20use crate::{fmt, mem, sys};
21
22cfg_select! {
23    any(target_os = "nto", target_os = "qnx") => {
24        use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t};
25
26        use crate::sync::LazyLock;
27        use crate::thread;
28        use crate::time::Duration;
29        // Get smallest amount of time we can sleep.
30        // Return a common value if it cannot be determined.
31        fn get_clock_resolution() -> Duration {
32            static MIN_DELAY: LazyLock<Duration, fn() -> Duration> = LazyLock::new(|| {
33                let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 };
34                if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0 {
35                    Duration::from_nanos(mindelay.tv_nsec as u64)
36                } else {
37                    Duration::from_millis(1)
38                }
39            });
40            *MIN_DELAY
41        }
42        // Arbitrary minimum sleep duration for retrying fork/spawn
43        const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1);
44        // Maximum duration of sleeping before giving up and returning an error
45        const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000);
46    }
47    _ => {}
48}
49
50////////////////////////////////////////////////////////////////////////////////
51// Command
52////////////////////////////////////////////////////////////////////////////////
53
54impl Command {
55    pub fn spawn(
56        &mut self,
57        default: Stdio,
58        needs_stdin: bool,
59    ) -> io::Result<(Process, StdioPipes)> {
60        const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX";
61
62        let envp = self.capture_env();
63
64        if self.saw_nul() {
65            return Err(::core::hint::must_use(::core::io::Error::from_static_message(const {
                &::core::io::SimpleMessage {
                        kind: ErrorKind::InvalidInput,
                        message: "nul byte found in provided data",
                    }
            }))io::const_error!(
66                ErrorKind::InvalidInput,
67                "nul byte found in provided data",
68            ));
69        }
70
71        let (ours, theirs) = self.setup_io(default, needs_stdin)?;
72
73        if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
74            return Ok((ret, ours));
75        }
76
77        #[cfg(target_os = "linux")]
78        let (input, output) = sys::net::Socket::new_pair(libc::AF_UNIX, libc::SOCK_SEQPACKET)?;
79
80        #[cfg(not(target_os = "linux"))]
81        let (input, output) = sys::pipe::pipe()?;
82
83        // Whatever happens after the fork is almost for sure going to touch or
84        // look at the environment in one way or another (PATH in `execvp` or
85        // accessing the `environ` pointer ourselves). Make sure no other thread
86        // is accessing the environment when we do the fork itself.
87        //
88        // Note that as soon as we're done with the fork there's no need to hold
89        // a lock any more because the parent won't do anything and the child is
90        // in its own process. Thus the parent drops the lock guard immediately.
91        // The child calls `mem::forget` to leak the lock, which is crucial because
92        // releasing a lock is not async-signal-safe.
93        let env_lock = sys::env::env_read_lock();
94        let pid = unsafe { self.do_fork()? };
95
96        if pid == 0 {
97            crate::panic::always_abort();
98            mem::forget(env_lock); // avoid non-async-signal-safe unlocking
99            drop(input);
100            #[cfg(target_os = "linux")]
101            if self.get_create_pidfd() {
102                self.send_pidfd(&output);
103            }
104            let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) };
105            let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
106            let errno = errno.to_be_bytes();
107            let bytes = [
108                errno[0],
109                errno[1],
110                errno[2],
111                errno[3],
112                CLOEXEC_MSG_FOOTER[0],
113                CLOEXEC_MSG_FOOTER[1],
114                CLOEXEC_MSG_FOOTER[2],
115                CLOEXEC_MSG_FOOTER[3],
116            ];
117            // pipe I/O up to PIPE_BUF bytes should be atomic, and then
118            // we want to be sure we *don't* run at_exit destructors as
119            // we're being torn down regardless
120            if !output.write(&bytes).is_ok() {
    {
        if let Some(mut out) = crate::sys::stdio::panic_output() {
            let _ =
                crate::io::Write::write_fmt(&mut out,
                    format_args!("fatal runtime error: {0}, aborting\n",
                        format_args!("assertion failed: output.write(&bytes).is_ok()")));
        };
        crate::process::abort();
    };
};rtassert!(output.write(&bytes).is_ok());
121            unsafe { libc::_exit(1) }
122        }
123
124        drop(env_lock);
125        drop(output);
126
127        #[cfg(target_os = "linux")]
128        let pidfd = if self.get_create_pidfd() { self.recv_pidfd(&input) } else { -1 };
129
130        #[cfg(not(target_os = "linux"))]
131        let pidfd = -1;
132
133        // Safety: We obtained the pidfd (on Linux) using SOCK_SEQPACKET, so it's valid.
134        let mut p = unsafe { Process::new(pid, pidfd) };
135        let mut bytes = [0; 8];
136
137        // loop to handle EINTR
138        loop {
139            match input.read(&mut bytes) {
140                Ok(0) => return Ok((p, ours)),
141                Ok(8) => {
142                    let (errno, footer) = bytes.split_at(4);
143                    {
    match (&CLOEXEC_MSG_FOOTER, &footer) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("Validation on the CLOEXEC pipe failed: {0:?}",
                            bytes)));
            }
        }
    }
};assert_eq!(
144                        CLOEXEC_MSG_FOOTER, footer,
145                        "Validation on the CLOEXEC pipe failed: {:?}",
146                        bytes
147                    );
148                    let errno = i32::from_be_bytes(errno.try_into().unwrap());
149                    if !p.wait().is_ok() {
    {
        ::core::panicking::panic_fmt(format_args!("wait() should either return Ok or panic"));
    }
};assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
150                    return Err(Error::from_raw_os_error(errno));
151                }
152                Err(ref e) if e.is_interrupted() => {}
153                Err(e) => {
154                    if !p.wait().is_ok() {
    {
        ::core::panicking::panic_fmt(format_args!("wait() should either return Ok or panic"));
    }
};assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
155                    {
    ::core::panicking::panic_fmt(format_args!("the CLOEXEC pipe failed: {0:?}",
            e));
}panic!("the CLOEXEC pipe failed: {e:?}")
156                }
157                Ok(..) => {
158                    // pipe I/O up to PIPE_BUF bytes should be atomic
159                    // similarly SOCK_SEQPACKET messages should arrive whole
160                    if !p.wait().is_ok() {
    {
        ::core::panicking::panic_fmt(format_args!("wait() should either return Ok or panic"));
    }
};assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
161                    {
    ::core::panicking::panic_fmt(format_args!("short read on the CLOEXEC pipe"));
}panic!("short read on the CLOEXEC pipe")
162                }
163            }
164        }
165    }
166
167    // WatchOS and TVOS headers mark the `fork`/`exec*` functions with
168    // `__WATCHOS_PROHIBITED __TVOS_PROHIBITED`, and indicate that the
169    // `posix_spawn*` functions should be used instead. It isn't entirely clear
170    // what `PROHIBITED` means here (e.g. if calls to these functions are
171    // allowed to exist in dead code), but it sounds bad, so we go out of our
172    // way to avoid that all-together.
173    #[cfg(any(target_os = "tvos", target_os = "watchos"))]
174    const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_error!(
175        ErrorKind::Unsupported,
176        "`fork`+`exec`-based process spawning is not supported on this target",
177    );
178
179    #[cfg(any(target_os = "tvos", target_os = "watchos"))]
180    unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> {
181        return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC);
182    }
183
184    // Attempts to fork the process. If successful, returns Ok((0, -1))
185    // in the child, and Ok((child_pid, -1)) in the parent.
186    #[cfg(not(any(
187        target_os = "watchos",
188        target_os = "tvos",
189        target_os = "nto",
190        target_os = "qnx"
191    )))]
192    unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> {
193        cvt(libc::fork())
194    }
195
196    // On QNX SDP, fork can fail with EBADF in case "another thread might have opened
197    // or closed a file descriptor while the fork() was occurring".
198    // Documentation says "... or try calling fork() again". This is what we do here.
199    // See also https://www.qnx.com/developers/docs/7.1/com.qnx.doc.neutrino.lib_ref/topic/f/fork.html
200    #[cfg(any(target_os = "nto", target_os = "qnx"))]
201    unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> {
202        use crate::sys::io::errno;
203
204        let mut delay = MIN_FORKSPAWN_SLEEP;
205
206        loop {
207            let r = libc::fork();
208            if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF {
209                if delay < get_clock_resolution() {
210                    // We cannot sleep this short (it would be longer).
211                    // Yield instead.
212                    thread::yield_now();
213                } else if delay < MAX_FORKSPAWN_SLEEP {
214                    thread::sleep(delay);
215                } else {
216                    return Err(io::const_error!(
217                        ErrorKind::WouldBlock,
218                        "forking returned EBADF too often",
219                    ));
220                }
221                delay *= 2;
222                continue;
223            } else {
224                return cvt(r);
225            }
226        }
227    }
228
229    pub fn exec(&mut self, default: Stdio) -> io::Error {
230        let envp = self.capture_env();
231
232        if self.saw_nul() {
233            return ::core::hint::must_use(::core::io::Error::from_static_message(const {
                &::core::io::SimpleMessage {
                        kind: ErrorKind::InvalidInput,
                        message: "nul byte found in provided data",
                    }
            }))io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data");
234        }
235
236        match self.setup_io(default, true) {
237            Ok((_, theirs)) => {
238                unsafe {
239                    // Similar to when forking, we want to ensure that access to
240                    // the environment is synchronized, so make sure to grab the
241                    // environment lock before we try to exec.
242                    let _lock = sys::env::env_read_lock();
243
244                    let Err(e) = self.do_exec(theirs, envp.as_ref());
245                    e
246                }
247            }
248            Err(e) => e,
249        }
250    }
251
252    // And at this point we've reached a special time in the life of the
253    // child. The child must now be considered hamstrung and unable to
254    // do anything other than syscalls really. Consider the following
255    // scenario:
256    //
257    //      1. Thread A of process 1 grabs the malloc() mutex
258    //      2. Thread B of process 1 forks(), creating thread C
259    //      3. Thread C of process 2 then attempts to malloc()
260    //      4. The memory of process 2 is the same as the memory of
261    //         process 1, so the mutex is locked.
262    //
263    // This situation looks a lot like deadlock, right? It turns out
264    // that this is what pthread_atfork() takes care of, which is
265    // presumably implemented across platforms. The first thing that
266    // threads to *before* forking is to do things like grab the malloc
267    // mutex, and then after the fork they unlock it.
268    //
269    // Despite this information, libnative's spawn has been witnessed to
270    // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
271    // all collected backtraces point at malloc/free traffic in the
272    // child spawned process.
273    //
274    // For this reason, the block of code below should contain 0
275    // invocations of either malloc of free (or their related friends).
276    //
277    // As an example of not having malloc/free traffic, we don't close
278    // this file descriptor by dropping the FileDesc (which contains an
279    // allocation). Instead we just close it manually. This will never
280    // have the drop glue anyway because this code never returns (the
281    // child will either exec() or invoke libc::exit)
282    #[cfg(not(any(target_os = "tvos", target_os = "watchos")))]
283    unsafe fn do_exec(
284        &mut self,
285        stdio: ChildPipes,
286        maybe_envp: Option<&CStringArray>,
287    ) -> Result<!, io::Error> {
288        use crate::sys::{self, cvt_r};
289
290        if let Some(fd) = stdio.stdin.fd() {
291            cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?;
292        }
293        if let Some(fd) = stdio.stdout.fd() {
294            cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?;
295        }
296        if let Some(fd) = stdio.stderr.fd() {
297            cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?;
298        }
299
300        #[cfg(not(target_os = "l4re"))]
301        {
302            if let Some(_g) = self.get_groups() {
303                //FIXME: Redox kernel does not support setgroups yet
304                #[cfg(not(target_os = "redox"))]
305                cvt(libc::setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?;
306            }
307            if let Some(u) = self.get_gid() {
308                cvt(libc::setgid(u as gid_t))?;
309            }
310            if let Some(u) = self.get_uid() {
311                // When dropping privileges from root, the `setgroups` call
312                // will remove any extraneous groups. We only drop groups
313                // if we have CAP_SETGID and we weren't given an explicit
314                // set of groups. If we don't call this, then even though our
315                // uid has dropped, we may still have groups that enable us to
316                // do super-user things.
317                //FIXME: Redox kernel does not support setgroups yet
318                #[cfg(not(target_os = "redox"))]
319                if self.get_groups().is_none() {
320                    let res = cvt(libc::setgroups(0, crate::ptr::null()));
321                    if let Err(e) = res {
322                        // Here we ignore the case of not having CAP_SETGID.
323                        // An alternative would be to require CAP_SETGID (in
324                        // addition to CAP_SETUID) for setting the UID.
325                        if e.raw_os_error() != Some(libc::EPERM) {
326                            return Err(e);
327                        }
328                    }
329                }
330                cvt(libc::setuid(u as uid_t))?;
331            }
332        }
333        if let Some(chroot) = self.get_chroot() {
334            #[cfg(not(target_os = "fuchsia"))]
335            cvt(libc::chroot(chroot.as_ptr()))?;
336            #[cfg(target_os = "fuchsia")]
337            return Err(io::const_error!(
338                io::ErrorKind::Unsupported,
339                "chroot not supported by fuchsia"
340            ));
341        }
342        if let Some(cwd) = self.get_cwd() {
343            cvt(libc::chdir(cwd.as_ptr()))?;
344        }
345
346        if let Some(pgroup) = self.get_pgroup() {
347            cvt(libc::setpgid(0, pgroup))?;
348        }
349
350        if self.get_setsid() {
351            cvt(libc::setsid())?;
352        }
353
354        // emscripten has no signal support.
355        #[cfg(not(target_os = "emscripten"))]
356        {
357            // Inherit the signal mask from the parent rather than resetting it (i.e. do not call
358            // pthread_sigmask).
359
360            // If -Zon-broken-pipe is used, don't reset SIGPIPE to SIG_DFL.
361            // If -Zon-broken-pipe is not used, reset SIGPIPE to SIG_DFL for backward compatibility.
362            //
363            // -Zon-broken-pipe is an opportunity to change the default here.
364            if !crate::sys::pal::on_broken_pipe_used() {
365                #[cfg(target_os = "android")] // see issue #88585
366                {
367                    let mut action: libc::sigaction = mem::zeroed();
368                    action.sa_sigaction = libc::SIG_DFL;
369                    cvt(libc::sigaction(libc::SIGPIPE, &action, crate::ptr::null_mut()))?;
370                }
371                #[cfg(not(target_os = "android"))]
372                {
373                    let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
374                    if ret == libc::SIG_ERR {
375                        return Err(io::Error::last_os_error());
376                    }
377                }
378                #[cfg(target_os = "hurd")]
379                {
380                    let ret = sys::signal(libc::SIGLOST, libc::SIG_DFL);
381                    if ret == libc::SIG_ERR {
382                        return Err(io::Error::last_os_error());
383                    }
384                }
385            }
386        }
387
388        for callback in self.get_closures().iter_mut() {
389            callback()?;
390        }
391
392        // Although we're performing an exec here we may also return with an
393        // error from this function (without actually exec'ing) in which case we
394        // want to be sure to restore the global environment back to what it
395        // once was, ensuring that our temporary override, when free'd, doesn't
396        // corrupt our process's environment.
397        let _reset;
398        if let Some(envp) = maybe_envp {
399            _reset = core::mem::DropGuard::new(*sys::env::environ(), |prev| {
400                *sys::env::environ() = prev;
401            });
402            *sys::env::environ() = envp.as_ptr();
403        }
404
405        libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
406        Err(io::Error::last_os_error())
407    }
408
409    #[cfg(any(target_os = "tvos", target_os = "watchos"))]
410    unsafe fn do_exec(
411        &mut self,
412        _stdio: ChildPipes,
413        _maybe_envp: Option<&CStringArray>,
414    ) -> Result<!, io::Error> {
415        return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC);
416    }
417
418    #[cfg(not(any(
419        target_os = "freebsd",
420        target_os = "illumos",
421        all(target_os = "linux", target_env = "gnu"),
422        all(target_os = "linux", target_env = "musl"),
423        target_os = "nto",
424        target_os = "qnx",
425        target_vendor = "apple",
426        target_os = "cygwin",
427    )))]
428    fn posix_spawn(
429        &mut self,
430        _: &ChildPipes,
431        _: Option<&CStringArray>,
432    ) -> io::Result<Option<Process>> {
433        Ok(None)
434    }
435
436    // Only support platforms for which posix_spawn() can return ENOENT
437    // directly.
438    #[cfg(any(
439        target_os = "freebsd",
440        target_os = "illumos",
441        all(target_os = "linux", target_env = "gnu"),
442        all(target_os = "linux", target_env = "musl"),
443        target_os = "nto",
444        target_os = "qnx",
445        target_vendor = "apple",
446        target_os = "cygwin",
447    ))]
448    fn posix_spawn(
449        &mut self,
450        stdio: &ChildPipes,
451        envp: Option<&CStringArray>,
452    ) -> io::Result<Option<Process>> {
453        #[cfg(target_os = "linux")]
454        use core::sync::atomic::{Atomic, AtomicU8, Ordering};
455
456        use crate::mem::{DropGuard, MaybeUninit};
457        use crate::pin::pin;
458        use crate::sys::helpers::COpaque;
459        use crate::sys::{self, cvt_nz, on_broken_pipe_used};
460
461        if self.get_gid().is_some()
462            || self.get_uid().is_some()
463            || (self.env_saw_path() && !self.program_is_path())
464            || !self.get_closures().is_empty()
465            || self.get_groups().is_some()
466            || self.get_chroot().is_some()
467        {
468            return Ok(None);
469        }
470
471        cfg_select! {
472            target_os = "linux" => {
473                use crate::sys::weak::weak;
474
475                let ref pidfd_spawnp:
        ExternWeak<unsafe extern "C" fn(*mut libc::c_int, *const libc::c_char,
            *const libc::posix_spawn_file_actions_t,
            *const libc::posix_spawnattr_t, *const *mut libc::c_char,
            *const *mut libc::c_char) -> libc::c_int> =
    {
        unsafe extern "C" {
            #[linkage = "extern_weak"]
            static pidfd_spawnp:
                Option<unsafe extern "C" fn(*mut libc::c_int,
                    *const libc::c_char,
                    *const libc::posix_spawn_file_actions_t,
                    *const libc::posix_spawnattr_t, *const *mut libc::c_char,
                    *const *mut libc::c_char) -> libc::c_int>;
        }

        #[allow(unused_unsafe)]
        ExternWeak::new(unsafe { pidfd_spawnp })
    };weak!(
476                    fn pidfd_spawnp(
477                        pidfd: *mut libc::c_int,
478                        path: *const libc::c_char,
479                        file_actions: *const libc::posix_spawn_file_actions_t,
480                        attrp: *const libc::posix_spawnattr_t,
481                        argv: *const *mut libc::c_char,
482                        envp: *const *mut libc::c_char,
483                    ) -> libc::c_int;
484                );
485
486                static PIDFD_SUPPORTED: Atomic<u8> = AtomicU8::new(0);
487                const UNKNOWN: u8 = 0;
488                const SPAWN: u8 = 1;
489                // Obtaining a pidfd via the fork+exec path might work
490                const FORK_EXEC: u8 = 2;
491                // Neither pidfd_spawn nor fork/exec will get us a pidfd.
492                // Instead we'll just posix_spawn if the other preconditions are met.
493                const NO: u8 = 3;
494
495                if self.get_create_pidfd() {
496                    let mut support = PIDFD_SUPPORTED.load(Ordering::Relaxed);
497                    if support == FORK_EXEC {
498                        return Ok(None);
499                    }
500                    if support == UNKNOWN {
501                        support = NO;
502
503                        match PidFd::current_process() {
504                            Ok(pidfd) => {
505                                // if pidfd_open works then we at least know the fork path is available.
506                                support = FORK_EXEC;
507                                // but for the fast path we need both spawnp and the
508                                // pidfd -> pid conversion to work.
509                                if pidfd_spawnp.get().is_some()
510                                    && let Ok(pid) = pidfd.pid()
511                                {
512                                    {
    match (&pid, &crate::process::id()) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val,
                    ::core::option::Option::Some(format_args!("sanity check")));
            }
        }
    }
};assert_eq!(pid, crate::process::id(), "sanity check");
513                                    support = SPAWN;
514                                }
515                            }
516                            Err(e)
517                                if #[allow(non_exhaustive_omitted_patterns)] match e.raw_os_error() {
    Some(libc::EMFILE | libc::ENFILE | libc::ENOMEM) => true,
    _ => false,
}matches!(
518                                    e.raw_os_error(),
519                                    Some(libc::EMFILE | libc::ENFILE | libc::ENOMEM)
520                                ) =>
521                            {
522                                // We're temporarily(?) out of file descriptors or memory. In this case pidfd_spawnp would also fail
523                                // Don't update the support flag so we can probe again later.
524                                return Err(e);
525                            }
526                            _ => {
527                                // pidfd_open not available? likely an old kernel without pidfd support.
528                            }
529                        }
530                        PIDFD_SUPPORTED.store(support, Ordering::Relaxed);
531                        if support == FORK_EXEC {
532                            return Ok(None);
533                        }
534                    }
535                    if true {
    {
        match support {
            SPAWN | NO => {}
            ref left_val => {
                ::core::panicking::assert_matches_failed(left_val,
                    "SPAWN | NO", ::core::option::Option::None);
            }
        }
    };
};core::debug_assert_matches!(support, SPAWN | NO);
536                }
537            }
538            _ => {
539                if self.get_create_pidfd() {
540                    unreachable!("only implemented on linux")
541                }
542            }
543        }
544
545        // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
546        #[cfg(all(target_os = "linux", target_env = "gnu"))]
547        {
548            if let Some(version) = sys::pal::conf::glibc_version() {
549                if version < (2, 24) {
550                    return Ok(None);
551                }
552            } else {
553                return Ok(None);
554            }
555        }
556
557        // On QNX SDP, posix_spawnp can fail with EBADF in case "another thread might have opened
558        // or closed a file descriptor while the posix_spawn() was occurring".
559        // Documentation says "... or try calling posix_spawn() again". This is what we do here.
560        // See also https://www.qnx.com/developers/docs/7.1/com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html
561        #[cfg(any(target_os = "nto", target_os = "qnx"))]
562        unsafe fn retrying_libc_posix_spawnp(
563            pid: *mut pid_t,
564            file: *const c_char,
565            file_actions: *const posix_spawn_file_actions_t,
566            attrp: *const posix_spawnattr_t,
567            argv: *const *mut c_char,
568            envp: *const *mut c_char,
569        ) -> io::Result<i32> {
570            let mut delay = MIN_FORKSPAWN_SLEEP;
571            loop {
572                match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) {
573                    libc::EBADF => {
574                        if delay < get_clock_resolution() {
575                            // We cannot sleep this short (it would be longer).
576                            // Yield instead.
577                            thread::yield_now();
578                        } else if delay < MAX_FORKSPAWN_SLEEP {
579                            thread::sleep(delay);
580                        } else {
581                            return Err(io::const_error!(
582                                ErrorKind::WouldBlock,
583                                "posix_spawnp returned EBADF too often",
584                            ));
585                        }
586                        delay *= 2;
587                        continue;
588                    }
589                    r => {
590                        return Ok(r);
591                    }
592                }
593            }
594        }
595
596        type PosixSpawnAddChdirFn = unsafe extern "C" fn(
597            *mut libc::posix_spawn_file_actions_t,
598            *const libc::c_char,
599        ) -> libc::c_int;
600
601        /// Get the function pointer for adding a chdir action to a
602        /// `posix_spawn_file_actions_t`, if available, assuming a dynamic libc.
603        ///
604        /// Some platforms can set a new working directory for a spawned process in the
605        /// `posix_spawn` path. This function looks up the function pointer for adding
606        /// such an action to a `posix_spawn_file_actions_t` struct.
607        #[cfg(not(any(all(target_os = "linux", target_env = "musl"), target_os = "cygwin")))]
608        fn get_posix_spawn_addchdir() -> Option<PosixSpawnAddChdirFn> {
609            use crate::sys::weak::weak;
610
611            // POSIX.1-2024 standardizes this function:
612            // https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn_file_actions_addchdir.html.
613            // The _np version is more widely available, though, so try that first.
614
615            let ref posix_spawn_file_actions_addchdir_np:
        ExternWeak<unsafe extern "C" fn(*mut libc::posix_spawn_file_actions_t,
            *const libc::c_char) -> libc::c_int> =
    {
        unsafe extern "C" {
            #[linkage = "extern_weak"]
            static posix_spawn_file_actions_addchdir_np:
                Option<unsafe extern "C" fn(*mut libc::posix_spawn_file_actions_t,
                    *const libc::c_char) -> libc::c_int>;
        }

        #[allow(unused_unsafe)]
        ExternWeak::new(unsafe { posix_spawn_file_actions_addchdir_np })
    };weak!(
616                fn posix_spawn_file_actions_addchdir_np(
617                    file_actions: *mut libc::posix_spawn_file_actions_t,
618                    path: *const libc::c_char,
619                ) -> libc::c_int;
620            );
621
622            let ref posix_spawn_file_actions_addchdir:
        ExternWeak<unsafe extern "C" fn(*mut libc::posix_spawn_file_actions_t,
            *const libc::c_char) -> libc::c_int> =
    {
        unsafe extern "C" {
            #[linkage = "extern_weak"]
            static posix_spawn_file_actions_addchdir:
                Option<unsafe extern "C" fn(*mut libc::posix_spawn_file_actions_t,
                    *const libc::c_char) -> libc::c_int>;
        }

        #[allow(unused_unsafe)]
        ExternWeak::new(unsafe { posix_spawn_file_actions_addchdir })
    };weak!(
623                fn posix_spawn_file_actions_addchdir(
624                    file_actions: *mut libc::posix_spawn_file_actions_t,
625                    path: *const libc::c_char,
626                ) -> libc::c_int;
627            );
628
629            posix_spawn_file_actions_addchdir_np
630                .get()
631                .or_else(|| posix_spawn_file_actions_addchdir.get())
632        }
633
634        /// Get the function pointer for adding a chdir action to a
635        /// `posix_spawn_file_actions_t`, if available, on platforms where the function
636        /// is known to exist.
637        ///
638        /// Weak symbol lookup doesn't work with statically linked libcs, so in cases
639        /// where static linking is possible we need to either check for the presence
640        /// of the symbol at compile time or know about it upfront.
641        ///
642        /// Cygwin doesn't support weak symbol, so just link it.
643        #[cfg(any(all(target_os = "linux", target_env = "musl"), target_os = "cygwin"))]
644        fn get_posix_spawn_addchdir() -> Option<PosixSpawnAddChdirFn> {
645            // Our minimum required musl supports this function, so we can just use it.
646            Some(libc::posix_spawn_file_actions_addchdir_np)
647        }
648
649        let addchdir = match self.get_cwd() {
650            Some(cwd) => {
651                if falsecfg!(target_vendor = "apple") {
652                    // There is a bug in macOS where a relative executable
653                    // path like "../myprogram" will cause `posix_spawn` to
654                    // successfully launch the program, but erroneously return
655                    // ENOENT when used with posix_spawn_file_actions_addchdir_np
656                    // which was introduced in macOS 10.15.
657                    if self.get_program_kind() == ProgramKind::Relative {
658                        return Ok(None);
659                    }
660                }
661                // Check for the availability of the posix_spawn addchdir
662                // function now. If it isn't available, bail and use the
663                // fork/exec path.
664                match get_posix_spawn_addchdir() {
665                    Some(f) => Some((f, cwd)),
666                    None => return Ok(None),
667                }
668            }
669            None => None,
670        };
671
672        let pgroup = self.get_pgroup();
673
674        unsafe {
675            let attrs = {
    super let mut pinned: ::core::pin::PinMacroHelper<_> =
        ::core::pin::PinMacroHelper { value: COpaque::uninit() };
    unsafe { ::core::pin::pin_new_unchecked_in_helper(&mut pinned) }
}pin!(COpaque::uninit());
676            // FIXME(pin-ergonomics): remove the next line.
677            let attrs = attrs.into_ref();
678            cvt_nz(libc::posix_spawnattr_init(attrs.get()))?;
679            let attrs = DropGuard::new(attrs, |attrs| {
680                libc::posix_spawnattr_destroy(attrs.get());
681            });
682
683            let mut flags = 0;
684
685            let file_actions = {
    super let mut pinned: ::core::pin::PinMacroHelper<_> =
        ::core::pin::PinMacroHelper { value: COpaque::uninit() };
    unsafe { ::core::pin::pin_new_unchecked_in_helper(&mut pinned) }
}pin!(COpaque::uninit());
686            let file_actions = file_actions.into_ref();
687            cvt_nz(libc::posix_spawn_file_actions_init(file_actions.get()))?;
688            let file_actions = DropGuard::new(file_actions, |file_actions| {
689                libc::posix_spawn_file_actions_destroy(file_actions.get());
690            });
691
692            if let Some(fd) = stdio.stdin.fd() {
693                cvt_nz(libc::posix_spawn_file_actions_adddup2(
694                    file_actions.get(),
695                    fd,
696                    libc::STDIN_FILENO,
697                ))?;
698            }
699            if let Some(fd) = stdio.stdout.fd() {
700                cvt_nz(libc::posix_spawn_file_actions_adddup2(
701                    file_actions.get(),
702                    fd,
703                    libc::STDOUT_FILENO,
704                ))?;
705            }
706            if let Some(fd) = stdio.stderr.fd() {
707                cvt_nz(libc::posix_spawn_file_actions_adddup2(
708                    file_actions.get(),
709                    fd,
710                    libc::STDERR_FILENO,
711                ))?;
712            }
713            if let Some((f, cwd)) = addchdir {
714                cvt_nz(f(file_actions.get(), cwd.as_ptr()))?;
715            }
716
717            if let Some(pgroup) = pgroup {
718                flags |= libc::POSIX_SPAWN_SETPGROUP;
719                cvt_nz(libc::posix_spawnattr_setpgroup(attrs.get(), pgroup))?;
720            }
721
722            // Inherit the signal mask from this process rather than resetting it (i.e. do not call
723            // posix_spawnattr_setsigmask).
724
725            // If -Zon-broken-pipe is used, don't reset SIGPIPE to SIG_DFL.
726            // If -Zon-broken-pipe is not used, reset SIGPIPE to SIG_DFL for backward compatibility.
727            //
728            // -Zon-broken-pipe is an opportunity to change the default here.
729            if !on_broken_pipe_used() {
730                let mut default_set = MaybeUninit::<libc::sigset_t>::uninit();
731                cvt(sigemptyset(default_set.as_mut_ptr()))?;
732                cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGPIPE))?;
733                #[cfg(target_os = "hurd")]
734                {
735                    cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGLOST))?;
736                }
737                cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.get(), default_set.as_ptr()))?;
738                flags |= libc::POSIX_SPAWN_SETSIGDEF;
739            }
740
741            if self.get_setsid() {
742                cfg_select! {
743                    all(target_os = "linux", target_env = "gnu") => {
744                        flags |= libc::POSIX_SPAWN_SETSID as i32;
745                    }
746                    _ => {
747                        return Ok(None);
748                    }
749                }
750            }
751
752            cvt_nz(libc::posix_spawnattr_setflags(attrs.get(), flags as _))?;
753
754            // Make sure we synchronize access to the global `environ` resource
755            let _env_lock = sys::env::env_read_lock();
756            let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::env::environ() as *const _);
757
758            #[cfg(not(any(target_os = "nto", target_os = "qnx")))]
759            let spawn_fn = libc::posix_spawnp;
760            #[cfg(any(target_os = "nto", target_os = "qnx"))]
761            let spawn_fn = retrying_libc_posix_spawnp;
762
763            #[cfg(target_os = "linux")]
764            if self.get_create_pidfd() && PIDFD_SUPPORTED.load(Ordering::Relaxed) == SPAWN {
765                let mut pidfd: libc::c_int = -1;
766                let spawn_res = pidfd_spawnp.get().unwrap()(
767                    &mut pidfd,
768                    self.get_program_cstr().as_ptr(),
769                    file_actions.get(),
770                    attrs.get(),
771                    self.get_argv().as_ptr() as *const _,
772                    envp as *const _,
773                );
774
775                let spawn_res = cvt_nz(spawn_res);
776                if let Err(ref e) = spawn_res
777                    && e.raw_os_error() == Some(libc::ENOSYS)
778                {
779                    PIDFD_SUPPORTED.store(FORK_EXEC, Ordering::Relaxed);
780                    return Ok(None);
781                }
782                spawn_res?;
783
784                use crate::os::fd::{FromRawFd, IntoRawFd};
785
786                let pidfd = PidFd::from_raw_fd(pidfd);
787                let pid = match pidfd.pid() {
788                    Ok(pid) => pid,
789                    Err(e) => {
790                        // The child has been spawned and we are holding its pidfd.
791                        // But we cannot obtain its pid even though pidfd_spawnp and getpid support
792                        // was verified earlier.
793                        // This is quite unlikely, but might happen if the ioctl is not supported,
794                        // glibc tries to use procfs and we're out of file descriptors.
795                        return Err(Error::new(
796                            e.kind(),
797                            "pidfd_spawnp succeeded but the child's PID could not be obtained",
798                        ));
799                    }
800                };
801
802                return Ok(Some(Process::new(pid as i32, pidfd.into_raw_fd())));
803            }
804
805            // Safety: -1 indicates we don't have a pidfd.
806            let mut p = Process::new(0, -1);
807
808            let spawn_res = spawn_fn(
809                &mut p.pid,
810                self.get_program_cstr().as_ptr(),
811                file_actions.get(),
812                attrs.get(),
813                self.get_argv().as_ptr() as *const _,
814                envp as *const _,
815            );
816
817            #[cfg(any(target_os = "nto", target_os = "qnx"))]
818            let spawn_res = spawn_res?;
819
820            cvt_nz(spawn_res)?;
821            Ok(Some(p))
822        }
823    }
824
825    #[cfg(target_os = "linux")]
826    fn send_pidfd(&self, sock: &crate::sys::net::Socket) {
827        use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET};
828
829        use crate::io::IoSlice;
830        use crate::os::fd::RawFd;
831        use crate::sys::cvt_r;
832
833        unsafe {
834            let child_pid = libc::getpid();
835            // pidfd_open sets CLOEXEC by default
836            let pidfd = libc::syscall(libc::SYS_pidfd_open, child_pid, 0);
837
838            let fds: [c_int; 1] = [pidfd as RawFd];
839
840            const SCM_MSG_LEN: usize = size_of::<[c_int; 1]>();
841
842            #[repr(C)]
843            union Cmsg {
844                buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }],
845                _align: libc::cmsghdr,
846            }
847
848            let mut cmsg: Cmsg = mem::zeroed();
849
850            // 0-length message to send through the socket so we can pass along the fd
851            let mut iov = [IoSlice::new(b"")];
852            let mut msg: libc::msghdr = mem::zeroed();
853
854            msg.msg_iov = (&raw mut iov) as *mut _;
855            msg.msg_iovlen = 1;
856
857            // only attach cmsg if we successfully acquired the pidfd
858            if pidfd >= 0 {
859                msg.msg_controllen = size_of_val(&cmsg.buf) as _;
860                msg.msg_control = (&raw mut cmsg.buf) as *mut _;
861
862                let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _);
863                (*hdr).cmsg_level = SOL_SOCKET;
864                (*hdr).cmsg_type = SCM_RIGHTS;
865                (*hdr).cmsg_len = CMSG_LEN(SCM_MSG_LEN as _) as _;
866                let data = CMSG_DATA(hdr);
867                crate::ptr::copy_nonoverlapping(
868                    fds.as_ptr().cast::<u8>(),
869                    data as *mut _,
870                    SCM_MSG_LEN,
871                );
872            }
873
874            // we send the 0-length message even if we failed to acquire the pidfd
875            // so we get a consistent SEQPACKET order
876            match cvt_r(|| libc::sendmsg(sock.as_raw(), &msg, libc::MSG_EOR)) {
877                Ok(0) => {}
878                other => {
    if let Some(mut out) = crate::sys::stdio::panic_output() {
        let _ =
            crate::io::Write::write_fmt(&mut out,
                format_args!("fatal runtime error: {0}, aborting\n",
                    format_args!("failed to communicate with parent process. {0:?}",
                        other)));
    };
    crate::process::abort();
}rtabort!("failed to communicate with parent process. {:?}", other),
879            }
880        }
881    }
882
883    #[cfg(target_os = "linux")]
884    fn recv_pidfd(&self, sock: &crate::sys::net::Socket) -> pid_t {
885        use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET};
886
887        use crate::io::IoSliceMut;
888        use crate::sys::cvt_r;
889
890        unsafe {
891            const SCM_MSG_LEN: usize = size_of::<[c_int; 1]>();
892
893            #[repr(C)]
894            union Cmsg {
895                _buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }],
896                _align: libc::cmsghdr,
897            }
898            let mut cmsg: Cmsg = mem::zeroed();
899            // 0-length read to get the fd
900            let mut iov = [IoSliceMut::new(&mut [])];
901
902            let mut msg: libc::msghdr = mem::zeroed();
903
904            msg.msg_iov = (&raw mut iov) as *mut _;
905            msg.msg_iovlen = 1;
906            msg.msg_controllen = size_of::<Cmsg>() as _;
907            msg.msg_control = (&raw mut cmsg) as *mut _;
908
909            if cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, libc::MSG_CMSG_CLOEXEC)).is_err() {
910                return -1;
911            }
912
913            let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _);
914            if hdr.is_null()
915                || (*hdr).cmsg_level != SOL_SOCKET
916                || (*hdr).cmsg_type != SCM_RIGHTS
917                || (*hdr).cmsg_len != CMSG_LEN(SCM_MSG_LEN as _) as _
918            {
919                return -1;
920            }
921            let data = CMSG_DATA(hdr);
922
923            let mut fds = [-1 as c_int];
924
925            crate::ptr::copy_nonoverlapping(
926                data as *const _,
927                fds.as_mut_ptr().cast::<u8>(),
928                SCM_MSG_LEN,
929            );
930
931            fds[0]
932        }
933    }
934}
935
936////////////////////////////////////////////////////////////////////////////////
937// Processes
938////////////////////////////////////////////////////////////////////////////////
939
940/// The unique ID of the process (this should never be negative).
941pub struct Process {
942    pid: pid_t,
943    status: Option<ExitStatus>,
944    // On Linux, stores the pidfd created for this child.
945    // This is None if the user did not request pidfd creation,
946    // or if the pidfd could not be created for some reason
947    // (e.g. the `pidfd_open` syscall was not available).
948    #[cfg(target_os = "linux")]
949    pidfd: Option<PidFd>,
950}
951
952impl Process {
953    #[cfg(target_os = "linux")]
954    /// # Safety
955    ///
956    /// `pidfd` must either be -1 (representing no file descriptor) or a valid, exclusively owned file
957    /// descriptor (See [I/O Safety]).
958    ///
959    /// [I/O Safety]: crate::io#io-safety
960    unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self {
961        use crate::os::unix::io::FromRawFd;
962        use crate::sys::FromInner;
963        // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned.
964        let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::from_raw_fd(pidfd)));
965        Process { pid, status: None, pidfd }
966    }
967
968    #[cfg(not(target_os = "linux"))]
969    unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self {
970        Process { pid, status: None }
971    }
972
973    pub fn id(&self) -> u32 {
974        self.pid as u32
975    }
976
977    pub fn kill(&self) -> io::Result<()> {
978        self.send_signal(libc::SIGKILL)
979    }
980
981    pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
982        // If we've already waited on this process then the pid can be recycled and
983        // used for another process, and we probably shouldn't be sending signals to
984        // random processes, so return Ok because the process has exited already.
985        if self.status.is_some() {
986            return Ok(());
987        }
988        #[cfg(target_os = "linux")]
989        if let Some(pid_fd) = self.pidfd.as_ref() {
990            // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too
991            return pid_fd.send_signal(signal);
992        }
993        cvt(unsafe { libc::kill(self.pid, signal) }).map(drop)
994    }
995
996    pub(crate) fn send_process_group_signal(&self, signal: i32) -> io::Result<()> {
997        // See note in `send_signal` regarding recycled PIDs.
998        if self.status.is_some() {
999            return Ok(());
1000        }
1001        #[cfg(target_os = "linux")]
1002        if let Some(pid_fd) = self.pidfd.as_ref() {
1003            // The `PIDFD_SIGNAL_PROCESS_GROUP` flag requires kernel >= 6.9
1004            return pid_fd.send_process_group_signal(signal);
1005        }
1006        cvt(unsafe { libc::killpg(self.pid, signal) }).map(drop)
1007    }
1008
1009    pub fn wait(&mut self) -> io::Result<ExitStatus> {
1010        use crate::sys::cvt_r;
1011        if let Some(status) = self.status {
1012            return Ok(status);
1013        }
1014        #[cfg(target_os = "linux")]
1015        if let Some(pid_fd) = self.pidfd.as_ref() {
1016            let status = pid_fd.wait()?;
1017            self.status = Some(status);
1018            return Ok(status);
1019        }
1020        let mut status = 0 as c_int;
1021        cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
1022        self.status = Some(ExitStatus::new(status));
1023        Ok(ExitStatus::new(status))
1024    }
1025
1026    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
1027        if let Some(status) = self.status {
1028            return Ok(Some(status));
1029        }
1030        #[cfg(target_os = "linux")]
1031        if let Some(pid_fd) = self.pidfd.as_ref() {
1032            let status = pid_fd.try_wait()?;
1033            if let Some(status) = status {
1034                self.status = Some(status)
1035            }
1036            return Ok(status);
1037        }
1038        let mut status = 0 as c_int;
1039        let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
1040        if pid == 0 {
1041            Ok(None)
1042        } else {
1043            self.status = Some(ExitStatus::new(status));
1044            Ok(Some(ExitStatus::new(status)))
1045        }
1046    }
1047}
1048
1049/// Unix exit statuses
1050//
1051// This is not actually an "exit status" in Unix terminology.  Rather, it is a "wait status".
1052// See the discussion in comments and doc comments for `std::process::ExitStatus`.
1053#[derive(#[automatically_derived]
impl ::core::marker::StructuralPartialEq for ExitStatus { }
#[automatically_derived]
impl ::core::cmp::PartialEq for ExitStatus {
    #[inline]
    fn eq(&self, other: &ExitStatus) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for ExitStatus {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<c_int>;
    }
}Eq, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ExitStatus { }
#[automatically_derived]
impl ::core::clone::Clone for ExitStatus {
    #[inline]
    fn clone(&self) -> ExitStatus {
        let _: ::core::clone::AssertParamIsClone<c_int>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ExitStatus { }Copy, #[automatically_derived]
impl ::core::default::Default for ExitStatus {
    #[inline]
    fn default() -> ExitStatus {
        ExitStatus(::core::default::Default::default())
    }
}Default)]
1054pub struct ExitStatus(c_int);
1055
1056impl fmt::Debug for ExitStatus {
1057    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1058        f.debug_tuple("unix_wait_status").field(&self.0).finish()
1059    }
1060}
1061
1062impl ExitStatus {
1063    pub fn new(status: c_int) -> ExitStatus {
1064        ExitStatus(status)
1065    }
1066
1067    #[cfg(target_os = "linux")]
1068    pub fn from_waitid_siginfo(siginfo: libc::siginfo_t) -> ExitStatus {
1069        let status = unsafe { siginfo.si_status() };
1070
1071        match siginfo.si_code {
1072            libc::CLD_EXITED => ExitStatus((status & 0xff) << 8),
1073            libc::CLD_KILLED => ExitStatus(status),
1074            libc::CLD_DUMPED => ExitStatus(status | 0x80),
1075            libc::CLD_CONTINUED => ExitStatus(0xffff),
1076            libc::CLD_STOPPED | libc::CLD_TRAPPED => ExitStatus(((status & 0xff) << 8) | 0x7f),
1077            _ => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("waitid() should only return the above codes")));
}unreachable!("waitid() should only return the above codes"),
1078        }
1079    }
1080
1081    fn exited(&self) -> bool {
1082        libc::WIFEXITED(self.0)
1083    }
1084
1085    pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
1086        // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
1087        // true on all actual versions of Unix, is widely assumed, and is specified in SuS
1088        // https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html. If it is not
1089        // true for a platform pretending to be Unix, the tests (our doctests, and also
1090        // unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
1091        match NonZero::try_from(self.0) {
1092            /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
1093            /* was zero, couldn't convert */ Err(_) => Ok(()),
1094        }
1095    }
1096
1097    pub fn code(&self) -> Option<i32> {
1098        self.exited().then(|| libc::WEXITSTATUS(self.0))
1099    }
1100
1101    pub fn signal(&self) -> Option<i32> {
1102        libc::WIFSIGNALED(self.0).then(|| libc::WTERMSIG(self.0))
1103    }
1104
1105    pub fn core_dumped(&self) -> bool {
1106        libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0)
1107    }
1108
1109    pub fn stopped_signal(&self) -> Option<i32> {
1110        libc::WIFSTOPPED(self.0).then(|| libc::WSTOPSIG(self.0))
1111    }
1112
1113    pub fn continued(&self) -> bool {
1114        libc::WIFCONTINUED(self.0)
1115    }
1116
1117    pub fn into_raw(&self) -> c_int {
1118        self.0
1119    }
1120}
1121
1122/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
1123impl From<c_int> for ExitStatus {
1124    fn from(a: c_int) -> ExitStatus {
1125        ExitStatus(a)
1126    }
1127}
1128
1129/// Converts a signal number to a readable, searchable name.
1130///
1131/// This string should be displayed right after the signal number.
1132/// If a signal is unrecognized, it returns the empty string, so that
1133/// you just get the number like "0". If it is recognized, you'll get
1134/// something like "9 (SIGKILL)".
1135fn signal_string(signal: i32) -> &'static str {
1136    match signal {
1137        libc::SIGHUP => " (SIGHUP)",
1138        libc::SIGINT => " (SIGINT)",
1139        libc::SIGQUIT => " (SIGQUIT)",
1140        libc::SIGILL => " (SIGILL)",
1141        libc::SIGTRAP => " (SIGTRAP)",
1142        libc::SIGABRT => " (SIGABRT)",
1143        #[cfg(not(target_os = "l4re"))]
1144        libc::SIGBUS => " (SIGBUS)",
1145        libc::SIGFPE => " (SIGFPE)",
1146        libc::SIGKILL => " (SIGKILL)",
1147        #[cfg(not(target_os = "l4re"))]
1148        libc::SIGUSR1 => " (SIGUSR1)",
1149        libc::SIGSEGV => " (SIGSEGV)",
1150        #[cfg(not(target_os = "l4re"))]
1151        libc::SIGUSR2 => " (SIGUSR2)",
1152        libc::SIGPIPE => " (SIGPIPE)",
1153        libc::SIGALRM => " (SIGALRM)",
1154        libc::SIGTERM => " (SIGTERM)",
1155        #[cfg(not(target_os = "l4re"))]
1156        libc::SIGCHLD => " (SIGCHLD)",
1157        #[cfg(not(target_os = "l4re"))]
1158        libc::SIGCONT => " (SIGCONT)",
1159        #[cfg(not(target_os = "l4re"))]
1160        libc::SIGSTOP => " (SIGSTOP)",
1161        #[cfg(not(target_os = "l4re"))]
1162        libc::SIGTSTP => " (SIGTSTP)",
1163        #[cfg(not(target_os = "l4re"))]
1164        libc::SIGTTIN => " (SIGTTIN)",
1165        #[cfg(not(target_os = "l4re"))]
1166        libc::SIGTTOU => " (SIGTTOU)",
1167        #[cfg(not(target_os = "l4re"))]
1168        libc::SIGURG => " (SIGURG)",
1169        #[cfg(not(target_os = "l4re"))]
1170        libc::SIGXCPU => " (SIGXCPU)",
1171        #[cfg(not(any(target_os = "l4re", target_os = "rtems")))]
1172        libc::SIGXFSZ => " (SIGXFSZ)",
1173        #[cfg(not(any(target_os = "l4re", target_os = "rtems")))]
1174        libc::SIGVTALRM => " (SIGVTALRM)",
1175        #[cfg(not(target_os = "l4re"))]
1176        libc::SIGPROF => " (SIGPROF)",
1177        #[cfg(not(any(target_os = "l4re", target_os = "rtems")))]
1178        libc::SIGWINCH => " (SIGWINCH)",
1179        #[cfg(not(any(target_os = "haiku", target_os = "l4re")))]
1180        libc::SIGIO => " (SIGIO)",
1181        #[cfg(target_os = "haiku")]
1182        libc::SIGPOLL => " (SIGPOLL)",
1183        #[cfg(not(target_os = "l4re"))]
1184        libc::SIGSYS => " (SIGSYS)",
1185        // For information on Linux signals, run `man 7 signal`
1186        #[cfg(all(
1187            target_os = "linux",
1188            any(
1189                target_arch = "x86_64",
1190                target_arch = "x86",
1191                target_arch = "arm",
1192                target_arch = "aarch64"
1193            )
1194        ))]
1195        libc::SIGSTKFLT => " (SIGSTKFLT)",
1196        #[cfg(any(
1197            target_os = "linux",
1198            target_os = "nto",
1199            target_os = "qnx",
1200            target_os = "cygwin"
1201        ))]
1202        libc::SIGPWR => " (SIGPWR)",
1203        #[cfg(any(
1204            target_os = "freebsd",
1205            target_os = "netbsd",
1206            target_os = "openbsd",
1207            target_os = "dragonfly",
1208            target_os = "nto",
1209            target_os = "qnx",
1210            target_vendor = "apple",
1211            target_os = "cygwin",
1212        ))]
1213        libc::SIGEMT => " (SIGEMT)",
1214        #[cfg(any(
1215            target_os = "freebsd",
1216            target_os = "netbsd",
1217            target_os = "openbsd",
1218            target_os = "dragonfly",
1219            target_vendor = "apple",
1220        ))]
1221        libc::SIGINFO => " (SIGINFO)",
1222        #[cfg(target_os = "hurd")]
1223        libc::SIGLOST => " (SIGLOST)",
1224        #[cfg(target_os = "freebsd")]
1225        libc::SIGTHR => " (SIGTHR)",
1226        #[cfg(target_os = "freebsd")]
1227        libc::SIGLIBRT => " (SIGLIBRT)",
1228        _ => "",
1229    }
1230}
1231
1232impl fmt::Display for ExitStatus {
1233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1234        if let Some(code) = self.code() {
1235            f.write_fmt(format_args!("exit status: {0}", code))write!(f, "exit status: {code}")
1236        } else if let Some(signal) = self.signal() {
1237            let signal_string = signal_string(signal);
1238            if self.core_dumped() {
1239                f.write_fmt(format_args!("signal: {0}{1} (core dumped)", signal,
        signal_string))write!(f, "signal: {signal}{signal_string} (core dumped)")
1240            } else {
1241                f.write_fmt(format_args!("signal: {0}{1}", signal, signal_string))write!(f, "signal: {signal}{signal_string}")
1242            }
1243        } else if let Some(signal) = self.stopped_signal() {
1244            let signal_string = signal_string(signal);
1245            f.write_fmt(format_args!("stopped (not terminated) by signal: {0}{1}", signal,
        signal_string))write!(f, "stopped (not terminated) by signal: {signal}{signal_string}")
1246        } else if self.continued() {
1247            f.write_fmt(format_args!("continued (WIFCONTINUED)"))write!(f, "continued (WIFCONTINUED)")
1248        } else {
1249            f.write_fmt(format_args!("unrecognised wait status: {0} {1:#x}", self.0,
        self.0))write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0)
1250        }
1251    }
1252}
1253
1254#[derive(#[automatically_derived]
impl ::core::marker::StructuralPartialEq for ExitStatusError { }
#[automatically_derived]
impl ::core::cmp::PartialEq for ExitStatusError {
    #[inline]
    fn eq(&self, other: &ExitStatusError) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for ExitStatusError {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<NonZero<c_int>>;
    }
}Eq, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ExitStatusError { }
#[automatically_derived]
impl ::core::clone::Clone for ExitStatusError {
    #[inline]
    fn clone(&self) -> ExitStatusError {
        let _: ::core::clone::AssertParamIsClone<NonZero<c_int>>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ExitStatusError { }Copy)]
1255pub struct ExitStatusError(NonZero<c_int>);
1256
1257impl Into<ExitStatus> for ExitStatusError {
1258    fn into(self) -> ExitStatus {
1259        ExitStatus(self.0.into())
1260    }
1261}
1262
1263impl fmt::Debug for ExitStatusError {
1264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1265        f.debug_tuple("unix_wait_status").field(&self.0).finish()
1266    }
1267}
1268
1269impl ExitStatusError {
1270    pub fn code(self) -> Option<NonZero<i32>> {
1271        ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
1272    }
1273}
1274
1275#[cfg(target_os = "linux")]
1276mod linux_child_ext {
1277    use crate::io::ErrorKind;
1278    use crate::os::linux::process as os;
1279    use crate::sys::{FromInner, process as imp};
1280    use crate::{io, mem};
1281
1282    #[unstable(feature = "linux_pidfd", issue = "82971")]
1283    impl crate::os::linux::process::ChildExt for crate::process::Child {
1284        fn pidfd(&self) -> io::Result<&os::PidFd> {
1285            self.handle
1286                .pidfd
1287                .as_ref()
1288                // SAFETY: The os type is a transparent wrapper, therefore we can transmute references
1289                .map(|fd| unsafe { mem::transmute::<&imp::PidFd, &os::PidFd>(fd) })
1290                .ok_or_else(|| ::core::hint::must_use(::core::io::Error::from_static_message(const {
                &::core::io::SimpleMessage {
                        kind: ErrorKind::Uncategorized,
                        message: "no pidfd was created.",
                    }
            }))io::const_error!(ErrorKind::Uncategorized, "no pidfd was created."))
1291        }
1292
1293        fn into_pidfd(mut self) -> Result<os::PidFd, Self> {
1294            self.handle
1295                .pidfd
1296                .take()
1297                .map(<os::PidFd as FromInner<imp::PidFd>>::from_inner)
1298                .ok_or_else(|| self)
1299        }
1300    }
1301}
1302
1303#[cfg(test)]
1304mod tests;
1305
1306// See [`unsupported_wait_status::compare_with_linux`];
1307#[cfg(all(test, target_os = "linux"))]
1308#[path = "unsupported/wait_status.rs"]
1309mod unsupported_wait_status;