aboutsummaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2024-03-25 15:58:31 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2024-03-25 15:59:02 +0000
commit2295cae7e606d75016575927c3d8729745abb17a (patch)
tree5932ef3519f200865d9c10a1c2ff38faefbadf50 /bin
parentcaccf6d3c008d3c778986734c2705cdae849a877 (diff)
downloadsrc-2295cae7e606d75016575927c3d8729745abb17a.tar.gz
src-2295cae7e606d75016575927c3d8729745abb17a.zip
sleep: Overhaul.
Program: * Add a dummy getopt(3) loop to handle `--`. * Move interval parsing out into a separate function. * Print a diagnostic for every invalid interval. * Check for NaN and infinity. * Improve bounds checks. Manual page: * Miscellaneous markup fixes. * Reword DESCRIPTION section. * Move text about GNU compatibility to STANDARDS section. * Convert examples from csh to sh. Sponsored by: Klara, Inc. Reviewed by: kevans Differential Revision: https://reviews.freebsd.org/D44471
Diffstat (limited to 'bin')
-rw-r--r--bin/sleep/sleep.177
-rw-r--r--bin/sleep/sleep.c111
2 files changed, 98 insertions, 90 deletions
diff --git a/bin/sleep/sleep.1 b/bin/sleep/sleep.1
index b6af3a648a97..c9069a015706 100644
--- a/bin/sleep/sleep.1
+++ b/bin/sleep/sleep.1
@@ -29,7 +29,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd May 25, 2022
+.Dd March 22, 2024
.Dt SLEEP 1
.Os
.Sh NAME
@@ -38,21 +38,26 @@
.Sh SYNOPSIS
.Nm
.Ar number Ns Op Ar unit
-.Ar ...
+.Op ...
.Sh DESCRIPTION
The
.Nm
command suspends execution for a minimum of
.Ar number
seconds (the default, or unit
-.Cm s ) ,
+.Li s ) ,
minutes (unit
-.Cm m ) ,
+.Li m ) ,
hours (unit
-.Cm h ) ,
+.Li h ) ,
or days (unit
-.Cm d ) .
-If multiple arguments are passed, the delay will be the sum of all values.
+.Li d ) .
+Intervals can be written in any form allowed by
+.Xr strtod 3 .
+If multiple intervals are given, they are added together.
+If the final sum is zero or negative,
+.Nm
+exits immediately.
.Pp
If the
.Nm
@@ -65,57 +70,49 @@ sleep is printed on the standard output.
The
.Dv SIGALRM
signal is not handled specially by this implementation.
-.Pp
-The
-.Nm
-command supports other time units than seconds,
-honors a non-integer number of time units to sleep in any form acceptable by
-.Xr strtod 3 ,
-and accepts more than one delay value.
-These are non-portable extensions, but they have also been implemented
-in GNU sh-utils since version 2.0a (released in 2002).
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
-To schedule the execution of a command for
-.Va x
-number seconds later (with
-.Xr csh 1 ) :
+To run a command after half an hour:
.Pp
-.Dl (sleep 1800; sh command_file >& errors)&
+.Dl (sleep 0.5h; sh command_file >out 2>err)&
.Pp
-This incantation would wait a half hour before
-running the script command_file.
-(See the
+This incantation would wait half an hour before
+running the script
+.Pa command_file .
+See the
.Xr at 1
-utility.)
+utility for another way to do this.
.Pp
-To reiteratively run a command (with the
-.Xr csh 1 ) :
+To reiteratively run a command:
.Pp
.Bd -literal -offset indent -compact
-while (1)
- if (! -r zzz.rawdata) then
- sleep 300
+while :; do
+ if ! [ -r zzz.rawdata ] ; then
+ sleep 5m
else
- foreach i (`ls *.rawdata`)
+ for i in *.rawdata ; do
sleep 70
- awk -f collapse_data $i >> results
- end
+ awk -f collapse_data "$i"
+ done >results
break
- endif
-end
+ fi
+done
.Ed
.Pp
The scenario for a script such as this might be: a program currently
running is taking longer than expected to process a series of
files, and it would be nice to have
another program start processing the files created by the first
-program as soon as it is finished (when zzz.rawdata is created).
-The script checks every five minutes for the file zzz.rawdata,
+program as soon as it is finished (when
+.Pa zzz.rawdata
+is created).
+The script checks every five minutes for the file
+.Pa zzz.rawdata ,
when the file is found, then another portion processing
is done courteously by sleeping for 70 seconds in between each
-awk job.
+.Xr awk 1
+job.
.Sh SEE ALSO
.Xr nanosleep 2 ,
.Xr sleep 3
@@ -125,6 +122,10 @@ The
command is expected to be
.St -p1003.2
compatible.
+.Pp
+Support for non-integer intervals, units other than seconds, and
+multiple intervals which are added together are non-portable
+extensions first introduced in GNU sh-utils 2.0a (released in 2002).
.Sh HISTORY
A
.Nm
diff --git a/bin/sleep/sleep.c b/bin/sleep/sleep.c
index 20e525ce093c..34d335cf4736 100644
--- a/bin/sleep/sleep.c
+++ b/bin/sleep/sleep.c
@@ -31,93 +31,100 @@
#include <err.h>
#include <errno.h>
#include <limits.h>
+#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
-
-static void usage(void) __dead2;
+#include <unistd.h>
static volatile sig_atomic_t report_requested;
+
static void
report_request(int signo __unused)
{
-
report_requested = 1;
}
+static void __dead2
+usage(void)
+{
+ fprintf(stderr, "usage: sleep number[unit] [...]\n"
+ "Unit can be 's' (seconds, the default), "
+ "m (minutes), h (hours), or d (days).\n");
+ exit(1);
+}
+
+static double
+parse_interval(const char *arg)
+{
+ double num;
+ char unit, extra;
+
+ switch (sscanf(arg, "%lf%c%c", &num, &unit, &extra)) {
+ case 2:
+ switch (unit) {
+ case 'd':
+ num *= 24;
+ /* FALLTHROUGH */
+ case 'h':
+ num *= 60;
+ /* FALLTHROUGH */
+ case 'm':
+ num *= 60;
+ /* FALLTHROUGH */
+ case 's':
+ if (!isnan(num))
+ return (num);
+ }
+ break;
+ case 1:
+ if (!isnan(num))
+ return (num);
+ }
+ warnx("invalid time interval: %s", arg);
+ return (INFINITY);
+}
+
int
main(int argc, char *argv[])
{
struct timespec time_to_sleep;
- double d, seconds;
+ double seconds;
time_t original;
- char unit;
- char buf[2];
- int i, matches;
if (caph_limit_stdio() < 0 || caph_enter() < 0)
err(1, "capsicum");
- if (argc < 2)
+ while (getopt(argc, argv, "") != -1)
+ usage();
+ argc -= optind;
+ argv += optind;
+ if (argc < 1)
usage();
seconds = 0;
- for (i = 1; i < argc; i++) {
- matches = sscanf(argv[i], "%lf%c%1s", &d, &unit, buf);
- if (matches == 2)
- switch(unit) {
- case 'd':
- d *= 24;
- /* FALLTHROUGH */
- case 'h':
- d *= 60;
- /* FALLTHROUGH */
- case 'm':
- d *= 60;
- /* FALLTHROUGH */
- case 's':
- break;
- default:
- usage();
- }
- else
- if (matches != 1)
- usage();
- seconds += d;
- }
+ while (argc--)
+ seconds += parse_interval(*argv++);
if (seconds > INT_MAX)
usage();
- if (seconds <= 0)
- return (0);
+ if (seconds < 1e-9)
+ exit(0);
original = time_to_sleep.tv_sec = (time_t)seconds;
time_to_sleep.tv_nsec = 1e9 * (seconds - time_to_sleep.tv_sec);
signal(SIGINFO, report_request);
- /*
- * Note: [EINTR] is supposed to happen only when a signal was handled
- * but the kernel also returns it when a ptrace-based debugger
- * attaches. This is a bug but it is hard to fix.
- */
while (nanosleep(&time_to_sleep, &time_to_sleep) != 0) {
+ if (errno != EINTR)
+ err(1, "nanosleep");
if (report_requested) {
/* Reporting does not bother with nanoseconds. */
- warnx("about %d second(s) left out of the original %d",
- (int)time_to_sleep.tv_sec, (int)original);
+ warnx("about %ld second(s) left out of the original %ld",
+ (long)time_to_sleep.tv_sec, (long)original);
report_requested = 0;
- } else if (errno != EINTR)
- err(1, "nanosleep");
+ }
}
- return (0);
-}
-static void
-usage(void)
-{
-
- fprintf(stderr, "usage: sleep number[unit] ...\n");
- fprintf(stderr, "Unit can be 's' (seconds, the default), "
- "m (minutes), h (hours), or d (days).\n");
- exit(1);
+ exit(0);
}