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 +----------------------------------------------------------------------+
19 This software was contributed to PHP by Facebook Inc. in 2008.
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.
25 All other licensing and usage conditions are those of the PHP Group.
34 #include "zend_globals.h"
46 #include "zend_signal.h"
49 ZEND_API
int zend_signal_globals_id
;
51 zend_signal_globals_t zend_signal_globals
;
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
);
58 #define TIMEOUT_SIG SIGALRM
60 #define TIMEOUT_SIG SIGPROF
63 static int zend_sigs
[] = { TIMEOUT_SIG
, SIGHUP
, SIGINT
, SIGQUIT
, SIGTERM
, SIGUSR1
, SIGUSR2
};
65 #define SA_FLAGS_MASK ~(SA_NODEFER | SA_RESETHAND)
67 /* True globals, written only at process startup */
68 static zend_signal_entry_t global_orig_handlers
[NSIG
];
69 static sigset_t global_sigmask
;
71 /* {{{ zend_signal_handler_defer
72 * Blocks signals if in critical section */
73 void zend_signal_handler_defer(int signo
, siginfo_t
*siginfo
, void *context
)
75 int errno_save
= errno
;
76 zend_signal_queue_t
*queue
, *qtmp
;
80 if (SIGG(depth
) == 0) { /* try to handle signal */
81 if (SIGG(blocked
) != -1) { /* inverse */
82 SIGG(blocked
) = -1; /* signal is not blocked */
84 if (SIGG(running
) == 0) {
86 zend_signal_handler(signo
, siginfo
, context TSRMLS_CC
);
92 zend_signal_handler(queue
->zend_signal
.signo
, queue
->zend_signal
.siginfo
, queue
->zend_signal
.context TSRMLS_CC
);
94 queue
->next
= SIGG(pavail
);
95 queue
->zend_signal
.signo
= 0;
101 } else { /* delay signal handling */
102 SIGG(blocked
) = 0; /* signal is blocked */
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
;
111 if (SIGG(phead
) && SIGG(ptail
)) {
112 SIGG(ptail
)->next
= queue
;
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
);
125 /* need to just run handler if we're inactive and getting a signal */
126 zend_signal_handler(signo
, siginfo
, context TSRMLS_CC
);
132 /* {{{ zend_signal_handler_unblock
133 * Handle deferred signal from HANDLE_UNBLOCK_ALARMS */
134 ZEND_API
void zend_signal_handler_unblock(TSRMLS_D
)
136 zend_signal_queue_t
*queue
;
137 zend_signal_t zend_signal
;
140 SIGNAL_BEGIN_CRITICAL(); /* procmask to protect handler_defer as if it were called by the kernel */
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
;
148 zend_signal_handler_defer(zend_signal
.signo
, zend_signal
.siginfo
, zend_signal
.context
);
149 SIGNAL_END_CRITICAL();
154 /* {{{ zend_signal_handler
155 * Call the previously registered handler for a signal
157 static void zend_signal_handler(int signo
, siginfo_t
*siginfo
, void *context TSRMLS_DC
)
159 int errno_save
= errno
;
160 struct sigaction sa
= {{0}};
162 zend_signal_entry_t p_sig
= SIGG(handlers
)[signo
-1];
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
);
169 sigemptyset(&sigset
);
170 sigaddset(&sigset
, signo
);
172 if (sigaction(signo
, &sa
, NULL
) == 0) {
173 /* throw away any blocked signals */
174 sigprocmask(SIG_UNBLOCK
, &sigset
, NULL
);
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
;
184 (*(void (*)(int, siginfo_t
*, void*))p_sig
.handler
)(signo
, siginfo
, context
);
186 (*(void (*)(int))p_sig
.handler
)(signo
);
193 /* {{{ zend_sigaction
194 * Register a signal handler that will be deferred in critical sections */
195 ZEND_API
int zend_sigaction(int signo
, const struct sigaction
*act
, struct sigaction
*oldact TSRMLS_DC
)
197 struct sigaction sa
= {{0}};
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
;
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
;
210 SIGG(handlers
)[signo
-1].handler
= (void *) act
->sa_handler
;
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
;
217 if (sigaction(signo
, &sa
, NULL
) < 0) {
218 zend_error(E_WARNING
, "Error installing signal handler for %d", signo
);
221 /* unsure this signal is not blocked */
222 sigemptyset(&sigset
);
223 sigaddset(&sigset
, signo
);
224 zend_sigprocmask(SIG_UNBLOCK
, &sigset
, NULL
);
232 * Register a signal handler that will be deferred in critical sections */
233 ZEND_API
int zend_signal(int signo
, void (*handler
)(int) TSRMLS_DC
)
235 struct sigaction sa
= {{0}};
238 sa
.sa_handler
= handler
;
239 sa
.sa_mask
= global_sigmask
;
241 return zend_sigaction(signo
, &sa
, NULL TSRMLS_CC
);
245 /* {{{ zend_signal_register
246 * Set a handler for a signal we want to defer.
247 * Previously set handler must have been saved before.
249 static int zend_signal_register(int signo
, void (*handler
)(int, siginfo_t
*, void*) TSRMLS_DC
)
251 struct sigaction sa
= {{0}};
253 if (sigaction(signo
, NULL
, &sa
) == 0) {
254 if ((sa
.sa_flags
& SA_SIGINFO
) && sa
.sa_sigaction
== handler
) {
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
;
262 SIGG(handlers
)[signo
-1].handler
= (void *)sa
.sa_handler
;
265 sa
.sa_flags
= SA_SIGINFO
; /* we'll use a siginfo handler */
266 sa
.sa_sigaction
= handler
;
267 sa
.sa_mask
= global_sigmask
;
269 if (sigaction(signo
, &sa
, NULL
) < 0) {
270 zend_error(E_WARNING
, "Error installing signal handler for %d", signo
);
278 /* {{{ zend_signal_activate
279 * Install our signal handlers, per request */
280 void zend_signal_activate(TSRMLS_D
)
284 memcpy(&SIGG(handlers
), &global_orig_handlers
, sizeof(global_orig_handlers
));
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
);
294 /* {{{ zend_signal_deactivate
296 void zend_signal_deactivate(TSRMLS_D
)
299 struct sigaction sa
= {{0}};
302 if (SIGG(depth
) != 0) {
303 zend_error(E_CORE_WARNING
, "zend_signal: shutdown with non-zero blocking depth (%d)", SIGG(depth
));
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
]);
314 SIGNAL_BEGIN_CRITICAL();
319 SIGNAL_END_CRITICAL();
323 static void zend_signal_globals_ctor(zend_signal_globals_t
*zend_signal_globals TSRMLS_DC
)
327 memset(zend_signal_globals
, 0, sizeof(*zend_signal_globals
));
328 zend_signal_globals
->blocked
= -1;
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
;
338 static void zend_signal_globals_dtor(zend_signal_globals_t
*zend_signal_globals TSRMLS_DC
)
340 zend_signal_globals
->blocked
= -1;
343 /* {{{ zend_signal_startup
344 * alloc zend signal globals */
345 void zend_signal_startup()
348 struct sigaction sa
= {{0}};
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
);
353 zend_signal_globals_ctor(&zend_signal_globals
);
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
);
369 sigdelset(&global_sigmask
, SIGBUS
);
372 sigdelset(&global_sigmask
, SIGSYS
);
375 sigdelset(&global_sigmask
, SIGTRAP
);
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
;
386 global_orig_handlers
[signo
-1].handler
= (void *) sa
.sa_handler
;
393 /* {{{ zend_signal_shutdown
394 * called by zend_shutdown */
395 void zend_signal_shutdown(TSRMLS_D
)
398 zend_signal_globals_dtor(&zend_signal_globals
);
404 #endif /* ZEND_SIGNALS */
410 * indent-tabs-mode: t
413 * vim: noet sw=4 ts=4