duct/
lib.rs

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