nstd_sys/
proc.rs

1//! Calling/Child process management.
2use crate::{
3    alloc::CBox,
4    core::{
5        optional::{gen_optional, NSTDOptional},
6        slice::NSTDSlice,
7        str::NSTDStr,
8    },
9    io::NSTDIOError,
10    NSTDInt32, NSTDUInt32,
11};
12use nstdapi::nstdapi;
13use std::process::{Child, Command};
14
15/// A handle to a child process.
16#[nstdapi]
17pub struct NSTDChildProcess {
18    /// A handle to a child process.
19    proc: CBox<Child>,
20}
21gen_optional!(NSTDOptionalChildProcess, NSTDChildProcess);
22
23/// Spawns a new child process with the name `program` and returns a handle to it.
24///
25/// # Parameters:
26///
27/// - `const NSTDStr *program` - A path to the program to run as a child process.
28///
29/// - `const NSTDSlice *args` - A slice of `NSTDStr` arguments to pass to the program.
30///
31/// - `const NSTDSlice *vars` - A slice of `NSTDStr[2]` key/value environment variables to
32/// give to the program.
33///
34/// # Returns
35///
36/// `NSTDOptionalChildProcess child` - A handle to the new child process on success, or an
37/// uninitialized "none" variant if spawning the child process fails.
38///
39/// # Safety
40///
41/// The user must ensure that all of `program`, `args`, and `vars` and their data remain valid for
42/// reads while this function is executing.
43#[nstdapi]
44pub unsafe fn nstd_proc_spawn(
45    program: &NSTDStr,
46    args: &NSTDSlice,
47    vars: &NSTDSlice,
48) -> NSTDOptionalChildProcess {
49    // Create the process command builder.
50    let mut cmd = Command::new(program.as_str());
51    if let Some(args) = args.as_slice::<NSTDStr>() {
52        if let Some(vars) = vars.as_slice::<[NSTDStr; 2]>() {
53            // Add the arguments.
54            cmd.args(args.iter().map(|arg| arg.as_str()));
55            // Add the environment variables.
56            cmd.envs(vars.iter().map(|vars| {
57                (
58                    vars.get_unchecked(0).as_str(),
59                    vars.get_unchecked(1).as_str(),
60                )
61            }));
62            // Spawn the process.
63            if let Ok(proc) = cmd.spawn() {
64                if let Some(proc) = CBox::new(proc) {
65                    return NSTDOptional::Some(NSTDChildProcess { proc });
66                }
67            }
68        }
69    }
70    NSTDOptional::None
71}
72
73/// Returns the OS-assigned ID of a child process.
74///
75/// # Parameters:
76///
77/// - `const NSTDChildProcess *handle` - A handle to the process.
78///
79/// # Returns
80///
81/// `NSTDUInt32 ID` - The child process ID.
82#[inline]
83#[nstdapi]
84pub fn nstd_proc_child_id(handle: &NSTDChildProcess) -> NSTDUInt32 {
85    handle.proc.id()
86}
87
88/// Attempts to kill a child process.
89///
90/// # Parameters:
91///
92/// - `NSTDChildProcess *handle` - A handle to the child process.
93///
94/// # Returns
95///
96/// `NSTDIOError errc` - The operation error code.
97#[inline]
98#[nstdapi]
99pub fn nstd_proc_kill(handle: &mut NSTDChildProcess) -> NSTDIOError {
100    if let Err(err) = handle.proc.kill() {
101        return NSTDIOError::from_err(err.kind());
102    }
103    NSTDIOError::NSTD_IO_ERROR_NONE
104}
105
106/// Waits for a child process to exit.
107///
108/// # Parameters:
109///
110/// - `NSTDChildProcess *handle` - A handle to the process.
111///
112/// # Returns
113///
114/// `NSTDIOError errc` - The operation error code.
115#[nstdapi]
116pub fn nstd_proc_join(handle: &mut NSTDChildProcess) -> NSTDIOError {
117    match handle.proc.wait() {
118        Ok(status) if status.success() => NSTDIOError::NSTD_IO_ERROR_NONE,
119        Err(err) => NSTDIOError::from_err(err.kind()),
120        _ => NSTDIOError::NSTD_IO_ERROR_UNKNOWN,
121    }
122}
123
124/// Frees a handle to a child process, allowing the process to run in the background.
125///
126/// # Parameters:
127///
128/// - `NSTDChildProcess handle` - A handle to the child process.
129#[inline]
130#[nstdapi]
131#[allow(
132    unused_variables,
133    clippy::missing_const_for_fn,
134    clippy::needless_pass_by_value
135)]
136pub fn nstd_proc_free(handle: NSTDChildProcess) {}
137
138/// Terminates the process with the given `exit_code`.
139///
140/// # Parameters:
141///
142/// - `NSTDInt32 exit_code` - The process exit code.
143///
144/// # Example
145///
146/// ```
147/// use nstd_sys::proc::nstd_proc_exit;
148///
149/// unsafe { nstd_proc_exit(0) };
150/// ```
151#[inline]
152#[nstdapi]
153pub fn nstd_proc_exit(exit_code: NSTDInt32) -> ! {
154    std::process::exit(exit_code);
155}
156
157/// Terminates the program in an abnormal fashion.
158#[inline]
159#[nstdapi]
160pub fn nstd_proc_abort() -> ! {
161    std::process::abort();
162}
163
164/// Returns the ID of the current process.
165///
166/// # Returns
167///
168/// `NSTDUInt32 ID` - The process ID.
169#[inline]
170#[nstdapi]
171pub fn nstd_proc_id() -> NSTDUInt32 {
172    std::process::id()
173}