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