]> git.proxmox.com Git - rustc.git/blob - library/std/src/sys/unix/process/process_unix.rs
New upstream version 1.72.1+dfsg1
[rustc.git] / library / std / src / sys / unix / process / process_unix.rs
1 use crate::fmt;
2 use crate::io::{self, Error, ErrorKind};
3 use crate::mem;
4 use crate::num::NonZeroI32;
5 use crate::sys;
6 use crate::sys::cvt;
7 use crate::sys::process::process_common::*;
8 use core::ffi::NonZero_c_int;
9
10 #[cfg(target_os = "linux")]
11 use crate::os::linux::process::PidFd;
12
13 #[cfg(target_os = "linux")]
14 use crate::sys::weak::raw_syscall;
15
16 #[cfg(any(
17 target_os = "macos",
18 target_os = "watchos",
19 target_os = "tvos",
20 target_os = "freebsd",
21 all(target_os = "linux", target_env = "gnu"),
22 all(target_os = "linux", target_env = "musl"),
23 target_os = "nto",
24 ))]
25 use crate::sys::weak::weak;
26
27 #[cfg(target_os = "vxworks")]
28 use libc::RTP_ID as pid_t;
29
30 #[cfg(not(target_os = "vxworks"))]
31 use libc::{c_int, pid_t};
32
33 #[cfg(not(any(
34 target_os = "vxworks",
35 target_os = "l4re",
36 target_os = "tvos",
37 target_os = "watchos",
38 )))]
39 use libc::{gid_t, uid_t};
40
41 cfg_if::cfg_if! {
42 if #[cfg(all(target_os = "nto", target_env = "nto71"))] {
43 use crate::thread;
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
53 {
54 Duration::from_nanos(mindelay.tv_nsec as u64)
55 } else {
56 Duration::from_millis(1)
57 }
58 });
59 *MIN_DELAY
60 }
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);
65 }
66 }
67
68 ////////////////////////////////////////////////////////////////////////////////
69 // Command
70 ////////////////////////////////////////////////////////////////////////////////
71
72 impl Command {
73 pub fn spawn(
74 &mut self,
75 default: Stdio,
76 needs_stdin: bool,
77 ) -> io::Result<(Process, StdioPipes)> {
78 const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX";
79
80 let envp = self.capture_env();
81
82 if self.saw_nul() {
83 return Err(io::const_io_error!(
84 ErrorKind::InvalidInput,
85 "nul byte found in provided data",
86 ));
87 }
88
89 let (ours, theirs) = self.setup_io(default, needs_stdin)?;
90
91 if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
92 return Ok((ret, ours));
93 }
94 let (input, output) = sys::pipe::anon_pipe()?;
95
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.
100 //
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()? };
108
109 if pid == 0 {
110 crate::panic::always_abort();
111 mem::forget(env_lock); // avoid non-async-signal-safe unlocking
112 drop(input);
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();
116 let bytes = [
117 errno[0],
118 errno[1],
119 errno[2],
120 errno[3],
121 CLOEXEC_MSG_FOOTER[0],
122 CLOEXEC_MSG_FOOTER[1],
123 CLOEXEC_MSG_FOOTER[2],
124 CLOEXEC_MSG_FOOTER[3],
125 ];
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) }
131 }
132
133 drop(env_lock);
134 drop(output);
135
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];
140
141 // loop to handle EINTR
142 loop {
143 match input.read(&mut bytes) {
144 Ok(0) => return Ok((p, ours)),
145 Ok(8) => {
146 let (errno, footer) = bytes.split_at(4);
147 assert_eq!(
148 CLOEXEC_MSG_FOOTER, footer,
149 "Validation on the CLOEXEC pipe failed: {:?}",
150 bytes
151 );
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));
155 }
156 Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
157 Err(e) => {
158 assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
159 panic!("the CLOEXEC pipe failed: {e:?}")
160 }
161 Ok(..) => {
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")
165 }
166 }
167 }
168 }
169
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)
173 }
174
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",
185 );
186
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);
190 }
191
192 // Attempts to fork the process. If successful, returns Ok((0, -1))
193 // in the child, and Ok((child_pid, -1)) in the parent.
194 #[cfg(not(any(
195 target_os = "linux",
196 target_os = "watchos",
197 target_os = "tvos",
198 all(target_os = "nto", target_env = "nto71"),
199 )))]
200 unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
201 cvt(libc::fork()).map(|res| (res, -1))
202 }
203
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;
211
212 let mut delay = MIN_FORKSPAWN_SLEEP;
213
214 loop {
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).
219 // Yield instead.
220 thread::yield_now();
221 } else if delay < MAX_FORKSPAWN_SLEEP {
222 thread::sleep(delay);
223 } else {
224 return Err(io::const_io_error!(
225 ErrorKind::WouldBlock,
226 "forking returned EBADF too often",
227 ));
228 }
229 delay *= 2;
230 continue;
231 } else {
232 return cvt(r).map(|res| (res, -1));
233 }
234 }
235 }
236
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};
242
243 static HAS_CLONE3: AtomicBool = AtomicBool::new(true);
244 const CLONE_PIDFD: u64 = 0x00001000;
245
246 #[repr(C)]
247 struct clone_args {
248 flags: u64,
249 pidfd: u64,
250 child_tid: u64,
251 parent_tid: u64,
252 exit_signal: u64,
253 stack: u64,
254 stack_size: u64,
255 tls: u64,
256 set_tid: u64,
257 set_tid_size: u64,
258 cgroup: u64,
259 }
260
261 raw_syscall! {
262 fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long
263 }
264
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();
270
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;
274
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 {
280 flags: CLONE_PIDFD,
281 pidfd: &mut pidfd as *mut pid_t as u64,
282 child_tid: 0,
283 parent_tid: 0,
284 exit_signal: libc::SIGCHLD as u64,
285 stack: 0,
286 stack_size: 0,
287 tls: 0,
288 set_tid: 0,
289 set_tid_size: 0,
290 cgroup: 0,
291 };
292
293 let args_ptr = &mut args as *mut clone_args;
294 let args_size = crate::mem::size_of::<clone_args>();
295
296 let res = cvt(clone3(args_ptr, args_size));
297 match res {
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) => {}
307 _ => return Err(e),
308 },
309 }
310 }
311
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))
315 }
316
317 pub fn exec(&mut self, default: Stdio) -> io::Error {
318 let envp = self.capture_env();
319
320 if self.saw_nul() {
321 return io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data",);
322 }
323
324 match self.setup_io(default, true) {
325 Ok((_, theirs)) => {
326 unsafe {
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();
331
332 let Err(e) = self.do_exec(theirs, envp.as_ref());
333 e
334 }
335 }
336 Err(e) => e,
337 }
338 }
339
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
343 // scenario:
344 //
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.
350 //
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.
356 //
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.
361 //
362 // For this reason, the block of code below should contain 0
363 // invocations of either malloc of free (or their related friends).
364 //
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")))]
371 unsafe fn do_exec(
372 &mut self,
373 stdio: ChildPipes,
374 maybe_envp: Option<&CStringArray>,
375 ) -> Result<!, io::Error> {
376 use crate::sys::{self, cvt_r};
377
378 if let Some(fd) = stdio.stdin.fd() {
379 cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?;
380 }
381 if let Some(fd) = stdio.stdout.fd() {
382 cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?;
383 }
384 if let Some(fd) = stdio.stderr.fd() {
385 cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?;
386 }
387
388 #[cfg(not(target_os = "l4re"))]
389 {
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()))?;
394 }
395 if let Some(u) = self.get_gid() {
396 cvt(libc::setgid(u as gid_t))?;
397 }
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()))?;
409 }
410 cvt(libc::setuid(u as uid_t))?;
411 }
412 }
413 if let Some(ref cwd) = *self.get_cwd() {
414 cvt(libc::chdir(cwd.as_ptr()))?;
415 }
416
417 if let Some(pgroup) = self.get_pgroup() {
418 cvt(libc::setpgid(0, pgroup))?;
419 }
420
421 // emscripten has no signal support.
422 #[cfg(not(target_os = "emscripten"))]
423 {
424 // Inherit the signal mask from the parent rather than resetting it (i.e. do not call
425 // pthread_sigmask).
426
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.
429 //
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
433 {
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()))?;
437 }
438 #[cfg(not(target_os = "android"))]
439 {
440 let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
441 if ret == libc::SIG_ERR {
442 return Err(io::Error::last_os_error());
443 }
444 }
445 }
446 }
447
448 for callback in self.get_closures().iter_mut() {
449 callback()?;
450 }
451
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);
460
461 impl Drop for Reset {
462 fn drop(&mut self) {
463 unsafe {
464 *sys::os::environ() = self.0;
465 }
466 }
467 }
468
469 _reset = Some(Reset(*sys::os::environ()));
470 *sys::os::environ() = envp.as_ptr();
471 }
472
473 libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
474 Err(io::Error::last_os_error())
475 }
476
477 #[cfg(any(target_os = "tvos", target_os = "watchos"))]
478 unsafe fn do_exec(
479 &mut self,
480 _stdio: ChildPipes,
481 _maybe_envp: Option<&CStringArray>,
482 ) -> Result<!, io::Error> {
483 return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC);
484 }
485
486 #[cfg(not(any(
487 target_os = "macos",
488 target_os = "tvos",
489 target_os = "watchos",
490 target_os = "freebsd",
491 all(target_os = "linux", target_env = "gnu"),
492 all(target_os = "linux", target_env = "musl"),
493 target_os = "nto",
494 )))]
495 fn posix_spawn(
496 &mut self,
497 _: &ChildPipes,
498 _: Option<&CStringArray>,
499 ) -> io::Result<Option<Process>> {
500 Ok(None)
501 }
502
503 // Only support platforms for which posix_spawn() can return ENOENT
504 // directly.
505 #[cfg(any(
506 target_os = "macos",
507 // FIXME: `target_os = "ios"`?
508 target_os = "tvos",
509 target_os = "watchos",
510 target_os = "freebsd",
511 all(target_os = "linux", target_env = "gnu"),
512 all(target_os = "linux", target_env = "musl"),
513 target_os = "nto",
514 ))]
515 fn posix_spawn(
516 &mut self,
517 stdio: &ChildPipes,
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};
522
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()
529 {
530 return Ok(None);
531 }
532
533 // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
534 #[cfg(all(target_os = "linux", target_env = "gnu"))]
535 {
536 if let Some(version) = sys::os::glibc_version() {
537 if version < (2, 24) {
538 return Ok(None);
539 }
540 } else {
541 return Ok(None);
542 }
543 }
544
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(
551 pid: *mut pid_t,
552 file: *const c_char,
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;
559 loop {
560 match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) {
561 libc::EBADF => {
562 if delay < get_clock_resolution() {
563 // We cannot sleep this short (it would be longer).
564 // Yield instead.
565 thread::yield_now();
566 } else if delay < MAX_FORKSPAWN_SLEEP {
567 thread::sleep(delay);
568 } else {
569 return Err(io::const_io_error!(
570 ErrorKind::WouldBlock,
571 "posix_spawnp returned EBADF too often",
572 ));
573 }
574 delay *= 2;
575 continue;
576 }
577 r => {
578 return Ok(r);
579 }
580 }
581 }
582 }
583
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.
588 weak! {
589 fn posix_spawn_file_actions_addchdir_np(
590 *mut libc::posix_spawn_file_actions_t,
591 *const libc::c_char
592 ) -> libc::c_int
593 }
594 let addchdir = match self.get_cwd() {
595 Some(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 {
603 return Ok(None);
604 }
605 }
606 match posix_spawn_file_actions_addchdir_np.get() {
607 Some(f) => Some((f, cwd)),
608 None => return Ok(None),
609 }
610 }
611 None => None,
612 };
613
614 let pgroup = self.get_pgroup();
615
616 // Safety: -1 indicates we don't have a pidfd.
617 let mut p = unsafe { Process::new(0, -1) };
618
619 struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit<libc::posix_spawn_file_actions_t>);
620
621 impl Drop for PosixSpawnFileActions<'_> {
622 fn drop(&mut self) {
623 unsafe {
624 libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr());
625 }
626 }
627 }
628
629 struct PosixSpawnattr<'a>(&'a mut MaybeUninit<libc::posix_spawnattr_t>);
630
631 impl Drop for PosixSpawnattr<'_> {
632 fn drop(&mut self) {
633 unsafe {
634 libc::posix_spawnattr_destroy(self.0.as_mut_ptr());
635 }
636 }
637 }
638
639 unsafe {
640 let mut attrs = MaybeUninit::uninit();
641 cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?;
642 let attrs = PosixSpawnattr(&mut attrs);
643
644 let mut flags = 0;
645
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);
649
650 if let Some(fd) = stdio.stdin.fd() {
651 cvt_nz(libc::posix_spawn_file_actions_adddup2(
652 file_actions.0.as_mut_ptr(),
653 fd,
654 libc::STDIN_FILENO,
655 ))?;
656 }
657 if let Some(fd) = stdio.stdout.fd() {
658 cvt_nz(libc::posix_spawn_file_actions_adddup2(
659 file_actions.0.as_mut_ptr(),
660 fd,
661 libc::STDOUT_FILENO,
662 ))?;
663 }
664 if let Some(fd) = stdio.stderr.fd() {
665 cvt_nz(libc::posix_spawn_file_actions_adddup2(
666 file_actions.0.as_mut_ptr(),
667 fd,
668 libc::STDERR_FILENO,
669 ))?;
670 }
671 if let Some((f, cwd)) = addchdir {
672 cvt_nz(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?;
673 }
674
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))?;
678 }
679
680 // Inherit the signal mask from this process rather than resetting it (i.e. do not call
681 // posix_spawnattr_setsigmask).
682
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.
685 //
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(),
694 ))?;
695 flags |= libc::POSIX_SPAWN_SETSIGDEF;
696 }
697
698 cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
699
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 _);
703
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;
708
709 let spawn_res = spawn_fn(
710 &mut p.pid,
711 self.get_program_cstr().as_ptr(),
712 file_actions.0.as_ptr(),
713 attrs.0.as_ptr(),
714 self.get_argv().as_ptr() as *const _,
715 envp as *const _,
716 );
717
718 #[cfg(target_os = "nto")]
719 let spawn_res = spawn_res?;
720
721 cvt_nz(spawn_res)?;
722 Ok(Some(p))
723 }
724 }
725 }
726
727 ////////////////////////////////////////////////////////////////////////////////
728 // Processes
729 ////////////////////////////////////////////////////////////////////////////////
730
731 /// The unique ID of the process (this should never be negative).
732 pub struct Process {
733 pid: pid_t,
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>,
741 }
742
743 impl Process {
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 }
751 }
752
753 #[cfg(not(target_os = "linux"))]
754 unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self {
755 Process { pid, status: None }
756 }
757
758 pub fn id(&self) -> u32 {
759 self.pid as u32
760 }
761
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() {
767 Ok(())
768 } else {
769 cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
770 }
771 }
772
773 pub fn wait(&mut self) -> io::Result<ExitStatus> {
774 use crate::sys::cvt_r;
775 if let Some(status) = self.status {
776 return Ok(status);
777 }
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))
782 }
783
784 pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
785 if let Some(status) = self.status {
786 return Ok(Some(status));
787 }
788 let mut status = 0 as c_int;
789 let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
790 if pid == 0 {
791 Ok(None)
792 } else {
793 self.status = Some(ExitStatus::new(status));
794 Ok(Some(ExitStatus::new(status)))
795 }
796 }
797 }
798
799 /// Unix exit statuses
800 //
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);
805
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()
809 }
810 }
811
812 impl ExitStatus {
813 pub fn new(status: c_int) -> ExitStatus {
814 ExitStatus(status)
815 }
816
817 fn exited(&self) -> bool {
818 libc::WIFEXITED(self.0)
819 }
820
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(()),
830 }
831 }
832
833 pub fn code(&self) -> Option<i32> {
834 self.exited().then(|| libc::WEXITSTATUS(self.0))
835 }
836
837 pub fn signal(&self) -> Option<i32> {
838 libc::WIFSIGNALED(self.0).then(|| libc::WTERMSIG(self.0))
839 }
840
841 pub fn core_dumped(&self) -> bool {
842 libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0)
843 }
844
845 pub fn stopped_signal(&self) -> Option<i32> {
846 libc::WIFSTOPPED(self.0).then(|| libc::WSTOPSIG(self.0))
847 }
848
849 pub fn continued(&self) -> bool {
850 libc::WIFCONTINUED(self.0)
851 }
852
853 pub fn into_raw(&self) -> c_int {
854 self.0
855 }
856 }
857
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 {
861 ExitStatus(a)
862 }
863 }
864
865 /// Convert a signal number to a readable, searchable name.
866 ///
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 {
872 match signal {
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`
922 #[cfg(all(
923 target_os = "linux",
924 any(
925 target_arch = "x86_64",
926 target_arch = "x86",
927 target_arch = "arm",
928 target_arch = "aarch64"
929 )
930 ))]
931 libc::SIGSTKFLT => " (SIGSTKFLT)",
932 #[cfg(any(target_os = "linux", target_os = "nto"))]
933 libc::SIGPWR => " (SIGPWR)",
934 #[cfg(any(
935 target_os = "macos",
936 target_os = "ios",
937 target_os = "tvos",
938 target_os = "freebsd",
939 target_os = "netbsd",
940 target_os = "openbsd",
941 target_os = "dragonfly",
942 target_os = "nto",
943 ))]
944 libc::SIGEMT => " (SIGEMT)",
945 #[cfg(any(
946 target_os = "macos",
947 target_os = "ios",
948 target_os = "tvos",
949 target_os = "freebsd",
950 target_os = "netbsd",
951 target_os = "openbsd",
952 target_os = "dragonfly"
953 ))]
954 libc::SIGINFO => " (SIGINFO)",
955 _ => "",
956 }
957 }
958
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)")
967 } else {
968 write!(f, "signal: {signal}{signal_string}")
969 }
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)")
975 } else {
976 write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0)
977 }
978 }
979 }
980
981 #[derive(PartialEq, Eq, Clone, Copy)]
982 pub struct ExitStatusError(NonZero_c_int);
983
984 impl Into<ExitStatus> for ExitStatusError {
985 fn into(self) -> ExitStatus {
986 ExitStatus(self.0.into())
987 }
988 }
989
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()
993 }
994 }
995
996 impl ExitStatusError {
997 pub fn code(self) -> Option<NonZeroI32> {
998 ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
999 }
1000 }
1001
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> {
1006 self.handle
1007 .pidfd
1008 .as_ref()
1009 .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
1010 }
1011
1012 fn take_pidfd(&mut self) -> io::Result<PidFd> {
1013 self.handle
1014 .pidfd
1015 .take()
1016 .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created."))
1017 }
1018 }
1019
1020 #[cfg(test)]
1021 #[path = "process_unix/tests.rs"]
1022 mod tests;