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}