The Pedigree Project 0.1
signal-timer-contract-test/main.c
1#define _GNU_SOURCE
2#include <errno.h>
3#include <signal.h>
4#include <stdio.h>
5#include <string.h>
6#include <time.h>
7#include <unistd.h>
8
9#include "contract.h"
10#include <sys/wait.h>
11
12int64_t st_now(clockid_t clock) {
13 struct timespec now;
14 return clock_gettime(clock, &now) ? -1 : (int64_t)now.tv_sec * 1000000000 + now.tv_nsec;
15}
16struct timespec st_timespec(int64_t nanoseconds) {
17 return (struct timespec){nanoseconds / 1000000000, nanoseconds % 1000000000};
18}
19void st_pause(int milliseconds) {
20 struct timespec pause = st_timespec((int64_t)milliseconds * 1000000);
21 while (nanosleep(&pause, &pause) && errno == EINTR) {
22 }
23}
24int st_wait(volatile int* flag, int milliseconds) {
25 int64_t deadline = st_now(CLOCK_MONOTONIC) + (int64_t)milliseconds * 1000000;
26 while (!__atomic_load_n(flag, __ATOMIC_ACQUIRE)) {
27 if (st_now(CLOCK_MONOTONIC) >= deadline)
28 return -1;
29 st_pause(2);
30 }
31 return 0;
32}
33int st_reap(pid_t child, int milliseconds) {
34 int64_t deadline = st_now(CLOCK_MONOTONIC) + (int64_t)milliseconds * 1000000;
35 while (st_now(CLOCK_MONOTONIC) < deadline) {
36 int status;
37 pid_t result = waitpid(child, &status, WNOHANG);
38 if (result == child)
39 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
40 if (result < 0 && errno != EINTR)
41 return -1;
42 st_pause(5);
43 }
44 kill(child, SIGKILL);
45 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
46 }
47 return -1;
48}
49
50static int run(const char* name, int (*test)(void)) {
51 printf("SIGNAL-TIMER-CONTRACT: BEGIN %s\n", name);
52 fflush(stdout);
53 const int clock_suite = !strcmp(name, "clock") || !strcmp(name, "raw-clock");
54 int64_t realtime = st_now(CLOCK_REALTIME), monotonic = st_now(CLOCK_MONOTONIC);
55 pid_t child = fork();
56 if (child < 0)
57 return -1;
58 if (!child) {
59 alarm(30);
60 int result = test();
61 fflush(stdout);
62 fflush(stderr);
63 _exit(result ? 1 : 0);
64 }
65 int result = st_reap(child, 35000);
66 if (clock_suite && geteuid() == 0) {
67 // Restore the guest clock even if the child fails after changing it.
68 struct timespec restored = st_timespec(realtime + st_now(CLOCK_MONOTONIC) - monotonic);
69 if (clock_settime(CLOCK_REALTIME, &restored))
70 result = -1;
71 }
72 printf("SIGNAL-TIMER-CONTRACT: %s %s status=%d\n", result ? "FAIL" : "PASS", name, result);
73 fflush(stdout);
74 return result;
75}
76int main(int argc, char** argv) {
77 if (argc > 1 && !strcmp(argv[1], "timer-exec"))
78 return signal_timer_exec(argc, argv);
79 static const struct {
80 const char* name;
81 int (*test)(void);
82 } suites[] = {{"signals", signal_timer_test_signals},
83 {"timers", signal_timer_test_timers},
84 {"cpu-itimers", signal_timer_test_cpu_itimers},
85 {"clock", signal_timer_test_clock},
86 {"raw-clock", signal_timer_test_raw_clock}};
87 int selected = 0;
88 for (unsigned n = 0; n < sizeof(suites) / sizeof(suites[0]); ++n) {
89 if (argc > 1 && strcmp(argv[1], suites[n].name))
90 continue;
91 selected = 1;
92 if (run(suites[n].name, suites[n].test))
93 return 1;
94 }
95 if (!selected)
96 return 2;
97 puts("SIGNAL-TIMER-CONTRACT: END PASS");
98 return 0;
99}