1pub(crate) mod orphan;
25use orphan::{OrphanQueue, OrphanQueueImpl, Wait};
26
27mod reap;
28use reap::Reaper;
29
30#[cfg(all(target_os = "linux", feature = "rt"))]
31mod pidfd_reaper;
32
33use crate::io::{AsyncRead, AsyncWrite, PollEvented, ReadBuf};
34use crate::process::kill::Kill;
35use crate::process::SpawnedChild;
36use crate::runtime::signal::Handle as SignalHandle;
37use crate::signal::unix::{signal, Signal, SignalKind};
38
39use mio::event::Source;
40use mio::unix::SourceFd;
41use std::fmt;
42use std::fs::File;
43use std::future::Future;
44use std::io;
45use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
46use std::pin::Pin;
47use std::process::{Child as StdChild, ExitStatus, Stdio};
48use std::task::Context;
49use std::task::Poll;
50
51impl Wait for StdChild {
52 fn id(&self) -> u32 {
53 self.id()
54 }
55
56 fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
57 self.try_wait()
58 }
59}
60
61impl Kill for StdChild {
62 fn kill(&mut self) -> io::Result<()> {
63 self.kill()
64 }
65}
66
67cfg_not_has_const_mutex_new! {
68 fn get_orphan_queue() -> &'static OrphanQueueImpl<StdChild> {
69 use std::sync::OnceLock;
70
71 static ORPHAN_QUEUE: OnceLock<OrphanQueueImpl<StdChild>> = OnceLock::new();
72
73 ORPHAN_QUEUE.get_or_init(OrphanQueueImpl::new)
74 }
75}
76
77cfg_has_const_mutex_new! {
78 fn get_orphan_queue() -> &'static OrphanQueueImpl<StdChild> {
79 static ORPHAN_QUEUE: OrphanQueueImpl<StdChild> = OrphanQueueImpl::new();
80
81 &ORPHAN_QUEUE
82 }
83}
84
85pub(crate) struct GlobalOrphanQueue;
86
87impl fmt::Debug for GlobalOrphanQueue {
88 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
89 get_orphan_queue().fmt(fmt)
90 }
91}
92
93impl GlobalOrphanQueue {
94 pub(crate) fn reap_orphans(handle: &SignalHandle) {
95 get_orphan_queue().reap_orphans(handle);
96 }
97}
98
99impl OrphanQueue<StdChild> for GlobalOrphanQueue {
100 fn push_orphan(&self, orphan: StdChild) {
101 get_orphan_queue().push_orphan(orphan);
102 }
103}
104
105#[must_use = "futures do nothing unless polled"]
106pub(crate) enum Child {
107 SignalReaper(Reaper<StdChild, GlobalOrphanQueue, Signal>),
108 #[cfg(all(target_os = "linux", feature = "rt"))]
109 PidfdReaper(pidfd_reaper::PidfdReaper<StdChild, GlobalOrphanQueue>),
110}
111
112impl fmt::Debug for Child {
113 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
114 fmt.debug_struct("Child").field("pid", &self.id()).finish()
115 }
116}
117
118pub(crate) fn build_child(mut child: StdChild) -> io::Result<SpawnedChild> {
119 let stdin = child.stdin.take().map(stdio).transpose()?;
120 let stdout = child.stdout.take().map(stdio).transpose()?;
121 let stderr = child.stderr.take().map(stdio).transpose()?;
122
123 #[cfg(all(target_os = "linux", feature = "rt"))]
124 match pidfd_reaper::PidfdReaper::new(child, GlobalOrphanQueue) {
125 Ok(pidfd_reaper) => {
126 return Ok(SpawnedChild {
127 child: Child::PidfdReaper(pidfd_reaper),
128 stdin,
129 stdout,
130 stderr,
131 })
132 }
133 Err((Some(err), _child)) => return Err(err),
134 Err((None, child_returned)) => child = child_returned,
135 }
136
137 let signal = signal(SignalKind::child())?;
138
139 Ok(SpawnedChild {
140 child: Child::SignalReaper(Reaper::new(child, GlobalOrphanQueue, signal)),
141 stdin,
142 stdout,
143 stderr,
144 })
145}
146
147impl Child {
148 pub(crate) fn id(&self) -> u32 {
149 match self {
150 Self::SignalReaper(signal_reaper) => signal_reaper.id(),
151 #[cfg(all(target_os = "linux", feature = "rt"))]
152 Self::PidfdReaper(pidfd_reaper) => pidfd_reaper.id(),
153 }
154 }
155
156 fn std_child(&mut self) -> &mut StdChild {
157 match self {
158 Self::SignalReaper(signal_reaper) => signal_reaper.inner_mut(),
159 #[cfg(all(target_os = "linux", feature = "rt"))]
160 Self::PidfdReaper(pidfd_reaper) => pidfd_reaper.inner_mut(),
161 }
162 }
163
164 pub(crate) fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
165 self.std_child().try_wait()
166 }
167}
168
169impl Kill for Child {
170 fn kill(&mut self) -> io::Result<()> {
171 self.std_child().kill()
172 }
173}
174
175impl Future for Child {
176 type Output = io::Result<ExitStatus>;
177
178 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
179 match Pin::into_inner(self) {
180 Self::SignalReaper(signal_reaper) => Pin::new(signal_reaper).poll(cx),
181 #[cfg(all(target_os = "linux", feature = "rt"))]
182 Self::PidfdReaper(pidfd_reaper) => Pin::new(pidfd_reaper).poll(cx),
183 }
184 }
185}
186
187#[derive(Debug)]
188pub(crate) struct Pipe {
189 fd: File,
192}
193
194impl<T: IntoRawFd> From<T> for Pipe {
195 fn from(fd: T) -> Self {
196 let fd = unsafe { File::from_raw_fd(fd.into_raw_fd()) };
197 Self { fd }
198 }
199}
200
201impl<'a> io::Read for &'a Pipe {
202 fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
203 (&self.fd).read(bytes)
204 }
205}
206
207impl<'a> io::Write for &'a Pipe {
208 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
209 (&self.fd).write(bytes)
210 }
211
212 fn flush(&mut self) -> io::Result<()> {
213 (&self.fd).flush()
214 }
215
216 fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
217 (&self.fd).write_vectored(bufs)
218 }
219}
220
221impl AsRawFd for Pipe {
222 fn as_raw_fd(&self) -> RawFd {
223 self.fd.as_raw_fd()
224 }
225}
226
227impl AsFd for Pipe {
228 fn as_fd(&self) -> BorrowedFd<'_> {
229 unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
230 }
231}
232
233fn convert_to_blocking_file(io: ChildStdio) -> io::Result<File> {
234 let mut fd = io.inner.into_inner()?.fd;
235
236 set_nonblocking(&mut fd, false)?;
241
242 Ok(fd)
243}
244
245pub(crate) fn convert_to_stdio(io: ChildStdio) -> io::Result<Stdio> {
246 convert_to_blocking_file(io).map(Stdio::from)
247}
248
249impl Source for Pipe {
250 fn register(
251 &mut self,
252 registry: &mio::Registry,
253 token: mio::Token,
254 interest: mio::Interest,
255 ) -> io::Result<()> {
256 SourceFd(&self.as_raw_fd()).register(registry, token, interest)
257 }
258
259 fn reregister(
260 &mut self,
261 registry: &mio::Registry,
262 token: mio::Token,
263 interest: mio::Interest,
264 ) -> io::Result<()> {
265 SourceFd(&self.as_raw_fd()).reregister(registry, token, interest)
266 }
267
268 fn deregister(&mut self, registry: &mio::Registry) -> io::Result<()> {
269 SourceFd(&self.as_raw_fd()).deregister(registry)
270 }
271}
272
273pub(crate) struct ChildStdio {
274 inner: PollEvented<Pipe>,
275}
276
277impl ChildStdio {
278 pub(super) fn into_owned_fd(self) -> io::Result<OwnedFd> {
279 convert_to_blocking_file(self).map(OwnedFd::from)
280 }
281}
282
283impl fmt::Debug for ChildStdio {
284 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
285 self.inner.fmt(fmt)
286 }
287}
288
289impl AsRawFd for ChildStdio {
290 fn as_raw_fd(&self) -> RawFd {
291 self.inner.as_raw_fd()
292 }
293}
294
295impl AsFd for ChildStdio {
296 fn as_fd(&self) -> BorrowedFd<'_> {
297 unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
298 }
299}
300
301impl AsyncWrite for ChildStdio {
302 fn poll_write(
303 self: Pin<&mut Self>,
304 cx: &mut Context<'_>,
305 buf: &[u8],
306 ) -> Poll<io::Result<usize>> {
307 self.inner.poll_write(cx, buf)
308 }
309
310 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
311 Poll::Ready(Ok(()))
312 }
313
314 fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
315 Poll::Ready(Ok(()))
316 }
317
318 fn poll_write_vectored(
319 self: Pin<&mut Self>,
320 cx: &mut Context<'_>,
321 bufs: &[io::IoSlice<'_>],
322 ) -> Poll<Result<usize, io::Error>> {
323 self.inner.poll_write_vectored(cx, bufs)
324 }
325
326 fn is_write_vectored(&self) -> bool {
327 true
328 }
329}
330
331impl AsyncRead for ChildStdio {
332 fn poll_read(
333 self: Pin<&mut Self>,
334 cx: &mut Context<'_>,
335 buf: &mut ReadBuf<'_>,
336 ) -> Poll<io::Result<()>> {
337 unsafe { self.inner.poll_read(cx, buf) }
339 }
340}
341
342fn set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()> {
343 unsafe {
344 let fd = fd.as_raw_fd();
345 let previous = libc::fcntl(fd, libc::F_GETFL);
346 if previous == -1 {
347 return Err(io::Error::last_os_error());
348 }
349
350 let new = if nonblocking {
351 previous | libc::O_NONBLOCK
352 } else {
353 previous & !libc::O_NONBLOCK
354 };
355
356 let r = libc::fcntl(fd, libc::F_SETFL, new);
357 if r == -1 {
358 return Err(io::Error::last_os_error());
359 }
360 }
361
362 Ok(())
363}
364
365pub(super) fn stdio<T>(io: T) -> io::Result<ChildStdio>
366where
367 T: IntoRawFd,
368{
369 let mut pipe = Pipe::from(io);
371 set_nonblocking(&mut pipe, true)?;
372
373 PollEvented::new(pipe).map(|inner| ChildStdio { inner })
374}