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