diff --git a/lib/wasix/src/os/command/builtins/cmd_wasmer.rs b/lib/wasix/src/os/command/builtins/cmd_wasmer.rs index 4f609933a3b9..40f263ce0e65 100644 --- a/lib/wasix/src/os/command/builtins/cmd_wasmer.rs +++ b/lib/wasix/src/os/command/builtins/cmd_wasmer.rs @@ -7,7 +7,7 @@ use crate::{ runtime::module_cache::HashedModuleData, }; use shared_buffer::OwnedBuffer; -use virtual_fs::{AsyncReadExt, FileSystem}; +use virtual_fs::{AsyncReadExt, AsyncWriteExt, FileSystem}; use virtual_mio::block_on; use wasmer::FunctionEnvMut; use wasmer_package::utils::from_bytes; @@ -24,6 +24,7 @@ const HELP: &str = r#"USAGE: OPTIONS: -h, --help Print help information + -V, --version Print version information SUBCOMMANDS: run Run a WebAssembly file. Formats accepted: wasm, wat @@ -50,6 +51,37 @@ impl CmdWasmer { pub fn new(runtime: Arc) -> Self { Self { runtime } } + + #[allow(clippy::await_holding_lock)] + async fn write_stdout(env: &WasiEnv, buf: &[u8]) -> Result<(), Errno> { + let fd = env.state.fs.get_fd(1)?; + let handle = { + let mut guard = fd.inode.write(); + match &mut *guard { + crate::fs::Kind::File { + handle: Some(handle), + .. + } => handle.clone(), + crate::fs::Kind::PipeTx { tx } => { + return std::io::Write::write_all(tx, buf).map_err(crate::utils::map_io_err); + } + crate::fs::Kind::DuplexPipe { pipe } => { + return std::io::Write::write_all(pipe, buf).map_err(crate::utils::map_io_err); + } + crate::fs::Kind::Buffer { buffer } => { + buffer.extend_from_slice(buf); + return Ok(()); + } + _ => return Err(Errno::Badf), + } + }; + + let mut file = handle.write().unwrap(); + file.write_all(buf) + .await + .map_err(crate::utils::map_io_err)?; + file.flush().await.map_err(crate::utils::map_io_err) + } } #[derive(Debug, Clone)] @@ -211,6 +243,15 @@ impl VirtualCommand for CmdWasmer { OwnedTaskStatus::new_finished_with_code(Errno::Success.into()).handle(); Ok(handle) } + Some("--version" | "-V") => { + let version = format!("wasmer {}\n", wasmer_types::VERSION); + if let Some(env) = env.as_ref() { + Self::write_stdout(env, version.as_bytes()).await.ok(); + } + let handle = + OwnedTaskStatus::new_finished_with_code(Errno::Success.into()).handle(); + Ok(handle) + } Some(what) => { let what = Some(what.to_string()); let args = args.map(|a| a.to_string()).collect(); diff --git a/lib/wasix/src/syscalls/wasix/mod.rs b/lib/wasix/src/syscalls/wasix/mod.rs index 55de4480ce3e..30aa2f770ed5 100644 --- a/lib/wasix/src/syscalls/wasix/mod.rs +++ b/lib/wasix/src/syscalls/wasix/mod.rs @@ -188,6 +188,28 @@ pub use thread_spawn::*; pub use tty_get::*; pub use tty_set::*; +fn propagate_virtual_task_completion( + tasks: &Arc, + mut task: crate::os::task::TaskJoinHandle, + child_finished: Arc, +) -> Result<(), crate::WasiThreadError> { + if let Some(result) = task.status().into_finished() { + child_finished.set_finished(result); + return Ok(()); + } + + let finished_on_error = child_finished.clone(); + let result = tasks.task_shared(Box::new(move || { + Box::pin(async move { + child_finished.set_finished(task.wait_finished().await); + }) + })); + if result.is_err() { + finished_on_error.set_finished(Ok(Errno::Child.into())); + } + result +} + use tracing::{Span, debug_span, field, instrument, trace_span}; use wasmer::WasmRef; diff --git a/lib/wasix/src/syscalls/wasix/proc_spawn.rs b/lib/wasix/src/syscalls/wasix/proc_spawn.rs index d7d0ee040684..d1281520cf05 100644 --- a/lib/wasix/src/syscalls/wasix/proc_spawn.rs +++ b/lib/wasix/src/syscalls/wasix/proc_spawn.rs @@ -118,6 +118,8 @@ pub fn proc_spawn_internal( } }; let child_process = child_env.process.clone(); + let child_finished = child_process.finished.clone(); + let tasks = child_env.tasks().clone(); if let Some(args) = args { let mut child_state = env.state.fork(); child_state.args = std::sync::Mutex::new(args); @@ -238,8 +240,12 @@ pub fn proc_spawn_internal( let mut builder = Some(child_env); // First we try the built in commands - let mut process = match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut builder) { - Ok(a) => a, + match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut builder) { + Ok(task) => { + if let Err(err) = propagate_virtual_task_completion(&tasks, task, child_finished) { + return Ok(Err(err.into())); + } + } Err(err) => { if !err.is_not_found() { error!("builtin failed - {}", err); @@ -250,12 +256,12 @@ pub fn proc_spawn_internal( match __asyncify(&mut ctx, None, async move { Ok(child_work.await) })? .map_err(|err| Errno::Unknown) { - Ok(Ok(a)) => a, + Ok(Ok(_)) => {} Ok(Err(err)) => return Ok(Err(conv_spawn_err_to_errno(&err))), Err(err) => return Ok(Err(err)), } } - }; + } // Add the process to the environment state { diff --git a/lib/wasix/src/syscalls/wasix/proc_spawn3.rs b/lib/wasix/src/syscalls/wasix/proc_spawn3.rs index 809122ffc1a2..10da8dbc7222 100644 --- a/lib/wasix/src/syscalls/wasix/proc_spawn3.rs +++ b/lib/wasix/src/syscalls/wasix/proc_spawn3.rs @@ -140,6 +140,8 @@ pub(crate) fn proc_spawn3_impl( // Setup some properties in the child environment let pid = child_env.pid(); let tid = child_env.tid(); + let child_finished = child_env.process.finished.clone(); + let tasks = child_env.tasks().clone(); wasi_try_mem_ok!(ret.write(&memory, pid.raw())); Span::current() .record("pid", pid.raw()) @@ -157,7 +159,12 @@ pub(crate) fn proc_spawn3_impl( let mut builder = Some(child_env); let process = match bin_factory.try_built_in(name.clone(), Some(&ctx), &mut builder) { - Ok(a) => Ok(a), + Ok(task) => { + if let Err(err) = propagate_virtual_task_completion(&tasks, task, child_finished) { + return Ok(err.into()); + } + Ok(()) + } Err(err) => { if !err.is_not_found() { error!("builtin failed - {}", err); @@ -166,7 +173,7 @@ pub(crate) fn proc_spawn3_impl( let env = builder.take().unwrap(); // Spawn a new process with this current execution environment - block_on(bin_factory.spawn(name.clone(), env)) + block_on(bin_factory.spawn(name.clone(), env)).map(|_| ()) } }; diff --git a/lib/wasix/tests/wasm_tests/process/builtin-wasmer-version/main.c b/lib/wasix/tests/wasm_tests/process/builtin-wasmer-version/main.c new file mode 100644 index 000000000000..a3cedfe06f26 --- /dev/null +++ b/lib/wasix/tests/wasm_tests/process/builtin-wasmer-version/main.c @@ -0,0 +1,93 @@ +//#ExpectedStdout: builtin wasmer version passed + +#include +#include +#include +#include +#include +#include + +extern char** environ; + +static void fail(const char* message) { + perror(message); + exit(1); +} + +static size_t read_all(int fd, char* buffer, size_t capacity) { + size_t length = 0; + while (length < capacity) { + ssize_t count = read(fd, buffer + length, capacity - length); + if (count < 0) { + fail("read"); + } + if (count == 0) { + break; + } + length += count; + } + return length; +} + +int main(void) { + int stdout_pipe[2]; + int stderr_pipe[2]; + if (pipe(stdout_pipe) != 0 || pipe(stderr_pipe) != 0) { + fail("pipe"); + } + + posix_spawn_file_actions_t actions; + if (posix_spawn_file_actions_init(&actions) != 0 || + posix_spawn_file_actions_adddup2(&actions, stdout_pipe[1], + STDOUT_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, stderr_pipe[1], + STDERR_FILENO) != 0 || + posix_spawn_file_actions_addclose(&actions, stdout_pipe[0]) != 0 || + posix_spawn_file_actions_addclose(&actions, stdout_pipe[1]) != 0 || + posix_spawn_file_actions_addclose(&actions, stderr_pipe[0]) != 0 || + posix_spawn_file_actions_addclose(&actions, stderr_pipe[1]) != 0) { + return 1; + } + + pid_t pid; + char* argv[] = {"wasmer", "--version", NULL}; + int spawn_error = posix_spawnp(&pid, "wasmer", &actions, NULL, argv, environ); + posix_spawn_file_actions_destroy(&actions); + if (spawn_error != 0) { + return spawn_error; + } + + close(stdout_pipe[1]); + close(stderr_pipe[1]); + char stdout_output[128] = {0}; + char stderr_output[128] = {0}; + size_t stdout_length = + read_all(stdout_pipe[0], stdout_output, sizeof(stdout_output) - 1); + size_t stderr_length = + read_all(stderr_pipe[0], stderr_output, sizeof(stderr_output) - 1); + close(stdout_pipe[0]); + close(stderr_pipe[0]); + + int status; + if (waitpid(pid, &status, 0) != pid) { + fail("waitpid"); + } + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + fprintf(stderr, "wasmer exited with status %d\n", status); + return 1; + } + + if (stdout_length == 0 || + strncmp(stdout_output, "wasmer ", strlen("wasmer ")) != 0) { + fprintf(stderr, "unexpected wasmer stdout: %s\n", stdout_output); + return 1; + } + if (stderr_length != 0) { + fprintf(stderr, "unexpected wasmer stderr: %s\n", stderr_output); + return 1; + } + + puts("builtin wasmer version passed"); + return 0; +}