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};
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::raw::{NonNullSlice, Optional, RawSlice};
15
16use crate::Lazy;
17use alloc::ffi::CString;
18
19use super::args::RawArgsStatic;
20
21// Environment variables
22
23struct EnvVars {
24    env: Vec<(Box<[u8]>, Box<CStr>)>,
25    /// hints the size of the environment variables in bytes (key.length + value.length + 1 ('='))
26    /// which can then be used to duplicate the environment variables
27    size_hint: usize,
28}
29
30impl EnvVars {
31    pub const fn new() -> Self {
32        Self {
33            env: Vec::new(),
34            size_hint: 0,
35        }
36    }
37
38    pub fn get(&self, key: &[u8]) -> Option<&[u8]> {
39        for (k, v) in &self.env {
40            if &**k == key {
41                return Some(v.to_bytes());
42            }
43        }
44        None
45    }
46
47    /// # Safety
48    /// This function is unsafe because it should only be used if there is no environment variable with the same key.
49    /// otherwise use [`EnvVars::set`]
50    #[inline(always)]
51    pub unsafe fn push(&mut self, key: &[u8], value: &[u8]) {
52        let cstr = CString::new(value)
53            .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
54
55        self.env
56            .push((key.to_vec().into_boxed_slice(), cstr.into_boxed_c_str()));
57
58        // + null + '='
59        self.size_hint += key.len() + value.len() + 2;
60    }
61
62    #[inline(always)]
63    pub fn set(&mut self, key: &[u8], value: &[u8]) {
64        for (k, v) in &mut self.env {
65            if &**k == key {
66                let old_len = v.count_bytes();
67
68                let new_value = CString::new(value)
69                    .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
70                *v = new_value.into_boxed_c_str();
71                self.size_hint -= old_len + 1;
72                // + null
73                self.size_hint += value.len() + 1;
74                return;
75            }
76        }
77
78        unsafe {
79            self.push(key, value);
80        }
81    }
82
83    #[inline(always)]
84    pub fn remove(&mut self, key: &[u8]) {
85        for (i, (k, v)) in self.env.iter().enumerate() {
86            if &**k == key {
87                // order doesn't matter
88                self.size_hint -= key.len() + 1 + v.count_bytes();
89                self.env.swap_remove(i);
90                return;
91            }
92        }
93    }
94
95    /// Insert a raw slice of environment variables into the environment.
96    /// # Safety
97    /// This function is unsafe because any usage of [`RawSlice<T>`] is unsafe.
98    unsafe fn insert_raw(&mut self, raw: &[NonNullSlice<u8>]) {
99        self.env.reserve(raw.len());
100
101        for slice in raw {
102            let slice = slice.into_slice_mut();
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<RawSlice<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 {
134                RawSlice::from_raw_parts(ptr, key.len() + 1 + value.count_bytes())
135            });
136
137            buf[offset..offset + key.len()].copy_from_slice(key);
138            offset += key.len();
139
140            buf[offset] = b'=';
141            offset += 1;
142
143            let value_len = value.count_bytes() + 1;
144            buf[offset..offset + value_len].copy_from_slice(value.to_bytes_with_nul());
145            offset += value_len;
146        }
147
148        (buf, slices)
149    }
150}
151
152// TODO: refactor all of this
153pub(super) static RAW_ENV: RawArgsStatic = RawArgsStatic::new();
154
155// Lazy always implements Send and Sync LOL
156static ENV: Lazy<UnsafeCell<EnvVars>> = Lazy::new(|| {
157    let mut env = EnvVars::new();
158    unsafe { env.insert_raw(RAW_ENV.as_slice()) };
159    UnsafeCell::new(env)
160});
161
162// FIXME: unsafe after adding threads
163/// Gets all the environment variables in the current process
164#[inline]
165pub fn env_get_all() -> &'static [(Box<[u8]>, Box<CStr>)] {
166    let env = unsafe { &*ENV.get() };
167    &env.env
168}
169
170#[inline]
171pub fn env_get(key: &[u8]) -> Option<&[u8]> {
172    let env = unsafe { &*ENV.get() };
173    env.get(key)
174}
175
176#[inline]
177pub fn env_set(key: &[u8], value: &[u8]) {
178    let env = unsafe { &mut *ENV.get() };
179    env.set(key, value);
180}
181
182#[inline]
183pub fn env_remove(key: &[u8]) {
184    let env = unsafe { &mut *ENV.get() };
185    env.remove(key);
186}
187
188/// Duplicate the environment variables so that they can be used in a child process by being passed to `_start`.
189///
190/// # Safety
191/// unsafe because it requires for the output to not be dropped before the child process is created.
192/// the first element in the tuple represents the raw environment variables, while the second element is a vector of pointers within the first element.
193#[inline]
194pub(crate) unsafe fn duplicate_env() -> (Box<[u8]>, Vec<RawSlice<u8>>) {
195    let env = unsafe { &*ENV.get() };
196    env.duplicate()
197}
198
199#[inline]
200pub fn env_clear() {
201    let env = unsafe { &mut *ENV.get() };
202    env.clear();
203}
204
205#[cfg_attr(
206    not(any(feature = "std", feature = "rustc-dep-of-std")),
207    unsafe(no_mangle)
208)]
209/// Get an environment variable by key.
210pub extern "C" fn sysenv_get(key: RawSlice<u8>) -> Optional<RawSlice<u8>> {
211    unsafe {
212        let Some(key) = key.into_slice() else {
213            return Optional::None;
214        };
215
216        env_get(key).map(|slice| RawSlice::from_slice(slice)).into()
217    }
218}
219
220#[cfg_attr(
221    not(any(feature = "std", feature = "rustc-dep-of-std")),
222    unsafe(no_mangle)
223)]
224/// Set an environment variable by key.
225pub extern "C" fn sysenv_set(key: RawSlice<u8>, value: RawSlice<u8>) {
226    unsafe {
227        let Some(key) = key.into_slice() else {
228            return;
229        };
230        let value = if let Some(value) = value.into_slice() {
231            value
232        } else {
233            &[]
234        };
235
236        env_set(key, value);
237    }
238}
239
240#[cfg_attr(
241    not(any(feature = "std", feature = "rustc-dep-of-std")),
242    unsafe(no_mangle)
243)]
244/// Remove an environment variable by key.
245pub extern "C" fn sysenv_remove(key: RawSlice<u8>) {
246    unsafe {
247        let Some(key) = key.into_slice() else {
248            return;
249        };
250
251        env_remove(key);
252    }
253}
254
255#[cfg_attr(
256    not(any(feature = "std", feature = "rustc-dep-of-std")),
257    unsafe(no_mangle)
258)]
259/// Clear all environment variables.
260pub extern "C" fn sysenv_clear() {
261    env_clear();
262}