Skip to content

Commit

Permalink
Add openpty()
Browse files Browse the repository at this point in the history
  • Loading branch information
cactorium committed Jun 12, 2017
1 parent f37b0d3 commit 45b7b1b
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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.
Expand Down
56 changes: 54 additions & 2 deletions src/pty.rs
Original file line number Diff line number Diff line change
@@ -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
///
Expand Down Expand Up @@ -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<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(winsize: T, termios: U) -> Result<OpenptyResult> {
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,
})
}
12 changes: 5 additions & 7 deletions test/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
82 changes: 82 additions & 0 deletions test/test_pty.rs
Original file line number Diff line number Diff line change
@@ -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]
Expand Down Expand Up @@ -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();
}

0 comments on commit 45b7b1b

Please sign in to comment.