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