use std::fs::File;
use std::io::{Read, Write};
use std::os::unix::prelude::*;
use std::path::Path;
use tempfile::tempfile;
use libc::{_exit, STDOUT_FILENO};
use nix::fcntl::{open, OFlag};
use nix::pty::*;
use nix::sys::stat;
use nix::sys::termios::*;
use nix::unistd::{close, pause, write};
#[test]
fn test_explicit_close() {
let mut f = {
let m = posix_openpt(OFlag::O_RDWR).unwrap();
close(m.into_raw_fd()).unwrap();
tempfile().unwrap()
};
f.write_all(b"whatever").unwrap();
}
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptsname_equivalence() {
let _m = crate::PTSNAME_MTX.lock();
let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
assert!(master_fd.as_raw_fd() > 0);
let slave_name = unsafe { ptsname(&master_fd) }.unwrap();
let slave_name_r = ptsname_r(&master_fd).unwrap();
assert_eq!(slave_name, slave_name_r);
}
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptsname_copy() {
let _m = crate::PTSNAME_MTX.lock();
let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
assert!(master_fd.as_raw_fd() > 0);
let slave_name1 = unsafe { ptsname(&master_fd) }.unwrap();
let slave_name2 = unsafe { ptsname(&master_fd) }.unwrap();
assert_eq!(slave_name1, slave_name2);
assert_ne!(slave_name1.as_ptr(), slave_name2.as_ptr());
}
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptsname_r_copy() {
let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
assert!(master_fd.as_raw_fd() > 0);
let slave_name1 = ptsname_r(&master_fd).unwrap();
let slave_name2 = ptsname_r(&master_fd).unwrap();
assert_eq!(slave_name1, slave_name2);
assert_ne!(slave_name1.as_ptr(), slave_name2.as_ptr());
}
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptsname_unique() {
let _m = crate::PTSNAME_MTX.lock();
let master1_fd = posix_openpt(OFlag::O_RDWR).unwrap();
assert!(master1_fd.as_raw_fd() > 0);
let master2_fd = posix_openpt(OFlag::O_RDWR).unwrap();
assert!(master2_fd.as_raw_fd() > 0);
let slave_name1 = unsafe { ptsname(&master1_fd) }.unwrap();
let slave_name2 = unsafe { ptsname(&master2_fd) }.unwrap();
assert_ne!(slave_name1, slave_name2);
}
fn open_ptty_pair() -> (PtyMaster, File) {
let _m = crate::PTSNAME_MTX.lock();
let master = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
grantpt(&master).expect("grantpt failed");
unlockpt(&master).expect("unlockpt failed");
let slave_name = unsafe { ptsname(&master) }.expect("ptsname failed");
let slave_fd =
open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty())
.unwrap();
#[cfg(target_os = "illumos")]
#[allow(clippy::comparison_chain)]
{
use libc::{ioctl, I_FIND, I_PUSH};
let ptem = b"ptem\0";
let ldterm = b"ldterm\0";
let r = unsafe { ioctl(slave_fd, I_FIND, ldterm.as_ptr()) };
if r < 0 {
panic!("I_FIND failure");
} else if r == 0 {
if unsafe { ioctl(slave_fd, I_PUSH, ptem.as_ptr()) } < 0 {
panic!("I_PUSH ptem failure");
}
if unsafe { ioctl(slave_fd, I_PUSH, ldterm.as_ptr()) } < 0 {
panic!("I_PUSH ldterm failure");
}
}
}
let slave = unsafe { File::from_raw_fd(slave_fd) };
(master, slave)
}
#[test]
fn test_open_ptty_pair() {
let (master, slave) = open_ptty_pair();
assert!(master.as_raw_fd() > 0);
assert!(slave.as_raw_fd() > 0);
}
fn make_raw(fd: RawFd) {
let mut termios = tcgetattr(fd).unwrap();
cfmakeraw(&mut termios);
tcsetattr(fd, SetArg::TCSANOW, &termios).unwrap();
}
#[test]
fn test_read_ptty_pair() {
let (mut master, mut slave) = open_ptty_pair();
make_raw(slave.as_raw_fd());
let mut buf = [0u8; 5];
slave.write_all(b"hello").unwrap();
master.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"hello");
let mut master = &master;
slave.write_all(b"hello").unwrap();
master.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"hello");
}
#[test]
fn test_write_ptty_pair() {
let (mut master, mut slave) = open_ptty_pair();
make_raw(slave.as_raw_fd());
let mut buf = [0u8; 5];
master.write_all(b"adios").unwrap();
slave.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"adios");
let mut master = &master;
master.write_all(b"adios").unwrap();
slave.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"adios");
}
#[test]
fn test_openpty() {
let _m = crate::PTSNAME_MTX.lock();
let pty = openpty(None, None).unwrap();
assert!(pty.master > 0);
assert!(pty.slave > 0);
let string = "foofoofoo\n";
let mut buf = [0u8; 10];
write(pty.master, string.as_bytes()).unwrap();
crate::read_exact(pty.slave, &mut buf);
assert_eq!(&buf, string.as_bytes());
let echoed_string = "foofoofoo\r\n";
let mut buf = [0u8; 11];
crate::read_exact(pty.master, &mut buf);
assert_eq!(&buf, echoed_string.as_bytes());
let string2 = "barbarbarbar\n";
let echoed_string2 = "barbarbarbar\r\n";
let mut buf = [0u8; 14];
write(pty.slave, string2.as_bytes()).unwrap();
crate::read_exact(pty.master, &mut buf);
assert_eq!(&buf, echoed_string2.as_bytes());
close(pty.master).unwrap();
close(pty.slave).unwrap();
}
#[test]
fn test_openpty_with_termios() {
let _m = crate::PTSNAME_MTX.lock();
let mut termios = {
let pty = openpty(None, None).unwrap();
assert!(pty.master > 0);
assert!(pty.slave > 0);
let termios = tcgetattr(pty.slave).unwrap();
close(pty.master).unwrap();
close(pty.slave).unwrap();
termios
};
termios.output_flags.remove(OutputFlags::ONLCR);
let pty = openpty(None, &termios).unwrap();
assert!(pty.master > 0);
assert!(pty.slave > 0);
let string = "foofoofoo\n";
let mut buf = [0u8; 10];
write(pty.master, string.as_bytes()).unwrap();
crate::read_exact(pty.slave, &mut buf);
assert_eq!(&buf, string.as_bytes());
let echoed_string = "foofoofoo\n";
crate::read_exact(pty.master, &mut buf);
assert_eq!(&buf, echoed_string.as_bytes());
let string2 = "barbarbarbar\n";
let echoed_string2 = "barbarbarbar\n";
let mut buf = [0u8; 13];
write(pty.slave, string2.as_bytes()).unwrap();
crate::read_exact(pty.master, &mut buf);
assert_eq!(&buf, echoed_string2.as_bytes());
close(pty.master).unwrap();
close(pty.slave).unwrap();
}
#[test]
fn test_forkpty() {
use nix::sys::signal::*;
use nix::sys::wait::wait;
use nix::unistd::ForkResult::*;
let _m0 = crate::PTSNAME_MTX.lock();
let _m1 = crate::FORK_MTX.lock();
let string = "naninani\n";
let echoed_string = "naninani\r\n";
let pty = unsafe { forkpty(None, None).unwrap() };
match pty.fork_result {
Child => {
write(STDOUT_FILENO, string.as_bytes()).unwrap();
pause(); unsafe {
_exit(0);
}
}
Parent { child } => {
let mut buf = [0u8; 10];
assert!(child.as_raw() > 0);
crate::read_exact(pty.master, &mut buf);
kill(child, SIGTERM).unwrap();
wait().unwrap(); assert_eq!(&buf, echoed_string.as_bytes());
close(pty.master).unwrap();
}
}
}