1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 * 3 * Permission is hereby granted, free of charge, to any person obtaining a copy 4 * of this software and associated documentation files (the "Software"), to 5 * deal in the Software without restriction, including without limitation the 6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 * sell copies of the Software, and to permit persons to whom the Software is 8 * furnished to do so, subject to the following conditions: 9 * 10 * The above copyright notice and this permission notice shall be included in 11 * all copies or substantial portions of the Software. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 * IN THE SOFTWARE. 20 */ 21 22 #include "uv.h" 23 #include "task.h" 24 25 #ifdef _WIN32 26 # include <io.h> 27 # include <windows.h> 28 #else /* Unix */ 29 # include <fcntl.h> 30 # include <unistd.h> 31 # if defined(__linux__) && !defined(__ANDROID__) 32 # include <pty.h> 33 # elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 34 # include <util.h> 35 # elif defined(__FreeBSD__) || defined(__DragonFly__) 36 # include <libutil.h> 37 # endif 38 #endif 39 40 #include <string.h> 41 #include <errno.h> 42 43 44 TEST_IMPL(tty) { 45 int r, width, height; 46 int ttyin_fd, ttyout_fd; 47 uv_tty_t tty_in, tty_out; 48 uv_loop_t* loop = uv_default_loop(); 49 50 /* Make sure we have an FD that refers to a tty */ 51 #ifdef _WIN32 52 HANDLE handle; 53 handle = CreateFileA("conin$", 54 GENERIC_READ | GENERIC_WRITE, 55 FILE_SHARE_READ | FILE_SHARE_WRITE, 56 NULL, 57 OPEN_EXISTING, 58 FILE_ATTRIBUTE_NORMAL, 59 NULL); 60 ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); 61 ttyin_fd = _open_osfhandle((intptr_t) handle, 0); 62 63 handle = CreateFileA("conout$", 64 GENERIC_READ | GENERIC_WRITE, 65 FILE_SHARE_READ | FILE_SHARE_WRITE, 66 NULL, 67 OPEN_EXISTING, 68 FILE_ATTRIBUTE_NORMAL, 69 NULL); 70 ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); 71 ttyout_fd = _open_osfhandle((intptr_t) handle, 0); 72 73 #else /* unix */ 74 ttyin_fd = open("/dev/tty", O_RDONLY, 0); 75 if (ttyin_fd < 0) { 76 fprintf(stderr, "Cannot open /dev/tty as read-only: %s\n", strerror(errno)); 77 fflush(stderr); 78 return TEST_SKIP; 79 } 80 81 ttyout_fd = open("/dev/tty", O_WRONLY, 0); 82 if (ttyout_fd < 0) { 83 fprintf(stderr, "Cannot open /dev/tty as write-only: %s\n", strerror(errno)); 84 fflush(stderr); 85 return TEST_SKIP; 86 } 87 #endif 88 89 ASSERT_GE(ttyin_fd, 0); 90 ASSERT_GE(ttyout_fd, 0); 91 92 ASSERT_EQ(UV_UNKNOWN_HANDLE, uv_guess_handle(-1)); 93 94 ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd)); 95 ASSERT_EQ(UV_TTY, uv_guess_handle(ttyout_fd)); 96 97 r = uv_tty_init(loop, &tty_in, ttyin_fd, 1); /* Readable. */ 98 ASSERT_OK(r); 99 ASSERT(uv_is_readable((uv_stream_t*) &tty_in)); 100 ASSERT(!uv_is_writable((uv_stream_t*) &tty_in)); 101 102 r = uv_tty_init(loop, &tty_out, ttyout_fd, 0); /* Writable. */ 103 ASSERT_OK(r); 104 ASSERT(!uv_is_readable((uv_stream_t*) &tty_out)); 105 ASSERT(uv_is_writable((uv_stream_t*) &tty_out)); 106 107 r = uv_tty_get_winsize(&tty_out, &width, &height); 108 ASSERT_OK(r); 109 110 printf("width=%d height=%d\n", width, height); 111 112 if (width == 0 && height == 0) { 113 /* Some environments such as containers or Jenkins behave like this 114 * sometimes */ 115 MAKE_VALGRIND_HAPPY(loop); 116 return TEST_SKIP; 117 } 118 119 ASSERT_GT(width, 0); 120 ASSERT_GT(height, 0); 121 122 /* Turn on raw mode. */ 123 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); 124 ASSERT_OK(r); 125 126 /* Turn off raw mode. */ 127 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_NORMAL); 128 ASSERT_OK(r); 129 130 /* Calling uv_tty_reset_mode() repeatedly should not clobber errno. */ 131 errno = 0; 132 ASSERT_OK(uv_tty_reset_mode()); 133 ASSERT_OK(uv_tty_reset_mode()); 134 ASSERT_OK(uv_tty_reset_mode()); 135 ASSERT_OK(errno); 136 137 /* TODO check the actual mode! */ 138 139 uv_close((uv_handle_t*) &tty_in, NULL); 140 uv_close((uv_handle_t*) &tty_out, NULL); 141 142 uv_run(loop, UV_RUN_DEFAULT); 143 144 MAKE_VALGRIND_HAPPY(uv_default_loop()); 145 return 0; 146 } 147 148 149 #ifdef _WIN32 150 static void tty_raw_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) { 151 buf->base = malloc(size); 152 buf->len = size; 153 } 154 155 static void tty_raw_read(uv_stream_t* tty_in, ssize_t nread, const uv_buf_t* buf) { 156 if (nread > 0) { 157 ASSERT_EQ(1, nread ); 158 ASSERT_EQ(buf->base[0], ' '); 159 uv_close((uv_handle_t*) tty_in, NULL); 160 } else { 161 ASSERT_OK(nread); 162 } 163 } 164 165 TEST_IMPL(tty_raw) { 166 int r; 167 int ttyin_fd; 168 uv_tty_t tty_in; 169 uv_loop_t* loop = uv_default_loop(); 170 HANDLE handle; 171 INPUT_RECORD record; 172 DWORD written; 173 174 /* Make sure we have an FD that refers to a tty */ 175 handle = CreateFileA("conin$", 176 GENERIC_READ | GENERIC_WRITE, 177 FILE_SHARE_READ | FILE_SHARE_WRITE, 178 NULL, 179 OPEN_EXISTING, 180 FILE_ATTRIBUTE_NORMAL, 181 NULL); 182 ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); 183 ttyin_fd = _open_osfhandle((intptr_t) handle, 0); 184 ASSERT_GE(ttyin_fd, 0); 185 ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd)); 186 187 r = uv_tty_init(loop, &tty_in, ttyin_fd, 1); /* Readable. */ 188 ASSERT_OK(r); 189 ASSERT(uv_is_readable((uv_stream_t*) &tty_in)); 190 ASSERT(!uv_is_writable((uv_stream_t*) &tty_in)); 191 192 r = uv_read_start((uv_stream_t*)&tty_in, tty_raw_alloc, tty_raw_read); 193 ASSERT_OK(r); 194 195 /* Give uv_tty_line_read_thread time to block on ReadConsoleW */ 196 Sleep(100); 197 198 /* Turn on raw mode. */ 199 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); 200 ASSERT_OK(r); 201 202 /* Write ' ' that should be read in raw mode */ 203 record.EventType = KEY_EVENT; 204 record.Event.KeyEvent.bKeyDown = TRUE; 205 record.Event.KeyEvent.wRepeatCount = 1; 206 record.Event.KeyEvent.wVirtualKeyCode = VK_SPACE; 207 record.Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW(VK_SPACE, MAPVK_VK_TO_VSC); 208 record.Event.KeyEvent.uChar.UnicodeChar = L' '; 209 record.Event.KeyEvent.dwControlKeyState = 0; 210 WriteConsoleInputW(handle, &record, 1, &written); 211 212 uv_run(loop, UV_RUN_DEFAULT); 213 214 MAKE_VALGRIND_HAPPY(loop); 215 return 0; 216 } 217 218 TEST_IMPL(tty_empty_write) { 219 int r; 220 int ttyout_fd; 221 uv_tty_t tty_out; 222 char dummy[1]; 223 uv_buf_t bufs[1]; 224 uv_loop_t* loop; 225 226 /* Make sure we have an FD that refers to a tty */ 227 HANDLE handle; 228 229 loop = uv_default_loop(); 230 231 handle = CreateFileA("conout$", 232 GENERIC_READ | GENERIC_WRITE, 233 FILE_SHARE_READ | FILE_SHARE_WRITE, 234 NULL, 235 OPEN_EXISTING, 236 FILE_ATTRIBUTE_NORMAL, 237 NULL); 238 ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); 239 ttyout_fd = _open_osfhandle((intptr_t) handle, 0); 240 241 ASSERT_GE(ttyout_fd, 0); 242 243 ASSERT_EQ(UV_TTY, uv_guess_handle(ttyout_fd)); 244 245 r = uv_tty_init(loop, &tty_out, ttyout_fd, 0); /* Writable. */ 246 ASSERT_OK(r); 247 ASSERT(!uv_is_readable((uv_stream_t*) &tty_out)); 248 ASSERT(uv_is_writable((uv_stream_t*) &tty_out)); 249 250 bufs[0].len = 0; 251 bufs[0].base = &dummy[0]; 252 253 r = uv_try_write((uv_stream_t*) &tty_out, bufs, 1); 254 ASSERT_OK(r); 255 256 uv_close((uv_handle_t*) &tty_out, NULL); 257 258 uv_run(loop, UV_RUN_DEFAULT); 259 260 MAKE_VALGRIND_HAPPY(loop); 261 return 0; 262 } 263 264 TEST_IMPL(tty_large_write) { 265 int r; 266 int ttyout_fd; 267 uv_tty_t tty_out; 268 char dummy[10000]; 269 uv_buf_t bufs[1]; 270 uv_loop_t* loop; 271 272 /* Make sure we have an FD that refers to a tty */ 273 HANDLE handle; 274 275 loop = uv_default_loop(); 276 277 handle = CreateFileA("conout$", 278 GENERIC_READ | GENERIC_WRITE, 279 FILE_SHARE_READ | FILE_SHARE_WRITE, 280 NULL, 281 OPEN_EXISTING, 282 FILE_ATTRIBUTE_NORMAL, 283 NULL); 284 ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); 285 ttyout_fd = _open_osfhandle((intptr_t) handle, 0); 286 287 ASSERT_GE(ttyout_fd, 0); 288 289 ASSERT_EQ(UV_TTY, uv_guess_handle(ttyout_fd)); 290 291 r = uv_tty_init(loop, &tty_out, ttyout_fd, 0); /* Writable. */ 292 ASSERT_OK(r); 293 294 memset(dummy, '.', sizeof(dummy) - 1); 295 dummy[sizeof(dummy) - 1] = '\n'; 296 297 bufs[0] = uv_buf_init(dummy, sizeof(dummy)); 298 299 r = uv_try_write((uv_stream_t*) &tty_out, bufs, 1); 300 ASSERT_EQ(10000, r); 301 302 uv_close((uv_handle_t*) &tty_out, NULL); 303 304 uv_run(loop, UV_RUN_DEFAULT); 305 306 MAKE_VALGRIND_HAPPY(loop); 307 return 0; 308 } 309 310 TEST_IMPL(tty_raw_cancel) { 311 int r; 312 int ttyin_fd; 313 uv_tty_t tty_in; 314 HANDLE handle; 315 316 /* Make sure we have an FD that refers to a tty */ 317 handle = CreateFileA("conin$", 318 GENERIC_READ | GENERIC_WRITE, 319 FILE_SHARE_READ | FILE_SHARE_WRITE, 320 NULL, 321 OPEN_EXISTING, 322 FILE_ATTRIBUTE_NORMAL, 323 NULL); 324 ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); 325 ttyin_fd = _open_osfhandle((intptr_t) handle, 0); 326 ASSERT_GE(ttyin_fd, 0); 327 ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd)); 328 329 r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ 330 ASSERT_OK(r); 331 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); 332 ASSERT_OK(r); 333 r = uv_read_start((uv_stream_t*)&tty_in, tty_raw_alloc, tty_raw_read); 334 ASSERT_OK(r); 335 336 r = uv_read_stop((uv_stream_t*) &tty_in); 337 ASSERT_OK(r); 338 339 MAKE_VALGRIND_HAPPY(uv_default_loop()); 340 return 0; 341 } 342 #endif 343 344 345 TEST_IMPL(tty_file) { 346 #ifndef _WIN32 347 uv_loop_t loop; 348 uv_tty_t tty; 349 uv_tty_t tty_ro; 350 uv_tty_t tty_wo; 351 int fd; 352 353 ASSERT_OK(uv_loop_init(&loop)); 354 355 fd = open("test/fixtures/empty_file", O_RDONLY); 356 if (fd != -1) { 357 ASSERT_EQ(UV_EINVAL, uv_tty_init(&loop, &tty, fd, 1)); 358 ASSERT_OK(close(fd)); 359 /* test EBADF handling */ 360 ASSERT_EQ(UV_EINVAL, uv_tty_init(&loop, &tty, fd, 1)); 361 } 362 363 /* Bug on AIX where '/dev/random' returns 1 from isatty() */ 364 #ifndef _AIX 365 fd = open("/dev/random", O_RDONLY); 366 if (fd != -1) { 367 ASSERT_EQ(UV_EINVAL, uv_tty_init(&loop, &tty, fd, 1)); 368 ASSERT_OK(close(fd)); 369 } 370 #endif /* _AIX */ 371 372 fd = open("/dev/zero", O_RDONLY); 373 if (fd != -1) { 374 ASSERT_EQ(UV_EINVAL, uv_tty_init(&loop, &tty, fd, 1)); 375 ASSERT_OK(close(fd)); 376 } 377 378 fd = open("/dev/tty", O_RDWR); 379 if (fd != -1) { 380 ASSERT_OK(uv_tty_init(&loop, &tty, fd, 1)); 381 ASSERT_OK(close(fd)); /* TODO: it's indeterminate who owns fd now */ 382 ASSERT(uv_is_readable((uv_stream_t*) &tty)); 383 ASSERT(uv_is_writable((uv_stream_t*) &tty)); 384 uv_close((uv_handle_t*) &tty, NULL); 385 ASSERT(!uv_is_readable((uv_stream_t*) &tty)); 386 ASSERT(!uv_is_writable((uv_stream_t*) &tty)); 387 } 388 389 fd = open("/dev/tty", O_RDONLY); 390 if (fd != -1) { 391 ASSERT_OK(uv_tty_init(&loop, &tty_ro, fd, 1)); 392 ASSERT_OK(close(fd)); /* TODO: it's indeterminate who owns fd now */ 393 ASSERT(uv_is_readable((uv_stream_t*) &tty_ro)); 394 ASSERT(!uv_is_writable((uv_stream_t*) &tty_ro)); 395 uv_close((uv_handle_t*) &tty_ro, NULL); 396 ASSERT(!uv_is_readable((uv_stream_t*) &tty_ro)); 397 ASSERT(!uv_is_writable((uv_stream_t*) &tty_ro)); 398 } 399 400 fd = open("/dev/tty", O_WRONLY); 401 if (fd != -1) { 402 ASSERT_OK(uv_tty_init(&loop, &tty_wo, fd, 0)); 403 ASSERT_OK(close(fd)); /* TODO: it's indeterminate who owns fd now */ 404 ASSERT(!uv_is_readable((uv_stream_t*) &tty_wo)); 405 ASSERT(uv_is_writable((uv_stream_t*) &tty_wo)); 406 uv_close((uv_handle_t*) &tty_wo, NULL); 407 ASSERT(!uv_is_readable((uv_stream_t*) &tty_wo)); 408 ASSERT(!uv_is_writable((uv_stream_t*) &tty_wo)); 409 } 410 411 412 ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); 413 414 MAKE_VALGRIND_HAPPY(&loop); 415 #endif 416 return 0; 417 } 418 419 TEST_IMPL(tty_pty) { 420 /* TODO(gengjiawen): Fix test on QEMU. */ 421 #if defined(__QEMU__) 422 RETURN_SKIP("Test does not currently work in QEMU"); 423 #endif 424 #if defined(__ASAN__) 425 RETURN_SKIP("Test does not currently work in ASAN"); 426 #endif 427 428 #if defined(__APPLE__) || \ 429 defined(__DragonFly__) || \ 430 defined(__FreeBSD__) || \ 431 (defined(__linux__) && !defined(__ANDROID__)) || \ 432 defined(__NetBSD__) || \ 433 defined(__OpenBSD__) 434 int master_fd, slave_fd, r; 435 struct winsize w; 436 uv_loop_t loop; 437 uv_tty_t master_tty, slave_tty; 438 439 ASSERT_OK(uv_loop_init(&loop)); 440 441 r = openpty(&master_fd, &slave_fd, NULL, NULL, &w); 442 if (r != 0) 443 RETURN_SKIP("No pty available, skipping."); 444 445 ASSERT_OK(uv_tty_init(&loop, &slave_tty, slave_fd, 0)); 446 ASSERT_OK(uv_tty_init(&loop, &master_tty, master_fd, 0)); 447 ASSERT(uv_is_readable((uv_stream_t*) &slave_tty)); 448 ASSERT(uv_is_writable((uv_stream_t*) &slave_tty)); 449 ASSERT(uv_is_readable((uv_stream_t*) &master_tty)); 450 ASSERT(uv_is_writable((uv_stream_t*) &master_tty)); 451 /* Check if the file descriptor was reopened. If it is, 452 * UV_HANDLE_BLOCKING_WRITES (value 0x100000) isn't set on flags. 453 */ 454 ASSERT_OK((slave_tty.flags & 0x100000)); 455 /* The master_fd of a pty should never be reopened. 456 */ 457 ASSERT(master_tty.flags & 0x100000); 458 ASSERT_OK(close(slave_fd)); 459 uv_close((uv_handle_t*) &slave_tty, NULL); 460 ASSERT_OK(close(master_fd)); 461 uv_close((uv_handle_t*) &master_tty, NULL); 462 463 ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); 464 465 MAKE_VALGRIND_HAPPY(&loop); 466 #endif 467 return 0; 468 } 469