#include <atf-c.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "h_macros.h"
#define TIME_MAX __type_max(time_t)
static timer_t t;
static sig_atomic_t expired;
enum mode {
PAST,
EXPIRE,
NOEXPIRE,
};
static void
timer_signal_handler(int signo, siginfo_t *si, void *osi __unused)
{
const int errno_save = errno;
timer_t *tp;
tp = si->si_value.sival_ptr;
if (*tp == t && signo == SIGALRM)
expired = 1;
(void)fprintf(stderr, "%s: %s\n", __func__, strsignal(signo));
errno = errno_save;
}
static void
timer_signal_create(clockid_t cid, enum mode mode, int flags)
{
struct itimerspec tim, rtim, otim;
struct timespec t0, t1, dt;
struct sigaction act;
struct sigevent evt;
sigset_t set;
t = 0;
expired = 0;
(void)memset(&evt, 0, sizeof(struct sigevent));
(void)memset(&act, 0, sizeof(struct sigaction));
(void)memset(&tim, 0, sizeof(struct itimerspec));
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = timer_signal_handler;
ATF_REQUIRE(sigemptyset(&set) == 0);
ATF_REQUIRE(sigemptyset(&act.sa_mask) == 0);
ATF_REQUIRE(sigaction(SIGALRM, &act, NULL) == 0);
ATF_REQUIRE(sigaddset(&set, SIGALRM) == 0);
ATF_REQUIRE(sigprocmask(SIG_SETMASK, &set, NULL) == 0);
evt.sigev_signo = SIGALRM;
evt.sigev_value.sival_ptr = &t;
evt.sigev_notify = SIGEV_SIGNAL;
ATF_REQUIRE(timer_create(cid, &evt, &t) == 0);
switch (mode) {
case PAST:
ATF_REQUIRE(flags & TIMER_ABSTIME);
tim.it_value.tv_sec = -1;
break;
case EXPIRE:
tim.it_value.tv_sec = 1;
break;
case NOEXPIRE:
tim.it_value.tv_sec = 5;
break;
}
tim.it_value.tv_nsec = 0;
rtim = tim;
RL(clock_gettime(cid, &t0));
if (flags & TIMER_ABSTIME)
timespecadd(&t0, &tim.it_value, &tim.it_value);
fprintf(stderr, "now is %lld sec %d nsec\n",
(long long)t0.tv_sec, (int)t0.tv_nsec);
fprintf(stderr, "expire at %lld sec %d nsec\n",
(long long)tim.it_value.tv_sec, (int)tim.it_value.tv_nsec);
RL(timer_settime(t, flags, &tim, NULL));
RL(timer_settime(t, flags, &tim, &otim));
RL(clock_gettime(cid, &t1));
timespecsub(&t1, &t0, &dt);
fprintf(stderr, "%lld sec %d nsec elapsed\n",
(long long)dt.tv_sec, (int)dt.tv_nsec);
if (flags & TIMER_ABSTIME) {
timespecadd(&rtim.it_value, (&(const struct timespec){2, 0}),
&rtim.it_value);
}
ATF_CHECK_MSG(timespeccmp(&otim.it_value, &rtim.it_value, <=),
"time remaining %lld sec %d nsec,"
" expected at most %lld sec %d nsec",
(long long)otim.it_value.tv_sec, (int)otim.it_value.tv_nsec,
(long long)rtim.it_value.tv_sec, (int)rtim.it_value.tv_nsec);
#if 0
timespecsub(&t1, &t0, &dt);
timespecsub(&rtim.it_value, &otim.it_value, &rtim.it_value);
ATF_CHECK_MSG(timespeccmp(&rtim.it_value, &dt, <=),
"time remaining went down by %lld sec %d nsec,"
" expected at most %lld sec %d nsec",
(long long)rtim.it_value.tv_sec, (int)rtim.it_value.tv_nsec,
(long long)dt.tv_sec, (int)dt.tv_nsec);
#endif
ATF_CHECK_MSG(timespeccmp(&otim.it_interval, &rtim.it_interval, ==),
"interval %lld sec %d nsec,"
" expected %lld sec %d nsec",
(long long)otim.it_interval.tv_sec, (int)otim.it_interval.tv_nsec,
(long long)rtim.it_interval.tv_sec, (int)rtim.it_interval.tv_nsec);
(void)sigprocmask(SIG_UNBLOCK, &set, NULL);
switch (mode) {
case PAST:
RL(clock_nanosleep(cid, 0, &(const struct timespec){0, 1},
NULL));
ATF_CHECK_MSG(expired, "timer failed to fire immediately");
break;
case EXPIRE:
case NOEXPIRE:
ATF_CHECK_MSG(!expired, "timer fired too soon");
(void)sleep(2);
switch (mode) {
case PAST:
__unreachable();
case EXPIRE:
ATF_CHECK_MSG(expired,
"timer failed to fire immediately");
break;
case NOEXPIRE:
ATF_CHECK_MSG(!expired, "timer fired too soon");
break;
}
break;
}
ATF_REQUIRE(timer_delete(t) == 0);
}
ATF_TC(timer_create_err);
ATF_TC_HEAD(timer_create_err, tc)
{
atf_tc_set_md_var(tc, "descr",
"Check errors from timer_create(2) (PR lib/42434)");
}
ATF_TC_BODY(timer_create_err, tc)
{
struct sigevent ev;
(void)memset(&ev, 0, sizeof(struct sigevent));
errno = 0;
ev.sigev_signo = -1;
ev.sigev_notify = SIGEV_SIGNAL;
ATF_REQUIRE_ERRNO(EINVAL, timer_create(CLOCK_REALTIME, &ev, &t) == -1);
errno = 0;
ev.sigev_signo = SIGUSR1;
ev.sigev_notify = SIGEV_THREAD + 100;
ATF_REQUIRE_ERRNO(EINVAL, timer_create(CLOCK_REALTIME, &ev, &t) == -1);
}
ATF_TC(timer_create_real);
ATF_TC_HEAD(timer_create_real, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), "
"SIGEV_SIGNAL");
}
ATF_TC_BODY(timer_create_real, tc)
{
timer_signal_create(CLOCK_REALTIME, NOEXPIRE, 0);
}
ATF_TC(timer_create_real_abs);
ATF_TC_HEAD(timer_create_real_abs, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), "
"SIGEV_SIGNAL, using absolute time");
}
ATF_TC_BODY(timer_create_real_abs, tc)
{
timer_signal_create(CLOCK_REALTIME, NOEXPIRE, TIMER_ABSTIME);
}
ATF_TC(timer_create_mono);
ATF_TC_HEAD(timer_create_mono, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), "
"SIGEV_SIGNAL");
}
ATF_TC_BODY(timer_create_mono, tc)
{
timer_signal_create(CLOCK_MONOTONIC, NOEXPIRE, 0);
}
ATF_TC(timer_create_mono_abs);
ATF_TC_HEAD(timer_create_mono_abs, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), "
"SIGEV_SIGNAL, using absolute time");
}
ATF_TC_BODY(timer_create_mono_abs, tc)
{
timer_signal_create(CLOCK_MONOTONIC, NOEXPIRE, TIMER_ABSTIME);
}
ATF_TC(timer_create_real_expire);
ATF_TC_HEAD(timer_create_real_expire, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), "
"SIGEV_SIGNAL, with expiration");
}
ATF_TC_BODY(timer_create_real_expire, tc)
{
timer_signal_create(CLOCK_REALTIME, EXPIRE, 0);
}
ATF_TC(timer_create_real_expire_abs);
ATF_TC_HEAD(timer_create_real_expire_abs, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), "
"SIGEV_SIGNAL, with expiration, using absolute time");
}
ATF_TC_BODY(timer_create_real_expire_abs, tc)
{
timer_signal_create(CLOCK_REALTIME, EXPIRE, TIMER_ABSTIME);
}
ATF_TC(timer_create_mono_expire);
ATF_TC_HEAD(timer_create_mono_expire, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), "
"SIGEV_SIGNAL, with expiration");
}
ATF_TC_BODY(timer_create_mono_expire, tc)
{
timer_signal_create(CLOCK_MONOTONIC, EXPIRE, 0);
}
ATF_TC(timer_create_mono_expire_abs);
ATF_TC_HEAD(timer_create_mono_expire_abs, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), "
"SIGEV_SIGNAL, with expiration, using absolute time");
}
ATF_TC_BODY(timer_create_mono_expire_abs, tc)
{
timer_signal_create(CLOCK_MONOTONIC, EXPIRE, TIMER_ABSTIME);
}
ATF_TC(timer_create_real_past_abs);
ATF_TC_HEAD(timer_create_real_past_abs, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), "
"SIGEV_SIGNAL, with expiration passed before timer_settime(2),"
" using absolute time");
}
ATF_TC_BODY(timer_create_real_past_abs, tc)
{
timer_signal_create(CLOCK_REALTIME, PAST, TIMER_ABSTIME);
}
ATF_TC(timer_create_mono_past_abs);
ATF_TC_HEAD(timer_create_mono_past_abs, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), "
"SIGEV_SIGNAL, with expiration passed before timer_settime(2),"
" using absolute time");
}
ATF_TC_BODY(timer_create_mono_past_abs, tc)
{
timer_signal_create(CLOCK_MONOTONIC, PAST, TIMER_ABSTIME);
}
ATF_TC(timer_invalidtime);
ATF_TC_HEAD(timer_invalidtime, tc)
{
atf_tc_set_md_var(tc, "descr",
"Verify timer_settime(2) rejects invalid times");
}
ATF_TC_BODY(timer_invalidtime, tc)
{
const struct itimerspec einval_its[] = {
[0] = { .it_value = {-1, 0} },
[1] = { .it_value = {0, -1} },
[2] = { .it_value = {0, 1000000001} },
[3] = { .it_value = {1, 0}, .it_interval = {-1, 0} },
[4] = { .it_value = {1, 0}, .it_interval = {0, -1} },
[5] = { .it_value = {1, 0}, .it_interval = {0, 1000000001} },
[6] = { .it_value = {TIME_MAX - 1, 0}, .it_interval = {0, 0} },
[7] = { .it_value = {TIME_MAX, 0}, .it_interval = {0, 0} },
};
struct timespec now;
sigset_t sigs, mask;
unsigned i;
RL(sigemptyset(&sigs));
RL(sigaddset(&sigs, SIGALRM));
RL(sigprocmask(SIG_BLOCK, &sigs, &mask));
RL(clock_gettime(CLOCK_MONOTONIC, &now));
RL(timer_create(CLOCK_MONOTONIC, NULL, &t));
for (i = 0; i < __arraycount(einval_its); i++) {
struct itimerspec its;
fprintf(stderr, "case %u\n", i);
ATF_CHECK_ERRNO(EINVAL,
timer_settime(t, 0, &einval_its[i], NULL) == -1);
if (i == 0)
continue;
its.it_value = einval_its[i].it_value;
its.it_value.tv_sec += now.tv_sec;
its.it_interval = einval_its[i].it_interval;
ATF_CHECK_ERRNO(EINVAL,
timer_settime(t, TIMER_ABSTIME, &its, NULL) == -1);
}
ATF_CHECK_ERRNO(EAGAIN,
sigtimedwait(&sigs, NULL, &(const struct timespec){2, 0}) == -1);
RL(sigprocmask(SIG_SETMASK, &mask, NULL));
RL(timer_delete(t));
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, timer_create_err);
ATF_TP_ADD_TC(tp, timer_create_real);
ATF_TP_ADD_TC(tp, timer_create_real_abs);
ATF_TP_ADD_TC(tp, timer_create_mono);
ATF_TP_ADD_TC(tp, timer_create_mono_abs);
ATF_TP_ADD_TC(tp, timer_create_real_expire);
ATF_TP_ADD_TC(tp, timer_create_real_expire_abs);
ATF_TP_ADD_TC(tp, timer_create_mono_expire);
ATF_TP_ADD_TC(tp, timer_create_mono_expire_abs);
ATF_TP_ADD_TC(tp, timer_create_real_past_abs);
ATF_TP_ADD_TC(tp, timer_create_mono_past_abs);
ATF_TP_ADD_TC(tp, timer_invalidtime);
return atf_no_error();
}