From: Pádraig Brady
Date: Fri, 21 Nov 2025 11:43:35 +0000 (+0000)
Subject: timeout: honor ignored signal dispositions
X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8c24619334114a2524c8f87dd831fa0912560555;p=thirdparty%2Fcoreutils.git
timeout: honor ignored signal dispositions
This behavior was depended on in our trap_sigpipe_or_skip_ helper,
and now that we're handling all terminating signals, we should
consistently honor their ignored signal dispositions.
* NEWS: Mention the change in behavior, especially in regard
to shell background jobs.
* src/timeout.c (sig_needs_handling): A new helper that return TRUE,
for --signal, SIG_ALRM, or non ignored signals.
(cleanup_install): Filter handled signals with the helper.
(block_cleanup_and_chld): Likewise.
* tests/timeout/timeout-group.sh: Adjust to use the now required
`env --default-signal=...` wrapper to reset (auto) ignored signals.
Also change the termination signal from SIGINT to SIGUSR1
to generalize the test signals not specially handled by the shell,
and newly handled by timeout(1).
* tests/timeout/timeout.sh: Add a test case for SIGPIPE
to ensure the ignored signal disposition is honored.
---
diff --git a/NEWS b/NEWS
index 834c490d15..86dd62c5dd 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,12 @@ GNU coreutils NEWS -*- outline -*-
'tail' now accepts the --debug option, which is currently used to
detail the --follow implementation being used.
+** Changes in behavior
+
+ 'timeout' now honors ignored signals and will not propagate them. E.g.,
+ timeout(1) in a shell backgrounded job, will not terminate upon receiving
+ SIGINT or SIGQUIT, as these are ignored by default in shell background jobs.
+
* Noteworthy changes in release 9.9 (2025-11-10) [stable]
diff --git a/src/timeout.c b/src/timeout.c
index 541017cd7e..2e770e9ff5 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -444,6 +444,23 @@ install_sigchld (void)
unblock_signal (SIGCHLD);
}
+/* Filter out signals that were ignored. */
+
+static bool
+sig_needs_handling (int sig, int sigterm)
+{
+ if (sig == SIGALRM || sig == sigterm)
+ return true; /* We can't ignore these. */
+
+ /* Note background jobs in shells have SIGINT and SIGQUIT
+ set to SIG_IGN by default. I.e., those signals will
+ not be propagated through background timeout jobs. */
+ struct sigaction old_sa;
+ sigaction (sig, nullptr, &old_sa);
+ bool ret = old_sa.sa_handler != SIG_IGN;
+ return ret;
+}
+
static void
install_cleanup (int sigterm)
{
@@ -454,11 +471,13 @@ install_cleanup (int sigterm)
more likely to work cleanly. */
for (int i = 0; i < countof (term_sig); i++)
- sigaction (term_sig[i], &sa, nullptr);
+ if (sig_needs_handling (term_sig[i], sigterm))
+ sigaction (term_sig[i], &sa, nullptr);
/* Real Time signals also terminate by default. */
for (int s = SIGRTMIN; s <= SIGRTMAX; s++)
- sigaction (s, &sa, nullptr);
+ if (sig_needs_handling (s, sigterm))
+ sigaction (s, &sa, nullptr);
sigaction (sigterm, &sa, nullptr); /* user specified termination signal. */
}
@@ -475,10 +494,12 @@ block_cleanup_and_chld (int sigterm, sigset_t *old_set)
sigemptyset (&block_set);
for (int i = 0; i < countof (term_sig); i++)
- sigaddset (&block_set, term_sig[i]);
+ if (sig_needs_handling (term_sig[i], sigterm))
+ sigaddset (&block_set, term_sig[i]);
for (int s = SIGRTMIN; s <= SIGRTMAX; s++)
- sigaddset (&block_set, s);
+ if (sig_needs_handling (s, sigterm))
+ sigaddset (&block_set, s);
sigaddset (&block_set, sigterm);
diff --git a/tests/timeout/timeout-group.sh b/tests/timeout/timeout-group.sh
index f73e6fb7c1..81dadcf9d5 100755
--- a/tests/timeout/timeout-group.sh
+++ b/tests/timeout/timeout-group.sh
@@ -17,7 +17,7 @@
# along with this program. If not, see