unix: open ttyname instead of /dev/tty

Find the real name of the tty using ttyname_r(3) instead of
opening "/dev/tty" which causes trouble if the fd doesn't point to the
controlling terminal.

PR-URL: https://github.com/libuv/libuv/pull/779
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
Enno Boland 2016-03-23 23:18:53 +01:00 committed by Saúl Ibarra Corretgé
parent 643c9e9c32
commit 387102b247
6 changed files with 93 additions and 5 deletions

View File

@ -129,6 +129,7 @@ EXTRA_DIST = test/fixtures/empty_file \
TESTS = test/run-tests
check_PROGRAMS = test/run-tests
test_run_tests_CFLAGS =
test_run_tests_LDFLAGS =
test_run_tests_SOURCES = test/blackhole-server.c \
test/dns-server.c \
test/echo-server.c \
@ -303,15 +304,18 @@ libuv_la_SOURCES += src/unix/darwin.c \
src/unix/fsevents.c \
src/unix/kqueue.c \
src/unix/proctitle.c
test_run_tests_LDFLAGS += -lutil
endif
if DRAGONFLY
include_HEADERS += include/uv-bsd.h
test_run_tests_LDFLAGS += -lutil
endif
if FREEBSD
include_HEADERS += include/uv-bsd.h
libuv_la_SOURCES += src/unix/freebsd.c src/unix/kqueue.c
test_run_tests_LDFLAGS += -lutil
endif
if LINUX
@ -322,16 +326,19 @@ libuv_la_SOURCES += src/unix/linux-core.c \
src/unix/linux-syscalls.c \
src/unix/linux-syscalls.h \
src/unix/proctitle.c
test_run_tests_LDFLAGS += -lutil
endif
if NETBSD
include_HEADERS += include/uv-bsd.h
libuv_la_SOURCES += src/unix/kqueue.c src/unix/netbsd.c
test_run_tests_LDFLAGS += -lutil
endif
if OPENBSD
include_HEADERS += include/uv-bsd.h
libuv_la_SOURCES += src/unix/kqueue.c src/unix/openbsd.c
test_run_tests_LDFLAGS += -lutil
endif
if SUNOS

View File

@ -58,14 +58,22 @@ API
`readable`, specifies if you plan on calling :c:func:`uv_read_start` with
this stream. stdin is readable, stdout is not.
On Unix this function will try to open ``/dev/tty`` and use it if the passed
file descriptor refers to a TTY. This lets libuv put the tty in non-blocking
mode without affecting other processes that share the tty.
On Unix this function will determine the path of the fd of the terminal
using :man:`ttyname_r(3)`, open it, and use it if the passed file descriptor
refers to a TTY. This lets libuv put the tty in non-blocking mode without
affecting other processes that share the tty.
This function is not thread safe on systems that don't support
ioctl TIOCGPTN or TIOCPTYGNAME, for instance OpenBSD and Solaris.
.. note::
If opening ``/dev/tty`` fails, libuv falls back to blocking writes for
If reopening the TTY fails, libuv falls back to blocking writes for
non-readable TTY streams.
.. versionchanged:: 1.9.0: the path of the TTY is determined by
:man:`ttyname_r(3)`. In earlier versions libuv opened
`/dev/tty` instead.
.. versionchanged:: 1.5.0: trying to initialize a TTY stream with a file
descriptor that refers to a file returns `UV_EINVAL`
on UNIX.

View File

@ -23,6 +23,7 @@
#include "internal.h"
#include "spinlock.h"
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <termios.h>
@ -33,12 +34,30 @@ static int orig_termios_fd = -1;
static struct termios orig_termios;
static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER;
static int uv__tty_is_slave(const int fd) {
int result;
#if defined(__linux__) || defined(__FreeBSD__)
int dummy;
result = ioctl(fd, TIOCGPTN, &dummy) != 0;
#elif defined(__APPLE__)
char dummy[256];
result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0;
#else
/* Fallback to ptsname
*/
result = ptsname(fd) == NULL;
#endif
return result;
}
int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) {
uv_handle_type type;
int flags;
int newfd;
int r;
char path[256];
/* File descriptors that refer to files cannot be monitored with epoll.
* That restriction also applies to character devices like /dev/random
@ -62,7 +81,15 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) {
* other processes.
*/
if (type == UV_TTY) {
r = uv__open_cloexec("/dev/tty", O_RDWR);
/* Reopening a pty in master mode won't work either because the reopened
* pty will be in slave mode (*BSD) or reopening will allocate a new
* master/slave pair (Linux). Therefore check if the fd points to a
* slave device.
*/
if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0)
r = uv__open_cloexec(path, O_RDWR);
else
r = -1;
if (r < 0) {
/* fallback to using blocking writes */

View File

@ -44,6 +44,7 @@ TEST_DECLARE (semaphore_2)
TEST_DECLARE (semaphore_3)
TEST_DECLARE (tty)
TEST_DECLARE (tty_file)
TEST_DECLARE (tty_pty)
TEST_DECLARE (stdio_over_pipes)
TEST_DECLARE (ip6_pton)
TEST_DECLARE (ipc_listen_before_write)
@ -387,6 +388,7 @@ TASK_LIST_START
TEST_ENTRY (pipe_set_non_blocking)
TEST_ENTRY (tty)
TEST_ENTRY (tty_file)
TEST_ENTRY (tty_pty)
TEST_ENTRY (stdio_over_pipes)
TEST_ENTRY (ip6_pton)
TEST_ENTRY (ipc_listen_before_write)

View File

@ -28,6 +28,13 @@
#else /* Unix */
# include <fcntl.h>
# include <unistd.h>
# if defined(__linux__)
# include <pty.h>
# elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
# include <util.h>
# elif defined(__FreeBSD__) || defined(__DragonFly__)
# include <libutil.h>
# endif
#endif
#include <string.h>
@ -182,3 +189,35 @@ TEST_IMPL(tty_file) {
#endif
return 0;
}
TEST_IMPL(tty_pty) {
# if defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__) || \
defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
int master_fd, slave_fd;
struct winsize w;
uv_loop_t loop;
uv_tty_t master_tty, slave_tty;
ASSERT(0 == uv_loop_init(&loop));
ASSERT(0 == openpty(&master_fd, &slave_fd, NULL, NULL, &w));
ASSERT(0 == uv_tty_init(&loop, &slave_tty, slave_fd, 0));
ASSERT(0 == uv_tty_init(&loop, &master_tty, master_fd, 0));
/* Check if the file descriptor was reopened. If it is,
* UV_STREAM_BLOCKING (value 0x80) isn't set on flags.
*/
ASSERT(0 == (slave_tty.flags & 0x80));
/* The master_fd of a pty should never be reopened.
*/
ASSERT(master_tty.flags & 0x80);
ASSERT(0 == close(slave_fd));
uv_close((uv_handle_t*) &slave_tty, NULL);
ASSERT(0 == close(master_fd));
uv_close((uv_handle_t*) &master_tty, NULL);
ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT));
MAKE_VALGRIND_HAPPY();
#endif
return 0;
}

5
uv.gyp
View File

@ -426,6 +426,11 @@
'test/runner-unix.h',
],
}],
[ 'OS in "mac dragonflybsd freebsd linux netbsd openbsd".split()', {
'link_settings': {
'libraries': [ '-lutil' ],
},
}],
[ 'OS=="solaris"', { # make test-fs.c compile, needs _POSIX_C_SOURCE
'defines': [
'__EXTENSIONS__',