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