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