1 /* $OpenBSD: signal.c,v 1.25 2015/01/06 11:42:37 bluhm Exp $ */ 2 3 /* 4 * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu> 5 * All rights reserved. 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 30 #include <sys/types.h> 31 #include <sys/time.h> 32 #include <sys/queue.h> 33 #include <sys/socket.h> 34 35 #include <signal.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <assert.h> 43 44 #include "event.h" 45 #include "event-internal.h" 46 #include "evsignal.h" 47 #include "log.h" 48 49 struct event_base *evsignal_base = NULL; 50 51 static void evsignal_handler(int sig); 52 53 /* Callback for when the signal handler write a byte to our signaling socket */ 54 static void 55 evsignal_cb(int fd, short what, void *arg) 56 { 57 static char signals[1]; 58 ssize_t n; 59 60 n = recv(fd, signals, sizeof(signals), 0); 61 if (n == -1) { 62 if (errno != EAGAIN) 63 event_err(1, "%s: read", __func__); 64 } 65 } 66 67 int 68 evsignal_init(struct event_base *base) 69 { 70 int i; 71 72 /* 73 * Our signal handler is going to write to one end of the socket 74 * pair to wake up our event loop. The event loop then scans for 75 * signals that got delivered. 76 */ 77 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 78 0, base->sig.ev_signal_pair) == -1) { 79 event_err(1, "%s: socketpair", __func__); 80 return -1; 81 } 82 83 base->sig.sh_old = NULL; 84 base->sig.sh_old_max = 0; 85 base->sig.evsignal_caught = 0; 86 memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG); 87 /* initialize the queues for all events */ 88 for (i = 0; i < NSIG; ++i) 89 TAILQ_INIT(&base->sig.evsigevents[i]); 90 91 event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1], 92 EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal); 93 base->sig.ev_signal.ev_base = base; 94 base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL; 95 96 return 0; 97 } 98 99 /* Helper: set the signal handler for evsignal to handler in base, so that 100 * we can restore the original handler when we clear the current one. */ 101 int 102 _evsignal_set_handler(struct event_base *base, 103 int evsignal, void (*handler)(int)) 104 { 105 struct sigaction sa; 106 struct evsignal_info *sig = &base->sig; 107 void *p; 108 109 /* 110 * resize saved signal handler array up to the highest signal number. 111 * a dynamic array is used to keep footprint on the low side. 112 */ 113 if (evsignal >= sig->sh_old_max) { 114 int new_max = evsignal + 1; 115 event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing", 116 __func__, evsignal, sig->sh_old_max)); 117 p = reallocarray(sig->sh_old, new_max, sizeof(*sig->sh_old)); 118 if (p == NULL) { 119 event_warn("realloc"); 120 return (-1); 121 } 122 123 memset((char *)p + sig->sh_old_max * sizeof(*sig->sh_old), 124 0, (new_max - sig->sh_old_max) * sizeof(*sig->sh_old)); 125 126 sig->sh_old_max = new_max; 127 sig->sh_old = p; 128 } 129 130 /* allocate space for previous handler out of dynamic array */ 131 sig->sh_old[evsignal] = malloc(sizeof *sig->sh_old[evsignal]); 132 if (sig->sh_old[evsignal] == NULL) { 133 event_warn("malloc"); 134 return (-1); 135 } 136 137 /* save previous handler and setup new handler */ 138 memset(&sa, 0, sizeof(sa)); 139 sa.sa_handler = handler; 140 sa.sa_flags |= SA_RESTART; 141 sigfillset(&sa.sa_mask); 142 143 if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) { 144 event_warn("sigaction"); 145 free(sig->sh_old[evsignal]); 146 sig->sh_old[evsignal] = NULL; 147 return (-1); 148 } 149 150 return (0); 151 } 152 153 int 154 evsignal_add(struct event *ev) 155 { 156 int evsignal; 157 struct event_base *base = ev->ev_base; 158 struct evsignal_info *sig = &ev->ev_base->sig; 159 160 if (ev->ev_events & (EV_READ|EV_WRITE)) 161 event_errx(1, "%s: EV_SIGNAL incompatible use", __func__); 162 evsignal = EVENT_SIGNAL(ev); 163 assert(evsignal >= 0 && evsignal < NSIG); 164 if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) { 165 event_debug(("%s: %p: changing signal handler", __func__, ev)); 166 if (_evsignal_set_handler( 167 base, evsignal, evsignal_handler) == -1) 168 return (-1); 169 170 /* catch signals if they happen quickly */ 171 evsignal_base = base; 172 173 if (!sig->ev_signal_added) { 174 if (event_add(&sig->ev_signal, NULL)) 175 return (-1); 176 sig->ev_signal_added = 1; 177 } 178 } 179 180 /* multiple events may listen to the same signal */ 181 TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next); 182 183 return (0); 184 } 185 186 int 187 _evsignal_restore_handler(struct event_base *base, int evsignal) 188 { 189 int ret = 0; 190 struct evsignal_info *sig = &base->sig; 191 struct sigaction *sh; 192 193 /* restore previous handler */ 194 sh = sig->sh_old[evsignal]; 195 sig->sh_old[evsignal] = NULL; 196 if (sigaction(evsignal, sh, NULL) == -1) { 197 event_warn("sigaction"); 198 ret = -1; 199 } 200 free(sh); 201 202 return ret; 203 } 204 205 int 206 evsignal_del(struct event *ev) 207 { 208 struct event_base *base = ev->ev_base; 209 struct evsignal_info *sig = &base->sig; 210 int evsignal = EVENT_SIGNAL(ev); 211 212 assert(evsignal >= 0 && evsignal < NSIG); 213 214 /* multiple events may listen to the same signal */ 215 TAILQ_REMOVE(&sig->evsigevents[evsignal], ev, ev_signal_next); 216 217 if (!TAILQ_EMPTY(&sig->evsigevents[evsignal])) 218 return (0); 219 220 event_debug(("%s: %p: restoring signal handler", __func__, ev)); 221 222 return (_evsignal_restore_handler(ev->ev_base, EVENT_SIGNAL(ev))); 223 } 224 225 static void 226 evsignal_handler(int sig) 227 { 228 int save_errno = errno; 229 230 if (evsignal_base == NULL) { 231 event_warn( 232 "%s: received signal %d, but have no base configured", 233 __func__, sig); 234 return; 235 } 236 237 evsignal_base->sig.evsigcaught[sig]++; 238 evsignal_base->sig.evsignal_caught = 1; 239 240 /* Wake up our notification mechanism */ 241 send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0); 242 errno = save_errno; 243 } 244 245 void 246 evsignal_process(struct event_base *base) 247 { 248 struct evsignal_info *sig = &base->sig; 249 struct event *ev, *next_ev; 250 sig_atomic_t ncalls; 251 int i; 252 253 base->sig.evsignal_caught = 0; 254 for (i = 1; i < NSIG; ++i) { 255 ncalls = sig->evsigcaught[i]; 256 if (ncalls == 0) 257 continue; 258 sig->evsigcaught[i] -= ncalls; 259 260 for (ev = TAILQ_FIRST(&sig->evsigevents[i]); 261 ev != NULL; ev = next_ev) { 262 next_ev = TAILQ_NEXT(ev, ev_signal_next); 263 if (!(ev->ev_events & EV_PERSIST)) 264 event_del(ev); 265 event_active(ev, EV_SIGNAL, ncalls); 266 } 267 268 } 269 } 270 271 void 272 evsignal_dealloc(struct event_base *base) 273 { 274 int i = 0; 275 if (base->sig.ev_signal_added) { 276 event_del(&base->sig.ev_signal); 277 base->sig.ev_signal_added = 0; 278 } 279 for (i = 0; i < NSIG; ++i) { 280 if (i < base->sig.sh_old_max && base->sig.sh_old[i] != NULL) 281 _evsignal_restore_handler(base, i); 282 } 283 284 if (base->sig.ev_signal_pair[0] != -1) { 285 close(base->sig.ev_signal_pair[0]); 286 base->sig.ev_signal_pair[0] = -1; 287 } 288 if (base->sig.ev_signal_pair[1] != -1) { 289 close(base->sig.ev_signal_pair[1]); 290 base->sig.ev_signal_pair[1] = -1; 291 } 292 base->sig.sh_old_max = 0; 293 294 /* per index frees are handled in evsignal_del() */ 295 if (base->sig.sh_old) { 296 free(base->sig.sh_old); 297 base->sig.sh_old = NULL; 298 } 299 } 300