Skip to main content

process_wrap/tokio/
core.rs

1use std::{
2	any::Any,
3	future::Future,
4	io::Result,
5	pin::Pin,
6	process::{ExitStatus, Output},
7};
8
9#[cfg(windows)]
10use std::os::windows::io::BorrowedHandle;
11
12use futures::future::try_join3;
13#[cfg(unix)]
14use nix::{
15	sys::signal::{Signal, kill},
16	unistd::Pid,
17};
18use tokio::{
19	io::{AsyncRead, AsyncReadExt},
20	process::{Child, ChildStderr, ChildStdin, ChildStdout, Command},
21};
22
23crate::generic_wrap::Wrap!(Command, Child, ChildWrapper, |child| child);
24
25/// Wrapper for `tokio::process::Child`.
26///
27/// This trait exposes most of the functionality of the underlying [`Child`]. It is implemented for
28/// [`Child`] and by wrappers.
29///
30/// The required methods are `inner`, `inner_mut`, and `into_inner`. Together they expose each lower
31/// layer, allowing wrappers to be unwrapped and the native [`Child`] to be used directly when
32/// necessary.
33///
34/// Each non-terminal wrapper must use them to expose its direct lower layer. A terminal non-native
35/// child returns itself from all three methods. Wrapper chains must otherwise be acyclic and
36/// terminate in either a native [`Child`] or a self-returning non-native child.
37///
38/// The `try_inner_child`, `try_inner_child_mut`, and `try_into_inner_child` convenience methods on
39/// the trait object traverse these layers when access to a native [`Child`] is required.
40///
41/// It also makes it possible for all the other methods to have default implementations. Some are
42/// direct passthroughs to the underlying `Child`, while others are more complex.
43///
44/// Here's a simple example of a wrapper:
45///
46/// ```rust
47/// use process_wrap::tokio::*;
48/// use tokio::process::Child;
49///
50/// #[derive(Debug)]
51/// pub struct YourChildWrapper(Child);
52///
53/// impl ChildWrapper for YourChildWrapper {
54///     fn inner(&self) -> &dyn ChildWrapper {
55///         &self.0
56///     }
57///
58///     fn inner_mut(&mut self) -> &mut dyn ChildWrapper {
59///         &mut self.0
60///     }
61///
62///     fn into_inner(self: Box<Self>) -> Box<dyn ChildWrapper> {
63///         Box::new((*self).0)
64///     }
65///
66///     #[cfg(windows)]
67///     fn process_handle(
68///         &self,
69///     ) -> Option<std::os::windows::io::BorrowedHandle<'_>> {
70///         self.0.process_handle()
71///     }
72/// }
73/// ```
74pub trait ChildWrapper: Any + std::fmt::Debug + Send + Sync {
75	/// Obtain a reference to the wrapped child.
76	fn inner(&self) -> &dyn ChildWrapper;
77
78	/// Obtain a mutable reference to the wrapped child.
79	fn inner_mut(&mut self) -> &mut dyn ChildWrapper;
80
81	/// Consume the current wrapper and return the wrapped child.
82	///
83	/// Note that this may disrupt whatever the current wrapper was doing. However, wrappers must
84	/// ensure that the wrapped child is in a consistent state when this is called or they are
85	/// dropped, so that this is always safe.
86	fn into_inner(self: Box<Self>) -> Box<dyn ChildWrapper>;
87
88	/// Borrow the handle for the process represented by this child, if available.
89	///
90	/// This method is only available on Windows. The returned handle cannot outlive the borrow of
91	/// `self`. Transparent child wrappers should override this method and delegate directly to the
92	/// child they own. Terminal custom children which do not represent a native process may retain the
93	/// default implementation.
94	///
95	/// Implementations returning `Some` must return a process handle, rather than another kind of
96	/// Windows object.
97	#[cfg(windows)]
98	fn process_handle(&self) -> Option<BorrowedHandle<'_>> {
99		None
100	}
101
102	/// Obtain a clone if possible.
103	///
104	/// Some implementations may make it possible to clone the implementing structure, even though
105	/// Tokio's `Child` isn't `Clone`. In those cases, this method should be overridden.
106	fn try_clone(&self) -> Option<Box<dyn ChildWrapper>> {
107		None
108	}
109
110	/// Obtain the `Child`'s stdin.
111	///
112	/// By default this is a passthrough to the wrapped child.
113	fn stdin(&mut self) -> &mut Option<ChildStdin> {
114		self.inner_mut().stdin()
115	}
116
117	/// Obtain the `Child`'s stdout.
118	///
119	/// By default this is a passthrough to the wrapped child.
120	fn stdout(&mut self) -> &mut Option<ChildStdout> {
121		self.inner_mut().stdout()
122	}
123
124	/// Obtain the `Child`'s stderr.
125	///
126	/// By default this is a passthrough to the wrapped child.
127	fn stderr(&mut self) -> &mut Option<ChildStderr> {
128		self.inner_mut().stderr()
129	}
130
131	/// Obtain the `Child`'s process ID.
132	///
133	/// In general this should be the PID of the top-level spawned process that was spawned
134	/// However, that may vary depending on what a wrapper does.
135	///
136	/// Returns an `Option` to resemble Tokio's API, but isn't expected to be `None` in practice.
137	fn id(&self) -> Option<u32> {
138		self.inner().id()
139	}
140
141	/// Kill the `Child` and wait for it to exit.
142	///
143	/// By default this calls `start_kill()` and then `wait()`, which is the same way it is done on
144	/// the underlying `Child`, but that way implementing either or both of those methods will use
145	/// them when calling `kill()`, instead of requiring a stub implementation.
146	fn kill(&mut self) -> Box<dyn Future<Output = Result<()>> + Send + '_> {
147		Box::new(async {
148			self.start_kill()?;
149			self.wait().await?;
150			Ok(())
151		})
152	}
153
154	/// Kill the `Child` without waiting for it to exit.
155	///
156	/// By default this is a passthrough to the underlying `Child`, which:
157	/// - on Unix, sends a `SIGKILL` signal to the process;
158	/// - otherwise, passes through to the `kill()` method.
159	fn start_kill(&mut self) -> Result<()> {
160		self.inner_mut().start_kill()
161	}
162
163	/// Check if the `Child` has exited without waiting, and if it has, return its exit status.
164	///
165	/// Wrappers must ensure that repeatedly calling this (or other wait methods) after the child
166	/// has exited will always return the same result.
167	///
168	/// By default this is a passthrough to the underlying `Child`.
169	fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
170		self.inner_mut().try_wait()
171	}
172
173	/// Wait for the `Child` to exit and return its exit status.
174	///
175	/// Wrappers must ensure that repeatedly calling this (or other wait methods) after the child
176	/// has exited will always return the same result.
177	///
178	/// By default this is a passthrough to the underlying `Child`.
179	fn wait(&mut self) -> Pin<Box<dyn Future<Output = Result<ExitStatus>> + Send + '_>> {
180		Box::pin(self.inner_mut().wait())
181	}
182
183	/// Wait for the `Child` to exit and return its exit status and outputs.
184	///
185	/// Note that this method reads the child's stdout and stderr to completion into memory.
186	///
187	/// By default this is a reimplementation of the Tokio method, so that it can use the wrapper's
188	/// `wait()` method instead of the underlying `Child`'s `wait()`.
189	fn wait_with_output(mut self: Box<Self>) -> Box<dyn Future<Output = Result<Output>> + Send>
190	where
191		Self: 'static,
192	{
193		Box::new(async move {
194			async fn read_to_end<A: AsyncRead + Unpin>(io: &mut Option<A>) -> Result<Vec<u8>> {
195				let mut vec = Vec::new();
196				if let Some(io) = io.as_mut() {
197					io.read_to_end(&mut vec).await?;
198				}
199				Ok(vec)
200			}
201
202			let mut stdout_pipe = self.stdout().take();
203			let mut stderr_pipe = self.stderr().take();
204
205			let stdout_fut = read_to_end(&mut stdout_pipe);
206			let stderr_fut = read_to_end(&mut stderr_pipe);
207
208			let (status, stdout, stderr) = try_join3(self.wait(), stdout_fut, stderr_fut).await?;
209
210			// Drop happens after `try_join` due to <https://github.com/tokio-rs/tokio/issues/4309>
211			drop(stdout_pipe);
212			drop(stderr_pipe);
213
214			Ok(Output {
215				status,
216				stdout,
217				stderr,
218			})
219		})
220	}
221
222	/// Send a signal to the `Child`.
223	///
224	/// This method is only available on Unix. It doesn't exist on Tokio's `Child`, nor on std's. It
225	/// was introduced by command-group to abstract over the signal behaviour between process groups
226	/// and unwrapped processes.
227	#[cfg(unix)]
228	fn signal(&self, sig: i32) -> Result<()> {
229		self.inner().signal(sig)
230	}
231}
232
233impl ChildWrapper for Child {
234	fn inner(&self) -> &dyn ChildWrapper {
235		self
236	}
237	fn inner_mut(&mut self) -> &mut dyn ChildWrapper {
238		self
239	}
240	fn into_inner(self: Box<Self>) -> Box<dyn ChildWrapper> {
241		self
242	}
243	#[cfg(windows)]
244	fn process_handle(&self) -> Option<BorrowedHandle<'_>> {
245		let handle = self.raw_handle()?;
246		// SAFETY: `raw_handle` returns the handle owned by `self`, and the returned borrow cannot
247		// outlive `self`.
248		Some(unsafe { BorrowedHandle::borrow_raw(handle) })
249	}
250	fn stdin(&mut self) -> &mut Option<ChildStdin> {
251		&mut self.stdin
252	}
253	fn stdout(&mut self) -> &mut Option<ChildStdout> {
254		&mut self.stdout
255	}
256	fn stderr(&mut self) -> &mut Option<ChildStderr> {
257		&mut self.stderr
258	}
259	fn id(&self) -> Option<u32> {
260		Child::id(self)
261	}
262	fn start_kill(&mut self) -> Result<()> {
263		Child::start_kill(self)
264	}
265	fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
266		Child::try_wait(self)
267	}
268	fn wait(&mut self) -> Pin<Box<dyn Future<Output = Result<ExitStatus>> + Send + '_>> {
269		Box::pin(Child::wait(self))
270	}
271	#[cfg(unix)]
272	fn signal(&self, sig: i32) -> Result<()> {
273		if let Some(id) = self.id() {
274			kill(
275				Pid::from_raw(i32::try_from(id).map_err(std::io::Error::other)?),
276				Signal::try_from(sig)?,
277			)
278			.map_err(std::io::Error::from)
279		} else {
280			Ok(())
281		}
282	}
283}
284
285fn same_child(left: &dyn ChildWrapper, right: &dyn ChildWrapper) -> bool {
286	std::ptr::addr_eq(left, right) && left.type_id() == right.type_id()
287}
288
289impl dyn ChildWrapper + '_ {
290	fn downcast_ref<T: 'static>(&self) -> Option<&T> {
291		(self as &dyn Any).downcast_ref()
292	}
293
294	fn is_raw_child(&self) -> bool {
295		self.downcast_ref::<Child>().is_some()
296	}
297
298	/// Try to obtain a reference to the underlying native [`Child`].
299	///
300	/// Returns `None` if the wrapper chain terminates in a non-native child.
301	pub fn try_inner_child(&self) -> Option<&Child> {
302		let mut inner = self;
303		loop {
304			if let Some(child) = inner.downcast_ref::<Child>() {
305				return Some(child);
306			}
307
308			let next = inner.inner();
309			if same_child(inner, next) {
310				return None;
311			}
312			inner = next;
313		}
314	}
315
316	/// Try to obtain a mutable reference to the underlying native [`Child`].
317	///
318	/// Returns `None` if the wrapper chain terminates in a non-native child.
319	///
320	/// # Safety
321	///
322	/// The caller must ensure that using the returned mutable child does not violate invariants
323	/// maintained by any wrapper in the chain.
324	pub unsafe fn try_inner_child_mut(&mut self) -> Option<&mut Child> {
325		let mut inner = self;
326		loop {
327			if inner.is_raw_child() {
328				return (inner as &mut dyn Any).downcast_mut();
329			}
330
331			let inner_type = (&*inner as &dyn Any).type_id();
332			let inner_ptr = std::ptr::from_mut(inner);
333			let next = inner.inner_mut();
334			if std::ptr::addr_eq(inner_ptr, std::ptr::from_mut(next))
335				&& inner_type == (&*next as &dyn Any).type_id()
336			{
337				return None;
338			}
339			inner = next;
340		}
341	}
342
343	/// Try to consume the wrapper chain and obtain the underlying native [`Child`].
344	///
345	/// If the chain terminates in a non-native child, returns that terminal child without calling its
346	/// `into_inner` method. Wrappers already traversed before reaching it have been consumed.
347	///
348	/// # Safety
349	///
350	/// The caller must ensure that removing every traversed wrapper does not violate wrapper
351	/// invariants or bypass required cleanup. This also applies when the method returns `Err`, because
352	/// wrappers above the returned terminal child have already been consumed.
353	pub unsafe fn try_into_inner_child(self: Box<Self>) -> std::result::Result<Child, Box<Self>> {
354		let mut inner = self;
355		loop {
356			if inner.is_raw_child() {
357				return match (inner as Box<dyn Any>).downcast::<Child>() {
358					Ok(child) => Ok(*child),
359					Err(_) => unreachable!("native child type was checked before downcasting"),
360				};
361			}
362
363			let terminal = {
364				let next = inner.inner();
365				same_child(inner.as_ref(), next)
366			};
367			if terminal {
368				return Err(inner);
369			}
370			inner = inner.into_inner();
371		}
372	}
373}
374
375const _: () = {
376	const fn assert_sync<T: ?Sized + Sync>() {}
377	assert_sync::<dyn ChildWrapper>();
378};