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