safa_api/
process.rs

1//! Module for process-related high-level functions over process related syscalls
2//!
3//! Such as api initialization functions [`_c_api_init`] and [`sysapi_init`], environment variables, and process arguments
4// FIXME: refactor this mess of a module and make it not available when feature = "std" because it breaks things
5
6use core::{cell::UnsafeCell, ffi::CStr, mem::MaybeUninit, ptr::NonNull};
7
8#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
9extern crate alloc;
10use crate::{
11    alloc::GLOBAL_SYSTEM_ALLOCATOR,
12    syscalls::{self, define_syscall},
13};
14use alloc::boxed::Box;
15use alloc::vec::Vec;
16use safa_abi::{
17    errors::ErrorStatus,
18    raw::{processes::TaskMetadata, NonNullSlice, Optional, RawSlice, RawSliceMut},
19};
20
21use crate::{syscalls::err_from_u16, syscalls::SyscallNum, Lazy};
22use alloc::ffi::CString;
23// args
24
25#[derive(Debug, Clone, Copy)]
26struct RawArgs {
27    args: NonNull<[NonNullSlice<u8>]>,
28}
29
30impl RawArgs {
31    const fn new(args: NonNull<[NonNullSlice<u8>]>) -> Self {
32        Self { args }
33    }
34
35    fn len(&self) -> usize {
36        unsafe { self.args.as_ref().len() }
37    }
38
39    fn get(&self, index: usize) -> Option<NonNullSlice<u8>> {
40        unsafe { self.args.as_ref().get(index).copied() }
41    }
42
43    unsafe fn into_slice(self) -> &'static [NonNullSlice<u8>] {
44        unsafe { self.args.as_ref() }
45    }
46}
47
48struct RawArgsStatic(UnsafeCell<MaybeUninit<Option<RawArgs>>>);
49unsafe impl Sync for RawArgsStatic {}
50
51impl RawArgsStatic {
52    const fn new() -> Self {
53        Self(UnsafeCell::new(MaybeUninit::uninit()))
54    }
55
56    unsafe fn init(&self, args: Option<RawArgs>) {
57        unsafe {
58            self.0.get().write(MaybeUninit::new(args));
59        }
60    }
61
62    unsafe fn get(&self, index: usize) -> Option<NonNullSlice<u8>> {
63        unsafe { (*self.0.get()).assume_init()?.get(index) }
64    }
65
66    unsafe fn len(&self) -> usize {
67        if let Some(args) = unsafe { (*self.0.get()).assume_init() } {
68            args.len()
69        } else {
70            0
71        }
72    }
73
74    unsafe fn get_raw(&self) -> Option<RawArgs> {
75        unsafe { (*self.0.get()).assume_init() }
76    }
77
78    unsafe fn as_slice(&self) -> &'static [NonNullSlice<u8>] {
79        unsafe {
80            if let Some(raw) = self.get_raw() {
81                raw.into_slice()
82            } else {
83                &mut []
84            }
85        }
86    }
87}
88
89static RAW_ARGS: RawArgsStatic = RawArgsStatic::new();
90
91/// Get the number of arguments passed to the program.
92#[cfg_attr(
93    not(any(feature = "std", feature = "rustc-dep-of-std")),
94    unsafe(no_mangle)
95)]
96#[inline(always)]
97pub extern "C" fn sysget_argc() -> usize {
98    unsafe { RAW_ARGS.len() }
99}
100
101/// Get the argument at the given index.
102#[cfg_attr(
103    not(any(feature = "std", feature = "rustc-dep-of-std")),
104    unsafe(no_mangle)
105)]
106#[inline(always)]
107pub extern "C" fn sysget_arg(index: usize) -> Optional<NonNullSlice<u8>> {
108    unsafe { RAW_ARGS.get(index).into() }
109}
110
111// Environment variables
112
113struct EnvVars {
114    env: Vec<(Box<[u8]>, Box<CStr>)>,
115    /// hints the size of the environment variables in bytes (key.length + value.length + 1 ('='))
116    /// which can then be used to duplicate the environment variables
117    size_hint: usize,
118}
119
120impl EnvVars {
121    pub const fn new() -> Self {
122        Self {
123            env: Vec::new(),
124            size_hint: 0,
125        }
126    }
127
128    pub fn get(&self, key: &[u8]) -> Option<&[u8]> {
129        for (k, v) in &self.env {
130            if &**k == key {
131                return Some(v.to_bytes());
132            }
133        }
134        None
135    }
136
137    /// # Safety
138    /// This function is unsafe because it should only be used if there is no environment variable with the same key.
139    /// otherwise use [`EnvVars::set`]
140    #[inline(always)]
141    pub unsafe fn push(&mut self, key: &[u8], value: &[u8]) {
142        let cstr = CString::new(value)
143            .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
144
145        self.env
146            .push((key.to_vec().into_boxed_slice(), cstr.into_boxed_c_str()));
147
148        self.size_hint += key.len() + value.len() + 1;
149    }
150
151    #[inline(always)]
152    pub fn set(&mut self, key: &[u8], value: &[u8]) {
153        for (k, v) in &mut self.env {
154            if &**k == key {
155                let old_len = v.count_bytes();
156
157                let new_value = CString::new(value)
158                    .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
159                *v = new_value.into_boxed_c_str();
160                self.size_hint -= old_len;
161                self.size_hint += value.len();
162                return;
163            }
164        }
165
166        unsafe {
167            self.push(key, value);
168        }
169    }
170
171    #[inline(always)]
172    pub fn remove(&mut self, key: &[u8]) {
173        for (i, (k, v)) in self.env.iter().enumerate() {
174            if &**k == key {
175                // order doesn't matter
176                self.size_hint -= key.len() + 1 + v.count_bytes();
177                self.env.swap_remove(i);
178                return;
179            }
180        }
181    }
182
183    /// Insert a raw slice of environment variables into the environment.
184    /// # Safety
185    /// This function is unsafe because any usage of [`RawSlice<T>`] is unsafe.
186    unsafe fn insert_raw(&mut self, raw: &[NonNullSlice<u8>]) {
187        self.env.reserve(raw.len());
188
189        for slice in raw {
190            let slice = slice.into_slice_mut();
191            let mut spilt = slice.splitn(2, |c| *c == b'=');
192
193            let Some(key) = spilt.next() else {
194                continue;
195            };
196
197            let value = spilt.next();
198            let value = value.unwrap_or_default();
199
200            self.push(key, value);
201        }
202    }
203
204    pub fn clear(&mut self) {
205        self.env.clear();
206        self.size_hint = 0;
207    }
208
209    fn duplicate(&self) -> (Vec<u8>, Vec<RawSlice<u8>>) {
210        let mut buf: Vec<u8> = Vec::with_capacity(self.size_hint);
211        let mut slices = Vec::with_capacity(self.env.len());
212
213        for (key, value) in &self.env {
214            let ptr = unsafe { buf.as_mut_ptr().add(buf.len()) };
215            slices.push(unsafe {
216                RawSlice::from_raw_parts(ptr, key.len() + 1 + value.count_bytes())
217            });
218
219            buf.extend_from_slice(key);
220            buf.push(b'=');
221            buf.extend_from_slice(value.to_bytes_with_nul());
222        }
223
224        (buf, slices)
225    }
226}
227
228// TODO: refactor all of this
229static RAW_ENV: RawArgsStatic = RawArgsStatic::new();
230
231// Lazy always implements Send and Sync LOL
232static ENV: Lazy<UnsafeCell<EnvVars>> = Lazy::new(|| {
233    let mut env = EnvVars::new();
234    unsafe { env.insert_raw(RAW_ENV.as_slice()) };
235    UnsafeCell::new(env)
236});
237
238// FIXME: unsafe after adding threads
239/// Gets all the environment variables in the current process
240#[inline]
241pub fn env_get_all() -> &'static [(Box<[u8]>, Box<CStr>)] {
242    let env = unsafe { &*ENV.get() };
243    &env.env
244}
245
246#[inline]
247pub fn env_get(key: &[u8]) -> Option<&[u8]> {
248    let env = unsafe { &*ENV.get() };
249    env.get(key)
250}
251
252#[inline]
253pub fn env_set(key: &[u8], value: &[u8]) {
254    let env = unsafe { &mut *ENV.get() };
255    env.set(key, value);
256}
257
258#[inline]
259pub fn env_remove(key: &[u8]) {
260    let env = unsafe { &mut *ENV.get() };
261    env.remove(key);
262}
263
264/// Duplicate the environment variables so that they can be used in a child process by being passed to `_start`.
265///
266/// # Safety
267/// unsafe because it requires for the output to not be dropped before the child process is created.
268/// the first element in the tuple respresents the raw environment variables, while the second element is a vector of pointers within the first element.
269#[inline]
270pub(crate) unsafe fn duplicate_env() -> (Vec<u8>, Vec<RawSlice<u8>>) {
271    let env = unsafe { &*ENV.get() };
272    env.duplicate()
273}
274
275#[inline]
276pub fn env_clear() {
277    let env = unsafe { &mut *ENV.get() };
278    env.clear();
279}
280
281#[cfg_attr(
282    not(any(feature = "std", feature = "rustc-dep-of-std")),
283    unsafe(no_mangle)
284)]
285/// Get an environment variable by key.
286pub extern "C" fn sysenv_get(key: RawSlice<u8>) -> Optional<RawSlice<u8>> {
287    unsafe {
288        let Some(key) = key.into_slice() else {
289            return Optional::None;
290        };
291
292        env_get(key).map(|slice| RawSlice::from_slice(slice)).into()
293    }
294}
295
296#[cfg_attr(
297    not(any(feature = "std", feature = "rustc-dep-of-std")),
298    unsafe(no_mangle)
299)]
300/// Set an environment variable by key.
301pub extern "C" fn sysenv_set(key: RawSlice<u8>, value: RawSlice<u8>) {
302    unsafe {
303        let Some(key) = key.into_slice() else {
304            return;
305        };
306        let value = if let Some(value) = value.into_slice() {
307            value
308        } else {
309            &[]
310        };
311
312        env_set(key, value);
313    }
314}
315
316#[cfg_attr(
317    not(any(feature = "std", feature = "rustc-dep-of-std")),
318    unsafe(no_mangle)
319)]
320/// Remove an environment variable by key.
321pub extern "C" fn sysenv_remove(key: RawSlice<u8>) {
322    unsafe {
323        let Some(key) = key.into_slice() else {
324            return;
325        };
326
327        env_remove(key);
328    }
329}
330
331#[cfg_attr(
332    not(any(feature = "std", feature = "rustc-dep-of-std")),
333    unsafe(no_mangle)
334)]
335/// Clear all environment variables.
336pub extern "C" fn sysenv_clear() {
337    env_clear();
338}
339
340/// An iterator over the arguments passed to the program.
341pub struct ArgsIter {
342    args: &'static [NonNullSlice<u8>],
343    index: usize,
344}
345
346impl ArgsIter {
347    pub fn get() -> Self {
348        let args = unsafe { RAW_ARGS.as_slice() };
349        Self { args, index: 0 }
350    }
351
352    pub fn get_index(&self, index: usize) -> Option<NonNullSlice<u8>> {
353        self.args.get(index).copied()
354    }
355
356    pub fn next(&mut self) -> Option<NonNullSlice<u8>> {
357        if self.index < self.args.len() {
358            let arg = self.args[self.index];
359            self.index += 1;
360            Some(arg)
361        } else {
362            None
363        }
364    }
365    /// The total amount of args in the iterator before calling [`Self::next`]
366    pub fn total_len(&self) -> usize {
367        self.args.len()
368    }
369    /// The amount of remaining args in the iterator
370    pub fn len(&self) -> usize {
371        self.total_len() - self.index
372    }
373}
374
375static META: Lazy<TaskMetadata> =
376    Lazy::new(|| meta_take().expect("failed to take ownership of the task metadata"));
377
378// PEAK design
379
380static STDIN: Lazy<usize> = Lazy::new(|| {
381    let stdin: Option<usize> = META.stdin.into();
382    if let Some(stdin) = stdin {
383        stdin
384    } else {
385        syscalls::open("dev:/tty").expect("failed to fall back to `dev:/tty` for stdin")
386    }
387});
388
389static STDOUT: Lazy<usize> = Lazy::new(|| {
390    let stdout: Option<usize> = META.stdout.into();
391    if let Some(stdout) = stdout {
392        stdout
393    } else {
394        syscalls::open("dev:/tty").expect("failed to fall back to `dev:/tty` for stdout")
395    }
396});
397
398use syscalls::types::SyscallResult;
399static STDERR: Lazy<usize> = Lazy::new(|| {
400    let stderr: Option<usize> = META.stderr.into();
401    if let Some(stderr) = stderr {
402        stderr
403    } else {
404        syscalls::open("dev:/tty").expect("failed to fall back to `dev:/tty` for stderr")
405    }
406});
407
408define_syscall!(SyscallNum::SysMetaTake => {
409    /// Takes ownership of the task metadata
410    /// the task metadata is used to store the stdin, stdout, and stderr file descriptors
411    /// this syscall can only be called once otherwise it will return [`ErrorStatus::Generic`] (1)
412    sysmeta_take(dest_task: *mut TaskMetadata)
413});
414
415#[inline]
416pub fn meta_take() -> Result<TaskMetadata, ErrorStatus> {
417    let mut dest_meta: TaskMetadata = unsafe { core::mem::zeroed() };
418    err_from_u16!(sysmeta_take(&raw mut dest_meta), dest_meta)
419}
420
421/// Returns the resource id of the stdout file descriptor
422#[cfg_attr(
423    not(any(feature = "std", feature = "rustc-dep-of-std")),
424    unsafe(no_mangle)
425)]
426#[inline(always)]
427pub extern "C" fn sysmeta_stdout() -> usize {
428    **STDOUT
429}
430
431/// Returns the resource id of the stderr file descriptor
432#[cfg_attr(
433    not(any(feature = "std", feature = "rustc-dep-of-std")),
434    unsafe(no_mangle)
435)]
436#[inline(always)]
437pub extern "C" fn sysmeta_stderr() -> usize {
438    **STDERR
439}
440
441/// Returns the resource id of the stdin file descriptor
442#[cfg_attr(
443    not(any(feature = "std", feature = "rustc-dep-of-std")),
444    unsafe(no_mangle)
445)]
446#[inline(always)]
447pub extern "C" fn sysmeta_stdin() -> usize {
448    **STDIN
449}
450
451// Initialization
452
453fn init_args(args: RawSliceMut<NonNullSlice<u8>>) {
454    unsafe {
455        let slice = args
456            .into_slice_mut()
457            .map(|inner| RawArgs::new(NonNull::new_unchecked(inner as *mut _)));
458        RAW_ARGS.init(slice)
459    }
460}
461
462fn init_env(env: RawSliceMut<NonNullSlice<u8>>) {
463    unsafe {
464        let slice = env
465            .into_slice_mut()
466            .map(|inner| RawArgs::new(NonNull::new_unchecked(inner as *mut _)));
467        RAW_ENV.init(slice)
468    }
469}
470
471/// Initializes the safa-api
472#[cfg_attr(
473    not(any(feature = "std", feature = "rustc-dep-of-std")),
474    unsafe(no_mangle)
475)]
476#[inline(always)]
477pub extern "C" fn sysapi_init(
478    args: RawSliceMut<NonNullSlice<u8>>,
479    env: RawSliceMut<NonNullSlice<u8>>,
480) {
481    init_args(args);
482    init_env(env);
483}
484
485/// Initializes the safa-api, converts arguments to C-style arguments, calls `main`, and exits with the result
486/// main are designed as C main function,
487///
488/// this function is designed to be called from C code at _start before main,
489/// main should be passed as a parameter
490#[cfg_attr(
491    not(any(feature = "std", feature = "rustc-dep-of-std")),
492    unsafe(no_mangle)
493)]
494pub unsafe extern "C" fn _c_api_init(
495    args: RawSliceMut<NonNullSlice<u8>>,
496    env: RawSliceMut<NonNullSlice<u8>>,
497    main: extern "C" fn(argc: i32, argv: *const NonNull<u8>) -> i32,
498) -> ! {
499    sysapi_init(args, env);
500
501    // Convert SafaOS `_start` arguments to `main` arguments
502    fn c_main_args(args: RawSliceMut<NonNullSlice<u8>>) -> (i32, *const NonNull<u8>) {
503        let argv_slice = unsafe { args.into_slice_mut().unwrap_or_default() };
504        if argv_slice.is_empty() {
505            return (0, core::ptr::null());
506        }
507
508        let bytes = args.len() * size_of::<usize>();
509
510        let c_argv_bytes = GLOBAL_SYSTEM_ALLOCATOR.allocate(bytes).unwrap();
511        let c_argv_slice = unsafe {
512            core::slice::from_raw_parts_mut(c_argv_bytes.as_ptr() as *mut NonNull<u8>, args.len())
513        };
514
515        for (i, arg) in argv_slice.iter().enumerate() {
516            c_argv_slice[i] = arg.as_non_null();
517        }
518
519        (args.len() as i32, c_argv_slice.as_ptr())
520    }
521
522    let (argc, argv) = c_main_args(args);
523    let result = main(argc, argv);
524    syscalls::exit(result as usize)
525}