1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <unittest/unittest.h>
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <poll.h>
10 #include <stdio.h>
11 #include <unistd.h>
12 
13 #include <lib/fdio/io.h>
14 
15 #include <zircon/device/pty.h>
16 
17 // returns an int to avoid sign errors from ASSERT_*()
fd_signals(int fd)18 static int fd_signals(int fd) {
19     uint32_t signals = 0;
20     fdio_wait_fd(fd, 0, &signals, 0);
21     return signals;
22 }
23 
write_full(int fd)24 static int write_full(int fd) {
25     char tmp[300];
26     memset(tmp, 0x33, sizeof(tmp));
27     int total = 0;
28     for (;;) {
29         int r = write(fd, tmp, sizeof(tmp));
30         if (r < 0) {
31             if (errno == EAGAIN) {
32                 break;
33             }
34             return -errno;
35         }
36         if (r == 0) {
37             break;
38         }
39         total += r;
40     }
41     return total;
42 }
43 
read_all(int fd)44 static int read_all(int fd) {
45     char tmp[700];
46     int total = 0;
47     for (;;) {
48         int r = read(fd, tmp, sizeof(tmp));
49         if (r < 0) {
50             if (errno == EAGAIN) {
51                 break;
52             }
53             return -errno;
54         }
55         if (r == 0) {
56             break;
57         }
58         for (int n = 0; n < r; n++) {
59             if (tmp[n] != 0x33) {
60                 return -EFAULT;
61             }
62         }
63         total += r;
64     }
65     return total;
66 }
67 
pty_test(void)68 static bool pty_test(void) {
69     BEGIN_TEST;
70 
71     int ps = open("/dev/misc/ptmx", O_RDWR | O_NONBLOCK);
72     ASSERT_GE(ps, 0, "");
73 
74     int pc = openat(ps, "0", O_RDWR | O_NONBLOCK);
75     ASSERT_GE(pc, 0, "");
76 
77     char tmp[32];
78 
79     ASSERT_EQ(fd_signals(ps), POLLOUT, "");
80     ASSERT_EQ(fd_signals(pc), POLLOUT, "");
81 
82     // nothing to read
83     ASSERT_EQ(read(ps, tmp, 32), -1, "");
84     ASSERT_EQ(errno, EAGAIN, "");
85     ASSERT_EQ(read(pc, tmp, 32), -1, "");
86     ASSERT_EQ(errno, EAGAIN, "");
87 
88     // write server, read client
89     ASSERT_EQ(write(ps, "xyzzy", 5), 5, "");
90     ASSERT_EQ(fd_signals(pc), POLLIN | POLLOUT, "");
91 
92     memset(tmp, 0xee, 5);
93     ASSERT_EQ(read(pc, tmp, 5), 5, "");
94     ASSERT_EQ(memcmp(tmp, "xyzzy", 5), 0, "");
95     ASSERT_EQ(fd_signals(pc), POLLOUT, "");
96 
97     // write client, read server
98     ASSERT_EQ(write(pc, "xyzzy", 5), 5, "");
99     ASSERT_EQ(fd_signals(ps), POLLIN | POLLOUT, "");
100 
101     memset(tmp, 0xee, 5);
102     ASSERT_EQ(read(ps, tmp, 5), 5, "");
103     ASSERT_EQ(memcmp(tmp, "xyzzy", 5), 0, "");
104     ASSERT_EQ(fd_signals(ps), POLLOUT, "");
105 
106     // write server until full, then drain
107     ASSERT_EQ(write_full(ps), 4096, "");
108     ASSERT_EQ(fd_signals(ps), 0, "");
109     ASSERT_EQ(read_all(pc), 4096, "");
110     ASSERT_EQ(fd_signals(ps), POLLOUT, "");
111 
112     // write client until full, then drain
113     ASSERT_EQ(write_full(pc), 4096, "");
114     ASSERT_EQ(fd_signals(pc), 0, "");
115     ASSERT_EQ(read_all(ps), 4096, "");
116     ASSERT_EQ(fd_signals(pc), POLLOUT, "");
117 
118     // verify no events pending
119     uint32_t events;
120     ASSERT_EQ(ioctl_pty_read_events(pc, &events), (ssize_t)sizeof(events), "");
121     ASSERT_EQ(events, 0u, "");
122 
123     // write a ctrl-c
124     ASSERT_EQ(write(ps, "\x03", 1), 1, "");
125 
126     // should be an event now
127     ASSERT_EQ(ioctl_pty_read_events(pc, &events), (ssize_t)sizeof(events), "");
128     ASSERT_EQ(events, PTY_EVENT_INTERRUPT, "");
129 
130     // should vanish once we read it
131     ASSERT_EQ(ioctl_pty_read_events(pc, &events), (ssize_t)sizeof(events), "");
132     ASSERT_EQ(events, 0u, "");
133 
134     // write something containing a special char
135     // should write up to and including the special char
136     // converting the special char to a signal
137     ASSERT_EQ(write(ps, "hello\x03world", 11), 6, "");
138     ASSERT_EQ(read(pc, tmp, 6), 5, "");
139     ASSERT_EQ(memcmp(tmp, "hello", 5), 0, "");
140     ASSERT_EQ(ioctl_pty_read_events(pc, &events), (ssize_t)sizeof(events), "");
141     ASSERT_EQ(events, PTY_EVENT_INTERRUPT, "");
142 
143 
144     pty_window_size_t ws;
145     ASSERT_EQ(ioctl_pty_get_window_size(pc, &ws), (ssize_t)sizeof(ws), "");
146     ASSERT_EQ(ws.width, 0u, "");
147     ASSERT_EQ(ws.height, 0u, "");
148     ws.width = 80;
149     ws.height = 25;
150     ASSERT_EQ(ioctl_pty_set_window_size(ps, &ws), 0, "");
151     ASSERT_EQ(ioctl_pty_get_window_size(pc, &ws), (ssize_t)sizeof(ws), "");
152     ASSERT_EQ(ws.width, 80u, "");
153     ASSERT_EQ(ws.height, 25u, "");
154 
155     // verify that we don't get events for special chars in raw mode
156     pty_clr_set_t cs = {
157         .clr = 0,
158         .set = PTY_FEATURE_RAW,
159     };
160     ASSERT_EQ(ioctl_pty_clr_set_feature(pc, &cs), 0, "");
161     ASSERT_EQ(write(ps, "\x03", 1), 1, "");
162     ASSERT_EQ(read(pc, tmp, 1), 1, "");
163     ASSERT_EQ(tmp[0], '\x03', "");
164     ASSERT_EQ(ioctl_pty_read_events(pc, &events), (ssize_t)sizeof(events), "");
165     ASSERT_EQ(events, 0u, "");
166 
167     // create a second client
168     int pc1 = openat(pc, "1", O_RDWR | O_NONBLOCK);
169     ASSERT_GE(pc1, 0, "");
170 
171     // reads/writes to non-active client should block
172     ASSERT_EQ(fd_signals(pc1), 0, "");
173     ASSERT_EQ(write(pc1, "test", 4), -1, "");
174     ASSERT_EQ(errno, EAGAIN, "");
175     ASSERT_EQ(read(pc1, tmp, 4), -1, "");
176     ASSERT_EQ(errno, EAGAIN, "");
177 
178     uint32_t n = 2;
179     ASSERT_EQ(ioctl_pty_make_active(pc, &n), ZX_ERR_NOT_FOUND, "");
180 
181     // non-controlling client cannot change active client
182     ASSERT_EQ(ioctl_pty_make_active(pc1, &n), ZX_ERR_ACCESS_DENIED, "");
183 
184     // but controlling client can
185     n = 1;
186     ASSERT_EQ(ioctl_pty_make_active(pc, &n), ZX_OK, "");
187     ASSERT_EQ(fd_signals(pc), 0, "");
188     ASSERT_EQ(fd_signals(pc1), POLLOUT, "");
189     ASSERT_EQ(write(pc1, "test", 4), 4, "");
190     ASSERT_EQ(read(ps, tmp, 4), 4, "");
191     ASSERT_EQ(memcmp(tmp, "test", 4), 0, "");
192 
193     // make sure controlling client observes departing active client
194     close(pc1);
195     ASSERT_EQ(fd_signals(pc), POLLHUP | POLLPRI, "");
196     ASSERT_EQ(ioctl_pty_read_events(pc, &events), (ssize_t)sizeof(events), "");
197     ASSERT_EQ(events, PTY_EVENT_HANGUP, "");
198 
199     // verify that server observes depature of last client
200     close(pc);
201     ASSERT_EQ(fd_signals(ps), POLLHUP | POLLIN, "");
202 
203     close(ps);
204 
205     END_TEST;
206 }
207 
not_a_pty_test(void)208 bool not_a_pty_test(void) {
209     BEGIN_TEST;
210 
211     int root_dir = open("/", O_DIRECTORY | O_RDONLY);
212     EXPECT_GE(root_dir, 0, "");
213 
214     // Calling pty ioctls such as 'get window size' should fail
215     // properly on things that are not ptys.
216     pty_window_size_t ws;
217     EXPECT_EQ(ioctl_pty_get_window_size(root_dir, &ws), ZX_ERR_NOT_SUPPORTED, "");
218 
219     close(root_dir);
220 
221     END_TEST;
222 }
223 
224 BEGIN_TEST_CASE(pty_tests)
RUN_TEST(pty_test)225 RUN_TEST(pty_test)
226 RUN_TEST(not_a_pty_test)
227 END_TEST_CASE(pty_tests)
228 
229 int main(int argc, char** argv) {
230     return unittest_run_all_tests(argc, argv) ? 0 : -1;
231 }
232