| File: | out/../deps/uv/src/unix/tty.c |
| Warning: | line 369, column 22 The left operand of '==' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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 "internal.h" | |||
| 24 | #include "spinlock.h" | |||
| 25 | ||||
| 26 | #include <stdlib.h> | |||
| 27 | #include <assert.h> | |||
| 28 | #include <unistd.h> | |||
| 29 | #include <termios.h> | |||
| 30 | #include <errno(*__errno_location ()).h> | |||
| 31 | #include <sys/ioctl.h> | |||
| 32 | ||||
| 33 | #if defined(__MVS__) && !defined(IMAXBEL0020000) | |||
| 34 | #define IMAXBEL0020000 0 | |||
| 35 | #endif | |||
| 36 | ||||
| 37 | #if defined(__PASE__) | |||
| 38 | /* On IBM i PASE, for better compatibility with running interactive programs in | |||
| 39 | * a 5250 environment, isatty() will return true for the stdin/stdout/stderr | |||
| 40 | * streams created by QSH/QP2TERM. | |||
| 41 | * | |||
| 42 | * For more, see docs on PASE_STDIO_ISATTY in | |||
| 43 | * https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_74/apis/pase_environ.htm | |||
| 44 | * | |||
| 45 | * This behavior causes problems for Node as it expects that if isatty() returns | |||
| 46 | * true that TTY ioctls will be supported by that fd (which is not an | |||
| 47 | * unreasonable expectation) and when they don't it crashes with assertion | |||
| 48 | * errors. | |||
| 49 | * | |||
| 50 | * Here, we create our own version of isatty() that uses ioctl() to identify | |||
| 51 | * whether the fd is *really* a TTY or not. | |||
| 52 | */ | |||
| 53 | static int isreallyatty(int file) { | |||
| 54 | int rc; | |||
| 55 | ||||
| 56 | rc = !ioctl(file, TXISATTY + 0x81, NULL((void*)0)); | |||
| 57 | if (!rc && errno(*__errno_location ()) != EBADF9) | |||
| 58 | errno(*__errno_location ()) = ENOTTY25; | |||
| 59 | ||||
| 60 | return rc; | |||
| 61 | } | |||
| 62 | #define isatty(fd) isreallyatty(fd) | |||
| 63 | #endif | |||
| 64 | ||||
| 65 | static int orig_termios_fd = -1; | |||
| 66 | static struct termios orig_termios; | |||
| 67 | static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER{ 0 }; | |||
| 68 | ||||
| 69 | static int uv__tty_is_slave(const int fd) { | |||
| 70 | int result; | |||
| 71 | #if defined(__linux__1) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) | |||
| 72 | int dummy; | |||
| 73 | ||||
| 74 | result = ioctl(fd, TIOCGPTN(((2U) << (((0 +8)+8)+14)) | ((('T')) << (0 +8)) | (((0x30)) << 0) | ((((sizeof(unsigned int)))) << ((0 +8)+8))), &dummy) != 0; | |||
| 75 | #elif defined(__APPLE__) | |||
| 76 | char dummy[256]; | |||
| 77 | ||||
| 78 | result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0; | |||
| 79 | #elif defined(__NetBSD__) | |||
| 80 | /* | |||
| 81 | * NetBSD as an extension returns with ptsname(3) and ptsname_r(3) the slave | |||
| 82 | * device name for both descriptors, the master one and slave one. | |||
| 83 | * | |||
| 84 | * Implement function to compare major device number with pts devices. | |||
| 85 | * | |||
| 86 | * The major numbers are machine-dependent, on NetBSD/amd64 they are | |||
| 87 | * respectively: | |||
| 88 | * - master tty: ptc - major 6 | |||
| 89 | * - slave tty: pts - major 5 | |||
| 90 | */ | |||
| 91 | ||||
| 92 | struct stat sb; | |||
| 93 | /* Lookup device's major for the pts driver and cache it. */ | |||
| 94 | static devmajor_t pts = NODEVMAJOR; | |||
| 95 | ||||
| 96 | if (pts == NODEVMAJOR) { | |||
| 97 | pts = getdevmajor("pts", S_IFCHR0020000); | |||
| 98 | if (pts == NODEVMAJOR) | |||
| 99 | abort(); | |||
| 100 | } | |||
| 101 | ||||
| 102 | /* Lookup stat structure behind the file descriptor. */ | |||
| 103 | if (fstat(fd, &sb) != 0) | |||
| 104 | abort(); | |||
| 105 | ||||
| 106 | /* Assert character device. */ | |||
| 107 | if (!S_ISCHR(sb.st_mode)((((sb.st_mode)) & 0170000) == (0020000))) | |||
| 108 | abort(); | |||
| 109 | ||||
| 110 | /* Assert valid major. */ | |||
| 111 | if (major(sb.st_rdev) == NODEVMAJOR) | |||
| 112 | abort(); | |||
| 113 | ||||
| 114 | result = (pts == major(sb.st_rdev)); | |||
| 115 | #else | |||
| 116 | /* Fallback to ptsname | |||
| 117 | */ | |||
| 118 | result = ptsname(fd) == NULL((void*)0); | |||
| 119 | #endif | |||
| 120 | return result; | |||
| 121 | } | |||
| 122 | ||||
| 123 | int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int unused) { | |||
| 124 | uv_handle_type type; | |||
| 125 | int flags; | |||
| 126 | int newfd; | |||
| 127 | int r; | |||
| 128 | int saved_flags; | |||
| 129 | int mode; | |||
| 130 | char path[256]; | |||
| 131 | (void)unused; /* deprecated parameter is no longer needed */ | |||
| 132 | ||||
| 133 | /* File descriptors that refer to files cannot be monitored with epoll. | |||
| 134 | * That restriction also applies to character devices like /dev/random | |||
| 135 | * (but obviously not /dev/tty.) | |||
| 136 | */ | |||
| 137 | type = uv_guess_handle(fd); | |||
| ||||
| 138 | if (type == UV_FILE || type == UV_UNKNOWN_HANDLE) | |||
| 139 | return UV_EINVAL; | |||
| 140 | ||||
| 141 | flags = 0; | |||
| 142 | newfd = -1; | |||
| 143 | ||||
| 144 | /* Save the fd flags in case we need to restore them due to an error. */ | |||
| 145 | do | |||
| 146 | saved_flags = fcntl(fd, F_GETFL3); | |||
| 147 | while (saved_flags == -1 && errno(*__errno_location ()) == EINTR4); | |||
| 148 | ||||
| 149 | if (saved_flags == -1) | |||
| 150 | return UV__ERR(errno)(-((*__errno_location ()))); | |||
| 151 | mode = saved_flags & O_ACCMODE0003; | |||
| 152 | ||||
| 153 | /* Reopen the file descriptor when it refers to a tty. This lets us put the | |||
| 154 | * tty in non-blocking mode without affecting other processes that share it | |||
| 155 | * with us. | |||
| 156 | * | |||
| 157 | * Example: `node | cat` - if we put our fd 0 in non-blocking mode, it also | |||
| 158 | * affects fd 1 of `cat` because both file descriptors refer to the same | |||
| 159 | * struct file in the kernel. When we reopen our fd 0, it points to a | |||
| 160 | * different struct file, hence changing its properties doesn't affect | |||
| 161 | * other processes. | |||
| 162 | */ | |||
| 163 | if (type == UV_TTY) { | |||
| 164 | /* Reopening a pty in master mode won't work either because the reopened | |||
| 165 | * pty will be in slave mode (*BSD) or reopening will allocate a new | |||
| 166 | * master/slave pair (Linux). Therefore check if the fd points to a | |||
| 167 | * slave device. | |||
| 168 | */ | |||
| 169 | if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0) | |||
| 170 | r = uv__open_cloexec(path, mode | O_NOCTTY0400); | |||
| 171 | else | |||
| 172 | r = -1; | |||
| 173 | ||||
| 174 | if (r < 0) { | |||
| 175 | /* fallback to using blocking writes */ | |||
| 176 | if (mode != O_RDONLY00) | |||
| 177 | flags |= UV_HANDLE_BLOCKING_WRITES; | |||
| 178 | goto skip; | |||
| 179 | } | |||
| 180 | ||||
| 181 | newfd = r; | |||
| 182 | ||||
| 183 | r = uv__dup2_cloexec(newfd, fd); | |||
| 184 | if (r < 0 && r != UV_EINVAL) { | |||
| 185 | /* EINVAL means newfd == fd which could conceivably happen if another | |||
| 186 | * thread called close(fd) between our calls to isatty() and open(). | |||
| 187 | * That's a rather unlikely event but let's handle it anyway. | |||
| 188 | */ | |||
| 189 | uv__close(newfd); | |||
| 190 | return r; | |||
| 191 | } | |||
| 192 | ||||
| 193 | fd = newfd; | |||
| 194 | } | |||
| 195 | ||||
| 196 | skip: | |||
| 197 | uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY); | |||
| 198 | ||||
| 199 | /* If anything fails beyond this point we need to remove the handle from | |||
| 200 | * the handle queue, since it was added by uv__handle_init in uv_stream_init. | |||
| 201 | */ | |||
| 202 | ||||
| 203 | if (!(flags & UV_HANDLE_BLOCKING_WRITES)) | |||
| 204 | uv__nonblockuv__nonblock_ioctl(fd, 1); | |||
| 205 | ||||
| 206 | #if defined(__APPLE__) | |||
| 207 | r = uv__stream_try_select((uv_stream_t*) tty, &fd); | |||
| 208 | if (r) { | |||
| 209 | int rc = r; | |||
| 210 | if (newfd != -1) | |||
| 211 | uv__close(newfd); | |||
| 212 | QUEUE_REMOVE(&tty->handle_queue)do { ((*(QUEUE **) &((*((*(QUEUE **) &((*(&tty-> handle_queue))[1]))))[0]))) = (*(QUEUE **) &((*(&tty-> handle_queue))[0])); ((*(QUEUE **) &((*((*(QUEUE **) & ((*(&tty->handle_queue))[0]))))[1]))) = (*(QUEUE **) & ((*(&tty->handle_queue))[1])); } while (0); | |||
| 213 | do | |||
| 214 | r = fcntl(fd, F_SETFL4, saved_flags); | |||
| 215 | while (r == -1 && errno(*__errno_location ()) == EINTR4); | |||
| 216 | return rc; | |||
| 217 | } | |||
| 218 | #endif | |||
| 219 | ||||
| 220 | if (mode != O_WRONLY01) | |||
| 221 | flags |= UV_HANDLE_READABLE; | |||
| 222 | if (mode != O_RDONLY00) | |||
| 223 | flags |= UV_HANDLE_WRITABLE; | |||
| 224 | ||||
| 225 | uv__stream_open((uv_stream_t*) tty, fd, flags); | |||
| 226 | tty->mode = UV_TTY_MODE_NORMAL; | |||
| 227 | ||||
| 228 | return 0; | |||
| 229 | } | |||
| 230 | ||||
| 231 | static void uv__tty_make_raw(struct termios* tio) { | |||
| 232 | assert(tio != NULL)((void) sizeof ((tio != ((void*)0)) ? 1 : 0), __extension__ ( { if (tio != ((void*)0)) ; else __assert_fail ("tio != NULL", "../deps/uv/src/unix/tty.c", 232, __extension__ __PRETTY_FUNCTION__ ); })); | |||
| 233 | ||||
| 234 | #if defined __sun || defined __MVS__ | |||
| 235 | /* | |||
| 236 | * This implementation of cfmakeraw for Solaris and derivatives is taken from | |||
| 237 | * http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html. | |||
| 238 | */ | |||
| 239 | tio->c_iflag &= ~(IMAXBEL0020000 | IGNBRK0000001 | BRKINT0000002 | PARMRK0000010 | ISTRIP0000040 | INLCR0000100 | | |||
| 240 | IGNCR0000200 | ICRNL0000400 | IXON0002000); | |||
| 241 | tio->c_oflag &= ~OPOST0000001; | |||
| 242 | tio->c_lflag &= ~(ECHO0000010 | ECHONL0000100 | ICANON0000002 | ISIG0000001 | IEXTEN0100000); | |||
| 243 | tio->c_cflag &= ~(CSIZE0000060 | PARENB0000400); | |||
| 244 | tio->c_cflag |= CS80000060; | |||
| 245 | ||||
| 246 | /* | |||
| 247 | * By default, most software expects a pending read to block until at | |||
| 248 | * least one byte becomes available. As per termio(7I), this requires | |||
| 249 | * setting the MIN and TIME parameters appropriately. | |||
| 250 | * | |||
| 251 | * As a somewhat unfortunate artifact of history, the MIN and TIME slots | |||
| 252 | * in the control character array overlap with the EOF and EOL slots used | |||
| 253 | * for canonical mode processing. Because the EOF character needs to be | |||
| 254 | * the ASCII EOT value (aka Control-D), it has the byte value 4. When | |||
| 255 | * switching to raw mode, this is interpreted as a MIN value of 4; i.e., | |||
| 256 | * reads will block until at least four bytes have been input. | |||
| 257 | * | |||
| 258 | * Other platforms with a distinct MIN slot like Linux and FreeBSD appear | |||
| 259 | * to default to a MIN value of 1, so we'll force that value here: | |||
| 260 | */ | |||
| 261 | tio->c_cc[VMIN6] = 1; | |||
| 262 | tio->c_cc[VTIME5] = 0; | |||
| 263 | #else | |||
| 264 | cfmakeraw(tio); | |||
| 265 | #endif /* #ifdef __sun */ | |||
| 266 | } | |||
| 267 | ||||
| 268 | int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { | |||
| 269 | struct termios tmp; | |||
| 270 | int fd; | |||
| 271 | ||||
| 272 | if (tty->mode == (int) mode) | |||
| 273 | return 0; | |||
| 274 | ||||
| 275 | fd = uv__stream_fd(tty)((tty)->io_watcher.fd); | |||
| 276 | if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) { | |||
| 277 | if (tcgetattr(fd, &tty->orig_termios)) | |||
| 278 | return UV__ERR(errno)(-((*__errno_location ()))); | |||
| 279 | ||||
| 280 | /* This is used for uv_tty_reset_mode() */ | |||
| 281 | uv_spinlock_lock(&termios_spinlock); | |||
| 282 | if (orig_termios_fd == -1) { | |||
| 283 | orig_termios = tty->orig_termios; | |||
| 284 | orig_termios_fd = fd; | |||
| 285 | } | |||
| 286 | uv_spinlock_unlock(&termios_spinlock); | |||
| 287 | } | |||
| 288 | ||||
| 289 | tmp = tty->orig_termios; | |||
| 290 | switch (mode) { | |||
| 291 | case UV_TTY_MODE_NORMAL: | |||
| 292 | break; | |||
| 293 | case UV_TTY_MODE_RAW: | |||
| 294 | tmp.c_iflag &= ~(BRKINT0000002 | ICRNL0000400 | INPCK0000020 | ISTRIP0000040 | IXON0002000); | |||
| 295 | tmp.c_oflag |= (ONLCR0000004); | |||
| 296 | tmp.c_cflag |= (CS80000060); | |||
| 297 | tmp.c_lflag &= ~(ECHO0000010 | ICANON0000002 | IEXTEN0100000 | ISIG0000001); | |||
| 298 | tmp.c_cc[VMIN6] = 1; | |||
| 299 | tmp.c_cc[VTIME5] = 0; | |||
| 300 | break; | |||
| 301 | case UV_TTY_MODE_IO: | |||
| 302 | uv__tty_make_raw(&tmp); | |||
| 303 | break; | |||
| 304 | } | |||
| 305 | ||||
| 306 | /* Apply changes after draining */ | |||
| 307 | if (tcsetattr(fd, TCSADRAIN1, &tmp)) | |||
| 308 | return UV__ERR(errno)(-((*__errno_location ()))); | |||
| 309 | ||||
| 310 | tty->mode = mode; | |||
| 311 | return 0; | |||
| 312 | } | |||
| 313 | ||||
| 314 | ||||
| 315 | int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { | |||
| 316 | struct winsize ws; | |||
| 317 | int err; | |||
| 318 | ||||
| 319 | do | |||
| 320 | err = ioctl(uv__stream_fd(tty)((tty)->io_watcher.fd), TIOCGWINSZ0x5413, &ws); | |||
| 321 | while (err == -1 && errno(*__errno_location ()) == EINTR4); | |||
| 322 | ||||
| 323 | if (err == -1) | |||
| 324 | return UV__ERR(errno)(-((*__errno_location ()))); | |||
| 325 | ||||
| 326 | *width = ws.ws_col; | |||
| 327 | *height = ws.ws_row; | |||
| 328 | ||||
| 329 | return 0; | |||
| 330 | } | |||
| 331 | ||||
| 332 | ||||
| 333 | uv_handle_type uv_guess_handle(uv_file file) { | |||
| 334 | struct sockaddr sa; | |||
| 335 | struct stat s; | |||
| 336 | socklen_t len; | |||
| 337 | int type; | |||
| 338 | ||||
| 339 | if (file < 0) | |||
| 340 | return UV_UNKNOWN_HANDLE; | |||
| 341 | ||||
| 342 | if (isatty(file)) | |||
| 343 | return UV_TTY; | |||
| 344 | ||||
| 345 | if (fstat(file, &s)) | |||
| 346 | return UV_UNKNOWN_HANDLE; | |||
| 347 | ||||
| 348 | if (S_ISREG(s.st_mode)((((s.st_mode)) & 0170000) == (0100000))) | |||
| 349 | return UV_FILE; | |||
| 350 | ||||
| 351 | if (S_ISCHR(s.st_mode)((((s.st_mode)) & 0170000) == (0020000))) | |||
| 352 | return UV_FILE; /* XXX UV_NAMED_PIPE? */ | |||
| 353 | ||||
| 354 | if (S_ISFIFO(s.st_mode)((((s.st_mode)) & 0170000) == (0010000))) | |||
| 355 | return UV_NAMED_PIPE; | |||
| 356 | ||||
| 357 | if (!S_ISSOCK(s.st_mode)((((s.st_mode)) & 0170000) == (0140000))) | |||
| 358 | return UV_UNKNOWN_HANDLE; | |||
| 359 | ||||
| 360 | len = sizeof(type); | |||
| 361 | if (getsockopt(file, SOL_SOCKET1, SO_TYPE3, &type, &len)) | |||
| 362 | return UV_UNKNOWN_HANDLE; | |||
| 363 | ||||
| 364 | len = sizeof(sa); | |||
| 365 | if (getsockname(file, &sa, &len)) | |||
| 366 | return UV_UNKNOWN_HANDLE; | |||
| 367 | ||||
| 368 | if (type == SOCK_DGRAMSOCK_DGRAM) | |||
| 369 | if (sa.sa_family == AF_INET2 || sa.sa_family == AF_INET610) | |||
| ||||
| 370 | return UV_UDP; | |||
| 371 | ||||
| 372 | if (type == SOCK_STREAMSOCK_STREAM) { | |||
| 373 | #if defined(_AIX) || defined(__DragonFly__) | |||
| 374 | /* on AIX/DragonFly the getsockname call returns an empty sa structure | |||
| 375 | * for sockets of type AF_UNIX. For all other types it will | |||
| 376 | * return a properly filled in structure. | |||
| 377 | */ | |||
| 378 | if (len == 0) | |||
| 379 | return UV_NAMED_PIPE; | |||
| 380 | #endif /* defined(_AIX) || defined(__DragonFly__) */ | |||
| 381 | ||||
| 382 | if (sa.sa_family == AF_INET2 || sa.sa_family == AF_INET610) | |||
| 383 | return UV_TCP; | |||
| 384 | if (sa.sa_family == AF_UNIX1) | |||
| 385 | return UV_NAMED_PIPE; | |||
| 386 | } | |||
| 387 | ||||
| 388 | return UV_UNKNOWN_HANDLE; | |||
| 389 | } | |||
| 390 | ||||
| 391 | ||||
| 392 | /* This function is async signal-safe, meaning that it's safe to call from | |||
| 393 | * inside a signal handler _unless_ execution was inside uv_tty_set_mode()'s | |||
| 394 | * critical section when the signal was raised. | |||
| 395 | */ | |||
| 396 | int uv_tty_reset_mode(void) { | |||
| 397 | int saved_errno; | |||
| 398 | int err; | |||
| 399 | ||||
| 400 | saved_errno = errno(*__errno_location ()); | |||
| 401 | if (!uv_spinlock_trylock(&termios_spinlock)) | |||
| 402 | return UV_EBUSY; /* In uv_tty_set_mode(). */ | |||
| 403 | ||||
| 404 | err = 0; | |||
| 405 | if (orig_termios_fd != -1) | |||
| 406 | if (tcsetattr(orig_termios_fd, TCSANOW0, &orig_termios)) | |||
| 407 | err = UV__ERR(errno)(-((*__errno_location ()))); | |||
| 408 | ||||
| 409 | uv_spinlock_unlock(&termios_spinlock); | |||
| 410 | errno(*__errno_location ()) = saved_errno; | |||
| 411 | ||||
| 412 | return err; | |||
| 413 | } | |||
| 414 | ||||
| 415 | void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) { | |||
| 416 | } | |||
| 417 | ||||
| 418 | int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) { | |||
| 419 | return UV_ENOTSUP; | |||
| 420 | } |