diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c index 3979d875a18e5..dddc3e7fee803 100644 --- a/sapi/fpm/fpm/fpm_conf.c +++ b/sapi/fpm/fpm/fpm_conf.c @@ -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; @@ -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(); } diff --git a/sapi/fpm/fpm/fpm_php.c b/sapi/fpm/fpm/fpm_php.c index 8deb107d0a389..176941bb38451 100644 --- a/sapi/fpm/fpm/fpm_php.c +++ b/sapi/fpm/fpm/fpm_php.c @@ -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; diff --git a/sapi/fpm/fpm/fpm_php.h b/sapi/fpm/fpm/fpm_php.h index d61857c5e0e11..90b3003ec202a 100644 --- a/sapi/fpm/fpm/fpm_php.h +++ b/sapi/fpm/fpm/fpm_php.h @@ -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); diff --git a/sapi/fpm/tests/config-test-ini-invalid.phpt b/sapi/fpm/tests/config-test-ini-invalid.phpt new file mode 100644 index 0000000000000..ebf7a24dbe800 --- /dev/null +++ b/sapi/fpm/tests/config-test-ini-invalid.phpt @@ -0,0 +1,34 @@ +--TEST-- +FPM: config test propagates INI errors +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/config-test-ini-unknown.phpt b/sapi/fpm/tests/config-test-ini-unknown.phpt new file mode 100644 index 0000000000000..f13971496ab6e --- /dev/null +++ b/sapi/fpm/tests/config-test-ini-unknown.phpt @@ -0,0 +1,33 @@ +--TEST-- +FPM: config test rejects unknown INI settings +--SKIPIF-- + +--FILE-- +testConfig(); + +?> +Done +--EXPECT-- +ERROR: [pool unconfined] Unknown 'does_not_exist' setting +ERROR: FPM initialization failed +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/config-test-ini-valid.phpt b/sapi/fpm/tests/config-test-ini-valid.phpt new file mode 100644 index 0000000000000..39bed0f531713 --- /dev/null +++ b/sapi/fpm/tests/config-test-ini-valid.phpt @@ -0,0 +1,31 @@ +--TEST-- +FPM: config test accepts valid INI entries +--SKIPIF-- + +--FILE-- +testConfig(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/config-test-process-max-invalid.phpt b/sapi/fpm/tests/config-test-process-max-invalid.phpt new file mode 100644 index 0000000000000..f57df153c1c17 --- /dev/null +++ b/sapi/fpm/tests/config-test-process-max-invalid.phpt @@ -0,0 +1,43 @@ +--TEST-- +FPM: config test rejects process.max lower than total initial children +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/config-test-process-max-valid.phpt b/sapi/fpm/tests/config-test-process-max-valid.phpt new file mode 100644 index 0000000000000..9e424a322f610 --- /dev/null +++ b/sapi/fpm/tests/config-test-process-max-valid.phpt @@ -0,0 +1,41 @@ +--TEST-- +FPM: config test accepts process.max equal to total initial children +--SKIPIF-- + +--FILE-- +testConfig(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- +