subprocess/exec.rs
1#[cfg(unix)]
2mod os {
3 pub const SHELL: [&str; 2] = ["sh", "-c"];
4}
5
6#[cfg(windows)]
7mod os {
8 pub const SHELL: [&str; 2] = ["cmd.exe", "/c"];
9}
10
11use crate::communicate::Communicator;
12use crate::process::ExitStatus;
13use std::collections::HashMap;
14use std::env;
15use std::ffi::OsString;
16use std::fmt;
17use std::fs::File;
18use std::io::{self, Read, Write};
19use std::ops::BitOr;
20use std::path::Path;
21use std::sync::Arc;
22
23use crate::job::Job;
24pub(crate) use crate::job::{ReadAdapter, ReadErrAdapter, WriteAdapter};
25use crate::pipeline::Pipeline;
26use crate::spawn::{Arg, OsOptions, SpawnResult, display_escape, spawn};
27
28use os::*;
29
30/// Instruction what to do with a stream in the child process.
31///
32/// `Redirection` values are used for the `stdin`, `stdout`, and `stderr` parameters when
33/// configuring a subprocess via [`Exec`] or [`Pipeline`].
34///
35/// [`Exec`]: struct.Exec.html
36/// [`Pipeline`]: struct.Pipeline.html
37#[derive(Debug)]
38pub enum Redirection {
39 /// Do nothing with the stream.
40 ///
41 /// The stream is typically inherited from the parent. The corresponding pipe field in
42 /// [`Job`] will be `None`.
43 ///
44 /// [`Job`]: struct.Job.html
45 None,
46
47 /// Redirect the stream to a pipe.
48 ///
49 /// This variant requests that a stream be redirected to a unidirectional pipe. One
50 /// end of the pipe is passed to the child process and configured as one of its
51 /// standard streams, and the other end is available to the parent for communicating
52 /// with the child.
53 Pipe,
54
55 /// Merge the stream to the other output stream.
56 ///
57 /// This variant is only valid when configuring redirection of standard output and
58 /// standard error. Using `Redirection::Merge` for stderr requests the child's stderr
59 /// to refer to the same underlying file as the child's stdout (which may or may not
60 /// itself be redirected), equivalent to the `2>&1` operator of the Bourne
61 /// shell. Analogously, using `Redirection::Merge` for stdout is equivalent to `1>&2`
62 /// in the shell.
63 ///
64 /// Specifying `Redirection::Merge` for stdin or specifying it for both stdout and
65 /// stderr is invalid and will cause an error.
66 Merge,
67
68 /// Redirect the stream to the specified open `File`.
69 ///
70 /// This does not create a pipe, it simply spawns the child so that the specified
71 /// stream sees that file. The child can read from or write to the provided file on
72 /// its own, without any intervention by the parent.
73 File(File),
74
75 /// Redirect the stream to the null device (`/dev/null` on Unix, `nul` on Windows).
76 ///
77 /// This is equivalent to `Redirection::File` with a null device file, but more
78 /// convenient and portable.
79 Null,
80}
81
82/// A builder for creating subprocesses.
83///
84/// `Exec` provides a builder API for spawning subprocesses, and includes convenience
85/// methods for capturing the output and for connecting subprocesses into pipelines.
86///
87/// # Examples
88///
89/// Execute an external command and wait for it to complete:
90///
91/// ```no_run
92/// # use subprocess::*;
93/// # fn dummy() -> std::io::Result<()> {
94/// # let dirname = "some_dir";
95/// let exit_status = Exec::cmd("umount").arg(dirname).join()?;
96/// # Ok(())
97/// # }
98/// ```
99///
100/// Execute the command using the OS shell, like C's `system`:
101///
102/// ```no_run
103/// # use subprocess::*;
104/// # fn dummy() -> std::io::Result<()> {
105/// Exec::shell("shutdown -h now").join()?;
106/// # Ok(())
107/// # }
108/// ```
109///
110/// Start a subprocess and obtain its output as an `impl Read`, like C's `popen`:
111///
112/// ```
113/// # use subprocess::*;
114/// # fn dummy() -> std::io::Result<()> {
115/// let stream = Exec::cmd("ls").stream_stdout()?;
116/// // call stream.read_to_string, construct io::BufReader(stream), etc.
117/// # Ok(())
118/// # }
119/// ```
120///
121/// Capture the output of a command:
122///
123/// ```
124/// # use subprocess::*;
125/// # fn dummy() -> std::io::Result<()> {
126/// let out = Exec::cmd("ls").capture()?.stdout_str();
127/// # Ok(())
128/// # }
129/// ```
130///
131/// Redirect standard error to standard output, and capture both in a single stream:
132///
133/// ```
134/// # use subprocess::*;
135/// # fn dummy() -> std::io::Result<()> {
136/// let out_and_err = Exec::cmd("ls")
137/// .stderr(Redirection::Merge)
138/// .capture()?
139/// .stdout_str();
140/// # Ok(())
141/// # }
142/// ```
143///
144/// Provide input to the command and read its output:
145///
146/// ```
147/// # use subprocess::*;
148/// # fn dummy() -> std::io::Result<()> {
149/// let out = Exec::cmd("sort")
150/// .stdin("b\nc\na\n")
151/// .capture()?
152/// .stdout_str();
153/// assert!(out == "a\nb\nc\n");
154/// # Ok(())
155/// # }
156/// ```
157#[must_use]
158pub struct Exec {
159 command: OsString,
160 args: Vec<Arg>,
161 check_success: bool,
162 stdin_data: Option<InputData>,
163 pub(crate) stdin_redirect: Arc<Redirection>,
164 pub(crate) stdout_redirect: Arc<Redirection>,
165 pub(crate) stderr_redirect: Arc<Redirection>,
166 detached: bool,
167 executable: Option<OsString>,
168 env: Option<Vec<(OsString, OsString)>>,
169 cwd: Option<OsString>,
170 os_options: OsOptions,
171}
172
173impl Exec {
174 /// Constructs a new `Exec`, configured to run `command`.
175 ///
176 /// The command will be run directly in the OS, without an intervening shell. To run
177 /// it through a shell, use [`Exec::shell`] instead.
178 ///
179 /// By default, the command will be run without arguments, and none of the standard
180 /// streams will be modified.
181 ///
182 /// [`Exec::shell`]: struct.Exec.html#method.shell
183 pub fn cmd(command: impl Into<OsString>) -> Exec {
184 Exec {
185 command: command.into(),
186 args: vec![],
187 check_success: false,
188 stdin_data: None,
189 stdin_redirect: Arc::new(Redirection::None),
190 stdout_redirect: Arc::new(Redirection::None),
191 stderr_redirect: Arc::new(Redirection::None),
192 detached: false,
193 executable: None,
194 env: None,
195 cwd: None,
196 os_options: Default::default(),
197 }
198 }
199
200 /// Constructs a new `Exec`, configured to run `cmdstr` with the system shell.
201 ///
202 /// `subprocess` never spawns shells without an explicit request. This command
203 /// requests the shell to be used; on Unix-like systems, this is equivalent to
204 /// `Exec::cmd("sh").arg("-c").arg(cmdstr)`. On Windows, it runs
205 /// `Exec::cmd("cmd.exe").arg("/c").raw_arg(cmdstr)`, passing the command
206 /// string without quoting so that `cmd.exe` interprets it correctly.
207 ///
208 /// `shell` is useful for porting code that uses the C `system` function, which also
209 /// spawns a shell.
210 ///
211 /// When invoking this function, be careful not to interpolate arguments into the
212 /// string run by the shell, such as `Exec::shell(format!("sort {}", filename))`. Such
213 /// code is prone to errors and, if `filename` comes from an untrusted source, to
214 /// shell injection attacks. Instead, use `Exec::cmd("sort").arg(filename)`.
215 pub fn shell(cmdstr: impl Into<OsString>) -> Exec {
216 let cmd = Exec::cmd(SHELL[0]).args(&SHELL[1..]);
217 #[cfg(not(windows))]
218 {
219 cmd.arg(cmdstr)
220 }
221 #[cfg(windows)]
222 {
223 use crate::ExecExt;
224 cmd.raw_arg(cmdstr)
225 }
226 }
227
228 /// Appends `arg` to argument list.
229 pub fn arg(mut self, arg: impl Into<OsString>) -> Exec {
230 self.args.push(Arg::Regular(arg.into()));
231 self
232 }
233
234 /// Extends the argument list with `args`.
235 pub fn args(mut self, args: impl IntoIterator<Item = impl Into<OsString>>) -> Exec {
236 self.args
237 .extend(args.into_iter().map(|x| Arg::Regular(x.into())));
238 self
239 }
240
241 /// Specifies that the process is initially detached.
242 ///
243 /// A detached process means that we will not wait for the process to finish when the
244 /// object that owns it goes out of scope.
245 pub fn detached(mut self) -> Exec {
246 self.detached = true;
247 self
248 }
249
250 /// If called, [`join`](Self::join) and [`capture`](Self::capture) will return an
251 /// error if the process exits with a non-zero status.
252 pub fn checked(mut self) -> Exec {
253 self.check_success = true;
254 self
255 }
256
257 fn ensure_env(&mut self) -> &mut Vec<(OsString, OsString)> {
258 self.env.get_or_insert_with(|| env::vars_os().collect())
259 }
260
261 /// Clears the environment of the subprocess.
262 ///
263 /// When this is invoked, the subprocess will not inherit the environment of this
264 /// process.
265 pub fn env_clear(mut self) -> Exec {
266 self.env = Some(vec![]);
267 self
268 }
269
270 /// Sets an environment variable in the child process.
271 ///
272 /// If the same variable is set more than once, the last value is used.
273 ///
274 /// Other environment variables are by default inherited from the current process. If
275 /// this is undesirable, call `env_clear` first.
276 pub fn env(mut self, key: impl Into<OsString>, value: impl Into<OsString>) -> Exec {
277 self.ensure_env().push((key.into(), value.into()));
278 self
279 }
280
281 /// Sets multiple environment variables in the child process.
282 ///
283 /// The keys and values of the variables are specified by the iterable. If the same
284 /// variable is set more than once, the last value is used.
285 ///
286 /// Other environment variables are by default inherited from the current process. If
287 /// this is undesirable, call `env_clear` first.
288 pub fn env_extend(
289 mut self,
290 vars: impl IntoIterator<Item = (impl Into<OsString>, impl Into<OsString>)>,
291 ) -> Exec {
292 self.ensure_env()
293 .extend(vars.into_iter().map(|(k, v)| (k.into(), v.into())));
294 self
295 }
296
297 /// Removes an environment variable from the child process.
298 ///
299 /// Other environment variables are inherited by default.
300 pub fn env_remove(mut self, key: impl Into<OsString>) -> Exec {
301 let key = key.into();
302 self.ensure_env().retain(|(k, _v)| *k != key);
303 self
304 }
305
306 /// Specifies the current working directory of the child process.
307 ///
308 /// If unspecified, the current working directory is inherited from the parent.
309 pub fn cwd(mut self, dir: impl AsRef<Path>) -> Exec {
310 self.cwd = Some(dir.as_ref().as_os_str().to_owned());
311 self
312 }
313
314 /// Specifies the source for the standard input of the child process.
315 ///
316 /// The source can be:
317 ///
318 /// * a [`Redirection`];
319 /// * a `File`, which is a shorthand for `Redirection::File(file)`;
320 /// * a `Vec<u8>`, `&str`, `&[u8]`, `Box<[u8]>`, or `[u8; N]`, which will set up a
321 /// `Redirection::Pipe` for stdin, feeding that data into the standard input of the
322 /// subprocess;
323 /// * an [`InputData`], which also sets up a pipe, but wraps any reader and feeds its
324 /// content to the standard input of the subprocess. Use [`InputData::from_bytes`]
325 /// for in-memory byte containers not covered by the above, like `bytes::Bytes` or
326 /// `memmap2::Mmap`. Use [`InputData::from_reader`] for a custom `Read` that
327 /// generates or transforms data.
328 ///
329 /// If the child exits before consuming all input, the `BrokenPipe` error is silently
330 /// ignored. Use the exit status and output to check if the child processed the input
331 /// correctly.
332 ///
333 /// [`Redirection`]: enum.Redirection.html
334 /// [`InputData`]: struct.InputData.html
335 pub fn stdin<T>(mut self, stdin: T) -> Exec
336 where
337 InputRedirection: FromSource<T>,
338 {
339 match InputRedirection::from_source(stdin) {
340 InputRedirection::Redirection(new) => {
341 self.stdin_redirect = Arc::new(new);
342 self.stdin_data = None;
343 }
344 InputRedirection::Data(data) => {
345 self.stdin_redirect = Arc::new(Redirection::Pipe);
346 self.stdin_data = Some(data);
347 }
348 }
349 self
350 }
351
352 /// Specifies the sink for the standard output of the child process.
353 ///
354 /// The sink can be:
355 ///
356 /// * a [`Redirection`];
357 /// * a `File`, which is a shorthand for `Redirection::File(file)`.
358 ///
359 /// [`Redirection`]: enum.Redirection.html
360 pub fn stdout<T>(mut self, stdout: T) -> Exec
361 where
362 Redirection: FromSink<T>,
363 {
364 self.stdout_redirect = Arc::new(Redirection::from_sink(stdout));
365 self
366 }
367
368 /// Specifies the sink for the standard error of the child process.
369 ///
370 /// The sink can be:
371 ///
372 /// * a [`Redirection`];
373 /// * a `File`, which is a shorthand for `Redirection::File(file)`.
374 ///
375 /// [`Redirection`]: enum.Redirection.html
376 pub fn stderr<T>(mut self, stderr: T) -> Exec
377 where
378 Redirection: FromSink<T>,
379 {
380 self.stderr_redirect = Arc::new(Redirection::from_sink(stderr));
381 self
382 }
383
384 fn check_no_stdin_data(&self, meth: &str) {
385 if self.stdin_data.is_some() {
386 panic!("{} called with input data specified", meth);
387 }
388 }
389
390 // Terminators
391
392 /// Spawn the process and return the raw spawn result.
393 ///
394 /// This is the low-level entry point used by both `start()` and
395 /// `Pipeline::start()`. It calls `crate::spawn::spawn()` with the Exec's fields.
396 pub(crate) fn spawn(self) -> io::Result<SpawnResult> {
397 let mut argv = self.args;
398 argv.insert(0, Arg::Regular(self.command));
399
400 spawn(
401 argv,
402 self.stdin_redirect,
403 self.stdout_redirect,
404 self.stderr_redirect,
405 self.detached,
406 self.executable.as_deref(),
407 self.env.as_deref(),
408 self.cwd.as_deref(),
409 self.os_options,
410 )
411 }
412
413 /// Starts the process and returns a [`Job`] handle with the running process and its
414 /// pipe ends.
415 pub fn start(mut self) -> io::Result<Job> {
416 let stdin_data = self.stdin_data.take().unwrap_or_default();
417 let check_success = self.check_success;
418
419 let result = self.spawn()?;
420
421 Ok(Job {
422 stdin: result.stdin,
423 stdout: result.stdout,
424 stderr: result.stderr,
425 stdin_data,
426 check_success,
427 processes: vec![result.process],
428 })
429 }
430
431 /// Starts the process, waits for it to finish, and returns the exit status.
432 pub fn join(self) -> io::Result<ExitStatus> {
433 self.start()?.join()
434 }
435
436 /// Starts the process and returns a value implementing the `Read` trait that reads
437 /// from the standard output of the child process.
438 ///
439 /// This will automatically set up `stdout(Redirection::Pipe)`, so it is not necessary
440 /// to do that beforehand.
441 ///
442 /// When the trait object is dropped, it will wait for the process to finish. If this
443 /// is undesirable, use `detached()`.
444 ///
445 /// # Panics
446 ///
447 /// Panics if input data was specified with [`stdin`](Self::stdin). Use
448 /// [`capture`](Self::capture) or [`communicate`](Self::communicate) to both feed
449 /// input and read output.
450 pub fn stream_stdout(self) -> io::Result<impl Read> {
451 self.check_no_stdin_data("stream_stdout");
452 Ok(ReadAdapter(self.stdout(Redirection::Pipe).start()?))
453 }
454
455 /// Starts the process and returns a value implementing the `Read` trait that reads
456 /// from the standard error of the child process.
457 ///
458 /// This will automatically set up `stderr(Redirection::Pipe)`, so it is not necessary
459 /// to do that beforehand.
460 ///
461 /// When the trait object is dropped, it will wait for the process to finish. If this
462 /// is undesirable, use `detached()`.
463 ///
464 /// # Panics
465 ///
466 /// Panics if input data was specified with [`stdin`](Self::stdin). Use
467 /// [`capture`](Self::capture) or [`communicate`](Self::communicate) to both feed
468 /// input and read output.
469 pub fn stream_stderr(self) -> io::Result<impl Read> {
470 self.check_no_stdin_data("stream_stderr");
471 Ok(ReadErrAdapter(self.stderr(Redirection::Pipe).start()?))
472 }
473
474 /// Starts the process and returns a value implementing the `Write` trait that writes
475 /// to the standard input of the child process.
476 ///
477 /// This will automatically set up `stdin(Redirection::Pipe)`, so it is not necessary
478 /// to do that beforehand.
479 ///
480 /// When the trait object is dropped, it will wait for the process to finish. If this
481 /// is undesirable, use `detached()`.
482 ///
483 /// # Panics
484 ///
485 /// Panics if input data was specified with [`stdin`](Self::stdin).
486 pub fn stream_stdin(self) -> io::Result<impl Write> {
487 self.check_no_stdin_data("stream_stdin");
488 Ok(WriteAdapter(self.stdin(Redirection::Pipe).start()?))
489 }
490
491 /// Starts the process and returns a `Communicator` handle.
492 ///
493 /// Unless already configured, stdout and stderr are redirected to pipes. If you
494 /// need different redirection (e.g. `stderr(Merge)`), set it up before calling
495 /// this method and it will be preserved.
496 ///
497 /// Compared to `capture()`, this offers more choice in how communication is
498 /// performed, such as read size limit and timeout. Unlike `capture()`, this
499 /// method doesn't wait for the process to finish, effectively detaching it.
500 pub fn communicate(mut self) -> io::Result<Communicator> {
501 self = self.detached();
502 if matches!(*self.stdout_redirect, Redirection::None) {
503 self = self.stdout(Redirection::Pipe);
504 }
505 if matches!(*self.stderr_redirect, Redirection::None) {
506 self = self.stderr(Redirection::Pipe);
507 }
508 self.start()?.communicate()
509 }
510
511 /// Starts the process, collects its output, and waits for it to finish.
512 ///
513 /// The return value provides the standard output and standard error as bytes or
514 /// optionally strings, as well as the exit status.
515 ///
516 /// Unless already configured, stdout and stderr are redirected to pipes so they
517 /// can be captured. If you need different redirection (e.g. `stderr(Merge)`),
518 /// set it up before calling this method and it will be preserved.
519 ///
520 /// This method waits for the process to finish, rather than simply waiting for
521 /// its standard streams to close. If this is undesirable, use `detached()`.
522 pub fn capture(mut self) -> io::Result<Capture> {
523 if matches!(*self.stdout_redirect, Redirection::None) {
524 self = self.stdout(Redirection::Pipe);
525 }
526 if matches!(*self.stderr_redirect, Redirection::None) {
527 self = self.stderr(Redirection::Pipe);
528 }
529 self.start()?.capture()
530 }
531
532 /// Show Exec as command-line string quoted in the Unix style.
533 pub fn to_cmdline_lossy(&self) -> String {
534 let mut out = String::new();
535 if let Some(cmd_env) = &self.env {
536 let current: Vec<_> = env::vars_os().collect();
537 let current_map: HashMap<_, _> = current.iter().map(|(x, y)| (x, y)).collect();
538 for (k, v) in cmd_env {
539 if current_map.get(k) == Some(&v) {
540 continue;
541 }
542 out.push_str(&display_escape(&k.to_string_lossy()));
543 out.push('=');
544 out.push_str(&display_escape(&v.to_string_lossy()));
545 out.push(' ');
546 }
547 let cmd_env: HashMap<_, _> = cmd_env.iter().map(|(k, v)| (k, v)).collect();
548 for (k, _) in current {
549 if !cmd_env.contains_key(&k) {
550 out.push_str(&display_escape(&k.to_string_lossy()));
551 out.push('=');
552 out.push(' ');
553 }
554 }
555 }
556 out.push_str(&display_escape(&self.command.to_string_lossy()));
557 for arg in &self.args {
558 out.push(' ');
559 out.push_str(&arg.display_escaped());
560 }
561 out
562 }
563
564 pub(crate) fn stdin_is_set(&self) -> bool {
565 !matches!(*self.stdin_redirect, Redirection::None)
566 }
567
568 pub(crate) fn stdout_is_set(&self) -> bool {
569 !matches!(*self.stdout_redirect, Redirection::None)
570 }
571
572 #[cfg(unix)]
573 pub(crate) fn setpgid_is_set(&self) -> bool {
574 self.os_options.setpgid_is_set()
575 }
576
577 #[cfg(unix)]
578 pub(crate) fn set_pgid_value(&mut self, pgid: u32) {
579 self.os_options.set_pgid_value(pgid);
580 }
581}
582
583impl BitOr for Exec {
584 type Output = Pipeline;
585
586 /// Create a `Pipeline` from `self` and `rhs`.
587 fn bitor(self, rhs: Exec) -> Pipeline {
588 Pipeline::new().pipe(self).pipe(rhs)
589 }
590}
591
592impl fmt::Debug for Exec {
593 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
594 write!(f, "Exec {{ {} }}", self.to_cmdline_lossy())
595 }
596}
597
598/// Data captured by [`Exec::capture`] and [`Pipeline::capture`].
599///
600/// [`Exec::capture`]: struct.Exec.html#method.capture
601/// [`Pipeline::capture`]: struct.Pipeline.html#method.capture
602#[derive(Debug)]
603pub struct Capture {
604 /// Standard output as bytes.
605 pub stdout: Vec<u8>,
606 /// Standard error as bytes.
607 pub stderr: Vec<u8>,
608 /// Exit status.
609 pub exit_status: ExitStatus,
610}
611
612impl Capture {
613 /// Returns the standard output as string, converted from bytes using
614 /// `String::from_utf8_lossy`.
615 pub fn stdout_str(&self) -> String {
616 String::from_utf8_lossy(&self.stdout).into_owned()
617 }
618
619 /// Returns the standard error as string, converted from bytes using
620 /// `String::from_utf8_lossy`.
621 pub fn stderr_str(&self) -> String {
622 String::from_utf8_lossy(&self.stderr).into_owned()
623 }
624
625 /// True if the exit status of the process or pipeline is 0.
626 pub fn success(&self) -> bool {
627 self.exit_status.success()
628 }
629
630 /// Returns `self` if the exit status is successful, or an error otherwise.
631 pub fn check(self) -> io::Result<Self> {
632 if self.success() {
633 Ok(self)
634 } else {
635 Err(io::Error::other(format!(
636 "command failed: {}",
637 self.exit_status
638 )))
639 }
640 }
641}
642
643/// Type-erased readable source for input data fed to a subprocess's stdin.
644///
645/// `InputData` wraps a reader, so it can supply stdin data from any readable source:
646/// in-memory bytes, a custom `Read` implementation that generates or transforms data,
647/// etc.
648///
649/// Use [`InputData::from_bytes`] for in-memory byte data (`Vec<u8>`, `bytes::Bytes`,
650/// `memmap2::Mmap`, etc.) and [`InputData::from_reader`] for a custom `Read`
651/// implementation.
652pub struct InputData(Box<dyn Read + Send + Sync>);
653
654impl InputData {
655 /// Create `InputData` from in-memory byte data.
656 ///
657 /// Accepts any type that implements `AsRef<[u8]>`, such as `Vec<u8>`, `Box<[u8]>`,
658 /// `bytes::Bytes`, or `memmap2::Mmap`.
659 pub fn from_bytes(data: impl AsRef<[u8]> + Send + Sync + 'static) -> Self {
660 InputData(Box::new(io::Cursor::new(data)))
661 }
662
663 /// Create `InputData` from a custom `Read` implementation that generates or
664 /// transforms data on the fly.
665 pub fn from_reader(reader: impl Read + Send + Sync + 'static) -> Self {
666 InputData(Box::new(reader))
667 }
668}
669
670impl Read for InputData {
671 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
672 self.0.read(buf)
673 }
674}
675
676impl Default for InputData {
677 fn default() -> Self {
678 InputData(Box::new(io::empty()))
679 }
680}
681
682impl fmt::Debug for InputData {
683 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
684 f.write_str("InputData(..)")
685 }
686}
687
688#[derive(Debug)]
689pub enum InputRedirection {
690 Redirection(Redirection),
691 Data(InputData),
692}
693
694/// Trait for converting a source type into an input redirection.
695///
696/// Implemented for each type accepted by [`Exec::stdin`] and
697/// [`Pipeline::stdin`](crate::Pipeline::stdin).
698pub trait FromSource<T> {
699 /// Create the input redirection from the given source.
700 fn from_source(source: T) -> Self;
701}
702
703/// Trait for converting a sink type into an output redirection.
704///
705/// Implemented on [`Redirection`] for each type accepted by
706/// [`Exec::stdout`], [`Exec::stderr`], and their `Pipeline` equivalents.
707pub trait FromSink<T> {
708 /// Create the output redirection from the given sink.
709 fn from_sink(sink: T) -> Self;
710}
711
712impl FromSource<Redirection> for InputRedirection {
713 fn from_source(source: Redirection) -> Self {
714 if let Redirection::Merge = source {
715 panic!("Redirection::Merge is only allowed for output streams");
716 }
717 InputRedirection::Redirection(source)
718 }
719}
720
721impl FromSource<File> for InputRedirection {
722 fn from_source(source: File) -> Self {
723 InputRedirection::Redirection(Redirection::File(source))
724 }
725}
726
727impl FromSource<InputData> for InputRedirection {
728 fn from_source(source: InputData) -> Self {
729 InputRedirection::Data(source)
730 }
731}
732
733impl FromSource<Vec<u8>> for InputRedirection {
734 fn from_source(source: Vec<u8>) -> Self {
735 InputRedirection::Data(InputData::from_bytes(source))
736 }
737}
738
739impl FromSource<&'static str> for InputRedirection {
740 fn from_source(source: &'static str) -> Self {
741 InputRedirection::Data(InputData::from_bytes(source))
742 }
743}
744
745impl FromSource<&'static [u8]> for InputRedirection {
746 fn from_source(source: &'static [u8]) -> Self {
747 InputRedirection::Data(InputData::from_bytes(source))
748 }
749}
750
751impl<const N: usize> FromSource<&'static [u8; N]> for InputRedirection {
752 fn from_source(source: &'static [u8; N]) -> Self {
753 InputRedirection::Data(InputData::from_bytes(source))
754 }
755}
756
757impl<const N: usize> FromSource<[u8; N]> for InputRedirection {
758 fn from_source(source: [u8; N]) -> Self {
759 InputRedirection::Data(InputData::from_bytes(source))
760 }
761}
762
763impl FromSource<Box<[u8]>> for InputRedirection {
764 fn from_source(source: Box<[u8]>) -> Self {
765 InputRedirection::Data(InputData::from_bytes(source))
766 }
767}
768
769impl FromSink<Redirection> for Redirection {
770 fn from_sink(sink: Redirection) -> Self {
771 sink
772 }
773}
774
775impl FromSink<File> for Redirection {
776 fn from_sink(sink: File) -> Self {
777 Redirection::File(sink)
778 }
779}
780
781#[cfg(unix)]
782pub mod unix {
783 use super::Exec;
784 use crate::job::Job;
785 use crate::pipeline::Pipeline;
786 use crate::unix::ProcessExt;
787 use std::io;
788
789 /// Unix-specific extension methods for [`Job`].
790 pub trait JobExt {
791 /// Send the specified signal to all processes in the pipeline.
792 ///
793 /// Delegates to [`ProcessExt::send_signal`] on each process.
794 fn send_signal(&self, signal: i32) -> io::Result<()>;
795
796 /// Send the specified signal to the process group of the first process.
797 ///
798 /// When used with [`PipelineExt::setpgid`], all pipeline processes share the
799 /// first process's group, so signaling it reaches the entire pipeline. For a
800 /// single process started with [`ExecExt::setpgid`], this signals its group.
801 fn send_signal_group(&self, signal: i32) -> io::Result<()>;
802 }
803
804 impl JobExt for Job {
805 fn send_signal(&self, signal: i32) -> io::Result<()> {
806 for p in &self.processes {
807 p.send_signal(signal)?;
808 }
809 Ok(())
810 }
811
812 fn send_signal_group(&self, signal: i32) -> io::Result<()> {
813 if let Some(p) = self.processes.first() {
814 p.send_signal_group(signal)?;
815 }
816 Ok(())
817 }
818 }
819
820 /// Extension trait for Unix-specific process creation options.
821 pub trait ExecExt {
822 /// Set the user ID for the spawned process.
823 ///
824 /// The child process will run with the specified user ID, which affects file
825 /// access permissions and process ownership. This calls `setuid(2)` in the child
826 /// process after `fork()` but before `exec()`.
827 fn setuid(self, uid: u32) -> Self;
828
829 /// Set the group ID for the spawned process.
830 ///
831 /// The child process will run with the specified group ID, which affects file
832 /// access permissions based on group ownership. This calls `setgid(2)` in the
833 /// child process after `fork()` but before `exec()`.
834 fn setgid(self, gid: u32) -> Self;
835
836 /// Put the subprocess into its own process group.
837 ///
838 /// This calls `setpgid(0, 0)` before execing the child process, making it the
839 /// leader of a new process group. Useful for a single process that spawns
840 /// children, allowing them all to be signaled as a group with
841 /// [`ProcessExt::send_signal_group`].
842 ///
843 /// For pipelines, use [`PipelineExt::setpgid`] instead, which puts all pipeline
844 /// processes into a shared group.
845 ///
846 /// [`ProcessExt::send_signal_group`]: crate::unix::ProcessExt::send_signal_group
847 /// [`PipelineExt::setpgid`]: PipelineExt::setpgid
848 fn setpgid(self) -> Self;
849 }
850
851 impl ExecExt for Exec {
852 fn setuid(mut self, uid: u32) -> Exec {
853 self.os_options.setuid = Some(uid);
854 self
855 }
856
857 fn setgid(mut self, gid: u32) -> Exec {
858 self.os_options.setgid = Some(gid);
859 self
860 }
861
862 fn setpgid(mut self) -> Exec {
863 self.os_options.setpgid = Some(0);
864 self
865 }
866 }
867
868 /// Unix-specific extension methods for [`Pipeline`].
869 pub trait PipelineExt {
870 /// Put all pipeline processes into a shared process group.
871 ///
872 /// The first process becomes the group leader (via `setpgid(0, 0)`) and
873 /// subsequent processes join its group. This allows signaling the entire
874 /// pipeline as a unit using [`JobExt::send_signal_group`].
875 ///
876 /// For single processes that spawn children, use [`ExecExt::setpgid`] instead.
877 fn setpgid(self) -> Self;
878 }
879
880 impl PipelineExt for Pipeline {
881 fn setpgid(mut self) -> Pipeline {
882 self.set_setpgid(true);
883 self
884 }
885 }
886}
887
888#[cfg(any(windows, docsrs))]
889pub mod windows {
890 use std::ffi::OsString;
891
892 use super::Exec;
893 #[cfg(windows)]
894 use crate::spawn::Arg;
895
896 /// Process creation flag: The process does not have a console window.
897 pub const CREATE_NO_WINDOW: u32 = 0x08000000;
898
899 /// Process creation flag: The new process has a new console.
900 pub const CREATE_NEW_CONSOLE: u32 = 0x00000010;
901
902 /// Process creation flag: The new process is the root of a new process
903 /// group.
904 pub const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
905
906 /// Process creation flag: The process does not inherit its parent's
907 /// console.
908 pub const DETACHED_PROCESS: u32 = 0x00000008;
909
910 /// Extension trait for Windows-specific process creation options.
911 pub trait ExecExt {
912 /// Set process creation flags for Windows.
913 ///
914 /// This value is passed to the `dwCreationFlags` parameter of
915 /// `CreateProcessW`. Use this to control process creation behavior
916 /// such as creating the process without a console window.
917 fn creation_flags(self, flags: u32) -> Self;
918
919 /// Appends `arg` to the command line without quoting or escaping.
920 ///
921 /// This is useful for passing arguments to programs that use non-standard command
922 /// line parsing, such as `cmd.exe /c`. For example, passing `git commit -m "feat:
923 /// widget"` through [`arg`](super::Exec::arg) would apply MSVC-style argv
924 /// quoting, producing backslash escapes that `cmd.exe` would interpret literally.
925 ///
926 /// [`Exec::shell`](super::Exec::shell) uses this method to pass the command
927 /// string to `cmd.exe /c`.
928 ///
929 /// # Background
930 ///
931 /// On Windows, process arguments are not passed as an array (as on Unix) but as a
932 /// single command-line string to `CreateProcessW`. The regular
933 /// [`arg`](super::Exec::arg) method applies MSVC C runtime quoting rules so that
934 /// programs using standard `CommandLineToArgvW` parsing can reconstruct the
935 /// original arguments. `raw_arg` bypasses all quoting and passes its argument to
936 /// the command line verbatim. This is equivalent to [`raw_arg` in the standard
937 /// library](https://doc.rust-lang.org/std/os/windows/process/trait.CommandExt.html#tymethod.raw_arg).
938 ///
939 /// Only use `raw_arg` with hardcoded or carefully validated input, as no quoting
940 /// or escaping is applied. [`Exec::shell`](super::Exec::shell) is an exception -
941 /// a shell command must reach the shell verbatim, so quoting would corrupt it
942 /// rather than protect it.
943 fn raw_arg(self, arg: impl Into<OsString>) -> Self;
944 }
945
946 impl ExecExt for Exec {
947 fn creation_flags(mut self, flags: u32) -> Exec {
948 #[cfg(windows)]
949 {
950 self.os_options.creation_flags = flags;
951 self
952 }
953 #[cfg(not(windows))]
954 {
955 let _ = flags;
956 unimplemented!()
957 }
958 }
959
960 fn raw_arg(mut self, arg: impl Into<OsString>) -> Exec {
961 #[cfg(windows)]
962 {
963 self.args.push(Arg::Raw(arg.into()));
964 self
965 }
966 #[cfg(not(windows))]
967 {
968 let _ = arg;
969 unimplemented!()
970 }
971 }
972 }
973}