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