Skip to content

Commit

Permalink
Use WASI builder directly in C API (#8572) (#8578)
Browse files Browse the repository at this point in the history
* Use WASI builder directly in C API

This commit updates the C API to use the `WasiCtxBuilder` directly
within `wasi_config_t` instead of buffering up the options separately.
This keeps the behavior of the Rust-based API more similar to the C API
and should also help resolve #8552 due to errors being returned more
eagerly in the builder-based API.

This additionally makes some minor modifications to the C APIs here as
appropriate.

Close #8552

* Review comments
  • Loading branch information
alexcrichton authored May 7, 2024
1 parent 89af353 commit c9d9098
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 142 deletions.
11 changes: 9 additions & 2 deletions crates/c-api/include/wasi.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,11 @@ WASI_API_EXTERN own wasi_config_t *wasi_config_new();
*
* The arguments are copied into the `config` object as part of this function
* call, so the `argv` pointer only needs to stay alive for this function call.
*
* This function returns `true` if all arguments were registered successfully,
* or `false` if an argument was not valid UTF-8.
*/
WASI_API_EXTERN void wasi_config_set_argv(wasi_config_t *config, int argc,
WASI_API_EXTERN bool wasi_config_set_argv(wasi_config_t *config, size_t argc,
const char *argv[]);

/**
Expand All @@ -76,8 +79,12 @@ WASI_API_EXTERN void wasi_config_inherit_argv(wasi_config_t *config);
* The env vars are copied into the `config` object as part of this function
* call, so the `names` and `values` pointers only need to stay alive for this
* function call.
*
* This function returns `true` if all environment variables were successfully
* registered. This returns `false` if environment variables are not valid
* UTF-8.
*/
WASI_API_EXTERN void wasi_config_set_env(wasi_config_t *config, int envc,
WASI_API_EXTERN bool wasi_config_set_env(wasi_config_t *config, size_t envc,
const char *names[],
const char *values[]);

Expand Down
196 changes: 56 additions & 140 deletions crates/c-api/src/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
use crate::wasm_byte_vec_t;
use anyhow::Result;
use std::ffi::CStr;
use std::ffi::{c_char, CStr};
use std::fs::File;
use std::os::raw::{c_char, c_int};
use std::path::{Path, PathBuf};
use std::path::Path;
use std::slice;
use wasmtime_wasi::{preview1::WasiP1Ctx, WasiCtxBuilder};

Expand All @@ -26,163 +25,73 @@ unsafe fn create_file(path: *const c_char) -> Option<File> {
}

#[repr(C)]
#[derive(Default)]
pub struct wasi_config_t {
args: Vec<Vec<u8>>,
env: Vec<(Vec<u8>, Vec<u8>)>,
stdin: WasiConfigReadPipe,
stdout: WasiConfigWritePipe,
stderr: WasiConfigWritePipe,
preopen_dirs: Vec<(PathBuf, String)>,
inherit_args: bool,
inherit_env: bool,
}

#[repr(C)]
#[derive(Default)]
pub enum WasiConfigReadPipe {
#[default]
None,
Inherit,
File(File),
Bytes(Vec<u8>),
}

#[repr(C)]
#[derive(Default)]
pub enum WasiConfigWritePipe {
#[default]
None,
Inherit,
File(File),
builder: WasiCtxBuilder,
}

wasmtime_c_api_macros::declare_own!(wasi_config_t);

impl wasi_config_t {
pub fn into_wasi_ctx(self) -> Result<WasiP1Ctx> {
let mut builder = WasiCtxBuilder::new();
if self.inherit_args {
builder.inherit_args();
} else if !self.args.is_empty() {
let args = self
.args
.into_iter()
.map(|bytes| Ok(String::from_utf8(bytes)?))
.collect::<Result<Vec<String>>>()?;
builder.args(&args);
}
if self.inherit_env {
builder.inherit_env();
} else if !self.env.is_empty() {
let env = self
.env
.into_iter()
.map(|(kbytes, vbytes)| {
let k = String::from_utf8(kbytes)?;
let v = String::from_utf8(vbytes)?;
Ok((k, v))
})
.collect::<Result<Vec<(String, String)>>>()?;
builder.envs(&env);
}
match self.stdin {
WasiConfigReadPipe::None => {}
WasiConfigReadPipe::Inherit => {
builder.inherit_stdin();
}
WasiConfigReadPipe::File(file) => {
let file = tokio::fs::File::from_std(file);
let stdin_stream = wasmtime_wasi::AsyncStdinStream::new(
wasmtime_wasi::pipe::AsyncReadStream::new(file),
);
builder.stdin(stdin_stream);
}
WasiConfigReadPipe::Bytes(binary) => {
let binary = wasmtime_wasi::pipe::MemoryInputPipe::new(binary);
builder.stdin(binary);
}
};
match self.stdout {
WasiConfigWritePipe::None => {}
WasiConfigWritePipe::Inherit => {
builder.inherit_stdout();
}
WasiConfigWritePipe::File(file) => {
builder.stdout(wasmtime_wasi::OutputFile::new(file));
}
};
match self.stderr {
WasiConfigWritePipe::None => {}
WasiConfigWritePipe::Inherit => {
builder.inherit_stderr();
}
WasiConfigWritePipe::File(file) => {
builder.stderr(wasmtime_wasi::OutputFile::new(file));
}
};
for (host_path, guest_path) in self.preopen_dirs {
builder.preopened_dir(
host_path,
guest_path,
wasmtime_wasi::DirPerms::all(),
wasmtime_wasi::FilePerms::all(),
)?;
}
Ok(builder.build_p1())
pub fn into_wasi_ctx(mut self) -> Result<WasiP1Ctx> {
Ok(self.builder.build_p1())
}
}

#[no_mangle]
pub extern "C" fn wasi_config_new() -> Box<wasi_config_t> {
Box::new(wasi_config_t::default())
Box::new(wasi_config_t {
builder: WasiCtxBuilder::new(),
})
}

#[no_mangle]
pub unsafe extern "C" fn wasi_config_set_argv(
config: &mut wasi_config_t,
argc: c_int,
argc: usize,
argv: *const *const c_char,
) {
config.args = slice::from_raw_parts(argv, argc as usize)
.iter()
.map(|p| CStr::from_ptr(*p).to_bytes().to_owned())
.collect();
config.inherit_args = false;
) -> bool {
for arg in slice::from_raw_parts(argv, argc) {
let arg = match CStr::from_ptr(*arg).to_str() {
Ok(s) => s,
Err(_) => return false,
};
config.builder.arg(arg);
}
true
}

#[no_mangle]
pub extern "C" fn wasi_config_inherit_argv(config: &mut wasi_config_t) {
config.args.clear();
config.inherit_args = true;
config.builder.inherit_args();
}

#[no_mangle]
pub unsafe extern "C" fn wasi_config_set_env(
config: &mut wasi_config_t,
envc: c_int,
envc: usize,
names: *const *const c_char,
values: *const *const c_char,
) {
let names = slice::from_raw_parts(names, envc as usize);
let values = slice::from_raw_parts(values, envc as usize);
) -> bool {
let names = slice::from_raw_parts(names, envc);
let values = slice::from_raw_parts(values, envc);

config.env = names
.iter()
.map(|p| CStr::from_ptr(*p).to_bytes().to_owned())
.zip(
values
.iter()
.map(|p| CStr::from_ptr(*p).to_bytes().to_owned()),
)
.collect();
config.inherit_env = false;
for (k, v) in names.iter().zip(values) {
let k = match cstr_to_str(*k) {
Some(s) => s,
None => return false,
};
let v = match cstr_to_str(*v) {
Some(s) => s,
None => return false,
};
config.builder.env(k, v);
}
true
}

#[no_mangle]
pub extern "C" fn wasi_config_inherit_env(config: &mut wasi_config_t) {
config.env.clear();
config.inherit_env = true;
config.builder.inherit_env();
}

#[no_mangle]
Expand All @@ -195,7 +104,10 @@ pub unsafe extern "C" fn wasi_config_set_stdin_file(
None => return false,
};

config.stdin = WasiConfigReadPipe::File(file);
let file = tokio::fs::File::from_std(file);
let stdin_stream =
wasmtime_wasi::AsyncStdinStream::new(wasmtime_wasi::pipe::AsyncReadStream::new(file));
config.builder.stdin(stdin_stream);

true
}
Expand All @@ -206,13 +118,13 @@ pub unsafe extern "C" fn wasi_config_set_stdin_bytes(
binary: &mut wasm_byte_vec_t,
) {
let binary = binary.take();

config.stdin = WasiConfigReadPipe::Bytes(binary);
let binary = wasmtime_wasi::pipe::MemoryInputPipe::new(binary);
config.builder.stdin(binary);
}

#[no_mangle]
pub extern "C" fn wasi_config_inherit_stdin(config: &mut wasi_config_t) {
config.stdin = WasiConfigReadPipe::Inherit;
config.builder.inherit_stdin();
}

#[no_mangle]
Expand All @@ -225,14 +137,14 @@ pub unsafe extern "C" fn wasi_config_set_stdout_file(
None => return false,
};

config.stdout = WasiConfigWritePipe::File(file);
config.builder.stdout(wasmtime_wasi::OutputFile::new(file));

true
}

#[no_mangle]
pub extern "C" fn wasi_config_inherit_stdout(config: &mut wasi_config_t) {
config.stdout = WasiConfigWritePipe::Inherit;
config.builder.inherit_stdout();
}

#[no_mangle]
Expand All @@ -245,14 +157,14 @@ pub unsafe extern "C" fn wasi_config_set_stderr_file(
None => return false,
};

config.stderr = WasiConfigWritePipe::File(file);
config.builder.stderr(wasmtime_wasi::OutputFile::new(file));

true
}

#[no_mangle]
pub extern "C" fn wasi_config_inherit_stderr(config: &mut wasi_config_t) {
config.stderr = WasiConfigWritePipe::Inherit;
config.builder.inherit_stderr();
}

#[no_mangle]
Expand All @@ -271,9 +183,13 @@ pub unsafe extern "C" fn wasi_config_preopen_dir(
None => return false,
};

(*config)
.preopen_dirs
.push((host_path.to_owned(), guest_path.to_owned()));

true
config
.builder
.preopened_dir(
host_path,
guest_path,
wasmtime_wasi::DirPerms::all(),
wasmtime_wasi::FilePerms::all(),
)
.is_ok()
}

0 comments on commit c9d9098

Please sign in to comment.