1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-10-18 02:19:39 +00:00

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
This commit is contained in:
Dag-Erling Smørgrav 2024-03-25 16:58:31 +01:00
parent caccf6d3c0
commit 2295cae7e6
2 changed files with 98 additions and 90 deletions

View File

@ -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

View File

@ -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);
}