Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to see inherited envs on Command #110327

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 42 additions & 2 deletions library/std/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ use crate::path::Path;
use crate::str;
use crate::sys::pipe::{read2, AnonPipe};
use crate::sys::process as imp;
#[unstable(feature = "command_captured_envs_access", issue = "none")]
pub use crate::sys_common::process::CapturedEnvs;
#[stable(feature = "command_access", since = "1.57.0")]
pub use crate::sys_common::process::CommandEnvs;
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
Expand Down Expand Up @@ -1048,8 +1050,9 @@ impl Command {
/// Environment variables explicitly set using [`Command::env`], [`Command::envs`], and
/// [`Command::env_remove`] can be retrieved with this method.
///
/// Note that this output does not include environment variables inherited from the parent
/// process.
/// Note that this output does not include environment variables inherited
/// from the parent process. To get the variables that will be set
/// when the child spawns use [`Command::capture_envs`].
///
/// Each element is a tuple key/value pair `(&OsStr, Option<&OsStr>)`. A [`None`] value
/// indicates its key was explicitly removed via [`Command::env_remove`]. The associated key for
Expand Down Expand Up @@ -1078,6 +1081,43 @@ impl Command {
self.inner.get_envs()
}

/// Captures a snapshot of all environment variables, inherited and explicitly set, that would
/// have been passed to the child process.
///
/// Return value is an iterator where each element is a tuple `(OsString, OsString)`. The first
/// value is the environment variable key, and the second is its value. Unlike
/// [`Command::get_envs`], this method includes inherited and explicitly set environment
/// variables.
///
/// Note that this method does not prevent time-of-check to time-of-use (TOCTOU) bugs. You
/// should only use it when those bugs are not an issue. If no changes occur between when this
/// method is called and when the command is spawned, the values returned will be the same as
/// those of the spawned child process.
///
/// # Examples
///
/// ```
/// use std::ffi::OsString;
/// use std::process::Command;
/// use std::collections::HashMap;
///
/// let mut cmd = Command::new("ls");
/// cmd.env("TERM", "dumb");
/// cmd.env_remove("TZ");
///
/// let envs: HashMap<OsString, OsString> = cmd.capture_envs().collect();
/// // Inherited environment variable(s)
/// assert!(envs.contains_key(&OsString::from("PATH")));
///
/// // Explicitly set environment variable(s)
/// assert!(envs.contains_key(&OsString::from("TERM")));
/// assert!(!envs.contains_key(&OsString::from("TZ")));
/// ```
#[unstable(feature = "command_captured_envs_access", issue = "none")]
pub fn capture_envs(&self) -> CapturedEnvs {
self.inner.capture_envs()
}

/// Returns the working directory for the child process.
///
/// This returns [`None`] if the working directory will not be changed.
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/unix/process/process_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::ptr;
use crate::sys::fd::FileDesc;
use crate::sys::fs::File;
use crate::sys::pipe::{self, AnonPipe};
use crate::sys_common::process::{CommandEnv, CommandEnvs};
use crate::sys_common::process::{CapturedEnvs, CommandEnv, CommandEnvs};
use crate::sys_common::{FromInner, IntoInner};

#[cfg(not(target_os = "fuchsia"))]
Expand Down Expand Up @@ -302,6 +302,10 @@ impl Command {
self.env.iter()
}

pub fn capture_envs(&self) -> CapturedEnvs {
self.env.capture_iter()
}

pub fn get_current_dir(&self) -> Option<&Path> {
self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes())))
}
Expand Down
6 changes: 5 additions & 1 deletion library/std/src/sys/windows/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::sys::handle::Handle;
use crate::sys::path;
use crate::sys::pipe::{self, AnonPipe};
use crate::sys::stdio;
use crate::sys_common::process::{CommandEnv, CommandEnvs};
use crate::sys_common::process::{CapturedEnvs, CommandEnv, CommandEnvs};
use crate::sys_common::IntoInner;

use core::ffi::c_void;
Expand Down Expand Up @@ -244,6 +244,10 @@ impl Command {
self.env.iter()
}

pub fn capture_envs(&self) -> CapturedEnvs {
self.env.capture_iter()
}

pub fn get_current_dir(&self) -> Option<&Path> {
self.cwd.as_ref().map(|cwd| Path::new(cwd))
}
Expand Down
39 changes: 38 additions & 1 deletion library/std/src/sys_common/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,46 @@ impl CommandEnv {
let iter = self.vars.iter();
CommandEnvs { iter }
}

pub fn capture_iter(&self) -> CapturedEnvs {
let iter = self.capture().into_iter();

CapturedEnvs { iter }
}
}

/// An iterator over captured [`Command`][crate::process::Command] environment variables.
///
/// This struct is created by
/// [`Command::capture_envs`][crate::process::Command::capture_envs]. It includes inherited and
/// explicitly set environment variables.
///
/// See [`Command::capture_envs`][crate::process::Command::capture_envs] documentation for more.
pub struct CapturedEnvs {
iter: crate::collections::btree_map::IntoIter<EnvKey, OsString>,
}

/// An iterator over the command environment variables.
impl Iterator for CapturedEnvs {
type Item = (OsString, OsString);

fn next(&mut self) -> Option<Self::Item> {
self.iter.next().into()
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}

/// An iterator over the explicitly set [`Command`][crate::process::Command] environment variables.
///
/// Each element is a tuple key/value pair `(&OsStr, Option<&OsStr>)`. A [`None`] value
/// indicates its key was explicitly removed. The associated key for the [`None`] value will no
/// longer inherit from its parent process.
///
/// Note that this output does not include environment variables inherited from the parent process.
/// To get the variables that will be set when the child spawns use
/// [`Command::captured_envs`][crate::process::Command::captured_envs].
///
/// This struct is created by
/// [`Command::get_envs`][crate::process::Command::get_envs]. See its
Expand Down
Loading