relibc/header/spawn/file_actions.rs
1use alloc::{ffi::CString, vec::Vec};
2use core::ptr;
3
4use crate::{
5 header::errno::EBADF,
6 platform::types::{c_char, c_int, c_uchar, mode_t, size_t},
7};
8
9#[derive(Debug, Clone)]
10pub enum Action {
11 Open {
12 fd: c_int,
13 path: CString,
14 flag: c_int,
15 mode: mode_t,
16 },
17 Close(c_int),
18 Chdir(CString),
19 FChdir(c_int),
20 Dup2(c_int, c_int),
21}
22
23struct FileActions(Vec<Action>);
24
25/// A spawn file actions object.
26#[repr(C)]
27#[derive(Debug, Clone, Copy)]
28pub struct posix_spawn_file_actions_t {
29 __relibc_internal_size: [c_uchar; 24],
30 __relibc_internal_align: size_t,
31}
32
33impl posix_spawn_file_actions_t {
34 /// Adds an `Action` to `posix_spawn_file_actions_t`.
35 pub fn add_action(&mut self, action: Action) {
36 let v = ptr::from_mut(self).cast::<FileActions>();
37 unsafe {
38 (*v).0.push(action);
39 }
40 }
41}
42
43pub struct FileActionsIter {
44 actions: posix_spawn_file_actions_t,
45 curr: usize,
46}
47
48impl Iterator for FileActionsIter {
49 type Item = Action;
50
51 fn next(&mut self) -> Option<Self::Item> {
52 let actions = unsafe {
53 ptr::from_mut(&mut self.actions)
54 .cast::<FileActions>()
55 .as_ref()
56 .unwrap()
57 };
58 let e = actions.0.get(self.curr)?;
59 self.curr += 1;
60 Some((*e).clone())
61 }
62}
63
64impl IntoIterator for &posix_spawn_file_actions_t {
65 type Item = Action;
66
67 type IntoIter = FileActionsIter;
68
69 fn into_iter(self) -> Self::IntoIter {
70 FileActionsIter {
71 actions: (*self),
72 curr: 0,
73 }
74 }
75}
76
77/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn_file_actions_init.html>.
78///
79/// Initializes the object referenced by `file_actions` to contain no file
80/// actions for `posix_spawn()` or `posix_spawnp()` to perform.
81///
82/// Upon success, returns `0`. Upon failure, an error number is returned.
83///
84/// # Panics
85/// Panics if `file_actions` is `NULL`.
86#[unsafe(no_mangle)]
87pub extern "C" fn posix_spawn_file_actions_init(
88 file_actions: *mut posix_spawn_file_actions_t,
89) -> c_int {
90 if file_actions.is_null() {
91 panic!("file_actions cannot be NULL");
92 }
93 let v = Vec::new();
94 let actions = FileActions(v);
95 unsafe {
96 ptr::write(file_actions.cast::<FileActions>(), actions);
97 }
98 0
99}
100
101/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn_file_actions_destroy.html>.
102///
103/// Destroys the object referenced by `file_actions`, effectively making it
104/// uninitialized.
105///
106/// Upon success, returns `0`. Upon failure, an error number is returned.
107///
108/// # Panics
109/// Panics if `file_actions` is `NULL`.
110///
111/// # Safety
112/// If `file_actions` is not `NULL`, then it must be a pointer to a
113/// `file_actions` object that was initialised by calling
114/// `posix_spawn_file_actions_init()`.
115#[unsafe(no_mangle)]
116pub unsafe extern "C" fn posix_spawn_file_actions_destroy(
117 file_actions: *mut posix_spawn_file_actions_t,
118) -> c_int {
119 // TODO should we be returning EINVAL when `file_actions` is invalid?
120 if file_actions.is_null() {
121 panic!("file_actions cannot be NULL");
122 }
123 unsafe {
124 let _ = *(file_actions.cast::<FileActions>());
125 }
126 0
127}
128
129/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn_file_actions_addopen.html>.
130///
131/// Adds an open action to a spawn file actions object.
132///
133/// Upon success, returns `0`. Upon failure, an error number is returned.
134///
135/// # Panics
136/// Panics if `file_actions` is `NULL`.
137///
138/// # Safety
139/// `path` must be a valid null-terminated C string.
140#[unsafe(no_mangle)]
141pub unsafe extern "C" fn posix_spawn_file_actions_addopen(
142 file_actions: *mut posix_spawn_file_actions_t,
143 fildes: c_int,
144 path: *const c_char,
145 oflag: c_int,
146 mode: mode_t,
147) -> c_int {
148 let file_actions = unsafe { file_actions.as_mut().expect("file_actions cannot be NULL") };
149 if fildes < 0 {
150 return EBADF;
151 }
152 file_actions.add_action(Action::Open {
153 fd: fildes,
154 path: if path.is_null() {
155 CString::new("").unwrap()
156 } else {
157 unsafe { CString::from_raw(path.cast_mut()) }
158 },
159 flag: oflag,
160 mode,
161 });
162 0
163}
164
165/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn_file_actions_addclose.html>.
166///
167/// Adds a close action to a spawn file actions object.
168///
169/// Upon success, returns `0`. Upon failure, an error number is returned.
170///
171/// # Panics
172/// Panics if `file_actions` is `NULL`.
173///
174/// # Safety
175/// `file_actions` must be initialised.
176#[unsafe(no_mangle)]
177pub unsafe extern "C" fn posix_spawn_file_actions_addclose(
178 file_actions: *mut posix_spawn_file_actions_t,
179 fildes: c_int,
180) -> c_int {
181 let file_actions = unsafe { file_actions.as_mut().expect("file_actions cannot be NULL") };
182 if fildes < 0 {
183 return EBADF;
184 }
185 file_actions.add_action(Action::Close(fildes));
186 0
187}
188
189/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn_file_actions_addchdir.html>.
190///
191/// Adds a chdir action to the object referenced by `file_actions` that shall
192/// cause the working directory to be set to `path` (as if `chdir(path)` had
193/// been called) when a new process is spawned using this file actions object.
194///
195/// Upon success, returns `0`. Upon failure, an error number is returned.
196///
197/// # Panics
198/// Panics if `file_actions` is `NULL`.
199///
200/// # Safety
201/// `file_actions` must be initialised and `path` must be a valid
202/// null-terminated C string.
203#[unsafe(no_mangle)]
204pub unsafe extern "C" fn posix_spawn_file_actions_addchdir(
205 file_actions: *mut posix_spawn_file_actions_t,
206 path: *const c_char,
207) -> c_int {
208 let file_actions = unsafe { file_actions.as_mut().expect("file_actions cannot be NULL") };
209 file_actions.add_action(Action::Chdir(unsafe {
210 if path.is_null() {
211 CString::new("").unwrap()
212 } else {
213 CString::from_raw(path.cast_mut())
214 }
215 }));
216 0
217}
218
219/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn_file_actions_addfchdir.html>.
220///
221/// Adds a fchdir action to the object referenced by `file_actions` that shall
222/// cause the working directory to be set to `fildes` (as if `fchdir(fildes)`
223/// had been called) when a new process is spawned using this file actions
224/// object.
225///
226/// Upon success, returns `0`. Upon failure, an error number is returned.
227///
228/// # Panics
229/// Panics if `file_actions` is `NULL`.
230///
231/// # Safety
232/// `file_actions` must be initialised.
233#[unsafe(no_mangle)]
234pub unsafe extern "C" fn posix_spawn_file_actions_addfchdir(
235 file_actions: *mut posix_spawn_file_actions_t,
236 fildes: c_int,
237) -> c_int {
238 let file_actions = unsafe { file_actions.as_mut().expect("file_actions cannot be NULL") };
239 if fildes < 0 {
240 return EBADF;
241 }
242 file_actions.add_action(Action::FChdir(fildes));
243 0
244}
245
246/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_spawn_file_actions_adddup2.html>.
247///
248/// Adds a dup2 action to the object referenced by `file_actions` that shall
249/// cause the file descriptor `fildes` to be duplicated as `newfildes` (as if
250/// `dup2(fildes, newfildes)` had been called) when a new process is spawned
251/// using this file actions object.
252///
253/// Upon success, returns `0`. Upon failure, an error number is returned.
254///
255/// # Panics
256/// Panics if `file_actions` is `NULL`.
257///
258/// # Safety
259/// `file_actions` must be initialised.
260#[unsafe(no_mangle)]
261pub unsafe extern "C" fn posix_spawn_file_actions_adddup2(
262 file_actions: *mut posix_spawn_file_actions_t,
263 fildes: c_int,
264 newfildes: c_int,
265) -> c_int {
266 let file_actions = unsafe { file_actions.as_mut().expect("file_actions cannot be NULL") };
267 if fildes < 0 || newfildes < 0 {
268 return EBADF;
269 }
270 file_actions.add_action(Action::Dup2(fildes, newfildes));
271 0
272}