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 | |