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 mod orphan;
25 use orphan::{OrphanQueue, OrphanQueueImpl, Wait};
26
27 mod reap;
28 use reap::Reaper;
29
30 use crate::io::PollEvented;
31 use crate::process::kill::Kill;
32 use crate::process::SpawnedChild;
33 use crate::signal::unix::{signal, Signal, SignalKind};
34
35 use mio::event::Evented;
36 use mio::unix::{EventedFd, UnixReady};
37 use mio::{Poll as MioPoll, PollOpt, Ready, Token};
38 use std::fmt;
39 use std::future::Future;
40 use std::io;
41 use std::os::unix::io::{AsRawFd, RawFd};
42 use std::pin::Pin;
43 use std::process::ExitStatus;
44 use std::task::Context;
45 use std::task::Poll;
46
47 impl Wait for std::process::Child {
id(&self) -> u3248 fn id(&self) -> u32 {
49 self.id()
50 }
51
try_wait(&mut self) -> io::Result<Option<ExitStatus>>52 fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
53 self.try_wait()
54 }
55 }
56
57 impl Kill for std::process::Child {
kill(&mut self) -> io::Result<()>58 fn kill(&mut self) -> io::Result<()> {
59 self.kill()
60 }
61 }
62
63 lazy_static::lazy_static! {
64 static ref ORPHAN_QUEUE: OrphanQueueImpl<std::process::Child> = OrphanQueueImpl::new();
65 }
66
67 struct GlobalOrphanQueue;
68
69 impl fmt::Debug for GlobalOrphanQueue {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result70 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
71 ORPHAN_QUEUE.fmt(fmt)
72 }
73 }
74
75 impl OrphanQueue<std::process::Child> for GlobalOrphanQueue {
push_orphan(&self, orphan: std::process::Child)76 fn push_orphan(&self, orphan: std::process::Child) {
77 ORPHAN_QUEUE.push_orphan(orphan)
78 }
79
reap_orphans(&self)80 fn reap_orphans(&self) {
81 ORPHAN_QUEUE.reap_orphans()
82 }
83 }
84
85 #[must_use = "futures do nothing unless polled"]
86 pub(crate) struct Child {
87 inner: Reaper<std::process::Child, GlobalOrphanQueue, Signal>,
88 }
89
90 impl fmt::Debug for Child {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result91 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
92 fmt.debug_struct("Child")
93 .field("pid", &self.inner.id())
94 .finish()
95 }
96 }
97
spawn_child(cmd: &mut std::process::Command) -> io::Result<SpawnedChild>98 pub(crate) fn spawn_child(cmd: &mut std::process::Command) -> io::Result<SpawnedChild> {
99 let mut child = cmd.spawn()?;
100 let stdin = stdio(child.stdin.take())?;
101 let stdout = stdio(child.stdout.take())?;
102 let stderr = stdio(child.stderr.take())?;
103
104 let signal = signal(SignalKind::child())?;
105
106 Ok(SpawnedChild {
107 child: Child {
108 inner: Reaper::new(child, GlobalOrphanQueue, signal),
109 },
110 stdin,
111 stdout,
112 stderr,
113 })
114 }
115
116 impl Child {
id(&self) -> u32117 pub(crate) fn id(&self) -> u32 {
118 self.inner.id()
119 }
120 }
121
122 impl Kill for Child {
kill(&mut self) -> io::Result<()>123 fn kill(&mut self) -> io::Result<()> {
124 self.inner.kill()
125 }
126 }
127
128 impl Future for Child {
129 type Output = io::Result<ExitStatus>;
130
poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>131 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
132 Pin::new(&mut self.inner).poll(cx)
133 }
134 }
135
136 #[derive(Debug)]
137 pub(crate) struct Fd<T> {
138 inner: T,
139 }
140
141 impl<T> io::Read for Fd<T>
142 where
143 T: io::Read,
144 {
read(&mut self, bytes: &mut [u8]) -> io::Result<usize>145 fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
146 self.inner.read(bytes)
147 }
148 }
149
150 impl<T> io::Write for Fd<T>
151 where
152 T: io::Write,
153 {
write(&mut self, bytes: &[u8]) -> io::Result<usize>154 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
155 self.inner.write(bytes)
156 }
157
flush(&mut self) -> io::Result<()>158 fn flush(&mut self) -> io::Result<()> {
159 self.inner.flush()
160 }
161 }
162
163 impl<T> AsRawFd for Fd<T>
164 where
165 T: AsRawFd,
166 {
as_raw_fd(&self) -> RawFd167 fn as_raw_fd(&self) -> RawFd {
168 self.inner.as_raw_fd()
169 }
170 }
171
172 impl<T> Evented for Fd<T>
173 where
174 T: AsRawFd,
175 {
register( &self, poll: &MioPoll, token: Token, interest: Ready, opts: PollOpt, ) -> io::Result<()>176 fn register(
177 &self,
178 poll: &MioPoll,
179 token: Token,
180 interest: Ready,
181 opts: PollOpt,
182 ) -> io::Result<()> {
183 EventedFd(&self.as_raw_fd()).register(poll, token, interest | UnixReady::hup(), opts)
184 }
185
reregister( &self, poll: &MioPoll, token: Token, interest: Ready, opts: PollOpt, ) -> io::Result<()>186 fn reregister(
187 &self,
188 poll: &MioPoll,
189 token: Token,
190 interest: Ready,
191 opts: PollOpt,
192 ) -> io::Result<()> {
193 EventedFd(&self.as_raw_fd()).reregister(poll, token, interest | UnixReady::hup(), opts)
194 }
195
deregister(&self, poll: &MioPoll) -> io::Result<()>196 fn deregister(&self, poll: &MioPoll) -> io::Result<()> {
197 EventedFd(&self.as_raw_fd()).deregister(poll)
198 }
199 }
200
201 pub(crate) type ChildStdin = PollEvented<Fd<std::process::ChildStdin>>;
202 pub(crate) type ChildStdout = PollEvented<Fd<std::process::ChildStdout>>;
203 pub(crate) type ChildStderr = PollEvented<Fd<std::process::ChildStderr>>;
204
stdio<T>(option: Option<T>) -> io::Result<Option<PollEvented<Fd<T>>>> where T: AsRawFd,205 fn stdio<T>(option: Option<T>) -> io::Result<Option<PollEvented<Fd<T>>>>
206 where
207 T: AsRawFd,
208 {
209 let io = match option {
210 Some(io) => io,
211 None => return Ok(None),
212 };
213
214 // Set the fd to nonblocking before we pass it to the event loop
215 unsafe {
216 let fd = io.as_raw_fd();
217 let r = libc::fcntl(fd, libc::F_GETFL);
218 if r == -1 {
219 return Err(io::Error::last_os_error());
220 }
221 let r = libc::fcntl(fd, libc::F_SETFL, r | libc::O_NONBLOCK);
222 if r == -1 {
223 return Err(io::Error::last_os_error());
224 }
225 }
226 Ok(Some(PollEvented::new(Fd { inner: io })?))
227 }
228