wasmer_wasix/syscalls/wasix/
proc_exit2.rs

1use super::*;
2use crate::{WasiVForkAsyncify, syscalls::*};
3
4/// ### `proc_exit2()`
5/// Similar to `proc_exit()`
6///
7/// If used in a `proc_fork_env` (vfork) context it will exit the
8/// child, switch the process back into the parent process and
9/// return. If used for vforking, restoring the control to the
10/// place where the vfork happened is the responsibility of the
11/// caller.
12#[instrument(level = "trace", skip(ctx))]
13pub fn proc_exit2<M: MemorySize>(
14    mut ctx: FunctionEnvMut<'_, WasiEnv>,
15    code: ExitCode,
16) -> Result<(), WasiError> {
17    WasiEnv::do_pending_operations(&mut ctx)?;
18
19    let Some(mut vfork) = ctx.data_mut().vfork.take() else {
20        // Not in a vfork, just exit normally
21        return Err(WasiError::Exit(code));
22    };
23
24    tracing::debug!(
25        parent_pid = %vfork.env.process.pid(),
26        child_pid = %ctx.data().process.pid(),
27        "proc_exit from vfork, returning control to parent process"
28    );
29
30    // Prepare the child env for teardown by closing its FDs
31    block_on(
32        unsafe { ctx.data().get_memory_and_wasi_state(&ctx, 0) }
33            .1
34            .fs
35            .close_all(),
36    );
37
38    // Restore the WasiEnv to the point when we vforked
39    let mut parent_env = vfork.env;
40    ctx.data_mut().swap_inner(parent_env.as_mut());
41    let mut child_env = std::mem::replace(ctx.data_mut(), *parent_env);
42
43    // Terminate the child process
44    child_env.owned_handles.push(vfork.handle);
45    child_env.process.terminate(code);
46
47    let Some(asyncify_info) = vfork.asyncify else {
48        // vfork without asyncify only forks the WasiEnv, which we have restored
49        // above. We now return to the guest side in the parent process. Restoring
50        // the control flow is done on the guest side.
51        // See `proc_fork_env()` for information about this.
52
53        return Ok(());
54    };
55
56    // Jump back to the vfork point and continue execution
57    let child_pid = child_env.process.pid();
58    let rewind_stack = asyncify_info.rewind_stack.freeze();
59    let store_data = asyncify_info.store_data;
60    unwind::<M, _>(ctx, move |mut ctx, _, _| {
61        // Now rewind the previous stack and carry on from where we did the vfork
62        match rewind::<M, _>(
63            ctx,
64            None,
65            rewind_stack,
66            store_data,
67            ForkResult {
68                pid: child_pid.raw() as Pid,
69                ret: Errno::Success,
70            },
71        ) {
72            Errno::Success => OnCalledAction::InvokeAgain,
73            err => {
74                warn!("fork failed - could not rewind the stack - errno={}", err);
75                OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
76            }
77        }
78    })?;
79    Ok(())
80}