1 //! Unix handling of child processes.
2 //!
3 //! Right now the only "fancy" thing about this is how we implement the
4 //! `Future` implementation on `Child` to get the exit status. Unix offers
5 //! no way to register a child with epoll, and the only real way to get a
6 //! notification when a process exits is the SIGCHLD signal.
7 //!
8 //! Signal handling in general is *super* hairy and complicated, and it's even
9 //! more complicated here with the fact that signals are coalesced, so we may
10 //! not get a SIGCHLD-per-child.
11 //!
12 //! Our best approximation here is to check *all spawned processes* for all
13 //! SIGCHLD signals received. To do that we create a `Signal`, implemented in
14 //! the `tokio-net` crate, which is a stream over signals being received.
15 //!
16 //! Later when we poll the process's exit status we simply check to see if a
17 //! SIGCHLD has happened since we last checked, and while that returns "yes" we
18 //! keep trying.
19 //!
20 //! Note that this means that this isn't really scalable, but then again
21 //! processes in general aren't scalable (e.g. millions) so it shouldn't be that
22 //! bad in theory...
23 
24 pub(crate) mod driver;
25 
26 pub(crate) mod orphan;
27 use orphan::{OrphanQueue, OrphanQueueImpl, Wait};
28 
29 mod reap;
30 use reap::Reaper;
31 
32 use crate::io::PollEvented;
33 use crate::process::kill::Kill;
34 use crate::process::SpawnedChild;
35 use crate::signal::unix::driver::Handle as SignalHandle;
36 use crate::signal::unix::{signal, Signal, SignalKind};
37 
38 use mio::event::Source;
39 use mio::unix::SourceFd;
40 use once_cell::sync::Lazy;
41 use std::fmt;
42 use std::fs::File;
43 use std::future::Future;
44 use std::io;
45 use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
46 use std::pin::Pin;
47 use std::process::{Child as StdChild, ExitStatus, Stdio};
48 use std::task::Context;
49 use std::task::Poll;
50 
51 impl Wait for StdChild {
id(&self) -> u3252     fn id(&self) -> u32 {
53         self.id()
54     }
55 
try_wait(&mut self) -> io::Result<Option<ExitStatus>>56     fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
57         self.try_wait()
58     }
59 }
60 
61 impl Kill for StdChild {
kill(&mut self) -> io::Result<()>62     fn kill(&mut self) -> io::Result<()> {
63         self.kill()
64     }
65 }
66 
67 static ORPHAN_QUEUE: Lazy<OrphanQueueImpl<StdChild>> = Lazy::new(OrphanQueueImpl::new);
68 
69 pub(crate) struct GlobalOrphanQueue;
70 
71 impl fmt::Debug for GlobalOrphanQueue {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result72     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
73         ORPHAN_QUEUE.fmt(fmt)
74     }
75 }
76 
77 impl GlobalOrphanQueue {
reap_orphans(handle: &SignalHandle)78     fn reap_orphans(handle: &SignalHandle) {
79         ORPHAN_QUEUE.reap_orphans(handle)
80     }
81 }
82 
83 impl OrphanQueue<StdChild> for GlobalOrphanQueue {
push_orphan(&self, orphan: StdChild)84     fn push_orphan(&self, orphan: StdChild) {
85         ORPHAN_QUEUE.push_orphan(orphan)
86     }
87 }
88 
89 #[must_use = "futures do nothing unless polled"]
90 pub(crate) struct Child {
91     inner: Reaper<StdChild, GlobalOrphanQueue, Signal>,
92 }
93 
94 impl fmt::Debug for Child {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result95     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
96         fmt.debug_struct("Child")
97             .field("pid", &self.inner.id())
98             .finish()
99     }
100 }
101 
spawn_child(cmd: &mut std::process::Command) -> io::Result<SpawnedChild>102 pub(crate) fn spawn_child(cmd: &mut std::process::Command) -> io::Result<SpawnedChild> {
103     let mut child = cmd.spawn()?;
104     let stdin = child.stdin.take().map(stdio).transpose()?;
105     let stdout = child.stdout.take().map(stdio).transpose()?;
106     let stderr = child.stderr.take().map(stdio).transpose()?;
107 
108     let signal = signal(SignalKind::child())?;
109 
110     Ok(SpawnedChild {
111         child: Child {
112             inner: Reaper::new(child, GlobalOrphanQueue, signal),
113         },
114         stdin,
115         stdout,
116         stderr,
117     })
118 }
119 
120 impl Child {
id(&self) -> u32121     pub(crate) fn id(&self) -> u32 {
122         self.inner.id()
123     }
124 
try_wait(&mut self) -> io::Result<Option<ExitStatus>>125     pub(crate) fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
126         self.inner.inner_mut().try_wait()
127     }
128 }
129 
130 impl Kill for Child {
kill(&mut self) -> io::Result<()>131     fn kill(&mut self) -> io::Result<()> {
132         self.inner.kill()
133     }
134 }
135 
136 impl Future for Child {
137     type Output = io::Result<ExitStatus>;
138 
poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>139     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
140         Pin::new(&mut self.inner).poll(cx)
141     }
142 }
143 
144 #[derive(Debug)]
145 pub(crate) struct Pipe {
146     // Actually a pipe and not a File. However, we are reusing `File` to get
147     // close on drop. This is a similar trick as `mio`.
148     fd: File,
149 }
150 
151 impl<T: IntoRawFd> From<T> for Pipe {
from(fd: T) -> Self152     fn from(fd: T) -> Self {
153         let fd = unsafe { File::from_raw_fd(fd.into_raw_fd()) };
154         Self { fd }
155     }
156 }
157 
158 impl<'a> io::Read for &'a Pipe {
read(&mut self, bytes: &mut [u8]) -> io::Result<usize>159     fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
160         (&self.fd).read(bytes)
161     }
162 }
163 
164 impl<'a> io::Write for &'a Pipe {
write(&mut self, bytes: &[u8]) -> io::Result<usize>165     fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
166         (&self.fd).write(bytes)
167     }
168 
flush(&mut self) -> io::Result<()>169     fn flush(&mut self) -> io::Result<()> {
170         (&self.fd).flush()
171     }
172 }
173 
174 impl AsRawFd for Pipe {
as_raw_fd(&self) -> RawFd175     fn as_raw_fd(&self) -> RawFd {
176         self.fd.as_raw_fd()
177     }
178 }
179 
convert_to_stdio(io: PollEvented<Pipe>) -> io::Result<Stdio>180 pub(crate) fn convert_to_stdio(io: PollEvented<Pipe>) -> io::Result<Stdio> {
181     let mut fd = io.into_inner()?.fd;
182 
183     // Ensure that the fd to be inherited is set to *blocking* mode, as this
184     // is the default that virtually all programs expect to have. Those
185     // programs that know how to work with nonblocking stdio will know how to
186     // change it to nonblocking mode.
187     set_nonblocking(&mut fd, false)?;
188 
189     Ok(Stdio::from(fd))
190 }
191 
192 impl Source for Pipe {
register( &mut self, registry: &mio::Registry, token: mio::Token, interest: mio::Interest, ) -> io::Result<()>193     fn register(
194         &mut self,
195         registry: &mio::Registry,
196         token: mio::Token,
197         interest: mio::Interest,
198     ) -> io::Result<()> {
199         SourceFd(&self.as_raw_fd()).register(registry, token, interest)
200     }
201 
reregister( &mut self, registry: &mio::Registry, token: mio::Token, interest: mio::Interest, ) -> io::Result<()>202     fn reregister(
203         &mut self,
204         registry: &mio::Registry,
205         token: mio::Token,
206         interest: mio::Interest,
207     ) -> io::Result<()> {
208         SourceFd(&self.as_raw_fd()).reregister(registry, token, interest)
209     }
210 
deregister(&mut self, registry: &mio::Registry) -> io::Result<()>211     fn deregister(&mut self, registry: &mio::Registry) -> io::Result<()> {
212         SourceFd(&self.as_raw_fd()).deregister(registry)
213     }
214 }
215 
216 pub(crate) type ChildStdio = PollEvented<Pipe>;
217 
set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()>218 fn set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()> {
219     unsafe {
220         let fd = fd.as_raw_fd();
221         let previous = libc::fcntl(fd, libc::F_GETFL);
222         if previous == -1 {
223             return Err(io::Error::last_os_error());
224         }
225 
226         let new = if nonblocking {
227             previous | libc::O_NONBLOCK
228         } else {
229             previous & !libc::O_NONBLOCK
230         };
231 
232         let r = libc::fcntl(fd, libc::F_SETFL, new);
233         if r == -1 {
234             return Err(io::Error::last_os_error());
235         }
236     }
237 
238     Ok(())
239 }
240 
stdio<T>(io: T) -> io::Result<PollEvented<Pipe>> where T: IntoRawFd,241 pub(super) fn stdio<T>(io: T) -> io::Result<PollEvented<Pipe>>
242 where
243     T: IntoRawFd,
244 {
245     // Set the fd to nonblocking before we pass it to the event loop
246     let mut pipe = Pipe::from(io);
247     set_nonblocking(&mut pipe, true)?;
248 
249     PollEvented::new(pipe)
250 }
251