1 /* $NetBSD: sleep.c,v 1.30 2019/03/10 15:18:45 kre Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)sleep.c 8.3 (Berkeley) 4/2/94"; 41 #else 42 __RCSID("$NetBSD: sleep.c,v 1.30 2019/03/10 15:18:45 kre Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <ctype.h> 47 #include <err.h> 48 #include <errno.h> 49 #include <locale.h> 50 #include <math.h> 51 #include <signal.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <time.h> 56 #include <unistd.h> 57 58 __dead static void alarmhandle(int); 59 __dead static void usage(void); 60 61 static void report(const time_t, const time_t, const char *const); 62 63 static volatile sig_atomic_t report_requested; 64 static void 65 report_request(int signo __unused) 66 { 67 68 report_requested = 1; 69 } 70 71 int 72 main(int argc, char *argv[]) 73 { 74 char *arg, *temp; 75 const char *msg; 76 double fval, ival, val; 77 struct timespec ntime; 78 struct timespec endtime; 79 struct timespec now; 80 time_t original; 81 int ch, fracflag; 82 83 setprogname(argv[0]); 84 (void)setlocale(LC_ALL, ""); 85 86 (void)signal(SIGALRM, alarmhandle); 87 88 while ((ch = getopt(argc, argv, "")) != -1) 89 switch(ch) { 90 default: 91 usage(); 92 } 93 argc -= optind; 94 argv += optind; 95 96 if (argc != 1) 97 usage(); 98 99 /* 100 * Okay, why not just use atof for everything? Why bother 101 * checking if there is a fraction in use? Because the old 102 * sleep handled the full range of integers, that's why, and a 103 * double can't handle a large long. This is fairly useless 104 * given how large a number a double can hold on most 105 * machines, but now we won't ever have trouble. If you want 106 * 1000000000.9 seconds of sleep, well, that's your 107 * problem. Why use an isdigit() check instead of checking for 108 * a period? Because doing it this way means locales will be 109 * handled transparently by the atof code. 110 * 111 * Since fracflag is set for any non-digit, we also fall 112 * into the floating point conversion path if the input 113 * is hex (the 'x' in 0xA is not a digit). Then if 114 * strtod() handles hex (on NetBSD it does) so will we. 115 * That path is also taken for scientific notation (1.2e+3) 116 * and when the input is simply nonsense. 117 */ 118 fracflag = 0; 119 arg = *argv; 120 for (temp = arg; *temp != '\0'; temp++) 121 if (!isdigit((unsigned char)*temp)) { 122 ch = *temp; 123 fracflag++; 124 } 125 126 if (fracflag) { 127 /* 128 * If we cannot convert the value using the user's locale 129 * then try again using the C locale, so strtod() can always 130 * parse values like 2.5, even if the user's locale uses 131 * a different decimal radix character (like ',') 132 * 133 * (but only if that is the potential problem) 134 */ 135 val = strtod(arg, &temp); 136 if (*temp != '\0') 137 val = strtod_l(arg, &temp, LC_C_LOCALE); 138 if (val < 0 || temp == arg || *temp != '\0') 139 usage(); 140 141 ival = floor(val); 142 fval = (1000000000 * (val-ival)); 143 ntime.tv_sec = ival; 144 if ((double)ntime.tv_sec != ival) 145 errx(1, "requested delay (%s) out of range", arg); 146 ntime.tv_nsec = fval; 147 148 if (ntime.tv_sec == 0 && ntime.tv_nsec == 0) 149 return EXIT_SUCCESS; /* was 0.0 or underflowed */ 150 } else { 151 errno = 0; 152 ntime.tv_sec = strtol(arg, &temp, 10); 153 if (ntime.tv_sec < 0 || temp == arg || *temp != '\0') 154 usage(); 155 if (errno == ERANGE) 156 errx(EXIT_FAILURE, "Requested delay (%s) out of range", 157 arg); 158 else if (errno != 0) 159 err(EXIT_FAILURE, "Requested delay (%s)", arg); 160 161 if (ntime.tv_sec == 0) 162 return EXIT_SUCCESS; 163 ntime.tv_nsec = 0; 164 } 165 166 original = ntime.tv_sec; 167 if (original < 86400) { 168 if (ntime.tv_nsec > 1000000000 * 2 / 3) { 169 original++; 170 msg = " less a bit"; 171 } else if (ntime.tv_nsec != 0) 172 msg = " and a bit"; 173 else 174 msg = ""; 175 } else 176 msg = ""; 177 178 if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) 179 err(EXIT_FAILURE, "clock_gettime"); 180 timespecadd(&now, &ntime, &endtime); 181 182 if (endtime.tv_sec < now.tv_sec || (endtime.tv_sec == now.tv_sec && 183 endtime.tv_nsec <= now.tv_nsec)) 184 errx(EXIT_FAILURE, "cannot sleep beyond the end of time"); 185 186 signal(SIGINFO, report_request); 187 for (;;) { 188 int e; 189 190 if ((e = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, 191 &endtime, NULL)) == 0) 192 return EXIT_SUCCESS; 193 194 if (!report_requested || e != EINTR) { 195 errno = e; 196 err(EXIT_FAILURE, "clock_nanotime"); 197 } 198 199 report_requested = 0; 200 if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) /* Huh? */ 201 continue; 202 203 timespecsub(&endtime, &now, &ntime); 204 report(ntime.tv_sec, original, msg); 205 } 206 } 207 208 /* Reporting does not bother with nanoseconds. */ 209 static void 210 report(const time_t remain, const time_t original, const char * const msg) 211 { 212 if (remain == 0) 213 warnx("In the final moments of the original" 214 " %g%s second%s", (double)original, msg, 215 original == 1 && *msg == '\0' ? "" : "s"); 216 else if (remain < 2000) 217 warnx("Between %jd and %jd seconds left" 218 " out of the original %g%s", 219 (intmax_t)remain, (intmax_t)remain + 1, (double)original, 220 msg); 221 else if ((original - remain) < 100000 && (original-remain) < original/8) 222 warnx("Have waited only %jd second%s of the original %g%s", 223 (intmax_t)(original - remain), 224 (original - remain) == 1 ? "" : "s", 225 (double)original, msg); 226 else 227 warnx("Approximately %g seconds left out of the original %g%s", 228 (double)remain, (double)original, msg); 229 } 230 231 static void 232 usage(void) 233 { 234 (void)fprintf(stderr, "usage: %s seconds\n", getprogname()); 235 exit(EXIT_FAILURE); 236 /* NOTREACHED */ 237 } 238 239 /* ARGSUSED */ 240 static void 241 alarmhandle(int i) 242 { 243 _exit(EXIT_SUCCESS); 244 /* NOTREACHED */ 245 } 246