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.h>
31#include <sys/ioctl.h>
32
33#if defined(__MVS__) && !defined(IMAXBEL)
34#define IMAXBEL 0
35#endif
36
37static int orig_termios_fd = -1;
38static struct termios orig_termios;
39static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER;
40
41static int uv__tty_is_slave(const int fd) {
42 int result;
43#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
44 int dummy;
45
46 result = ioctl(fd, TIOCGPTN, &dummy) != 0;
47#elif defined(__APPLE__)
48 char dummy[256];
49
50 result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0;
51#elif defined(__NetBSD__)
52 /*
53 * NetBSD as an extension returns with ptsname(3) and ptsname_r(3) the slave
54 * device name for both descriptors, the master one and slave one.
55 *
56 * Implement function to compare major device number with pts devices.
57 *
58 * The major numbers are machine-dependent, on NetBSD/amd64 they are
59 * respectively:
60 * - master tty: ptc - major 6
61 * - slave tty: pts - major 5
62 */
63
64 struct stat sb;
65 /* Lookup device's major for the pts driver and cache it. */
66 static devmajor_t pts = NODEVMAJOR;
67
68 if (pts == NODEVMAJOR) {
69 pts = getdevmajor("pts", S_IFCHR);
70 if (pts == NODEVMAJOR)
71 abort();
72 }
73
74 /* Lookup stat structure behind the file descriptor. */
75 if (fstat(fd, &sb) != 0)
76 abort();
77
78 /* Assert character device. */
79 if (!S_ISCHR(sb.st_mode))
80 abort();
81
82 /* Assert valid major. */
83 if (major(sb.st_rdev) == NODEVMAJOR)
84 abort();
85
86 result = (pts == major(sb.st_rdev));
87#else
88 /* Fallback to ptsname
89 */
90 result = ptsname(fd) == NULL;
91#endif
92 return result;
93}
94
95int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int unused) {
96 uv_handle_type type;
97 int flags;
98 int newfd;
99 int r;
100 int saved_flags;
101 int mode;
102 char path[256];
103 (void)unused; /* deprecated parameter is no longer needed */
104
105 /* File descriptors that refer to files cannot be monitored with epoll.
106 * That restriction also applies to character devices like /dev/random
107 * (but obviously not /dev/tty.)
108 */
109 type = uv_guess_handle(fd);
110 if (type == UV_FILE || type == UV_UNKNOWN_HANDLE)
111 return UV_EINVAL;
112
113 flags = 0;
114 newfd = -1;
115
116 /* Save the fd flags in case we need to restore them due to an error. */
117 do
118 saved_flags = fcntl(fd, F_GETFL);
119 while (saved_flags == -1 && errno == EINTR);
120
121 if (saved_flags == -1)
122 return UV__ERR(errno);
123 mode = saved_flags & O_ACCMODE;
124
125 /* Reopen the file descriptor when it refers to a tty. This lets us put the
126 * tty in non-blocking mode without affecting other processes that share it
127 * with us.
128 *
129 * Example: `node | cat` - if we put our fd 0 in non-blocking mode, it also
130 * affects fd 1 of `cat` because both file descriptors refer to the same
131 * struct file in the kernel. When we reopen our fd 0, it points to a
132 * different struct file, hence changing its properties doesn't affect
133 * other processes.
134 */
135 if (type == UV_TTY) {
136 /* Reopening a pty in master mode won't work either because the reopened
137 * pty will be in slave mode (*BSD) or reopening will allocate a new
138 * master/slave pair (Linux). Therefore check if the fd points to a
139 * slave device.
140 */
141 if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0)
142 r = uv__open_cloexec(path, mode);
143 else
144 r = -1;
145
146 if (r < 0) {
147 /* fallback to using blocking writes */
148 if (mode != O_RDONLY)
149 flags |= UV_HANDLE_BLOCKING_WRITES;
150 goto skip;
151 }
152
153 newfd = r;
154
155 r = uv__dup2_cloexec(newfd, fd);
156 if (r < 0 && r != UV_EINVAL) {
157 /* EINVAL means newfd == fd which could conceivably happen if another
158 * thread called close(fd) between our calls to isatty() and open().
159 * That's a rather unlikely event but let's handle it anyway.
160 */
161 uv__close(newfd);
162 return r;
163 }
164
165 fd = newfd;
166 }
167
168skip:
169 uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY);
170
171 /* If anything fails beyond this point we need to remove the handle from
172 * the handle queue, since it was added by uv__handle_init in uv_stream_init.
173 */
174
175 if (!(flags & UV_HANDLE_BLOCKING_WRITES))
176 uv__nonblock(fd, 1);
177
178#if defined(__APPLE__)
179 r = uv__stream_try_select((uv_stream_t*) tty, &fd);
180 if (r) {
181 int rc = r;
182 if (newfd != -1)
183 uv__close(newfd);
184 QUEUE_REMOVE(&tty->handle_queue);
185 do
186 r = fcntl(fd, F_SETFL, saved_flags);
187 while (r == -1 && errno == EINTR);
188 return rc;
189 }
190#endif
191
192 if (mode != O_WRONLY)
193 flags |= UV_HANDLE_READABLE;
194 if (mode != O_RDONLY)
195 flags |= UV_HANDLE_WRITABLE;
196
197 uv__stream_open((uv_stream_t*) tty, fd, flags);
198 tty->mode = UV_TTY_MODE_NORMAL;
199
200 return 0;
201}
202
203static void uv__tty_make_raw(struct termios* tio) {
204 assert(tio != NULL);
205
206#if defined __sun || defined __MVS__
207 /*
208 * This implementation of cfmakeraw for Solaris and derivatives is taken from
209 * http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html.
210 */
211 tio->c_iflag &= ~(IMAXBEL | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR |
212 IGNCR | ICRNL | IXON);
213 tio->c_oflag &= ~OPOST;
214 tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
215 tio->c_cflag &= ~(CSIZE | PARENB);
216 tio->c_cflag |= CS8;
217#else
218 cfmakeraw(tio);
219#endif /* #ifdef __sun */
220}
221
222int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
223 struct termios tmp;
224 int fd;
225
226 if (tty->mode == (int) mode)
227 return 0;
228
229 fd = uv__stream_fd(tty);
230 if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) {
231 if (tcgetattr(fd, &tty->orig_termios))
232 return UV__ERR(errno);
233
234 /* This is used for uv_tty_reset_mode() */
235 uv_spinlock_lock(&termios_spinlock);
236 if (orig_termios_fd == -1) {
237 orig_termios = tty->orig_termios;
238 orig_termios_fd = fd;
239 }
240 uv_spinlock_unlock(&termios_spinlock);
241 }
242
243 tmp = tty->orig_termios;
244 switch (mode) {
245 case UV_TTY_MODE_NORMAL:
246 break;
247 case UV_TTY_MODE_RAW:
248 tmp.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
249 tmp.c_oflag |= (ONLCR);
250 tmp.c_cflag |= (CS8);
251 tmp.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
252 tmp.c_cc[VMIN] = 1;
253 tmp.c_cc[VTIME] = 0;
254 break;
255 case UV_TTY_MODE_IO:
256 uv__tty_make_raw(&tmp);
257 break;
258 }
259
260 /* Apply changes after draining */
261 if (tcsetattr(fd, TCSADRAIN, &tmp))
262 return UV__ERR(errno);
263
264 tty->mode = mode;
265 return 0;
266}
267
268
269int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
270 struct winsize ws;
271 int err;
272
273 do
274 err = ioctl(uv__stream_fd(tty), TIOCGWINSZ, &ws);
275 while (err == -1 && errno == EINTR);
276
277 if (err == -1)
278 return UV__ERR(errno);
279
280 *width = ws.ws_col;
281 *height = ws.ws_row;
282
283 return 0;
284}
285
286
287uv_handle_type uv_guess_handle(uv_file file) {
288 struct sockaddr sa;
289 struct stat s;
290 socklen_t len;
291 int type;
292
293 if (file < 0)
294 return UV_UNKNOWN_HANDLE;
295
296 if (isatty(file))
297 return UV_TTY;
298
299 if (fstat(file, &s))
300 return UV_UNKNOWN_HANDLE;
301
302 if (S_ISREG(s.st_mode))
303 return UV_FILE;
304
305 if (S_ISCHR(s.st_mode))
306 return UV_FILE; /* XXX UV_NAMED_PIPE? */
307
308 if (S_ISFIFO(s.st_mode))
309 return UV_NAMED_PIPE;
310
311 if (!S_ISSOCK(s.st_mode))
312 return UV_UNKNOWN_HANDLE;
313
314 len = sizeof(type);
315 if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len))
316 return UV_UNKNOWN_HANDLE;
317
318 len = sizeof(sa);
319 if (getsockname(file, &sa, &len))
320 return UV_UNKNOWN_HANDLE;
321
322 if (type == SOCK_DGRAM)
323 if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6)
324 return UV_UDP;
325
326 if (type == SOCK_STREAM) {
327#if defined(_AIX) || defined(__DragonFly__)
328 /* on AIX/DragonFly the getsockname call returns an empty sa structure
329 * for sockets of type AF_UNIX. For all other types it will
330 * return a properly filled in structure.
331 */
332 if (len == 0)
333 return UV_NAMED_PIPE;
334#endif /* defined(_AIX) || defined(__DragonFly__) */
335
336 if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6)
337 return UV_TCP;
338 if (sa.sa_family == AF_UNIX)
339 return UV_NAMED_PIPE;
340 }
341
342 return UV_UNKNOWN_HANDLE;
343}
344
345
346/* This function is async signal-safe, meaning that it's safe to call from
347 * inside a signal handler _unless_ execution was inside uv_tty_set_mode()'s
348 * critical section when the signal was raised.
349 */
350int uv_tty_reset_mode(void) {
351 int saved_errno;
352 int err;
353
354 saved_errno = errno;
355 if (!uv_spinlock_trylock(&termios_spinlock))
356 return UV_EBUSY; /* In uv_tty_set_mode(). */
357
358 err = 0;
359 if (orig_termios_fd != -1)
360 if (tcsetattr(orig_termios_fd, TCSANOW, &orig_termios))
361 err = UV__ERR(errno);
362
363 uv_spinlock_unlock(&termios_spinlock);
364 errno = saved_errno;
365
366 return err;
367}
368