mirror of
https://github.com/coreutils/coreutils.git
synced 2024-11-24 10:23:31 +08:00
timeout: add the --kill-after option
Based on a report from Kim Hansen who wanted to send a KILL signal to the monitored command when `timeout` itself received a termination signal. Rather than changing such a signal into a KILL, we provide the more general mechanism of sending the KILL after the specified grace period. * src/timeout.c (cleanup): If a non zero kill delay is specified, (re)set the alarm to that delay, after which a KILL signal will be sent to the process group. (usage): Mention the new option. Separate the description of DURATION since it's now specified in 2 places. Clarify that the duration is an integer. (parse_duration): A new function refactored from main(), since this logic is now called for two parameters. (main): Parse the -k option. * doc/coreutils.texi (timeout invocation): Describe the new --kill-after option and use @display rather than @table to show the duration suffixes. Clarify that a duration of 0 disables the associated timeout. * tests/misc/timeout-parameters: Check invalid --kill-after. * tests/misc/timeout: Check a valid --kill-after works. * NEWS: Mention the new feature.
This commit is contained in:
parent
4edb86215d
commit
c403c31e88
4
NEWS
4
NEWS
@ -17,6 +17,10 @@ GNU coreutils NEWS -*- outline -*-
|
||||
join now accepts the --header option, to treat the first line of each
|
||||
file as a header line to be joined and printed unconditionally.
|
||||
|
||||
timeout now accepts the --kill-after option which sends a kill
|
||||
signal to the monitored command if it's still running the specified
|
||||
duration after the initial signal was sent.
|
||||
|
||||
who: the "+/-" --mesg (-T) indicator of whether a user/tty is accepting
|
||||
messages could be incorrectly listed as "+", when in fact, the user was
|
||||
not accepting messages (mesg no). Before, who would examine only the
|
||||
|
@ -15221,31 +15221,25 @@ might find this idea strange at first.
|
||||
still running after the specified time interval. Synopsis:
|
||||
|
||||
@example
|
||||
timeout [@var{option}] @var{number}[smhd] @var{command} [@var{arg}]@dots{}
|
||||
timeout [@var{option}] @var{duration} @var{command} [@var{arg}]@dots{}
|
||||
@end example
|
||||
|
||||
@cindex time units
|
||||
@var{number} is an integer followed by an optional unit; the default
|
||||
is seconds. The units are:
|
||||
|
||||
@table @samp
|
||||
@item s
|
||||
seconds
|
||||
@item m
|
||||
minutes
|
||||
@item h
|
||||
hours
|
||||
@item d
|
||||
days
|
||||
@end table
|
||||
|
||||
@var{command} must not be a special built-in utility (@pxref{Special
|
||||
built-in utilities}).
|
||||
|
||||
The program accepts the following option. Also see @ref{Common options}.
|
||||
The program accepts the following options. Also see @ref{Common options}.
|
||||
Options must precede operands.
|
||||
|
||||
@table @samp
|
||||
@item -k @var{duration}
|
||||
@itemx --kill-after=@var{duration}
|
||||
@opindex -k
|
||||
@opindex --kill-after
|
||||
Ensure the monitored @var{command} is killed by also sending a @samp{KILL}
|
||||
signal, after the specified @var{duration}. Without this option, if the
|
||||
selected signal proves not to be fatal, @command{timeout} does not kill
|
||||
the @var{command}.
|
||||
|
||||
@item -s @var{signal}
|
||||
@itemx --signal=@var{signal}
|
||||
@opindex -s
|
||||
@ -15253,9 +15247,18 @@ Options must precede operands.
|
||||
Send this @var{signal} to @var{command} on timeout, rather than the
|
||||
default @samp{TERM} signal. @var{signal} may be a name like @samp{HUP}
|
||||
or a number. Also see @xref{Signal specifications}.
|
||||
|
||||
@end table
|
||||
|
||||
@cindex time units
|
||||
@var{duration} is an integer followed by an optional unit:
|
||||
@display
|
||||
@samp{s} for seconds (the default)
|
||||
@samp{m} for minutes
|
||||
@samp{h} for hours
|
||||
@samp{d} for days
|
||||
@end display
|
||||
A duration of 0 disables the associated timeout.
|
||||
|
||||
@cindex exit status of @command{timeout}
|
||||
Exit status:
|
||||
|
||||
|
@ -77,9 +77,11 @@ static int timed_out;
|
||||
static int term_signal = SIGTERM; /* same default as kill command. */
|
||||
static int monitored_pid;
|
||||
static int sigs_to_ignore[NSIG]; /* so monitor can ignore sigs it resends. */
|
||||
static unsigned long kill_after;
|
||||
|
||||
static struct option const long_options[] =
|
||||
{
|
||||
{"kill-after", required_argument, NULL, 'k'},
|
||||
{"signal", required_argument, NULL, 's'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
@ -108,6 +110,13 @@ cleanup (int sig)
|
||||
sigs_to_ignore[sig] = 0;
|
||||
return;
|
||||
}
|
||||
if (kill_after)
|
||||
{
|
||||
/* Start a new timeout after which we'll send SIGKILL. */
|
||||
term_signal = SIGKILL;
|
||||
alarm (kill_after);
|
||||
kill_after = 0; /* Don't let later signals reset kill alarm. */
|
||||
}
|
||||
send_sig (0, sig);
|
||||
if (sig != SIGKILL && sig != SIGCONT)
|
||||
send_sig (0, SIGCONT);
|
||||
@ -125,20 +134,18 @@ usage (int status)
|
||||
else
|
||||
{
|
||||
printf (_("\
|
||||
Usage: %s [OPTION] NUMBER[SUFFIX] COMMAND [ARG]...\n\
|
||||
Usage: %s [OPTION] DURATION COMMAND [ARG]...\n\
|
||||
or: %s [OPTION]\n"), program_name, program_name);
|
||||
|
||||
fputs (_("\
|
||||
Start COMMAND, and kill it if still running after NUMBER seconds.\n\
|
||||
SUFFIX may be `s' for seconds (the default), `m' for minutes,\n\
|
||||
`h' for hours or `d' for days.\n\
|
||||
Start COMMAND, and kill it if still running after DURATION.\n\
|
||||
\n\
|
||||
"), stdout);
|
||||
|
||||
fputs (_("\
|
||||
Mandatory arguments to long options are mandatory for short options too.\n\
|
||||
"), stdout);
|
||||
fputs (_("\
|
||||
-k, --kill-after=DURATION\n\
|
||||
also send a KILL signal if COMMAND is still running\n\
|
||||
this long after the initial signal was sent.\n\
|
||||
-s, --signal=SIGNAL\n\
|
||||
specify the signal to be sent on timeout.\n\
|
||||
SIGNAL may be a name like `HUP' or a number.\n\
|
||||
@ -146,6 +153,12 @@ Mandatory arguments to long options are mandatory for short options too.\n\
|
||||
|
||||
fputs (HELP_OPTION_DESCRIPTION, stdout);
|
||||
fputs (VERSION_OPTION_DESCRIPTION, stdout);
|
||||
|
||||
fputs (_("\n\
|
||||
DURATION is an integer with an optional suffix:\n\
|
||||
`s' for seconds(the default), `m' for minutes, `h' for hours or `d' for days.\n\
|
||||
"), stdout);
|
||||
|
||||
fputs (_("\n\
|
||||
If the command times out, then exit with status 124. Otherwise, exit\n\
|
||||
with the status of COMMAND. If no signal is specified, send the TERM\n\
|
||||
@ -195,11 +208,32 @@ apply_time_suffix (unsigned long *x, char suffix_char)
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
parse_duration (const char* str)
|
||||
{
|
||||
unsigned long duration;
|
||||
char *ep;
|
||||
|
||||
if (xstrtoul (str, &ep, 10, &duration, NULL)
|
||||
/* Invalid interval. Note 0 disables timeout */
|
||||
|| (duration > UINT_MAX)
|
||||
/* Extra chars after the number and an optional s,m,h,d char. */
|
||||
|| (*ep && *(ep + 1))
|
||||
/* Check any suffix char and update timeout based on the suffix. */
|
||||
|| !apply_time_suffix (&duration, *ep))
|
||||
{
|
||||
error (0, 0, _("invalid time interval %s"), quote (str));
|
||||
usage (EXIT_CANCELED);
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
static void
|
||||
install_signal_handlers (int sigterm)
|
||||
{
|
||||
struct sigaction sa;
|
||||
sigemptyset(&sa.sa_mask); /* Allow concurrent calls to handler */
|
||||
sigemptyset (&sa.sa_mask); /* Allow concurrent calls to handler */
|
||||
sa.sa_handler = cleanup;
|
||||
sa.sa_flags = SA_RESTART; /* restart syscalls (like wait() below) */
|
||||
|
||||
@ -217,7 +251,6 @@ main (int argc, char **argv)
|
||||
unsigned long timeout;
|
||||
char signame[SIG2STR_MAX];
|
||||
int c;
|
||||
char *ep;
|
||||
|
||||
initialize_main (&argc, &argv);
|
||||
set_program_name (argv[0]);
|
||||
@ -231,10 +264,13 @@ main (int argc, char **argv)
|
||||
parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
|
||||
usage, AUTHORS, (char const *) NULL);
|
||||
|
||||
while ((c = getopt_long (argc, argv, "+s:", long_options, NULL)) != -1)
|
||||
while ((c = getopt_long (argc, argv, "+k:s:", long_options, NULL)) != -1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'k':
|
||||
kill_after = parse_duration (optarg);
|
||||
break;
|
||||
case 's':
|
||||
term_signal = operand2sig (optarg, signame);
|
||||
if (term_signal == -1)
|
||||
@ -249,18 +285,7 @@ main (int argc, char **argv)
|
||||
if (argc - optind < 2)
|
||||
usage (EXIT_CANCELED);
|
||||
|
||||
if (xstrtoul (argv[optind], &ep, 10, &timeout, NULL)
|
||||
/* Invalid interval. Note 0 disables timeout */
|
||||
|| (timeout > UINT_MAX)
|
||||
/* Extra chars after the number and an optional s,m,h,d char. */
|
||||
|| (*ep && *(ep + 1))
|
||||
/* Check any suffix char and update timeout based on the suffix. */
|
||||
|| !apply_time_suffix (&timeout, *ep))
|
||||
{
|
||||
error (0, 0, _("invalid time interval %s"), quote (argv[optind]));
|
||||
usage (EXIT_CANCELED);
|
||||
}
|
||||
optind++;
|
||||
timeout = parse_duration (argv[optind++]);
|
||||
|
||||
argv += optind;
|
||||
|
||||
|
@ -40,6 +40,12 @@ test $? = 2 || fail=1
|
||||
timeout 1 sleep 10
|
||||
test $? = 124 || fail=1
|
||||
|
||||
# kill delay. Note once the initial timeout triggers,
|
||||
# the exit status will be 124 even if the command
|
||||
# exits on its own accord.
|
||||
timeout -s0 -k1 1 sleep 10
|
||||
test $? = 124 && fail=1
|
||||
|
||||
# Ensure `timeout` is immune to parent's SIGCHLD handler
|
||||
# Use a subshell and an exec to work around a bug in FreeBSD 5.0 /bin/sh.
|
||||
(
|
||||
|
@ -35,6 +35,10 @@ test $? = 125 || fail=1
|
||||
timeout invalid sleep 0
|
||||
test $? = 125 || fail=1
|
||||
|
||||
# invalid kill delay
|
||||
timeout --kill-after=invalid 1 sleep 0
|
||||
test $? = 125 || fail=1
|
||||
|
||||
# invalid timeout suffix
|
||||
timeout 42D sleep 0
|
||||
test $? = 125 || fail=1
|
||||
|
Loading…
Reference in New Issue
Block a user