Back to index

php5  5.3.10
fpm_conf.c
Go to the documentation of this file.
00001 
00002        /* $Id: fpm_conf.c,v 1.33.2.3 2008/12/13 03:50:29 anight Exp $ */
00003        /* (c) 2007,2008 Andrei Nigmatulin */
00004 
00005 #include "fpm_config.h"
00006 
00007 #include <sys/types.h>
00008 #include <sys/stat.h>
00009 #include <fcntl.h>
00010 #include <string.h>
00011 #include <stdlib.h>
00012 #include <stddef.h>
00013 #include <string.h>
00014 #if HAVE_INTTYPES_H
00015 # include <inttypes.h>
00016 #else
00017 # include <stdint.h>
00018 #endif
00019 #ifdef HAVE_GLOB
00020 # ifndef PHP_WIN32
00021 #  include <glob.h>
00022 # else
00023 #  include "win32/glob.h"
00024 # endif
00025 #endif
00026 
00027 #include <stdio.h>
00028 #include <unistd.h>
00029 
00030 #include "php.h"
00031 #include "zend_ini_scanner.h"
00032 #include "zend_globals.h"
00033 #include "zend_stream.h"
00034 #include "php_syslog.h"
00035 
00036 #include "fpm.h"
00037 #include "fpm_conf.h"
00038 #include "fpm_stdio.h"
00039 #include "fpm_worker_pool.h"
00040 #include "fpm_cleanup.h"
00041 #include "fpm_php.h"
00042 #include "fpm_sockets.h"
00043 #include "fpm_shm.h"
00044 #include "fpm_status.h"
00045 #include "fpm_log.h"
00046 #include "fpm_events.h"
00047 #include "zlog.h"
00048 
00049 #define STR2STR(a) (a ? a : "undefined")
00050 #define BOOL2STR(a) (a ? "yes" : "no")
00051 #define GO(field) offsetof(struct fpm_global_config_s, field)
00052 #define WPO(field) offsetof(struct fpm_worker_pool_config_s, field)
00053 
00054 static int fpm_conf_load_ini_file(char *filename TSRMLS_DC);
00055 static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset);
00056 static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset);
00057 static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset);
00058 static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset);
00059 static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset);
00060 static char *fpm_conf_set_log_level(zval *value, void **config, intptr_t offset);
00061 static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offset);
00062 static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset);
00063 #ifdef HAVE_SYSLOG_H
00064 static char *fpm_conf_set_syslog_facility(zval *value, void **config, intptr_t offset);
00065 #endif
00066 
00067 struct fpm_global_config_s fpm_global_config = {
00068        .daemonize = 1,
00069 #ifdef HAVE_SYSLOG_H
00070        .syslog_facility = -1,
00071 #endif
00072        .process_max = 0,
00073 };
00074 static struct fpm_worker_pool_s *current_wp = NULL;
00075 static int ini_recursion = 0;
00076 static char *ini_filename = NULL;
00077 static int ini_lineno = 0;
00078 static char *ini_include = NULL;
00079 
00080 /* 
00081  * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
00082  */
00083 static struct ini_value_parser_s ini_fpm_global_options[] = {
00084        { "pid",                         &fpm_conf_set_string,          GO(pid_file) },
00085        { "error_log",                   &fpm_conf_set_string,          GO(error_log) },
00086 #ifdef HAVE_SYSLOG_H
00087        { "syslog.ident",                &fpm_conf_set_string,          GO(syslog_ident) },
00088        { "syslog.facility",             &fpm_conf_set_syslog_facility, GO(syslog_facility) },
00089 #endif
00090        { "log_level",                   &fpm_conf_set_log_level,       GO(log_level) },
00091        { "emergency_restart_threshold", &fpm_conf_set_integer,         GO(emergency_restart_threshold) },
00092        { "emergency_restart_interval",  &fpm_conf_set_time,            GO(emergency_restart_interval) },
00093        { "process_control_timeout",     &fpm_conf_set_time,            GO(process_control_timeout) },
00094        { "process.max",                 &fpm_conf_set_integer,         GO(process_max) },
00095        { "daemonize",                   &fpm_conf_set_boolean,         GO(daemonize) },
00096        { "rlimit_files",                &fpm_conf_set_integer,         GO(rlimit_files) },
00097        { "rlimit_core",                 &fpm_conf_set_rlimit_core,     GO(rlimit_core) },
00098        { "events.mechanism",            &fpm_conf_set_string,          GO(events_mechanism) },
00099        { 0, 0, 0 }
00100 };
00101 
00102 /* 
00103  * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
00104  */
00105 static struct ini_value_parser_s ini_fpm_pool_options[] = {
00106        { "prefix",                    &fpm_conf_set_string,      WPO(prefix) },
00107        { "user",                      &fpm_conf_set_string,      WPO(user) },
00108        { "group",                     &fpm_conf_set_string,      WPO(group) },
00109        { "listen",                    &fpm_conf_set_string,      WPO(listen_address) },
00110        { "listen.backlog",            &fpm_conf_set_integer,     WPO(listen_backlog) },
00111        { "listen.owner",              &fpm_conf_set_string,      WPO(listen_owner) },
00112        { "listen.group",              &fpm_conf_set_string,      WPO(listen_group) },
00113        { "listen.mode",               &fpm_conf_set_string,      WPO(listen_mode) },
00114        { "listen.allowed_clients",    &fpm_conf_set_string,      WPO(listen_allowed_clients) },
00115        { "pm",                        &fpm_conf_set_pm,          WPO(pm) },
00116        { "pm.max_children",           &fpm_conf_set_integer,     WPO(pm_max_children) },
00117        { "pm.start_servers",          &fpm_conf_set_integer,     WPO(pm_start_servers) },
00118        { "pm.min_spare_servers",      &fpm_conf_set_integer,     WPO(pm_min_spare_servers) },
00119        { "pm.max_spare_servers",      &fpm_conf_set_integer,     WPO(pm_max_spare_servers) },
00120        { "pm.process_idle_timeout",   &fpm_conf_set_time,        WPO(pm_process_idle_timeout) },
00121        { "pm.max_requests",           &fpm_conf_set_integer,     WPO(pm_max_requests) },
00122        { "pm.status_path",            &fpm_conf_set_string,      WPO(pm_status_path) },
00123        { "ping.path",                 &fpm_conf_set_string,      WPO(ping_path) },
00124        { "ping.response",             &fpm_conf_set_string,      WPO(ping_response) },
00125        { "access.log",                &fpm_conf_set_string,      WPO(access_log) },
00126        { "access.format",             &fpm_conf_set_string,      WPO(access_format) },
00127        { "slowlog",                   &fpm_conf_set_string,      WPO(slowlog) },
00128        { "request_slowlog_timeout",   &fpm_conf_set_time,        WPO(request_slowlog_timeout) },
00129        { "request_terminate_timeout", &fpm_conf_set_time,        WPO(request_terminate_timeout) },
00130        { "rlimit_files",              &fpm_conf_set_integer,     WPO(rlimit_files) },
00131        { "rlimit_core",               &fpm_conf_set_rlimit_core, WPO(rlimit_core) },
00132        { "chroot",                    &fpm_conf_set_string,      WPO(chroot) },
00133        { "chdir",                     &fpm_conf_set_string,      WPO(chdir) },
00134        { "catch_workers_output",      &fpm_conf_set_boolean,     WPO(catch_workers_output) },
00135        { "security.limit_extensions", &fpm_conf_set_string,      WPO(security_limit_extensions) },
00136        { 0, 0, 0 }
00137 };
00138 
00139 static int fpm_conf_is_dir(char *path) /* {{{ */
00140 {
00141        struct stat sb;
00142 
00143        if (stat(path, &sb) != 0) {
00144               return 0;
00145        }
00146 
00147        return (sb.st_mode & S_IFMT) == S_IFDIR;
00148 }
00149 /* }}} */
00150 
00151 /*
00152  * Expands the '$pool' token in a dynamically allocated string
00153  */
00154 static int fpm_conf_expand_pool_name(char **value) {
00155        char *token;
00156 
00157        if (!value || !*value) {
00158               return 0;
00159        }
00160 
00161        while (*value && (token = strstr(*value, "$pool"))) {
00162               char *buf;
00163               char *p2 = token + strlen("$pool");
00164 
00165               /* If we are not in a pool, we cannot expand this name now */
00166               if (!current_wp || !current_wp->config  || !current_wp->config->name) {
00167                      return -1;
00168               }
00169 
00170               /* "aaa$poolbbb" becomes "aaa\0oolbbb" */
00171               token[0] = '\0';
00172 
00173               /* Build a brand new string with the expanded token */
00174               spprintf(&buf, 0, "%s%s%s", *value, current_wp->config->name, p2);
00175 
00176               /* Free the previous value and save the new one */
00177               free(*value);
00178               *value = strdup(buf);
00179               efree(buf);
00180        }
00181 
00182        return 0;
00183 }
00184 
00185 static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset) /* {{{ */
00186 {
00187        char *val = Z_STRVAL_P(value);
00188        long value_y = !strcasecmp(val, "1");
00189        long value_n = !strcasecmp(val, "");
00190 
00191        if (!value_y && !value_n) {
00192               return "invalid boolean value";
00193        }
00194 
00195        * (int *) ((char *) *config + offset) = value_y ? 1 : 0;
00196        return NULL;
00197 }
00198 /* }}} */
00199 
00200 static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset) /* {{{ */
00201 {
00202        char **config_val = (char **) ((char *) *config + offset);
00203 
00204        if (!config_val) {
00205               return "internal error: NULL value";
00206        }
00207 
00208        /* Check if there is a previous value to deallocate */
00209        if (*config_val) {
00210               free(*config_val);
00211        }
00212 
00213        *config_val = strdup(Z_STRVAL_P(value));
00214        if (!*config_val) {
00215               return "fpm_conf_set_string(): strdup() failed";
00216        }
00217        if (fpm_conf_expand_pool_name(config_val) == -1) {
00218               return "Can't use '$pool' when the pool is not defined";
00219        }
00220 
00221        return NULL;
00222 }
00223 /* }}} */
00224 
00225 static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset) /* {{{ */
00226 {
00227        char *val = Z_STRVAL_P(value);
00228        char *p;
00229 
00230        /* we don't use strtol because we don't want to allow negative values */
00231        for (p = val; *p; p++) {
00232               if (p == val && *p == '-') continue;
00233               if (*p < '0' || *p > '9') {
00234                      return "is not a valid number (greater or equal than zero)";
00235               }
00236        }
00237        * (int *) ((char *) *config + offset) = atoi(val);
00238        return NULL;
00239 }
00240 /* }}} */
00241 
00242 static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset) /* {{{ */
00243 {
00244        char *val = Z_STRVAL_P(value);
00245        char *p;
00246 
00247        for (p = val; *p; p++) {
00248               if ( p == val && *p == '-' ) continue;
00249               if (*p < '0' || *p > '9') {
00250                      return "is not a valid number (greater or equal than zero)";
00251               }
00252        }
00253        * (long int *) ((char *) *config + offset) = atol(val);
00254        return NULL;
00255 }
00256 /* }}} */
00257 
00258 static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset) /* {{{ */
00259 {
00260        char *val = Z_STRVAL_P(value);
00261        int len = strlen(val);
00262        char suffix;
00263        int seconds;
00264        if (!len) {
00265               return "invalid time value";
00266        }
00267 
00268        suffix = val[len-1];
00269        switch (suffix) {
00270               case 'm' :
00271                      val[len-1] = '\0';
00272                      seconds = 60 * atoi(val);
00273                      break;
00274               case 'h' :
00275                      val[len-1] = '\0';
00276                      seconds = 60 * 60 * atoi(val);
00277                      break;
00278               case 'd' :
00279                      val[len-1] = '\0';
00280                      seconds = 24 * 60 * 60 * atoi(val);
00281                      break;
00282               case 's' : /* s is the default suffix */
00283                      val[len-1] = '\0';
00284                      suffix = '0';
00285               default :
00286                      if (suffix < '0' || suffix > '9') {
00287                             return "unknown suffix used in time value";
00288                      }
00289                      seconds = atoi(val);
00290                      break;
00291        }
00292 
00293        * (int *) ((char *) *config + offset) = seconds;
00294        return NULL;
00295 }
00296 /* }}} */
00297 
00298 static char *fpm_conf_set_log_level(zval *value, void **config, intptr_t offset) /* {{{ */
00299 {
00300        char *val = Z_STRVAL_P(value);
00301        int log_level;
00302 
00303        if (!strcasecmp(val, "debug")) {
00304               log_level = ZLOG_DEBUG;
00305        } else if (!strcasecmp(val, "notice")) {
00306               log_level = ZLOG_NOTICE;
00307        } else if (!strcasecmp(val, "warning") || !strcasecmp(val, "warn")) {
00308               log_level = ZLOG_WARNING;
00309        } else if (!strcasecmp(val, "error")) {
00310               log_level = ZLOG_ERROR;
00311        } else if (!strcasecmp(val, "alert")) {
00312               log_level = ZLOG_ALERT;
00313        } else {
00314               return "invalid value for 'log_level'";
00315        }
00316 
00317        * (int *) ((char *) *config + offset) = log_level;
00318        return NULL;
00319 }
00320 /* }}} */
00321 
00322 #ifdef HAVE_SYSLOG_H
00323 static char *fpm_conf_set_syslog_facility(zval *value, void **config, intptr_t offset) /* {{{ */
00324 {
00325        char *val = Z_STRVAL_P(value);
00326        int *conf = (int *) ((char *) *config + offset);
00327 
00328 #ifdef LOG_AUTH
00329        if (!strcasecmp(val, "AUTH")) {
00330               *conf = LOG_AUTH;
00331               return NULL;
00332        }
00333 #endif
00334 
00335 #ifdef LOG_AUTHPRIV
00336        if (!strcasecmp(val, "AUTHPRIV")) {
00337               *conf = LOG_AUTHPRIV;
00338               return NULL;
00339        }
00340 #endif
00341 
00342 #ifdef LOG_CRON
00343        if (!strcasecmp(val, "CRON")) {
00344               *conf = LOG_CRON;
00345               return NULL;
00346        }
00347 #endif
00348 
00349 #ifdef LOG_DAEMON
00350        if (!strcasecmp(val, "DAEMON")) {
00351               *conf = LOG_DAEMON;
00352               return NULL;
00353        }
00354 #endif
00355 
00356 #ifdef LOG_FTP
00357        if (!strcasecmp(val, "FTP")) {
00358               *conf = LOG_FTP;
00359               return NULL;
00360        }
00361 #endif
00362 
00363 #ifdef LOG_KERN
00364        if (!strcasecmp(val, "KERN")) {
00365               *conf = LOG_KERN;
00366               return NULL;
00367        }
00368 #endif
00369 
00370 #ifdef LOG_LPR
00371        if (!strcasecmp(val, "LPR")) {
00372               *conf = LOG_LPR;
00373               return NULL;
00374        }
00375 #endif
00376 
00377 #ifdef LOG_MAIL
00378        if (!strcasecmp(val, "MAIL")) {
00379               *conf = LOG_MAIL;
00380               return NULL;
00381        }
00382 #endif
00383 
00384 #ifdef LOG_NEWS
00385        if (!strcasecmp(val, "NEWS")) {
00386               *conf = LOG_NEWS;
00387               return NULL;
00388        }
00389 #endif
00390 
00391 #ifdef LOG_SYSLOG
00392        if (!strcasecmp(val, "SYSLOG")) {
00393               *conf = LOG_SYSLOG;
00394               return NULL;
00395        }
00396 #endif
00397 
00398 #ifdef LOG_USER
00399        if (!strcasecmp(val, "USER")) {
00400               *conf = LOG_USER;
00401               return NULL;
00402        }
00403 #endif
00404 
00405 #ifdef LOG_UUCP
00406        if (!strcasecmp(val, "UUCP")) {
00407               *conf = LOG_UUCP;
00408               return NULL;
00409        }
00410 #endif
00411 
00412 #ifdef LOG_LOCAL0
00413        if (!strcasecmp(val, "LOCAL0")) {
00414               *conf = LOG_LOCAL0;
00415               return NULL;
00416        }
00417 #endif
00418 
00419 #ifdef LOG_LOCAL1
00420        if (!strcasecmp(val, "LOCAL1")) {
00421               *conf = LOG_LOCAL1;
00422               return NULL;
00423        }
00424 #endif
00425 
00426 #ifdef LOG_LOCAL2
00427        if (!strcasecmp(val, "LOCAL2")) {
00428               *conf = LOG_LOCAL2;
00429               return NULL;
00430        }
00431 #endif
00432 
00433 #ifdef LOG_LOCAL3
00434        if (!strcasecmp(val, "LOCAL3")) {
00435               *conf = LOG_LOCAL3;
00436               return NULL;
00437        }
00438 #endif
00439 
00440 #ifdef LOG_LOCAL4
00441        if (!strcasecmp(val, "LOCAL4")) {
00442               *conf = LOG_LOCAL4;
00443               return NULL;
00444        }
00445 #endif
00446 
00447 #ifdef LOG_LOCAL5
00448        if (!strcasecmp(val, "LOCAL5")) {
00449               *conf = LOG_LOCAL5;
00450               return NULL;
00451        }
00452 #endif
00453 
00454 #ifdef LOG_LOCAL6
00455        if (!strcasecmp(val, "LOCAL6")) {
00456               *conf = LOG_LOCAL6;
00457               return NULL;
00458        }
00459 #endif
00460 
00461 #ifdef LOG_LOCAL7
00462        if (!strcasecmp(val, "LOCAL7")) {
00463               *conf = LOG_LOCAL7;
00464               return NULL;
00465        }
00466 #endif
00467 
00468        return "invalid value";
00469 }
00470 /* }}} */
00471 #endif
00472 
00473 static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offset) /* {{{ */
00474 {
00475        char *val = Z_STRVAL_P(value);
00476        int *ptr = (int *) ((char *) *config + offset);
00477 
00478        if (!strcasecmp(val, "unlimited")) {
00479               *ptr = -1;
00480        } else {
00481               int int_value;
00482               void *subconf = &int_value;
00483               char *error;
00484 
00485               error = fpm_conf_set_integer(value, &subconf, 0);
00486 
00487               if (error) { 
00488                      return error;
00489               }
00490 
00491               if (int_value < 0) {
00492                      return "must be greater than zero or 'unlimited'";
00493               }
00494 
00495               *ptr = int_value;
00496        }
00497 
00498        return NULL;
00499 }
00500 /* }}} */
00501 
00502 static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset) /* {{{ */
00503 {
00504        char *val = Z_STRVAL_P(value);
00505        struct fpm_worker_pool_config_s  *c = *config;
00506        if (!strcasecmp(val, "static")) {
00507               c->pm = PM_STYLE_STATIC;
00508        } else if (!strcasecmp(val, "dynamic")) {
00509               c->pm = PM_STYLE_DYNAMIC;
00510        } else if (!strcasecmp(val, "ondemand")) {
00511               c->pm = PM_STYLE_ONDEMAND;
00512        } else {
00513               return "invalid process manager (static, dynamic or ondemand)";
00514        }
00515        return NULL;
00516 }
00517 /* }}} */
00518 
00519 static char *fpm_conf_set_array(zval *key, zval *value, void **config, int convert_to_bool) /* {{{ */
00520 {
00521        struct key_value_s *kv;
00522        struct key_value_s ***parent = (struct key_value_s ***) config;
00523        int b;
00524        void *subconf = &b;
00525 
00526        kv = malloc(sizeof(*kv));
00527 
00528        if (!kv) {
00529               return "malloc() failed";
00530        }
00531 
00532        memset(kv, 0, sizeof(*kv));
00533        kv->key = strdup(Z_STRVAL_P(key));
00534 
00535        if (!kv->key) {
00536               return "fpm_conf_set_array: strdup(key) failed";
00537        }
00538 
00539        if (convert_to_bool) {
00540               char *err = fpm_conf_set_boolean(value, &subconf, 0);
00541               if (err) return err;
00542               kv->value = strdup(b ? "1" : "0");
00543        } else {
00544               kv->value = strdup(Z_STRVAL_P(value));
00545               if (fpm_conf_expand_pool_name(&kv->value) == -1) {
00546                      return "Can't use '$pool' when the pool is not defined";
00547               }
00548        }
00549 
00550        if (!kv->value) {
00551               free(kv->key);
00552               return "fpm_conf_set_array: strdup(value) failed";
00553        }
00554 
00555        kv->next = **parent;
00556        **parent = kv;
00557        return NULL;
00558 }
00559 /* }}} */
00560 
00561 static void *fpm_worker_pool_config_alloc() /* {{{ */
00562 {
00563        struct fpm_worker_pool_s *wp;
00564 
00565        wp = fpm_worker_pool_alloc();
00566 
00567        if (!wp) {
00568               return 0;
00569        }
00570 
00571        wp->config = malloc(sizeof(struct fpm_worker_pool_config_s));
00572 
00573        if (!wp->config) { 
00574               return 0;
00575        }
00576 
00577        memset(wp->config, 0, sizeof(struct fpm_worker_pool_config_s));
00578        wp->config->listen_backlog = FPM_BACKLOG_DEFAULT;
00579        wp->config->pm_process_idle_timeout = 10; /* 10s by default */
00580 
00581        if (!fpm_worker_all_pools) {
00582               fpm_worker_all_pools = wp;
00583        } else {
00584               struct fpm_worker_pool_s *tmp = fpm_worker_all_pools;
00585               while (tmp) {
00586                      if (!tmp->next) {
00587                             tmp->next = wp;
00588                             break;
00589                      }
00590                      tmp = tmp->next;
00591               }
00592        }
00593 
00594        current_wp = wp;
00595        return wp->config;
00596 }
00597 /* }}} */
00598 
00599 int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc) /* {{{ */
00600 {
00601        struct key_value_s *kv, *kv_next;
00602 
00603        free(wpc->name);
00604        free(wpc->prefix);
00605        free(wpc->user);
00606        free(wpc->group);
00607        free(wpc->listen_address);
00608        free(wpc->listen_owner);
00609        free(wpc->listen_group);
00610        free(wpc->listen_mode);
00611        free(wpc->listen_allowed_clients);
00612        free(wpc->pm_status_path);
00613        free(wpc->ping_path);
00614        free(wpc->ping_response);
00615        free(wpc->access_log);
00616        free(wpc->access_format);
00617        free(wpc->slowlog);
00618        free(wpc->chroot);
00619        free(wpc->chdir);
00620        free(wpc->security_limit_extensions);
00621 
00622        for (kv = wpc->php_values; kv; kv = kv_next) {
00623               kv_next = kv->next;
00624               free(kv->key);
00625               free(kv->value);
00626               free(kv);
00627        }
00628        for (kv = wpc->php_admin_values; kv; kv = kv_next) {
00629               kv_next = kv->next;
00630               free(kv->key);
00631               free(kv->value);
00632               free(kv);
00633        }
00634        for (kv = wpc->env; kv; kv = kv_next) {
00635               kv_next = kv->next;
00636               free(kv->key);
00637               free(kv->value);
00638               free(kv);
00639        }
00640 
00641        return 0;
00642 }
00643 /* }}} */
00644 
00645 static int fpm_evaluate_full_path(char **path, struct fpm_worker_pool_s *wp, char *default_prefix, int expand) /* {{{ */
00646 {
00647        char *prefix = NULL;
00648        char *full_path;
00649 
00650        if (!path || !*path || **path == '/') {
00651               return 0;
00652        }
00653 
00654        if (wp && wp->config) {
00655               prefix = wp->config->prefix;
00656        }
00657 
00658        /* if the wp prefix is not set */
00659        if (prefix == NULL) {
00660               prefix = fpm_globals.prefix;
00661        }
00662 
00663        /* if the global prefix is not set */
00664        if (prefix == NULL) {
00665               prefix = default_prefix ? default_prefix : PHP_PREFIX;
00666        }
00667 
00668        if (expand) {
00669               char *tmp;
00670               tmp = strstr(*path, "$prefix");
00671               if (tmp != NULL) {
00672 
00673                      if (tmp != *path) {
00674                             zlog(ZLOG_ERROR, "'$prefix' must be use at the begining of the value");
00675                             return -1;
00676                      }
00677 
00678                      if (strlen(*path) > strlen("$prefix")) {
00679                             free(*path);
00680                             tmp = strdup((*path) + strlen("$prefix"));
00681                             *path = tmp;
00682                      } else {
00683                             free(*path);
00684                             *path = NULL;
00685                      }
00686               }
00687        }
00688 
00689        if (*path) {
00690               spprintf(&full_path, 0, "%s/%s", prefix, *path);
00691               free(*path);
00692               *path = strdup(full_path);
00693               efree(full_path);
00694        } else {
00695               *path = strdup(prefix);
00696        }
00697 
00698        if (**path != '/' && wp != NULL && wp->config) {
00699               return fpm_evaluate_full_path(path, NULL, default_prefix, expand);
00700        }
00701        return 0;
00702 }
00703 /* }}} */
00704 
00705 static int fpm_conf_process_all_pools() /* {{{ */
00706 {
00707        struct fpm_worker_pool_s *wp;
00708 
00709        if (!fpm_worker_all_pools) {
00710               zlog(ZLOG_ERROR, "No pool defined. at least one pool section must be specified in config file");
00711               return -1;
00712        }
00713 
00714        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
00715 
00716               /* prefix */
00717               if (wp->config->prefix && *wp->config->prefix) {
00718                      fpm_evaluate_full_path(&wp->config->prefix, NULL, NULL, 0);
00719 
00720                      if (!fpm_conf_is_dir(wp->config->prefix)) {
00721                             zlog(ZLOG_ERROR, "[pool %s] the prefix '%s' does not exist or is not a directory", wp->config->name, wp->config->prefix);
00722                             return -1;
00723                      }
00724               }
00725 
00726               /* user */
00727               if (!wp->config->user) {
00728                      zlog(ZLOG_ALERT, "[pool %s] user has not been defined", wp->config->name);
00729                      return -1;
00730               }
00731 
00732               /* listen */
00733               if (wp->config->listen_address && *wp->config->listen_address) {
00734                      wp->listen_address_domain = fpm_sockets_domain_from_address(wp->config->listen_address);
00735 
00736                      if (wp->listen_address_domain == FPM_AF_UNIX && *wp->config->listen_address != '/') {
00737                             fpm_evaluate_full_path(&wp->config->listen_address, wp, NULL, 0);
00738                      }
00739               } else {
00740                      zlog(ZLOG_ALERT, "[pool %s] no listen address have been defined!", wp->config->name);
00741                      return -1;
00742               }
00743 
00744               /* pm */
00745               if (wp->config->pm != PM_STYLE_STATIC && wp->config->pm != PM_STYLE_DYNAMIC && wp->config->pm != PM_STYLE_ONDEMAND) {
00746                      zlog(ZLOG_ALERT, "[pool %s] the process manager is missing (static, dynamic or ondemand)", wp->config->name);
00747                      return -1;
00748               }
00749 
00750               /* pm.max_children */
00751               if (wp->config->pm_max_children < 1) {
00752                      zlog(ZLOG_ALERT, "[pool %s] pm.max_children must be a positive value", wp->config->name);
00753                      return -1;
00754               }
00755 
00756               /* pm.start_servers, pm.min_spare_servers, pm.max_spare_servers */
00757               if (wp->config->pm == PM_STYLE_DYNAMIC) {
00758                      struct fpm_worker_pool_config_s *config = wp->config;
00759 
00760                      if (config->pm_min_spare_servers <= 0) {
00761                             zlog(ZLOG_ALERT, "[pool %s] pm.min_spare_servers(%d) must be a positive value", wp->config->name, config->pm_min_spare_servers);
00762                             return -1;
00763                      }
00764 
00765                      if (config->pm_max_spare_servers <= 0) {
00766                             zlog(ZLOG_ALERT, "[pool %s] pm.max_spare_servers(%d) must be a positive value", wp->config->name, config->pm_max_spare_servers);
00767                             return -1;
00768                      }
00769 
00770                      if (config->pm_min_spare_servers > config->pm_max_children ||
00771                                    config->pm_max_spare_servers > config->pm_max_children) {
00772                             zlog(ZLOG_ALERT, "[pool %s] pm.min_spare_servers(%d) and pm.max_spare_servers(%d) cannot be greater than pm.max_children(%d)", wp->config->name, config->pm_min_spare_servers, config->pm_max_spare_servers, config->pm_max_children);
00773                             return -1;
00774                      }
00775 
00776                      if (config->pm_max_spare_servers < config->pm_min_spare_servers) {
00777                             zlog(ZLOG_ALERT, "[pool %s] pm.max_spare_servers(%d) must not be less than pm.min_spare_servers(%d)", wp->config->name, config->pm_max_spare_servers, config->pm_min_spare_servers);
00778                             return -1;
00779                      }
00780 
00781                      if (config->pm_start_servers <= 0) {
00782                             config->pm_start_servers = config->pm_min_spare_servers + ((config->pm_max_spare_servers - config->pm_min_spare_servers) / 2);
00783                             zlog(ZLOG_WARNING, "[pool %s] pm.start_servers is not set. It's been set to %d.", wp->config->name, config->pm_start_servers);
00784 
00785                      } else if (config->pm_start_servers < config->pm_min_spare_servers || config->pm_start_servers > config->pm_max_spare_servers) {
00786                             zlog(ZLOG_ALERT, "[pool %s] pm.start_servers(%d) must not be less than pm.min_spare_servers(%d) and not greater than pm.max_spare_servers(%d)", wp->config->name, config->pm_start_servers, config->pm_min_spare_servers, config->pm_max_spare_servers);
00787                             return -1;
00788                      }
00789               } else if (wp->config->pm == PM_STYLE_ONDEMAND) {
00790                      struct fpm_worker_pool_config_s *config = wp->config;
00791 
00792                      if (!fpm_event_support_edge_trigger()) {
00793                             zlog(ZLOG_ALERT, "[pool %s] ondemand process manager can ONLY be used when events.mechanisme is either epoll (Linux) or kqueue (*BSD).", wp->config->name);
00794                             return -1;
00795                      }
00796 
00797                      if (config->pm_process_idle_timeout < 1) {
00798                             zlog(ZLOG_ALERT, "[pool %s] pm.process_idle_timeout(%ds) must be greater than 0s", wp->config->name, config->pm_process_idle_timeout);
00799                             return -1;
00800                      }
00801 
00802                      if (config->listen_backlog < FPM_BACKLOG_DEFAULT) {
00803                             zlog(ZLOG_WARNING, "[pool %s] listen.backlog(%d) was too low for the ondemand process manager. I updated it for you to %d.", wp->config->name, config->listen_backlog, FPM_BACKLOG_DEFAULT);
00804                             config->listen_backlog = FPM_BACKLOG_DEFAULT;
00805                      }
00806 
00807                      /* certainely useless but proper */
00808                      config->pm_start_servers = 0;
00809                      config->pm_min_spare_servers = 0;
00810                      config->pm_max_spare_servers = 0;
00811               }
00812 
00813               /* status */
00814               if (wp->config->pm_status_path && *wp->config->pm_status_path) {
00815                      int i;
00816                      char *status = wp->config->pm_status_path;
00817 
00818                      if (*status != '/') {
00819                             zlog(ZLOG_ERROR, "[pool %s] the status path '%s' must start with a '/'", wp->config->name, status);
00820                             return -1;
00821                      }
00822 
00823                      if (strlen(status) < 2) {
00824                             zlog(ZLOG_ERROR, "[pool %s] the status path '%s' is not long enough", wp->config->name, status);
00825                             return -1;
00826                      }
00827 
00828                      for (i = 0; i < strlen(status); i++) {
00829                             if (!isalnum(status[i]) && status[i] != '/' && status[i] != '-' && status[i] != '_' && status[i] != '.') {
00830                                    zlog(ZLOG_ERROR, "[pool %s] the status path '%s' must contain only the following characters '[alphanum]/_-.'", wp->config->name, status);
00831                                    return -1;
00832                             }
00833                      }
00834               }
00835 
00836               /* ping */
00837               if (wp->config->ping_path && *wp->config->ping_path) {
00838                      char *ping = wp->config->ping_path;
00839                      int i;
00840 
00841                      if (*ping != '/') {
00842                             zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' must start with a '/'", wp->config->name, ping);
00843                             return -1;
00844                      }
00845 
00846                      if (strlen(ping) < 2) {
00847                             zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' is not long enough", wp->config->name, ping);
00848                             return -1;
00849                      }
00850 
00851                      for (i = 0; i < strlen(ping); i++) {
00852                             if (!isalnum(ping[i]) && ping[i] != '/' && ping[i] != '-' && ping[i] != '_' && ping[i] != '.') {
00853                                    zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' must containt only the following characters '[alphanum]/_-.'", wp->config->name, ping);
00854                                    return -1;
00855                             }
00856                      }
00857 
00858                      if (!wp->config->ping_response) {
00859                             wp->config->ping_response = strdup("pong");
00860                      } else {
00861                             if (strlen(wp->config->ping_response) < 1) {
00862                                    zlog(ZLOG_ERROR, "[pool %s] the ping response page '%s' is not long enough", wp->config->name, wp->config->ping_response);
00863                                    return -1;
00864                             }
00865                      }
00866               } else {
00867                      if (wp->config->ping_response) {
00868                             free(wp->config->ping_response);
00869                             wp->config->ping_response = NULL;
00870                      }
00871               }
00872 
00873               /* access.log, access.format */
00874               if (wp->config->access_log && *wp->config->access_log) {
00875                      fpm_evaluate_full_path(&wp->config->access_log, wp, NULL, 0);
00876                      if (!wp->config->access_format) {
00877                             wp->config->access_format = strdup("%R - %u %t \"%m %r\" %s");
00878                      }
00879               }
00880 
00881               if (wp->config->request_terminate_timeout) {
00882                      fpm_globals.heartbeat = fpm_globals.heartbeat ? MIN(fpm_globals.heartbeat, (wp->config->request_terminate_timeout * 1000) / 3) : (wp->config->request_terminate_timeout * 1000) / 3;
00883               }
00884 
00885               /* slowlog */
00886               if (wp->config->slowlog && *wp->config->slowlog) {
00887                      fpm_evaluate_full_path(&wp->config->slowlog, wp, NULL, 0);
00888               }
00889 
00890               /* request_slowlog_timeout */
00891               if (wp->config->request_slowlog_timeout) {
00892 #if HAVE_FPM_TRACE
00893                      if (! (wp->config->slowlog && *wp->config->slowlog)) {
00894                             zlog(ZLOG_ERROR, "[pool %s] 'slowlog' must be specified for use with 'request_slowlog_timeout'", wp->config->name);
00895                             return -1;
00896                      }
00897 #else
00898                      static int warned = 0;
00899 
00900                      if (!warned) {
00901                             zlog(ZLOG_WARNING, "[pool %s] 'request_slowlog_timeout' is not supported on your system",  wp->config->name);
00902                             warned = 1;
00903                      }
00904 
00905                      wp->config->request_slowlog_timeout = 0;
00906 #endif
00907 
00908                      if (wp->config->slowlog && *wp->config->slowlog) {
00909                             int fd;
00910 
00911                             fd = open(wp->config->slowlog, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
00912 
00913                             if (0 > fd) {
00914                                    zlog(ZLOG_SYSERROR, "Unable to create or open slowlog(%s)", wp->config->slowlog);
00915                                    return -1;
00916                             }
00917                             close(fd);
00918                      }
00919 
00920                      fpm_globals.heartbeat = fpm_globals.heartbeat ? MIN(fpm_globals.heartbeat, (wp->config->request_slowlog_timeout * 1000) / 3) : (wp->config->request_slowlog_timeout * 1000) / 3;
00921 
00922                      if (wp->config->request_terminate_timeout && wp->config->request_slowlog_timeout > wp->config->request_terminate_timeout) {
00923                             zlog(ZLOG_ERROR, "[pool %s] 'request_slowlog_timeout' (%d) can't be greater than 'request_terminate_timeout' (%d)", wp->config->name, wp->config->request_slowlog_timeout, wp->config->request_terminate_timeout);
00924                             return -1;
00925                      }
00926               }
00927 
00928               /* chroot */
00929               if (wp->config->chroot && *wp->config->chroot) {
00930 
00931                      fpm_evaluate_full_path(&wp->config->chroot, wp, NULL, 1);
00932 
00933                      if (*wp->config->chroot != '/') {
00934                             zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' must start with a '/'", wp->config->name, wp->config->chroot);
00935                             return -1;
00936                      }
00937 
00938                      if (!fpm_conf_is_dir(wp->config->chroot)) {
00939                             zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' does not exist or is not a directory", wp->config->name, wp->config->chroot);
00940                             return -1;
00941                      }
00942               }
00943 
00944               /* chdir */
00945               if (wp->config->chdir && *wp->config->chdir) {
00946 
00947                      fpm_evaluate_full_path(&wp->config->chdir, wp, NULL, 0);
00948 
00949                      if (*wp->config->chdir != '/') {
00950                             zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' must start with a '/'", wp->config->name, wp->config->chdir);
00951                             return -1;
00952                      }
00953 
00954                      if (wp->config->chroot) {
00955                             char *buf;
00956 
00957                             spprintf(&buf, 0, "%s/%s", wp->config->chroot, wp->config->chdir);
00958 
00959                             if (!fpm_conf_is_dir(buf)) {
00960                                    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);
00961                                    efree(buf);
00962                                    return -1;
00963                             }
00964 
00965                             efree(buf);
00966                      } else {
00967                             if (!fpm_conf_is_dir(wp->config->chdir)) {
00968                                    zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' does not exist or is not a directory", wp->config->name, wp->config->chdir);
00969                                    return -1;
00970                             }
00971                      }
00972               }
00973 
00974               /* security.limit_extensions */
00975               if (!wp->config->security_limit_extensions) {
00976                      wp->config->security_limit_extensions = strdup(".php .phar");
00977               }
00978 
00979               if (*wp->config->security_limit_extensions) {
00980                      int nb_ext;
00981                      char *ext;
00982                      char *security_limit_extensions;
00983                      char *limit_extensions;
00984 
00985 
00986                      /* strdup because strtok(3) alters the string it parses */
00987                      security_limit_extensions = strdup(wp->config->security_limit_extensions);
00988                      limit_extensions = security_limit_extensions;
00989                      nb_ext = 0;
00990 
00991                      /* find the number of extensions */
00992                      while ((ext = strtok(limit_extensions, " \t"))) {
00993                             limit_extensions = NULL;
00994                             nb_ext++;
00995                      }
00996                      free(security_limit_extensions);
00997 
00998                      /* if something found */
00999                      if (nb_ext > 0) {
01000 
01001                             /* malloc the extension array */
01002                             wp->limit_extensions = malloc(sizeof(char *) * (nb_ext + 1));
01003                             if (!wp->limit_extensions) {
01004                                    zlog(ZLOG_ERROR, "[pool %s] unable to malloc extensions array", wp->config->name);
01005                                    return -1;
01006                             }
01007 
01008                             /* strdup because strtok(3) alters the string it parses */
01009                             security_limit_extensions = strdup(wp->config->security_limit_extensions);
01010                             limit_extensions = security_limit_extensions;
01011                             nb_ext = 0;
01012 
01013                             /* parse the string and save the extension in the array */
01014                             while ((ext = strtok(security_limit_extensions, " \t"))) {
01015                                    security_limit_extensions = NULL;
01016                                    wp->limit_extensions[nb_ext++] = strdup(ext);
01017                             }
01018 
01019                             /* end the array with NULL in order to parse it */
01020                             wp->limit_extensions[nb_ext] = NULL;
01021                             free(security_limit_extensions);
01022                      }
01023               }
01024 
01025               /* env[], php_value[], php_admin_values[] */
01026               if (!wp->config->chroot) {
01027                      struct key_value_s *kv;
01028                      char *options[] = FPM_PHP_INI_TO_EXPAND;
01029                      char **p;
01030 
01031                      for (kv = wp->config->php_values; kv; kv = kv->next) {
01032                             for (p = options; *p; p++) {
01033                                    if (!strcasecmp(kv->key, *p)) {
01034                                           fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
01035                                    }
01036                             }
01037                      }
01038                      for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
01039                             for (p = options; *p; p++) {
01040                                    if (!strcasecmp(kv->key, *p)) {
01041                                           fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
01042                                    }
01043                             }
01044                      }
01045               }
01046        }
01047        return 0;
01048 }
01049 /* }}} */
01050 
01051 int fpm_conf_unlink_pid() /* {{{ */
01052 {
01053        if (fpm_global_config.pid_file) {
01054               if (0 > unlink(fpm_global_config.pid_file)) {
01055                      zlog(ZLOG_SYSERROR, "Unable to remove the PID file (%s).", fpm_global_config.pid_file);
01056                      return -1;
01057               }
01058        }
01059        return 0;
01060 }
01061 /* }}} */
01062 
01063 int fpm_conf_write_pid() /* {{{ */
01064 {
01065        int fd;
01066 
01067        if (fpm_global_config.pid_file) {
01068               char buf[64];
01069               int len;
01070 
01071               unlink(fpm_global_config.pid_file);
01072               fd = creat(fpm_global_config.pid_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
01073 
01074               if (fd < 0) {
01075                      zlog(ZLOG_SYSERROR, "Unable to create the PID file (%s).", fpm_global_config.pid_file);
01076                      return -1;
01077               }
01078 
01079               len = sprintf(buf, "%d", (int) fpm_globals.parent_pid);
01080 
01081               if (len != write(fd, buf, len)) {
01082                      zlog(ZLOG_SYSERROR, "Unable to write to the PID file.");
01083                      return -1;
01084               }
01085               close(fd);
01086        }
01087        return 0;
01088 }
01089 /* }}} */
01090 
01091 static int fpm_conf_post_process(TSRMLS_D) /* {{{ */
01092 {
01093        struct fpm_worker_pool_s *wp;
01094 
01095        if (fpm_global_config.pid_file) {
01096               fpm_evaluate_full_path(&fpm_global_config.pid_file, NULL, PHP_LOCALSTATEDIR, 0);
01097        }
01098 
01099        fpm_globals.log_level = fpm_global_config.log_level;
01100 
01101        if (fpm_global_config.process_max < 0) {
01102               zlog(ZLOG_ERROR, "process_max can't be negative");
01103               return -1;
01104        }
01105 
01106        if (!fpm_global_config.error_log) {
01107               fpm_global_config.error_log = strdup("log/php-fpm.log");
01108        }
01109 
01110 #ifdef HAVE_SYSLOG_H
01111        if (!fpm_global_config.syslog_ident) {
01112               fpm_global_config.syslog_ident = strdup("php-fpm");
01113        }
01114 
01115        if (fpm_global_config.syslog_facility < 0) {
01116               fpm_global_config.syslog_facility = LOG_DAEMON;
01117        }
01118 
01119        if (strcasecmp(fpm_global_config.error_log, "syslog") != 0)
01120 #endif
01121        {
01122               fpm_evaluate_full_path(&fpm_global_config.error_log, NULL, PHP_LOCALSTATEDIR, 0);
01123        }
01124 
01125        if (0 > fpm_stdio_open_error_log(0)) {
01126               return -1;
01127        }
01128 
01129        if (0 > fpm_log_open(0)) {
01130               return -1;
01131        }
01132 
01133        if (0 > fpm_event_pre_init(fpm_global_config.events_mechanism)) {
01134               return -1;
01135        }
01136 
01137        if (0 > fpm_conf_process_all_pools()) {
01138               return -1;
01139        }
01140 
01141        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
01142               if (!wp->config->access_log || !*wp->config->access_log) {
01143                      continue;
01144               }
01145               if (0 > fpm_log_write(wp->config->access_format TSRMLS_CC)) {
01146                      zlog(ZLOG_ERROR, "[pool %s] wrong format for access.format '%s'", wp->config->name, wp->config->access_format);
01147                      return -1;
01148               }
01149        }
01150 
01151        return 0;
01152 }
01153 /* }}} */
01154 
01155 static void fpm_conf_cleanup(int which, void *arg) /* {{{ */
01156 {
01157        free(fpm_global_config.pid_file);
01158        free(fpm_global_config.error_log);
01159        free(fpm_global_config.events_mechanism);
01160        fpm_global_config.pid_file = 0;
01161        fpm_global_config.error_log = 0;
01162 #ifdef HAVE_SYSLOG_H
01163        free(fpm_global_config.syslog_ident);
01164        fpm_global_config.syslog_ident = 0;
01165 #endif
01166        free(fpm_globals.config);
01167 }
01168 /* }}} */
01169 
01170 static void fpm_conf_ini_parser_include(char *inc, void *arg TSRMLS_DC) /* {{{ */
01171 {
01172        char *filename;
01173        int *error = (int *)arg;;
01174        glob_t g;
01175        int i;
01176 
01177        if (!inc || !arg) return;
01178        if (*error) return; /* We got already an error. Switch to the end. */
01179        spprintf(&filename, 0, "%s", ini_filename); 
01180 
01181 #ifdef HAVE_GLOB
01182        {
01183               g.gl_offs = 0;
01184               if ((i = glob(inc, GLOB_ERR | GLOB_MARK | GLOB_NOSORT, NULL, &g)) != 0) {
01185 #ifdef GLOB_NOMATCH
01186                      if (i == GLOB_NOMATCH) {
01187                             zlog(ZLOG_WARNING, "Nothing matches the include pattern '%s' from %s at line %d.", inc, filename, ini_lineno);
01188                             efree(filename);
01189                             return;
01190                      } 
01191 #endif /* GLOB_NOMATCH */
01192                      zlog(ZLOG_ERROR, "Unable to globalize '%s' (ret=%d) from %s at line %d.", inc, i, filename, ini_lineno);
01193                      *error = 1;
01194                      efree(filename);
01195                      return;
01196               }
01197 
01198               for (i = 0; i < g.gl_pathc; i++) {
01199                      int len = strlen(g.gl_pathv[i]);
01200                      if (len < 1) continue;
01201                      if (g.gl_pathv[i][len - 1] == '/') continue; /* don't parse directories */
01202                      if (0 > fpm_conf_load_ini_file(g.gl_pathv[i] TSRMLS_CC)) {
01203                             zlog(ZLOG_ERROR, "Unable to include %s from %s at line %d", g.gl_pathv[i], filename, ini_lineno);
01204                             *error = 1;
01205                             efree(filename);
01206                             return;
01207                      }
01208               }
01209               globfree(&g);
01210        }
01211 #else /* HAVE_GLOB */
01212        if (0 > fpm_conf_load_ini_file(inc TSRMLS_CC)) {
01213               zlog(ZLOG_ERROR, "Unable to include %s from %s at line %d", inc, filename, ini_lineno);
01214               *error = 1;
01215               efree(filename);
01216               return;
01217        }
01218 #endif /* HAVE_GLOB */
01219 
01220        efree(filename);
01221 }
01222 /* }}} */
01223 
01224 static void fpm_conf_ini_parser_section(zval *section, void *arg TSRMLS_DC) /* {{{ */
01225 {
01226        struct fpm_worker_pool_s *wp;
01227        struct fpm_worker_pool_config_s *config;
01228        int *error = (int *)arg;
01229 
01230        /* switch to global conf */
01231        if (!strcasecmp(Z_STRVAL_P(section), "global")) {
01232               current_wp = NULL;
01233               return;
01234        }
01235 
01236        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
01237               if (!wp->config) continue;
01238               if (!wp->config->name) continue;
01239               if (!strcasecmp(wp->config->name, Z_STRVAL_P(section))) {
01240                      /* Found a wp with the same name. Bring it back */
01241                      current_wp = wp;
01242                      return;
01243               }
01244        }
01245 
01246        /* it's a new pool */
01247        config = (struct fpm_worker_pool_config_s *)fpm_worker_pool_config_alloc();
01248        if (!current_wp || !config) {
01249               zlog(ZLOG_ERROR, "[%s:%d] Unable to alloc a new WorkerPool for worker '%s'", ini_filename, ini_lineno, Z_STRVAL_P(section));
01250               *error = 1;
01251               return;
01252        }
01253        config->name = strdup(Z_STRVAL_P(section));
01254        if (!config->name) {
01255               zlog(ZLOG_ERROR, "[%s:%d] Unable to alloc memory for configuration name for worker '%s'", ini_filename, ini_lineno, Z_STRVAL_P(section));
01256               *error = 1;
01257               return;
01258        }
01259 }
01260 /* }}} */
01261 
01262 static void fpm_conf_ini_parser_entry(zval *name, zval *value, void *arg TSRMLS_DC) /* {{{ */
01263 {
01264        struct ini_value_parser_s *parser;
01265        void *config = NULL;
01266 
01267        int *error = (int *)arg;
01268        if (!value) {
01269               zlog(ZLOG_ERROR, "[%s:%d] value is NULL for a ZEND_INI_PARSER_ENTRY", ini_filename, ini_lineno);
01270               *error = 1;
01271               return;
01272        }
01273 
01274        if (!strcmp(Z_STRVAL_P(name), "include")) {
01275               if (ini_include) {
01276                      zlog(ZLOG_ERROR, "[%s:%d] two includes at the same time !", ini_filename, ini_lineno);
01277                      *error = 1;
01278                      return;
01279               }
01280               ini_include = strdup(Z_STRVAL_P(value));
01281               return;
01282        }
01283 
01284        if (!current_wp) { /* we are in the global section */
01285               parser = ini_fpm_global_options;
01286               config = &fpm_global_config;
01287        } else {
01288               parser = ini_fpm_pool_options;
01289               config = current_wp->config;
01290        }
01291 
01292        for (; parser->name; parser++) {
01293               if (!strcasecmp(parser->name, Z_STRVAL_P(name))) {
01294                      char *ret;
01295                      if (!parser->parser) {
01296                             zlog(ZLOG_ERROR, "[%s:%d] the parser for entry '%s' is not defined", ini_filename, ini_lineno, parser->name);
01297                             *error = 1;
01298                             return;
01299                      }
01300 
01301                      ret = parser->parser(value, &config, parser->offset);
01302                      if (ret) {
01303                             zlog(ZLOG_ERROR, "[%s:%d] unable to parse value for entry '%s': %s", ini_filename, ini_lineno, parser->name, ret);
01304                             *error = 1;
01305                             return;
01306                      }
01307 
01308                      /* all is good ! */
01309                      return;
01310               }
01311        }
01312 
01313        /* nothing has been found if we got here */
01314        zlog(ZLOG_ERROR, "[%s:%d] unknown entry '%s'", ini_filename, ini_lineno, Z_STRVAL_P(name));
01315        *error = 1;
01316 }
01317 /* }}} */
01318 
01319 static void fpm_conf_ini_parser_array(zval *name, zval *key, zval *value, void *arg TSRMLS_DC) /* {{{ */
01320 {
01321        int *error = (int *)arg;
01322        char *err = NULL;
01323        void *config;
01324 
01325        if (!Z_STRVAL_P(key) || !Z_STRVAL_P(value) || !*Z_STRVAL_P(key)) {
01326               zlog(ZLOG_ERROR, "[%s:%d] Misspelled  array ?", ini_filename, ini_lineno);
01327               *error = 1;
01328               return;
01329        }
01330        if (!current_wp || !current_wp->config) {
01331               zlog(ZLOG_ERROR, "[%s:%d] Array are not allowed in the global section", ini_filename, ini_lineno);
01332               *error = 1;
01333               return;
01334        }
01335 
01336        if (!strcmp("env", Z_STRVAL_P(name))) {
01337               if (!*Z_STRVAL_P(value)) {
01338                      zlog(ZLOG_ERROR, "[%s:%d] empty value", ini_filename, ini_lineno);
01339                      *error = 1;
01340                      return;
01341               }
01342               config = (char *)current_wp->config + WPO(env);
01343               err = fpm_conf_set_array(key, value, &config, 0);
01344 
01345        } else if (!strcmp("php_value", Z_STRVAL_P(name))) {
01346               config = (char *)current_wp->config + WPO(php_values);
01347               err = fpm_conf_set_array(key, value, &config, 0);
01348 
01349        } else if (!strcmp("php_admin_value", Z_STRVAL_P(name))) {
01350               config = (char *)current_wp->config + WPO(php_admin_values);
01351               err = fpm_conf_set_array(key, value, &config, 0);
01352 
01353        } else if (!strcmp("php_flag", Z_STRVAL_P(name))) {
01354               config = (char *)current_wp->config + WPO(php_values);
01355               err = fpm_conf_set_array(key, value, &config, 1);
01356 
01357        } else if (!strcmp("php_admin_flag", Z_STRVAL_P(name))) {
01358               config = (char *)current_wp->config + WPO(php_admin_values);
01359               err = fpm_conf_set_array(key, value, &config, 1);
01360 
01361        } else {
01362               zlog(ZLOG_ERROR, "[%s:%d] unknown directive '%s'", ini_filename, ini_lineno, Z_STRVAL_P(name));
01363               *error = 1;
01364               return;
01365        }
01366 
01367        if (err) {
01368               zlog(ZLOG_ERROR, "[%s:%d] error while parsing '%s[%s]' : %s", ini_filename, ini_lineno, Z_STRVAL_P(name), Z_STRVAL_P(key), err);
01369               *error = 1;
01370               return;
01371        }
01372 }
01373 /* }}} */
01374 
01375 static void fpm_conf_ini_parser(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC) /* {{{ */
01376 {
01377        int *error;
01378 
01379        if (!arg1 || !arg) return;
01380        error = (int *)arg;
01381        if (*error) return; /* We got already an error. Switch to the end. */
01382 
01383        switch(callback_type) {
01384               case ZEND_INI_PARSER_ENTRY:
01385                      fpm_conf_ini_parser_entry(arg1, arg2, error TSRMLS_CC);
01386                      break;;
01387               case ZEND_INI_PARSER_SECTION:
01388                      fpm_conf_ini_parser_section(arg1, error TSRMLS_CC);
01389                      break;;
01390               case ZEND_INI_PARSER_POP_ENTRY:
01391                      fpm_conf_ini_parser_array(arg1, arg3, arg2, error TSRMLS_CC);
01392                      break;;
01393               default:
01394                      zlog(ZLOG_ERROR, "[%s:%d] Unknown INI syntax", ini_filename, ini_lineno);
01395                      *error = 1;
01396                      break;;
01397        }
01398 }
01399 /* }}} */
01400 
01401 int fpm_conf_load_ini_file(char *filename TSRMLS_DC) /* {{{ */
01402 {
01403        int error = 0;
01404        char buf[1024+1];
01405        int fd, n;
01406        int nb_read = 1;
01407        char c = '*';
01408 
01409        int ret = 1;
01410 
01411        if (!filename || !filename[0]) {
01412               zlog(ZLOG_ERROR, "configuration filename is empty");
01413               return -1;
01414        }
01415 
01416        fd = open(filename, O_RDONLY, 0);
01417        if (fd < 0) {
01418               zlog(ZLOG_SYSERROR, "failed to open configuration file '%s'", filename);
01419               return -1;
01420        }
01421 
01422        if (ini_recursion++ > 4) {
01423               zlog(ZLOG_ERROR, "failed to include more than 5 files recusively");
01424               return -1;
01425        }
01426 
01427        ini_lineno = 0;
01428        while (nb_read > 0) {
01429               int tmp;
01430               memset(buf, 0, sizeof(char) * (1024 + 1));
01431               for (n = 0; n < 1024 && (nb_read = read(fd, &c, sizeof(char))) == sizeof(char) && c != '\n'; n++) {
01432                      buf[n] = c;
01433               }
01434               buf[n++] = '\n';
01435               ini_lineno++;
01436               ini_filename = filename;
01437               tmp = zend_parse_ini_string(buf, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t)fpm_conf_ini_parser, &error TSRMLS_CC);
01438               ini_filename = filename;
01439               if (error || tmp == FAILURE) {
01440                      if (ini_include) free(ini_include);
01441                      ini_recursion--;
01442                      close(fd);
01443                      return -1;
01444               }
01445               if (ini_include) {
01446                      char *tmp = ini_include;
01447                      ini_include = NULL;
01448                      fpm_evaluate_full_path(&tmp, NULL, NULL, 0);
01449                      fpm_conf_ini_parser_include(tmp, &error TSRMLS_CC);
01450                      if (error) {
01451                             free(tmp);
01452                             ini_recursion--;
01453                             close(fd);
01454                             return -1;
01455                      }
01456                      free(tmp);
01457               }
01458        }
01459 
01460        ini_recursion--;
01461        close(fd);
01462        return ret;
01463 
01464 }
01465 /* }}} */
01466 
01467 static void fpm_conf_dump() /* {{{ */
01468 {
01469        struct fpm_worker_pool_s *wp;
01470 
01471        /*
01472         * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
01473         */
01474        zlog(ZLOG_NOTICE, "[General]");
01475        zlog(ZLOG_NOTICE, "\tpid = %s",                         STR2STR(fpm_global_config.pid_file));
01476        zlog(ZLOG_NOTICE, "\terror_log = %s",                   STR2STR(fpm_global_config.error_log));
01477 #ifdef HAVE_SYSLOG_H
01478        zlog(ZLOG_NOTICE, "\tsyslog.ident = %s",                STR2STR(fpm_global_config.syslog_ident));
01479        zlog(ZLOG_NOTICE, "\tsyslog.facility = %d",             fpm_global_config.syslog_facility); /* FIXME: convert to string */
01480 #endif
01481        zlog(ZLOG_NOTICE, "\tlog_level = %s",                   zlog_get_level_name(fpm_globals.log_level));
01482        zlog(ZLOG_NOTICE, "\temergency_restart_interval = %ds", fpm_global_config.emergency_restart_interval);
01483        zlog(ZLOG_NOTICE, "\temergency_restart_threshold = %d", fpm_global_config.emergency_restart_threshold);
01484        zlog(ZLOG_NOTICE, "\tprocess_control_timeout = %ds",    fpm_global_config.process_control_timeout);
01485        zlog(ZLOG_NOTICE, "\tprocess.max = %d",                 fpm_global_config.process_max);
01486        zlog(ZLOG_NOTICE, "\tdaemonize = %s",                   BOOL2STR(fpm_global_config.daemonize));
01487        zlog(ZLOG_NOTICE, "\trlimit_files = %d",                fpm_global_config.rlimit_files);
01488        zlog(ZLOG_NOTICE, "\trlimit_core = %d",                 fpm_global_config.rlimit_core);
01489        zlog(ZLOG_NOTICE, "\tevents.mechanism = %s",            fpm_event_machanism_name());
01490        zlog(ZLOG_NOTICE, " ");
01491 
01492        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
01493               struct key_value_s *kv;
01494               if (!wp->config) continue;
01495               zlog(ZLOG_NOTICE, "[%s]",                              STR2STR(wp->config->name));
01496               zlog(ZLOG_NOTICE, "\tprefix = %s",                     STR2STR(wp->config->prefix));
01497               zlog(ZLOG_NOTICE, "\tuser = %s",                       STR2STR(wp->config->user));
01498               zlog(ZLOG_NOTICE, "\tgroup = %s",                      STR2STR(wp->config->group));
01499               zlog(ZLOG_NOTICE, "\tlisten = %s",                     STR2STR(wp->config->listen_address));
01500               zlog(ZLOG_NOTICE, "\tlisten.backlog = %d",             wp->config->listen_backlog);
01501               zlog(ZLOG_NOTICE, "\tlisten.owner = %s",               STR2STR(wp->config->listen_owner));
01502               zlog(ZLOG_NOTICE, "\tlisten.group = %s",               STR2STR(wp->config->listen_group));
01503               zlog(ZLOG_NOTICE, "\tlisten.mode = %s",                STR2STR(wp->config->listen_mode));
01504               zlog(ZLOG_NOTICE, "\tlisten.allowed_clients = %s",     STR2STR(wp->config->listen_allowed_clients));
01505               zlog(ZLOG_NOTICE, "\tpm = %s",                         PM2STR(wp->config->pm));
01506               zlog(ZLOG_NOTICE, "\tpm.max_children = %d",            wp->config->pm_max_children);
01507               zlog(ZLOG_NOTICE, "\tpm.start_servers = %d",           wp->config->pm_start_servers);
01508               zlog(ZLOG_NOTICE, "\tpm.min_spare_servers = %d",       wp->config->pm_min_spare_servers);
01509               zlog(ZLOG_NOTICE, "\tpm.max_spare_servers = %d",       wp->config->pm_max_spare_servers);
01510               zlog(ZLOG_NOTICE, "\tpm.process_idle_timeout = %d",    wp->config->pm_process_idle_timeout);
01511               zlog(ZLOG_NOTICE, "\tpm.max_requests = %d",            wp->config->pm_max_requests);
01512               zlog(ZLOG_NOTICE, "\tpm.status_path = %s",             STR2STR(wp->config->pm_status_path));
01513               zlog(ZLOG_NOTICE, "\tping.path = %s",                  STR2STR(wp->config->ping_path));
01514               zlog(ZLOG_NOTICE, "\tping.response = %s",              STR2STR(wp->config->ping_response));
01515               zlog(ZLOG_NOTICE, "\taccess.log = %s",                 STR2STR(wp->config->access_log));
01516               zlog(ZLOG_NOTICE, "\taccess.format = %s",              STR2STR(wp->config->access_format));
01517               zlog(ZLOG_NOTICE, "\tslowlog = %s",                    STR2STR(wp->config->slowlog));
01518               zlog(ZLOG_NOTICE, "\trequest_slowlog_timeout = %ds",   wp->config->request_slowlog_timeout);
01519               zlog(ZLOG_NOTICE, "\trequest_terminate_timeout = %ds", wp->config->request_terminate_timeout);
01520               zlog(ZLOG_NOTICE, "\trlimit_files = %d",               wp->config->rlimit_files);
01521               zlog(ZLOG_NOTICE, "\trlimit_core = %d",                wp->config->rlimit_core);
01522               zlog(ZLOG_NOTICE, "\tchroot = %s",                     STR2STR(wp->config->chroot));
01523               zlog(ZLOG_NOTICE, "\tchdir = %s",                      STR2STR(wp->config->chdir));
01524               zlog(ZLOG_NOTICE, "\tcatch_workers_output = %s",       BOOL2STR(wp->config->catch_workers_output));
01525               zlog(ZLOG_NOTICE, "\tsecurity.limit_extensions = %s",  wp->config->security_limit_extensions);
01526 
01527               for (kv = wp->config->env; kv; kv = kv->next) {
01528                      zlog(ZLOG_NOTICE, "\tenv[%s] = %s", kv->key, kv->value);
01529               }
01530 
01531               for (kv = wp->config->php_values; kv; kv = kv->next) {
01532                      zlog(ZLOG_NOTICE, "\tphp_value[%s] = %s", kv->key, kv->value);
01533               }
01534 
01535               for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
01536                      zlog(ZLOG_NOTICE, "\tphp_admin_value[%s] = %s", kv->key, kv->value);
01537               }
01538               zlog(ZLOG_NOTICE, " ");
01539        }
01540 }
01541 /* }}} */
01542 
01543 int fpm_conf_init_main(int test_conf) /* {{{ */
01544 {
01545        int ret;
01546        TSRMLS_FETCH();
01547 
01548        if (fpm_globals.prefix && *fpm_globals.prefix) {
01549               if (!fpm_conf_is_dir(fpm_globals.prefix)) {
01550                      zlog(ZLOG_ERROR, "the global prefix '%s' does not exist or is not a directory", fpm_globals.prefix);
01551                      return -1;
01552               }
01553        }
01554 
01555        if (fpm_globals.pid && *fpm_globals.pid) {
01556               fpm_global_config.pid_file = strdup(fpm_globals.pid);
01557        }
01558 
01559        if (fpm_globals.config == NULL) {
01560               char *tmp;
01561 
01562               if (fpm_globals.prefix == NULL) {
01563                      spprintf(&tmp, 0, "%s/php-fpm.conf", PHP_SYSCONFDIR);
01564               } else {
01565                      spprintf(&tmp, 0, "%s/etc/php-fpm.conf", fpm_globals.prefix);
01566               }
01567 
01568               if (!tmp) {
01569                      zlog(ZLOG_SYSERROR, "spprintf() failed (tmp for fpm_globals.config)");
01570                      return -1;
01571               }
01572 
01573               fpm_globals.config = strdup(tmp);
01574               efree(tmp);
01575 
01576               if (!fpm_globals.config) {
01577                      zlog(ZLOG_SYSERROR, "spprintf() failed (fpm_globals.config)");
01578                      return -1;
01579               }
01580        }
01581 
01582        ret = fpm_conf_load_ini_file(fpm_globals.config TSRMLS_CC);
01583 
01584        if (0 > ret) {
01585               zlog(ZLOG_ERROR, "failed to load configuration file '%s'", fpm_globals.config);
01586               return -1;
01587        }
01588 
01589        if (0 > fpm_conf_post_process(TSRMLS_C)) {
01590               zlog(ZLOG_ERROR, "failed to post process the configuration");
01591               return -1;
01592        }
01593 
01594        if (test_conf) {
01595               if (test_conf > 1) {
01596                      fpm_conf_dump();
01597               }
01598               zlog(ZLOG_NOTICE, "configuration file %s test is successful\n", fpm_globals.config);
01599               fpm_globals.test_successful = 1;
01600               return -1;
01601        }
01602 
01603        if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_conf_cleanup, 0)) {
01604               return -1;
01605        }
01606 
01607        return 0;
01608 }
01609 /* }}} */