Skip to content

Commit b6ed40d

Browse files
committed
Add openpty()
1 parent 7b5dd78 commit b6ed40d

File tree

4 files changed

+91
-0
lines changed

4 files changed

+91
-0
lines changed

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ pub mod mount;
4444
#[cfg(target_os = "linux")]
4545
pub mod mqueue;
4646

47+
pub mod pty;
48+
4749
#[cfg(any(target_os = "linux", target_os = "macos"))]
4850
pub mod poll;
4951

src/pty.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use libc;
2+
3+
use {Errno, Result};
4+
use std::os::unix::io::RawFd;
5+
6+
use sys::termios::Termios;
7+
8+
pub use libc::pid_t as SessionId;
9+
pub use libc::winsize as Winsize;
10+
11+
pub struct OpenptyResult {
12+
pub master: RawFd,
13+
pub slave: RawFd,
14+
}
15+
16+
/// Create a new pseudoterminal, returning the slave and master file descriptors
17+
/// in `OpenptyResult`
18+
/// (see [openpty](http://man7.org/linux/man-pages/man3/openpty.3.html)).
19+
///
20+
/// If `winsize` is not `None`, the window size of the slave will be set to
21+
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
22+
/// terminal settings of the slave will be set to the values in `termios`.
23+
#[inline]
24+
pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(winsize: T, termios: U) -> Result<OpenptyResult> {
25+
use std::ptr;
26+
27+
let mut slave: libc::c_int = -1;
28+
let mut master: libc::c_int = -1;
29+
let c_termios = match termios.into() {
30+
Some(termios) => termios as *const Termios,
31+
None => ptr::null() as *const Termios,
32+
};
33+
let c_winsize = match winsize.into() {
34+
Some(ws) => ws as *const Winsize,
35+
None => ptr::null() as *const Winsize,
36+
};
37+
let ret = unsafe {
38+
libc::openpty(
39+
&mut master as *mut libc::c_int,
40+
&mut slave as *mut libc::c_int,
41+
ptr::null_mut(),
42+
c_termios as *mut libc::termios,
43+
c_winsize as *mut Winsize)
44+
};
45+
46+
let _ = try!(Errno::result(ret));
47+
48+
Ok(OpenptyResult {
49+
master: master,
50+
slave: slave,
51+
})
52+
}

test/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod sys;
1313
mod test_fcntl;
1414
mod test_net;
1515
mod test_nix_path;
16+
mod test_pty;
1617
#[cfg(any(target_os = "linux", target_os = "android"))]
1718
mod test_sendfile;
1819
mod test_stat;

test/test_pty.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use nix::pty::openpty;
2+
use nix::unistd::{read, write, close};
3+
4+
#[test]
5+
fn test_openpty() {
6+
// TODO: figure out the right termios settings to pass in here
7+
let pty = openpty(None, None).unwrap();
8+
assert!(pty.master > 0); // must be valid file descriptors
9+
assert!(pty.slave > 0);
10+
11+
// writing to one should be readable on the other one
12+
let string = "foofoofoo\n";
13+
let mut buf = [0u8; 16];
14+
write(pty.master, string.as_bytes()).unwrap();
15+
let len = read(pty.slave, &mut buf).unwrap();
16+
17+
assert_eq!(len, string.len());
18+
assert_eq!(&buf[0..len], string.as_bytes());
19+
20+
// read the echo as well
21+
let echoed_string = "foofoofoo\r\n";
22+
let len = read(pty.master, &mut buf).unwrap();
23+
assert_eq!(len, echoed_string.len());
24+
assert_eq!(&buf[0..len], echoed_string.as_bytes());
25+
26+
let string2 = "barbarbarbar\n";
27+
let echoed_string2 = "barbarbarbar\r\n";
28+
write(pty.slave, string2.as_bytes()).unwrap();
29+
let len = read(pty.master, &mut buf).unwrap();
30+
31+
assert_eq!(len, echoed_string2.len());
32+
assert_eq!(&buf[0..len], echoed_string2.as_bytes());
33+
34+
close(pty.master).unwrap();
35+
close(pty.slave).unwrap();
36+
}

0 commit comments

Comments
 (0)