Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 415 lines (349 sloc) 11.915 kB
9398751 Zend Signal Handling (see RFC: https://wiki.php.net/rfc/zendsignals)
Ilia Alshanetsky authored
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 */
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 */
0d49305 Expose zend_signal_handler_unblock() to DSO extensions to allow usage…
Dmitry Stogov authored
134 ZEND_API void zend_signal_handler_unblock(TSRMLS_D)
9398751 Zend Signal Handling (see RFC: https://wiki.php.net/rfc/zendsignals)
Ilia Alshanetsky authored
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 */
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 */
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_ERROR, "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 */
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 */
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_ERROR, "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 */
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 * */
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
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
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 */
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));
379c32b @felipensp - Fixed wrong sigaction() call passing signal number 65 (invalid)
felipensp authored
380 for (signo = 1; signo < NSIG; ++signo) {
9398751 Zend Signal Handling (see RFC: https://wiki.php.net/rfc/zendsignals)
Ilia Alshanetsky authored
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 */
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 */
Something went wrong with that request. Please try again.