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