:: [devuan-dev] bug#860: bug#860: util…
Top Pagina
Delete this message
Reply to this message
Auteur: Mark Hindley
Datum:  
Aan: Arthur Marsh
CC: Devuan Bug Tracking System
Onderwerp: [devuan-dev] bug#860: bug#860: util-linux: more when waiting for keyboard response has 100 percent cpu utilisation
Control: tags -1 = upstream

Arthur,

I have now managed to reproduce this with util-linux 2.41-4devuan1, but not
2.38.1-5+deb12u3devuan1.

The more.c diff does show some poll() changes. This probably needs to go
upstream.

Mark
diff --git a/text-utils/more.c b/text-utils/more.c
index e3e2a4336..a035591af 100644
--- a/text-utils/more.c
+++ b/text-utils/more.c
@@ -57,7 +57,6 @@
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/file.h>
-#include <sys/ttydefaults.h>
#include <sys/wait.h>
#include <regex.h>
#include <assert.h>
@@ -66,6 +65,10 @@
#include <paths.h>
#include <getopt.h>

+#ifdef HAVE_SYS_TTYDEFAULTS_H
+# include <sys/ttydefaults.h>
+#endif
+
#if defined(HAVE_NCURSESW_TERM_H)
# include <ncursesw/term.h>
#elif defined(HAVE_NCURSES_TERM_H)
@@ -83,7 +86,6 @@
#include "xalloc.h"
#include "widechar.h"
#include "closestream.h"
-#include "rpmatch.h"
#include "env.h"

 #ifdef TEST_PROGRAM
@@ -199,37 +201,38 @@ struct more_control {
     magic_t magic;            /* libmagic database entries */
 #endif
     unsigned int
-        bad_stdout:1,        /* true if overwriting does not turn off standout */
-        catch_suspend:1,    /* we should catch the SIGTSTP signal */
-        clear_line_ends:1,    /* do not scroll, paint each screen from the top */
-        clear_first:1,        /* is first character in file \f */
-        dumb_tty:1,        /* is terminal type known */
-        eat_newline:1,        /* is newline ignored after 80 cols */
-        erase_input_ok:1,    /* is erase input supported */
-        erase_previous_ok:1,    /* is erase previous supported */
-        exit_on_eof:1,        /* exit on EOF */
-        first_file:1,        /* is the input file the first in list */
-        fold_long_lines:1,    /* fold long lines */
-        hard_tabs:1,        /* print spaces instead of '\t' */
-        hard_tty:1,        /* is this hard copy terminal (a printer or such) */
-        leading_colon:1,    /* key command has leading ':' character */
-        is_eof:1,               /* EOF detected */
-        is_paused:1,        /* is output paused */
-        no_quit_dialog:1,    /* suppress quit dialog */
-        no_scroll:1,        /* do not scroll, clear the screen and then display text */
-        no_tty_in:1,        /* is input in interactive mode */
-        no_tty_out:1,        /* is output in interactive mode */
-        no_tty_err:1,           /* is stderr terminal */
-        print_banner:1,        /* print file name banner */
-        reading_num:1,        /* are we reading leading_number */
-        report_errors:1,    /* is an error reported */
-        search_at_start:1,    /* search pattern defined at start up */
-        search_called:1,    /* previous more command was a search */
-        squeeze_spaces:1,    /* suppress white space */
-        stdout_glitch:1,    /* terminal has standout mode glitch */
-        stop_after_formfeed:1,    /* stop after form feeds */
-        suppress_bell:1,    /* suppress bell */
-        wrap_margin:1;        /* set if automargins */
+        ignore_stdin,          /* POLLHUP; peer closed pipe */
+        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 */
+        clear_first,          /* is first character in file \f */
+        dumb_tty,          /* is terminal type known */
+        eat_newline,          /* is newline ignored after 80 cols */
+        erase_input_ok,      /* is erase input supported */
+        erase_previous_ok,      /* is erase previous supported */
+        exit_on_eof,          /* exit on EOF */
+        first_file,          /* is the input file the first in list */
+        fold_long_lines,      /* fold long lines */
+        hard_tabs,          /* print spaces instead of '\t' */
+        hard_tty,          /* is this hard copy terminal (a printer or such) */
+        leading_colon,      /* key command has leading ':' character */
+        is_eof,                 /* EOF detected */
+        is_paused,          /* is output paused */
+        no_quit_dialog,      /* suppress quit dialog */
+        no_scroll,          /* do not scroll, clear the screen and then display text */
+        no_tty_in,          /* is input in interactive mode */
+        no_tty_out,          /* is output in interactive mode */
+        no_tty_err,             /* is stderr terminal */
+        print_banner,          /* print file name banner */
+        reading_num,          /* are we reading leading_number */
+        report_errors,      /* is an error reported */
+        search_at_start,      /* search pattern defined at start up */
+        search_called,      /* previous more command was a search */
+        squeeze_spaces,      /* suppress white space */
+        stdout_glitch,      /* terminal has standout mode glitch */
+        stop_after_formfeed,      /* stop after form feeds */
+        suppress_bell,      /* suppress bell */
+        wrap_margin;        /* set if automargins */
 };


 static void __attribute__((__noreturn__)) usage(void)
@@ -238,7 +241,7 @@ static void __attribute__((__noreturn__)) usage(void)
     printf(_(" %s [options] <file>...\n"), program_invocation_short_name);


     printf("%s", USAGE_SEPARATOR);
-    printf("%s\n", _("A file perusal filter for CRT viewing."));
+    printf("%s\n", _("Display the contents of a file in a terminal."));


     printf("%s", USAGE_OPTIONS);
     printf("%s\n", _(" -d, --silent          display help instead of ringing bell"));
@@ -299,7 +302,7 @@ static void argscan(struct more_control *ctl, int as_argc, char **as_argv)
             }
         }
         if (move) {
-            as_argc = remote_entry(as_argv, opt, as_argc);
+            as_argc = ul_remove_entry(as_argv, opt, as_argc);
             opt--;
         }
     }
@@ -354,14 +357,14 @@ static void env_argscan(struct more_control *ctl, const char *s)
     char *str = xstrdup(s);
     char *key = NULL, *tok;


-    env_argv = xmalloc(sizeof(char *) * size);
+    env_argv = xreallocarray(NULL, size, sizeof(char *));
     env_argv[0] = _("MORE environment variable");    /* program name */
     for (tok = strtok_r(str, delim, &key); tok; tok = strtok_r(NULL, delim, &key)) {
-        env_argv[env_argc++] = tok;
-        if (size < env_argc) {
+        if (size == env_argc) {
             size *= 2;
-            env_argv = xrealloc(env_argv, sizeof(char *) * size);
+            env_argv = xreallocarray(env_argv, size, sizeof(char *));
         }
+        env_argv[env_argc++] = tok;
     }


     argscan(ctl, env_argc, env_argv);
@@ -379,7 +382,7 @@ static void more_fseek(struct more_control *ctl, off_t pos)


 static int more_getc(struct more_control *ctl)
 {
-    int ret = getc(ctl->current_file);
+    int ret = fgetc(ctl->current_file);
     ctl->file_position = ftello(ctl->current_file);
     return ret;
 }
@@ -626,9 +629,6 @@ static int get_line(struct more_control *ctl, int *length)
             *p++ = 'L';
             column += 2;
             ctl->is_paused = 1;
-        } else if (c == EOF) {
-            *length = p - ctl->line_buf;
-            return column;
         } else {
 #ifdef HAVE_WIDECHAR
             if (ctl->fold_long_lines && MB_CUR_MAX > 1) {
@@ -1254,8 +1254,7 @@ static void __attribute__((__format__ (__printf__, 3, 4)))
         }
         va_end(argp);


-        args = alloca(sizeof(char *) * (argcount + 1));
-        args[argcount] = NULL;
+        args = xcalloc(argcount + 1, sizeof(char *));


         va_start(argp, cmd);
         arg = va_arg(argp, char *);
@@ -1353,50 +1352,98 @@ static void read_line(struct more_control *ctl)
     *p = '\0';
 }


-static int more_poll(struct more_control *ctl, int timeout)
+/* returns: 0 timeout or nothing; <0 error, >0 success */
+static int more_poll(struct more_control *ctl, int timeout, int *stderr_active)
 {
-    struct pollfd pfd[2];
+    enum {
+        POLLFD_SIGNAL = 0,
+        POLLFD_STDIN,
+        POLLFD_STDERR,
+    };
+    struct pollfd pfd[] = {
+        [POLLFD_SIGNAL] = { .fd = ctl->sigfd,    .events = POLLIN | POLLERR | POLLHUP },
+        [POLLFD_STDIN]  = { .fd = STDIN_FILENO,  .events = POLLIN | POLLERR | POLLHUP },
+        [POLLFD_STDERR] = { .fd = STDERR_FILENO, .events = POLLIN | POLLERR | POLLHUP }
+    };
+    int has_data = 0;


-    pfd[0].fd = ctl->sigfd;
-    pfd[0].events = POLLIN | POLLERR | POLLHUP;
-    pfd[1].fd = STDIN_FILENO;
-    pfd[1].events = POLLIN;
+    if (stderr_active)
+        *stderr_active = 0;


-    if (poll(pfd, 2, timeout) < 0) {
-        if (errno == EAGAIN)
-            return 1;
-        more_error(ctl, _("poll failed"));
-        return 1;
-    }
-    if (pfd[0].revents != 0) {
-        struct signalfd_siginfo info;
-        ssize_t sz;
-
-        sz = read(pfd[0].fd, &info, sizeof(info));
-        assert(sz == sizeof(info));
-        switch (info.ssi_signo) {
-        case SIGINT:
-            more_exit(ctl);
-            break;
-        case SIGQUIT:
-            sigquit_handler(ctl);
-            break;
-        case SIGTSTP:
-            sigtstp_handler(ctl);
-            break;
-        case SIGCONT:
-            sigcont_handler(ctl);
-            break;
-        case SIGWINCH:
-            sigwinch_handler(ctl);
-            break;
-        default:
-            abort();
+    while (!has_data) {
+        int rc;
+
+        if (ctl->ignore_stdin)
+            pfd[POLLFD_STDIN].fd = -1;    /* probably closed, ignore */
+
+        rc = poll(pfd, ARRAY_SIZE(pfd), timeout);
+
+        /* error */
+        if (rc < 0) {
+            if (errno == EAGAIN)
+                continue;
+
+            more_error(ctl, _("poll failed"));
+            return rc;
+        }
+
+        /* timeout */
+        if (rc == 0)
+            return 0;
+
+        /* event on signal FD */
+        if (pfd[POLLFD_SIGNAL].revents) {
+            struct signalfd_siginfo info;
+            ssize_t sz;
+
+            sz = read(pfd[POLLFD_SIGNAL].fd, &info, sizeof(info));
+            assert(sz == sizeof(info));
+            switch (info.ssi_signo) {
+            case SIGINT:
+                more_exit(ctl);
+                break;
+            case SIGQUIT:
+                sigquit_handler(ctl);
+                break;
+            case SIGTSTP:
+                sigtstp_handler(ctl);
+                break;
+            case SIGCONT:
+                sigcont_handler(ctl);
+                break;
+            case SIGWINCH:
+                sigwinch_handler(ctl);
+                break;
+            default:
+                abort();
+            }
+        }
+
+        /* event on stdin */
+        if (pfd[POLLFD_STDIN].revents) {
+            /* Check for POLLERR and POLLHUP in stdin revents */
+            if ((pfd[POLLFD_STDIN].revents & POLLERR) &&
+                (pfd[POLLFD_STDIN].revents & POLLHUP))
+                more_exit(ctl);
+
+            /* poll() return POLLHUP event after pipe close() and POLLNVAL
+             * means that fd is already closed. */
+            if ((pfd[POLLFD_STDIN].revents & POLLHUP) ||
+                (pfd[POLLFD_STDIN].revents & POLLNVAL))
+                ctl->ignore_stdin = 1;
+            else
+                has_data++;
+        }
+
+        /* event on stderr (we reads user commands from stderr!) */
+        if (pfd[POLLFD_STDERR].revents) {
+            has_data++;
+            if (stderr_active)
+                *stderr_active = 1;
         }
     }
-    if (pfd[1].revents == 0)
-        return 1;
-    return 0;
+
+    return has_data;
 }


 /* Search for nth occurrence of regular expression contained in buf in
@@ -1464,7 +1511,7 @@ static void search(struct more_control *ctl, char buf[], int n)
             }
             break;
         }
-        more_poll(ctl, 1);
+        more_poll(ctl, 0, NULL);
     }
     /* Move ctrl+c signal handling back to more_key_command(). */
     signal(SIGINT, SIG_DFL);
@@ -1618,7 +1665,7 @@ static int skip_forwards(struct more_control *ctl, int nlines, cc_t comchar)
 static int more_key_command(struct more_control *ctl, char *filename)
 {
     int retval = 0;
-    int done = 0, search_again = 0;
+    int done = 0, search_again = 0, stderr_active = 0;
     char cmdbuf[INIT_BUF];
     struct number_command cmd;


@@ -1628,13 +1675,18 @@ static int more_key_command(struct more_control *ctl, char *filename)
         ctl->report_errors = 0;
     ctl->search_called = 0;
     for (;;) {
-        if (more_poll(ctl, -1) != 0)
+        if (more_poll(ctl, -1, &stderr_active) <= 0)
+            continue;
+        if (stderr_active == 0)
             continue;
         cmd = read_command(ctl);
         if (cmd.key == more_kc_unknown_command)
             continue;
         if (cmd.key == more_kc_repeat_previous)
             cmd = ctl->previous_command;
+        else
+            ctl->previous_command = cmd;
+
         switch (cmd.key) {
         case more_kc_backwards:
             if (ctl->no_tty_in) {
@@ -1800,7 +1852,6 @@ static int more_key_command(struct more_control *ctl, char *filename)
             fflush(NULL);
             break;
         }
-        ctl->previous_command = cmd;
         if (done) {
             cmd.key = more_kc_unknown_command;
             break;
@@ -2046,14 +2097,16 @@ int main(int argc, char **argv)
     bindtextdomain(PACKAGE, LOCALEDIR);
     textdomain(PACKAGE);
     close_stdout_atexit();
-    setlocale(LC_ALL, "");


     /* Auto set no scroll on when binary is called page */
     if (!(strcmp(program_invocation_short_name, "page")))
         ctl.no_scroll++;


+    ctl.exit_on_eof = getenv("POSIXLY_CORRECT") ? 0 : 1;
+
     if ((s = getenv("MORE")) != NULL)
         env_argscan(&ctl, s);
+
     argscan(&ctl, argc, argv);


     /* clear any inherited settings */