Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions sapi/fpm/fpm/fpm_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1814,6 +1814,74 @@ static void fpm_conf_dump(void)
}
}

static bool fpm_conf_test_process_max(void)
{
if (fpm_global_config.process_max <= 0) {
return true;
}

int total_start_children = 0;
for (const struct fpm_worker_pool_s *wp = fpm_worker_all_pools; wp; wp = wp->next) {
switch (wp->config->pm) {
case PM_STYLE_STATIC:
total_start_children += wp->config->pm_max_children;
break;
case PM_STYLE_DYNAMIC:
total_start_children += wp->config->pm_start_servers;
break;
case PM_STYLE_ONDEMAND:
/* Starts with 0 children. */
break;
}
}
if (total_start_children > fpm_global_config.process_max) {
zlog(ZLOG_ERROR, "process.max (%d) must not be less than the total number of children that start initially across all pools (%d)",
fpm_global_config.process_max, total_start_children);
return false;
}

return true;
}

static struct fpm_worker_pool_s *fpm_conf_test_current_wp;
static bool fpm_conf_test_ini_errors_emitted;

static void fpm_conf_test_error_cb(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message)
{
zlog(ZLOG_ERROR, "[pool %s] %s", fpm_conf_test_current_wp->config->name, ZSTR_VAL(message));
fpm_conf_test_ini_errors_emitted = true;
}

static bool fpm_conf_test_defines(struct fpm_worker_pool_s *wp)
{
struct { struct key_value_s *list; int mode; } sources[] = {
{ wp->config->php_values, ZEND_INI_USER },
{ wp->config->php_admin_values, ZEND_INI_SYSTEM },
};

for (int i = 0; i < (sizeof(sources) / sizeof(sources[0])); i++) {
for (struct key_value_s *kv = sources[i].list; kv; kv = kv->next) {
if (!strcmp(kv->key, "extension") || !strcmp(kv->key, "disable_functions")) {
continue;
}

fpm_conf_test_current_wp = wp;
void (*old_zend_error_cb)(int, zend_string *, const uint32_t, zend_string *) = zend_error_cb;
zend_error_cb = fpm_conf_test_error_cb;
int ret = fpm_php_zend_ini_alter_master(kv->key, strlen(kv->key), kv->value, strlen(kv->value), sources[i].mode, PHP_INI_STAGE_ACTIVATE);
zend_error_cb = old_zend_error_cb;

if (ret == FAILURE) {
zlog(ZLOG_ERROR, "[pool %s] Unknown '%s' setting", wp->config->name, kv->key);
return false;
} else if (fpm_conf_test_ini_errors_emitted) {
return false;
}
}
}
return true;
}

int fpm_conf_init_main(int test_conf, int force_daemon) /* {{{ */
{
int ret;
Expand Down Expand Up @@ -1871,6 +1939,16 @@ int fpm_conf_init_main(int test_conf, int force_daemon) /* {{{ */
}
}

if (!fpm_conf_test_process_max()) {
return -1;
}

for (struct fpm_worker_pool_s *wp = fpm_worker_all_pools; wp; wp = wp->next) {
if (!fpm_conf_test_defines(wp)) {
return -1;
}
}

if (test_conf > 1) {
fpm_conf_dump();
}
Expand Down
2 changes: 1 addition & 1 deletion sapi/fpm/fpm/fpm_php.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

static char **limit_extensions = NULL;

static int fpm_php_zend_ini_alter_master(char *name, int name_length, char *new_value, int new_value_length, int mode, int stage) /* {{{ */
int fpm_php_zend_ini_alter_master(char *name, int name_length, char *new_value, int new_value_length, int mode, int stage) /* {{{ */
{
zend_ini_entry *ini_entry;
zend_string *duplicate;
Expand Down
1 change: 1 addition & 0 deletions sapi/fpm/fpm/fpm_php.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ size_t fpm_php_content_length(void);
void fpm_php_soft_quit(void);
int fpm_php_init_main(void);
int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode);
int fpm_php_zend_ini_alter_master(char *name, int name_length, char *new_value, int new_value_length, int mode, int stage);
int fpm_php_limit_extensions(char *path);
bool fpm_php_is_key_in_table(zend_string *table, const char *key, size_t key_len);

Expand Down
34 changes: 34 additions & 0 deletions sapi/fpm/tests/config-test-ini-invalid.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--TEST--
FPM: config test propagates INI errors
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php

require_once "tester.inc";

$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR}}
pm = static
pm.max_children = 5
php_admin_value[memory_limit] = 1MB
EOT;

$tester = new FPM\Tester($cfg);
$tester->testConfig();

?>
Done
--EXPECTF--
ERROR: [pool unconfined] Invalid "memory_limit" setting. Invalid quantity "1MB": unknown multiplier "B", interpreting as "1" for backwards compatibility
ERROR: [pool unconfined] Failed to set memory limit to 1 bytes (Current memory usage is %d bytes)
ERROR: FPM initialization failed
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>
33 changes: 33 additions & 0 deletions sapi/fpm/tests/config-test-ini-unknown.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
FPM: config test rejects unknown INI settings
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php

require_once "tester.inc";

$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR}}
pm = static
pm.max_children = 5
php_admin_value[does_not_exist] = 1
EOT;

$tester = new FPM\Tester($cfg);
$tester->testConfig();

?>
Done
--EXPECT--
ERROR: [pool unconfined] Unknown 'does_not_exist' setting
ERROR: FPM initialization failed
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>
31 changes: 31 additions & 0 deletions sapi/fpm/tests/config-test-ini-valid.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
FPM: config test accepts valid INI entries
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php

require_once "tester.inc";

$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
[unconfined]
listen = {{ADDR}}
pm = static
pm.max_children = 5
php_admin_value[memory_limit] = 128M
EOT;

$tester = new FPM\Tester($cfg);
$tester->testConfig();

?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>
43 changes: 43 additions & 0 deletions sapi/fpm/tests/config-test-process-max-invalid.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
--TEST--
FPM: config test rejects process.max lower than total initial children
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php

require_once "tester.inc";

$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
process.max = 3
[pool1]
listen = {{ADDR[pool1]}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
[pool2]
listen = {{ADDR[pool2]}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;

$tester = new FPM\Tester($cfg);
$tester->testConfig();

?>
Done
--EXPECTF--
ERROR: process.max (3) must not be less than the total number of children that start initially across all pools (4)
ERROR: FPM initialization failed
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>
41 changes: 41 additions & 0 deletions sapi/fpm/tests/config-test-process-max-valid.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
--TEST--
FPM: config test accepts process.max equal to total initial children
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php

require_once "tester.inc";

$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
process.max = 4
[pool1]
listen = {{ADDR[pool1]}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
[pool2]
listen = {{ADDR[pool2]}}
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;

$tester = new FPM\Tester($cfg);
$tester->testConfig();

?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>
Loading