13da5c369Sopenharmony_ciuse std::os::unix::prelude::*; 23da5c369Sopenharmony_ciuse tempfile::tempfile; 33da5c369Sopenharmony_ci 43da5c369Sopenharmony_ciuse nix::errno::Errno; 53da5c369Sopenharmony_ciuse nix::fcntl; 63da5c369Sopenharmony_ciuse nix::pty::openpty; 73da5c369Sopenharmony_ciuse nix::sys::termios::{self, tcgetattr, LocalFlags, OutputFlags}; 83da5c369Sopenharmony_ciuse nix::unistd::{close, read, write}; 93da5c369Sopenharmony_ci 103da5c369Sopenharmony_ci/// Helper function analogous to `std::io::Write::write_all`, but for `RawFd`s 113da5c369Sopenharmony_cifn write_all(f: RawFd, buf: &[u8]) { 123da5c369Sopenharmony_ci let mut len = 0; 133da5c369Sopenharmony_ci while len < buf.len() { 143da5c369Sopenharmony_ci len += write(f, &buf[len..]).unwrap(); 153da5c369Sopenharmony_ci } 163da5c369Sopenharmony_ci} 173da5c369Sopenharmony_ci 183da5c369Sopenharmony_ci// Test tcgetattr on a terminal 193da5c369Sopenharmony_ci#[test] 203da5c369Sopenharmony_cifn test_tcgetattr_pty() { 213da5c369Sopenharmony_ci // openpty uses ptname(3) internally 223da5c369Sopenharmony_ci let _m = crate::PTSNAME_MTX.lock(); 233da5c369Sopenharmony_ci 243da5c369Sopenharmony_ci let pty = openpty(None, None).expect("openpty failed"); 253da5c369Sopenharmony_ci termios::tcgetattr(pty.slave).unwrap(); 263da5c369Sopenharmony_ci close(pty.master).expect("closing the master failed"); 273da5c369Sopenharmony_ci close(pty.slave).expect("closing the slave failed"); 283da5c369Sopenharmony_ci} 293da5c369Sopenharmony_ci 303da5c369Sopenharmony_ci// Test tcgetattr on something that isn't a terminal 313da5c369Sopenharmony_ci#[test] 323da5c369Sopenharmony_cifn test_tcgetattr_enotty() { 333da5c369Sopenharmony_ci let file = tempfile().unwrap(); 343da5c369Sopenharmony_ci assert_eq!( 353da5c369Sopenharmony_ci termios::tcgetattr(file.as_raw_fd()).err(), 363da5c369Sopenharmony_ci Some(Errno::ENOTTY) 373da5c369Sopenharmony_ci ); 383da5c369Sopenharmony_ci} 393da5c369Sopenharmony_ci 403da5c369Sopenharmony_ci// Test tcgetattr on an invalid file descriptor 413da5c369Sopenharmony_ci#[test] 423da5c369Sopenharmony_cifn test_tcgetattr_ebadf() { 433da5c369Sopenharmony_ci assert_eq!(termios::tcgetattr(-1).err(), Some(Errno::EBADF)); 443da5c369Sopenharmony_ci} 453da5c369Sopenharmony_ci 463da5c369Sopenharmony_ci// Test modifying output flags 473da5c369Sopenharmony_ci#[test] 483da5c369Sopenharmony_cifn test_output_flags() { 493da5c369Sopenharmony_ci // openpty uses ptname(3) internally 503da5c369Sopenharmony_ci let _m = crate::PTSNAME_MTX.lock(); 513da5c369Sopenharmony_ci 523da5c369Sopenharmony_ci // Open one pty to get attributes for the second one 533da5c369Sopenharmony_ci let mut termios = { 543da5c369Sopenharmony_ci let pty = openpty(None, None).expect("openpty failed"); 553da5c369Sopenharmony_ci assert!(pty.master > 0); 563da5c369Sopenharmony_ci assert!(pty.slave > 0); 573da5c369Sopenharmony_ci let termios = tcgetattr(pty.slave).expect("tcgetattr failed"); 583da5c369Sopenharmony_ci close(pty.master).unwrap(); 593da5c369Sopenharmony_ci close(pty.slave).unwrap(); 603da5c369Sopenharmony_ci termios 613da5c369Sopenharmony_ci }; 623da5c369Sopenharmony_ci 633da5c369Sopenharmony_ci // Make sure postprocessing '\r' isn't specified by default or this test is useless. 643da5c369Sopenharmony_ci assert!(!termios 653da5c369Sopenharmony_ci .output_flags 663da5c369Sopenharmony_ci .contains(OutputFlags::OPOST | OutputFlags::OCRNL)); 673da5c369Sopenharmony_ci 683da5c369Sopenharmony_ci // Specify that '\r' characters should be transformed to '\n' 693da5c369Sopenharmony_ci // OPOST is specified to enable post-processing 703da5c369Sopenharmony_ci termios 713da5c369Sopenharmony_ci .output_flags 723da5c369Sopenharmony_ci .insert(OutputFlags::OPOST | OutputFlags::OCRNL); 733da5c369Sopenharmony_ci 743da5c369Sopenharmony_ci // Open a pty 753da5c369Sopenharmony_ci let pty = openpty(None, &termios).unwrap(); 763da5c369Sopenharmony_ci assert!(pty.master > 0); 773da5c369Sopenharmony_ci assert!(pty.slave > 0); 783da5c369Sopenharmony_ci 793da5c369Sopenharmony_ci // Write into the master 803da5c369Sopenharmony_ci let string = "foofoofoo\r"; 813da5c369Sopenharmony_ci write_all(pty.master, string.as_bytes()); 823da5c369Sopenharmony_ci 833da5c369Sopenharmony_ci // Read from the slave verifying that the output has been properly transformed 843da5c369Sopenharmony_ci let mut buf = [0u8; 10]; 853da5c369Sopenharmony_ci crate::read_exact(pty.slave, &mut buf); 863da5c369Sopenharmony_ci let transformed_string = "foofoofoo\n"; 873da5c369Sopenharmony_ci close(pty.master).unwrap(); 883da5c369Sopenharmony_ci close(pty.slave).unwrap(); 893da5c369Sopenharmony_ci assert_eq!(&buf, transformed_string.as_bytes()); 903da5c369Sopenharmony_ci} 913da5c369Sopenharmony_ci 923da5c369Sopenharmony_ci// Test modifying local flags 933da5c369Sopenharmony_ci#[test] 943da5c369Sopenharmony_cifn test_local_flags() { 953da5c369Sopenharmony_ci // openpty uses ptname(3) internally 963da5c369Sopenharmony_ci let _m = crate::PTSNAME_MTX.lock(); 973da5c369Sopenharmony_ci 983da5c369Sopenharmony_ci // Open one pty to get attributes for the second one 993da5c369Sopenharmony_ci let mut termios = { 1003da5c369Sopenharmony_ci let pty = openpty(None, None).unwrap(); 1013da5c369Sopenharmony_ci assert!(pty.master > 0); 1023da5c369Sopenharmony_ci assert!(pty.slave > 0); 1033da5c369Sopenharmony_ci let termios = tcgetattr(pty.slave).unwrap(); 1043da5c369Sopenharmony_ci close(pty.master).unwrap(); 1053da5c369Sopenharmony_ci close(pty.slave).unwrap(); 1063da5c369Sopenharmony_ci termios 1073da5c369Sopenharmony_ci }; 1083da5c369Sopenharmony_ci 1093da5c369Sopenharmony_ci // Make sure echo is specified by default or this test is useless. 1103da5c369Sopenharmony_ci assert!(termios.local_flags.contains(LocalFlags::ECHO)); 1113da5c369Sopenharmony_ci 1123da5c369Sopenharmony_ci // Disable local echo 1133da5c369Sopenharmony_ci termios.local_flags.remove(LocalFlags::ECHO); 1143da5c369Sopenharmony_ci 1153da5c369Sopenharmony_ci // Open a new pty with our modified termios settings 1163da5c369Sopenharmony_ci let pty = openpty(None, &termios).unwrap(); 1173da5c369Sopenharmony_ci assert!(pty.master > 0); 1183da5c369Sopenharmony_ci assert!(pty.slave > 0); 1193da5c369Sopenharmony_ci 1203da5c369Sopenharmony_ci // Set the master is in nonblocking mode or reading will never return. 1213da5c369Sopenharmony_ci let flags = fcntl::fcntl(pty.master, fcntl::F_GETFL).unwrap(); 1223da5c369Sopenharmony_ci let new_flags = 1233da5c369Sopenharmony_ci fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK; 1243da5c369Sopenharmony_ci fcntl::fcntl(pty.master, fcntl::F_SETFL(new_flags)).unwrap(); 1253da5c369Sopenharmony_ci 1263da5c369Sopenharmony_ci // Write into the master 1273da5c369Sopenharmony_ci let string = "foofoofoo\r"; 1283da5c369Sopenharmony_ci write_all(pty.master, string.as_bytes()); 1293da5c369Sopenharmony_ci 1303da5c369Sopenharmony_ci // Try to read from the master, which should not have anything as echoing was disabled. 1313da5c369Sopenharmony_ci let mut buf = [0u8; 10]; 1323da5c369Sopenharmony_ci let read = read(pty.master, &mut buf).unwrap_err(); 1333da5c369Sopenharmony_ci close(pty.master).unwrap(); 1343da5c369Sopenharmony_ci close(pty.slave).unwrap(); 1353da5c369Sopenharmony_ci assert_eq!(read, Errno::EAGAIN); 1363da5c369Sopenharmony_ci} 137