git: 844cef26e810 - main - timeout(1): Catch all signals and propagate them
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 16 Apr 2025 19:46:37 UTC
The branch main has been updated by bapt: URL: https://cgit.FreeBSD.org/src/commit/?id=844cef26e810d903e11bf83cc6f6fd2e22a299f1 commit 844cef26e810d903e11bf83cc6f6fd2e22a299f1 Author: Aaron LI <aly@aaronly.me> AuthorDate: 2025-04-03 01:20:01 +0000 Commit: Baptiste Daroussin <bapt@FreeBSD.org> CommitDate: 2025-04-16 19:45:38 +0000 timeout(1): Catch all signals and propagate them The POSIX.1-2024 standard requires that timeout(1) utility propagate all signals except SIGALRM, so timeout(1) needs to catch all signals for this purpose. In addition, we need to separate those signals whose default action is to terminate the program, because timeout(1) should start the second timer for the kill signal if those signals are received. Obtained-from: DragonFly BSD Reference: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/timeout.html --- bin/timeout/timeout.1 | 22 ++++++++++++ bin/timeout/timeout.c | 92 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 79 insertions(+), 35 deletions(-) diff --git a/bin/timeout/timeout.1 b/bin/timeout/timeout.1 index 371a167d19f3..44525daaec59 100644 --- a/bin/timeout/timeout.1 +++ b/bin/timeout/timeout.1 @@ -73,6 +73,28 @@ inherited, except for the signal that will be sent upon timeout, which is reset to take the default action and should terminate the process. .Pp +If +.Nm +receives the +.Dv SIGALRM +signal, it will behave as if the time limit has been reached +and send the specified signal to +.Ar command . +For any other signals delivered to +.Nm , +it will propagate them to +.Ar command , +with the exception of +.Dv SIGKILL +and +.Dv SIGSTOP . +If you want to prevent the +.Ar command +from being timed out, send +.Dv SIGKILL +to +.Nm . +.Pp The options are as follows: .Bl -tag -width indent .It Fl f , Fl -foreground diff --git a/bin/timeout/timeout.c b/bin/timeout/timeout.c index 1c4cfa6e017d..6e93e9e2911c 100644 --- a/bin/timeout/timeout.c +++ b/bin/timeout/timeout.c @@ -1,6 +1,7 @@ /*- * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org> * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org> + * Copyright (c) 2025 Aaron LI <aly@aaronly.me> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,8 +48,10 @@ #define EXIT_CMD_NOENT 127 static volatile sig_atomic_t sig_chld = 0; -static volatile sig_atomic_t sig_term = 0; static volatile sig_atomic_t sig_alrm = 0; +static volatile sig_atomic_t sig_term = 0; /* signal to terminate children */ +static volatile sig_atomic_t sig_other = 0; /* signal to propagate */ +static int killsig = SIGTERM; /* signal to kill children */ static const char *command = NULL; static bool verbose = false; @@ -137,19 +140,46 @@ parse_signal(const char *str) static void sig_handler(int signo) { - switch (signo) { - case SIGINT: - case SIGHUP: - case SIGQUIT: - case SIGTERM: + if (signo == killsig) { sig_term = signo; - break; + return; + } + + switch (signo) { case SIGCHLD: sig_chld = 1; break; case SIGALRM: sig_alrm = 1; break; + case SIGHUP: + case SIGINT: + case SIGQUIT: + case SIGILL: + case SIGTRAP: + case SIGABRT: + case SIGEMT: + case SIGFPE: + case SIGBUS: + case SIGSEGV: + case SIGSYS: + case SIGPIPE: + case SIGTERM: + case SIGXCPU: + case SIGXFSZ: + case SIGVTALRM: + case SIGPROF: + case SIGUSR1: + case SIGUSR2: + /* + * Signals with default action to terminate the process. + * See the sigaction(2) man page. + */ + sig_term = signo; + break; + default: + sig_other = signo; + break; } } @@ -214,8 +244,6 @@ main(int argc, char **argv) { int ch, status, sig; int pstat = 0; - int killsig = SIGTERM; - size_t i; pid_t pid, cpid; double first_kill; double second_kill = 0; @@ -225,17 +253,8 @@ main(int argc, char **argv) bool do_second_kill = false; bool child_done = false; sigset_t zeromask, allmask, oldmask; - struct sigaction signals; + struct sigaction sa; struct procctl_reaper_status info; - int signums[] = { - -1, - SIGTERM, - SIGINT, - SIGHUP, - SIGCHLD, - SIGALRM, - SIGQUIT, - }; const char optstr[] = "+fhk:ps:v"; const struct option longopts[] = { @@ -316,22 +335,17 @@ main(int argc, char **argv) /* parent continues here */ - memset(&signals, 0, sizeof(signals)); - sigemptyset(&signals.sa_mask); - - if (killsig != SIGKILL && killsig != SIGSTOP) - signums[0] = killsig; - - for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i++) - sigaddset(&signals.sa_mask, signums[i]); - - signals.sa_handler = sig_handler; - signals.sa_flags = SA_RESTART; - - for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i++) { - if (signums[i] > 0 && - sigaction(signums[i], &signals, NULL) == -1) - err(EXIT_FAILURE, "sigaction()"); + /* Catch all signals in order to propagate them. */ + memset(&sa, 0, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_handler = sig_handler; + sa.sa_flags = SA_RESTART; + for (sig = 1; sig < sys_nsig; sig++) { + if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT || + sig == SIGTTIN || sig == SIGTTOU) + continue; + if (sigaction(sig, &sa, NULL) == -1) + err(EXIT_FAILURE, "sigaction(%d)", sig); } /* Don't stop if background child needs TTY */ @@ -399,6 +413,14 @@ main(int argc, char **argv) do_second_kill = false; killsig = SIGKILL; } + + } else if (sig_other) { + /* Propagate any other signals. */ + sig = sig_other; + sig_other = 0; + logv("received signal %s(%d)", sys_signame[sig], sig); + + send_sig(pid, sig, foreground); } }