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.
9 #include <sys/select.h>
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"
24 using namespace nacl_io;
28 class TtyTest : public ::testing::Test {
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());
45 ScopedMountNode dev_tty_;
48 TEST_F(TtyTest, InvalidIoctl) {
49 // 123 is not a valid ioctl request.
50 EXPECT_EQ(EINVAL, dev_tty_->Ioctl(123));
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();
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.
67 char backup_buffer[100];
68 memset(buffer, 'a', 100);
69 memset(backup_buffer, 'a', 100);
71 // Now we actually send the data
72 EXPECT_EQ(0, dev_tty_->Ioctl(TIOCNACLINPUT, &packaged_message));
74 // We read a small chunk first to ensure it doesn't give us
75 // more than we ask for.
77 EXPECT_EQ(0, dev_tty_->Read(attrs, buffer, 5, &bytes_read));
78 EXPECT_EQ(bytes_read, 5);
79 EXPECT_EQ(0, memcmp(message.data(), buffer, 5));
80 EXPECT_EQ(0, memcmp(buffer + 5, backup_buffer + 5, 95));
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()));
93 const char* output_buf;
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;
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);
110 EXPECT_EQ(EIO, dev_tty_->Write(attrs, message, message_len, &bytes_written));
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;
117 tioc_nacl_output handler;
118 handler.handler = output_handler;
119 handler.user_data = &user_data;
121 EXPECT_EQ(0, dev_tty_->Ioctl(TIOCNACLOUTPUT, &handler));
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));
129 static int ki_ioctl_wrapper(int fd, int request, ...) {
131 va_start(ap, request);
132 int rtn = ki_ioctl(fd, request, ap);
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);
147 // -1 -> Error occured
148 static int IsReadable(int fd) {
149 struct timeval timeout = { 0, 0 };
154 FD_SET(fd, &readfds);
155 FD_SET(fd, &errorfds);
156 int rtn = ki_select(fd + 1, &readfds, NULL, &errorfds, &timeout);
158 return 0; // not readable
161 if (FD_ISSET(fd, &errorfds))
163 if (!FD_ISSET(fd, &readfds))
165 return 1; // readable
168 TEST_F(TtyTest, TtySelect) {
169 struct timeval timeout;
174 int tty_fd = ki_open("/dev/tty", O_RDONLY);
175 ASSERT_TRUE(tty_fd >= 0) << "tty open failed: " << errno;
179 FD_SET(tty_fd, &readfds);
180 FD_SET(tty_fd, &errorfds);
181 // 10 millisecond timeout
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(rtn, 0) << "select failed: " << rtn << " err=" << strerror(errno);
187 ASSERT_FALSE(FD_ISSET(tty_fd, &readfds));
188 ASSERT_FALSE(FD_ISSET(tty_fd, &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);
199 ASSERT_TRUE(FD_ISSET(tty_fd, &writefds));
200 ASSERT_FALSE(FD_ISSET(tty_fd, &readfds));
201 ASSERT_FALSE(FD_ISSET(tty_fd, &errorfds));
203 // Send 4 bytes to TTY input
204 ASSERT_EQ(0, TtyWrite(tty_fd, "input:test"));
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"));
210 // TTY should now be readable
211 ASSERT_EQ(IsReadable(tty_fd), 1);
216 TEST_F(TtyTest, TtyICANON) {
217 int tty_fd = ki_open("/dev/tty", O_RDONLY);
219 ASSERT_EQ(IsReadable(tty_fd), 0);
221 struct termios tattr;
222 tcgetattr(tty_fd, &tattr);
223 tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
224 tcsetattr(tty_fd, TCSAFLUSH, &tattr);
226 ASSERT_EQ(IsReadable(tty_fd), 0);
228 // Set some bytes to the TTY, not including newline
229 ASSERT_EQ(0, TtyWrite(tty_fd, "a"));
231 // Since we are not in canonical mode the bytes should be
232 // immediately readable.
233 ASSERT_EQ(IsReadable(tty_fd), 1);
235 // Read byte from tty.
237 ASSERT_EQ(1, read(tty_fd, &c, 1));
240 ASSERT_EQ(IsReadable(tty_fd), 0);
243 int g_recieved_signal = 0;
245 void sighandler(int sig) {
246 g_recieved_signal = sig;
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));
254 // Install signal handler
255 sighandler_t new_handler = sighandler;
256 sighandler_t old_handler = ki_signal(SIGWINCH, new_handler);
257 ASSERT_NE(old_handler, SIG_ERR) << "signal return error: " << errno;
259 // Set a new windows size
260 struct winsize winsize;
261 winsize.ws_col = 100;
262 winsize.ws_row = 200;
263 EXPECT_EQ(0, dev_tty_->Ioctl(TIOCSWINSZ, &winsize));
264 EXPECT_EQ(g_recieved_signal, SIGWINCH);
266 // Restore old signal handler
267 EXPECT_EQ(new_handler, ki_signal(SIGWINCH, old_handler));
269 // Verify new window size can be queried correctly.
272 EXPECT_EQ(0, dev_tty_->Ioctl(TIOCGWINSZ, &winsize));
273 EXPECT_EQ(winsize.ws_col, 100);
274 EXPECT_EQ(winsize.ws_row, 200);
276 // Restore original windows size.
277 EXPECT_EQ(0, dev_tty_->Ioctl(TIOCSWINSZ, &old_winsize));
281 * Sleep for 50ms then send a resize event to /dev/tty.
283 static void* resize_thread_main(void* arg) {
286 int* tty_fd = static_cast<int*>(arg);
287 struct winsize winsize;
288 winsize.ws_col = 100;
289 winsize.ws_row = 200;
290 ki_ioctl_wrapper(*tty_fd, TIOCSWINSZ, &winsize);
294 TEST_F(TtyTest, ResizeDuringSelect) {
295 // Test that a window resize during a call
296 // to select(3) will cause it to fail with EINTR.
297 int tty_fd = ki_open("/dev/tty", O_RDONLY);
303 FD_SET(tty_fd, &readfds);
304 FD_SET(tty_fd, &errorfds);
306 pthread_t resize_thread;
307 pthread_create(&resize_thread, NULL, resize_thread_main,
310 struct timeval timeout;
314 // TTY should not be readable either before or after the
315 // call to select(3).
316 ASSERT_EQ(IsReadable(tty_fd), 0);
318 int rtn = ki_select(tty_fd + 1, &readfds, NULL, &errorfds, &timeout);
319 pthread_join(resize_thread, NULL);
321 ASSERT_EQ(EINTR, errno);
322 ASSERT_EQ(IsReadable(tty_fd), 0);
326 * Sleep for 50ms then send some input to the /dev/tty.
328 static void* input_thread_main(void* arg) {
331 int fd = ki_open("/dev/tty", O_RDONLY);
332 TtyWrite(fd, "test\n");
336 TEST_F(TtyTest, InputDuringSelect) {
337 // Test that input which occurs while in select causes
339 int tty_fd = ki_open("/dev/tty", O_RDONLY);
345 FD_SET(tty_fd, &readfds);
346 FD_SET(tty_fd, &errorfds);
348 pthread_t resize_thread;
349 pthread_create(&resize_thread, NULL, input_thread_main,
352 struct timeval timeout;
356 int rtn = ki_select(tty_fd + 1, &readfds, NULL, &errorfds, &timeout);
357 pthread_join(resize_thread, NULL);