Skip to content

Commit 736bf3d

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

File tree

5 files changed

+138
-0
lines changed

5 files changed

+138
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1010
([#582](https://github.com/nix-rust/nix/pull/582)
1111
- Added `nix::unistd::{openat, fstatat, readlink, readlinkat}`
1212
([#551](https://github.com/nix-rust/nix/pull/551))
13+
- Added `nix::ptr::openpty`
14+
([#456](https://github.com/nix-rust/nix/pull/456))
1315

1416
### Changed
1517
- Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe.

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: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use nix::pty::openpty;
2+
use nix::sys::termios::*;
3+
use nix::unistd::{read, write, close};
4+
5+
#[test]
6+
fn test_openpty() {
7+
let pty = openpty(None, None).unwrap();
8+
assert!(pty.master > 0);
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+
}
37+
38+
#[test]
39+
fn test_openpty_with_termios() {
40+
// open one pty to get attributes for the second one
41+
let mut termios = {
42+
let pty = openpty(None, None).unwrap();
43+
assert!(pty.master > 0);
44+
assert!(pty.slave > 0);
45+
let termios = tcgetattr(pty.master).unwrap();
46+
close(pty.master).unwrap();
47+
close(pty.slave).unwrap();
48+
termios
49+
};
50+
termios.c_oflag &= !ONLCR;
51+
52+
let pty = openpty(None, &termios).unwrap();
53+
assert!(pty.master > 0); // must be valid file descriptors
54+
assert!(pty.slave > 0);
55+
56+
// writing to one should be readable on the other one
57+
let string = "foofoofoo\n";
58+
let mut buf = [0u8; 16];
59+
write(pty.master, string.as_bytes()).unwrap();
60+
let len = read(pty.slave, &mut buf).unwrap();
61+
62+
assert_eq!(len, string.len());
63+
assert_eq!(&buf[0..len], string.as_bytes());
64+
65+
// read the echo as well
66+
let echoed_string = "foofoofoo\n";
67+
let len = read(pty.master, &mut buf).unwrap();
68+
assert_eq!(len, echoed_string.len());
69+
assert_eq!(&buf[0..len], echoed_string.as_bytes());
70+
71+
let string2 = "barbarbarbar\n";
72+
let echoed_string2 = "barbarbarbar\n";
73+
write(pty.slave, string2.as_bytes()).unwrap();
74+
let len = read(pty.master, &mut buf).unwrap();
75+
76+
assert_eq!(len, echoed_string2.len());
77+
assert_eq!(&buf[0..len], echoed_string2.as_bytes());
78+
79+
close(pty.master).unwrap();
80+
close(pty.slave).unwrap();
81+
}

0 commit comments

Comments
 (0)