1use core::slice::memchr;
2
3use libc::c_char;
4
5pub use super::common::Env;
6use crate::ffi::{CStr, OsStr, OsString};
7use crate::io;
8use crate::os::unix::prelude::*;
9use crate::sync::{PoisonError, RwLock};
10use crate::sys::cvt;
11use crate::sys::helpers::run_with_cstr;
12
13#[cfg(target_vendor = "apple")]
37pub unsafe fn environ() -> *mut *const *const c_char {
38 unsafe { libc::_NSGetEnviron() as *mut *const *const c_char }
39}
40
41#[cfg(target_os = "freebsd")]
43pub unsafe fn environ() -> *mut *const *const c_char {
44 use crate::sync::LazyLock;
45
46 struct Environ(*mut *const *const c_char);
47 unsafe impl Send for Environ {}
48 unsafe impl Sync for Environ {}
49
50 static ENVIRON: LazyLock<Environ> = LazyLock::new(|| {
51 Environ(unsafe {
52 libc::dlsym(libc::RTLD_DEFAULT, c"environ".as_ptr()) as *mut *const *const c_char
53 })
54 });
55 ENVIRON.0
56}
57
58#[cfg(not(any(target_os = "freebsd", target_vendor = "apple")))]
60pub unsafe fn environ() -> *mut *const *const c_char {
61 unsafe extern "C" {
62 static mut environ: *const *const c_char;
63 }
64 &raw mut environ
65}
66
67static ENV_LOCK: RwLock<()> = RwLock::new(());
68
69pub fn env_read_lock() -> impl Drop {
70 ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
71}
72
73pub fn env() -> Env {
76 unsafe {
77 let _guard = env_read_lock();
78 let mut environ = *environ();
79 let mut result = Vec::new();
80 if !environ.is_null() {
81 while !(*environ).is_null() {
82 if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
83 result.push(key_value);
84 }
85 environ = environ.add(1);
86 }
87 }
88 return Env::new(result);
89 }
90
91 fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
92 if input.is_empty() {
97 return None;
98 }
99 let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
100 pos.map(|p| {
101 (
102 OsStringExt::from_vec(input[..p].to_vec()),
103 OsStringExt::from_vec(input[p + 1..].to_vec()),
104 )
105 })
106 }
107}
108
109pub fn getenv(k: &OsStr) -> Option<OsString> {
110 run_with_cstr(k.as_bytes(), &|k| {
113 let _guard = env_read_lock();
114 let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char;
115
116 if v.is_null() {
117 Ok(None)
118 } else {
119 let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
121
122 Ok(Some(OsStringExt::from_vec(bytes)))
123 }
124 })
125 .ok()
126 .flatten()
127}
128
129pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
130 run_with_cstr(k.as_bytes(), &|k| {
131 run_with_cstr(v.as_bytes(), &|v| {
132 let _guard = ENV_LOCK.write();
133 cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
134 })
135 })
136}
137
138pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
139 run_with_cstr(n.as_bytes(), &|nbuf| {
140 let _guard = ENV_LOCK.write();
141 cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
142 })
143}