1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
//! `wasmtime-wasi` now supports using multiple snapshots to interface to the
//! same `WasiCtx`!
//!
//! `wasmtime_wasi::Wasi::new(&Store, WasiCtx)` is a struct which owns your
//! `WasiCtx` and provides linkage to every available snapshot.
//!
//! Individual snapshots are available through
//! `wasmtime_wasi::snapshots::preview_{0, 1}::Wasi::new(&Store, Rc<RefCell<WasiCtx>>)`.
#![warn(clippy::cast_sign_loss)]
#[cfg(feature = "preview2")]
pub mod preview2;
pub use wasi_common::{Error, I32Exit, WasiCtx, WasiDir, WasiFile};
/// Re-export the commonly used wasi-cap-std-sync crate here. This saves
/// consumers of this library from having to keep additional dependencies
/// in sync.
#[cfg(feature = "sync")]
pub mod sync {
pub use wasi_cap_std_sync::*;
super::define_wasi!(block_on);
}
/// Sync mode is the "default" of this crate, so we also export it at the top
/// level.
#[cfg(feature = "sync")]
pub use sync::*;
/// Re-export the wasi-tokio crate here. This saves consumers of this library from having
/// to keep additional dependencies in sync.
#[cfg(feature = "tokio")]
pub mod tokio {
pub use wasi_tokio::*;
super::define_wasi!(async T: Send);
}
// The only difference between these definitions for sync vs async is whether
// the wasmtime::Funcs generated are async (& therefore need an async Store and an executor to run)
// or whether they have an internal "dummy executor" that expects the implementation of all
// the async funcs to poll to Ready immediately.
#[doc(hidden)]
#[macro_export]
macro_rules! define_wasi {
($async_mode:tt $($bounds:tt)*) => {
use wasmtime::Linker;
pub fn add_to_linker<T, U>(
linker: &mut Linker<T>,
get_cx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
) -> anyhow::Result<()>
where U: Send
+ wasi_common::snapshots::preview_0::wasi_unstable::WasiUnstable
+ wasi_common::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1,
$($bounds)*
{
snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(linker, get_cx)?;
snapshots::preview_0::add_wasi_unstable_to_linker(linker, get_cx)?;
Ok(())
}
pub mod snapshots {
pub mod preview_1 {
wiggle::wasmtime_integration!({
// The wiggle code to integrate with lives here:
target: wasi_common::snapshots::preview_1,
// This must be the same witx document as used above. This should be ensured by
// the `WASI_ROOT` env variable, which is set in wasi-common's `build.rs`.
witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"],
errors: { errno => trappable Error },
$async_mode: *
});
}
pub mod preview_0 {
wiggle::wasmtime_integration!({
// The wiggle code to integrate with lives here:
target: wasi_common::snapshots::preview_0,
// This must be the same witx document as used above. This should be ensured by
// the `WASI_ROOT` env variable, which is set in wasi-common's `build.rs`.
witx: ["$WASI_ROOT/phases/old/snapshot_0/witx/wasi_unstable.witx"],
errors: { errno => trappable Error },
$async_mode: *
});
}
}
}
}
/// Exit the process with a conventional OS error code as long as Wasmtime
/// understands the error. If the error is not an `I32Exit` or `Trap`, return
/// the error back to the caller for it to decide what to do.
///
/// Note: this function is designed for usage where it is acceptable for
/// Wasmtime failures to terminate the parent process, such as in the Wasmtime
/// CLI; this would not be suitable for use in multi-tenant embeddings.
#[cfg(feature = "exit")]
pub fn maybe_exit_on_error(e: anyhow::Error) -> anyhow::Error {
use std::process;
use wasmtime::Trap;
// If a specific WASI error code was requested then that's
// forwarded through to the process here without printing any
// extra error information.
let code = e
.downcast_ref::<I32Exit>()
.map(|e| e.0)
.or_else(|| e.downcast_ref::<preview2::I32Exit>().map(|e| e.0));
if let Some(exit) = code {
// Print the error message in the usual way.
// On Windows, exit status 3 indicates an abort (see below),
// so return 1 indicating a non-zero status to avoid ambiguity.
if cfg!(windows) && exit >= 3 {
process::exit(1);
}
process::exit(exit);
}
// If the program exited because of a trap, return an error code
// to the outside environment indicating a more severe problem
// than a simple failure.
if e.is::<Trap>() {
eprintln!("Error: {:?}", e);
if cfg!(unix) {
// On Unix, return the error code of an abort.
process::exit(128 + libc::SIGABRT);
} else if cfg!(windows) {
// On Windows, return 3.
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/abort?view=vs-2019
process::exit(3);
}
}
e
}