Move sys::pal::os::Env into sys::env

Although `Env` (as `Vars`), `Args`, path functions, and OS constants are
publicly exposed via `std::env`, their implementations are each
self-contained. Keep them separate in `std::sys` and make a new module,
`sys::env`, for `Env`.
This commit is contained in:
Thalia Archibald 2025-04-21 20:56:43 -07:00
parent d6c1e454aa
commit 90fe2805df
27 changed files with 1228 additions and 1198 deletions

View file

@ -13,7 +13,7 @@
use crate::error::Error;
use crate::ffi::{OsStr, OsString};
use crate::path::{Path, PathBuf};
use crate::sys::os as os_imp;
use crate::sys::{env as env_imp, os as os_imp};
use crate::{fmt, io, sys};
/// Returns the current working directory as a [`PathBuf`].
@ -96,7 +96,7 @@ pub struct Vars {
/// [`env::vars_os()`]: vars_os
#[stable(feature = "env", since = "1.0.0")]
pub struct VarsOs {
inner: os_imp::Env,
inner: env_imp::Env,
}
/// Returns an iterator of (variable, value) pairs of strings, for all the
@ -150,7 +150,7 @@ pub fn vars() -> Vars {
#[must_use]
#[stable(feature = "env", since = "1.0.0")]
pub fn vars_os() -> VarsOs {
VarsOs { inner: os_imp::env() }
VarsOs { inner: env_imp::env() }
}
#[stable(feature = "env", since = "1.0.0")]
@ -259,7 +259,7 @@ pub fn var_os<K: AsRef<OsStr>>(key: K) -> Option<OsString> {
}
fn _var_os(key: &OsStr) -> Option<OsString> {
os_imp::getenv(key)
env_imp::getenv(key)
}
/// The error type for operations interacting with environment variables.
@ -363,7 +363,7 @@ impl Error for VarError {
#[stable(feature = "env", since = "1.0.0")]
pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
let (key, value) = (key.as_ref(), value.as_ref());
unsafe { os_imp::setenv(key, value) }.unwrap_or_else(|e| {
unsafe { env_imp::setenv(key, value) }.unwrap_or_else(|e| {
panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}")
})
}
@ -434,7 +434,7 @@ pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
#[stable(feature = "env", since = "1.0.0")]
pub unsafe fn remove_var<K: AsRef<OsStr>>(key: K) {
let key = key.as_ref();
unsafe { os_imp::unsetenv(key) }
unsafe { env_imp::unsetenv(key) }
.unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}"))
}

116
library/std/src/sys/env/hermit.rs vendored Normal file
View file

@ -0,0 +1,116 @@
use core::slice::memchr;
use crate::collections::HashMap;
use crate::ffi::{CStr, OsStr, OsString, c_char};
use crate::os::hermit::ffi::OsStringExt;
use crate::sync::Mutex;
use crate::{fmt, io, vec};
static ENV: Mutex<Option<HashMap<OsString, OsString>>> = Mutex::new(None);
pub fn init(env: *const *const c_char) {
let mut guard = ENV.lock().unwrap();
let map = guard.insert(HashMap::new());
if env.is_null() {
return;
}
unsafe {
let mut environ = env;
while !(*environ).is_null() {
if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) {
map.insert(key, value);
}
environ = environ.add(1);
}
}
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
// Strategy (copied from glibc): Variable name and value are separated
// by an ASCII equals sign '='. Since a variable name must not be
// empty, allow variable names starting with an equals sign. Skip all
// malformed lines.
if input.is_empty() {
return None;
}
let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
pos.map(|p| {
(
OsStringExt::from_vec(input[..p].to_vec()),
OsStringExt::from_vec(input[p + 1..].to_vec()),
)
})
}
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
/// Returns a vector of (variable, value) byte-vector pairs for all the
/// environment variables of the current process.
pub fn env() -> Env {
let guard = ENV.lock().unwrap();
let env = guard.as_ref().unwrap();
let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect::<Vec<_>>();
Env { iter: result.into_iter() }
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
ENV.lock().unwrap().as_ref().unwrap().get(k).cloned()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let (k, v) = (k.to_owned(), v.to_owned());
ENV.lock().unwrap().as_mut().unwrap().insert(k, v);
Ok(())
}
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
ENV.lock().unwrap().as_mut().unwrap().remove(k);
Ok(())
}

37
library/std/src/sys/env/mod.rs vendored Normal file
View file

@ -0,0 +1,37 @@
//! Platform-dependent environment variables abstraction.
#![deny(unsafe_op_in_unsafe_fn)]
cfg_if::cfg_if! {
if #[cfg(target_family = "unix")] {
mod unix;
pub use unix::*;
} else if #[cfg(target_family = "windows")] {
mod windows;
pub use windows::*;
} else if #[cfg(target_os = "hermit")] {
mod hermit;
pub use hermit::*;
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
mod sgx;
pub use sgx::*;
} else if #[cfg(target_os = "solid_asp3")] {
mod solid;
pub use solid::*;
} else if #[cfg(target_os = "uefi")] {
mod uefi;
pub use uefi::*;
} else if #[cfg(target_os = "wasi")] {
mod wasi;
pub use wasi::*;
} else if #[cfg(target_os = "xous")] {
mod xous;
pub use xous::*;
} else if #[cfg(target_os = "zkvm")] {
mod zkvm;
pub use zkvm::*;
} else {
mod unsupported;
pub use unsupported::*;
}
}

102
library/std/src/sys/env/sgx.rs vendored Normal file
View file

@ -0,0 +1,102 @@
#![allow(fuzzy_provenance_casts)] // FIXME: this module systematically confuses pointers and integers
use crate::collections::HashMap;
use crate::ffi::{OsStr, OsString};
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sync::{Mutex, Once};
use crate::{fmt, io, vec};
// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests
#[cfg_attr(test, linkage = "available_externally")]
#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os3ENVE")]
static ENV: AtomicUsize = AtomicUsize::new(0);
// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests
#[cfg_attr(test, linkage = "available_externally")]
#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os8ENV_INITE")]
static ENV_INIT: Once = Once::new();
type EnvStore = Mutex<HashMap<OsString, OsString>>;
fn get_env_store() -> Option<&'static EnvStore> {
unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() }
}
fn create_env_store() -> &'static EnvStore {
ENV_INIT.call_once(|| {
ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed)
});
unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) }
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub fn env() -> Env {
let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> {
map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
};
let iter = get_env_store()
.map(|env| clone_to_vec(&env.lock().unwrap()))
.unwrap_or_default()
.into_iter();
Env { iter }
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned())
}
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 unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
if let Some(env) = get_env_store() {
env.lock().unwrap().remove(k);
}
Ok(())
}

140
library/std/src/sys/env/solid.rs vendored Normal file
View file

@ -0,0 +1,140 @@
use core::slice::memchr;
use crate::ffi::{CStr, OsStr, OsString};
use crate::os::raw::{c_char, c_int};
use crate::os::solid::ffi::{OsStrExt, OsStringExt};
use crate::sync::{PoisonError, RwLock};
use crate::sys::common::small_c_string::run_with_cstr;
use crate::{fmt, io, vec};
static ENV_LOCK: RwLock<()> = RwLock::new(());
pub fn env_read_lock() -> impl Drop {
ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
/// Returns a vector of (variable, value) byte-vector pairs for all the
/// environment variables of the current process.
pub fn env() -> Env {
unsafe extern "C" {
static mut environ: *const *const c_char;
}
unsafe {
let _guard = env_read_lock();
let mut result = Vec::new();
if !environ.is_null() {
while !(*environ).is_null() {
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
result.push(key_value);
}
environ = environ.add(1);
}
}
return Env { iter: result.into_iter() };
}
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
// Strategy (copied from glibc): Variable name and value are separated
// by an ASCII equals sign '='. Since a variable name must not be
// empty, allow variable names starting with an equals sign. Skip all
// malformed lines.
if input.is_empty() {
return None;
}
let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
pos.map(|p| {
(
OsStringExt::from_vec(input[..p].to_vec()),
OsStringExt::from_vec(input[p + 1..].to_vec()),
)
})
}
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
// environment variables with a nul byte can't be set, so their value is
// always None as well
run_with_cstr(k.as_bytes(), &|k| {
let _guard = env_read_lock();
let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
if v.is_null() {
Ok(None)
} else {
// SAFETY: `v` cannot be mutated while executing this line since we've a read lock
let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
Ok(Some(OsStringExt::from_vec(bytes)))
}
})
.ok()
.flatten()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
run_with_cstr(k.as_bytes(), &|k| {
run_with_cstr(v.as_bytes(), &|v| {
let _guard = ENV_LOCK.write();
cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
})
})
}
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
run_with_cstr(n.as_bytes(), &|nbuf| {
let _guard = ENV_LOCK.write();
cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
})
}
/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
/// function just returns a generic error.
fn cvt_env(t: c_int) -> io::Result<c_int> {
if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) }
}

138
library/std/src/sys/env/uefi.rs vendored Normal file
View file

@ -0,0 +1,138 @@
use crate::ffi::{OsStr, OsString};
use crate::{fmt, io};
pub struct EnvStrDebug<'a> {
iter: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut list = f.debug_list();
for (a, b) in self.iter {
list.entry(&(a.to_str().unwrap(), b.to_str().unwrap()));
}
list.finish()
}
}
pub struct Env(crate::vec::IntoIter<(OsString, OsString)>);
impl Env {
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
EnvStrDebug { iter: self.0.as_slice() }
}
}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.0.next()
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
pub fn env() -> Env {
let env = uefi_env::get_all().expect("not supported on this platform");
Env(env.into_iter())
}
pub fn getenv(key: &OsStr) -> Option<OsString> {
uefi_env::get(key)
}
pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> {
uefi_env::set(key, val)
}
pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> {
uefi_env::unset(key)
}
mod uefi_env {
use crate::ffi::{OsStr, OsString};
use crate::io;
use crate::os::uefi::ffi::OsStringExt;
use crate::ptr::NonNull;
use crate::sys::{helpers, unsupported_err};
pub(crate) fn get(key: &OsStr) -> Option<OsString> {
let shell = helpers::open_shell()?;
let mut key_ptr = helpers::os_string_to_raw(key)?;
unsafe { get_raw(shell, key_ptr.as_mut_ptr()) }
}
pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> {
let mut key_ptr = helpers::os_string_to_raw(key)
.ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?;
let mut val_ptr = helpers::os_string_to_raw(val)
.ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?;
unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) }
}
pub(crate) fn unset(key: &OsStr) -> io::Result<()> {
let mut key_ptr = helpers::os_string_to_raw(key)
.ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?;
unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) }
}
pub(crate) fn get_all() -> io::Result<Vec<(OsString, OsString)>> {
let shell = helpers::open_shell().ok_or(unsupported_err())?;
let mut vars = Vec::new();
let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) };
if val.is_null() {
return Ok(vars);
}
let mut start = 0;
// UEFI Shell returns all keys separated by NULL.
// End of string is denoted by two NULLs
for i in 0.. {
if unsafe { *val.add(i) } == 0 {
// Two NULL signal end of string
if i == start {
break;
}
let key = OsString::from_wide(unsafe {
crate::slice::from_raw_parts(val.add(start), i - start)
});
// SAFETY: val.add(start) is always NULL terminated
let val = unsafe { get_raw(shell, val.add(start)) }
.ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?;
vars.push((key, val));
start = i + 1;
}
}
Ok(vars)
}
unsafe fn get_raw(
shell: NonNull<r_efi::efi::protocols::shell::Protocol>,
key_ptr: *mut r_efi::efi::Char16,
) -> Option<OsString> {
let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) };
helpers::os_string_from_raw(val)
}
unsafe fn set_raw(
key_ptr: *mut r_efi::efi::Char16,
val_ptr: *mut r_efi::efi::Char16,
) -> io::Result<()> {
let shell = helpers::open_shell().ok_or(unsupported_err())?;
let r =
unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) };
if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
}
}

172
library/std/src/sys/env/unix.rs vendored Normal file
View file

@ -0,0 +1,172 @@
#![allow(unsafe_op_in_unsafe_fn)]
use core::slice::memchr;
use libc::c_char;
use crate::ffi::{CStr, OsStr, OsString};
use crate::os::unix::prelude::*;
use crate::sync::{PoisonError, RwLock};
use crate::sys::common::small_c_string::run_with_cstr;
use crate::sys::cvt;
use crate::{fmt, io, vec};
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
// Use `_NSGetEnviron` on Apple platforms.
//
// `_NSGetEnviron` is the documented alternative (see `man environ`), and has
// been available since the first versions of both macOS and iOS.
//
// Nowadays, specifically since macOS 10.8, `environ` has been exposed through
// `libdyld.dylib`, which is linked via. `libSystem.dylib`:
// <https://github.com/apple-oss-distributions/dyld/blob/dyld-1160.6/libdyld/libdyldGlue.cpp#L913>
//
// So in the end, it likely doesn't really matter which option we use, but the
// performance cost of using `_NSGetEnviron` is extremely miniscule, and it
// might be ever so slightly more supported, so let's just use that.
//
// NOTE: The header where this is defined (`crt_externs.h`) was added to the
// iOS 13.0 SDK, which has been the source of a great deal of confusion in the
// past about the availability of this API.
//
// NOTE(madsmtm): Neither this nor using `environ` has been verified to not
// cause App Store rejections; if this is found to be the case, an alternative
// implementation of this is possible using `[NSProcessInfo environment]`
// - which internally uses `_NSGetEnviron` and a system-wide lock on the
// environment variables to protect against `setenv`, so using that might be
// desirable anyhow? Though it also means that we have to link to Foundation.
#[cfg(target_vendor = "apple")]
pub unsafe fn environ() -> *mut *const *const c_char {
libc::_NSGetEnviron() as *mut *const *const c_char
}
// Use the `environ` static which is part of POSIX.
#[cfg(not(target_vendor = "apple"))]
pub unsafe fn environ() -> *mut *const *const c_char {
unsafe extern "C" {
static mut environ: *const *const c_char;
}
&raw mut environ
}
static ENV_LOCK: RwLock<()> = RwLock::new(());
pub fn env_read_lock() -> impl Drop {
ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
}
/// Returns a vector of (variable, value) byte-vector pairs for all the
/// environment variables of the current process.
pub fn env() -> Env {
unsafe {
let _guard = env_read_lock();
let mut environ = *environ();
let mut result = Vec::new();
if !environ.is_null() {
while !(*environ).is_null() {
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
result.push(key_value);
}
environ = environ.add(1);
}
}
return Env { iter: result.into_iter() };
}
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
// Strategy (copied from glibc): Variable name and value are separated
// by an ASCII equals sign '='. Since a variable name must not be
// empty, allow variable names starting with an equals sign. Skip all
// malformed lines.
if input.is_empty() {
return None;
}
let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
pos.map(|p| {
(
OsStringExt::from_vec(input[..p].to_vec()),
OsStringExt::from_vec(input[p + 1..].to_vec()),
)
})
}
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
// environment variables with a nul byte can't be set, so their value is
// always None as well
run_with_cstr(k.as_bytes(), &|k| {
let _guard = env_read_lock();
let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
if v.is_null() {
Ok(None)
} else {
// SAFETY: `v` cannot be mutated while executing this line since we've a read lock
let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
Ok(Some(OsStringExt::from_vec(bytes)))
}
})
.ok()
.flatten()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
run_with_cstr(k.as_bytes(), &|k| {
run_with_cstr(v.as_bytes(), &|v| {
let _guard = ENV_LOCK.write();
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
})
})
}
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
run_with_cstr(n.as_bytes(), &|nbuf| {
let _guard = ENV_LOCK.write();
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
})
}

43
library/std/src/sys/env/unsupported.rs vendored Normal file
View file

@ -0,0 +1,43 @@
use crate::ffi::{OsStr, OsString};
use crate::{fmt, io};
pub struct Env(!);
impl Env {
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self(inner) = self;
match *inner {}
}
}
impl fmt::Debug for Env {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(inner) = self;
match *inner {}
}
}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
let Self(inner) = self;
match *inner {}
}
}
pub fn env() -> Env {
panic!("not supported on this platform")
}
pub fn getenv(_: &OsStr) -> Option<OsString> {
None
}
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
}
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
}

146
library/std/src/sys/env/wasi.rs vendored Normal file
View file

@ -0,0 +1,146 @@
use core::slice::memchr;
use crate::ffi::{CStr, OsStr, OsString};
use crate::os::wasi::prelude::*;
use crate::sys::common::small_c_string::run_with_cstr;
use crate::sys::pal::os::{cvt, libc};
use crate::{fmt, io, vec};
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
cfg_if::cfg_if! {
if #[cfg(target_feature = "atomics")] {
// Access to the environment must be protected by a lock in multi-threaded scenarios.
use crate::sync::{PoisonError, RwLock};
static ENV_LOCK: RwLock<()> = RwLock::new(());
pub fn env_read_lock() -> impl Drop {
ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
}
pub fn env_write_lock() -> impl Drop {
ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner)
}
} else {
// No need for a lock if we are single-threaded.
pub fn env_read_lock() -> impl Drop {
Box::new(())
}
pub fn env_write_lock() -> impl Drop {
Box::new(())
}
}
}
pub fn env() -> Env {
unsafe {
let _guard = env_read_lock();
// Use `__wasilibc_get_environ` instead of `environ` here so that we
// don't require wasi-libc to eagerly initialize the environment
// variables.
let mut environ = libc::__wasilibc_get_environ();
let mut result = Vec::new();
if !environ.is_null() {
while !(*environ).is_null() {
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
result.push(key_value);
}
environ = environ.add(1);
}
}
return Env { iter: result.into_iter() };
}
// See src/libstd/sys/pal/unix/os.rs, same as that
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
if input.is_empty() {
return None;
}
let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
pos.map(|p| {
(
OsStringExt::from_vec(input[..p].to_vec()),
OsStringExt::from_vec(input[p + 1..].to_vec()),
)
})
}
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
// environment variables with a nul byte can't be set, so their value is
// always None as well
run_with_cstr(k.as_bytes(), &|k| {
let _guard = env_read_lock();
let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
if v.is_null() {
Ok(None)
} else {
// SAFETY: `v` cannot be mutated while executing this line since we've a read lock
let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
Ok(Some(OsStringExt::from_vec(bytes)))
}
})
.ok()
.flatten()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
run_with_cstr(k.as_bytes(), &|k| {
run_with_cstr(v.as_bytes(), &|v| unsafe {
let _guard = env_write_lock();
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
})
})
}
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
run_with_cstr(n.as_bytes(), &|nbuf| unsafe {
let _guard = env_write_lock();
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
})
}

133
library/std/src/sys/env/windows.rs vendored Normal file
View file

@ -0,0 +1,133 @@
use crate::ffi::{OsStr, OsString};
use crate::os::windows::prelude::*;
use crate::sys::pal::{c, cvt, fill_utf16_buf, to_u16s};
use crate::{fmt, io, ptr, slice};
pub struct Env {
base: *mut c::WCHAR,
iter: EnvIterator,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
iter: &'a EnvIterator,
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
let iter: EnvIterator = (*iter).clone();
let mut list = f.debug_list();
for (a, b) in iter {
list.entry(&(a.to_str().unwrap(), b.to_str().unwrap()));
}
list.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { base: _, iter } = self;
EnvStrDebug { iter }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { base: _, iter } = self;
f.debug_list().entries(iter.clone()).finish()
}
}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
let Self { base: _, iter } = self;
iter.next()
}
}
#[derive(Clone)]
struct EnvIterator(*mut c::WCHAR);
impl Iterator for EnvIterator {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
let Self(cur) = self;
loop {
unsafe {
if **cur == 0 {
return None;
}
let p = *cur as *const u16;
let mut len = 0;
while *p.add(len) != 0 {
len += 1;
}
let s = slice::from_raw_parts(p, len);
*cur = cur.add(len + 1);
// Windows allows environment variables to start with an equals
// symbol (in any other position, this is the separator between
// variable name and value). Since`s` has at least length 1 at
// this point (because the empty string terminates the array of
// environment variables), we can safely slice.
let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) {
Some(p) => p,
None => continue,
};
return Some((
OsStringExt::from_wide(&s[..pos]),
OsStringExt::from_wide(&s[pos + 1..]),
));
}
}
}
}
impl Drop for Env {
fn drop(&mut self) {
unsafe {
c::FreeEnvironmentStringsW(self.base);
}
}
}
pub fn env() -> Env {
unsafe {
let ch = c::GetEnvironmentStringsW();
if ch.is_null() {
panic!("failure getting env string from OS: {}", io::Error::last_os_error());
}
Env { base: ch, iter: EnvIterator(ch) }
}
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
let k = to_u16s(k).ok()?;
fill_utf16_buf(
|buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
OsStringExt::from_wide,
)
.ok()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
// SAFETY: We ensure that k and v are null-terminated wide strings.
unsafe {
let k = to_u16s(k)?;
let v = to_u16s(v)?;
cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop)
}
}
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
// SAFETY: We ensure that v is a null-terminated wide strings.
unsafe {
let v = to_u16s(n)?;
cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop)
}
}

99
library/std/src/sys/env/xous.rs vendored Normal file
View file

@ -0,0 +1,99 @@
use crate::collections::HashMap;
use crate::ffi::{OsStr, OsString};
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sync::{Mutex, Once};
use crate::sys::pal::os::{get_application_parameters, params};
use crate::{fmt, io, vec};
static ENV: AtomicUsize = AtomicUsize::new(0);
static ENV_INIT: Once = Once::new();
type EnvStore = Mutex<HashMap<OsString, OsString>>;
fn get_env_store() -> &'static EnvStore {
ENV_INIT.call_once(|| {
let env_store = EnvStore::default();
if let Some(params) = get_application_parameters() {
for param in params {
if let Ok(envs) = params::EnvironmentBlock::try_from(&param) {
let mut env_store = env_store.lock().unwrap();
for env in envs {
env_store.insert(env.key.into(), env.value.into());
}
break;
}
}
}
ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed)
});
unsafe { &*core::ptr::with_exposed_provenance::<EnvStore>(ENV.load(Ordering::Relaxed)) }
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub fn env() -> Env {
let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> {
map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
};
let iter = clone_to_vec(&*get_env_store().lock().unwrap()).into_iter();
Env { iter }
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
get_env_store().lock().unwrap().get(k).cloned()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let (k, v) = (k.to_owned(), v.to_owned());
get_env_store().lock().unwrap().insert(k, v);
Ok(())
}
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
get_env_store().lock().unwrap().remove(k);
Ok(())
}

63
library/std/src/sys/env/zkvm.rs vendored Normal file
View file

@ -0,0 +1,63 @@
use crate::ffi::{OsStr, OsString};
use crate::sys::os_str;
use crate::sys::pal::{WORD_SIZE, abi};
use crate::sys_common::FromInner;
use crate::{fmt, io};
pub struct Env(!);
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.0
}
}
pub fn env() -> Env {
panic!("not supported on this platform")
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self(inner) = self;
match *inner {}
}
}
impl fmt::Debug for Env {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(inner) = self;
match *inner {}
}
}
pub fn getenv(varname: &OsStr) -> Option<OsString> {
let varname = varname.as_encoded_bytes();
let nbytes =
unsafe { abi::sys_getenv(crate::ptr::null_mut(), 0, varname.as_ptr(), varname.len()) };
if nbytes == usize::MAX {
return None;
}
let nwords = (nbytes + WORD_SIZE - 1) / WORD_SIZE;
let words = unsafe { abi::sys_alloc_words(nwords) };
let nbytes2 = unsafe { abi::sys_getenv(words, nwords, varname.as_ptr(), varname.len()) };
debug_assert_eq!(nbytes, nbytes2);
// Convert to OsString.
//
// FIXME: We can probably get rid of the extra copy here if we
// reimplement "os_str" instead of just using the generic unix
// "os_str".
let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) };
Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() }))
}
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
}
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
}

View file

@ -12,6 +12,7 @@ pub mod anonymous_pipe;
pub mod args;
pub mod backtrace;
pub mod cmath;
pub mod env;
pub mod env_consts;
pub mod exit_guard;
pub mod fd;

View file

@ -16,7 +16,10 @@
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(missing_docs, nonstandard_style)]
use crate::io::ErrorKind;
use crate::os::hermit::hermit_abi;
use crate::os::raw::c_char;
use crate::sys::env;
pub mod futex;
pub mod os;
@ -25,9 +28,6 @@ pub mod pipe;
pub mod thread;
pub mod time;
use crate::io::ErrorKind;
use crate::os::hermit::hermit_abi;
pub fn unsupported<T>() -> crate::io::Result<T> {
Err(unsupported_err())
}
@ -76,7 +76,7 @@ pub unsafe extern "C" fn runtime_entry(
}
// initialize environment
os::init_environment(env);
env::init(env);
let result = unsafe { main(argc as isize, argv) };

View file

@ -1,15 +1,10 @@
use core::slice::memchr;
use super::hermit_abi;
use crate::collections::HashMap;
use crate::error::Error as StdError;
use crate::ffi::{CStr, OsStr, OsString, c_char};
use crate::ffi::{OsStr, OsString};
use crate::marker::PhantomData;
use crate::os::hermit::ffi::OsStringExt;
use crate::path::{self, PathBuf};
use crate::sync::Mutex;
use crate::sys::unsupported;
use crate::{fmt, io, str, vec};
use crate::{fmt, io, str};
pub fn errno() -> i32 {
unsafe { hermit_abi::get_errno() }
@ -68,115 +63,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
unsupported()
}
static ENV: Mutex<Option<HashMap<OsString, OsString>>> = Mutex::new(None);
pub fn init_environment(env: *const *const c_char) {
let mut guard = ENV.lock().unwrap();
let map = guard.insert(HashMap::new());
if env.is_null() {
return;
}
unsafe {
let mut environ = env;
while !(*environ).is_null() {
if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) {
map.insert(key, value);
}
environ = environ.add(1);
}
}
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
// Strategy (copied from glibc): Variable name and value are separated
// by an ASCII equals sign '='. Since a variable name must not be
// empty, allow variable names starting with an equals sign. Skip all
// malformed lines.
if input.is_empty() {
return None;
}
let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
pos.map(|p| {
(
OsStringExt::from_vec(input[..p].to_vec()),
OsStringExt::from_vec(input[p + 1..].to_vec()),
)
})
}
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
/// Returns a vector of (variable, value) byte-vector pairs for all the
/// environment variables of the current process.
pub fn env() -> Env {
let guard = ENV.lock().unwrap();
let env = guard.as_ref().unwrap();
let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect::<Vec<_>>();
Env { iter: result.into_iter() }
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
ENV.lock().unwrap().as_ref().unwrap().get(k).cloned()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let (k, v) = (k.to_owned(), v.to_owned());
ENV.lock().unwrap().as_mut().unwrap().insert(k, v);
Ok(())
}
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
ENV.lock().unwrap().as_mut().unwrap().remove(k);
Ok(())
}
pub fn temp_dir() -> PathBuf {
PathBuf::from("/tmp")
}

View file

@ -1,14 +1,11 @@
use fortanix_sgx_abi::{Error, RESULT_SUCCESS};
use crate::collections::HashMap;
use crate::error::Error as StdError;
use crate::ffi::{OsStr, OsString};
use crate::marker::PhantomData;
use crate::path::{self, PathBuf};
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sync::{Mutex, Once};
use crate::sys::{decode_error_kind, sgx_ineffective, unsupported};
use crate::{fmt, io, str, vec};
use crate::{fmt, io, str};
pub fn errno() -> i32 {
RESULT_SUCCESS
@ -73,101 +70,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
unsupported()
}
// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests
#[cfg_attr(test, linkage = "available_externally")]
#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os3ENVE")]
static ENV: AtomicUsize = AtomicUsize::new(0);
// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests
#[cfg_attr(test, linkage = "available_externally")]
#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os8ENV_INITE")]
static ENV_INIT: Once = Once::new();
type EnvStore = Mutex<HashMap<OsString, OsString>>;
fn get_env_store() -> Option<&'static EnvStore> {
unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() }
}
fn create_env_store() -> &'static EnvStore {
ENV_INIT.call_once(|| {
ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed)
});
unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) }
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub fn env() -> Env {
let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> {
map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
};
let iter = get_env_store()
.map(|env| clone_to_vec(&env.lock().unwrap()))
.unwrap_or_default()
.into_iter();
Env { iter }
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned())
}
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 unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
if let Some(env) = get_env_store() {
env.lock().unwrap().remove(k);
}
Ok(())
}
pub fn temp_dir() -> PathBuf {
panic!("no filesystem in SGX")
}

View file

@ -1,14 +1,8 @@
use core::slice::memchr;
use super::{error, itron, unsupported};
use crate::error::Error as StdError;
use crate::ffi::{CStr, OsStr, OsString};
use crate::os::raw::{c_char, c_int};
use crate::os::solid::ffi::{OsStrExt, OsStringExt};
use crate::ffi::{OsStr, OsString};
use crate::path::{self, PathBuf};
use crate::sync::{PoisonError, RwLock};
use crate::sys::common::small_c_string::run_with_cstr;
use crate::{fmt, io, vec};
use crate::{fmt, io};
// `solid` directly maps `errno`s to μITRON error codes.
impl itron::error::ItronError {
@ -75,138 +69,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
unsupported()
}
static ENV_LOCK: RwLock<()> = RwLock::new(());
pub fn env_read_lock() -> impl Drop {
ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
/// Returns a vector of (variable, value) byte-vector pairs for all the
/// environment variables of the current process.
pub fn env() -> Env {
unsafe extern "C" {
static mut environ: *const *const c_char;
}
unsafe {
let _guard = env_read_lock();
let mut result = Vec::new();
if !environ.is_null() {
while !(*environ).is_null() {
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
result.push(key_value);
}
environ = environ.add(1);
}
}
return Env { iter: result.into_iter() };
}
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
// Strategy (copied from glibc): Variable name and value are separated
// by an ASCII equals sign '='. Since a variable name must not be
// empty, allow variable names starting with an equals sign. Skip all
// malformed lines.
if input.is_empty() {
return None;
}
let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
pos.map(|p| {
(
OsStringExt::from_vec(input[..p].to_vec()),
OsStringExt::from_vec(input[p + 1..].to_vec()),
)
})
}
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
// environment variables with a nul byte can't be set, so their value is
// always None as well
run_with_cstr(k.as_bytes(), &|k| {
let _guard = env_read_lock();
let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
if v.is_null() {
Ok(None)
} else {
// SAFETY: `v` cannot be mutated while executing this line since we've a read lock
let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
Ok(Some(OsStringExt::from_vec(bytes)))
}
})
.ok()
.flatten()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
run_with_cstr(k.as_bytes(), &|k| {
run_with_cstr(v.as_bytes(), &|v| {
let _guard = ENV_LOCK.write();
cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
})
})
}
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
run_with_cstr(n.as_bytes(), &|nbuf| {
let _guard = ENV_LOCK.write();
cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
})
}
/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
/// function just returns a generic error.
fn cvt_env(t: c_int) -> io::Result<c_int> {
if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) }
}
pub fn temp_dir() -> PathBuf {
panic!("no standard temporary directory on this platform")
}

View file

@ -73,47 +73,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
unsupported()
}
pub struct Env(!);
impl Env {
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self(inner) = self;
match *inner {}
}
}
impl fmt::Debug for Env {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(inner) = self;
match *inner {}
}
}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
let Self(inner) = self;
match *inner {}
}
}
pub fn env() -> Env {
panic!("not supported on this platform")
}
pub fn getenv(_: &OsStr) -> Option<OsString> {
None
}
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
}
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
}
pub fn temp_dir() -> PathBuf {
panic!("no filesystem on this platform")
}

View file

@ -131,60 +131,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
helpers::device_path_to_text(protocol).map(PathBuf::from)
}
pub struct EnvStrDebug<'a> {
iter: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut list = f.debug_list();
for (a, b) in self.iter {
list.entry(&(a.to_str().unwrap(), b.to_str().unwrap()));
}
list.finish()
}
}
pub struct Env(crate::vec::IntoIter<(OsString, OsString)>);
impl Env {
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
EnvStrDebug { iter: self.0.as_slice() }
}
}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.0.next()
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
pub fn env() -> Env {
let env = uefi_env::get_all().expect("not supported on this platform");
Env(env.into_iter())
}
pub fn getenv(key: &OsStr) -> Option<OsString> {
uefi_env::get(key)
}
pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> {
uefi_env::set(key, val)
}
pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> {
uefi_env::unset(key)
}
pub fn temp_dir() -> PathBuf {
panic!("no filesystem on this platform")
}
@ -213,85 +159,3 @@ pub fn exit(code: i32) -> ! {
pub fn getpid() -> u32 {
panic!("no pids on this platform")
}
mod uefi_env {
use crate::ffi::{OsStr, OsString};
use crate::io;
use crate::os::uefi::ffi::OsStringExt;
use crate::ptr::NonNull;
use crate::sys::{helpers, unsupported_err};
pub(crate) fn get(key: &OsStr) -> Option<OsString> {
let shell = helpers::open_shell()?;
let mut key_ptr = helpers::os_string_to_raw(key)?;
unsafe { get_raw(shell, key_ptr.as_mut_ptr()) }
}
pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> {
let mut key_ptr = helpers::os_string_to_raw(key)
.ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?;
let mut val_ptr = helpers::os_string_to_raw(val)
.ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?;
unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) }
}
pub(crate) fn unset(key: &OsStr) -> io::Result<()> {
let mut key_ptr = helpers::os_string_to_raw(key)
.ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?;
unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) }
}
pub(crate) fn get_all() -> io::Result<Vec<(OsString, OsString)>> {
let shell = helpers::open_shell().ok_or(unsupported_err())?;
let mut vars = Vec::new();
let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) };
if val.is_null() {
return Ok(vars);
}
let mut start = 0;
// UEFI Shell returns all keys separated by NULL.
// End of string is denoted by two NULLs
for i in 0.. {
if unsafe { *val.add(i) } == 0 {
// Two NULL signal end of string
if i == start {
break;
}
let key = OsString::from_wide(unsafe {
crate::slice::from_raw_parts(val.add(start), i - start)
});
// SAFETY: val.add(start) is always NULL terminated
let val = unsafe { get_raw(shell, val.add(start)) }
.ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?;
vars.push((key, val));
start = i + 1;
}
}
Ok(vars)
}
unsafe fn get_raw(
shell: NonNull<r_efi::efi::protocols::shell::Protocol>,
key_ptr: *mut r_efi::efi::Char16,
) -> Option<OsString> {
let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) };
helpers::os_string_from_raw(val)
}
unsafe fn set_raw(
key_ptr: *mut r_efi::efi::Char16,
val_ptr: *mut r_efi::efi::Char16,
) -> io::Result<()> {
let shell = helpers::open_shell().ok_or(unsupported_err())?;
let r =
unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) };
if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
}
}

View file

@ -5,20 +5,15 @@
#[cfg(test)]
mod tests;
use core::slice::memchr;
use libc::{c_char, c_int, c_void};
use crate::error::Error as StdError;
use crate::ffi::{CStr, CString, OsStr, OsString};
use crate::ffi::{CStr, OsStr, OsString};
use crate::os::unix::prelude::*;
use crate::path::{self, PathBuf};
use crate::sync::{PoisonError, RwLock};
use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr};
#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
use crate::sys::weak::weak;
use crate::sys::{cvt, fd};
use crate::{fmt, io, iter, mem, ptr, slice, str, vec};
use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::cvt;
use crate::{fmt, io, iter, mem, ptr, slice, str};
const TMPBUF_SZ: usize = 128;
@ -552,166 +547,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) }
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
// Use `_NSGetEnviron` on Apple platforms.
//
// `_NSGetEnviron` is the documented alternative (see `man environ`), and has
// been available since the first versions of both macOS and iOS.
//
// Nowadays, specifically since macOS 10.8, `environ` has been exposed through
// `libdyld.dylib`, which is linked via. `libSystem.dylib`:
// <https://github.com/apple-oss-distributions/dyld/blob/dyld-1160.6/libdyld/libdyldGlue.cpp#L913>
//
// So in the end, it likely doesn't really matter which option we use, but the
// performance cost of using `_NSGetEnviron` is extremely miniscule, and it
// might be ever so slightly more supported, so let's just use that.
//
// NOTE: The header where this is defined (`crt_externs.h`) was added to the
// iOS 13.0 SDK, which has been the source of a great deal of confusion in the
// past about the availability of this API.
//
// NOTE(madsmtm): Neither this nor using `environ` has been verified to not
// cause App Store rejections; if this is found to be the case, an alternative
// implementation of this is possible using `[NSProcessInfo environment]`
// - which internally uses `_NSGetEnviron` and a system-wide lock on the
// environment variables to protect against `setenv`, so using that might be
// desirable anyhow? Though it also means that we have to link to Foundation.
#[cfg(target_vendor = "apple")]
pub unsafe fn environ() -> *mut *const *const c_char {
libc::_NSGetEnviron() as *mut *const *const c_char
}
// Use the `environ` static which is part of POSIX.
#[cfg(not(target_vendor = "apple"))]
pub unsafe fn environ() -> *mut *const *const c_char {
unsafe extern "C" {
static mut environ: *const *const c_char;
}
&raw mut environ
}
static ENV_LOCK: RwLock<()> = RwLock::new(());
pub fn env_read_lock() -> impl Drop {
ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
}
/// Returns a vector of (variable, value) byte-vector pairs for all the
/// environment variables of the current process.
pub fn env() -> Env {
unsafe {
let _guard = env_read_lock();
let mut environ = *environ();
let mut result = Vec::new();
if !environ.is_null() {
while !(*environ).is_null() {
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
result.push(key_value);
}
environ = environ.add(1);
}
}
return Env { iter: result.into_iter() };
}
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
// Strategy (copied from glibc): Variable name and value are separated
// by an ASCII equals sign '='. Since a variable name must not be
// empty, allow variable names starting with an equals sign. Skip all
// malformed lines.
if input.is_empty() {
return None;
}
let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
pos.map(|p| {
(
OsStringExt::from_vec(input[..p].to_vec()),
OsStringExt::from_vec(input[p + 1..].to_vec()),
)
})
}
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
// environment variables with a nul byte can't be set, so their value is
// always None as well
run_with_cstr(k.as_bytes(), &|k| {
let _guard = env_read_lock();
let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
if v.is_null() {
Ok(None)
} else {
// SAFETY: `v` cannot be mutated while executing this line since we've a read lock
let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
Ok(Some(OsStringExt::from_vec(bytes)))
}
})
.ok()
.flatten()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
run_with_cstr(k.as_bytes(), &|k| {
run_with_cstr(v.as_bytes(), &|v| {
let _guard = ENV_LOCK.write();
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
})
})
}
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
run_with_cstr(n.as_bytes(), &|nbuf| {
let _guard = ENV_LOCK.write();
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
})
}
#[cfg(not(target_os = "espidf"))]
pub fn page_size() -> usize {
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }

View file

@ -62,47 +62,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
unsupported()
}
pub struct Env(!);
impl Env {
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self(inner) = self;
match *inner {}
}
}
impl fmt::Debug for Env {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(inner) = self;
match *inner {}
}
}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
let Self(inner) = self;
match *inner {}
}
}
pub fn env() -> Env {
panic!("not supported on this platform")
}
pub fn getenv(_: &OsStr) -> Option<OsString> {
None
}
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
}
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
}
pub fn temp_dir() -> PathBuf {
panic!("no filesystem on this platform")
}

View file

@ -1,19 +1,16 @@
#![forbid(unsafe_op_in_unsafe_fn)]
use core::slice::memchr;
use crate::error::Error as StdError;
use crate::ffi::{CStr, OsStr, OsString};
use crate::marker::PhantomData;
use crate::ops::Drop;
use crate::os::wasi::prelude::*;
use crate::path::{self, PathBuf};
use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr};
use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::unsupported;
use crate::{fmt, io, str, vec};
use crate::{fmt, io, str};
// Add a few symbols not in upstream `libc` just yet.
mod libc {
pub mod libc {
pub use libc::*;
unsafe extern "C" {
@ -23,28 +20,6 @@ mod libc {
}
}
cfg_if::cfg_if! {
if #[cfg(target_feature = "atomics")] {
// Access to the environment must be protected by a lock in multi-threaded scenarios.
use crate::sync::{PoisonError, RwLock};
static ENV_LOCK: RwLock<()> = RwLock::new(());
pub fn env_read_lock() -> impl Drop {
ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
}
pub fn env_write_lock() -> impl Drop {
ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner)
}
} else {
// No need for a lock if we are single-threaded.
pub fn env_read_lock() -> impl Drop {
Box::new(())
}
pub fn env_write_lock() -> impl Drop {
Box::new(())
}
}
}
pub fn errno() -> i32 {
unsafe extern "C" {
#[thread_local]
@ -141,123 +116,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
unsupported()
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub fn env() -> Env {
unsafe {
let _guard = env_read_lock();
// Use `__wasilibc_get_environ` instead of `environ` here so that we
// don't require wasi-libc to eagerly initialize the environment
// variables.
let mut environ = libc::__wasilibc_get_environ();
let mut result = Vec::new();
if !environ.is_null() {
while !(*environ).is_null() {
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
result.push(key_value);
}
environ = environ.add(1);
}
}
return Env { iter: result.into_iter() };
}
// See src/libstd/sys/pal/unix/os.rs, same as that
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
if input.is_empty() {
return None;
}
let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
pos.map(|p| {
(
OsStringExt::from_vec(input[..p].to_vec()),
OsStringExt::from_vec(input[p + 1..].to_vec()),
)
})
}
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
// environment variables with a nul byte can't be set, so their value is
// always None as well
run_with_cstr(k.as_bytes(), &|k| {
let _guard = env_read_lock();
let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
if v.is_null() {
Ok(None)
} else {
// SAFETY: `v` cannot be mutated while executing this line since we've a read lock
let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
Ok(Some(OsStringExt::from_vec(bytes)))
}
})
.ok()
.flatten()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
run_with_cstr(k.as_bytes(), &|k| {
run_with_cstr(v.as_bytes(), &|v| unsafe {
let _guard = env_write_lock();
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
})
})
}
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
run_with_cstr(n.as_bytes(), &|nbuf| unsafe {
let _guard = env_write_lock();
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
})
}
#[allow(dead_code)]
pub fn page_size() -> usize {
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
@ -294,6 +152,6 @@ macro_rules! impl_is_minus_one {
impl_is_minus_one! { i8 i16 i32 i64 isize }
fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) }
}

View file

@ -5,16 +5,16 @@
#[cfg(test)]
mod tests;
use super::api;
#[cfg(not(target_vendor = "uwp"))]
use super::api::WinError;
use super::{api, to_u16s};
use crate::error::Error as StdError;
use crate::ffi::{OsStr, OsString};
use crate::os::windows::ffi::EncodeWide;
use crate::os::windows::prelude::*;
use crate::path::{self, PathBuf};
use crate::sys::{c, cvt};
use crate::{fmt, io, ptr, slice};
use crate::sys::pal::{c, cvt};
use crate::{fmt, io, ptr};
pub fn errno() -> i32 {
api::get_last_error().code as i32
@ -76,108 +76,6 @@ pub fn error_string(mut errnum: i32) -> String {
}
}
pub struct Env {
base: *mut c::WCHAR,
iter: EnvIterator,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
iter: &'a EnvIterator,
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
let iter: EnvIterator = (*iter).clone();
let mut list = f.debug_list();
for (a, b) in iter {
list.entry(&(a.to_str().unwrap(), b.to_str().unwrap()));
}
list.finish()
}
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { base: _, iter } = self;
EnvStrDebug { iter }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { base: _, iter } = self;
f.debug_list().entries(iter.clone()).finish()
}
}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
let Self { base: _, iter } = self;
iter.next()
}
}
#[derive(Clone)]
struct EnvIterator(*mut c::WCHAR);
impl Iterator for EnvIterator {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
let Self(cur) = self;
loop {
unsafe {
if **cur == 0 {
return None;
}
let p = *cur as *const u16;
let mut len = 0;
while *p.add(len) != 0 {
len += 1;
}
let s = slice::from_raw_parts(p, len);
*cur = cur.add(len + 1);
// Windows allows environment variables to start with an equals
// symbol (in any other position, this is the separator between
// variable name and value). Since`s` has at least length 1 at
// this point (because the empty string terminates the array of
// environment variables), we can safely slice.
let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) {
Some(p) => p,
None => continue,
};
return Some((
OsStringExt::from_wide(&s[..pos]),
OsStringExt::from_wide(&s[pos + 1..]),
));
}
}
}
}
impl Drop for Env {
fn drop(&mut self) {
unsafe {
c::FreeEnvironmentStringsW(self.base);
}
}
}
pub fn env() -> Env {
unsafe {
let ch = c::GetEnvironmentStringsW();
if ch.is_null() {
panic!("failure getting env string from OS: {}", io::Error::last_os_error());
}
Env { base: ch, iter: EnvIterator(ch) }
}
}
pub struct SplitPaths<'a> {
data: EncodeWide<'a>,
must_yield: bool,
@ -290,33 +188,6 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop)
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
let k = to_u16s(k).ok()?;
super::fill_utf16_buf(
|buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
OsStringExt::from_wide,
)
.ok()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
// SAFETY: We ensure that k and v are null-terminated wide strings.
unsafe {
let k = to_u16s(k)?;
let v = to_u16s(v)?;
cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop)
}
}
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
// SAFETY: We ensure that v is a null-terminated wide strings.
unsafe {
let v = to_u16s(n)?;
cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop)
}
}
pub fn temp_dir() -> PathBuf {
super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap()
}

View file

@ -1,13 +1,11 @@
use super::unsupported;
use crate::collections::HashMap;
use crate::error::Error as StdError;
use crate::ffi::{OsStr, OsString};
use crate::marker::PhantomData;
use crate::os::xous::ffi::Error as XousError;
use crate::path::{self, PathBuf};
use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use crate::sync::{Mutex, Once};
use crate::{fmt, io, vec};
use crate::sync::atomic::{AtomicPtr, Ordering};
use crate::{fmt, io};
pub(crate) mod params;
@ -136,100 +134,6 @@ pub(crate) fn get_application_parameters() -> Option<params::ApplicationParamete
unsafe { params::ApplicationParameters::new_from_ptr(params_address) }
}
// ---------- Environment handling ---------- //
static ENV: AtomicUsize = AtomicUsize::new(0);
static ENV_INIT: Once = Once::new();
type EnvStore = Mutex<HashMap<OsString, OsString>>;
fn get_env_store() -> &'static EnvStore {
ENV_INIT.call_once(|| {
let env_store = EnvStore::default();
if let Some(params) = get_application_parameters() {
for param in params {
if let Ok(envs) = params::EnvironmentBlock::try_from(&param) {
let mut env_store = env_store.lock().unwrap();
for env in envs {
env_store.insert(env.key.into(), env.value.into());
}
break;
}
}
}
ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed)
});
unsafe { &*core::ptr::with_exposed_provenance::<EnvStore>(ENV.load(Ordering::Relaxed)) }
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub struct EnvStrDebug<'a> {
slice: &'a [(OsString, OsString)],
}
impl fmt::Debug for EnvStrDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { slice } = self;
f.debug_list()
.entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())))
.finish()
}
}
impl Env {
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self { iter } = self;
EnvStrDebug { slice: iter.as_slice() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub fn env() -> Env {
let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> {
map.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
};
let iter = clone_to_vec(&*get_env_store().lock().unwrap()).into_iter();
Env { iter }
}
pub fn getenv(k: &OsStr) -> Option<OsString> {
get_env_store().lock().unwrap().get(k).cloned()
}
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let (k, v) = (k.to_owned(), v.to_owned());
get_env_store().lock().unwrap().insert(k, v);
Ok(())
}
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
get_env_store().lock().unwrap().remove(k);
Ok(())
}
pub fn temp_dir() -> PathBuf {
panic!("no filesystem on this platform")
}

View file

@ -1,10 +1,8 @@
use super::{WORD_SIZE, abi, unsupported};
use super::unsupported;
use crate::error::Error as StdError;
use crate::ffi::{OsStr, OsString};
use crate::marker::PhantomData;
use crate::path::{self, PathBuf};
use crate::sys::os_str;
use crate::sys_common::FromInner;
use crate::{fmt, io};
pub fn errno() -> i32 {
@ -64,64 +62,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
unsupported()
}
pub struct Env(!);
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.0
}
}
pub fn env() -> Env {
panic!("not supported on this platform")
}
impl Env {
pub fn str_debug(&self) -> impl fmt::Debug + '_ {
let Self(inner) = self;
match *inner {}
}
}
impl fmt::Debug for Env {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(inner) = self;
match *inner {}
}
}
pub fn getenv(varname: &OsStr) -> Option<OsString> {
let varname = varname.as_encoded_bytes();
let nbytes =
unsafe { abi::sys_getenv(crate::ptr::null_mut(), 0, varname.as_ptr(), varname.len()) };
if nbytes == usize::MAX {
return None;
}
let nwords = (nbytes + WORD_SIZE - 1) / WORD_SIZE;
let words = unsafe { abi::sys_alloc_words(nwords) };
let nbytes2 = unsafe { abi::sys_getenv(words, nwords, varname.as_ptr(), varname.len()) };
debug_assert_eq!(nbytes, nbytes2);
// Convert to OsString.
//
// FIXME: We can probably get rid of the extra copy here if we
// reimplement "os_str" instead of just using the generic unix
// "os_str".
let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) };
Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() }))
}
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
}
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
}
pub fn temp_dir() -> PathBuf {
panic!("no filesystem on this platform")
}

View file

@ -88,7 +88,7 @@ impl Command {
// in its own process. Thus the parent drops the lock guard immediately.
// The child calls `mem::forget` to leak the lock, which is crucial because
// releasing a lock is not async-signal-safe.
let env_lock = sys::os::env_read_lock();
let env_lock = sys::env::env_read_lock();
let pid = unsafe { self.do_fork()? };
if pid == 0 {
@ -237,7 +237,7 @@ impl Command {
// Similar to when forking, we want to ensure that access to
// the environment is synchronized, so make sure to grab the
// environment lock before we try to exec.
let _lock = sys::os::env_read_lock();
let _lock = sys::env::env_read_lock();
let Err(e) = self.do_exec(theirs, envp.as_ref());
e
@ -386,13 +386,13 @@ impl Command {
impl Drop for Reset {
fn drop(&mut self) {
unsafe {
*sys::os::environ() = self.0;
*sys::env::environ() = self.0;
}
}
}
_reset = Some(Reset(*sys::os::environ()));
*sys::os::environ() = envp.as_ptr();
_reset = Some(Reset(*sys::env::environ()));
*sys::env::environ() = envp.as_ptr();
}
libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
@ -735,8 +735,8 @@ impl Command {
cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
// Make sure we synchronize access to the global `environ` resource
let _env_lock = sys::os::env_read_lock();
let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
let _env_lock = sys::env::env_read_lock();
let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::env::environ() as *const _);
#[cfg(not(target_os = "nto"))]
let spawn_fn = libc::posix_spawnp;

View file

@ -67,7 +67,7 @@ impl Command {
let c_envp = envp
.as_ref()
.map(|c| c.as_ptr())
.unwrap_or_else(|| *sys::os::environ() as *const _);
.unwrap_or_else(|| *sys::env::environ() as *const _);
let stack_size = crate::cmp::max(
crate::env::var_os("RUST_MIN_STACK")
.and_then(|s| s.to_str().and_then(|s| s.parse().ok()))
@ -76,7 +76,7 @@ impl Command {
);
// ensure that access to the environment is synchronized
let _lock = sys::os::env_read_lock();
let _lock = sys::env::env_read_lock();
let ret = libc::rtpSpawn(
self.get_program_cstr().as_ptr(),