xref: /illumos-gate/usr/src/cmd/sleep/sleep.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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