xref: /openbsd-src/lib/libevent/signal.c (revision 666046da478bf52cf6fe923638cbbeb27f3ad85d)
1*666046daSbluhm /*	$OpenBSD: signal.c,v 1.25 2015/01/06 11:42:37 bluhm Exp $	*/
21770acb2Smarkus 
31770acb2Smarkus /*
41770acb2Smarkus  * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
51770acb2Smarkus  * All rights reserved.
61770acb2Smarkus  *
71770acb2Smarkus  * Redistribution and use in source and binary forms, with or without
81770acb2Smarkus  * modification, are permitted provided that the following conditions
91770acb2Smarkus  * are met:
101770acb2Smarkus  * 1. Redistributions of source code must retain the above copyright
111770acb2Smarkus  *    notice, this list of conditions and the following disclaimer.
121770acb2Smarkus  * 2. Redistributions in binary form must reproduce the above copyright
131770acb2Smarkus  *    notice, this list of conditions and the following disclaimer in the
141770acb2Smarkus  *    documentation and/or other materials provided with the distribution.
15ff9272daSbrad  * 3. The name of the author may not be used to endorse or promote products
161770acb2Smarkus  *    derived from this software without specific prior written permission.
171770acb2Smarkus  *
181770acb2Smarkus  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
191770acb2Smarkus  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
201770acb2Smarkus  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
211770acb2Smarkus  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
221770acb2Smarkus  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
231770acb2Smarkus  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
241770acb2Smarkus  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
251770acb2Smarkus  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
261770acb2Smarkus  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
271770acb2Smarkus  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
281770acb2Smarkus  */
291770acb2Smarkus 
301770acb2Smarkus #include <sys/types.h>
311770acb2Smarkus #include <sys/time.h>
321770acb2Smarkus #include <sys/queue.h>
334643be29Sbrad #include <sys/socket.h>
34defc4074Sbluhm 
351770acb2Smarkus #include <signal.h>
361770acb2Smarkus #include <stdio.h>
371770acb2Smarkus #include <stdlib.h>
381770acb2Smarkus #include <string.h>
391770acb2Smarkus #include <unistd.h>
401770acb2Smarkus #include <errno.h>
414643be29Sbrad #include <fcntl.h>
42bdce580dSbrad #include <assert.h>
431770acb2Smarkus 
441770acb2Smarkus #include "event.h"
45bdce580dSbrad #include "event-internal.h"
46ff9272daSbrad #include "evsignal.h"
474643be29Sbrad #include "log.h"
481770acb2Smarkus 
49bdce580dSbrad struct event_base *evsignal_base = NULL;
501770acb2Smarkus 
51bdce580dSbrad static void evsignal_handler(int sig);
524643be29Sbrad 
534643be29Sbrad /* Callback for when the signal handler write a byte to our signaling socket */
54a80bbcd3Sderaadt static void
evsignal_cb(int fd,short what,void * arg)55a80bbcd3Sderaadt evsignal_cb(int fd, short what, void *arg)
564643be29Sbrad {
578ead113eSnicm 	static char signals[1];
58a80bbcd3Sderaadt 	ssize_t n;
594643be29Sbrad 
608ead113eSnicm 	n = recv(fd, signals, sizeof(signals), 0);
61*666046daSbluhm 	if (n == -1) {
62*666046daSbluhm 		if (errno != EAGAIN)
634643be29Sbrad 			event_err(1, "%s: read", __func__);
644643be29Sbrad 	}
65*666046daSbluhm }
664643be29Sbrad 
678ead113eSnicm int
evsignal_init(struct event_base * base)68bdce580dSbrad evsignal_init(struct event_base *base)
691770acb2Smarkus {
70*666046daSbluhm 	int i;
718ead113eSnicm 
724643be29Sbrad 	/*
734643be29Sbrad 	 * Our signal handler is going to write to one end of the socket
744643be29Sbrad 	 * pair to wake up our event loop.  The event loop then scans for
754643be29Sbrad 	 * signals that got delivered.
764643be29Sbrad 	 */
77*666046daSbluhm 	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
78*666046daSbluhm 	    0, base->sig.ev_signal_pair) == -1) {
794643be29Sbrad 		event_err(1, "%s: socketpair", __func__);
808ead113eSnicm 		return -1;
818ead113eSnicm 	}
824643be29Sbrad 
838ead113eSnicm 	base->sig.sh_old = NULL;
848ead113eSnicm 	base->sig.sh_old_max = 0;
85bdce580dSbrad 	base->sig.evsignal_caught = 0;
86bdce580dSbrad 	memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG);
878ead113eSnicm 	/* initialize the queues for all events */
888ead113eSnicm 	for (i = 0; i < NSIG; ++i)
898ead113eSnicm 		TAILQ_INIT(&base->sig.evsigevents[i]);
904643be29Sbrad 
918ead113eSnicm 	event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1],
928ead113eSnicm 		EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal);
93bdce580dSbrad 	base->sig.ev_signal.ev_base = base;
94bdce580dSbrad 	base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
958ead113eSnicm 
968ead113eSnicm 	return 0;
978ead113eSnicm }
988ead113eSnicm 
998ead113eSnicm /* Helper: set the signal handler for evsignal to handler in base, so that
1008ead113eSnicm  * we can restore the original handler when we clear the current one. */
1018ead113eSnicm int
_evsignal_set_handler(struct event_base * base,int evsignal,void (* handler)(int))1028ead113eSnicm _evsignal_set_handler(struct event_base *base,
1038ead113eSnicm 		      int evsignal, void (*handler)(int))
1048ead113eSnicm {
1058ead113eSnicm 	struct sigaction sa;
1068ead113eSnicm 	struct evsignal_info *sig = &base->sig;
1078ead113eSnicm 	void *p;
1088ead113eSnicm 
1098ead113eSnicm 	/*
1108ead113eSnicm 	 * resize saved signal handler array up to the highest signal number.
1118ead113eSnicm 	 * a dynamic array is used to keep footprint on the low side.
1128ead113eSnicm 	 */
1138ead113eSnicm 	if (evsignal >= sig->sh_old_max) {
1148ead113eSnicm 		int new_max = evsignal + 1;
1158ead113eSnicm 		event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing",
1168ead113eSnicm 			    __func__, evsignal, sig->sh_old_max));
1172af34b00Sderaadt 		p = reallocarray(sig->sh_old, new_max, sizeof(*sig->sh_old));
1188ead113eSnicm 		if (p == NULL) {
1198ead113eSnicm 			event_warn("realloc");
1208ead113eSnicm 			return (-1);
1218ead113eSnicm 		}
1228ead113eSnicm 
1238ead113eSnicm 		memset((char *)p + sig->sh_old_max * sizeof(*sig->sh_old),
1248ead113eSnicm 		    0, (new_max - sig->sh_old_max) * sizeof(*sig->sh_old));
1258ead113eSnicm 
1268ead113eSnicm 		sig->sh_old_max = new_max;
1278ead113eSnicm 		sig->sh_old = p;
1288ead113eSnicm 	}
1298ead113eSnicm 
1308ead113eSnicm 	/* allocate space for previous handler out of dynamic array */
1318ead113eSnicm 	sig->sh_old[evsignal] = malloc(sizeof *sig->sh_old[evsignal]);
1328ead113eSnicm 	if (sig->sh_old[evsignal] == NULL) {
1338ead113eSnicm 		event_warn("malloc");
1348ead113eSnicm 		return (-1);
1358ead113eSnicm 	}
1368ead113eSnicm 
1378ead113eSnicm 	/* save previous handler and setup new handler */
1388ead113eSnicm 	memset(&sa, 0, sizeof(sa));
1398ead113eSnicm 	sa.sa_handler = handler;
1408ead113eSnicm 	sa.sa_flags |= SA_RESTART;
1418ead113eSnicm 	sigfillset(&sa.sa_mask);
1428ead113eSnicm 
1438ead113eSnicm 	if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) {
1448ead113eSnicm 		event_warn("sigaction");
1458ead113eSnicm 		free(sig->sh_old[evsignal]);
146e175bf56Snicm 		sig->sh_old[evsignal] = NULL;
1478ead113eSnicm 		return (-1);
1488ead113eSnicm 	}
1498ead113eSnicm 
1508ead113eSnicm 	return (0);
1511770acb2Smarkus }
1521770acb2Smarkus 
1531770acb2Smarkus int
evsignal_add(struct event * ev)154bdce580dSbrad evsignal_add(struct event *ev)
1551770acb2Smarkus {
156ff9272daSbrad 	int evsignal;
157bdce580dSbrad 	struct event_base *base = ev->ev_base;
1588ead113eSnicm 	struct evsignal_info *sig = &ev->ev_base->sig;
1591770acb2Smarkus 
1601770acb2Smarkus 	if (ev->ev_events & (EV_READ|EV_WRITE))
1614643be29Sbrad 		event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
162ff9272daSbrad 	evsignal = EVENT_SIGNAL(ev);
1638ead113eSnicm 	assert(evsignal >= 0 && evsignal < NSIG);
1648ead113eSnicm 	if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) {
1658ead113eSnicm 		event_debug(("%s: %p: changing signal handler", __func__, ev));
1668ead113eSnicm 		if (_evsignal_set_handler(
1678ead113eSnicm 			    base, evsignal, evsignal_handler) == -1)
1688ead113eSnicm 			return (-1);
169bdce580dSbrad 
170bdce580dSbrad 		/* catch signals if they happen quickly */
171bdce580dSbrad 		evsignal_base = base;
172bdce580dSbrad 
1738ead113eSnicm 		if (!sig->ev_signal_added) {
1748ead113eSnicm 			if (event_add(&sig->ev_signal, NULL))
175bdce580dSbrad 				return (-1);
1768ead113eSnicm 			sig->ev_signal_added = 1;
177bdce580dSbrad 		}
1788ead113eSnicm 	}
1798ead113eSnicm 
1808ead113eSnicm 	/* multiple events may listen to the same signal */
1818ead113eSnicm 	TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);
1821770acb2Smarkus 
1831770acb2Smarkus 	return (0);
1841770acb2Smarkus }
1851770acb2Smarkus 
1861770acb2Smarkus int
_evsignal_restore_handler(struct event_base * base,int evsignal)1878ead113eSnicm _evsignal_restore_handler(struct event_base *base, int evsignal)
1888ead113eSnicm {
1898ead113eSnicm 	int ret = 0;
1908ead113eSnicm 	struct evsignal_info *sig = &base->sig;
1918ead113eSnicm 	struct sigaction *sh;
1928ead113eSnicm 
1938ead113eSnicm 	/* restore previous handler */
1948ead113eSnicm 	sh = sig->sh_old[evsignal];
1958ead113eSnicm 	sig->sh_old[evsignal] = NULL;
1968ead113eSnicm 	if (sigaction(evsignal, sh, NULL) == -1) {
1978ead113eSnicm 		event_warn("sigaction");
1988ead113eSnicm 		ret = -1;
1998ead113eSnicm 	}
2008ead113eSnicm 	free(sh);
2018ead113eSnicm 
2028ead113eSnicm 	return ret;
2038ead113eSnicm }
2048ead113eSnicm 
2058ead113eSnicm int
evsignal_del(struct event * ev)206bdce580dSbrad evsignal_del(struct event *ev)
2071770acb2Smarkus {
2088ead113eSnicm 	struct event_base *base = ev->ev_base;
2098ead113eSnicm 	struct evsignal_info *sig = &base->sig;
2108ead113eSnicm 	int evsignal = EVENT_SIGNAL(ev);
211bf19d398Sguenther 
2128ead113eSnicm 	assert(evsignal >= 0 && evsignal < NSIG);
2138ead113eSnicm 
2148ead113eSnicm 	/* multiple events may listen to the same signal */
2158ead113eSnicm 	TAILQ_REMOVE(&sig->evsigevents[evsignal], ev, ev_signal_next);
2168ead113eSnicm 
2178ead113eSnicm 	if (!TAILQ_EMPTY(&sig->evsigevents[evsignal]))
2188ead113eSnicm 		return (0);
2198ead113eSnicm 
2208ead113eSnicm 	event_debug(("%s: %p: restoring signal handler", __func__, ev));
2218ead113eSnicm 
2228ead113eSnicm 	return (_evsignal_restore_handler(ev->ev_base, EVENT_SIGNAL(ev)));
2231770acb2Smarkus }
2241770acb2Smarkus 
2251770acb2Smarkus static void
evsignal_handler(int sig)2261770acb2Smarkus evsignal_handler(int sig)
2271770acb2Smarkus {
228a80bbcd3Sderaadt 	int save_errno = errno;
229a80bbcd3Sderaadt 
230bdce580dSbrad 	if (evsignal_base == NULL) {
231bdce580dSbrad 		event_warn(
2328ead113eSnicm 			"%s: received signal %d, but have no base configured",
233bdce580dSbrad 			__func__, sig);
234bdce580dSbrad 		return;
235bdce580dSbrad 	}
236bdce580dSbrad 
237bdce580dSbrad 	evsignal_base->sig.evsigcaught[sig]++;
238bdce580dSbrad 	evsignal_base->sig.evsignal_caught = 1;
2394643be29Sbrad 
2404643be29Sbrad 	/* Wake up our notification mechanism */
2418ead113eSnicm 	send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
242a80bbcd3Sderaadt 	errno = save_errno;
2431770acb2Smarkus }
2441770acb2Smarkus 
2451770acb2Smarkus void
evsignal_process(struct event_base * base)246bdce580dSbrad evsignal_process(struct event_base *base)
2471770acb2Smarkus {
2488ead113eSnicm 	struct evsignal_info *sig = &base->sig;
2498ead113eSnicm 	struct event *ev, *next_ev;
250c54818f7Sderaadt 	sig_atomic_t ncalls;
2518ead113eSnicm 	int i;
2521770acb2Smarkus 
253bdce580dSbrad 	base->sig.evsignal_caught = 0;
2548ead113eSnicm 	for (i = 1; i < NSIG; ++i) {
2558ead113eSnicm 		ncalls = sig->evsigcaught[i];
2568ead113eSnicm 		if (ncalls == 0)
2578ead113eSnicm 			continue;
2588ead113eSnicm 		sig->evsigcaught[i] -= ncalls;
2598ead113eSnicm 
2608ead113eSnicm 		for (ev = TAILQ_FIRST(&sig->evsigevents[i]);
2618ead113eSnicm 		    ev != NULL; ev = next_ev) {
2628ead113eSnicm 			next_ev = TAILQ_NEXT(ev, ev_signal_next);
2631770acb2Smarkus 			if (!(ev->ev_events & EV_PERSIST))
2641770acb2Smarkus 				event_del(ev);
2651770acb2Smarkus 			event_active(ev, EV_SIGNAL, ncalls);
2661770acb2Smarkus 		}
2678ead113eSnicm 
2681770acb2Smarkus 	}
2691770acb2Smarkus }
270bdce580dSbrad 
271bdce580dSbrad void
evsignal_dealloc(struct event_base * base)272bdce580dSbrad evsignal_dealloc(struct event_base *base)
273bdce580dSbrad {
2748ead113eSnicm 	int i = 0;
275bdce580dSbrad 	if (base->sig.ev_signal_added) {
276bdce580dSbrad 		event_del(&base->sig.ev_signal);
277bdce580dSbrad 		base->sig.ev_signal_added = 0;
278bdce580dSbrad 	}
2798ead113eSnicm 	for (i = 0; i < NSIG; ++i) {
2808ead113eSnicm 		if (i < base->sig.sh_old_max && base->sig.sh_old[i] != NULL)
2818ead113eSnicm 			_evsignal_restore_handler(base, i);
2828ead113eSnicm 	}
283bdce580dSbrad 
284e175bf56Snicm 	if (base->sig.ev_signal_pair[0] != -1) {
28584ea67e3Sbluhm 		close(base->sig.ev_signal_pair[0]);
286bdce580dSbrad 		base->sig.ev_signal_pair[0] = -1;
287e175bf56Snicm 	}
288e175bf56Snicm 	if (base->sig.ev_signal_pair[1] != -1) {
28984ea67e3Sbluhm 		close(base->sig.ev_signal_pair[1]);
290bdce580dSbrad 		base->sig.ev_signal_pair[1] = -1;
291e175bf56Snicm 	}
2928ead113eSnicm 	base->sig.sh_old_max = 0;
2938ead113eSnicm 
2948ead113eSnicm 	/* per index frees are handled in evsignal_del() */
295e175bf56Snicm 	if (base->sig.sh_old) {
2968ead113eSnicm 		free(base->sig.sh_old);
297e175bf56Snicm 		base->sig.sh_old = NULL;
298e175bf56Snicm 	}
299bdce580dSbrad }
300