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