Skip to content

Commit 9c497e0

Browse files
bors[bot]cuviper
andauthored
Merge #1210
1210: impl io::{Read,Write} for PtyMaster r=asomers a=cuviper `PtyMaster` acts like an owned file descriptor, even closing on `Drop`. Implementing `io::Read` and `io::Write` lets it be used directly in standard I/O operations. Co-authored-by: Josh Stone <[email protected]>
2 parents f54b5a8 + 5f7f9d8 commit 9c497e0

File tree

2 files changed

+70
-14
lines changed

2 files changed

+70
-14
lines changed

src/pty.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub use libc::pid_t as SessionId;
66
pub use libc::winsize as Winsize;
77

88
use std::ffi::CStr;
9+
use std::io;
910
use std::mem;
1011
use std::os::unix::prelude::*;
1112

@@ -77,6 +78,21 @@ impl Drop for PtyMaster {
7778
}
7879
}
7980

81+
impl io::Read for PtyMaster {
82+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
83+
::unistd::read(self.0, buf).map_err(|e| e.as_errno().unwrap().into())
84+
}
85+
}
86+
87+
impl io::Write for PtyMaster {
88+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
89+
::unistd::write(self.0, buf).map_err(|e| e.as_errno().unwrap().into())
90+
}
91+
fn flush(&mut self) -> io::Result<()> {
92+
Ok(())
93+
}
94+
}
95+
8096
/// Grant access to a slave pseudoterminal (see
8197
/// [`grantpt(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html))
8298
///

test/test_pty.rs

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use std::io::Write;
1+
use std::fs::File;
2+
use std::io::{Read, Write};
23
use std::path::Path;
34
use std::os::unix::prelude::*;
45
use tempfile::tempfile;
@@ -95,29 +96,68 @@ fn test_ptsname_unique() {
9596
assert!(slave_name1 != slave_name2);
9697
}
9798

98-
/// Test opening a master/slave PTTY pair
99-
///
100-
/// This is a single larger test because much of these functions aren't useful by themselves. So for
101-
/// this test we perform the basic act of getting a file handle for a connect master/slave PTTY
102-
/// pair.
103-
#[test]
104-
fn test_open_ptty_pair() {
99+
/// Common setup for testing PTTY pairs
100+
fn open_ptty_pair() -> (PtyMaster, File) {
105101
let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
106102

107103
// Open a new PTTY master
108-
let master_fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
109-
assert!(master_fd.as_raw_fd() > 0);
104+
let master = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
110105

111106
// Allow a slave to be generated for it
112-
grantpt(&master_fd).expect("grantpt failed");
113-
unlockpt(&master_fd).expect("unlockpt failed");
107+
grantpt(&master).expect("grantpt failed");
108+
unlockpt(&master).expect("unlockpt failed");
114109

115110
// Get the name of the slave
116-
let slave_name = unsafe { ptsname(&master_fd) }.expect("ptsname failed");
111+
let slave_name = unsafe { ptsname(&master) }.expect("ptsname failed");
117112

118113
// Open the slave device
119114
let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty()).unwrap();
120-
assert!(slave_fd > 0);
115+
let slave = unsafe { File::from_raw_fd(slave_fd) };
116+
117+
(master, slave)
118+
}
119+
120+
/// Test opening a master/slave PTTY pair
121+
///
122+
/// This uses a common `open_ptty_pair` because much of these functions aren't useful by
123+
/// themselves. So for this test we perform the basic act of getting a file handle for a
124+
/// master/slave PTTY pair, then just sanity-check the raw values.
125+
#[test]
126+
fn test_open_ptty_pair() {
127+
let (master, slave) = open_ptty_pair();
128+
assert!(master.as_raw_fd() > 0);
129+
assert!(slave.as_raw_fd() > 0);
130+
}
131+
132+
/// Put the terminal in raw mode.
133+
fn make_raw(fd: RawFd) {
134+
let mut termios = tcgetattr(fd).unwrap();
135+
cfmakeraw(&mut termios);
136+
tcsetattr(fd, SetArg::TCSANOW, &termios).unwrap();
137+
}
138+
139+
/// Test `io::Read` on the PTTY master
140+
#[test]
141+
fn test_read_ptty_pair() {
142+
let (mut master, mut slave) = open_ptty_pair();
143+
make_raw(slave.as_raw_fd());
144+
145+
let mut buf = [0u8; 5];
146+
slave.write_all(b"hello").unwrap();
147+
master.read_exact(&mut buf).unwrap();
148+
assert_eq!(&buf, b"hello");
149+
}
150+
151+
/// Test `io::Write` on the PTTY master
152+
#[test]
153+
fn test_write_ptty_pair() {
154+
let (mut master, mut slave) = open_ptty_pair();
155+
make_raw(slave.as_raw_fd());
156+
157+
let mut buf = [0u8; 5];
158+
master.write_all(b"adios").unwrap();
159+
slave.read_exact(&mut buf).unwrap();
160+
assert_eq!(&buf, b"adios");
121161
}
122162

123163
#[test]

0 commit comments

Comments
 (0)