1 /* $OpenBSD: nanosleep.c,v 1.7 2018/05/22 18:33:41 cheloha 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 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 default: 54 fprintf(stderr, "Usage: nanosleep [-itseSE]\n"); 55 exit(1); 56 } 57 } 58 59 return (ret); 60 } 61 62 void 63 sighandler(int signum) 64 { 65 } 66 67 int 68 trivial(void) 69 { 70 struct timespec ts, rts; 71 72 ts.tv_sec = 0; 73 ts.tv_nsec = 30000000; 74 rts.tv_sec = 4711; /* Just add to the confusion */ 75 rts.tv_nsec = 4711; 76 if (nanosleep(&ts, &rts) < 0) { 77 warn("trivial: nanosleep"); 78 return 1; 79 } 80 81 /* 82 * Just check that we don't get any leftover time if we sleep the 83 * amount of time we want to sleep. 84 * If we receive any signal, something is wrong anyway. 85 */ 86 if (rts.tv_sec != 0 || rts.tv_nsec != 0) { 87 warnx("trivial: non-zero time? %lld/%ld", (long long)rts.tv_sec, 88 rts.tv_nsec); 89 return 1; 90 } 91 92 return 0; 93 } 94 95 int 96 with_signal(void) 97 { 98 struct timespec ts, rts; 99 pid_t pid; 100 int status; 101 102 signal(SIGUSR1, sighandler); 103 104 pid = getpid(); 105 106 switch(fork()) { 107 case -1: 108 err(1, "fork"); 109 default: 110 ts.tv_sec = 1; 111 ts.tv_nsec = 0; 112 nanosleep(&ts, NULL); 113 kill(pid, SIGUSR1); 114 exit(0); 115 } 116 117 ts.tv_sec = 10; 118 ts.tv_nsec = 0; 119 rts.tv_sec = 0; 120 rts.tv_nsec = 0; 121 if (nanosleep(&ts, &rts) == 0) { 122 warnx("with-signal: nanosleep"); 123 return 1; 124 } 125 if (rts.tv_sec == 0 && rts.tv_nsec == 0) { 126 warnx("with-signal: zero time"); 127 return 1; 128 } 129 130 if (wait(&status) < 0) 131 err(1, "wait"); 132 133 return 0; 134 } 135 136 int 137 time_elapsed(void) 138 { 139 struct timespec ts; 140 struct timeval stv, etv; 141 142 ts.tv_sec = 0; 143 ts.tv_nsec = 500000000; 144 145 if (gettimeofday(&stv, NULL) < 0) { 146 warn("gettimeofday"); 147 return 1; 148 } 149 150 if (nanosleep(&ts, NULL) < 0) { 151 warn("nanosleep"); 152 return 1; 153 } 154 155 if (gettimeofday(&etv, NULL) < 0) { 156 warn("gettimeofday"); 157 return 1; 158 } 159 160 timersub(&etv, &stv, &stv); 161 162 if (stv.tv_sec == 0 && stv.tv_usec < 500000) { 163 warnx("slept less than 0.5 sec"); 164 return 1; 165 } 166 167 return 0; 168 } 169 170 int 171 time_elapsed_with_signal(void) 172 { 173 struct timespec ts, rts; 174 struct timeval stv, etv; 175 pid_t pid; 176 int status; 177 178 signal(SIGUSR1, sighandler); 179 180 pid = getpid(); 181 182 switch(fork()) { 183 case -1: 184 err(1, "fork"); 185 default: 186 ts.tv_sec = 1; 187 ts.tv_nsec = 0; 188 nanosleep(&ts, NULL); 189 kill(pid, SIGUSR1); 190 exit(0); 191 } 192 193 ts.tv_sec = 10; 194 ts.tv_nsec = 0; 195 rts.tv_sec = 0; 196 rts.tv_nsec = 0; 197 198 if (gettimeofday(&stv, NULL) < 0) { 199 warn("gettimeofday"); 200 return 1; 201 } 202 203 if (nanosleep(&ts, &rts) == 0) { 204 warnx("nanosleep"); 205 return 1; 206 } 207 208 if (gettimeofday(&etv, NULL) < 0) { 209 warn("gettimeofday"); 210 return 1; 211 } 212 213 timersub(&etv, &stv, &stv); 214 215 etv.tv_sec = rts.tv_sec; 216 etv.tv_usec = rts.tv_nsec / 1000 + 1; /* the '+ 1' is a "roundup" */ 217 218 timeradd(&etv, &stv, &stv); 219 220 if (stv.tv_sec < 10) { 221 warnx("slept time + leftover time < 10 sec"); 222 return 1; 223 } 224 225 226 if (wait(&status) < 0) 227 err(1, "wait"); 228 229 return 0; 230 } 231 232 int 233 short_time(void) 234 { 235 struct timespec ts, rts; 236 pid_t pid; 237 int status; 238 239 signal(SIGUSR1, sighandler); 240 241 pid = getpid(); 242 243 switch(fork()) { 244 case -1: 245 err(1, "fork"); 246 default: 247 /* Sleep two seconds, then shoot parent. */ 248 ts.tv_sec = 2; 249 ts.tv_nsec = 0; 250 nanosleep(&ts, NULL); 251 kill(pid, SIGUSR1); 252 exit(0); 253 } 254 255 ts.tv_sec = 0; 256 ts.tv_nsec = 1; 257 if (nanosleep(&ts, NULL) <= 0) { 258 warn("short_time: nanosleep"); 259 return 1; 260 } 261 262 if (wait(&status) < 0) 263 err(1, "wait"); 264 265 return 0; 266 } 267 268 int 269 invalid_time(void) 270 { 271 struct timespec ts[3] = { {-1, 0}, {0, -1}, {0, 1000000000L} }; 272 int i, status; 273 274 for (i = 0; i < 3; i++) { 275 status = nanosleep(&ts[i], NULL); 276 if (status != -1 || errno != EINVAL) { 277 warnx("invalid-time: nanosleep %lld %ld", 278 (long long)ts[i].tv_sec, ts[i].tv_nsec); 279 return 1; 280 } 281 } 282 return 0; 283 } 284