]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/pager: Gracefully handle SIGPIPE errors
authorTobias Stoeckmann <tobias@stoeckmann.org>
Thu, 19 Feb 2026 17:16:32 +0000 (18:16 +0100)
committerTobias Stoeckmann <tobias@stoeckmann.org>
Thu, 19 Feb 2026 17:53:50 +0000 (18:53 +0100)
A user might prematurely close the pager before the parent wrote its
full output into the pipe. This triggers SIGPIPE, which would eventually
lead to an error return value.

The programs dmesg and fdisk can keep running just fine in these cases.
In fact, dmesg's source code was already prepared for EPIPE error return
values.

If the pager returns with a success return value, ignore SIGPIPE. If
there was an error within the pager, stop the parent as well.

With this, dmesg does not return an error anymore if the ring buffer was
not fully sent to the pager before it was closed.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
lib/pager.c

index 38b4af57a0a6eccb9a0d294578ec8413b8fa0eb1..3dc213dc964ab6d9757ec89c2d5c165a0996eaef 100644 (file)
@@ -45,6 +45,7 @@ struct child_process {
 static struct child_process pager_process;
 
 static volatile sig_atomic_t pager_caught_signal;
+static volatile sig_atomic_t pager_caught_sigpipe;
 
 static inline void close_pair(int fd[2])
 {
@@ -98,18 +99,24 @@ static int start_command(struct child_process *cmd)
        return 0;
 }
 
-static void wait_for_pager(void)
+static int wait_for_pager(void)
 {
        pid_t waiting;
+       int status;
 
        if (!pager_process.pid)
-               return;
+               return 0;
 
        do {
-               waiting = waitpid(pager_process.pid, NULL, 0);
+               waiting = waitpid(pager_process.pid, &status, 0);
                if (waiting == -1 && errno != EINTR)
                        ul_sig_err(EXIT_FAILURE, "waitpid failed");
        } while (waiting == -1);
+
+       if (waiting == pager_process.pid && WIFEXITED(status))
+               return WEXITSTATUS(status);
+
+       return 1;
 }
 
 static void catch_signal(int signo)
@@ -117,6 +124,11 @@ static void catch_signal(int signo)
        pager_caught_signal = signo;
 }
 
+static void catch_sigpipe(int signo)
+{
+       pager_caught_sigpipe = signo;
+}
+
 static void wait_for_pager_signal(int signo __attribute__ ((__unused__)))
 {
        UL_PROTECT_ERRNO;
@@ -220,6 +232,10 @@ static void __setup_pager(void)
        sigaction(SIGHUP,  &sa, &pager_process.orig_sighup);
        sigaction(SIGTERM, &sa, &pager_process.orig_sigterm);
        sigaction(SIGQUIT, &sa, &pager_process.orig_sigquit);
+
+       sa.sa_handler = catch_sigpipe;
+
+       /* this allows graceful handling of premature termination of pager */
        sigaction(SIGPIPE, &sa, &pager_process.orig_sigpipe);
 }
 
@@ -247,6 +263,7 @@ void pager_open(void)
 void pager_close(void)
 {
        struct sigaction sa;
+       int ret;
 
        if (pager_process.pid == 0)
                return;
@@ -259,7 +276,6 @@ void pager_close(void)
        sigaction(SIGHUP,  &sa, NULL);
        sigaction(SIGTERM, &sa, NULL);
        sigaction(SIGQUIT, &sa, NULL);
-       sigaction(SIGPIPE, &sa, NULL);
 
        fflush(NULL);
 
@@ -276,7 +292,7 @@ void pager_close(void)
        close(pager_process.org_out);
        close(pager_process.org_err);
 
-       wait_for_pager();
+       ret = wait_for_pager();
 
        /* restore original signal settings */
        sigaction(SIGCHLD, &pager_process.orig_sigchld, NULL);
@@ -286,11 +302,12 @@ void pager_close(void)
        sigaction(SIGQUIT, &pager_process.orig_sigquit, NULL);
        sigaction(SIGPIPE, &pager_process.orig_sigpipe, NULL);
 
-       if (pager_caught_signal)
+       if (pager_caught_signal || (pager_caught_sigpipe && ret))
                exit(EXIT_FAILURE);
 
        memset(&pager_process, 0, sizeof(pager_process));
        pager_caught_signal = 0;
+       pager_caught_sigpipe = 0;
 }
 
 #ifdef TEST_PROGRAM_PAGER