Skip to main content

relibc/header/spawn/
mod.rs

1//! `spawn.h` implementation
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/spawn.h.html>.
4
5mod file_actions;
6mod spawn_attr;
7
8pub use file_actions::{Action, posix_spawn_file_actions_t};
9pub use spawn_attr::{Flags, posix_spawnattr_t};
10
11use crate::{
12    c_str::CStr,
13    header::{
14        errno,
15        stdlib::getenv,
16        unistd::{F_OK, path::PathSearchIter},
17    },
18    iter::NulTerminated,
19    platform::{
20        self, Pal, Sys,
21        types::{c_char, c_int, pid_t},
22    },
23};
24
25/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn.html>.
26///
27/// Creates a new process (child process) from the specified process image. The
28/// `path` argument is a pathname that identifies the new process image file to
29/// execute.
30///
31/// Returns the process ID of the child process to the parent process, in the
32/// variable pointed to by a non-null `pid` argument, and shall return `0` as
33/// the function return value. Upon error, an error number shall be returned
34/// as the function return value.
35///
36/// # Panics
37/// `argv` must **not** be `NULL` and must contain atleast the program name.
38/// `path` must also **not** be `NULL`. Failure to ensure any of this will
39/// result in a panic.
40///
41/// # Safety
42/// `file_actions` and `attrp` must either be `NULL` or be pointers to properly
43/// initialised objects. Doing otherwise is undefined behaviour.
44///
45/// `path` and the elements in `argv` must be a pointers to valid
46/// null-terminated character arrays. Failure to ensure any of this will result
47/// in undefined behaviour.
48#[unsafe(no_mangle)]
49pub unsafe extern "C" fn posix_spawn(
50    pid: *mut pid_t,
51    path: *const c_char,
52    file_actions: *const posix_spawn_file_actions_t,
53    attrp: *const posix_spawnattr_t,
54    argv: *const *mut c_char,
55    envp: *const *mut c_char,
56) -> c_int {
57    let argv = {
58        if argv.is_null() || unsafe { (*argv).is_null() } {
59            return errno::EINVAL;
60        }
61
62        unsafe { NulTerminated::new(argv).unwrap() }
63    };
64    let envp = unsafe { NulTerminated::new(envp) };
65    let program = unsafe { CStr::from_ptr(path) };
66
67    match unsafe {
68        platform::Sys::spawn(program, file_actions.as_ref(), attrp.as_ref(), argv, envp)
69    } {
70        Ok(v) => {
71            if let Some(pid) = unsafe { pid.as_mut() } {
72                *pid = v;
73            }
74            0
75        }
76        Err(e) => e.0,
77    }
78}
79
80/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawnp.html>.
81///
82/// Creates a new process (child process) from the specified process image. The
83/// `file` argument is used to construct a pathname that identifies the new
84/// process image file.
85///
86/// Returns the process ID of the child process to the parent process, in the
87/// variable pointed to by a non-null `pid` argument, and shall return `0` as
88/// the function return value. Upon error, an error number shall be returned
89/// as the function return value.
90///
91/// # Panics
92/// `argv` must **not** be `NULL` and must contain atleast the program name.
93/// `file` must also **not** be `NULL`. Failure to ensure any of this will
94/// result in a panic.
95///
96/// # Safety
97/// `file_actions` and `attrp` must either be `NULL` or be pointers to properly
98/// initialised objects. Doing otherwise is undefined behaviour.
99///
100/// `file` and the elements in `argv` must be a pointers to valid
101/// null-terminated character arrays. Failure to ensure any of this will result
102/// in undefined behaviour.
103#[unsafe(no_mangle)]
104pub unsafe extern "C" fn posix_spawnp(
105    pid: *mut pid_t,
106    file: *const c_char,
107    file_actions: *const posix_spawn_file_actions_t,
108    attrp: *const posix_spawnattr_t,
109    argv: *const *mut c_char,
110    envp: *const *mut c_char,
111) -> c_int {
112    let program = unsafe { CStr::from_ptr(file) };
113    if program.contains(b'/') {
114        return unsafe { posix_spawn(pid, file, file_actions, attrp, argv, envp) };
115    }
116    let path_env = unsafe { getenv(c"PATH".as_ptr()) };
117    if path_env.is_null() {
118        return errno::ENOENT;
119    }
120    let path_env = unsafe { CStr::from_ptr(path_env) };
121    for program_buf in PathSearchIter::new(&program.to_bytes(), &path_env) {
122        // SAFETY: CStr::from_ptr().to_bytes() always stop at null, no need to check again
123        let program_c = unsafe { CStr::from_bytes_with_nul_unchecked(program_buf.as_slice()) };
124        if Sys::access(program_c, F_OK).is_err() {
125            continue;
126        }
127        return unsafe {
128            posix_spawn(
129                pid,
130                program_buf.as_ptr() as *mut _,
131                file_actions,
132                attrp,
133                argv,
134                envp,
135            )
136        };
137    }
138    errno::ENOENT
139}