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

Provide clearenv() #1185

Merged
merged 1 commit into from Feb 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion Cargo.toml
Expand Up @@ -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"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you actually verify that 0.1.10 is the minimum version that works?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I mentioned before that the error is still there on 0.1.9. I didn't try every version in between 0.1.2 and 0.1.9; it seems unlikely any of those work work if the same error is in both 0.1.2 and 0.1.9, but I guess it's not impossible. Is there an impact to jumping to 0.1.10?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, given that 0.1.10 comes immediately after 0.1.9, I'd say that confirms it. Too bad that the cfg-if crate doesn't maintain a CHANGELOG.

void = "1.0.2"

[target.'cfg(target_os = "dragonfly")'.build-dependencies]
Expand All @@ -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"
Expand Down
52 changes: 52 additions & 0 deletions 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a matter of style, I suggest:

let ret = cfg_if! {
    if something {
        libc::clearenv()
    } else {
        0
    }
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that first, but it looks like that's not allowed with cfg_if!. It generates a lot of compilation errors; the most relevant looks like:

error: macro expansion ignores token `$crate` and any following
  --> <::cfg_if::cfg_if macros>:23:37
   |
23 |       { @ __identity $ ($ tokens) * } $ crate :: cfg_if !
   |                                       ^^^^^^^
   |
  ::: src/env.rs:28:15
   |
28 |       let ret = cfg_if! {
   |  _______________-
29 | |         if #[cfg(any(target_os = "fuchsia",
30 | |                      target_os = "wasi",
31 | |                      target_env = "wasi",
...  |
43 | |         }
44 | |     };
   | |     -
   | |     |
   | |_____caused by the macro expansion here
   |       help: you might be missing a semicolon here: `;`
   |
   = note: the usage of `cfg_if!` is likely invalid in expression context
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

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)
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Expand Up @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions 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);
}