The Pedigree Project 0.1
child-wait-contract-test/fixture.c
1#define _GNU_SOURCE
2#include <poll.h>
3#include <sched.h>
4#include <string.h>
5#include <time.h>
6#include <unistd.h>
7
8#include "contract.h"
9#include <sys/syscall.h>
10
11size_t cw_page;
12
13int64_t cw_now(void) {
14 struct timespec now;
15 return clock_gettime(CLOCK_MONOTONIC, &now) ? -1 : (int64_t)now.tv_sec * 1000000000 + now.tv_nsec;
16}
17
18int cw_read(int fd, void* data, size_t size) {
19 unsigned char* bytes = data;
20 const int64_t end = cw_now() + INT64_C(10000000000);
21 while (size) {
22 int64_t left = end - cw_now();
23 if (left <= 0)
24 return -1;
25 struct pollfd ready = {.fd = fd, .events = POLLIN};
26 int result = poll(&ready, 1, (int)(left / 1000000 + 1));
27 if (result < 0 && errno == EINTR)
28 continue;
29 if (result <= 0)
30 return -1;
31 ssize_t n = read(fd, bytes, size);
32 if (n < 0 && errno == EINTR)
33 continue;
34 if (n <= 0)
35 return -1;
36 bytes += n;
37 size -= (size_t)n;
38 }
39 return 0;
40}
41
42int cw_write(int fd, const void* data, size_t size) {
43 const unsigned char* bytes = data;
44 while (size) {
45 ssize_t n = write(fd, bytes, size);
46 if (n < 0 && errno == EINTR)
47 continue;
48 if (n <= 0)
49 return -1;
50 bytes += n;
51 size -= (size_t)n;
52 }
53 return 0;
54}
55
56int cw_send(int fd, char value) {
57 return cw_write(fd, &value, 1);
58}
59
60int cw_receive(int fd, char value) {
61 char actual;
62 return cw_read(fd, &actual, 1) || actual != value ? -1 : 0;
63}
64
65int cw_atomic_wait(atomic_uint* value, unsigned expected) {
66 const int64_t end = cw_now() + INT64_C(10000000000);
67 while (cw_now() < end) {
68 if (atomic_load_explicit(value, memory_order_acquire) == expected)
69 return 0;
70 sched_yield();
71 }
72 return -1;
73}
74
75int cw_spawn(struct cw_child* child, pid_t group, uid_t uid, int exit_code) {
76 int command[2], report[2];
77 if (pipe(command))
78 return -1;
79 if (pipe(report)) {
80 close(command[0]);
81 close(command[1]);
82 return -1;
83 }
84 pid_t pid = fork();
85 if (!pid) {
86 close(command[1]);
87 close(report[0]);
88 alarm(30);
89 if ((group >= 0 && setpgid(0, group)) ||
90 (uid != (uid_t)-1 && setresuid(uid, uid + 1, uid + 2)) || cw_send(report[1], 'R'))
91 _exit(120);
92 for (;;) {
93 char request;
94 if (cw_read(command[0], &request, 1))
95 _exit(121);
96 if (request == 'E')
97 _exit(exit_code);
98 if (request == 'S') {
99 if (raise(SIGSTOP) || cw_send(report[1], 'C'))
100 _exit(122);
101 } else if (request == 'B') {
102 volatile unsigned value = 1;
103 for (unsigned i = 0; i < 2000000; ++i)
104 value = value * 1664525u + i;
105 if (cw_send(report[1], 'B'))
106 _exit(123);
107 } else {
108 _exit(124);
109 }
110 }
111 }
112 close(command[0]);
113 close(report[1]);
114 if (pid < 0) {
115 close(command[1]);
116 close(report[0]);
117 return -1;
118 }
119 *child = (struct cw_child){pid, command[1], report[0]};
120 if (cw_receive(child->report, 'R')) {
121 cw_cleanup(child);
122 return -1;
123 }
124 return 0;
125}
126
127int cw_reap(pid_t pid, int milliseconds) {
128 const int64_t end = cw_now() + (int64_t)milliseconds * 1000000;
129 while (cw_now() < end) {
130 int status;
131 pid_t result = waitpid(pid, &status, WNOHANG);
132 if (result == pid)
133 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
134 if (result < 0 && errno != EINTR)
135 return -1;
136 struct timespec delay = {0, 2000000};
137 nanosleep(&delay, NULL);
138 }
139 kill(pid, SIGKILL);
140 while (waitpid(pid, NULL, 0) < 0 && errno == EINTR) {
141 }
142 return -1;
143}
144
145void cw_cleanup(struct cw_child* child) {
146 if (child->pid > 0) {
147 kill(child->pid, SIGKILL);
148 cw_reap(child->pid, 1000);
149 }
150 if (child->command >= 0)
151 close(child->command);
152 if (child->report >= 0)
153 close(child->report);
154 *child = (struct cw_child)CW_CHILD_INIT;
155}
156
157int cw_info(const siginfo_t* info, pid_t pid, uid_t uid, int code, int status) {
158 if (info->si_signo == SIGCHLD && !info->si_errno && info->si_pid == pid && info->si_uid == uid &&
159 info->si_code == code && info->si_status == status)
160 return 0;
161 fprintf(stderr,
162 "siginfo: signo=%d errno=%d pid=%d uid=%u code=%d status=%d; expected %d/%u/%d/%d\n",
163 info->si_signo, info->si_errno, info->si_pid, info->si_uid, info->si_code,
164 info->si_status, pid, uid, code, status);
165 return -1;
166}
167
168int cw_empty_info(const siginfo_t* info) {
169 return info->si_signo || info->si_errno || info->si_pid || info->si_uid || info->si_code ||
170 info->si_status;
171}
172
173int cw_usage(const struct rusage* usage) {
174 return usage->ru_utime.tv_sec < 0 || usage->ru_utime.tv_usec < 0 ||
175 usage->ru_utime.tv_usec >= 1000000 || usage->ru_stime.tv_sec < 0 ||
176 usage->ru_stime.tv_usec < 0 || usage->ru_stime.tv_usec >= 1000000 ||
177 cw_bytes((const unsigned char*)usage + CW_RUSAGE_BYTES, sizeof(*usage) - CW_RUSAGE_BYTES,
178 0xa5);
179}
180
181int cw_bytes(const void* buffer, size_t size, unsigned char value) {
182 const unsigned char* bytes = buffer;
183 for (size_t i = 0; i < size; ++i)
184 if (bytes[i] != value)
185 return -1;
186 return 0;
187}
188
189long cw_raw_waitid(idtype_t type, id_t id, void* info, int options, struct rusage* usage) {
190 return syscall(SYS_waitid, type, id, info, options, usage);
191}