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