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