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