• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TEST_IMPL(tty)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
tty_raw_alloc(uv_handle_t * handle,size_t size,uv_buf_t * buf)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 
tty_raw_read(uv_stream_t * tty_in,ssize_t nread,const uv_buf_t * buf)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 
TEST_IMPL(tty_raw)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 
TEST_IMPL(tty_empty_write)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 
TEST_IMPL(tty_large_write)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 
TEST_IMPL(tty_raw_cancel)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 
TEST_IMPL(tty_file)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 
TEST_IMPL(tty_pty)424 TEST_IMPL(tty_pty) {
425 /* TODO(gengjiawen): Fix test on QEMU. */
426 #if defined(__QEMU__)
427   RETURN_SKIP("Test does not currently work in QEMU");
428 #endif
429 #if defined(__ASAN__)
430   RETURN_SKIP("Test does not currently work in ASAN");
431 #endif
432 
433 #if defined(__APPLE__)                            || \
434     defined(__DragonFly__)                        || \
435     defined(__FreeBSD__)                          || \
436     defined(__FreeBSD_kernel__)                   || \
437     (defined(__linux__) && !defined(__ANDROID__)) || \
438     defined(__NetBSD__)                           || \
439     defined(__OpenBSD__)
440   int master_fd, slave_fd, r;
441   struct winsize w;
442   uv_loop_t loop;
443   uv_tty_t master_tty, slave_tty;
444 
445   ASSERT(0 == uv_loop_init(&loop));
446 
447   r = openpty(&master_fd, &slave_fd, NULL, NULL, &w);
448   if (r != 0)
449     RETURN_SKIP("No pty available, skipping.");
450 
451   ASSERT(0 == uv_tty_init(&loop, &slave_tty, slave_fd, 0));
452   ASSERT(0 == uv_tty_init(&loop, &master_tty, master_fd, 0));
453   ASSERT(uv_is_readable((uv_stream_t*) &slave_tty));
454   ASSERT(uv_is_writable((uv_stream_t*) &slave_tty));
455   ASSERT(uv_is_readable((uv_stream_t*) &master_tty));
456   ASSERT(uv_is_writable((uv_stream_t*) &master_tty));
457   /* Check if the file descriptor was reopened. If it is,
458    * UV_HANDLE_BLOCKING_WRITES (value 0x100000) isn't set on flags.
459    */
460   ASSERT(0 == (slave_tty.flags & 0x100000));
461   /* The master_fd of a pty should never be reopened.
462    */
463   ASSERT(master_tty.flags & 0x100000);
464   ASSERT(0 == close(slave_fd));
465   uv_close((uv_handle_t*) &slave_tty, NULL);
466   ASSERT(0 == close(master_fd));
467   uv_close((uv_handle_t*) &master_tty, NULL);
468 
469   ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT));
470 
471   MAKE_VALGRIND_HAPPY();
472 #endif
473   return 0;
474 }
475