The Pedigree Project 0.1
event-descriptor-contract-test/main.c
1#define _GNU_SOURCE
2#include <poll.h>
3#include <signal.h>
4#include <string.h>
5#include <unistd.h>
6
7#include "contract.h"
8#include <sys/socket.h>
9#include <sys/wait.h>
10
11int64_t ed_now(clockid_t clock) {
12 struct timespec now;
13 return clock_gettime(clock, &now) ? -1 : (int64_t)now.tv_sec * 1000000000 + now.tv_nsec;
14}
15struct timespec ed_timespec(int64_t nanoseconds) {
16 return (struct timespec){nanoseconds / 1000000000, nanoseconds % 1000000000};
17}
18void ed_pause(int milliseconds) {
19 struct timespec pause = ed_timespec((int64_t)milliseconds * 1000000);
20 while (nanosleep(&pause, &pause) && errno == EINTR) {
21 }
22}
23int ed_wait(volatile int* flag, int milliseconds) {
24 int64_t deadline = ed_now(CLOCK_MONOTONIC) + (int64_t)milliseconds * 1000000;
25 while (!__atomic_load_n(flag, __ATOMIC_ACQUIRE)) {
26 if (ed_now(CLOCK_MONOTONIC) >= deadline)
27 return -1;
28 ed_pause(2);
29 }
30 return 0;
31}
32int ed_reap(pid_t child, int milliseconds) {
33 int64_t deadline = ed_now(CLOCK_MONOTONIC) + (int64_t)milliseconds * 1000000;
34 while (ed_now(CLOCK_MONOTONIC) < deadline) {
35 int status;
36 pid_t result = waitpid(child, &status, WNOHANG);
37 if (result == child)
38 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
39 if (result < 0 && errno != EINTR)
40 return -1;
41 ed_pause(5);
42 }
43 kill(child, SIGKILL);
44 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
45 }
46 return -1;
47}
48int ed_send_fd(int socket, int fd) {
49 char byte = 'f';
50 struct iovec iov = {&byte, 1};
51 union {
52 struct cmsghdr align;
53 char bytes[CMSG_SPACE(sizeof(int))];
54 } control = {0};
55 struct msghdr message = {.msg_iov = &iov,
56 .msg_iovlen = 1,
57 .msg_control = control.bytes,
58 .msg_controllen = sizeof(control.bytes)};
59 struct cmsghdr* item = CMSG_FIRSTHDR(&message);
60 item->cmsg_level = SOL_SOCKET;
61 item->cmsg_type = SCM_RIGHTS;
62 item->cmsg_len = CMSG_LEN(sizeof(int));
63 memcpy(CMSG_DATA(item), &fd, sizeof(fd));
64 return sendmsg(socket, &message, 0) == 1 ? 0 : -1;
65}
66int ed_receive_fd(int socket) {
67 char byte;
68 struct iovec iov = {&byte, 1};
69 union {
70 struct cmsghdr align;
71 char bytes[CMSG_SPACE(sizeof(int))];
72 } control = {0};
73 struct msghdr message = {.msg_iov = &iov,
74 .msg_iovlen = 1,
75 .msg_control = control.bytes,
76 .msg_controllen = sizeof(control.bytes)};
77 if (recvmsg(socket, &message, MSG_CMSG_CLOEXEC) != 1 || message.msg_flags & MSG_CTRUNC)
78 return -1;
79 struct cmsghdr* item = CMSG_FIRSTHDR(&message);
80 if (!item || item->cmsg_level != SOL_SOCKET || item->cmsg_type != SCM_RIGHTS ||
81 item->cmsg_len != CMSG_LEN(sizeof(int)))
82 return -1;
83 int fd;
84 memcpy(&fd, CMSG_DATA(item), sizeof(fd));
85 return fd;
86}
87int ed_readable(int fd, int milliseconds) {
88 struct pollfd entry = {.fd = fd, .events = POLLIN};
89 int result = poll(&entry, 1, milliseconds);
90 return result > 0 ? !!(entry.revents & POLLIN) : result;
91}
92void* ed_read_thread(void* argument) {
93 struct ed_reader* reader = argument;
94 __atomic_store_n(&reader->started, 1, __ATOMIC_RELEASE);
95 reader->result = read(reader->fd, reader->bytes, reader->size);
96 reader->error = errno;
97 reader->finished = ed_now(CLOCK_MONOTONIC);
98 __atomic_store_n(&reader->done, 1, __ATOMIC_RELEASE);
99 return NULL;
100}
101
102static int run(const char* name, int (*test)(void)) {
103 printf("EVENT-DESCRIPTOR-CONTRACT: BEGIN %s\n", name);
104 fflush(stdout);
105 const int clock_suite = !strcmp(name, "clock");
106 int64_t realtime = ed_now(CLOCK_REALTIME), monotonic = ed_now(CLOCK_MONOTONIC);
107 pid_t child = fork();
108 if (child < 0)
109 return -1;
110 if (!child) {
111 alarm(30);
112 int result = test();
113 fflush(stdout);
114 fflush(stderr);
115 _exit(result ? 1 : 0);
116 }
117 int result = ed_reap(child, 35000);
118 if (clock_suite && geteuid() == 0) {
119 // Restore the guest clock even if the child fails after changing it.
120 struct timespec restored = ed_timespec(realtime + ed_now(CLOCK_MONOTONIC) - monotonic);
121 if (clock_settime(CLOCK_REALTIME, &restored))
122 result = -1;
123 }
124 printf("EVENT-DESCRIPTOR-CONTRACT: %s %s status=%d\n", result ? "FAIL" : "PASS", name, result);
125 fflush(stdout);
126 return result;
127}
128int main(int argc, char** argv) {
129 if (argc > 1 && !strcmp(argv[1], "signalfd-exec"))
130 return event_descriptor_signalfd_exec(argc, argv);
131 if (argc > 1 && !strcmp(argv[1], "timerfd-exec"))
132 return event_descriptor_timerfd_exec(argc, argv);
133 static const struct {
134 const char* name;
135 int (*test)(void);
136 } suites[] = {{"signalfd", event_descriptor_test_signalfd},
137 {"timerfd", event_descriptor_test_timerfd},
138 {"clock", event_descriptor_test_clock}};
139 int selected = 0;
140 for (unsigned n = 0; n < sizeof(suites) / sizeof(suites[0]); ++n) {
141 if (argc > 1 && strcmp(argv[1], suites[n].name))
142 continue;
143 selected = 1;
144 if (run(suites[n].name, suites[n].test))
145 return 1;
146 }
147 if (!selected)
148 return 2;
149 puts("EVENT-DESCRIPTOR-CONTRACT: END PASS");
150 return 0;
151}
Definition waits.c:9