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