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_*()
18static int fd_signals(int fd) {
19    uint32_t signals = 0;
20    fdio_wait_fd(fd, 0, &signals, 0);
21    return signals;
22}
23
24static 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
44static 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
68static 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
208bool 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
224BEGIN_TEST_CASE(pty_tests)
225RUN_TEST(pty_test)
226RUN_TEST(not_a_pty_test)
227END_TEST_CASE(pty_tests)
228
229int main(int argc, char** argv) {
230    return unittest_run_all_tests(argc, argv) ? 0 : -1;
231}
232