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