test-tty.c
Go to the documentation of this file.
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 
45  int r, width, height;
46  int ttyin_fd, ttyout_fd;
47  uv_tty_t tty_in, tty_out;
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);
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);
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 
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 */
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. */
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 
147 
149  return 0;
150 }
151 
152 
153 #ifdef _WIN32
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 
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 
169 TEST_IMPL(tty_raw) {
170  int r;
171  int ttyin_fd;
172  uv_tty_t tty_in;
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);
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 
217 
219  return 0;
220 }
221 
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);
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 
263 
265  return 0;
266 }
267 
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);
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 
309 
311  return 0;
312 }
313 
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);
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 
344  return 0;
345 }
346 #endif
347 
348 
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 */
388  uv_close((uv_handle_t*) &tty, NULL);
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 
420 #endif
421  return 0;
422 }
423 
424 TEST_IMPL(tty_pty) {
425 #if defined(__APPLE__) || \
426  defined(__DragonFly__) || \
427  defined(__FreeBSD__) || \
428  defined(__FreeBSD_kernel__) || \
429  (defined(__linux__) && !defined(__ANDROID__)) || \
430  defined(__NetBSD__) || \
431  defined(__OpenBSD__)
432  int master_fd, slave_fd, r;
433  struct winsize w;
434  uv_loop_t loop;
435  uv_tty_t master_tty, slave_tty;
436 
437  ASSERT(0 == uv_loop_init(&loop));
438 
439  r = openpty(&master_fd, &slave_fd, NULL, NULL, &w);
440  if (r != 0)
441  RETURN_SKIP("No pty available, skipping.");
442 
443  ASSERT(0 == uv_tty_init(&loop, &slave_tty, slave_fd, 0));
444  ASSERT(0 == uv_tty_init(&loop, &master_tty, master_fd, 0));
445  ASSERT(uv_is_readable((uv_stream_t*) &slave_tty));
446  ASSERT(uv_is_writable((uv_stream_t*) &slave_tty));
447  ASSERT(uv_is_readable((uv_stream_t*) &master_tty));
448  ASSERT(uv_is_writable((uv_stream_t*) &master_tty));
449  /* Check if the file descriptor was reopened. If it is,
450  * UV_HANDLE_BLOCKING_WRITES (value 0x100000) isn't set on flags.
451  */
452  ASSERT(0 == (slave_tty.flags & 0x100000));
453  /* The master_fd of a pty should never be reopened.
454  */
455  ASSERT(master_tty.flags & 0x100000);
456  ASSERT(0 == close(slave_fd));
457  uv_close((uv_handle_t*) &slave_tty, NULL);
458  ASSERT(0 == close(master_fd));
459  uv_close((uv_handle_t*) &master_tty, NULL);
460 
461  ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT));
462 
464 #endif
465  return 0;
466 }
TRUE
const BOOL TRUE
Definition: undname.c:48
async_greeter_server_with_graceful_shutdown.loop
loop
Definition: async_greeter_server_with_graceful_shutdown.py:59
width
int width
Definition: libuv/docs/code/tty-gravity/main.c:10
uv_guess_handle
UV_EXTERN uv_handle_type uv_guess_handle(uv_file file)
Definition: unix/tty.c:315
TEST_IMPL
TEST_IMPL(tty)
Definition: test-tty.c:44
task.h
UV_TTY_MODE_NORMAL
@ UV_TTY_MODE_NORMAL
Definition: uv.h:712
memset
return memset(p, 0, total)
UV_TTY_MODE_RAW
@ UV_TTY_MODE_RAW
Definition: uv.h:714
string.h
uv_tty_s
Definition: uv.h:704
buf
voidpf void * buf
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:136
printf
_Use_decl_annotations_ int __cdecl printf(const char *_Format,...)
Definition: cs_driver.c:91
ASSERT
#define ASSERT(expr)
Definition: task.h:102
uv_run
UV_EXTERN int uv_run(uv_loop_t *, uv_run_mode mode)
Definition: unix/core.c:361
python_utils.port_server.stderr
stderr
Definition: port_server.py:51
uv_close
UV_EXTERN void uv_close(uv_handle_t *handle, uv_close_cb close_cb)
Definition: unix/core.c:112
uv_stream_s
Definition: uv.h:491
uv_loop_close
UV_EXTERN int uv_loop_close(uv_loop_t *loop)
Definition: uv-common.c:761
uv_default_loop
UV_EXTERN uv_loop_t * uv_default_loop(void)
Definition: uv-common.c:733
ssize_t
intptr_t ssize_t
Definition: win.h:27
tty
uv_tty_t tty
Definition: libuv/docs/code/tty/main.c:7
uv_is_readable
UV_EXTERN int uv_is_readable(const uv_stream_t *handle)
Definition: unix/stream.c:1606
UV_RUN_DEFAULT
@ UV_RUN_DEFAULT
Definition: uv.h:254
uv_read_start
UV_EXTERN int uv_read_start(uv_stream_t *, uv_alloc_cb alloc_cb, uv_read_cb read_cb)
Definition: unix/stream.c:1555
uv_try_write
UV_EXTERN int uv_try_write(uv_stream_t *handle, const uv_buf_t bufs[], unsigned int nbufs)
Definition: unix/stream.c:1507
close
#define close
Definition: test-fs.c:48
intptr_t
_W64 signed int intptr_t
Definition: stdint-msvc2008.h:118
uv_loop_init
UV_EXTERN int uv_loop_init(uv_loop_t *loop)
Definition: loop.c:30
uv_buf_t::base
char * base
Definition: unix.h:122
bufs
static uv_buf_t bufs[5]
Definition: benchmark-udp-pummel.c:51
uv.h
MAKE_VALGRIND_HAPPY
#define MAKE_VALGRIND_HAPPY()
Definition: task.h:229
uv_buf_t
Definition: unix.h:121
uv_tty_get_winsize
UV_EXTERN int uv_tty_get_winsize(uv_tty_t *, int *width, int *height)
Definition: unix/tty.c:297
L
lua_State * L
Definition: upb/upb/bindings/lua/main.c:35
fix_build_deps.r
r
Definition: fix_build_deps.py:491
uv_tty_init
UV_EXTERN int uv_tty_init(uv_loop_t *, uv_tty_t *, uv_file fd, int readable)
Definition: unix/tty.c:123
uv_tty_set_mode
UV_EXTERN int uv_tty_set_mode(uv_tty_t *, uv_tty_mode_t mode)
Definition: unix/tty.c:250
INVALID_HANDLE_VALUE
#define INVALID_HANDLE_VALUE
Definition: bloaty/third_party/zlib/contrib/minizip/iowin32.c:21
TEST_SKIP
@ TEST_SKIP
Definition: task.h:254
uv_buf_init
UV_EXTERN uv_buf_t uv_buf_init(char *base, unsigned int len)
Definition: uv-common.c:157
absl::ABSL_NAMESPACE_BEGIN::dummy
int dummy
Definition: function_type_benchmark.cc:28
uv_buf_t::len
size_t len
Definition: unix.h:123
RETURN_SKIP
#define RETURN_SKIP(explanation)
Definition: task.h:262
open
#define open
Definition: test-fs.c:46
handle
static csh handle
Definition: test_arm_regression.c:16
uv_handle_s
Definition: uv.h:441
uv_loop_s
Definition: uv.h:1767
uv_is_writable
UV_EXTERN int uv_is_writable(const uv_stream_t *handle)
Definition: unix/stream.c:1611
size
voidpf void uLong size
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:136
uv_read_stop
UV_EXTERN int uv_read_stop(uv_stream_t *)
Definition: unix/stream.c:1590
uv_tty_reset_mode
UV_EXTERN int uv_tty_reset_mode(void)
Definition: unix/tty.c:378
errno.h
height
int height
Definition: libuv/docs/code/tty-gravity/main.c:10
UV_UNKNOWN_HANDLE
@ UV_UNKNOWN_HANDLE
Definition: uv.h:190


grpc
Author(s):
autogenerated on Thu Mar 13 2025 03:01:30