duct/lib.rs
1//! Duct is a library for running child processes. Duct makes it easy to build
2//! pipelines and redirect IO like a shell. At the same time, Duct helps you
3//! write correct, portable code: whitespace is never significant, errors from
4//! child processes get reported by default, and a variety of [gotchas, bugs,
5//! and platform
6//! inconsistencies](https://github.com/oconnor663/duct.py/blob/master/gotchas.md)
7//! are handled for you the Right Way™.
8//!
9//! - [Documentation](https://docs.rs/duct)
10//! - [Crate](https://crates.io/crates/duct)
11//! - [GitHub repo](https://github.com/oconnor663/duct.rs)
12//! - [the same library, in Python](https://github.com/oconnor663/duct.py)
13//!
14//! Examples
15//! --------
16//!
17//! Run a command without capturing any output. Here "hi" is printed directly
18//! to the terminal:
19//!
20//! ```
21//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
22//! # if cfg!(not(windows)) {
23//! use duct::cmd;
24//! cmd!("echo", "hi").run()?;
25//! # }
26//! # Ok(())
27//! # }
28//! ```
29//!
30//! Capture the standard output of a command. Here "hi" is returned as a
31//! `String`:
32//!
33//! ```
34//! # use duct::cmd;
35//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
36//! # if cfg!(not(windows)) {
37//! let stdout = cmd!("echo", "hi").read()?;
38//! assert_eq!(stdout, "hi");
39//! # }
40//! # Ok(())
41//! # }
42//! ```
43//!
44//! Capture the standard output of a pipeline:
45//!
46//! ```
47//! # use duct::cmd;
48//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
49//! # if cfg!(not(windows)) {
50//! let stdout = cmd!("echo", "hi").pipe(cmd!("sed", "s/i/o/")).read()?;
51//! assert_eq!(stdout, "ho");
52//! # }
53//! # Ok(())
54//! # }
55//! ```
56//!
57//! Merge standard error into standard output and read both incrementally:
58//!
59//! ```
60//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
61//! # if cfg!(not(windows)) {
62//! use duct::cmd;
63//! use std::io::prelude::*;
64//! use std::io::BufReader;
65//!
66//! let big_cmd = cmd!("bash", "-c", "echo out && echo err 1>&2");
67//! let reader = big_cmd.stderr_to_stdout().reader()?;
68//! let mut lines = BufReader::new(reader).lines();
69//! assert_eq!(lines.next().unwrap()?, "out");
70//! assert_eq!(lines.next().unwrap()?, "err");
71//! # }
72//! # Ok(())
73//! # }
74//! ```
75//!
76//! Children that exit with a non-zero status return an error by default:
77//!
78//! ```
79//! # use duct::cmd;
80//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
81//! # if cfg!(not(windows)) {
82//! let result = cmd!("false").run();
83//! assert!(result.is_err());
84//! let result = cmd!("false").unchecked().run();
85//! assert!(result.is_ok());
86//! # }
87//! # Ok(())
88//! # }
89//! ```
90
91use shared_child::SharedChild;
92use shared_thread::SharedThread;
93use std::collections::HashMap;
94use std::ffi::{OsStr, OsString};
95use std::fmt;
96use std::fs::File;
97use std::io;
98use std::io::prelude::*;
99use std::mem;
100use std::path::{Path, PathBuf};
101use std::process::{Command, ExitStatus, Output, Stdio};
102use std::sync::{Arc, OnceLock, RwLock};
103
104#[cfg(not(windows))]
105use std::os::unix::prelude::*;
106#[cfg(windows)]
107use std::os::windows::prelude::*;
108
109/// Unix-specific extensions to duct, for sending signals.
110#[cfg(unix)]
111pub mod unix;
112
113// enums defined below
114use ExpressionInner::*;
115use IoExpressionInner::*;
116
117/// Create a command given a program name and a collection of arguments. See
118/// also the [`cmd!`](macro.cmd.html) macro, which doesn't require a collection.
119///
120/// # Example
121///
122/// ```
123/// use duct::cmd;
124///
125/// let args = vec!["foo", "bar", "baz"];
126///
127/// # // NOTE: Normally this wouldn't work on Windows, but we have an "echo"
128/// # // binary that gets built for our main tests, and it's sitting around by
129/// # // the time we get here. If this ever stops working, then we can disable
130/// # // the tests that depend on it.
131/// let output = cmd("echo", &args).read();
132///
133/// assert_eq!("foo bar baz", output.unwrap());
134/// ```
135pub fn cmd<T, U>(program: T, args: U) -> Expression
136where
137 T: IntoExecutablePath,
138 U: IntoIterator,
139 U::Item: Into<OsString>,
140{
141 let mut argv_vec = Vec::new();
142 argv_vec.push(program.to_executable());
143 argv_vec.extend(args.into_iter().map(Into::<OsString>::into));
144 Expression::new(Cmd(argv_vec))
145}
146
147/// Create a command with any number of of positional arguments, which may be
148/// different types (anything that implements
149/// [`Into<OsString>`](https://doc.rust-lang.org/std/convert/trait.From.html)).
150/// See also the [`cmd`](fn.cmd.html) function, which takes a collection of
151/// arguments.
152///
153/// # Example
154///
155/// ```
156/// use duct::cmd;
157/// use std::path::Path;
158///
159/// let arg1 = "foo";
160/// let arg2 = "bar".to_owned();
161/// let arg3 = Path::new("baz");
162///
163/// let output = cmd!("echo", arg1, arg2, arg3).read();
164///
165/// assert_eq!("foo bar baz", output.unwrap());
166/// ```
167#[macro_export]
168macro_rules! cmd {
169 ( $program:expr $(, $arg:expr )* $(,)? ) => {
170 {
171 use std::ffi::OsString;
172 let args: std::vec::Vec<OsString> = std::vec![$( Into::<OsString>::into($arg) ),*];
173 $crate::cmd($program, args)
174 }
175 };
176}
177
178/// The central objects in Duct, Expressions are created with
179/// [`cmd`](fn.cmd.html) or [`cmd!`](macro.cmd.html), combined with
180/// [`pipe`](struct.Expression.html#method.pipe), and finally executed with
181/// [`run`](struct.Expression.html#method.run),
182/// [`read`](struct.Expression.html#method.read),
183/// [`start`](struct.Expression.html#method.start), or
184/// [`reader`](struct.Expression.html#method.reader). They also support several
185/// methods to control their execution, like
186/// [`stdin_bytes`](struct.Expression.html#method.stdin_bytes),
187/// [`stdout_capture`](struct.Expression.html#method.stdout_capture),
188/// [`env`](struct.Expression.html#method.env), and
189/// [`unchecked`](struct.Expression.html#method.unchecked).
190///
191/// Expressions are immutable, and they do a lot of
192/// [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) sharing
193/// internally, so all of the methods below take `&self` and return a new
194/// `Expression` cheaply.
195///
196/// Expressions using `pipe` form trees, and the order in which you call
197/// different methods can matter, just like it matters where you put
198/// redirections in Bash. For example, each of these expressions suppresses
199/// output differently:
200///
201/// ```no_run
202/// # use duct::cmd;
203/// # fn main() -> std::io::Result<()> {
204/// // Only suppress stderr on the left side.
205/// cmd!("foo").stderr_null().pipe(cmd!("bar")).run()?;
206///
207/// // Only suppress stderr on the right side.
208/// cmd!("foo").pipe(cmd!("bar").stderr_null()).run()?;
209///
210/// // Suppress stderr on both sides.
211/// cmd!("foo").pipe(cmd!("bar")).stderr_null().run()?;
212/// # Ok(())
213/// # }
214/// ```
215#[derive(Clone)]
216#[must_use]
217pub struct Expression(Arc<ExpressionInner>);
218
219impl Expression {
220 /// Execute an expression, wait for it to complete, and return a
221 /// [`std::process::Output`](https://doc.rust-lang.org/std/process/struct.Output.html)
222 /// object containing the results. Nothing is captured by default, but if
223 /// you build the expression with
224 /// [`stdout_capture`](struct.Expression.html#method.stdout_capture) or
225 /// [`stderr_capture`](struct.Expression.html#method.stderr_capture) then
226 /// the `Output` will hold those captured bytes.
227 ///
228 /// # Errors
229 ///
230 /// In addition to all the IO errors possible with
231 /// [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html),
232 /// `run` will return an
233 /// [`ErrorKind::Other`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html)
234 /// IO error if child returns a non-zero exit status. To suppress this error
235 /// and return an `Output` even when the exit status is non-zero, use the
236 /// [`unchecked`](struct.Expression.html#method.unchecked) method.
237 ///
238 /// # Example
239 ///
240 /// ```
241 /// # use duct::cmd;
242 /// # fn main() {
243 /// # if cfg!(not(windows)) {
244 /// let output = cmd!("echo", "hi").stdout_capture().run().unwrap();
245 /// assert_eq!(b"hi\n".to_vec(), output.stdout);
246 /// # }
247 /// # }
248 /// ```
249 pub fn run(&self) -> io::Result<Output> {
250 // This could be optimized to avoid creating a background threads, by
251 // using the current thread to read stdout or stderr if only one of
252 // them is captured, or by using async IO to read both.
253 self.start()?.into_output()
254 }
255
256 /// Execute an expression, capture its standard output, and return the
257 /// captured output as a `String`. This is a convenience wrapper around
258 /// [`reader`](struct.Expression.html#method.reader). Like backticks and
259 /// `$()` in the shell, `read` trims trailing newlines.
260 ///
261 /// # Errors
262 ///
263 /// In addition to all the errors possible with
264 /// [`run`](struct.Expression.html#method.run), `read` will return an error
265 /// if the captured bytes aren't valid UTF-8.
266 ///
267 /// # Example
268 ///
269 /// ```
270 /// # use duct::cmd;
271 /// # fn main() {
272 /// # if cfg!(not(windows)) {
273 /// let output = cmd!("echo", "hi").stdout_capture().read().unwrap();
274 /// assert_eq!("hi", output);
275 /// # }
276 /// # }
277 /// ```
278 pub fn read(&self) -> io::Result<String> {
279 let mut reader = self.reader()?;
280 let mut output = String::new();
281 reader.read_to_string(&mut output)?;
282 while output.ends_with('\n') || output.ends_with('\r') {
283 output.truncate(output.len() - 1);
284 }
285 Ok(output)
286 }
287
288 /// Start running an expression, and immediately return a
289 /// [`Handle`](struct.Handle.html) that represents all the child processes.
290 /// This is analogous to the
291 /// [`spawn`](https://doc.rust-lang.org/std/process/struct.Command.html#method.spawn)
292 /// method in the standard library. The `Handle` may be shared between
293 /// multiple threads.
294 ///
295 /// # Example
296 ///
297 /// ```
298 /// # use duct::cmd;
299 /// # fn main() {
300 /// # if cfg!(not(windows)) {
301 /// let handle = cmd!("echo", "hi").stdout_capture().start().unwrap();
302 /// let output = handle.wait().unwrap();
303 /// assert_eq!(b"hi\n".to_vec(), output.stdout);
304 /// # }
305 /// # }
306 /// ```
307 pub fn start(&self) -> io::Result<Handle> {
308 let stdout_capture = OutputCaptureContext::new();
309 let stderr_capture = OutputCaptureContext::new();
310 let context = IoContext::new(&stdout_capture, &stderr_capture);
311
312 Ok(Handle {
313 inner: self.0.start(context)?,
314 result: OnceLock::new(),
315 readers: RwLock::new((
316 stdout_capture.maybe_read_thread(),
317 stderr_capture.maybe_read_thread(),
318 )),
319 })
320 }
321
322 /// Start running an expression, and immediately return a
323 /// [`ReaderHandle`](struct.ReaderHandle.html) attached to the child's
324 /// stdout. This is similar to `.stdout_capture().start()`, but it returns
325 /// the reader to the caller rather than reading from a background thread.
326 ///
327 /// Note that because this method doesn't read child output on a background
328 /// thread, it's a best practice to only create one `ReaderHandle` at a
329 /// time. Child processes with a lot of output will eventually block if
330 /// their stdout pipe isn't read from. If you have multiple children
331 /// running, but you're only reading from one of them at a time, that could
332 /// block the others and lead to performance issues or deadlocks. For
333 /// reading from multiple children at once, prefer
334 /// `.stdout_capture().start()`.
335 ///
336 /// # Example
337 ///
338 /// ```
339 /// # use duct::cmd;
340 /// # use std::io::prelude::*;
341 /// # fn main() {
342 /// # if cfg!(not(windows)) {
343 /// let mut reader = cmd!("echo", "hi").reader().unwrap();
344 /// let mut stdout = Vec::new();
345 /// reader.read_to_end(&mut stdout).unwrap();
346 /// assert_eq!(b"hi\n".to_vec(), stdout);
347 /// # }
348 /// # }
349 /// ```
350 pub fn reader(&self) -> io::Result<ReaderHandle> {
351 let stdout_capture = OutputCaptureContext::new();
352 let stderr_capture = OutputCaptureContext::new();
353 let context = IoContext::new(&stdout_capture, &stderr_capture);
354 let handle = Handle {
355 inner: self.stdout_capture().0.start(context)?,
356 result: OnceLock::new(),
357 readers: RwLock::new((None, stderr_capture.maybe_read_thread())),
358 };
359 Ok(ReaderHandle {
360 handle,
361 reader: stdout_capture.pair.into_inner().expect("pipe opened").0,
362 })
363 }
364
365 /// Join two expressions into a pipe expression, where the standard output
366 /// of the left will be hooked up to the standard input of the right, like
367 /// `|` in the shell.
368 ///
369 /// # Errors
370 ///
371 /// During execution, if one side of the pipe returns a non-zero exit
372 /// status, that becomes the status of the whole pipe. If both sides return
373 /// non-zero, and one of them is
374 /// [`unchecked`](struct.Expression.html#method.unchecked), the checked
375 /// side wins. Otherwise the right side wins. This is close to the behavior
376 /// of Bash with `-o pipefail`.
377 ///
378 /// During spawning, if the left side of the pipe spawns successfully but
379 /// the right side fails to spawn (e.g. the binary doesn't exist), the left
380 /// side will be cleaned up without blocking, which might require spawning
381 /// a thread. See the [`Handle`] docs for more on this behavior.
382 ///
383 /// # Example
384 ///
385 /// ```
386 /// # use duct::cmd;
387 /// # fn main() {
388 /// # if cfg!(not(windows)) {
389 /// let output = cmd!("echo", "hi").pipe(cmd!("sed", "s/h/p/")).read();
390 /// assert_eq!("pi", output.unwrap());
391 /// # }
392 /// # }
393 /// ```
394 pub fn pipe<T: Into<Expression>>(&self, right: T) -> Expression {
395 Self::new(Pipe(self.clone(), right.into()))
396 }
397
398 /// Use bytes or a string as input for an expression, like `<<<` in the
399 /// shell. A worker thread will write the input at runtime.
400 ///
401 /// # Example
402 ///
403 /// ```
404 /// # use duct::cmd;
405 /// # fn main() {
406 /// # if cfg!(not(windows)) {
407 /// // Many types implement Into<Vec<u8>>. Here's a string.
408 /// let output = cmd!("cat").stdin_bytes("foo").read().unwrap();
409 /// assert_eq!("foo", output);
410 ///
411 /// // And here's a byte slice.
412 /// let output = cmd!("cat").stdin_bytes(&b"foo"[..]).read().unwrap();
413 /// assert_eq!("foo", output);
414 /// # }
415 /// # }
416 /// ```
417 pub fn stdin_bytes<T: Into<Vec<u8>>>(&self, bytes: T) -> Expression {
418 Self::new(Io(StdinBytes(Arc::new(bytes.into())), self.clone()))
419 }
420
421 /// Open a file at the given path and use it as input for an expression,
422 /// like `<` in the shell.
423 ///
424 /// # Example
425 ///
426 /// ```
427 /// # use duct::cmd;
428 /// # fn main() {
429 /// # if cfg!(not(windows)) {
430 /// // Many types implement Into<PathBuf>, including &str.
431 /// let output = cmd!("head", "-c", "3").stdin_path("/dev/zero").read().unwrap();
432 /// assert_eq!("\0\0\0", output);
433 /// # }
434 /// # }
435 /// ```
436 pub fn stdin_path<T: Into<PathBuf>>(&self, path: T) -> Expression {
437 Self::new(Io(StdinPath(path.into()), self.clone()))
438 }
439
440 /// Use an already opened file or pipe as input for an expression.
441 ///
442 /// # Example
443 ///
444 /// ```
445 /// # use duct::cmd;
446 /// # fn main() {
447 /// # if cfg!(not(windows)) {
448 /// let input_file = std::fs::File::open("/dev/zero").unwrap();
449 /// let output = cmd!("head", "-c", "3").stdin_file(input_file).read().unwrap();
450 /// assert_eq!("\0\0\0", output);
451 /// # }
452 /// # }
453 /// ```
454 #[cfg(not(windows))]
455 pub fn stdin_file<T: IntoRawFd>(&self, file: T) -> Expression {
456 Self::new(Io(StdinFile(into_file(file)), self.clone()))
457 }
458 #[cfg(windows)]
459 pub fn stdin_file<T: IntoRawHandle>(&self, file: T) -> Expression {
460 Self::new(Io(StdinFile(into_file(file)), self.clone()))
461 }
462
463 /// Use `/dev/null` (or `NUL` on Windows) as input for an expression.
464 ///
465 /// # Example
466 ///
467 /// ```
468 /// # use duct::cmd;
469 /// # fn main() {
470 /// # if cfg!(not(windows)) {
471 /// let output = cmd!("cat").stdin_null().read().unwrap();
472 /// assert_eq!("", output);
473 /// # }
474 /// # }
475 /// ```
476 pub fn stdin_null(&self) -> Expression {
477 Self::new(Io(StdinNull, self.clone()))
478 }
479
480 /// Open a file at the given path and use it as output for an expression,
481 /// like `>` in the shell.
482 ///
483 /// # Example
484 ///
485 /// ```
486 /// # use duct::cmd;
487 /// # fn main() {
488 /// # use std::io::prelude::*;
489 /// # if cfg!(not(windows)) {
490 /// // Many types implement Into<PathBuf>, including &str.
491 /// let path = cmd!("mktemp").read().unwrap();
492 /// cmd!("echo", "wee").stdout_path(&path).run().unwrap();
493 /// let mut output = String::new();
494 /// std::fs::File::open(&path).unwrap().read_to_string(&mut output).unwrap();
495 /// assert_eq!("wee\n", output);
496 /// # }
497 /// # }
498 /// ```
499 pub fn stdout_path<T: Into<PathBuf>>(&self, path: T) -> Expression {
500 Self::new(Io(StdoutPath(path.into()), self.clone()))
501 }
502
503 /// Use an already opened file or pipe as output for an expression.
504 ///
505 /// # Example
506 ///
507 /// ```
508 /// # use duct::cmd;
509 /// # fn main() {
510 /// # use std::io::prelude::*;
511 /// # if cfg!(not(windows)) {
512 /// let path = cmd!("mktemp").read().unwrap();
513 /// let file = std::fs::File::create(&path).unwrap();
514 /// cmd!("echo", "wee").stdout_file(file).run().unwrap();
515 /// let mut output = String::new();
516 /// std::fs::File::open(&path).unwrap().read_to_string(&mut output).unwrap();
517 /// assert_eq!("wee\n", output);
518 /// # }
519 /// # }
520 /// ```
521 #[cfg(not(windows))]
522 pub fn stdout_file<T: IntoRawFd>(&self, file: T) -> Expression {
523 Self::new(Io(StdoutFile(into_file(file)), self.clone()))
524 }
525 #[cfg(windows)]
526 pub fn stdout_file<T: IntoRawHandle>(&self, file: T) -> Expression {
527 Self::new(Io(StdoutFile(into_file(file)), self.clone()))
528 }
529
530 /// Use `/dev/null` (or `NUL` on Windows) as output for an expression.
531 ///
532 /// # Example
533 ///
534 /// ```
535 /// # use duct::cmd;
536 /// # fn main() {
537 /// // This echo command won't print anything.
538 /// cmd!("echo", "foo", "bar", "baz").stdout_null().run().unwrap();
539 ///
540 /// // And you won't get anything even if you try to read its output! The
541 /// // null redirect happens farther down in the expression tree than the
542 /// // implicit `stdout_capture`, and so it takes precedence.
543 /// let output = cmd!("echo", "foo", "bar", "baz").stdout_null().read().unwrap();
544 /// assert_eq!("", output);
545 /// # }
546 /// ```
547 pub fn stdout_null(&self) -> Expression {
548 Self::new(Io(StdoutNull, self.clone()))
549 }
550
551 /// Capture the standard output of an expression. The captured bytes will
552 /// be available on the `stdout` field of the
553 /// [`std::process::Output`](https://doc.rust-lang.org/std/process/struct.Output.html)
554 /// object returned by [`run`](struct.Expression.html#method.run) or
555 /// [`wait`](struct.Handle.html#method.wait). Output is read by a
556 /// background thread, so the child will never block writing to stdout. But
557 /// note that [`read`](struct.Expression.html#method.read) and
558 /// [`reader`](struct.Expression.html#method.reader) can be more
559 /// convenient, and they don't require the background thread.
560 ///
561 /// # Example
562 ///
563 /// ```
564 /// # use duct::cmd;
565 /// # fn main() {
566 /// # if cfg!(not(windows)) {
567 /// // The most direct way to read stdout bytes is `stdout_capture`.
568 /// let output1 = cmd!("echo", "foo").stdout_capture().run().unwrap().stdout;
569 /// assert_eq!(&b"foo\n"[..], &output1[..]);
570 ///
571 /// // The `read` method is a shorthand for `stdout_capture`, and it also
572 /// // does string parsing and newline trimming.
573 /// let output2 = cmd!("echo", "foo").read().unwrap();
574 /// assert_eq!("foo", output2)
575 /// # }
576 /// # }
577 /// ```
578 pub fn stdout_capture(&self) -> Expression {
579 Self::new(Io(StdoutCapture, self.clone()))
580 }
581
582 /// Join the standard output of an expression to its standard error pipe,
583 /// similar to `1>&2` in the shell.
584 ///
585 /// # Example
586 ///
587 /// ```
588 /// # use duct::cmd;
589 /// # fn main() {
590 /// # if cfg!(not(windows)) {
591 /// let output = cmd!("echo", "foo").stdout_to_stderr().stderr_capture().run().unwrap();
592 /// assert_eq!(&b"foo\n"[..], &output.stderr[..]);
593 /// # }
594 /// # }
595 /// ```
596 pub fn stdout_to_stderr(&self) -> Expression {
597 Self::new(Io(StdoutToStderr, self.clone()))
598 }
599
600 /// Open a file at the given path and use it as error output for an
601 /// expression, like `2>` in the shell.
602 ///
603 /// # Example
604 ///
605 /// ```
606 /// # use duct::cmd;
607 /// # fn main() {
608 /// # use std::io::prelude::*;
609 /// # if cfg!(not(windows)) {
610 /// // Many types implement Into<PathBuf>, including &str.
611 /// let path = cmd!("mktemp").read().unwrap();
612 /// cmd!("sh", "-c", "echo wee >&2").stderr_path(&path).run().unwrap();
613 /// let mut error_output = String::new();
614 /// std::fs::File::open(&path).unwrap().read_to_string(&mut error_output).unwrap();
615 /// assert_eq!("wee\n", error_output);
616 /// # }
617 /// # }
618 /// ```
619 pub fn stderr_path<T: Into<PathBuf>>(&self, path: T) -> Expression {
620 Self::new(Io(StderrPath(path.into()), self.clone()))
621 }
622
623 /// Use an already opened file or pipe as error output for an expression.
624 ///
625 /// # Example
626 ///
627 /// ```
628 /// # use duct::cmd;
629 /// # fn main() {
630 /// # use std::io::prelude::*;
631 /// # if cfg!(not(windows)) {
632 /// let path = cmd!("mktemp").read().unwrap();
633 /// let file = std::fs::File::create(&path).unwrap();
634 /// cmd!("sh", "-c", "echo wee >&2").stderr_file(file).run().unwrap();
635 /// let mut error_output = String::new();
636 /// std::fs::File::open(&path).unwrap().read_to_string(&mut error_output).unwrap();
637 /// assert_eq!("wee\n", error_output);
638 /// # }
639 /// # }
640 /// ```
641 #[cfg(not(windows))]
642 pub fn stderr_file<T: IntoRawFd>(&self, file: T) -> Expression {
643 Self::new(Io(StderrFile(into_file(file)), self.clone()))
644 }
645 #[cfg(windows)]
646 pub fn stderr_file<T: IntoRawHandle>(&self, file: T) -> Expression {
647 Self::new(Io(StderrFile(into_file(file)), self.clone()))
648 }
649
650 /// Use `/dev/null` (or `NUL` on Windows) as error output for an expression.
651 ///
652 /// # Example
653 ///
654 /// ```
655 /// # use duct::cmd;
656 /// # fn main() {
657 /// # if cfg!(not(windows)) {
658 /// // This echo-to-stderr command won't print anything.
659 /// cmd!("sh", "-c", "echo foo bar baz >&2").stderr_null().run().unwrap();
660 /// # }
661 /// # }
662 /// ```
663 pub fn stderr_null(&self) -> Expression {
664 Self::new(Io(StderrNull, self.clone()))
665 }
666
667 /// Capture the error output of an expression. The captured bytes will be
668 /// available on the `stderr` field of the `Output` object returned by
669 /// [`run`](struct.Expression.html#method.run) or
670 /// [`wait`](struct.Handle.html#method.wait). Output is read by a
671 /// background thread, so the child will never block writing to stderr.
672 ///
673 /// # Example
674 ///
675 /// ```
676 /// # use duct::cmd;
677 /// # fn main() {
678 /// # if cfg!(not(windows)) {
679 /// let output_obj = cmd!("sh", "-c", "echo foo >&2").stderr_capture().run().unwrap();
680 /// assert_eq!(&b"foo\n"[..], &output_obj.stderr[..]);
681 /// # }
682 /// # }
683 /// ```
684 pub fn stderr_capture(&self) -> Expression {
685 Self::new(Io(StderrCapture, self.clone()))
686 }
687
688 /// Join the standard error of an expression to its standard output pipe,
689 /// similar to `2>&1` in the shell.
690 ///
691 /// # Example
692 ///
693 /// ```
694 /// # use duct::cmd;
695 /// # fn main() {
696 /// # if cfg!(not(windows)) {
697 /// let error_output = cmd!("sh", "-c", "echo foo >&2").stderr_to_stdout().read().unwrap();
698 /// assert_eq!("foo", error_output);
699 /// # }
700 /// # }
701 /// ```
702 pub fn stderr_to_stdout(&self) -> Expression {
703 Self::new(Io(StderrToStdout, self.clone()))
704 }
705
706 /// Swap the stdout and stderr of an expression.
707 ///
708 /// # Example
709 ///
710 /// ```
711 /// # use duct::cmd;
712 /// # fn main() {
713 /// # if cfg!(not(windows)) {
714 /// let output = cmd!("sh", "-c", "echo foo && echo bar >&2")
715 /// .stdout_stderr_swap()
716 /// .stdout_capture()
717 /// .stderr_capture()
718 /// .run()
719 /// .unwrap();
720 /// assert_eq!(b"bar\n", &*output.stdout);
721 /// assert_eq!(b"foo\n", &*output.stderr);
722 /// # }
723 /// # }
724 /// ```
725 pub fn stdout_stderr_swap(&self) -> Expression {
726 Self::new(Io(StdoutStderrSwap, self.clone()))
727 }
728
729 /// Set the working directory where the expression will execute.
730 ///
731 /// Note that in some languages (Rust and Python at least), there are
732 /// tricky platform differences in the way relative exe paths interact with
733 /// child working directories. In particular, the exe path will be
734 /// interpreted relative to the child dir on Unix, but relative to the
735 /// parent dir on Windows. Duct prefers the Windows behavior, and in order
736 /// to get that behavior on all platforms it calls
737 /// [`std::fs::canonicalize`](https://doc.rust-lang.org/std/fs/fn.canonicalize.html)
738 /// on relative exe paths when `dir` is in use. Paths in this sense are any
739 /// program name containing a path separator, regardless of the type. (Note
740 /// also that `Path` and `PathBuf` program names get a `./` prepended to
741 /// them automatically by the
742 /// [`IntoExecutablePath`](trait.IntoExecutablePath.html) trait, and so
743 /// will always contain a separator.)
744 ///
745 /// # Errors
746 ///
747 /// Canonicalization can fail on some filesystems, or if the current
748 /// directory has been removed, and
749 /// [`run`](struct.Expression.html#method.run) will return those errors
750 /// rather than trying any sneaky workarounds.
751 ///
752 /// # Example
753 ///
754 /// ```
755 /// # use duct::cmd;
756 /// # fn main() {
757 /// # if cfg!(not(windows)) {
758 /// let output = cmd!("pwd").dir("/").read().unwrap();
759 /// assert_eq!("/", output);
760 /// # }
761 /// # }
762 /// ```
763 pub fn dir<T: Into<PathBuf>>(&self, path: T) -> Expression {
764 Self::new(Io(Dir(path.into()), self.clone()))
765 }
766
767 /// Set a variable in the expression's environment.
768 ///
769 /// # Example
770 ///
771 /// ```
772 /// # use duct::cmd;
773 /// # fn main() {
774 /// # if cfg!(not(windows)) {
775 /// let output = cmd!("sh", "-c", "echo $FOO").env("FOO", "bar").read().unwrap();
776 /// assert_eq!("bar", output);
777 /// # }
778 /// # }
779 /// ```
780 pub fn env<T, U>(&self, name: T, val: U) -> Expression
781 where
782 T: Into<OsString>,
783 U: Into<OsString>,
784 {
785 Self::new(Io(
786 Env(canonicalize_env_var_name(name.into()), val.into()),
787 self.clone(),
788 ))
789 }
790
791 /// Remove a variable from the expression's environment.
792 ///
793 /// Note that all the environment functions try to do whatever the platform
794 /// does with respect to case sensitivity. That means that
795 /// `env_remove("foo")` will unset the uppercase variable `FOO` on Windows,
796 /// but not on Unix.
797 ///
798 /// # Example
799 ///
800 /// ```
801 /// # use duct::cmd;
802 /// # fn main() {
803 /// # if cfg!(not(windows)) {
804 /// std::env::set_var("TESTING", "true");
805 /// let output = cmd!("sh", "-c", "echo a${TESTING}b")
806 /// .env_remove("TESTING")
807 /// .read()
808 /// .unwrap();
809 /// assert_eq!("ab", output);
810 /// # }
811 /// # }
812 /// ```
813 pub fn env_remove<T>(&self, name: T) -> Expression
814 where
815 T: Into<OsString>,
816 {
817 Self::new(Io(
818 EnvRemove(canonicalize_env_var_name(name.into())),
819 self.clone(),
820 ))
821 }
822
823 /// Set the expression's entire environment, from a collection of
824 /// name-value pairs (like a `HashMap`). Note that some environment
825 /// variables are required for normal program execution (like `SystemRoot`
826 /// on Windows), so copying the parent's environment is usually preferable
827 /// to starting with an empty one.
828 ///
829 /// # Example
830 ///
831 /// ```
832 /// # use duct::cmd;
833 /// # fn main() {
834 /// # use std::collections::HashMap;
835 /// # if cfg!(not(windows)) {
836 /// let mut env_map: HashMap<_, _> = std::env::vars().collect();
837 /// env_map.insert("FOO".into(), "bar".into());
838 /// let output = cmd!("sh", "-c", "echo $FOO").full_env(&env_map).read().unwrap();
839 /// assert_eq!("bar", output);
840 /// // The IntoIterator/Into<OsString> bounds are pretty flexible. Passing
841 /// // by value works here too.
842 /// let output = cmd!("sh", "-c", "echo $FOO").full_env(env_map).read().unwrap();
843 /// assert_eq!("bar", output);
844 /// # }
845 /// # }
846 /// ```
847 pub fn full_env<T, U, V>(&self, name_vals: T) -> Expression
848 where
849 T: IntoIterator<Item = (U, V)>,
850 U: Into<OsString>,
851 V: Into<OsString>,
852 {
853 let env_map = name_vals
854 .into_iter()
855 .map(|(k, v)| (canonicalize_env_var_name(k.into()), v.into()))
856 .collect();
857 Self::new(Io(FullEnv(env_map), self.clone()))
858 }
859
860 /// Prevent a non-zero exit status from causing
861 /// [`run`](struct.Expression.html#method.run) or
862 /// [`read`](struct.Expression.html#method.read) to return an error. The
863 /// unchecked exit code will still be there on the `Output` returned by
864 /// `run`; its value doesn't change.
865 ///
866 /// "Uncheckedness" sticks to an exit code as it bubbles up through
867 /// complicated pipelines, but it doesn't "infect" other exit codes. So for
868 /// example, if only one sub-expression in a pipe has `unchecked`, then
869 /// errors returned by the other side will still be checked. That said,
870 /// most commonly you'll just call `unchecked` right before `run`, and
871 /// it'll apply to an entire expression.
872 ///
873 /// # Example
874 ///
875 /// Note the differences among these three cases:
876 ///
877 /// ```no_run
878 /// # use duct::cmd;
879 /// # fn main() -> std::io::Result<()> {
880 /// // Don't check errors on the left side.
881 /// cmd!("foo").unchecked().pipe(cmd!("bar")).run()?;
882 ///
883 /// // Don't check errors on the right side.
884 /// cmd!("foo").pipe(cmd!("bar").unchecked()).run()?;
885 ///
886 /// // Don't check errors on either side.
887 /// cmd!("foo").pipe(cmd!("bar")).unchecked().run()?;
888 /// # Ok(())
889 /// # }
890 /// ```
891 pub fn unchecked(&self) -> Expression {
892 Self::new(Io(Unchecked, self.clone()))
893 }
894
895 /// Add a hook for modifying
896 /// [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html)
897 /// objects immediately before they're executed.
898 ///
899 /// The hook is called for each command in its sub-expression, and each time the expression is
900 /// executed. The call happens after other features like `stdout` and `env` have been applied,
901 /// so any changes made by the hook take priority. More than one hook can be added, in which
902 /// case the innermost is executed last. For example, if one call to `before_spawn` is applied
903 /// to an entire pipe expression, and another call is applied to just one command within the
904 /// pipe, the hook for the entire pipeline will be called first over the command where both
905 /// hooks apply.
906 ///
907 /// This is intended for rare and tricky cases, like callers who want to change the group ID of
908 /// their child processes, or who want to run code in `before_exec`. Most callers shouldn't
909 /// need to use it.
910 ///
911 /// # Example
912 ///
913 /// ```
914 /// # use duct::cmd;
915 /// # fn main() {
916 /// let output = cmd!("echo", "foo")
917 /// .before_spawn(|cmd| {
918 /// // Sneakily add an extra argument.
919 /// cmd.arg("bar");
920 /// Ok(())
921 /// })
922 /// .read()
923 /// .unwrap();
924 /// assert_eq!("foo bar", output);
925 /// # }
926 /// ```
927 pub fn before_spawn<F>(&self, hook: F) -> Expression
928 where
929 F: Fn(&mut Command) -> io::Result<()> + Send + Sync + 'static,
930 {
931 Self::new(Io(BeforeSpawn(BeforeSpawnHook::new(hook)), self.clone()))
932 }
933
934 fn new(inner: ExpressionInner) -> Expression {
935 Expression(Arc::new(inner))
936 }
937}
938
939// Delegate to the ExpressionInner for debug formatting. This avoids printing
940// redundant Expression() constructors around everything.
941impl fmt::Debug for Expression {
942 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
943 self.0.fmt(f)
944 }
945}
946
947// Implementing Into<Expression> for references lets us accept both references
948// and values in `pipe`.
949impl<'a> From<&'a Expression> for Expression {
950 fn from(expr: &Expression) -> Expression {
951 expr.clone()
952 }
953}
954
955/// A handle to a running expression, returned by the
956/// [`start`](struct.Expression.html#method.start) method.
957///
958/// Calling `start` followed by
959/// [`into_output`](struct.Handle.html#method.into_output) on the handle is
960/// equivalent to [`run`](struct.Expression.html#method.run). Note that unlike
961/// [`std::process::Child`](https://doc.rust-lang.org/std/process/struct.Child.html),
962/// most of the methods on `Handle` take `&self` rather than `&mut self`, and a
963/// `Handle` may be shared between multiple threads.
964///
965/// If you drop a `Handle` without first [`wait`][Handle::wait]ing on the child to exit, it will
966/// [`try_wait`][Handle::try_wait] internally to see if it can reap the child. If the child is
967/// still running, its handle will be added to a global list and polled whenever new child
968/// processes are spawned. This avoids leaking [zombie
969/// processes](https://en.wikipedia.org/wiki/Zombie_process) on Unix platforms. This `Drop`
970/// implementation is omitted on Windows, where zombies aren't a problem.
971///
972/// See the [`shared_child`](https://github.com/oconnor663/shared_child.rs)
973/// crate for implementation details behind making handles thread safe.
974#[derive(Debug)]
975pub struct Handle {
976 inner: HandleInner,
977 result: OnceLock<(ExpressionStatus, Output)>,
978 readers: RwLock<(Option<ReaderThread>, Option<ReaderThread>)>,
979}
980
981impl Handle {
982 /// Wait for the running expression to finish, and return a reference to its
983 /// [`std::process::Output`](https://doc.rust-lang.org/std/process/struct.Output.html).
984 /// Multiple threads may wait at the same time.
985 ///
986 /// # Errors
987 ///
988 /// In addition to all the IO errors possible with
989 /// [`std::process::Child`](https://doc.rust-lang.org/std/process/struct.Child.html),
990 /// `wait` will return an
991 /// [`ErrorKind::Other`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html)
992 /// IO error if child returns a non-zero exit status. To suppress this
993 /// error and return an `Output` even when the exit status is non-zero, use
994 /// the [`unchecked`](struct.Expression.html#method.unchecked) method.
995 pub fn wait(&self) -> io::Result<&Output> {
996 // Await the children and any threads that are reading their output.
997 // Another caller may already have done this.
998 let (expression_status, output) = wait_on_handle_and_output(self, WaitMode::Blocking)?
999 .expect("blocking wait shouldn't return None");
1000 // If the child returned a "checked" non-zero exit status, make that an error.
1001 if expression_status.is_checked_error() {
1002 return Err(io::Error::new(
1003 io::ErrorKind::Other,
1004 expression_status.message(),
1005 ));
1006 }
1007 Ok(output)
1008 }
1009
1010 /// Check whether the running expression is finished. If it is, return a
1011 /// reference to its
1012 /// [`std::process::Output`](https://doc.rust-lang.org/std/process/struct.Output.html).
1013 /// If it's still running, return `Ok(None)`.
1014 ///
1015 /// # Errors
1016 ///
1017 /// In addition to all the IO errors possible with
1018 /// [`std::process::Child`](https://doc.rust-lang.org/std/process/struct.Child.html),
1019 /// `try_wait` will return an
1020 /// [`ErrorKind::Other`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html)
1021 /// IO error if child returns a non-zero exit status. To suppress this
1022 /// error and return an `Output` even when the exit status is non-zero, use
1023 /// the [`unchecked`](struct.Expression.html#method.unchecked) method.
1024 pub fn try_wait(&self) -> io::Result<Option<&Output>> {
1025 // Check whether child processes and IO threads are finished.
1026 let Some((expression_status, output)) =
1027 wait_on_handle_and_output(self, WaitMode::NonBlocking)?
1028 else {
1029 return Ok(None);
1030 };
1031 // If the child returned a "checked" non-zero exit status, make that an error.
1032 if expression_status.is_checked_error() {
1033 return Err(io::Error::new(
1034 io::ErrorKind::Other,
1035 expression_status.message(),
1036 ));
1037 }
1038 Ok(Some(output))
1039 }
1040
1041 /// Wait for the running expression to finish, and then return a
1042 /// [`std::process::Output`](https://doc.rust-lang.org/std/process/struct.Output.html)
1043 /// object containing the results, including any captured output. This
1044 /// consumes the `Handle`. Calling
1045 /// [`start`](struct.Expression.html#method.start) followed by
1046 /// `into_output` is equivalent to
1047 /// [`run`](struct.Expression.html#method.run).
1048 ///
1049 /// # Errors
1050 ///
1051 /// In addition to all the IO errors possible with
1052 /// [`std::process::Child`](https://doc.rust-lang.org/std/process/struct.Child.html),
1053 /// `into_output` will return an
1054 /// [`ErrorKind::Other`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html)
1055 /// IO error if child returns a non-zero exit status. To suppress this
1056 /// error and return an `Output` even when the exit status is non-zero, use
1057 /// the [`unchecked`](struct.Expression.html#method.unchecked) method.
1058 pub fn into_output(self) -> io::Result<Output> {
1059 self.wait()?;
1060 let (_, output) = self.result.into_inner().expect("result missing");
1061 Ok(output)
1062 }
1063
1064 /// Kill all the child processes in the running expression.
1065 ///
1066 /// Note that as with
1067 /// [`std::process::Child::kill`](https://doc.rust-lang.org/beta/std/process/struct.Child.html#method.kill),
1068 /// this does not kill any grandchild processes that the children have
1069 /// spawned on their own. It only kills the child processes that Duct
1070 /// spawned itself. See
1071 /// [`gotchas.md`](https://github.com/oconnor663/duct.py/blob/master/gotchas.md)
1072 /// for an extensive discussion of this behavior.
1073 ///
1074 /// This method does not wait on the child processes to exit. Calling [`wait`][Handle::wait]
1075 /// after `kill` almost always returns quickly, but there are edge cases where it might not.
1076 /// One case is if a child process is blocked reading an unresponsive FUSE filesystem. Another
1077 /// is if a child process is paused by a debugger.
1078 pub fn kill(&self) -> io::Result<()> {
1079 self.inner.kill()
1080 }
1081
1082 /// Return a `Vec<u32>` containing the PIDs of all of the child processes.
1083 /// The PIDs are given in pipeline order, from left to right.
1084 pub fn pids(&self) -> Vec<u32> {
1085 self.inner.pids()
1086 }
1087}
1088
1089// Wait on the handle and on captured output. Depending on the mode, this wait might or might not
1090// be blocking. This does not do any status checking.
1091fn wait_on_handle_and_output(
1092 handle: &Handle,
1093 mode: WaitMode,
1094) -> io::Result<Option<&(ExpressionStatus, Output)>> {
1095 let Some(status) = handle.inner.wait(mode)? else {
1096 return Ok(None);
1097 };
1098 // We need non-blocking waiters (try_wait) to be able to access the SharedThread IO readers,
1099 // even while a blocking waiter (wait) is blocking, so we can't take RwLock::write until we
1100 // know the threads have already exited.
1101 let shared_lock = handle.readers.read().unwrap();
1102 let (maybe_stdout_reader, maybe_stderr_reader) = &*shared_lock;
1103 if let Some(stdout_reader) = maybe_stdout_reader {
1104 if mode.maybe_join_io_thread(stdout_reader)?.is_none() {
1105 return Ok(None);
1106 }
1107 }
1108 if let Some(stderr_reader) = maybe_stderr_reader {
1109 if mode.maybe_join_io_thread(stderr_reader)?.is_none() {
1110 return Ok(None);
1111 }
1112 }
1113 drop(shared_lock);
1114
1115 // At this point we know that the child and all its IO threads (if any) have exited, so we can
1116 // collect output without blocking. Take the RwLock::write lock and take ownership of the
1117 // output vectors. If another thread has already done this, .unwrap_or_default() will return
1118 // empty Vecs, and result.set() will be a no-op.
1119 let mut unique_lock = handle.readers.write().unwrap();
1120 let (maybe_stdout_reader, maybe_stderr_reader) = &mut *unique_lock;
1121 let stdout: Vec<u8> = maybe_stdout_reader
1122 .take()
1123 .map(SharedThread::into_output)
1124 .transpose()
1125 .expect("IO errors already short-circuited")
1126 .unwrap_or_default();
1127 let stderr: Vec<u8> = maybe_stderr_reader
1128 .take()
1129 .map(SharedThread::into_output)
1130 .transpose()
1131 .expect("IO errors already short-circuited")
1132 .unwrap_or_default();
1133 let output = Output {
1134 status: status.status,
1135 stdout,
1136 stderr,
1137 };
1138 let _ = handle.result.set((status, output)); // might already be set
1139 Ok(handle.result.get())
1140}
1141
1142#[derive(Debug)]
1143enum ExpressionInner {
1144 Cmd(Vec<OsString>),
1145 Pipe(Expression, Expression),
1146 Io(IoExpressionInner, Expression),
1147}
1148
1149impl ExpressionInner {
1150 fn start(&self, context: IoContext) -> io::Result<HandleInner> {
1151 Ok(match self {
1152 Cmd(argv) => HandleInner::Child(ChildHandle::start(argv, context)?),
1153 Pipe(left, right) => {
1154 HandleInner::Pipe(Box::new(PipeHandle::start(left, right, context)?))
1155 }
1156 Io(io_inner, expr) => start_io(io_inner, expr, context)?,
1157 })
1158 }
1159}
1160
1161#[derive(Debug)]
1162enum HandleInner {
1163 Child(ChildHandle),
1164 // If the left side of a pipe fails to start, there's nothing to wait for,
1165 // and we return an error immediately. But if the right side fails to start,
1166 // the caller still needs to wait on the left, and we must return a handle.
1167 // Thus the handle preserves the right side's errors here.
1168 Pipe(Box<PipeHandle>),
1169 StdinBytes(Box<StdinBytesHandle>),
1170 // Why does "uncheckedness" need to be a handle type and not just a field on
1171 // IoContext? Because when one side of a pipe is checked, that side's errors
1172 // take priority over the checked side, even when the pipe expression
1173 // *itself* is also unchecked.
1174 Unchecked(Box<HandleInner>),
1175}
1176
1177impl HandleInner {
1178 fn wait(&self, mode: WaitMode) -> io::Result<Option<ExpressionStatus>> {
1179 match self {
1180 HandleInner::Child(child_handle) => child_handle.wait(mode),
1181 HandleInner::Pipe(pipe_handle) => pipe_handle.wait(mode),
1182 HandleInner::StdinBytes(stdin_bytes_handle) => stdin_bytes_handle.wait(mode),
1183 HandleInner::Unchecked(inner_handle) => {
1184 Ok(inner_handle.wait(mode)?.map(|mut status| {
1185 status.checked = false;
1186 status
1187 }))
1188 }
1189 }
1190 }
1191
1192 fn kill(&self) -> io::Result<()> {
1193 match self {
1194 HandleInner::Child(child_handle) => child_handle.kill(),
1195 HandleInner::Pipe(pipe_handle) => pipe_handle.kill(),
1196 HandleInner::StdinBytes(stdin_bytes_handle) => stdin_bytes_handle.kill(),
1197 HandleInner::Unchecked(inner_handle) => inner_handle.kill(),
1198 }
1199 }
1200
1201 fn pids(&self) -> Vec<u32> {
1202 match self {
1203 HandleInner::Child(child_handle) => vec![child_handle.child().id()],
1204 HandleInner::Pipe(pipe_handle) => pipe_handle.pids(),
1205 HandleInner::StdinBytes(stdin_bytes_handle) => stdin_bytes_handle.inner_handle.pids(),
1206 HandleInner::Unchecked(inner_handle) => inner_handle.pids(),
1207 }
1208 }
1209}
1210
1211// Use std::process::Child instead of SharedChild to avoid taking extra locks when we poll.
1212#[cfg(not(windows))]
1213static LEAKED_CHILDREN: RwLock<Vec<std::process::Child>> = RwLock::new(Vec::new());
1214
1215// In `impl Drop for ChildHandle` below, children who are still running when they're dropped get
1216// added to this global list. Poll the list to reap as many zombie children as possible each time
1217// we spawn a new child process. See the comments in the Drop impl regarding tradeoffs.
1218#[cfg(not(windows))]
1219fn cleanup_leaked_children() {
1220 if !LEAKED_CHILDREN.read().unwrap().is_empty() {
1221 LEAKED_CHILDREN.write().unwrap().retain_mut(|child| {
1222 match child.try_wait() {
1223 Ok(Some(_)) => false, // remove
1224 Ok(None) => true, // retain
1225 Err(e) => {
1226 // try_wait errors require odd circumstances to trigger. For example, something
1227 // else might call libc::waitpid (or its safe wrapper from `nix`) and reap the
1228 // child, causing us to get a "process not found" error here. If that happens
1229 // in a test, go ahead and panic, but otherwise ignore the error. The most
1230 // important thing is that we don't leave the whole process in a broken state
1231 // where every future call to ChildHandle::start returns an error. Also, it
1232 // might not be helpful or appropriate to report this error to our caller.
1233 // Remember that this is lazy, global cleanup of some state that might belong
1234 // to some other thread, running code from some other crate. Let it go...
1235 if cfg!(test) {
1236 panic!("cleanup_leaked_children failed: {e}");
1237 }
1238 false // remove
1239 }
1240 }
1241 });
1242 }
1243}
1244
1245#[derive(Debug)]
1246struct ChildHandle {
1247 child: Option<shared_child::SharedChild>, // optional so that `drop` can take ownership
1248 command_string: String,
1249}
1250
1251impl ChildHandle {
1252 fn start(argv: &[OsString], context: IoContext) -> io::Result<ChildHandle> {
1253 // See comments in the Drop impl below.
1254 #[cfg(not(windows))]
1255 cleanup_leaked_children();
1256
1257 let exe = canonicalize_exe_path_for_dir(&argv[0], &context)?;
1258 let mut command = Command::new(exe);
1259 command.args(&argv[1..]);
1260 if !matches!(context.stdin, IoValue::ParentStdin) {
1261 command.stdin(context.stdin.into_stdio()?);
1262 }
1263 if !matches!(context.stdout, IoValue::ParentStdout) {
1264 command.stdout(context.stdout.into_stdio()?);
1265 }
1266 if !matches!(context.stderr, IoValue::ParentStderr) {
1267 command.stderr(context.stderr.into_stdio()?);
1268 }
1269 if let Some(dir) = context.dir {
1270 command.current_dir(dir);
1271 }
1272 command.env_clear();
1273 for (name, val) in context.env {
1274 command.env(name, val);
1275 }
1276 // The innermost hooks are pushed last, and we execute them last.
1277 for hook in context.before_spawn_hooks.iter() {
1278 hook.call(&mut command)?;
1279 }
1280 let shared_child = SharedChild::spawn(&mut command)?;
1281 let command_string = format!("{:?}", argv);
1282 Ok(ChildHandle {
1283 child: Some(shared_child),
1284 command_string,
1285 })
1286 }
1287
1288 // a helper to reduce the need for .as_ref().unwrap() everywhere
1289 fn child(&self) -> &SharedChild {
1290 self.child
1291 .as_ref()
1292 .expect("ChildHandle should not yet have been dropped")
1293 }
1294
1295 fn wait(&self, mode: WaitMode) -> io::Result<Option<ExpressionStatus>> {
1296 let maybe_status = mode.maybe_wait_on_child(self.child())?;
1297 if let Some(status) = maybe_status {
1298 Ok(Some(ExpressionStatus {
1299 status,
1300 checked: true,
1301 command: self.command_string.clone(),
1302 }))
1303 } else {
1304 Ok(None)
1305 }
1306 }
1307
1308 fn kill(&self) -> io::Result<()> {
1309 self.child().kill()
1310 }
1311}
1312
1313// Use Drop to prevent zombie processes on Unix. Zombies aren't a thing on Windows, so omit the
1314// entire impl there.
1315#[cfg(not(windows))]
1316impl Drop for ChildHandle {
1317 fn drop(&mut self) {
1318 let child = self.child.take().expect("only drop should take the child");
1319 match child.try_wait() {
1320 // The child has been cleaned up. Cool.
1321 Ok(Some(_)) => (),
1322
1323 // Ignore IO errors here unless we're running tests. It's hard to provoke these without
1324 // doing something very weird (transmute, libc::waitpid, etc).
1325 Err(e) => {
1326 if cfg!(test) {
1327 panic!("ChildHandle cleanup failed: {e}");
1328 }
1329 }
1330
1331 // This child is still running, but the caller is never going to wait on it. Add it to
1332 // a global list of (potentially) zombie children. We poll this list whenever we spawn
1333 // new child processes, to mitigate leaks. This is the same strategy used in the
1334 // CPython `subprocess` module:
1335 // - https://github.com/python/cpython/blob/v3.13.3/Lib/subprocess.py#L1133-L1146
1336 // - https://github.com/python/cpython/blob/v3.13.3/Lib/subprocess.py#L268-L285
1337 // The main downside of this strategy is that spawning N child processes becomes O(N^2)
1338 // if you leak all of them and they're all long-lived. I think that's an ok tradeoff
1339 // for a couple of reasons:
1340 // 1. Dropping un-waited-for child handles isn't usually what you want to be doing in
1341 // your happy path, because it means you can't hear about error codes your
1342 // children return. Children whose handles are retained don't enter this list and
1343 // don't contribute to O(N^2) behavior.
1344 // 2. Callers who do "very advanced" things (say, a systemd clone) probably shouldn't
1345 // be using Duct. They need more fine-grained control than Duct is designed to
1346 // provide.
1347 Ok(None) => LEAKED_CHILDREN.write().unwrap().push(child.into_inner()),
1348 }
1349 }
1350}
1351
1352#[derive(Debug)]
1353struct PipeHandle {
1354 left_handle: HandleInner,
1355 right_handle: HandleInner,
1356}
1357
1358impl PipeHandle {
1359 fn start(left: &Expression, right: &Expression, context: IoContext) -> io::Result<PipeHandle> {
1360 let (reader, writer) = os_pipe::pipe()?;
1361 // dup'ing stdin/stdout isn't strictly necessary, but no big deal
1362 let mut left_context = context.try_clone()?;
1363 left_context.stdout = IoValue::Handle(into_file(writer));
1364 let mut right_context = context;
1365 right_context.stdin = IoValue::Handle(into_file(reader));
1366 let left_handle = left.0.start(left_context)?;
1367 // The left side has already started. If we fail to start the right
1368 // side, ChildHandle::drop will clean it up one way or another. Note
1369 // that `right_context` is passed by value here, so if start() returns
1370 // an error, all pipe readers will already have been closed, and
1371 // there's a decent chance the left side will exit quickly via EPIPE
1372 // before we .try_wait() it in ChildHandle::drop.
1373 let right_handle = right.0.start(right_context)?;
1374 Ok(PipeHandle {
1375 left_handle,
1376 right_handle,
1377 })
1378 }
1379
1380 fn wait(&self, mode: WaitMode) -> io::Result<Option<ExpressionStatus>> {
1381 // Wait on both sides first, without propagating any errors.
1382 let left_wait_result = self.left_handle.wait(mode);
1383 let right_wait_result = self.right_handle.wait(mode);
1384
1385 // Now we deal with errors from either of those waits. The left wait
1386 // happened first, so that one takes precedence. Note that this is the
1387 // reverse order of exit status precedence.
1388 let left_status = left_wait_result?;
1389 let right_status = right_wait_result?;
1390
1391 // If both waits succeeded, return one of the two statuses.
1392 Ok(pipe_status_precedence(left_status, right_status))
1393 }
1394
1395 // As with wait, we need to call kill on both sides even if the left side
1396 // returns an error.
1397 fn kill(&self) -> io::Result<()> {
1398 let left_kill_result = self.left_handle.kill();
1399 let right_kill_result = self.right_handle.kill();
1400 // As with wait, the left side happened first, so its errors take
1401 // precedence.
1402 left_kill_result.and(right_kill_result)
1403 }
1404
1405 fn pids(&self) -> Vec<u32> {
1406 let mut pids = self.left_handle.pids();
1407 pids.extend_from_slice(&self.right_handle.pids());
1408 pids
1409 }
1410}
1411
1412// The rules of precedence are:
1413// 1) If either side unfinished, the result is unfinished.
1414// 2) Checked errors trump unchecked errors.
1415// 3) Any errors trump success.
1416// 4) All else equal, the right side wins (as in Bash).
1417fn pipe_status_precedence(
1418 left_maybe_status: Option<ExpressionStatus>,
1419 right_maybe_status: Option<ExpressionStatus>,
1420) -> Option<ExpressionStatus> {
1421 let (left_status, right_status) = match (left_maybe_status, right_maybe_status) {
1422 (Some(left), Some(right)) => (left, right),
1423 _ => return None,
1424 };
1425 Some(if right_status.is_checked_error() {
1426 right_status
1427 } else if left_status.is_checked_error() {
1428 left_status
1429 } else if !right_status.status.success() {
1430 right_status
1431 } else {
1432 left_status
1433 })
1434}
1435
1436fn start_io(
1437 io_inner: &IoExpressionInner,
1438 expr_inner: &Expression,
1439 mut context: IoContext,
1440) -> io::Result<HandleInner> {
1441 match io_inner {
1442 StdinBytes(v) => {
1443 return Ok(HandleInner::StdinBytes(Box::new(StdinBytesHandle::start(
1444 expr_inner,
1445 context,
1446 Arc::clone(v),
1447 )?)));
1448 }
1449 StdinPath(p) => {
1450 context.stdin = IoValue::Handle(File::open(p)?);
1451 }
1452 StdinFile(f) => {
1453 context.stdin = IoValue::Handle(f.try_clone()?);
1454 }
1455 StdinNull => {
1456 context.stdin = IoValue::Null;
1457 }
1458 StdoutPath(p) => {
1459 context.stdout = IoValue::Handle(File::create(p)?);
1460 }
1461 StdoutFile(f) => {
1462 context.stdout = IoValue::Handle(f.try_clone()?);
1463 }
1464 StdoutNull => {
1465 context.stdout = IoValue::Null;
1466 }
1467 StdoutCapture => {
1468 context.stdout = IoValue::Handle(into_file(context.stdout_capture.write_pipe()?));
1469 }
1470 StdoutToStderr => {
1471 context.stdout = context.stderr.try_clone()?;
1472 }
1473 StderrPath(p) => {
1474 context.stderr = IoValue::Handle(File::create(p)?);
1475 }
1476 StderrFile(f) => {
1477 context.stderr = IoValue::Handle(f.try_clone()?);
1478 }
1479 StderrNull => {
1480 context.stderr = IoValue::Null;
1481 }
1482 StderrCapture => {
1483 context.stderr = IoValue::Handle(into_file(context.stderr_capture.write_pipe()?));
1484 }
1485 StderrToStdout => {
1486 context.stderr = context.stdout.try_clone()?;
1487 }
1488 StdoutStderrSwap => {
1489 mem::swap(&mut context.stdout, &mut context.stderr);
1490 }
1491 Dir(p) => {
1492 context.dir = Some(p.clone());
1493 }
1494 Env(name, val) => {
1495 context.env.insert(name.clone(), val.clone());
1496 }
1497 EnvRemove(name) => {
1498 context.env.remove(name);
1499 }
1500 FullEnv(map) => {
1501 context.env = map.clone();
1502 }
1503 Unchecked => {
1504 let inner_handle = expr_inner.0.start(context)?;
1505 return Ok(HandleInner::Unchecked(Box::new(inner_handle)));
1506 }
1507 BeforeSpawn(hook) => {
1508 context.before_spawn_hooks.push(hook.clone());
1509 }
1510 }
1511 expr_inner.0.start(context)
1512}
1513
1514#[derive(Debug)]
1515struct StdinBytesHandle {
1516 inner_handle: HandleInner,
1517 writer_thread: SharedThread<io::Result<()>>,
1518}
1519
1520impl StdinBytesHandle {
1521 fn start(
1522 expression: &Expression,
1523 mut context: IoContext,
1524 input: Arc<Vec<u8>>,
1525 ) -> io::Result<StdinBytesHandle> {
1526 let (reader, mut writer) = os_pipe::pipe()?;
1527 context.stdin = IoValue::Handle(into_file(reader));
1528 let inner_handle = expression.0.start(context)?;
1529 let writer_thread = SharedThread::spawn(move || {
1530 // Broken pipe errors are expected here. Suppress them.
1531 match writer.write_all(&input) {
1532 Err(e) if e.kind() != io::ErrorKind::BrokenPipe => Err(e),
1533 _ => Ok(()),
1534 }
1535 });
1536 Ok(StdinBytesHandle {
1537 inner_handle,
1538 writer_thread,
1539 })
1540 }
1541
1542 fn wait(&self, mode: WaitMode) -> io::Result<Option<ExpressionStatus>> {
1543 let maybe_status = self.inner_handle.wait(mode)?;
1544 // Even if the child has exited, grandchild processes might still be reading from the
1545 // writer thread. If the wait mode is Blocking, mode.maybe_join_io_thread will wait for
1546 // the writer thread to finish (i.e. it will never return Ok(false)). Otherwise, if the IO
1547 // thread is still
1548 let io_finished = mode.maybe_join_io_thread(&self.writer_thread)?.is_some();
1549 if !io_finished {
1550 return Ok(None);
1551 }
1552 Ok(maybe_status)
1553 }
1554
1555 fn kill(&self) -> io::Result<()> {
1556 self.inner_handle.kill()
1557 }
1558}
1559
1560#[derive(Debug)]
1561enum IoExpressionInner {
1562 StdinBytes(Arc<Vec<u8>>),
1563 StdinPath(PathBuf),
1564 StdinFile(File),
1565 StdinNull,
1566 StdoutPath(PathBuf),
1567 StdoutFile(File),
1568 StdoutNull,
1569 StdoutCapture,
1570 StdoutToStderr,
1571 StderrPath(PathBuf),
1572 StderrFile(File),
1573 StderrNull,
1574 StderrCapture,
1575 StderrToStdout,
1576 StdoutStderrSwap,
1577 Dir(PathBuf),
1578 Env(OsString, OsString),
1579 EnvRemove(OsString),
1580 FullEnv(HashMap<OsString, OsString>),
1581 Unchecked,
1582 BeforeSpawn(BeforeSpawnHook),
1583}
1584
1585type HookFn = Arc<dyn Fn(&mut Command) -> io::Result<()> + Send + Sync>;
1586
1587#[derive(Clone)]
1588struct BeforeSpawnHook {
1589 inner: HookFn,
1590}
1591
1592impl BeforeSpawnHook {
1593 fn new<F>(hook: F) -> Self
1594 where
1595 F: Fn(&mut Command) -> io::Result<()> + Send + Sync + 'static,
1596 {
1597 Self {
1598 inner: Arc::new(hook),
1599 }
1600 }
1601
1602 fn call(&self, command: &mut Command) -> io::Result<()> {
1603 (self.inner)(command)
1604 }
1605}
1606
1607impl fmt::Debug for BeforeSpawnHook {
1608 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1609 write!(f, "<closure>")
1610 }
1611}
1612
1613// An IoContext represents the file descriptors child processes are talking to at execution time.
1614// It's initialized in run(), with dups of the stdin/stdout/stderr pipes, and then passed down to
1615// sub-expressions. Compound expressions will clone() it, and redirections will modify it.
1616#[derive(Debug)]
1617struct IoContext<'a> {
1618 stdin: IoValue,
1619 stdout: IoValue,
1620 stderr: IoValue,
1621 stdout_capture: &'a OutputCaptureContext,
1622 stderr_capture: &'a OutputCaptureContext,
1623 dir: Option<PathBuf>,
1624 env: HashMap<OsString, OsString>,
1625 before_spawn_hooks: Vec<BeforeSpawnHook>,
1626}
1627
1628impl<'a> IoContext<'a> {
1629 // Returns (context, stdout_reader, stderr_reader).
1630 fn new(
1631 stdout_capture: &'a OutputCaptureContext,
1632 stderr_capture: &'a OutputCaptureContext,
1633 ) -> Self {
1634 Self {
1635 stdin: IoValue::ParentStdin,
1636 stdout: IoValue::ParentStdout,
1637 stderr: IoValue::ParentStderr,
1638 stdout_capture,
1639 stderr_capture,
1640 dir: None,
1641 env: std::env::vars_os().collect(),
1642 before_spawn_hooks: Vec::new(),
1643 }
1644 }
1645
1646 fn try_clone(&self) -> io::Result<IoContext<'a>> {
1647 Ok(IoContext {
1648 stdin: self.stdin.try_clone()?,
1649 stdout: self.stdout.try_clone()?,
1650 stderr: self.stderr.try_clone()?,
1651 stdout_capture: self.stdout_capture,
1652 stderr_capture: self.stderr_capture,
1653 dir: self.dir.clone(),
1654 env: self.env.clone(),
1655 before_spawn_hooks: self.before_spawn_hooks.clone(),
1656 })
1657 }
1658}
1659
1660#[derive(Debug)]
1661enum IoValue {
1662 ParentStdin,
1663 ParentStdout,
1664 ParentStderr,
1665 Null,
1666 // We store all handles as File, even when they're e.g. anonymous pipes,
1667 // using the into_file() conversion below. The File type is a very thin
1668 // wrapper around the raw handle, but it gives us try_clone() and drop().
1669 Handle(File),
1670}
1671
1672impl IoValue {
1673 fn try_clone(&self) -> io::Result<IoValue> {
1674 Ok(match self {
1675 IoValue::ParentStdin => IoValue::ParentStdin,
1676 IoValue::ParentStdout => IoValue::ParentStdout,
1677 IoValue::ParentStderr => IoValue::ParentStderr,
1678 IoValue::Null => IoValue::Null,
1679 IoValue::Handle(f) => IoValue::Handle(f.try_clone()?),
1680 })
1681 }
1682
1683 fn into_stdio(self) -> io::Result<Stdio> {
1684 Ok(match self {
1685 IoValue::ParentStdin => os_pipe::dup_stdin()?.into(),
1686 IoValue::ParentStdout => os_pipe::dup_stdout()?.into(),
1687 IoValue::ParentStderr => os_pipe::dup_stderr()?.into(),
1688 IoValue::Null => Stdio::null(),
1689 IoValue::Handle(f) => f.into(),
1690 })
1691 }
1692}
1693
1694// We would rather convert an fd-owning object directly into a
1695// std::process::Stdio, since all you can do with that is give it to a
1696// std::process::Command. Unfortunately, Stdio doesn't provide a try_clone
1697// method, and we need that in order to pass the object to multiple children.
1698// As a workaround, convert the object to a std::fs::File. All we will use this
1699// File for is try_clone and Into<Stdio>, which should be sound on any type of
1700// descriptor. (Some types will lead to an error, like a TcpStream, but that's
1701// not unsound.) If we discover any unsound cases, we might have to replace
1702// this with a new trait.
1703#[cfg(not(windows))]
1704fn into_file<T: IntoRawFd>(handle: T) -> File {
1705 unsafe { File::from_raw_fd(handle.into_raw_fd()) }
1706}
1707#[cfg(windows)]
1708fn into_file<T: IntoRawHandle>(handle: T) -> File {
1709 unsafe { File::from_raw_handle(handle.into_raw_handle()) }
1710}
1711
1712// This struct keeps track of a child exit status, whether or not it's been
1713// unchecked(), and what the command was that gave it (for error messages).
1714#[derive(Clone, Debug)]
1715struct ExpressionStatus {
1716 status: ExitStatus,
1717 checked: bool,
1718 command: String,
1719}
1720
1721impl ExpressionStatus {
1722 fn is_checked_error(&self) -> bool {
1723 self.checked && !self.status.success()
1724 }
1725
1726 fn message(&self) -> String {
1727 format!(
1728 "command {} exited with code {}",
1729 self.command,
1730 self.exit_code_string()
1731 )
1732 }
1733
1734 #[cfg(not(windows))]
1735 fn exit_code_string(&self) -> String {
1736 if self.status.code().is_none() {
1737 return format!("<signal {}>", self.status.signal().unwrap());
1738 }
1739 self.status.code().unwrap().to_string()
1740 }
1741
1742 #[cfg(windows)]
1743 fn exit_code_string(&self) -> String {
1744 self.status.code().unwrap().to_string()
1745 }
1746}
1747
1748fn canonicalize_exe_path_for_dir(exe_name: &OsStr, context: &IoContext) -> io::Result<OsString> {
1749 // There's a tricky interaction between exe paths and `dir`. Exe paths can
1750 // be relative, and so we have to ask: Is an exe path interpreted relative
1751 // to the parent's cwd, or the child's? The answer is that it's platform
1752 // dependent! >.< (Windows uses the parent's cwd, but because of the
1753 // fork-chdir-exec pattern, Unix usually uses the child's.)
1754 //
1755 // We want to use the parent's cwd consistently, because that saves the
1756 // caller from having to worry about whether `dir` will have side effects,
1757 // and because it's easy for the caller to use Path::join if they want to.
1758 // That means that when `dir` is in use, we need to detect exe names that
1759 // are relative paths, and absolutify them. We want to do that as little as
1760 // possible though, both because canonicalization can fail, and because we
1761 // prefer to let the caller control the child's argv[0].
1762 //
1763 // We never want to absolutify a name like "emacs", because that's probably
1764 // a program in the PATH rather than a local file. So we look for slashes
1765 // in the name to determine what's a filepath and what isn't. Note that
1766 // anything given as a std::path::Path will always have a slash by the time
1767 // we get here, because we specialize the IntoExecutablePath trait to
1768 // prepend a ./ to them when they're relative. This leaves the case where
1769 // Windows users might pass a local file like "foo.bat" as a string, which
1770 // we can't distinguish from a global program name. However, because the
1771 // Windows has the preferred "relative to parent's cwd" behavior already,
1772 // this case actually works without our help. (Windows users previously
1773 // needed to watch out for local files shadowing global program names, but
1774 // Rust 1.58 improved this.)
1775
1776 let has_separator = exe_name
1777 .to_string_lossy()
1778 .chars()
1779 .any(std::path::is_separator);
1780 let is_relative = Path::new(exe_name).is_relative();
1781 if context.dir.is_some() && has_separator && is_relative {
1782 Path::new(exe_name).canonicalize().map(Into::into)
1783 } else {
1784 Ok(exe_name.to_owned())
1785 }
1786}
1787
1788// We want to allow Path("foo") to refer to the local file "./foo" on
1789// Unix, and we want to *prevent* Path("echo") from referring to the
1790// global "echo" command on either Unix or Windows. Prepend a dot to all
1791// relative paths to accomplish both of those.
1792fn dotify_relative_exe_path(path: &Path) -> PathBuf {
1793 // This is a no-op if path is absolute or begins with a Windows prefix.
1794 Path::new(".").join(path)
1795}
1796
1797/// An implementation detail of [`cmd`](fn.cmd.html), to distinguish paths from
1798/// other string types.
1799///
1800/// `Path("foo.sh")` means the file named `foo.sh` in the current directory.
1801/// However if you try to execute that path with
1802/// [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html),
1803/// Unix will get upset that it doesn't have a leading `./`. Rust knows that the
1804/// string is a path, but that distinction gets lost by the time execution
1805/// happens.
1806///
1807/// To execute relative paths correctly, duct prepends the `./` to them
1808/// automatically. This trait captures the distinction between the path types
1809/// and other types of strings, which don't get modified. See the trait bounds
1810/// on [`cmd`](fn.cmd.html).
1811pub trait IntoExecutablePath {
1812 fn to_executable(self) -> OsString;
1813}
1814
1815// TODO: Get rid of most of these impls once specialization lands.
1816
1817impl<'a> IntoExecutablePath for &'a Path {
1818 fn to_executable(self) -> OsString {
1819 dotify_relative_exe_path(self).into()
1820 }
1821}
1822
1823impl IntoExecutablePath for PathBuf {
1824 fn to_executable(self) -> OsString {
1825 dotify_relative_exe_path(&self).into()
1826 }
1827}
1828
1829impl<'a> IntoExecutablePath for &'a PathBuf {
1830 fn to_executable(self) -> OsString {
1831 dotify_relative_exe_path(self).into()
1832 }
1833}
1834
1835impl<'a> IntoExecutablePath for &'a str {
1836 fn to_executable(self) -> OsString {
1837 self.into()
1838 }
1839}
1840
1841impl IntoExecutablePath for String {
1842 fn to_executable(self) -> OsString {
1843 self.into()
1844 }
1845}
1846
1847impl<'a> IntoExecutablePath for &'a String {
1848 fn to_executable(self) -> OsString {
1849 self.into()
1850 }
1851}
1852
1853impl<'a> IntoExecutablePath for &'a OsStr {
1854 fn to_executable(self) -> OsString {
1855 self.into()
1856 }
1857}
1858
1859impl IntoExecutablePath for OsString {
1860 fn to_executable(self) -> OsString {
1861 self
1862 }
1863}
1864
1865impl<'a> IntoExecutablePath for &'a OsString {
1866 fn to_executable(self) -> OsString {
1867 self.into()
1868 }
1869}
1870
1871// io::Error doesn't implement clone directly, so we kind of hack it together.
1872fn clone_io_error(error: &io::Error) -> io::Error {
1873 if let Some(code) = error.raw_os_error() {
1874 io::Error::from_raw_os_error(code)
1875 } else {
1876 io::Error::new(error.kind(), error.to_string())
1877 }
1878}
1879
1880#[derive(Clone, Copy, Debug)]
1881enum WaitMode {
1882 Blocking, // like .wait(), wait until everything is finished
1883 NonBlocking, // like .try_wait(), never block
1884}
1885
1886impl WaitMode {
1887 fn maybe_wait_on_child(self, child: &SharedChild) -> io::Result<Option<ExitStatus>> {
1888 match self {
1889 WaitMode::Blocking => child.wait().map(Some),
1890 WaitMode::NonBlocking => child.try_wait(),
1891 }
1892 }
1893
1894 /// Returns Ok(true) if IO finished successfully, Ok(false) if IO is not yet finished (and the
1895 /// mode is not Blocking), or Err(e) if IO failed with an error.
1896 fn maybe_join_io_thread<T>(
1897 self,
1898 io_thread: &SharedThread<io::Result<T>>,
1899 ) -> io::Result<Option<&T>> {
1900 match self {
1901 WaitMode::Blocking => match io_thread.join() {
1902 Ok(val) => Ok(Some(val)),
1903 Err(e) => Err(clone_io_error(e)),
1904 },
1905 WaitMode::NonBlocking => match io_thread.try_join() {
1906 Some(Ok(val)) => Ok(Some(val)),
1907 Some(Err(e)) => Err(clone_io_error(e)),
1908 None => Ok(None),
1909 },
1910 }
1911 }
1912}
1913
1914#[cfg(windows)]
1915fn canonicalize_env_var_name(name: OsString) -> OsString {
1916 // On Windows, because env vars are case-insensitive, we uppercase all env
1917 // var names. That makes assignments and deletions in our internal map work
1918 // the same way they would on the real environment.
1919 match name.into_string() {
1920 Ok(name) => name.to_uppercase().into(),
1921 // If the name isn't valid Unicode then just leave it as is.
1922 Err(name) => name,
1923 }
1924}
1925
1926#[cfg(not(windows))]
1927fn canonicalize_env_var_name(name: OsString) -> OsString {
1928 // No-op on all other platforms.
1929 name
1930}
1931
1932type ReaderThread = SharedThread<io::Result<Vec<u8>>>;
1933
1934#[derive(Debug)]
1935struct OutputCaptureContext {
1936 pair: OnceLock<(os_pipe::PipeReader, os_pipe::PipeWriter)>,
1937}
1938
1939impl OutputCaptureContext {
1940 fn new() -> Self {
1941 Self {
1942 pair: OnceLock::new(),
1943 }
1944 }
1945
1946 fn write_pipe(&self) -> io::Result<os_pipe::PipeWriter> {
1947 // OnceLock::get_or_try_init would be nice if it stabilizes.
1948 match self.pair.get() {
1949 Some((_, writer)) => writer.try_clone(),
1950 None => {
1951 let (reader, writer) = os_pipe::pipe()?;
1952 let clone = writer.try_clone();
1953 self.pair.set((reader, writer)).unwrap();
1954 clone
1955 }
1956 }
1957 }
1958
1959 // Only spawn a read thread if the write pipe was used.
1960 fn maybe_read_thread(self) -> Option<ReaderThread> {
1961 if let Some((mut reader, _)) = self.pair.into_inner() {
1962 Some(SharedThread::spawn(move || {
1963 let mut output = Vec::new();
1964 reader.read_to_end(&mut output)?;
1965 Ok(output)
1966 }))
1967 } else {
1968 None
1969 }
1970 }
1971}
1972
1973/// An incremental reader created with the
1974/// [`Expression::reader`](struct.Expression.html#method.reader) method.
1975///
1976/// When this reader reaches EOF, it automatically calls
1977/// [`wait`](struct.Handle.html#method.wait) on the inner handle. If the child
1978/// returns a non-zero exit status, the read at EOF will return an error,
1979/// unless you use [`unchecked`](struct.Expression.html#method.unchecked).
1980///
1981/// Both `ReaderHandle` and `&ReaderHandle` implement
1982/// [`std::io::Read`](https://doc.rust-lang.org/std/io/trait.Read.html). The
1983/// latter makes it possible for one thread to
1984/// [`kill`](struct.ReaderHandle.html#method.kill) the `ReaderHandle` while
1985/// another thread is reading it. That can be useful for effectively canceling
1986/// the read and unblocking the reader thread. However, note that killed child
1987/// processes return a non-zero exit status, which is an error for the reader
1988/// by default, unless you use
1989/// [`unchecked`](struct.Expression.html#method.unchecked).
1990///
1991/// `ReaderHandle` contains a [`Handle`] internally, and it takes the same
1992/// approach to cleaning up zombie processess on Unix.
1993///
1994/// # Example
1995///
1996/// ```
1997/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1998/// # if cfg!(not(windows)) {
1999/// use duct::cmd;
2000/// use duct::ReaderHandle;
2001/// use std::sync::Arc;
2002/// use std::io::prelude::*;
2003///
2004/// // This child process prints a single byte and then sleeps.
2005/// //
2006/// // CAUTION: Using Bash for this example would probably hang, because Bash
2007/// // would spawn a `sleep` grandchild processes, and that grandchild wouldn't
2008/// // receive the kill signal.
2009/// let python_child = "\
2010/// import sys
2011/// import time
2012/// print()
2013/// sys.stdout.flush()
2014/// time.sleep(24 * 60 * 60)
2015/// ";
2016/// let reader: ReaderHandle = cmd!("python3", "-c", python_child)
2017/// .unchecked()
2018/// .reader()?;
2019///
2020/// // Spawn two threads that both try to read the single byte. Whichever one
2021/// // succeeds then calls kill() to unblock the other.
2022/// let arc_reader: Arc<ReaderHandle> = Arc::new(reader);
2023/// let mut threads = Vec::new();
2024/// for _ in 0..2 {
2025/// let arc_reader = arc_reader.clone();
2026/// threads.push(std::thread::spawn(move || -> std::io::Result<()> {
2027/// let mut single_byte = [0u8];
2028/// (&*arc_reader).read(&mut single_byte)?;
2029/// arc_reader.kill()?;
2030/// Ok(())
2031/// }));
2032/// }
2033///
2034/// // Join both threads. Because of the kill() above, both threads will exit
2035/// // quickly.
2036/// for thread in threads {
2037/// thread.join().unwrap()?;
2038/// }
2039/// # }
2040/// # Ok(())
2041/// # }
2042/// ```
2043#[derive(Debug)]
2044pub struct ReaderHandle {
2045 handle: Handle,
2046 reader: os_pipe::PipeReader,
2047}
2048
2049impl ReaderHandle {
2050 /// Check whether the underlying expression is finished. This is equivalent
2051 /// to [`Handle::try_wait`](struct.Handle.html#method.try_wait). If the
2052 /// `ReaderHandle` has indicated EOF successfully, then it's guaranteed
2053 /// that this method will return `Ok(Some(_))`.
2054 ///
2055 /// Note that the
2056 /// [`stdout`](https://doc.rust-lang.org/std/process/struct.Output.html#structfield.stdout)
2057 /// field of the returned
2058 /// [`Output`](https://doc.rust-lang.org/std/process/struct.Output.html)
2059 /// will always be empty, because the `ReaderHandle` itself owns the
2060 /// child's stdout pipe.
2061 pub fn try_wait(&self) -> io::Result<Option<&Output>> {
2062 self.handle.try_wait()
2063 }
2064
2065 /// Kill all the child processes in the running expression.
2066 ///
2067 /// See [`Handle::kill`]. Note that as with
2068 /// [`std::process::Child::kill`](https://doc.rust-lang.org/beta/std/process/struct.Child.html#method.kill),
2069 /// this does not kill any grandchild processes that the children have
2070 /// spawned on their own. It only kills the child processes that Duct
2071 /// spawned itself. This is **especially relevant** for `ReaderHandle`,
2072 /// because if you're using `kill` to unblock another thread that's
2073 /// reading, an unkilled grandchild process might keep the child's stdout
2074 /// pipe open and keep your reader thread blocked. For that use case, you
2075 /// need to ensure that any grandchild processes your child might spawn are
2076 /// going to be short-lived. See
2077 /// [`gotchas.md`](https://github.com/oconnor663/duct.py/blob/master/gotchas.md)
2078 /// for an extensive discussion of these issues.
2079 pub fn kill(&self) -> io::Result<()> {
2080 self.handle.kill()
2081 }
2082
2083 /// Return a `Vec<u32>` containing the PIDs of all of the child processes.
2084 /// The PIDs are given in pipeline order, from left to right.
2085 pub fn pids(&self) -> Vec<u32> {
2086 self.handle.pids()
2087 }
2088}
2089
2090impl<'a> Read for &'a ReaderHandle {
2091 /// Note that if you don't use
2092 /// [`unchecked`](struct.Expression.html#method.unchecked), and the child
2093 /// returns a non-zero exit status, the final call to `read` will return an
2094 /// error, just as [`run`](struct.Expression.html#method.run) would.
2095 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
2096 let n = (&self.reader).read(buf)?;
2097 if n == 0 && !buf.is_empty() {
2098 // EOF detected. Wait on the child to clean it up before returning.
2099 self.handle.wait()?;
2100 }
2101 Ok(n)
2102 }
2103}
2104
2105impl Read for ReaderHandle {
2106 /// Note that if you don't use
2107 /// [`unchecked`](struct.Expression.html#method.unchecked), and the child
2108 /// returns a non-zero exit status, the final call to `read` will return an
2109 /// error, just as [`run`](struct.Expression.html#method.run) would.
2110 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
2111 (&*self).read(buf)
2112 }
2113}
2114
2115#[cfg(test)]
2116mod test;