#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <err.h>
#include <signal.h>
int invalid_time(void);
int trivial(void);
int with_signal(void);
int time_elapsed(void);
int time_elapsed_with_signal(void);
int short_time(void);
void sighandler(int);
int
main(int argc, char **argv)
{
int ch, ret;
ret = 0;
while ((ch = getopt(argc, argv, "itseES")) != -1) {
switch (ch) {
case 'i':
ret |= invalid_time();
break;
case 't':
ret |= trivial();
break;
case 's':
ret |= with_signal();
break;
case 'e':
ret |= time_elapsed();
break;
case 'E':
ret |= time_elapsed_with_signal();
break;
case 'S':
ret |= short_time();
break;
default:
fprintf(stderr, "Usage: nanosleep [-itseSE]\n");
exit(1);
}
}
return (ret);
}
void
sighandler(int signum)
{
}
int
trivial(void)
{
struct timespec timeout, remainder;
timeout.tv_sec = 0;
timeout.tv_nsec = 30000000;
remainder.tv_sec = 4711;
remainder.tv_nsec = 4711;
if (nanosleep(&timeout, &remainder) < 0) {
warn("%s: nanosleep", __func__);
return 1;
}
if (remainder.tv_sec != 0 || remainder.tv_nsec != 0) {
warnx("%s: non-zero time: %lld.%09ld", __func__,
(long long)remainder.tv_sec, remainder.tv_nsec);
return 1;
}
return 0;
}
int
with_signal(void)
{
struct timespec timeout, remainder;
pid_t pid;
int status;
signal(SIGUSR1, sighandler);
pid = getpid();
switch(fork()) {
case -1:
err(1, "fork");
case 0:
timeout.tv_sec = 1;
timeout.tv_nsec = 0;
nanosleep(&timeout, NULL);
kill(pid, SIGUSR1);
_exit(0);
default:
break;
}
timeout.tv_sec = 10;
timeout.tv_nsec = 0;
remainder.tv_sec = 0;
remainder.tv_nsec = 0;
if (nanosleep(&timeout, &remainder) == 0) {
warnx("%s: nanosleep", __func__);
return 1;
}
if (remainder.tv_sec == 0 && remainder.tv_nsec == 0) {
warnx("%s: zero time", __func__);
return 1;
}
if (wait(&status) < 0)
err(1, "wait");
if (status != 0)
errx(1, "status");
return 0;
}
int
time_elapsed(void)
{
struct timespec timeout;
struct timespec start, end, duration;
timeout.tv_sec = 0;
timeout.tv_nsec = 500000000;
if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
warn("%s: clock_gettime", __func__);
return 1;
}
if (nanosleep(&timeout, NULL) < 0) {
warn("%s: nanosleep", __func__);
return 1;
}
if (clock_gettime(CLOCK_MONOTONIC, &end) < 0) {
warn("%s: clock_gettime", __func__);
return 1;
}
timespecsub(&end, &start, &duration);
if (duration.tv_sec == 0 && duration.tv_nsec < 500000000) {
warnx("%s: slept less than 0.5 sec: %lld.%09ld", __func__,
(long long)duration.tv_sec, duration.tv_nsec);
return 1;
}
return 0;
}
int
time_elapsed_with_signal(void)
{
struct timespec timeout, remainder;
struct timespec start, end, duration;
pid_t pid;
int status;
signal(SIGUSR1, sighandler);
pid = getpid();
switch(fork()) {
case -1:
err(1, "fork");
case 0:
timeout.tv_sec = 1;
timeout.tv_nsec = 0;
nanosleep(&timeout, NULL);
kill(pid, SIGUSR1);
_exit(0);
default:
break;
}
if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
warn("%s: clock_gettime", __func__);
return 1;
}
timeout.tv_sec = 10;
timeout.tv_nsec = 0;
remainder.tv_sec = 0;
remainder.tv_nsec = 0;
if (nanosleep(&timeout, &remainder) == 0) {
warnx("%s: nanosleep", __func__);
return 1;
}
if (clock_gettime(CLOCK_MONOTONIC, &end) < 0) {
warn("%s: clock_gettime", __func__);
return 1;
}
timespecsub(&end, &start, &duration);
timespecadd(&duration, &remainder, &timeout);
remainder.tv_sec = 0;
remainder.tv_nsec = 10000000;
timespecadd(&timeout, &remainder, &timeout);
if (timeout.tv_sec < 10) {
warnx("%s: slept time + leftover time < 10 sec: %lld.%09ld",
__func__, (long long)timeout.tv_sec, timeout.tv_nsec);
return 1;
}
if (wait(&status) < 0)
err(1, "wait");
if (status != 0)
errx(1, "status");
return 0;
}
int
short_time(void)
{
struct timespec timeout;
pid_t pid;
int status;
signal(SIGUSR1, sighandler);
pid = getpid();
switch(fork()) {
case -1:
err(1, "fork");
case 0:
timeout.tv_sec = 2;
timeout.tv_nsec = 0;
nanosleep(&timeout, NULL);
kill(pid, SIGUSR1);
_exit(0);
default:
break;
}
timeout.tv_sec = 0;
timeout.tv_nsec = 1;
if (nanosleep(&timeout, NULL) < 0) {
warn("%s: nanosleep", __func__);
return 1;
}
if (wait(&status) < 0)
err(1, "wait");
if (status != 0)
errx(1, "status");
return 0;
}
int
invalid_time(void)
{
struct timespec timeout[3] = { {-1, 0}, {0, -1}, {0, 1000000000L} };
int i, status;
for (i = 0; i < 3; i++) {
status = nanosleep(&timeout[i], NULL);
if (status != -1 || errno != EINVAL) {
warnx("%s: nanosleep %lld %ld", __func__,
(long long)timeout[i].tv_sec, timeout[i].tv_nsec);
return 1;
}
}
return 0;
}