1 /* $OpenBSD: nanosleep.c,v 1.9 2024/02/29 21:47:02 bluhm Exp $ */
2 /*
3 * Written by Artur Grabowski <art@openbsd.org> 2002 Public Domain.
4 */
5 #include <sys/types.h>
6 #include <sys/time.h>
7 #include <sys/wait.h>
8
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <time.h>
14 #include <err.h>
15 #include <signal.h>
16
17 int invalid_time(void);
18 int trivial(void);
19 int with_signal(void);
20 int time_elapsed(void);
21 int time_elapsed_with_signal(void);
22
23 int short_time(void);
24
25 void sighandler(int);
26
27 int
main(int argc,char ** argv)28 main(int argc, char **argv)
29 {
30 int ch, ret;
31
32 ret = 0;
33
34 while ((ch = getopt(argc, argv, "itseES")) != -1) {
35 switch (ch) {
36 case 'i':
37 ret |= invalid_time();
38 break;
39 case 't':
40 ret |= trivial();
41 break;
42 case 's':
43 ret |= with_signal();
44 break;
45 case 'e':
46 ret |= time_elapsed();
47 break;
48 case 'E':
49 ret |= time_elapsed_with_signal();
50 break;
51 case 'S':
52 ret |= short_time();
53 break;
54 default:
55 fprintf(stderr, "Usage: nanosleep [-itseSE]\n");
56 exit(1);
57 }
58 }
59
60 return (ret);
61 }
62
63 void
sighandler(int signum)64 sighandler(int signum)
65 {
66 }
67
68 int
trivial(void)69 trivial(void)
70 {
71 struct timespec timeout, remainder;
72
73 timeout.tv_sec = 0;
74 timeout.tv_nsec = 30000000;
75 remainder.tv_sec = 4711; /* Just add to the confusion */
76 remainder.tv_nsec = 4711;
77 if (nanosleep(&timeout, &remainder) < 0) {
78 warn("%s: nanosleep", __func__);
79 return 1;
80 }
81
82 /*
83 * Just check that we don't get any leftover time if we sleep the
84 * amount of time we want to sleep.
85 * If we receive any signal, something is wrong anyway.
86 */
87 if (remainder.tv_sec != 0 || remainder.tv_nsec != 0) {
88 warnx("%s: non-zero time: %lld.%09ld", __func__,
89 (long long)remainder.tv_sec, remainder.tv_nsec);
90 return 1;
91 }
92
93 return 0;
94 }
95
96 int
with_signal(void)97 with_signal(void)
98 {
99 struct timespec timeout, remainder;
100 pid_t pid;
101 int status;
102
103 signal(SIGUSR1, sighandler);
104
105 pid = getpid();
106
107 switch(fork()) {
108 case -1:
109 err(1, "fork");
110 case 0:
111 timeout.tv_sec = 1;
112 timeout.tv_nsec = 0;
113 nanosleep(&timeout, NULL);
114 kill(pid, SIGUSR1);
115 _exit(0);
116 default:
117 break;
118 }
119
120 timeout.tv_sec = 10;
121 timeout.tv_nsec = 0;
122 remainder.tv_sec = 0;
123 remainder.tv_nsec = 0;
124 if (nanosleep(&timeout, &remainder) == 0) {
125 warnx("%s: nanosleep", __func__);
126 return 1;
127 }
128
129 if (remainder.tv_sec == 0 && remainder.tv_nsec == 0) {
130 warnx("%s: zero time", __func__);
131 return 1;
132 }
133
134 if (wait(&status) < 0)
135 err(1, "wait");
136 if (status != 0)
137 errx(1, "status");
138
139 return 0;
140 }
141
142 int
time_elapsed(void)143 time_elapsed(void)
144 {
145 struct timespec timeout;
146 struct timespec start, end, duration;
147
148 timeout.tv_sec = 0;
149 timeout.tv_nsec = 500000000;
150
151 if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
152 warn("%s: clock_gettime", __func__);
153 return 1;
154 }
155
156 if (nanosleep(&timeout, NULL) < 0) {
157 warn("%s: nanosleep", __func__);
158 return 1;
159 }
160
161 if (clock_gettime(CLOCK_MONOTONIC, &end) < 0) {
162 warn("%s: clock_gettime", __func__);
163 return 1;
164 }
165
166 timespecsub(&end, &start, &duration);
167
168 if (duration.tv_sec == 0 && duration.tv_nsec < 500000000) {
169 warnx("%s: slept less than 0.5 sec: %lld.%09ld", __func__,
170 (long long)duration.tv_sec, duration.tv_nsec);
171 return 1;
172 }
173
174 return 0;
175 }
176
177 int
time_elapsed_with_signal(void)178 time_elapsed_with_signal(void)
179 {
180 struct timespec timeout, remainder;
181 struct timespec start, end, duration;
182 pid_t pid;
183 int status;
184
185 signal(SIGUSR1, sighandler);
186
187 pid = getpid();
188
189 switch(fork()) {
190 case -1:
191 err(1, "fork");
192 case 0:
193 timeout.tv_sec = 1;
194 timeout.tv_nsec = 0;
195 nanosleep(&timeout, NULL);
196 kill(pid, SIGUSR1);
197 _exit(0);
198 default:
199 break;
200 }
201
202 if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
203 warn("%s: clock_gettime", __func__);
204 return 1;
205 }
206
207 timeout.tv_sec = 10;
208 timeout.tv_nsec = 0;
209 remainder.tv_sec = 0;
210 remainder.tv_nsec = 0;
211 if (nanosleep(&timeout, &remainder) == 0) {
212 warnx("%s: nanosleep", __func__);
213 return 1;
214 }
215
216 if (clock_gettime(CLOCK_MONOTONIC, &end) < 0) {
217 warn("%s: clock_gettime", __func__);
218 return 1;
219 }
220
221 timespecsub(&end, &start, &duration);
222 timespecadd(&duration, &remainder, &timeout);
223 /* XXX remainder may be one tick too small */
224 remainder.tv_sec = 0;
225 remainder.tv_nsec = 10000000;
226 timespecadd(&timeout, &remainder, &timeout);
227
228 if (timeout.tv_sec < 10) {
229 warnx("%s: slept time + leftover time < 10 sec: %lld.%09ld",
230 __func__, (long long)timeout.tv_sec, timeout.tv_nsec);
231 return 1;
232 }
233
234 if (wait(&status) < 0)
235 err(1, "wait");
236 if (status != 0)
237 errx(1, "status");
238
239 return 0;
240 }
241
242 int
short_time(void)243 short_time(void)
244 {
245 struct timespec timeout;
246 pid_t pid;
247 int status;
248
249 signal(SIGUSR1, sighandler);
250
251 pid = getpid();
252
253 switch(fork()) {
254 case -1:
255 err(1, "fork");
256 case 0:
257 /* Sleep two seconds, then shoot parent. */
258 timeout.tv_sec = 2;
259 timeout.tv_nsec = 0;
260 nanosleep(&timeout, NULL);
261 kill(pid, SIGUSR1);
262 _exit(0);
263 default:
264 break;
265 }
266
267 timeout.tv_sec = 0;
268 timeout.tv_nsec = 1;
269 if (nanosleep(&timeout, NULL) < 0) {
270 warn("%s: nanosleep", __func__);
271 return 1;
272 }
273
274 if (wait(&status) < 0)
275 err(1, "wait");
276 if (status != 0)
277 errx(1, "status");
278
279 return 0;
280 }
281
282 int
invalid_time(void)283 invalid_time(void)
284 {
285 struct timespec timeout[3] = { {-1, 0}, {0, -1}, {0, 1000000000L} };
286 int i, status;
287
288 for (i = 0; i < 3; i++) {
289 status = nanosleep(&timeout[i], NULL);
290 if (status != -1 || errno != EINVAL) {
291 warnx("%s: nanosleep %lld %ld", __func__,
292 (long long)timeout[i].tv_sec, timeout[i].tv_nsec);
293 return 1;
294 }
295 }
296 return 0;
297 }
298