safa_api/syscalls/
process.rs

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