summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--bootstrap.conf1
-rw-r--r--doc/coreutils.texi4
-rw-r--r--src/Makefile.am1
-rw-r--r--src/timeout.c70
-rwxr-xr-xtests/misc/timeout-parameters3
6 files changed, 61 insertions, 20 deletions
diff --git a/NEWS b/NEWS
index 07207196c..fb444f605 100644
--- a/NEWS
+++ b/NEWS
@@ -59,6 +59,8 @@ GNU coreutils NEWS -*- outline -*-
stat -f now recognizes the GPFS, MQUEUE and PSTOREFS file system types.
+ timeout now supports sub-second timeouts.
+
** Build-related
Changes inherited from gnulib address a build failure on HP-UX 11.11
diff --git a/bootstrap.conf b/bootstrap.conf
index d91bee0c8..937f7f326 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -212,6 +212,7 @@ gnulib_modules="
sys_stat
sys_wait
termios
+ timer-time
timespec
tzset
uname
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 424446c25..b406a3cfb 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -15694,7 +15694,7 @@ or a number. Also see @xref{Signal specifications}.
@end table
@cindex time units
-@var{duration} is an integer followed by an optional unit:
+@var{duration} is a floating point number followed by an optional unit:
@display
@samp{s} for seconds (the default)
@samp{m} for minutes
@@ -15702,6 +15702,8 @@ or a number. Also see @xref{Signal specifications}.
@samp{d} for days
@end display
A duration of 0 disables the associated timeout.
+Note that the actual timeout duration is dependent on system conditions,
+which should be especially considered when specifying sub-second timeouts.
@cindex exit status of @command{timeout}
Exit status:
diff --git a/src/Makefile.am b/src/Makefile.am
index ef0e7a4d3..b0b7eb527 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -329,6 +329,7 @@ date_LDADD += $(LIB_CLOCK_GETTIME)
ginstall_LDADD += $(LIB_CLOCK_GETTIME)
ls_LDADD += $(LIB_CLOCK_GETTIME)
pr_LDADD += $(LIB_CLOCK_GETTIME)
+timeout_LDADD += $(LIB_TIMER_TIME)
touch_LDADD += $(LIB_CLOCK_GETTIME)
# for gethrxtime
diff --git a/src/timeout.c b/src/timeout.c
index ccb4f85d4..6a37508fd 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -78,7 +78,7 @@ 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 double kill_after;
static bool foreground; /* whether to use another program group. */
/* for long options with no corresponding short option, use enum */
@@ -97,6 +97,50 @@ static struct option const long_options[] =
{NULL, 0, NULL, 0}
};
+/* 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
+settimeout (double duration)
+{
+/* 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;
+ if (timer_create (CLOCK_REALTIME, NULL, &timerid) == 0)
+ {
+ if (timer_settime (timerid, 0, &its, NULL) == 0)
+ return;
+ else
+ {
+ error (0, errno, _("warning: timer_settime"));
+ timer_delete (timerid);
+ }
+ }
+ else
+ 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);
+}
+
/* send sig to group but not ourselves.
* FIXME: Is there a better way to achieve this? */
static int
@@ -125,7 +169,7 @@ cleanup (int sig)
{
/* Start a new timeout after which we'll send SIGKILL. */
term_signal = SIGKILL;
- alarm (kill_after);
+ settimeout (kill_after);
kill_after = 0; /* Don't let later signals reset kill alarm. */
}
@@ -182,7 +226,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\n\
-DURATION is an integer with an optional suffix:\n\
+DURATION is a floating point number with an optional suffix:\n\
`s' for seconds (the default), `m' for minutes, `h' for hours \
or `d' for days.\n"), stdout);
@@ -232,7 +276,7 @@ apply_time_suffix (double *x, char suffix_char)
return true;
}
-static unsigned int
+static double
parse_duration (const char* str)
{
double duration;
@@ -250,19 +294,7 @@ parse_duration (const char* str)
usage (EXIT_CANCELED);
}
- /* Return the requested duration, rounded 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.
-
- FIXME: Use dtotimespec + setitimer if setitimer is available,
- as that has higher resolution. */
- if (UINT_MAX <= duration)
- return UINT_MAX;
- else
- {
- unsigned int duration_floor = duration;
- return duration_floor + (duration_floor < duration);
- }
+ return duration;
}
static void
@@ -285,7 +317,7 @@ install_signal_handlers (int sigterm)
int
main (int argc, char **argv)
{
- unsigned long timeout;
+ double timeout;
char signame[SIG2STR_MAX];
int c;
@@ -373,7 +405,7 @@ main (int argc, char **argv)
pid_t wait_result;
int status;
- alarm (timeout);
+ settimeout (timeout);
while ((wait_result = waitpid (monitored_pid, &status, 0)) < 0
&& errno == EINTR)
diff --git a/tests/misc/timeout-parameters b/tests/misc/timeout-parameters
index 56804a808..e34a72807 100755
--- a/tests/misc/timeout-parameters
+++ b/tests/misc/timeout-parameters
@@ -55,6 +55,9 @@ test $? = 0 || fail=1
timeout 2.34e+5d sleep 0
test $? = 0 || fail=1
+# nanoseconds potentially supported
+timeout .999999999 sleep 0 || fail=1
+
# invalid signal spec
timeout --signal=invalid 1 sleep 0
test $? = 125 || fail=1