From ae01a576b34e4c1cc2145144e1dcc2b30492d696 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Loyet?= Date: Sun, 14 Nov 2010 22:01:34 +0000 Subject: [PATCH] - Fixed #52691 (allow multiple instance of FPM using a custom prefix) --- sapi/fpm/config.m4 | 2 + sapi/fpm/fpm/fpm.c | 3 +- sapi/fpm/fpm/fpm.h | 3 +- sapi/fpm/fpm/fpm_conf.c | 179 +++++++++++++++++++++++++++++++-------- sapi/fpm/fpm/fpm_conf.h | 1 + sapi/fpm/fpm/fpm_main.c | 17 +++- sapi/fpm/fpm/fpm_php.h | 20 +++++ sapi/fpm/php-fpm.8.in | 5 ++ sapi/fpm/php-fpm.conf.in | 42 +++++++-- 9 files changed, 225 insertions(+), 47 deletions(-) diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4 index f2273be020..0c8271101b 100644 --- a/sapi/fpm/config.m4 +++ b/sapi/fpm/config.m4 @@ -603,6 +603,8 @@ if test "$PHP_FPM" != "no"; then PHP_SUBST_OLD(php_fpm_sysconfdir) php_fpm_localstatedir=`eval echo $localstatedir` PHP_SUBST_OLD(php_fpm_localstatedir) + php_fpm_prefix=`eval echo $prefix` + PHP_SUBST_OLD(php_fpm_prefix) AC_DEFINE_UNQUOTED(PHP_FPM_USER, "$php_fpm_user", [fpm user name]) AC_DEFINE_UNQUOTED(PHP_FPM_GROUP, "$php_fpm_group", [fpm group name]) diff --git a/sapi/fpm/fpm/fpm.c b/sapi/fpm/fpm/fpm.c index 871d764546..eb1ff40197 100644 --- a/sapi/fpm/fpm/fpm.c +++ b/sapi/fpm/fpm/fpm.c @@ -23,11 +23,12 @@ struct fpm_globals_s fpm_globals; -int fpm_init(int argc, char **argv, char *config, struct event_base **base) /* {{{ */ +int fpm_init(int argc, char **argv, char *config, char *prefix, struct event_base **base) /* {{{ */ { fpm_globals.argc = argc; fpm_globals.argv = argv; fpm_globals.config = config; + fpm_globals.prefix = prefix; if (0 > fpm_php_init_main() || 0 > fpm_stdio_init_main() || diff --git a/sapi/fpm/fpm/fpm.h b/sapi/fpm/fpm/fpm.h index 626afbe0e9..b0d8e13699 100644 --- a/sapi/fpm/fpm/fpm.h +++ b/sapi/fpm/fpm/fpm.h @@ -10,13 +10,14 @@ #include int fpm_run(int *max_requests, struct event_base *base); -int fpm_init(int argc, char **argv, char *config, struct event_base **base); +int fpm_init(int argc, char **argv, char *config, char *prefix, struct event_base **base); struct fpm_globals_s { pid_t parent_pid; int argc; char **argv; char *config; + char *prefix; int running_children; int error_log_fd; int log_level; diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c index ff0ae7fcdb..2c49410e20 100644 --- a/sapi/fpm/fpm/fpm_conf.c +++ b/sapi/fpm/fpm/fpm_conf.c @@ -74,6 +74,7 @@ static struct ini_value_parser_s ini_fpm_global_options[] = { }; static struct ini_value_parser_s ini_fpm_pool_options[] = { + { "prefix", &fpm_conf_set_string, WPO(prefix) }, { "user", &fpm_conf_set_string, WPO(user) }, { "group", &fpm_conf_set_string, WPO(group) }, { "chroot", &fpm_conf_set_string, WPO(chroot) }, @@ -114,6 +115,29 @@ static int fpm_conf_is_dir(char *path) /* {{{ */ } /* }}} */ +static int fpm_conf_expand_pool_name(char **value) { + char *token; + + if (!value || !*value) { + return 0; + } + + while ((token = strstr(*value, "$pool"))) { + char *buf; + char *p1 = *value; + char *p2 = token + strlen("$pool"); + if (!current_wp || !current_wp->config || !current_wp->config->name) { + return -1; + } + token[0] = '\0'; + spprintf(&buf, 0, "%s%s%s", p1, current_wp->config->name, p2); + *value = strdup(buf); + efree(buf); + } + + return 0; +} + static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset) /* {{{ */ { char *val = Z_STRVAL_P(value); @@ -141,6 +165,9 @@ static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset) /* if (!new) { return "fpm_conf_set_string(): strdup() failed"; } + if (fpm_conf_expand_pool_name(&new) == -1) { + return "Can't use '$pool' when the pool is not defined"; + } *old = new; return NULL; @@ -295,6 +322,9 @@ static char *fpm_conf_set_array(zval *key, zval *value, void **config, int conve kv->value = strdup(b ? "On" : "Off"); } else { kv->value = strdup(Z_STRVAL_P(value)); + if (fpm_conf_expand_pool_name(&kv->value) == -1) { + return "Can't use '$pool' when the pool is not defined"; + } } if (!kv->value) { @@ -381,27 +411,68 @@ int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc) /* {{{ */ free(wpc->chroot); free(wpc->chdir); free(wpc->slowlog); + free(wpc->prefix); return 0; } /* }}} */ -static int fpm_evaluate_full_path(char **path) /* {{{ */ +static int fpm_evaluate_full_path(char **path, struct fpm_worker_pool_s *wp, char *default_prefix, int expand) /* {{{ */ { - if (**path != '/') { - char *full_path; + char *prefix = NULL; + char *full_path; - full_path = malloc(sizeof(PHP_PREFIX) + strlen(*path) + 1); + if (!path || !*path || **path == '/') { + return 0; + } - if (!full_path) { - return -1; + if (wp && wp->config) { + prefix = wp->config->prefix; + } + + /* if the wp prefix is not set */ + if (prefix == NULL) { + prefix = fpm_globals.prefix; + } + + /* if the global prefix is not set */ + if (prefix == NULL) { + prefix = default_prefix ? default_prefix : PHP_PREFIX; + } + + if (expand) { + char *tmp; + tmp = strstr(*path, "$prefix"); + if (tmp != NULL) { + + if (tmp != *path) { + zlog(ZLOG_ERROR, "'$prefix' must be use at the begining of the value"); + return -1; + } + + if (strlen(*path) > strlen("$prefix")) { + free(*path); + tmp = strdup((*path) + strlen("$prefix")); + *path = tmp; + } else { + free(*path); + *path = NULL; + } } + } - sprintf(full_path, "%s/%s", PHP_PREFIX, *path); + if (*path) { + spprintf(&full_path, 0, "%s/%s", prefix, *path); free(*path); - *path = full_path; + *path = strdup(full_path); + efree(full_path); + } else { + *path = strdup(prefix); } + if (**path != '/' && wp != NULL && wp->config) { + return fpm_evaluate_full_path(path, NULL, default_prefix, expand); + } return 0; } /* }}} */ @@ -417,11 +488,20 @@ static int fpm_conf_process_all_pools() /* {{{ */ for (wp = fpm_worker_all_pools; wp; wp = wp->next) { + if (wp->config->prefix && *wp->config->prefix) { + fpm_evaluate_full_path(&wp->config->prefix, NULL, NULL, 0); + + if (!fpm_conf_is_dir(wp->config->prefix)) { + zlog(ZLOG_ERROR, "[pool %s] the prefix '%s' does not exist or is not a directory", wp->config->name, wp->config->prefix); + return -1; + } + } + if (wp->config->listen_address && *wp->config->listen_address) { wp->listen_address_domain = fpm_sockets_domain_from_address(wp->config->listen_address); if (wp->listen_address_domain == FPM_AF_UNIX && *wp->config->listen_address != '/') { - fpm_evaluate_full_path(&wp->config->listen_address); + fpm_evaluate_full_path(&wp->config->listen_address, wp, NULL, 0); } } else { zlog(ZLOG_ALERT, "[pool %s] no listen address have been defined!", wp->config->name); @@ -478,6 +558,9 @@ static int fpm_conf_process_all_pools() /* {{{ */ } + if (wp->config->slowlog && *wp->config->slowlog) { + fpm_evaluate_full_path(&wp->config->slowlog, wp, NULL, 0); + } if (wp->config->request_slowlog_timeout) { #if HAVE_FPM_TRACE @@ -495,14 +578,10 @@ static int fpm_conf_process_all_pools() /* {{{ */ wp->config->request_slowlog_timeout = 0; #endif - } - - if (wp->config->request_slowlog_timeout && wp->config->slowlog && *wp->config->slowlog) { - int fd; - fpm_evaluate_full_path(&wp->config->slowlog); + if (wp->config->slowlog && *wp->config->slowlog) { + int fd; - if (wp->config->request_slowlog_timeout) { fd = open(wp->config->slowlog, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); if (0 > fd) { @@ -583,6 +662,9 @@ static int fpm_conf_process_all_pools() /* {{{ */ } if (wp->config->chroot && *wp->config->chroot) { + + fpm_evaluate_full_path(&wp->config->chroot, wp, NULL, 1); + if (*wp->config->chroot != '/') { zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' must start with a '/'", wp->config->name, wp->config->chroot); return -1; @@ -594,6 +676,9 @@ static int fpm_conf_process_all_pools() /* {{{ */ } if (wp->config->chdir && *wp->config->chdir) { + + fpm_evaluate_full_path(&wp->config->chdir, wp, NULL, 0); + if (*wp->config->chdir != '/') { zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' must start with a '/'", wp->config->name, wp->config->chdir); return -1; @@ -601,21 +686,16 @@ static int fpm_conf_process_all_pools() /* {{{ */ if (wp->config->chroot) { char *buf; - size_t len; - len = strlen(wp->config->chroot) + strlen(wp->config->chdir) + 1; - buf = malloc(sizeof(char) * len); - if (!buf) { - zlog(ZLOG_SYSERROR, "[pool %s] malloc() failed", wp->config->name); - return -1; - } - snprintf(buf, len, "%s%s", wp->config->chroot, wp->config->chdir); + spprintf(&buf, 0, "%s/%s", wp->config->chroot, wp->config->chdir); + if (!fpm_conf_is_dir(buf)) { zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' within the chroot path '%s' ('%s') does not exist or is not a directory", wp->config->name, wp->config->chdir, wp->config->chroot, buf); - free(buf); + efree(buf); return -1; } - free(buf); + + efree(buf); } else { if (!fpm_conf_is_dir(wp->config->chdir)) { zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' does not exist or is not a directory", wp->config->name, wp->config->chdir); @@ -623,6 +703,26 @@ static int fpm_conf_process_all_pools() /* {{{ */ } } } + if (!wp->config->chroot) { + struct key_value_s *kv; + char *options[] = FPM_PHP_INI_TO_EXPAND; + char **p; + + for (kv = wp->config->php_values; kv; kv = kv->next) { + for (p=options; *p; p++) { + if (!strcasecmp(kv->key, *p)) { + fpm_evaluate_full_path(&kv->value, wp, NULL, 0); + } + } + } + for (kv = wp->config->php_admin_values; kv; kv = kv->next) { + for (p=options; *p; p++) { + if (!strcasecmp(kv->key, *p)) { + fpm_evaluate_full_path(&kv->value, wp, NULL, 0); + } + } + } + } } return 0; } @@ -671,18 +771,14 @@ int fpm_conf_write_pid() /* {{{ */ static int fpm_conf_post_process() /* {{{ */ { if (fpm_global_config.pid_file) { - fpm_evaluate_full_path(&fpm_global_config.pid_file); + fpm_evaluate_full_path(&fpm_global_config.pid_file, NULL, PHP_LOCALSTATEDIR, 0); } if (!fpm_global_config.error_log) { - char *tmp_log_path; - - spprintf(&tmp_log_path, 0, "%s/log/php-fpm.log", PHP_LOCALSTATEDIR); - fpm_global_config.error_log = strdup(tmp_log_path); - efree(tmp_log_path); + fpm_global_config.error_log = strdup("log/php-fpm.log"); } - fpm_evaluate_full_path(&fpm_global_config.error_log); + fpm_evaluate_full_path(&fpm_global_config.error_log, NULL, PHP_LOCALSTATEDIR, 0); if (0 > fpm_stdio_open_error_log(0)) { return -1; @@ -990,7 +1086,7 @@ int fpm_conf_load_ini_file(char *filename TSRMLS_DC) /* {{{ */ if (ini_include) { char *tmp = ini_include; ini_include = NULL; - fpm_evaluate_full_path(&tmp); + fpm_evaluate_full_path(&tmp, NULL, NULL, 0); fpm_conf_ini_parser_include(tmp, &error TSRMLS_CC); if (error) { free(tmp); @@ -1014,10 +1110,23 @@ int fpm_conf_init_main() /* {{{ */ int ret; TSRMLS_FETCH(); + if (fpm_globals.prefix && *fpm_globals.prefix) { + if (!fpm_conf_is_dir(fpm_globals.prefix)) { + zlog(ZLOG_ERROR, "the global prefix '%s' does not exist or is not a directory", fpm_globals.prefix); + return -1; + } + } + if (fpm_globals.config == NULL) { - spprintf(&fpm_globals.config, 0, "%s/php-fpm.conf", PHP_SYSCONFDIR); + + if (fpm_globals.prefix == NULL) { + spprintf(&fpm_globals.config, 0, "%s/php-fpm.conf", PHP_SYSCONFDIR); + } else { + spprintf(&fpm_globals.config, 0, "%s/etc/php-fpm.conf", fpm_globals.prefix); + } + if (!fpm_globals.config) { - zlog(ZLOG_SYSERROR, "spprintf() failed (\"%s/php-fpm.conf\")", PHP_SYSCONFDIR); + zlog(ZLOG_SYSERROR, "spprintf() failed (fpm_globals.config)"); return -1; } } diff --git a/sapi/fpm/fpm/fpm_conf.h b/sapi/fpm/fpm/fpm_conf.h index aace7811eb..2e65efe2ea 100644 --- a/sapi/fpm/fpm/fpm_conf.h +++ b/sapi/fpm/fpm/fpm_conf.h @@ -31,6 +31,7 @@ extern struct fpm_global_config_s fpm_global_config; struct fpm_worker_pool_config_s { char *name; + char *prefix; char *user; char *group; char *chroot; diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c index 8086a7bdfd..b254a765cd 100644 --- a/sapi/fpm/fpm/fpm_main.c +++ b/sapi/fpm/fpm/fpm_main.c @@ -153,6 +153,7 @@ static const opt_struct OPTIONS[] = { {'v', 0, "version"}, {'y', 1, "fpm-config"}, {'t', 0, "test"}, + {'p', 1, "prefix"}, {'-', 0, NULL} /* end of args */ }; @@ -958,7 +959,7 @@ static void php_cgi_usage(char *argv0) prog = "php"; } - php_printf( "Usage: %s [-n] [-e] [-h] [-i] [-m] [-v] [-t] [-c ] [-d foo[=bar]] [-y ]\n" + php_printf( "Usage: %s [-n] [-e] [-h] [-i] [-m] [-v] [-t] [-p ] [-c ] [-d foo[=bar]] [-y ]\n" " -c | Look for php.ini file in this directory\n" " -n No php.ini file will be used\n" " -d foo[=bar] Define INI entry foo with value 'bar'\n" @@ -967,10 +968,12 @@ static void php_cgi_usage(char *argv0) " -i PHP information\n" " -m Show compiled in modules\n" " -v Version number\n" + " -p, --prefix \n" + " Specify alternative prefix path to FastCGI process manager (default: %s).\n" " -y, --fpm-config \n" " Specify alternative path to FastCGI process manager config file.\n" " -t, --test Test FPM configuration and exit\n", - prog); + prog, PHP_PREFIX); } /* }}} */ @@ -1547,6 +1550,7 @@ int main(int argc, char *argv[]) int fcgi_fd = 0; fcgi_request request; char *fpm_config = NULL; + char *fpm_prefix = NULL; fcgi_init(); @@ -1585,9 +1589,11 @@ int main(int argc, char *argv[]) } cgi_sapi_module.php_ini_path_override = strdup(php_optarg); break; + case 'n': cgi_sapi_module.php_ini_ignore = 1; break; + case 'd': { /* define ini entries on command line */ int len = strlen(php_optarg); @@ -1619,10 +1625,15 @@ int main(int argc, char *argv[]) } break; } + case 'y': fpm_config = php_optarg; break; + case 'p': + fpm_prefix = php_optarg; + break; + case 'e': /* enable extended info output */ CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO; break; @@ -1767,7 +1778,7 @@ consult the installation file that came with this distribution, or visit \n\ } } - if (0 > fpm_init(argc, argv, fpm_config ? fpm_config : CGIG(fpm_config), &CGIG(event_base))) { + if (0 > fpm_init(argc, argv, fpm_config ? fpm_config : CGIG(fpm_config), fpm_prefix, &CGIG(event_base))) { return FAILURE; } diff --git a/sapi/fpm/fpm/fpm_php.h b/sapi/fpm/fpm/fpm_php.h index 8c4b58ceb6..81e5332671 100644 --- a/sapi/fpm/fpm/fpm_php.h +++ b/sapi/fpm/fpm/fpm_php.h @@ -11,6 +11,26 @@ #include "build-defs.h" /* for PHP_ defines */ #include "fpm/fpm_conf.h" +#define FPM_PHP_INI_TO_EXPAND \ + { \ + "error_log", \ + "extension_dir", \ + "mime_magic.magicfile", \ + "sendmail_path", \ + "session.cookie_path", \ + "session_pgsql.sem_file_name", \ + "soap.wsdl_cache_dir", \ + "uploadprogress.file.filename_template", \ + "xdebug.output_dir", \ + "xdebug.profiler_output_dir", \ + "xdebug.trace_output_dir", \ + "xmms.path", \ + "axis2.client_home", \ + "blenc.key_file", \ + "coin_acceptor.device", \ + NULL \ + } + struct fpm_worker_pool_s; int fpm_php_init_child(struct fpm_worker_pool_s *wp); diff --git a/sapi/fpm/php-fpm.8.in b/sapi/fpm/php-fpm.8.in index 1aa8b62b95..3d9dc0981c 100644 --- a/sapi/fpm/php-fpm.8.in +++ b/sapi/fpm/php-fpm.8.in @@ -77,6 +77,11 @@ Show compiled in modules .PD 1 .B \-v Version number +.B \-\-prefix \fIpath\fP +.TP +.PD 1 +.B \-p +Specify alternative prefix path (the default is @php_fpm_prefix@) .TP .PD 0 .B \-\-fpm\-config \fIfile\fP diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in index ccf864a5e8..bceef97e1c 100644 --- a/sapi/fpm/php-fpm.conf.in +++ b/sapi/fpm/php-fpm.conf.in @@ -3,12 +3,16 @@ ;;;;;;;;;;;;;;;;;;;;; ; All relative paths in this configuration file are relative to PHP's install -; prefix. +; prefix (@prefix@). This prefix can be dynamicaly changed by using the +; '-p' argument from the command line. ; Include one or more files. If glob(3) exists, it is used to include a bunch of ; files from a glob(3) pattern. This directive can be used everywhere in the ; file. -;include=@EXPANDED_SYSCONFDIR@/fpm.d/*.conf +; Relative path can also be used. They will be prefixed by: +; - the global prefix if it's been set (-p arguement) +; - @prefix@ otherwise +;include=etc/fpm.d/*.conf ;;;;;;;;;;;;;;;;;; ; Global Options ; @@ -16,12 +20,14 @@ [global] ; Pid file +; Note: the default prefix is @EXPANDED_LOCALSTATEDIR@ ; Default Value: none -;pid = @EXPANDED_LOCALSTATEDIR@/run/php-fpm.pid +;pid = run/php-fpm.pid ; Error log file -; Default Value: @EXPANDED_LOCALSTATEDIR@/log/php-fpm.log -;error_log = @EXPANDED_LOCALSTATEDIR@/log/php-fpm.log +; Note: the default prefix is @EXPANDED_LOCALSTATEDIR@ +; Default Value: log/php-fpm.log +;error_log = log/php-fpm.log ; Log level ; Possible Values: alert, error, warning, notice, debug @@ -62,8 +68,23 @@ ; FPM can handle. Your system will tell you anyway :) ; Start a new pool named 'www'. +; the variable $pool can we used in any directive and will be replaced by the +; pool name ('www' here) [www] +; Per pool prefix +; It only applies on the following directives: +; - 'slowlog' +; - 'listen' (unixsocket) +; - 'chroot' +; - 'chdir' +; - 'php_values' +; - 'php_admin_values' +; When not set, the global prefix (or @php_fpm_prefix@) applies instead. +; Note: This directive can also be relative to the global prefix. +; Default Value: none +;prefix = /path/to/pools/$pool + ; The address on which to accept FastCGI requests. ; Valid syntaxes are: ; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on @@ -218,7 +239,7 @@ pm.max_children = 50 ; The log file for slow requests ; Default Value: not set ; Note: slowlog is mandatory if request_slowlog_timeout is set -;slowlog = @EXPANDED_LOCALSTATEDIR@/log/php-fpm.log.slow +;slowlog = log/$pool.log.slow ; Set open file descriptor rlimit. ; Default Value: system defined value @@ -231,13 +252,17 @@ pm.max_children = 50 ; Chroot to this directory at the start. This value must be defined as an ; absolute path. When this value is not set, chroot is not used. +; Note: you can prefix with '$prefix' to chroot to the pool prefix or one +; of its subdirectories. If the pool prefix is not set, the global prefix +; will be used instead. ; Note: chrooting is a great security feature and should be used whenever ; possible. However, all PHP paths will be relative to the chroot ; (error_log, sessions.save_path, ...). ; Default Value: not set ;chroot = -; Chdir to this directory at the start. This value must be an absolute path. +; Chdir to this directory at the start. +; Note: relative path can be used. ; Default Value: current directory or / when chroot ;chdir = /var/www @@ -269,6 +294,9 @@ pm.max_children = 50 ; overwrite previously defined php.ini values, but will append the new value ; instead. +; Note: path INI options can be relative and will be expanded with the prefix +; (pool, global or @prefix@) + ; Default Value: nothing is defined by default except the values in php.ini and ; specified at startup with the -d argument ;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com -- 2.40.0