safa_api/process/
env.rs

1//! contains functions related to environment variables,
2//! api must be initialized before using these functions, see [`super::init`]
3
4use core::{cell::UnsafeCell, ffi::CStr, mem::MaybeUninit, ptr::NonNull};
5
6#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
7extern crate alloc;
8
9#[cfg(feature = "std")]
10use std as alloc;
11
12use alloc::boxed::Box;
13use alloc::vec::Vec;
14use safa_abi::ffi::option::OptZero;
15use safa_abi::ffi::slice::Slice;
16
17use alloc::ffi::CString;
18
19use crate::sync::cell::LazyCell;
20use crate::sync::locks::Mutex;
21
22// Environment variables
23
24struct EnvVars {
25    env: Vec<(Box<[u8]>, Box<CStr>)>,
26    /// hints the size of the environment variables in bytes (key.length + value.length + 1 ('='))
27    /// which can then be used to duplicate the environment variables
28    size_hint: usize,
29}
30
31impl EnvVars {
32    pub const fn new() -> Self {
33        Self {
34            env: Vec::new(),
35            size_hint: 0,
36        }
37    }
38
39    pub fn get(&self, key: &[u8]) -> Option<&[u8]> {
40        for (k, v) in &self.env {
41            if &**k == key {
42                return Some(v.to_bytes());
43            }
44        }
45        None
46    }
47
48    /// # Safety
49    /// This function is unsafe because it should only be used if there is no environment variable with the same key.
50    /// otherwise use [`EnvVars::set`]
51    #[inline(always)]
52    pub unsafe fn push(&mut self, key: &[u8], value: &[u8]) {
53        let cstr = CString::new(value)
54            .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
55
56        self.env
57            .push((key.to_vec().into_boxed_slice(), cstr.into_boxed_c_str()));
58
59        // + null + '='
60        self.size_hint += key.len() + value.len() + 2;
61    }
62
63    #[inline(always)]
64    pub fn set(&mut self, key: &[u8], value: &[u8]) {
65        for (k, v) in &mut self.env {
66            if &**k == key {
67                let old_len = v.count_bytes();
68
69                let new_value = CString::new(value)
70                    .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
71                *v = new_value.into_boxed_c_str();
72                self.size_hint -= old_len + 1;
73                // + null
74                self.size_hint += value.len() + 1;
75                return;
76            }
77        }
78
79        unsafe {
80            self.push(key, value);
81        }
82    }
83
84    #[inline(always)]
85    pub fn remove(&mut self, key: &[u8]) {
86        for (i, (k, v)) in self.env.iter().enumerate() {
87            if &**k == key {
88                // order doesn't matter
89                self.size_hint -= key.len() + 1 + v.count_bytes();
90                self.env.swap_remove(i);
91                return;
92            }
93        }
94    }
95
96    /// Insert a raw slice of environment variables into the environment.
97    /// # Safety
98    /// This function is unsafe because any usage of [`RawSlice<T>`] is unsafe.
99    unsafe fn insert_raw(&mut self, raw: &[&[u8]]) {
100        self.env.reserve(raw.len());
101
102        for slice in raw {
103            let mut spilt = slice.splitn(2, |c| *c == b'=');
104
105            let Some(key) = spilt.next() else {
106                continue;
107            };
108
109            let value = spilt.next();
110            let value = value.unwrap_or_default();
111
112            self.push(key, value);
113        }
114    }
115
116    pub fn clear(&mut self) {
117        self.env.clear();
118        self.size_hint = 0;
119    }
120
121    fn duplicate(&self) -> (Box<[u8]>, Vec<Slice<u8>>) {
122        // buf must not reallocate so size_hint must be accurate
123        // TODO: maybe rename `size_hint`?
124        let mut buf: Vec<u8> = Vec::with_capacity(self.size_hint);
125        unsafe { buf.set_len(buf.capacity()) };
126        let mut buf = buf.into_boxed_slice();
127        let mut offset = 0;
128
129        let mut slices = Vec::with_capacity(self.env.len());
130
131        for (key, value) in &self.env {
132            let ptr = unsafe { buf.as_mut_ptr().add(offset) };
133            slices.push(unsafe { Slice::from_raw_parts(ptr, key.len() + 1 + value.count_bytes()) });
134
135            buf[offset..offset + key.len()].copy_from_slice(key);
136            offset += key.len();
137
138            buf[offset] = b'=';
139            offset += 1;
140
141            let value_len = value.count_bytes() + 1;
142            buf[offset..offset + value_len].copy_from_slice(value.to_bytes_with_nul());
143            offset += value_len;
144        }
145
146        (buf, slices)
147    }
148}
149
150#[derive(Debug, Clone, Copy)]
151pub(super) struct RawEnv {
152    args: NonNull<[&'static [u8]]>,
153}
154
155impl RawEnv {
156    pub const fn new(args: Option<NonNull<[&'static [u8]]>>) -> Self {
157        Self {
158            args: match args {
159                Some(args) => args,
160                None => unsafe { NonNull::new_unchecked(&mut []) },
161            },
162        }
163    }
164
165    const unsafe fn into_slice(self) -> &'static [&'static [u8]] {
166        unsafe { self.args.as_ref() }
167    }
168}
169
170pub(super) struct RawEnvStatic(UnsafeCell<MaybeUninit<RawEnv>>);
171unsafe impl Sync for RawEnvStatic {}
172
173impl RawEnvStatic {
174    pub const fn new() -> Self {
175        Self(UnsafeCell::new(MaybeUninit::uninit()))
176    }
177
178    pub unsafe fn init(&self, env: RawEnv) {
179        unsafe {
180            self.0.get().write(MaybeUninit::new(env));
181        }
182    }
183
184    const unsafe fn get_unchecked(&self) -> &mut RawEnv {
185        (*self.0.get()).assume_init_mut()
186    }
187
188    pub const unsafe fn as_slice(&self) -> &'static [&'static [u8]] {
189        unsafe {
190            let raw = self.get_unchecked();
191            raw.into_slice()
192        }
193    }
194}
195
196// TODO: refactor all of this
197pub(super) static RAW_ENV: RawEnvStatic = RawEnvStatic::new();
198
199// FIXME: use a RwLock
200static ENV: LazyCell<Mutex<EnvVars>> = LazyCell::new(|| {
201    let mut env = EnvVars::new();
202    unsafe { env.insert_raw(RAW_ENV.as_slice()) };
203    Mutex::new(env)
204});
205
206/// Gets all the environment variables in the current process
207#[inline]
208pub fn env_get_all() -> Vec<(Box<[u8]>, Box<CStr>)> {
209    let env = ENV.lock();
210    env.env.clone()
211}
212
213#[inline]
214pub fn env_get(key: &[u8]) -> Option<Box<[u8]>> {
215    let env = ENV.lock();
216    env.get(key).map(|v| v.to_vec().into_boxed_slice())
217}
218
219#[inline]
220pub fn env_set(key: &[u8], value: &[u8]) {
221    let mut env = ENV.lock();
222    env.set(key, value);
223}
224
225#[inline]
226pub fn env_remove(key: &[u8]) {
227    let mut env = ENV.lock();
228    env.remove(key);
229}
230
231/// Duplicate the environment variables so that they can be used in a child process by being passed to `_start`.
232///
233/// # Safety
234/// unsafe because it requires for the output to not be dropped before the child process is created.
235/// the first element in the tuple represents the raw environment variables, while the second element is a vector of pointers within the first element.
236#[inline]
237pub(crate) unsafe fn duplicate_env() -> (Box<[u8]>, Vec<Slice<u8>>) {
238    let env = ENV.lock();
239    env.duplicate()
240}
241
242#[inline]
243pub fn env_clear() {
244    let mut env = ENV.lock();
245    env.clear();
246}
247
248#[cfg_attr(
249    not(any(feature = "std", feature = "rustc-dep-of-std")),
250    unsafe(no_mangle)
251)]
252
253/// Get an environment variable by key.
254///
255/// # Safety
256/// unsafe because it returns a pointer to the environment variable, which may be invalid after the environment is modified.
257pub unsafe extern "C" fn sysenv_get(key: OptZero<Slice<u8>>) -> OptZero<Slice<u8>> {
258    unsafe {
259        let Some(key) = key.into_option() else {
260            return OptZero::none();
261        };
262
263        ENV.lock()
264            .get(key.as_slice_unchecked())
265            .map(|slice| Slice::from_slice(slice))
266            .into()
267    }
268}
269
270#[cfg_attr(
271    not(any(feature = "std", feature = "rustc-dep-of-std")),
272    unsafe(no_mangle)
273)]
274/// Set an environment variable by key.
275pub extern "C" fn sysenv_set(key: Slice<u8>, value: OptZero<Slice<u8>>) {
276    unsafe {
277        let key = key
278            .try_as_slice()
279            .expect("invalid key passed to sysenv_set");
280
281        let value = if let Some(value) = value.into_option() {
282            value
283                .try_as_slice()
284                .expect("invalid value passed to sysenv_set")
285        } else {
286            &[]
287        };
288
289        env_set(key, value);
290    }
291}
292
293#[cfg_attr(
294    not(any(feature = "std", feature = "rustc-dep-of-std")),
295    unsafe(no_mangle)
296)]
297/// Remove an environment variable by key.
298pub extern "C" fn sysenv_remove(key: Slice<u8>) {
299    unsafe {
300        let key = key
301            .try_as_slice()
302            .expect("invalid key passed to sysenv_remove");
303
304        env_remove(key);
305    }
306}
307
308#[cfg_attr(
309    not(any(feature = "std", feature = "rustc-dep-of-std")),
310    unsafe(no_mangle)
311)]
312/// Clear all environment variables.
313pub extern "C" fn sysenv_clear() {
314    env_clear();
315}