process_wrap/std/core.rs
1use std::{
2 io::{Read, Result},
3 process::{Child, ChildStderr, ChildStdin, ChildStdout, Command, ExitStatus, Output},
4};
5
6#[cfg(unix)]
7use nix::{
8 sys::signal::{kill, Signal},
9 unistd::Pid,
10};
11
12crate::generic_wrap::Wrap!(
13 StdCommandWrap,
14 Command,
15 StdCommandWrapper,
16 Child,
17 StdChildWrapper,
18 StdChild // |child| StdChild(child)
19);
20
21crate::generic_wrap::MaybeAnyTrait! {
22/// Wrapper for `std::process::Child`.
23///
24/// This trait exposes most of the functionality of the underlying [`Child`]. It is implemented for
25/// [`StdChild`] (a thin wrapper around [`Child`]) (because implementing directly on [`Child`] would
26/// loop) and by wrappers.
27///
28/// The required methods are `inner`, `inner_mut`, and `into_inner`. That provides access to the
29/// underlying `Child` and allows the wrapper to be dropped and the `Child` to be used directly if
30/// necessary.
31///
32/// It also makes it possible for all the other methods to have default implementations. Some are
33/// direct passthroughs to the underlying `Child`, while others are more complex.
34///
35/// Here's a simple example of a wrapper:
36///
37/// ```rust
38/// use process_wrap::std::*;
39/// use std::process::Child;
40///
41/// #[derive(Debug)]
42/// pub struct YourChildWrapper(Child);
43///
44/// impl StdChildWrapper for YourChildWrapper {
45/// fn inner(&self) -> &Child {
46/// &self.0
47/// }
48///
49/// fn inner_mut(&mut self) -> &mut Child {
50/// &mut self.0
51/// }
52///
53/// fn into_inner(self: Box<Self>) -> Child {
54/// (*self).0
55/// }
56/// }
57/// ```
58pub trait StdChildWrapper {
59 /// Obtain a reference to the underlying `Child`.
60 fn inner(&self) -> &Child;
61
62 /// Obtain a mutable reference to the underlying `Child`.
63 fn inner_mut(&mut self) -> &mut Child;
64
65 /// Consume the wrapper and return the underlying `Child`.
66 ///
67 /// Note that this may disrupt whatever the wrappers were doing. However, wrappers must ensure
68 /// that the `Child` is in a consistent state when this is called or they are dropped, so that
69 /// this is always safe.
70 fn into_inner(self: Box<Self>) -> Child;
71
72 /// Obtain a clone if possible.
73 ///
74 /// Some implementations may make it possible to clone the implementing structure, even though
75 /// std's `Child` isn't `Clone`. In those cases, this method should be overridden.
76 fn try_clone(&self) -> Option<Box<dyn StdChildWrapper>> {
77 None
78 }
79
80 /// Obtain the `Child`'s stdin.
81 ///
82 /// By default this is a passthrough to the underlying `Child`.
83 fn stdin(&mut self) -> &mut Option<ChildStdin> {
84 &mut self.inner_mut().stdin
85 }
86
87 /// Obtain the `Child`'s stdout.
88 ///
89 /// By default this is a passthrough to the underlying `Child`.
90 fn stdout(&mut self) -> &mut Option<ChildStdout> {
91 &mut self.inner_mut().stdout
92 }
93
94 /// Obtain the `Child`'s stderr.
95 ///
96 /// By default this is a passthrough to the underlying `Child`.
97 fn stderr(&mut self) -> &mut Option<ChildStderr> {
98 &mut self.inner_mut().stderr
99 }
100
101 /// Obtain the `Child`'s process ID.
102 ///
103 /// In general this should be the PID of the top-level spawned process that was spawned
104 /// However, that may vary depending on what a wrapper does.
105 fn id(&self) -> u32 {
106 self.inner().id()
107 }
108
109 /// Kill the `Child` and wait for it to exit.
110 ///
111 /// By default this calls `start_kill()` and then `wait()`, which is the same way it is done on
112 /// the underlying `Child`, but that way implementing either or both of those methods will use
113 /// them when calling `kill()`, instead of requiring a stub implementation.
114 fn kill(&mut self) -> Result<()> {
115 self.start_kill()?;
116 self.wait()?;
117 Ok(())
118 }
119
120 /// Kill the `Child` without waiting for it to exit.
121 ///
122 /// By default this is:
123 /// - on Unix, sending a `SIGKILL` signal to the process;
124 /// - otherwise, a passthrough to the underlying `kill()` method.
125 ///
126 /// The `start_kill()` method doesn't exist on std's `Child`, and was introduced by Tokio. This
127 /// library uses it to provide a consistent API across both std and Tokio (and because it's a
128 /// generally useful API).
129 fn start_kill(&mut self) -> Result<()> {
130 #[cfg(unix)]
131 {
132 self.signal(Signal::SIGKILL as _)
133 }
134
135 #[cfg(not(unix))]
136 {
137 self.inner_mut().kill()
138 }
139 }
140
141 /// Check if the `Child` has exited without blocking, and if so, return its exit status.
142 ///
143 /// Wrappers must ensure that repeatedly calling this (or other wait methods) after the child
144 /// has exited will always return the same result.
145 ///
146 /// By default this is a passthrough to the underlying `Child`.
147 fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
148 self.inner_mut().try_wait()
149 }
150
151 /// Wait for the `Child` to exit and return its exit status.
152 ///
153 /// Wrappers must ensure that repeatedly calling this (or other wait methods) after the child
154 /// has exited will always return the same result.
155 ///
156 /// By default this is a passthrough to the underlying `Child`.
157 fn wait(&mut self) -> Result<ExitStatus> {
158 self.inner_mut().wait()
159 }
160
161 /// Wait for the `Child` to exit and return its exit status and outputs.
162 ///
163 /// Note that this method reads the child's stdout and stderr to completion into memory.
164 ///
165 /// On Unix, this reads from stdout and stderr simultaneously. On other platforms, it reads from
166 /// stdout first, then stderr (pull requests welcome to improve this).
167 ///
168 /// By default this is a reimplementation of the std method, so that it can use the wrapper's
169 /// `wait()` method instead of the underlying `Child`'s `wait()`.
170 fn wait_with_output(mut self: Box<Self>) -> Result<Output>
171 where
172 Self: 'static,
173 {
174 drop(self.stdin().take());
175
176 let (mut stdout, mut stderr) = (Vec::new(), Vec::new());
177 match (self.stdout().take(), self.stderr().take()) {
178 (None, None) => {}
179 (Some(mut out), None) => {
180 let res = out.read_to_end(&mut stdout);
181 res.unwrap();
182 }
183 (None, Some(mut err)) => {
184 let res = err.read_to_end(&mut stderr);
185 res.unwrap();
186 }
187 (Some(out), Some(err)) => {
188 let res = read2(out, &mut stdout, err, &mut stderr);
189 res.unwrap();
190 }
191 }
192
193 let status = self.wait()?;
194 Ok(Output {
195 status,
196 stdout,
197 stderr,
198 })
199 }
200
201 /// Send a signal to the `Child`.
202 ///
203 /// This method is only available on Unix. It doesn't exist on std's `Child`, nor on Tokio's. It
204 /// was introduced by command-group to abstract over the signal behaviour between process groups
205 /// and unwrapped processes.
206 #[cfg(unix)]
207 fn signal(&self, sig: i32) -> Result<()> {
208 kill(
209 Pid::from_raw(i32::try_from(self.id()).map_err(std::io::Error::other)?),
210 Signal::try_from(sig)?,
211 )
212 .map_err(std::io::Error::from)
213 }
214}
215}
216
217/// A thin wrapper around [`Child`].
218///
219/// This is used only because implementing [`StdChildWrapper`] directly on std's [`Child`] creates
220/// loops in the type system. It is not intended to be used directly, but only to be used internally
221/// by the library.
222#[derive(Debug)]
223pub struct StdChild(pub Child);
224
225impl StdChildWrapper for StdChild {
226 fn inner(&self) -> &Child {
227 &self.0
228 }
229 fn inner_mut(&mut self) -> &mut Child {
230 &mut self.0
231 }
232 fn into_inner(self: Box<Self>) -> Child {
233 (*self).0
234 }
235}
236
237#[cfg(unix)]
238fn read2(
239 mut out_r: ChildStdout,
240 out_v: &mut Vec<u8>,
241 mut err_r: ChildStderr,
242 err_v: &mut Vec<u8>,
243) -> Result<()> {
244 use nix::{
245 errno::Errno,
246 libc,
247 poll::{poll, PollFd, PollFlags, PollTimeout},
248 };
249 use std::{
250 io::Error,
251 os::fd::{AsRawFd, BorrowedFd},
252 };
253
254 let out_fd = out_r.as_raw_fd();
255 let err_fd = err_r.as_raw_fd();
256 // SAFETY: these are dropped at the same time as all other FDs here
257 let out_bfd = unsafe { BorrowedFd::borrow_raw(out_fd) };
258 let err_bfd = unsafe { BorrowedFd::borrow_raw(err_fd) };
259
260 set_nonblocking(out_bfd, true)?;
261 set_nonblocking(err_bfd, true)?;
262
263 let mut fds = [
264 PollFd::new(out_bfd, PollFlags::POLLIN),
265 PollFd::new(err_bfd, PollFlags::POLLIN),
266 ];
267
268 loop {
269 poll(&mut fds, PollTimeout::NONE)?;
270
271 if fds[0].revents().is_some() && read(&mut out_r, out_v)? {
272 set_nonblocking(err_bfd, false)?;
273 return err_r.read_to_end(err_v).map(drop);
274 }
275 if fds[1].revents().is_some() && read(&mut err_r, err_v)? {
276 set_nonblocking(out_bfd, false)?;
277 return out_r.read_to_end(out_v).map(drop);
278 }
279 }
280
281 fn read(r: &mut impl Read, dst: &mut Vec<u8>) -> Result<bool> {
282 match r.read_to_end(dst) {
283 Ok(_) => Ok(true),
284 Err(e) => {
285 if e.raw_os_error() == Some(libc::EWOULDBLOCK)
286 || e.raw_os_error() == Some(libc::EAGAIN)
287 {
288 Ok(false)
289 } else {
290 Err(e)
291 }
292 }
293 }
294 }
295
296 #[cfg(target_os = "linux")]
297 fn set_nonblocking(fd: BorrowedFd, nonblocking: bool) -> Result<()> {
298 let v = nonblocking as libc::c_int;
299 let res = unsafe { libc::ioctl(fd.as_raw_fd(), libc::FIONBIO, &v) };
300
301 Errno::result(res).map_err(Error::from).map(drop)
302 }
303
304 #[cfg(not(target_os = "linux"))]
305 fn set_nonblocking(fd: BorrowedFd, nonblocking: bool) -> Result<()> {
306 use nix::fcntl::{fcntl, FcntlArg, OFlag};
307
308 let mut flags = OFlag::from_bits_truncate(fcntl(fd, FcntlArg::F_GETFL)?);
309 flags.set(OFlag::O_NONBLOCK, nonblocking);
310
311 fcntl(fd, FcntlArg::F_SETFL(flags))
312 .map_err(Error::from)
313 .map(drop)
314 }
315}
316
317// if you're reading this code and despairing, we'd love
318// your contribution of a proper read2 for your platform!
319#[cfg(not(unix))]
320fn read2(
321 mut out_r: ChildStdout,
322 out_v: &mut Vec<u8>,
323 mut err_r: ChildStderr,
324 err_v: &mut Vec<u8>,
325) -> Result<()> {
326 out_r.read_to_end(out_v)?;
327 err_r.read_to_end(err_v)?;
328 Ok(())
329}