1 use std::io::Write;
2 use std::path::Path;
3 use std::os::unix::prelude::*;
4 use tempfile::tempfile;
5 
6 use libc::{_exit, STDOUT_FILENO};
7 use nix::fcntl::{OFlag, open};
8 use nix::pty::*;
9 use nix::sys::stat;
10 use nix::sys::termios::*;
11 use nix::unistd::{write, close, pause};
12 
13 /// Regression test for Issue #659
14 /// This is the correct way to explicitly close a `PtyMaster`
15 #[test]
test_explicit_close()16 fn test_explicit_close() {
17     let mut f = {
18         let m = posix_openpt(OFlag::O_RDWR).unwrap();
19         close(m.into_raw_fd()).unwrap();
20         tempfile().unwrap()
21     };
22     // This should work.  But if there's been a double close, then it will
23     // return EBADF
24     f.write_all(b"whatever").unwrap();
25 }
26 
27 /// Test equivalence of `ptsname` and `ptsname_r`
28 #[test]
29 #[cfg(any(target_os = "android", target_os = "linux"))]
test_ptsname_equivalence()30 fn test_ptsname_equivalence() {
31     let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
32 
33     // Open a new PTTY master
34     let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
35     assert!(master_fd.as_raw_fd() > 0);
36 
37     // Get the name of the slave
38     let slave_name = unsafe { ptsname(&master_fd) }.unwrap() ;
39     let slave_name_r = ptsname_r(&master_fd).unwrap();
40     assert_eq!(slave_name, slave_name_r);
41 }
42 
43 /// Test data copying of `ptsname`
44 // TODO need to run in a subprocess, since ptsname is non-reentrant
45 #[test]
46 #[cfg(any(target_os = "android", target_os = "linux"))]
test_ptsname_copy()47 fn test_ptsname_copy() {
48     let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
49 
50     // Open a new PTTY master
51     let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
52     assert!(master_fd.as_raw_fd() > 0);
53 
54     // Get the name of the slave
55     let slave_name1 = unsafe { ptsname(&master_fd) }.unwrap();
56     let slave_name2 = unsafe { ptsname(&master_fd) }.unwrap();
57     assert_eq!(slave_name1, slave_name2);
58     // Also make sure that the string was actually copied and they point to different parts of
59     // memory.
60     assert!(slave_name1.as_ptr() != slave_name2.as_ptr());
61 }
62 
63 /// Test data copying of `ptsname_r`
64 #[test]
65 #[cfg(any(target_os = "android", target_os = "linux"))]
test_ptsname_r_copy()66 fn test_ptsname_r_copy() {
67     // Open a new PTTY master
68     let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
69     assert!(master_fd.as_raw_fd() > 0);
70 
71     // Get the name of the slave
72     let slave_name1 = ptsname_r(&master_fd).unwrap();
73     let slave_name2 = ptsname_r(&master_fd).unwrap();
74     assert_eq!(slave_name1, slave_name2);
75     assert!(slave_name1.as_ptr() != slave_name2.as_ptr());
76 }
77 
78 /// Test that `ptsname` returns different names for different devices
79 #[test]
80 #[cfg(any(target_os = "android", target_os = "linux"))]
test_ptsname_unique()81 fn test_ptsname_unique() {
82     let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
83 
84     // Open a new PTTY master
85     let master1_fd = posix_openpt(OFlag::O_RDWR).unwrap();
86     assert!(master1_fd.as_raw_fd() > 0);
87 
88     // Open a second PTTY master
89     let master2_fd = posix_openpt(OFlag::O_RDWR).unwrap();
90     assert!(master2_fd.as_raw_fd() > 0);
91 
92     // Get the name of the slave
93     let slave_name1 = unsafe { ptsname(&master1_fd) }.unwrap();
94     let slave_name2 = unsafe { ptsname(&master2_fd) }.unwrap();
95     assert!(slave_name1 != slave_name2);
96 }
97 
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]
test_open_ptty_pair()104 fn test_open_ptty_pair() {
105     let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
106 
107     // 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);
110 
111     // Allow a slave to be generated for it
112     grantpt(&master_fd).expect("grantpt failed");
113     unlockpt(&master_fd).expect("unlockpt failed");
114 
115     // Get the name of the slave
116     let slave_name = unsafe { ptsname(&master_fd) }.expect("ptsname failed");
117 
118     // Open the slave device
119     let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty()).unwrap();
120     assert!(slave_fd > 0);
121 }
122 
123 #[test]
test_openpty()124 fn test_openpty() {
125     // openpty uses ptname(3) internally
126     let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
127 
128     let pty = openpty(None, None).unwrap();
129     assert!(pty.master > 0);
130     assert!(pty.slave > 0);
131 
132     // Writing to one should be readable on the other one
133     let string = "foofoofoo\n";
134     let mut buf = [0u8; 10];
135     write(pty.master, string.as_bytes()).unwrap();
136     ::read_exact(pty.slave, &mut buf);
137 
138     assert_eq!(&buf, string.as_bytes());
139 
140     // Read the echo as well
141     let echoed_string = "foofoofoo\r\n";
142     let mut buf = [0u8; 11];
143     ::read_exact(pty.master, &mut buf);
144     assert_eq!(&buf, echoed_string.as_bytes());
145 
146     let string2 = "barbarbarbar\n";
147     let echoed_string2 = "barbarbarbar\r\n";
148     let mut buf = [0u8; 14];
149     write(pty.slave, string2.as_bytes()).unwrap();
150     ::read_exact(pty.master, &mut buf);
151 
152     assert_eq!(&buf, echoed_string2.as_bytes());
153 
154     close(pty.master).unwrap();
155     close(pty.slave).unwrap();
156 }
157 
158 #[test]
test_openpty_with_termios()159 fn test_openpty_with_termios() {
160     // openpty uses ptname(3) internally
161     let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
162 
163     // Open one pty to get attributes for the second one
164     let mut termios = {
165         let pty = openpty(None, None).unwrap();
166         assert!(pty.master > 0);
167         assert!(pty.slave > 0);
168         let termios = tcgetattr(pty.master).unwrap();
169         close(pty.master).unwrap();
170         close(pty.slave).unwrap();
171         termios
172     };
173     // Make sure newlines are not transformed so the data is preserved when sent.
174     termios.output_flags.remove(OutputFlags::ONLCR);
175 
176     let pty = openpty(None, &termios).unwrap();
177     // Must be valid file descriptors
178     assert!(pty.master > 0);
179     assert!(pty.slave > 0);
180 
181     // Writing to one should be readable on the other one
182     let string = "foofoofoo\n";
183     let mut buf = [0u8; 10];
184     write(pty.master, string.as_bytes()).unwrap();
185     ::read_exact(pty.slave, &mut buf);
186 
187     assert_eq!(&buf, string.as_bytes());
188 
189     // read the echo as well
190     let echoed_string = "foofoofoo\n";
191     ::read_exact(pty.master, &mut buf);
192     assert_eq!(&buf, echoed_string.as_bytes());
193 
194     let string2 = "barbarbarbar\n";
195     let echoed_string2 = "barbarbarbar\n";
196     let mut buf = [0u8; 13];
197     write(pty.slave, string2.as_bytes()).unwrap();
198     ::read_exact(pty.master, &mut buf);
199 
200     assert_eq!(&buf, echoed_string2.as_bytes());
201 
202     close(pty.master).unwrap();
203     close(pty.slave).unwrap();
204 }
205 
206 #[test]
test_forkpty()207 fn test_forkpty() {
208     use nix::unistd::ForkResult::*;
209     use nix::sys::signal::*;
210     use nix::sys::wait::wait;
211     // forkpty calls openpty which uses ptname(3) internally.
212     let _m0 = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
213     // forkpty spawns a child process
214     let _m1 = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
215 
216     let string = "naninani\n";
217     let echoed_string = "naninani\r\n";
218     let pty = forkpty(None, None).unwrap();
219     match pty.fork_result {
220         Child => {
221             write(STDOUT_FILENO, string.as_bytes()).unwrap();
222             pause();  // we need the child to stay alive until the parent calls read
223             unsafe { _exit(0); }
224         },
225         Parent { child } => {
226             let mut buf = [0u8; 10];
227             assert!(child.as_raw() > 0);
228             ::read_exact(pty.master, &mut buf);
229             kill(child, SIGTERM).unwrap();
230             wait().unwrap(); // keep other tests using generic wait from getting our child
231             assert_eq!(&buf, echoed_string.as_bytes());
232             close(pty.master).unwrap();
233         },
234     }
235 }
236