From b6ed40dbe857f773eb1084310905fcd249550302 Mon Sep 17 00:00:00 2001 From: Kelvin Ly Date: Wed, 2 Nov 2016 09:26:29 -0400 Subject: [PATCH] Add openpty() --- src/lib.rs | 2 ++ src/pty.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ test/test.rs | 1 + test/test_pty.rs | 36 +++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+) create mode 100644 src/pty.rs create mode 100644 test/test_pty.rs diff --git a/src/lib.rs b/src/lib.rs index ecbf139dc6..56ed5c3fa8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,8 @@ pub mod mount; #[cfg(target_os = "linux")] pub mod mqueue; +pub mod pty; + #[cfg(any(target_os = "linux", target_os = "macos"))] pub mod poll; diff --git a/src/pty.rs b/src/pty.rs new file mode 100644 index 0000000000..d07e7684a5 --- /dev/null +++ b/src/pty.rs @@ -0,0 +1,52 @@ +use libc; + +use {Errno, Result}; +use std::os::unix::io::RawFd; + +use sys::termios::Termios; + +pub use libc::pid_t as SessionId; +pub use libc::winsize as Winsize; + +pub struct OpenptyResult { + pub master: RawFd, + pub slave: RawFd, +} + +/// 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) + }; + + let _ = try!(Errno::result(ret)); + + Ok(OpenptyResult { + master: master, + slave: slave, + }) +} diff --git a/test/test.rs b/test/test.rs index 1357642ef6..d73a021882 100644 --- a/test/test.rs +++ b/test/test.rs @@ -13,6 +13,7 @@ mod sys; mod test_fcntl; mod test_net; mod test_nix_path; +mod test_pty; #[cfg(any(target_os = "linux", target_os = "android"))] mod test_sendfile; mod test_stat; diff --git a/test/test_pty.rs b/test/test_pty.rs new file mode 100644 index 0000000000..1fcd4a891b --- /dev/null +++ b/test/test_pty.rs @@ -0,0 +1,36 @@ +use nix::pty::openpty; +use nix::unistd::{read, write, close}; + +#[test] +fn test_openpty() { + // TODO: figure out the right termios settings to pass in here + let pty = openpty(None, None).unwrap(); + assert!(pty.master > 0); // must be valid file descriptors + 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(); +}