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 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 const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1);
44 const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000);
46 }
47 _ => {}
48}
49
50impl 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 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); 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 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 let mut p = unsafe { Process::new(pid, pidfd) };
135 let mut bytes = [0; 8];
136
137 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 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 #[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 #[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 #[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 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 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 #[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 #[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 #[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 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 #[cfg(not(target_os = "emscripten"))]
356 {
357 if !crate::sys::pal::on_broken_pipe_used() {
365 #[cfg(target_os = "android")] {
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 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 #[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 const FORK_EXEC: u8 = 2;
491 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 support = FORK_EXEC;
507 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 return Err(e);
525 }
526 _ => {
527 }
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 #[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 #[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 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 #[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 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 #[cfg(any(all(target_os = "linux", target_env = "musl"), target_os = "cygwin"))]
644 fn get_posix_spawn_addchdir() -> Option<PosixSpawnAddChdirFn> {
645 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 if self.get_program_kind() == ProgramKind::Relative {
658 return Ok(None);
659 }
660 }
661 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 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 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 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 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 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 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 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 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 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 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
936pub struct Process {
942 pid: pid_t,
943 status: Option<ExitStatus>,
944 #[cfg(target_os = "linux")]
949 pidfd: Option<PidFd>,
950}
951
952impl Process {
953 #[cfg(target_os = "linux")]
954 unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self {
961 use crate::os::unix::io::FromRawFd;
962 use crate::sys::FromInner;
963 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 self.status.is_some() {
986 return Ok(());
987 }
988 #[cfg(target_os = "linux")]
989 if let Some(pid_fd) = self.pidfd.as_ref() {
990 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 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 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#[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 match NonZero::try_from(self.0) {
1092 Ok(failure) => Err(ExitStatusError(failure)),
1093 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
1122impl From<c_int> for ExitStatus {
1124 fn from(a: c_int) -> ExitStatus {
1125 ExitStatus(a)
1126 }
1127}
1128
1129fn 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 #[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 .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#[cfg(all(test, target_os = "linux"))]
1308#[path = "unsupported/wait_status.rs"]
1309mod unsupported_wait_status;