git: 790e3617361a - main - timeout(1): Multiple minor tweaks and cleanups
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 16 Apr 2025 19:46:28 UTC
The branch main has been updated by bapt: URL: https://cgit.FreeBSD.org/src/commit/?id=790e3617361a686c76276e0c2252a761726cd919 commit 790e3617361a686c76276e0c2252a761726cd919 Author: Aaron LI <aly@aaronly.me> AuthorDate: 2025-04-02 11:53:42 +0000 Commit: Baptiste Daroussin <bapt@FreeBSD.org> CommitDate: 2025-04-16 19:45:38 +0000 timeout(1): Multiple minor tweaks and cleanups --- bin/timeout/timeout.1 | 65 ++++++++++++++------------ bin/timeout/timeout.c | 124 +++++++++++++++++++++++++------------------------- 2 files changed, 97 insertions(+), 92 deletions(-) diff --git a/bin/timeout/timeout.1 b/bin/timeout/timeout.1 index 1a5fd95a6256..b81ce2f74625 100644 --- a/bin/timeout/timeout.1 +++ b/bin/timeout/timeout.1 @@ -24,7 +24,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd January 4, 2025 +.Dd April 2, 2025 .Dt TIMEOUT 1 .Os .Sh NAME @@ -33,36 +33,44 @@ .Sh SYNOPSIS .Nm .Op Fl k Ar time | Fl -kill-after Ar time -.Op Fl s Ar sig | Fl -signal Ar sig +.Op Fl s Ar signal | Fl -signal Ar signal .Op Fl v | Fl -verbose .Op Fl -foreground .Op Fl -preserve-status .Ar duration .Ar command -.Op Ar args ... +.Op Ar arg ... .Sh DESCRIPTION -.Nm +.Nm Timeout starts the .Ar command with its -.Ar args . +.Ar arg +list. If the .Ar command is still running after .Ar duration , -it is killed. -By default, +it is killed by sending the +.Ar signal , +or .Dv SIGTERM -is sent. +if the +.Fl s +option is unspecified. The special .Ar duration , zero, signifies no limit. -Therefore a signal is never sent if +Therefore, a signal is never sent if .Ar duration is 0. .Pp The options are as follows: .Bl -tag -width indent +.It Fl -foreground +Only time out the +.Ar command +itself, but do not propagate signals to its descendants. .It Fl k Ar time , Fl -kill-after Ar time Send a .Dv SIGKILL @@ -70,32 +78,29 @@ signal if .Ar command is still running after .Ar time -after the first signal was sent. -.It Fl s Ar sig , Fl -signal Ar sig +since the first signal was sent. +.It Fl -preserve-status +Always exit with the same status as +.Ar command , +even if the timeout was reached. +.It Fl s Ar signal , Fl -signal Ar signal Specify the signal to send on timeout. By default, .Dv SIGTERM is sent. .It Fl v , Fl -verbose Show information to stderr about any signal sent on timeout. -.It Fl -foreground -Do not propagate timeout to the children of -.Ar command . -.It Fl -preserve-status -Exit with the same status as -.Ar command , -even if it times out and is killed. .El -.Sh DURATION FORMAT +.Ss Duration Format The .Ar duration and .Ar time are non-negative integer or real (decimal) numbers, with an optional -unit-specifying suffix. +suffix specifying the unit. Values without an explicit unit are interpreted as seconds. .Pp -Supported unit symbols are: +Supported unit suffixes are: .Bl -tag -offset indent -width indent -compact .It Cm s seconds @@ -152,9 +157,9 @@ $ echo $? Run .Xr sleep 1 for 4 seconds and terminate process after 2 seconds. -124 is returned since no +The exit status is 124 since .Fl -preserve-status -is used: +is not used: .Bd -literal -offset indent $ timeout 2 sleep 4 $ echo $? @@ -162,8 +167,8 @@ $ echo $? .Ed .Pp Same as above but preserving status. -Exit status is 128 + signal number (15 for -.Va SIGTERM ) : +The exit status is 128 + signal number (15 for +.Dv SIGTERM ) : .Bd -literal -offset indent $ timeout --preserve-status 2 sleep 4 $ echo $? @@ -171,9 +176,9 @@ $ echo $? .Ed .Pp Same as above but sending -.Va SIGALRM +.Dv SIGALRM (signal number 14) instead of -.Va SIGTERM : +.Dv SIGTERM : .Bd -literal -offset indent $ timeout --preserve-status -s SIGALRM 2 sleep 4 $ echo $? @@ -186,9 +191,9 @@ the PDF version of the .Fx Handbook. Send a -.Va SIGTERM +.Dv SIGTERM signal after 1 minute and send a -.Va SIGKILL +.Dv SIGKILL signal 5 seconds later if the process refuses to stop: .Bd -literal -offset indent $ timeout -k 5s 1m fetch \\ @@ -202,7 +207,7 @@ $ timeout -k 5s 1m fetch \\ .Sh STANDARDS The .Nm -utility is compliant with the +utility is expected to conform to the .St -p1003.1-2024 specification. .Sh HISTORY diff --git a/bin/timeout/timeout.c b/bin/timeout/timeout.c index 397d692d8647..aaecd7fec2e5 100644 --- a/bin/timeout/timeout.c +++ b/bin/timeout/timeout.c @@ -40,10 +40,10 @@ #include <string.h> #include <unistd.h> -#define EXIT_TIMEOUT 124 -#define EXIT_INVALID 125 -#define EXIT_CMD_ERROR 126 -#define EXIT_CMD_NOENT 127 +#define EXIT_TIMEOUT 124 +#define EXIT_INVALID 125 +#define EXIT_CMD_ERROR 126 +#define EXIT_CMD_NOENT 127 static volatile sig_atomic_t sig_chld = 0; static volatile sig_atomic_t sig_term = 0; @@ -52,15 +52,14 @@ static volatile sig_atomic_t sig_ign = 0; static const char *command = NULL; static bool verbose = false; -static void +static void __dead2 usage(void) { - - fprintf(stderr, "Usage: %s [-k time | --kill-after time]" - " [-s sig | --signal sig] [-v | --verbose] [--foreground]" - " [--preserve-status] <duration> <command> <arg ...>\n", + fprintf(stderr, + "Usage: %s [--foreground] [-k time | --kill-after time]" + " [--preserve-status] [-s signal | --signal signal] " + " [-v | --verbose] <duration> <command> [arg ...]\n", getprogname()); - exit(EXIT_FAILURE); } @@ -109,13 +108,11 @@ parse_signal(const char *str) const char *errstr; sig = strtonum(str, 1, sys_nsig - 1, &errstr); - if (errstr == NULL) return (sig); if (strncasecmp(str, "SIG", 3) == 0) str += 3; - for (i = 1; i < sys_nsig; i++) { if (strcasecmp(str, sys_signame[i]) == 0) return (i); @@ -133,7 +130,6 @@ sig_handler(int signo) } switch (signo) { - case 0: case SIGINT: case SIGHUP: case SIGQUIT: @@ -154,7 +150,7 @@ send_sig(pid_t pid, int signo) { if (verbose) { warnx("sending signal %s(%d) to command '%s'", - sys_signame[signo], signo, command); + sys_signame[signo], signo, command); } kill(pid, signo); } @@ -165,9 +161,11 @@ set_interval(double iv) struct itimerval tim; memset(&tim, 0, sizeof(tim)); - tim.it_value.tv_sec = (time_t)iv; - iv -= (double)tim.it_value.tv_sec; - tim.it_value.tv_usec = (suseconds_t)(iv * 1000000UL); + if (iv > 0) { + tim.it_value.tv_sec = (time_t)iv; + iv -= (double)(time_t)iv; + tim.it_value.tv_usec = (suseconds_t)(iv * 1000000UL); + } if (setitimer(ITIMER_REAL, &tim, NULL) == -1) err(EXIT_FAILURE, "setitimer()"); @@ -176,9 +174,9 @@ set_interval(double iv) int main(int argc, char **argv) { - int ch; + int ch, status; int foreground, preserve; - int pstat, status; + int pstat = 0; int killsig = SIGTERM; size_t i; pid_t pid, cpid; @@ -204,38 +202,36 @@ main(int argc, char **argv) second_kill = 0; const struct option longopts[] = { - { "preserve-status", no_argument, &preserve, 1 }, - { "foreground", no_argument, &foreground, 1 }, - { "kill-after", required_argument, NULL, 'k'}, - { "signal", required_argument, NULL, 's'}, - { "help", no_argument, NULL, 'h'}, - { "verbose", no_argument, NULL, 'v'}, - { NULL, 0, NULL, 0 } + { "foreground", no_argument, &foreground, 1 }, + { "help", no_argument, NULL, 'h' }, + { "kill-after", required_argument, NULL, 'k' }, + { "preserve-status", no_argument, &preserve, 1 }, + { "signal", required_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 }, }; while ((ch = getopt_long(argc, argv, "+k:s:vh", longopts, NULL)) != -1) { switch (ch) { - case 'k': - do_second_kill = true; - second_kill = parse_duration(optarg); - break; - case 's': - killsig = parse_signal(optarg); - break; - case 'v': - verbose = true; - break; - case 0: - break; - case 'h': - default: - usage(); + case 'k': + do_second_kill = true; + second_kill = parse_duration(optarg); + break; + case 's': + killsig = parse_signal(optarg); + break; + case 'v': + verbose = true; + break; + case 0: + break; + default: + usage(); } } argc -= optind; argv += optind; - if (argc < 2) usage(); @@ -247,7 +243,7 @@ main(int argc, char **argv) if (!foreground) { /* Acquire a reaper */ if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) == -1) - err(EXIT_FAILURE, "Fail to acquire the reaper"); + err(EXIT_FAILURE, "procctl(PROC_REAP_ACQUIRE)"); } memset(&signals, 0, sizeof(signals)); @@ -263,7 +259,7 @@ main(int argc, char **argv) signals.sa_flags = SA_RESTART; for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i++) { - if (signums[i] != -1 && signums[i] != 0 && + if (signums[i] > 0 && sigaction(signums[i], &signals, NULL) == -1) err(EXIT_FAILURE, "sigaction()"); } @@ -273,9 +269,9 @@ main(int argc, char **argv) signal(SIGTTOU, SIG_IGN); pid = fork(); - if (pid == -1) + if (pid == -1) { err(EXIT_FAILURE, "fork()"); - else if (pid == 0) { + } else if (pid == 0) { /* child process */ signal(SIGTTIN, SIG_DFL); signal(SIGTTOU, SIG_DFL); @@ -285,14 +281,15 @@ main(int argc, char **argv) _exit(errno == ENOENT ? EXIT_CMD_NOENT : EXIT_CMD_ERROR); } + /* parent continues here */ + if (sigprocmask(SIG_BLOCK, &signals.sa_mask, NULL) == -1) err(EXIT_FAILURE, "sigprocmask()"); - /* parent continues here */ set_interval(first_kill); + sigemptyset(&signals.sa_mask); for (;;) { - sigemptyset(&signals.sa_mask); sigsuspend(&signals.sa_mask); if (sig_chld) { @@ -300,9 +297,7 @@ main(int argc, char **argv) while ((cpid = waitpid(-1, &status, WNOHANG)) != 0) { if (cpid < 0) { - if (errno == EINTR) - continue; - else + if (errno != EINTR) break; } else if (cpid == pid) { pstat = status; @@ -328,16 +323,18 @@ main(int argc, char **argv) killemall.rk_flags = 0; procctl(P_PID, getpid(), PROC_REAP_KILL, &killemall); - } else + } else { send_sig(pid, killsig); + } if (do_second_kill) { set_interval(second_kill); do_second_kill = false; sig_ign = killsig; killsig = SIGKILL; - } else + } else { break; + } } else if (sig_term) { if (!foreground) { @@ -345,34 +342,37 @@ main(int argc, char **argv) killemall.rk_flags = 0; procctl(P_PID, getpid(), PROC_REAP_KILL, &killemall); - } else + } else { send_sig(pid, sig_term); + } if (do_second_kill) { set_interval(second_kill); do_second_kill = false; sig_ign = killsig; killsig = SIGKILL; - } else + } else { break; + } } } while (!child_done && wait(&pstat) == -1) { if (errno != EINTR) - err(EXIT_FAILURE, "waitpid()"); + err(EXIT_FAILURE, "wait()"); } if (!foreground) procctl(P_PID, getpid(), PROC_REAP_RELEASE, NULL); - if (WEXITSTATUS(pstat)) - pstat = WEXITSTATUS(pstat); - else if (WIFSIGNALED(pstat)) - pstat = 128 + WTERMSIG(pstat); - - if (timedout && !preserve) + if (timedout && !preserve) { pstat = EXIT_TIMEOUT; + } else { + if (WIFEXITED(pstat)) + pstat = WEXITSTATUS(pstat); + else if (WIFSIGNALED(pstat)) + pstat = 128 + WTERMSIG(pstat); + } return (pstat); }