subprocess/builder.rs
1#[cfg(unix)]
2mod os {
3 pub const NULL_DEVICE: &str = "/dev/null";
4 pub const SHELL: [&str; 2] = ["sh", "-c"];
5}
6
7#[cfg(windows)]
8mod os {
9 pub const NULL_DEVICE: &str = "nul";
10 pub const SHELL: [&str; 2] = ["cmd.exe", "/c"];
11}
12
13pub use self::exec::{CaptureData, Exec, NullFile};
14pub use self::os::*;
15pub use self::pipeline::Pipeline;
16
17#[cfg(unix)]
18pub use exec::unix;
19
20mod exec {
21 use std::borrow::Cow;
22 use std::collections::HashMap;
23 use std::env;
24 use std::ffi::{OsStr, OsString};
25 use std::fmt;
26 use std::fs::{File, OpenOptions};
27 use std::io::{self, Read, Write};
28 use std::ops::BitOr;
29 use std::path::Path;
30
31 use crate::communicate::Communicator;
32 use crate::os_common::ExitStatus;
33 use crate::popen::{Popen, PopenConfig, Redirection, Result as PopenResult};
34
35 use super::os::*;
36 use super::Pipeline;
37
38 /// A builder for [`Popen`] instances, providing control and
39 /// convenience methods.
40 ///
41 /// `Exec` provides a builder API for [`Popen::create`], and
42 /// includes convenience methods for capturing the output, and for
43 /// connecting subprocesses into pipelines.
44 ///
45 /// # Examples
46 ///
47 /// Execute an external command and wait for it to complete:
48 ///
49 /// ```no_run
50 /// # use subprocess::*;
51 /// # fn dummy() -> Result<()> {
52 /// # let dirname = "some_dir";
53 /// let exit_status = Exec::cmd("umount").arg(dirname).join()?;
54 /// # Ok(())
55 /// # }
56 /// ```
57 ///
58 /// Execute the command using the OS shell, like C's `system`:
59 ///
60 /// ```no_run
61 /// # use subprocess::*;
62 /// # fn dummy() -> Result<()> {
63 /// Exec::shell("shutdown -h now").join()?;
64 /// # Ok(())
65 /// # }
66 /// ```
67 ///
68 /// Start a subprocess and obtain its output as a `Read` trait object,
69 /// like C's `popen`:
70 ///
71 /// ```
72 /// # use subprocess::*;
73 /// # fn dummy() -> Result<()> {
74 /// let stream = Exec::cmd("ls").stream_stdout()?;
75 /// // call stream.read_to_string, construct io::BufReader(stream), etc.
76 /// # Ok(())
77 /// # }
78 /// ```
79 ///
80 /// Capture the output of a command:
81 ///
82 /// ```
83 /// # use subprocess::*;
84 /// # fn dummy() -> Result<()> {
85 /// let out = Exec::cmd("ls")
86 /// .stdout(Redirection::Pipe)
87 /// .capture()?
88 /// .stdout_str();
89 /// # Ok(())
90 /// # }
91 /// ```
92 ///
93 /// Redirect errors to standard output, and capture both in a single stream:
94 ///
95 /// ```
96 /// # use subprocess::*;
97 /// # fn dummy() -> Result<()> {
98 /// let out_and_err = Exec::cmd("ls")
99 /// .stdout(Redirection::Pipe)
100 /// .stderr(Redirection::Merge)
101 /// .capture()?
102 /// .stdout_str();
103 /// # Ok(())
104 /// # }
105 /// ```
106 ///
107 /// Provide input to the command and read its output:
108 ///
109 /// ```
110 /// # use subprocess::*;
111 /// # fn dummy() -> Result<()> {
112 /// let out = Exec::cmd("sort")
113 /// .stdin("b\nc\na\n")
114 /// .stdout(Redirection::Pipe)
115 /// .capture()?
116 /// .stdout_str();
117 /// assert!(out == "a\nb\nc\n");
118 /// # Ok(())
119 /// # }
120 /// ```
121 ///
122 /// [`Popen`]: struct.Popen.html
123 /// [`Popen::create`]: struct.Popen.html#method.create
124
125 #[must_use]
126 pub struct Exec {
127 command: OsString,
128 args: Vec<OsString>,
129 config: PopenConfig,
130 stdin_data: Option<Vec<u8>>,
131 }
132
133 impl Exec {
134 /// Constructs a new `Exec`, configured to run `command`.
135 ///
136 /// The command will be run directly in the OS, without an
137 /// intervening shell. To run it through a shell, use
138 /// [`Exec::shell`] instead.
139 ///
140 /// By default, the command will be run without arguments, and
141 /// none of the standard streams will be modified.
142 ///
143 /// [`Exec::shell`]: struct.Exec.html#method.shell
144 pub fn cmd(command: impl AsRef<OsStr>) -> Exec {
145 Exec {
146 command: command.as_ref().to_owned(),
147 args: vec![],
148 config: PopenConfig::default(),
149 stdin_data: None,
150 }
151 }
152
153 /// Constructs a new `Exec`, configured to run `cmdstr` with
154 /// the system shell.
155 ///
156 /// `subprocess` never spawns shells without an explicit
157 /// request. This command requests the shell to be used; on
158 /// Unix-like systems, this is equivalent to
159 /// `Exec::cmd("sh").arg("-c").arg(cmdstr)`. On Windows, it
160 /// runs `Exec::cmd("cmd.exe").arg("/c")`.
161 ///
162 /// `shell` is useful for porting code that uses the C
163 /// `system` function, which also spawns a shell.
164 ///
165 /// When invoking this function, be careful not to interpolate
166 /// arguments into the string run by the shell, such as
167 /// `Exec::shell(format!("sort {}", filename))`. Such code is
168 /// prone to errors and, if `filename` comes from an untrusted
169 /// source, to shell injection attacks. Instead, use
170 /// `Exec::cmd("sort").arg(filename)`.
171 pub fn shell(cmdstr: impl AsRef<OsStr>) -> Exec {
172 Exec::cmd(SHELL[0]).args(&SHELL[1..]).arg(cmdstr)
173 }
174
175 /// Appends `arg` to argument list.
176 pub fn arg(mut self, arg: impl AsRef<OsStr>) -> Exec {
177 self.args.push(arg.as_ref().to_owned());
178 self
179 }
180
181 /// Extends the argument list with `args`.
182 pub fn args(mut self, args: &[impl AsRef<OsStr>]) -> Exec {
183 self.args.extend(args.iter().map(|x| x.as_ref().to_owned()));
184 self
185 }
186
187 /// Specifies that the process is initially detached.
188 ///
189 /// A detached process means that we will not wait for the
190 /// process to finish when the object that owns it goes out of
191 /// scope.
192 pub fn detached(mut self) -> Exec {
193 self.config.detached = true;
194 self
195 }
196
197 fn ensure_env(&mut self) {
198 if self.config.env.is_none() {
199 self.config.env = Some(PopenConfig::current_env());
200 }
201 }
202
203 /// Clears the environment of the subprocess.
204 ///
205 /// When this is invoked, the subprocess will not inherit the
206 /// environment of this process.
207 pub fn env_clear(mut self) -> Exec {
208 self.config.env = Some(vec![]);
209 self
210 }
211
212 /// Sets an environment variable in the child process.
213 ///
214 /// If the same variable is set more than once, the last value
215 /// is used.
216 ///
217 /// Other environment variables are by default inherited from
218 /// the current process. If this is undesirable, call
219 /// `env_clear` first.
220 pub fn env(mut self, key: impl AsRef<OsStr>, value: impl AsRef<OsStr>) -> Exec {
221 self.ensure_env();
222 self.config
223 .env
224 .as_mut()
225 .unwrap()
226 .push((key.as_ref().to_owned(), value.as_ref().to_owned()));
227 self
228 }
229
230 /// Sets multiple environment variables in the child process.
231 ///
232 /// The keys and values of the variables are specified by the
233 /// slice. If the same variable is set more than once, the
234 /// last value is used.
235 ///
236 /// Other environment variables are by default inherited from
237 /// the current process. If this is undesirable, call
238 /// `env_clear` first.
239 pub fn env_extend(mut self, vars: &[(impl AsRef<OsStr>, impl AsRef<OsStr>)]) -> Exec {
240 self.ensure_env();
241 {
242 let envvec = self.config.env.as_mut().unwrap();
243 for &(ref k, ref v) in vars {
244 envvec.push((k.as_ref().to_owned(), v.as_ref().to_owned()));
245 }
246 }
247 self
248 }
249
250 /// Removes an environment variable from the child process.
251 ///
252 /// Other environment variables are inherited by default.
253 pub fn env_remove(mut self, key: impl AsRef<OsStr>) -> Exec {
254 self.ensure_env();
255 self.config
256 .env
257 .as_mut()
258 .unwrap()
259 .retain(|&(ref k, ref _v)| k != key.as_ref());
260 self
261 }
262
263 /// Specifies the current working directory of the child process.
264 ///
265 /// If unspecified, the current working directory is inherited
266 /// from the parent.
267 pub fn cwd(mut self, dir: impl AsRef<Path>) -> Exec {
268 self.config.cwd = Some(dir.as_ref().as_os_str().to_owned());
269 self
270 }
271
272 /// Specifies how to set up the standard input of the child process.
273 ///
274 /// Argument can be:
275 ///
276 /// * a [`Redirection`];
277 /// * a `File`, which is a shorthand for `Redirection::File(file)`;
278 /// * a `Vec<u8>` or `&str`, which will set up a `Redirection::Pipe`
279 /// for stdin, making sure that `capture` feeds that data into the
280 /// standard input of the subprocess;
281 /// * [`NullFile`], which will redirect the standard input to read from
282 /// `/dev/null`.
283 ///
284 /// [`Redirection`]: enum.Redirection.html
285 /// [`NullFile`]: struct.NullFile.html
286 pub fn stdin(mut self, stdin: impl Into<InputRedirection>) -> Exec {
287 match (&self.config.stdin, stdin.into()) {
288 (&Redirection::None, InputRedirection::AsRedirection(new)) => {
289 self.config.stdin = new
290 }
291 (&Redirection::Pipe, InputRedirection::AsRedirection(Redirection::Pipe)) => (),
292 (&Redirection::None, InputRedirection::FeedData(data)) => {
293 self.config.stdin = Redirection::Pipe;
294 self.stdin_data = Some(data);
295 }
296 (_, _) => panic!("stdin is already set"),
297 }
298 self
299 }
300
301 /// Specifies how to set up the standard output of the child process.
302 ///
303 /// Argument can be:
304 ///
305 /// * a [`Redirection`];
306 /// * a `File`, which is a shorthand for `Redirection::File(file)`;
307 /// * [`NullFile`], which will redirect the standard output to go to
308 /// `/dev/null`.
309 ///
310 /// [`Redirection`]: enum.Redirection.html
311 /// [`NullFile`]: struct.NullFile.html
312 pub fn stdout(mut self, stdout: impl Into<OutputRedirection>) -> Exec {
313 match (&self.config.stdout, stdout.into().into_redirection()) {
314 (&Redirection::None, new) => self.config.stdout = new,
315 (&Redirection::Pipe, Redirection::Pipe) => (),
316 (_, _) => panic!("stdout is already set"),
317 }
318 self
319 }
320
321 /// Specifies how to set up the standard error of the child process.
322 ///
323 /// Argument can be:
324 ///
325 /// * a [`Redirection`];
326 /// * a `File`, which is a shorthand for `Redirection::File(file)`;
327 /// * [`NullFile`], which will redirect the standard error to go to
328 /// `/dev/null`.
329 ///
330 /// [`Redirection`]: enum.Redirection.html
331 /// [`NullFile`]: struct.NullFile.html
332 pub fn stderr(mut self, stderr: impl Into<OutputRedirection>) -> Exec {
333 match (&self.config.stderr, stderr.into().into_redirection()) {
334 (&Redirection::None, new) => self.config.stderr = new,
335 (&Redirection::Pipe, Redirection::Pipe) => (),
336 (_, _) => panic!("stderr is already set"),
337 }
338 self
339 }
340
341 fn check_no_stdin_data(&self, meth: &str) {
342 if self.stdin_data.is_some() {
343 panic!("{} called with input data specified", meth);
344 }
345 }
346
347 // Terminators
348
349 /// Starts the process, returning a `Popen` for the running process.
350 pub fn popen(mut self) -> PopenResult<Popen> {
351 self.check_no_stdin_data("popen");
352 self.args.insert(0, self.command);
353 let p = Popen::create(&self.args, self.config)?;
354 Ok(p)
355 }
356
357 /// Starts the process, waits for it to finish, and returns
358 /// the exit status.
359 ///
360 /// This method will wait for as long as necessary for the process to
361 /// finish. If a timeout is needed, use
362 /// `<...>.detached().popen()?.wait_timeout(...)` instead.
363 pub fn join(self) -> PopenResult<ExitStatus> {
364 self.check_no_stdin_data("join");
365 self.popen()?.wait()
366 }
367
368 /// Starts the process and returns a value implementing the `Read`
369 /// trait that reads from the standard output of the child process.
370 ///
371 /// This will automatically set up
372 /// `stdout(Redirection::Pipe)`, so it is not necessary to do
373 /// that beforehand.
374 ///
375 /// When the trait object is dropped, it will wait for the
376 /// process to finish. If this is undesirable, use
377 /// `detached()`.
378 pub fn stream_stdout(self) -> PopenResult<impl Read> {
379 self.check_no_stdin_data("stream_stdout");
380 let p = self.stdout(Redirection::Pipe).popen()?;
381 Ok(ReadOutAdapter(p))
382 }
383
384 /// Starts the process and returns a value implementing the `Read`
385 /// trait that reads from the standard error of the child process.
386 ///
387 /// This will automatically set up
388 /// `stderr(Redirection::Pipe)`, so it is not necessary to do
389 /// that beforehand.
390 ///
391 /// When the trait object is dropped, it will wait for the
392 /// process to finish. If this is undesirable, use
393 /// `detached()`.
394 pub fn stream_stderr(self) -> PopenResult<impl Read> {
395 self.check_no_stdin_data("stream_stderr");
396 let p = self.stderr(Redirection::Pipe).popen()?;
397 Ok(ReadErrAdapter(p))
398 }
399
400 /// Starts the process and returns a value implementing the `Write`
401 /// trait that writes to the standard input of the child process.
402 ///
403 /// This will automatically set up `stdin(Redirection::Pipe)`,
404 /// so it is not necessary to do that beforehand.
405 ///
406 /// When the trait object is dropped, it will wait for the
407 /// process to finish. If this is undesirable, use
408 /// `detached()`.
409 pub fn stream_stdin(self) -> PopenResult<impl Write> {
410 self.check_no_stdin_data("stream_stdin");
411 let p = self.stdin(Redirection::Pipe).popen()?;
412 Ok(WriteAdapter(p))
413 }
414
415 fn setup_communicate(mut self) -> PopenResult<(Communicator, Popen)> {
416 let stdin_data = self.stdin_data.take();
417 if let (&Redirection::None, &Redirection::None) =
418 (&self.config.stdout, &self.config.stderr)
419 {
420 self = self.stdout(Redirection::Pipe);
421 }
422 let mut p = self.popen()?;
423
424 Ok((p.communicate_start(stdin_data), p))
425 }
426
427 /// Starts the process and returns a `Communicator` handle.
428 ///
429 /// This is a lower-level API that offers more choice in how
430 /// communication is performed, such as read size limit and timeout,
431 /// equivalent to [`Popen::communicate`].
432 ///
433 /// Unlike `capture()`, this method doesn't wait for the process to
434 /// finish, effectively detaching it.
435 ///
436 /// [`Popen::communicate`]: struct.Popen.html#method.communicate
437 pub fn communicate(self) -> PopenResult<Communicator> {
438 let comm = self.detached().setup_communicate()?.0;
439 Ok(comm)
440 }
441
442 /// Starts the process, collects its output, and waits for it
443 /// to finish.
444 ///
445 /// The return value provides the standard output and standard
446 /// error as bytes or optionally strings, as well as the exit
447 /// status.
448 ///
449 /// Unlike `Popen::communicate`, this method actually waits
450 /// for the process to finish, rather than simply waiting for
451 /// its standard streams to close. If this is undesirable,
452 /// use `detached()`.
453 pub fn capture(self) -> PopenResult<CaptureData> {
454 let (mut comm, mut p) = self.setup_communicate()?;
455 let (maybe_out, maybe_err) = comm.read()?;
456 Ok(CaptureData {
457 stdout: maybe_out.unwrap_or_else(Vec::new),
458 stderr: maybe_err.unwrap_or_else(Vec::new),
459 exit_status: p.wait()?,
460 })
461 }
462
463 // used for Debug impl
464 fn display_escape(s: &str) -> Cow<'_, str> {
465 fn nice_char(c: char) -> bool {
466 match c {
467 '-' | '_' | '.' | ',' | '/' => true,
468 c if c.is_ascii_alphanumeric() => true,
469 _ => false,
470 }
471 }
472 if !s.chars().all(nice_char) {
473 Cow::Owned(format!("'{}'", s.replace("'", r#"'\''"#)))
474 } else {
475 Cow::Borrowed(s)
476 }
477 }
478
479 /// Show Exec as command-line string quoted in the Unix style.
480 pub fn to_cmdline_lossy(&self) -> String {
481 let mut out = String::new();
482 if let Some(ref cmd_env) = self.config.env {
483 let current: Vec<_> = env::vars_os().collect();
484 let current_map: HashMap<_, _> = current.iter().map(|(x, y)| (x, y)).collect();
485 for (k, v) in cmd_env {
486 if current_map.get(&k) == Some(&&v) {
487 continue;
488 }
489 out.push_str(&Exec::display_escape(&k.to_string_lossy()));
490 out.push('=');
491 out.push_str(&Exec::display_escape(&v.to_string_lossy()));
492 out.push(' ');
493 }
494 let cmd_env: HashMap<_, _> = cmd_env.iter().map(|(k, v)| (k, v)).collect();
495 for (k, _) in current {
496 if !cmd_env.contains_key(&k) {
497 out.push_str(&Exec::display_escape(&k.to_string_lossy()));
498 out.push('=');
499 out.push(' ');
500 }
501 }
502 }
503 out.push_str(&Exec::display_escape(&self.command.to_string_lossy()));
504 for arg in &self.args {
505 out.push(' ');
506 out.push_str(&Exec::display_escape(&arg.to_string_lossy()));
507 }
508 out
509 }
510 }
511
512 impl Clone for Exec {
513 /// Returns a copy of the value.
514 ///
515 /// This method is guaranteed not to fail as long as none of
516 /// the `Redirection` values contain a `Redirection::File`
517 /// variant. If a redirection to `File` is present, cloning
518 /// that field will use `File::try_clone` method, which
519 /// duplicates a file descriptor and can (but is not likely
520 /// to) fail. In that scenario, `Exec::clone` panics.
521 fn clone(&self) -> Exec {
522 Exec {
523 command: self.command.clone(),
524 args: self.args.clone(),
525 config: self.config.try_clone().unwrap(),
526 stdin_data: self.stdin_data.as_ref().cloned(),
527 }
528 }
529 }
530
531 impl BitOr for Exec {
532 type Output = Pipeline;
533
534 /// Create a `Pipeline` from `self` and `rhs`.
535 fn bitor(self, rhs: Exec) -> Pipeline {
536 Pipeline::new(self, rhs)
537 }
538 }
539
540 impl fmt::Debug for Exec {
541 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542 write!(f, "Exec {{ {} }}", self.to_cmdline_lossy())
543 }
544 }
545
546 #[derive(Debug)]
547 struct ReadOutAdapter(Popen);
548
549 impl Read for ReadOutAdapter {
550 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
551 self.0.stdout.as_mut().unwrap().read(buf)
552 }
553 }
554
555 #[derive(Debug)]
556 struct ReadErrAdapter(Popen);
557
558 impl Read for ReadErrAdapter {
559 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
560 self.0.stderr.as_mut().unwrap().read(buf)
561 }
562 }
563
564 #[derive(Debug)]
565 struct WriteAdapter(Popen);
566
567 impl Write for WriteAdapter {
568 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
569 self.0.stdin.as_mut().unwrap().write(buf)
570 }
571 fn flush(&mut self) -> io::Result<()> {
572 self.0.stdin.as_mut().unwrap().flush()
573 }
574 }
575
576 // We must implement Drop in order to close the stream. The typical
577 // use case for stream_stdin() is a process that reads something from
578 // stdin. WriteAdapter going out of scope invokes Popen::drop(),
579 // which waits for the process to exit. Without closing stdin, this
580 // deadlocks because the child process hangs reading its stdin.
581
582 impl Drop for WriteAdapter {
583 fn drop(&mut self) {
584 self.0.stdin.take();
585 }
586 }
587
588 /// Data captured by [`Exec::capture`] and [`Pipeline::capture`].
589 ///
590 /// [`Exec::capture`]: struct.Exec.html#method.capture
591 /// [`Pipeline::capture`]: struct.Pipeline.html#method.capture
592 #[derive(Debug)]
593 pub struct CaptureData {
594 /// Standard output as bytes.
595 pub stdout: Vec<u8>,
596 /// Standard error as bytes.
597 pub stderr: Vec<u8>,
598 /// Exit status.
599 pub exit_status: ExitStatus,
600 }
601
602 impl CaptureData {
603 /// Returns the standard output as string, converted from bytes using
604 /// `String::from_utf8_lossy`.
605 pub fn stdout_str(&self) -> String {
606 String::from_utf8_lossy(&self.stdout).into_owned()
607 }
608
609 /// Returns the standard error as string, converted from bytes using
610 /// `String::from_utf8_lossy`.
611 pub fn stderr_str(&self) -> String {
612 String::from_utf8_lossy(&self.stderr).into_owned()
613 }
614
615 /// True if the exit status of the process or pipeline is 0.
616 pub fn success(&self) -> bool {
617 self.exit_status.success()
618 }
619 }
620
621 #[derive(Debug)]
622 pub enum InputRedirection {
623 AsRedirection(Redirection),
624 FeedData(Vec<u8>),
625 }
626
627 impl From<Redirection> for InputRedirection {
628 fn from(r: Redirection) -> Self {
629 if let Redirection::Merge = r {
630 panic!("Redirection::Merge is only allowed for output streams");
631 }
632 InputRedirection::AsRedirection(r)
633 }
634 }
635
636 impl From<File> for InputRedirection {
637 fn from(f: File) -> Self {
638 InputRedirection::AsRedirection(Redirection::File(f))
639 }
640 }
641
642 /// Marker value for [`stdin`], [`stdout`], and [`stderr`] methods
643 /// of [`Exec`] and [`Pipeline`].
644 ///
645 /// Use of this value means that the corresponding stream should
646 /// be redirected to the devnull device.
647 ///
648 /// [`stdin`]: struct.Exec.html#method.stdin
649 /// [`stdout`]: struct.Exec.html#method.stdout
650 /// [`stderr`]: struct.Exec.html#method.stderr
651 /// [`Exec`]: struct.Exec.html
652 /// [`Pipeline`]: struct.Pipeline.html
653 #[derive(Debug)]
654 pub struct NullFile;
655
656 impl From<NullFile> for InputRedirection {
657 fn from(_nf: NullFile) -> Self {
658 let null_file = OpenOptions::new().read(true).open(NULL_DEVICE).unwrap();
659 InputRedirection::AsRedirection(Redirection::File(null_file))
660 }
661 }
662
663 impl From<Vec<u8>> for InputRedirection {
664 fn from(v: Vec<u8>) -> Self {
665 InputRedirection::FeedData(v)
666 }
667 }
668
669 impl<'a> From<&'a str> for InputRedirection {
670 fn from(s: &'a str) -> Self {
671 InputRedirection::FeedData(s.as_bytes().to_vec())
672 }
673 }
674
675 #[derive(Debug)]
676 pub struct OutputRedirection(Redirection);
677
678 impl OutputRedirection {
679 pub fn into_redirection(self) -> Redirection {
680 self.0
681 }
682 }
683
684 impl From<Redirection> for OutputRedirection {
685 fn from(r: Redirection) -> Self {
686 OutputRedirection(r)
687 }
688 }
689
690 impl From<File> for OutputRedirection {
691 fn from(f: File) -> Self {
692 OutputRedirection(Redirection::File(f))
693 }
694 }
695
696 impl From<NullFile> for OutputRedirection {
697 fn from(_nf: NullFile) -> Self {
698 let null_file = OpenOptions::new().write(true).open(NULL_DEVICE).unwrap();
699 OutputRedirection(Redirection::File(null_file))
700 }
701 }
702
703 #[cfg(unix)]
704 pub mod unix {
705 use super::Exec;
706
707 pub trait ExecExt {
708 fn setuid(self, uid: u32) -> Self;
709 fn setgid(self, gid: u32) -> Self;
710 }
711
712 impl ExecExt for Exec {
713 fn setuid(mut self, uid: u32) -> Exec {
714 self.config.setuid = Some(uid);
715 self
716 }
717
718 fn setgid(mut self, gid: u32) -> Exec {
719 self.config.setgid = Some(gid);
720 self
721 }
722 }
723 }
724}
725
726mod pipeline {
727 use std::fmt;
728 use std::fs::File;
729 use std::io::{self, Read, Write};
730 use std::ops::BitOr;
731 use std::rc::Rc;
732
733 use crate::communicate::{self, Communicator};
734 use crate::os_common::ExitStatus;
735 use crate::popen::{Popen, Redirection, Result as PopenResult};
736
737 use super::exec::{CaptureData, Exec, InputRedirection, OutputRedirection};
738
739 /// A builder for multiple [`Popen`] instances connected via
740 /// pipes.
741 ///
742 /// A pipeline is a sequence of two or more [`Exec`] commands
743 /// connected via pipes. Just like in a Unix shell pipeline, each
744 /// command receives standard input from the previous command, and
745 /// passes standard output to the next command. Optionally, the
746 /// standard input of the first command can be provided from the
747 /// outside, and the output of the last command can be captured.
748 ///
749 /// In most cases you do not need to create [`Pipeline`] instances
750 /// directly; instead, combine [`Exec`] instances using the `|`
751 /// operator which produces `Pipeline`.
752 ///
753 /// # Examples
754 ///
755 /// Execute a pipeline and return the exit status of the last command:
756 ///
757 /// ```no_run
758 /// # use subprocess::*;
759 /// # fn dummy() -> Result<()> {
760 /// let exit_status =
761 /// (Exec::shell("ls *.bak") | Exec::cmd("xargs").arg("rm")).join()?;
762 /// # Ok(())
763 /// # }
764 /// ```
765 ///
766 /// Capture the pipeline's output:
767 ///
768 /// ```no_run
769 /// # use subprocess::*;
770 /// # fn dummy() -> Result<()> {
771 /// let dir_checksum = {
772 /// Exec::cmd("find . -type f") | Exec::cmd("sort") | Exec::cmd("sha1sum")
773 /// }.capture()?.stdout_str();
774 /// # Ok(())
775 /// # }
776 /// ```
777 ///
778 /// [`Popen`]: struct.Popen.html
779 /// [`Exec`]: struct.Exec.html
780 /// [`Pipeline`]: struct.Pipeline.html
781
782 #[must_use]
783 pub struct Pipeline {
784 cmds: Vec<Exec>,
785 stdin: Redirection,
786 stdout: Redirection,
787 stderr_file: Option<File>,
788 stdin_data: Option<Vec<u8>>,
789 }
790
791 impl Pipeline {
792 /// Creates a new pipeline by combining two commands.
793 ///
794 /// Equivalent to `cmd1 | cmd2`.
795 pub fn new(cmd1: Exec, cmd2: Exec) -> Pipeline {
796 Pipeline {
797 cmds: vec![cmd1, cmd2],
798 stdin: Redirection::None,
799 stdout: Redirection::None,
800 stderr_file: None,
801 stdin_data: None,
802 }
803 }
804
805 /// Creates a new pipeline from a list of commands. Useful if
806 /// a pipeline should be created dynamically.
807 ///
808 /// Example:
809 /// ```
810 /// use subprocess::Exec;
811 ///
812 /// let commands = vec![
813 /// Exec::shell("echo tset"),
814 /// Exec::shell("tr '[:lower:]' '[:upper:]'"),
815 /// Exec::shell("rev")
816 /// ];
817 ///
818 /// let pipeline = subprocess::Pipeline::from_exec_iter(commands);
819 /// let output = pipeline.capture().unwrap().stdout_str();
820 /// assert_eq!(output, "TEST\n");
821 /// ```
822 /// ```should_panic
823 /// use subprocess::Exec;
824 ///
825 /// let commands = vec![
826 /// Exec::shell("echo tset"),
827 /// ];
828 ///
829 /// // This will panic as the iterator contains less than two (2) items.
830 /// let pipeline = subprocess::Pipeline::from_exec_iter(commands);
831 /// ```
832 /// Errors:
833 /// - Panics when the passed iterator contains less than two (2) items.
834 pub fn from_exec_iter<I>(iterable: I) -> Pipeline
835 where
836 I: IntoIterator<Item = Exec>,
837 {
838 let cmds: Vec<_> = iterable.into_iter().collect();
839
840 if cmds.len() < 2 {
841 panic!("iterator needs to contain at least two (2) elements")
842 }
843
844 Pipeline {
845 cmds,
846 stdin: Redirection::None,
847 stdout: Redirection::None,
848 stderr_file: None,
849 stdin_data: None,
850 }
851 }
852
853 /// Specifies how to set up the standard input of the first
854 /// command in the pipeline.
855 ///
856 /// Argument can be:
857 ///
858 /// * a [`Redirection`];
859 /// * a `File`, which is a shorthand for `Redirection::File(file)`;
860 /// * a `Vec<u8>` or `&str`, which will set up a `Redirection::Pipe`
861 /// for stdin, making sure that `capture` feeds that data into the
862 /// standard input of the subprocess.
863 /// * `NullFile`, which will redirect the standard input to read from
864 /// /dev/null.
865 ///
866 /// [`Redirection`]: enum.Redirection.html
867 pub fn stdin(mut self, stdin: impl Into<InputRedirection>) -> Pipeline {
868 match stdin.into() {
869 InputRedirection::AsRedirection(r) => self.stdin = r,
870 InputRedirection::FeedData(data) => {
871 self.stdin = Redirection::Pipe;
872 self.stdin_data = Some(data);
873 }
874 };
875 self
876 }
877
878 /// Specifies how to set up the standard output of the last
879 /// command in the pipeline.
880 ///
881 /// Argument can be:
882 ///
883 /// * a [`Redirection`];
884 /// * a `File`, which is a shorthand for `Redirection::File(file)`;
885 /// * `NullFile`, which will redirect the standard output to write to
886 /// /dev/null.
887 ///
888 /// [`Redirection`]: enum.Redirection.html
889 pub fn stdout(mut self, stdout: impl Into<OutputRedirection>) -> Pipeline {
890 self.stdout = stdout.into().into_redirection();
891 self
892 }
893
894 /// Specifies a file to which to redirect the standard error of all
895 /// the commands in the pipeline.
896 ///
897 /// It is useful for capturing the standard error of the pipeline as a
898 /// whole. Unlike `stdout()`, which only affects the last command in
899 /// the pipeline, this affects all commands. The difference is
900 /// because standard output is piped from one command to the next, so
901 /// only the output of the last command is "free". In contrast, the
902 /// standard errors are not connected in any way. This is also the
903 /// reason only a `File` is supported - it allows for efficient
904 /// sharing of the same file by all commands.
905 pub fn stderr_to(mut self, to: File) -> Pipeline {
906 self.stderr_file = Some(to);
907 self
908 }
909
910 fn check_no_stdin_data(&self, meth: &str) {
911 if self.stdin_data.is_some() {
912 panic!("{} called with input data specified", meth);
913 }
914 }
915
916 // Terminators:
917
918 /// Starts all commands in the pipeline, and returns a
919 /// `Vec<Popen>` whose members correspond to running commands.
920 ///
921 /// If some command fails to start, the remaining commands
922 /// will not be started, and the appropriate error will be
923 /// returned. The commands that have already started will be
924 /// waited to finish (but will probably exit immediately due
925 /// to missing output), except for the ones for which
926 /// `detached()` was called. This is equivalent to what the
927 /// shell does.
928 pub fn popen(mut self) -> PopenResult<Vec<Popen>> {
929 self.check_no_stdin_data("popen");
930 assert!(self.cmds.len() >= 2);
931
932 if let Some(stderr_to) = self.stderr_file {
933 let stderr_to = Rc::new(stderr_to);
934 self.cmds = self
935 .cmds
936 .into_iter()
937 .map(|cmd| cmd.stderr(Redirection::RcFile(Rc::clone(&stderr_to))))
938 .collect();
939 }
940
941 let first_cmd = self.cmds.drain(..1).next().unwrap();
942 self.cmds.insert(0, first_cmd.stdin(self.stdin));
943
944 let last_cmd = self.cmds.drain(self.cmds.len() - 1..).next().unwrap();
945 self.cmds.push(last_cmd.stdout(self.stdout));
946
947 let mut ret = Vec::<Popen>::new();
948 let cnt = self.cmds.len();
949
950 for (idx, mut runner) in self.cmds.into_iter().enumerate() {
951 if idx != 0 {
952 let prev_stdout = ret[idx - 1].stdout.take().unwrap();
953 runner = runner.stdin(prev_stdout);
954 }
955 if idx != cnt - 1 {
956 runner = runner.stdout(Redirection::Pipe);
957 }
958 ret.push(runner.popen()?);
959 }
960 Ok(ret)
961 }
962
963 /// Starts the pipeline, waits for it to finish, and returns
964 /// the exit status of the last command.
965 pub fn join(self) -> PopenResult<ExitStatus> {
966 self.check_no_stdin_data("join");
967 let mut v = self.popen()?;
968 // Waiting on a pipeline waits for all commands, but
969 // returns the status of the last one. This is how the
970 // shells do it. If the caller needs more precise control
971 // over which status is returned, they can call popen().
972 v.last_mut().unwrap().wait()
973 }
974
975 /// Starts the pipeline and returns a value implementing the `Read`
976 /// trait that reads from the standard output of the last command.
977 ///
978 /// This will automatically set up
979 /// `stdout(Redirection::Pipe)`, so it is not necessary to do
980 /// that beforehand.
981 ///
982 /// When the trait object is dropped, it will wait for the
983 /// pipeline to finish. If this is undesirable, use
984 /// `detached()`.
985 pub fn stream_stdout(self) -> PopenResult<impl Read> {
986 self.check_no_stdin_data("stream_stdout");
987 let v = self.stdout(Redirection::Pipe).popen()?;
988 Ok(ReadPipelineAdapter(v))
989 }
990
991 /// Starts the pipeline and returns a value implementing the `Write`
992 /// trait that writes to the standard input of the last command.
993 ///
994 /// This will automatically set up `stdin(Redirection::Pipe)`,
995 /// so it is not necessary to do that beforehand.
996 ///
997 /// When the trait object is dropped, it will wait for the
998 /// process to finish. If this is undesirable, use
999 /// `detached()`.
1000 pub fn stream_stdin(self) -> PopenResult<impl Write> {
1001 self.check_no_stdin_data("stream_stdin");
1002 let v = self.stdin(Redirection::Pipe).popen()?;
1003 Ok(WritePipelineAdapter(v))
1004 }
1005
1006 fn setup_communicate(mut self) -> PopenResult<(Communicator, Vec<Popen>)> {
1007 assert!(self.cmds.len() >= 2);
1008
1009 let (err_read, err_write) = crate::popen::make_pipe()?;
1010 self = self.stderr_to(err_write);
1011
1012 let stdin_data = self.stdin_data.take();
1013 let mut v = self.stdout(Redirection::Pipe).popen()?;
1014 let vlen = v.len();
1015
1016 let comm = communicate::communicate(
1017 v[0].stdin.take(),
1018 v[vlen - 1].stdout.take(),
1019 Some(err_read),
1020 stdin_data,
1021 );
1022 Ok((comm, v))
1023 }
1024
1025 /// Starts the pipeline and returns a `Communicator` handle.
1026 ///
1027 /// This is a lower-level API that offers more choice in how
1028 /// communication is performed, such as read size limit and timeout,
1029 /// equivalent to [`Popen::communicate`].
1030 ///
1031 /// Unlike `capture()`, this method doesn't wait for the pipeline to
1032 /// finish, effectively detaching it.
1033 ///
1034 /// [`Popen::communicate`]: struct.Popen.html#method.communicate
1035 pub fn communicate(mut self) -> PopenResult<Communicator> {
1036 self.cmds = self.cmds.into_iter().map(|cmd| cmd.detached()).collect();
1037 let comm = self.setup_communicate()?.0;
1038 Ok(comm)
1039 }
1040
1041 /// Starts the pipeline, collects its output, and waits for all
1042 /// commands to finish.
1043 ///
1044 /// The return value provides the standard output of the last command,
1045 /// the combined standard error of all commands, and the exit status
1046 /// of the last command. The captured outputs can be accessed as
1047 /// bytes or strings.
1048 ///
1049 /// Unlike `Popen::communicate`, this method actually waits for the
1050 /// processes to finish, rather than simply waiting for the output to
1051 /// close. If this is undesirable, use `detached()`.
1052 pub fn capture(self) -> PopenResult<CaptureData> {
1053 let (mut comm, mut v) = self.setup_communicate()?;
1054 let (out, err) = comm.read()?;
1055 let out = out.unwrap_or_else(Vec::new);
1056 let err = err.unwrap();
1057
1058 let vlen = v.len();
1059 let status = v[vlen - 1].wait()?;
1060
1061 Ok(CaptureData {
1062 stdout: out,
1063 stderr: err,
1064 exit_status: status,
1065 })
1066 }
1067 }
1068
1069 impl Clone for Pipeline {
1070 /// Returns a copy of the value.
1071 ///
1072 /// This method is guaranteed not to fail as long as none of
1073 /// the `Redirection` values contain a `Redirection::File`
1074 /// variant. If a redirection to `File` is present, cloning
1075 /// that field will use `File::try_clone` method, which
1076 /// duplicates a file descriptor and can (but is not likely
1077 /// to) fail. In that scenario, `Exec::clone` panics.
1078 fn clone(&self) -> Pipeline {
1079 Pipeline {
1080 cmds: self.cmds.clone(),
1081 stdin: self.stdin.try_clone().unwrap(),
1082 stdout: self.stdout.try_clone().unwrap(),
1083 stderr_file: self.stderr_file.as_ref().map(|f| f.try_clone().unwrap()),
1084 stdin_data: self.stdin_data.clone(),
1085 }
1086 }
1087 }
1088
1089 impl BitOr<Exec> for Pipeline {
1090 type Output = Pipeline;
1091
1092 /// Append a command to the pipeline and return a new pipeline.
1093 fn bitor(mut self, rhs: Exec) -> Pipeline {
1094 self.cmds.push(rhs);
1095 self
1096 }
1097 }
1098
1099 impl BitOr for Pipeline {
1100 type Output = Pipeline;
1101
1102 /// Append a pipeline to the pipeline and return a new pipeline.
1103 fn bitor(mut self, rhs: Pipeline) -> Pipeline {
1104 self.cmds.extend(rhs.cmds);
1105 self.stdout = rhs.stdout;
1106 self
1107 }
1108 }
1109
1110 impl fmt::Debug for Pipeline {
1111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1112 let mut args = vec![];
1113 for cmd in &self.cmds {
1114 args.push(cmd.to_cmdline_lossy());
1115 }
1116 write!(f, "Pipeline {{ {} }}", args.join(" | "))
1117 }
1118 }
1119
1120 #[derive(Debug)]
1121 struct ReadPipelineAdapter(Vec<Popen>);
1122
1123 impl Read for ReadPipelineAdapter {
1124 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1125 let last = self.0.last_mut().unwrap();
1126 last.stdout.as_mut().unwrap().read(buf)
1127 }
1128 }
1129
1130 #[derive(Debug)]
1131 struct WritePipelineAdapter(Vec<Popen>);
1132
1133 impl WritePipelineAdapter {
1134 fn stdin(&mut self) -> &mut File {
1135 let first = self.0.first_mut().unwrap();
1136 first.stdin.as_mut().unwrap()
1137 }
1138 }
1139
1140 impl Write for WritePipelineAdapter {
1141 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1142 self.stdin().write(buf)
1143 }
1144 fn flush(&mut self) -> io::Result<()> {
1145 self.stdin().flush()
1146 }
1147 }
1148
1149 impl Drop for WritePipelineAdapter {
1150 // the same rationale as Drop for WriteAdapter
1151 fn drop(&mut self) {
1152 let first = &mut self.0[0];
1153 first.stdin.take();
1154 }
1155 }
1156}