Skip to content

Commit 54c46ff

Browse files
committed
Add openpty()
1 parent ba5c837 commit 54c46ff

File tree

4 files changed

+144
-9
lines changed

4 files changed

+144
-9
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1212
([#551](https://github.com/nix-rust/nix/pull/551))
1313
- Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}`
1414
([#556](https://github.com/nix-rust/nix/pull/556)
15+
- Added `nix::ptr::openpty`
16+
([#456](https://github.com/nix-rust/nix/pull/456))
1517

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

src/pty.rs

+55-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
11
//! Create master and slave virtual pseudo-terminals (PTYs)
22
3+
use libc;
4+
5+
pub use libc::pid_t as SessionId;
6+
pub use libc::winsize as Winsize;
7+
38
use std::ffi::CStr;
49
use std::mem;
510
use std::os::unix::prelude::*;
11+
use std::os::unix::io::RawFd;
612

7-
use libc;
13+
use sys::termios::Termios;
14+
use {Errno, Result, Error, fcntl};
15+
16+
/// Representation of a master/slave pty pair
17+
///
18+
/// This is returned by `openpty`
19+
pub struct OpenptyResult {
20+
pub master: RawFd,
21+
pub slave: RawFd,
22+
}
823

9-
use {Error, fcntl, Result};
1024

1125
/// Representation of the Master device in a master/slave pty pair
1226
///
@@ -162,3 +176,42 @@ pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
162176

163177
Ok(())
164178
}
179+
180+
181+
/// Create a new pseudoterminal, returning the slave and master file descriptors
182+
/// in `OpenptyResult`
183+
/// (see [openpty](http://man7.org/linux/man-pages/man3/openpty.3.html)).
184+
///
185+
/// If `winsize` is not `None`, the window size of the slave will be set to
186+
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
187+
/// terminal settings of the slave will be set to the values in `termios`.
188+
#[inline]
189+
pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(winsize: T, termios: U) -> Result<OpenptyResult> {
190+
use std::ptr;
191+
192+
let mut slave: libc::c_int = -1;
193+
let mut master: libc::c_int = -1;
194+
let c_termios = match termios.into() {
195+
Some(termios) => termios as *const Termios,
196+
None => ptr::null() as *const Termios,
197+
};
198+
let c_winsize = match winsize.into() {
199+
Some(ws) => ws as *const Winsize,
200+
None => ptr::null() as *const Winsize,
201+
};
202+
let ret = unsafe {
203+
libc::openpty(
204+
&mut master as *mut libc::c_int,
205+
&mut slave as *mut libc::c_int,
206+
ptr::null_mut(),
207+
c_termios as *mut libc::termios,
208+
c_winsize as *mut Winsize)
209+
};
210+
211+
Errno::result(ret)?;
212+
213+
Ok(OpenptyResult {
214+
master: master,
215+
slave: slave,
216+
})
217+
}

test/test.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,18 @@ extern crate nix_test as nixtest;
1111

1212
mod sys;
1313
mod test_fcntl;
14+
#[cfg(any(target_os = "linux"))]
15+
mod test_mq;
1416
mod test_net;
1517
mod test_nix_path;
18+
#[cfg(any(target_os = "linux", target_os = "macos"))]
19+
mod test_poll;
20+
mod test_pty;
1621
#[cfg(any(target_os = "linux", target_os = "android"))]
1722
mod test_sendfile;
1823
mod test_stat;
1924
mod test_unistd;
2025

21-
#[cfg(any(target_os = "linux"))]
22-
mod test_mq;
23-
24-
#[cfg(any(target_os = "linux", target_os = "macos"))]
25-
mod test_poll;
26-
mod test_pty;
27-
2826
use nixtest::assert_size_of;
2927

3028
#[test]

test/test_pty.rs

+82
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use std::path::Path;
22
use std::os::unix::prelude::*;
3+
34
use nix::fcntl::{O_RDWR, open};
45
use nix::pty::*;
56
use nix::sys::stat;
7+
use nix::sys::termios::*;
8+
use nix::unistd::{read, write, close};
69

710
/// Test equivalence of `ptsname` and `ptsname_r`
811
#[test]
@@ -90,3 +93,82 @@ fn test_open_ptty_pair() {
9093
let slave_fd = open(Path::new(&slave_name), O_RDWR, stat::Mode::empty()).unwrap();
9194
assert!(slave_fd > 0);
9295
}
96+
97+
#[test]
98+
fn test_openpty() {
99+
let pty = openpty(None, None).unwrap();
100+
assert!(pty.master > 0);
101+
assert!(pty.slave > 0);
102+
103+
// Writing to one should be readable on the other one
104+
let string = "foofoofoo\n";
105+
let mut buf = [0u8; 16];
106+
write(pty.master, string.as_bytes()).unwrap();
107+
let len = read(pty.slave, &mut buf).unwrap();
108+
109+
assert_eq!(len, string.len());
110+
assert_eq!(&buf[0..len], string.as_bytes());
111+
112+
// Read the echo as well
113+
let echoed_string = "foofoofoo\r\n";
114+
let len = read(pty.master, &mut buf).unwrap();
115+
assert_eq!(len, echoed_string.len());
116+
assert_eq!(&buf[0..len], echoed_string.as_bytes());
117+
118+
let string2 = "barbarbarbar\n";
119+
let echoed_string2 = "barbarbarbar\r\n";
120+
write(pty.slave, string2.as_bytes()).unwrap();
121+
let len = read(pty.master, &mut buf).unwrap();
122+
123+
assert_eq!(len, echoed_string2.len());
124+
assert_eq!(&buf[0..len], echoed_string2.as_bytes());
125+
126+
close(pty.master).unwrap();
127+
close(pty.slave).unwrap();
128+
}
129+
130+
#[test]
131+
fn test_openpty_with_termios() {
132+
// Open one pty to get attributes for the second one
133+
let mut termios = {
134+
let pty = openpty(None, None).unwrap();
135+
assert!(pty.master > 0);
136+
assert!(pty.slave > 0);
137+
let termios = tcgetattr(pty.master).unwrap();
138+
close(pty.master).unwrap();
139+
close(pty.slave).unwrap();
140+
termios
141+
};
142+
termios.c_oflag &= !ONLCR;
143+
144+
let pty = openpty(None, &termios).unwrap();
145+
// Must be valid file descriptors
146+
assert!(pty.master > 0);
147+
assert!(pty.slave > 0);
148+
149+
// Writing to one should be readable on the other one
150+
let string = "foofoofoo\n";
151+
let mut buf = [0u8; 16];
152+
write(pty.master, string.as_bytes()).unwrap();
153+
let len = read(pty.slave, &mut buf).unwrap();
154+
155+
assert_eq!(len, string.len());
156+
assert_eq!(&buf[0..len], string.as_bytes());
157+
158+
// read the echo as well
159+
let echoed_string = "foofoofoo\n";
160+
let len = read(pty.master, &mut buf).unwrap();
161+
assert_eq!(len, echoed_string.len());
162+
assert_eq!(&buf[0..len], echoed_string.as_bytes());
163+
164+
let string2 = "barbarbarbar\n";
165+
let echoed_string2 = "barbarbarbar\n";
166+
write(pty.slave, string2.as_bytes()).unwrap();
167+
let len = read(pty.master, &mut buf).unwrap();
168+
169+
assert_eq!(len, echoed_string2.len());
170+
assert_eq!(&buf[0..len], echoed_string2.as_bytes());
171+
172+
close(pty.master).unwrap();
173+
close(pty.slave).unwrap();
174+
}

0 commit comments

Comments
 (0)