pty.ha (2116B)
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 11 // Opens an available pseudoterminal and returns the file descriptors of the 12 // master and slave. 13 export fn openpty() ((io::file, io::file) | fs::error) = { 14 let master = open_master()?; 15 let slave = match (get_slave(master)) { 16 case let e: fs::error => 17 io::close(master)!; 18 return e; 19 case let s: io::file => 20 yield s; 21 }; 22 return (master, slave); 23 }; 24 25 // Opens an available pseudoterminal master. 26 fn open_master() (io::file | fs::error) = { 27 return os::open("/dev/ptmx", fs::flag::RDWR); 28 }; 29 30 // Returns a file descriptor referring to the pseudoterminal slave for a 31 // pseudoterminal master. 32 fn get_slave(master: io::file) (io::file | fs::error) = { 33 // Unlock the pseudoterminal slave 34 match (rt::ioctl(master, rt::TIOCSPTLCK, &0)) { 35 case rt::errno => 36 return errors::invalid; 37 case => void; 38 }; 39 40 let ioctl = rt::ioctl( 41 master, rt::TIOCGPTPEER, 42 (rt::O_RDWR | rt::O_NOCTTY): u64); 43 match (ioctl) { 44 case let e: rt::errno => 45 return errors::errno(e); 46 case let fd: int => 47 return io::fdopen(fd); 48 }; 49 }; 50 51 // Returns the filename of the pseudoterminal slave. 52 export fn ptsname(master: io::file) (str | error) = { 53 let pty = 0; 54 match (rt::ioctl(master, rt::TIOCGPTN, &pty)) { 55 case let e: rt::errno => 56 switch (e) { 57 case rt::EBADF => 58 return errors::invalid; 59 case rt::ENOTTY => 60 return errors::unsupported; 61 case => 62 abort("Unexpected error from ioctl"); 63 }; 64 case => 65 static let buf: [9 + 20]u8 = [0...]; 66 return fmt::bsprintf(buf[..len(buf)], "/dev/pts/{}", pty); 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 => 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 };