Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/bin/sleep Deal with overflow when the sleep duration given i...



details:   https://anonhg.NetBSD.org/src/rev/4ba049dabba5
branches:  trunk
changeset: 449550:4ba049dabba5
user:      kre <kre%NetBSD.org@localhost>
date:      Sun Mar 10 15:18:45 2019 +0000

description:
Deal with overflow when the sleep duration given is a simple
integer (previously it was just clamped at the max possible value).
This would have caused
        sleep 10000000000000000000
(or anything bigger) to have only actually slept for 9223372036854775807
secs.   Someone would have noticed that happen, one day, in some other
universe.

This is now an error, as it was previously if this had been entered as
        sleep 1e19

Also detect an attempt to sleep for so long that a time_t will no longer
be able to represent the current time when the sleep is done.

Undo the attempts to work around a broken kernel nanosleep()
implementation (by only ever issuing shortish sleep requests,
and looping).   That code was broken (idiot botch of mine) though
you would have had to wait a month to observe it happen.  I was going
to just fix it, but sanity prevailed, and the kernel got fixed instead.

That allows this to be much simplified, only looping as needed to
handle dealing with SIGINFO.   Switch to using clock_nanosleep()
to implement the delay, as while our nanosleep() uses CLOCK_MONOTONIC
the standards say it should use CLOCK_REALTIME, and if that we
ever changed that, the old way would alter "sleep 5" from
"sleep for 5 seconds" to "sleep until now + 5 secs", which is
subtly different.

Always use %g format to print the original sleep duration in reports of how
much time remains - this works best for both long and short durations.
A couple of other minor (frill) mods to the SIGINFO report message as well.

diffstat:

 bin/sleep/sleep.c |  86 ++++++++++++++++++++++++++++++++----------------------
 1 files changed, 51 insertions(+), 35 deletions(-)

diffs (158 lines):

diff -r 04551fee55b8 -r 4ba049dabba5 bin/sleep/sleep.c
--- a/bin/sleep/sleep.c Sun Mar 10 14:45:53 2019 +0000
+++ b/bin/sleep/sleep.c Sun Mar 10 15:18:45 2019 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: sleep.c,v 1.29 2019/01/27 02:00:45 christos Exp $ */
+/* $NetBSD: sleep.c,v 1.30 2019/03/10 15:18:45 kre Exp $ */
 
 /*
  * Copyright (c) 1988, 1993, 1994
@@ -39,17 +39,19 @@
 #if 0
 static char sccsid[] = "@(#)sleep.c    8.3 (Berkeley) 4/2/94";
 #else
-__RCSID("$NetBSD: sleep.c,v 1.29 2019/01/27 02:00:45 christos Exp $");
+__RCSID("$NetBSD: sleep.c,v 1.30 2019/03/10 15:18:45 kre Exp $");
 #endif
 #endif /* not lint */
 
 #include <ctype.h>
 #include <err.h>
+#include <errno.h>
 #include <locale.h>
 #include <math.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -73,9 +75,10 @@
        const char *msg;
        double fval, ival, val;
        struct timespec ntime;
+       struct timespec endtime;
+       struct timespec now;
        time_t original;
        int ch, fracflag;
-       unsigned delay;
 
        setprogname(argv[0]);
        (void)setlocale(LC_ALL, "");
@@ -145,9 +148,15 @@
                if (ntime.tv_sec == 0 && ntime.tv_nsec == 0)
                        return EXIT_SUCCESS;    /* was 0.0 or underflowed */
        } else {
+               errno = 0;
                ntime.tv_sec = strtol(arg, &temp, 10);
                if (ntime.tv_sec < 0 || temp == arg || *temp != '\0')
                        usage();
+               if (errno == ERANGE)
+                       errx(EXIT_FAILURE, "Requested delay (%s) out of range",
+                           arg);
+               else if (errno != 0)
+                       err(EXIT_FAILURE, "Requested delay (%s)", arg);
 
                if (ntime.tv_sec == 0)
                        return EXIT_SUCCESS;
@@ -155,40 +164,45 @@
        }
 
        original = ntime.tv_sec;
-       if (ntime.tv_nsec != 0)
-               msg = " and a bit";
-       else
+       if (original < 86400) {
+               if (ntime.tv_nsec > 1000000000 * 2 / 3) {
+                       original++;
+                       msg = " less a bit";
+               } else if (ntime.tv_nsec != 0)
+                       msg = " and a bit";
+               else
+                       msg = "";
+       } else
                msg = "";
 
+       if (clock_gettime(CLOCK_MONOTONIC, &now) != 0)
+               err(EXIT_FAILURE, "clock_gettime");
+       timespecadd(&now, &ntime, &endtime);
+
+       if (endtime.tv_sec < now.tv_sec || (endtime.tv_sec == now.tv_sec &&
+           endtime.tv_nsec <= now.tv_nsec))
+               errx(EXIT_FAILURE, "cannot sleep beyond the end of time");
+
        signal(SIGINFO, report_request);
+       for (;;) {
+               int e;
 
-       if (ntime.tv_sec <= 10000) {                    /* arbitrary */
-               while (nanosleep(&ntime, &ntime) != 0) {
-                       if (report_requested) {
-                               report(ntime.tv_sec, original, msg);
-                               report_requested = 0;
-                       } else
-                               err(EXIT_FAILURE, "nanosleep failed");
-               }
-       } else while (ntime.tv_sec > 0) {
-               delay = (unsigned int)ntime.tv_sec;
+               if ((e = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
+                    &endtime, NULL)) == 0)
+                       return EXIT_SUCCESS;
 
-               if ((time_t)delay != ntime.tv_sec || delay > 30 * 86400)
-                       delay = 30 * 86400;
-
-               ntime.tv_sec -= delay;
-               delay = sleep(delay);
-               ntime.tv_sec += delay;
+               if (!report_requested || e != EINTR) {
+                       errno = e;
+                       err(EXIT_FAILURE, "clock_nanotime");
+               }
 
-               if (delay != 0 && report_requested) {
-                       report(ntime.tv_sec, original, "");
-                       report_requested = 0;
-               } else
-                       break;
+               report_requested = 0;
+               if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) /* Huh? */
+                       continue;
+
+               timespecsub(&endtime, &now, &ntime);
+               report(ntime.tv_sec, original, msg);
        }
-
-       return EXIT_SUCCESS;
-       /* NOTREACHED */
 }
 
        /* Reporting does not bother with nanoseconds. */
@@ -197,7 +211,7 @@
 {
        if (remain == 0)
                warnx("In the final moments of the original"
-                   " %jd%s second%s", (intmax_t)original, msg,
+                   " %g%s second%s", (double)original, msg,
                    original == 1 && *msg == '\0' ? "" : "s");
        else if (remain < 2000)
                warnx("Between %jd and %jd seconds left"
@@ -205,11 +219,13 @@
                    (intmax_t)remain, (intmax_t)remain + 1, (double)original,
                    msg);
        else if ((original - remain) < 100000 && (original-remain) < original/8)
-               warnx("Have waited only %jd seconds of the original %g",
-                       (intmax_t)(original - remain), (double)original);
+               warnx("Have waited only %jd second%s of the original %g%s",
+                       (intmax_t)(original - remain),
+                       (original - remain) == 1 ? "" : "s",
+                       (double)original, msg);
        else
-               warnx("Approximately %g seconds left out of the original %g",
-                       (double)remain, (double)original);
+               warnx("Approximately %g seconds left out of the original %g%s",
+                       (double)remain, (double)original, msg);
 }
 
 static void



Home | Main Index | Thread Index | Old Index