2 use crate::io
::{self, Error, ErrorKind}
;
4 use crate::num
::NonZeroI32
;
7 use crate::sys
::process
::process_common
::*;
8 use core
::ffi
::NonZero_c_int
;
10 #[cfg(target_os = "linux")]
11 use crate::os
::linux
::process
::PidFd
;
13 #[cfg(target_os = "linux")]
14 use crate::sys
::weak
::raw_syscall
;
18 target_os
= "watchos",
20 target_os
= "freebsd",
21 all(target_os
= "linux", target_env
= "gnu"),
22 all(target_os
= "linux", target_env
= "musl"),
25 use crate::sys
::weak
::weak
;
27 #[cfg(target_os = "vxworks")]
28 use libc
::RTP_ID
as pid_t
;
30 #[cfg(not(target_os = "vxworks"))]
31 use libc
::{c_int, pid_t}
;
34 target_os
= "vxworks",
37 target_os
= "watchos",
39 use libc
::{gid_t, uid_t}
;
42 if #[cfg(all(target_os = "nto", target_env = "nto71"))] {
44 use libc
::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}
;
45 use crate::time
::Duration
;
46 use crate::sync
::LazyLock
;
47 // Get smallest amount of time we can sleep.
48 // Return a common value if it cannot be determined.
49 fn get_clock_resolution() -> Duration
{
50 static MIN_DELAY
: LazyLock
<Duration
, fn() -> Duration
> = LazyLock
::new(|| {
51 let mut mindelay
= libc
::timespec { tv_sec: 0, tv_nsec: 0 }
;
52 if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) }
== 0
54 Duration
::from_nanos(mindelay
.tv_nsec
as u64)
56 Duration
::from_millis(1)
61 // Arbitrary minimum sleep duration for retrying fork/spawn
62 const MIN_FORKSPAWN_SLEEP
: Duration
= Duration
::from_nanos(1);
63 // Maximum duration of sleeping before giving up and returning an error
64 const MAX_FORKSPAWN_SLEEP
: Duration
= Duration
::from_millis(1000);
68 ////////////////////////////////////////////////////////////////////////////////
70 ////////////////////////////////////////////////////////////////////////////////
77 ) -> io
::Result
<(Process
, StdioPipes
)> {
78 const CLOEXEC_MSG_FOOTER
: [u8; 4] = *b
"NOEX";
80 let envp
= self.capture_env();
83 return Err(io
::const_io_error
!(
84 ErrorKind
::InvalidInput
,
85 "nul byte found in provided data",
89 let (ours
, theirs
) = self.setup_io(default, needs_stdin
)?
;
91 if let Some(ret
) = self.posix_spawn(&theirs
, envp
.as_ref())?
{
92 return Ok((ret
, ours
));
94 let (input
, output
) = sys
::pipe
::anon_pipe()?
;
96 // Whatever happens after the fork is almost for sure going to touch or
97 // look at the environment in one way or another (PATH in `execvp` or
98 // accessing the `environ` pointer ourselves). Make sure no other thread
99 // is accessing the environment when we do the fork itself.
101 // Note that as soon as we're done with the fork there's no need to hold
102 // a lock any more because the parent won't do anything and the child is
103 // in its own process. Thus the parent drops the lock guard immediately.
104 // The child calls `mem::forget` to leak the lock, which is crucial because
105 // releasing a lock is not async-signal-safe.
106 let env_lock
= sys
::os
::env_read_lock();
107 let (pid
, pidfd
) = unsafe { self.do_fork()? }
;
110 crate::panic
::always_abort();
111 mem
::forget(env_lock
); // avoid non-async-signal-safe unlocking
113 let Err(err
) = unsafe { self.do_exec(theirs, envp.as_ref()) }
;
114 let errno
= err
.raw_os_error().unwrap_or(libc
::EINVAL
) as u32;
115 let errno
= errno
.to_be_bytes();
121 CLOEXEC_MSG_FOOTER
[0],
122 CLOEXEC_MSG_FOOTER
[1],
123 CLOEXEC_MSG_FOOTER
[2],
124 CLOEXEC_MSG_FOOTER
[3],
126 // pipe I/O up to PIPE_BUF bytes should be atomic, and then
127 // we want to be sure we *don't* run at_exit destructors as
128 // we're being torn down regardless
129 rtassert
!(output
.write(&bytes
).is_ok());
130 unsafe { libc::_exit(1) }
136 // Safety: We obtained the pidfd from calling `clone3` with
137 // `CLONE_PIDFD` so it's valid an otherwise unowned.
138 let mut p
= unsafe { Process::new(pid, pidfd) }
;
139 let mut bytes
= [0; 8];
141 // loop to handle EINTR
143 match input
.read(&mut bytes
) {
144 Ok(0) => return Ok((p
, ours
)),
146 let (errno
, footer
) = bytes
.split_at(4);
148 CLOEXEC_MSG_FOOTER
, footer
,
149 "Validation on the CLOEXEC pipe failed: {:?}",
152 let errno
= i32::from_be_bytes(errno
.try_into().unwrap());
153 assert
!(p
.wait().is_ok(), "wait() should either return Ok or panic");
154 return Err(Error
::from_raw_os_error(errno
));
156 Err(ref e
) if e
.kind() == ErrorKind
::Interrupted
=> {}
158 assert
!(p
.wait().is_ok(), "wait() should either return Ok or panic");
159 panic
!("the CLOEXEC pipe failed: {e:?}")
162 // pipe I/O up to PIPE_BUF bytes should be atomic
163 assert
!(p
.wait().is_ok(), "wait() should either return Ok or panic");
164 panic
!("short read on the CLOEXEC pipe")
170 pub fn output(&mut self) -> io
::Result
<(ExitStatus
, Vec
<u8>, Vec
<u8>)> {
171 let (proc
, pipes
) = self.spawn(Stdio
::MakePipe
, false)?
;
172 crate::sys_common
::process
::wait_with_output(proc
, pipes
)
175 // WatchOS and TVOSÂ headers mark the `fork`/`exec*` functions with
176 // `__WATCHOS_PROHIBITED __TVOS_PROHIBITED`, and indicate that the
177 // `posix_spawn*` functions should be used instead. It isn't entirely clear
178 // what `PROHIBITED` means here (e.g. if calls to these functions are
179 // allowed to exist in dead code), but it sounds bad, so we go out of our
180 // way to avoid that all-together.
181 #[cfg(any(target_os = "tvos", target_os = "watchos"))]
182 const ERR_APPLE_TV_WATCH_NO_FORK_EXEC
: Error
= io
::const_io_error
!(
183 ErrorKind
::Unsupported
,
184 "`fork`+`exec`-based process spawning is not supported on this target",
187 #[cfg(any(target_os = "tvos", target_os = "watchos"))]
188 unsafe fn do_fork(&mut self) -> Result
<(pid_t
, pid_t
), io
::Error
> {
189 return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC
);
192 // Attempts to fork the process. If successful, returns Ok((0, -1))
193 // in the child, and Ok((child_pid, -1)) in the parent.
196 target_os
= "watchos",
198 all(target_os
= "nto", target_env
= "nto71"),
200 unsafe fn do_fork(&mut self) -> Result
<(pid_t
, pid_t
), io
::Error
> {
201 cvt(libc
::fork()).map(|res
| (res
, -1))
204 // On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened
205 // or closed a file descriptor while the fork() was occurring".
206 // Documentation says "... or try calling fork() again". This is what we do here.
207 // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html
208 #[cfg(all(target_os = "nto", target_env = "nto71"))]
209 unsafe fn do_fork(&mut self) -> Result
<(pid_t
, pid_t
), io
::Error
> {
210 use crate::sys
::os
::errno
;
212 let mut delay
= MIN_FORKSPAWN_SLEEP
;
215 let r
= libc
::fork();
216 if r
== -1 as libc
::pid_t
&& errno() as libc
::c_int
== libc
::EBADF
{
217 if delay
< get_clock_resolution() {
218 // We cannot sleep this short (it would be longer).
221 } else if delay
< MAX_FORKSPAWN_SLEEP
{
222 thread
::sleep(delay
);
224 return Err(io
::const_io_error
!(
225 ErrorKind
::WouldBlock
,
226 "forking returned EBADF too often",
232 return cvt(r
).map(|res
| (res
, -1));
237 // Attempts to fork the process. If successful, returns Ok((0, -1))
238 // in the child, and Ok((child_pid, child_pidfd)) in the parent.
239 #[cfg(target_os = "linux")]
240 unsafe fn do_fork(&mut self) -> Result
<(pid_t
, pid_t
), io
::Error
> {
241 use crate::sync
::atomic
::{AtomicBool, Ordering}
;
243 static HAS_CLONE3
: AtomicBool
= AtomicBool
::new(true);
244 const CLONE_PIDFD
: u64 = 0x00001000;
262 fn clone3(cl_args
: *mut clone_args
, len
: libc
::size_t
) -> libc
::c_long
265 // Bypassing libc for `clone3` can make further libc calls unsafe,
266 // so we use it sparingly for now. See #89522 for details.
267 // Some tools (e.g. sandboxing tools) may also expect `fork`
268 // rather than `clone3`.
269 let want_clone3_pidfd
= self.get_create_pidfd();
271 // If we fail to create a pidfd for any reason, this will
272 // stay as -1, which indicates an error.
273 let mut pidfd
: pid_t
= -1;
275 // Attempt to use the `clone3` syscall, which supports more arguments
276 // (in particular, the ability to create a pidfd). If this fails,
277 // we will fall through this block to a call to `fork()`
278 if want_clone3_pidfd
&& HAS_CLONE3
.load(Ordering
::Relaxed
) {
279 let mut args
= clone_args
{
281 pidfd
: &mut pidfd
as *mut pid_t
as u64,
284 exit_signal
: libc
::SIGCHLD
as u64,
293 let args_ptr
= &mut args
as *mut clone_args
;
294 let args_size
= crate::mem
::size_of
::<clone_args
>();
296 let res
= cvt(clone3(args_ptr
, args_size
));
298 Ok(n
) => return Ok((n
as pid_t
, pidfd
)),
299 Err(e
) => match e
.raw_os_error() {
300 // Multiple threads can race to execute this store,
301 // but that's fine - that just means that multiple threads
302 // will have tried and failed to execute the same syscall,
303 // with no other side effects.
304 Some(libc
::ENOSYS
) => HAS_CLONE3
.store(false, Ordering
::Relaxed
),
305 // Fallback to fork if `EPERM` is returned. (e.g. blocked by seccomp)
306 Some(libc
::EPERM
) => {}
312 // Generally, we just call `fork`. If we get here after wanting `clone3`,
313 // then the syscall does not exist or we do not have permission to call it.
314 cvt(libc
::fork()).map(|res
| (res
, pidfd
))
317 pub fn exec(&mut self, default: Stdio
) -> io
::Error
{
318 let envp
= self.capture_env();
321 return io
::const_io_error
!(ErrorKind
::InvalidInput
, "nul byte found in provided data",);
324 match self.setup_io(default, true) {
327 // Similar to when forking, we want to ensure that access to
328 // the environment is synchronized, so make sure to grab the
329 // environment lock before we try to exec.
330 let _lock
= sys
::os
::env_read_lock();
332 let Err(e
) = self.do_exec(theirs
, envp
.as_ref());
340 // And at this point we've reached a special time in the life of the
341 // child. The child must now be considered hamstrung and unable to
342 // do anything other than syscalls really. Consider the following
345 // 1. Thread A of process 1 grabs the malloc() mutex
346 // 2. Thread B of process 1 forks(), creating thread C
347 // 3. Thread C of process 2 then attempts to malloc()
348 // 4. The memory of process 2 is the same as the memory of
349 // process 1, so the mutex is locked.
351 // This situation looks a lot like deadlock, right? It turns out
352 // that this is what pthread_atfork() takes care of, which is
353 // presumably implemented across platforms. The first thing that
354 // threads to *before* forking is to do things like grab the malloc
355 // mutex, and then after the fork they unlock it.
357 // Despite this information, libnative's spawn has been witnessed to
358 // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
359 // all collected backtraces point at malloc/free traffic in the
360 // child spawned process.
362 // For this reason, the block of code below should contain 0
363 // invocations of either malloc of free (or their related friends).
365 // As an example of not having malloc/free traffic, we don't close
366 // this file descriptor by dropping the FileDesc (which contains an
367 // allocation). Instead we just close it manually. This will never
368 // have the drop glue anyway because this code never returns (the
369 // child will either exec() or invoke libc::exit)
370 #[cfg(not(any(target_os = "tvos", target_os = "watchos")))]
374 maybe_envp
: Option
<&CStringArray
>,
375 ) -> Result
<!, io
::Error
> {
376 use crate::sys
::{self, cvt_r}
;
378 if let Some(fd
) = stdio
.stdin
.fd() {
379 cvt_r(|| libc
::dup2(fd
, libc
::STDIN_FILENO
))?
;
381 if let Some(fd
) = stdio
.stdout
.fd() {
382 cvt_r(|| libc
::dup2(fd
, libc
::STDOUT_FILENO
))?
;
384 if let Some(fd
) = stdio
.stderr
.fd() {
385 cvt_r(|| libc
::dup2(fd
, libc
::STDERR_FILENO
))?
;
388 #[cfg(not(target_os = "l4re"))]
390 if let Some(_g
) = self.get_groups() {
391 //FIXME: Redox kernel does not support setgroups yet
392 #[cfg(not(target_os = "redox"))]
393 cvt(libc
::setgroups(_g
.len().try_into().unwrap(), _g
.as_ptr()))?
;
395 if let Some(u
) = self.get_gid() {
396 cvt(libc
::setgid(u
as gid_t
))?
;
398 if let Some(u
) = self.get_uid() {
399 // When dropping privileges from root, the `setgroups` call
400 // will remove any extraneous groups. We only drop groups
401 // if the current uid is 0 and we weren't given an explicit
402 // set of groups. If we don't call this, then even though our
403 // uid has dropped, we may still have groups that enable us to
404 // do super-user things.
405 //FIXME: Redox kernel does not support setgroups yet
406 #[cfg(not(target_os = "redox"))]
407 if libc
::getuid() == 0 && self.get_groups().is_none() {
408 cvt(libc
::setgroups(0, crate::ptr
::null()))?
;
410 cvt(libc
::setuid(u
as uid_t
))?
;
413 if let Some(ref cwd
) = *self.get_cwd() {
414 cvt(libc
::chdir(cwd
.as_ptr()))?
;
417 if let Some(pgroup
) = self.get_pgroup() {
418 cvt(libc
::setpgid(0, pgroup
))?
;
421 // emscripten has no signal support.
422 #[cfg(not(target_os = "emscripten"))]
424 // Inherit the signal mask from the parent rather than resetting it (i.e. do not call
427 // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL.
428 // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility.
430 // #[unix_sigpipe] is an opportunity to change the default here.
431 if !crate::sys
::unix_sigpipe_attr_specified() {
432 #[cfg(target_os = "android")] // see issue #88585
434 let mut action
: libc
::sigaction
= mem
::zeroed();
435 action
.sa_sigaction
= libc
::SIG_DFL
;
436 cvt(libc
::sigaction(libc
::SIGPIPE
, &action
, crate::ptr
::null_mut()))?
;
438 #[cfg(not(target_os = "android"))]
440 let ret
= sys
::signal(libc
::SIGPIPE
, libc
::SIG_DFL
);
441 if ret
== libc
::SIG_ERR
{
442 return Err(io
::Error
::last_os_error());
448 for callback
in self.get_closures().iter_mut() {
452 // Although we're performing an exec here we may also return with an
453 // error from this function (without actually exec'ing) in which case we
454 // want to be sure to restore the global environment back to what it
455 // once was, ensuring that our temporary override, when free'd, doesn't
456 // corrupt our process's environment.
457 let mut _reset
= None
;
458 if let Some(envp
) = maybe_envp
{
459 struct Reset(*const *const libc
::c_char
);
461 impl Drop
for Reset
{
464 *sys
::os
::environ() = self.0;
469 _reset
= Some(Reset(*sys
::os
::environ()));
470 *sys
::os
::environ() = envp
.as_ptr();
473 libc
::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
474 Err(io
::Error
::last_os_error())
477 #[cfg(any(target_os = "tvos", target_os = "watchos"))]
481 _maybe_envp
: Option
<&CStringArray
>,
482 ) -> Result
<!, io
::Error
> {
483 return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC
);
489 target_os
= "watchos",
490 target_os
= "freebsd",
491 all(target_os
= "linux", target_env
= "gnu"),
492 all(target_os
= "linux", target_env
= "musl"),
498 _
: Option
<&CStringArray
>,
499 ) -> io
::Result
<Option
<Process
>> {
503 // Only support platforms for which posix_spawn() can return ENOENT
507 // FIXME: `target_os = "ios"`?
509 target_os
= "watchos",
510 target_os
= "freebsd",
511 all(target_os
= "linux", target_env
= "gnu"),
512 all(target_os
= "linux", target_env
= "musl"),
518 envp
: Option
<&CStringArray
>,
519 ) -> io
::Result
<Option
<Process
>> {
520 use crate::mem
::MaybeUninit
;
521 use crate::sys
::{self, cvt_nz, unix_sigpipe_attr_specified}
;
523 if self.get_gid().is_some()
524 || self.get_uid().is_some()
525 || (self.env_saw_path() && !self.program_is_path())
526 || !self.get_closures().is_empty()
527 || self.get_groups().is_some()
528 || self.get_create_pidfd()
533 // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
534 #[cfg(all(target_os = "linux", target_env = "gnu"))]
536 if let Some(version
) = sys
::os
::glibc_version() {
537 if version
< (2, 24) {
545 // On QNX Neutrino, posix_spawnp can fail with EBADF in case "another thread might have opened
546 // or closed a file descriptor while the posix_spawn() was occurring".
547 // Documentation says "... or try calling posix_spawn() again". This is what we do here.
548 // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html
549 #[cfg(all(target_os = "nto", target_env = "nto71"))]
550 unsafe fn retrying_libc_posix_spawnp(
553 file_actions
: *const posix_spawn_file_actions_t
,
554 attrp
: *const posix_spawnattr_t
,
555 argv
: *const *mut c_char
,
556 envp
: *const *mut c_char
,
557 ) -> io
::Result
<i32> {
558 let mut delay
= MIN_FORKSPAWN_SLEEP
;
560 match libc
::posix_spawnp(pid
, file
, file_actions
, attrp
, argv
, envp
) {
562 if delay
< get_clock_resolution() {
563 // We cannot sleep this short (it would be longer).
566 } else if delay
< MAX_FORKSPAWN_SLEEP
{
567 thread
::sleep(delay
);
569 return Err(io
::const_io_error
!(
570 ErrorKind
::WouldBlock
,
571 "posix_spawnp returned EBADF too often",
584 // Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory,
585 // and maybe others will gain this non-POSIX function too. We'll check
586 // for this weak symbol as soon as it's needed, so we can return early
587 // otherwise to do a manual chdir before exec.
589 fn posix_spawn_file_actions_addchdir_np(
590 *mut libc
::posix_spawn_file_actions_t
,
594 let addchdir
= match self.get_cwd() {
596 if cfg
!(any(target_os
= "macos", target_os
= "tvos", target_os
= "watchos")) {
597 // There is a bug in macOS where a relative executable
598 // path like "../myprogram" will cause `posix_spawn` to
599 // successfully launch the program, but erroneously return
600 // ENOENT when used with posix_spawn_file_actions_addchdir_np
601 // which was introduced in macOS 10.15.
602 if self.get_program_kind() == ProgramKind
::Relative
{
606 match posix_spawn_file_actions_addchdir_np
.get() {
607 Some(f
) => Some((f
, cwd
)),
608 None
=> return Ok(None
),
614 let pgroup
= self.get_pgroup();
616 // Safety: -1 indicates we don't have a pidfd.
617 let mut p
= unsafe { Process::new(0, -1) }
;
619 struct PosixSpawnFileActions
<'a
>(&'a
mut MaybeUninit
<libc
::posix_spawn_file_actions_t
>);
621 impl Drop
for PosixSpawnFileActions
<'_
> {
624 libc
::posix_spawn_file_actions_destroy(self.0.as_mut_ptr());
629 struct PosixSpawnattr
<'a
>(&'a
mut MaybeUninit
<libc
::posix_spawnattr_t
>);
631 impl Drop
for PosixSpawnattr
<'_
> {
634 libc
::posix_spawnattr_destroy(self.0.as_mut_ptr());
640 let mut attrs
= MaybeUninit
::uninit();
641 cvt_nz(libc
::posix_spawnattr_init(attrs
.as_mut_ptr()))?
;
642 let attrs
= PosixSpawnattr(&mut attrs
);
646 let mut file_actions
= MaybeUninit
::uninit();
647 cvt_nz(libc
::posix_spawn_file_actions_init(file_actions
.as_mut_ptr()))?
;
648 let file_actions
= PosixSpawnFileActions(&mut file_actions
);
650 if let Some(fd
) = stdio
.stdin
.fd() {
651 cvt_nz(libc
::posix_spawn_file_actions_adddup2(
652 file_actions
.0.as_mut_ptr(),
657 if let Some(fd
) = stdio
.stdout
.fd() {
658 cvt_nz(libc
::posix_spawn_file_actions_adddup2(
659 file_actions
.0.as_mut_ptr(),
664 if let Some(fd
) = stdio
.stderr
.fd() {
665 cvt_nz(libc
::posix_spawn_file_actions_adddup2(
666 file_actions
.0.as_mut_ptr(),
671 if let Some((f
, cwd
)) = addchdir
{
672 cvt_nz(f(file_actions
.0.as_mut_ptr(), cwd
.as_ptr()))?
;
675 if let Some(pgroup
) = pgroup
{
676 flags
|= libc
::POSIX_SPAWN_SETPGROUP
;
677 cvt_nz(libc
::posix_spawnattr_setpgroup(attrs
.0.as_mut_ptr(), pgroup
))?
;
680 // Inherit the signal mask from this process rather than resetting it (i.e. do not call
681 // posix_spawnattr_setsigmask).
683 // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL.
684 // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility.
686 // #[unix_sigpipe] is an opportunity to change the default here.
687 if !unix_sigpipe_attr_specified() {
688 let mut default_set
= MaybeUninit
::<libc
::sigset_t
>::uninit();
689 cvt(sigemptyset(default_set
.as_mut_ptr()))?
;
690 cvt(sigaddset(default_set
.as_mut_ptr(), libc
::SIGPIPE
))?
;
691 cvt_nz(libc
::posix_spawnattr_setsigdefault(
692 attrs
.0.as_mut_ptr(),
693 default_set
.as_ptr(),
695 flags
|= libc
::POSIX_SPAWN_SETSIGDEF
;
698 cvt_nz(libc
::posix_spawnattr_setflags(attrs
.0.as_mut_ptr(), flags
as _
))?
;
700 // Make sure we synchronize access to the global `environ` resource
701 let _env_lock
= sys
::os
::env_read_lock();
702 let envp
= envp
.map(|c
| c
.as_ptr()).unwrap_or_else(|| *sys
::os
::environ() as *const _
);
704 #[cfg(not(target_os = "nto"))]
705 let spawn_fn
= libc
::posix_spawnp
;
706 #[cfg(target_os = "nto")]
707 let spawn_fn
= retrying_libc_posix_spawnp
;
709 let spawn_res
= spawn_fn(
711 self.get_program_cstr().as_ptr(),
712 file_actions
.0.as_ptr(),
714 self.get_argv().as_ptr() as *const _
,
718 #[cfg(target_os = "nto")]
719 let spawn_res
= spawn_res?
;
727 ////////////////////////////////////////////////////////////////////////////////
729 ////////////////////////////////////////////////////////////////////////////////
731 /// The unique ID of the process (this should never be negative).
734 status
: Option
<ExitStatus
>,
735 // On Linux, stores the pidfd created for this child.
736 // This is None if the user did not request pidfd creation,
737 // or if the pidfd could not be created for some reason
738 // (e.g. the `clone3` syscall was not available).
739 #[cfg(target_os = "linux")]
740 pidfd
: Option
<PidFd
>,
744 #[cfg(target_os = "linux")]
745 unsafe fn new(pid
: pid_t
, pidfd
: pid_t
) -> Self {
746 use crate::os
::unix
::io
::FromRawFd
;
747 use crate::sys_common
::FromInner
;
748 // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned.
749 let pidfd
= (pidfd
>= 0).then(|| PidFd
::from_inner(sys
::fd
::FileDesc
::from_raw_fd(pidfd
)));
750 Process { pid, status: None, pidfd }
753 #[cfg(not(target_os = "linux"))]
754 unsafe fn new(pid
: pid_t
, _pidfd
: pid_t
) -> Self {
755 Process { pid, status: None }
758 pub fn id(&self) -> u32 {
762 pub fn kill(&mut self) -> io
::Result
<()> {
763 // If we've already waited on this process then the pid can be recycled
764 // and used for another process, and we probably shouldn't be killing
765 // random processes, so return Ok because the process has exited already.
766 if self.status
.is_some() {
769 cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }
).map(drop
)
773 pub fn wait(&mut self) -> io
::Result
<ExitStatus
> {
774 use crate::sys
::cvt_r
;
775 if let Some(status
) = self.status
{
778 let mut status
= 0 as c_int
;
779 cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) }
)?
;
780 self.status
= Some(ExitStatus
::new(status
));
781 Ok(ExitStatus
::new(status
))
784 pub fn try_wait(&mut self) -> io
::Result
<Option
<ExitStatus
>> {
785 if let Some(status
) = self.status
{
786 return Ok(Some(status
));
788 let mut status
= 0 as c_int
;
789 let pid
= cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) }
)?
;
793 self.status
= Some(ExitStatus
::new(status
));
794 Ok(Some(ExitStatus
::new(status
)))
799 /// Unix exit statuses
801 // This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status".
802 // See the discussion in comments and doc comments for `std::process::ExitStatus`.
803 #[derive(PartialEq, Eq, Clone, Copy)]
804 pub struct ExitStatus(c_int
);
806 impl fmt
::Debug
for ExitStatus
{
807 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
808 f
.debug_tuple("unix_wait_status").field(&self.0).finish()
813 pub fn new(status
: c_int
) -> ExitStatus
{
817 fn exited(&self) -> bool
{
818 libc
::WIFEXITED(self.0)
821 pub fn exit_ok(&self) -> Result
<(), ExitStatusError
> {
822 // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
823 // true on all actual versions of Unix, is widely assumed, and is specified in SuS
824 // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
825 // true for a platform pretending to be Unix, the tests (our doctests, and also
826 // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
827 match NonZero_c_int
::try_from(self.0) {
828 /* was nonzero */ Ok(failure
) => Err(ExitStatusError(failure
)),
829 /* was zero, couldn't convert */ Err(_
) => Ok(()),
833 pub fn code(&self) -> Option
<i32> {
834 self.exited().then(|| libc
::WEXITSTATUS(self.0))
837 pub fn signal(&self) -> Option
<i32> {
838 libc
::WIFSIGNALED(self.0).then(|| libc
::WTERMSIG(self.0))
841 pub fn core_dumped(&self) -> bool
{
842 libc
::WIFSIGNALED(self.0) && libc
::WCOREDUMP(self.0)
845 pub fn stopped_signal(&self) -> Option
<i32> {
846 libc
::WIFSTOPPED(self.0).then(|| libc
::WSTOPSIG(self.0))
849 pub fn continued(&self) -> bool
{
850 libc
::WIFCONTINUED(self.0)
853 pub fn into_raw(&self) -> c_int
{
858 /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
859 impl From
<c_int
> for ExitStatus
{
860 fn from(a
: c_int
) -> ExitStatus
{
865 /// Convert a signal number to a readable, searchable name.
867 /// This string should be displayed right after the signal number.
868 /// If a signal is unrecognized, it returns the empty string, so that
869 /// you just get the number like "0". If it is recognized, you'll get
870 /// something like "9 (SIGKILL)".
871 fn signal_string(signal
: i32) -> &'
static str {
873 libc
::SIGHUP
=> " (SIGHUP)",
874 libc
::SIGINT
=> " (SIGINT)",
875 libc
::SIGQUIT
=> " (SIGQUIT)",
876 libc
::SIGILL
=> " (SIGILL)",
877 libc
::SIGTRAP
=> " (SIGTRAP)",
878 libc
::SIGABRT
=> " (SIGABRT)",
879 #[cfg(not(target_os = "l4re"))]
880 libc
::SIGBUS
=> " (SIGBUS)",
881 libc
::SIGFPE
=> " (SIGFPE)",
882 libc
::SIGKILL
=> " (SIGKILL)",
883 #[cfg(not(target_os = "l4re"))]
884 libc
::SIGUSR1
=> " (SIGUSR1)",
885 libc
::SIGSEGV
=> " (SIGSEGV)",
886 #[cfg(not(target_os = "l4re"))]
887 libc
::SIGUSR2
=> " (SIGUSR2)",
888 libc
::SIGPIPE
=> " (SIGPIPE)",
889 libc
::SIGALRM
=> " (SIGALRM)",
890 libc
::SIGTERM
=> " (SIGTERM)",
891 #[cfg(not(target_os = "l4re"))]
892 libc
::SIGCHLD
=> " (SIGCHLD)",
893 #[cfg(not(target_os = "l4re"))]
894 libc
::SIGCONT
=> " (SIGCONT)",
895 #[cfg(not(target_os = "l4re"))]
896 libc
::SIGSTOP
=> " (SIGSTOP)",
897 #[cfg(not(target_os = "l4re"))]
898 libc
::SIGTSTP
=> " (SIGTSTP)",
899 #[cfg(not(target_os = "l4re"))]
900 libc
::SIGTTIN
=> " (SIGTTIN)",
901 #[cfg(not(target_os = "l4re"))]
902 libc
::SIGTTOU
=> " (SIGTTOU)",
903 #[cfg(not(target_os = "l4re"))]
904 libc
::SIGURG
=> " (SIGURG)",
905 #[cfg(not(target_os = "l4re"))]
906 libc
::SIGXCPU
=> " (SIGXCPU)",
907 #[cfg(not(target_os = "l4re"))]
908 libc
::SIGXFSZ
=> " (SIGXFSZ)",
909 #[cfg(not(target_os = "l4re"))]
910 libc
::SIGVTALRM
=> " (SIGVTALRM)",
911 #[cfg(not(target_os = "l4re"))]
912 libc
::SIGPROF
=> " (SIGPROF)",
913 #[cfg(not(target_os = "l4re"))]
914 libc
::SIGWINCH
=> " (SIGWINCH)",
915 #[cfg(not(any(target_os = "haiku", target_os = "l4re")))]
916 libc
::SIGIO
=> " (SIGIO)",
917 #[cfg(target_os = "haiku")]
918 libc
::SIGPOLL
=> " (SIGPOLL)",
919 #[cfg(not(target_os = "l4re"))]
920 libc
::SIGSYS
=> " (SIGSYS)",
921 // For information on Linux signals, run `man 7 signal`
925 target_arch
= "x86_64",
928 target_arch
= "aarch64"
931 libc
::SIGSTKFLT
=> " (SIGSTKFLT)",
932 #[cfg(any(target_os = "linux", target_os = "nto"))]
933 libc
::SIGPWR
=> " (SIGPWR)",
938 target_os
= "freebsd",
939 target_os
= "netbsd",
940 target_os
= "openbsd",
941 target_os
= "dragonfly",
944 libc
::SIGEMT
=> " (SIGEMT)",
949 target_os
= "freebsd",
950 target_os
= "netbsd",
951 target_os
= "openbsd",
952 target_os
= "dragonfly"
954 libc
::SIGINFO
=> " (SIGINFO)",
959 impl fmt
::Display
for ExitStatus
{
960 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
961 if let Some(code
) = self.code() {
962 write
!(f
, "exit status: {code}")
963 } else if let Some(signal
) = self.signal() {
964 let signal_string
= signal_string(signal
);
965 if self.core_dumped() {
966 write
!(f
, "signal: {signal}{signal_string} (core dumped)")
968 write
!(f
, "signal: {signal}{signal_string}")
970 } else if let Some(signal
) = self.stopped_signal() {
971 let signal_string
= signal_string(signal
);
972 write
!(f
, "stopped (not terminated) by signal: {signal}{signal_string}")
973 } else if self.continued() {
974 write
!(f
, "continued (WIFCONTINUED)")
976 write
!(f
, "unrecognised wait status: {} {:#x}", self.0, self.0)
981 #[derive(PartialEq, Eq, Clone, Copy)]
982 pub struct ExitStatusError(NonZero_c_int
);
984 impl Into
<ExitStatus
> for ExitStatusError
{
985 fn into(self) -> ExitStatus
{
986 ExitStatus(self.0.into
())
990 impl fmt
::Debug
for ExitStatusError
{
991 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
992 f
.debug_tuple("unix_wait_status").field(&self.0).finish()
996 impl ExitStatusError
{
997 pub fn code(self) -> Option
<NonZeroI32
> {
998 ExitStatus(self.0.into
()).code().map(|st
| st
.try_into().unwrap())
1002 #[cfg(target_os = "linux")]
1003 #[unstable(feature = "linux_pidfd", issue = "82971")]
1004 impl crate::os
::linux
::process
::ChildExt
for crate::process
::Child
{
1005 fn pidfd(&self) -> io
::Result
<&PidFd
> {
1009 .ok_or_else(|| Error
::new(ErrorKind
::Uncategorized
, "No pidfd was created."))
1012 fn take_pidfd(&mut self) -> io
::Result
<PidFd
> {
1016 .ok_or_else(|| Error
::new(ErrorKind
::Uncategorized
, "No pidfd was created."))
1021 #[path = "process_unix/tests.rs"]