unix: reopen tty as /dev/tty

Reopen the file descriptor when it refers to a tty. This lets us put the tty in
non-blocking mode without affecting other processes that share it with us.

Fixes #601.
This commit is contained in:
Ben Noordhuis 2012-10-24 03:32:28 +02:00
parent b7f38b1e53
commit 31f9fbce63

View File

@ -34,18 +34,53 @@ static struct termios orig_termios;
int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) {
int flags;
int newfd;
int r;
uv__stream_init(loop, (uv_stream_t*)tty, UV_TTY);
if (readable) {
uv__nonblock(fd, 1);
uv__stream_open((uv_stream_t*)tty, fd, UV_STREAM_READABLE);
} else {
/* Note: writable tty we set to blocking mode. */
uv__stream_open((uv_stream_t*)tty, fd, UV_STREAM_WRITABLE);
tty->flags |= UV_STREAM_BLOCKING;
/* Reopen the file descriptor when it refers to a tty. This lets us put the
* tty in non-blocking mode without affecting other processes that share it
* with us.
*
* Example: `node | cat` - if we put our fd 0 in non-blocking mode, it also
* affects fd 1 of `cat` because both file descriptors refer to the same
* 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 (isatty(fd)) {
newfd = open("/dev/tty", O_RDWR);
if (newfd == -1)
return uv__set_sys_error(loop, errno);
do
r = dup2(newfd, fd);
while (r == -1 && (errno == EINTR || errno == EBUSY));
/* EINVAL means newfd == fd which could conceivably happen if another
* thread called close(fd) between our calls to isatty() and open().
* That's a rather unlikely event but let's handle it anyway.
*/
if (r == -1 && errno != EINVAL) {
close(newfd);
return uv__set_sys_error(loop, errno);
}
fd = newfd;
}
if (readable)
flags = UV_STREAM_READABLE;
else
flags = UV_STREAM_WRITABLE;
uv__nonblock(fd, 1);
uv__stream_open((uv_stream_t*)tty, fd, flags);
tty->mode = 0;
return 0;
}