From 66cd9af9156a0d251a1807de4de4fdc82465dbcd Mon Sep 17 00:00:00 2001 From: Kelvin Ly Date: Wed, 2 Nov 2016 09:26:29 -0400 Subject: [PATCH] Add openpty() --- CHANGELOG.md | 2 ++ src/pty.rs | 56 +++++++++++++++++++++++++++++++-- test/test.rs | 12 +++---- test/test_pty.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d59d217bd..8bcdeedb43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#551](https://github.com/nix-rust/nix/pull/551)) - Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` ([#556](https://github.com/nix-rust/nix/pull/556) +- Added `nix::ptr::openpty` + ([#456](https://github.com/nix-rust/nix/pull/456)) ### Changed - Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe. diff --git a/src/pty.rs b/src/pty.rs index 3e9c44ea68..c450e0588b 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -1,12 +1,25 @@ //! Create master and slave virtual pseudo-terminals (PTYs) +use libc; + +pub use libc::pid_t as SessionId; +pub use libc::winsize as Winsize; + use std::ffi::CStr; use std::mem; use std::os::unix::prelude::*; -use libc; +use sys::termios::Termios; +use {Errno, Result, Error, fcntl}; + +/// Representation of a master/slave pty pair +/// +/// This is returned by `openpty` +pub struct OpenptyResult { + pub master: RawFd, + pub slave: RawFd, +} -use {Error, fcntl, Result}; /// Representation of the Master device in a master/slave pty pair /// @@ -162,3 +175,42 @@ pub fn unlockpt(fd: &PtyMaster) -> Result<()> { Ok(()) } + + +/// Create a new pseudoterminal, returning the slave and master file descriptors +/// in `OpenptyResult` +/// (see [openpty](http://man7.org/linux/man-pages/man3/openpty.3.html)). +/// +/// If `winsize` is not `None`, the window size of the slave will be set to +/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's +/// terminal settings of the slave will be set to the values in `termios`. +#[inline] +pub fn openpty<'a, 'b, T: Into>, U: Into>>(winsize: T, termios: U) -> Result { + use std::ptr; + + let mut slave: libc::c_int = -1; + let mut master: libc::c_int = -1; + let c_termios = match termios.into() { + Some(termios) => termios as *const Termios, + None => ptr::null() as *const Termios, + }; + let c_winsize = match winsize.into() { + Some(ws) => ws as *const Winsize, + None => ptr::null() as *const Winsize, + }; + let ret = unsafe { + libc::openpty( + &mut master as *mut libc::c_int, + &mut slave as *mut libc::c_int, + ptr::null_mut(), + c_termios as *mut libc::termios, + c_winsize as *mut Winsize) + }; + + Errno::result(ret)?; + + Ok(OpenptyResult { + master: master, + slave: slave, + }) +} diff --git a/test/test.rs b/test/test.rs index 1f87171da5..e2f5a02444 100644 --- a/test/test.rs +++ b/test/test.rs @@ -11,20 +11,18 @@ extern crate nix_test as nixtest; mod sys; mod test_fcntl; +#[cfg(any(target_os = "linux"))] +mod test_mq; mod test_net; mod test_nix_path; +#[cfg(any(target_os = "linux", target_os = "macos"))] +mod test_poll; +mod test_pty; #[cfg(any(target_os = "linux", target_os = "android"))] mod test_sendfile; mod test_stat; mod test_unistd; -#[cfg(any(target_os = "linux"))] -mod test_mq; - -#[cfg(any(target_os = "linux", target_os = "macos"))] -mod test_poll; -mod test_pty; - use nixtest::assert_size_of; #[test] diff --git a/test/test_pty.rs b/test/test_pty.rs index 61780421b7..8b6ed2b870 100644 --- a/test/test_pty.rs +++ b/test/test_pty.rs @@ -1,8 +1,11 @@ use std::path::Path; use std::os::unix::prelude::*; + use nix::fcntl::{O_RDWR, open}; use nix::pty::*; use nix::sys::stat; +use nix::sys::termios::*; +use nix::unistd::{read, write, close}; /// Test equivalence of `ptsname` and `ptsname_r` #[test] @@ -90,3 +93,82 @@ fn test_open_ptty_pair() { let slave_fd = open(Path::new(&slave_name), O_RDWR, stat::Mode::empty()).unwrap(); assert!(slave_fd > 0); } + +#[test] +fn test_openpty() { + let pty = openpty(None, None).unwrap(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + + // Writing to one should be readable on the other one + let string = "foofoofoo\n"; + let mut buf = [0u8; 16]; + write(pty.master, string.as_bytes()).unwrap(); + let len = read(pty.slave, &mut buf).unwrap(); + + assert_eq!(len, string.len()); + assert_eq!(&buf[0..len], string.as_bytes()); + + // Read the echo as well + let echoed_string = "foofoofoo\r\n"; + let len = read(pty.master, &mut buf).unwrap(); + assert_eq!(len, echoed_string.len()); + assert_eq!(&buf[0..len], echoed_string.as_bytes()); + + let string2 = "barbarbarbar\n"; + let echoed_string2 = "barbarbarbar\r\n"; + write(pty.slave, string2.as_bytes()).unwrap(); + let len = read(pty.master, &mut buf).unwrap(); + + assert_eq!(len, echoed_string2.len()); + assert_eq!(&buf[0..len], echoed_string2.as_bytes()); + + close(pty.master).unwrap(); + close(pty.slave).unwrap(); +} + +#[test] +fn test_openpty_with_termios() { + // Open one pty to get attributes for the second one + let mut termios = { + let pty = openpty(None, None).unwrap(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + let termios = tcgetattr(pty.master).unwrap(); + close(pty.master).unwrap(); + close(pty.slave).unwrap(); + termios + }; + termios.c_oflag &= !ONLCR; + + let pty = openpty(None, &termios).unwrap(); + // Must be valid file descriptors + assert!(pty.master > 0); + assert!(pty.slave > 0); + + // Writing to one should be readable on the other one + let string = "foofoofoo\n"; + let mut buf = [0u8; 16]; + write(pty.master, string.as_bytes()).unwrap(); + let len = read(pty.slave, &mut buf).unwrap(); + + assert_eq!(len, string.len()); + assert_eq!(&buf[0..len], string.as_bytes()); + + // read the echo as well + let echoed_string = "foofoofoo\n"; + let len = read(pty.master, &mut buf).unwrap(); + assert_eq!(len, echoed_string.len()); + assert_eq!(&buf[0..len], echoed_string.as_bytes()); + + let string2 = "barbarbarbar\n"; + let echoed_string2 = "barbarbarbar\n"; + write(pty.slave, string2.as_bytes()).unwrap(); + let len = read(pty.master, &mut buf).unwrap(); + + assert_eq!(len, echoed_string2.len()); + assert_eq!(&buf[0..len], echoed_string2.as_bytes()); + + close(pty.master).unwrap(); + close(pty.slave).unwrap(); +}