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