Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wasi/src/ctx.rs
1691 views
1
use crate::cli::{StdinStream, StdoutStream, WasiCliCtx};
2
use crate::clocks::{HostMonotonicClock, HostWallClock, WasiClocksCtx};
3
use crate::filesystem::{Dir, WasiFilesystemCtx};
4
use crate::random::WasiRandomCtx;
5
use crate::sockets::{SocketAddrCheck, SocketAddrUse, WasiSocketsCtx};
6
use crate::{DirPerms, FilePerms, OpenMode};
7
use anyhow::Result;
8
use cap_rand::RngCore;
9
use cap_std::ambient_authority;
10
use std::future::Future;
11
use std::mem;
12
use std::net::SocketAddr;
13
use std::path::Path;
14
use std::pin::Pin;
15
use tokio::io::{stderr, stdin, stdout};
16
17
/// Builder-style structure used to create a [`WasiCtx`].
18
///
19
/// This type is used to create a [`WasiCtx`] that is considered per-[`Store`]
20
/// state. The [`build`][WasiCtxBuilder::build] method is used to finish the
21
/// building process and produce a finalized [`WasiCtx`].
22
///
23
/// # Examples
24
///
25
/// ```
26
/// use wasmtime_wasi::WasiCtx;
27
///
28
/// let mut wasi = WasiCtx::builder();
29
/// wasi.arg("./foo.wasm");
30
/// wasi.arg("--help");
31
/// wasi.env("FOO", "bar");
32
///
33
/// let wasi: WasiCtx = wasi.build();
34
/// ```
35
///
36
/// [`Store`]: wasmtime::Store
37
#[derive(Default)]
38
pub struct WasiCtxBuilder {
39
cli: WasiCliCtx,
40
clocks: WasiClocksCtx,
41
filesystem: WasiFilesystemCtx,
42
random: WasiRandomCtx,
43
sockets: WasiSocketsCtx,
44
built: bool,
45
}
46
47
impl WasiCtxBuilder {
48
/// Creates a builder for a new context with default parameters set.
49
///
50
/// The current defaults are:
51
///
52
/// * stdin is closed
53
/// * stdout and stderr eat all input and it doesn't go anywhere
54
/// * no env vars
55
/// * no arguments
56
/// * no preopens
57
/// * clocks use the host implementation of wall/monotonic clocks
58
/// * RNGs are all initialized with random state and suitable generator
59
/// quality to satisfy the requirements of WASI APIs.
60
/// * TCP/UDP are allowed but all addresses are denied by default.
61
/// * `wasi:sockets/ip-name-lookup` is denied by default.
62
///
63
/// These defaults can all be updated via the various builder configuration
64
/// methods below.
65
pub fn new() -> Self {
66
Self::default()
67
}
68
69
/// Provides a custom implementation of stdin to use.
70
///
71
/// By default stdin is closed but an example of using the host's native
72
/// stdin looks like:
73
///
74
/// ```
75
/// use wasmtime_wasi::WasiCtx;
76
/// use wasmtime_wasi::cli::stdin;
77
///
78
/// let mut wasi = WasiCtx::builder();
79
/// wasi.stdin(stdin());
80
/// ```
81
///
82
/// Note that inheriting the process's stdin can also be done through
83
/// [`inherit_stdin`](WasiCtxBuilder::inherit_stdin).
84
pub fn stdin(&mut self, stdin: impl StdinStream + 'static) -> &mut Self {
85
self.cli.stdin = Box::new(stdin);
86
self
87
}
88
89
/// Same as [`stdin`](WasiCtxBuilder::stdin), but for stdout.
90
pub fn stdout(&mut self, stdout: impl StdoutStream + 'static) -> &mut Self {
91
self.cli.stdout = Box::new(stdout);
92
self
93
}
94
95
/// Same as [`stdin`](WasiCtxBuilder::stdin), but for stderr.
96
pub fn stderr(&mut self, stderr: impl StdoutStream + 'static) -> &mut Self {
97
self.cli.stderr = Box::new(stderr);
98
self
99
}
100
101
/// Configures this context's stdin stream to read the host process's
102
/// stdin.
103
///
104
/// Note that concurrent reads of stdin can produce surprising results so
105
/// when using this it's typically best to have a single wasm instance in
106
/// the process using this.
107
pub fn inherit_stdin(&mut self) -> &mut Self {
108
self.stdin(stdin())
109
}
110
111
/// Configures this context's stdout stream to write to the host process's
112
/// stdout.
113
///
114
/// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin)
115
/// multiple instances printing to stdout works well.
116
pub fn inherit_stdout(&mut self) -> &mut Self {
117
self.stdout(stdout())
118
}
119
120
/// Configures this context's stderr stream to write to the host process's
121
/// stderr.
122
///
123
/// Note that unlike [`inherit_stdin`](WasiCtxBuilder::inherit_stdin)
124
/// multiple instances printing to stderr works well.
125
pub fn inherit_stderr(&mut self) -> &mut Self {
126
self.stderr(stderr())
127
}
128
129
/// Configures all of stdin, stdout, and stderr to be inherited from the
130
/// host process.
131
///
132
/// See [`inherit_stdin`](WasiCtxBuilder::inherit_stdin) for some rationale
133
/// on why this should only be done in situations of
134
/// one-instance-per-process.
135
pub fn inherit_stdio(&mut self) -> &mut Self {
136
self.inherit_stdin().inherit_stdout().inherit_stderr()
137
}
138
139
/// Configures whether or not blocking operations made through this
140
/// `WasiCtx` are allowed to block the current thread.
141
///
142
/// WASI is currently implemented on top of the Rust
143
/// [Tokio](https://tokio.rs/) library. While most WASI APIs are
144
/// non-blocking some are instead blocking from the perspective of
145
/// WebAssembly. For example opening a file is a blocking operation with
146
/// respect to WebAssembly but it's implemented as an asynchronous operation
147
/// on the host. This is currently done with Tokio's
148
/// [`spawn_blocking`](https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html).
149
///
150
/// When WebAssembly is used in a synchronous context, for example when
151
/// [`Config::async_support`] is disabled, then this asynchronous operation
152
/// is quickly turned back into a synchronous operation with a `block_on` in
153
/// Rust. This switching back-and-forth between a blocking a non-blocking
154
/// context can have overhead, and this option exists to help alleviate this
155
/// overhead.
156
///
157
/// This option indicates that for WASI functions that are blocking from the
158
/// perspective of WebAssembly it's ok to block the native thread as well.
159
/// This means that this back-and-forth between async and sync won't happen
160
/// and instead blocking operations are performed on-thread (such as opening
161
/// a file). This can improve the performance of WASI operations when async
162
/// support is disabled.
163
///
164
/// [`Config::async_support`]: https://docs.rs/wasmtime/latest/wasmtime/struct.Config.html#method.async_support
165
pub fn allow_blocking_current_thread(&mut self, enable: bool) -> &mut Self {
166
self.filesystem.allow_blocking_current_thread = enable;
167
self
168
}
169
170
/// Appends multiple environment variables at once for this builder.
171
///
172
/// All environment variables are appended to the list of environment
173
/// variables that this builder will configure.
174
///
175
/// At this time environment variables are not deduplicated and if the same
176
/// key is set twice then the guest will see two entries for the same key.
177
///
178
/// # Examples
179
///
180
/// ```
181
/// use wasmtime_wasi::WasiCtxBuilder;
182
///
183
/// let mut wasi = WasiCtxBuilder::new();
184
/// wasi.envs(&[
185
/// ("FOO", "bar"),
186
/// ("HOME", "/somewhere"),
187
/// ]);
188
/// ```
189
pub fn envs(&mut self, env: &[(impl AsRef<str>, impl AsRef<str>)]) -> &mut Self {
190
self.cli.environment.extend(
191
env.iter()
192
.map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().to_owned())),
193
);
194
self
195
}
196
197
/// Appends a single environment variable for this builder.
198
///
199
/// At this time environment variables are not deduplicated and if the same
200
/// key is set twice then the guest will see two entries for the same key.
201
///
202
/// # Examples
203
///
204
/// ```
205
/// use wasmtime_wasi::WasiCtxBuilder;
206
///
207
/// let mut wasi = WasiCtxBuilder::new();
208
/// wasi.env("FOO", "bar");
209
/// ```
210
pub fn env(&mut self, k: impl AsRef<str>, v: impl AsRef<str>) -> &mut Self {
211
self.cli
212
.environment
213
.push((k.as_ref().to_owned(), v.as_ref().to_owned()));
214
self
215
}
216
217
/// Configures all environment variables to be inherited from the calling
218
/// process into this configuration.
219
///
220
/// This will use [`envs`](WasiCtxBuilder::envs) to append all host-defined
221
/// environment variables.
222
pub fn inherit_env(&mut self) -> &mut Self {
223
self.envs(&std::env::vars().collect::<Vec<(String, String)>>())
224
}
225
226
/// Appends a list of arguments to the argument array to pass to wasm.
227
pub fn args(&mut self, args: &[impl AsRef<str>]) -> &mut Self {
228
self.cli
229
.arguments
230
.extend(args.iter().map(|a| a.as_ref().to_owned()));
231
self
232
}
233
234
/// Appends a single argument to get passed to wasm.
235
pub fn arg(&mut self, arg: impl AsRef<str>) -> &mut Self {
236
self.cli.arguments.push(arg.as_ref().to_owned());
237
self
238
}
239
240
/// Appends all host process arguments to the list of arguments to get
241
/// passed to wasm.
242
pub fn inherit_args(&mut self) -> &mut Self {
243
self.args(&std::env::args().collect::<Vec<String>>())
244
}
245
246
/// Configures a "preopened directory" to be available to WebAssembly.
247
///
248
/// By default WebAssembly does not have access to the filesystem because
249
/// there are no preopened directories. All filesystem operations, such as
250
/// opening a file, are done through a preexisting handle. This means that
251
/// to provide WebAssembly access to a directory it must be configured
252
/// through this API.
253
///
254
/// WASI will also prevent access outside of files provided here. For
255
/// example `..` can't be used to traverse up from the `host_path` provided here
256
/// to the containing directory.
257
///
258
/// * `host_path` - a path to a directory on the host to open and make
259
/// accessible to WebAssembly. Note that the name of this directory in the
260
/// guest is configured with `guest_path` below.
261
/// * `guest_path` - the name of the preopened directory from WebAssembly's
262
/// perspective. Note that this does not need to match the host's name for
263
/// the directory.
264
/// * `dir_perms` - this is the permissions that wasm will have to operate on
265
/// `guest_path`. This can be used, for example, to provide readonly access to a
266
/// directory.
267
/// * `file_perms` - similar to `dir_perms` but corresponds to the maximum set
268
/// of permissions that can be used for any file in this directory.
269
///
270
/// # Errors
271
///
272
/// This method will return an error if `host_path` cannot be opened.
273
///
274
/// # Examples
275
///
276
/// ```
277
/// use wasmtime_wasi::WasiCtxBuilder;
278
/// use wasmtime_wasi::{DirPerms, FilePerms};
279
///
280
/// # fn main() {}
281
/// # fn foo() -> wasmtime::Result<()> {
282
/// let mut wasi = WasiCtxBuilder::new();
283
///
284
/// // Make `./host-directory` available in the guest as `.`
285
/// wasi.preopened_dir("./host-directory", ".", DirPerms::all(), FilePerms::all());
286
///
287
/// // Make `./readonly` available in the guest as `./ro`
288
/// wasi.preopened_dir("./readonly", "./ro", DirPerms::READ, FilePerms::READ);
289
/// # Ok(())
290
/// # }
291
/// ```
292
pub fn preopened_dir(
293
&mut self,
294
host_path: impl AsRef<Path>,
295
guest_path: impl AsRef<str>,
296
dir_perms: DirPerms,
297
file_perms: FilePerms,
298
) -> Result<&mut Self> {
299
let dir = cap_std::fs::Dir::open_ambient_dir(host_path.as_ref(), ambient_authority())?;
300
let mut open_mode = OpenMode::empty();
301
if dir_perms.contains(DirPerms::READ) {
302
open_mode |= OpenMode::READ;
303
}
304
if dir_perms.contains(DirPerms::MUTATE) {
305
open_mode |= OpenMode::WRITE;
306
}
307
self.filesystem.preopens.push((
308
Dir::new(
309
dir,
310
dir_perms,
311
file_perms,
312
open_mode,
313
self.filesystem.allow_blocking_current_thread,
314
),
315
guest_path.as_ref().to_owned(),
316
));
317
Ok(self)
318
}
319
320
/// Set the generator for the `wasi:random/random` number generator to the
321
/// custom generator specified.
322
///
323
/// Note that contexts have a default RNG configured which is a suitable
324
/// generator for WASI and is configured with a random seed per-context.
325
///
326
/// Guest code may rely on this random number generator to produce fresh
327
/// unpredictable random data in order to maintain its security invariants,
328
/// and ideally should use the insecure random API otherwise, so using any
329
/// prerecorded or otherwise predictable data may compromise security.
330
pub fn secure_random(&mut self, random: impl RngCore + Send + 'static) -> &mut Self {
331
self.random.random = Box::new(random);
332
self
333
}
334
335
/// Configures the generator for `wasi:random/insecure`.
336
///
337
/// The `insecure_random` generator provided will be used for all randomness
338
/// requested by the `wasi:random/insecure` interface.
339
pub fn insecure_random(&mut self, insecure_random: impl RngCore + Send + 'static) -> &mut Self {
340
self.random.insecure_random = Box::new(insecure_random);
341
self
342
}
343
344
/// Configures the seed to be returned from `wasi:random/insecure-seed` to
345
/// the specified custom value.
346
///
347
/// By default this number is randomly generated when a builder is created.
348
pub fn insecure_random_seed(&mut self, insecure_random_seed: u128) -> &mut Self {
349
self.random.insecure_random_seed = insecure_random_seed;
350
self
351
}
352
353
/// Configures `wasi:clocks/wall-clock` to use the `clock` specified.
354
///
355
/// By default the host's wall clock is used.
356
pub fn wall_clock(&mut self, clock: impl HostWallClock + 'static) -> &mut Self {
357
self.clocks.wall_clock = Box::new(clock);
358
self
359
}
360
361
/// Configures `wasi:clocks/monotonic-clock` to use the `clock` specified.
362
///
363
/// By default the host's monotonic clock is used.
364
pub fn monotonic_clock(&mut self, clock: impl HostMonotonicClock + 'static) -> &mut Self {
365
self.clocks.monotonic_clock = Box::new(clock);
366
self
367
}
368
369
/// Allow all network addresses accessible to the host.
370
///
371
/// This method will inherit all network addresses meaning that any address
372
/// can be bound by the guest or connected to by the guest using any
373
/// protocol.
374
///
375
/// See also [`WasiCtxBuilder::socket_addr_check`].
376
pub fn inherit_network(&mut self) -> &mut Self {
377
self.socket_addr_check(|_, _| Box::pin(async { true }))
378
}
379
380
/// A check that will be called for each socket address that is used.
381
///
382
/// Returning `true` will permit socket connections to the `SocketAddr`,
383
/// while returning `false` will reject the connection.
384
pub fn socket_addr_check<F>(&mut self, check: F) -> &mut Self
385
where
386
F: Fn(SocketAddr, SocketAddrUse) -> Pin<Box<dyn Future<Output = bool> + Send + Sync>>
387
+ Send
388
+ Sync
389
+ 'static,
390
{
391
self.sockets.socket_addr_check = SocketAddrCheck::new(check);
392
self
393
}
394
395
/// Allow usage of `wasi:sockets/ip-name-lookup`
396
///
397
/// By default this is disabled.
398
pub fn allow_ip_name_lookup(&mut self, enable: bool) -> &mut Self {
399
self.sockets.allowed_network_uses.ip_name_lookup = enable;
400
self
401
}
402
403
/// Allow usage of UDP.
404
///
405
/// This is enabled by default, but can be disabled if UDP should be blanket
406
/// disabled.
407
pub fn allow_udp(&mut self, enable: bool) -> &mut Self {
408
self.sockets.allowed_network_uses.udp = enable;
409
self
410
}
411
412
/// Allow usage of TCP
413
///
414
/// This is enabled by default, but can be disabled if TCP should be blanket
415
/// disabled.
416
pub fn allow_tcp(&mut self, enable: bool) -> &mut Self {
417
self.sockets.allowed_network_uses.tcp = enable;
418
self
419
}
420
421
/// Uses the configured context so far to construct the final [`WasiCtx`].
422
///
423
/// Note that each `WasiCtxBuilder` can only be used to "build" once, and
424
/// calling this method twice will panic.
425
///
426
/// # Panics
427
///
428
/// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
429
/// used to create only a single [`WasiCtx`]. Repeated usage of this method
430
/// is not allowed and should use a second builder instead.
431
pub fn build(&mut self) -> WasiCtx {
432
assert!(!self.built);
433
434
let Self {
435
cli,
436
clocks,
437
filesystem,
438
random,
439
sockets,
440
built: _,
441
} = mem::replace(self, Self::new());
442
self.built = true;
443
444
WasiCtx {
445
cli,
446
clocks,
447
filesystem,
448
random,
449
sockets,
450
}
451
}
452
/// Builds a WASIp1 context instead of a [`WasiCtx`].
453
///
454
/// This method is the same as [`build`](WasiCtxBuilder::build) but it
455
/// creates a [`WasiP1Ctx`] instead. This is intended for use with the
456
/// [`p1`] module of this crate
457
///
458
/// [`WasiP1Ctx`]: crate::p1::WasiP1Ctx
459
/// [`p1`]: crate::p1
460
///
461
/// # Panics
462
///
463
/// Panics if this method is called twice. Each [`WasiCtxBuilder`] can be
464
/// used to create only a single [`WasiCtx`] or [`WasiP1Ctx`]. Repeated
465
/// usage of this method is not allowed and should use a second builder
466
/// instead.
467
#[cfg(feature = "p1")]
468
pub fn build_p1(&mut self) -> crate::p1::WasiP1Ctx {
469
let wasi = self.build();
470
crate::p1::WasiP1Ctx::new(wasi)
471
}
472
}
473
474
/// Per-[`Store`] state which holds state necessary to implement WASI from this
475
/// crate.
476
///
477
/// This structure is created through [`WasiCtxBuilder`] and is stored within
478
/// the `T` of [`Store<T>`][`Store`]. Access to the structure is provided
479
/// through the [`WasiView`](crate::WasiView) trait as an implementation on `T`.
480
///
481
/// Note that this structure itself does not have any accessors, it's here for
482
/// internal use within the `wasmtime-wasi` crate's implementation of
483
/// bindgen-generated traits.
484
///
485
/// [`Store`]: wasmtime::Store
486
///
487
/// # Example
488
///
489
/// ```
490
/// use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxView, WasiView, WasiCtxBuilder};
491
///
492
/// struct MyState {
493
/// ctx: WasiCtx,
494
/// table: ResourceTable,
495
/// }
496
///
497
/// impl WasiView for MyState {
498
/// fn ctx(&mut self) -> WasiCtxView<'_> {
499
/// WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }
500
/// }
501
/// }
502
///
503
/// impl MyState {
504
/// fn new() -> MyState {
505
/// let mut wasi = WasiCtxBuilder::new();
506
/// wasi.arg("./foo.wasm");
507
/// wasi.arg("--help");
508
/// wasi.env("FOO", "bar");
509
///
510
/// MyState {
511
/// ctx: wasi.build(),
512
/// table: ResourceTable::new(),
513
/// }
514
/// }
515
/// }
516
/// ```
517
#[derive(Default)]
518
pub struct WasiCtx {
519
pub(crate) cli: WasiCliCtx,
520
pub(crate) clocks: WasiClocksCtx,
521
pub(crate) filesystem: WasiFilesystemCtx,
522
pub(crate) random: WasiRandomCtx,
523
pub(crate) sockets: WasiSocketsCtx,
524
}
525
526
impl WasiCtx {
527
/// Convenience function for calling [`WasiCtxBuilder::new`].
528
pub fn builder() -> WasiCtxBuilder {
529
WasiCtxBuilder::new()
530
}
531
532
/// Returns access to the underlying [`WasiRandomCtx`].
533
pub fn random(&mut self) -> &mut WasiRandomCtx {
534
&mut self.random
535
}
536
537
/// Returns access to the underlying [`WasiClocksCtx`].
538
pub fn clocks(&mut self) -> &mut WasiClocksCtx {
539
&mut self.clocks
540
}
541
542
/// Returns access to the underlying [`WasiFilesystemCtx`].
543
pub fn filesystem(&mut self) -> &mut WasiFilesystemCtx {
544
&mut self.filesystem
545
}
546
547
/// Returns access to the underlying [`WasiCliCtx`].
548
pub fn cli(&mut self) -> &mut WasiCliCtx {
549
&mut self.cli
550
}
551
552
/// Returns access to the underlying [`WasiSocketsCtx`].
553
pub fn sockets(&mut self) -> &mut WasiSocketsCtx {
554
&mut self.sockets
555
}
556
}
557
558