pty.ha (2287B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use errors; 5 use fmt; 6 use fs; 7 use io; 8 use os; 9 use rt; 10 use types::c; 11 12 // Opens an available pseudoterminal and returns the file descriptors of the 13 // master and slave. 14 export fn openpty() ((io::file, io::file) | fs::error) = { 15 let master = open_master()?; 16 let slave = match (get_slave(master)) { 17 case let e: fs::error => 18 io::close(master)!; 19 return e; 20 case let s: io::file => 21 yield s; 22 }; 23 return (master, slave); 24 }; 25 26 // Opens an available pseudoterminal master. 27 fn open_master() (io::file | fs::error) = { 28 match (rt::posix_openpt(rt::O_RDWR | rt::O_NOCTTY)) { 29 case let e: rt::errno => 30 return errors::errno(e); 31 case let i: int => 32 return io::fdopen(i); 33 }; 34 }; 35 36 // Returns a file descriptor referring to the pseudoterminal slave for a 37 // pseudoterminal master. 38 fn get_slave(master: io::file) (io::file | fs::error) = 39 os::open(ptsname(master)?, fs::flag::RDWR); 40 41 // Returns the filename of the pseudoterminal slave. 42 export fn ptsname(master: io::file) (str | error) = { 43 // Ensure that the file descriptor refers to a master 44 match (rt::ioctl(master, rt::TIOCPTMASTER, null)) { 45 case let e: rt::errno => 46 if (e == rt::EBADF) return errors::invalid 47 else return errors::unsupported; 48 case => void; 49 }; 50 51 let name: [rt::PATH_MAX]u8 = [0...]; 52 let fiodgname_arg = (len(name), &name); 53 match (rt::ioctl(master, rt::FIODGNAME, &fiodgname_arg)) { 54 case let e: rt::errno => 55 switch (e) { 56 case rt::EBADF => 57 return errors::invalid; 58 case rt::EINVAL, rt::ENOTTY => 59 return errors::unsupported; 60 case => 61 abort("Unexpected error from ioctl"); 62 }; 63 case => 64 static let path_buf: [rt::PATH_MAX]u8 = [0...]; 65 return fmt::bsprintf(path_buf, 66 "/dev/{}", c::tostr(&name: *const c::char)!); 67 }; 68 }; 69 70 // Sets the dimensions of the underlying pseudoterminal for an [[io::file]]. 71 export fn set_winsize(pty: io::file, sz: ttysize) (void | error) = { 72 let wsz = rt::winsize { ws_row = sz.rows, ws_col = sz.columns, ... }; 73 match (rt::ioctl(pty, rt::TIOCSWINSZ, &wsz)) { 74 case let e: rt::errno => 75 switch (e) { 76 case rt::EBADF, rt::EINVAL => 77 return errors::invalid; 78 case rt::ENOTTY => 79 return errors::unsupported; 80 case => 81 abort("Unexpected error from ioctl"); 82 }; 83 case => void; 84 }; 85 };