command_group/stdlib/
child.rs

1use std::{
2	fmt,
3	io::{Read, Result},
4	process::{Child, ExitStatus, Output},
5};
6
7#[cfg(unix)]
8use unix::ChildImp;
9#[cfg(windows)]
10use windows::ChildImp;
11
12#[cfg(unix)]
13use crate::UnixChildExt;
14
15#[cfg(unix)]
16use nix::sys::signal::Signal;
17
18#[cfg(windows)]
19use winapi::um::winnt::HANDLE;
20
21#[cfg(unix)]
22mod unix;
23#[cfg(windows)]
24mod windows;
25
26/// Representation of a running or exited child process group.
27///
28/// This wraps the [`Child`] type in the standard library with methods that work
29/// with process groups.
30///
31/// # Examples
32///
33/// ```should_panic
34/// use std::process::Command;
35/// use command_group::CommandGroup;
36///
37/// let mut child = Command::new("/bin/cat")
38///                         .arg("file.txt")
39///                         .group_spawn()
40///                         .expect("failed to execute child");
41///
42/// let ecode = child.wait()
43///                  .expect("failed to wait on child");
44///
45/// assert!(ecode.success());
46/// ```
47pub struct GroupChild {
48	imp: ChildImp,
49	exitstatus: Option<ExitStatus>,
50}
51
52impl fmt::Debug for GroupChild {
53	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54		f.debug_struct("GroupChild").finish()
55	}
56}
57
58impl GroupChild {
59	#[cfg(unix)]
60	pub(crate) fn new(inner: Child) -> Self {
61		Self {
62			imp: ChildImp::new(inner),
63			exitstatus: None,
64		}
65	}
66
67	#[cfg(windows)]
68	pub(crate) fn new(inner: Child, j: HANDLE, c: HANDLE) -> Self {
69		Self {
70			imp: ChildImp::new(inner, j, c),
71			exitstatus: None,
72		}
73	}
74
75	/// Returns the stdlib [`Child`] object.
76	///
77	/// Note that the inner child may not be in the same state as this output child, due to how
78	/// methods like `wait` and `kill` are implemented. It is not recommended to use this method
79	/// _after_ using any of the other methods on this struct.
80	///
81	/// # Examples
82	///
83	/// Reading from stdout:
84	///
85	/// ```no_run
86	/// use std::io::Read;
87	/// use std::process::{Command, Stdio};
88	/// use command_group::CommandGroup;
89	///
90	/// let mut child = Command::new("ls").stdout(Stdio::piped()).group_spawn().expect("ls command didn't start");
91	/// let mut output = String::new();
92	/// if let Some(mut out) = child.inner().stdout.take() {
93	///     out.read_to_string(&mut output).expect("failed to read from child");
94	/// }
95	/// println!("output: {}", output);
96	/// ```
97	pub fn inner(&mut self) -> &mut Child {
98		self.imp.inner()
99	}
100
101	/// Consumes itself and returns the stdlib [`Child`] object.
102	///
103	/// Note that the inner child may not be in the same state as this output child, due to how
104	/// methods like `wait` and `kill` are implemented. It is not recommended to use this method
105	/// _after_ using any of the other methods on this struct.
106	///
107	#[cfg_attr(
108		windows,
109		doc = "On Windows, this unnavoidably leaves a handle unclosed. Prefer [`inner()`](Self::inner)."
110	)]
111	///
112	/// # Examples
113	///
114	/// Writing to input:
115	///
116	/// ```no_run
117	/// use std::io::Write;
118	/// use std::process::{Command, Stdio};
119	/// use command_group::CommandGroup;
120	///
121	/// let mut child = Command::new("cat").stdin(Stdio::piped()).group_spawn().expect("cat command didn't start");
122	/// if let Some(mut din) = child.into_inner().stdin.take() {
123	///      din.write_all(b"Woohoo!").expect("failed to write");
124	/// }
125	/// ```
126	pub fn into_inner(self) -> Child {
127		self.imp.into_inner()
128	}
129
130	/// Forces the child process group to exit.
131	///
132	/// If the group has already exited, an [`InvalidInput`] error is returned.
133	///
134	/// This is equivalent to sending a SIGKILL on Unix platforms.
135	///
136	/// See [the stdlib documentation](Child::kill) for more.
137	///
138	/// # Examples
139	///
140	/// Basic usage:
141	///
142	/// ```no_run
143	/// use std::process::Command;
144	/// use command_group::CommandGroup;
145	///
146	/// let mut command = Command::new("yes");
147	/// if let Ok(mut child) = command.group_spawn() {
148	///     child.kill().expect("command wasn't running");
149	/// } else {
150	///     println!("yes command didn't start");
151	/// }
152	/// ```
153	///
154	/// [`InvalidInput`]: std::io::ErrorKind::InvalidInput
155	pub fn kill(&mut self) -> Result<()> {
156		self.imp.kill()
157	}
158
159	/// Returns the OS-assigned process group identifier.
160	///
161	/// See [the stdlib documentation](Child::id) for more.
162	///
163	/// # Examples
164	///
165	/// Basic usage:
166	///
167	/// ```no_run
168	/// use std::process::Command;
169	/// use command_group::CommandGroup;
170	///
171	/// let mut command = Command::new("ls");
172	/// if let Ok(child) = command.group_spawn() {
173	///     println!("Child group's ID is {}", child.id());
174	/// } else {
175	///     println!("ls command didn't start");
176	/// }
177	/// ```
178	pub fn id(&self) -> u32 {
179		self.imp.id()
180	}
181
182	/// Waits for the child group to exit completely, returning the status that
183	/// the process leader exited with.
184	///
185	/// See [the stdlib documentation](Child::wait) for more.
186	///
187	/// # Examples
188	///
189	/// Basic usage:
190	///
191	/// ```no_run
192	/// use std::process::Command;
193	/// use command_group::CommandGroup;
194	///
195	/// let mut command = Command::new("ls");
196	/// if let Ok(mut child) = command.group_spawn() {
197	///     child.wait().expect("command wasn't running");
198	///     println!("Child has finished its execution!");
199	/// } else {
200	///     println!("ls command didn't start");
201	/// }
202	/// ```
203	pub fn wait(&mut self) -> Result<ExitStatus> {
204		if let Some(es) = self.exitstatus {
205			return Ok(es);
206		}
207
208		drop(self.imp.take_stdin());
209		let status = self.imp.wait()?;
210		self.exitstatus = Some(status);
211		Ok(status)
212	}
213
214	/// Attempts to collect the exit status of the child if it has already
215	/// exited.
216	///
217	/// See [the stdlib documentation](Child::try_wait) for more.
218	///
219	/// # Examples
220	///
221	/// Basic usage:
222	///
223	/// ```no_run
224	/// use std::process::Command;
225	/// use command_group::CommandGroup;
226	///
227	/// let mut child = Command::new("ls").group_spawn().unwrap();
228	///
229	/// match child.try_wait() {
230	///     Ok(Some(status)) => println!("exited with: {}", status),
231	///     Ok(None) => {
232	///         println!("status not ready yet, let's really wait");
233	///         let res = child.wait();
234	///         println!("result: {:?}", res);
235	///     }
236	///     Err(e) => println!("error attempting to wait: {}", e),
237	/// }
238	/// ```
239	pub fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
240		if self.exitstatus.is_some() {
241			return Ok(self.exitstatus);
242		}
243
244		match self.imp.try_wait()? {
245			Some(es) => {
246				self.exitstatus = Some(es);
247				Ok(Some(es))
248			}
249			None => Ok(None),
250		}
251	}
252
253	/// Simultaneously waits for the child to exit and collect all remaining
254	/// output on the stdout/stderr handles, returning an `Output`
255	/// instance.
256	///
257	/// See [the stdlib documentation](Child::wait_with_output) for more.
258	///
259	/// # Bugs
260	///
261	/// On Windows, STDOUT is read before STDERR if both are piped, which may block. This is mostly
262	/// because reading two outputs at the same time in synchronous code is horrendous. If you want
263	/// this, please contribute a better version. Alternatively, prefer using the async API.
264	///
265	/// # Examples
266	///
267	/// Basic usage:
268	///
269	/// ```should_panic
270	/// use std::process::{Command, Stdio};
271	/// use command_group::CommandGroup;
272	///
273	/// let child = Command::new("/bin/cat")
274	///     .arg("file.txt")
275	///     .stdout(Stdio::piped())
276	///     .group_spawn()
277	///     .expect("failed to execute child");
278	///
279	/// let output = child
280	///     .wait_with_output()
281	///     .expect("failed to wait on child");
282	///
283	/// assert!(output.status.success());
284	/// ```
285	pub fn wait_with_output(mut self) -> Result<Output> {
286		drop(self.imp.take_stdin());
287
288		let (mut stdout, mut stderr) = (Vec::new(), Vec::new());
289		match (self.imp.take_stdout(), self.imp.take_stderr()) {
290			(None, None) => {}
291			(Some(mut out), None) => {
292				out.read_to_end(&mut stdout)?;
293			}
294			(None, Some(mut err)) => {
295				err.read_to_end(&mut stderr)?;
296			}
297			(Some(out), Some(err)) => {
298				let res = ChildImp::read_both(out, &mut stdout, err, &mut stderr);
299				res.unwrap();
300			}
301		}
302
303		let status = self.imp.wait()?;
304		Ok(Output {
305			status,
306			stdout,
307			stderr,
308		})
309	}
310}
311
312#[cfg(unix)]
313impl UnixChildExt for GroupChild {
314	fn signal(&self, sig: Signal) -> Result<()> {
315		self.imp.signal_imp(sig)
316	}
317}