1 use crate::clocks::WasiClocks;
2 use crate::dir::{DirEntry, WasiDir};
3 use crate::file::{FileAccessMode, FileEntry, WasiFile};
4 use crate::sched::WasiSched;
5 use crate::string_array::StringArray;
6 use crate::table::Table;
7 use crate::{Error, StringArrayError};
8 use cap_rand::RngCore;
9 use std::ops::Deref;
10 use std::path::{Path, PathBuf};
11 use 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)]
19 pub struct WasiCtx(Arc<WasiCtxInner>);
20 
21 pub 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 
33 impl WasiCtx {
new( random: Box<dyn RngCore + Send + Sync>, clocks: WasiClocks, sched: Box<dyn WasiSched>, table: Table, ) -> Self34     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 
insert_file(&self, fd: u32, file: Box<dyn WasiFile>, access_mode: FileAccessMode)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 
push_file( &self, file: Box<dyn WasiFile>, access_mode: FileAccessMode, ) -> Result<u32, Error>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 
insert_dir(&self, fd: u32, dir: Box<dyn WasiDir>, path: PathBuf)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 
push_dir(&self, dir: Box<dyn WasiDir>, path: PathBuf) -> Result<u32, Error>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 
table(&self) -> &Table77     pub fn table(&self) -> &Table {
78         &self.table
79     }
80 
table_mut(&mut self) -> Option<&mut Table>81     pub fn table_mut(&mut self) -> Option<&mut Table> {
82         Arc::get_mut(&mut self.0).map(|c| &mut c.table)
83     }
84 
push_arg(&mut self, arg: &str) -> Result<(), StringArrayError>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 
push_env(&mut self, var: &str, value: &str) -> Result<(), StringArrayError>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 
set_stdin(&self, f: Box<dyn WasiFile>)100     pub fn set_stdin(&self, f: Box<dyn WasiFile>) {
101         self.insert_file(0, f, FileAccessMode::READ);
102     }
103 
set_stdout(&self, f: Box<dyn WasiFile>)104     pub fn set_stdout(&self, f: Box<dyn WasiFile>) {
105         self.insert_file(1, f, FileAccessMode::WRITE);
106     }
107 
set_stderr(&self, f: Box<dyn WasiFile>)108     pub fn set_stderr(&self, f: Box<dyn WasiFile>) {
109         self.insert_file(2, f, FileAccessMode::WRITE);
110     }
111 
push_preopened_dir( &self, dir: Box<dyn WasiDir>, path: impl AsRef<Path>, ) -> Result<(), Error>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 
123 impl Deref for WasiCtx {
124     type Target = WasiCtxInner;
deref(&self) -> &Self::Target125     fn deref(&self) -> &Self::Target {
126         &self.0
127     }
128 }
129