Skip to content

Commit

Permalink
Add openpty()
Browse files Browse the repository at this point in the history
  • Loading branch information
cactorium committed Apr 23, 2017
1 parent 7b5dd78 commit 736bf3d
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#582](https://github.com/nix-rust/nix/pull/582)
- Added `nix::unistd::{openat, fstatat, readlink, readlinkat}`
([#551](https://github.com/nix-rust/nix/pull/551))
- 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
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
52 changes: 52 additions & 0 deletions src/pty.rs
Original file line number Diff line number Diff line change
@@ -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<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)
};

let _ = try!(Errno::result(ret));

Ok(OpenptyResult {
master: master,
slave: slave,
})
}
1 change: 1 addition & 0 deletions test/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
81 changes: 81 additions & 0 deletions test/test_pty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use nix::pty::openpty;
use nix::sys::termios::*;
use nix::unistd::{read, write, close};

#[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();
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\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 736bf3d

Please sign in to comment.