]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
more: temporarily ignore stdin when waiting for stderr
authorKarel Zak <kzak@redhat.com>
Mon, 30 Jun 2025 10:20:13 +0000 (12:20 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 30 Jun 2025 10:20:13 +0000 (12:20 +0200)
more waits for user commands on stderr, but at the same time, it
monitors sigalfd and stdin (for data and POLLHUP|POLLNVAL). We need to
temporarily ignore stdin if there is new data waiting for read()
(e.g., dmesg | more); otherwise, more_key_command() will end in a busy
loop.

Fixes: https://github.com/util-linux/util-linux/issues/3634
Signed-off-by: Karel Zak <kzak@redhat.com>
text-utils/more.c

index fa5513fd0d8e0eff28cd51e1def8f4023d421224..51c63e9245cda88bf65d1daa3b8ab687cb965051 100644 (file)
@@ -204,6 +204,7 @@ struct more_control {
 #endif
        unsigned int
                ignore_stdin,           /* POLLHUP; peer closed pipe */
+               pending_stdin,          /* data on stdin temporary ignored when waiting for stderr */
                bad_stdout,             /* true if overwriting does not turn off standout */
                catch_suspend,          /* we should catch the SIGTSTP signal */
                clear_line_ends,        /* do not scroll, paint each screen from the top */
@@ -1378,11 +1379,14 @@ static int more_poll(struct more_control *ctl, int timeout, int *stderr_active)
 
        if (stderr_active)
                *stderr_active = 0;
+       else
+               /* always check stdin if not care about stderr */
+               ctl->pending_stdin = 0;
 
        while (!has_data) {
                int rc;
 
-               if (ctl->ignore_stdin)
+               if (ctl->ignore_stdin || ctl->pending_stdin)
                        pfd[POLLFD_STDIN].fd = -1;      /* probably closed, ignore */
 
                rc = poll(pfd, ARRAY_SIZE(pfd), timeout);
@@ -1440,8 +1444,11 @@ static int more_poll(struct more_control *ctl, int timeout, int *stderr_active)
                        if ((pfd[POLLFD_STDIN].revents & POLLHUP) ||
                            (pfd[POLLFD_STDIN].revents & POLLNVAL))
                                ctl->ignore_stdin = 1;
-                       else
+                       else {
                                has_data++;
+                               if (ctl->current_file == stdin)
+                                       ctl->pending_stdin = 1;
+                       }
                }
 
                /* event on stderr (we reads user commands from stderr!) */
@@ -1690,6 +1697,13 @@ static int more_key_command(struct more_control *ctl, char *filename)
                        continue;
                if (stderr_active == 0)
                        continue;
+
+               /* There could be new data on stdin (e.g. prog | more) while
+                * we are waiting for user's activity on stderr. These stdin
+                * events are temporarily ignored to avoid a busy loop. Let's
+                * reset this to ensure stdin is checked next time. */
+               ctl->pending_stdin = 0;
+
                cmd = read_command(ctl);
                if (cmd.key == more_kc_unknown_command)
                        continue;