diff --git a/CHANGELOG.md b/CHANGELOG.md index def99c18fe..8e7a79307b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate ### Added +- Added `env::clearenv()`: calls `libc::clearenv` on platforms + where it's available, and clears the environment of all variables + via `std::env::vars` and `std::env::remove_var` on others. + (#[1185](https://github.com/nix-rust/nix/pull/1185)) ### Changed ### Fixed ### Removed diff --git a/Cargo.toml b/Cargo.toml index f7e0194b60..7327987b56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ exclude = [ [dependencies] libc = { version = "0.2.60", features = [ "extra_traits" ] } bitflags = "1.1" -cfg-if = "0.1.2" +cfg-if = "0.1.10" void = "1.0.2" [target.'cfg(target_os = "dragonfly")'.build-dependencies] @@ -44,6 +44,10 @@ path = "test/test.rs" name = "test-aio-drop" path = "test/sys/test_aio_drop.rs" +[[test]] +name = "test-clearenv" +path = "test/test_clearenv.rs" + [[test]] name = "test-lio-listio-resubmit" path = "test/sys/test_lio_listio_resubmit.rs" diff --git a/src/env.rs b/src/env.rs new file mode 100644 index 0000000000..0d815be91e --- /dev/null +++ b/src/env.rs @@ -0,0 +1,52 @@ +use {Error, Result}; + +/// Clear the environment of all name-value pairs. +/// +/// On platforms where libc provides `clearenv()`, it will be used. libc's +/// `clearenv()` is documented to return an error code but not set errno; if the +/// return value indicates a failure, this function will return +/// `Error::UnsupportedOperation`. +/// +/// On platforms where libc does not provide `clearenv()`, a fallback +/// implementation will be used that iterates over all environment variables and +/// removes them one-by-one. +/// +/// # Safety +/// +/// This function is not threadsafe and can cause undefined behavior in +/// combination with `std::env` or other program components that access the +/// environment. See, for example, the discussion on `std::env::remove_var`; this +/// function is a case of an "inherently unsafe non-threadsafe API" dealing with +/// the environment. +/// +/// The caller must ensure no other threads access the process environment while +/// this function executes and that no raw pointers to an element of libc's +/// `environ` is currently held. The latter is not an issue if the only other +/// environment access in the program is via `std::env`, but the requirement on +/// thread safety must still be upheld. +pub unsafe fn clearenv() -> Result<()> { + let ret; + cfg_if! { + if #[cfg(any(target_os = "fuchsia", + target_os = "wasi", + target_env = "wasi", + target_env = "uclibc", + target_os = "linux", + target_os = "android", + target_os = "emscripten"))] { + ret = libc::clearenv(); + } else { + use std::env; + for (name, _) in env::vars_os() { + env::remove_var(name); + } + ret = 0; + } + } + + if ret == 0 { + Ok(()) + } else { + Err(Error::UnsupportedOperation) + } +} diff --git a/src/lib.rs b/src/lib.rs index 410db18639..0ba7ace88c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,7 @@ pub extern crate libc; // Public crates pub mod dir; +pub mod env; pub mod errno; #[deny(missing_docs)] pub mod features; diff --git a/test/test_clearenv.rs b/test/test_clearenv.rs new file mode 100644 index 0000000000..f122f7c67f --- /dev/null +++ b/test/test_clearenv.rs @@ -0,0 +1,11 @@ +extern crate nix; + +use std::env; + +#[test] +fn clearenv() { + env::set_var("FOO", "BAR"); + unsafe { nix::env::clearenv() }.unwrap(); + assert_eq!(env::var("FOO").unwrap_err(), env::VarError::NotPresent); + assert_eq!(env::vars().count(), 0); +}