safa_api/syscalls/
process.rs

1use core::num::NonZero;
2
3use safa_abi::{
4    errors::ErrorStatus,
5    ffi::{
6        num::ShouldNotBeZero,
7        option::{COption, OptZero},
8        ptr::FFINonNull,
9        slice::Slice,
10        str::Str,
11    },
12    process::{ProcessStdio, RawContextPriority, RawPSpawnConfig, SpawnFlags},
13};
14
15use crate::{
16    exported_func,
17    process::stdio::{systry_get_stderr, systry_get_stdin, systry_get_stdout},
18    syscalls::types::{OptionalPtrMut, Pid, RequiredPtr, RequiredPtrMut, Ri, SyscallResult},
19};
20
21use super::{define_syscall, SyscallNum};
22
23#[cfg(not(feature = "rustc-dep-of-std"))]
24extern crate alloc;
25use alloc::vec::Vec;
26
27define_syscall! {
28    SyscallNum::SysPExit => {
29        /// Exits the process with the exit code [`code`]
30        sysp_exit(code: usize) unreachable
31    },
32    SyscallNum::SysPWait => {
33        /// Waits for a child process with the pid `pid` to exit
34        ///
35        /// # Returns
36        /// - [`ErrorStatus::InvalidPid`] if the target process doesn't exist at the time of wait
37        ///
38        /// - [`ErrorStatus::MissingPermissions`] if the target process isn't a child of self
39        ///
40        /// - if `exit_code` is not null, it will be set to the exit code of the process if successful
41        sysp_wait(pid: Pid, exit_code: OptionalPtrMut<usize>)
42    },
43    SyscallNum::SysPTryCleanUp => {
44      /// Attempts to cleanup the process with pid `pid` and returns it's exit status on success
45      ///
46      /// # Returns
47      /// - [`ErrorStatus::InvalidPid`] if the target process doesn't exist at the time of attempted cleanup
48      ///
49      /// - [`ErrorStatus::Generic`] if the target process isn't dead and awaiting cleanup
50      sysp_try_cleanup(pid: Pid, dest_exit_code: OptionalPtrMut<usize>)
51    },
52    SyscallNum::SysPSpawn => {
53        sysp_spawn_inner(path: Str, raw_config: RequiredPtr<RawPSpawnConfig>, dest_pid: OptionalPtrMut<Pid>)
54    }
55}
56
57/// Exits the process with the exit code `code`
58#[inline]
59pub fn exit(code: usize) -> ! {
60    sysp_exit(code)
61}
62
63#[inline]
64/// Waits for the process with the resource id `pid` to exit
65/// and returns the exit code of the process
66/// # Returns
67/// - Ok(exit_code) if the target process was found, was a child of self, and was awaited successfully
68///
69/// - [`ErrorStatus::InvalidPid`] if the target process doesn't exist at the time of wait
70///
71/// - [`ErrorStatus::MissingPermissions`] if the target process isn't a child of self
72pub fn wait(pid: Pid) -> Result<usize, ErrorStatus> {
73    let mut dest_exit_code = 0;
74    let ptr = RequiredPtrMut::new(&mut dest_exit_code).into();
75    err_from_u16!(sysp_wait(pid, ptr), dest_exit_code)
76}
77#[inline]
78/// Attempts to cleanup the process with pid `pid` and returns it's exit status on success
79///
80/// # Returns
81/// - Err([`ErrorStatus::InvalidPid`]) if the target process doesn't exist at the time of attempted cleanup
82/// - Ok(None) if the target process isn't dead and awaitng cleanup
83/// - Ok(Some(exit_code)) if successful
84pub fn try_cleanup(pid: Pid) -> Result<Option<usize>, ErrorStatus> {
85    let mut dest_exit_code = 0;
86    let ptr = RequiredPtrMut::new(&mut dest_exit_code).into();
87    let results = err_from_u16!(sysp_try_cleanup(pid, ptr), dest_exit_code);
88
89    match results {
90        Ok(results) => Ok(Some(results)),
91        Err(ErrorStatus::Generic) => Ok(None),
92        Err(e) => Err(e),
93    }
94}
95
96exported_func! {
97    // doesn't use define_syscall because we use a different signature then the rest of the syscalls
98    /// Spawns a new process with the path `path` with arguments `argv` and flags `flags`
99    /// name_ptr can be null, in which case the name will be the path
100    /// path and name must be valid utf-8
101    ///
102    /// - if `dest_pid` is not null, it will be set to the pid of the new process
103    ///
104    /// - if `stdin`, `stdout`, or `stderr` are not `None`, the corresponding file descriptors will be inherited from the parent
105    ///   if they are None they will be inherited from the parent
106    ///
107    /// - the behavior isn't defined if `priority` is None, currently it will be set to a default
108    extern "C" fn sysp_spawn(
109        name: OptZero<Str>,
110        path: Str,
111        args: OptZero<Slice<Str>>,
112        // flags and return
113        flags: SpawnFlags,
114        priority: RawContextPriority,
115        // stdio
116        stdin: COption<Ri>,
117        stdout: COption<Ri>,
118        stderr: COption<Ri>,
119        custom_stack_size: OptZero<ShouldNotBeZero<usize>>,
120        dest_pid: OptionalPtrMut<Pid>,
121    ) -> SyscallResult {
122        let (stdin, stdout, stderr): (Option<_>, Option<_>, Option<_>) =
123            (stdin.into(), stdout.into(), stderr.into());
124
125        let stdio = {
126            if stdin.is_none() && stdout.is_none() && stderr.is_none() {
127                None
128            } else {
129                let stdout = stdout.or(systry_get_stdout().into());
130                let stdin = stdin.or(systry_get_stdin().into());
131                let stderr = stderr.or(systry_get_stderr().into());
132
133                Some(ProcessStdio::new(stdout, stdin, stderr))
134            }
135        };
136
137        let stdio = stdio.as_ref();
138        let stdio_ptr = stdio.map(|m| unsafe {FFINonNull::new_unchecked(m as *const _ as *mut _)}).into();
139
140        let (_, mut env) = unsafe { crate::process::env::duplicate_env() };
141
142        let env = unsafe {OptZero::some(Slice::from_raw_parts(env.as_mut_ptr(), env.len()))};
143        let config = RawPSpawnConfig::new_from_raw(name, args, env, flags, stdio_ptr, priority, custom_stack_size);
144
145        let raw_config_ptr = unsafe {RequiredPtr::new_unchecked(&config as *const _ as *mut _) };
146        sysp_spawn_inner(path, raw_config_ptr, dest_pid)
147    }
148}
149
150/// spawns a new process
151/// # Arguments
152/// * `stdin`, `stdout`, `stderr` are the file descriptors of stdio, if None, they will be inherited from the parent
153/// * `priority` is the process's default priority (that the threads, including the root one, will inherit by default),
154/// if set to None the behavior isn't well defined, however for now it will default to a constant value
155/// # Safety
156/// - `argv` must be valid pointers to a slice of slices of `&str`
157///
158/// - `argv` will become invalid after use, using them is UB
159#[inline]
160pub unsafe fn unsafe_spawn(
161    name: Option<&str>,
162    path: &str,
163    args: *mut [&str],
164    flags: SpawnFlags,
165    priority: RawContextPriority,
166    stdin: Option<Ri>,
167    stdout: Option<Ri>,
168    stderr: Option<Ri>,
169    custom_stack_size: Option<NonZero<usize>>,
170) -> Result<Pid, ErrorStatus> {
171    let mut pid = 0;
172    let pid_ptr = RequiredPtrMut::new(&mut pid).into();
173
174    let name = name.map(|s| Str::from_str(s)).into();
175    let path = Str::from_str(path);
176    let args = unsafe { OptZero::some(Slice::from_str_slices_mut(args as *mut [*mut str])) };
177
178    err_from_u16!(
179        sysp_spawn(
180            name,
181            path,
182            args,
183            flags,
184            priority.into(),
185            stdin.into(),
186            stdout.into(),
187            stderr.into(),
188            match custom_stack_size {
189                None => OptZero::none(),
190                Some(size) => OptZero::some(unsafe { ShouldNotBeZero::new_unchecked(size.get()) }),
191            },
192            pid_ptr,
193        ),
194        pid
195    )
196}
197
198// FIXME: I convert the argv form &[u8] to RawSlice
199// and that is why it is consumed,
200// i reuse the argv buffer as the result of the conversion
201// even though this might be inefficient especially that RawSlice should have the same layout as &[u8] and same for env
202// although this is fine because this method is only used in the rust standard library which gives args as an owned Vec anyways
203//
204/// same as [`unsafe_spawn`] but safe because it makes it clear that `argv`  are consumed
205#[inline]
206pub fn spawn(
207    name: Option<&str>,
208    path: &str,
209    mut argv: Vec<&str>,
210    flags: SpawnFlags,
211    priority: RawContextPriority,
212    stdin: Option<Ri>,
213    stdout: Option<Ri>,
214    stderr: Option<Ri>,
215    custom_stack_size: Option<NonZero<usize>>,
216) -> Result<Pid, ErrorStatus> {
217    let argv: &mut [&str] = &mut argv;
218    unsafe {
219        unsafe_spawn(
220            name,
221            path,
222            argv as *mut _,
223            flags,
224            priority,
225            stdin,
226            stdout,
227            stderr,
228            custom_stack_size,
229        )
230    }
231}