process_wrap/std/
core.rs

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