xref: /netbsd-src/external/bsd/ntp/dist/sntp/libevent/signal.c (revision f8cf1a9151c7af1cb0bd8b09c13c66bca599c027)
1 /*	$NetBSD: signal.c,v 1.6 2024/08/18 20:47:21 christos Exp $	*/
2 
3 /*	$OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $	*/
4 
5 /*
6  * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu>
7  * Copyright 2007-2012 Niels Provos and Nick Mathewson
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 #include "event2/event-config.h"
32 #include "evconfig-private.h"
33 
34 #ifdef _WIN32
35 #define WIN32_LEAN_AND_MEAN
36 #include <winsock2.h>
37 #include <windows.h>
38 #undef WIN32_LEAN_AND_MEAN
39 #endif
40 #include <sys/types.h>
41 #ifdef EVENT__HAVE_SYS_TIME_H
42 #include <sys/time.h>
43 #endif
44 #include <sys/queue.h>
45 #ifdef EVENT__HAVE_SYS_SOCKET_H
46 #include <sys/socket.h>
47 #endif
48 #include <signal.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #ifdef EVENT__HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55 #include <errno.h>
56 #ifdef EVENT__HAVE_FCNTL_H
57 #include <fcntl.h>
58 #endif
59 
60 #include "event2/event.h"
61 #include "event2/event_struct.h"
62 #include "event-internal.h"
63 #include "event2/util.h"
64 #include "evsignal-internal.h"
65 #include "log-internal.h"
66 #include "evmap-internal.h"
67 #include "evthread-internal.h"
68 
69 /*
70   signal.c
71 
72   This is the signal-handling implementation we use for backends that don't
73   have a better way to do signal handling.  It uses sigaction() or signal()
74   to set a signal handler, and a socket pair to tell the event base when
75 
76   Note that I said "the event base" : only one event base can be set up to use
77   this at a time.  For historical reasons and backward compatibility, if you
78   add an event for a signal to event_base A, then add an event for a signal
79   (any signal!) to event_base B, event_base B will get informed about the
80   signal, but event_base A won't.
81 
82   It would be neat to change this behavior in some future version of Libevent.
83   kqueue already does something far more sensible.  We can make all backends
84   on Linux do a reasonable thing using signalfd.
85 */
86 
87 #ifndef _WIN32
88 /* Windows wants us to call our signal handlers as __cdecl.  Nobody else
89  * expects you to do anything crazy like this. */
90 #ifndef __cdecl
91 #define __cdecl
92 #endif
93 #endif
94 
95 static int evsig_add(struct event_base *, evutil_socket_t, short, short, void *);
96 static int evsig_del(struct event_base *, evutil_socket_t, short, short, void *);
97 
98 static const struct eventop evsigops = {
99 	"signal",
100 	NULL,
101 	evsig_add,
102 	evsig_del,
103 	NULL,
104 	NULL,
105 	0, 0, 0
106 };
107 
108 #ifndef EVENT__DISABLE_THREAD_SUPPORT
109 /* Lock for evsig_base and evsig_base_n_signals_added fields. */
110 static void *evsig_base_lock = NULL;
111 #endif
112 /* The event base that's currently getting informed about signals. */
113 static struct event_base *evsig_base = NULL;
114 /* A copy of evsig_base->sigev_n_signals_added. */
115 static int evsig_base_n_signals_added = 0;
116 static evutil_socket_t evsig_base_fd = -1;
117 
118 static void __cdecl evsig_handler(int sig);
119 
120 #define EVSIGBASE_LOCK() EVLOCK_LOCK(evsig_base_lock, 0)
121 #define EVSIGBASE_UNLOCK() EVLOCK_UNLOCK(evsig_base_lock, 0)
122 
123 void
124 evsig_set_base_(struct event_base *base)
125 {
126 	EVSIGBASE_LOCK();
127 	evsig_base = base;
128 	evsig_base_n_signals_added = base->sig.ev_n_signals_added;
129 	evsig_base_fd = base->sig.ev_signal_pair[1];
130 	EVSIGBASE_UNLOCK();
131 }
132 
133 /* Callback for when the signal handler write a byte to our signaling socket */
134 static void
135 evsig_cb(evutil_socket_t fd, short what, void *arg)
136 {
137 	static char signals[1024];
138 	ev_ssize_t n;
139 	int i;
140 	int ncaught[NSIG];
141 	struct event_base *base;
142 
143 	base = arg;
144 
145 	memset(&ncaught, 0, sizeof(ncaught));
146 
147 	while (1) {
148 #ifdef _WIN32
149 		n = recv(fd, signals, sizeof(signals), 0);
150 #else
151 		n = read(fd, signals, sizeof(signals));
152 #endif
153 		if (n == -1) {
154 			int err = evutil_socket_geterror(fd);
155 			if (! EVUTIL_ERR_RW_RETRIABLE(err))
156 				event_sock_err(1, fd, "%s: recv", __func__);
157 			break;
158 		} else if (n == 0) {
159 			/* XXX warn? */
160 			break;
161 		}
162 		for (i = 0; i < n; ++i) {
163 			ev_uint8_t sig = signals[i];
164 			if (sig < NSIG)
165 				ncaught[sig]++;
166 		}
167 	}
168 
169 	EVBASE_ACQUIRE_LOCK(base, th_base_lock);
170 	for (i = 0; i < NSIG; ++i) {
171 		if (ncaught[i])
172 			evmap_signal_active_(base, i, ncaught[i]);
173 	}
174 	EVBASE_RELEASE_LOCK(base, th_base_lock);
175 }
176 
177 int
178 evsig_init_(struct event_base *base)
179 {
180 	/*
181 	 * Our signal handler is going to write to one end of the socket
182 	 * pair to wake up our event loop.  The event loop then scans for
183 	 * signals that got delivered.
184 	 */
185 	if (evutil_make_internal_pipe_(base->sig.ev_signal_pair) == -1) {
186 #ifdef _WIN32
187 		/* Make this nonfatal on win32, where sometimes people
188 		   have localhost firewalled. */
189 		event_sock_warn(-1, "%s: socketpair", __func__);
190 #else
191 		event_sock_err(1, -1, "%s: socketpair", __func__);
192 #endif
193 		return -1;
194 	}
195 
196 	if (base->sig.sh_old) {
197 		mm_free(base->sig.sh_old);
198 	}
199 	base->sig.sh_old = NULL;
200 	base->sig.sh_old_max = 0;
201 
202 	event_assign(&base->sig.ev_signal, base, base->sig.ev_signal_pair[0],
203 		EV_READ | EV_PERSIST, evsig_cb, base);
204 
205 	base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
206 	event_priority_set(&base->sig.ev_signal, 0);
207 
208 	base->evsigsel = &evsigops;
209 
210 	return 0;
211 }
212 
213 /* Helper: set the signal handler for evsignal to handler in base, so that
214  * we can restore the original handler when we clear the current one. */
215 int
216 evsig_set_handler_(struct event_base *base,
217     int evsignal, void (__cdecl *handler)(int))
218 {
219 #ifdef EVENT__HAVE_SIGACTION
220 	struct sigaction sa;
221 #else
222 	ev_sighandler_t sh;
223 #endif
224 	struct evsig_info *sig = &base->sig;
225 	void *p;
226 
227 	/*
228 	 * resize saved signal handler array up to the highest signal number.
229 	 * a dynamic array is used to keep footprint on the low side.
230 	 */
231 	if (evsignal >= sig->sh_old_max) {
232 		int new_max = evsignal + 1;
233 		event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing",
234 			    __func__, evsignal, sig->sh_old_max));
235 		p = mm_realloc(sig->sh_old, new_max * sizeof(*sig->sh_old));
236 		if (p == NULL) {
237 			event_warn("realloc");
238 			return (-1);
239 		}
240 
241 		memset((char *)p + sig->sh_old_max * sizeof(*sig->sh_old),
242 		    0, (new_max - sig->sh_old_max) * sizeof(*sig->sh_old));
243 
244 		sig->sh_old_max = new_max;
245 		sig->sh_old = p;
246 	}
247 
248 	/* allocate space for previous handler out of dynamic array */
249 	sig->sh_old[evsignal] = mm_malloc(sizeof *sig->sh_old[evsignal]);
250 	if (sig->sh_old[evsignal] == NULL) {
251 		event_warn("malloc");
252 		return (-1);
253 	}
254 
255 	/* save previous handler and setup new handler */
256 #ifdef EVENT__HAVE_SIGACTION
257 	memset(&sa, 0, sizeof(sa));
258 	sa.sa_handler = handler;
259 	sa.sa_flags |= SA_RESTART;
260 	sigfillset(&sa.sa_mask);
261 
262 	if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) {
263 		event_warn("sigaction");
264 		mm_free(sig->sh_old[evsignal]);
265 		sig->sh_old[evsignal] = NULL;
266 		return (-1);
267 	}
268 #else
269 	if ((sh = signal(evsignal, handler)) == SIG_ERR) {
270 		event_warn("signal");
271 		mm_free(sig->sh_old[evsignal]);
272 		sig->sh_old[evsignal] = NULL;
273 		return (-1);
274 	}
275 	*sig->sh_old[evsignal] = sh;
276 #endif
277 
278 	return (0);
279 }
280 
281 static int
282 evsig_add(struct event_base *base, evutil_socket_t evsignal, short old, short events, void *p)
283 {
284 	struct evsig_info *sig = &base->sig;
285 	(void)p;
286 
287 	EVUTIL_ASSERT(evsignal >= 0 && evsignal < NSIG);
288 
289 	/* catch signals if they happen quickly */
290 	EVSIGBASE_LOCK();
291 	if (evsig_base != base && evsig_base_n_signals_added) {
292 		event_warnx("Added a signal to event base %p with signals "
293 		    "already added to event_base %p.  Only one can have "
294 		    "signals at a time with the %s backend.  The base with "
295 		    "the most recently added signal or the most recent "
296 		    "event_base_loop() call gets preference; do "
297 		    "not rely on this behavior in future Libevent versions.",
298 		    base, evsig_base, base->evsel->name);
299 	}
300 	evsig_base = base;
301 	evsig_base_n_signals_added = ++sig->ev_n_signals_added;
302 	evsig_base_fd = base->sig.ev_signal_pair[1];
303 	EVSIGBASE_UNLOCK();
304 
305 	event_debug(("%s: %d: changing signal handler", __func__, (int)evsignal));
306 	if (evsig_set_handler_(base, (int)evsignal, evsig_handler) == -1) {
307 		goto err;
308 	}
309 
310 
311 	if (!sig->ev_signal_added) {
312 		if (event_add_nolock_(&sig->ev_signal, NULL, 0))
313 			goto err;
314 		sig->ev_signal_added = 1;
315 	}
316 
317 	return (0);
318 
319 err:
320 	EVSIGBASE_LOCK();
321 	--evsig_base_n_signals_added;
322 	--sig->ev_n_signals_added;
323 	EVSIGBASE_UNLOCK();
324 	return (-1);
325 }
326 
327 int
328 evsig_restore_handler_(struct event_base *base, int evsignal)
329 {
330 	int ret = 0;
331 	struct evsig_info *sig = &base->sig;
332 #ifdef EVENT__HAVE_SIGACTION
333 	struct sigaction *sh;
334 #else
335 	ev_sighandler_t *sh;
336 #endif
337 
338 	if (evsignal >= sig->sh_old_max) {
339 		/* Can't actually restore. */
340 		/* XXXX.*/
341 		return 0;
342 	}
343 
344 	/* restore previous handler */
345 	sh = sig->sh_old[evsignal];
346 	sig->sh_old[evsignal] = NULL;
347 #ifdef EVENT__HAVE_SIGACTION
348 	if (sigaction(evsignal, sh, NULL) == -1) {
349 		event_warn("sigaction");
350 		ret = -1;
351 	}
352 #else
353 	if (signal(evsignal, *sh) == SIG_ERR) {
354 		event_warn("signal");
355 		ret = -1;
356 	}
357 #endif
358 
359 	mm_free(sh);
360 
361 	return ret;
362 }
363 
364 static int
365 evsig_del(struct event_base *base, evutil_socket_t evsignal, short old, short events, void *p)
366 {
367 	EVUTIL_ASSERT(evsignal >= 0 && evsignal < NSIG);
368 
369 	event_debug(("%s: "EV_SOCK_FMT": restoring signal handler",
370 		__func__, EV_SOCK_ARG(evsignal)));
371 
372 	EVSIGBASE_LOCK();
373 	--evsig_base_n_signals_added;
374 	--base->sig.ev_n_signals_added;
375 	EVSIGBASE_UNLOCK();
376 
377 	return (evsig_restore_handler_(base, (int)evsignal));
378 }
379 
380 static void __cdecl
381 evsig_handler(int sig)
382 {
383 	int save_errno = errno;
384 #ifdef _WIN32
385 	int socket_errno = EVUTIL_SOCKET_ERROR();
386 #endif
387 	ev_uint8_t msg;
388 
389 	if (evsig_base == NULL) {
390 		event_warnx(
391 			"%s: received signal %d, but have no base configured",
392 			__func__, sig);
393 		return;
394 	}
395 
396 #ifndef EVENT__HAVE_SIGACTION
397 	signal(sig, evsig_handler);
398 #endif
399 
400 	/* Wake up our notification mechanism */
401 	msg = sig;
402 #ifdef _WIN32
403 	send(evsig_base_fd, (char*)&msg, 1, 0);
404 #else
405 	{
406 		int r = write(evsig_base_fd, (char*)&msg, 1);
407 		(void)r; /* Suppress 'unused return value' and 'unused var' */
408 	}
409 #endif
410 	errno = save_errno;
411 #ifdef _WIN32
412 	EVUTIL_SET_SOCKET_ERROR(socket_errno);
413 #endif
414 }
415 
416 void
417 evsig_dealloc_(struct event_base *base)
418 {
419 	int i = 0;
420 	if (base->sig.ev_signal_added) {
421 		event_del(&base->sig.ev_signal);
422 		base->sig.ev_signal_added = 0;
423 	}
424 	/* debug event is created in evsig_init_/event_assign even when
425 	 * ev_signal_added == 0, so unassign is required */
426 	event_debug_unassign(&base->sig.ev_signal);
427 
428 	for (i = 0; i < NSIG; ++i) {
429 		if (i < base->sig.sh_old_max && base->sig.sh_old[i] != NULL)
430 			evsig_restore_handler_(base, i);
431 	}
432 	EVSIGBASE_LOCK();
433 	if (base == evsig_base) {
434 		evsig_base = NULL;
435 		evsig_base_n_signals_added = 0;
436 		evsig_base_fd = -1;
437 	}
438 	EVSIGBASE_UNLOCK();
439 
440 	if (base->sig.ev_signal_pair[0] != -1) {
441 		evutil_closesocket(base->sig.ev_signal_pair[0]);
442 		base->sig.ev_signal_pair[0] = -1;
443 	}
444 	if (base->sig.ev_signal_pair[1] != -1) {
445 		evutil_closesocket(base->sig.ev_signal_pair[1]);
446 		base->sig.ev_signal_pair[1] = -1;
447 	}
448 	base->sig.sh_old_max = 0;
449 
450 	/* per index frees are handled in evsig_del() */
451 	if (base->sig.sh_old) {
452 		mm_free(base->sig.sh_old);
453 		base->sig.sh_old = NULL;
454 	}
455 }
456 
457 static void
458 evsig_free_globals_locks(void)
459 {
460 #ifndef EVENT__DISABLE_THREAD_SUPPORT
461 	if (evsig_base_lock != NULL) {
462 		EVTHREAD_FREE_LOCK(evsig_base_lock, 0);
463 		evsig_base_lock = NULL;
464 	}
465 #endif
466 	return;
467 }
468 
469 void
470 evsig_free_globals_(void)
471 {
472 	evsig_free_globals_locks();
473 }
474 
475 #ifndef EVENT__DISABLE_THREAD_SUPPORT
476 int
477 evsig_global_setup_locks_(const int enable_locks)
478 {
479 	EVTHREAD_SETUP_GLOBAL_LOCK(evsig_base_lock, 0);
480 	return 0;
481 }
482 
483 #endif
484