The Pedigree Project 0.1
interruption.c
1#define _GNU_SOURCE
2#include <poll.h>
3#include <sched.h>
4#include <string.h>
5#include <unistd.h>
6
7#include "contract.h"
8
9static volatile sig_atomic_t handler_report = -1;
10
11static void caught(int signal) {
12 int saved = errno;
13 if (signal == SIGUSR1 && handler_report >= 0) {
14 char value = 'H';
15 ssize_t result = write(handler_report, &value, 1);
16 (void)result;
17 }
18 errno = saved;
19}
20
21struct waiter {
22 pid_t child;
23 int use_waitpid;
24 atomic_uint ready, done;
25 int result, error, status;
26 siginfo_t info;
27};
28
29static void* wait_for_child(void* argument) {
30 struct waiter* waiter = argument;
31 sigset_t mask;
32 sigemptyset(&mask);
33 sigaddset(&mask, SIGUSR1);
34 pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
35 memset(&waiter->info, 0xa5, sizeof(waiter->info));
36 atomic_store(&waiter->ready, 1);
37 errno = 0;
38 waiter->result = waiter->use_waitpid ? waitpid(waiter->child, &waiter->status, 0)
39 : waitid(P_PID, waiter->child, &waiter->info, WEXITED);
40 waiter->error = errno;
41 atomic_store(&waiter->done, 1);
42 return NULL;
43}
44
45static int interrupt_case(int use_waitpid, int restart) {
46 int failed = 0, started = 0, installed = 0;
47 int report[2] = {-1, -1};
48 struct cw_child child = CW_CHILD_INIT;
49 struct waiter waiter = {.use_waitpid = use_waitpid};
50 struct sigaction action = {.sa_handler = caught, .sa_flags = restart ? SA_RESTART : 0}, old;
51 pthread_t thread;
52 CHECK(pipe(report) == 0);
53 handler_report = report[1];
54 sigemptyset(&action.sa_mask);
55 CHECK(sigaction(SIGUSR1, &action, &old) == 0);
56 installed = 1;
57 CHECK(cw_spawn(&child, -1, (uid_t)-1, 96) == 0);
58 waiter.child = child.pid;
59 CHECK(pthread_create(&thread, NULL, wait_for_child, &waiter) == 0);
60 started = 1;
61 CHECK(cw_atomic_wait(&waiter.ready, 1) == 0);
62 if (restart) {
63 /* Each acknowledgment comes from the target handler; the child stays gated. */
64 for (int i = 0; i < 8; ++i) {
65 CHECK(!atomic_load(&waiter.done));
66 CHECK(pthread_kill(thread, SIGUSR1) == 0 && cw_receive(report[0], 'H') == 0);
67 }
68 CHECK(!atomic_load(&waiter.done));
69 CHECK(cw_send(child.command, 'E') == 0 && cw_atomic_wait(&waiter.done, 1) == 0);
70 CHECK(waiter.result == (use_waitpid ? child.pid : 0));
71 if (use_waitpid)
72 CHECK(WIFEXITED(waiter.status) && WEXITSTATUS(waiter.status) == 96);
73 else
74 CHECK(cw_info(&waiter.info, child.pid, getuid(), CLD_EXITED, 96) == 0);
75 child.pid = -1;
76 } else {
77 const int64_t end = cw_now() + INT64_C(5000000000);
78 while (!atomic_load(&waiter.done) && cw_now() < end) {
79 int result = pthread_kill(thread, SIGUSR1);
80 CHECK(result == 0 || (result == ESRCH && atomic_load(&waiter.done)));
81 struct pollfd pending = {.fd = report[0], .events = POLLIN};
82 if (poll(&pending, 1, 20) > 0)
83 CHECK(cw_receive(report[0], 'H') == 0);
84 sched_yield();
85 }
86 CHECK(atomic_load(&waiter.done) && waiter.result == -1 && waiter.error == EINTR);
87 if (!use_waitpid)
88 CHECK(!cw_empty_info(&waiter.info));
89 siginfo_t info;
90 CHECK(waitid(P_PID, child.pid, &info, WEXITED | WNOHANG) == 0 && !cw_empty_info(&info));
91 CHECK(cw_send(child.command, 'E') == 0 && waitid(P_PID, child.pid, &info, WEXITED) == 0);
92 CHECK(cw_info(&info, child.pid, getuid(), CLD_EXITED, 96) == 0);
93 child.pid = -1;
94 }
95out:
96 cw_cleanup(&child);
97 if (started) {
98 if (!atomic_load(&waiter.done))
99 pthread_cancel(thread);
100 if (pthread_join(thread, NULL))
101 failed = 1;
102 }
103 handler_report = -1;
104 if (installed && sigaction(SIGUSR1, &old, NULL))
105 failed = 1;
106 if (report[0] >= 0)
107 close(report[0]);
108 if (report[1] >= 0)
109 close(report[1]);
110 return failed;
111}
112
113static int cancel_case(int use_waitpid) {
114 int failed = 0, started = 0;
115 struct cw_child child = CW_CHILD_INIT;
116 struct waiter waiter = {.use_waitpid = use_waitpid};
117 pthread_t thread;
118 void* result = NULL;
119 siginfo_t info;
120 CHECK(cw_spawn(&child, -1, (uid_t)-1, 97) == 0);
121 waiter.child = child.pid;
122 CHECK(pthread_create(&thread, NULL, wait_for_child, &waiter) == 0);
123 started = 1;
124 CHECK(cw_atomic_wait(&waiter.ready, 1) == 0);
125 CHECK(pthread_cancel(thread) == 0 && pthread_join(thread, &result) == 0);
126 started = 0;
127 CHECK(result == PTHREAD_CANCELED && !atomic_load(&waiter.done));
128 CHECK(waitid(P_PID, child.pid, &info, WEXITED | WNOHANG) == 0 && !cw_empty_info(&info));
129 CHECK(cw_send(child.command, 'E') == 0 && waitid(P_PID, child.pid, &info, WEXITED) == 0);
130 CHECK(cw_info(&info, child.pid, getuid(), CLD_EXITED, 97) == 0);
131 child.pid = -1;
132out:
133 cw_cleanup(&child);
134 if (started) {
135 pthread_cancel(thread);
136 if (pthread_join(thread, NULL))
137 failed = 1;
138 }
139 return failed;
140}
141
142int cw_interruption(void) {
143 for (int use_waitpid = 0; use_waitpid < 2; ++use_waitpid)
144 if (interrupt_case(use_waitpid, 0) || interrupt_case(use_waitpid, 1) ||
145 cancel_case(use_waitpid))
146 return 1;
147 return 0;
148}