• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, ReapOrphanQueue, 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::{signal, Signal, SignalKind};
36 
37 use mio::event::Source;
38 use mio::unix::SourceFd;
39 use once_cell::sync::Lazy;
40 use std::fmt;
41 use std::fs::File;
42 use std::future::Future;
43 use std::io;
44 use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
45 use std::pin::Pin;
46 use std::process::{Child as StdChild, ExitStatus, Stdio};
47 use std::task::Context;
48 use std::task::Poll;
49 
50 impl Wait for StdChild {
id(&self) -> u3251     fn id(&self) -> u32 {
52         self.id()
53     }
54 
try_wait(&mut self) -> io::Result<Option<ExitStatus>>55     fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
56         self.try_wait()
57     }
58 }
59 
60 impl Kill for StdChild {
kill(&mut self) -> io::Result<()>61     fn kill(&mut self) -> io::Result<()> {
62         self.kill()
63     }
64 }
65 
66 static ORPHAN_QUEUE: Lazy<OrphanQueueImpl<StdChild>> = Lazy::new(OrphanQueueImpl::new);
67 
68 pub(crate) struct GlobalOrphanQueue;
69 
70 impl fmt::Debug for GlobalOrphanQueue {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result71     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
72         ORPHAN_QUEUE.fmt(fmt)
73     }
74 }
75 
76 impl ReapOrphanQueue for GlobalOrphanQueue {
reap_orphans(&self)77     fn reap_orphans(&self) {
78         ORPHAN_QUEUE.reap_orphans()
79     }
80 }
81 
82 impl OrphanQueue<StdChild> for GlobalOrphanQueue {
push_orphan(&self, orphan: StdChild)83     fn push_orphan(&self, orphan: StdChild) {
84         ORPHAN_QUEUE.push_orphan(orphan)
85     }
86 }
87 
88 #[must_use = "futures do nothing unless polled"]
89 pub(crate) struct Child {
90     inner: Reaper<StdChild, GlobalOrphanQueue, Signal>,
91 }
92 
93 impl fmt::Debug for Child {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result94     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
95         fmt.debug_struct("Child")
96             .field("pid", &self.inner.id())
97             .finish()
98     }
99 }
100 
spawn_child(cmd: &mut std::process::Command) -> io::Result<SpawnedChild>101 pub(crate) fn spawn_child(cmd: &mut std::process::Command) -> io::Result<SpawnedChild> {
102     let mut child = cmd.spawn()?;
103     let stdin = stdio(child.stdin.take())?;
104     let stdout = stdio(child.stdout.take())?;
105     let stderr = stdio(child.stderr.take())?;
106 
107     let signal = signal(SignalKind::child())?;
108 
109     Ok(SpawnedChild {
110         child: Child {
111             inner: Reaper::new(child, GlobalOrphanQueue, signal),
112         },
113         stdin,
114         stdout,
115         stderr,
116     })
117 }
118 
119 impl Child {
id(&self) -> u32120     pub(crate) fn id(&self) -> u32 {
121         self.inner.id()
122     }
123 
try_wait(&mut self) -> io::Result<Option<ExitStatus>>124     pub(crate) fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
125         self.inner.inner_mut().try_wait()
126     }
127 }
128 
129 impl Kill for Child {
kill(&mut self) -> io::Result<()>130     fn kill(&mut self) -> io::Result<()> {
131         self.inner.kill()
132     }
133 }
134 
135 impl Future for Child {
136     type Output = io::Result<ExitStatus>;
137 
poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>138     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
139         Pin::new(&mut self.inner).poll(cx)
140     }
141 }
142 
143 #[derive(Debug)]
144 pub(crate) struct Pipe {
145     // Actually a pipe and not a File. However, we are reusing `File` to get
146     // close on drop. This is a similar trick as `mio`.
147     fd: File,
148 }
149 
150 impl<T: IntoRawFd> From<T> for Pipe {
from(fd: T) -> Self151     fn from(fd: T) -> Self {
152         let fd = unsafe { File::from_raw_fd(fd.into_raw_fd()) };
153         Self { fd }
154     }
155 }
156 
157 impl<'a> io::Read for &'a Pipe {
read(&mut self, bytes: &mut [u8]) -> io::Result<usize>158     fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
159         (&self.fd).read(bytes)
160     }
161 }
162 
163 impl<'a> io::Write for &'a Pipe {
write(&mut self, bytes: &[u8]) -> io::Result<usize>164     fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
165         (&self.fd).write(bytes)
166     }
167 
flush(&mut self) -> io::Result<()>168     fn flush(&mut self) -> io::Result<()> {
169         (&self.fd).flush()
170     }
171 }
172 
173 impl AsRawFd for Pipe {
as_raw_fd(&self) -> RawFd174     fn as_raw_fd(&self) -> RawFd {
175         self.fd.as_raw_fd()
176     }
177 }
178 
convert_to_stdio(io: PollEvented<Pipe>) -> io::Result<Stdio>179 pub(crate) fn convert_to_stdio(io: PollEvented<Pipe>) -> io::Result<Stdio> {
180     let mut fd = io.into_inner()?.fd;
181 
182     // Ensure that the fd to be inherited is set to *blocking* mode, as this
183     // is the default that virtually all programs expect to have. Those
184     // programs that know how to work with nonblocking stdio will know how to
185     // change it to nonblocking mode.
186     set_nonblocking(&mut fd, false)?;
187 
188     Ok(Stdio::from(fd))
189 }
190 
191 impl Source for Pipe {
register( &mut self, registry: &mio::Registry, token: mio::Token, interest: mio::Interest, ) -> io::Result<()>192     fn register(
193         &mut self,
194         registry: &mio::Registry,
195         token: mio::Token,
196         interest: mio::Interest,
197     ) -> io::Result<()> {
198         SourceFd(&self.as_raw_fd()).register(registry, token, interest)
199     }
200 
reregister( &mut self, registry: &mio::Registry, token: mio::Token, interest: mio::Interest, ) -> io::Result<()>201     fn reregister(
202         &mut self,
203         registry: &mio::Registry,
204         token: mio::Token,
205         interest: mio::Interest,
206     ) -> io::Result<()> {
207         SourceFd(&self.as_raw_fd()).reregister(registry, token, interest)
208     }
209 
deregister(&mut self, registry: &mio::Registry) -> io::Result<()>210     fn deregister(&mut self, registry: &mio::Registry) -> io::Result<()> {
211         SourceFd(&self.as_raw_fd()).deregister(registry)
212     }
213 }
214 
215 pub(crate) type ChildStdin = PollEvented<Pipe>;
216 pub(crate) type ChildStdout = PollEvented<Pipe>;
217 pub(crate) type ChildStderr = PollEvented<Pipe>;
218 
set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()>219 fn set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()> {
220     unsafe {
221         let fd = fd.as_raw_fd();
222         let previous = libc::fcntl(fd, libc::F_GETFL);
223         if previous == -1 {
224             return Err(io::Error::last_os_error());
225         }
226 
227         let new = if nonblocking {
228             previous | libc::O_NONBLOCK
229         } else {
230             previous & !libc::O_NONBLOCK
231         };
232 
233         let r = libc::fcntl(fd, libc::F_SETFL, new);
234         if r == -1 {
235             return Err(io::Error::last_os_error());
236         }
237     }
238 
239     Ok(())
240 }
241 
stdio<T>(option: Option<T>) -> io::Result<Option<PollEvented<Pipe>>> where T: IntoRawFd,242 fn stdio<T>(option: Option<T>) -> io::Result<Option<PollEvented<Pipe>>>
243 where
244     T: IntoRawFd,
245 {
246     let io = match option {
247         Some(io) => io,
248         None => return Ok(None),
249     };
250 
251     // Set the fd to nonblocking before we pass it to the event loop
252     let mut pipe = Pipe::from(io);
253     set_nonblocking(&mut pipe, true)?;
254 
255     Ok(Some(PollEvented::new(pipe)?))
256 }
257