Skip to main content

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}