wasmer_wasix/syscalls/wasix/proc_fork_env.rs
1use crate::{WasiEnv, WasiError, WasiVFork};
2use wasmer::{FunctionEnvMut, Memory, MemorySize, WasmPtr};
3use wasmer_wasix_types::wasi::{Errno, Pid};
4
5/// ### `proc_fork_env()`
6/// Forks the environment of the current process into a new child process.
7/// The child process will start with the same memory and execution context
8/// as the parent process, similar to `fork()`.
9///
10/// Most syscalls are undefined behavior in the child process, except for
11/// `proc_exit2()` and `proc_exec3()`. `proc_exit2()` will terminate the
12/// child process and set the environment back to the parent process.
13/// `proc_exec3()` will exec the module in the child process and promote
14/// the child process to a real process. Then it will return with the
15/// environment back to the parent process.
16///
17/// This function differs from a traditional `vfork` in that it does not
18/// modify the control flow of the program. Instead, it only forks the
19/// WasiEnv, but leaves everything else (memory, call stack, store etc.)
20/// untouched.
21///
22/// This function is intended to be used in conjunction with
23/// setjmp/longjmp to build a lightweight implementation of vforking.
24///
25/// The value at child_pid_ptr will only be modified by a successful
26/// call to this function. It will contain the process id of the
27/// child process.
28#[tracing::instrument(level = "trace", skip_all, fields(pid = ctx.data().process.pid().raw()), ret)]
29pub fn proc_fork_env<M: MemorySize>(
30 mut ctx: FunctionEnvMut<'_, WasiEnv>,
31 child_pid_ptr: WasmPtr<Pid, M>,
32) -> Result<Errno, WasiError> {
33 WasiEnv::do_pending_operations(&mut ctx)?;
34
35 let env = ctx.data();
36
37 if let Some(vfork) = env.vfork.as_ref() {
38 tracing::warn!("Nested vforks are not supported");
39 return Ok(Errno::Notsup);
40 }
41
42 // Fork the environment which will copy all the open file handlers
43 // and associate a new context but otherwise shares things like the
44 // file system interface. The handle to the forked process is stored
45 // in the parent process context
46 let (mut child_env, child_handle) = match env.fork() {
47 Ok(p) => p,
48 Err(err) => {
49 tracing::error!("Could not fork process: {err}");
50 // TODO: evaluate the appropriate error code, document it in the spec.
51 return Ok(Errno::Perm);
52 }
53 };
54
55 // Write the child's PID to the provided pointer
56 let memory = unsafe { env.memory_view(&ctx) };
57 wasi_try_mem_ok!(child_pid_ptr.write(&memory, child_env.pid().raw()));
58
59 let parent_env = ctx.data_mut();
60
61 // Add the child to the parent's list of children
62 parent_env
63 .process
64 .lock()
65 .children
66 .push(child_env.process.clone());
67 // Swap the current environment with the child environment
68 child_env.swap_inner(parent_env);
69 std::mem::swap(parent_env, &mut child_env);
70
71 let previous_vfork = parent_env.vfork.replace(WasiVFork {
72 asyncify: None,
73 env: Box::new(child_env),
74 handle: child_handle,
75 });
76 assert!(previous_vfork.is_none()); // Already checked at the start of the function
77
78 Ok(Errno::Success)
79}