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