use std::fs::File; use std::io::{stdout, Read, Write}; use std::os::unix::prelude::*; use std::path::Path;
use libc::_exit; use nix::fcntl::{open, OFlag}; use nix::pty::*; use nix::sys::stat; use nix::sys::termios::*; use nix::sys::wait::WaitStatus; use nix::unistd::{pause, write};
/// Test equivalence of `ptsname` and `ptsname_r` #[test] #[cfg(linux_android)] fn test_ptsname_equivalence() { let _m = crate::PTSNAME_MTX.lock();
// Open a new PTY master let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
assert!(master_fd.as_raw_fd() > 0);
assert!(master_fd.as_fd().as_raw_fd() == master_fd.as_raw_fd());
// Get the name of the slave 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 data copying of `ptsname` // TODO need to run in a subprocess, since ptsname is non-reentrant #[test] #[cfg(linux_android)] fn test_ptsname_copy() { let _m = crate::PTSNAME_MTX.lock();
// Open a new PTTY master let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
// Get the name of the slave let slave_name1 = unsafe { ptsname(&master_fd) }.unwrap(); let slave_name2 = unsafe { ptsname(&master_fd) }.unwrap();
assert_eq!(slave_name1, slave_name2); // Also make sure that the string was actually copied and they point to different parts of // memory.
assert_ne!(slave_name1.as_ptr(), slave_name2.as_ptr());
}
/// Test data copying of `ptsname_r` #[test] #[cfg(linux_android)] fn test_ptsname_r_copy() { // Open a new PTTY master let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
// Get the name of the slave 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 that `ptsname` returns different names for different devices #[test] #[cfg(linux_android)] fn test_ptsname_unique() { let _m = crate::PTSNAME_MTX.lock();
// Open a new PTTY master let master1_fd = posix_openpt(OFlag::O_RDWR).unwrap();
// Open a second PTTY master let master2_fd = posix_openpt(OFlag::O_RDWR).unwrap();
// Get the name of the slave let slave_name1 = unsafe { ptsname(&master1_fd) }.unwrap(); let slave_name2 = unsafe { ptsname(&master2_fd) }.unwrap();
assert_ne!(slave_name1, slave_name2);
}
/// Common setup for testing PTTY pairs fn open_ptty_pair() -> (PtyMaster, File) { let _m = crate::PTSNAME_MTX.lock();
// Open a new PTTY master let master = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
// Allow a slave to be generated for it
grantpt(&master).expect("grantpt failed");
unlockpt(&master).expect("unlockpt failed");
// Get the name of the slave let slave_name = unsafe { ptsname(&master) }.expect("ptsname failed");
// Open the slave device let slave_fd =
open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty())
.unwrap();
#[cfg(solarish)] // TODO: rewrite using ioctl! #[allow(clippy::comparison_chain)]
{ use libc::{ioctl, I_FIND, I_PUSH};
// On illumos systems, as per pts(7D), one must push STREAMS modules // after opening a device path returned from ptsname(). 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");
} elseif r == 0 { ifunsafe { ioctl(slave_fd, I_PUSH, ptem.as_ptr()) } < 0 {
panic!("I_PUSH ptem failure");
} ifunsafe { 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 opening a master/slave PTTY pair /// /// This uses a common `open_ptty_pair` because much of these functions aren't useful by /// themselves. So for this test we perform the basic act of getting a file handle for a /// master/slave PTTY pair. #[test] fn test_open_ptty_pair() { let (_, _) = open_ptty_pair();
}
/// Put the terminal in raw mode. fn make_raw<Fd: AsFd>(fd: Fd) { letmut termios = tcgetattr(&fd).unwrap();
cfmakeraw(&mut termios);
tcsetattr(&fd, SetArg::TCSANOW, &termios).unwrap();
}
/// Test `io::Read` on the PTTY master #[test] fn test_read_ptty_pair() { let (mut master, mut slave) = open_ptty_pair();
make_raw(&slave);
// Writing to one should be readable on the other one let string = "foofoofoo\n"; letmut buf = [0u8; 10];
write(&pty.master, string.as_bytes()).unwrap(); crate::read_exact(&pty.slave, &mut buf);
assert_eq!(&buf, string.as_bytes());
// Read the echo as well let echoed_string = "foofoofoo\r\n"; letmut 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"; letmut buf = [0u8; 14];
write(&pty.slave, string2.as_bytes()).unwrap(); crate::read_exact(&pty.master, &mut buf);
// Open one pty to get attributes for the second one letmut termios = { let pty = openpty(None, None).unwrap();
tcgetattr(&pty.slave).unwrap()
}; // Make sure newlines are not transformed so the data is preserved when sent.
termios.output_flags.remove(OutputFlags::ONLCR);
let pty = openpty(None, &termios).unwrap(); // Must be valid file descriptors
// Writing to one should be readable on the other one let string = "foofoofoo\n"; letmut buf = [0u8; 10];
write(&pty.master, string.as_bytes()).unwrap(); crate::read_exact(&pty.slave, &mut buf);
assert_eq!(&buf, string.as_bytes());
// read the echo as well 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"; letmut buf = [0u8; 13];
write(&pty.slave, string2.as_bytes()).unwrap(); crate::read_exact(&pty.master, &mut buf);
assert_eq!(&buf, echoed_string2.as_bytes());
}
#[test] fn test_forkpty() { use nix::sys::signal::*; use nix::sys::wait::wait; // forkpty calls openpty which uses ptname(3) internally. let _m0 = crate::PTSNAME_MTX.lock(); // forkpty spawns a child process let _m1 = crate::FORK_MTX.lock();
let string = "naninani\n"; let echoed_string = "naninani\r\n"; let res = unsafe { forkpty(None, None).unwrap() }; match res {
ForkptyResult::Child => {
write(stdout(), string.as_bytes()).unwrap();
pause(); // we need the child to stay alive until the parent calls read unsafe {
_exit(0);
}
}
ForkptyResult::Parent { child, master } => { letmut buf = [0u8; 10];
assert!(child.as_raw() > 0); crate::read_exact(&master, &mut buf);
kill(child, SIGTERM).unwrap(); let status = wait().unwrap(); // keep other tests using generic wait from getting our child
assert_eq!(status, WaitStatus::Signaled(child, SIGTERM, false));
assert_eq!(&buf, echoed_string.as_bytes());
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.