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