diff options
| author | bors <bors@rust-lang.org> | 2017-12-24 20:57:20 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2017-12-24 20:57:20 +0000 |
| commit | c284f8807eb3a1d728242bb6a767b0306d6f6bd5 (patch) | |
| tree | 7e9882d51643927aa2160bdcb7ecd9cad0df36d2 /src/libstd/sys/unix | |
| parent | b9e4d3417fbd89563fcf15238d6741ffdee600bf (diff) | |
| parent | ccc91d7b4873a50678b3f65c895290915c54f6f5 (diff) | |
| download | rust-c284f8807eb3a1d728242bb6a767b0306d6f6bd5.tar.gz rust-c284f8807eb3a1d728242bb6a767b0306d6f6bd5.zip | |
Auto merge of #46789 - Diggsey:command-env-capture, r=dtolnay
Capture `Command` environment at spawn Fixes #28975 This tracks a set of changes to the environment and then replays them at spawn time.
Diffstat (limited to 'src/libstd/sys/unix')
| -rw-r--r-- | src/libstd/sys/unix/process/process_common.rs | 143 | ||||
| -rw-r--r-- | src/libstd/sys/unix/process/process_fuchsia.rs | 10 | ||||
| -rw-r--r-- | src/libstd/sys/unix/process/process_unix.rs | 16 |
3 files changed, 78 insertions, 91 deletions
diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs index 383434b1cd8..c53bcdbf8e3 100644 --- a/src/libstd/sys/unix/process/process_common.rs +++ b/src/libstd/sys/unix/process/process_common.rs @@ -10,8 +10,6 @@ use os::unix::prelude::*; -use collections::hash_map::{HashMap, Entry}; -use env; use ffi::{OsString, OsStr, CString, CStr}; use fmt; use io; @@ -20,6 +18,8 @@ use ptr; use sys::fd::FileDesc; use sys::fs::{File, OpenOptions}; use sys::pipe::{self, AnonPipe}; +use sys_common::process::{CommandEnv, DefaultEnvKey}; +use collections::BTreeMap; //////////////////////////////////////////////////////////////////////////////// // Command @@ -45,9 +45,8 @@ pub struct Command { // other keys. program: CString, args: Vec<CString>, - env: Option<HashMap<OsString, (usize, CString)>>, argv: Vec<*const c_char>, - envp: Option<Vec<*const c_char>>, + env: CommandEnv<DefaultEnvKey>, cwd: Option<CString>, uid: Option<uid_t>, @@ -96,8 +95,7 @@ impl Command { argv: vec![program.as_ptr(), ptr::null()], program, args: Vec::new(), - env: None, - envp: None, + env: Default::default(), cwd: None, uid: None, gid: None, @@ -121,68 +119,6 @@ impl Command { self.args.push(arg); } - fn init_env_map(&mut self) -> (&mut HashMap<OsString, (usize, CString)>, - &mut Vec<*const c_char>) { - if self.env.is_none() { - let mut map = HashMap::new(); - let mut envp = Vec::new(); - for (k, v) in env::vars_os() { - let s = pair_to_key(&k, &v, &mut self.saw_nul); - envp.push(s.as_ptr()); - map.insert(k, (envp.len() - 1, s)); - } - envp.push(ptr::null()); - self.env = Some(map); - self.envp = Some(envp); - } - (self.env.as_mut().unwrap(), self.envp.as_mut().unwrap()) - } - - pub fn env(&mut self, key: &OsStr, val: &OsStr) { - let new_key = pair_to_key(key, val, &mut self.saw_nul); - let (map, envp) = self.init_env_map(); - - // If `key` is already present then we just update `envp` in place - // (and store the owned value), but if it's not there we override the - // trailing NULL pointer, add a new NULL pointer, and store where we - // were located. - match map.entry(key.to_owned()) { - Entry::Occupied(mut e) => { - let (i, ref mut s) = *e.get_mut(); - envp[i] = new_key.as_ptr(); - *s = new_key; - } - Entry::Vacant(e) => { - let len = envp.len(); - envp[len - 1] = new_key.as_ptr(); - envp.push(ptr::null()); - e.insert((len - 1, new_key)); - } - } - } - - pub fn env_remove(&mut self, key: &OsStr) { - let (map, envp) = self.init_env_map(); - - // If we actually ended up removing a key, then we need to update the - // position of all keys that come after us in `envp` because they're all - // one element sooner now. - if let Some((i, _)) = map.remove(key) { - envp.remove(i); - - for (_, &mut (ref mut j, _)) in map.iter_mut() { - if *j >= i { - *j -= 1; - } - } - } - } - - pub fn env_clear(&mut self) { - self.env = Some(HashMap::new()); - self.envp = Some(vec![ptr::null()]); - } - pub fn cwd(&mut self, dir: &OsStr) { self.cwd = Some(os2c(dir, &mut self.saw_nul)); } @@ -196,9 +132,6 @@ impl Command { pub fn saw_nul(&self) -> bool { self.saw_nul } - pub fn get_envp(&self) -> &Option<Vec<*const c_char>> { - &self.envp - } pub fn get_argv(&self) -> &Vec<*const c_char> { &self.argv } @@ -237,6 +170,15 @@ impl Command { self.stderr = Some(stderr); } + pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> { + &mut self.env + } + + pub fn capture_env(&mut self) -> Option<CStringArray> { + let maybe_env = self.env.capture_if_changed(); + maybe_env.map(|env| construct_envp(env, &mut self.saw_nul)) + } + pub fn setup_io(&self, default: Stdio, needs_stdin: bool) -> io::Result<(StdioPipes, ChildPipes)> { let null = Stdio::Null; @@ -268,6 +210,53 @@ fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { }) } +// Helper type to manage ownership of the strings within a C-style array. +pub struct CStringArray { + items: Vec<CString>, + ptrs: Vec<*const c_char> +} + +impl CStringArray { + pub fn with_capacity(capacity: usize) -> Self { + let mut result = CStringArray { + items: Vec::with_capacity(capacity), + ptrs: Vec::with_capacity(capacity+1) + }; + result.ptrs.push(ptr::null()); + result + } + pub fn push(&mut self, item: CString) { + let l = self.ptrs.len(); + self.ptrs[l-1] = item.as_ptr(); + self.ptrs.push(ptr::null()); + self.items.push(item); + } + pub fn as_ptr(&self) -> *const *const c_char { + self.ptrs.as_ptr() + } +} + +fn construct_envp(env: BTreeMap<DefaultEnvKey, OsString>, saw_nul: &mut bool) -> CStringArray { + let mut result = CStringArray::with_capacity(env.len()); + for (k, v) in env { + let mut k: OsString = k.into(); + + // Reserve additional space for '=' and null terminator + k.reserve_exact(v.len() + 2); + k.push("="); + k.push(&v); + + // Add the new entry into the array + if let Ok(item) = CString::new(k.into_vec()) { + result.push(item); + } else { + *saw_nul = true; + } + } + + result +} + impl Stdio { pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option<AnonPipe>)> { @@ -337,18 +326,6 @@ impl ChildStdio { } } -fn pair_to_key(key: &OsStr, value: &OsStr, saw_nul: &mut bool) -> CString { - let (key, value) = (key.as_bytes(), value.as_bytes()); - let mut v = Vec::with_capacity(key.len() + value.len() + 1); - v.extend(key); - v.push(b'='); - v.extend(value); - CString::new(v).unwrap_or_else(|_e| { - *saw_nul = true; - CString::new("foo=bar").unwrap() - }) -} - impl fmt::Debug for Command { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.program)?; diff --git a/src/libstd/sys/unix/process/process_fuchsia.rs b/src/libstd/sys/unix/process/process_fuchsia.rs index a7a67ed36e8..06c0540fec0 100644 --- a/src/libstd/sys/unix/process/process_fuchsia.rs +++ b/src/libstd/sys/unix/process/process_fuchsia.rs @@ -23,6 +23,8 @@ use sys::process::process_common::*; impl Command { pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) -> io::Result<(Process, StdioPipes)> { + let envp = self.capture_env(); + if self.saw_nul() { return Err(io::Error::new(io::ErrorKind::InvalidInput, "nul byte found in provided data")); @@ -30,7 +32,7 @@ impl Command { let (ours, theirs) = self.setup_io(default, needs_stdin)?; - let process_handle = unsafe { self.do_exec(theirs)? }; + let process_handle = unsafe { self.do_exec(theirs, envp.as_ref())? }; Ok((Process { handle: Handle::new(process_handle) }, ours)) } @@ -50,13 +52,13 @@ impl Command { } } - unsafe fn do_exec(&mut self, stdio: ChildPipes) + unsafe fn do_exec(&mut self, stdio: ChildPipes, maybe_envp: Option<&CStringArray>) -> io::Result<zx_handle_t> { use sys::process::zircon::*; let job_handle = zx_job_default(); - let envp = match *self.get_envp() { - Some(ref envp) => envp.as_ptr(), + let envp = match maybe_envp { + Some(envp) => envp.as_ptr(), None => ptr::null(), }; diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs index 743c458d580..189280a4ba9 100644 --- a/src/libstd/sys/unix/process/process_unix.rs +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -26,6 +26,8 @@ impl Command { const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + let envp = self.capture_env(); + if self.saw_nul() { return Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data")); @@ -38,7 +40,7 @@ impl Command { match cvt(libc::fork())? { 0 => { drop(input); - let err = self.do_exec(theirs); + let err = self.do_exec(theirs, envp.as_ref()); let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; let bytes = [ (errno >> 24) as u8, @@ -99,13 +101,15 @@ impl Command { } pub fn exec(&mut self, default: Stdio) -> io::Error { + let envp = self.capture_env(); + if self.saw_nul() { return io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data") } match self.setup_io(default, true) { - Ok((_, theirs)) => unsafe { self.do_exec(theirs) }, + Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) }, Err(e) => e, } } @@ -140,7 +144,11 @@ impl Command { // allocation). Instead we just close it manually. This will never // have the drop glue anyway because this code never returns (the // child will either exec() or invoke libc::exit) - unsafe fn do_exec(&mut self, stdio: ChildPipes) -> io::Error { + unsafe fn do_exec( + &mut self, + stdio: ChildPipes, + maybe_envp: Option<&CStringArray> + ) -> io::Error { use sys::{self, cvt_r}; macro_rules! t { @@ -180,7 +188,7 @@ impl Command { if let Some(ref cwd) = *self.get_cwd() { t!(cvt(libc::chdir(cwd.as_ptr()))); } - if let Some(ref envp) = *self.get_envp() { + if let Some(envp) = maybe_envp { *sys::os::environ() = envp.as_ptr(); } |
