1use crate::fmt;
2use crate::io::{self, Error, ErrorKind};
3use crate::mem;
4use crate::num::{NonZero, NonZeroI32};
5use crate::sys;
6use crate::sys::cvt;
7use crate::sys::process::process_common::*;
8
9#[cfg(target_os = "linux")]
10use crate::os::linux::process::PidFd;
11#[cfg(target_os = "linux")]
12use crate::os::unix::io::AsRawFd;
13
14#[cfg(any(
15 target_os = "macos",
16 target_os = "watchos",
17 target_os = "tvos",
18 target_os = "freebsd",
19 all(target_os = "linux", target_env = "gnu"),
20 all(target_os = "linux", target_env = "musl"),
21 target_os = "nto",
22))]
23use crate::sys::weak::weak;
24
25#[cfg(target_os = "vxworks")]
26use libc::RTP_ID as pid_t;
27
28#[cfg(not(target_os = "vxworks"))]
29use libc::{c_int, pid_t};
30
31#[cfg(not(any(
32 target_os = "vxworks",
33 target_os = "l4re",
34 target_os = "tvos",
35 target_os = "watchos",
36)))]
37use libc::{gid_t, uid_t};
38
39cfg_if::cfg_if! {
40 if #[cfg(all(target_os = "nto", target_env = "nto71"))] {
41 use crate::thread;
42 use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t};
43 use crate::time::Duration;
44 use crate::sync::LazyLock;
45 // Get smallest amount of time we can sleep.
46 // Return a common value if it cannot be determined.
47 fn get_clock_resolution() -> Duration {
48 static MIN_DELAY: LazyLock<Duration, fn() -> Duration> = LazyLock::new(|| {
49 let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 };
50 if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0
51 {
52 Duration::from_nanos(mindelay.tv_nsec as u64)
53 } else {
54 Duration::from_millis(1)
55 }
56 });
57 *MIN_DELAY
58 }
59 // Arbitrary minimum sleep duration for retrying fork/spawn
60 const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1);
61 // Maximum duration of sleeping before giving up and returning an error
62 const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000);
63 }
64}
65
66////////////////////////////////////////////////////////////////////////////////
67// Command
68////////////////////////////////////////////////////////////////////////////////
69
70impl Command {
71 pub fn spawn(
72 &mut self,
73 default: Stdio,
74 needs_stdin: bool,
75 ) -> io::Result<(Process, StdioPipes)> {
76 const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX";
77
78 let envp = self.capture_env();
79
80 if self.saw_nul() {
81 return Err(io::const_io_error!(
82 ErrorKind::InvalidInput,
83 "nul byte found in provided data",
84 ));
85 }
86
87 let (ours, theirs) = self.setup_io(default, needs_stdin)?;
88
89 if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
90 return Ok((ret, ours));
91 }
92
93 #[cfg(target_os = "linux")]
94 let (input, output) = sys::net::Socket::new_pair(libc::AF_UNIX, libc::SOCK_SEQPACKET)?;
95
96 #[cfg(not(target_os = "linux"))]
97 let (input, output) = sys::pipe::anon_pipe()?;
98
99 // Whatever happens after the fork is almost for sure going to touch or
100 // look at the environment in one way or another (PATH in `execvp` or
101 // accessing the `environ` pointer ourselves). Make sure no other thread
102 // is accessing the environment when we do the fork itself.
103 //
104 // Note that as soon as we're done with the fork there's no need to hold
105 // a lock any more because the parent won't do anything and the child is
106 // in its own process. Thus the parent drops the lock guard immediately.
107 // The child calls `mem::forget` to leak the lock, which is crucial because
108 // releasing a lock is not async-signal-safe.
109 let env_lock = sys::os::env_read_lock();
110 let pid = unsafe { self.do_fork()? };
111
112 if pid == 0 {
113 crate::panic::always_abort();
114 mem::forget(env_lock); // avoid non-async-signal-safe unlocking
115 drop(input);
116 #[cfg(target_os = "linux")]
117 if self.get_create_pidfd() {
118 self.send_pidfd(&output);
119 }
120 let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) };
121 let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
122 let errno = errno.to_be_bytes();
123 let bytes = [
124 errno[0],
125 errno[1],
126 errno[2],
127 errno[3],
128 CLOEXEC_MSG_FOOTER[0],
129 CLOEXEC_MSG_FOOTER[1],
130 CLOEXEC_MSG_FOOTER[2],
131 CLOEXEC_MSG_FOOTER[3],
132 ];
133 // pipe I/O up to PIPE_BUF bytes should be atomic, and then
134 // we want to be sure we *don't* run at_exit destructors as
135 // we're being torn down regardless
136 rtassert!(output.write(&bytes).is_ok());
137 unsafe { libc::_exit(1) }
138 }
139
140 drop(env_lock);
141 drop(output);
142
143 #[cfg(target_os = "linux")]
144 let pidfd = if self.get_create_pidfd() { self.recv_pidfd(&input) } else { -1 };
145
146 #[cfg(not(target_os = "linux"))]
147 let pidfd = -1;
148
149 // Safety: We obtained the pidfd (on Linux) using SOCK_SEQPACKET, so it's valid.
150 let mut p = unsafe { Process::new(pid, pidfd) };
151 let mut bytes = [0; 8];
152
153 // loop to handle EINTR
154 loop {
155 match input.read(&mut bytes) {
156 Ok(0) => return Ok((p, ours)),
157 Ok(8) => {
158 let (errno, footer) = bytes.split_at(4);
159 assert_eq!(
160 CLOEXEC_MSG_FOOTER, footer,
161 "Validation on the CLOEXEC pipe failed: {:?}",
162 bytes
163 );
164 let errno = i32::from_be_bytes(errno.try_into().unwrap());
165 assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
166 return Err(Error::from_raw_os_error(errno));
167 }
168 Err(ref e) if e.is_interrupted() => {}
169 Err(e) => {
170 assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
171 panic!("the CLOEXEC pipe failed: {e:?}")
172 }
173 Ok(..) => {
174 // pipe I/O up to PIPE_BUF bytes should be atomic
175 // similarly SOCK_SEQPACKET messages should arrive whole
176 assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
177 panic!("short read on the CLOEXEC pipe")
178 }
179 }
180 }
181 }
182
183 pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
184 let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
185 crate::sys_common::process::wait_with_output(proc, pipes)
186 }
187
188 // WatchOS and TVOS headers mark the `fork`/`exec*` functions with
189 // `__WATCHOS_PROHIBITED __TVOS_PROHIBITED`, and indicate that the
190 // `posix_spawn*` functions should be used instead. It isn't entirely clear
191 // what `PROHIBITED` means here (e.g. if calls to these functions are
192 // allowed to exist in dead code), but it sounds bad, so we go out of our
193 // way to avoid that all-together.
194 #[cfg(any(target_os = "tvos", target_os = "watchos"))]
195 const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_io_error!(
196 ErrorKind::Unsupported,
197 "`fork`+`exec`-based process spawning is not supported on this target",
198 );
199
200 #[cfg(any(target_os = "tvos", target_os = "watchos"))]
201 unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> {
202 return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC);
203 }
204
205 // Attempts to fork the process. If successful, returns Ok((0, -1))
206 // in the child, and Ok((child_pid, -1)) in the parent.
207 #[cfg(not(any(
208 target_os = "watchos",
209 target_os = "tvos",
210 all(target_os = "nto", target_env = "nto71"),
211 )))]
212 unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> {
213 cvt(libc::fork())
214 }
215
216 // On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened
217 // or closed a file descriptor while the fork() was occurring".
218 // Documentation says "... or try calling fork() again". This is what we do here.
219 // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html
220 #[cfg(all(target_os = "nto", target_env = "nto71"))]
221 unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> {
222 use crate::sys::os::errno;
223
224 let mut delay = MIN_FORKSPAWN_SLEEP;
225
226 loop {
227 let r = libc::fork();
228 if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF {
229 if delay < get_clock_resolution() {
230 // We cannot sleep this short (it would be longer).
231 // Yield instead.
232 thread::yield_now();
233 } else if delay < MAX_FORKSPAWN_SLEEP {
234 thread::sleep(delay);
235 } else {
236 return Err(io::const_io_error!(
237 ErrorKind::WouldBlock,
238 "forking returned EBADF too often",
239 ));
240 }
241 delay *= 2;
242 continue;
243 } else {
244 return cvt(r);
245 }
246 }
247 }
248
249 pub fn exec(&mut self, default: Stdio) -> io::Error {
250 let envp = self.capture_env();
251
252 if self.saw_nul() {
253 return io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data",);
254 }
255
256 match self.setup_io(default, true) {
257 Ok((_, theirs)) => {
258 unsafe {
259 // Similar to when forking, we want to ensure that access to
260 // the environment is synchronized, so make sure to grab the
261 // environment lock before we try to exec.
262 let _lock = sys::os::env_read_lock();
263
264 let Err(e) = self.do_exec(theirs, envp.as_ref());
265 e
266 }
267 }
268 Err(e) => e,
269 }
270 }
271
272 // And at this point we've reached a special time in the life of the
273 // child. The child must now be considered hamstrung and unable to
274 // do anything other than syscalls really. Consider the following
275 // scenario:
276 //
277 // 1. Thread A of process 1 grabs the malloc() mutex
278 // 2. Thread B of process 1 forks(), creating thread C
279 // 3. Thread C of process 2 then attempts to malloc()
280 // 4. The memory of process 2 is the same as the memory of
281 // process 1, so the mutex is locked.
282 //
283 // This situation looks a lot like deadlock, right? It turns out
284 // that this is what pthread_atfork() takes care of, which is
285 // presumably implemented across platforms. The first thing that
286 // threads to *before* forking is to do things like grab the malloc
287 // mutex, and then after the fork they unlock it.
288 //
289 // Despite this information, libnative's spawn has been witnessed to
290 // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
291 // all collected backtraces point at malloc/free traffic in the
292 // child spawned process.
293 //
294 // For this reason, the block of code below should contain 0
295 // invocations of either malloc of free (or their related friends).
296 //
297 // As an example of not having malloc/free traffic, we don't close
298 // this file descriptor by dropping the FileDesc (which contains an
299 // allocation). Instead we just close it manually. This will never
300 // have the drop glue anyway because this code never returns (the
301 // child will either exec() or invoke libc::exit)
302 #[cfg(not(any(target_os = "tvos", target_os = "watchos")))]
303 unsafe fn do_exec(
304 &mut self,
305 stdio: ChildPipes,
306 maybe_envp: Option<&CStringArray>,
307 ) -> Result<!, io::Error> {
308 use crate::sys::{self, cvt_r};
309
310 if let Some(fd) = stdio.stdin.fd() {
311 cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?;
312 }
313 if let Some(fd) = stdio.stdout.fd() {
314 cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?;
315 }
316 if let Some(fd) = stdio.stderr.fd() {
317 cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?;
318 }
319
320 #[cfg(not(target_os = "l4re"))]
321 {
322 if let Some(_g) = self.get_groups() {
323 //FIXME: Redox kernel does not support setgroups yet
324 #[cfg(not(target_os = "redox"))]
325 cvt(libc::setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?;
326 }
327 if let Some(u) = self.get_gid() {
328 cvt(libc::setgid(u as gid_t))?;
329 }
330 if let Some(u) = self.get_uid() {
331 // When dropping privileges from root, the `setgroups` call
332 // will remove any extraneous groups. We only drop groups
333 // if the current uid is 0 and we weren't given an explicit
334 // set of groups. If we don't call this, then even though our
335 // uid has dropped, we may still have groups that enable us to
336 // do super-user things.
337 //FIXME: Redox kernel does not support setgroups yet
338 #[cfg(not(target_os = "redox"))]
339 if libc::getuid() == 0 && self.get_groups().is_none() {
340 cvt(libc::setgroups(0, crate::ptr::null()))?;
341 }
342 cvt(libc::setuid(u as uid_t))?;
343 }
344 }
345 if let Some(ref cwd) = *self.get_cwd() {
346 cvt(libc::chdir(cwd.as_ptr()))?;
347 }
348
349 if let Some(pgroup) = self.get_pgroup() {
350 cvt(libc::setpgid(0, pgroup))?;
351 }
352
353 // emscripten has no signal support.
354 #[cfg(not(target_os = "emscripten"))]
355 {
356 // Inherit the signal mask from the parent rather than resetting it (i.e. do not call
357 // pthread_sigmask).
358
359 // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL.
360 // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility.
361 //
362 // #[unix_sigpipe] is an opportunity to change the default here.
363 if !crate::sys::pal::unix_sigpipe_attr_specified() {
364 #[cfg(target_os = "android")] // see issue #88585
365 {
366 let mut action: libc::sigaction = mem::zeroed();
367 action.sa_sigaction = libc::SIG_DFL;
368 cvt(libc::sigaction(libc::SIGPIPE, &action, crate::ptr::null_mut()))?;
369 }
370 #[cfg(not(target_os = "android"))]
371 {
372 let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
373 if ret == libc::SIG_ERR {
374 return Err(io::Error::last_os_error());
375 }
376 }
377 #[cfg(target_os = "hurd")]
378 {
379 let ret = sys::signal(libc::SIGLOST, libc::SIG_DFL);
380 if ret == libc::SIG_ERR {
381 return Err(io::Error::last_os_error());
382 }
383 }
384 }
385 }
386
387 for callback in self.get_closures().iter_mut() {
388 callback()?;
389 }
390
391 // Although we're performing an exec here we may also return with an
392 // error from this function (without actually exec'ing) in which case we
393 // want to be sure to restore the global environment back to what it
394 // once was, ensuring that our temporary override, when free'd, doesn't
395 // corrupt our process's environment.
396 let mut _reset = None;
397 if let Some(envp) = maybe_envp {
398 struct Reset(*const *const libc::c_char);
399
400 impl Drop for Reset {
401 fn drop(&mut self) {
402 unsafe {
403 *sys::os::environ() = self.0;
404 }
405 }
406 }
407
408 _reset = Some(Reset(*sys::os::environ()));
409 *sys::os::environ() = envp.as_ptr();
410 }
411
412 libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
413 Err(io::Error::last_os_error())
414 }
415
416 #[cfg(any(target_os = "tvos", target_os = "watchos"))]
417 unsafe fn do_exec(
418 &mut self,
419 _stdio: ChildPipes,
420 _maybe_envp: Option<&CStringArray>,
421 ) -> Result<!, io::Error> {
422 return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC);
423 }
424
425 #[cfg(not(any(
426 target_os = "macos",
427 target_os = "tvos",
428 target_os = "watchos",
429 target_os = "freebsd",
430 all(target_os = "linux", target_env = "gnu"),
431 all(target_os = "linux", target_env = "musl"),
432 target_os = "nto",
433 )))]
434 fn posix_spawn(
435 &mut self,
436 _: &ChildPipes,
437 _: Option<&CStringArray>,
438 ) -> io::Result<Option<Process>> {
439 Ok(None)
440 }
441
442 // Only support platforms for which posix_spawn() can return ENOENT
443 // directly.
444 #[cfg(any(
445 target_os = "macos",
446 // FIXME: `target_os = "ios"`?
447 target_os = "tvos",
448 target_os = "watchos",
449 target_os = "freebsd",
450 all(target_os = "linux", target_env = "gnu"),
451 all(target_os = "linux", target_env = "musl"),
452 target_os = "nto",
453 ))]
454 fn posix_spawn(
455 &mut self,
456 stdio: &ChildPipes,
457 envp: Option<&CStringArray>,
458 ) -> io::Result<Option<Process>> {
459 use crate::mem::MaybeUninit;
460 use crate::sys::{self, cvt_nz, unix_sigpipe_attr_specified};
461
462 if self.get_gid().is_some()
463 || self.get_uid().is_some()
464 || (self.env_saw_path() && !self.program_is_path())
465 || !self.get_closures().is_empty()
466 || self.get_groups().is_some()
467 || self.get_create_pidfd()
468 {
469 return Ok(None);
470 }
471
472 // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
473 #[cfg(all(target_os = "linux", target_env = "gnu"))]
474 {
475 if let Some(version) = sys::os::glibc_version() {
476 if version < (2, 24) {
477 return Ok(None);
478 }
479 } else {
480 return Ok(None);
481 }
482 }
483
484 // On QNX Neutrino, posix_spawnp can fail with EBADF in case "another thread might have opened
485 // or closed a file descriptor while the posix_spawn() was occurring".
486 // Documentation says "... or try calling posix_spawn() again". This is what we do here.
487 // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html
488 #[cfg(all(target_os = "nto", target_env = "nto71"))]
489 unsafe fn retrying_libc_posix_spawnp(
490 pid: *mut pid_t,
491 file: *const c_char,
492 file_actions: *const posix_spawn_file_actions_t,
493 attrp: *const posix_spawnattr_t,
494 argv: *const *mut c_char,
495 envp: *const *mut c_char,
496 ) -> io::Result<i32> {
497 let mut delay = MIN_FORKSPAWN_SLEEP;
498 loop {
499 match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) {
500 libc::EBADF => {
501 if delay < get_clock_resolution() {
502 // We cannot sleep this short (it would be longer).
503 // Yield instead.
504 thread::yield_now();
505 } else if delay < MAX_FORKSPAWN_SLEEP {
506 thread::sleep(delay);
507 } else {
508 return Err(io::const_io_error!(
509 ErrorKind::WouldBlock,
510 "posix_spawnp returned EBADF too often",
511 ));
512 }
513 delay *= 2;
514 continue;
515 }
516 r => {
517 return Ok(r);
518 }
519 }
520 }
521 }
522
523 // Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory,
524 // and maybe others will gain this non-POSIX function too. We'll check
525 // for this weak symbol as soon as it's needed, so we can return early
526 // otherwise to do a manual chdir before exec.
527 weak! {
528 fn posix_spawn_file_actions_addchdir_np(
529 *mut libc::posix_spawn_file_actions_t,
530 *const libc::c_char
531 ) -> libc::c_int
532 }
533 let addchdir = match self.get_cwd() {
534 Some(cwd) => {
535 if cfg!(any(target_os = "macos", target_os = "tvos", target_os = "watchos")) {
536 // There is a bug in macOS where a relative executable
537 // path like "../myprogram" will cause `posix_spawn` to
538 // successfully launch the program, but erroneously return
539 // ENOENT when used with posix_spawn_file_actions_addchdir_np
540 // which was introduced in macOS 10.15.
541 if self.get_program_kind() == ProgramKind::Relative {
542 return Ok(None);
543 }
544 }
545 match posix_spawn_file_actions_addchdir_np.get() {
546 Some(f) => Some((f, cwd)),
547 None => return Ok(None),
548 }
549 }
550 None => None,
551 };
552
553 let pgroup = self.get_pgroup();
554
555 // Safety: -1 indicates we don't have a pidfd.
556 let mut p = unsafe { Process::new(0, -1) };
557
558 struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit<libc::posix_spawn_file_actions_t>);
559
560 impl Drop for PosixSpawnFileActions<'_> {
561 fn drop(&mut self) {
562 unsafe {
563 libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr());
564 }
565 }
566 }
567
568 struct PosixSpawnattr<'a>(&'a mut MaybeUninit<libc::posix_spawnattr_t>);
569
570 impl Drop for PosixSpawnattr<'_> {
571 fn drop(&mut self) {
572 unsafe {
573 libc::posix_spawnattr_destroy(self.0.as_mut_ptr());
574 }
575 }
576 }
577
578 unsafe {
579 let mut attrs = MaybeUninit::uninit();
580 cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?;
581 let attrs = PosixSpawnattr(&mut attrs);
582
583 let mut flags = 0;
584
585 let mut file_actions = MaybeUninit::uninit();
586 cvt_nz(libc::posix_spawn_file_actions_init(file_actions.as_mut_ptr()))?;
587 let file_actions = PosixSpawnFileActions(&mut file_actions);
588
589 if let Some(fd) = stdio.stdin.fd() {
590 cvt_nz(libc::posix_spawn_file_actions_adddup2(
591 file_actions.0.as_mut_ptr(),
592 fd,
593 libc::STDIN_FILENO,
594 ))?;
595 }
596 if let Some(fd) = stdio.stdout.fd() {
597 cvt_nz(libc::posix_spawn_file_actions_adddup2(
598 file_actions.0.as_mut_ptr(),
599 fd,
600 libc::STDOUT_FILENO,
601 ))?;
602 }
603 if let Some(fd) = stdio.stderr.fd() {
604 cvt_nz(libc::posix_spawn_file_actions_adddup2(
605 file_actions.0.as_mut_ptr(),
606 fd,
607 libc::STDERR_FILENO,
608 ))?;
609 }
610 if let Some((f, cwd)) = addchdir {
611 cvt_nz(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?;
612 }
613
614 if let Some(pgroup) = pgroup {
615 flags |= libc::POSIX_SPAWN_SETPGROUP;
616 cvt_nz(libc::posix_spawnattr_setpgroup(attrs.0.as_mut_ptr(), pgroup))?;
617 }
618
619 // Inherit the signal mask from this process rather than resetting it (i.e. do not call
620 // posix_spawnattr_setsigmask).
621
622 // If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL.
623 // If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility.
624 //
625 // #[unix_sigpipe] is an opportunity to change the default here.
626 if !unix_sigpipe_attr_specified() {
627 let mut default_set = MaybeUninit::<libc::sigset_t>::uninit();
628 cvt(sigemptyset(default_set.as_mut_ptr()))?;
629 cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGPIPE))?;
630 #[cfg(target_os = "hurd")]
631 {
632 cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGLOST))?;
633 }
634 cvt_nz(libc::posix_spawnattr_setsigdefault(
635 attrs.0.as_mut_ptr(),
636 default_set.as_ptr(),
637 ))?;
638 flags |= libc::POSIX_SPAWN_SETSIGDEF;
639 }
640
641 cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
642
643 // Make sure we synchronize access to the global `environ` resource
644 let _env_lock = sys::os::env_read_lock();
645 let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
646
647 #[cfg(not(target_os = "nto"))]
648 let spawn_fn = libc::posix_spawnp;
649 #[cfg(target_os = "nto")]
650 let spawn_fn = retrying_libc_posix_spawnp;
651
652 let spawn_res = spawn_fn(
653 &mut p.pid,
654 self.get_program_cstr().as_ptr(),
655 file_actions.0.as_ptr(),
656 attrs.0.as_ptr(),
657 self.get_argv().as_ptr() as *const _,
658 envp as *const _,
659 );
660
661 #[cfg(target_os = "nto")]
662 let spawn_res = spawn_res?;
663
664 cvt_nz(spawn_res)?;
665 Ok(Some(p))
666 }
667 }
668
669 #[cfg(target_os = "linux")]
670 fn send_pidfd(&self, sock: &crate::sys::net::Socket) {
671 use crate::io::IoSlice;
672 use crate::os::fd::RawFd;
673 use crate::sys::cvt_r;
674 use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET};
675
676 unsafe {
677 let child_pid = libc::getpid();
678 // pidfd_open sets CLOEXEC by default
679 let pidfd = libc::syscall(libc::SYS_pidfd_open, child_pid, 0);
680
681 let fds: [c_int; 1] = [pidfd as RawFd];
682
683 const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>();
684
685 #[repr(C)]
686 union Cmsg {
687 buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }],
688 _align: libc::cmsghdr,
689 }
690
691 let mut cmsg: Cmsg = mem::zeroed();
692
693 // 0-length message to send through the socket so we can pass along the fd
694 let mut iov = [IoSlice::new(b"")];
695 let mut msg: libc::msghdr = mem::zeroed();
696
697 msg.msg_iov = &mut iov as *mut _ as *mut _;
698 msg.msg_iovlen = 1;
699
700 // only attach cmsg if we successfully acquired the pidfd
701 if pidfd >= 0 {
702 msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _;
703 msg.msg_control = &mut cmsg.buf as *mut _ as *mut _;
704
705 let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _);
706 (*hdr).cmsg_level = SOL_SOCKET;
707 (*hdr).cmsg_type = SCM_RIGHTS;
708 (*hdr).cmsg_len = CMSG_LEN(SCM_MSG_LEN as _) as _;
709 let data = CMSG_DATA(hdr);
710 crate::ptr::copy_nonoverlapping(
711 fds.as_ptr().cast::<u8>(),
712 data as *mut _,
713 SCM_MSG_LEN,
714 );
715 }
716
717 // we send the 0-length message even if we failed to acquire the pidfd
718 // so we get a consistent SEQPACKET order
719 match cvt_r(|| libc::sendmsg(sock.as_raw(), &msg, 0)) {
720 Ok(0) => {}
721 other => rtabort!("failed to communicate with parent process. {:?}", other),
722 }
723 }
724 }
725
726 #[cfg(target_os = "linux")]
727 fn recv_pidfd(&self, sock: &crate::sys::net::Socket) -> pid_t {
728 use crate::io::IoSliceMut;
729 use crate::sys::cvt_r;
730
731 use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET};
732
733 unsafe {
734 const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>();
735
736 #[repr(C)]
737 union Cmsg {
738 _buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }],
739 _align: libc::cmsghdr,
740 }
741 let mut cmsg: Cmsg = mem::zeroed();
742 // 0-length read to get the fd
743 let mut iov = [IoSliceMut::new(&mut [])];
744
745 let mut msg: libc::msghdr = mem::zeroed();
746
747 msg.msg_iov = &mut iov as *mut _ as *mut _;
748 msg.msg_iovlen = 1;
749 msg.msg_controllen = mem::size_of::<Cmsg>() as _;
750 msg.msg_control = &mut cmsg as *mut _ as *mut _;
751
752 match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, libc::MSG_CMSG_CLOEXEC)) {
753 Err(_) => return -1,
754 Ok(_) => {}
755 }
756
757 let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _);
758 if hdr.is_null()
759 || (*hdr).cmsg_level != SOL_SOCKET
760 || (*hdr).cmsg_type != SCM_RIGHTS
761 || (*hdr).cmsg_len != CMSG_LEN(SCM_MSG_LEN as _) as _
762 {
763 return -1;
764 }
765 let data = CMSG_DATA(hdr);
766
767 let mut fds = [-1 as c_int];
768
769 crate::ptr::copy_nonoverlapping(
770 data as *const _,
771 fds.as_mut_ptr().cast::<u8>(),
772 SCM_MSG_LEN,
773 );
774
775 fds[0]
776 }
777 }
778}
779
780////////////////////////////////////////////////////////////////////////////////
781// Processes
782////////////////////////////////////////////////////////////////////////////////
783
784/// The unique ID of the process (this should never be negative).
785pub struct Process {
786 pid: pid_t,
787 status: Option<ExitStatus>,
788 // On Linux, stores the pidfd created for this child.
789 // This is None if the user did not request pidfd creation,
790 // or if the pidfd could not be created for some reason
791 // (e.g. the `pidfd_open` syscall was not available).
792 #[cfg(target_os = "linux")]
793 pidfd: Option<PidFd>,
794}
795
796impl Process {
797 #[cfg(target_os = "linux")]
798 unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self {
799 use crate::os::unix::io::FromRawFd;
800 use crate::sys_common::FromInner;
801 // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned.
802 let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::from_raw_fd(pidfd)));
803 Process { pid, status: None, pidfd }
804 }
805
806 #[cfg(not(target_os = "linux"))]
807 unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self {
808 Process { pid, status: None }
809 }
810
811 pub fn id(&self) -> u32 {
812 self.pid as u32
813 }
814
815 pub fn kill(&mut self) -> io::Result<()> {
816 // If we've already waited on this process then the pid can be recycled
817 // and used for another process, and we probably shouldn't be killing
818 // random processes, so return Ok because the process has exited already.
819 if self.status.is_some() {
820 return Ok(());
821 }
822 #[cfg(target_os = "linux")]
823 if let Some(pid_fd) = self.pidfd.as_ref() {
824 // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too
825 return cvt(unsafe {
826 libc::syscall(
827 libc::SYS_pidfd_send_signal,
828 pid_fd.as_raw_fd(),
829 libc::SIGKILL,
830 crate::ptr::null::<()>(),
831 0,
832 )
833 })
834 .map(drop);
835 }
836 cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
837 }
838
839 pub fn wait(&mut self) -> io::Result<ExitStatus> {
840 use crate::sys::cvt_r;
841 if let Some(status) = self.status {
842 return Ok(status);
843 }
844 #[cfg(target_os = "linux")]
845 if let Some(pid_fd) = self.pidfd.as_ref() {
846 let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() };
847
848 cvt_r(|| unsafe {
849 libc::waitid(libc::P_PIDFD, pid_fd.as_raw_fd() as u32, &mut siginfo, libc::WEXITED)
850 })?;
851 let status = ExitStatus::from_waitid_siginfo(siginfo);
852 self.status = Some(status);
853 return Ok(status);
854 }
855 let mut status = 0 as c_int;
856 cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
857 self.status = Some(ExitStatus::new(status));
858 Ok(ExitStatus::new(status))
859 }
860
861 pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
862 if let Some(status) = self.status {
863 return Ok(Some(status));
864 }
865 #[cfg(target_os = "linux")]
866 if let Some(pid_fd) = self.pidfd.as_ref() {
867 let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() };
868
869 cvt(unsafe {
870 libc::waitid(
871 libc::P_PIDFD,
872 pid_fd.as_raw_fd() as u32,
873 &mut siginfo,
874 libc::WEXITED | libc::WNOHANG,
875 )
876 })?;
877 if unsafe { siginfo.si_pid() } == 0 {
878 return Ok(None);
879 }
880 let status = ExitStatus::from_waitid_siginfo(siginfo);
881 self.status = Some(status);
882 return Ok(Some(status));
883 }
884 let mut status = 0 as c_int;
885 let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
886 if pid == 0 {
887 Ok(None)
888 } else {
889 self.status = Some(ExitStatus::new(status));
890 Ok(Some(ExitStatus::new(status)))
891 }
892 }
893}
894
895/// Unix exit statuses
896//
897// This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status".
898// See the discussion in comments and doc comments for `std::process::ExitStatus`.
899#[derive(PartialEq, Eq, Clone, Copy, Default)]
900pub struct ExitStatus(c_int);
901
902impl fmt::Debug for ExitStatus {
903 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
904 f.debug_tuple(name:"unix_wait_status").field(&self.0).finish()
905 }
906}
907
908impl ExitStatus {
909 pub fn new(status: c_int) -> ExitStatus {
910 ExitStatus(status)
911 }
912
913 #[cfg(target_os = "linux")]
914 pub fn from_waitid_siginfo(siginfo: libc::siginfo_t) -> ExitStatus {
915 let status = unsafe { siginfo.si_status() };
916
917 match siginfo.si_code {
918 libc::CLD_EXITED => ExitStatus((status & 0xff) << 8),
919 libc::CLD_KILLED => ExitStatus(status),
920 libc::CLD_DUMPED => ExitStatus(status | 0x80),
921 libc::CLD_CONTINUED => ExitStatus(0xffff),
922 libc::CLD_STOPPED | libc::CLD_TRAPPED => ExitStatus(((status & 0xff) << 8) | 0x7f),
923 _ => unreachable!("waitid() should only return the above codes"),
924 }
925 }
926
927 fn exited(&self) -> bool {
928 libc::WIFEXITED(self.0)
929 }
930
931 pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
932 // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
933 // true on all actual versions of Unix, is widely assumed, and is specified in SuS
934 // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
935 // true for a platform pretending to be Unix, the tests (our doctests, and also
936 // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
937 match NonZero::try_from(self.0) {
938 /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
939 /* was zero, couldn't convert */ Err(_) => Ok(()),
940 }
941 }
942
943 pub fn code(&self) -> Option<i32> {
944 self.exited().then(|| libc::WEXITSTATUS(self.0))
945 }
946
947 pub fn signal(&self) -> Option<i32> {
948 libc::WIFSIGNALED(self.0).then(|| libc::WTERMSIG(self.0))
949 }
950
951 pub fn core_dumped(&self) -> bool {
952 libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0)
953 }
954
955 pub fn stopped_signal(&self) -> Option<i32> {
956 libc::WIFSTOPPED(self.0).then(|| libc::WSTOPSIG(self.0))
957 }
958
959 pub fn continued(&self) -> bool {
960 libc::WIFCONTINUED(self.0)
961 }
962
963 pub fn into_raw(&self) -> c_int {
964 self.0
965 }
966}
967
968/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
969impl From<c_int> for ExitStatus {
970 fn from(a: c_int) -> ExitStatus {
971 ExitStatus(a)
972 }
973}
974
975/// Convert a signal number to a readable, searchable name.
976///
977/// This string should be displayed right after the signal number.
978/// If a signal is unrecognized, it returns the empty string, so that
979/// you just get the number like "0". If it is recognized, you'll get
980/// something like "9 (SIGKILL)".
981fn signal_string(signal: i32) -> &'static str {
982 match signal {
983 libc::SIGHUP => " (SIGHUP)",
984 libc::SIGINT => " (SIGINT)",
985 libc::SIGQUIT => " (SIGQUIT)",
986 libc::SIGILL => " (SIGILL)",
987 libc::SIGTRAP => " (SIGTRAP)",
988 libc::SIGABRT => " (SIGABRT)",
989 #[cfg(not(target_os = "l4re"))]
990 libc::SIGBUS => " (SIGBUS)",
991 libc::SIGFPE => " (SIGFPE)",
992 libc::SIGKILL => " (SIGKILL)",
993 #[cfg(not(target_os = "l4re"))]
994 libc::SIGUSR1 => " (SIGUSR1)",
995 libc::SIGSEGV => " (SIGSEGV)",
996 #[cfg(not(target_os = "l4re"))]
997 libc::SIGUSR2 => " (SIGUSR2)",
998 libc::SIGPIPE => " (SIGPIPE)",
999 libc::SIGALRM => " (SIGALRM)",
1000 libc::SIGTERM => " (SIGTERM)",
1001 #[cfg(not(target_os = "l4re"))]
1002 libc::SIGCHLD => " (SIGCHLD)",
1003 #[cfg(not(target_os = "l4re"))]
1004 libc::SIGCONT => " (SIGCONT)",
1005 #[cfg(not(target_os = "l4re"))]
1006 libc::SIGSTOP => " (SIGSTOP)",
1007 #[cfg(not(target_os = "l4re"))]
1008 libc::SIGTSTP => " (SIGTSTP)",
1009 #[cfg(not(target_os = "l4re"))]
1010 libc::SIGTTIN => " (SIGTTIN)",
1011 #[cfg(not(target_os = "l4re"))]
1012 libc::SIGTTOU => " (SIGTTOU)",
1013 #[cfg(not(target_os = "l4re"))]
1014 libc::SIGURG => " (SIGURG)",
1015 #[cfg(not(target_os = "l4re"))]
1016 libc::SIGXCPU => " (SIGXCPU)",
1017 #[cfg(not(target_os = "l4re"))]
1018 libc::SIGXFSZ => " (SIGXFSZ)",
1019 #[cfg(not(target_os = "l4re"))]
1020 libc::SIGVTALRM => " (SIGVTALRM)",
1021 #[cfg(not(target_os = "l4re"))]
1022 libc::SIGPROF => " (SIGPROF)",
1023 #[cfg(not(target_os = "l4re"))]
1024 libc::SIGWINCH => " (SIGWINCH)",
1025 #[cfg(not(any(target_os = "haiku", target_os = "l4re")))]
1026 libc::SIGIO => " (SIGIO)",
1027 #[cfg(target_os = "haiku")]
1028 libc::SIGPOLL => " (SIGPOLL)",
1029 #[cfg(not(target_os = "l4re"))]
1030 libc::SIGSYS => " (SIGSYS)",
1031 // For information on Linux signals, run `man 7 signal`
1032 #[cfg(all(
1033 target_os = "linux",
1034 any(
1035 target_arch = "x86_64",
1036 target_arch = "x86",
1037 target_arch = "arm",
1038 target_arch = "aarch64"
1039 )
1040 ))]
1041 libc::SIGSTKFLT => " (SIGSTKFLT)",
1042 #[cfg(any(target_os = "linux", target_os = "nto"))]
1043 libc::SIGPWR => " (SIGPWR)",
1044 #[cfg(any(
1045 target_os = "macos",
1046 target_os = "ios",
1047 target_os = "tvos",
1048 target_os = "freebsd",
1049 target_os = "netbsd",
1050 target_os = "openbsd",
1051 target_os = "dragonfly",
1052 target_os = "nto",
1053 ))]
1054 libc::SIGEMT => " (SIGEMT)",
1055 #[cfg(any(
1056 target_os = "macos",
1057 target_os = "ios",
1058 target_os = "tvos",
1059 target_os = "freebsd",
1060 target_os = "netbsd",
1061 target_os = "openbsd",
1062 target_os = "dragonfly"
1063 ))]
1064 libc::SIGINFO => " (SIGINFO)",
1065 #[cfg(target_os = "hurd")]
1066 libc::SIGLOST => " (SIGLOST)",
1067 _ => "",
1068 }
1069}
1070
1071impl fmt::Display for ExitStatus {
1072 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1073 if let Some(code: i32) = self.code() {
1074 write!(f, "exit status: {code}")
1075 } else if let Some(signal: i32) = self.signal() {
1076 let signal_string: &str = signal_string(signal);
1077 if self.core_dumped() {
1078 write!(f, "signal: {signal}{signal_string} (core dumped)")
1079 } else {
1080 write!(f, "signal: {signal}{signal_string}")
1081 }
1082 } else if let Some(signal: i32) = self.stopped_signal() {
1083 let signal_string: &str = signal_string(signal);
1084 write!(f, "stopped (not terminated) by signal: {signal}{signal_string}")
1085 } else if self.continued() {
1086 write!(f, "continued (WIFCONTINUED)")
1087 } else {
1088 write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0)
1089 }
1090 }
1091}
1092
1093#[derive(PartialEq, Eq, Clone, Copy)]
1094pub struct ExitStatusError(NonZero<c_int>);
1095
1096impl Into<ExitStatus> for ExitStatusError {
1097 fn into(self) -> ExitStatus {
1098 ExitStatus(self.0.into())
1099 }
1100}
1101
1102impl fmt::Debug for ExitStatusError {
1103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1104 f.debug_tuple(name:"unix_wait_status").field(&self.0).finish()
1105 }
1106}
1107
1108impl ExitStatusError {
1109 pub fn code(self) -> Option<NonZeroI32> {
1110 ExitStatus(self.0.into()).code().map(|st: i32| st.try_into().unwrap())
1111 }
1112}
1113
1114#[cfg(target_os = "linux")]
1115#[unstable(feature = "linux_pidfd", issue = "82971")]
1116impl crate::os::linux::process::ChildExt for crate::process::Child {
1117 fn pidfd(&self) -> io::Result<&PidFd> {
1118 self.handle
1119 .pidfd
1120 .as_ref()
1121 .ok_or_else(|| Error::new(kind:ErrorKind::Uncategorized, error:"No pidfd was created."))
1122 }
1123
1124 fn take_pidfd(&mut self) -> io::Result<PidFd> {
1125 self.handle
1126 .pidfd
1127 .take()
1128 .ok_or_else(|| Error::new(kind:ErrorKind::Uncategorized, error:"No pidfd was created."))
1129 }
1130}
1131
1132#[cfg(test)]
1133#[path = "process_unix/tests.rs"]
1134mod tests;
1135
1136// See [`process_unsupported_wait_status::compare_with_linux`];
1137#[cfg(all(test, target_os = "linux"))]
1138#[path = "process_unsupported/wait_status.rs"]
1139mod process_unsupported_wait_status;
1140