Skip to content

impl io::{Read,Write} for PtyMaster #1210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/pty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub use libc::pid_t as SessionId;
pub use libc::winsize as Winsize;

use std::ffi::CStr;
use std::io;
use std::mem;
use std::os::unix::prelude::*;

Expand Down Expand Up @@ -77,6 +78,21 @@ impl Drop for PtyMaster {
}
}

impl io::Read for PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
::unistd::read(self.0, buf).map_err(|e| e.as_errno().unwrap().into())
}
}

impl io::Write for PtyMaster {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
::unistd::write(self.0, buf).map_err(|e| e.as_errno().unwrap().into())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}

/// Grant access to a slave pseudoterminal (see
/// [`grantpt(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html))
///
Expand Down
68 changes: 54 additions & 14 deletions test/test_pty.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::io::Write;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::os::unix::prelude::*;
use tempfile::tempfile;
Expand Down Expand Up @@ -95,29 +96,68 @@ fn test_ptsname_unique() {
assert!(slave_name1 != slave_name2);
}

/// Test opening a master/slave PTTY pair
///
/// This is a single larger test 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 connect master/slave PTTY
/// pair.
#[test]
fn test_open_ptty_pair() {
/// Common setup for testing PTTY pairs
fn open_ptty_pair() -> (PtyMaster, File) {
let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");

// Open a new PTTY master
let master_fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
assert!(master_fd.as_raw_fd() > 0);
let master = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");

// Allow a slave to be generated for it
grantpt(&master_fd).expect("grantpt failed");
unlockpt(&master_fd).expect("unlockpt failed");
grantpt(&master).expect("grantpt failed");
unlockpt(&master).expect("unlockpt failed");

// Get the name of the slave
let slave_name = unsafe { ptsname(&master_fd) }.expect("ptsname failed");
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();
assert!(slave_fd > 0);
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, then just sanity-check the raw values.
#[test]
fn test_open_ptty_pair() {
let (master, slave) = open_ptty_pair();
assert!(master.as_raw_fd() > 0);
assert!(slave.as_raw_fd() > 0);
}

/// Put the terminal in raw mode.
fn make_raw(fd: RawFd) {
let mut 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.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");
}

/// Test `io::Write` on the PTTY master
#[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");
}

#[test]
Expand Down