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