| 1 | use core::slice::memchr; |
| 2 | |
| 3 | use libc::c_char; |
| 4 | |
| 5 | pub use super::common::Env; |
| 6 | use crate::ffi::{CStr, OsStr, OsString}; |
| 7 | use crate::io; |
| 8 | use crate::os::unix::prelude::*; |
| 9 | use crate::sync::{PoisonError, RwLock}; |
| 10 | use crate::sys::common::small_c_string::run_with_cstr; |
| 11 | use crate::sys::cvt; |
| 12 | |
| 13 | // Use `_NSGetEnviron` on Apple platforms. |
| 14 | // |
| 15 | // `_NSGetEnviron` is the documented alternative (see `man environ`), and has |
| 16 | // been available since the first versions of both macOS and iOS. |
| 17 | // |
| 18 | // Nowadays, specifically since macOS 10.8, `environ` has been exposed through |
| 19 | // `libdyld.dylib`, which is linked via. `libSystem.dylib`: |
| 20 | // <https://github.com/apple-oss-distributions/dyld/blob/dyld-1160.6/libdyld/libdyldGlue.cpp#L913> |
| 21 | // |
| 22 | // So in the end, it likely doesn't really matter which option we use, but the |
| 23 | // performance cost of using `_NSGetEnviron` is extremely miniscule, and it |
| 24 | // might be ever so slightly more supported, so let's just use that. |
| 25 | // |
| 26 | // NOTE: The header where this is defined (`crt_externs.h`) was added to the |
| 27 | // iOS 13.0 SDK, which has been the source of a great deal of confusion in the |
| 28 | // past about the availability of this API. |
| 29 | // |
| 30 | // NOTE(madsmtm): Neither this nor using `environ` has been verified to not |
| 31 | // cause App Store rejections; if this is found to be the case, an alternative |
| 32 | // implementation of this is possible using `[NSProcessInfo environment]` |
| 33 | // - which internally uses `_NSGetEnviron` and a system-wide lock on the |
| 34 | // environment variables to protect against `setenv`, so using that might be |
| 35 | // desirable anyhow? Though it also means that we have to link to Foundation. |
| 36 | #[cfg (target_vendor = "apple" )] |
| 37 | pub unsafe fn environ() -> *mut *const *const c_char { |
| 38 | unsafe { libc::_NSGetEnviron() as *mut *const *const c_char } |
| 39 | } |
| 40 | |
| 41 | // Use the `environ` static which is part of POSIX. |
| 42 | #[cfg (not(target_vendor = "apple" ))] |
| 43 | pub unsafe fn environ() -> *mut *const *const c_char { |
| 44 | unsafe extern "C" { |
| 45 | unsafestatic mut environ: *const *const c_char; |
| 46 | } |
| 47 | &raw mut environ |
| 48 | } |
| 49 | |
| 50 | static ENV_LOCK: RwLock<()> = RwLock::new(()); |
| 51 | |
| 52 | pub fn env_read_lock() -> impl Drop { |
| 53 | ENV_LOCK.read().unwrap_or_else(op:PoisonError::into_inner) |
| 54 | } |
| 55 | |
| 56 | /// Returns a vector of (variable, value) byte-vector pairs for all the |
| 57 | /// environment variables of the current process. |
| 58 | pub fn env() -> Env { |
| 59 | unsafe { |
| 60 | let _guard = env_read_lock(); |
| 61 | let mut environ = *environ(); |
| 62 | let mut result = Vec::new(); |
| 63 | if !environ.is_null() { |
| 64 | while !(*environ).is_null() { |
| 65 | if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { |
| 66 | result.push(key_value); |
| 67 | } |
| 68 | environ = environ.add(1); |
| 69 | } |
| 70 | } |
| 71 | return Env::new(result); |
| 72 | } |
| 73 | |
| 74 | fn parse(input: &[u8]) -> Option<(OsString, OsString)> { |
| 75 | // Strategy (copied from glibc): Variable name and value are separated |
| 76 | // by an ASCII equals sign '='. Since a variable name must not be |
| 77 | // empty, allow variable names starting with an equals sign. Skip all |
| 78 | // malformed lines. |
| 79 | if input.is_empty() { |
| 80 | return None; |
| 81 | } |
| 82 | let pos = memchr::memchr(b'=' , &input[1..]).map(|p| p + 1); |
| 83 | pos.map(|p| { |
| 84 | ( |
| 85 | OsStringExt::from_vec(input[..p].to_vec()), |
| 86 | OsStringExt::from_vec(input[p + 1..].to_vec()), |
| 87 | ) |
| 88 | }) |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | pub fn getenv(k: &OsStr) -> Option<OsString> { |
| 93 | // environment variables with a nul byte can't be set, so their value is |
| 94 | // always None as well |
| 95 | run_with_cstrOption(k.as_bytes(), &|k: &CStr| { |
| 96 | let _guard: impl Drop = env_read_lock(); |
| 97 | let v: *const i8 = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; |
| 98 | |
| 99 | if v.is_null() { |
| 100 | Ok(None) |
| 101 | } else { |
| 102 | // SAFETY: `v` cannot be mutated while executing this line since we've a read lock |
| 103 | let bytes: Vec = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); |
| 104 | |
| 105 | Ok(Some(OsStringExt::from_vec(bytes))) |
| 106 | } |
| 107 | }) |
| 108 | .ok() |
| 109 | .flatten() |
| 110 | } |
| 111 | |
| 112 | pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { |
| 113 | run_with_cstr(k.as_bytes(), &|k: &CStr| { |
| 114 | run_with_cstr(v.as_bytes(), &|v: &CStr| { |
| 115 | let _guard: Result, …> = ENV_LOCK.write(); |
| 116 | cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(op:drop) |
| 117 | }) |
| 118 | }) |
| 119 | } |
| 120 | |
| 121 | pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { |
| 122 | run_with_cstr(n.as_bytes(), &|nbuf: &CStr| { |
| 123 | let _guard: Result, …> = ENV_LOCK.write(); |
| 124 | cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(op:drop) |
| 125 | }) |
| 126 | } |
| 127 | |