753 lines · 25.8 KB · Rust
RawDownload
// Allow disallowed types here. We actively want to use `std::process::Command` since this is the// wrapper implementation that allows us to not import the above type elsewhere in this workspace.#![allow(clippy::disallowed_types)] use std::ffi::OsStr;use std::io;use std::path::Path;use std::process::{Child, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio}; #[cfg(windows)]use anyhow::Context as _;#[cfg(windows)]use warp_errors::report_error;#[cfg(windows)]use {super::windows::JobObject, std::os::windows::io::AsRawHandle}; /// Wrapper around a [`std::process::Command`] that ensures any new Command is set with the windows/// `CREATE_NO_WINDOW` flag to avoid a console window temporarily popping up.#[derive(Debug)]pub struct Command {    pub(super) inner: std::process::Command,    #[cfg(windows)]    kill_on_parent_process_close: bool,    stdin_is_default: bool,    stdout_is_default: bool,    stderr_is_default: bool,} impl Command {    /// Constructs a new `Command` for launching the program at    /// path `program`, with the following default configuration:    ///    /// * No arguments to the program    /// * Inherit the current process's environment    /// * Inherit the current process's working directory    /// * Inherit stdin/stdout/stderr for [`spawn`] or [`status`], but create pipes for [`output`]    ///    /// [`spawn`]: Self::spawn    /// [`status`]: Self::status    /// [`output`]: Self::output    ///    /// Builder methods are provided to change these defaults and    /// otherwise configure the process.    ///    /// If `program` is not an absolute path, the `PATH` will be searched in    /// an OS-defined way.    ///    /// The search path to be used may be controlled by setting the    /// `PATH` environment variable on the Command,    /// but this has some implementation limitations on Windows    /// (see issue #37519).    ///    /// # Platform-specific behavior    ///    /// Note on Windows: For executable files with the .exe extension,    /// it can be omitted when specifying the program for this Command.    /// However, if the file has a different extension,    /// a filename including the extension needs to be provided,    /// otherwise the file won't be found.    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::Command;    ///    /// Command::new("sh")    ///     .spawn()    ///     .expect("sh command failed to start");    /// ```    pub fn new<S: AsRef<OsStr>>(program: S) -> Command {        let program = crate::wsl::translate_program_for_spawn(program.as_ref());        #[cfg_attr(not(windows), expect(unused_mut))]        let mut inner = std::process::Command::new(program);         #[cfg(windows)]        {            use std::os::windows::process::CommandExt;            // We need to set the `CREATE_BREAKAWAY_FROM_JOB` flag to avoid assigning            // the process to the same Job Object as the Warp process, otherwise the            // process will be killed when the Warp process is killed.            let flags = windows::Win32::System::Threading::CREATE_NO_WINDOW.0                | windows::Win32::System::Threading::CREATE_BREAKAWAY_FROM_JOB.0;            inner.creation_flags(flags);        }        Self {            inner,            #[cfg(windows)]            kill_on_parent_process_close: false,            stdin_is_default: true,            stdout_is_default: true,            stderr_is_default: true,        }    }     #[cfg(windows)]    /// Sets the [process creation flags][1] to be passed to `CreateProcess`.    ///    /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT` and `CREATE_NO_WINDOW`.    /// The latter is needed to avoid a console window temporarily popping up in Warp.    ///    /// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx    pub fn creation_flags(&mut self, flags: u32) -> &mut Self {        use std::os::windows::process::CommandExt;        let flags = windows::Win32::System::Threading::CREATE_NO_WINDOW.0 | flags;        self.inner.creation_flags(flags);        self    }     /// Adds an argument to pass to the program.    ///    /// Only one argument can be passed per use. So instead of:    ///    /// ```no_run    /// # std::process::Command::new("sh")    /// .arg("-C /path/to/repo")    /// # ;    /// ```    ///    /// usage would be:    ///    /// ```no_run    /// # std::process::Command::new("sh")    /// .arg("-C")    /// .arg("/path/to/repo")    /// # ;    /// ```    ///    /// To pass multiple arguments see [`args`].    ///    /// [`args`]: std::process::Command::args    ///    /// Note that the argument is not passed through a shell, but given    /// literally to the program. This means that shell syntax like quotes,    /// escaped characters, word splitting, glob patterns, variable substitution,    /// etc. have no effect.    ///    /// <div class="warning">    ///    /// On Windows, use caution with untrusted inputs. Most applications use the    /// standard convention for decoding arguments passed to them. These are safe to    /// use with `arg`. However, some applications such as `cmd.exe` and `.bat` files    /// use a non-standard way of decoding arguments. They are therefore vulnerable    /// to malicious input.    ///    /// In the case of `cmd.exe` this is especially important because a malicious    /// argument can potentially run arbitrary shell commands.    ///    /// See [Windows argument splitting][windows-args] for more details    /// or [`raw_arg`] for manually implementing non-standard argument encoding.    ///    /// [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg    /// [windows-args]: crate::process#windows-argument-splitting    ///    /// </div>    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::Command;    ///    /// Command::new("ls")    ///     .arg("-l")    ///     .arg("-a")    ///     .spawn()    ///     .expect("ls command failed to start");    /// ```    pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {        self.inner.arg(arg);        self    }     /// Adds multiple arguments to pass to the program.    ///    /// To pass a single argument see [`arg`].    ///    /// [`arg`]: std::process::Command::arg    ///    /// Note that the arguments are not passed through a shell, but given    /// literally to the program. This means that shell syntax like quotes,    /// escaped characters, word splitting, glob patterns, variable substitution, etc.    /// have no effect.    ///    /// <div class="warning">    ///    /// On Windows, use caution with untrusted inputs. Most applications use the    /// standard convention for decoding arguments passed to them. These are safe to    /// use with `arg`. However, some applications such as `cmd.exe` and `.bat` files    /// use a non-standard way of decoding arguments. They are therefore vulnerable    /// to malicious input.    ///    /// In the case of `cmd.exe` this is especially important because a malicious    /// argument can potentially run arbitrary shell commands.    ///    /// See [Windows argument splitting][windows-args] for more details    /// or [`raw_arg`] for manually implementing non-standard argument encoding.    ///    /// [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg    /// [windows-args]: crate::process#windows-argument-splitting    ///    /// </div>    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::Command;    ///    /// Command::new("ls")    ///     .args(["-l", "-a"])    ///     .spawn()    ///     .expect("ls command failed to start");    /// ```    pub fn args<I, S>(&mut self, args: I) -> &mut Command    where        I: IntoIterator<Item = S>,        S: AsRef<OsStr>,    {        self.inner.args(args);        self    }     /// Inserts or updates an explicit environment variable mapping.    ///    /// This method allows you to add an environment variable mapping to the spawned process or    /// overwrite a previously set value. You can use [`std::process::Command::envs`] to set multiple environment    /// variables simultaneously.    ///    /// Child processes will inherit environment variables from their parent process by default.    /// Environment variables explicitly set using [`std::process::Command::env`] take precedence over inherited    /// variables. You can disable environment variable inheritance entirely using    /// [`std::process::Command::env_clear`] or for a single key using [`std::process::Command::env_remove`].    ///    /// Note that environment variable names are case-insensitive (but    /// case-preserving) on Windows and case-sensitive on all other platforms.    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::Command;    ///    /// Command::new("ls")    ///     .env("PATH", "/bin")    ///     .spawn()    ///     .expect("ls command failed to start");    /// ```    pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Command    where        K: AsRef<OsStr>,        V: AsRef<OsStr>,    {        self.inner.env(key, val);        self    }     /// Inserts or updates multiple explicit environment variable mappings.    ///    /// This method allows you to add multiple environment variable mappings to the spawned process    /// or overwrite previously set values. You can use [`std::process::Command::env`] to set a single environment    /// variable.    ///    /// Child processes will inherit environment variables from their parent process by default.    /// Environment variables explicitly set using [`std::process::Command::envs`] take precedence over inherited    /// variables. You can disable environment variable inheritance entirely using    /// [`std::process::Command::env_clear`] or for a single key using [`std::process::Command::env_remove`].    ///    /// Note that environment variable names are case-insensitive (but case-preserving) on Windows    /// and case-sensitive on all other platforms.    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::{Command, Stdio};    /// use std::env;    /// use std::collections::HashMap;    ///    /// let filtered_env : HashMap<String, String> =    ///     env::vars().filter(|&(ref k, _)|    ///         k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH"    ///     ).collect();    ///    /// Command::new("printenv")    ///     .stdin(Stdio::null())    ///     .stdout(Stdio::inherit())    ///     .env_clear()    ///     .envs(&filtered_env)    ///     .spawn()    ///     .expect("printenv failed to start");    /// ```    pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Command    where        I: IntoIterator<Item = (K, V)>,        K: AsRef<OsStr>,        V: AsRef<OsStr>,    {        self.inner.envs(vars);        self    }     /// Removes an explicitly set environment variable and prevents inheriting it from a parent    /// process.    ///    /// This method will remove the explicit value of an environment variable set via    /// [`std::process::Command::env`] or [`std::process::Command::envs`]. In addition, it will prevent the spawned child    /// process from inheriting that environment variable from its parent process.    ///    /// After calling [`std::process::Command::env_remove`], the value associated with its key from    /// [`std::process::Command::get_envs`] will be [`None`].    ///    /// To clear all explicitly set environment variables and disable all environment variable    /// inheritance, you can use [`std::process::Command::env_clear`].    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::Command;    ///    /// Command::new("ls")    ///     .env_remove("PATH")    ///     .spawn()    ///     .expect("ls command failed to start");    /// ```    pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Command {        self.inner.env_remove(key);        self    }     /// Clears all explicitly set environment variables and prevents inheriting any parent process    /// environment variables.    ///    /// This method will remove all explicitly added environment variables set via [`std::process::Command::env`]    /// or [`std::process::Command::envs`]. In addition, it will prevent the spawned child process from inheriting    /// any environment variable from its parent process.    ///    /// After calling [`std::process::Command::env_clear`], the iterator from [`std::process::Command::get_envs`] will be    /// empty.    ///    /// You can use [`std::process::Command::env_remove`] to clear a single mapping.    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::Command;    ///    /// Command::new("ls")    ///     .env_clear()    ///     .spawn()    ///     .expect("ls command failed to start");    /// ```    pub fn env_clear(&mut self) -> &mut Command {        self.inner.env_clear();        self    }     /// Sets the working directory for the child process.    ///    /// # Platform-specific behavior    ///    /// If the program path is relative (e.g., `"./script.sh"`), it's ambiguous    /// whether it should be interpreted relative to the parent's working    /// directory or relative to `current_dir`. The behavior in this case is    /// platform specific and unstable, and it's recommended to use    /// [`canonicalize`] to get an absolute program path instead.    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::Command;    ///    /// Command::new("ls")    ///     .current_dir("/bin")    ///     .spawn()    ///     .expect("ls command failed to start");    /// ```    ///    /// [`canonicalize`]: crate::fs::canonicalize    pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command {        self.inner.current_dir(dir);        self    }     /// Configuration for the child process's standard input (stdin) handle.    ///    /// Defaults to [`inherit`] when used with [`spawn`] or [`status`], and    /// defaults to [`piped`] when used with [`output`].    ///    /// [`inherit`]: Stdio::inherit    /// [`piped`]: Stdio::piped    /// [`spawn`]: Self::spawn    /// [`status`]: Self::status    /// [`output`]: Self::output    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::{Command, Stdio};    ///    /// Command::new("ls")    ///     .stdin(Stdio::null())    ///     .spawn()    ///     .expect("ls command failed to start");    /// ```    pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {        self.inner.stdin(cfg);        self.stdin_is_default = false;        self    }     /// Configuration for the child process's standard output (stdout) handle.    ///    /// Defaults to [`inherit`] when used with [`spawn`] or [`status`], and    /// defaults to [`piped`] when used with [`output`].    ///    /// [`inherit`]: Stdio::inherit    /// [`piped`]: Stdio::piped    /// [`spawn`]: Self::spawn    /// [`status`]: Self::status    /// [`output`]: Self::output    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::{Command, Stdio};    ///    /// Command::new("ls")    ///     .stdout(Stdio::null())    ///     .spawn()    ///     .expect("ls command failed to start");    /// ```    pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {        self.inner.stdout(cfg);        self.stdout_is_default = false;        self    }     /// Configuration for the child process's standard error (stderr) handle.    ///    /// Defaults to [`inherit`] when used with [`spawn`] or [`status`], and    /// defaults to [`piped`] when used with [`output`].    ///    /// [`inherit`]: Stdio::inherit    /// [`piped`]: Stdio::piped    /// [`spawn`]: Self::spawn    /// [`status`]: Self::status    /// [`output`]: Self::output    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::{Command, Stdio};    ///    /// Command::new("ls")    ///     .stderr(Stdio::null())    ///     .spawn()    ///     .expect("ls command failed to start");    /// ```    pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Command {        self.inner.stderr(cfg);        self.stderr_is_default = false;        self    }     /// Executes the command as a child process, returning a handle to it.    ///    /// By default, stdin, stdout and stderr are set to null (diverging from    /// the std library behavior of defaulting to inheriting from the parent).    ///    /// # Examples    ///    /// Basic usage:    ///    /// ```no_run    /// use std::process::Command;    ///    /// Command::new("ls")    ///     .spawn()    ///     .expect("ls command failed to start");    /// ```    pub fn spawn(&mut self) -> io::Result<Child> {        if self.stdin_is_default {

Lines 1–500 of 753.