1 #include "signalfd_ctx.h"
2
3 #include <sys/types.h>
4
5 #include <sys/event.h>
6
7 #include <assert.h>
8 #include <errno.h>
9 #include <stdbool.h>
10 #include <string.h>
11
12 #include <fcntl.h>
13 #include <poll.h>
14 #include <unistd.h>
15
16 static errno_t
signalfd_has_pending(SignalFDCtx const * signalfd,bool * has_pending,sigset_t * pending)17 signalfd_has_pending(SignalFDCtx const *signalfd, bool *has_pending,
18 sigset_t *pending)
19 {
20 sigset_t pending_sigs;
21
22 if (sigpending(&pending_sigs) < 0 ||
23 sigandset(&pending_sigs, &pending_sigs, &signalfd->sigs) < 0) {
24 return errno;
25 }
26
27 *has_pending = !sigisemptyset(&pending_sigs);
28 if (pending) {
29 *pending = pending_sigs;
30 }
31 return 0;
32 }
33
34 static errno_t
signalfd_ctx_trigger_manually(SignalFDCtx * signalfd)35 signalfd_ctx_trigger_manually(SignalFDCtx *signalfd)
36 {
37 return kqueue_event_trigger(&signalfd->kqueue_event, signalfd->kq);
38 }
39
40 static void
signalfd_signal_handler(int signo)41 signalfd_signal_handler(int signo)
42 {
43 (void)signo;
44 }
45
46 errno_t
signalfd_ctx_init(SignalFDCtx * signalfd,int kq,const sigset_t * sigs)47 signalfd_ctx_init(SignalFDCtx *signalfd, int kq, const sigset_t *sigs)
48 {
49 errno_t ec;
50
51 assert(sigs != NULL);
52
53 *signalfd = (SignalFDCtx) { .kq = kq, .sigs = *sigs };
54
55 #ifndef _SIG_MAXSIG
56 #define _SIG_MAXSIG (8 * sizeof(sigset_t))
57 #endif
58
59 struct kevent kevs[_SIG_MAXSIG + 2];
60 int n = 0;
61
62 if ((ec = kqueue_event_init(&signalfd->kqueue_event, kevs, &n,
63 false)) != 0) {
64 goto out2;
65 }
66
67 for (int i = 1; i <= _SIG_MAXSIG; ++i) {
68 if (sigismember(&signalfd->sigs, i)) {
69 EV_SET(&kevs[n++], (unsigned int)i, EVFILT_SIGNAL,
70 EV_ADD, 0, 0, 0);
71
72 /*
73 * On Linux, signals with disposition SIG_DFL and a
74 * default action of "ignored" are returned from
75 * sigwait.
76 * We can emulate this by registering an empty signal
77 * handler.
78 */
79 if (i == SIGCHLD || /**/
80 i == SIGURG || /**/
81 i == SIGCONT || /**/
82 #ifdef SIGIO
83 i == SIGIO || /**/
84 #endif
85 #ifdef SIGWINCH
86 i == SIGWINCH || /**/
87 #endif
88 #ifdef SIGINFO
89 i == SIGINFO || /**/
90 #endif
91 #ifdef SIGPWR
92 i == SIGPWR || /**/
93 #endif
94 #ifdef SIGTHR
95 i == SIGTHR || /**/
96 #endif
97 false) {
98 struct sigaction sa;
99
100 if (sigaction(i, NULL, &sa) == 0 &&
101 !(sa.sa_flags & SA_SIGINFO) &&
102 sa.sa_handler == SIG_DFL) {
103 sa.sa_flags |= SA_RESTART;
104 sa.sa_handler = signalfd_signal_handler;
105 (void)sigaction(i, &sa, NULL);
106 }
107 }
108 }
109 }
110
111 n = kevent(signalfd->kq, kevs, n, NULL, 0, NULL);
112 if (n < 0) {
113 ec = errno;
114 goto out;
115 }
116
117 bool has_pending;
118 if ((ec = signalfd_has_pending(signalfd, &has_pending, NULL)) != 0) {
119 goto out;
120 }
121 if (has_pending) {
122 if ((ec = signalfd_ctx_trigger_manually(signalfd)) != 0) {
123 goto out;
124 }
125 }
126
127 return 0;
128
129 out:
130 (void)kqueue_event_terminate(&signalfd->kqueue_event);
131 out2:
132 return ec;
133 }
134
135 errno_t
signalfd_ctx_terminate(SignalFDCtx * signalfd)136 signalfd_ctx_terminate(SignalFDCtx *signalfd)
137 {
138 errno_t ec = 0;
139 errno_t ec_local;
140
141 ec_local = kqueue_event_terminate(&signalfd->kqueue_event);
142 ec = ec != 0 ? ec : ec_local;
143
144 return ec;
145 }
146
147 static errno_t
signalfd_ctx_read_impl(SignalFDCtx * signalfd,SignalFDCtxSiginfo * signalfd_siginfo)148 signalfd_ctx_read_impl(SignalFDCtx *signalfd,
149 SignalFDCtxSiginfo *signalfd_siginfo)
150 {
151 errno_t ec;
152
153 _Static_assert(sizeof(*signalfd_siginfo) == 128, "");
154
155 /*
156 * EVFILT_SIGNAL is an "observer". It does not hook into the
157 * signal disposition mechanism. On the other hand, `signalfd` does.
158 * Therefore, to properly emulate `signalfd`, `sigtimedwait` must be
159 * called.
160 */
161
162 siginfo_t siginfo;
163 memset(&siginfo, 0, sizeof(siginfo));
164
165 #if defined(__OpenBSD__)
166 for (;;) {
167 bool has_pending;
168 sigset_t pending_sigs;
169 if ((ec = signalfd_has_pending(signalfd, &has_pending,
170 &pending_sigs)) != 0) {
171 return ec;
172 }
173 if (!has_pending) {
174 return EAGAIN;
175 }
176
177 /*
178 * sigwait does not behave nicely when multiple signals
179 * are pending (as of OpenBSD 6.8). So, only try to
180 * grab one.
181 */
182 int signum = __builtin_ffsll((long long)pending_sigs);
183 sigset_t mask = sigmask(signum);
184
185 extern int __thrsigdivert(sigset_t set, siginfo_t * info,
186 struct timespec const *timeout);
187
188 /* `&(struct timespec){0, 0}` returns EAGAIN but spams
189 * the dmesg log. Let's do it with an invalid timespec
190 * and EINVAL. */
191 int s = __thrsigdivert(mask, NULL,
192 &(struct timespec) { 0, -1 });
193 ec = s < 0 ? errno : 0;
194 if (ec == EINVAL || ec == EAGAIN) {
195 /* We must retry because we only checked for
196 * one signal. There may be others pending. */
197 continue;
198 }
199 if (ec != 0) {
200 break;
201 }
202
203 siginfo.si_signo = s;
204
205 break;
206 }
207 #else
208 {
209 int s = sigtimedwait(&signalfd->sigs, &siginfo,
210 &(struct timespec) { 0, 0 });
211 ec = s < 0 ? errno : 0;
212 if (ec == 0) {
213 assert(siginfo.si_signo == s);
214 }
215 }
216 #endif
217 if (ec != 0) {
218 return ec;
219 }
220
221 /*
222 * First, fill the POSIX compatible fields, then anything else OS
223 * specific we have.
224 */
225
226 signalfd_siginfo->ssi_signo = (uint32_t)siginfo.si_signo;
227 signalfd_siginfo->ssi_code = siginfo.si_code;
228 signalfd_siginfo->ssi_errno = siginfo.si_errno;
229
230 signalfd_siginfo->ssi_pid = (uint32_t)siginfo.si_pid;
231 signalfd_siginfo->ssi_uid = siginfo.si_uid;
232
233 signalfd_siginfo->ssi_addr = (uint64_t)(uintptr_t)siginfo.si_addr;
234
235 signalfd_siginfo->ssi_status = siginfo.si_status;
236
237 #ifndef __OpenBSD__
238 signalfd_siginfo->ssi_band = (uint32_t)siginfo.si_band;
239 #endif
240
241 signalfd_siginfo->ssi_int = siginfo.si_value.sival_int;
242 signalfd_siginfo->ssi_ptr = (uint64_t)(uintptr_t)
243 siginfo.si_value.sival_ptr;
244
245 /*
246 * Sane defaults for Linux specific fields, may be amended in the
247 * future.
248 */
249 signalfd_siginfo->ssi_fd = -1;
250 signalfd_siginfo->ssi_tid = (uint32_t)-1;
251
252 #ifdef __FreeBSD__
253 signalfd_siginfo->ssi_trapno = (uint32_t)siginfo.si_trapno;
254
255 signalfd_siginfo->ssi_tid = (uint32_t)siginfo.si_timerid;
256 signalfd_siginfo->ssi_overrun = (uint32_t)siginfo.si_overrun;
257
258 if (siginfo.si_code == SI_MESGQ) {
259 /* Re-use this field for si_mqd. */
260 signalfd_siginfo->ssi_fd = siginfo.si_mqd;
261 }
262 #elif __NetBSD__
263 signalfd_siginfo->ssi_utime = siginfo.si_utime;
264 signalfd_siginfo->ssi_stime = siginfo.si_stime;
265
266 signalfd_siginfo->ssi_trapno = siginfo.si_trap;
267 /* No space for trap2/trap3 */
268
269 signalfd_siginfo->ssi_fd = siginfo.si_fd;
270
271 /* No space for syscall/ptrace_state */
272 #endif
273 return 0;
274 }
275
276 static bool
signalfd_ctx_clear_signal(SignalFDCtx * signalfd,bool was_triggered)277 signalfd_ctx_clear_signal(SignalFDCtx *signalfd, bool was_triggered)
278 {
279 if (was_triggered) {
280 /*
281 * When there are other signals pending we can keep the kq
282 * readable and therefore don't need to clear it.
283 */
284 bool has_pending;
285 if (signalfd_has_pending(signalfd, &has_pending, NULL) != 0 ||
286 has_pending) {
287 return true;
288 }
289 }
290
291 /*
292 * Clear the kq. Signals can arrive here, leading to a race.
293 */
294
295 kqueue_event_clear(&signalfd->kqueue_event, signalfd->kq);
296
297 /*
298 * Because of the race, we must recheck and manually trigger if
299 * necessary.
300 */
301 bool has_pending;
302 if (signalfd_has_pending(signalfd, &has_pending, NULL) != 0 ||
303 has_pending) {
304 (void)signalfd_ctx_trigger_manually(signalfd);
305 return true;
306 }
307 return false;
308 }
309
310 errno_t
signalfd_ctx_read(SignalFDCtx * signalfd,SignalFDCtxSiginfo * siginfo)311 signalfd_ctx_read(SignalFDCtx *signalfd, SignalFDCtxSiginfo *siginfo)
312 {
313 errno_t ec;
314
315 ec = signalfd_ctx_read_impl(signalfd, siginfo);
316 if (ec == 0 || ec == EAGAIN || ec == EWOULDBLOCK) {
317 (void)signalfd_ctx_clear_signal(signalfd, false);
318 }
319
320 return ec;
321 }
322
323 void
signalfd_ctx_poll(SignalFDCtx * signalfd,uint32_t * revents)324 signalfd_ctx_poll(SignalFDCtx *signalfd, uint32_t *revents)
325 {
326 bool pending = signalfd_ctx_clear_signal(signalfd, revents != NULL);
327 if (revents) {
328 *revents = pending ? POLLIN : 0;
329 }
330 }
331