1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2019 Robert Mustacchi 14 */ 15 16 #include <locale.h> 17 #include <stdlib.h> 18 #include <signal.h> 19 #include <unistd.h> 20 #include <stdio.h> 21 #include <err.h> 22 #include <errno.h> 23 #include <math.h> 24 #include <limits.h> 25 #include <time.h> 26 #include <libintl.h> 27 28 /* 29 * This implements the sleep(1) command. It allows for a number of extensions 30 * that match both the GNU implementation and parts of what ksh93 used to 31 * provide. Mainly: 32 * 33 * o Fractional seconds 34 * o Suffixes that change the amount of time 35 */ 36 37 typedef struct { 38 char sm_char; 39 uint64_t sm_adj; 40 } sleep_map_t; 41 42 static const sleep_map_t sleep_map[] = { 43 { 's', 1 }, 44 { 'm', 60 }, 45 { 'h', 60 * 60 }, 46 { 'd', 60 * 60 * 24 }, 47 { 'w', 60 * 60 * 24 * 7 }, 48 { 'y', 60 * 60 * 24 * 365 }, 49 { '\0', 0 } 50 }; 51 52 static void 53 sleep_sigalrm(int sig) 54 { 55 /* 56 * Note, the normal exit(2) function is not Async-Signal-Safe. 57 */ 58 _exit(0); 59 } 60 61 int 62 main(int argc, char *argv[]) 63 { 64 int c; 65 long double d, sec, frac; 66 char *eptr; 67 68 (void) setlocale(LC_ALL, ""); 69 #if !defined(TEXT_DOMAIN) 70 #define TEXT_DOMAIN "SYS_TEST" 71 #endif 72 (void) textdomain(TEXT_DOMAIN); 73 74 (void) signal(SIGALRM, sleep_sigalrm); 75 76 while ((c = getopt(argc, argv, ":")) != -1) { 77 switch (c) { 78 case '?': 79 warnx(gettext("illegal option -- %c"), optopt); 80 (void) fprintf(stderr, 81 gettext("Usage: sleep time[suffix]\n")); 82 exit(EXIT_FAILURE); 83 } 84 } 85 86 argc -= optind; 87 argv += optind; 88 89 if (argc != 1) { 90 warnx(gettext("only one operand is supported")); 91 (void) fprintf(stderr, gettext("Usage: sleep time[suffix]\n")); 92 exit(EXIT_FAILURE); 93 } 94 95 errno = 0; 96 d = strtold(argv[0], &eptr); 97 if (errno != 0 || (eptr[0] != '\0' && eptr[1] != '\0') || 98 eptr == argv[0] || d == NAN) { 99 errx(EXIT_FAILURE, gettext("failed to parse time '%s'"), 100 argv[0]); 101 } 102 103 if (d < 0.0) { 104 errx(EXIT_FAILURE, 105 gettext("time interval '%s', cannot be negative"), argv[0]); 106 } 107 108 if (eptr[0] != '\0') { 109 int i; 110 for (i = 0; sleep_map[i].sm_char != '\0'; i++) { 111 if (sleep_map[i].sm_char == eptr[0]) { 112 d *= sleep_map[i].sm_adj; 113 break; 114 } 115 } 116 117 if (sleep_map[i].sm_char == '\0') { 118 errx(EXIT_FAILURE, gettext("failed to parse time %s"), 119 argv[0]); 120 } 121 } 122 123 /* 124 * If we have no time, then we're done. Short circuit. 125 */ 126 if (d == 0) { 127 exit(EXIT_SUCCESS); 128 } 129 130 /* 131 * Split this apart into the fractional and seconds parts to make it 132 * easier to work with. 133 */ 134 frac = modfl(d, &sec); 135 136 /* 137 * We may have a rather large double value. Chop it up in units of 138 * INT_MAX. 139 */ 140 while (sec > 0 || frac != 0) { 141 struct timespec ts; 142 143 if (frac != 0) { 144 frac *= NANOSEC; 145 ts.tv_nsec = (long)frac; 146 frac = 0; 147 } else { 148 ts.tv_nsec = 0; 149 } 150 151 /* 152 * We have a floating point number of fractional seconds. We 153 * need to convert that to nanoseconds. 154 */ 155 if (sec > (float)INT_MAX) { 156 ts.tv_sec = INT_MAX; 157 } else { 158 ts.tv_sec = (time_t)sec; 159 } 160 sec -= ts.tv_sec; 161 162 if (nanosleep(&ts, NULL) != 0) { 163 err(EXIT_FAILURE, gettext("nanosleep failed")); 164 } 165 } 166 167 return (0); 168 } 169