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 | } |