wasi_common/
ctx.rs

1use crate::clocks::WasiClocks;
2use crate::dir::{DirEntry, WasiDir};
3use crate::file::{FileAccessMode, FileEntry, WasiFile};
4use crate::sched::WasiSched;
5use crate::string_array::StringArray;
6use crate::table::Table;
7use crate::{Error, StringArrayError};
8use cap_rand::RngCore;
9use std::ops::Deref;
10use std::path::{Path, PathBuf};
11use std::sync::{Arc, Mutex};
12
13/// An `Arc`-wrapper around the wasi-common context to allow mutable access to
14/// the file descriptor table. This wrapper is only necessary due to the
15/// signature of `fd_fdstat_set_flags`; if that changes, there are a variety of
16/// improvements that can be made (TODO:
17/// <https://github.com/bytecodealliance/wasmtime/issues/5643)>.
18#[derive(Clone)]
19pub struct WasiCtx(Arc<WasiCtxInner>);
20
21pub struct WasiCtxInner {
22    pub args: StringArray,
23    pub env: StringArray,
24    // TODO: this mutex should not be necessary, it forces threads to serialize
25    // their access to randomness unnecessarily
26    // (https://github.com/bytecodealliance/wasmtime/issues/5660).
27    pub random: Mutex<Box<dyn RngCore + Send + Sync>>,
28    pub clocks: WasiClocks,
29    pub sched: Box<dyn WasiSched>,
30    pub table: Table,
31}
32
33impl WasiCtx {
34    pub fn new(
35        random: Box<dyn RngCore + Send + Sync>,
36        clocks: WasiClocks,
37        sched: Box<dyn WasiSched>,
38        table: Table,
39    ) -> Self {
40        let s = WasiCtx(Arc::new(WasiCtxInner {
41            args: StringArray::new(),
42            env: StringArray::new(),
43            random: Mutex::new(random),
44            clocks,
45            sched,
46            table,
47        }));
48        s.set_stdin(Box::new(crate::pipe::ReadPipe::new(std::io::empty())));
49        s.set_stdout(Box::new(crate::pipe::WritePipe::new(std::io::sink())));
50        s.set_stderr(Box::new(crate::pipe::WritePipe::new(std::io::sink())));
51        s
52    }
53
54    pub fn insert_file(&self, fd: u32, file: Box<dyn WasiFile>, access_mode: FileAccessMode) {
55        self.table()
56            .insert_at(fd, Arc::new(FileEntry::new(file, access_mode)));
57    }
58
59    pub fn push_file(
60        &self,
61        file: Box<dyn WasiFile>,
62        access_mode: FileAccessMode,
63    ) -> Result<u32, Error> {
64        self.table()
65            .push(Arc::new(FileEntry::new(file, access_mode)))
66    }
67
68    pub fn insert_dir(&self, fd: u32, dir: Box<dyn WasiDir>, path: PathBuf) {
69        self.table()
70            .insert_at(fd, Arc::new(DirEntry::new(Some(path), dir)));
71    }
72
73    pub fn push_dir(&self, dir: Box<dyn WasiDir>, path: PathBuf) -> Result<u32, Error> {
74        self.table().push(Arc::new(DirEntry::new(Some(path), dir)))
75    }
76
77    pub fn table(&self) -> &Table {
78        &self.table
79    }
80
81    pub fn table_mut(&mut self) -> Option<&mut Table> {
82        Arc::get_mut(&mut self.0).map(|c| &mut c.table)
83    }
84
85    pub fn push_arg(&mut self, arg: &str) -> Result<(), StringArrayError> {
86        let s = Arc::get_mut(&mut self.0).expect(
87            "`push_arg` should only be used during initialization before the context is cloned",
88        );
89        s.args.push(arg.to_owned())
90    }
91
92    pub fn push_env(&mut self, var: &str, value: &str) -> Result<(), StringArrayError> {
93        let s = Arc::get_mut(&mut self.0).expect(
94            "`push_env` should only be used during initialization before the context is cloned",
95        );
96        s.env.push(format!("{var}={value}"))?;
97        Ok(())
98    }
99
100    pub fn set_stdin(&self, f: Box<dyn WasiFile>) {
101        self.insert_file(0, f, FileAccessMode::READ);
102    }
103
104    pub fn set_stdout(&self, f: Box<dyn WasiFile>) {
105        self.insert_file(1, f, FileAccessMode::WRITE);
106    }
107
108    pub fn set_stderr(&self, f: Box<dyn WasiFile>) {
109        self.insert_file(2, f, FileAccessMode::WRITE);
110    }
111
112    pub fn push_preopened_dir(
113        &self,
114        dir: Box<dyn WasiDir>,
115        path: impl AsRef<Path>,
116    ) -> Result<(), Error> {
117        self.table()
118            .push(Arc::new(DirEntry::new(Some(path.as_ref().to_owned()), dir)))?;
119        Ok(())
120    }
121}
122
123impl Deref for WasiCtx {
124    type Target = WasiCtxInner;
125    fn deref(&self) -> &Self::Target {
126        &self.0
127    }
128}