Skip to main content

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, MutexGuard, 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 From<&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    ///
995    /// # Panics
996    ///
997    /// Panics if `Instant::now() + timeout` overflows.
998    #[cfg(feature = "timeout")]
999    pub fn wait_timeout(&self, timeout: std::time::Duration) -> io::Result<Option<&Output>> {
1000        // `Instant` doesn't currently support saturating operations, so this addition can panic.
1001        // See https://internals.rust-lang.org/t/instant-systemtime-min-max/21375.
1002        let deadline = std::time::Instant::now() + timeout;
1003        self.wait_deadline(deadline)
1004    }
1005
1006    /// Same as [`wait_timeout`][Self::wait_timeout], but with a deadline instead of a timeout.
1007    ///
1008    /// # Errors
1009    ///
1010    /// Same as [`wait`][Self::wait].
1011    #[cfg(feature = "timeout")]
1012    pub fn wait_deadline(&self, deadline: std::time::Instant) -> io::Result<Option<&Output>> {
1013        wait_on_handle_and_output(self, WaitMode::Deadline(deadline))?;
1014        self.try_wait()
1015    }
1016
1017    /// Check whether the running [`Expression`] has already finished. If it has, return a
1018    /// reference to its
1019    /// [`std::process::Output`](https://doc.rust-lang.org/std/process/struct.Output.html).
1020    /// Otherwise, return `Ok(None)`.
1021    ///
1022    /// # Errors
1023    ///
1024    /// Same as [`wait`][Self::wait].
1025    pub fn try_wait(&self) -> io::Result<Option<&Output>> {
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::other(expression_status.message()));
1034        }
1035        Ok(Some(output))
1036    }
1037
1038    /// Same as [`wait`][Self::wait], but consume the `Handle` and return its
1039    /// [`std::process::Output`](https://doc.rust-lang.org/std/process/struct.Output.html) by
1040    /// value. Calling [`start`](struct.Expression.html#method.start) followed by `into_output` is
1041    /// equivalent to [`run`](struct.Expression.html#method.run).
1042    ///
1043    /// # Errors
1044    ///
1045    /// Same as [`wait`][Self::wait].
1046    pub fn into_output(self) -> io::Result<Output> {
1047        self.wait()?;
1048        let (_, output) = self.result.into_inner().expect("result missing");
1049        Ok(output)
1050    }
1051
1052    /// Kill all the child processes in the running [`Expression`].
1053    ///
1054    /// Note that as with
1055    /// [`std::process::Child::kill`](https://doc.rust-lang.org/beta/std/process/struct.Child.html#method.kill),
1056    /// this does not kill any grandchild processes that the children have
1057    /// spawned on their own. It only kills the child processes that Duct
1058    /// spawned itself. See
1059    /// [`gotchas.md`](https://github.com/oconnor663/duct.py/blob/master/gotchas.md)
1060    /// for an extensive discussion of this behavior.
1061    ///
1062    /// This method does not wait on the child processes to exit. Calling [`wait`][Handle::wait]
1063    /// after `kill` usually returns quickly, but there are edge cases where it might not. The most
1064    /// common case is if a grandchild process has inherited one or more of the child's
1065    /// stdin/stdout/stderr pipes, and a worker thread related to
1066    /// [`stdin_bytes`][Expression::stdin_bytes]/[`stdout_capture`][Expression::stdout_capture]/[`stderr_capture`][Expression::stderr_capture]
1067    /// is still running. The kill signal might also be delayed if the child is blocked reading an
1068    /// unresponsive FUSE filesystem, or paused by a debugger.
1069    pub fn kill(&self) -> io::Result<()> {
1070        self.inner.kill()
1071    }
1072
1073    /// Return a `Vec<u32>` containing the PIDs of all of the child processes.
1074    /// The PIDs are given in pipeline order, from left to right.
1075    pub fn pids(&self) -> Vec<u32> {
1076        self.inner.pids()
1077    }
1078}
1079
1080// Wait on the handle and on captured output. Depending on the mode, this wait might or might not
1081// be blocking. This does not do any status checking.
1082fn wait_on_handle_and_output(
1083    handle: &Handle,
1084    mode: WaitMode,
1085) -> io::Result<Option<&(ExpressionStatus, Output)>> {
1086    let Some(status) = handle.inner.wait(mode)? else {
1087        return Ok(None);
1088    };
1089    // We need non-blocking waiters (try_wait) to be able to access the SharedThread IO readers,
1090    // even while a blocking waiter (wait) is blocking, so we can't take RwLock::write until we
1091    // know the threads have already exited.
1092    let shared_lock = handle.readers.read().unwrap();
1093    let (maybe_stdout_reader, maybe_stderr_reader) = &*shared_lock;
1094    if let Some(stdout_reader) = maybe_stdout_reader {
1095        if mode.maybe_join_io_thread(stdout_reader)?.is_none() {
1096            return Ok(None);
1097        }
1098    }
1099    if let Some(stderr_reader) = maybe_stderr_reader {
1100        if mode.maybe_join_io_thread(stderr_reader)?.is_none() {
1101            return Ok(None);
1102        }
1103    }
1104    drop(shared_lock);
1105
1106    // At this point we know that the child and all its IO threads (if any) have exited, so we can
1107    // collect output without blocking. Take the RwLock::write lock and take ownership of the
1108    // output vectors. If another thread has already done this, .unwrap_or_default() will return
1109    // empty Vecs, and result.set() will be a no-op.
1110    let mut unique_lock = handle.readers.write().unwrap();
1111    let (maybe_stdout_reader, maybe_stderr_reader) = &mut *unique_lock;
1112    let stdout: Vec<u8> = maybe_stdout_reader
1113        .take()
1114        .map(SharedThread::into_output)
1115        .transpose()
1116        .expect("IO errors already short-circuited")
1117        .unwrap_or_default();
1118    let stderr: Vec<u8> = maybe_stderr_reader
1119        .take()
1120        .map(SharedThread::into_output)
1121        .transpose()
1122        .expect("IO errors already short-circuited")
1123        .unwrap_or_default();
1124    let output = Output {
1125        status: status.status,
1126        stdout,
1127        stderr,
1128    };
1129    let _ = handle.result.set((status, output)); // might already be set
1130    Ok(handle.result.get())
1131}
1132
1133#[derive(Debug)]
1134enum ExpressionInner {
1135    Cmd(Vec<OsString>),
1136    Pipe(Expression, Expression),
1137    Io(IoExpressionInner, Expression),
1138}
1139
1140impl ExpressionInner {
1141    fn start(&self, context: IoContext) -> io::Result<HandleInner> {
1142        Ok(match self {
1143            Cmd(argv) => HandleInner::Child(ChildHandle::start(argv, context)?),
1144            Pipe(left, right) => {
1145                HandleInner::Pipe(Box::new(PipeHandle::start(left, right, context)?))
1146            }
1147            Io(io_inner, expr) => start_io(io_inner, expr, context)?,
1148        })
1149    }
1150}
1151
1152#[derive(Debug)]
1153enum HandleInner {
1154    Child(ChildHandle),
1155    // If the left side of a pipe fails to start, there's nothing to wait for,
1156    // and we return an error immediately. But if the right side fails to start,
1157    // the caller still needs to wait on the left, and we must return a handle.
1158    // Thus the handle preserves the right side's errors here.
1159    Pipe(Box<PipeHandle>),
1160    StdinBytes(Box<StdinBytesHandle>),
1161    // Why does "uncheckedness" need to be a handle type and not just a field on
1162    // IoContext? Because when one side of a pipe is checked, that side's errors
1163    // take priority over the checked side, even when the pipe expression
1164    // *itself* is also unchecked.
1165    Unchecked(Box<HandleInner>),
1166}
1167
1168impl HandleInner {
1169    fn wait(&self, mode: WaitMode) -> io::Result<Option<ExpressionStatus>> {
1170        match self {
1171            HandleInner::Child(child_handle) => child_handle.wait(mode),
1172            HandleInner::Pipe(pipe_handle) => pipe_handle.wait(mode),
1173            HandleInner::StdinBytes(stdin_bytes_handle) => stdin_bytes_handle.wait(mode),
1174            HandleInner::Unchecked(inner_handle) => {
1175                Ok(inner_handle.wait(mode)?.map(|mut status| {
1176                    status.checked = false;
1177                    status
1178                }))
1179            }
1180        }
1181    }
1182
1183    fn kill(&self) -> io::Result<()> {
1184        match self {
1185            HandleInner::Child(child_handle) => child_handle.kill(),
1186            HandleInner::Pipe(pipe_handle) => pipe_handle.kill(),
1187            HandleInner::StdinBytes(stdin_bytes_handle) => stdin_bytes_handle.kill(),
1188            HandleInner::Unchecked(inner_handle) => inner_handle.kill(),
1189        }
1190    }
1191
1192    fn pids(&self) -> Vec<u32> {
1193        match self {
1194            HandleInner::Child(child_handle) => vec![child_handle.child().id()],
1195            HandleInner::Pipe(pipe_handle) => pipe_handle.pids(),
1196            HandleInner::StdinBytes(stdin_bytes_handle) => stdin_bytes_handle.inner_handle.pids(),
1197            HandleInner::Unchecked(inner_handle) => inner_handle.pids(),
1198        }
1199    }
1200}
1201
1202// Use std::process::Child instead of SharedChild to avoid taking extra locks when we poll.
1203#[cfg(not(windows))]
1204static LEAKED_CHILDREN: RwLock<Vec<std::process::Child>> = RwLock::new(Vec::new());
1205
1206// In `impl Drop for ChildHandle` below, children who are still running when they're dropped get
1207// added to this global list. Poll the list to reap as many zombie children as possible each time
1208// we spawn a new child process. See the comments in the Drop impl regarding tradeoffs.
1209#[cfg(not(windows))]
1210fn cleanup_leaked_children() {
1211    if !LEAKED_CHILDREN.read().unwrap().is_empty() {
1212        LEAKED_CHILDREN.write().unwrap().retain_mut(|child| {
1213            match child.try_wait() {
1214                Ok(Some(_)) => false, // remove
1215                Ok(None) => true,     // retain
1216                Err(e) => {
1217                    // try_wait errors require odd circumstances to trigger. For example, something
1218                    // else might call libc::waitpid (or its safe wrapper from `nix`) and reap the
1219                    // child, causing us to get a "process not found" error here. If that happens
1220                    // in a test, go ahead and panic, but otherwise ignore the error. The most
1221                    // important thing is that we don't leave the whole process in a broken state
1222                    // where every future call to ChildHandle::start returns an error. Also, it
1223                    // might not be helpful or appropriate to report this error to our caller.
1224                    // Remember that this is lazy, global cleanup of some state that might belong
1225                    // to some other thread, running code from some other crate. Let it go...
1226                    if cfg!(test) {
1227                        panic!("cleanup_leaked_children failed: {e}");
1228                    }
1229                    false // remove
1230                }
1231            }
1232        });
1233    }
1234}
1235
1236#[derive(Debug)]
1237struct ChildHandle {
1238    child: Option<shared_child::SharedChild>, // optional so that `drop` can take ownership
1239    command_string: String,
1240}
1241
1242impl ChildHandle {
1243    fn start(argv: &[OsString], context: IoContext) -> io::Result<ChildHandle> {
1244        // See comments in the Drop impl below.
1245        #[cfg(not(windows))]
1246        cleanup_leaked_children();
1247
1248        let exe = canonicalize_exe_path_for_dir(&argv[0], &context)?;
1249        let mut command = Command::new(exe);
1250        command.args(&argv[1..]);
1251        if !matches!(context.stdin, IoValue::ParentStdin) {
1252            command.stdin(context.stdin.into_stdio()?);
1253        }
1254        if !matches!(context.stdout, IoValue::ParentStdout) {
1255            command.stdout(context.stdout.into_stdio()?);
1256        }
1257        if !matches!(context.stderr, IoValue::ParentStderr) {
1258            command.stderr(context.stderr.into_stdio()?);
1259        }
1260        if let Some(dir) = context.dir {
1261            command.current_dir(dir);
1262        }
1263        command.env_clear();
1264        for (name, val) in context.env {
1265            command.env(name, val);
1266        }
1267        // The innermost hooks are pushed last, and we execute them last.
1268        for hook in context.before_spawn_hooks.iter() {
1269            hook.call(&mut command)?;
1270        }
1271
1272        // See comments below about why we take this lock (macOS only).
1273        let spawn_guard = pipe_and_spawn_lock_guard();
1274        let shared_child = SharedChild::spawn(&mut command)?;
1275        drop(spawn_guard);
1276
1277        let command_string = format!("{:?}", argv);
1278        Ok(ChildHandle {
1279            child: Some(shared_child),
1280            command_string,
1281        })
1282    }
1283
1284    // a helper to reduce the need for .as_ref().unwrap() everywhere
1285    fn child(&self) -> &SharedChild {
1286        self.child
1287            .as_ref()
1288            .expect("ChildHandle should not yet have been dropped")
1289    }
1290
1291    fn wait(&self, mode: WaitMode) -> io::Result<Option<ExpressionStatus>> {
1292        let maybe_status = mode.maybe_wait_on_child(self.child())?;
1293        if let Some(status) = maybe_status {
1294            Ok(Some(ExpressionStatus {
1295                status,
1296                checked: true,
1297                command: self.command_string.clone(),
1298            }))
1299        } else {
1300            Ok(None)
1301        }
1302    }
1303
1304    fn kill(&self) -> io::Result<()> {
1305        self.child().kill()
1306    }
1307}
1308
1309// Use Drop to prevent zombie processes on Unix. Zombies aren't a thing on Windows, so omit the
1310// entire impl there.
1311#[cfg(not(windows))]
1312impl Drop for ChildHandle {
1313    fn drop(&mut self) {
1314        let child = self.child.take().expect("only drop should take the child");
1315        match child.try_wait() {
1316            // The child has been cleaned up. Cool.
1317            Ok(Some(_)) => (),
1318
1319            // Ignore IO errors here unless we're running tests. It's hard to provoke these without
1320            // doing something very weird (transmute, libc::waitpid, etc).
1321            Err(e) => {
1322                if cfg!(test) {
1323                    panic!("ChildHandle cleanup failed: {e}");
1324                }
1325            }
1326
1327            // This child is still running, but the caller is never going to wait on it. Add it to
1328            // a global list of (potentially) zombie children. We poll this list whenever we spawn
1329            // new child processes, to mitigate leaks. This is the same strategy used in the
1330            // CPython `subprocess` module:
1331            //   - https://github.com/python/cpython/blob/v3.13.3/Lib/subprocess.py#L1133-L1146
1332            //   - https://github.com/python/cpython/blob/v3.13.3/Lib/subprocess.py#L268-L285
1333            // The main downside of this strategy is that spawning N child processes becomes O(N^2)
1334            // if you leak all of them and they're all long-lived. I think that's an ok tradeoff
1335            // for a couple of reasons:
1336            //   1. Dropping un-waited-for child handles isn't usually what you want to be doing in
1337            //      your happy path, because it means you can't hear about error codes your
1338            //      children return. Children whose handles are retained don't enter this list and
1339            //      don't contribute to O(N^2) behavior.
1340            //   2. Callers who do "very advanced" things (say, a systemd clone) probably shouldn't
1341            //      be using Duct. They need more fine-grained control than Duct is designed to
1342            //      provide.
1343            Ok(None) => LEAKED_CHILDREN.write().unwrap().push(child.into_inner()),
1344        }
1345    }
1346}
1347
1348#[derive(Debug)]
1349struct PipeHandle {
1350    left_handle: HandleInner,
1351    right_handle: HandleInner,
1352}
1353
1354impl PipeHandle {
1355    fn start(left: &Expression, right: &Expression, context: IoContext) -> io::Result<PipeHandle> {
1356        let (reader, writer) = open_pipe_protected()?;
1357        // dup'ing stdin/stdout isn't strictly necessary, but no big deal
1358        let mut left_context = context.try_clone()?;
1359        left_context.stdout = IoValue::Handle(writer.into());
1360        let mut right_context = context;
1361        right_context.stdin = IoValue::Handle(reader.into());
1362        let left_handle = left.0.start(left_context)?;
1363        // The left side has already started. If we fail to start the right
1364        // side, ChildHandle::drop will clean it up one way or another. Note
1365        // that `right_context` is passed by value here, so if start() returns
1366        // an error, all pipe readers will already have been closed, and
1367        // there's a decent chance the left side will exit quickly via EPIPE
1368        // before we .try_wait() it in ChildHandle::drop.
1369        let right_handle = right.0.start(right_context)?;
1370        Ok(PipeHandle {
1371            left_handle,
1372            right_handle,
1373        })
1374    }
1375
1376    fn wait(&self, mode: WaitMode) -> io::Result<Option<ExpressionStatus>> {
1377        // Wait on both sides first, without propagating any errors.
1378        let left_wait_result = self.left_handle.wait(mode);
1379        let right_wait_result = self.right_handle.wait(mode);
1380
1381        // Now we deal with errors from either of those waits. The left wait
1382        // happened first, so that one takes precedence. Note that this is the
1383        // reverse order of exit status precedence.
1384        let left_status = left_wait_result?;
1385        let right_status = right_wait_result?;
1386
1387        // If both waits succeeded, return one of the two statuses.
1388        Ok(pipe_status_precedence(left_status, right_status))
1389    }
1390
1391    // As with wait, we need to call kill on both sides even if the left side
1392    // returns an error.
1393    fn kill(&self) -> io::Result<()> {
1394        let left_kill_result = self.left_handle.kill();
1395        let right_kill_result = self.right_handle.kill();
1396        // As with wait, the left side happened first, so its errors take
1397        // precedence.
1398        left_kill_result.and(right_kill_result)
1399    }
1400
1401    fn pids(&self) -> Vec<u32> {
1402        let mut pids = self.left_handle.pids();
1403        pids.extend_from_slice(&self.right_handle.pids());
1404        pids
1405    }
1406}
1407
1408// The rules of precedence are:
1409// 1) If either side unfinished, the result is unfinished.
1410// 2) Checked errors trump unchecked errors.
1411// 3) Any errors trump success.
1412// 4) All else equal, the right side wins (as in Bash).
1413fn pipe_status_precedence(
1414    left_maybe_status: Option<ExpressionStatus>,
1415    right_maybe_status: Option<ExpressionStatus>,
1416) -> Option<ExpressionStatus> {
1417    let (left_status, right_status) = match (left_maybe_status, right_maybe_status) {
1418        (Some(left), Some(right)) => (left, right),
1419        _ => return None,
1420    };
1421    Some(if right_status.is_checked_error() {
1422        right_status
1423    } else if left_status.is_checked_error() {
1424        left_status
1425    } else if !right_status.status.success() {
1426        right_status
1427    } else {
1428        left_status
1429    })
1430}
1431
1432fn start_io(
1433    io_inner: &IoExpressionInner,
1434    expr_inner: &Expression,
1435    mut context: IoContext,
1436) -> io::Result<HandleInner> {
1437    match io_inner {
1438        StdinBytes(v) => {
1439            return Ok(HandleInner::StdinBytes(Box::new(StdinBytesHandle::start(
1440                expr_inner,
1441                context,
1442                Arc::clone(v),
1443            )?)));
1444        }
1445        StdinPath(p) => {
1446            context.stdin = IoValue::Handle(File::open(p)?.into());
1447        }
1448        StdinFile(f) => {
1449            context.stdin = IoValue::Handle(f.try_clone()?);
1450        }
1451        StdinNull => {
1452            context.stdin = IoValue::Null;
1453        }
1454        StdoutPath(p) => {
1455            context.stdout = IoValue::Handle(File::create(p)?.into());
1456        }
1457        StdoutFile(f) => {
1458            context.stdout = IoValue::Handle(f.try_clone()?);
1459        }
1460        StdoutNull => {
1461            context.stdout = IoValue::Null;
1462        }
1463        StdoutCapture => {
1464            context.stdout = IoValue::Handle(context.stdout_capture.write_pipe()?.into());
1465        }
1466        StdoutToStderr => {
1467            context.stdout = context.stderr.try_clone()?;
1468        }
1469        StderrPath(p) => {
1470            context.stderr = IoValue::Handle(File::create(p)?.into());
1471        }
1472        StderrFile(f) => {
1473            context.stderr = IoValue::Handle(f.try_clone()?);
1474        }
1475        StderrNull => {
1476            context.stderr = IoValue::Null;
1477        }
1478        StderrCapture => {
1479            context.stderr = IoValue::Handle(context.stderr_capture.write_pipe()?.into());
1480        }
1481        StderrToStdout => {
1482            context.stderr = context.stdout.try_clone()?;
1483        }
1484        StdoutStderrSwap => {
1485            mem::swap(&mut context.stdout, &mut context.stderr);
1486        }
1487        Dir(p) => {
1488            context.dir = Some(p.clone());
1489        }
1490        Env(name, val) => {
1491            // Note that HashMap::insert overwrites a preexisting *value*, but not a preexisting
1492            // *key*. We rely on this to match platform behavior on Windows, where the original
1493            // casing of a variable name is preserved even if an equivalent name with a different
1494            // casing is added.
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) = open_pipe_protected()?;
1527        context.stdin = IoValue::Handle(reader.into());
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, some grandchild process might keep this pipe open and keep
1545        // reading from it. It's tempting not to wait for this IO thread at all and just let it run
1546        // in the background, but that wouldn't work if the current process exited. (When the main
1547        // function returns and the process exits, all background threads are terminated.) Waiting
1548        // on this Handle might be the last thing the caller does in main, who knows, so for
1549        // correctness a blocking waiter does need to wait until this IO thread is finished.
1550        let io_finished = mode.maybe_join_io_thread(&self.writer_thread)?.is_some();
1551        if !io_finished {
1552            return Ok(None);
1553        }
1554        Ok(maybe_status)
1555    }
1556
1557    fn kill(&self) -> io::Result<()> {
1558        self.inner_handle.kill()
1559    }
1560}
1561
1562#[derive(Debug)]
1563enum IoExpressionInner {
1564    StdinBytes(Arc<Vec<u8>>),
1565    StdinPath(PathBuf),
1566    StdinFile(FdOrHandle),
1567    StdinNull,
1568    StdoutPath(PathBuf),
1569    StdoutFile(FdOrHandle),
1570    StdoutNull,
1571    StdoutCapture,
1572    StdoutToStderr,
1573    StderrPath(PathBuf),
1574    StderrFile(FdOrHandle),
1575    StderrNull,
1576    StderrCapture,
1577    StderrToStdout,
1578    StdoutStderrSwap,
1579    Dir(PathBuf),
1580    Env(EnvNameString, OsString),
1581    EnvRemove(EnvNameString),
1582    FullEnv(HashMap<EnvNameString, OsString>),
1583    Unchecked,
1584    BeforeSpawn(BeforeSpawnHook),
1585}
1586
1587type HookFn = Arc<dyn Fn(&mut Command) -> io::Result<()> + Send + Sync>;
1588
1589#[derive(Clone)]
1590struct BeforeSpawnHook {
1591    inner: HookFn,
1592}
1593
1594impl BeforeSpawnHook {
1595    fn new<F>(hook: F) -> Self
1596    where
1597        F: Fn(&mut Command) -> io::Result<()> + Send + Sync + 'static,
1598    {
1599        Self {
1600            inner: Arc::new(hook),
1601        }
1602    }
1603
1604    fn call(&self, command: &mut Command) -> io::Result<()> {
1605        (self.inner)(command)
1606    }
1607}
1608
1609impl fmt::Debug for BeforeSpawnHook {
1610    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1611        write!(f, "<closure>")
1612    }
1613}
1614
1615// An IoContext represents the file descriptors child processes are talking to at execution time.
1616// It's initialized in run(), with dups of the stdin/stdout/stderr pipes, and then passed down to
1617// sub-expressions. Compound expressions will clone() it, and redirections will modify it.
1618#[derive(Debug)]
1619struct IoContext<'a> {
1620    stdin: IoValue,
1621    stdout: IoValue,
1622    stderr: IoValue,
1623    stdout_capture: &'a OutputCaptureContext,
1624    stderr_capture: &'a OutputCaptureContext,
1625    dir: Option<PathBuf>,
1626    env: HashMap<EnvNameString, OsString>,
1627    before_spawn_hooks: Vec<BeforeSpawnHook>,
1628}
1629
1630impl<'a> IoContext<'a> {
1631    // Returns (context, stdout_reader, stderr_reader).
1632    fn new(
1633        stdout_capture: &'a OutputCaptureContext,
1634        stderr_capture: &'a OutputCaptureContext,
1635    ) -> Self {
1636        Self {
1637            stdin: IoValue::ParentStdin,
1638            stdout: IoValue::ParentStdout,
1639            stderr: IoValue::ParentStderr,
1640            stdout_capture,
1641            stderr_capture,
1642            dir: None,
1643            env: std::env::vars_os().map(|(k, v)| (k.into(), v)).collect(),
1644            before_spawn_hooks: Vec::new(),
1645        }
1646    }
1647
1648    fn try_clone(&self) -> io::Result<IoContext<'a>> {
1649        Ok(IoContext {
1650            stdin: self.stdin.try_clone()?,
1651            stdout: self.stdout.try_clone()?,
1652            stderr: self.stderr.try_clone()?,
1653            stdout_capture: self.stdout_capture,
1654            stderr_capture: self.stderr_capture,
1655            dir: self.dir.clone(),
1656            env: self.env.clone(),
1657            before_spawn_hooks: self.before_spawn_hooks.clone(),
1658        })
1659    }
1660}
1661
1662#[derive(Debug)]
1663enum IoValue {
1664    ParentStdin,
1665    ParentStdout,
1666    ParentStderr,
1667    Null,
1668    Handle(FdOrHandle),
1669}
1670
1671impl IoValue {
1672    fn try_clone(&self) -> io::Result<IoValue> {
1673        Ok(match self {
1674            IoValue::ParentStdin => IoValue::ParentStdin,
1675            IoValue::ParentStdout => IoValue::ParentStdout,
1676            IoValue::ParentStderr => IoValue::ParentStderr,
1677            IoValue::Null => IoValue::Null,
1678            IoValue::Handle(f) => IoValue::Handle(f.try_clone()?),
1679        })
1680    }
1681
1682    fn into_stdio(self) -> io::Result<Stdio> {
1683        Ok(match self {
1684            IoValue::ParentStdin => os_pipe::dup_stdin()?.into(),
1685            IoValue::ParentStdout => os_pipe::dup_stdout()?.into(),
1686            IoValue::ParentStderr => os_pipe::dup_stderr()?.into(),
1687            IoValue::Null => Stdio::null(),
1688            IoValue::Handle(f) => f.into(),
1689        })
1690    }
1691}
1692
1693// This struct keeps track of a child exit status, whether or not it's been
1694// unchecked(), and what the command was that gave it (for error messages).
1695#[derive(Clone, Debug)]
1696struct ExpressionStatus {
1697    status: ExitStatus,
1698    checked: bool,
1699    command: String,
1700}
1701
1702impl ExpressionStatus {
1703    fn is_checked_error(&self) -> bool {
1704        self.checked && !self.status.success()
1705    }
1706
1707    fn message(&self) -> String {
1708        format!(
1709            "command {} exited with code {}",
1710            self.command,
1711            self.exit_code_string()
1712        )
1713    }
1714
1715    #[cfg(not(windows))]
1716    fn exit_code_string(&self) -> String {
1717        if self.status.code().is_none() {
1718            return format!("<signal {}>", self.status.signal().unwrap());
1719        }
1720        self.status.code().unwrap().to_string()
1721    }
1722
1723    #[cfg(windows)]
1724    fn exit_code_string(&self) -> String {
1725        self.status.code().unwrap().to_string()
1726    }
1727}
1728
1729fn canonicalize_exe_path_for_dir(exe_name: &OsStr, context: &IoContext) -> io::Result<OsString> {
1730    // There's a tricky interaction between exe paths and `dir`. Exe paths can
1731    // be relative, and so we have to ask: Is an exe path interpreted relative
1732    // to the parent's cwd, or the child's? The answer is that it's platform
1733    // dependent! >.< (Windows uses the parent's cwd, but because of the
1734    // fork-chdir-exec pattern, Unix usually uses the child's.)
1735    //
1736    // We want to use the parent's cwd consistently, because that saves the
1737    // caller from having to worry about whether `dir` will have side effects,
1738    // and because it's easy for the caller to use Path::join if they want to.
1739    // That means that when `dir` is in use, we need to detect exe names that
1740    // are relative paths, and absolutify them. We want to do that as little as
1741    // possible though, both because canonicalization can fail, and because we
1742    // prefer to let the caller control the child's argv[0].
1743    //
1744    // We never want to absolutify a name like "emacs", because that's probably
1745    // a program in the PATH rather than a local file. So we look for slashes
1746    // in the name to determine what's a filepath and what isn't. Note that
1747    // anything given as a std::path::Path will always have a slash by the time
1748    // we get here, because we specialize the IntoExecutablePath trait to
1749    // prepend a ./ to them when they're relative. This leaves the case where
1750    // Windows users might pass a local file like "foo.bat" as a string, which
1751    // we can't distinguish from a global program name. However, because the
1752    // Windows has the preferred "relative to parent's cwd" behavior already,
1753    // this case actually works without our help. (Windows users previously
1754    // needed to watch out for local files shadowing global program names, but
1755    // Rust 1.58 improved this.)
1756
1757    let has_separator = exe_name
1758        .to_string_lossy()
1759        .chars()
1760        .any(std::path::is_separator);
1761    let is_relative = Path::new(exe_name).is_relative();
1762    if context.dir.is_some() && has_separator && is_relative {
1763        Path::new(exe_name).canonicalize().map(Into::into)
1764    } else {
1765        Ok(exe_name.to_owned())
1766    }
1767}
1768
1769// We want to allow Path("foo") to refer to the local file "./foo" on
1770// Unix, and we want to *prevent* Path("echo") from referring to the
1771// global "echo" command on either Unix or Windows. Prepend a dot to all
1772// relative paths to accomplish both of those.
1773fn dotify_relative_exe_path(path: &Path) -> PathBuf {
1774    // This is a no-op if path is absolute or begins with a Windows prefix.
1775    Path::new(".").join(path)
1776}
1777
1778/// An implementation detail of [`cmd`](fn.cmd.html), to distinguish paths from
1779/// other string types.
1780///
1781/// `Path("foo.sh")` means the file named `foo.sh` in the current directory.
1782/// However if you try to execute that path with
1783/// [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html),
1784/// Unix will get upset that it doesn't have a leading `./`. Rust knows that the
1785/// string is a path, but that distinction gets lost by the time execution
1786/// happens.
1787///
1788/// To execute relative paths correctly, duct prepends the `./` to them
1789/// automatically. This trait captures the distinction between the path types
1790/// and other types of strings, which don't get modified. See the trait bounds
1791/// on [`cmd`](fn.cmd.html).
1792pub trait IntoExecutablePath {
1793    fn to_executable(self) -> OsString;
1794}
1795
1796// TODO: Get rid of most of these impls once specialization lands.
1797
1798impl IntoExecutablePath for &Path {
1799    fn to_executable(self) -> OsString {
1800        dotify_relative_exe_path(self).into()
1801    }
1802}
1803
1804impl IntoExecutablePath for PathBuf {
1805    fn to_executable(self) -> OsString {
1806        dotify_relative_exe_path(&self).into()
1807    }
1808}
1809
1810impl IntoExecutablePath for &PathBuf {
1811    fn to_executable(self) -> OsString {
1812        dotify_relative_exe_path(self).into()
1813    }
1814}
1815
1816impl IntoExecutablePath for &str {
1817    fn to_executable(self) -> OsString {
1818        self.into()
1819    }
1820}
1821
1822impl IntoExecutablePath for String {
1823    fn to_executable(self) -> OsString {
1824        self.into()
1825    }
1826}
1827
1828impl IntoExecutablePath for &String {
1829    fn to_executable(self) -> OsString {
1830        self.into()
1831    }
1832}
1833
1834impl IntoExecutablePath for &OsStr {
1835    fn to_executable(self) -> OsString {
1836        self.into()
1837    }
1838}
1839
1840impl IntoExecutablePath for OsString {
1841    fn to_executable(self) -> OsString {
1842        self
1843    }
1844}
1845
1846impl IntoExecutablePath for &OsString {
1847    fn to_executable(self) -> OsString {
1848        self.into()
1849    }
1850}
1851
1852// io::Error doesn't implement clone directly, so we kind of hack it together.
1853fn clone_io_error(error: &io::Error) -> io::Error {
1854    if let Some(code) = error.raw_os_error() {
1855        io::Error::from_raw_os_error(code)
1856    } else {
1857        io::Error::new(error.kind(), error.to_string())
1858    }
1859}
1860
1861#[derive(Clone, Copy, Debug)]
1862enum WaitMode {
1863    // block until everything is finished, as in .wait()
1864    Blocking,
1865    // don't block at all, as in .try_wait()
1866    NonBlocking,
1867    // block with a deadline, as in .wait_deadline() or .wait_timeout()
1868    #[cfg(feature = "timeout")]
1869    Deadline(std::time::Instant),
1870}
1871
1872impl WaitMode {
1873    fn maybe_wait_on_child(self, child: &SharedChild) -> io::Result<Option<ExitStatus>> {
1874        match self {
1875            WaitMode::Blocking => child.wait().map(Some),
1876            WaitMode::NonBlocking => child.try_wait(),
1877            #[cfg(feature = "timeout")]
1878            WaitMode::Deadline(deadline) => child.wait_deadline(deadline),
1879        }
1880    }
1881
1882    /// Returns Ok(true) if IO finished successfully, Ok(false) if IO is not yet finished (and the
1883    /// mode is not Blocking), or Err(e) if IO failed with an error.
1884    fn maybe_join_io_thread<T>(
1885        self,
1886        io_thread: &SharedThread<io::Result<T>>,
1887    ) -> io::Result<Option<&T>> {
1888        match self {
1889            WaitMode::Blocking => match io_thread.join() {
1890                Ok(val) => Ok(Some(val)),
1891                Err(e) => Err(clone_io_error(e)),
1892            },
1893            WaitMode::NonBlocking => match io_thread.try_join() {
1894                Some(Ok(val)) => Ok(Some(val)),
1895                Some(Err(e)) => Err(clone_io_error(e)),
1896                None => Ok(None),
1897            },
1898            #[cfg(feature = "timeout")]
1899            WaitMode::Deadline(deadline) => match io_thread.join_deadline(deadline) {
1900                Some(Ok(val)) => Ok(Some(val)),
1901                Some(Err(e)) => Err(clone_io_error(e)),
1902                None => Ok(None),
1903            },
1904        }
1905    }
1906}
1907
1908type ReaderThread = SharedThread<io::Result<Vec<u8>>>;
1909
1910#[derive(Debug)]
1911struct OutputCaptureContext {
1912    pair: OnceLock<(os_pipe::PipeReader, os_pipe::PipeWriter)>,
1913}
1914
1915impl OutputCaptureContext {
1916    fn new() -> Self {
1917        Self {
1918            pair: OnceLock::new(),
1919        }
1920    }
1921
1922    fn write_pipe(&self) -> io::Result<os_pipe::PipeWriter> {
1923        // OnceLock::get_or_try_init would be nice if it stabilizes.
1924        match self.pair.get() {
1925            Some((_, writer)) => writer.try_clone(),
1926            None => {
1927                let (reader, writer) = open_pipe_protected()?;
1928                let clone = writer.try_clone();
1929                self.pair.set((reader, writer)).unwrap();
1930                clone
1931            }
1932        }
1933    }
1934
1935    // Only spawn a read thread if the write pipe was used.
1936    fn maybe_read_thread(self) -> Option<ReaderThread> {
1937        if let Some((mut reader, _)) = self.pair.into_inner() {
1938            Some(SharedThread::spawn(move || {
1939                let mut output = Vec::new();
1940                reader.read_to_end(&mut output)?;
1941                Ok(output)
1942            }))
1943        } else {
1944            None
1945        }
1946    }
1947}
1948
1949/// An incremental reader created with the
1950/// [`Expression::reader`](struct.Expression.html#method.reader) method.
1951///
1952/// When this reader reaches EOF, it automatically calls
1953/// [`wait`](struct.Handle.html#method.wait) on the inner handle. If the child
1954/// returns a non-zero exit status, the read at EOF will return an error,
1955/// unless you use [`unchecked`](struct.Expression.html#method.unchecked).
1956///
1957/// Both `ReaderHandle` and `&ReaderHandle` implement
1958/// [`std::io::Read`](https://doc.rust-lang.org/std/io/trait.Read.html). The
1959/// latter makes it possible for one thread to
1960/// [`kill`](struct.ReaderHandle.html#method.kill) the `ReaderHandle` while
1961/// another thread is reading it. That can be useful for effectively canceling
1962/// the read and unblocking the reader thread. However, note that killed child
1963/// processes return a non-zero exit status, which is an error for the reader
1964/// by default, unless you use
1965/// [`unchecked`](struct.Expression.html#method.unchecked).
1966///
1967/// `ReaderHandle` contains a [`Handle`] internally, and it takes the same
1968/// approach to cleaning up zombie processess on Unix.
1969///
1970/// # Example
1971///
1972/// ```
1973/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1974/// # if cfg!(not(windows)) {
1975/// use duct::cmd;
1976/// use duct::ReaderHandle;
1977/// use std::sync::Arc;
1978/// use std::io::prelude::*;
1979///
1980/// // This child process prints a single byte and then sleeps.
1981/// //
1982/// // CAUTION: Using Bash for this example would probably hang, because Bash
1983/// // would spawn a `sleep` grandchild processes, and that grandchild wouldn't
1984/// // receive the kill signal.
1985/// let python_child = "\
1986/// import sys
1987/// import time
1988/// print()
1989/// sys.stdout.flush()
1990/// time.sleep(24 * 60 * 60)
1991/// ";
1992/// let reader: ReaderHandle = cmd!("python3", "-c", python_child)
1993///     .unchecked()
1994///     .reader()?;
1995///
1996/// // Spawn two threads that both try to read the single byte. Whichever one
1997/// // succeeds then calls kill() to unblock the other.
1998/// let arc_reader: Arc<ReaderHandle> = Arc::new(reader);
1999/// let mut threads = Vec::new();
2000/// for _ in 0..2 {
2001///     let arc_reader = arc_reader.clone();
2002///     threads.push(std::thread::spawn(move || -> std::io::Result<()> {
2003///         let mut single_byte = [0u8];
2004///         (&*arc_reader).read(&mut single_byte)?;
2005///         arc_reader.kill()?;
2006///         Ok(())
2007///     }));
2008/// }
2009///
2010/// // Join both threads. Because of the kill() above, both threads will exit
2011/// // quickly.
2012/// for thread in threads {
2013///     thread.join().unwrap()?;
2014/// }
2015/// # }
2016/// # Ok(())
2017/// # }
2018/// ```
2019#[derive(Debug)]
2020pub struct ReaderHandle {
2021    handle: Handle,
2022    reader: os_pipe::PipeReader,
2023}
2024
2025impl ReaderHandle {
2026    /// Check whether the underlying expression is finished. This is equivalent
2027    /// to [`Handle::try_wait`](struct.Handle.html#method.try_wait). If the
2028    /// `ReaderHandle` has indicated EOF successfully, then it's guaranteed
2029    /// that this method will return `Ok(Some(_))`.
2030    ///
2031    /// Note that the
2032    /// [`stdout`](https://doc.rust-lang.org/std/process/struct.Output.html#structfield.stdout)
2033    /// field of the returned
2034    /// [`Output`](https://doc.rust-lang.org/std/process/struct.Output.html)
2035    /// will always be empty, because the `ReaderHandle` itself owns the
2036    /// child's stdout pipe.
2037    pub fn try_wait(&self) -> io::Result<Option<&Output>> {
2038        self.handle.try_wait()
2039    }
2040
2041    /// Kill all the child processes in the running expression.
2042    ///
2043    /// See [`Handle::kill`]. Note that as with
2044    /// [`std::process::Child::kill`](https://doc.rust-lang.org/beta/std/process/struct.Child.html#method.kill),
2045    /// this does not kill any grandchild processes that the children have
2046    /// spawned on their own. It only kills the child processes that Duct
2047    /// spawned itself. This is **especially relevant** for `ReaderHandle`,
2048    /// because if you're using `kill` to unblock another thread that's
2049    /// reading, an unkilled grandchild process might keep the child's stdout
2050    /// pipe open and keep your reader thread blocked. For that use case, you
2051    /// need to ensure that any grandchild processes your child might spawn are
2052    /// going to be short-lived. See
2053    /// [`gotchas.md`](https://github.com/oconnor663/duct.py/blob/master/gotchas.md)
2054    /// for an extensive discussion of these issues.
2055    pub fn kill(&self) -> io::Result<()> {
2056        self.handle.kill()
2057    }
2058
2059    /// Return a `Vec<u32>` containing the PIDs of all of the child processes.
2060    /// The PIDs are given in pipeline order, from left to right.
2061    pub fn pids(&self) -> Vec<u32> {
2062        self.handle.pids()
2063    }
2064}
2065
2066impl Read for &ReaderHandle {
2067    /// Note that if you don't use
2068    /// [`unchecked`](struct.Expression.html#method.unchecked), and the child
2069    /// returns a non-zero exit status, the final call to `read` will return an
2070    /// error, just as [`run`](struct.Expression.html#method.run) would.
2071    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
2072        let n = (&self.reader).read(buf)?;
2073        if n == 0 && !buf.is_empty() {
2074            // EOF detected. Wait on the child to clean it up before returning.
2075            self.handle.wait()?;
2076        }
2077        Ok(n)
2078    }
2079}
2080
2081impl Read for ReaderHandle {
2082    /// Note that if you don't use
2083    /// [`unchecked`](struct.Expression.html#method.unchecked), and the child
2084    /// returns a non-zero exit status, the final call to `read` will return an
2085    /// error, just as [`run`](struct.Expression.html#method.run) would.
2086    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
2087        (&*self).read(buf)
2088    }
2089}
2090
2091#[cfg(not(windows))]
2092type FdOrHandle = OwnedFd;
2093#[cfg(windows)]
2094type FdOrHandle = OwnedHandle;
2095
2096// Without these conversions this crate could be 100% safe code, so this is kind of a shame, but I
2097// don't want to change the trait bounds on stdin_file/stdout_file/stderr_file. There are types
2098// that implement IntoRawFd but not Into<OwnedFd>, including RawFd itself.
2099#[cfg(not(windows))]
2100fn owned_from_raw(raw: impl IntoRawFd) -> OwnedFd {
2101    unsafe { OwnedFd::from_raw_fd(raw.into_raw_fd()) }
2102}
2103#[cfg(windows)]
2104fn owned_from_raw(raw: impl IntoRawHandle) -> OwnedHandle {
2105    unsafe { OwnedHandle::from_raw_handle(raw.into_raw_handle()) }
2106}
2107
2108fn open_pipe_protected() -> io::Result<(os_pipe::PipeReader, os_pipe::PipeWriter)> {
2109    let _guard = pipe_and_spawn_lock_guard(); // See comments below.
2110    os_pipe::pipe()
2111}
2112
2113#[allow(unreachable_code)]
2114fn pipe_and_spawn_lock_guard() -> Option<MutexGuard<'static, ()>> {
2115    // macOS and some other Unixes are missing the pipe2() syscall, which means that opening pipes
2116    // can't atomically set CLOEXEC. That creates a race condition between pipe opening threads and
2117    // child spawning threads, where children can unintentionally inherit extra pipes, possibly
2118    // leading to deadlocks. Use a global lock to prevent this race within Duct itself.
2119    // Unfortunately, callers who open their own pipes with e.g. std::io::pipe(), and who can't
2120    // guarantee that other (macOS) threads aren't spawning child processes at the same time, need
2121    // to create their own global lock.
2122    //
2123    // Ideally we'd keep this list of targets in sync with the `os_pipe` create, which also should
2124    // try to stay in sync with `std::io::pipe()`. In practice I haven't set up any automation for
2125    // that, so if you're reading this in 10+ years it might be stale :)
2126    #[cfg(any(target_os = "aix", target_vendor = "apple", target_os = "haiku"))]
2127    {
2128        use std::sync::Mutex;
2129        static PIPE_OPENING_LOCK: Mutex<()> = Mutex::new(());
2130        return Some(PIPE_OPENING_LOCK.lock().unwrap());
2131    }
2132    // On Windows, Linux, and most other Unixes, this lock isn't needed.
2133    None
2134}
2135
2136#[cfg(test)]
2137mod test;