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