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(__GLIBC__)) && !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(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(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(ttyin_fd >= 0); 90 ASSERT(ttyout_fd >= 0); 91 92 ASSERT(UV_UNKNOWN_HANDLE == uv_guess_handle(-1)); 93 94 ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); 95 ASSERT(UV_TTY == uv_guess_handle(ttyout_fd)); 96 97 r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ 98 ASSERT(r == 0); 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(uv_default_loop(), &tty_out, ttyout_fd, 0); /* Writable. */ 103 ASSERT(r == 0); 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(r == 0); 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(); 116 return TEST_SKIP; 117 } 118 119 /* 120 * Is it a safe assumption that most people have terminals larger than 121 * 10x10? 122 */ 123 ASSERT(width > 10); 124 ASSERT(height > 10); 125 126 /* Turn on raw mode. */ 127 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); 128 ASSERT(r == 0); 129 130 /* Turn off raw mode. */ 131 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_NORMAL); 132 ASSERT(r == 0); 133 134 /* Calling uv_tty_reset_mode() repeatedly should not clobber errno. */ 135 errno = 0; 136 ASSERT(0 == uv_tty_reset_mode()); 137 ASSERT(0 == uv_tty_reset_mode()); 138 ASSERT(0 == uv_tty_reset_mode()); 139 ASSERT(0 == errno); 140 141 /* TODO check the actual mode! */ 142 143 uv_close((uv_handle_t*) &tty_in, NULL); 144 uv_close((uv_handle_t*) &tty_out, NULL); 145 146 uv_run(loop, UV_RUN_DEFAULT); 147 148 MAKE_VALGRIND_HAPPY(); 149 return 0; 150 } 151 152 153 #ifdef _WIN32 154 static void tty_raw_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) { 155 buf->base = malloc(size); 156 buf->len = size; 157 } 158 159 static void tty_raw_read(uv_stream_t* tty_in, ssize_t nread, const uv_buf_t* buf) { 160 if (nread > 0) { 161 ASSERT(nread == 1); 162 ASSERT(buf->base[0] == ' '); 163 uv_close((uv_handle_t*) tty_in, NULL); 164 } else { 165 ASSERT(nread == 0); 166 } 167 } 168 169 TEST_IMPL(tty_raw) { 170 int r; 171 int ttyin_fd; 172 uv_tty_t tty_in; 173 uv_loop_t* loop = uv_default_loop(); 174 HANDLE handle; 175 INPUT_RECORD record; 176 DWORD written; 177 178 /* Make sure we have an FD that refers to a tty */ 179 handle = CreateFileA("conin$", 180 GENERIC_READ | GENERIC_WRITE, 181 FILE_SHARE_READ | FILE_SHARE_WRITE, 182 NULL, 183 OPEN_EXISTING, 184 FILE_ATTRIBUTE_NORMAL, 185 NULL); 186 ASSERT(handle != INVALID_HANDLE_VALUE); 187 ttyin_fd = _open_osfhandle((intptr_t) handle, 0); 188 ASSERT(ttyin_fd >= 0); 189 ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); 190 191 r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ 192 ASSERT(r == 0); 193 ASSERT(uv_is_readable((uv_stream_t*) &tty_in)); 194 ASSERT(!uv_is_writable((uv_stream_t*) &tty_in)); 195 196 r = uv_read_start((uv_stream_t*)&tty_in, tty_raw_alloc, tty_raw_read); 197 ASSERT(r == 0); 198 199 /* Give uv_tty_line_read_thread time to block on ReadConsoleW */ 200 Sleep(100); 201 202 /* Turn on raw mode. */ 203 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); 204 ASSERT(r == 0); 205 206 /* Write ' ' that should be read in raw mode */ 207 record.EventType = KEY_EVENT; 208 record.Event.KeyEvent.bKeyDown = TRUE; 209 record.Event.KeyEvent.wRepeatCount = 1; 210 record.Event.KeyEvent.wVirtualKeyCode = VK_SPACE; 211 record.Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW(VK_SPACE, MAPVK_VK_TO_VSC); 212 record.Event.KeyEvent.uChar.UnicodeChar = L' '; 213 record.Event.KeyEvent.dwControlKeyState = 0; 214 WriteConsoleInputW(handle, &record, 1, &written); 215 216 uv_run(loop, UV_RUN_DEFAULT); 217 218 MAKE_VALGRIND_HAPPY(); 219 return 0; 220 } 221 222 TEST_IMPL(tty_empty_write) { 223 int r; 224 int ttyout_fd; 225 uv_tty_t tty_out; 226 char dummy[1]; 227 uv_buf_t bufs[1]; 228 uv_loop_t* loop; 229 230 /* Make sure we have an FD that refers to a tty */ 231 HANDLE handle; 232 233 loop = uv_default_loop(); 234 235 handle = CreateFileA("conout$", 236 GENERIC_READ | GENERIC_WRITE, 237 FILE_SHARE_READ | FILE_SHARE_WRITE, 238 NULL, 239 OPEN_EXISTING, 240 FILE_ATTRIBUTE_NORMAL, 241 NULL); 242 ASSERT(handle != INVALID_HANDLE_VALUE); 243 ttyout_fd = _open_osfhandle((intptr_t) handle, 0); 244 245 ASSERT(ttyout_fd >= 0); 246 247 ASSERT(UV_TTY == uv_guess_handle(ttyout_fd)); 248 249 r = uv_tty_init(uv_default_loop(), &tty_out, ttyout_fd, 0); /* Writable. */ 250 ASSERT(r == 0); 251 ASSERT(!uv_is_readable((uv_stream_t*) &tty_out)); 252 ASSERT(uv_is_writable((uv_stream_t*) &tty_out)); 253 254 bufs[0].len = 0; 255 bufs[0].base = &dummy[0]; 256 257 r = uv_try_write((uv_stream_t*) &tty_out, bufs, 1); 258 ASSERT(r == 0); 259 260 uv_close((uv_handle_t*) &tty_out, NULL); 261 262 uv_run(loop, UV_RUN_DEFAULT); 263 264 MAKE_VALGRIND_HAPPY(); 265 return 0; 266 } 267 268 TEST_IMPL(tty_large_write) { 269 int r; 270 int ttyout_fd; 271 uv_tty_t tty_out; 272 char dummy[10000]; 273 uv_buf_t bufs[1]; 274 uv_loop_t* loop; 275 276 /* Make sure we have an FD that refers to a tty */ 277 HANDLE handle; 278 279 loop = uv_default_loop(); 280 281 handle = CreateFileA("conout$", 282 GENERIC_READ | GENERIC_WRITE, 283 FILE_SHARE_READ | FILE_SHARE_WRITE, 284 NULL, 285 OPEN_EXISTING, 286 FILE_ATTRIBUTE_NORMAL, 287 NULL); 288 ASSERT(handle != INVALID_HANDLE_VALUE); 289 ttyout_fd = _open_osfhandle((intptr_t) handle, 0); 290 291 ASSERT(ttyout_fd >= 0); 292 293 ASSERT(UV_TTY == uv_guess_handle(ttyout_fd)); 294 295 r = uv_tty_init(uv_default_loop(), &tty_out, ttyout_fd, 0); /* Writable. */ 296 ASSERT(r == 0); 297 298 memset(dummy, '.', sizeof(dummy) - 1); 299 dummy[sizeof(dummy) - 1] = '\n'; 300 301 bufs[0] = uv_buf_init(dummy, sizeof(dummy)); 302 303 r = uv_try_write((uv_stream_t*) &tty_out, bufs, 1); 304 ASSERT(r == 10000); 305 306 uv_close((uv_handle_t*) &tty_out, NULL); 307 308 uv_run(loop, UV_RUN_DEFAULT); 309 310 MAKE_VALGRIND_HAPPY(); 311 return 0; 312 } 313 314 TEST_IMPL(tty_raw_cancel) { 315 int r; 316 int ttyin_fd; 317 uv_tty_t tty_in; 318 HANDLE handle; 319 320 /* Make sure we have an FD that refers to a tty */ 321 handle = CreateFileA("conin$", 322 GENERIC_READ | GENERIC_WRITE, 323 FILE_SHARE_READ | FILE_SHARE_WRITE, 324 NULL, 325 OPEN_EXISTING, 326 FILE_ATTRIBUTE_NORMAL, 327 NULL); 328 ASSERT(handle != INVALID_HANDLE_VALUE); 329 ttyin_fd = _open_osfhandle((intptr_t) handle, 0); 330 ASSERT(ttyin_fd >= 0); 331 ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); 332 333 r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ 334 ASSERT(r == 0); 335 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); 336 ASSERT(r == 0); 337 r = uv_read_start((uv_stream_t*)&tty_in, tty_raw_alloc, tty_raw_read); 338 ASSERT(r == 0); 339 340 r = uv_read_stop((uv_stream_t*) &tty_in); 341 ASSERT(r == 0); 342 343 MAKE_VALGRIND_HAPPY(); 344 return 0; 345 } 346 #endif 347 348 349 TEST_IMPL(tty_file) { 350 #ifndef _WIN32 351 uv_loop_t loop; 352 uv_tty_t tty; 353 uv_tty_t tty_ro; 354 uv_tty_t tty_wo; 355 int fd; 356 357 ASSERT(0 == uv_loop_init(&loop)); 358 359 fd = open("test/fixtures/empty_file", O_RDONLY); 360 if (fd != -1) { 361 ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); 362 ASSERT(0 == close(fd)); 363 /* test EBADF handling */ 364 ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); 365 } 366 367 /* Bug on AIX where '/dev/random' returns 1 from isatty() */ 368 #ifndef _AIX 369 fd = open("/dev/random", O_RDONLY); 370 if (fd != -1) { 371 ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); 372 ASSERT(0 == close(fd)); 373 } 374 #endif /* _AIX */ 375 376 fd = open("/dev/zero", O_RDONLY); 377 if (fd != -1) { 378 ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); 379 ASSERT(0 == close(fd)); 380 } 381 382 fd = open("/dev/tty", O_RDWR); 383 if (fd != -1) { 384 ASSERT(0 == uv_tty_init(&loop, &tty, fd, 1)); 385 ASSERT(0 == close(fd)); /* TODO: it's indeterminate who owns fd now */ 386 ASSERT(uv_is_readable((uv_stream_t*) &tty)); 387 ASSERT(uv_is_writable((uv_stream_t*) &tty)); 388 uv_close((uv_handle_t*) &tty, NULL); 389 ASSERT(!uv_is_readable((uv_stream_t*) &tty)); 390 ASSERT(!uv_is_writable((uv_stream_t*) &tty)); 391 } 392 393 fd = open("/dev/tty", O_RDONLY); 394 if (fd != -1) { 395 ASSERT(0 == uv_tty_init(&loop, &tty_ro, fd, 1)); 396 ASSERT(0 == close(fd)); /* TODO: it's indeterminate who owns fd now */ 397 ASSERT(uv_is_readable((uv_stream_t*) &tty_ro)); 398 ASSERT(!uv_is_writable((uv_stream_t*) &tty_ro)); 399 uv_close((uv_handle_t*) &tty_ro, NULL); 400 ASSERT(!uv_is_readable((uv_stream_t*) &tty_ro)); 401 ASSERT(!uv_is_writable((uv_stream_t*) &tty_ro)); 402 } 403 404 fd = open("/dev/tty", O_WRONLY); 405 if (fd != -1) { 406 ASSERT(0 == uv_tty_init(&loop, &tty_wo, fd, 0)); 407 ASSERT(0 == close(fd)); /* TODO: it's indeterminate who owns fd now */ 408 ASSERT(!uv_is_readable((uv_stream_t*) &tty_wo)); 409 ASSERT(uv_is_writable((uv_stream_t*) &tty_wo)); 410 uv_close((uv_handle_t*) &tty_wo, NULL); 411 ASSERT(!uv_is_readable((uv_stream_t*) &tty_wo)); 412 ASSERT(!uv_is_writable((uv_stream_t*) &tty_wo)); 413 } 414 415 416 ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); 417 ASSERT(0 == uv_loop_close(&loop)); 418 419 MAKE_VALGRIND_HAPPY(); 420 #endif 421 return 0; 422 } 423 424 TEST_IMPL(tty_pty) { 425 #if defined(__APPLE__) || \ 426 defined(__DragonFly__) || \ 427 defined(__FreeBSD__) || \ 428 defined(__FreeBSD_kernel__) || \ 429 (defined(__linux__) && !defined(__ANDROID__)) || \ 430 defined(__NetBSD__) || \ 431 defined(__OpenBSD__) 432 int master_fd, slave_fd, r; 433 struct winsize w; 434 uv_loop_t loop; 435 uv_tty_t master_tty, slave_tty; 436 437 ASSERT(0 == uv_loop_init(&loop)); 438 439 r = openpty(&master_fd, &slave_fd, NULL, NULL, &w); 440 if (r != 0) 441 RETURN_SKIP("No pty available, skipping."); 442 443 ASSERT(0 == uv_tty_init(&loop, &slave_tty, slave_fd, 0)); 444 ASSERT(0 == uv_tty_init(&loop, &master_tty, master_fd, 0)); 445 ASSERT(uv_is_readable((uv_stream_t*) &slave_tty)); 446 ASSERT(uv_is_writable((uv_stream_t*) &slave_tty)); 447 ASSERT(uv_is_readable((uv_stream_t*) &master_tty)); 448 ASSERT(uv_is_writable((uv_stream_t*) &master_tty)); 449 /* Check if the file descriptor was reopened. If it is, 450 * UV_HANDLE_BLOCKING_WRITES (value 0x100000) isn't set on flags. 451 */ 452 ASSERT(0 == (slave_tty.flags & 0x100000)); 453 /* The master_fd of a pty should never be reopened. 454 */ 455 ASSERT(master_tty.flags & 0x100000); 456 ASSERT(0 == close(slave_fd)); 457 uv_close((uv_handle_t*) &slave_tty, NULL); 458 ASSERT(0 == close(master_fd)); 459 uv_close((uv_handle_t*) &master_tty, NULL); 460 461 ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); 462 463 MAKE_VALGRIND_HAPPY(); 464 #endif 465 return 0; 466 } 467