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