1 //! Virtual pipes.
2 //!
3 //! These types provide easy implementations of `WasiFile` that mimic much of the behavior of Unix
4 //! pipes. These are particularly helpful for redirecting WASI stdio handles to destinations other
5 //! than OS files.
6 //!
7 //! Some convenience constructors are included for common backing types like `Vec<u8>` and `String`,
8 //! but the virtual pipes can be instantiated with any `Read` or `Write` type.
9 //!
10 use crate::Error;
11 use crate::file::{FdFlags, FileType, WasiFile};
12 use std::any::Any;
13 use std::io::{self, Read, Write};
14 use std::sync::{Arc, RwLock};
15 
16 /// A virtual pipe read end.
17 ///
18 /// A variety of `From` impls are provided so that common pipe types are easy to create. For example:
19 ///
20 /// ```rust
21 /// use wasi_common::{pipe::ReadPipe, WasiCtx, Table};
22 /// let stdin = ReadPipe::from("hello from stdin!");
23 /// // Bring these instances from elsewhere (e.g. wasi-cap-std-sync or wasi-cap-std-tokio):
24 /// use wasi_common::sync::{random_ctx, clocks_ctx, sched_ctx};
25 /// let random = random_ctx();
26 /// let clocks = clocks_ctx();
27 /// let sched = sched_ctx();
28 /// let table = Table::new();
29 /// let mut ctx = WasiCtx::new(random, clocks, sched, table);
30 /// ctx.set_stdin(Box::new(stdin.clone()));
31 /// ```
32 #[derive(Debug)]
33 pub struct ReadPipe<R: Read> {
34     reader: Arc<RwLock<R>>,
35 }
36 
37 impl<R: Read> Clone for ReadPipe<R> {
clone(&self) -> Self38     fn clone(&self) -> Self {
39         Self {
40             reader: self.reader.clone(),
41         }
42     }
43 }
44 
45 impl<R: Read> ReadPipe<R> {
46     /// Create a new pipe from a `Read` type.
47     ///
48     /// All `Handle` read operations delegate to reading from this underlying reader.
new(r: R) -> Self49     pub fn new(r: R) -> Self {
50         Self::from_shared(Arc::new(RwLock::new(r)))
51     }
52 
53     /// Create a new pipe from a shareable `Read` type.
54     ///
55     /// All `Handle` read operations delegate to reading from this underlying reader.
from_shared(reader: Arc<RwLock<R>>) -> Self56     pub fn from_shared(reader: Arc<RwLock<R>>) -> Self {
57         Self { reader }
58     }
59 
60     /// Try to convert this `ReadPipe<R>` back to the underlying `R` type.
61     ///
62     /// This will fail with `Err(self)` if multiple references to the underlying `R` exist.
try_into_inner(mut self) -> Result<R, Self>63     pub fn try_into_inner(mut self) -> Result<R, Self> {
64         match Arc::try_unwrap(self.reader) {
65             Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),
66             Err(reader) => {
67                 self.reader = reader;
68                 Err(self)
69             }
70         }
71     }
borrow(&self) -> std::sync::RwLockWriteGuard<'_, R>72     fn borrow(&self) -> std::sync::RwLockWriteGuard<'_, R> {
73         RwLock::write(&self.reader).unwrap()
74     }
75 }
76 
77 impl From<Vec<u8>> for ReadPipe<io::Cursor<Vec<u8>>> {
from(r: Vec<u8>) -> Self78     fn from(r: Vec<u8>) -> Self {
79         Self::new(io::Cursor::new(r))
80     }
81 }
82 
83 impl From<&[u8]> for ReadPipe<io::Cursor<Vec<u8>>> {
from(r: &[u8]) -> Self84     fn from(r: &[u8]) -> Self {
85         Self::from(r.to_vec())
86     }
87 }
88 
89 impl From<String> for ReadPipe<io::Cursor<String>> {
from(r: String) -> Self90     fn from(r: String) -> Self {
91         Self::new(io::Cursor::new(r))
92     }
93 }
94 
95 impl From<&str> for ReadPipe<io::Cursor<String>> {
from(r: &str) -> Self96     fn from(r: &str) -> Self {
97         Self::from(r.to_string())
98     }
99 }
100 
101 #[async_trait::async_trait]
102 impl<R: Read + Any + Send + Sync> WasiFile for ReadPipe<R> {
as_any(&self) -> &dyn Any103     fn as_any(&self) -> &dyn Any {
104         self
105     }
get_filetype(&self) -> Result<FileType, Error>106     async fn get_filetype(&self) -> Result<FileType, Error> {
107         Ok(FileType::Pipe)
108     }
read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error>109     async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
110         let n = self.borrow().read_vectored(bufs)?;
111         Ok(n.try_into()?)
112     }
113 }
114 
115 /// A virtual pipe write end.
116 ///
117 /// ```rust
118 /// use wasi_common::{pipe::WritePipe, WasiCtx, Table};
119 /// let stdout = WritePipe::new_in_memory();
120 /// // Bring these instances from elsewhere (e.g. wasi-cap-std-sync or wasi-cap-std-tokio):
121 /// use wasi_common::sync::{random_ctx, clocks_ctx, sched_ctx};
122 /// let random = random_ctx();
123 /// let clocks = clocks_ctx();
124 /// let sched = sched_ctx();
125 /// let table = Table::new();
126 /// let mut ctx = WasiCtx::new(random, clocks, sched, table);
127 /// ctx.set_stdout(Box::new(stdout.clone()));
128 /// // use ctx in an instance, then make sure it is dropped:
129 /// drop(ctx);
130 /// let contents: Vec<u8> = stdout.try_into_inner().expect("sole remaining reference to WritePipe").into_inner();
131 /// println!("contents of stdout: {:?}", contents);
132 /// ```
133 #[derive(Debug)]
134 pub struct WritePipe<W: Write> {
135     writer: Arc<RwLock<W>>,
136 }
137 
138 impl<W: Write> Clone for WritePipe<W> {
clone(&self) -> Self139     fn clone(&self) -> Self {
140         Self {
141             writer: self.writer.clone(),
142         }
143     }
144 }
145 
146 impl<W: Write> WritePipe<W> {
147     /// Create a new pipe from a `Write` type.
148     ///
149     /// All `Handle` write operations delegate to writing to this underlying writer.
new(w: W) -> Self150     pub fn new(w: W) -> Self {
151         Self::from_shared(Arc::new(RwLock::new(w)))
152     }
153 
154     /// Create a new pipe from a shareable `Write` type.
155     ///
156     /// All `Handle` write operations delegate to writing to this underlying writer.
from_shared(writer: Arc<RwLock<W>>) -> Self157     pub fn from_shared(writer: Arc<RwLock<W>>) -> Self {
158         Self { writer }
159     }
160 
161     /// Try to convert this `WritePipe<W>` back to the underlying `W` type.
162     ///
163     /// This will fail with `Err(self)` if multiple references to the underlying `W` exist.
try_into_inner(mut self) -> Result<W, Self>164     pub fn try_into_inner(mut self) -> Result<W, Self> {
165         match Arc::try_unwrap(self.writer) {
166             Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),
167             Err(writer) => {
168                 self.writer = writer;
169                 Err(self)
170             }
171         }
172     }
173 
borrow(&self) -> std::sync::RwLockWriteGuard<'_, W>174     fn borrow(&self) -> std::sync::RwLockWriteGuard<'_, W> {
175         RwLock::write(&self.writer).unwrap()
176     }
177 }
178 
179 impl WritePipe<io::Cursor<Vec<u8>>> {
180     /// Create a new writable virtual pipe backed by a `Vec<u8>` buffer.
new_in_memory() -> Self181     pub fn new_in_memory() -> Self {
182         Self::new(io::Cursor::new(vec![]))
183     }
184 }
185 
186 #[async_trait::async_trait]
187 impl<W: Write + Any + Send + Sync> WasiFile for WritePipe<W> {
as_any(&self) -> &dyn Any188     fn as_any(&self) -> &dyn Any {
189         self
190     }
get_filetype(&self) -> Result<FileType, Error>191     async fn get_filetype(&self) -> Result<FileType, Error> {
192         Ok(FileType::Pipe)
193     }
get_fdflags(&self) -> Result<FdFlags, Error>194     async fn get_fdflags(&self) -> Result<FdFlags, Error> {
195         Ok(FdFlags::APPEND)
196     }
write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error>197     async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
198         let n = self.borrow().write_vectored(bufs)?;
199         Ok(n.try_into()?)
200     }
201 }
202