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