Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce proc_exec2 #4756

Merged
merged 13 commits into from
Jun 7, 2024
2 changes: 2 additions & 0 deletions lib/wasix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ fn wasix_exports_32(mut store: &mut impl AsStoreMut, env: &FunctionEnv<WasiEnv>)
"proc_join" => Function::new_typed_with_env(&mut store, env, proc_join::<Memory32>),
"proc_signal" => Function::new_typed_with_env(&mut store, env, proc_signal::<Memory32>),
"proc_exec" => Function::new_typed_with_env(&mut store, env, proc_exec::<Memory32>),
"proc_exec2" => Function::new_typed_with_env(&mut store, env, proc_exec2::<Memory32>),
"proc_raise" => Function::new_typed_with_env(&mut store, env, proc_raise),
"proc_raise_interval" => Function::new_typed_with_env(&mut store, env, proc_raise_interval),
"proc_spawn" => Function::new_typed_with_env(&mut store, env, proc_spawn::<Memory32>),
Expand Down Expand Up @@ -652,6 +653,7 @@ fn wasix_exports_64(mut store: &mut impl AsStoreMut, env: &FunctionEnv<WasiEnv>)
"proc_join" => Function::new_typed_with_env(&mut store, env, proc_join::<Memory64>),
"proc_signal" => Function::new_typed_with_env(&mut store, env, proc_signal::<Memory64>),
"proc_exec" => Function::new_typed_with_env(&mut store, env, proc_exec::<Memory64>),
"proc_exec2" => Function::new_typed_with_env(&mut store, env, proc_exec2::<Memory64>),
"proc_raise" => Function::new_typed_with_env(&mut store, env, proc_raise),
"proc_raise_interval" => Function::new_typed_with_env(&mut store, env, proc_raise_interval),
"proc_spawn" => Function::new_typed_with_env(&mut store, env, proc_spawn::<Memory64>),
Expand Down
42 changes: 40 additions & 2 deletions lib/wasix/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ pub(crate) use self::types::{
},
*,
};
use self::{state::WasiInstanceGuardMemory, utils::WasiDummyWaker};
use self::{
state::{conv_env_vars, WasiInstanceGuardMemory},
utils::WasiDummyWaker,
};
pub(crate) use crate::os::task::{
process::{WasiProcessId, WasiProcessWait},
thread::{WasiThread, WasiThreadId},
Expand Down Expand Up @@ -1481,14 +1484,49 @@ where
}

// Function to prepare the WASI environment
pub(crate) fn _prepare_wasi(wasi_env: &mut WasiEnv, args: Option<Vec<String>>) {
pub(crate) fn _prepare_wasi(
wasi_env: &mut WasiEnv,
args: Option<Vec<String>>,
envs: Option<Vec<(String, String)>>,
) {
// Swap out the arguments with the new ones
if let Some(args) = args {
let mut wasi_state = wasi_env.state.fork();
wasi_state.args = args;
wasi_env.state = Arc::new(wasi_state);
}

// Append the new env vars to the old ones
if let Some(envs) = envs {
// append new env vars and replace old ones if they exist
let mut guard = wasi_env.state.envs.lock().unwrap();

let mut env_map = guard
.iter()
.map(|b| {
let string = String::from_utf8_lossy(b);
let (key, val) = string.split_once('=').expect("env var is malformed");

(key.to_string(), val.to_string())
})
.collect::<HashMap<_, _>>();
maminrayej marked this conversation as resolved.
Show resolved Hide resolved

for (key, val) in envs {
env_map.insert(key, val);
}

let envs = env_map
.into_iter()
.map(|(k, v)| (k, v.as_bytes().to_vec()))
.collect();

let envs = conv_env_vars(envs);

*guard = envs;

drop(guard)
}

// Close any files after the STDERR that are not preopened
let close_fds = {
let preopen_fds = {
Expand Down
2 changes: 2 additions & 0 deletions lib/wasix/src/syscalls/wasix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod port_route_list;
mod port_route_remove;
mod port_unbridge;
mod proc_exec;
mod proc_exec2;
mod proc_fork;
mod proc_id;
mod proc_join;
Expand Down Expand Up @@ -90,6 +91,7 @@ pub use port_route_list::*;
pub use port_route_remove::*;
pub use port_unbridge::*;
pub use proc_exec::*;
pub use proc_exec2::*;
pub use proc_fork::*;
pub use proc_id::*;
pub use proc_join::*;
Expand Down
235 changes: 9 additions & 226 deletions lib/wasix/src/syscalls/wasix/proc_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,230 +25,13 @@ pub fn proc_exec<M: MemorySize>(
args: WasmPtr<u8, M>,
args_len: M::Offset,
) -> Result<(), WasiError> {
WasiEnv::process_signals_and_exit(&mut ctx)?;

// If we were just restored the stack then we were woken after a deep sleep
if let Some(exit_code) = unsafe { handle_rewind::<M, i32>(&mut ctx) } {
// We should never get here as the process will be termined
// in the `WasiEnv::process_signals_and_exit()` call
let exit_code = ExitCode::from_native(exit_code);
ctx.data().process.terminate(exit_code);
return Err(WasiError::Exit(exit_code));
}

let memory = unsafe { ctx.data().memory_view(&ctx) };
let mut name = name.read_utf8_string(&memory, name_len).map_err(|err| {
warn!("failed to execve as the name could not be read - {}", err);
WasiError::Exit(Errno::Inval.into())
})?;
Span::current().record("name", name.as_str());
let args = args.read_utf8_string(&memory, args_len).map_err(|err| {
warn!("failed to execve as the args could not be read - {}", err);
WasiError::Exit(Errno::Inval.into())
})?;
let args: Vec<_> = args
.split(&['\n', '\r'])
.map(|a| a.to_string())
.filter(|a| !a.is_empty())
.collect();

// Convert relative paths into absolute paths
if name.starts_with("./") {
name = ctx.data().state.fs.relative_path_to_absolute(name);
}
trace!(name);

// Convert the preopen directories
let preopen = ctx.data().state.preopen.clone();

// Get the current working directory
let (_, cur_dir) = {
let (memory, state, inodes) =
unsafe { ctx.data().get_memory_and_wasi_state_and_inodes(&ctx, 0) };
match state.fs.get_current_dir(inodes, crate::VIRTUAL_ROOT_FD) {
Ok(a) => a,
Err(err) => {
warn!("failed to create subprocess for fork - {}", err);
return Err(WasiError::Exit(err.into()));
}
}
};

let new_store = ctx.data().runtime.new_store();

// If we are in a vfork we need to first spawn a subprocess of this type
// with the forked WasiEnv, then do a longjmp back to the vfork point.
if let Some(mut vfork) = ctx.data_mut().vfork.take() {
// We will need the child pid later
let child_process = ctx.data().process.clone();
let child_pid = child_process.pid();
let child_finished = child_process.finished;

// Restore the WasiEnv to the point when we vforked
vfork.env.swap_inner(ctx.data_mut());
std::mem::swap(vfork.env.as_mut(), ctx.data_mut());
let mut wasi_env = *vfork.env;
wasi_env.owned_handles.push(vfork.handle);
_prepare_wasi(&mut wasi_env, Some(args));

// Recrod the stack offsets before we give up ownership of the wasi_env
let stack_lower = wasi_env.layout.stack_lower;
let stack_upper = wasi_env.layout.stack_upper;

// Spawn a new process with this current execution environment
let mut err_exit_code: ExitCode = Errno::Success.into();

{
let bin_factory = Box::new(ctx.data().bin_factory.clone());
let tasks = wasi_env.tasks().clone();

let mut new_store = Some(new_store);
let mut config = Some(wasi_env);

match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut new_store, &mut config) {
Ok(a) => {}
Err(err) => {
if !err.is_not_found() {
error!("builtin failed - {}", err);
}

let new_store = new_store.take().unwrap();
let env = config.take().unwrap();

let name_inner = name.clone();
__asyncify_light(ctx.data(), None, async {
let ret = bin_factory.spawn(name_inner, new_store, env).await;
match ret {
Ok(ret) => {
trace!(%child_pid, "spawned sub-process");
}
Err(err) => {
err_exit_code = conv_spawn_err_to_exit_code(&err);

debug!(%child_pid, "process failed with (err={})", err_exit_code);
child_finished.set_finished(Ok(err_exit_code));

warn!(
"failed to execve as the process could not be spawned (vfork) - {}",
err
);
let _ = unsafe {
stderr_write(
&ctx,
format!(
"wasm execute failed [{}] - {}\n",
name.as_str(),
err
)
.as_bytes(),
)
}
.await;
}
}

Ok(())
});
}
}
};

// Jump back to the vfork point and current on execution
// note: fork does not return any values hence passing `()`
let memory_stack = vfork.memory_stack.freeze();
let rewind_stack = vfork.rewind_stack.freeze();
let store_data = vfork.store_data;
unwind::<M, _>(ctx, move |mut ctx, _, _| {
// Rewind the stack
match rewind::<M, _>(
ctx,
memory_stack,
rewind_stack,
store_data,
ForkResult {
pid: child_pid.raw() as Pid,
ret: Errno::Success,
},
) {
Errno::Success => OnCalledAction::InvokeAgain,
err => {
warn!("fork failed - could not rewind the stack - errno={}", err);
OnCalledAction::Trap(Box::new(WasiError::Exit(err.into())))
}
}
})?;
Ok(())
}
// Otherwise we need to unwind the stack to get out of the current executing
// callstack, steal the memory/WasiEnv and switch it over to a new thread
// on the new module
else {
// Prepare the environment
let mut wasi_env = ctx.data().clone();
_prepare_wasi(&mut wasi_env, Some(args));

// Get a reference to the runtime
let bin_factory = ctx.data().bin_factory.clone();
let tasks = wasi_env.tasks().clone();

// Create the process and drop the context
let bin_factory = Box::new(ctx.data().bin_factory.clone());

let mut new_store = Some(new_store);
let mut builder = Some(wasi_env);

let process = match bin_factory.try_built_in(
name.clone(),
Some(&ctx),
&mut new_store,
&mut builder,
) {
Ok(a) => Ok(a),
Err(err) => {
if !err.is_not_found() {
error!("builtin failed - {}", err);
}

let new_store = new_store.take().unwrap();
let env = builder.take().unwrap();

// Spawn a new process with this current execution environment
InlineWaker::block_on(bin_factory.spawn(name, new_store, env))
}
};

match process {
Ok(mut process) => {
// If we support deep sleeping then we switch to deep sleep mode
let env = ctx.data();
let thread = env.thread.clone();

// The poller will wait for the process to actually finish
let res = __asyncify_with_deep_sleep::<M, _, _>(ctx, async move {
process
.wait_finished()
.await
.unwrap_or_else(|_| Errno::Child.into())
.to_native()
})?;
match res {
AsyncifyAction::Finish(mut ctx, result) => {
// When we arrive here the process should already be terminated
let exit_code = ExitCode::from_native(result);
ctx.data().process.terminate(exit_code);
WasiEnv::process_signals_and_exit(&mut ctx)?;
Err(WasiError::Exit(Errno::Unknown.into()))
}
AsyncifyAction::Unwind => Ok(()),
}
}
Err(err) => {
warn!(
"failed to execve as the process could not be spawned (fork)[0] - {}",
err
);
Err(WasiError::Exit(Errno::Noexec.into()))
}
}
}
proc_exec2(
ctx,
name,
name_len,
args,
args_len,
WasmPtr::null(),
M::ZERO,
)
}
Loading
Loading