The Pedigree Project 0.1
signal-exec-test/main.c
1#define _GNU_SOURCE
2
3#include <errno.h>
4#include <sched.h>
5#include <signal.h>
6#include <stdio.h>
7#include <string.h>
8#include <time.h>
9#include <unistd.h>
10
11#include "contracts.h"
12#include <sys/wait.h>
13
14long long test_milliseconds(void) {
15 struct timespec now;
16 if (clock_gettime(CLOCK_MONOTONIC, &now)) {
17 return -1;
18 }
19 return (long long)now.tv_sec * 1000 + now.tv_nsec / 1000000;
20}
21
22int test_wait_flag(volatile int* flag) {
23 const long long start = test_milliseconds();
24 if (start < 0) {
25 return -1;
26 }
27 while (!__atomic_load_n(flag, __ATOMIC_ACQUIRE)) {
28 const long long now = test_milliseconds();
29 if (now < 0 || now - start >= 3000) {
30 return -1;
31 }
32 sched_yield();
33 }
34 return 0;
35}
36
37void test_pause(void) {
38 struct timespec delay = {.tv_nsec = 2000000};
39 while (nanosleep(&delay, &delay) && errno == EINTR) {
40 }
41}
42
43static int run_bounded(const char* program, const char* name) {
44 printf("SIGNAL-EXEC-TEST: BEGIN %s\n", name);
45 fflush(stdout);
46 const pid_t child = fork();
47 if (child < 0) {
48 return 255;
49 }
50 if (!child) {
51 alarm(10);
52 const int result =
53 !strncmp(name, "exec-", 5) ? exec_contract(program, name) : signal_contract(name);
54 _exit(result);
55 }
56
57 int status = 0;
58 const long long start = test_milliseconds();
59 while (start >= 0) {
60 const pid_t waited = waitpid(child, &status, WNOHANG);
61 if (waited == child) {
62 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
63 }
64 if (waited < 0 && errno != EINTR) {
65 break;
66 }
67 const long long now = test_milliseconds();
68 if (now < 0 || now - start >= 15000) {
69 break;
70 }
71 test_pause();
72 }
73 (void)kill(child, SIGKILL);
74 while (waitpid(child, &status, 0) < 0 && errno == EINTR) {
75 }
76 return 254;
77}
78
79int main(int argc, char* argv[]) {
80 if (argc > 1 &&
81 (!strcmp(argv[1], "--exec-image") || !strcmp(argv[1], "--exec-race-image") ||
82 !strcmp(argv[1], "--exec-pending-image") || !strcmp(argv[1], "--exec-exit-image"))) {
83 return exec_image_contract(argc, argv);
84 }
85
86 const char* cases[] = {
87 "read-eintr", "read-restart", "wait-eintr", "wait-restart",
88 "partial-write", "nanosleep-eintr", "ppoll-eintr", "pselect-eintr",
89 "epoll-eintr", "reset-hand", "exec-main", "exec-worker",
90 "exec-failure", "futex-eintr", "futex-restart", "exec-race",
91 "exec-pending-main", "exec-pending-worker", "exec-exit-race", "sigsuspend-eintr"};
92 int selected = 0;
93 for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); ++i) {
94 if (argc > 1 && strcmp(argv[1], cases[i])) {
95 continue;
96 }
97 selected = 1;
98 const int exit_race = !strcmp(cases[i], "exec-exit-race");
99 for (int iteration = 0; iteration < (exit_race ? 3 : 1); ++iteration) {
100 const int result = run_bounded(argv[0], cases[i]);
101 if (result && !(exit_race && result == 37)) {
102 printf("SIGNAL-EXEC-TEST: FAIL %s code=%d\n", cases[i], result);
103 puts("SIGNAL-EXEC-TEST: END FAIL");
104 return 1;
105 }
106 if (exit_race) {
107 printf("SIGNAL-EXEC-TEST: EXIT-RACE iteration=%d code=%d\n", iteration + 1, result);
108 }
109 }
110 printf("SIGNAL-EXEC-TEST: PASS %s\n", cases[i]);
111 fflush(stdout);
112 }
113 if (!selected) {
114 fprintf(stderr, "Unknown signal/exec test: %s\n", argv[1]);
115 return 2;
116 }
117 puts("SIGNAL-EXEC-TEST: PASS");
118 puts("SIGNAL-EXEC-TEST: END PASS");
119 return 0;
120}