From a0c314d6b04c0a20d566b15c759d18cdb2916c14 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 13 Dec 2024 20:11:35 +0100 Subject: [PATCH] terminal-util: temporarily turn on nonblocking mode when waiting for ANSI seq responses We never know, maybe there's some spurious POLLIN and read() will fail with EAGAIN even though we saw POLLIN. Handle that. (this can happen if we area not the only process reading from the tty, or in case of hups and such). Fixes: #35499 --- src/basic/terminal-util.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 878c1ec06a6..c81001ab677 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -1900,6 +1900,7 @@ static int scan_background_color_response( } int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue) { + _cleanup_close_ int nonblock_input_fd = -EBADF; int r; assert(ret_red); @@ -1933,6 +1934,13 @@ int get_default_background_color(double *ret_red, double *ret_green, double *ret if (r < 0) goto finish; + /* Open a 2nd input fd, in non-blocking mode, so that we won't ever hang in read() should someone + * else process the POLLIN. */ + + nonblock_input_fd = fd_reopen(STDIN_FILENO, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (nonblock_input_fd < 0) + return nonblock_input_fd; + usec_t end = usec_add(now(CLOCK_MONOTONIC), 333 * USEC_PER_MSEC); char buf[STRLEN(ANSI_OSC "11;rgb:0/0/0" ANSI_ST)]; /* shortest possible reply */ size_t buf_full = 0; @@ -1941,13 +1949,12 @@ int get_default_background_color(double *ret_red, double *ret_green, double *ret for (bool first = true;; first = false) { if (buf_full == 0) { usec_t n = now(CLOCK_MONOTONIC); - if (n >= end) { r = -EOPNOTSUPP; goto finish; } - r = fd_wait_for_event(STDIN_FILENO, POLLIN, usec_sub_unsigned(end, n)); + r = fd_wait_for_event(nonblock_input_fd, POLLIN, usec_sub_unsigned(end, n)); if (r < 0) goto finish; if (r == 0) { @@ -1958,8 +1965,10 @@ int get_default_background_color(double *ret_red, double *ret_green, double *ret /* On the first try, read multiple characters, i.e. the shortest valid * reply. Afterwards read byte-wise, since we don't want to read too much, and * unnecessarily drop too many characters from the input queue. */ - ssize_t l = read(STDIN_FILENO, buf, first ? sizeof(buf) : 1); + ssize_t l = read(nonblock_input_fd, buf, first ? sizeof(buf) : 1); if (l < 0) { + if (errno == EAGAIN) + continue; r = -errno; goto finish; } @@ -2083,6 +2092,8 @@ int terminal_get_size_by_dsr( unsigned *ret_rows, unsigned *ret_columns) { + _cleanup_close_ int nonblock_input_fd = -EBADF; + assert(input_fd >= 0); assert(output_fd >= 0); @@ -2130,6 +2141,13 @@ int terminal_get_size_by_dsr( if (r < 0) goto finish; + /* Open a 2nd input fd, in non-blocking mode, so that we won't ever hang in read() should someone + * else process the POLLIN. */ + + nonblock_input_fd = fd_reopen(input_fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (nonblock_input_fd < 0) + return nonblock_input_fd; + usec_t end = usec_add(now(CLOCK_MONOTONIC), 333 * USEC_PER_MSEC); char buf[STRLEN("\x1B[1;1R")]; /* The shortest valid reply possible */ size_t buf_full = 0; @@ -2138,13 +2156,12 @@ int terminal_get_size_by_dsr( for (bool first = true;; first = false) { if (buf_full == 0) { usec_t n = now(CLOCK_MONOTONIC); - if (n >= end) { r = -EOPNOTSUPP; goto finish; } - r = fd_wait_for_event(input_fd, POLLIN, usec_sub_unsigned(end, n)); + r = fd_wait_for_event(nonblock_input_fd, POLLIN, usec_sub_unsigned(end, n)); if (r < 0) goto finish; if (r == 0) { @@ -2155,8 +2172,11 @@ int terminal_get_size_by_dsr( /* On the first try, read multiple characters, i.e. the shortest valid * reply. Afterwards read byte-wise, since we don't want to read too much, and * unnecessarily drop too many characters from the input queue. */ - ssize_t l = read(input_fd, buf, first ? sizeof(buf) : 1); + ssize_t l = read(nonblock_input_fd, buf, first ? sizeof(buf) : 1); if (l < 0) { + if (errno == EAGAIN) + continue; + r = -errno; goto finish; }