2008-03-28 11:05:55 +00:00
|
|
|
/* timeout -- run a command with bounded time
|
2013-01-01 03:54:51 +01:00
|
|
|
Copyright (C) 2008-2013 Free Software Foundation, Inc.
|
2008-03-28 11:05:55 +00:00
|
|
|
|
|
|
|
|
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 <http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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:
|
2008-06-09 16:10:47 +01:00
|
|
|
EXIT_TIMEDOUT 124 job timed out
|
|
|
|
|
EXIT_CANCELED 125 internal error
|
2008-03-28 11:05:55 +00:00
|
|
|
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,
|
2008-06-09 16:10:47 +01:00
|
|
|
the monitor is killed and so exits with 128+9 rather than 124.
|
2008-03-28 11:05:55 +00:00
|
|
|
|
|
|
|
|
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.
|
maint: quote 'like this' or "like this", not `like this'
* doc/coreutils.texi (Formatting the file names):
coreutils now quotes 'like this'.
* man/help2man:
* src/timeout.c (usage): Quote 'like this' in diagnostics.
* HACKING, Makefile.am, NEWS, README, README-hacking, TODO, cfg.mk:
* doc/Makefile.am, doc/coreutils.texi, m4/jm-macros.m4:
* man/Makefile.am, man/help2man, src/Makefile.am, src/copy.h:
* src/extract-magic, src/ls.c, src/pinky.c, src/pr.c, src/sort.c:
* src/split.c, src/timeout.c, src/who.c, tests/dd/skip-seek-past-file:
* tests/pr/pr-tests: Quote 'like this' in commentary.
* cfg.mk (old_NEWS_hash): Update due to changed old NEWS.
2012-01-22 15:26:00 -08:00
|
|
|
This can be seen with 'timeout 10 dd&' for example.
|
|
|
|
|
However if one brings this group to the foreground with the 'fg'
|
2008-03-28 11:05:55 +00:00
|
|
|
command before the timer expires, the command will remain
|
2011-07-16 06:03:47 -07:00
|
|
|
in the stop state as the shell doesn't send a SIGCONT
|
2008-03-28 11:05:55 +00:00
|
|
|
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>
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <signal.h>
|
2012-09-12 03:21:11 +01:00
|
|
|
#if HAVE_PRCTL
|
|
|
|
|
# include <sys/prctl.h>
|
|
|
|
|
#endif
|
2010-04-24 17:38:13 +02:00
|
|
|
#include <sys/wait.h>
|
2008-03-28 11:05:55 +00:00
|
|
|
|
|
|
|
|
#include "system.h"
|
2011-07-16 12:07:46 -07:00
|
|
|
#include "c-strtod.h"
|
|
|
|
|
#include "xstrtod.h"
|
2008-03-28 11:05:55 +00:00
|
|
|
#include "sig2str.h"
|
|
|
|
|
#include "operand2sig.h"
|
|
|
|
|
#include "error.h"
|
|
|
|
|
#include "quote.h"
|
|
|
|
|
|
2011-07-08 14:49:05 +01:00
|
|
|
#if HAVE_SETRLIMIT
|
|
|
|
|
/* 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>
|
|
|
|
|
#endif
|
|
|
|
|
|
2011-07-15 17:48:38 -07:00
|
|
|
/* NonStop circa 2011 lacks both SA_RESTART and siginterrupt. */
|
|
|
|
|
#ifndef SA_RESTART
|
|
|
|
|
# define SA_RESTART 0
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-03-28 11:05:55 +00:00
|
|
|
#define PROGRAM_NAME "timeout"
|
|
|
|
|
|
|
|
|
|
#define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
|
|
|
|
|
|
|
|
|
|
static int timed_out;
|
|
|
|
|
static int term_signal = SIGTERM; /* same default as kill command. */
|
|
|
|
|
static int monitored_pid;
|
2011-07-18 10:49:17 +01:00
|
|
|
static double kill_after;
|
2012-10-29 00:36:08 +00:00
|
|
|
static bool foreground; /* whether to use another program group. */
|
|
|
|
|
static bool preserve_status; /* whether to use a timeout status or not. */
|
2011-07-06 23:17:10 +01:00
|
|
|
|
|
|
|
|
/* for long options with no corresponding short option, use enum */
|
|
|
|
|
enum
|
|
|
|
|
{
|
2012-10-29 00:36:08 +00:00
|
|
|
FOREGROUND_OPTION = CHAR_MAX + 1,
|
|
|
|
|
PRESERVE_STATUS_OPTION
|
2011-07-06 23:17:10 +01:00
|
|
|
};
|
2008-06-02 07:53:26 -06:00
|
|
|
|
remove redundant const directives
In 1463824d8e7f72c31f1d803d7cfe2b608ccafc5c, I added some
missing "const" directives, as well as some new, redundant ones.
This removes the redundant ones. Pointed out by Eric Blake.
* base64.c, cat.c, chcon.c, chgrp.c, chmod.c, chown.c, comm.c:
* cp.c, csplit.c, cut.c, date.c, dd.c, df.c, dircolors.c, du.c:
* env.c, expand.c, fmt.c, fold.c, groups.c, head.c, id.c:
* install.c, join.c, kill.c, ln.c, ls.c, md5sum.c, mkdir.c:
* mkfifo.c, mknod.c, mktemp.c, mv.c, nice.c, nl.c, od.c:
* paste.c, pathchk.c, pinky.c, pr.c, ptx.c, readlink.c, rm.c:
* rmdir.c, runcon.c, seq.c, shred.c, shuf.c, sort.c, split.c:
* stat.c, stty.c, su.c, sum.c, tac.c, tail.c, tee.c, timeout.c:
* touch.c, tr.c, truncate.c, tty.c, uname.c, unexpand.c, uniq.c:
* wc.c, who.c: Remove redundant const directives.
* maint.mk (sc_const_long_option): Don't require redundant "const".
2008-06-16 14:55:06 +02:00
|
|
|
static struct option const long_options[] =
|
add "const" attribute, where possible
* maint.mk (sc_const_long_option): New rule. Enforce global change.
* src/base64.c (long_options): Use "const" where possible.
* src/cat.c (main): Likewise.
* src/chcon.c (long_options): Likewise.
* src/chgrp.c (long_options): Likewise.
* src/chmod.c (long_options): Likewise.
* src/chown.c (long_options): Likewise.
* src/comm.c (long_options, OUTPUT_DELIMITER_OPTION): Likewise.
* src/cp.c (long_opts): Likewise.
* src/csplit.c (longopts): Likewise.
* src/cut.c (longopts): Likewise.
* src/date.c (long_options): Likewise.
* src/dd.c (conversions, flags, statuses): Likewise.
* src/df.c (long_options): Likewise.
* src/dircolors.c (long_options): Likewise.
* src/du.c (long_options): Likewise.
* src/env.c (longopts): Likewise.
* src/expand.c (longopts): Likewise.
* src/fmt.c (long_options): Likewise.
* src/fold.c (longopts): Likewise.
* src/groups.c (longopts): Likewise.
* src/head.c (long_options): Likewise.
* src/id.c (longopts): Likewise.
* src/install.c (long_options): Likewise.
* src/join.c (longopts): Likewise.
* src/kill.c (long_options): Likewise.
* src/ln.c (long_options): Likewise.
* src/ls.c (long_time_format, long_options, sort_functions): Likewise.
* src/md5sum.c (long_options): Likewise.
* src/mkdir.c (longopts): Likewise.
* src/mkfifo.c (longopts): Likewise.
* src/mknod.c (longopts): Likewise.
* src/mktemp.c (longopts): Likewise.
* src/mv.c (long_options): Likewise.
* src/nice.c (longopts): Likewise.
* src/nl.c (longopts): Likewise.
* src/od.c (charname, long_options): Likewise.
* src/paste.c (longopts): Likewise.
* src/pathchk.c (longopts): Likewise.
* src/pinky.c (longopts): Likewise.
* src/pr.c (long_options): Likewise.
* src/ptx.c (long_options): Likewise.
* src/readlink.c (longopts): Likewise.
* src/rm.c (long_opts): Likewise.
* src/rmdir.c (longopts): Likewise.
* src/runcon.c (long_options): Likewise.
* src/seq.c (long_options): Likewise.
* src/shred.c (long_opts): Likewise.
* src/shuf.c (long_opts): Likewise.
* src/sort.c (monthtab, long_options): Likewise.
* src/split.c (longopts): Likewise.
* src/stat.c (long_options): Likewise.
* src/stty.c (mode_info, control_info, longopts, set_mode) Likewise.
(set_control_char, speeds): Likewise.
* src/su.c (longopts): Likewise.
* src/sum.c (longopts): Likewise.
* src/tac.c (longopts): Likewise.
* src/tail.c (long_options): Likewise.
* src/tee.c (long_options): Likewise.
* src/timeout.c (long_options): Likewise.
* src/touch.c (longopts): Likewise.
* src/tr.c (long_options): Likewise.
* src/truncate.c (longopts): Likewise.
* src/tty.c (longopts): Likewise.
* src/uname.c (uname_long_options, arch_long_options): Likewise.
* src/unexpand.c (longopts): Likewise.
* src/uniq.c (longopts): Likewise.
* src/wc.c (longopts): Likewise.
* src/who.c (longopts): Likewise.
2008-06-12 22:06:15 +02:00
|
|
|
{
|
2010-03-15 23:03:30 +00:00
|
|
|
{"kill-after", required_argument, NULL, 'k'},
|
2008-03-28 11:05:55 +00:00
|
|
|
{"signal", required_argument, NULL, 's'},
|
2011-07-06 23:17:10 +01:00
|
|
|
{"foreground", no_argument, NULL, FOREGROUND_OPTION},
|
2012-10-29 00:36:08 +00:00
|
|
|
{"preserve-status", no_argument, NULL, PRESERVE_STATUS_OPTION},
|
2010-03-25 17:43:10 +00:00
|
|
|
{GETOPT_HELP_OPTION_DECL},
|
|
|
|
|
{GETOPT_VERSION_OPTION_DECL},
|
2008-03-28 11:05:55 +00:00
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
|
};
|
|
|
|
|
|
2013-01-25 02:48:46 +00:00
|
|
|
static void
|
|
|
|
|
unblock_signal (int sig)
|
|
|
|
|
{
|
|
|
|
|
sigset_t unblock_set;
|
|
|
|
|
sigemptyset (&unblock_set);
|
|
|
|
|
sigaddset (&unblock_set, sig);
|
|
|
|
|
if (sigprocmask (SIG_UNBLOCK, &unblock_set, NULL) != 0)
|
|
|
|
|
error (0, errno, _("warning: sigprocmask"));
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-18 10:49:17 +01:00
|
|
|
/* 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
|
2013-11-26 14:27:25 +00:00
|
|
|
settimeout (double duration, bool warn)
|
2011-07-18 10:49:17 +01:00
|
|
|
{
|
2013-01-25 02:48:46 +00:00
|
|
|
|
|
|
|
|
/* We configure timers below so that SIGALRM is sent on expiry.
|
|
|
|
|
Therefore ensure we don't inherit a mask blocking SIGALRM. */
|
|
|
|
|
unblock_signal (SIGALRM);
|
|
|
|
|
|
2011-07-18 10:49:17 +01:00
|
|
|
/* 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
|
|
|
|
|
struct timespec ts = dtotimespec (duration);
|
|
|
|
|
struct itimerspec its = { {0, 0}, ts };
|
|
|
|
|
timer_t timerid;
|
2011-09-01 15:50:08 +01:00
|
|
|
if (timer_create (CLOCK_REALTIME, NULL, &timerid) == 0)
|
2011-07-18 10:49:17 +01:00
|
|
|
{
|
|
|
|
|
if (timer_settime (timerid, 0, &its, NULL) == 0)
|
|
|
|
|
return;
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-11-26 14:27:25 +00:00
|
|
|
if (warn)
|
|
|
|
|
error (0, errno, _("warning: timer_settime"));
|
2011-07-18 10:49:17 +01:00
|
|
|
timer_delete (timerid);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-11-26 14:27:25 +00:00
|
|
|
else if (warn && errno != ENOSYS)
|
2011-07-18 10:49:17 +01:00
|
|
|
error (0, errno, _("warning: timer_create"));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
unsigned int timeint;
|
|
|
|
|
if (UINT_MAX <= duration)
|
|
|
|
|
timeint = UINT_MAX;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
unsigned int duration_floor = duration;
|
|
|
|
|
timeint = duration_floor + (duration_floor < duration);
|
|
|
|
|
}
|
|
|
|
|
alarm (timeint);
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-21 15:22:52 +01:00
|
|
|
/* send SIG avoiding the current process. */
|
|
|
|
|
|
2008-03-28 11:05:55 +00:00
|
|
|
static int
|
|
|
|
|
send_sig (int where, int sig)
|
|
|
|
|
{
|
2011-09-21 15:22:52 +01:00
|
|
|
/* 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_signal_handlers(), 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)
|
|
|
|
|
signal (sig, SIG_IGN);
|
2008-03-28 11:05:55 +00:00
|
|
|
return kill (where, sig);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
cleanup (int sig)
|
|
|
|
|
{
|
|
|
|
|
if (sig == SIGALRM)
|
|
|
|
|
{
|
|
|
|
|
timed_out = 1;
|
|
|
|
|
sig = term_signal;
|
|
|
|
|
}
|
|
|
|
|
if (monitored_pid)
|
|
|
|
|
{
|
2010-03-15 23:03:30 +00:00
|
|
|
if (kill_after)
|
|
|
|
|
{
|
2013-11-26 14:27:25 +00:00
|
|
|
int saved_errno = errno; /* settimeout may reset. */
|
2010-03-15 23:03:30 +00:00
|
|
|
/* Start a new timeout after which we'll send SIGKILL. */
|
|
|
|
|
term_signal = SIGKILL;
|
2013-11-26 14:27:25 +00:00
|
|
|
settimeout (kill_after, false);
|
2010-03-15 23:03:30 +00:00
|
|
|
kill_after = 0; /* Don't let later signals reset kill alarm. */
|
2013-11-26 14:27:25 +00:00
|
|
|
errno = saved_errno;
|
2010-03-15 23:03:30 +00:00
|
|
|
}
|
2011-07-06 23:17:10 +01:00
|
|
|
|
2011-07-08 13:31:05 +01:00
|
|
|
/* Send the signal directly to the monitored child,
|
|
|
|
|
in case it has itself become group leader,
|
|
|
|
|
or is not running in a separate group. */
|
|
|
|
|
send_sig (monitored_pid, sig);
|
|
|
|
|
/* The normal case is the job has remained in our
|
|
|
|
|
newly created process group, so send to all processes in that. */
|
|
|
|
|
if (!foreground)
|
|
|
|
|
send_sig (0, sig);
|
2008-03-28 11:05:55 +00:00
|
|
|
if (sig != SIGKILL && sig != SIGCONT)
|
2011-07-08 13:31:05 +01:00
|
|
|
{
|
|
|
|
|
send_sig (monitored_pid, SIGCONT);
|
|
|
|
|
if (!foreground)
|
|
|
|
|
send_sig (0, SIGCONT);
|
|
|
|
|
}
|
2008-03-28 11:05:55 +00:00
|
|
|
}
|
|
|
|
|
else /* we're the child or the child is not exec'd yet. */
|
|
|
|
|
_exit (128 + sig);
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-12 11:11:54 +01:00
|
|
|
void
|
2008-03-28 11:05:55 +00:00
|
|
|
usage (int status)
|
|
|
|
|
{
|
|
|
|
|
if (status != EXIT_SUCCESS)
|
2012-01-07 16:54:26 +01:00
|
|
|
emit_try_help ();
|
2008-03-28 11:05:55 +00:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
printf (_("\
|
2010-03-15 23:03:30 +00:00
|
|
|
Usage: %s [OPTION] DURATION COMMAND [ARG]...\n\
|
2008-03-28 11:05:55 +00:00
|
|
|
or: %s [OPTION]\n"), program_name, program_name);
|
|
|
|
|
|
|
|
|
|
fputs (_("\
|
2010-03-15 23:03:30 +00:00
|
|
|
Start COMMAND, and kill it if still running after DURATION.\n\
|
2008-03-28 11:05:55 +00:00
|
|
|
"), stdout);
|
maint: define usage note about mandatory args centrally
Each program with at least one long option which is marked as
'required_argument' and which has also a short option for that
option, should print a note about mandatory arguments.
Define that well-known note centrally and use it rather than
literal printf/fputs, and add it where it was missing.
* src/system.h (emit_mandatory_arg_note): Add new function.
* src/cp.c (usage): Use it rather than literal printf/fputs.
* src/csplit.c, src/cut.c, src/date.c, src/df.c, src/du.c:
* src/expand.c, src/fmt.c, src/fold.c, src/head.c, src/install.c:
* src/kill.c, src/ln.c, src/ls.c, src/mkdir.c, src/mkfifo.c:
* src/mknod.c, src/mv.c, src/nl.c, src/od.c, src/paste.c:
* src/pr.c, src/ptx.c, src/shred.c, src/shuf.c, src/sort.c:
* src/split.c, src/stdbuf.c, src/tac.c, src/tail.c, src/timeout.c:
* src/touch.c, src/truncate.c, src/unexpand.c, src/uniq.c:
Likewise.
* src/base64.c (usage): Add call of the above new function
because at least one long option has a required argument.
* src/basename.c, src/chcon.c, src/date.c, src/env.c:
* src/nice.c, src/runcon.c, src/seq.c, src/stat.c, src/stty.c:
Likewise.
2013-01-23 01:03:38 +01:00
|
|
|
|
|
|
|
|
emit_mandatory_arg_note ();
|
|
|
|
|
|
2008-03-28 11:05:55 +00:00
|
|
|
fputs (_("\
|
2012-10-29 00:36:08 +00:00
|
|
|
--preserve-status\n\
|
|
|
|
|
exit with the same status as COMMAND, even when the\n\
|
doc: use semicolon instead of period in option descriptions
Also do not end option descriptions with a period, properly indent
continuation lines, and make some tiny clarifications.
* src/du.c (usage): Lowercase after semicolon.
* src/ls.c (usage): Semicolons instead of periods, small rephrasing
and two hyphens for clarity, proper indentation.
* src/mktemp.c (usage): Semicolons and lowercase.
* src/od.c (usage): Semicolons.
* src/ptx.c (usage): Use the standard phrase, clarify default option.
* src/setuidgid.c (usage): Properly indent continuation line.
* src/split.c (usage): Semicolons, lowercase, no final period.
* src/stat.c (usage): Semicolons, lowercase.
* src/tail.c (usage): Proper indentation, one shorter rephrasing,
semicolons, no final periods.
* src/timeout.c (usage): Properly indent, semicolons, no final periods.
Fixes http://bugs.gnu.org/14976
2013-07-28 13:54:20 +02:00
|
|
|
command times out\n\
|
2011-07-06 23:17:10 +01:00
|
|
|
--foreground\n\
|
doc: use semicolon instead of period in option descriptions
Also do not end option descriptions with a period, properly indent
continuation lines, and make some tiny clarifications.
* src/du.c (usage): Lowercase after semicolon.
* src/ls.c (usage): Semicolons instead of periods, small rephrasing
and two hyphens for clarity, proper indentation.
* src/mktemp.c (usage): Semicolons and lowercase.
* src/od.c (usage): Semicolons.
* src/ptx.c (usage): Use the standard phrase, clarify default option.
* src/setuidgid.c (usage): Properly indent continuation line.
* src/split.c (usage): Semicolons, lowercase, no final period.
* src/stat.c (usage): Semicolons, lowercase.
* src/tail.c (usage): Proper indentation, one shorter rephrasing,
semicolons, no final periods.
* src/timeout.c (usage): Properly indent, semicolons, no final periods.
Fixes http://bugs.gnu.org/14976
2013-07-28 13:54:20 +02:00
|
|
|
when not running timeout directly from a shell prompt,\n\
|
|
|
|
|
allow COMMAND to read from the TTY and get TTY signals;\n\
|
|
|
|
|
in this mode, children of COMMAND will not be timed out\n\
|
2010-03-15 23:03:30 +00:00
|
|
|
-k, --kill-after=DURATION\n\
|
2011-07-06 23:17:10 +01:00
|
|
|
also send a KILL signal if COMMAND is still running\n\
|
doc: use semicolon instead of period in option descriptions
Also do not end option descriptions with a period, properly indent
continuation lines, and make some tiny clarifications.
* src/du.c (usage): Lowercase after semicolon.
* src/ls.c (usage): Semicolons instead of periods, small rephrasing
and two hyphens for clarity, proper indentation.
* src/mktemp.c (usage): Semicolons and lowercase.
* src/od.c (usage): Semicolons.
* src/ptx.c (usage): Use the standard phrase, clarify default option.
* src/setuidgid.c (usage): Properly indent continuation line.
* src/split.c (usage): Semicolons, lowercase, no final period.
* src/stat.c (usage): Semicolons, lowercase.
* src/tail.c (usage): Proper indentation, one shorter rephrasing,
semicolons, no final periods.
* src/timeout.c (usage): Properly indent, semicolons, no final periods.
Fixes http://bugs.gnu.org/14976
2013-07-28 13:54:20 +02:00
|
|
|
this long after the initial signal was sent\n\
|
2008-03-28 11:05:55 +00:00
|
|
|
-s, --signal=SIGNAL\n\
|
doc: use semicolon instead of period in option descriptions
Also do not end option descriptions with a period, properly indent
continuation lines, and make some tiny clarifications.
* src/du.c (usage): Lowercase after semicolon.
* src/ls.c (usage): Semicolons instead of periods, small rephrasing
and two hyphens for clarity, proper indentation.
* src/mktemp.c (usage): Semicolons and lowercase.
* src/od.c (usage): Semicolons.
* src/ptx.c (usage): Use the standard phrase, clarify default option.
* src/setuidgid.c (usage): Properly indent continuation line.
* src/split.c (usage): Semicolons, lowercase, no final period.
* src/stat.c (usage): Semicolons, lowercase.
* src/tail.c (usage): Proper indentation, one shorter rephrasing,
semicolons, no final periods.
* src/timeout.c (usage): Properly indent, semicolons, no final periods.
Fixes http://bugs.gnu.org/14976
2013-07-28 13:54:20 +02:00
|
|
|
specify the signal to be sent on timeout;\n\
|
|
|
|
|
SIGNAL may be a name like 'HUP' or a number;\n\
|
|
|
|
|
see 'kill -l' for a list of signals\n"), stdout);
|
2008-03-28 11:05:55 +00:00
|
|
|
|
|
|
|
|
fputs (HELP_OPTION_DESCRIPTION, stdout);
|
|
|
|
|
fputs (VERSION_OPTION_DESCRIPTION, stdout);
|
2010-03-15 23:03:30 +00:00
|
|
|
|
|
|
|
|
fputs (_("\n\
|
2011-07-18 10:49:17 +01:00
|
|
|
DURATION is a floating point number with an optional suffix:\n\
|
2012-01-08 14:08:03 +01:00
|
|
|
's' for seconds (the default), 'm' for minutes, 'h' for hours \
|
|
|
|
|
or 'd' for days.\n"), stdout);
|
2010-03-15 23:03:30 +00:00
|
|
|
|
2008-03-28 11:05:55 +00:00
|
|
|
fputs (_("\n\
|
2012-10-29 00:36:08 +00:00
|
|
|
If the command times out, and --preserve-status is not set, then exit with\n\
|
|
|
|
|
status 124. Otherwise, exit with the status of COMMAND. If no signal\n\
|
|
|
|
|
is specified, send the TERM signal upon timeout. The TERM signal kills\n\
|
|
|
|
|
any process that does not block or catch that signal. It may be necessary\n\
|
|
|
|
|
to use the KILL (9) signal, since this signal cannot be caught, in which\n\
|
|
|
|
|
case the exit status is 128+9 rather than 124.\n"), stdout);
|
2009-09-18 23:06:21 +01:00
|
|
|
emit_ancillary_info ();
|
2008-03-28 11:05:55 +00:00
|
|
|
}
|
|
|
|
|
exit (status);
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-16 12:07:46 -07:00
|
|
|
/* Given a floating point value *X, and a suffix character, SUFFIX_CHAR,
|
2008-03-28 11:05:55 +00:00
|
|
|
scale *X by the multiplier implied by SUFFIX_CHAR. SUFFIX_CHAR may
|
2012-01-08 15:08:30 +01:00
|
|
|
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
|
2011-07-16 12:07:46 -07:00
|
|
|
and return false. Otherwise return true. */
|
2008-03-28 11:05:55 +00:00
|
|
|
|
|
|
|
|
static bool
|
2011-07-16 12:07:46 -07:00
|
|
|
apply_time_suffix (double *x, char suffix_char)
|
2008-03-28 11:05:55 +00:00
|
|
|
{
|
2011-07-16 12:07:46 -07:00
|
|
|
int multiplier;
|
2008-03-28 11:05:55 +00:00
|
|
|
|
|
|
|
|
switch (suffix_char)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
case 's':
|
2011-07-16 12:07:46 -07:00
|
|
|
multiplier = 1;
|
|
|
|
|
break;
|
2008-03-28 11:05:55 +00:00
|
|
|
case 'm':
|
2011-07-16 12:07:46 -07:00
|
|
|
multiplier = 60;
|
|
|
|
|
break;
|
|
|
|
|
case 'h':
|
|
|
|
|
multiplier = 60 * 60;
|
|
|
|
|
break;
|
|
|
|
|
case 'd':
|
|
|
|
|
multiplier = 60 * 60 * 24;
|
2008-03-28 11:05:55 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*x *= multiplier;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-18 10:49:17 +01:00
|
|
|
static double
|
2010-03-15 23:03:30 +00:00
|
|
|
parse_duration (const char* str)
|
|
|
|
|
{
|
2011-07-16 12:07:46 -07:00
|
|
|
double duration;
|
|
|
|
|
const char *ep;
|
2010-03-15 23:03:30 +00:00
|
|
|
|
2011-07-16 12:07:46 -07:00
|
|
|
if (!xstrtod (str, &ep, &duration, c_strtod)
|
|
|
|
|
/* Nonnegative interval. */
|
|
|
|
|
|| ! (0 <= duration)
|
|
|
|
|
/* No extra chars after the number and an optional s,m,h,d char. */
|
2010-03-15 23:03:30 +00:00
|
|
|
|| (*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);
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-18 10:49:17 +01:00
|
|
|
return duration;
|
2010-03-15 23:03:30 +00:00
|
|
|
}
|
|
|
|
|
|
2008-03-28 11:05:55 +00:00
|
|
|
static void
|
2009-10-23 08:52:25 +01:00
|
|
|
install_signal_handlers (int sigterm)
|
2008-03-28 11:05:55 +00:00
|
|
|
{
|
|
|
|
|
struct sigaction sa;
|
2010-03-15 23:03:30 +00:00
|
|
|
sigemptyset (&sa.sa_mask); /* Allow concurrent calls to handler */
|
2008-03-28 11:05:55 +00:00
|
|
|
sa.sa_handler = cleanup;
|
2011-07-15 17:48:38 -07:00
|
|
|
sa.sa_flags = SA_RESTART; /* Restart syscalls if possible, as that's
|
|
|
|
|
more likely to work cleanly. */
|
2008-03-28 11:05:55 +00:00
|
|
|
|
|
|
|
|
sigaction (SIGALRM, &sa, NULL); /* our timeout. */
|
|
|
|
|
sigaction (SIGINT, &sa, NULL); /* Ctrl-C at terminal for example. */
|
|
|
|
|
sigaction (SIGQUIT, &sa, NULL); /* Ctrl-\ at terminal for example. */
|
|
|
|
|
sigaction (SIGHUP, &sa, NULL); /* terminal closed for example. */
|
2009-10-23 08:52:25 +01:00
|
|
|
sigaction (SIGTERM, &sa, NULL); /* if we're killed, stop monitored proc. */
|
|
|
|
|
sigaction (sigterm, &sa, NULL); /* user specified termination signal. */
|
2008-03-28 11:05:55 +00:00
|
|
|
}
|
|
|
|
|
|
2012-09-12 03:21:11 +01:00
|
|
|
/* Try to disable core dumps for this process.
|
|
|
|
|
Return TRUE if successful, FALSE otherwise. */
|
|
|
|
|
static bool
|
|
|
|
|
disable_core_dumps (void)
|
|
|
|
|
{
|
|
|
|
|
#if HAVE_PRCTL && defined PR_SET_DUMPABLE
|
|
|
|
|
if (prctl (PR_SET_DUMPABLE, 0) == 0)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
#elif HAVE_SETRLIMIT && defined RLIMIT_CORE
|
|
|
|
|
/* 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)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
return false;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
error (0, errno, _("warning: disabling core dumps failed"));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-28 11:05:55 +00:00
|
|
|
int
|
|
|
|
|
main (int argc, char **argv)
|
|
|
|
|
{
|
2011-07-18 10:49:17 +01:00
|
|
|
double timeout;
|
2008-03-28 11:05:55 +00:00
|
|
|
char signame[SIG2STR_MAX];
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
initialize_main (&argc, &argv);
|
2008-06-03 08:34:09 +02:00
|
|
|
set_program_name (argv[0]);
|
2008-03-28 11:05:55 +00:00
|
|
|
setlocale (LC_ALL, "");
|
|
|
|
|
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
|
|
|
textdomain (PACKAGE);
|
|
|
|
|
|
2008-06-09 16:10:47 +01:00
|
|
|
initialize_exit_failure (EXIT_CANCELED);
|
2008-03-28 11:05:55 +00:00
|
|
|
atexit (close_stdout);
|
|
|
|
|
|
2010-03-15 23:03:30 +00:00
|
|
|
while ((c = getopt_long (argc, argv, "+k:s:", long_options, NULL)) != -1)
|
2008-03-28 11:05:55 +00:00
|
|
|
{
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
2010-03-15 23:03:30 +00:00
|
|
|
case 'k':
|
|
|
|
|
kill_after = parse_duration (optarg);
|
|
|
|
|
break;
|
2010-03-25 17:43:10 +00:00
|
|
|
|
2008-03-28 11:05:55 +00:00
|
|
|
case 's':
|
|
|
|
|
term_signal = operand2sig (optarg, signame);
|
|
|
|
|
if (term_signal == -1)
|
2008-06-09 16:10:47 +01:00
|
|
|
usage (EXIT_CANCELED);
|
2008-03-28 11:05:55 +00:00
|
|
|
break;
|
2010-03-25 17:43:10 +00:00
|
|
|
|
2011-07-06 23:17:10 +01:00
|
|
|
case FOREGROUND_OPTION:
|
|
|
|
|
foreground = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2012-10-29 00:36:08 +00:00
|
|
|
case PRESERVE_STATUS_OPTION:
|
|
|
|
|
preserve_status = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2010-03-25 17:43:10 +00:00
|
|
|
case_GETOPT_HELP_CHAR;
|
|
|
|
|
|
|
|
|
|
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
|
|
|
|
2008-03-28 11:05:55 +00:00
|
|
|
default:
|
2008-06-09 16:10:47 +01:00
|
|
|
usage (EXIT_CANCELED);
|
2008-03-28 11:05:55 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (argc - optind < 2)
|
2008-06-09 16:10:47 +01:00
|
|
|
usage (EXIT_CANCELED);
|
2008-03-28 11:05:55 +00:00
|
|
|
|
2010-03-15 23:03:30 +00:00
|
|
|
timeout = parse_duration (argv[optind++]);
|
2008-03-28 11:05:55 +00:00
|
|
|
|
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
|
|
/* Ensure we're in our own group so all subprocesses can be killed.
|
2008-07-30 14:29:52 +02:00
|
|
|
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. */
|
2011-07-06 23:17:10 +01:00
|
|
|
if (!foreground)
|
|
|
|
|
setpgid (0, 0);
|
2008-03-28 11:05:55 +00:00
|
|
|
|
|
|
|
|
/* Setup handlers before fork() so that we
|
2008-07-30 14:29:52 +02:00
|
|
|
handle any signals caused by child, without races. */
|
2009-10-23 08:52:25 +01:00
|
|
|
install_signal_handlers (term_signal);
|
2011-07-16 06:03:47 -07:00
|
|
|
signal (SIGTTIN, SIG_IGN); /* Don't stop if background child needs tty. */
|
|
|
|
|
signal (SIGTTOU, SIG_IGN); /* Don't stop if background child needs tty. */
|
|
|
|
|
signal (SIGCHLD, SIG_DFL); /* Don't inherit CHLD handling from parent. */
|
2008-03-28 11:05:55 +00:00
|
|
|
|
|
|
|
|
monitored_pid = fork ();
|
|
|
|
|
if (monitored_pid == -1)
|
|
|
|
|
{
|
2008-10-03 18:29:39 +02:00
|
|
|
error (0, errno, _("fork system call failed"));
|
|
|
|
|
return EXIT_CANCELED;
|
2008-03-28 11:05:55 +00:00
|
|
|
}
|
|
|
|
|
else if (monitored_pid == 0)
|
|
|
|
|
{ /* child */
|
|
|
|
|
int exit_status;
|
|
|
|
|
|
|
|
|
|
/* exec doesn't reset SIG_IGN -> SIG_DFL. */
|
|
|
|
|
signal (SIGTTIN, SIG_DFL);
|
|
|
|
|
signal (SIGTTOU, SIG_DFL);
|
|
|
|
|
|
|
|
|
|
execvp (argv[0], argv); /* FIXME: should we use "sh -c" ... here? */
|
|
|
|
|
|
|
|
|
|
/* exit like sh, env, nohup, ... */
|
|
|
|
|
exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
|
2009-06-13 16:36:48 +02:00
|
|
|
error (0, errno, _("failed to run command %s"), quote (argv[0]));
|
2008-03-28 11:05:55 +00:00
|
|
|
return exit_status;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2011-07-15 17:48:38 -07:00
|
|
|
pid_t wait_result;
|
2008-03-28 11:05:55 +00:00
|
|
|
int status;
|
|
|
|
|
|
2013-11-26 14:27:25 +00:00
|
|
|
settimeout (timeout, true);
|
2008-03-28 11:05:55 +00:00
|
|
|
|
2011-07-16 05:57:19 -07:00
|
|
|
while ((wait_result = waitpid (monitored_pid, &status, 0)) < 0
|
|
|
|
|
&& errno == EINTR)
|
2011-07-15 17:48:38 -07:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (wait_result < 0)
|
2009-08-31 19:18:27 +01:00
|
|
|
{
|
|
|
|
|
/* shouldn't happen. */
|
|
|
|
|
error (0, errno, _("error waiting for command"));
|
|
|
|
|
status = EXIT_CANCELED;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (WIFEXITED (status))
|
|
|
|
|
status = WEXITSTATUS (status);
|
|
|
|
|
else if (WIFSIGNALED (status))
|
2011-07-08 14:49:05 +01:00
|
|
|
{
|
|
|
|
|
int sig = WTERMSIG (status);
|
2012-09-12 03:21:11 +01:00
|
|
|
if (WCOREDUMP (status))
|
|
|
|
|
error (0, 0, _("the monitored command dumped core"));
|
|
|
|
|
if (!timed_out && disable_core_dumps ())
|
2011-07-08 14:49:05 +01:00
|
|
|
{
|
2012-09-12 03:21:11 +01:00
|
|
|
/* exit with the signal flag set. */
|
|
|
|
|
signal (sig, SIG_DFL);
|
|
|
|
|
raise (sig);
|
2011-07-08 14:49:05 +01:00
|
|
|
}
|
|
|
|
|
status = sig + 128; /* what sh returns for signaled processes. */
|
|
|
|
|
}
|
2009-08-31 19:18:27 +01:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* shouldn't happen. */
|
|
|
|
|
error (0, 0, _("unknown status from command (0x%X)"), status);
|
|
|
|
|
status = EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-03-28 11:05:55 +00:00
|
|
|
|
2012-10-29 00:36:08 +00:00
|
|
|
if (timed_out && !preserve_status)
|
2008-06-09 16:10:47 +01:00
|
|
|
return EXIT_TIMEDOUT;
|
2008-03-28 11:05:55 +00:00
|
|
|
else
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|