ext/libuv/src/unix/tty.c in libuv-2.0.8 vs ext/libuv/src/unix/tty.c in libuv-2.0.9

- old
+ new

@@ -21,26 +21,46 @@ #include "uv.h" #include "internal.h" #include "spinlock.h" +#include <stdlib.h> #include <assert.h> #include <unistd.h> #include <termios.h> #include <errno.h> #include <sys/ioctl.h> 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; + int saved_flags; + char path[256]; /* File descriptors that refer to files cannot be monitored with epoll. * That restriction also applies to character devices like /dev/random * (but obviously not /dev/tty.) */ @@ -60,11 +80,19 @@ * struct file in the kernel. When we reopen our fd 0, it points to a * different struct file, hence changing its properties doesn't affect * 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 */ if (!readable) flags |= UV_STREAM_BLOCKING; @@ -84,35 +112,55 @@ } fd = newfd; } +#if defined(__APPLE__) + /* Save the fd flags in case we need to restore them due to an error. */ + do + saved_flags = fcntl(fd, F_GETFL); + while (saved_flags == -1 && errno == EINTR); + + if (saved_flags == -1) { + if (newfd != -1) + uv__close(newfd); + return -errno; + } +#endif + + /* Pacify the compiler. */ + (void) &saved_flags; + skip: uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY); /* If anything fails beyond this point we need to remove the handle from * the handle queue, since it was added by uv__handle_init in uv_stream_init. */ + if (!(flags & UV_STREAM_BLOCKING)) + uv__nonblock(fd, 1); + #if defined(__APPLE__) r = uv__stream_try_select((uv_stream_t*) tty, &fd); if (r) { + int rc = r; if (newfd != -1) uv__close(newfd); QUEUE_REMOVE(&tty->handle_queue); - return r; + do + r = fcntl(fd, F_SETFL, saved_flags); + while (r == -1 && errno == EINTR); + return rc; } #endif if (readable) flags |= UV_STREAM_READABLE; else flags |= UV_STREAM_WRITABLE; - if (!(flags & UV_STREAM_BLOCKING)) - uv__nonblock(fd, 1); - uv__stream_open((uv_stream_t*) tty, fd, flags); tty->mode = UV_TTY_MODE_NORMAL; return 0; } @@ -239,17 +287,17 @@ if (type == SOCK_DGRAM) if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) return UV_UDP; if (type == SOCK_STREAM) { -#if defined(_AIX) - /* on AIX the getsockname call returns an empty sa structure +#if defined(_AIX) || defined(__DragonFly__) + /* on AIX/DragonFly the getsockname call returns an empty sa structure * for sockets of type AF_UNIX. For all other types it will * return a properly filled in structure. */ if (len == 0) return UV_NAMED_PIPE; -#endif /* defined(_AIX) */ +#endif /* defined(_AIX) || defined(__DragonFly__) */ if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) return UV_TCP; if (sa.sa_family == AF_UNIX) return UV_NAMED_PIPE;