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