wasmtime_wasi/
lib.rs

1//! # Wasmtime's WASI Implementation
2//!
3//!
4//! This crate provides a Wasmtime host implementation of WASI 0.2 (aka WASIp2
5//! aka Preview 2) and WASI 0.1 (aka WASIp1 aka Preview 1). WASI is implemented
6//! with the Rust crates [`tokio`] and [`cap-std`] primarily, meaning that
7//! operations are implemented in terms of their native platform equivalents by
8//! default.
9//!
10//! For components and WASIp2, continue reading below. For WASIp1 and core
11//! modules, see the [`preview1`] module documentation.
12//!
13//! # WASIp2 interfaces
14//!
15//! This crate contains implementations of the following interfaces:
16//!
17//! * [`wasi:cli/environment`]
18//! * [`wasi:cli/exit`]
19//! * [`wasi:cli/stderr`]
20//! * [`wasi:cli/stdin`]
21//! * [`wasi:cli/stdout`]
22//! * [`wasi:cli/terminal-input`]
23//! * [`wasi:cli/terminal-output`]
24//! * [`wasi:cli/terminal-stderr`]
25//! * [`wasi:cli/terminal-stdin`]
26//! * [`wasi:cli/terminal-stdout`]
27//! * [`wasi:clocks/monotonic-clock`]
28//! * [`wasi:clocks/wall-clock`]
29//! * [`wasi:filesystem/preopens`]
30//! * [`wasi:filesystem/types`]
31//! * [`wasi:random/insecure-seed`]
32//! * [`wasi:random/insecure`]
33//! * [`wasi:random/random`]
34//! * [`wasi:sockets/instance-network`]
35//! * [`wasi:sockets/ip-name-lookup`]
36//! * [`wasi:sockets/network`]
37//! * [`wasi:sockets/tcp-create-socket`]
38//! * [`wasi:sockets/tcp`]
39//! * [`wasi:sockets/udp-create-socket`]
40//! * [`wasi:sockets/udp`]
41//!
42//! All traits are implemented in terms of a [`WasiView`] trait which provides
43//! access to [`WasiCtx`], which defines the configuration for WASI.
44//! The [`WasiView`] trait imples [`IoView`], which provides access to a common
45//! [`ResourceTable`], which owns all host-defined component model resources.
46//!
47//! The [`wasmtime-wasi-io`] crate contains implementations of the
48//! following interfaces, and this crate reuses those implementations:
49//!
50//! * [`wasi:io/error`]
51//! * [`wasi:io/poll`]
52//! * [`wasi:io/streams`]
53//!
54//! These traits are implemented in terms of a [`IoView`] trait, which only
55//! provides access to a common [`ResourceTable`]. All aspects of
56//! `wasmtime-wasi-io` that are used by this crate are re-exported. Unless you
57//! are implementing other host functionality that needs to interact with the
58//! WASI scheduler and don't want to use other functionality provided by
59//! `wasmtime-wasi`, you don't need to take a direct dependency on
60//! `wasmtime-wasi-io`.
61//!
62//! # Generated Bindings
63//!
64//! This crate uses [`wasmtime::component::bindgen!`] to generate bindings for
65//! all WASI interfaces. Raw bindings are available in the [`bindings`] module
66//! of this crate. Downstream users can either implement these traits themselves
67//! or you can use the built-in implementations in this crate for
68//! `WasiImpl<T: WasiView>`.
69//!
70//! # The `WasiView` trait
71//!
72//! This crate's implementation of WASI is done in terms of an implementation of
73//! [`WasiView`]. This trait provides a "view" into WASI-related state that is
74//! contained within a [`Store<T>`](wasmtime::Store). [`WasiView`] implies the
75//! [`IoView`] trait, which provides access to common [`ResourceTable`] which
76//! owns all host-implemented component model resources.
77//!
78//! For all of the generated bindings in this crate (Host traits),
79//! implementations are provided looking like:
80//!
81//! ```
82//! # use wasmtime_wasi::WasiImpl;
83//! # trait WasiView {}
84//! # mod bindings { pub mod wasi { pub trait Host {} } }
85//! impl<T: WasiView> bindings::wasi::Host for WasiImpl<T> {
86//!     // ...
87//! }
88//! ```
89//!
90//! The [`add_to_linker_sync`] and [`add_to_linker_async`] function then require
91//! that `T: WasiView` with [`Linker<T>`](wasmtime::component::Linker).
92//!
93//! To implement the [`WasiView`] and [`IoView`] trait you will first select a
94//! `T` to put in `Store<T>` (typically, by defining your own struct).
95//! Somewhere within `T` you'll store:
96//!
97//! * [`ResourceTable`] - created through default constructors.
98//! * [`WasiCtx`] - created through [`WasiCtxBuilder`].
99//!
100//! You'll then write implementations of the [`IoView`] and [`WasiView`]
101//! traits to access those items in your `T`. For example:
102//! ```
103//! use wasmtime::component::ResourceTable;
104//! use wasmtime_wasi::{WasiCtx, IoView, WasiView};
105//! struct MyCtx {
106//!     table: ResourceTable,
107//!     wasi: WasiCtx,
108//! }
109//! impl IoView for MyCtx {
110//!     fn table(&mut self) -> &mut ResourceTable {
111//!         &mut self.table
112//!     }
113//! }
114//! impl WasiView for MyCtx {
115//!     fn ctx(&mut self) -> &mut WasiCtx {
116//!         &mut self.wasi
117//!     }
118//! }
119//!
120//! ```
121//!
122//! # Async and Sync
123//!
124//! As of WASI0.2, WASI functions are not blocking from WebAssembly's point of
125//! view: a WebAssembly call into these functions returns when they are
126//! complete.
127//!
128//! This crate provides an implementation of those functions in the host,
129//! where for some functions, it is appropriate to implement them using
130//! async Rust and the Tokio executor, so that the host implementation can be
131//! nonblocking when Wasmtime's [`Config::async_support`][async] is set.
132//! Synchronous wrappers are provided for all async implementations, which
133//! creates a private Tokio executor.
134//!
135//! Users can choose between these modes of implementation using variants
136//! of the add_to_linker functions:
137//!
138//! * For non-async users (the default of `Config`), use [`add_to_linker_sync`].
139//! * For async users, use [`add_to_linker_async`].
140//!
141//! Note that bindings are generated once for async and once for sync. Most
142//! interfaces do not change, however, so only interfaces with blocking
143//! functions have bindings generated twice. Bindings are organized as:
144//!
145//! * [`bindings`] - default location of all bindings, blocking functions are
146//!   `async`
147//! * [`bindings::sync`] - blocking interfaces have synchronous versions here.
148//!
149//! # Crate-specific traits
150//!
151//! This crate's default implementation of WASI bindings to native primitives
152//! for the platform that it is compiled for. For example opening a TCP socket
153//! uses the native platform to open a TCP socket (so long as [`WasiCtxBuilder`]
154//! allows it). There are a few important traits, however, that are specific to
155//! this crate.
156//!
157//! * [`InputStream`] and [`OutputStream`] - these are the host traits
158//!   behind the WASI `input-stream` and `output-stream` types in the
159//!   `wasi:io/streams` interface. These enable embedders to build their own
160//!   custom stream and insert them into a [`ResourceTable`] (as a boxed trait
161//!   object, see [`DynInputStream`] and [`DynOutputStream`]) to be used from
162//!   wasm.
163//!
164//! * [`Pollable`] - this trait enables building arbitrary logic to get hooked
165//!   into a `pollable` resource from `wasi:io/poll`. A pollable resource is
166//!   created through the [`subscribe`] function.
167//!
168//! * [`HostWallClock`] and [`HostMonotonicClock`] are used in conjunction with
169//!   [`WasiCtxBuilder::wall_clock`] and [`WasiCtxBuilder::monotonic_clock`] if
170//!   the defaults host's clock should not be used.
171//!
172//! * [`StdinStream`] and [`StdoutStream`] are used to provide custom
173//!   stdin/stdout streams if they're not inherited (or null, which is the
174//!   default).
175//!
176//! These traits enable embedders to customize small portions of WASI interfaces
177//! provided while still providing all other interfaces.
178//!
179//! # Examples
180//!
181//! Usage of this crate is done through a few steps to get everything hooked up:
182//!
183//! 1. First implement [`IoView`] and [`WasiView`] for your type which is the
184//!    `T` in `Store<T>`.
185//! 2. Add WASI interfaces to a `wasmtime::component::Linker<T>`. This is either
186//!    done through top-level functions like [`add_to_linker_sync`] or through
187//!    individual `add_to_linker` functions in generated bindings throughout
188//!    this crate.
189//! 3. Create a [`WasiCtx`] for each `Store<T>` through [`WasiCtxBuilder`]. Each
190//!    WASI context is "null" or "empty" by default, so items must be explicitly
191//!    added to get accessed by wasm (such as env vars or program arguments).
192//! 4. Use the previous `Linker<T>` to instantiate a `Component` within a
193//!    `Store<T>`.
194//!
195//! For examples see each of [`WasiView`], [`WasiCtx`], [`WasiCtxBuilder`],
196//! [`add_to_linker_sync`], and [`bindings::Command`].
197//!
198//! [`wasmtime::component::bindgen!`]: https://docs.rs/wasmtime/latest/wasmtime/component/macro.bindgen.html
199//! [`tokio`]: https://crates.io/crates/tokio
200//! [`cap-std`]: https://crates.io/crates/cap-std
201//! [`wasmtime-wasi-io`]: https://crates.io/crates/wasmtime-wasi-io
202//! [`wasi:cli/environment`]: bindings::cli::environment::Host
203//! [`wasi:cli/exit`]: bindings::cli::exit::Host
204//! [`wasi:cli/stderr`]: bindings::cli::stderr::Host
205//! [`wasi:cli/stdin`]: bindings::cli::stdin::Host
206//! [`wasi:cli/stdout`]: bindings::cli::stdout::Host
207//! [`wasi:cli/terminal-input`]: bindings::cli::terminal_input::Host
208//! [`wasi:cli/terminal-output`]: bindings::cli::terminal_output::Host
209//! [`wasi:cli/terminal-stdin`]: bindings::cli::terminal_stdin::Host
210//! [`wasi:cli/terminal-stdout`]: bindings::cli::terminal_stdout::Host
211//! [`wasi:cli/terminal-stderr`]: bindings::cli::terminal_stderr::Host
212//! [`wasi:clocks/monotonic-clock`]: bindings::clocks::monotonic_clock::Host
213//! [`wasi:clocks/wall-clock`]: bindings::clocks::wall_clock::Host
214//! [`wasi:filesystem/preopens`]: bindings::filesystem::preopens::Host
215//! [`wasi:filesystem/types`]: bindings::filesystem::types::Host
216//! [`wasi:io/error`]: wasmtime_wasi_io::bindings::wasi::io::error::Host
217//! [`wasi:io/poll`]: wasmtime_wasi_io::bindings::wasi::io::poll::Host
218//! [`wasi:io/streams`]: wasmtime_wasi_io::bindings::wasi::io::streams::Host
219//! [`wasi:random/insecure-seed`]: bindings::random::insecure_seed::Host
220//! [`wasi:random/insecure`]: bindings::random::insecure::Host
221//! [`wasi:random/random`]: bindings::random::random::Host
222//! [`wasi:sockets/instance-network`]: bindings::sockets::instance_network::Host
223//! [`wasi:sockets/ip-name-lookup`]: bindings::sockets::ip_name_lookup::Host
224//! [`wasi:sockets/network`]: bindings::sockets::network::Host
225//! [`wasi:sockets/tcp-create-socket`]: bindings::sockets::tcp_create_socket::Host
226//! [`wasi:sockets/tcp`]: bindings::sockets::tcp::Host
227//! [`wasi:sockets/udp-create-socket`]: bindings::sockets::udp_create_socket::Host
228//! [`wasi:sockets/udp`]: bindings::sockets::udp::Host
229//! [async]: https://docs.rs/wasmtime/latest/wasmtime/struct.Config.html#method.async_support
230//! [`ResourceTable`]: wasmtime::component::ResourceTable
231
232#![cfg_attr(docsrs, feature(doc_auto_cfg))]
233#![expect(clippy::allow_attributes_without_reason, reason = "crate not migrated")]
234
235use wasmtime::component::Linker;
236
237pub mod bindings;
238mod clocks;
239mod ctx;
240mod error;
241mod filesystem;
242mod host;
243mod ip_name_lookup;
244mod network;
245pub mod pipe;
246mod poll;
247#[cfg(feature = "preview1")]
248pub mod preview0;
249#[cfg(feature = "preview1")]
250pub mod preview1;
251mod random;
252pub mod runtime;
253mod stdio;
254mod tcp;
255mod udp;
256mod view;
257mod write_stream;
258
259pub use self::clocks::{HostMonotonicClock, HostWallClock};
260pub use self::ctx::{WasiCtx, WasiCtxBuilder};
261pub use self::error::{I32Exit, TrappableError};
262pub use self::filesystem::{DirPerms, FileInputStream, FilePerms, FsError, FsResult};
263pub use self::network::{Network, SocketAddrUse, SocketError, SocketResult};
264pub use self::random::{thread_rng, Deterministic};
265pub use self::stdio::{
266    stderr, stdin, stdout, AsyncStdinStream, AsyncStdoutStream, IsATTY, OutputFile, Stderr, Stdin,
267    StdinStream, Stdout, StdoutStream,
268};
269pub use self::view::{WasiImpl, WasiView};
270#[doc(no_inline)]
271pub use async_trait::async_trait;
272#[doc(no_inline)]
273pub use cap_fs_ext::SystemTimeSpec;
274#[doc(no_inline)]
275pub use cap_rand::RngCore;
276#[doc(no_inline)]
277pub use wasmtime::component::{ResourceTable, ResourceTableError};
278// These contents of wasmtime-wasi-io are re-exported by this crate for compatibility:
279// they were originally defined in this crate before being factored out, and many
280// users of this crate depend on them at these names.
281pub use wasmtime_wasi_io::poll::{subscribe, DynFuture, DynPollable, MakeFuture, Pollable};
282pub use wasmtime_wasi_io::streams::{
283    DynInputStream, DynOutputStream, InputStream, OutputStream, StreamError, StreamResult,
284};
285pub use wasmtime_wasi_io::{IoImpl, IoView};
286
287/// Add all WASI interfaces from this crate into the `linker` provided.
288///
289/// This function will add the `async` variant of all interfaces into the
290/// [`Linker`] provided. By `async` this means that this function is only
291/// compatible with [`Config::async_support(true)`][async]. For embeddings with
292/// async support disabled see [`add_to_linker_sync`] instead.
293///
294/// This function will add all interfaces implemented by this crate to the
295/// [`Linker`], which corresponds to the `wasi:cli/imports` world supported by
296/// this crate.
297///
298/// [async]: wasmtime::Config::async_support
299///
300/// # Example
301///
302/// ```
303/// use wasmtime::{Engine, Result, Store, Config};
304/// use wasmtime::component::{ResourceTable, Linker};
305/// use wasmtime_wasi::{IoView, WasiCtx, WasiView, WasiCtxBuilder};
306///
307/// fn main() -> Result<()> {
308///     let mut config = Config::new();
309///     config.async_support(true);
310///     let engine = Engine::new(&config)?;
311///
312///     let mut linker = Linker::<MyState>::new(&engine);
313///     wasmtime_wasi::add_to_linker_async(&mut linker)?;
314///     // ... add any further functionality to `linker` if desired ...
315///
316///     let mut builder = WasiCtxBuilder::new();
317///
318///     // ... configure `builder` more to add env vars, args, etc ...
319///
320///     let mut store = Store::new(
321///         &engine,
322///         MyState {
323///             ctx: builder.build(),
324///             table: ResourceTable::new(),
325///         },
326///     );
327///
328///     // ... use `linker` to instantiate within `store` ...
329///
330///     Ok(())
331/// }
332///
333/// struct MyState {
334///     ctx: WasiCtx,
335///     table: ResourceTable,
336/// }
337///
338/// impl IoView for MyState {
339///     fn table(&mut self) -> &mut ResourceTable { &mut self.table }
340/// }
341/// impl WasiView for MyState {
342///     fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx }
343/// }
344/// ```
345pub fn add_to_linker_async<T: WasiView>(linker: &mut Linker<T>) -> anyhow::Result<()> {
346    let options = crate::bindings::LinkOptions::default();
347    add_to_linker_with_options_async(linker, &options)
348}
349
350/// Similar to [`add_to_linker_async`], but with the ability to enable unstable features.
351pub fn add_to_linker_with_options_async<T: WasiView>(
352    linker: &mut Linker<T>,
353    options: &crate::bindings::LinkOptions,
354) -> anyhow::Result<()> {
355    let l = linker;
356    wasmtime_wasi_io::add_to_linker_async(l)?;
357
358    let closure = type_annotate::<T, _>(|t| WasiImpl(IoImpl(t)));
359
360    crate::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
361    crate::bindings::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
362    crate::bindings::filesystem::types::add_to_linker_get_host(l, closure)?;
363    crate::bindings::filesystem::preopens::add_to_linker_get_host(l, closure)?;
364    crate::bindings::random::random::add_to_linker_get_host(l, closure)?;
365    crate::bindings::random::insecure::add_to_linker_get_host(l, closure)?;
366    crate::bindings::random::insecure_seed::add_to_linker_get_host(l, closure)?;
367    crate::bindings::cli::exit::add_to_linker_get_host(l, &options.into(), closure)?;
368    crate::bindings::cli::environment::add_to_linker_get_host(l, closure)?;
369    crate::bindings::cli::stdin::add_to_linker_get_host(l, closure)?;
370    crate::bindings::cli::stdout::add_to_linker_get_host(l, closure)?;
371    crate::bindings::cli::stderr::add_to_linker_get_host(l, closure)?;
372    crate::bindings::cli::terminal_input::add_to_linker_get_host(l, closure)?;
373    crate::bindings::cli::terminal_output::add_to_linker_get_host(l, closure)?;
374    crate::bindings::cli::terminal_stdin::add_to_linker_get_host(l, closure)?;
375    crate::bindings::cli::terminal_stdout::add_to_linker_get_host(l, closure)?;
376    crate::bindings::cli::terminal_stderr::add_to_linker_get_host(l, closure)?;
377    crate::bindings::sockets::tcp::add_to_linker_get_host(l, closure)?;
378    crate::bindings::sockets::tcp_create_socket::add_to_linker_get_host(l, closure)?;
379    crate::bindings::sockets::udp::add_to_linker_get_host(l, closure)?;
380    crate::bindings::sockets::udp_create_socket::add_to_linker_get_host(l, closure)?;
381    crate::bindings::sockets::instance_network::add_to_linker_get_host(l, closure)?;
382    crate::bindings::sockets::network::add_to_linker_get_host(l, &options.into(), closure)?;
383    crate::bindings::sockets::ip_name_lookup::add_to_linker_get_host(l, closure)?;
384    Ok(())
385}
386
387/// Add all WASI interfaces from this crate into the `linker` provided.
388///
389/// This function will add the synchronous variant of all interfaces into the
390/// [`Linker`] provided. By synchronous this means that this function is only
391/// compatible with [`Config::async_support(false)`][async]. For embeddings
392/// with async support enabled see [`add_to_linker_async`] instead.
393///
394/// This function will add all interfaces implemented by this crate to the
395/// [`Linker`], which corresponds to the `wasi:cli/imports` world supported by
396/// this crate.
397///
398/// [async]: wasmtime::Config::async_support
399///
400/// # Example
401///
402/// ```
403/// use wasmtime::{Engine, Result, Store, Config};
404/// use wasmtime::component::{ResourceTable, Linker};
405/// use wasmtime_wasi::{IoView, WasiCtx, WasiView, WasiCtxBuilder};
406///
407/// fn main() -> Result<()> {
408///     let engine = Engine::default();
409///
410///     let mut linker = Linker::<MyState>::new(&engine);
411///     wasmtime_wasi::add_to_linker_sync(&mut linker)?;
412///     // ... add any further functionality to `linker` if desired ...
413///
414///     let mut builder = WasiCtxBuilder::new();
415///
416///     // ... configure `builder` more to add env vars, args, etc ...
417///
418///     let mut store = Store::new(
419///         &engine,
420///         MyState {
421///             ctx: builder.build(),
422///             table: ResourceTable::new(),
423///         },
424///     );
425///
426///     // ... use `linker` to instantiate within `store` ...
427///
428///     Ok(())
429/// }
430///
431/// struct MyState {
432///     ctx: WasiCtx,
433///     table: ResourceTable,
434/// }
435/// impl IoView for MyState {
436///     fn table(&mut self) -> &mut ResourceTable { &mut self.table }
437/// }
438/// impl WasiView for MyState {
439///     fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx }
440/// }
441/// ```
442pub fn add_to_linker_sync<T: WasiView>(
443    linker: &mut wasmtime::component::Linker<T>,
444) -> anyhow::Result<()> {
445    let options = crate::bindings::sync::LinkOptions::default();
446    add_to_linker_with_options_sync(linker, &options)
447}
448
449/// Similar to [`add_to_linker_sync`], but with the ability to enable unstable features.
450pub fn add_to_linker_with_options_sync<T: WasiView>(
451    linker: &mut wasmtime::component::Linker<T>,
452    options: &crate::bindings::sync::LinkOptions,
453) -> anyhow::Result<()> {
454    let l = linker;
455    let io_closure = io_type_annotate::<T, _>(|t| IoImpl(t));
456    wasmtime_wasi_io::bindings::wasi::io::error::add_to_linker_get_host(l, io_closure)?;
457
458    crate::bindings::sync::io::poll::add_to_linker_get_host(l, io_closure)?;
459    crate::bindings::sync::io::streams::add_to_linker_get_host(l, io_closure)?;
460
461    let closure = type_annotate::<T, _>(|t| WasiImpl(IoImpl(t)));
462
463    crate::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
464    crate::bindings::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
465    crate::bindings::sync::filesystem::types::add_to_linker_get_host(l, closure)?;
466    crate::bindings::filesystem::preopens::add_to_linker_get_host(l, closure)?;
467    crate::bindings::random::random::add_to_linker_get_host(l, closure)?;
468    crate::bindings::random::insecure::add_to_linker_get_host(l, closure)?;
469    crate::bindings::random::insecure_seed::add_to_linker_get_host(l, closure)?;
470    crate::bindings::cli::exit::add_to_linker_get_host(l, &options.into(), closure)?;
471    crate::bindings::cli::environment::add_to_linker_get_host(l, closure)?;
472    crate::bindings::cli::stdin::add_to_linker_get_host(l, closure)?;
473    crate::bindings::cli::stdout::add_to_linker_get_host(l, closure)?;
474    crate::bindings::cli::stderr::add_to_linker_get_host(l, closure)?;
475    crate::bindings::cli::terminal_input::add_to_linker_get_host(l, closure)?;
476    crate::bindings::cli::terminal_output::add_to_linker_get_host(l, closure)?;
477    crate::bindings::cli::terminal_stdin::add_to_linker_get_host(l, closure)?;
478    crate::bindings::cli::terminal_stdout::add_to_linker_get_host(l, closure)?;
479    crate::bindings::cli::terminal_stderr::add_to_linker_get_host(l, closure)?;
480    crate::bindings::sync::sockets::tcp::add_to_linker_get_host(l, closure)?;
481    crate::bindings::sockets::tcp_create_socket::add_to_linker_get_host(l, closure)?;
482    crate::bindings::sync::sockets::udp::add_to_linker_get_host(l, closure)?;
483    crate::bindings::sockets::udp_create_socket::add_to_linker_get_host(l, closure)?;
484    crate::bindings::sockets::instance_network::add_to_linker_get_host(l, closure)?;
485    crate::bindings::sockets::network::add_to_linker_get_host(l, &options.into(), closure)?;
486    crate::bindings::sockets::ip_name_lookup::add_to_linker_get_host(l, closure)?;
487    Ok(())
488}
489
490// NB: workaround some rustc inference - a future refactoring may make this
491// obsolete.
492fn io_type_annotate<T: IoView, F>(val: F) -> F
493where
494    F: Fn(&mut T) -> IoImpl<&mut T>,
495{
496    val
497}
498fn type_annotate<T: WasiView, F>(val: F) -> F
499where
500    F: Fn(&mut T) -> WasiImpl<&mut T>,
501{
502    val
503}