tty_test.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1// Copyright 2013 The Chromium 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 <errno.h>
6#include <fcntl.h>
7#include <string.h>
8#include <sys/ioctl.h>
9#include <sys/select.h>
10#include <sys/stat.h>
11#include <sys/time.h>
12#include <string>
13
14#include "dev_fs_for_testing.h"
15#include "gtest/gtest.h"
16#include "nacl_io/devfs/dev_fs.h"
17#include "nacl_io/filesystem.h"
18#include "nacl_io/ioctl.h"
19#include "nacl_io/kernel_intercept.h"
20#include "nacl_io/kernel_proxy.h"
21#include "nacl_io/osdirent.h"
22
23using namespace nacl_io;
24
25namespace {
26
27class TtyTest : public ::testing::Test {
28 public:
29  void SetUp() {
30    ASSERT_EQ(0, ki_push_state_for_testing());
31    ASSERT_EQ(0, ki_init(&kp_));
32    ASSERT_EQ(0, fs_.Access(Path("/tty"), R_OK | W_OK));
33    ASSERT_EQ(EACCES, fs_.Access(Path("/tty"), X_OK));
34    ASSERT_EQ(0, fs_.Open(Path("/tty"), O_RDWR, &dev_tty_));
35    ASSERT_NE(NULL_NODE, dev_tty_.get());
36  }
37
38  void TearDown() { ki_uninit(); }
39
40 protected:
41  KernelProxy kp_;
42  DevFsForTesting fs_;
43  ScopedNode dev_tty_;
44};
45
46TEST_F(TtyTest, InvalidIoctl) {
47  // 123 is not a valid ioctl request.
48  EXPECT_EQ(EINVAL, dev_tty_->Ioctl(123));
49}
50
51TEST_F(TtyTest, TtyInput) {
52  // Now let's try sending some data over.
53  // First we create the message.
54  std::string message("hello, how are you?\n");
55  struct tioc_nacl_input_string packaged_message;
56  packaged_message.length = message.size();
57  packaged_message.buffer = message.data();
58
59  // Now we make buffer we'll read into.
60  // We fill the buffer and a backup buffer with arbitrary data
61  // and compare them after reading to make sure read doesn't
62  // clobber parts of the buffer it shouldn't.
63  int bytes_read;
64  char buffer[100];
65  char backup_buffer[100];
66  memset(buffer, 'a', 100);
67  memset(backup_buffer, 'a', 100);
68
69  // Now we actually send the data
70  EXPECT_EQ(0, dev_tty_->Ioctl(TIOCNACLINPUT, &packaged_message));
71
72  // We read a small chunk first to ensure it doesn't give us
73  // more than we ask for.
74  HandleAttr attrs;
75  EXPECT_EQ(0, dev_tty_->Read(attrs, buffer, 5, &bytes_read));
76  EXPECT_EQ(5, bytes_read);
77  EXPECT_EQ(0, memcmp(message.data(), buffer, 5));
78  EXPECT_EQ(0, memcmp(buffer + 5, backup_buffer + 5, 95));
79
80  // Now we ask for more data than is left in the tty, to ensure
81  // it doesn't give us more than is there.
82  EXPECT_EQ(0, dev_tty_->Read(attrs, buffer + 5, 95, &bytes_read));
83  EXPECT_EQ(bytes_read, message.size() - 5);
84  EXPECT_EQ(0, memcmp(message.data(), buffer, message.size()));
85  EXPECT_EQ(0, memcmp(buffer + message.size(),
86                      backup_buffer + message.size(),
87                      100 - message.size()));
88}
89
90struct user_data_t {
91  const char* output_buf;
92  size_t output_count;
93};
94
95static ssize_t output_handler(const char* buf, size_t count, void* data) {
96  user_data_t* user_data = static_cast<user_data_t*>(data);
97  user_data->output_buf = buf;
98  user_data->output_count = count;
99  return count;
100}
101
102TEST_F(TtyTest, TtyOutput) {
103  // When no handler is registered then all writes should return EIO
104  int bytes_written = 10;
105  const char* message = "hello\n";
106  int message_len = strlen(message);
107  HandleAttr attrs;
108  EXPECT_EQ(EIO, dev_tty_->Write(attrs, message, message_len, &bytes_written));
109
110  // Setup output handler with user_data to record calls.
111  user_data_t user_data;
112  user_data.output_buf = NULL;
113  user_data.output_count = 0;
114
115  tioc_nacl_output handler;
116  handler.handler = output_handler;
117  handler.user_data = &user_data;
118
119  EXPECT_EQ(0, dev_tty_->Ioctl(TIOCNACLOUTPUT, &handler));
120
121  EXPECT_EQ(0, dev_tty_->Write(attrs, message, message_len, &bytes_written));
122  EXPECT_EQ(message_len, bytes_written);
123  EXPECT_EQ(message_len, user_data.output_count);
124  EXPECT_EQ(0, strncmp(user_data.output_buf, message, message_len));
125}
126
127static int ki_ioctl_wrapper(int fd, int request, ...) {
128  va_list ap;
129  va_start(ap, request);
130  int rtn = ki_ioctl(fd, request, ap);
131  va_end(ap);
132  return rtn;
133}
134
135static int TtyWrite(int fd, const char* string) {
136  struct tioc_nacl_input_string input;
137  input.buffer = string;
138  input.length = strlen(input.buffer);
139  return ki_ioctl_wrapper(fd, TIOCNACLINPUT, &input);
140}
141
142// Returns:
143//   0 -> Not readable
144//   1 -> Readable
145//  -1 -> Error occured
146static int IsReadable(int fd) {
147  struct timeval timeout = {0, 0};
148  fd_set readfds;
149  fd_set errorfds;
150  FD_ZERO(&readfds);
151  FD_ZERO(&errorfds);
152  FD_SET(fd, &readfds);
153  FD_SET(fd, &errorfds);
154  int rtn = ki_select(fd + 1, &readfds, NULL, &errorfds, &timeout);
155  if (rtn == 0)
156    return 0;  // not readable
157  if (rtn != 1)
158    return -1;  // error
159  if (FD_ISSET(fd, &errorfds))
160    return -2;  // error
161  if (!FD_ISSET(fd, &readfds))
162    return -3;  // error
163  return 1;     // readable
164}
165
166TEST_F(TtyTest, TtySelect) {
167  struct timeval timeout;
168  fd_set readfds;
169  fd_set writefds;
170  fd_set errorfds;
171
172  int tty_fd = ki_open("/dev/tty", O_RDONLY);
173  ASSERT_GT(tty_fd, 0) << "tty open failed: " << errno;
174
175  FD_ZERO(&readfds);
176  FD_ZERO(&errorfds);
177  FD_SET(tty_fd, &readfds);
178  FD_SET(tty_fd, &errorfds);
179  // 10 millisecond timeout
180  timeout.tv_sec = 0;
181  timeout.tv_usec = 10 * 1000;
182  // Should timeout when no input is available.
183  int rtn = ki_select(tty_fd + 1, &readfds, NULL, &errorfds, &timeout);
184  ASSERT_EQ(0, rtn) << "select failed: " << rtn << " err=" << strerror(errno);
185  ASSERT_FALSE(FD_ISSET(tty_fd, &readfds));
186  ASSERT_FALSE(FD_ISSET(tty_fd, &errorfds));
187
188  FD_ZERO(&readfds);
189  FD_ZERO(&writefds);
190  FD_ZERO(&errorfds);
191  FD_SET(tty_fd, &readfds);
192  FD_SET(tty_fd, &writefds);
193  FD_SET(tty_fd, &errorfds);
194  // TTY should be writable on startup.
195  rtn = ki_select(tty_fd + 1, &readfds, &writefds, &errorfds, NULL);
196  ASSERT_EQ(1, rtn);
197  ASSERT_TRUE(FD_ISSET(tty_fd, &writefds));
198  ASSERT_FALSE(FD_ISSET(tty_fd, &readfds));
199  ASSERT_FALSE(FD_ISSET(tty_fd, &errorfds));
200
201  // Send 4 bytes to TTY input
202  ASSERT_EQ(0, TtyWrite(tty_fd, "input:test"));
203
204  // TTY should not be readable until newline in written
205  ASSERT_EQ(IsReadable(tty_fd), 0);
206  ASSERT_EQ(0, TtyWrite(tty_fd, "input:\n"));
207
208  // TTY should now be readable
209  ASSERT_EQ(1, IsReadable(tty_fd));
210
211  ki_close(tty_fd);
212}
213
214TEST_F(TtyTest, TtyICANON) {
215  int tty_fd = ki_open("/dev/tty", O_RDONLY);
216
217  ASSERT_EQ(0, IsReadable(tty_fd));
218
219  struct termios tattr;
220  ki_tcgetattr(tty_fd, &tattr);
221  tattr.c_lflag &= ~(ICANON | ECHO); /* Clear ICANON and ECHO. */
222  ki_tcsetattr(tty_fd, TCSAFLUSH, &tattr);
223
224  ASSERT_EQ(0, IsReadable(tty_fd));
225
226  // Set some bytes to the TTY, not including newline
227  ASSERT_EQ(0, TtyWrite(tty_fd, "a"));
228
229  // Since we are not in canonical mode the bytes should be
230  // immediately readable.
231  ASSERT_EQ(1, IsReadable(tty_fd));
232
233  // Read byte from tty.
234  char c;
235  ASSERT_EQ(1, ki_read(tty_fd, &c, 1));
236  ASSERT_EQ('a', c);
237
238  ASSERT_EQ(0, IsReadable(tty_fd));
239}
240
241static int g_recieved_signal;
242
243static void sighandler(int sig) { g_recieved_signal = sig; }
244
245TEST_F(TtyTest, WindowSize) {
246  // Get current window size
247  struct winsize old_winsize = {0};
248  ASSERT_EQ(0, dev_tty_->Ioctl(TIOCGWINSZ, &old_winsize));
249
250  // Install signal handler
251  sighandler_t new_handler = sighandler;
252  sighandler_t old_handler = ki_signal(SIGWINCH, new_handler);
253  ASSERT_NE(SIG_ERR, old_handler) << "signal return error: " << errno;
254
255  g_recieved_signal = 0;
256
257  // Set a new windows size
258  struct winsize winsize;
259  winsize.ws_col = 100;
260  winsize.ws_row = 200;
261  EXPECT_EQ(0, dev_tty_->Ioctl(TIOCSWINSZ, &winsize));
262  EXPECT_EQ(SIGWINCH, g_recieved_signal);
263
264  // Restore old signal handler
265  EXPECT_EQ(new_handler, ki_signal(SIGWINCH, old_handler));
266
267  // Verify new window size can be queried correctly.
268  winsize.ws_col = 0;
269  winsize.ws_row = 0;
270  EXPECT_EQ(0, dev_tty_->Ioctl(TIOCGWINSZ, &winsize));
271  EXPECT_EQ(100, winsize.ws_col);
272  EXPECT_EQ(200, winsize.ws_row);
273
274  // Restore original windows size.
275  EXPECT_EQ(0, dev_tty_->Ioctl(TIOCSWINSZ, &old_winsize));
276}
277
278/*
279 * Sleep for 50ms then send a resize event to /dev/tty.
280 */
281static void* resize_thread_main(void* arg) {
282  usleep(50 * 1000);
283
284  int* tty_fd = static_cast<int*>(arg);
285  struct winsize winsize;
286  winsize.ws_col = 100;
287  winsize.ws_row = 200;
288  ki_ioctl_wrapper(*tty_fd, TIOCSWINSZ, &winsize);
289  return NULL;
290}
291
292TEST_F(TtyTest, ResizeDuringSelect) {
293  // Test that a window resize during a call
294  // to select(3) will cause it to fail with EINTR.
295  int tty_fd = ki_open("/dev/tty", O_RDONLY);
296
297  fd_set readfds;
298  fd_set errorfds;
299  FD_ZERO(&readfds);
300  FD_ZERO(&errorfds);
301  FD_SET(tty_fd, &readfds);
302  FD_SET(tty_fd, &errorfds);
303
304  pthread_t resize_thread;
305  pthread_create(&resize_thread, NULL, resize_thread_main, &tty_fd);
306
307  struct timeval timeout;
308  timeout.tv_sec = 20;
309  timeout.tv_usec = 0;
310
311  // TTY should not be readable either before or after the
312  // call to select(3).
313  ASSERT_EQ(0, IsReadable(tty_fd));
314
315  int rtn = ki_select(tty_fd + 1, &readfds, NULL, &errorfds, &timeout);
316  pthread_join(resize_thread, NULL);
317  ASSERT_EQ(-1, rtn);
318  ASSERT_EQ(EINTR, errno);
319  ASSERT_EQ(0, IsReadable(tty_fd));
320}
321
322/*
323 * Sleep for 50ms then send some input to the /dev/tty.
324 */
325static void* input_thread_main(void* arg) {
326  usleep(50 * 1000);
327
328  int fd = ki_open("/dev/tty", O_RDONLY);
329  TtyWrite(fd, "test\n");
330  return NULL;
331}
332
333TEST_F(TtyTest, InputDuringSelect) {
334  // Test that input which occurs while in select causes
335  // select to return.
336  int tty_fd = ki_open("/dev/tty", O_RDONLY);
337
338  fd_set readfds;
339  fd_set errorfds;
340  FD_ZERO(&readfds);
341  FD_ZERO(&errorfds);
342  FD_SET(tty_fd, &readfds);
343  FD_SET(tty_fd, &errorfds);
344
345  pthread_t resize_thread;
346  pthread_create(&resize_thread, NULL, input_thread_main, &dev_tty_);
347
348  struct timeval timeout;
349  timeout.tv_sec = 20;
350  timeout.tv_usec = 0;
351
352  int rtn = ki_select(tty_fd + 1, &readfds, NULL, &errorfds, &timeout);
353  pthread_join(resize_thread, NULL);
354
355  ASSERT_EQ(1, rtn);
356}
357}
358