1use core::{
5 ffi::CStr,
6 mem,
7};
8
9use crate::{
10 clone3,
11 clone_args,
12 eprintln,
13 execve,
14 exit,
15 pid_t,
16 wait4,
17 CStrArray,
18 CLONE_VFORK,
19 CLONE_VM,
20 SIGCHLD,
21};
22
23struct CallbackData<'a> {
24 filename: &'a CStr,
25 argv: &'a CStrArray,
26 envp: &'a CStrArray,
27 pre_exec: unsafe fn(data: usize) -> bool,
28 pre_exec_data: usize,
29}
30
31unsafe fn callback(arg: usize) -> ! {
32 let arg = &*(arg as *const CallbackData);
33 if (arg.pre_exec)(arg.pre_exec_data) {
34 if let Err(err) = execve(arg.filename, arg.argv, arg.envp) {
35 eprintln!("failed to execve: {err}");
37 }
38 }
39 exit(1).unwrap();
40}
41
42fn noop(_data: usize) -> bool {
43 true
44}
45
46pub struct SpawnOptions<'a> {
47 clone_flags: u32,
48 callback_data: CallbackData<'a>,
49}
50
51impl<'a> SpawnOptions<'a> {
52 pub fn new(filename: &'a CStr, argv: &'a CStrArray, envp: &'a CStrArray) -> Self {
53 Self {
54 clone_flags: 0,
55 callback_data: CallbackData {
56 filename,
57 argv,
58 envp,
59 pre_exec: noop,
60 pre_exec_data: 0,
61 },
62 }
63 }
64
65 pub unsafe fn pre_exec(&mut self, f: unsafe fn(data: usize) -> bool, data: usize) -> &mut Self {
76 self.callback_data.pre_exec = f;
77 self.callback_data.pre_exec_data = data;
78 self
79 }
80
81 pub fn clone_flags(&mut self, flags: u32) -> &mut Self {
84 self.clone_flags = flags;
85 self
86 }
87
88 pub fn spawn(&self) -> crate::Result<pid_t> {
90 let mut args: clone_args = unsafe { mem::zeroed() };
91 args.flags = u64::from(self.clone_flags | CLONE_VM | CLONE_VFORK);
92 args.exit_signal = u64::try_from(SIGCHLD).unwrap();
93
94 unsafe {
95 clone3(
96 &mut args,
97 callback,
98 &self.callback_data as *const _ as usize,
99 )
100 }
101 }
102
103 pub fn spawn_and_wait(&self) -> crate::Result<i32> {
105 let pid = self.spawn()?;
106
107 let mut status = 0;
108 wait4(pid, Some(&mut status), 0, None)?;
109
110 Ok(status)
111 }
112}