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_.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 
     53 class 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 
     79 TEST_F(TtyNodeTest, InvalidIoctl) {
     80   // 123 is not a valid ioctl request.
     81   EXPECT_EQ(EINVAL, dev_tty_->Ioctl(123));
     82 }
     83 
     84 TEST_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 
    124 struct user_data_t {
    125   const char* output_buf;
    126   size_t output_count;
    127 };
    128 
    129 static 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 
    136 TEST_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
    165 static 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 
    185 TEST_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 
    233 TEST_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 
    260 static int g_received_signal;
    261 
    262 static void sighandler(int sig) { g_received_signal = sig; }
    263 
    264 TEST_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  */
    301 static 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 
    312 TEST_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  */
    345 static 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 
    355 TEST_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