1 // Copyright (c) 2023 Huawei Device Co., Ltd. 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 use std::ffi::CStr; 15 use std::fs::OpenOptions; 16 use std::io; 17 use std::io::{Read, Write}; 18 use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}; 19 use std::os::unix::ffi::OsStrExt; 20 use std::process::Stdio; 21 22 use ylong_io::sys::SourceFd; 23 use ylong_io::{Interest, Selector, Source, Token}; 24 25 #[derive(Debug)] 26 pub(crate) struct PtyInner(OwnedFd); 27 28 impl PtyInner { open() -> io::Result<Self>29 pub(crate) fn open() -> io::Result<Self> { 30 // Can not set CLOEXEC directly because it is linux-specific. 31 let raw = syscall!(posix_openpt(libc::O_RDWR | libc::O_NOCTTY))?; 32 33 syscall!(grantpt(raw))?; 34 syscall!(unlockpt(raw))?; 35 36 // Set CLOEXEC. 37 let mut flags = syscall!(fcntl(raw, libc::F_GETFD))?; 38 flags |= libc::O_CLOEXEC; 39 syscall!(fcntl(raw, libc::F_SETFD, flags))?; 40 41 let fd = unsafe { OwnedFd::from_raw_fd(raw) }; 42 Ok(Self(fd)) 43 } 44 set_size( &self, ws_row: u16, ws_col: u16, ws_xpixel: u16, ws_ypixel: u16, ) -> io::Result<()>45 pub(crate) fn set_size( 46 &self, 47 ws_row: u16, 48 ws_col: u16, 49 ws_xpixel: u16, 50 ws_ypixel: u16, 51 ) -> io::Result<()> { 52 let size = libc::winsize { 53 ws_row, 54 ws_col, 55 ws_xpixel, 56 ws_ypixel, 57 }; 58 let raw = self.0.as_raw_fd(); 59 syscall!(ioctl(raw, libc::TIOCSWINSZ, std::ptr::addr_of!(size))).map(|_| ()) 60 } 61 pts(&self, size: usize) -> io::Result<PtsInner>62 pub(crate) fn pts(&self, size: usize) -> io::Result<PtsInner> { 63 let mut name_buf: Vec<libc::c_char> = vec![0; size]; 64 65 loop { 66 let res = unsafe { 67 libc::ptsname_r( 68 self.0.as_raw_fd(), 69 name_buf.as_mut_ptr().cast(), 70 name_buf.len(), 71 ) 72 }; 73 match res { 74 0 => { 75 name_buf.resize(name_buf.capacity(), 0); 76 break; 77 } 78 // If the vec's capacity is too small, double it. 79 libc::ERANGE => { 80 name_buf.reserve(1); 81 name_buf.resize(name_buf.capacity(), 0) 82 } 83 _ => return Err(std::io::Error::last_os_error()), 84 } 85 } 86 87 let name = unsafe { CStr::from_ptr(name_buf.as_ptr()) }.to_owned(); 88 let path = std::ffi::OsStr::from_bytes(name.as_bytes()); 89 90 let file = OpenOptions::new().read(true).write(true).open(path)?; 91 Ok(PtsInner(file.into())) 92 } 93 set_nonblocking(&self) -> io::Result<()>94 pub(crate) fn set_nonblocking(&self) -> io::Result<()> { 95 let mut flags = syscall!(fcntl(self.0.as_raw_fd(), libc::F_GETFL))?; 96 flags |= libc::O_NONBLOCK; 97 syscall!(fcntl(self.0.as_raw_fd(), libc::F_SETFL, flags)).map(|_| ()) 98 } 99 } 100 101 impl AsFd for PtyInner { as_fd(&self) -> BorrowedFd<'_>102 fn as_fd(&self) -> BorrowedFd<'_> { 103 self.0.as_fd() 104 } 105 } 106 107 impl AsRawFd for PtyInner { as_raw_fd(&self) -> RawFd108 fn as_raw_fd(&self) -> RawFd { 109 self.0.as_raw_fd() 110 } 111 } 112 113 impl From<PtyInner> for OwnedFd { from(value: PtyInner) -> Self114 fn from(value: PtyInner) -> Self { 115 value.0 116 } 117 } 118 119 macro_rules! impl_read_write { 120 ($type:ty) => { 121 impl Read for $type { 122 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 123 syscall!(read( 124 self.0.as_raw_fd(), 125 buf.as_mut_ptr().cast::<libc::c_void>(), 126 buf.len() as libc::size_t 127 )) 128 .map(|res| res as usize) 129 } 130 } 131 132 impl Write for $type { 133 fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 134 syscall!(write( 135 self.0.as_raw_fd(), 136 buf.as_ptr().cast::<libc::c_void>(), 137 buf.len() as libc::size_t 138 )) 139 .map(|res| res as usize) 140 } 141 142 fn flush(&mut self) -> io::Result<()> { 143 Ok(()) 144 } 145 } 146 }; 147 } 148 149 impl_read_write!(PtyInner); 150 impl_read_write!(&PtyInner); 151 152 impl Source for PtyInner { register( &mut self, selector: &Selector, token: Token, interests: Interest, ) -> io::Result<()>153 fn register( 154 &mut self, 155 selector: &Selector, 156 token: Token, 157 interests: Interest, 158 ) -> io::Result<()> { 159 SourceFd(&self.get_fd()).register(selector, token, interests) 160 } 161 deregister(&mut self, selector: &Selector) -> io::Result<()>162 fn deregister(&mut self, selector: &Selector) -> io::Result<()> { 163 SourceFd(&self.get_fd()).deregister(selector) 164 } 165 get_fd(&self) -> ylong_io::Fd166 fn get_fd(&self) -> ylong_io::Fd { 167 self.0.as_raw_fd() 168 } 169 } 170 171 #[derive(Debug)] 172 pub(crate) struct PtsInner(OwnedFd); 173 174 impl PtsInner { clone_stdio(&self) -> io::Result<Stdio>175 pub(crate) fn clone_stdio(&self) -> io::Result<Stdio> { 176 Ok(self.0.try_clone()?.into()) 177 } 178 session_leader(&self) -> impl FnMut() -> io::Result<()>179 pub(crate) fn session_leader(&self) -> impl FnMut() -> io::Result<()> { 180 let fd = self.0.as_raw_fd(); 181 move || { 182 syscall!(setsid())?; 183 syscall!(ioctl(fd, libc::TIOCSCTTY, std::ptr::null::<libc::c_int>()))?; 184 Ok(()) 185 } 186 } 187 } 188 189 impl From<PtsInner> for OwnedFd { from(value: PtsInner) -> Self190 fn from(value: PtsInner) -> Self { 191 value.0 192 } 193 } 194 195 impl AsFd for PtsInner { as_fd(&self) -> BorrowedFd<'_>196 fn as_fd(&self) -> BorrowedFd<'_> { 197 self.0.as_fd() 198 } 199 } 200 201 impl AsRawFd for PtsInner { as_raw_fd(&self) -> RawFd202 fn as_raw_fd(&self) -> RawFd { 203 self.0.as_raw_fd() 204 } 205 } 206 207 #[cfg(test)] 208 mod tests { 209 use std::io::{Read, Write}; 210 use std::os::fd::{AsFd, AsRawFd, OwnedFd}; 211 212 use crate::process::pty_process::sys::PtyInner; 213 214 /// Basic UT test cases for `PtyInner.pts()`. 215 /// 216 /// # Brief 217 /// 1. Open a new `PtyInner`. 218 /// 2. Call pts() with small size. 219 /// 3. Check result is correct. 220 #[test] ut_pty_pts_size_test()221 fn ut_pty_pts_size_test() { 222 let pty = PtyInner::open().unwrap(); 223 let pts = pty.pts(1); 224 assert!(pts.is_ok()); 225 let pts = pts.unwrap(); 226 assert!(pts.as_fd().as_raw_fd() >= 0); 227 assert!(pts.as_raw_fd() >= 0); 228 let fd = OwnedFd::from(pts); 229 assert!(fd.as_raw_fd() >= 0); 230 } 231 232 /// Basic UT test cases for `PtyInner` read and write. 233 /// 234 /// # Brief 235 /// 1. Open a new `PtyInner`. 236 /// 2. Write something into `PtyInner`. 237 /// 3. Check read result is correct. 238 #[test] ut_pty_read_write_test()239 fn ut_pty_read_write_test() { 240 let mut pty = PtyInner::open().unwrap(); 241 let arg = "hello world!"; 242 pty.write_all(arg.as_bytes()).unwrap(); 243 244 let mut buf = [0; 12]; 245 pty.read_exact(&mut buf).unwrap(); 246 assert_eq!(buf, arg.as_bytes()); 247 } 248 } 249