Skip to content

Commit 878f8b0

Browse files
committed
Merge branch 'PHP-7.4'
2 parents cdacad8 + 666fb9a commit 878f8b0

File tree

5 files changed

+147
-0
lines changed

5 files changed

+147
-0
lines changed

sapi/fpm/fpm/fpm_main.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS;
8484
#include "fpm.h"
8585
#include "fpm_request.h"
8686
#include "fpm_status.h"
87+
#include "fpm_signals.h"
8788
#include "fpm_conf.h"
8889
#include "fpm_php.h"
8990
#include "fpm_log.h"
@@ -1568,6 +1569,15 @@ int main(int argc, char *argv[])
15681569
closes it. in apache|apxs mode apache
15691570
does that for us! thies@thieso.net
15701571
20000419 */
1572+
1573+
/* Subset of signals from fpm_signals_init_main() to avoid unexpected death during early init
1574+
or during reload just after execvp() or fork */
1575+
int init_signal_array[] = { SIGUSR1, SIGUSR2, SIGCHLD };
1576+
if (0 > fpm_signals_init_mask(init_signal_array, sizeof(init_signal_array)/sizeof(init_signal_array[0])) ||
1577+
0 > fpm_signals_block()) {
1578+
zlog(ZLOG_WARNING, "Could die in the case of too early reload signal");
1579+
}
1580+
zlog(ZLOG_DEBUG, "Blocked some signals");
15711581
#endif
15721582

15731583
#ifdef ZTS

sapi/fpm/fpm/fpm_process_ctl.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ static void fpm_pctl_exit() /* {{{ */
7777

7878
static void fpm_pctl_exec() /* {{{ */
7979
{
80+
zlog(ZLOG_DEBUG, "Blocking some signals before reexec");
81+
if (0 > fpm_signals_block()) {
82+
zlog(ZLOG_WARNING, "concurrent reloads may be unstable");
83+
}
8084

8185
zlog(ZLOG_NOTICE, "reloading: execvp(\"%s\", {\"%s\""
8286
"%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"

sapi/fpm/fpm/fpm_signals.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "zlog.h"
2020

2121
static int sp[2];
22+
static sigset_t block_sigset;
2223

2324
const char *fpm_signal_names[NSIG + 1] = {
2425
#ifdef SIGHUP
@@ -210,6 +211,11 @@ int fpm_signals_init_main() /* {{{ */
210211
zlog(ZLOG_SYSERROR, "failed to init signals: sigaction()");
211212
return -1;
212213
}
214+
215+
zlog(ZLOG_DEBUG, "Unblocking all signals");
216+
if (0 > fpm_signals_unblock()) {
217+
return -1;
218+
}
213219
return 0;
214220
}
215221
/* }}} */
@@ -250,3 +256,50 @@ int fpm_signals_get_fd() /* {{{ */
250256
return sp[0];
251257
}
252258
/* }}} */
259+
260+
int fpm_signals_init_mask(int *signum_array, size_t size) /* {{{ */
261+
{
262+
size_t i = 0;
263+
if (0 > sigemptyset(&block_sigset)) {
264+
zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigemptyset()");
265+
return -1;
266+
}
267+
for (i = 0; i < size; ++i) {
268+
int sig_i = signum_array[i];
269+
if (0 > sigaddset(&block_sigset, sig_i)) {
270+
if (sig_i <= NSIG && fpm_signal_names[sig_i] != NULL) {
271+
zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigaddset(%s)",
272+
fpm_signal_names[sig_i]);
273+
} else {
274+
zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigaddset(%d)", sig_i);
275+
}
276+
return -1;
277+
}
278+
}
279+
return 0;
280+
}
281+
/* }}} */
282+
283+
int fpm_signals_block() /* {{{ */
284+
{
285+
if (0 > sigprocmask(SIG_BLOCK, &block_sigset, NULL)) {
286+
zlog(ZLOG_SYSERROR, "failed to block signals");
287+
return -1;
288+
}
289+
return 0;
290+
}
291+
/* }}} */
292+
293+
int fpm_signals_unblock() /* {{{ */
294+
{
295+
/* Ensure that during reload after upgrade all signals are unblocked.
296+
block_sigset could have different value before execve() */
297+
sigset_t all_signals;
298+
sigfillset(&all_signals);
299+
if (0 > sigprocmask(SIG_UNBLOCK, &all_signals, NULL)) {
300+
zlog(ZLOG_SYSERROR, "failed to unblock signals");
301+
return -1;
302+
}
303+
return 0;
304+
}
305+
/* }}} */

sapi/fpm/fpm/fpm_signals.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
int fpm_signals_init_main();
99
int fpm_signals_init_child();
1010
int fpm_signals_get_fd();
11+
int fpm_signals_init_mask(int *signum_array, size_t size);
12+
int fpm_signals_block();
13+
int fpm_signals_unblock();
1114

1215
extern const char *fpm_signal_names[NSIG + 1];
1316

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
--TEST--
2+
Concurrent reload signals should not kill PHP-FPM master process. (Bug: #74083)
3+
--SKIPIF--
4+
<?php
5+
include "skipif.inc";
6+
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
7+
?>
8+
--FILE--
9+
<?php
10+
11+
require_once "tester.inc";
12+
13+
$cfg = <<<EOT
14+
[global]
15+
error_log = {{FILE:LOG}}
16+
pid = {{FILE:PID}}
17+
process_control_timeout=1
18+
[unconfined]
19+
listen = {{ADDR}}
20+
ping.path = /ping
21+
ping.response = pong
22+
pm = dynamic
23+
pm.max_children = 5
24+
pm.start_servers = 1
25+
pm.min_spare_servers = 1
26+
pm.max_spare_servers = 1
27+
EOT;
28+
29+
$code = <<<EOT
30+
<?php
31+
/* empty */
32+
EOT;
33+
34+
$tester = new FPM\Tester($cfg, $code);
35+
$tester->start();
36+
$tester->expectLogStartNotices();
37+
$tester->ping('{{ADDR}}');
38+
39+
/* Vary interval between concurrent reload requests
40+
since performance of test instance is not known in advance */
41+
$max_interval = 25000;
42+
$step = 1000;
43+
$pid = $tester->getPid();
44+
for ($interval = 0; $interval < $max_interval; $interval += $step) {
45+
exec("kill -USR2 $pid", $out, $killExitCode);
46+
if ($killExitCode) {
47+
echo "ERROR: master process is dead\n";
48+
break;
49+
}
50+
usleep($interval);
51+
}
52+
echo "Reached interval $interval us with $step us steps\n";
53+
$tester->expectLogNotice('Reloading in progress ...');
54+
/* Consume mix of 'Reloading in progress ...' and 'reloading: .*' */
55+
$tester->getLogLines(2000);
56+
57+
$tester->signal('USR2');
58+
$tester->expectLogNotice('Reloading in progress ...');
59+
$tester->expectLogNotice('reloading: .*');
60+
$tester->expectLogNotice('using inherited socket fd=\d+, "127.0.0.1:\d+"');
61+
$tester->expectLogStartNotices();
62+
$tester->ping('{{ADDR}}');
63+
64+
$tester->terminate();
65+
$tester->expectLogTerminatingNotices();
66+
$tester->close();
67+
68+
?>
69+
Done
70+
--EXPECT--
71+
Reached interval 25000 us with 1000 us steps
72+
Done
73+
--CLEAN--
74+
<?php
75+
require_once "tester.inc";
76+
FPM\Tester::clean();
77+
?>

0 commit comments

Comments
 (0)