1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! Easy to use helpers to spawn processes built using the `clone` and `execve` system calls, kind
//! of like the `posix_spawn` functions provided by libc.
use core::{
ffi::CStr,
mem,
};
use crate::{
clone3,
clone_args,
eprintln,
execve,
exit,
pid_t,
wait4,
CStrArray,
CLONE_VFORK,
CLONE_VM,
SIGCHLD,
};
struct CallbackData<'a> {
filename: &'a CStr,
argv: &'a CStrArray,
envp: &'a CStrArray,
pre_exec: unsafe fn(data: usize) -> bool,
pre_exec_data: usize,
}
unsafe fn callback(arg: usize) -> ! {
let arg = &*(arg as *const CallbackData);
if (arg.pre_exec)(arg.pre_exec_data) {
if let Err(err) = execve(arg.filename, arg.argv, arg.envp) {
// Note: do not panic or return when execve fails, as we still want to exit.
eprintln!("failed to execve: {err}");
}
}
exit(1).unwrap();
}
fn noop(_data: usize) -> bool {
true
}
pub struct SpawnOptions<'a> {
clone_flags: u32,
callback_data: CallbackData<'a>,
}
impl<'a> SpawnOptions<'a> {
pub fn new(filename: &'a CStr, argv: &'a CStrArray, envp: &'a CStrArray) -> Self {
Self {
clone_flags: 0,
callback_data: CallbackData {
filename,
argv,
envp,
pre_exec: noop,
pre_exec_data: 0,
},
}
}
/// Sets a function to be called with the `data` argument before `execve` is called. This
/// allows the caller to modify the environment for the new process.
///
/// # Safety
///
/// The function must not introduce undefined behavior.
///
/// The function must not break library guarantees. This could happen since it will be executed
/// in another process which shares some things with the current process like the virtual
/// memory, but not others like the file descriptor table, the threads, etc.
pub unsafe fn pre_exec(&mut self, f: unsafe fn(data: usize) -> bool, data: usize) -> &mut Self {
self.callback_data.pre_exec = f;
self.callback_data.pre_exec_data = data;
self
}
/// Sets additional clone flags to use when spawning the process. This can be used to spawn the
/// process in a new namespace for example.
pub fn clone_flags(&mut self, flags: u32) -> &mut Self {
self.clone_flags = flags;
self
}
/// Tries to spawn the new process and returns its PID on success.
pub fn spawn(&self) -> crate::Result<pid_t> {
let mut args: clone_args = unsafe { mem::zeroed() };
args.flags = u64::from(self.clone_flags | CLONE_VM | CLONE_VFORK);
args.exit_signal = u64::try_from(SIGCHLD).unwrap();
unsafe {
clone3(
&mut args,
callback,
&self.callback_data as *const _ as usize,
)
}
}
/// Spawns a new process, waits for it to die and returns its status code.
pub fn spawn_and_wait(&self) -> crate::Result<i32> {
let pid = self.spawn()?;
let mut status = 0;
wait4(pid, Some(&mut status), 0, None)?;
Ok(status)
}
}