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