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        self.size_hint += key.len() + value.len() + 1;
59    }
60
61    #[inline(always)]
62    pub fn set(&mut self, key: &[u8], value: &[u8]) {
63        for (k, v) in &mut self.env {
64            if &**k == key {
65                let old_len = v.count_bytes();
66
67                let new_value = CString::new(value)
68                    .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
69                *v = new_value.into_boxed_c_str();
70                self.size_hint -= old_len;
71                self.size_hint += value.len();
72                return;
73            }
74        }
75
76        unsafe {
77            self.push(key, value);
78        }
79    }
80
81    #[inline(always)]
82    pub fn remove(&mut self, key: &[u8]) {
83        for (i, (k, v)) in self.env.iter().enumerate() {
84            if &**k == key {
85                // order doesn't matter
86                self.size_hint -= key.len() + 1 + v.count_bytes();
87                self.env.swap_remove(i);
88                return;
89            }
90        }
91    }
92
93    /// Insert a raw slice of environment variables into the environment.
94    /// # Safety
95    /// This function is unsafe because any usage of [`RawSlice<T>`] is unsafe.
96    unsafe fn insert_raw(&mut self, raw: &[NonNullSlice<u8>]) {
97        self.env.reserve(raw.len());
98
99        for slice in raw {
100            let slice = slice.into_slice_mut();
101            let mut spilt = slice.splitn(2, |c| *c == b'=');
102
103            let Some(key) = spilt.next() else {
104                continue;
105            };
106
107            let value = spilt.next();
108            let value = value.unwrap_or_default();
109
110            self.push(key, value);
111        }
112    }
113
114    pub fn clear(&mut self) {
115        self.env.clear();
116        self.size_hint = 0;
117    }
118
119    fn duplicate(&self) -> (Vec<u8>, Vec<RawSlice<u8>>) {
120        let mut buf: Vec<u8> = Vec::with_capacity(self.size_hint);
121        let mut slices = Vec::with_capacity(self.env.len());
122
123        for (key, value) in &self.env {
124            let ptr = unsafe { buf.as_mut_ptr().add(buf.len()) };
125            slices.push(unsafe {
126                RawSlice::from_raw_parts(ptr, key.len() + 1 + value.count_bytes())
127            });
128
129            buf.extend_from_slice(key);
130            buf.push(b'=');
131            buf.extend_from_slice(value.to_bytes_with_nul());
132        }
133
134        (buf, slices)
135    }
136}
137
138// TODO: refactor all of this
139pub(super) static RAW_ENV: RawArgsStatic = RawArgsStatic::new();
140
141// Lazy always implements Send and Sync LOL
142static ENV: Lazy<UnsafeCell<EnvVars>> = Lazy::new(|| {
143    let mut env = EnvVars::new();
144    unsafe { env.insert_raw(RAW_ENV.as_slice()) };
145    UnsafeCell::new(env)
146});
147
148// FIXME: unsafe after adding threads
149/// Gets all the environment variables in the current process
150#[inline]
151pub fn env_get_all() -> &'static [(Box<[u8]>, Box<CStr>)] {
152    let env = unsafe { &*ENV.get() };
153    &env.env
154}
155
156#[inline]
157pub fn env_get(key: &[u8]) -> Option<&[u8]> {
158    let env = unsafe { &*ENV.get() };
159    env.get(key)
160}
161
162#[inline]
163pub fn env_set(key: &[u8], value: &[u8]) {
164    let env = unsafe { &mut *ENV.get() };
165    env.set(key, value);
166}
167
168#[inline]
169pub fn env_remove(key: &[u8]) {
170    let env = unsafe { &mut *ENV.get() };
171    env.remove(key);
172}
173
174/// Duplicate the environment variables so that they can be used in a child process by being passed to `_start`.
175///
176/// # Safety
177/// unsafe because it requires for the output to not be dropped before the child process is created.
178/// the first element in the tuple represents the raw environment variables, while the second element is a vector of pointers within the first element.
179#[inline]
180pub(crate) unsafe fn duplicate_env() -> (Vec<u8>, Vec<RawSlice<u8>>) {
181    let env = unsafe { &*ENV.get() };
182    env.duplicate()
183}
184
185#[inline]
186pub fn env_clear() {
187    let env = unsafe { &mut *ENV.get() };
188    env.clear();
189}
190
191#[cfg_attr(
192    not(any(feature = "std", feature = "rustc-dep-of-std")),
193    unsafe(no_mangle)
194)]
195/// Get an environment variable by key.
196pub extern "C" fn sysenv_get(key: RawSlice<u8>) -> Optional<RawSlice<u8>> {
197    unsafe {
198        let Some(key) = key.into_slice() else {
199            return Optional::None;
200        };
201
202        env_get(key).map(|slice| RawSlice::from_slice(slice)).into()
203    }
204}
205
206#[cfg_attr(
207    not(any(feature = "std", feature = "rustc-dep-of-std")),
208    unsafe(no_mangle)
209)]
210/// Set an environment variable by key.
211pub extern "C" fn sysenv_set(key: RawSlice<u8>, value: RawSlice<u8>) {
212    unsafe {
213        let Some(key) = key.into_slice() else {
214            return;
215        };
216        let value = if let Some(value) = value.into_slice() {
217            value
218        } else {
219            &[]
220        };
221
222        env_set(key, value);
223    }
224}
225
226#[cfg_attr(
227    not(any(feature = "std", feature = "rustc-dep-of-std")),
228    unsafe(no_mangle)
229)]
230/// Remove an environment variable by key.
231pub extern "C" fn sysenv_remove(key: RawSlice<u8>) {
232    unsafe {
233        let Some(key) = key.into_slice() else {
234            return;
235        };
236
237        env_remove(key);
238    }
239}
240
241#[cfg_attr(
242    not(any(feature = "std", feature = "rustc-dep-of-std")),
243    unsafe(no_mangle)
244)]
245/// Clear all environment variables.
246pub extern "C" fn sysenv_clear() {
247    env_clear();
248}