]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/unix/process/process_unix.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / library / std / src / sys / unix / process / process_unix.rs
1 use crate::fmt;
2 use crate::io::{self, Error, ErrorKind};
3 use crate::mem;
4 use crate::num::NonZeroI32;
5 use crate::sys;
6 use crate::sys::cvt;
7 use crate::sys::process::process_common::*;
8 use core::ffi::NonZero_c_int;
9
10 #[cfg(target_os = "linux")]
11 use crate::os::linux::process::PidFd;
12
13 #[cfg(target_os = "linux")]
14 use crate::sys::weak::raw_syscall;
15
16 #[cfg(any(
17     target_os = "macos",
18     target_os = "freebsd",
19     all(target_os = "linux", target_env = "gnu"),
20     all(target_os = "linux", target_env = "musl"),
21 ))]
22 use crate::sys::weak::weak;
23
24 #[cfg(target_os = "vxworks")]
25 use libc::RTP_ID as pid_t;
26
27 #[cfg(not(target_os = "vxworks"))]
28 use libc::{c_int, pid_t};
29
30 #[cfg(not(any(target_os = "vxworks", target_os = "l4re")))]
31 use libc::{gid_t, uid_t};
32
33 ////////////////////////////////////////////////////////////////////////////////
34 // Command
35 ////////////////////////////////////////////////////////////////////////////////
36
37 impl Command {
38     pub fn spawn(
39         &mut self,
40         default: Stdio,
41         needs_stdin: bool,
42     ) -> io::Result<(Process, StdioPipes)> {
43         const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX";
44
45         let envp = self.capture_env();
46
47         if self.saw_nul() {
48             return Err(io::const_io_error!(
49                 ErrorKind::InvalidInput,
50                 "nul byte found in provided data",
51             ));
52         }
53
54         let (ours, theirs) = self.setup_io(default, needs_stdin)?;
55
56         if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
57             return Ok((ret, ours));
58         }
59
60         let (input, output) = sys::pipe::anon_pipe()?;
61
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.
66         //
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()? };
74
75         if pid == 0 {
76             crate::panic::always_abort();
77             mem::forget(env_lock); // avoid non-async-signal-safe unlocking
78             drop(input);
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();
82             let bytes = [
83                 errno[0],
84                 errno[1],
85                 errno[2],
86                 errno[3],
87                 CLOEXEC_MSG_FOOTER[0],
88                 CLOEXEC_MSG_FOOTER[1],
89                 CLOEXEC_MSG_FOOTER[2],
90                 CLOEXEC_MSG_FOOTER[3],
91             ];
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) }
97         }
98
99         drop(env_lock);
100         drop(output);
101
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];
106
107         // loop to handle EINTR
108         loop {
109             match input.read(&mut bytes) {
110                 Ok(0) => return Ok((p, ours)),
111                 Ok(8) => {
112                     let (errno, footer) = bytes.split_at(4);
113                     assert_eq!(
114                         CLOEXEC_MSG_FOOTER, footer,
115                         "Validation on the CLOEXEC pipe failed: {:?}",
116                         bytes
117                     );
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));
121                 }
122                 Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
123                 Err(e) => {
124                     assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
125                     panic!("the CLOEXEC pipe failed: {e:?}")
126                 }
127                 Ok(..) => {
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")
131                 }
132             }
133         }
134     }
135
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)
139     }
140
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))
146     }
147
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};
153
154         static HAS_CLONE3: AtomicBool = AtomicBool::new(true);
155         const CLONE_PIDFD: u64 = 0x00001000;
156
157         #[repr(C)]
158         struct clone_args {
159             flags: u64,
160             pidfd: u64,
161             child_tid: u64,
162             parent_tid: u64,
163             exit_signal: u64,
164             stack: u64,
165             stack_size: u64,
166             tls: u64,
167             set_tid: u64,
168             set_tid_size: u64,
169             cgroup: u64,
170         }
171
172         raw_syscall! {
173             fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long
174         }
175
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();
181
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;
185
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 {
191                 flags: CLONE_PIDFD,
192                 pidfd: &mut pidfd as *mut pid_t as u64,
193                 child_tid: 0,
194                 parent_tid: 0,
195                 exit_signal: libc::SIGCHLD as u64,
196                 stack: 0,
197                 stack_size: 0,
198                 tls: 0,
199                 set_tid: 0,
200                 set_tid_size: 0,
201                 cgroup: 0,
202             };
203
204             let args_ptr = &mut args as *mut clone_args;
205             let args_size = crate::mem::size_of::<clone_args>();
206
207             let res = cvt(clone3(args_ptr, args_size));
208             match res {
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) => {}
218                     _ => return Err(e),
219                 },
220             }
221         }
222
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))
226     }
227
228     pub fn exec(&mut self, default: Stdio) -> io::Error {
229         let envp = self.capture_env();
230
231         if self.saw_nul() {
232             return io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data",);
233         }
234
235         match self.setup_io(default, true) {
236             Ok((_, theirs)) => {
237                 unsafe {
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();
242
243                     let Err(e) = self.do_exec(theirs, envp.as_ref());
244                     e
245                 }
246             }
247             Err(e) => e,
248         }
249     }
250
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
254     // scenario:
255     //
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.
261     //
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.
267     //
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.
272     //
273     // For this reason, the block of code below should contain 0
274     // invocations of either malloc of free (or their related friends).
275     //
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)
281     unsafe fn do_exec(
282         &mut self,
283         stdio: ChildPipes,
284         maybe_envp: Option<&CStringArray>,
285     ) -> Result<!, io::Error> {
286         use crate::sys::{self, cvt_r};
287
288         if let Some(fd) = stdio.stdin.fd() {
289             cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?;
290         }
291         if let Some(fd) = stdio.stdout.fd() {
292             cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?;
293         }
294         if let Some(fd) = stdio.stderr.fd() {
295             cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?;
296         }
297
298         #[cfg(not(target_os = "l4re"))]
299         {
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()))?;
304             }
305             if let Some(u) = self.get_gid() {
306                 cvt(libc::setgid(u as gid_t))?;
307             }
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()))?;
319                 }
320                 cvt(libc::setuid(u as uid_t))?;
321             }
322         }
323         if let Some(ref cwd) = *self.get_cwd() {
324             cvt(libc::chdir(cwd.as_ptr()))?;
325         }
326
327         if let Some(pgroup) = self.get_pgroup() {
328             cvt(libc::setpgid(0, pgroup))?;
329         }
330
331         // emscripten has no signal support.
332         #[cfg(not(target_os = "emscripten"))]
333         {
334             // Inherit the signal mask from the parent rather than resetting it (i.e. do not call
335             // pthread_sigmask).
336
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.
339             //
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
343                 {
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()))?;
347                 }
348                 #[cfg(not(target_os = "android"))]
349                 {
350                     let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
351                     if ret == libc::SIG_ERR {
352                         return Err(io::Error::last_os_error());
353                     }
354                 }
355             }
356         }
357
358         for callback in self.get_closures().iter_mut() {
359             callback()?;
360         }
361
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);
370
371             impl Drop for Reset {
372                 fn drop(&mut self) {
373                     unsafe {
374                         *sys::os::environ() = self.0;
375                     }
376                 }
377             }
378
379             _reset = Some(Reset(*sys::os::environ()));
380             *sys::os::environ() = envp.as_ptr();
381         }
382
383         libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
384         Err(io::Error::last_os_error())
385     }
386
387     #[cfg(not(any(
388         target_os = "macos",
389         target_os = "freebsd",
390         all(target_os = "linux", target_env = "gnu"),
391         all(target_os = "linux", target_env = "musl"),
392     )))]
393     fn posix_spawn(
394         &mut self,
395         _: &ChildPipes,
396         _: Option<&CStringArray>,
397     ) -> io::Result<Option<Process>> {
398         Ok(None)
399     }
400
401     // Only support platforms for which posix_spawn() can return ENOENT
402     // directly.
403     #[cfg(any(
404         target_os = "macos",
405         target_os = "freebsd",
406         all(target_os = "linux", target_env = "gnu"),
407         all(target_os = "linux", target_env = "musl"),
408     ))]
409     fn posix_spawn(
410         &mut self,
411         stdio: &ChildPipes,
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};
416
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()
423         {
424             return Ok(None);
425         }
426
427         // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
428         #[cfg(all(target_os = "linux", target_env = "gnu"))]
429         {
430             if let Some(version) = sys::os::glibc_version() {
431                 if version < (2, 24) {
432                     return Ok(None);
433                 }
434             } else {
435                 return Ok(None);
436             }
437         }
438
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.
443         weak! {
444             fn posix_spawn_file_actions_addchdir_np(
445                 *mut libc::posix_spawn_file_actions_t,
446                 *const libc::c_char
447             ) -> libc::c_int
448         }
449         let addchdir = match self.get_cwd() {
450             Some(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 {
458                         return Ok(None);
459                     }
460                 }
461                 match posix_spawn_file_actions_addchdir_np.get() {
462                     Some(f) => Some((f, cwd)),
463                     None => return Ok(None),
464                 }
465             }
466             None => None,
467         };
468
469         let pgroup = self.get_pgroup();
470
471         // Safety: -1 indicates we don't have a pidfd.
472         let mut p = unsafe { Process::new(0, -1) };
473
474         struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit<libc::posix_spawn_file_actions_t>);
475
476         impl Drop for PosixSpawnFileActions<'_> {
477             fn drop(&mut self) {
478                 unsafe {
479                     libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr());
480                 }
481             }
482         }
483
484         struct PosixSpawnattr<'a>(&'a mut MaybeUninit<libc::posix_spawnattr_t>);
485
486         impl Drop for PosixSpawnattr<'_> {
487             fn drop(&mut self) {
488                 unsafe {
489                     libc::posix_spawnattr_destroy(self.0.as_mut_ptr());
490                 }
491             }
492         }
493
494         unsafe {
495             let mut attrs = MaybeUninit::uninit();
496             cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?;
497             let attrs = PosixSpawnattr(&mut attrs);
498
499             let mut flags = 0;
500
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);
504
505             if let Some(fd) = stdio.stdin.fd() {
506                 cvt_nz(libc::posix_spawn_file_actions_adddup2(
507                     file_actions.0.as_mut_ptr(),
508                     fd,
509                     libc::STDIN_FILENO,
510                 ))?;
511             }
512             if let Some(fd) = stdio.stdout.fd() {
513                 cvt_nz(libc::posix_spawn_file_actions_adddup2(
514                     file_actions.0.as_mut_ptr(),
515                     fd,
516                     libc::STDOUT_FILENO,
517                 ))?;
518             }
519             if let Some(fd) = stdio.stderr.fd() {
520                 cvt_nz(libc::posix_spawn_file_actions_adddup2(
521                     file_actions.0.as_mut_ptr(),
522                     fd,
523                     libc::STDERR_FILENO,
524                 ))?;
525             }
526             if let Some((f, cwd)) = addchdir {
527                 cvt_nz(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?;
528             }
529
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))?;
533             }
534
535             // Inherit the signal mask from this process rather than resetting it (i.e. do not call
536             // posix_spawnattr_setsigmask).
537
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.
540             //
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(),
549                 ))?;
550                 flags |= libc::POSIX_SPAWN_SETSIGDEF;
551             }
552
553             cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
554
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(
559                 &mut p.pid,
560                 self.get_program_cstr().as_ptr(),
561                 file_actions.0.as_ptr(),
562                 attrs.0.as_ptr(),
563                 self.get_argv().as_ptr() as *const _,
564                 envp as *const _,
565             ))?;
566             Ok(Some(p))
567         }
568     }
569 }
570
571 ////////////////////////////////////////////////////////////////////////////////
572 // Processes
573 ////////////////////////////////////////////////////////////////////////////////
574
575 /// The unique ID of the process (this should never be negative).
576 pub struct Process {
577     pid: pid_t,
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>,
585 }
586
587 impl Process {
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 }
595     }
596
597     #[cfg(not(target_os = "linux"))]
598     unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self {
599         Process { pid, status: None }
600     }
601
602     pub fn id(&self) -> u32 {
603         self.pid as u32
604     }
605
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",
614             ))
615         } else {
616             cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
617         }
618     }
619
620     pub fn wait(&mut self) -> io::Result<ExitStatus> {
621         use crate::sys::cvt_r;
622         if let Some(status) = self.status {
623             return Ok(status);
624         }
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))
629     }
630
631     pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
632         if let Some(status) = self.status {
633             return Ok(Some(status));
634         }
635         let mut status = 0 as c_int;
636         let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
637         if pid == 0 {
638             Ok(None)
639         } else {
640             self.status = Some(ExitStatus::new(status));
641             Ok(Some(ExitStatus::new(status)))
642         }
643     }
644 }
645
646 /// Unix exit statuses
647 //
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);
652
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()
656     }
657 }
658
659 impl ExitStatus {
660     pub fn new(status: c_int) -> ExitStatus {
661         ExitStatus(status)
662     }
663
664     fn exited(&self) -> bool {
665         libc::WIFEXITED(self.0)
666     }
667
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(()),
677         }
678     }
679
680     pub fn code(&self) -> Option<i32> {
681         self.exited().then(|| libc::WEXITSTATUS(self.0))
682     }
683
684     pub fn signal(&self) -> Option<i32> {
685         libc::WIFSIGNALED(self.0).then(|| libc::WTERMSIG(self.0))
686     }
687
688     pub fn core_dumped(&self) -> bool {
689         libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0)
690     }
691
692     pub fn stopped_signal(&self) -> Option<i32> {
693         libc::WIFSTOPPED(self.0).then(|| libc::WSTOPSIG(self.0))
694     }
695
696     pub fn continued(&self) -> bool {
697         libc::WIFCONTINUED(self.0)
698     }
699
700     pub fn into_raw(&self) -> c_int {
701         self.0
702     }
703 }
704
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 {
708         ExitStatus(a)
709     }
710 }
711
712 /// Convert a signal number to a readable, searchable name.
713 ///
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 {
719     match signal {
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`
753         #[cfg(all(
754             target_os = "linux",
755             any(
756                 target_arch = "x86_64",
757                 target_arch = "x86",
758                 target_arch = "arm",
759                 target_arch = "aarch64"
760             )
761         ))]
762         libc::SIGSTKFLT => " (SIGSTKFLT)",
763         #[cfg(target_os = "linux")]
764         libc::SIGPWR => " (SIGPWR)",
765         #[cfg(any(
766             target_os = "macos",
767             target_os = "ios",
768             target_os = "tvos",
769             target_os = "freebsd",
770             target_os = "netbsd",
771             target_os = "openbsd",
772             target_os = "dragonfly"
773         ))]
774         libc::SIGEMT => " (SIGEMT)",
775         #[cfg(any(
776             target_os = "macos",
777             target_os = "ios",
778             target_os = "tvos",
779             target_os = "freebsd",
780             target_os = "netbsd",
781             target_os = "openbsd",
782             target_os = "dragonfly"
783         ))]
784         libc::SIGINFO => " (SIGINFO)",
785         _ => "",
786     }
787 }
788
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)")
797             } else {
798                 write!(f, "signal: {signal}{signal_string}")
799             }
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)")
805         } else {
806             write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0)
807         }
808     }
809 }
810
811 #[derive(PartialEq, Eq, Clone, Copy)]
812 pub struct ExitStatusError(NonZero_c_int);
813
814 impl Into<ExitStatus> for ExitStatusError {
815     fn into(self) -> ExitStatus {
816         ExitStatus(self.0.into())
817     }
818 }
819
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()
823     }
824 }
825
826 impl ExitStatusError {
827     pub fn code(self) -> Option<NonZeroI32> {
828         ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
829     }
830 }
831
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> {
836         self.handle
837             .pidfd
838             .as_ref()
839             .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
840     }
841
842     fn take_pidfd(&mut self) -> io::Result<PidFd> {
843         self.handle
844             .pidfd
845             .take()
846             .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
847     }
848 }
849
850 #[cfg(test)]
851 #[path = "process_unix/tests.rs"]
852 mod tests;