Skip to content

Commit 6783ffc

Browse files
committed
Merge #456
456: Add openpty r=Susurrus Lemme know if anything needs to be fixed
2 parents f37b0d3 + 45b7b1b commit 6783ffc

File tree

4 files changed

+143
-9
lines changed

4 files changed

+143
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1616
([#551](https://github.com/nix-rust/nix/pull/551))
1717
- Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}`
1818
([#556](https://github.com/nix-rust/nix/pull/556)
19+
- Added `nix::ptr::openpty`
20+
([#456](https://github.com/nix-rust/nix/pull/456))
1921

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

src/pty.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
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::*;
611

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

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

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

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

test/test.rs

Lines changed: 5 additions & 7 deletions
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

Lines changed: 82 additions & 0 deletions
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)