/* timeout -- run a command with bounded time This is the timeout utility
Copyright (C) 2008-2018 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* timeout - Start a command, and kill it if the specified timeout expires
We try to behave like a shell starting a single (foreground) job,
and will kill the job if we receive the alarm signal we setup.
The exit status of the job is returned, or one of these errors:
EXIT_TIMEDOUT 124 job timed out
EXIT_CANCELED 125 internal error
EXIT_CANNOT_INVOKE 126 error executing job
EXIT_ENOENT 127 couldn't find job to exec
Caveats:
If user specifies the KILL (9) signal is to be sent on timeout,
the monitor is killed and so exits with 128+9 rather than 124.
If you start a command in the background, which reads from the tty
and so is immediately sent SIGTTIN to stop, then the timeout
process will ignore this so it can timeout the command as expected.
This can be seen with 'timeout 10 dd&' for example.
However if one brings this group to the foreground with the 'fg'
command before the timer expires, the command will remain
in the stop state as the shell doesn't send a SIGCONT
because the timeout process (group leader) is already running.
To get the command running again one can Ctrl-Z, and do fg again.
Note one can Ctrl-C the whole job when in this state.
I think this could be fixed but I'm not sure the extra
complication is justified for this scenario.
Written by Pádraig Brady. */
#include <config.h> Provides system specific information
#include <getopt.h> ...!includes auto-comment...
#include <stdio.h> Provides standard I/O capability
#include <sys/types.h> Provides system data types
#include <signal.h> ...!includes auto-comment...
#if HAVE_PRCTL Line 52
# include <sys/prctl.h> Line 53
#endif Line 54
#include <sys/wait.h> ...!includes auto-comment...
#include "system.h" ...!includes auto-comment...
#include "c-strtod.h" ...!includes auto-comment...
#include "xstrtod.h" ...!includes auto-comment...
#include "sig2str.h" ...!includes auto-comment...
#include "operand2sig.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "quote.h" ...!includes auto-comment...
#if HAVE_SETRLIMIT Line 65
/* FreeBSD 5.0 at least needs <sys/types.h> and <sys/time.h> included
before <sys/resource.h>. Currently "system.h" includes <sys/time.h>. */
# include <sys/resource.h> ...!includes auto-comment...
#endif Line 69
/* NonStop circa 2011 lacks both SA_RESTART and siginterrupt. */
#ifndef SA_RESTART Line 72
# define SA_RESTART 0 Line 73
#endif Line 74
#define PROGRAM_NAME "timeout" Line 76
#define AUTHORS proper_name ("Padraig Brady") Line 78
static int timed_out; Line 80
static int term_signal = SIGTERM; /* same default as kill command. */ Line 81
static pid_t monitored_pid; Line 82
static double kill_after; Line 83
static bool foreground; /* whether to use another program group. */ Line 84
static bool preserve_status; /* whether to use a timeout status or not. */ Line 85
static bool verbose; /* whether to diagnose timeouts or not. */ Line 86
static char const* command; Line 87
/* for long options with no corresponding short option, use enum */
enum Line 90
{
FOREGROUND_OPTION = CHAR_MAX + 1, Line 92
PRESERVE_STATUS_OPTION Line 93
}; Block 1
static struct option const long_options[] = Line 96
{
{"kill-after", required_argument, NULL, 'k'}, Line 98
{"signal", required_argument, NULL, 's'}, Line 99
{"verbose", no_argument, NULL, 'v'}, Line 100
{"foreground", no_argument, NULL, FOREGROUND_OPTION}, Line 101
{"preserve-status", no_argument, NULL, PRESERVE_STATUS_OPTION}, Line 102
{GETOPT_HELP_OPTION_DECL}, Line 103
{GETOPT_VERSION_OPTION_DECL}, Line 104
{NULL, 0, NULL, 0} Line 105
}; Block 2
/* Start the timeout after which we'll receive a SIGALRM.
Round DURATION up to the next representable value.
Treat out-of-range values as if they were maximal,
as that's more useful in practice than reporting an error.
'0' means don't timeout. */
static void Line 113
settimeout (double duration, bool warn) Line 114
{
/* timer_settime() provides potentially nanosecond resolution.
setitimer() is more portable (to Darwin for example),
but only provides microsecond resolution and thus is
a little more awkward to use with timespecs, as well as being
deprecated by POSIX. Instead we fallback to single second
resolution provided by alarm(). */
#if HAVE_TIMER_SETTIME Line 124
struct timespec ts = dtotimespec (duration); Line 125
struct itimerspec its = { {0, 0}, ts }; Line 126
timer_t timerid; Line 127
if (timer_create (CLOCK_REALTIME, NULL, &timerid) == 0) Line 128
{
if (timer_settime (timerid, 0, &its, NULL) == 0) Line 130
return; Line 131
else Line 132
{
if (warn) Line 134
error (0, errno, _("warning: timer_settime")); Line 135
timer_delete (timerid); Line 136
}
}
else if (warn && errno != ENOSYS) Line 139
error (0, errno, _("warning: timer_create")); Line 140
#endif Line 141
unsigned int timeint; Line 143
if (UINT_MAX <= duration) Line 144
timeint = UINT_MAX; Line 145
else Line 146
{
unsigned int duration_floor = duration; Line 148
timeint = duration_floor + (duration_floor < duration); Line 149
}
alarm (timeint); Line 151
}
/* send SIG avoiding the current process. */
static int Line 156
send_sig (pid_t where, int sig) Line 157
{
/* If sending to the group, then ignore the signal,
so we don't go into a signal loop. Note that this will ignore any of the
signals registered in install_cleanup(), that are sent after we
propagate the first one, which hopefully won't be an issue. Note this
process can be implicitly multithreaded due to some timer_settime()
implementations, therefore a signal sent to the group, can be sent
multiple times to this process. */
if (where == 0) Line 166
signal (sig, SIG_IGN); Line 167
return kill (where, sig); Line 168
}
/* Signal handler which is required for sigsuspend() to be interrupted
whenever SIGCHLD is received. */
static void Line 173
chld (int sig) Line 174
{
}
static void Line 179
cleanup (int sig) Line 180
{
if (sig == SIGALRM) Line 182
{
timed_out = 1; Line 184
sig = term_signal; Line 185
}
if (monitored_pid) Line 187
{
if (kill_after) Line 189
{
int saved_errno = errno; /* settimeout may reset. */ Line 191
/* Start a new timeout after which we'll send SIGKILL. */
term_signal = SIGKILL; Line 193
settimeout (kill_after, false); Line 194
kill_after = 0; /* Don't let later signals reset kill alarm. */ Line 195
errno = saved_errno; Line 196
}
/* Send the signal directly to the monitored child,
in case it has itself become group leader,
or is not running in a separate group. */
if (verbose) Line 202
{
char signame[MAX (SIG2STR_MAX, INT_BUFSIZE_BOUND (int))]; Line 204
if (sig2str (sig, signame) != 0) Line 205
snprintf (signame, sizeof signame, "%d", sig); Line 206
error (0, 0, _("sending signal %s to command %s"), Line 207
signame, quote (command)); Line 208
}
send_sig (monitored_pid, sig); Line 210
/* The normal case is the job has remained in our
newly created process group, so send to all processes in that. */
if (!foreground) Line 214
{
send_sig (0, sig); Line 216
if (sig != SIGKILL && sig != SIGCONT) Line 217
{
send_sig (monitored_pid, SIGCONT); Line 219
send_sig (0, SIGCONT); Line 220
}
}
}
else /* we're the child or the child is not exec'd yet. */ Line 224
_exit (128 + sig); Line 225
} Block 6
void Line 228
usage (int status) Line 229
{
if (status != EXIT_SUCCESS) Line 231
emit_try_help (); ...!common auto-comment...
else Line 233
{
printf (_("\ Line 235
Usage: %s [OPTION] DURATION COMMAND [ARG]...\n\ Line 236
or: %s [OPTION]\n"), program_name, program_name); Line 237
fputs (_("\ Line 239
Start COMMAND, and kill it if still running after DURATION.\n\ Line 240
"), stdout); Line 241
emit_mandatory_arg_note (); ...!common auto-comment...
fputs (_("\ Line 245
--preserve-status\n\ Line 246
exit with the same status as COMMAND, even when the\n\ Line 247
command times out\n\ Line 248
--foreground\n\ Line 249
when not running timeout directly from a shell prompt,\n\ Line 250
allow COMMAND to read from the TTY and get TTY signals;\n\ Line 251
in this mode, children of COMMAND will not be timed out\n\ Line 252
-k, --kill-after=DURATION\n\ Line 253
also send a KILL signal if COMMAND is still running\n\ Line 254
this long after the initial signal was sent\n\ Line 255
-s, --signal=SIGNAL\n\ Line 256
specify the signal to be sent on timeout;\n\ Line 257
SIGNAL may be a name like 'HUP' or a number;\n\ Line 258
see 'kill -l' for a list of signals\n"), stdout); Line 259
fputs (_("\ Line 260
-v, --verbose diagnose to stderr any signal sent upon timeout\n"), stdout); Line 261
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 263
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 264
fputs (_("\n\ Line 266
DURATION is a floating point number with an optional suffix:\n\ Line 267
's' for seconds (the default), 'm' for minutes, 'h' for hours or \ Line 268
'd' for days.\nA duration of 0 disables the associated timeout.\n"), stdout); Line 269
fputs (_("\n\ Line 271
If the command times out, and --preserve-status is not set, then exit with\n\ Line 272
status 124. Otherwise, exit with the status of COMMAND. If no signal\n\ Line 273
is specified, send the TERM signal upon timeout. The TERM signal kills\n\ Line 274
any process that does not block or catch that signal. It may be necessary\n\ Line 275
to use the KILL (9) signal, since this signal cannot be caught, in which\n\ Line 276
case the exit status is 128+9 rather than 124.\n"), stdout); Line 277
emit_ancillary_info (PROGRAM_NAME); Line 278
}
exit (status); Line 280
} Block 7
/* Given a floating point value *X, and a suffix character, SUFFIX_CHAR,
scale *X by the multiplier implied by SUFFIX_CHAR. SUFFIX_CHAR may
be the NUL byte or 's' to denote seconds, 'm' for minutes, 'h' for
hours, or 'd' for days. If SUFFIX_CHAR is invalid, don't modify *X
and return false. Otherwise return true. */
static bool Line 289
apply_time_suffix (double *x, char suffix_char) Line 290
{
int multiplier; Line 292
switch (suffix_char) Line 294
{
case 0: Line 296
case 's': Line 297
multiplier = 1; Line 298
break; Line 299
case 'm': Line 300
multiplier = 60; Line 301
break; Line 302
case 'h': Line 303
multiplier = 60 * 60; Line 304
break; Line 305
case 'd': Line 306
multiplier = 60 * 60 * 24; Line 307
break; Line 308
default: Line 309
return false; Line 310
}
*x *= multiplier; Line 313
return true; Line 315
} Block 8
static double Line 318
parse_duration (const char* str) Line 319
{
double duration; Line 321
const char *ep; Line 322
if (! (xstrtod (str, &ep, &duration, c_strtod) || errno == ERANGE) Line 324
/* Nonnegative interval. */
|| ! (0 <= duration) Line 326
/* No extra chars after the number and an optional s,m,h,d char. */
|| (*ep && *(ep + 1)) Line 328
/* Check any suffix char and update timeout based on the suffix. */
|| !apply_time_suffix (&duration, *ep)) Line 330
{
error (0, 0, _("invalid time interval %s"), quote (str)); Line 332
usage (EXIT_CANCELED); Line 333
}
return duration; Line 336
} Block 9
static void Line 339
unblock_signal (int sig) Line 340
{
sigset_t unblock_set; Line 342
sigemptyset (&unblock_set); Line 343
sigaddset (&unblock_set, sig); Line 344
if (sigprocmask (SIG_UNBLOCK, &unblock_set, NULL) != 0) Line 345
error (0, errno, _("warning: sigprocmask")); Line 346
} Block 10
static void Line 349
install_sigchld (void) Line 350
{
struct sigaction sa; Line 352
sigemptyset (&sa.sa_mask); /* Allow concurrent calls to handler */ Line 353
sa.sa_handler = chld; Line 354
sa.sa_flags = SA_RESTART; /* Restart syscalls if possible, as that's Line 355
more likely to work cleanly. */
sigaction (SIGCHLD, &sa, NULL); Line 358
/* We inherit the signal mask from our parent process,
so ensure SIGCHLD is not blocked. */
unblock_signal (SIGCHLD); Line 362
} Block 11
static void Line 365
install_cleanup (int sigterm) Line 366
{
struct sigaction sa; Line 368
sigemptyset (&sa.sa_mask); /* Allow concurrent calls to handler */ Line 369
sa.sa_handler = cleanup; Line 370
sa.sa_flags = SA_RESTART; /* Restart syscalls if possible, as that's Line 371
more likely to work cleanly. */
sigaction (SIGALRM, &sa, NULL); /* our timeout. */ Line 374
sigaction (SIGINT, &sa, NULL); /* Ctrl-C at terminal for example. */ Line 375
sigaction (SIGQUIT, &sa, NULL); /* Ctrl-\ at terminal for example. */ Line 376
sigaction (SIGHUP, &sa, NULL); /* terminal closed for example. */ Line 377
sigaction (SIGTERM, &sa, NULL); /* if we're killed, stop monitored proc. */ Line 378
sigaction (sigterm, &sa, NULL); /* user specified termination signal. */ Line 379
} Block 12
/* Block all signals which were registered with cleanup() as the signal
handler, so we never kill processes after waitpid() returns.
Also block SIGCHLD to ensure it doesn't fire between
waitpid() polling and sigsuspend() waiting for a signal.
Return original mask in OLD_SET. */
static void Line 387
block_cleanup_and_chld (int sigterm, sigset_t *old_set) Line 388
{
sigset_t block_set; Line 390
sigemptyset (&block_set); Line 391
sigaddset (&block_set, SIGALRM); Line 393
sigaddset (&block_set, SIGINT); Line 394
sigaddset (&block_set, SIGQUIT); Line 395
sigaddset (&block_set, SIGHUP); Line 396
sigaddset (&block_set, SIGTERM); Line 397
sigaddset (&block_set, sigterm); Line 398
sigaddset (&block_set, SIGCHLD); Line 400
if (sigprocmask (SIG_BLOCK, &block_set, old_set) != 0) Line 402
error (0, errno, _("warning: sigprocmask")); Line 403
} Block 13
/* Try to disable core dumps for this process.
Return TRUE if successful, FALSE otherwise. */
static bool Line 408
disable_core_dumps (void) Line 409
{
#if HAVE_PRCTL && defined PR_SET_DUMPABLE Line 411
if (prctl (PR_SET_DUMPABLE, 0) == 0) Line 412
return true; Line 413
#elif HAVE_SETRLIMIT && defined RLIMIT_CORE Line 415
/* Note this doesn't disable processing by a filter in
/proc/sys/kernel/core_pattern on Linux. */
if (setrlimit (RLIMIT_CORE, &(struct rlimit) {0,0}) == 0) Line 418
return true; Line 419
#else Line 421
return false; Line 422
#endif Line 423
error (0, errno, _("warning: disabling core dumps failed")); Line 425
return false; Line 426
} Block 14
int
main (int argc, char **argv) Line 430
{
double timeout; Line 432
char signame[SIG2STR_MAX]; Line 433
int c; Line 434
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
initialize_exit_failure (EXIT_CANCELED); Line 442
atexit (close_stdout); Close stdout on exit (see gnulib)
while ((c = getopt_long (argc, argv, "+k:s:v", long_options, NULL)) != -1) Line 445
{
switch (c) Line 447
{
case 'k': Line 449
kill_after = parse_duration (optarg); Line 450
break; Line 451
case 's': Line 453
term_signal = operand2sig (optarg, signame); Line 454
if (term_signal == -1) Line 455
usage (EXIT_CANCELED); Line 456
break; Line 457
case 'v': Line 459
verbose = true; Line 460
break; Line 461
case FOREGROUND_OPTION: Line 463
foreground = true; Line 464
break; Line 465
case PRESERVE_STATUS_OPTION: Line 467
preserve_status = true; Line 468
break; Line 469
case_GETOPT_HELP_CHAR; Line 471
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 473
default: Line 475
usage (EXIT_CANCELED); Line 476
break; Line 477
}
}
if (argc - optind < 2) Line 481
usage (EXIT_CANCELED); Line 482
timeout = parse_duration (argv[optind++]); Line 484
argv += optind; Line 486
command = argv[0]; Line 487
/* Ensure we're in our own group so all subprocesses can be killed.
Note we don't just put the child in a separate group as
then we would need to worry about foreground and background groups
and propagating signals between them. */
if (!foreground) Line 493
setpgid (0, 0); Line 494
/* Setup handlers before fork() so that we
handle any signals caused by child, without races. */
install_cleanup (term_signal); Line 498
signal (SIGTTIN, SIG_IGN); /* Don't stop if background child needs tty. */ Line 499
signal (SIGTTOU, SIG_IGN); /* Don't stop if background child needs tty. */ Line 500
install_sigchld (); /* Interrupt sigsuspend() when child exits. */ Line 501
monitored_pid = fork (); Line 503...!syscalls auto-comment...
if (monitored_pid == -1) Line 504
{
error (0, errno, _("fork system call failed")); Line 506
return EXIT_CANCELED; Line 507
}
else if (monitored_pid == 0) Line 509
{ /* child */ Line 510
/* exec doesn't reset SIG_IGN -> SIG_DFL. */
signal (SIGTTIN, SIG_DFL); Line 512
signal (SIGTTOU, SIG_DFL); Line 513
execvp (argv[0], argv); /* FIXME: should we use "sh -c" ... here? */ Line 515
/* exit like sh, env, nohup, ... */
int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE; Line 518
error (0, errno, _("failed to run command %s"), quote (command)); Line 519
return exit_status; Line 520
}
else Line 522
{
pid_t wait_result; Line 524
int status; Line 525
/* We configure timers so that SIGALRM is sent on expiry.
Therefore ensure we don't inherit a mask blocking SIGALRM. */
unblock_signal (SIGALRM); Line 529
settimeout (timeout, true); Line 531
/* Ensure we don't cleanup() after waitpid() reaps the child,
to avoid sending signals to a possibly different process. */
sigset_t cleanup_set; Line 535
block_cleanup_and_chld (term_signal, &cleanup_set); Line 536
while ((wait_result = waitpid (monitored_pid, &status, WNOHANG)) == 0) Line 538
sigsuspend (&cleanup_set); /* Wait with cleanup signals unblocked. */ Line 539
if (wait_result < 0) Line 541
{
/* shouldn't happen. */
error (0, errno, _("error waiting for command")); Line 544
status = EXIT_CANCELED; Line 545
}
else Line 547
{
if (WIFEXITED (status)) Line 549
status = WEXITSTATUS (status); Line 550
else if (WIFSIGNALED (status)) Line 551
{
int sig = WTERMSIG (status); Line 553
if (WCOREDUMP (status)) Line 554
error (0, 0, _("the monitored command dumped core")); Line 555
if (!timed_out && disable_core_dumps ()) Line 556
{
/* exit with the signal flag set. */
signal (sig, SIG_DFL); Line 559
unblock_signal (sig); Line 560
raise (sig); Line 561
}
status = sig + 128; /* what sh returns for signaled processes. */Line 563
}
else Line 565
{
/* shouldn't happen. */
error (0, 0, _("unknown status from command (%d)"), status); Line 568
status = EXIT_FAILURE; Line 569
}
}
if (timed_out && !preserve_status) Line 573
status = EXIT_TIMEDOUT; Line 574
return status; Line 575
}
} Block 15