diff options
author | Ben Noordhuis <info@bnoordhuis.nl> | 2024-09-11 10:33:54 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-11 10:33:54 +0200 |
commit | a49f264dff6bc853d67e2d16f635ba8d80bc62a6 (patch) | |
tree | e6c03bddb8ebd947f080cff104cd63803c520eb1 /src | |
parent | 44e61dab7effb739f543b8b4eee43f577bf2db49 (diff) | |
download | libuv-a49f264dff6bc853d67e2d16f635ba8d80bc62a6.tar.gz libuv-a49f264dff6bc853d67e2d16f635ba8d80bc62a6.zip |
unix: restore tty attributes on handle close (#4399)
Libuv stores the `struct termios` for use inside uv_tty_reset_mode().
Node.js uses said function to restore the tty to its original mode
on SIGINT or SIGTERM, when there is no opportunity to shut down the
process normally.
Track uv_tty_t handle closing, otherwise we might be trying to use a
stale termios.
The current solution is not ideal because there can be multiple handles
that refer to the same tty/pty and, for various reasons, we can't really
determine when we close the last handle. The last handle may not even be
inside the current process.
Still, all things considered, it's probably (hopefully!) an improvement
over the status quo.
Refs: https://github.com/libuv/libuv/issues/4398
Diffstat (limited to 'src')
-rw-r--r-- | src/unix/core.c | 2 | ||||
-rw-r--r-- | src/unix/internal.h | 3 | ||||
-rw-r--r-- | src/unix/tty.c | 33 |
3 files changed, 36 insertions, 2 deletions
diff --git a/src/unix/core.c b/src/unix/core.c index cfa65023..b74552ef 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -156,7 +156,7 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { break; case UV_TTY: - uv__stream_close((uv_stream_t*)handle); + uv__tty_close((uv_tty_t*)handle); break; case UV_TCP: diff --git a/src/unix/internal.h b/src/unix/internal.h index a987be60..86c0d18a 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -293,6 +293,9 @@ int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); int uv__tcp_nodelay(int fd, int on); int uv__tcp_keepalive(int fd, int on, unsigned int delay); +/* tty */ +void uv__tty_close(uv_tty_t* handle); + /* pipe */ int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); diff --git a/src/unix/tty.c b/src/unix/tty.c index d099bdb3..793054ba 100644 --- a/src/unix/tty.c +++ b/src/unix/tty.c @@ -335,6 +335,37 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { } +void uv__tty_close(uv_tty_t* handle) { + int expected; + int fd; + + fd = handle->io_watcher.fd; + if (fd == -1) + goto done; + + /* This is used for uv_tty_reset_mode() */ + do + expected = 0; + while (!atomic_compare_exchange_strong(&termios_spinlock, &expected, 1)); + + if (fd == orig_termios_fd) { + /* XXX(bnoordhuis) the tcsetattr is probably wrong when there are still + * other uv_tty_t handles active that refer to the same tty/pty but it's + * hard to recognize that particular situation without maintaining some + * kind of process-global data structure, and that still won't work in a + * multi-process setup. + */ + uv__tcsetattr(fd, TCSANOW, &orig_termios); + orig_termios_fd = -1; + } + + atomic_store(&termios_spinlock, 0); + +done: + uv__stream_close((uv_stream_t*) handle); +} + + int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { struct winsize ws; int err; @@ -452,7 +483,7 @@ int uv_tty_reset_mode(void) { saved_errno = errno; if (atomic_exchange(&termios_spinlock, 1)) - return UV_EBUSY; /* In uv_tty_set_mode(). */ + return UV_EBUSY; /* In uv_tty_set_mode() or uv__tty_close(). */ err = 0; if (orig_termios_fd != -1) |