* AUTHORS: Register as the author.
* NEWS: Mention this change.
* README: Add timeout command to list.
* src/timeout.c: New file.
* src/kill.c (operand2sig): Move function to its own file,
now that timeout.c will also use it.
* src/operand2sig.c (operand2sig): New file, extracted from kill.c.
* src/operand2sig.h (operand2sig): Declare.
* src/Makefile.am (EXTRA_PROGRAMS): Add timeout.
* src/.gitignore: Add timeout binary to list to ignore.
* doc/coreutils.texi (timeout invocation): Add timeout info.
(Signal specifications): New section, also referenced by kill.
* man/Makefile.am (timeout.1): Add dependency.
* man/timeout.x: New file.
* po/POTFILES.in: Add timeout.c and operand2sig.c to list to translate.
* tests/Makefile.am (TESTS): Add the two new tests.
* tests/misc/help-version: Add support for new timeout command.
* tests/misc/invalid-opt: Add support for new timeout command.
* tests/misc/timeout: New file: check basic timeout operation.
* tests/misc/timeout-parameters: New file: check invalid parameter
combinations.
tail: Paul Rubin, David MacKenzie, Ian Lance Taylor, Jim Meyering
tee: Mike Parker, Richard M. Stallman, David MacKenzie
test: Kevin Braunsdorf, Matthew Bradburn
+timeout: Pádraig Brady
touch: Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie, Randy Smith
tr: Jim Meyering
true: Jim Meyering
* Noteworthy changes in release 7.0 (????-??-??) [beta]
+** New programs
+
+ timeout: Run a command with bounded time.
+
** New features
md5sum now accepts the new option, --quiet, to suppress the printing of
link ln logname ls md5sum mkdir mkfifo mknod mktemp mv nice nl nohup
od paste pathchk pinky pr printenv printf ptx pwd readlink rm rmdir
runcon seq sha1sum sha224sum sha256sum sha384sum sha512sum shred shuf
- sleep sort split stat stty su sum sync tac tail tee test touch tr true
- tsort tty uname unexpand uniq unlink uptime users vdir wc who whoami yes
+ sleep sort split stat stty su sum sync tac tail tee test timeout touch tr
+ true tsort tty uname unexpand uniq unlink uptime users vdir wc who whoami yes
See the file NEWS for a list of major changes in the current release.
AC_CONFIG_AUX_DIR(build-aux)
AC_CONFIG_HEADERS([lib/config.h:lib/config.hin])
-AM_INIT_AUTOMAKE([1.10.1 dist-lzma])
+AM_INIT_AUTOMAKE([1.10])
AC_PROG_CC_STDC
AM_PROG_CC_C_O
* tail: (coreutils)tail invocation. Output the last part of files.
* tee: (coreutils)tee invocation. Redirect to multiple files.
* test: (coreutils)test invocation. File/string tests.
+* timeout: (coreutils)timeout invocation. Run with time limit.
* touch: (coreutils)touch invocation. Change file timestamps.
* tr: (coreutils)tr invocation. Translate characters.
* true: (coreutils)true invocation. Do nothing, successfully.
* Working context:: pwd stty printenv tty
* User information:: id logname whoami groups users who
* System context:: date uname hostname hostid
-* Modified command invocation:: chroot env nice nohup su
+* Modified command invocation:: chroot env nice nohup su timeout
* Process control:: kill
* Delaying:: sleep
* Numeric operations:: factor seq
* Exit status:: Indicating program success or failure.
* Backup options:: Backup options
* Block size:: Block size
+* Signal specifications:: Specifying signals
* Disambiguating names and IDs:: chgrp and chown owner and group syntax
* Random sources:: Sources of random data
* Target directory:: Target directory
* nice invocation:: Run a command with modified niceness
* nohup invocation:: Run a command immune to hangups
* su invocation:: Run a command with substitute user and group ID
+* timeout invocation:: Run a command with a time limit
Process control
* Exit status:: Indicating program success or failure.
* Backup options:: -b -S, in some programs.
* Block size:: BLOCK_SIZE and --block-size, in some programs.
+* Signal specifications:: Specifying signals using the --signal option.
* Disambiguating names and IDs:: chgrp and chown owner and group syntax
* Random sources:: --random-source, in some programs.
* Target directory:: Specifying a target directory, in some programs.
meanings with the values @samp{0} and @samp{1}.
Here are some of the exceptions:
@command{chroot}, @command{env}, @command{expr},
-@command{nice}, @command{nohup}, @command{printenv},
-@command{sort}, @command{su}, @command{test}, @command{tty}.
+@command{nice}, @command{nohup}, @command{printenv}, @command{sort},
+@command{su}, @command{test}, @command{timeout}, @command{tty}.
@node Backup options
@option{--block-size=human-readable}. The @option{--si} option is
equivalent to @option{--block-size=si}.
+@node Signal specifications
+@section Signal specifications
+@cindex signals, specifying
+
+A @var{signal} may be a signal name like @samp{HUP}, or a signal
+number like @samp{1}, or an exit status of a process terminated by the
+signal. A signal name can be given in canonical form or prefixed by
+@samp{SIG}. The case of the letters is ignored. The following signal names
+and numbers are supported on all @acronym{POSIX} compliant systems:
+
+@table @samp
+@item HUP
+1. Hangup.
+@item INT
+2. Terminal interrupt.
+@item QUIT
+3. Terminal quit.
+@item ABRT
+6. Process abort.
+@item KILL
+9. Kill (cannot be caught or ignored).
+@item ALRM
+14. Alarm Clock.
+@item TERM
+15. Termination.
+@end table
+
+@noindent
+Other supported signal names have system-dependent corresponding
+numbers. All systems conforming to @acronym{POSIX} 1003.1-2001 also
+support the following signals:
+
+@table @samp
+@item BUS
+Access to an undefined portion of a memory object.
+@item CHLD
+Child process terminated, stopped, or continued.
+@item CONT
+Continue executing, if stopped.
+@item FPE
+Erroneous arithmetic operation.
+@item ILL
+Illegal Instruction.
+@item PIPE
+Write on a pipe with no one to read it.
+@item SEGV
+Invalid memory reference.
+@item STOP
+Stop executing (cannot be caught or ignored).
+@item TSTP
+Terminal stop.
+@item TTIN
+Background process attempting read.
+@item TTOU
+Background process attempting write.
+@item URG
+High bandwidth data is available at a socket.
+@item USR1
+User-defined signal 1.
+@item USR2
+User-defined signal 2.
+@end table
+
+@noindent
+@acronym{POSIX} 1003.1-2001 systems that support the @acronym{XSI} extension
+also support the following signals:
+
+@table @samp
+@item POLL
+Pollable event.
+@item PROF
+Profiling timer expired.
+@item SYS
+Bad system call.
+@item TRAP
+Trace/breakpoint trap.
+@item VTALRM
+Virtual timer expired.
+@item XCPU
+CPU time limit exceeded.
+@item XFSZ
+File size limit exceeded.
+@end table
+
+@noindent
+@acronym{POSIX} 1003.1-2001 systems that support the @acronym{XRT} extension
+also support at least eight real-time signals called @samp{RTMIN},
+@samp{RTMIN+1}, @dots{}, @samp{RTMAX-1}, @samp{RTMAX}.
+
@node Disambiguating names and IDs
@section chown and chgrp: Disambiguating user names and IDs
@cindex user names, disambiguating
* nice invocation:: Modify niceness.
* nohup invocation:: Immunize to hangups.
* su invocation:: Modify user and group ID.
+* timeout invocation:: Run with time limit.
@end menu
might find this idea strange at first.
+@node timeout invocation
+@section @command{timeout}: Run a command with a time limit
+
+@pindex timeout
+@cindex time limit
+@cindex run commands with bounded time
+
+@command{timeout} runs the given @var{command} and kills it if it is
+still running after the specified time interval. Synopsis:
+
+@example
+timeout [@var{option}] @var{number}[smhd] @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}.
+Options must precede operands.
+
+@table @samp
+@item -s @var{signal}
+@itemx --signal=@var{signal}
+@opindex -s
+@opindex --signal
+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 exit status of @command{timeout}
+Exit status:
+
+@display
+110 if @var{command} times out
+125 if @command{timeout} itself fails
+126 if @var{command} is found but cannot be invoked
+127 if @var{command} cannot be found
+the exit status of @var{command} otherwise
+@end display
+
+
@node Process control
@chapter Process control
signal. A signal name can be given in canonical form or prefixed by
@samp{SIG}. The case of the letters is ignored, except for the
@option{-@var{signal}} option which must use upper case to avoid
-ambiguity with lower case option letters. The following signal names
-and numbers are supported on all @acronym{POSIX} compliant systems:
-
-@table @samp
-@item HUP
-1. Hangup.
-@item INT
-2. Terminal interrupt.
-@item QUIT
-3. Terminal quit.
-@item ABRT
-6. Process abort.
-@item KILL
-9. Kill (cannot be caught or ignored).
-@item ALRM
-14. Alarm Clock.
-@item TERM
-15. Termination.
-@end table
-
-@noindent
-Other supported signal names have system-dependent corresponding
-numbers. All systems conforming to @acronym{POSIX} 1003.1-2001 also
-support the following signals:
-
-@table @samp
-@item BUS
-Access to an undefined portion of a memory object.
-@item CHLD
-Child process terminated, stopped, or continued.
-@item CONT
-Continue executing, if stopped.
-@item FPE
-Erroneous arithmetic operation.
-@item ILL
-Illegal Instruction.
-@item PIPE
-Write on a pipe with no one to read it.
-@item SEGV
-Invalid memory reference.
-@item STOP
-Stop executing (cannot be caught or ignored).
-@item TSTP
-Terminal stop.
-@item TTIN
-Background process attempting read.
-@item TTOU
-Background process attempting write.
-@item URG
-High bandwidth data is available at a socket.
-@item USR1
-User-defined signal 1.
-@item USR2
-User-defined signal 2.
-@end table
-
-@noindent
-@acronym{POSIX} 1003.1-2001 systems that support the @acronym{XSI} extension
-also support the following signals:
-
-@table @samp
-@item POLL
-Pollable event.
-@item PROF
-Profiling timer expired.
-@item SYS
-Bad system call.
-@item TRAP
-Trace/breakpoint trap.
-@item VTALRM
-Virtual timer expired.
-@item XCPU
-CPU time limit exceeded.
-@item XFSZ
-File size limit exceeded.
-@end table
-
-@noindent
-@acronym{POSIX} 1003.1-2001 systems that support the @acronym{XRT} extension
-also support at least eight real-time signals called @samp{RTMIN},
-@samp{RTMIN+1}, @dots{}, @samp{RTMAX-1}, @samp{RTMAX}.
-
+ambiguity with lower case option letters. For a list of supported
+signal names and numbers see @xref{Signal specifications}.
@node Delaying
@chapter Delaying
tail.1: $(common_dep) $(srcdir)/tail.x ../src/tail.c
tee.1: $(common_dep) $(srcdir)/tee.x ../src/tee.c
test.1: $(common_dep) $(srcdir)/test.x ../src/test.c
+timeout.1: $(common_dep) $(srcdir)/timeout.x ../src/timeout.c
touch.1: $(common_dep) $(srcdir)/touch.x ../src/touch.c
tr.1: $(common_dep) $(srcdir)/tr.x ../src/tr.c
true.1: $(common_dep) $(srcdir)/true.x ../src/true.c
--- /dev/null
+[NAME]
+timeout \- run a command with a time limit
+[DESCRIPTION]
+.\" Add any additional description here
+[SEE ALSO]
+kill(1)
src/nl.c
src/nohup.c
src/od.c
+src/operand2sig.c
src/paste.c
src/pathchk.c
src/pinky.c
src/tail.c
src/tee.c
src/test.c
+src/timeout.c
src/touch.c
src/tr.c
src/true.c
tail
tee
test
+timeout
touch
tr
true
basename date dirname echo env expr factor false \
id kill logname pathchk printenv printf pwd \
runcon seq sleep tee \
- test true tty whoami yes \
+ test timeout true tty whoami yes \
base64
bin_PROGRAMS = $(OPTIONAL_BIN_PROGS)
fs.h \
group-list.h \
ls.h \
+ operand2sig.h \
prog-fprintf.h \
remove.h \
system.h \
ls_SOURCES = ls.c ls-ls.c
chown_SOURCES = chown.c chown-core.c
chgrp_SOURCES = chgrp.c chown-core.c
+kill_SOURCES = kill.c operand2sig.c
+timeout_SOURCES = timeout.c operand2sig.c
mv_SOURCES = mv.c remove.c $(copy_sources)
rm_SOURCES = rm.c remove.c
#include "system.h"
#include "error.h"
#include "sig2str.h"
+#include "operand2sig.h"
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "kill"
exit (status);
}
\f
-/* Convert OPERAND to a signal number with printable representation SIGNAME.
- Return the signal number, or -1 if unsuccessful. */
-
-static int
-operand2sig (char const *operand, char *signame)
-{
- int signum;
-
- if (ISDIGIT (*operand))
- {
- char *endp;
- long int l = (errno = 0, strtol (operand, &endp, 10));
- int i = l;
- signum = (operand == endp || *endp || errno || i != l ? -1
- : WIFSIGNALED (i) ? WTERMSIG (i)
- : i);
- }
- else
- {
- /* Convert signal to upper case in the C locale, not in the
- current locale. Don't assume ASCII; it might be EBCDIC. */
- char *upcased = xstrdup (operand);
- char *p;
- for (p = upcased; *p; p++)
- if (strchr ("abcdefghijklmnopqrstuvwxyz", *p))
- *p += 'A' - 'a';
-
- /* Look for the signal name, possibly prefixed by "SIG",
- and possibly lowercased. */
- if (! (str2sig (upcased, &signum) == 0
- || (upcased[0] == 'S' && upcased[1] == 'I' && upcased[2] == 'G'
- && str2sig (upcased + 3, &signum) == 0)))
- signum = -1;
-
- free (upcased);
- }
-
- if (signum < 0 || sig2str (signum, signame) != 0)
- {
- error (0, 0, _("%s: invalid signal"), operand);
- return -1;
- }
-
- return signum;
-}
-\f
/* Print a row of `kill -t' output. NUM_WIDTH is the maximum signal
number width, and SIGNUM is the signal number to print. The
maximum name width is NAME_WIDTH, and SIGNAME is the name to print. */
--- /dev/null
+/* operand2sig.c -- common function for parsing signal specifications
+ Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. */
+
+/* Extracted from kill.c/timeout.c by Pádraig Brady.
+ FIXME: Move this to gnulib/str2sig.c */
+
+
+/* Convert OPERAND to a signal number with printable representation SIGNAME.
+ Return the signal number, or -1 if unsuccessful. */
+
+#include <config.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WIFSIGNALED
+# define WIFSIGNALED(s) (((s) & 0xFFFF) - 1 < (unsigned int) 0xFF)
+#endif
+#ifndef WTERMSIG
+# define WTERMSIG(s) ((s) & 0x7F)
+#endif
+
+#include "system.h"
+#include "error.h"
+#include "sig2str.h"
+#include "operand2sig.h"
+
+extern int
+operand2sig (char const *operand, char *signame)
+{
+ int signum;
+
+ if (ISDIGIT (*operand))
+ {
+ char *endp;
+ long int l = (errno = 0, strtol (operand, &endp, 10));
+ int i = l;
+ signum = (operand == endp || *endp || errno || i != l ? -1
+ : WIFSIGNALED (i) ? WTERMSIG (i) : i);
+ }
+ else
+ {
+ /* Convert signal to upper case in the C locale, not in the
+ current locale. Don't assume ASCII; it might be EBCDIC. */
+ char *upcased = xstrdup (operand);
+ char *p;
+ for (p = upcased; *p; p++)
+ if (strchr ("abcdefghijklmnopqrstuvwxyz", *p))
+ *p += 'A' - 'a';
+
+ /* Look for the signal name, possibly prefixed by "SIG",
+ and possibly lowercased. */
+ if (!(str2sig (upcased, &signum) == 0
+ || (upcased[0] == 'S' && upcased[1] == 'I' && upcased[2] == 'G'
+ && str2sig (upcased + 3, &signum) == 0)))
+ signum = -1;
+
+ free (upcased);
+ }
+
+ if (signum < 0 || sig2str (signum, signame) != 0)
+ {
+ error (0, 0, _("%s: invalid signal"), operand);
+ return -1;
+ }
+
+ return signum;
+}
--- /dev/null
+/* operand2sig.h -- prototype for signal specification function
+
+ Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. */
+
+extern int operand2sig (char const *operand, char *signame);
--- /dev/null
+/* timeout -- run a command with bounded time
+ Copyright (C) 2008 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 <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:
+ ETIMEDOUT 110 job timed out
+ ECANCELED 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 ETIMEDOUT.
+
+ 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>
+#include <getopt.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WIFSIGNALED
+# define WIFSIGNALED(s) (((s) & 0xFFFF) - 1 < (unsigned int) 0xFF)
+#endif
+#ifndef WTERMSIG
+# define WTERMSIG(s) ((s) & 0x7F)
+#endif
+
+#include "system.h"
+#include "xstrtol.h"
+#include "sig2str.h"
+#include "operand2sig.h"
+#include "cloexec.h"
+#include "error.h"
+#include "long-options.h"
+#include "quote.h"
+
+#define PROGRAM_NAME "timeout"
+
+#define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
+
+/* Internal failure. */
+#ifndef ECANCELED
+#define ECANCELED 125
+#endif
+
+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 char *program_name;
+
+static struct option const long_options[] = {
+ {"signal", required_argument, NULL, 's'},
+ {NULL, 0, NULL, 0}
+};
+
+/* send sig to group but not ourselves.
+ * FIXME: Is there a better way to achieve this? */
+static int
+send_sig (int where, int sig)
+{
+ sigs_to_ignore[sig] = 1;
+ return kill (where, sig);
+}
+
+static void
+cleanup (int sig)
+{
+ if (sig == SIGALRM)
+ {
+ timed_out = 1;
+ sig = term_signal;
+ }
+ if (monitored_pid)
+ {
+ if (sigs_to_ignore[sig])
+ {
+ sigs_to_ignore[sig] = 0;
+ return;
+ }
+ send_sig (0, sig);
+ if (sig != SIGKILL && sig != SIGCONT)
+ send_sig (0, SIGCONT);
+ }
+ else /* we're the child or the child is not exec'd yet. */
+ _exit (128 + sig);
+}
+
+static void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ fprintf (stderr, _("Try `%s --help' for more information.\n"),
+ program_name);
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] NUMBER[SUFFIX] 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\
+\n\
+"), stdout);
+
+ fputs (_("\
+Mandatory arguments to long options are mandatory for short options too.\n\
+"), stdout);
+ fputs (_("\
+ -s, --signal=SIGNAL\n\
+ 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);
+
+ fputs (HELP_OPTION_DESCRIPTION, stdout);
+ fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ fputs (_("\n\
+If the command times out, then we exit with status ETIMEDOUT,\n\
+otherwise the normal exit status of the command is returned.\n\
+If no signal is specified, the TERM signal is sent. The TERM signal\n\
+will kill processes which do not catch this signal. For other processes,\n\
+it may be necessary to use the KILL (9) signal, since this signal cannot\n\
+be caught.\n"), stdout);
+ emit_bug_reporting_address ();
+ }
+ exit (status);
+}
+
+/* Given an integer 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. If *X would overflow, don't modify *X and return false.
+ Otherwise return true. */
+
+static bool
+apply_time_suffix (unsigned int *x, char suffix_char)
+{
+ int multiplier=1;
+
+ switch (suffix_char)
+ {
+ case 0:
+ case 's':
+ return true;
+ case 'd':
+ multiplier *= 24;
+ case 'h':
+ multiplier *= 60;
+ case 'm':
+ multiplier *= 60;
+ break;
+ default:
+ return false;
+ }
+
+ if (*x > UINT_MAX / multiplier)
+ return false;
+
+ *x *= multiplier;
+
+ return true;
+}
+
+static void
+install_signal_handlers (void)
+{
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask); /* Allow concurrent calls to handler */
+ sa.sa_handler = cleanup;
+ sa.sa_flags = SA_RESTART; /* restart syscalls (like wait() below) */
+
+ 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 (SIGTERM, &sa, NULL); /* if we're killed, stop monitored proc. */
+ sigaction (SIGHUP, &sa, NULL); /* terminal closed for example. */
+}
+
+int
+main (int argc, char **argv)
+{
+ unsigned long timeout;
+ char signame[SIG2STR_MAX];
+ int c;
+ char *ep;
+
+ initialize_main (&argc, &argv);
+ program_name = argv[0];
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ initialize_exit_failure (ECANCELED);
+ atexit (close_stdout);
+
+ 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)
+ {
+ switch (c)
+ {
+ case 's':
+ term_signal = operand2sig (optarg, signame);
+ if (term_signal == -1)
+ usage (ECANCELED);
+ break;
+ default:
+ usage (ECANCELED);
+ break;
+ }
+ }
+
+ if (argc - optind < 2)
+ usage (ECANCELED);
+
+ 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 ((unsigned int *) &timeout, *ep))
+ {
+ error (0, 0, _("invalid time interval %s"), quote (argv[optind]));
+ usage (ECANCELED);
+ }
+ optind++;
+
+ argc -= optind;
+ argv += optind;
+
+ /* Ensure we're in our own group so all subprocesses can be killed.
+ * Note we don't put the just child in a separate group as
+ * then we would need to worry about foreground and background groups
+ * and propagating signals between them. */
+ setpgid (0, 0);
+
+ /* Setup handlers before fork() so that we
+ * handle any signals caused by child, without races. */
+ install_signal_handlers ();
+ signal (SIGTTIN, SIG_IGN); /* don't sTop if background child needs tty. */
+ signal (SIGTTOU, SIG_IGN); /* don't sTop if background child needs tty. */
+
+ monitored_pid = fork ();
+ if (monitored_pid == -1)
+ {
+ perror ("fork");
+ return errno;
+ }
+ 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);
+ perror (argv[0]);
+ return exit_status;
+ }
+ else
+ {
+ int status;
+
+ alarm ((unsigned int) timeout);
+
+ /* We're just waiting for a single process here, so wait() suffices.
+ * Note the signal() calls above on linux and BSD at least, essentially
+ * call the lower level sigaction() with the SA_RESTART flag set, which
+ * ensures the following wait call will only return if the child exits,
+ * not on this process receiving a signal. Also we're not passing
+ * WUNTRACED | WCONTINUED to a waitpid() call and so will not get
+ * indication that the child has stopped or continued. */
+ wait (&status);
+
+ if (WIFEXITED (status))
+ status = WEXITSTATUS (status);
+ else if (WIFSIGNALED (status))
+ status = WTERMSIG (status) + 128; /* what sh does at least. */
+
+ if (timed_out)
+ return ETIMEDOUT;
+ else
+ return status;
+ }
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: nil
+ * End:
+ */
misc/tee \
misc/tee-dash \
misc/test-diag \
+ misc/timeout \
+ misc/timeout-parameters \
misc/tr \
misc/tsort \
misc/tty-eof \
. $srcdir/test-lib.sh
expected_failure_status_nohup=127
+expected_failure_status_timeout=125
expected_failure_status_printenv=2
expected_failure_status_tty=3
expected_failure_status_sort=2
seq_args=10
sleep_args=0
su_args=--version
+timeout_args=--version
# I'd rather not run sync, since it spins up disks that I've
# deliberately caused to spin down (but not unmounted).
nohup => 127,
sort => 2,
test => 0,
+ timeout => 125,
true => 0,
tty => 2,
printf => 0,
--- /dev/null
+#!/bin/sh
+# Validate timeout basic operation
+
+# Copyright (C) 2008 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 <http://www.gnu.org/licenses/>.
+
+if test "$VERBOSE" = yes; then
+ set -x
+ timeout --version
+fi
+
+. $srcdir/test-lib.sh
+
+fail=0
+
+# no timeout
+timeout 1 true || fail=1
+
+# no timeout (suffix check)
+timeout 1d true || fail=1
+
+# disabled timeout
+timeout 0 true || fail=1
+
+# exit status propagation
+timeout 1 false && fail=1
+
+# timeout
+timeout 1 sleep 2
+test $? = 110 || fail=1
+
+(exit $fail); exit $fail
--- /dev/null
+#!/bin/sh
+# Validate timeout parameter combinations
+
+# Copyright (C) 2008 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 <http://www.gnu.org/licenses/>.
+
+if test "$VERBOSE" = yes; then
+ set -x
+ timeout --version
+fi
+
+. $srcdir/test-lib.sh
+
+fail=0
+
+# --help and --version must be specified alone
+timeout --help --version && fail=1
+
+# invalid timeout
+timeout invalid sleep 0 && fail=1
+
+# invalid timeout suffix
+timeout 42D sleep 0 && fail=1
+
+# timeout overflow
+timeout 4294967296 sleep 0 && fail=1
+
+# timeout overflow
+timeout 49711d sleep 0 && fail=1
+
+# invalid signal spec
+timeout --signal=invalid sleep 0 && fail=1
+
+# invalid signal number
+timeout --signal=128 sleep 0 && fail=1
+
+# invalid command
+timeout 1 . && fail=1
+
+# non existant command
+timeout 1 ... && fail=1
+
+(exit $fail); exit $fail