1 //! # wasi-common 2 //! 3 //! This is Wasmtime's legacy implementation of WASI 0.1 (Preview 1). The 4 //! Wasmtime maintainers suggest all users upgrade to the implementation 5 //! of WASI 0.1 and 0.2 provided by the `wasmtime-wasi` crate. This 6 //! implementation remains in the wasmtime tree because it is required to use 7 //! the `wasmtime-wasi-threads` crate, an implementation of the `wasi-threads` 8 //! proposal which is not compatible with WASI 0.2. 9 //! 10 //! In addition to integration with Wasmtime, this implementation may be used 11 //! by other runtimes by disabling the `wasmtime` feature on this crate. 12 //! 13 //! ## The `WasiFile` and `WasiDir` traits 14 //! 15 //! The WASI specification only defines one `handle` type, `fd`, on which all 16 //! operations on both files and directories (aka dirfds) are defined. We 17 //! believe this is a design mistake, and are architecting wasi-common to make 18 //! this straightforward to correct in future snapshots of WASI. Wasi-common 19 //! internally treats files and directories as two distinct resource types in 20 //! the table - `Box<dyn WasiFile>` and `Box<dyn WasiDir>`. The snapshot 0 and 21 //! 1 interfaces via `fd` will attempt to downcast a table element to one or 22 //! both of these interfaces depending on what is appropriate - e.g. 23 //! `fd_close` operates on both files and directories, `fd_read` only operates 24 //! on files, and `fd_readdir` only operates on directories. 25 26 //! The `WasiFile` and `WasiDir` traits are defined by `wasi-common` in terms 27 //! of types defined directly in the crate's source code (I decided it should 28 //! NOT those generated by the `wiggle` proc macros, see snapshot architecture 29 //! below), as well as the `cap_std::time` family of types. And, importantly, 30 //! `wasi-common` itself provides no implementation of `WasiDir`, and only two 31 //! trivial implementations of `WasiFile` on the `crate::pipe::{ReadPipe, 32 //! WritePipe}` types, which in turn just delegate to `std::io::{Read, 33 //! Write}`. In order for `wasi-common` to access the local filesystem at all, 34 //! you need to provide `WasiFile` and `WasiDir` impls through either the new 35 //! `wasi-cap-std-sync` crate found at `crates/wasi-common/cap-std-sync` - see 36 //! the section on that crate below - or by providing your own implementation 37 //! from elsewhere. 38 //! 39 //! This design makes it possible for `wasi-common` embedders to statically 40 //! reason about access to the local filesystem by examining what impls are 41 //! linked into an application. We found that this separation of concerns also 42 //! makes it pretty enjoyable to write alternative implementations, e.g. a 43 //! virtual filesystem. 44 //! 45 //! Implementations of the `WasiFile` and `WasiDir` traits are provided 46 //! for synchronous embeddings (i.e. Config::async_support(false)) in 47 //! `wasi_common::sync` and for Tokio embeddings in `wasi_common::tokio`. 48 //! 49 //! ## Traits for the rest of WASI's features 50 //! 51 //! Other aspects of a WASI implementation are not yet considered resources 52 //! and accessed by `handle`. We plan to correct this design deficiency in 53 //! WASI in the future, but for now we have designed the following traits to 54 //! provide embedders with the same sort of implementation flexibility they 55 //! get with WasiFile/WasiDir: 56 //! 57 //! * Timekeeping: `WasiSystemClock` and `WasiMonotonicClock` provide the two 58 //! interfaces for a clock. `WasiSystemClock` represents time as a 59 //! `cap_std::time::SystemTime`, and `WasiMonotonicClock` represents time as 60 //! `cap_std::time::Instant`. * Randomness: we re-use the `cap_rand::RngCore` 61 //! trait to represent a randomness source. A trivial `Deterministic` impl is 62 //! provided. * Scheduling: The `WasiSched` trait abstracts over the 63 //! `sched_yield` and `poll_oneoff` functions. 64 //! 65 //! Users can provide implementations of each of these interfaces to the 66 //! `WasiCtx::builder(...)` function. The 67 //! `wasi_cap_std_sync::WasiCtxBuilder::new()` function uses this public 68 //! interface to plug in its own implementations of each of these resources. 69 70 #![warn(clippy::cast_sign_loss)] 71 #![cfg_attr(docsrs, feature(doc_cfg))] 72 73 pub mod clocks; 74 mod ctx; 75 pub mod dir; 76 mod error; 77 pub mod file; 78 pub mod pipe; 79 pub mod random; 80 pub mod sched; 81 pub mod snapshots; 82 mod string_array; 83 #[cfg_attr(docsrs, doc(cfg(feature = "sync")))] 84 #[cfg(feature = "sync")] 85 pub mod sync; 86 pub mod table; 87 #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] 88 #[cfg(feature = "tokio")] 89 pub mod tokio; 90 91 pub use cap_rand::RngCore; 92 pub use clocks::{SystemTimeSpec, WasiClocks, WasiMonotonicClock, WasiSystemClock}; 93 pub use ctx::WasiCtx; 94 pub use dir::WasiDir; 95 pub use error::{Error, ErrorExt, I32Exit}; 96 pub use file::WasiFile; 97 pub use sched::{Poll, WasiSched}; 98 pub use string_array::{StringArray, StringArrayError}; 99 pub use table::Table; 100 101 // The only difference between these definitions for sync vs async is whether 102 // the wasmtime::Funcs generated are async (& therefore need an async Store and an executor to run) 103 // or whether they have an internal "dummy executor" that expects the implementation of all 104 // the async funcs to poll to Ready immediately. 105 #[cfg(feature = "wasmtime")] 106 #[doc(hidden)] 107 #[macro_export] 108 macro_rules! define_wasi { 109 ($async_mode:tt $($bounds:tt)*) => { 110 111 use wasmtime::Linker; 112 113 pub fn add_to_linker<T, U>( 114 linker: &mut Linker<T>, 115 get_cx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, 116 ) -> anyhow::Result<()> 117 where U: Send 118 + crate::snapshots::preview_0::wasi_unstable::WasiUnstable 119 + crate::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1, 120 T: 'static, 121 $($bounds)* 122 { 123 snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(linker, get_cx)?; 124 snapshots::preview_0::add_wasi_unstable_to_linker(linker, get_cx)?; 125 Ok(()) 126 } 127 128 pub mod snapshots { 129 pub mod preview_1 { 130 wiggle::wasmtime_integration!({ 131 // The wiggle code to integrate with lives here: 132 target: crate::snapshots::preview_1, 133 witx: ["witx/preview1/wasi_snapshot_preview1.witx"], 134 errors: { errno => trappable Error }, 135 $async_mode: * 136 }); 137 } 138 pub mod preview_0 { 139 wiggle::wasmtime_integration!({ 140 // The wiggle code to integrate with lives here: 141 target: crate::snapshots::preview_0, 142 witx: ["witx/preview0/wasi_unstable.witx"], 143 errors: { errno => trappable Error }, 144 $async_mode: * 145 }); 146 } 147 } 148 }} 149 150 /// Exit the process with a conventional OS error code as long as Wasmtime 151 /// understands the error. If the error is not an `I32Exit` or `Trap`, return 152 /// the error back to the caller for it to decide what to do. 153 /// 154 /// Note: this function is designed for usage where it is acceptable for 155 /// Wasmtime failures to terminate the parent process, such as in the Wasmtime 156 /// CLI; this would not be suitable for use in multi-tenant embeddings. 157 #[cfg_attr(docsrs, doc(cfg(feature = "exit")))] 158 #[cfg(feature = "exit")] 159 pub fn maybe_exit_on_error(e: anyhow::Error) -> anyhow::Error { 160 use std::process; 161 use wasmtime::Trap; 162 163 // If a specific WASI error code was requested then that's 164 // forwarded through to the process here without printing any 165 // extra error information. 166 if let Some(exit) = e.downcast_ref::<crate::I32Exit>() { 167 process::exit(exit.0); 168 } 169 170 // If the program exited because of a trap, return an error code 171 // to the outside environment indicating a more severe problem 172 // than a simple failure. 173 if e.is::<Trap>() { 174 eprintln!("Error: {e:?}"); 175 176 if cfg!(unix) { 177 // On Unix, return the error code of an abort. 178 process::exit(128 + libc::SIGABRT); 179 } else if cfg!(windows) { 180 // On Windows, return 3. 181 // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/abort?view=vs-2019 182 process::exit(3); 183 } 184 } 185 186 e 187 } 188