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};