]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/pager: Improve signal handling in pager_close
authorTobias Stoeckmann <tobias@stoeckmann.org>
Wed, 4 Feb 2026 19:30:18 +0000 (20:30 +0100)
committerTobias Stoeckmann <tobias@stoeckmann.org>
Wed, 4 Feb 2026 19:30:18 +0000 (20:30 +0100)
During pager_close execution, incoming signals can have different
effects:

For one, waitpid might be called multiple times. In this case,
stdout/stderr are closed two times, leading to failing system calls.

Also, the original stdout/stderr can be closed by the signal handler
when waitpid succeeded already and dup2 calls were performed. The signal
handler erroneously tries to signal EOF to the child this way.

To fix these issues, only set a flag and perform wait_for_pager exactly
once. If one of these signals is received during the operation, call
_exit to imitate the signal handling behavior as good as possible.

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

index acace79f2ef2b4847200769f296887972bf0d1d1..bf626607f474d5bd5cffeb6d22004b1d3ab0e831 100644 (file)
@@ -44,6 +44,8 @@ struct child_process {
 };
 static struct child_process pager_process;
 
+static volatile sig_atomic_t caught_signal;
+
 static inline void close_pair(int fd[2])
 {
        close(fd[0]);
@@ -114,6 +116,11 @@ static void wait_for_pager(void)
        } while (waiting == -1);
 }
 
+static void catch_signal(int signo)
+{
+       caught_signal = signo;
+}
+
 static void wait_for_pager_signal(int signo __attribute__ ((__unused__)))
 {
        UL_PROTECT_ERRNO;
@@ -244,11 +251,26 @@ void pager_open(void)
  */
 void pager_close(void)
 {
+       struct sigaction sa;
+
        if (pager_process.pid == 0)
                return;
 
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = catch_signal;
+
+       /* set flag instead of calling wait_for_pager again */
+       sigaction(SIGINT,  &sa, NULL);
+       sigaction(SIGHUP,  &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGQUIT, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
        wait_for_pager();
 
+       if (caught_signal)
+               _exit(EXIT_FAILURE);
+
        /* restore original output */
        dup2(pager_process.org_out, STDOUT_FILENO);
        dup2(pager_process.org_err, STDERR_FILENO);
@@ -265,6 +287,7 @@ void pager_close(void)
        sigaction(SIGPIPE, &pager_process.orig_sigpipe, NULL);
 
        memset(&pager_process, 0, sizeof(pager_process));
+       caught_signal = 0;
 }
 
 #ifdef TEST_PROGRAM_PAGER