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