Skip to content

Commit

Permalink
Introduce unsafe methods for mutating environment
Browse files Browse the repository at this point in the history
  • Loading branch information
jhpratt committed Mar 6, 2022
1 parent 8769f4e commit 4c6e77c
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 49 deletions.
73 changes: 71 additions & 2 deletions library/std/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,10 +338,43 @@ impl Error for VarError {
/// ```
#[stable(feature = "env", since = "1.0.0")]
pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
// Safety: This isn't sound. See #27970 for details.
unsafe { _set_var(key.as_ref(), value.as_ref()) }
}

/// Sets the environment variable `key` to the value `value` for the currently running
/// process.
///
/// # Panics
///
/// This function may panic if `key` is empty, contains an ASCII equals sign `'='`
/// or the NUL character `'\0'`, or when `value` contains the NUL character.
///
/// # Safety
///
/// Some platforms only expose non-threadsafe APIs for altering the environment.
/// On these platforms, you must ensure this method is not called when the
/// program is multithreaded and any other thread could be reading or writing
/// the environment.
///
/// # Examples
///
/// ```no_run
/// #![feature(unsafe_env)]
/// use std::env;
///
/// let key = "KEY";
/// unsafe { env::set(key, "VALUE"); } // Make sure you're single-threaded!
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
/// ```
#[unstable(feature = "unsafe_env", issue = "none")]
pub unsafe fn set<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
_set_var(key.as_ref(), value.as_ref())
}

fn _set_var(key: &OsStr, value: &OsStr) {
// Safety: This can only be called when the program is single-threaded or when
// no other thread touches the environment.
unsafe fn _set_var(key: &OsStr, value: &OsStr) {
os_imp::setenv(key, value).unwrap_or_else(|e| {
panic!("failed to set environment variable `{:?}` to `{:?}`: {}", key, value, e)
})
Expand Down Expand Up @@ -380,10 +413,46 @@ fn _set_var(key: &OsStr, value: &OsStr) {
/// ```
#[stable(feature = "env", since = "1.0.0")]
pub fn remove_var<K: AsRef<OsStr>>(key: K) {
// Safety: This isn't sound. See #27970 for details.
unsafe { _remove_var(key.as_ref()) }
}

/// Removes an environment variable from the environment of the currently running process.
///
/// # Panics
///
/// This function may panic if `key` is empty, contains an ASCII equals sign
/// `'='` or the NUL character `'\0'`, or when the value contains the NUL
/// character.
///
/// # Safety
///
/// Some platforms only expose non-threadsafe APIs for altering the environment.
/// On these platforms, you must ensure this method is not called when the
/// program is multithreaded and any other thread could be reading or writing
/// the environment.
///
/// # Examples
///
/// ```no_run
/// #![feature(unsafe_env)]
/// use std::env;
///
/// let key = "KEY";
/// unsafe { env::set(key, "VALUE"); } // Make sure you're single-threaded!
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
///
/// unsafe { env::unset(key); } // Make sure you're single-threaded!
/// assert!(env::var(key).is_err());
/// ```
#[unstable(feature = "unsafe_env", issue = "none")]
pub unsafe fn unset<K: AsRef<OsStr>>(key: K) {
_remove_var(key.as_ref())
}

fn _remove_var(key: &OsStr) {
// Safety: This can only be called when the program is single-threaded or when
// no other thread touches the environment.
unsafe fn _remove_var(key: &OsStr) {
os_imp::unsetenv(key)
.unwrap_or_else(|e| panic!("failed to remove environment variable `{:?}`: {}", key, e))
}
Expand Down
14 changes: 5 additions & 9 deletions library/std/src/sys/hermit/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,14 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() }
}

pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
unsafe {
let (k, v) = (k.to_owned(), v.to_owned());
ENV.as_ref().unwrap().lock().unwrap().insert(k, v);
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let (k, v) = (k.to_owned(), v.to_owned());
ENV.as_ref().unwrap().lock().unwrap().insert(k, v);
Ok(())
}

pub fn unsetenv(k: &OsStr) -> io::Result<()> {
unsafe {
ENV.as_ref().unwrap().lock().unwrap().remove(k);
}
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
ENV.as_ref().unwrap().lock().unwrap().remove(k);
Ok(())
}

Expand Down
4 changes: 2 additions & 2 deletions library/std/src/sys/sgx/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,13 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned())
}

pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let (k, v) = (k.to_owned(), v.to_owned());
create_env_store().lock().unwrap().insert(k, v);
Ok(())
}

pub fn unsetenv(k: &OsStr) -> io::Result<()> {
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
if let Some(env) = get_env_store() {
env.lock().unwrap().remove(k);
}
Expand Down
16 changes: 6 additions & 10 deletions library/std/src/sys/solid/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,23 +151,19 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
}
}

pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let k = CString::new(k.as_bytes())?;
let v = CString::new(v.as_bytes())?;

unsafe {
let _guard = ENV_LOCK.write();
cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
}
let _guard = ENV_LOCK.write();
cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
}

pub fn unsetenv(n: &OsStr) -> io::Result<()> {
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
let nbuf = CString::new(n.as_bytes())?;

unsafe {
let _guard = ENV_LOCK.write();
cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
}
let _guard = ENV_LOCK.write();
cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
}

/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
Expand Down
18 changes: 8 additions & 10 deletions library/std/src/sys/unix/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,23 +538,21 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
}
}

pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
// Safety: This can only be called when the program is single-threaded.
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let k = CString::new(k.as_bytes())?;
let v = CString::new(v.as_bytes())?;

unsafe {
let _guard = ENV_LOCK.write();
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
}
let _guard = ENV_LOCK.write();
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
}

pub fn unsetenv(n: &OsStr) -> io::Result<()> {
// Safety: This can only be called when the program is single-threaded.
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
let nbuf = CString::new(n.as_bytes())?;

unsafe {
let _guard = ENV_LOCK.write();
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
}
let _guard = ENV_LOCK.write();
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
}

#[cfg(not(target_os = "espidf"))]
Expand Down
4 changes: 2 additions & 2 deletions library/std/src/sys/unsupported/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ pub fn getenv(_: &OsStr) -> Option<OsString> {
None
}

pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
}

pub fn unsetenv(_: &OsStr) -> io::Result<()> {
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
}

Expand Down
16 changes: 6 additions & 10 deletions library/std/src/sys/wasi/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,23 +188,19 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
}
}

pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let k = CString::new(k.as_bytes())?;
let v = CString::new(v.as_bytes())?;

unsafe {
let _guard = env_lock();
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
}
let _guard = env_lock();
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
}

pub fn unsetenv(n: &OsStr) -> io::Result<()> {
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
let nbuf = CString::new(n.as_bytes())?;

unsafe {
let _guard = env_lock();
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
}
let _guard = env_lock();
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
}

pub fn temp_dir() -> PathBuf {
Expand Down
8 changes: 4 additions & 4 deletions library/std/src/sys/windows/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,16 +262,16 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
.ok()
}

pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let k = to_u16s(k)?;
let v = to_u16s(v)?;

cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }).map(drop)
cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop)
}

pub fn unsetenv(n: &OsStr) -> io::Result<()> {
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
let v = to_u16s(n)?;
cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop)
cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop)
}

pub fn temp_dir() -> PathBuf {
Expand Down

0 comments on commit 4c6e77c

Please sign in to comment.