1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//! macOS-only spawn primitives shared by [`super::disclaimed_output`] (the
//! capture-to-completion shape, [`capture`]), [`super::disclaimed_status`]
//! and [`super::disclaimed_spawn_detached`] (the inherited-stdio shapes,
//! [`status`]), [`super::disclaimed_piped_spawn`] (the long-lived async
//! piped shape, [`piped`]), and [`super::disclaimed_stderr_piped_spawn`]/
//! [`super::disclaimed_stdout_piped_spawn`] (the synchronous single-stream
//! piped shapes, [`stderr_piped`]/[`stdout_piped`] — issue #3267).
//!
//! Why: all five spawn shapes need the SAME low-level building blocks —
//! resolving the private disclaim SPI, reaping a pid, and (for most of the
//! five) a CLOEXEC pipe pair or the Darwin chdir file-action extension — so
//! they live here once rather than being copy-pasted into each shape's own
//! file.
//! What: [`resolve_disclaim_fn`] (dlsym lookup for
//! `responsibility_spawnattrs_setdisclaim`), [`pipe_cloexec`] (a pipe with
//! both ends `FD_CLOEXEC`), [`OwnedFd`] (an RAII raw-fd guard), and
//! [`wait_for`] (blocking `waitpid` on a specific pid). `environ` is declared
//! here since [`capture`] passes it straight through to `posix_spawnp`, and
//! `posix_spawn_file_actions_addchdir_np` since [`status`], [`piped`],
//! [`stderr_piped`], and [`stdout_piped`] all need to reproduce a
//! `Command::current_dir()`.
//! Test: exercised transitively by `capture`'s, `status`'s, `piped`'s,
//! `stderr_piped`'s, and `stdout_piped`'s own test modules; there is no
//! direct unit test of these helpers in isolation (they have no externally
//! observable behavior without a real spawn).
pub use spawn_capture_disclaimed;
pub use spawn_piped_disclaimed;
pub use ;
pub use spawn_stderr_piped_disclaimed;
pub use spawn_stdout_piped_disclaimed;
use io;
use ExitStatusExt;
use ExitStatus;
// The current process environment block, passed through to a disclaimed
// child so it inherits our environment exactly as `Command` would.
unsafe extern "C"
// `posix_spawn_file_actions_addchdir_np` — a Darwin non-portable ("_np")
// extension declared in <spawn.h> (public libSystem symbol, unlike the
// private disclaim SPI below, so it is linked directly rather than resolved
// via `dlsym`). Available since macOS 10.15; sets the child's working
// directory as a posix_spawn file action. Shared by `status` and `piped`,
// both of which need to reproduce a `Command::current_dir()` set on the
// `std::process::Command` they're given.
unsafe extern "C"
// Signature of the private libSystem SPI that marks a posix_spawn attribute
// set as "disclaim responsibility". Resolved via `dlsym` so its absence on
// any future macOS degrades gracefully to a normal spawn.
type DisclaimFn = unsafe extern "C" fn ;
/// Resolve `responsibility_spawnattrs_setdisclaim` at runtime, or `None`.
///
/// Why: the symbol is a private SPI; linking it directly would make the
/// whole binary fail to load if it ever disappeared. `dlsym` turns that into
/// a graceful "spawn without disclaim" fallback.
/// What: looks the symbol up in the global namespace (it lives in libSystem,
/// always loaded) and transmutes it to a typed fn pointer.
/// Test: covered indirectly — the capture/status/piped tests exercise the
/// whole spawn path; this returns `Some` on any TCC-era macOS.
pub
/// Reap `pid`, returning its `ExitStatus`.
///
/// Why: a spawned child must be waited on to avoid a zombie and to learn its
/// exit status. `piped` also uses this on a background `spawn_blocking` task
/// so the long-lived disclaimed child is always eventually reaped even if
/// nobody calls `wait()` explicitly.
/// What: loops over `waitpid` through `EINTR`, converting the raw wait status
/// into an `ExitStatus`.
/// Test: exercised by every `disclaimed_output_*`/`disclaimed_status_*`
/// capture test and the piped lifecycle tests.
pub
/// Create a pipe whose BOTH ends are `FD_CLOEXEC`.
///
/// Why: the read (or write) end kept by the parent must not leak into
/// unrelated spawns; the end handed to the child is re-established onto a
/// standard fd by dup2 (which clears CLOEXEC on the target), so marking the
/// sources CLOEXEC means the child keeps only its dup2'd fds, no strays.
/// What: `pipe()` then `fcntl(F_SETFD, FD_CLOEXEC)` on each end.
/// Test: exercised by the capture and piped tests (correct EOF/no-leak
/// behaviour).
pub
/// A raw fd that is closed on drop unless its value is `-1`.
///
/// Why: spawn setup has several fallible steps; an RAII guard guarantees no
/// pipe fd leaks on an early return without hand-writing close on every
/// error path.
/// What: wraps a `c_int`; `into_raw` releases ownership (sets the guard to
/// `-1`) when the fd is handed off (to a `File` or converted to a
/// `std::os::fd::OwnedFd`).
/// Test: exercised by the capture and piped tests.
pub c_int);