]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
terminal-util: temporarily turn on nonblocking mode when waiting for ANSI seq responses
authorLennart Poettering <lennart@poettering.net>
Fri, 13 Dec 2024 19:11:35 +0000 (20:11 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 16 Dec 2024 14:41:53 +0000 (15:41 +0100)
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

index 878c1ec06a629b841110a9e8e9c20848627ec8cc..c81001ab6775fce0c355e16891ca4c401d183085 100644 (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;
                         }