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
93static int evsig_add(struct event_base *, evutil_socket_t, short, short, void *);
94static int evsig_del(struct event_base *, evutil_socket_t, short, short, void *);
95
96static 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. */
108static void *evsig_base_lock = NULL;
109#endif
110/* The event base that's currently getting informed about signals. */
111static struct event_base *evsig_base = NULL;
112/* A copy of evsig_base->sigev_n_signals_added. */
113static int evsig_base_n_signals_added = 0;
114static evutil_socket_t evsig_base_fd = -1;
115
116static 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
121void
122evsig_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 */
132static void
133evsig_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
175int
176evsig_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. */
213int
214evsig_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
279static int
280evsig_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
317err:
318	EVSIGBASE_LOCK();
319	--evsig_base_n_signals_added;
320	--sig->ev_n_signals_added;
321	EVSIGBASE_UNLOCK();
322	return (-1);
323}
324
325int
326evsig_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
362static int
363evsig_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
378static void __cdecl
379evsig_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
414void
415evsig_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
455static void
456evsig_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
467void
468evsig_free_globals_(void)
469{
470	evsig_free_globals_locks();
471}
472
473#ifndef EVENT__DISABLE_THREAD_SUPPORT
474int
475evsig_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