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}