1 use crate::sync::file::convert_systimespec; 2 use fs_set_times::SetTimes; 3 use std::any::Any; 4 use std::convert::TryInto; 5 use std::io::{self, IsTerminal, Read, Write}; 6 use system_interface::io::ReadReady; 7 8 use crate::{ 9 file::{FdFlags, FileType, WasiFile}, 10 Error, ErrorExt, 11 }; 12 #[cfg(windows)] 13 use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket}; 14 #[cfg(unix)] 15 use io_lifetimes::{AsFd, BorrowedFd}; 16 #[cfg(windows)] 17 use io_lifetimes::{AsHandle, BorrowedHandle}; 18 19 pub struct Stdin(std::io::Stdin); 20 21 pub fn stdin() -> Stdin { 22 Stdin(std::io::stdin()) 23 } 24 25 #[wiggle::async_trait] 26 impl WasiFile for Stdin { 27 fn as_any(&self) -> &dyn Any { 28 self 29 } 30 31 #[cfg(unix)] 32 fn pollable(&self) -> Option<rustix::fd::BorrowedFd> { 33 Some(self.0.as_fd()) 34 } 35 36 #[cfg(windows)] 37 fn pollable(&self) -> Option<io_extras::os::windows::RawHandleOrSocket> { 38 Some(self.0.as_raw_handle_or_socket()) 39 } 40 41 async fn get_filetype(&self) -> Result<FileType, Error> { 42 if self.isatty() { 43 Ok(FileType::CharacterDevice) 44 } else { 45 Ok(FileType::Unknown) 46 } 47 } 48 async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> { 49 let n = self.0.lock().read_vectored(bufs)?; 50 Ok(n.try_into().map_err(|_| Error::range())?) 51 } 52 async fn read_vectored_at<'a>( 53 &self, 54 _bufs: &mut [io::IoSliceMut<'a>], 55 _offset: u64, 56 ) -> Result<u64, Error> { 57 Err(Error::seek_pipe()) 58 } 59 async fn seek(&self, _pos: std::io::SeekFrom) -> Result<u64, Error> { 60 Err(Error::seek_pipe()) 61 } 62 async fn peek(&self, _buf: &mut [u8]) -> Result<u64, Error> { 63 Err(Error::seek_pipe()) 64 } 65 async fn set_times( 66 &self, 67 atime: Option<crate::SystemTimeSpec>, 68 mtime: Option<crate::SystemTimeSpec>, 69 ) -> Result<(), Error> { 70 self.0 71 .set_times(convert_systimespec(atime), convert_systimespec(mtime))?; 72 Ok(()) 73 } 74 fn num_ready_bytes(&self) -> Result<u64, Error> { 75 Ok(self.0.num_ready_bytes()?) 76 } 77 fn isatty(&self) -> bool { 78 #[cfg(unix)] 79 return self.0.as_fd().is_terminal(); 80 #[cfg(windows)] 81 return self.0.as_handle().is_terminal(); 82 } 83 } 84 #[cfg(windows)] 85 impl AsHandle for Stdin { 86 fn as_handle(&self) -> BorrowedHandle<'_> { 87 self.0.as_handle() 88 } 89 } 90 #[cfg(windows)] 91 impl AsRawHandleOrSocket for Stdin { 92 #[inline] 93 fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket { 94 self.0.as_raw_handle_or_socket() 95 } 96 } 97 #[cfg(unix)] 98 impl AsFd for Stdin { 99 fn as_fd(&self) -> BorrowedFd<'_> { 100 self.0.as_fd() 101 } 102 } 103 104 macro_rules! wasi_file_write_impl { 105 ($ty:ty, $ident:ident) => { 106 #[wiggle::async_trait] 107 impl WasiFile for $ty { 108 fn as_any(&self) -> &dyn Any { 109 self 110 } 111 #[cfg(unix)] 112 fn pollable(&self) -> Option<rustix::fd::BorrowedFd> { 113 Some(self.0.as_fd()) 114 } 115 #[cfg(windows)] 116 fn pollable(&self) -> Option<io_extras::os::windows::RawHandleOrSocket> { 117 Some(self.0.as_raw_handle_or_socket()) 118 } 119 async fn get_filetype(&self) -> Result<FileType, Error> { 120 if self.isatty() { 121 Ok(FileType::CharacterDevice) 122 } else { 123 Ok(FileType::Unknown) 124 } 125 } 126 async fn get_fdflags(&self) -> Result<FdFlags, Error> { 127 Ok(FdFlags::APPEND) 128 } 129 async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> { 130 let mut io = self.0.lock(); 131 let n = io.write_vectored(bufs)?; 132 // On a successful write additionally flush out the bytes to 133 // handle stdio buffering done by libstd since WASI interfaces 134 // here aren't buffered. 135 io.flush()?; 136 Ok(n.try_into().map_err(|_| { 137 Error::range().context("converting write_vectored total length") 138 })?) 139 } 140 async fn write_vectored_at<'a>( 141 &self, 142 _bufs: &[io::IoSlice<'a>], 143 _offset: u64, 144 ) -> Result<u64, Error> { 145 Err(Error::seek_pipe()) 146 } 147 async fn seek(&self, _pos: std::io::SeekFrom) -> Result<u64, Error> { 148 Err(Error::seek_pipe()) 149 } 150 async fn set_times( 151 &self, 152 atime: Option<crate::SystemTimeSpec>, 153 mtime: Option<crate::SystemTimeSpec>, 154 ) -> Result<(), Error> { 155 self.0 156 .set_times(convert_systimespec(atime), convert_systimespec(mtime))?; 157 Ok(()) 158 } 159 fn isatty(&self) -> bool { 160 self.0.is_terminal() 161 } 162 } 163 #[cfg(windows)] 164 impl AsHandle for $ty { 165 fn as_handle(&self) -> BorrowedHandle<'_> { 166 self.0.as_handle() 167 } 168 } 169 #[cfg(unix)] 170 impl AsFd for $ty { 171 fn as_fd(&self) -> BorrowedFd<'_> { 172 self.0.as_fd() 173 } 174 } 175 #[cfg(windows)] 176 impl AsRawHandleOrSocket for $ty { 177 #[inline] 178 fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket { 179 self.0.as_raw_handle_or_socket() 180 } 181 } 182 }; 183 } 184 185 pub struct Stdout(std::io::Stdout); 186 187 pub fn stdout() -> Stdout { 188 Stdout(std::io::stdout()) 189 } 190 wasi_file_write_impl!(Stdout, Stdout); 191 192 pub struct Stderr(std::io::Stderr); 193 194 pub fn stderr() -> Stderr { 195 Stderr(std::io::stderr()) 196 } 197 wasi_file_write_impl!(Stderr, Stderr); 198