xref: /PHP-5.4/Zend/zend_signal.c (revision 6cfbcb22)
1 /*
2   +----------------------------------------------------------------------+
3   | Zend Signal Handling                                                 |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2008 The PHP Group                                     |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Lucas Nealan <lucas@php.net>                                |
16   |          Arnaud Le Blanc <lbarnaud@php.net>                          |
17   +----------------------------------------------------------------------+
18 
19    This software was contributed to PHP by Facebook Inc. in 2008.
20 
21    Future revisions and derivatives of this source code must acknowledge
22    Facebook Inc. as the original contributor of this module by leaving
23    this note intact in the source code.
24 
25    All other licensing and usage conditions are those of the PHP Group.
26 */
27 
28  /* $Id$ */
29 
30 #define _GNU_SOURCE
31 #include <string.h>
32 
33 #include "zend.h"
34 #include "zend_globals.h"
35 
36 #ifdef HAVE_SIGNAL_H
37 #include <signal.h>
38 #endif
39 
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 
44 #ifdef ZEND_SIGNALS
45 
46 #include "zend_signal.h"
47 
48 #ifdef ZTS
49 ZEND_API int zend_signal_globals_id;
50 #else
51 zend_signal_globals_t zend_signal_globals;
52 #endif
53 
54 static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context TSRMLS_DC);
55 static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*) TSRMLS_DC);
56 
57 #ifdef __CYGWIN__
58 #define TIMEOUT_SIG SIGALRM
59 #else
60 #define TIMEOUT_SIG SIGPROF
61 #endif
62 
63 static int zend_sigs[] = { TIMEOUT_SIG, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2 };
64 
65 #define SA_FLAGS_MASK ~(SA_NODEFER | SA_RESETHAND)
66 
67 /* True globals, written only at process startup */
68 static zend_signal_entry_t global_orig_handlers[NSIG];
69 static sigset_t            global_sigmask;
70 
71 /* {{{ zend_signal_handler_defer
72  *  Blocks signals if in critical section */
zend_signal_handler_defer(int signo,siginfo_t * siginfo,void * context)73 void zend_signal_handler_defer(int signo, siginfo_t *siginfo, void *context)
74 {
75 	int errno_save = errno;
76 	zend_signal_queue_t *queue, *qtmp;
77 	TSRMLS_FETCH();
78 
79 	if (SIGG(active)) {
80 		if (SIGG(depth) == 0) { /* try to handle signal */
81 			if (SIGG(blocked) != -1) { /* inverse */
82 				SIGG(blocked) = -1; /* signal is not blocked */
83 			}
84 			if (SIGG(running) == 0) {
85 				SIGG(running) = 1;
86 				zend_signal_handler(signo, siginfo, context TSRMLS_CC);
87 
88 				queue = SIGG(phead);
89 				SIGG(phead) = NULL;
90 
91 				while (queue) {
92 					zend_signal_handler(queue->zend_signal.signo, queue->zend_signal.siginfo, queue->zend_signal.context TSRMLS_CC);
93 					qtmp = queue->next;
94 					queue->next = SIGG(pavail);
95 					queue->zend_signal.signo = 0;
96 					SIGG(pavail) = queue;
97 					queue = qtmp;
98 				}
99 				SIGG(running) = 0;
100 			}
101 		} else { /* delay signal handling */
102 			SIGG(blocked) = 0; /* signal is blocked */
103 
104 			if ((queue = SIGG(pavail))) { /* if none available it's simply forgotton */
105 				SIGG(pavail) = queue->next;
106 				queue->zend_signal.signo = signo;
107 				queue->zend_signal.siginfo = siginfo;
108 				queue->zend_signal.context = context;
109 				queue->next = NULL;
110 
111 				if (SIGG(phead) && SIGG(ptail)) {
112 					SIGG(ptail)->next = queue;
113 				} else {
114 					SIGG(phead) = queue;
115 				}
116 				SIGG(ptail) = queue;
117 			}
118 #if ZEND_DEBUG
119 			else { /* this may not be safe to do, but could work and be useful */
120 				zend_output_debug_string(0, "zend_signal: not enough queue storage, lost signal (%d)", signo);
121 			}
122 #endif
123 		}
124 	} else {
125 		/* need to just run handler if we're inactive and getting a signal */
126 		zend_signal_handler(signo, siginfo, context TSRMLS_CC);
127 	}
128 
129 	errno = errno_save;
130 } /* }}} */
131 
132 /* {{{ zend_signal_handler_unblock
133  * Handle deferred signal from HANDLE_UNBLOCK_ALARMS */
zend_signal_handler_unblock(TSRMLS_D)134 ZEND_API void zend_signal_handler_unblock(TSRMLS_D)
135 {
136 	zend_signal_queue_t *queue;
137 	zend_signal_t zend_signal;
138 
139 	if (SIGG(active)) {
140 		SIGNAL_BEGIN_CRITICAL(); /* procmask to protect handler_defer as if it were called by the kernel */
141 		queue = SIGG(phead);
142 		SIGG(phead) = queue->next;
143 		zend_signal = queue->zend_signal;
144 		queue->next = SIGG(pavail);
145 		queue->zend_signal.signo = 0;
146 		SIGG(pavail) = queue;
147 
148 		zend_signal_handler_defer(zend_signal.signo, zend_signal.siginfo, zend_signal.context);
149 		SIGNAL_END_CRITICAL();
150 	}
151 }
152 /* }}} */
153 
154 /* {{{ zend_signal_handler
155  *  Call the previously registered handler for a signal
156  */
zend_signal_handler(int signo,siginfo_t * siginfo,void * context TSRMLS_DC)157 static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context TSRMLS_DC)
158 {
159 	int errno_save = errno;
160 	struct sigaction sa = {{0}};
161 	sigset_t sigset;
162 	zend_signal_entry_t p_sig = SIGG(handlers)[signo-1];
163 
164 	if (p_sig.handler == SIG_DFL) { /* raise default handler */
165 		if (sigaction(signo, NULL, &sa) == 0) {
166 			sa.sa_handler = SIG_DFL;
167 			sigemptyset(&sa.sa_mask);
168 
169 			sigemptyset(&sigset);
170 			sigaddset(&sigset, signo);
171 
172 			if (sigaction(signo, &sa, NULL) == 0) {
173 				/* throw away any blocked signals */
174 				sigprocmask(SIG_UNBLOCK, &sigset, NULL);
175 				raise(signo);
176 			}
177 		}
178 	} else if (p_sig.handler != SIG_IGN) { /* ignore SIG_IGN */
179 		if (p_sig.flags & SA_SIGINFO) {
180 			if (p_sig.flags & SA_RESETHAND) {
181 				SIGG(handlers)[signo-1].flags   = 0;
182 				SIGG(handlers)[signo-1].handler = SIG_DFL;
183 			}
184 			(*(void (*)(int, siginfo_t*, void*))p_sig.handler)(signo, siginfo, context);
185 		} else {
186 			(*(void (*)(int))p_sig.handler)(signo);
187 		}
188 	}
189 
190 	errno = errno_save;
191 } /* }}} */
192 
193 /* {{{ zend_sigaction
194  *  Register a signal handler that will be deferred in critical sections */
zend_sigaction(int signo,const struct sigaction * act,struct sigaction * oldact TSRMLS_DC)195 ZEND_API int zend_sigaction(int signo, const struct sigaction *act, struct sigaction *oldact TSRMLS_DC)
196 {
197 	struct sigaction sa = {{0}};
198 	sigset_t sigset;
199 
200 	if (oldact != NULL) {
201 		oldact->sa_flags   = SIGG(handlers)[signo-1].flags;
202 		oldact->sa_handler = (void *) SIGG(handlers)[signo-1].handler;
203 		oldact->sa_mask    = global_sigmask;
204 	}
205 	if (act != NULL) {
206 		SIGG(handlers)[signo-1].flags = act->sa_flags;
207 		if (act->sa_flags & SA_SIGINFO) {
208 			SIGG(handlers)[signo-1].handler = (void *) act->sa_sigaction;
209 		} else {
210 			SIGG(handlers)[signo-1].handler = (void *) act->sa_handler;
211 		}
212 
213 		sa.sa_flags     = SA_SIGINFO | (act->sa_flags & SA_FLAGS_MASK);
214 		sa.sa_sigaction = zend_signal_handler_defer;
215 		sa.sa_mask      = global_sigmask;
216 
217 		if (sigaction(signo, &sa, NULL) < 0) {
218 			zend_error(E_WARNING, "Error installing signal handler for %d", signo);
219 		}
220 
221 		/* unsure this signal is not blocked */
222 		sigemptyset(&sigset);
223 		sigaddset(&sigset, signo);
224 		zend_sigprocmask(SIG_UNBLOCK, &sigset, NULL);
225 	}
226 
227 	return SUCCESS;
228 }
229 /* }}} */
230 
231 /* {{{ zend_signal
232  *  Register a signal handler that will be deferred in critical sections */
zend_signal(int signo,void (* handler)(int)TSRMLS_DC)233 ZEND_API int zend_signal(int signo, void (*handler)(int) TSRMLS_DC)
234 {
235 	struct sigaction sa = {{0}};
236 
237 	sa.sa_flags   = 0;
238 	sa.sa_handler = handler;
239 	sa.sa_mask    = global_sigmask;
240 
241 	return zend_sigaction(signo, &sa, NULL TSRMLS_CC);
242 }
243 /* }}} */
244 
245 /* {{{ zend_signal_register
246  *  Set a handler for a signal we want to defer.
247  *  Previously set handler must have been saved before.
248  */
zend_signal_register(int signo,void (* handler)(int,siginfo_t *,void *)TSRMLS_DC)249 static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*) TSRMLS_DC)
250 {
251 	struct sigaction sa = {{0}};
252 
253 	if (sigaction(signo, NULL, &sa) == 0) {
254 		if ((sa.sa_flags & SA_SIGINFO) && sa.sa_sigaction == handler) {
255 			return FAILURE;
256 		}
257 
258 		SIGG(handlers)[signo-1].flags = sa.sa_flags;
259 		if (sa.sa_flags & SA_SIGINFO) {
260 			SIGG(handlers)[signo-1].handler = (void *)sa.sa_sigaction;
261 		} else {
262 			SIGG(handlers)[signo-1].handler = (void *)sa.sa_handler;
263 		}
264 
265 		sa.sa_flags     = SA_SIGINFO; /* we'll use a siginfo handler */
266 		sa.sa_sigaction = handler;
267 		sa.sa_mask      = global_sigmask;
268 
269 		if (sigaction(signo, &sa, NULL) < 0) {
270 			zend_error(E_WARNING, "Error installing signal handler for %d", signo);
271 		}
272 
273 		return SUCCESS;
274 	}
275 	return FAILURE;
276 } /* }}} */
277 
278 /* {{{ zend_signal_activate
279  *  Install our signal handlers, per request */
zend_signal_activate(TSRMLS_D)280 void zend_signal_activate(TSRMLS_D)
281 {
282 	int x;
283 
284 	memcpy(&SIGG(handlers), &global_orig_handlers, sizeof(global_orig_handlers));
285 
286 	for (x=0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) {
287 		zend_signal_register(zend_sigs[x], zend_signal_handler_defer TSRMLS_CC);
288 	}
289 
290 	SIGG(active) = 1;
291 	SIGG(depth)  = 0;
292 } /* }}} */
293 
294 /* {{{ zend_signal_deactivate
295  * */
zend_signal_deactivate(TSRMLS_D)296 void zend_signal_deactivate(TSRMLS_D)
297 {
298 	int x;
299 	struct sigaction sa = {{0}};
300 
301 	if (SIGG(check)) {
302 		if (SIGG(depth) != 0) {
303 			zend_error(E_CORE_WARNING, "zend_signal: shutdown with non-zero blocking depth (%d)", SIGG(depth));
304 		}
305 		/* did anyone steal our installed handler */
306 		for (x=0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) {
307 			sigaction(zend_sigs[x], NULL, &sa);
308 			if (sa.sa_sigaction != zend_signal_handler_defer) {
309 				zend_error(E_CORE_WARNING, "zend_signal: handler was replaced for signal (%d) after startup", zend_sigs[x]);
310 			}
311 		}
312 	}
313 
314 	SIGNAL_BEGIN_CRITICAL();
315 	SIGG(active) = 0;
316 	SIGG(running) = 0;
317 	SIGG(blocked) = -1;
318 	SIGG(depth) = 0;
319 	SIGNAL_END_CRITICAL();
320 }
321 /* }}} */
322 
zend_signal_globals_ctor(zend_signal_globals_t * zend_signal_globals TSRMLS_DC)323 static void zend_signal_globals_ctor(zend_signal_globals_t *zend_signal_globals TSRMLS_DC)
324 {
325 	size_t x;
326 
327 	memset(zend_signal_globals, 0, sizeof(*zend_signal_globals));
328 	zend_signal_globals->blocked = -1;
329 
330 	for (x = 0; x < sizeof(zend_signal_globals->pstorage) / sizeof(*zend_signal_globals->pstorage); ++x) {
331 		zend_signal_queue_t *queue = &zend_signal_globals->pstorage[x];
332 		queue->zend_signal.signo = 0;
333 		queue->next = zend_signal_globals->pavail;
334 		zend_signal_globals->pavail = queue;
335 	}
336 }
337 
zend_signal_globals_dtor(zend_signal_globals_t * zend_signal_globals TSRMLS_DC)338 static void zend_signal_globals_dtor(zend_signal_globals_t *zend_signal_globals TSRMLS_DC)
339 {
340 	zend_signal_globals->blocked = -1;
341 }
342 
343 /* {{{ zend_signal_startup
344  * alloc zend signal globals */
zend_signal_startup()345 void zend_signal_startup()
346 {
347 	int signo;
348 	struct sigaction sa = {{0}};
349 
350 #ifdef ZTS
351 	ts_allocate_id(&zend_signal_globals_id, sizeof(zend_signal_globals_t), (ts_allocate_ctor) zend_signal_globals_ctor, (ts_allocate_dtor) zend_signal_globals_dtor);
352 #else
353 	zend_signal_globals_ctor(&zend_signal_globals);
354 #endif
355 
356 	/* Used to block signals during execution of signal handlers */
357 	sigfillset(&global_sigmask);
358 	sigdelset(&global_sigmask, SIGILL);
359 	sigdelset(&global_sigmask, SIGABRT);
360 	sigdelset(&global_sigmask, SIGFPE);
361 	sigdelset(&global_sigmask, SIGKILL);
362 	sigdelset(&global_sigmask, SIGSEGV);
363 	sigdelset(&global_sigmask, SIGCONT);
364 	sigdelset(&global_sigmask, SIGSTOP);
365 	sigdelset(&global_sigmask, SIGTSTP);
366 	sigdelset(&global_sigmask, SIGTTIN);
367 	sigdelset(&global_sigmask, SIGTTOU);
368 #ifdef SIGBUS
369 	sigdelset(&global_sigmask, SIGBUS);
370 #endif
371 #ifdef SIGSYS
372 	sigdelset(&global_sigmask, SIGSYS);
373 #endif
374 #ifdef SIGTRAP
375 	sigdelset(&global_sigmask, SIGTRAP);
376 #endif
377 
378 	/* Save previously registered signal handlers into orig_handlers */
379 	memset(&global_orig_handlers, 0, sizeof(global_orig_handlers));
380 	for (signo = 1; signo < NSIG; ++signo) {
381 		if (sigaction(signo, NULL, &sa) == 0) {
382 			global_orig_handlers[signo-1].flags = sa.sa_flags;
383 			if (sa.sa_flags & SA_SIGINFO) {
384 				global_orig_handlers[signo-1].handler = (void *) sa.sa_sigaction;
385 			} else {
386 				global_orig_handlers[signo-1].handler = (void *) sa.sa_handler;
387 			}
388 		}
389 	}
390 }
391 /* }}} */
392 
393 /* {{{ zend_signal_shutdown
394  * called by zend_shutdown */
zend_signal_shutdown(TSRMLS_D)395 void zend_signal_shutdown(TSRMLS_D)
396 {
397 #ifndef ZTS
398 	zend_signal_globals_dtor(&zend_signal_globals);
399 #endif
400 }
401 /* }}} */
402 
403 
404 #endif /* ZEND_SIGNALS */
405 
406 /*
407  * Local variables:
408  * tab-width: 4
409  * c-basic-offset: 4
410  * indent-tabs-mode: t
411  * End:
412  * vim600: fdm=marker
413  * vim: noet sw=4 ts=4
414  */
415