use crate::cli::{StdinStream, StdoutStream, WasiCliCtx};1use crate::clocks::{HostMonotonicClock, HostWallClock, WasiClocksCtx};2use crate::filesystem::{Dir, WasiFilesystemCtx};3use crate::random::WasiRandomCtx;4use crate::sockets::{SocketAddrCheck, SocketAddrUse, WasiSocketsCtx};5use crate::{DirPerms, FilePerms, OpenMode};6use anyhow::Result;7use cap_rand::RngCore;8use cap_std::ambient_authority;9use std::future::Future;10use std::mem;11use std::net::SocketAddr;12use std::path::Path;13use std::pin::Pin;14use tokio::io::{stderr, stdin, stdout};1516/// Builder-style structure used to create a [`WasiCtx`].17///18/// This type is used to create a [`WasiCtx`] that is considered per-[`Store`]19/// state. The [`build`][WasiCtxBuilder::build] method is used to finish the20/// building process and produce a finalized [`WasiCtx`].21///22/// # Examples23///24/// ```25/// use wasmtime_wasi::WasiCtx;26///27/// let mut wasi = WasiCtx::builder();28/// wasi.arg("./foo.wasm");29/// wasi.arg("--help");30/// wasi.env("FOO", "bar");31///32/// let wasi: WasiCtx = wasi.build();33/// ```34///35/// [`Store`]: wasmtime::Store36#[derive(Default)]37pub struct WasiCtxBuilder {38cli: WasiCliCtx,39clocks: WasiClocksCtx,40filesystem: WasiFilesystemCtx,41random: WasiRandomCtx,42sockets: WasiSocketsCtx,43built: bool,44}4546impl WasiCtxBuilder {47/// Creates a builder for a new context with default parameters set.48///49/// The current defaults are:50///51/// * stdin is closed52/// * stdout and stderr eat all input and it doesn't go anywhere53/// * no env vars54/// * no arguments55/// * no preopens56/// * clocks use the host implementation of wall/monotonic clocks57/// * RNGs are all initialized with random state and suitable generator58/// quality to satisfy the requirements of WASI APIs.59/// * TCP/UDP are allowed but all addresses are denied by default.60/// * `wasi:sockets/ip-name-lookup` is denied by default.61///62/// These defaults can all be updated via the various builder configuration63/// methods below.64pub fn new() -> Self {65Self::default()66}6768/// Provides a custom implementation of stdin to use.69///70/// By default stdin is closed but an example of using the host's native71/// stdin looks like:72///73/// ```74/// use wasmtime_wasi::WasiCtx;75/// use wasmtime_wasi::cli::stdin;76///77/// let mut wasi = WasiCtx::builder();78/// wasi.stdin(stdin());79/// ```80///81/// Note that inheriting the process's stdin can also be done through82/// [`inherit_stdin`](WasiCtxBuilder::inherit_stdin).83pub fn stdin(&mut self, stdin: impl StdinStream + 'static) -> &mut Self {84self.cli.stdin = Box::new(stdin);85self86}8788/// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout.89pub fn stdout(&mut self, stdout: impl StdoutStream + 'static) -> &mut Self {90self.cli.stdout = Box::new(stdout);91self92}9394/// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr.95pub fn stderr(&mut self, stderr: impl StdoutStream + 'static) -> &mut Self {96self.cli.stderr = Box::new(stderr);97self98}99100/// Configures this context's stdin stream to read the host process's101/// stdin.102///103/// Note that concurrent reads of stdin can produce surprising results so104/// when using this it's typically best to have a single wasm instance in105/// the process using this.106pub fn inherit_stdin(&mut self) -> &mut Self {107self.stdin(stdin())108}109110/// Configures this context's stdout stream to write to the host process's111/// stdout.112///113/// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin)114/// multiple instances printing to stdout works well.115pub fn inherit_stdout(&mut self) -> &mut Self {116self.stdout(stdout())117}118119/// Configures this context's stderr stream to write to the host process's120/// stderr.121///122/// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin)123/// multiple instances printing to stderr works well.124pub fn inherit_stderr(&mut self) -> &mut Self {125self.stderr(stderr())126}127128/// Configures all of stdin, stdout, and stderr to be inherited from the129/// host process.130///131/// See [`inherit_stdin`](WasiCtxBuilder::inherit_stdin) for some rationale132/// on why this should only be done in situations of133/// one-instance-per-process.134pub fn inherit_stdio(&mut self) -> &mut Self {135self.inherit_stdin().inherit_stdout().inherit_stderr()136}137138/// Configures whether or not blocking operations made through this139/// `WasiCtx` are allowed to block the current thread.140///141/// WASI is currently implemented on top of the Rust142/// [Tokio](https://tokio.rs/) library. While most WASI APIs are143/// non-blocking some are instead blocking from the perspective of144/// WebAssembly. For example opening a file is a blocking operation with145/// respect to WebAssembly but it's implemented as an asynchronous operation146/// on the host. This is currently done with Tokio's147/// [`spawn_blocking`](https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html).148///149/// When WebAssembly is used in a synchronous context, for example when150/// [`Config::async_support`] is disabled, then this asynchronous operation151/// is quickly turned back into a synchronous operation with a `block_on` in152/// Rust. This switching back-and-forth between a blocking a non-blocking153/// context can have overhead, and this option exists to help alleviate this154/// overhead.155///156/// This option indicates that for WASI functions that are blocking from the157/// perspective of WebAssembly it's ok to block the native thread as well.158/// This means that this back-and-forth between async and sync won't happen159/// and instead blocking operations are performed on-thread (such as opening160/// a file). This can improve the performance of WASI operations when async161/// support is disabled.162///163/// [`Config::async_support`]: https://docs.rs/wasmtime/latest/wasmtime/struct.Config.html#method.async_support164pub fn allow_blocking_current_thread(&mut self, enable: bool) -> &mut Self {165self.filesystem.allow_blocking_current_thread = enable;166self167}168169/// Appends multiple environment variables at once for this builder.170///171/// All environment variables are appended to the list of environment172/// variables that this builder will configure.173///174/// At this time environment variables are not deduplicated and if the same175/// key is set twice then the guest will see two entries for the same key.176///177/// # Examples178///179/// ```180/// use wasmtime_wasi::WasiCtxBuilder;181///182/// let mut wasi = WasiCtxBuilder::new();183/// wasi.envs(&[184/// ("FOO", "bar"),185/// ("HOME", "/somewhere"),186/// ]);187/// ```188pub fn envs(&mut self, env: &[(impl AsRef<str>, impl AsRef<str>)]) -> &mut Self {189self.cli.environment.extend(190env.iter()191.map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().to_owned())),192);193self194}195196/// Appends a single environment variable for this builder.197///198/// At this time environment variables are not deduplicated and if the same199/// key is set twice then the guest will see two entries for the same key.200///201/// # Examples202///203/// ```204/// use wasmtime_wasi::WasiCtxBuilder;205///206/// let mut wasi = WasiCtxBuilder::new();207/// wasi.env("FOO", "bar");208/// ```209pub fn env(&mut self, k: impl AsRef<str>, v: impl AsRef<str>) -> &mut Self {210self.cli211.environment212.push((k.as_ref().to_owned(), v.as_ref().to_owned()));213self214}215216/// Configures all environment variables to be inherited from the calling217/// process into this configuration.218///219/// This will use [`envs`](WasiCtxBuilder::envs) to append all host-defined220/// environment variables.221pub fn inherit_env(&mut self) -> &mut Self {222self.envs(&std::env::vars().collect::<Vec<(String, String)>>())223}224225/// Appends a list of arguments to the argument array to pass to wasm.226pub fn args(&mut self, args: &[impl AsRef<str>]) -> &mut Self {227self.cli228.arguments229.extend(args.iter().map(|a| a.as_ref().to_owned()));230self231}232233/// Appends a single argument to get passed to wasm.234pub fn arg(&mut self, arg: impl AsRef<str>) -> &mut Self {235self.cli.arguments.push(arg.as_ref().to_owned());236self237}238239/// Appends all host process arguments to the list of arguments to get240/// passed to wasm.241pub fn inherit_args(&mut self) -> &mut Self {242self.args(&std::env::args().collect::<Vec<String>>())243}244245/// Configures a "preopened directory" to be available to WebAssembly.246///247/// By default WebAssembly does not have access to the filesystem because248/// there are no preopened directories. All filesystem operations, such as249/// opening a file, are done through a preexisting handle. This means that250/// to provide WebAssembly access to a directory it must be configured251/// through this API.252///253/// WASI will also prevent access outside of files provided here. For254/// example `..` can't be used to traverse up from the `host_path` provided here255/// to the containing directory.256///257/// * `host_path` - a path to a directory on the host to open and make258/// accessible to WebAssembly. Note that the name of this directory in the259/// guest is configured with `guest_path` below.260/// * `guest_path` - the name of the preopened directory from WebAssembly's261/// perspective. Note that this does not need to match the host's name for262/// the directory.263/// * `dir_perms` - this is the permissions that wasm will have to operate on264/// `guest_path`. This can be used, for example, to provide readonly access to a265/// directory.266/// * `file_perms` - similar to `dir_perms` but corresponds to the maximum set267/// of permissions that can be used for any file in this directory.268///269/// # Errors270///271/// This method will return an error if `host_path` cannot be opened.272///273/// # Examples274///275/// ```276/// use wasmtime_wasi::WasiCtxBuilder;277/// use wasmtime_wasi::{DirPerms, FilePerms};278///279/// # fn main() {}280/// # fn foo() -> wasmtime::Result<()> {281/// let mut wasi = WasiCtxBuilder::new();282///283/// // Make `./host-directory` available in the guest as `.`284/// wasi.preopened_dir("./host-directory", ".", DirPerms::all(), FilePerms::all());285///286/// // Make `./readonly` available in the guest as `./ro`287/// wasi.preopened_dir("./readonly", "./ro", DirPerms::READ, FilePerms::READ);288/// # Ok(())289/// # }290/// ```291pub fn preopened_dir(292&mut self,293host_path: impl AsRef<Path>,294guest_path: impl AsRef<str>,295dir_perms: DirPerms,296file_perms: FilePerms,297) -> Result<&mut Self> {298let dir = cap_std::fs::Dir::open_ambient_dir(host_path.as_ref(), ambient_authority())?;299let mut open_mode = OpenMode::empty();300if dir_perms.contains(DirPerms::READ) {301open_mode |= OpenMode::READ;302}303if dir_perms.contains(DirPerms::MUTATE) {304open_mode |= OpenMode::WRITE;305}306self.filesystem.preopens.push((307Dir::new(308dir,309dir_perms,310file_perms,311open_mode,312self.filesystem.allow_blocking_current_thread,313),314guest_path.as_ref().to_owned(),315));316Ok(self)317}318319/// Set the generator for the `wasi:random/random` number generator to the320/// custom generator specified.321///322/// Note that contexts have a default RNG configured which is a suitable323/// generator for WASI and is configured with a random seed per-context.324///325/// Guest code may rely on this random number generator to produce fresh326/// unpredictable random data in order to maintain its security invariants,327/// and ideally should use the insecure random API otherwise, so using any328/// prerecorded or otherwise predictable data may compromise security.329pub fn secure_random(&mut self, random: impl RngCore + Send + 'static) -> &mut Self {330self.random.random = Box::new(random);331self332}333334/// Configures the generator for `wasi:random/insecure`.335///336/// The `insecure_random` generator provided will be used for all randomness337/// requested by the `wasi:random/insecure` interface.338pub fn insecure_random(&mut self, insecure_random: impl RngCore + Send + 'static) -> &mut Self {339self.random.insecure_random = Box::new(insecure_random);340self341}342343/// Configures the seed to be returned from `wasi:random/insecure-seed` to344/// the specified custom value.345///346/// By default this number is randomly generated when a builder is created.347pub fn insecure_random_seed(&mut self, insecure_random_seed: u128) -> &mut Self {348self.random.insecure_random_seed = insecure_random_seed;349self350}351352/// Configures `wasi:clocks/wall-clock` to use the `clock` specified.353///354/// By default the host's wall clock is used.355pub fn wall_clock(&mut self, clock: impl HostWallClock + 'static) -> &mut Self {356self.clocks.wall_clock = Box::new(clock);357self358}359360/// Configures `wasi:clocks/monotonic-clock` to use the `clock` specified.361///362/// By default the host's monotonic clock is used.363pub fn monotonic_clock(&mut self, clock: impl HostMonotonicClock + 'static) -> &mut Self {364self.clocks.monotonic_clock = Box::new(clock);365self366}367368/// Allow all network addresses accessible to the host.369///370/// This method will inherit all network addresses meaning that any address371/// can be bound by the guest or connected to by the guest using any372/// protocol.373///374/// See also [`WasiCtxBuilder::socket_addr_check`].375pub fn inherit_network(&mut self) -> &mut Self {376self.socket_addr_check(|_, _| Box::pin(async { true }))377}378379/// A check that will be called for each socket address that is used.380///381/// Returning `true` will permit socket connections to the `SocketAddr`,382/// while returning `false` will reject the connection.383pub fn socket_addr_check<F>(&mut self, check: F) -> &mut Self384where385F: Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>386+ Send387+ Sync388+ 'static,389{390self.sockets.socket_addr_check = SocketAddrCheck::new(check);391self392}393394/// Allow usage of `wasi:sockets/ip-name-lookup`395///396/// By default this is disabled.397pub fn allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self {398self.sockets.allowed_network_uses.ip_name_lookup = enable;399self400}401402/// Allow usage of UDP.403///404/// This is enabled by default, but can be disabled if UDP should be blanket405/// disabled.406pub fn allow_udp(&mut self, enable: bool) -> &mut Self {407self.sockets.allowed_network_uses.udp = enable;408self409}410411/// Allow usage of TCP412///413/// This is enabled by default, but can be disabled if TCP should be blanket414/// disabled.415pub fn allow_tcp(&mut self, enable: bool) -> &mut Self {416self.sockets.allowed_network_uses.tcp = enable;417self418}419420/// Uses the configured context so far to construct the final [`WasiCtx`].421///422/// Note that each `WasiCtxBuilder` can only be used to "build" once, and423/// calling this method twice will panic.424///425/// # Panics426///427/// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be428/// used to create only a single [`WasiCtx`]. Repeated usage of this method429/// is not allowed and should use a second builder instead.430pub fn build(&mut self) -> WasiCtx {431assert!(!self.built);432433let Self {434cli,435clocks,436filesystem,437random,438sockets,439built: _,440} = mem::replace(self, Self::new());441self.built = true;442443WasiCtx {444cli,445clocks,446filesystem,447random,448sockets,449}450}451/// Builds a WASIp1 context instead of a [`WasiCtx`].452///453/// This method is the same as [`build`](WasiCtxBuilder::build) but it454/// creates a [`WasiP1Ctx`] instead. This is intended for use with the455/// [`p1`] module of this crate456///457/// [`WasiP1Ctx`]: crate::p1::WasiP1Ctx458/// [`p1`]: crate::p1459///460/// # Panics461///462/// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be463/// used to create only a single [`WasiCtx`] or [`WasiP1Ctx`]. Repeated464/// usage of this method is not allowed and should use a second builder465/// instead.466#[cfg(feature = "p1")]467pub fn build_p1(&mut self) -> crate::p1::WasiP1Ctx {468let wasi = self.build();469crate::p1::WasiP1Ctx::new(wasi)470}471}472473/// Per-[`Store`] state which holds state necessary to implement WASI from this474/// crate.475///476/// This structure is created through [`WasiCtxBuilder`] and is stored within477/// the `T` of [`Store<T>`][`Store`]. Access to the structure is provided478/// through the [`WasiView`](crate::WasiView) trait as an implementation on `T`.479///480/// Note that this structure itself does not have any accessors, it's here for481/// internal use within the `wasmtime-wasi` crate's implementation of482/// bindgen-generated traits.483///484/// [`Store`]: wasmtime::Store485///486/// # Example487///488/// ```489/// use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxView, WasiView, WasiCtxBuilder};490///491/// struct MyState {492/// ctx: WasiCtx,493/// table: ResourceTable,494/// }495///496/// impl WasiView for MyState {497/// fn ctx(&mut self) -> WasiCtxView<'_> {498/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }499/// }500/// }501///502/// impl MyState {503/// fn new() -> MyState {504/// let mut wasi = WasiCtxBuilder::new();505/// wasi.arg("./foo.wasm");506/// wasi.arg("--help");507/// wasi.env("FOO", "bar");508///509/// MyState {510/// ctx: wasi.build(),511/// table: ResourceTable::new(),512/// }513/// }514/// }515/// ```516#[derive(Default)]517pub struct WasiCtx {518pub(crate) cli: WasiCliCtx,519pub(crate) clocks: WasiClocksCtx,520pub(crate) filesystem: WasiFilesystemCtx,521pub(crate) random: WasiRandomCtx,522pub(crate) sockets: WasiSocketsCtx,523}524525impl WasiCtx {526/// Convenience function for calling [`WasiCtxBuilder::new`].527pub fn builder() -> WasiCtxBuilder {528WasiCtxBuilder::new()529}530531/// Returns access to the underlying [`WasiRandomCtx`].532pub fn random(&mut self) -> &mut WasiRandomCtx {533&mut self.random534}535536/// Returns access to the underlying [`WasiClocksCtx`].537pub fn clocks(&mut self) -> &mut WasiClocksCtx {538&mut self.clocks539}540541/// Returns access to the underlying [`WasiFilesystemCtx`].542pub fn filesystem(&mut self) -> &mut WasiFilesystemCtx {543&mut self.filesystem544}545546/// Returns access to the underlying [`WasiCliCtx`].547pub fn cli(&mut self) -> &mut WasiCliCtx {548&mut self.cli549}550551/// Returns access to the underlying [`WasiSocketsCtx`].552pub fn sockets(&mut self) -> &mut WasiSocketsCtx {553&mut self.sockets554}555}556557558