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
This commit is contained in:
Lennart Poettering
2024-12-16 15:41:53 +01:00
parent 9914b95a3f
commit a0c314d6b0
+26 -6
View File
@@ -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;
}