| 1 | use std::os::unix::io::{AsFd, AsRawFd}; |
| 2 | use tempfile::tempfile; |
| 3 | |
| 4 | use nix::errno::Errno; |
| 5 | use nix::fcntl; |
| 6 | use nix::pty::openpty; |
| 7 | use nix::sys::termios::{self, tcgetattr, LocalFlags, OutputFlags}; |
| 8 | use nix::unistd::{read, write}; |
| 9 | |
| 10 | /// Helper function analogous to `std::io::Write::write_all`, but for `Fd`s |
| 11 | fn write_all<Fd: AsFd>(f: Fd, buf: &[u8]) { |
| 12 | let mut len = 0; |
| 13 | while len < buf.len() { |
| 14 | len += write(f.as_fd().as_raw_fd(), &buf[len..]).unwrap(); |
| 15 | } |
| 16 | } |
| 17 | |
| 18 | // Test tcgetattr on a terminal |
| 19 | #[test] |
| 20 | fn test_tcgetattr_pty() { |
| 21 | // openpty uses ptname(3) internally |
| 22 | let _m = crate::PTSNAME_MTX.lock(); |
| 23 | |
| 24 | let pty = openpty(None, None).expect("openpty failed" ); |
| 25 | termios::tcgetattr(&pty.slave).unwrap(); |
| 26 | } |
| 27 | |
| 28 | // Test tcgetattr on something that isn't a terminal |
| 29 | #[test] |
| 30 | fn test_tcgetattr_enotty() { |
| 31 | let file = tempfile().unwrap(); |
| 32 | assert_eq!(termios::tcgetattr(&file).err(), Some(Errno::ENOTTY)); |
| 33 | } |
| 34 | |
| 35 | // Test modifying output flags |
| 36 | #[test] |
| 37 | fn test_output_flags() { |
| 38 | // openpty uses ptname(3) internally |
| 39 | let _m = crate::PTSNAME_MTX.lock(); |
| 40 | |
| 41 | // Open one pty to get attributes for the second one |
| 42 | let mut termios = { |
| 43 | let pty = openpty(None, None).expect("openpty failed" ); |
| 44 | tcgetattr(&pty.slave).expect("tcgetattr failed" ) |
| 45 | }; |
| 46 | |
| 47 | // Make sure postprocessing '\r' isn't specified by default or this test is useless. |
| 48 | assert!(!termios |
| 49 | .output_flags |
| 50 | .contains(OutputFlags::OPOST | OutputFlags::OCRNL)); |
| 51 | |
| 52 | // Specify that '\r' characters should be transformed to '\n' |
| 53 | // OPOST is specified to enable post-processing |
| 54 | termios |
| 55 | .output_flags |
| 56 | .insert(OutputFlags::OPOST | OutputFlags::OCRNL); |
| 57 | |
| 58 | // Open a pty |
| 59 | let pty = openpty(None, &termios).unwrap(); |
| 60 | |
| 61 | // Write into the master |
| 62 | let string = "foofoofoo \r" ; |
| 63 | write_all(&pty.master, string.as_bytes()); |
| 64 | |
| 65 | // Read from the slave verifying that the output has been properly transformed |
| 66 | let mut buf = [0u8; 10]; |
| 67 | crate::read_exact(&pty.slave, &mut buf); |
| 68 | let transformed_string = "foofoofoo \n" ; |
| 69 | assert_eq!(&buf, transformed_string.as_bytes()); |
| 70 | } |
| 71 | |
| 72 | // Test modifying local flags |
| 73 | #[test] |
| 74 | fn test_local_flags() { |
| 75 | // openpty uses ptname(3) internally |
| 76 | let _m = crate::PTSNAME_MTX.lock(); |
| 77 | |
| 78 | // Open one pty to get attributes for the second one |
| 79 | let mut termios = { |
| 80 | let pty = openpty(None, None).unwrap(); |
| 81 | tcgetattr(&pty.slave).unwrap() |
| 82 | }; |
| 83 | |
| 84 | // Make sure echo is specified by default or this test is useless. |
| 85 | assert!(termios.local_flags.contains(LocalFlags::ECHO)); |
| 86 | |
| 87 | // Disable local echo |
| 88 | termios.local_flags.remove(LocalFlags::ECHO); |
| 89 | |
| 90 | // Open a new pty with our modified termios settings |
| 91 | let pty = openpty(None, &termios).unwrap(); |
| 92 | |
| 93 | // Set the master is in nonblocking mode or reading will never return. |
| 94 | let flags = fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_GETFL).unwrap(); |
| 95 | let new_flags = |
| 96 | fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK; |
| 97 | fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_SETFL(new_flags)).unwrap(); |
| 98 | |
| 99 | // Write into the master |
| 100 | let string = "foofoofoo \r" ; |
| 101 | write_all(&pty.master, string.as_bytes()); |
| 102 | |
| 103 | // Try to read from the master, which should not have anything as echoing was disabled. |
| 104 | let mut buf = [0u8; 10]; |
| 105 | let read = read(pty.master.as_raw_fd(), &mut buf).unwrap_err(); |
| 106 | assert_eq!(read, Errno::EAGAIN); |
| 107 | } |
| 108 | |