/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2019 Robert Mustacchi */ #include #include #include #include #include #include #include #include #include #include #include /* * This implements the sleep(1) command. It allows for a number of extensions * that match both the GNU implementation and parts of what ksh93 used to * provide. Mainly: * * o Fractional seconds * o Suffixes that change the amount of time */ typedef struct { char sm_char; uint64_t sm_adj; } sleep_map_t; static const sleep_map_t sleep_map[] = { { 's', 1 }, { 'm', 60 }, { 'h', 60 * 60 }, { 'd', 60 * 60 * 24 }, { 'w', 60 * 60 * 24 * 7 }, { 'y', 60 * 60 * 24 * 365 }, { '\0', 0 } }; static void sleep_sigalrm(int sig) { /* * Note, the normal exit(2) function is not Async-Signal-Safe. */ _exit(0); } int main(int argc, char *argv[]) { int c; long double d, sec, frac; char *eptr; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); (void) signal(SIGALRM, sleep_sigalrm); while ((c = getopt(argc, argv, ":")) != -1) { switch (c) { case '?': warnx(gettext("illegal option -- %c"), optopt); (void) fprintf(stderr, gettext("Usage: sleep time[suffix]\n")); exit(EXIT_FAILURE); } } argc -= optind; argv += optind; if (argc != 1) { warnx(gettext("only one operand is supported")); (void) fprintf(stderr, gettext("Usage: sleep time[suffix]\n")); exit(EXIT_FAILURE); } errno = 0; d = strtold(argv[0], &eptr); if (errno != 0 || (eptr[0] != '\0' && eptr[1] != '\0') || eptr == argv[0] || d == NAN) { errx(EXIT_FAILURE, gettext("failed to parse time '%s'"), argv[0]); } if (d < 0.0) { errx(EXIT_FAILURE, gettext("time interval '%s', cannot be negative"), argv[0]); } if (eptr[0] != '\0') { int i; for (i = 0; sleep_map[i].sm_char != '\0'; i++) { if (sleep_map[i].sm_char == eptr[0]) { d *= sleep_map[i].sm_adj; break; } } if (sleep_map[i].sm_char == '\0') { errx(EXIT_FAILURE, gettext("failed to parse time %s"), argv[0]); } } /* * If we have no time, then we're done. Short circuit. */ if (d == 0) { exit(EXIT_SUCCESS); } /* * Split this apart into the fractional and seconds parts to make it * easier to work with. */ frac = modfl(d, &sec); /* * We may have a rather large double value. Chop it up in units of * INT_MAX. */ while (sec > 0 || frac != 0) { struct timespec ts; if (frac != 0) { frac *= NANOSEC; ts.tv_nsec = (long)frac; frac = 0; } else { ts.tv_nsec = 0; } /* * We have a floating point number of fractional seconds. We * need to convert that to nanoseconds. */ if (sec > (float)INT_MAX) { ts.tv_sec = INT_MAX; } else { ts.tv_sec = (time_t)sec; } sec -= ts.tv_sec; if (nanosleep(&ts, NULL) != 0) { err(EXIT_FAILURE, gettext("nanosleep failed")); } } return (0); }