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:
parent
caccf6d3c0
commit
2295cae7e6
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user