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}