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