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<'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
1269        // See comments below about why we take this lock (macOS only).
1270        let spawn_guard = pipe_and_spawn_lock_guard();
1271        let shared_child = SharedChild::spawn(&mut command)?;
1272        drop(spawn_guard);
1273
1274        let command_string = format!("{:?}", argv);
1275        Ok(ChildHandle {
1276            child: Some(shared_child),
1277            command_string,
1278        })
1279    }
1280
1281    // a helper to reduce the need for .as_ref().unwrap() everywhere
1282    fn child(&self) -> &SharedChild {
1283        self.child
1284            .as_ref()
1285            .expect("ChildHandle should not yet have been dropped")
1286    }
1287
1288    fn wait(&self, mode: WaitMode) -> io::Result<Option<ExpressionStatus>> {
1289        let maybe_status = mode.maybe_wait_on_child(self.child())?;
1290        if let Some(status) = maybe_status {
1291            Ok(Some(ExpressionStatus {
1292                status,
1293                checked: true,
1294                command: self.command_string.clone(),
1295            }))
1296        } else {
1297            Ok(None)
1298        }
1299    }
1300
1301    fn kill(&self) -> io::Result<()> {
1302        self.child().kill()
1303    }
1304}
1305
1306// Use Drop to prevent zombie processes on Unix. Zombies aren't a thing on Windows, so omit the
1307// entire impl there.
1308#[cfg(not(windows))]
1309impl Drop for ChildHandle {
1310    fn drop(&mut self) {
1311        let child = self.child.take().expect("only drop should take the child");
1312        match child.try_wait() {
1313            // The child has been cleaned up. Cool.
1314            Ok(Some(_)) => (),
1315
1316            // Ignore IO errors here unless we're running tests. It's hard to provoke these without
1317            // doing something very weird (transmute, libc::waitpid, etc).
1318            Err(e) => {
1319                if cfg!(test) {
1320                    panic!("ChildHandle cleanup failed: {e}");
1321                }
1322            }
1323
1324            // This child is still running, but the caller is never going to wait on it. Add it to
1325            // a global list of (potentially) zombie children. We poll this list whenever we spawn
1326            // new child processes, to mitigate leaks. This is the same strategy used in the
1327            // CPython `subprocess` module:
1328            //   - https://github.com/python/cpython/blob/v3.13.3/Lib/subprocess.py#L1133-L1146
1329            //   - https://github.com/python/cpython/blob/v3.13.3/Lib/subprocess.py#L268-L285
1330            // The main downside of this strategy is that spawning N child processes becomes O(N^2)
1331            // if you leak all of them and they're all long-lived. I think that's an ok tradeoff
1332            // for a couple of reasons:
1333            //   1. Dropping un-waited-for child handles isn't usually what you want to be doing in
1334            //      your happy path, because it means you can't hear about error codes your
1335            //      children return. Children whose handles are retained don't enter this list and
1336            //      don't contribute to O(N^2) behavior.
1337            //   2. Callers who do "very advanced" things (say, a systemd clone) probably shouldn't
1338            //      be using Duct. They need more fine-grained control than Duct is designed to
1339            //      provide.
1340            Ok(None) => LEAKED_CHILDREN.write().unwrap().push(child.into_inner()),
1341        }
1342    }
1343}
1344
1345#[derive(Debug)]
1346struct PipeHandle {
1347    left_handle: HandleInner,
1348    right_handle: HandleInner,
1349}
1350
1351impl PipeHandle {
1352    fn start(left: &Expression, right: &Expression, context: IoContext) -> io::Result<PipeHandle> {
1353        let (reader, writer) = open_pipe_protected()?;
1354        // dup'ing stdin/stdout isn't strictly necessary, but no big deal
1355        let mut left_context = context.try_clone()?;
1356        left_context.stdout = IoValue::Handle(writer.into());
1357        let mut right_context = context;
1358        right_context.stdin = IoValue::Handle(reader.into());
1359        let left_handle = left.0.start(left_context)?;
1360        // The left side has already started. If we fail to start the right
1361        // side, ChildHandle::drop will clean it up one way or another. Note
1362        // that `right_context` is passed by value here, so if start() returns
1363        // an error, all pipe readers will already have been closed, and
1364        // there's a decent chance the left side will exit quickly via EPIPE
1365        // before we .try_wait() it in ChildHandle::drop.
1366        let right_handle = right.0.start(right_context)?;
1367        Ok(PipeHandle {
1368            left_handle,
1369            right_handle,
1370        })
1371    }
1372
1373    fn wait(&self, mode: WaitMode) -> io::Result<Option<ExpressionStatus>> {
1374        // Wait on both sides first, without propagating any errors.
1375        let left_wait_result = self.left_handle.wait(mode);
1376        let right_wait_result = self.right_handle.wait(mode);
1377
1378        // Now we deal with errors from either of those waits. The left wait
1379        // happened first, so that one takes precedence. Note that this is the
1380        // reverse order of exit status precedence.
1381        let left_status = left_wait_result?;
1382        let right_status = right_wait_result?;
1383
1384        // If both waits succeeded, return one of the two statuses.
1385        Ok(pipe_status_precedence(left_status, right_status))
1386    }
1387
1388    // As with wait, we need to call kill on both sides even if the left side
1389    // returns an error.
1390    fn kill(&self) -> io::Result<()> {
1391        let left_kill_result = self.left_handle.kill();
1392        let right_kill_result = self.right_handle.kill();
1393        // As with wait, the left side happened first, so its errors take
1394        // precedence.
1395        left_kill_result.and(right_kill_result)
1396    }
1397
1398    fn pids(&self) -> Vec<u32> {
1399        let mut pids = self.left_handle.pids();
1400        pids.extend_from_slice(&self.right_handle.pids());
1401        pids
1402    }
1403}
1404
1405// The rules of precedence are:
1406// 1) If either side unfinished, the result is unfinished.
1407// 2) Checked errors trump unchecked errors.
1408// 3) Any errors trump success.
1409// 4) All else equal, the right side wins (as in Bash).
1410fn pipe_status_precedence(
1411    left_maybe_status: Option<ExpressionStatus>,
1412    right_maybe_status: Option<ExpressionStatus>,
1413) -> Option<ExpressionStatus> {
1414    let (left_status, right_status) = match (left_maybe_status, right_maybe_status) {
1415        (Some(left), Some(right)) => (left, right),
1416        _ => return None,
1417    };
1418    Some(if right_status.is_checked_error() {
1419        right_status
1420    } else if left_status.is_checked_error() {
1421        left_status
1422    } else if !right_status.status.success() {
1423        right_status
1424    } else {
1425        left_status
1426    })
1427}
1428
1429fn start_io(
1430    io_inner: &IoExpressionInner,
1431    expr_inner: &Expression,
1432    mut context: IoContext,
1433) -> io::Result<HandleInner> {
1434    match io_inner {
1435        StdinBytes(v) => {
1436            return Ok(HandleInner::StdinBytes(Box::new(StdinBytesHandle::start(
1437                expr_inner,
1438                context,
1439                Arc::clone(v),
1440            )?)));
1441        }
1442        StdinPath(p) => {
1443            context.stdin = IoValue::Handle(File::open(p)?.into());
1444        }
1445        StdinFile(f) => {
1446            context.stdin = IoValue::Handle(f.try_clone()?);
1447        }
1448        StdinNull => {
1449            context.stdin = IoValue::Null;
1450        }
1451        StdoutPath(p) => {
1452            context.stdout = IoValue::Handle(File::create(p)?.into());
1453        }
1454        StdoutFile(f) => {
1455            context.stdout = IoValue::Handle(f.try_clone()?);
1456        }
1457        StdoutNull => {
1458            context.stdout = IoValue::Null;
1459        }
1460        StdoutCapture => {
1461            context.stdout = IoValue::Handle(context.stdout_capture.write_pipe()?.into());
1462        }
1463        StdoutToStderr => {
1464            context.stdout = context.stderr.try_clone()?;
1465        }
1466        StderrPath(p) => {
1467            context.stderr = IoValue::Handle(File::create(p)?.into());
1468        }
1469        StderrFile(f) => {
1470            context.stderr = IoValue::Handle(f.try_clone()?);
1471        }
1472        StderrNull => {
1473            context.stderr = IoValue::Null;
1474        }
1475        StderrCapture => {
1476            context.stderr = IoValue::Handle(context.stderr_capture.write_pipe()?.into());
1477        }
1478        StderrToStdout => {
1479            context.stderr = context.stdout.try_clone()?;
1480        }
1481        StdoutStderrSwap => {
1482            mem::swap(&mut context.stdout, &mut context.stderr);
1483        }
1484        Dir(p) => {
1485            context.dir = Some(p.clone());
1486        }
1487        Env(name, val) => {
1488            // Note that HashMap::insert overwrites a preexisting *value*, but not a preexisting
1489            // *key*. We rely on this to match platform behavior on Windows, where the original
1490            // casing of a variable name is preserved even if an equivalent name with a different
1491            // casing is added.
1492            context.env.insert(name.clone(), val.clone());
1493        }
1494        EnvRemove(name) => {
1495            context.env.remove(name);
1496        }
1497        FullEnv(map) => {
1498            context.env = map.clone();
1499        }
1500        Unchecked => {
1501            let inner_handle = expr_inner.0.start(context)?;
1502            return Ok(HandleInner::Unchecked(Box::new(inner_handle)));
1503        }
1504        BeforeSpawn(hook) => {
1505            context.before_spawn_hooks.push(hook.clone());
1506        }
1507    }
1508    expr_inner.0.start(context)
1509}
1510
1511#[derive(Debug)]
1512struct StdinBytesHandle {
1513    inner_handle: HandleInner,
1514    writer_thread: SharedThread<io::Result<()>>,
1515}
1516
1517impl StdinBytesHandle {
1518    fn start(
1519        expression: &Expression,
1520        mut context: IoContext,
1521        input: Arc<Vec<u8>>,
1522    ) -> io::Result<StdinBytesHandle> {
1523        let (reader, mut writer) = open_pipe_protected()?;
1524        context.stdin = IoValue::Handle(reader.into());
1525        let inner_handle = expression.0.start(context)?;
1526        let writer_thread = SharedThread::spawn(move || {
1527            // Broken pipe errors are expected here. Suppress them.
1528            match writer.write_all(&input) {
1529                Err(e) if e.kind() != io::ErrorKind::BrokenPipe => Err(e),
1530                _ => Ok(()),
1531            }
1532        });
1533        Ok(StdinBytesHandle {
1534            inner_handle,
1535            writer_thread,
1536        })
1537    }
1538
1539    fn wait(&self, mode: WaitMode) -> io::Result<Option<ExpressionStatus>> {
1540        let maybe_status = self.inner_handle.wait(mode)?;
1541        // Even if the child has exited, some grandchild process might keep this pipe open and keep
1542        // reading from it. It's tempting not to wait for this IO thread at all and just let it run
1543        // in the background, but that wouldn't work if the current process exited. (When the main
1544        // function returns and the process exits, all background threads are terminated.) Waiting
1545        // on this Handle might be the last thing the caller does in main, who knows, so for
1546        // correctness a blocking waiter does need to wait until this IO thread is finished.
1547        let io_finished = mode.maybe_join_io_thread(&self.writer_thread)?.is_some();
1548        if !io_finished {
1549            return Ok(None);
1550        }
1551        Ok(maybe_status)
1552    }
1553
1554    fn kill(&self) -> io::Result<()> {
1555        self.inner_handle.kill()
1556    }
1557}
1558
1559#[derive(Debug)]
1560enum IoExpressionInner {
1561    StdinBytes(Arc<Vec<u8>>),
1562    StdinPath(PathBuf),
1563    StdinFile(FdOrHandle),
1564    StdinNull,
1565    StdoutPath(PathBuf),
1566    StdoutFile(FdOrHandle),
1567    StdoutNull,
1568    StdoutCapture,
1569    StdoutToStderr,
1570    StderrPath(PathBuf),
1571    StderrFile(FdOrHandle),
1572    StderrNull,
1573    StderrCapture,
1574    StderrToStdout,
1575    StdoutStderrSwap,
1576    Dir(PathBuf),
1577    Env(EnvNameString, OsString),
1578    EnvRemove(EnvNameString),
1579    FullEnv(HashMap<EnvNameString, OsString>),
1580    Unchecked,
1581    BeforeSpawn(BeforeSpawnHook),
1582}
1583
1584type HookFn = Arc<dyn Fn(&mut Command) -> io::Result<()> + Send + Sync>;
1585
1586#[derive(Clone)]
1587struct BeforeSpawnHook {
1588    inner: HookFn,
1589}
1590
1591impl BeforeSpawnHook {
1592    fn new<F>(hook: F) -> Self
1593    where
1594        F: Fn(&mut Command) -> io::Result<()> + Send + Sync + 'static,
1595    {
1596        Self {
1597            inner: Arc::new(hook),
1598        }
1599    }
1600
1601    fn call(&self, command: &mut Command) -> io::Result<()> {
1602        (self.inner)(command)
1603    }
1604}
1605
1606impl fmt::Debug for BeforeSpawnHook {
1607    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1608        write!(f, "<closure>")
1609    }
1610}
1611
1612// An IoContext represents the file descriptors child processes are talking to at execution time.
1613// It's initialized in run(), with dups of the stdin/stdout/stderr pipes, and then passed down to
1614// sub-expressions. Compound expressions will clone() it, and redirections will modify it.
1615#[derive(Debug)]
1616struct IoContext<'a> {
1617    stdin: IoValue,
1618    stdout: IoValue,
1619    stderr: IoValue,
1620    stdout_capture: &'a OutputCaptureContext,
1621    stderr_capture: &'a OutputCaptureContext,
1622    dir: Option<PathBuf>,
1623    env: HashMap<EnvNameString, OsString>,
1624    before_spawn_hooks: Vec<BeforeSpawnHook>,
1625}
1626
1627impl<'a> IoContext<'a> {
1628    // Returns (context, stdout_reader, stderr_reader).
1629    fn new(
1630        stdout_capture: &'a OutputCaptureContext,
1631        stderr_capture: &'a OutputCaptureContext,
1632    ) -> Self {
1633        Self {
1634            stdin: IoValue::ParentStdin,
1635            stdout: IoValue::ParentStdout,
1636            stderr: IoValue::ParentStderr,
1637            stdout_capture,
1638            stderr_capture,
1639            dir: None,
1640            env: std::env::vars_os().map(|(k, v)| (k.into(), v)).collect(),
1641            before_spawn_hooks: Vec::new(),
1642        }
1643    }
1644
1645    fn try_clone(&self) -> io::Result<IoContext<'a>> {
1646        Ok(IoContext {
1647            stdin: self.stdin.try_clone()?,
1648            stdout: self.stdout.try_clone()?,
1649            stderr: self.stderr.try_clone()?,
1650            stdout_capture: self.stdout_capture,
1651            stderr_capture: self.stderr_capture,
1652            dir: self.dir.clone(),
1653            env: self.env.clone(),
1654            before_spawn_hooks: self.before_spawn_hooks.clone(),
1655        })
1656    }
1657}
1658
1659#[derive(Debug)]
1660enum IoValue {
1661    ParentStdin,
1662    ParentStdout,
1663    ParentStderr,
1664    Null,
1665    Handle(FdOrHandle),
1666}
1667
1668impl IoValue {
1669    fn try_clone(&self) -> io::Result<IoValue> {
1670        Ok(match self {
1671            IoValue::ParentStdin => IoValue::ParentStdin,
1672            IoValue::ParentStdout => IoValue::ParentStdout,
1673            IoValue::ParentStderr => IoValue::ParentStderr,
1674            IoValue::Null => IoValue::Null,
1675            IoValue::Handle(f) => IoValue::Handle(f.try_clone()?),
1676        })
1677    }
1678
1679    fn into_stdio(self) -> io::Result<Stdio> {
1680        Ok(match self {
1681            IoValue::ParentStdin => os_pipe::dup_stdin()?.into(),
1682            IoValue::ParentStdout => os_pipe::dup_stdout()?.into(),
1683            IoValue::ParentStderr => os_pipe::dup_stderr()?.into(),
1684            IoValue::Null => Stdio::null(),
1685            IoValue::Handle(f) => f.into(),
1686        })
1687    }
1688}
1689
1690// This struct keeps track of a child exit status, whether or not it's been
1691// unchecked(), and what the command was that gave it (for error messages).
1692#[derive(Clone, Debug)]
1693struct ExpressionStatus {
1694    status: ExitStatus,
1695    checked: bool,
1696    command: String,
1697}
1698
1699impl ExpressionStatus {
1700    fn is_checked_error(&self) -> bool {
1701        self.checked && !self.status.success()
1702    }
1703
1704    fn message(&self) -> String {
1705        format!(
1706            "command {} exited with code {}",
1707            self.command,
1708            self.exit_code_string()
1709        )
1710    }
1711
1712    #[cfg(not(windows))]
1713    fn exit_code_string(&self) -> String {
1714        if self.status.code().is_none() {
1715            return format!("<signal {}>", self.status.signal().unwrap());
1716        }
1717        self.status.code().unwrap().to_string()
1718    }
1719
1720    #[cfg(windows)]
1721    fn exit_code_string(&self) -> String {
1722        self.status.code().unwrap().to_string()
1723    }
1724}
1725
1726fn canonicalize_exe_path_for_dir(exe_name: &OsStr, context: &IoContext) -> io::Result<OsString> {
1727    // There's a tricky interaction between exe paths and `dir`. Exe paths can
1728    // be relative, and so we have to ask: Is an exe path interpreted relative
1729    // to the parent's cwd, or the child's? The answer is that it's platform
1730    // dependent! >.< (Windows uses the parent's cwd, but because of the
1731    // fork-chdir-exec pattern, Unix usually uses the child's.)
1732    //
1733    // We want to use the parent's cwd consistently, because that saves the
1734    // caller from having to worry about whether `dir` will have side effects,
1735    // and because it's easy for the caller to use Path::join if they want to.
1736    // That means that when `dir` is in use, we need to detect exe names that
1737    // are relative paths, and absolutify them. We want to do that as little as
1738    // possible though, both because canonicalization can fail, and because we
1739    // prefer to let the caller control the child's argv[0].
1740    //
1741    // We never want to absolutify a name like "emacs", because that's probably
1742    // a program in the PATH rather than a local file. So we look for slashes
1743    // in the name to determine what's a filepath and what isn't. Note that
1744    // anything given as a std::path::Path will always have a slash by the time
1745    // we get here, because we specialize the IntoExecutablePath trait to
1746    // prepend a ./ to them when they're relative. This leaves the case where
1747    // Windows users might pass a local file like "foo.bat" as a string, which
1748    // we can't distinguish from a global program name. However, because the
1749    // Windows has the preferred "relative to parent's cwd" behavior already,
1750    // this case actually works without our help. (Windows users previously
1751    // needed to watch out for local files shadowing global program names, but
1752    // Rust 1.58 improved this.)
1753
1754    let has_separator = exe_name
1755        .to_string_lossy()
1756        .chars()
1757        .any(std::path::is_separator);
1758    let is_relative = Path::new(exe_name).is_relative();
1759    if context.dir.is_some() && has_separator && is_relative {
1760        Path::new(exe_name).canonicalize().map(Into::into)
1761    } else {
1762        Ok(exe_name.to_owned())
1763    }
1764}
1765
1766// We want to allow Path("foo") to refer to the local file "./foo" on
1767// Unix, and we want to *prevent* Path("echo") from referring to the
1768// global "echo" command on either Unix or Windows. Prepend a dot to all
1769// relative paths to accomplish both of those.
1770fn dotify_relative_exe_path(path: &Path) -> PathBuf {
1771    // This is a no-op if path is absolute or begins with a Windows prefix.
1772    Path::new(".").join(path)
1773}
1774
1775/// An implementation detail of [`cmd`](fn.cmd.html), to distinguish paths from
1776/// other string types.
1777///
1778/// `Path("foo.sh")` means the file named `foo.sh` in the current directory.
1779/// However if you try to execute that path with
1780/// [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html),
1781/// Unix will get upset that it doesn't have a leading `./`. Rust knows that the
1782/// string is a path, but that distinction gets lost by the time execution
1783/// happens.
1784///
1785/// To execute relative paths correctly, duct prepends the `./` to them
1786/// automatically. This trait captures the distinction between the path types
1787/// and other types of strings, which don't get modified. See the trait bounds
1788/// on [`cmd`](fn.cmd.html).
1789pub trait IntoExecutablePath {
1790    fn to_executable(self) -> OsString;
1791}
1792
1793// TODO: Get rid of most of these impls once specialization lands.
1794
1795impl<'a> IntoExecutablePath for &'a Path {
1796    fn to_executable(self) -> OsString {
1797        dotify_relative_exe_path(self).into()
1798    }
1799}
1800
1801impl IntoExecutablePath for PathBuf {
1802    fn to_executable(self) -> OsString {
1803        dotify_relative_exe_path(&self).into()
1804    }
1805}
1806
1807impl<'a> IntoExecutablePath for &'a PathBuf {
1808    fn to_executable(self) -> OsString {
1809        dotify_relative_exe_path(self).into()
1810    }
1811}
1812
1813impl<'a> IntoExecutablePath for &'a str {
1814    fn to_executable(self) -> OsString {
1815        self.into()
1816    }
1817}
1818
1819impl IntoExecutablePath for String {
1820    fn to_executable(self) -> OsString {
1821        self.into()
1822    }
1823}
1824
1825impl<'a> IntoExecutablePath for &'a String {
1826    fn to_executable(self) -> OsString {
1827        self.into()
1828    }
1829}
1830
1831impl<'a> IntoExecutablePath for &'a OsStr {
1832    fn to_executable(self) -> OsString {
1833        self.into()
1834    }
1835}
1836
1837impl IntoExecutablePath for OsString {
1838    fn to_executable(self) -> OsString {
1839        self
1840    }
1841}
1842
1843impl<'a> IntoExecutablePath for &'a OsString {
1844    fn to_executable(self) -> OsString {
1845        self.into()
1846    }
1847}
1848
1849// io::Error doesn't implement clone directly, so we kind of hack it together.
1850fn clone_io_error(error: &io::Error) -> io::Error {
1851    if let Some(code) = error.raw_os_error() {
1852        io::Error::from_raw_os_error(code)
1853    } else {
1854        io::Error::new(error.kind(), error.to_string())
1855    }
1856}
1857
1858#[derive(Clone, Copy, Debug)]
1859enum WaitMode {
1860    // block until everything is finished, as in .wait()
1861    Blocking,
1862    // don't block at all, as in .try_wait()
1863    NonBlocking,
1864    // block with a deadline, as in .wait_deadline() or .wait_timeout()
1865    #[cfg(feature = "timeout")]
1866    Deadline(std::time::Instant),
1867}
1868
1869impl WaitMode {
1870    fn maybe_wait_on_child(self, child: &SharedChild) -> io::Result<Option<ExitStatus>> {
1871        match self {
1872            WaitMode::Blocking => child.wait().map(Some),
1873            WaitMode::NonBlocking => child.try_wait(),
1874            #[cfg(feature = "timeout")]
1875            WaitMode::Deadline(deadline) => child.wait_deadline(deadline),
1876        }
1877    }
1878
1879    /// Returns Ok(true) if IO finished successfully, Ok(false) if IO is not yet finished (and the
1880    /// mode is not Blocking), or Err(e) if IO failed with an error.
1881    fn maybe_join_io_thread<T>(
1882        self,
1883        io_thread: &SharedThread<io::Result<T>>,
1884    ) -> io::Result<Option<&T>> {
1885        match self {
1886            WaitMode::Blocking => match io_thread.join() {
1887                Ok(val) => Ok(Some(val)),
1888                Err(e) => Err(clone_io_error(e)),
1889            },
1890            WaitMode::NonBlocking => match io_thread.try_join() {
1891                Some(Ok(val)) => Ok(Some(val)),
1892                Some(Err(e)) => Err(clone_io_error(e)),
1893                None => Ok(None),
1894            },
1895            #[cfg(feature = "timeout")]
1896            WaitMode::Deadline(deadline) => match io_thread.join_deadline(deadline) {
1897                Some(Ok(val)) => Ok(Some(val)),
1898                Some(Err(e)) => Err(clone_io_error(e)),
1899                None => Ok(None),
1900            },
1901        }
1902    }
1903}
1904
1905type ReaderThread = SharedThread<io::Result<Vec<u8>>>;
1906
1907#[derive(Debug)]
1908struct OutputCaptureContext {
1909    pair: OnceLock<(os_pipe::PipeReader, os_pipe::PipeWriter)>,
1910}
1911
1912impl OutputCaptureContext {
1913    fn new() -> Self {
1914        Self {
1915            pair: OnceLock::new(),
1916        }
1917    }
1918
1919    fn write_pipe(&self) -> io::Result<os_pipe::PipeWriter> {
1920        // OnceLock::get_or_try_init would be nice if it stabilizes.
1921        match self.pair.get() {
1922            Some((_, writer)) => writer.try_clone(),
1923            None => {
1924                let (reader, writer) = open_pipe_protected()?;
1925                let clone = writer.try_clone();
1926                self.pair.set((reader, writer)).unwrap();
1927                clone
1928            }
1929        }
1930    }
1931
1932    // Only spawn a read thread if the write pipe was used.
1933    fn maybe_read_thread(self) -> Option<ReaderThread> {
1934        if let Some((mut reader, _)) = self.pair.into_inner() {
1935            Some(SharedThread::spawn(move || {
1936                let mut output = Vec::new();
1937                reader.read_to_end(&mut output)?;
1938                Ok(output)
1939            }))
1940        } else {
1941            None
1942        }
1943    }
1944}
1945
1946/// An incremental reader created with the
1947/// [`Expression::reader`](struct.Expression.html#method.reader) method.
1948///
1949/// When this reader reaches EOF, it automatically calls
1950/// [`wait`](struct.Handle.html#method.wait) on the inner handle. If the child
1951/// returns a non-zero exit status, the read at EOF will return an error,
1952/// unless you use [`unchecked`](struct.Expression.html#method.unchecked).
1953///
1954/// Both `ReaderHandle` and `&ReaderHandle` implement
1955/// [`std::io::Read`](https://doc.rust-lang.org/std/io/trait.Read.html). The
1956/// latter makes it possible for one thread to
1957/// [`kill`](struct.ReaderHandle.html#method.kill) the `ReaderHandle` while
1958/// another thread is reading it. That can be useful for effectively canceling
1959/// the read and unblocking the reader thread. However, note that killed child
1960/// processes return a non-zero exit status, which is an error for the reader
1961/// by default, unless you use
1962/// [`unchecked`](struct.Expression.html#method.unchecked).
1963///
1964/// `ReaderHandle` contains a [`Handle`] internally, and it takes the same
1965/// approach to cleaning up zombie processess on Unix.
1966///
1967/// # Example
1968///
1969/// ```
1970/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1971/// # if cfg!(not(windows)) {
1972/// use duct::cmd;
1973/// use duct::ReaderHandle;
1974/// use std::sync::Arc;
1975/// use std::io::prelude::*;
1976///
1977/// // This child process prints a single byte and then sleeps.
1978/// //
1979/// // CAUTION: Using Bash for this example would probably hang, because Bash
1980/// // would spawn a `sleep` grandchild processes, and that grandchild wouldn't
1981/// // receive the kill signal.
1982/// let python_child = "\
1983/// import sys
1984/// import time
1985/// print()
1986/// sys.stdout.flush()
1987/// time.sleep(24 * 60 * 60)
1988/// ";
1989/// let reader: ReaderHandle = cmd!("python3", "-c", python_child)
1990///     .unchecked()
1991///     .reader()?;
1992///
1993/// // Spawn two threads that both try to read the single byte. Whichever one
1994/// // succeeds then calls kill() to unblock the other.
1995/// let arc_reader: Arc<ReaderHandle> = Arc::new(reader);
1996/// let mut threads = Vec::new();
1997/// for _ in 0..2 {
1998///     let arc_reader = arc_reader.clone();
1999///     threads.push(std::thread::spawn(move || -> std::io::Result<()> {
2000///         let mut single_byte = [0u8];
2001///         (&*arc_reader).read(&mut single_byte)?;
2002///         arc_reader.kill()?;
2003///         Ok(())
2004///     }));
2005/// }
2006///
2007/// // Join both threads. Because of the kill() above, both threads will exit
2008/// // quickly.
2009/// for thread in threads {
2010///     thread.join().unwrap()?;
2011/// }
2012/// # }
2013/// # Ok(())
2014/// # }
2015/// ```
2016#[derive(Debug)]
2017pub struct ReaderHandle {
2018    handle: Handle,
2019    reader: os_pipe::PipeReader,
2020}
2021
2022impl ReaderHandle {
2023    /// Check whether the underlying expression is finished. This is equivalent
2024    /// to [`Handle::try_wait`](struct.Handle.html#method.try_wait). If the
2025    /// `ReaderHandle` has indicated EOF successfully, then it's guaranteed
2026    /// that this method will return `Ok(Some(_))`.
2027    ///
2028    /// Note that the
2029    /// [`stdout`](https://doc.rust-lang.org/std/process/struct.Output.html#structfield.stdout)
2030    /// field of the returned
2031    /// [`Output`](https://doc.rust-lang.org/std/process/struct.Output.html)
2032    /// will always be empty, because the `ReaderHandle` itself owns the
2033    /// child's stdout pipe.
2034    pub fn try_wait(&self) -> io::Result<Option<&Output>> {
2035        self.handle.try_wait()
2036    }
2037
2038    /// Kill all the child processes in the running expression.
2039    ///
2040    /// See [`Handle::kill`]. Note that as with
2041    /// [`std::process::Child::kill`](https://doc.rust-lang.org/beta/std/process/struct.Child.html#method.kill),
2042    /// this does not kill any grandchild processes that the children have
2043    /// spawned on their own. It only kills the child processes that Duct
2044    /// spawned itself. This is **especially relevant** for `ReaderHandle`,
2045    /// because if you're using `kill` to unblock another thread that's
2046    /// reading, an unkilled grandchild process might keep the child's stdout
2047    /// pipe open and keep your reader thread blocked. For that use case, you
2048    /// need to ensure that any grandchild processes your child might spawn are
2049    /// going to be short-lived. See
2050    /// [`gotchas.md`](https://github.com/oconnor663/duct.py/blob/master/gotchas.md)
2051    /// for an extensive discussion of these issues.
2052    pub fn kill(&self) -> io::Result<()> {
2053        self.handle.kill()
2054    }
2055
2056    /// Return a `Vec<u32>` containing the PIDs of all of the child processes.
2057    /// The PIDs are given in pipeline order, from left to right.
2058    pub fn pids(&self) -> Vec<u32> {
2059        self.handle.pids()
2060    }
2061}
2062
2063impl<'a> Read for &'a ReaderHandle {
2064    /// Note that if you don't use
2065    /// [`unchecked`](struct.Expression.html#method.unchecked), and the child
2066    /// returns a non-zero exit status, the final call to `read` will return an
2067    /// error, just as [`run`](struct.Expression.html#method.run) would.
2068    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
2069        let n = (&self.reader).read(buf)?;
2070        if n == 0 && !buf.is_empty() {
2071            // EOF detected. Wait on the child to clean it up before returning.
2072            self.handle.wait()?;
2073        }
2074        Ok(n)
2075    }
2076}
2077
2078impl Read for ReaderHandle {
2079    /// Note that if you don't use
2080    /// [`unchecked`](struct.Expression.html#method.unchecked), and the child
2081    /// returns a non-zero exit status, the final call to `read` will return an
2082    /// error, just as [`run`](struct.Expression.html#method.run) would.
2083    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
2084        (&*self).read(buf)
2085    }
2086}
2087
2088#[cfg(not(windows))]
2089type FdOrHandle = OwnedFd;
2090#[cfg(windows)]
2091type FdOrHandle = OwnedHandle;
2092
2093// Without these conversions this crate could be 100% safe code, so this is kind of a shame, but I
2094// don't want to change the trait bounds on stdin_file/stdout_file/stderr_file. There are types
2095// that implement IntoRawFd but not Into<OwnedFd>, including RawFd itself.
2096#[cfg(not(windows))]
2097fn owned_from_raw(raw: impl IntoRawFd) -> OwnedFd {
2098    unsafe { OwnedFd::from_raw_fd(raw.into_raw_fd()) }
2099}
2100#[cfg(windows)]
2101fn owned_from_raw(raw: impl IntoRawHandle) -> OwnedHandle {
2102    unsafe { OwnedHandle::from_raw_handle(raw.into_raw_handle()) }
2103}
2104
2105fn open_pipe_protected() -> io::Result<(os_pipe::PipeReader, os_pipe::PipeWriter)> {
2106    let _guard = pipe_and_spawn_lock_guard(); // See comments below.
2107    os_pipe::pipe()
2108}
2109
2110#[allow(unreachable_code)]
2111fn pipe_and_spawn_lock_guard() -> Option<MutexGuard<'static, ()>> {
2112    // macOS and some other Unixes are missing the pipe2() syscall, which means that opening pipes
2113    // can't atomically set CLOEXEC. That creates a race condition between pipe opening threads and
2114    // child spawning threads, where children can unintentionally inherit extra pipes, possibly
2115    // leading to deadlocks. Use a global lock to prevent this race within Duct itself.
2116    // Unfortunately, callers who open their own pipes with e.g. std::io::pipe(), and who can't
2117    // guarantee that other (macOS) threads aren't spawning child processes at the same time, need
2118    // to create their own global lock.
2119    //
2120    // Ideally we'd keep this list of targets in sync with the `os_pipe` create, which also should
2121    // try to stay in sync with `std::io::pipe()`. In practice I haven't set up any automation for
2122    // that, so if you're reading this in 10+ years it might be stale :)
2123    #[cfg(any(target_os = "aix", target_vendor = "apple", target_os = "haiku"))]
2124    {
2125        use std::sync::Mutex;
2126        static PIPE_OPENING_LOCK: Mutex<()> = Mutex::new(());
2127        return Some(PIPE_OPENING_LOCK.lock().unwrap());
2128    }
2129    // On Windows, Linux, and most other Unixes, this lock isn't needed.
2130    None
2131}
2132
2133#[cfg(test)]
2134mod test;