Skip to main content

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(windows)]
8use std::os::windows::io::{AsHandle, BorrowedHandle};
9
10#[cfg(unix)]
11use nix::{
12	sys::signal::{Signal, kill},
13	unistd::Pid,
14};
15
16crate::generic_wrap::Wrap!(Command, Child, ChildWrapper, |child| child);
17
18/// Wrapper for `std::process::Child`.
19///
20/// This trait exposes most of the functionality of the underlying [`Child`]. It is implemented for
21/// [`Child`] and by wrappers.
22///
23/// The required methods are `inner`, `inner_mut`, and `into_inner`. Together they expose each lower
24/// layer, allowing wrappers to be unwrapped and the native [`Child`] to be used directly when
25/// necessary.
26///
27/// Each non-terminal wrapper must use them to expose its direct lower layer. A terminal non-native
28/// child returns itself from all three methods. Wrapper chains must otherwise be acyclic and
29/// terminate in either a native [`Child`] or a self-returning non-native child.
30///
31/// The `try_inner_child`, `try_inner_child_mut`, and `try_into_inner_child` convenience methods on
32/// the trait object traverse these layers when access to a native [`Child`] is required.
33///
34/// It also makes it possible for all the other methods to have default implementations. Some are
35/// direct passthroughs to the lower layers, while others are more complex.
36///
37/// Here's a simple example of a wrapper:
38///
39/// ```rust
40/// use process_wrap::std::*;
41/// use std::process::Child;
42///
43/// #[derive(Debug)]
44/// pub struct YourChildWrapper(Child);
45///
46/// impl ChildWrapper for YourChildWrapper {
47///     fn inner(&self) -> &dyn ChildWrapper {
48///         &self.0
49///     }
50///
51///     fn inner_mut(&mut self) -> &mut dyn ChildWrapper {
52///         &mut self.0
53///     }
54///
55///     fn into_inner(self: Box<Self>) -> Box<dyn ChildWrapper> {
56///         Box::new((*self).0)
57///     }
58///
59///     #[cfg(windows)]
60///     fn process_handle(
61///         &self,
62///     ) -> Option<std::os::windows::io::BorrowedHandle<'_>> {
63///         self.0.process_handle()
64///     }
65/// }
66/// ```
67pub trait ChildWrapper: Any + std::fmt::Debug + Send + Sync {
68	/// Obtain a reference to the wrapped child.
69	fn inner(&self) -> &dyn ChildWrapper;
70
71	/// Obtain a mutable reference to the wrapped child.
72	fn inner_mut(&mut self) -> &mut dyn ChildWrapper;
73
74	/// Consume the current wrapper and return the wrapped child.
75	///
76	/// Note that this may disrupt whatever the current wrapper was doing. However, wrappers must
77	/// ensure that the wrapped child is in a consistent state when this is called or they are
78	/// dropped, so that this is always safe.
79	fn into_inner(self: Box<Self>) -> Box<dyn ChildWrapper>;
80
81	/// Borrow the handle for the process represented by this child, if available.
82	///
83	/// This method is only available on Windows. The returned handle cannot outlive the borrow of
84	/// `self`. Transparent child wrappers should override this method and delegate directly to the
85	/// child they own. Terminal custom children which do not represent a native process may retain the
86	/// default implementation.
87	///
88	/// Implementations returning `Some` must return a process handle, rather than another kind of
89	/// Windows object.
90	#[cfg(windows)]
91	fn process_handle(&self) -> Option<BorrowedHandle<'_>> {
92		None
93	}
94
95	/// Obtain a clone if possible.
96	///
97	/// Some implementations may make it possible to clone the implementing structure, even though
98	/// std's `Child` isn't `Clone`. In those cases, this method should be overridden.
99	fn try_clone(&self) -> Option<Box<dyn ChildWrapper>> {
100		None
101	}
102
103	/// Obtain the `Child`'s stdin.
104	///
105	/// By default this is a passthrough to the wrapped child.
106	fn stdin(&mut self) -> &mut Option<ChildStdin> {
107		self.inner_mut().stdin()
108	}
109
110	/// Obtain the `Child`'s stdout.
111	///
112	/// By default this is a passthrough to the wrapped child.
113	fn stdout(&mut self) -> &mut Option<ChildStdout> {
114		self.inner_mut().stdout()
115	}
116
117	/// Obtain the `Child`'s stderr.
118	///
119	/// By default this is a passthrough to the wrapped child.
120	fn stderr(&mut self) -> &mut Option<ChildStderr> {
121		self.inner_mut().stderr()
122	}
123
124	/// Obtain the `Child`'s process ID.
125	///
126	/// In general this should be the PID of the top-level spawned process that was spawned
127	/// However, that may vary depending on what a wrapper does.
128	fn id(&self) -> u32 {
129		self.inner().id()
130	}
131
132	/// Kill the `Child` and wait for it to exit.
133	///
134	/// By default this calls `start_kill()` and then `wait()`, which is the same way it is done on
135	/// the underlying `Child`, but that way implementing either or both of those methods will use
136	/// them when calling `kill()`, instead of requiring a stub implementation.
137	fn kill(&mut self) -> Result<()> {
138		self.start_kill()?;
139		self.wait()?;
140		Ok(())
141	}
142
143	/// Kill the `Child` without waiting for it to exit.
144	///
145	/// By default this is:
146	/// - on Unix, sending a `SIGKILL` signal to the process;
147	/// - otherwise, a passthrough to the underlying `kill()` method.
148	///
149	/// The `start_kill()` method doesn't exist on std's `Child`, and was introduced by Tokio. This
150	/// library uses it to provide a consistent API across both std and Tokio (and because it's a
151	/// generally useful API).
152	fn start_kill(&mut self) -> Result<()> {
153		self.inner_mut().start_kill()
154	}
155
156	/// Check if the `Child` has exited without blocking, and if so, return its exit status.
157	///
158	/// Wrappers must ensure that repeatedly calling this (or other wait methods) after the child
159	/// has exited will always return the same result.
160	///
161	/// By default this is a passthrough to the underlying `Child`.
162	fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
163		self.inner_mut().try_wait()
164	}
165
166	/// Wait for the `Child` to exit and return its exit status.
167	///
168	/// Wrappers must ensure that repeatedly calling this (or other wait methods) after the child
169	/// has exited will always return the same result.
170	///
171	/// By default this is a passthrough to the underlying `Child`.
172	fn wait(&mut self) -> Result<ExitStatus> {
173		self.inner_mut().wait()
174	}
175
176	/// Wait for the `Child` to exit and return its exit status and outputs.
177	///
178	/// Note that this method reads the child's stdout and stderr to completion into memory.
179	///
180	/// On Unix, this reads from stdout and stderr simultaneously. On other platforms, it reads from
181	/// stdout first, then stderr (pull requests welcome to improve this).
182	///
183	/// By default this is a reimplementation of the std method, so that it can use the wrapper's
184	/// `wait()` method instead of the underlying `Child`'s `wait()`.
185	fn wait_with_output(mut self: Box<Self>) -> Result<Output>
186	where
187		Self: 'static,
188	{
189		drop(self.stdin().take());
190
191		let (mut stdout, mut stderr) = (Vec::new(), Vec::new());
192		match (self.stdout().take(), self.stderr().take()) {
193			(None, None) => {}
194			(Some(mut out), None) => {
195				let res = out.read_to_end(&mut stdout);
196				res.unwrap();
197			}
198			(None, Some(mut err)) => {
199				let res = err.read_to_end(&mut stderr);
200				res.unwrap();
201			}
202			(Some(out), Some(err)) => {
203				let res = read2(out, &mut stdout, err, &mut stderr);
204				res.unwrap();
205			}
206		}
207
208		let status = self.wait()?;
209		Ok(Output {
210			status,
211			stdout,
212			stderr,
213		})
214	}
215
216	/// Send a signal to the `Child`.
217	///
218	/// This method is only available on Unix. It doesn't exist on std's `Child`, nor on Tokio's. It
219	/// was introduced by command-group to abstract over the signal behaviour between process groups
220	/// and unwrapped processes.
221	#[cfg(unix)]
222	fn signal(&self, sig: i32) -> Result<()> {
223		self.inner().signal(sig)
224	}
225}
226
227impl ChildWrapper for Child {
228	fn inner(&self) -> &dyn ChildWrapper {
229		self
230	}
231	fn inner_mut(&mut self) -> &mut dyn ChildWrapper {
232		self
233	}
234	fn into_inner(self: Box<Self>) -> Box<dyn ChildWrapper> {
235		self
236	}
237	#[cfg(windows)]
238	fn process_handle(&self) -> Option<BorrowedHandle<'_>> {
239		Some(self.as_handle())
240	}
241	fn stdin(&mut self) -> &mut Option<ChildStdin> {
242		&mut self.stdin
243	}
244	fn stdout(&mut self) -> &mut Option<ChildStdout> {
245		&mut self.stdout
246	}
247	fn stderr(&mut self) -> &mut Option<ChildStderr> {
248		&mut self.stderr
249	}
250	fn id(&self) -> u32 {
251		Child::id(self)
252	}
253	fn start_kill(&mut self) -> Result<()> {
254		#[cfg(unix)]
255		{
256			self.signal(Signal::SIGKILL as _)
257		}
258
259		#[cfg(not(unix))]
260		{
261			Child::kill(self)
262		}
263	}
264	fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
265		Child::try_wait(self)
266	}
267	fn wait(&mut self) -> Result<ExitStatus> {
268		Child::wait(self)
269	}
270	#[cfg(unix)]
271	fn signal(&self, sig: i32) -> Result<()> {
272		kill(
273			Pid::from_raw(i32::try_from(self.id()).map_err(std::io::Error::other)?),
274			Signal::try_from(sig)?,
275		)
276		.map_err(std::io::Error::from)
277	}
278}
279
280fn same_child(left: &dyn ChildWrapper, right: &dyn ChildWrapper) -> bool {
281	std::ptr::addr_eq(left, right) && left.type_id() == right.type_id()
282}
283
284impl dyn ChildWrapper + '_ {
285	fn downcast_ref<T: 'static>(&self) -> Option<&T> {
286		(self as &dyn Any).downcast_ref()
287	}
288
289	fn is_raw_child(&self) -> bool {
290		self.downcast_ref::<Child>().is_some()
291	}
292
293	/// Try to obtain a reference to the underlying native [`Child`].
294	///
295	/// Returns `None` if the wrapper chain terminates in a non-native child.
296	pub fn try_inner_child(&self) -> Option<&Child> {
297		let mut inner = self;
298		loop {
299			if let Some(child) = inner.downcast_ref::<Child>() {
300				return Some(child);
301			}
302
303			let next = inner.inner();
304			if same_child(inner, next) {
305				return None;
306			}
307			inner = next;
308		}
309	}
310
311	/// Try to obtain a mutable reference to the underlying native [`Child`].
312	///
313	/// Returns `None` if the wrapper chain terminates in a non-native child.
314	///
315	/// # Safety
316	///
317	/// The caller must ensure that using the returned mutable child does not violate invariants
318	/// maintained by any wrapper in the chain.
319	pub unsafe fn try_inner_child_mut(&mut self) -> Option<&mut Child> {
320		let mut inner = self;
321		loop {
322			if inner.is_raw_child() {
323				return (inner as &mut dyn Any).downcast_mut();
324			}
325
326			let inner_type = (&*inner as &dyn Any).type_id();
327			let inner_ptr = std::ptr::from_mut(inner);
328			let next = inner.inner_mut();
329			if std::ptr::addr_eq(inner_ptr, std::ptr::from_mut(next))
330				&& inner_type == (&*next as &dyn Any).type_id()
331			{
332				return None;
333			}
334			inner = next;
335		}
336	}
337
338	/// Try to consume the wrapper chain and obtain the underlying native [`Child`].
339	///
340	/// If the chain terminates in a non-native child, returns that terminal child without calling its
341	/// `into_inner` method. Wrappers already traversed before reaching it have been consumed.
342	///
343	/// # Safety
344	///
345	/// The caller must ensure that removing every traversed wrapper does not violate wrapper
346	/// invariants or bypass required cleanup. This also applies when the method returns `Err`, because
347	/// wrappers above the returned terminal child have already been consumed.
348	pub unsafe fn try_into_inner_child(self: Box<Self>) -> std::result::Result<Child, Box<Self>> {
349		let mut inner = self;
350		loop {
351			if inner.is_raw_child() {
352				return match (inner as Box<dyn Any>).downcast::<Child>() {
353					Ok(child) => Ok(*child),
354					Err(_) => unreachable!("native child type was checked before downcasting"),
355				};
356			}
357
358			let terminal = {
359				let next = inner.inner();
360				same_child(inner.as_ref(), next)
361			};
362			if terminal {
363				return Err(inner);
364			}
365			inner = inner.into_inner();
366		}
367	}
368}
369
370#[cfg(unix)]
371fn read2(
372	mut out_r: ChildStdout,
373	out_v: &mut Vec<u8>,
374	mut err_r: ChildStderr,
375	err_v: &mut Vec<u8>,
376) -> Result<()> {
377	use nix::{
378		errno::Errno,
379		libc,
380		poll::{PollFd, PollFlags, PollTimeout, poll},
381	};
382	use std::{
383		io::Error,
384		os::fd::{AsRawFd, BorrowedFd},
385	};
386
387	let out_fd = out_r.as_raw_fd();
388	let err_fd = err_r.as_raw_fd();
389	// SAFETY: these are dropped at the same time as all other FDs here
390	let out_bfd = unsafe { BorrowedFd::borrow_raw(out_fd) };
391	let err_bfd = unsafe { BorrowedFd::borrow_raw(err_fd) };
392
393	set_nonblocking(out_bfd, true)?;
394	set_nonblocking(err_bfd, true)?;
395
396	let mut fds = [
397		PollFd::new(out_bfd, PollFlags::POLLIN),
398		PollFd::new(err_bfd, PollFlags::POLLIN),
399	];
400
401	loop {
402		poll(&mut fds, PollTimeout::NONE)?;
403
404		if fds[0].revents().is_some() && read(&mut out_r, out_v)? {
405			set_nonblocking(err_bfd, false)?;
406			return err_r.read_to_end(err_v).map(drop);
407		}
408		if fds[1].revents().is_some() && read(&mut err_r, err_v)? {
409			set_nonblocking(out_bfd, false)?;
410			return out_r.read_to_end(out_v).map(drop);
411		}
412	}
413
414	fn read(r: &mut impl Read, dst: &mut Vec<u8>) -> Result<bool> {
415		match r.read_to_end(dst) {
416			Ok(_) => Ok(true),
417			Err(e) => {
418				if e.raw_os_error() == Some(libc::EWOULDBLOCK)
419					|| e.raw_os_error() == Some(libc::EAGAIN)
420				{
421					Ok(false)
422				} else {
423					Err(e)
424				}
425			}
426		}
427	}
428
429	#[cfg(target_os = "linux")]
430	fn set_nonblocking(fd: BorrowedFd, nonblocking: bool) -> Result<()> {
431		let v = nonblocking as libc::c_int;
432		let res = unsafe { libc::ioctl(fd.as_raw_fd(), libc::FIONBIO, &v) };
433
434		Errno::result(res).map_err(Error::from).map(drop)
435	}
436
437	#[cfg(not(target_os = "linux"))]
438	fn set_nonblocking(fd: BorrowedFd, nonblocking: bool) -> Result<()> {
439		use nix::fcntl::{FcntlArg, OFlag, fcntl};
440
441		let mut flags = OFlag::from_bits_truncate(fcntl(fd, FcntlArg::F_GETFL)?);
442		flags.set(OFlag::O_NONBLOCK, nonblocking);
443
444		fcntl(fd, FcntlArg::F_SETFL(flags))
445			.map_err(Error::from)
446			.map(drop)
447	}
448}
449
450// if you're reading this code and despairing, we'd love
451// your contribution of a proper read2 for your platform!
452#[cfg(not(unix))]
453fn read2(
454	mut out_r: ChildStdout,
455	out_v: &mut Vec<u8>,
456	mut err_r: ChildStderr,
457	err_v: &mut Vec<u8>,
458) -> Result<()> {
459	out_r.read_to_end(out_v)?;
460	err_r.read_to_end(err_v)?;
461	Ok(())
462}
463
464const _: () = {
465	const fn assert_sync<T: ?Sized + Sync>() {}
466	assert_sync::<dyn ChildWrapper>();
467};