1
2 /* $Id: fpm_conf.c,v 1.33.2.3 2008/12/13 03:50:29 anight Exp $ */
3 /* (c) 2007,2008 Andrei Nigmatulin */
4
5 #include "fpm_config.h"
6
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <stddef.h>
13 #include <string.h>
14 #if HAVE_INTTYPES_H
15 # include <inttypes.h>
16 #else
17 # include <stdint.h>
18 #endif
19 #ifdef HAVE_GLOB
20 # ifndef PHP_WIN32
21 # include <glob.h>
22 # else
23 # include "win32/glob.h"
24 # endif
25 #endif
26
27 #include <stdio.h>
28 #include <unistd.h>
29
30 #include "php.h"
31 #include "zend_ini_scanner.h"
32 #include "zend_globals.h"
33 #include "zend_stream.h"
34 #include "php_syslog.h"
35
36 #include "fpm.h"
37 #include "fpm_conf.h"
38 #include "fpm_stdio.h"
39 #include "fpm_worker_pool.h"
40 #include "fpm_cleanup.h"
41 #include "fpm_php.h"
42 #include "fpm_sockets.h"
43 #include "fpm_shm.h"
44 #include "fpm_status.h"
45 #include "fpm_log.h"
46 #include "fpm_events.h"
47 #include "zlog.h"
48 #ifdef HAVE_SYSTEMD
49 #include "fpm_systemd.h"
50 #endif
51
52
53 #define STR2STR(a) (a ? a : "undefined")
54 #define BOOL2STR(a) (a ? "yes" : "no")
55 #define GO(field) offsetof(struct fpm_global_config_s, field)
56 #define WPO(field) offsetof(struct fpm_worker_pool_config_s, field)
57
58 static int fpm_conf_load_ini_file(char *filename TSRMLS_DC);
59 static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset);
60 #if 0 /* not used for now */
61 static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset);
62 #endif
63 static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset);
64 static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset);
65 static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset);
66 static char *fpm_conf_set_log_level(zval *value, void **config, intptr_t offset);
67 static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offset);
68 static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset);
69 #ifdef HAVE_SYSLOG_H
70 static char *fpm_conf_set_syslog_facility(zval *value, void **config, intptr_t offset);
71 #endif
72
73 struct fpm_global_config_s fpm_global_config = {
74 .daemonize = 1,
75 #ifdef HAVE_SYSLOG_H
76 .syslog_facility = -1,
77 #endif
78 .process_max = 0,
79 .process_priority = 64, /* 64 means unset */
80 #ifdef HAVE_SYSTEMD
81 .systemd_watchdog = 0,
82 .systemd_interval = -1, /* -1 means not set */
83 #endif
84 };
85 static struct fpm_worker_pool_s *current_wp = NULL;
86 static int ini_recursion = 0;
87 static char *ini_filename = NULL;
88 static int ini_lineno = 0;
89 static char *ini_include = NULL;
90
91 /*
92 * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
93 */
94 static struct ini_value_parser_s ini_fpm_global_options[] = {
95 { "pid", &fpm_conf_set_string, GO(pid_file) },
96 { "error_log", &fpm_conf_set_string, GO(error_log) },
97 #ifdef HAVE_SYSLOG_H
98 { "syslog.ident", &fpm_conf_set_string, GO(syslog_ident) },
99 { "syslog.facility", &fpm_conf_set_syslog_facility, GO(syslog_facility) },
100 #endif
101 { "log_level", &fpm_conf_set_log_level, GO(log_level) },
102 { "emergency_restart_threshold", &fpm_conf_set_integer, GO(emergency_restart_threshold) },
103 { "emergency_restart_interval", &fpm_conf_set_time, GO(emergency_restart_interval) },
104 { "process_control_timeout", &fpm_conf_set_time, GO(process_control_timeout) },
105 { "process.max", &fpm_conf_set_integer, GO(process_max) },
106 { "process.priority", &fpm_conf_set_integer, GO(process_priority) },
107 { "daemonize", &fpm_conf_set_boolean, GO(daemonize) },
108 { "rlimit_files", &fpm_conf_set_integer, GO(rlimit_files) },
109 { "rlimit_core", &fpm_conf_set_rlimit_core, GO(rlimit_core) },
110 { "events.mechanism", &fpm_conf_set_string, GO(events_mechanism) },
111 #ifdef HAVE_SYSTEMD
112 { "systemd_interval", &fpm_conf_set_time, GO(systemd_interval) },
113 #endif
114 { 0, 0, 0 }
115 };
116
117 /*
118 * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
119 */
120 static struct ini_value_parser_s ini_fpm_pool_options[] = {
121 { "prefix", &fpm_conf_set_string, WPO(prefix) },
122 { "user", &fpm_conf_set_string, WPO(user) },
123 { "group", &fpm_conf_set_string, WPO(group) },
124 { "listen", &fpm_conf_set_string, WPO(listen_address) },
125 { "listen.backlog", &fpm_conf_set_integer, WPO(listen_backlog) },
126 { "listen.owner", &fpm_conf_set_string, WPO(listen_owner) },
127 { "listen.group", &fpm_conf_set_string, WPO(listen_group) },
128 { "listen.mode", &fpm_conf_set_string, WPO(listen_mode) },
129 { "listen.allowed_clients", &fpm_conf_set_string, WPO(listen_allowed_clients) },
130 { "process.priority", &fpm_conf_set_integer, WPO(process_priority) },
131 { "pm", &fpm_conf_set_pm, WPO(pm) },
132 { "pm.max_children", &fpm_conf_set_integer, WPO(pm_max_children) },
133 { "pm.start_servers", &fpm_conf_set_integer, WPO(pm_start_servers) },
134 { "pm.min_spare_servers", &fpm_conf_set_integer, WPO(pm_min_spare_servers) },
135 { "pm.max_spare_servers", &fpm_conf_set_integer, WPO(pm_max_spare_servers) },
136 { "pm.process_idle_timeout", &fpm_conf_set_time, WPO(pm_process_idle_timeout) },
137 { "pm.max_requests", &fpm_conf_set_integer, WPO(pm_max_requests) },
138 { "pm.status_path", &fpm_conf_set_string, WPO(pm_status_path) },
139 { "ping.path", &fpm_conf_set_string, WPO(ping_path) },
140 { "ping.response", &fpm_conf_set_string, WPO(ping_response) },
141 { "access.log", &fpm_conf_set_string, WPO(access_log) },
142 { "access.format", &fpm_conf_set_string, WPO(access_format) },
143 { "slowlog", &fpm_conf_set_string, WPO(slowlog) },
144 { "request_slowlog_timeout", &fpm_conf_set_time, WPO(request_slowlog_timeout) },
145 { "request_terminate_timeout", &fpm_conf_set_time, WPO(request_terminate_timeout) },
146 { "rlimit_files", &fpm_conf_set_integer, WPO(rlimit_files) },
147 { "rlimit_core", &fpm_conf_set_rlimit_core, WPO(rlimit_core) },
148 { "chroot", &fpm_conf_set_string, WPO(chroot) },
149 { "chdir", &fpm_conf_set_string, WPO(chdir) },
150 { "catch_workers_output", &fpm_conf_set_boolean, WPO(catch_workers_output) },
151 { "clear_env", &fpm_conf_set_boolean, WPO(clear_env) },
152 { "security.limit_extensions", &fpm_conf_set_string, WPO(security_limit_extensions) },
153 { 0, 0, 0 }
154 };
155
fpm_conf_is_dir(char * path)156 static int fpm_conf_is_dir(char *path) /* {{{ */
157 {
158 struct stat sb;
159
160 if (stat(path, &sb) != 0) {
161 return 0;
162 }
163
164 return (sb.st_mode & S_IFMT) == S_IFDIR;
165 }
166 /* }}} */
167
168 /*
169 * Expands the '$pool' token in a dynamically allocated string
170 */
fpm_conf_expand_pool_name(char ** value)171 static int fpm_conf_expand_pool_name(char **value) {
172 char *token;
173
174 if (!value || !*value) {
175 return 0;
176 }
177
178 while (*value && (token = strstr(*value, "$pool"))) {
179 char *buf;
180 char *p2 = token + strlen("$pool");
181
182 /* If we are not in a pool, we cannot expand this name now */
183 if (!current_wp || !current_wp->config || !current_wp->config->name) {
184 return -1;
185 }
186
187 /* "aaa$poolbbb" becomes "aaa\0oolbbb" */
188 token[0] = '\0';
189
190 /* Build a brand new string with the expanded token */
191 spprintf(&buf, 0, "%s%s%s", *value, current_wp->config->name, p2);
192
193 /* Free the previous value and save the new one */
194 free(*value);
195 *value = strdup(buf);
196 efree(buf);
197 }
198
199 return 0;
200 }
201
fpm_conf_set_boolean(zval * value,void ** config,intptr_t offset)202 static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset) /* {{{ */
203 {
204 char *val = Z_STRVAL_P(value);
205 long value_y = !strcasecmp(val, "1");
206 long value_n = !strcasecmp(val, "");
207
208 if (!value_y && !value_n) {
209 return "invalid boolean value";
210 }
211
212 * (int *) ((char *) *config + offset) = value_y ? 1 : 0;
213 return NULL;
214 }
215 /* }}} */
216
fpm_conf_set_string(zval * value,void ** config,intptr_t offset)217 static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset) /* {{{ */
218 {
219 char **config_val = (char **) ((char *) *config + offset);
220
221 if (!config_val) {
222 return "internal error: NULL value";
223 }
224
225 /* Check if there is a previous value to deallocate */
226 if (*config_val) {
227 free(*config_val);
228 }
229
230 *config_val = strdup(Z_STRVAL_P(value));
231 if (!*config_val) {
232 return "fpm_conf_set_string(): strdup() failed";
233 }
234 if (fpm_conf_expand_pool_name(config_val) == -1) {
235 return "Can't use '$pool' when the pool is not defined";
236 }
237
238 return NULL;
239 }
240 /* }}} */
241
fpm_conf_set_integer(zval * value,void ** config,intptr_t offset)242 static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset) /* {{{ */
243 {
244 char *val = Z_STRVAL_P(value);
245 char *p;
246
247 /* we don't use strtol because we don't want to allow negative values */
248 for (p = val; *p; p++) {
249 if (p == val && *p == '-') continue;
250 if (*p < '0' || *p > '9') {
251 return "is not a valid number (greater or equal than zero)";
252 }
253 }
254 * (int *) ((char *) *config + offset) = atoi(val);
255 return NULL;
256 }
257 /* }}} */
258
259 #if 0 /* not used for now */
260 static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset) /* {{{ */
261 {
262 char *val = Z_STRVAL_P(value);
263 char *p;
264
265 for (p = val; *p; p++) {
266 if ( p == val && *p == '-' ) continue;
267 if (*p < '0' || *p > '9') {
268 return "is not a valid number (greater or equal than zero)";
269 }
270 }
271 * (long int *) ((char *) *config + offset) = atol(val);
272 return NULL;
273 }
274 /* }}} */
275 #endif
276
fpm_conf_set_time(zval * value,void ** config,intptr_t offset)277 static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset) /* {{{ */
278 {
279 char *val = Z_STRVAL_P(value);
280 int len = strlen(val);
281 char suffix;
282 int seconds;
283 if (!len) {
284 return "invalid time value";
285 }
286
287 suffix = val[len-1];
288 switch (suffix) {
289 case 'm' :
290 val[len-1] = '\0';
291 seconds = 60 * atoi(val);
292 break;
293 case 'h' :
294 val[len-1] = '\0';
295 seconds = 60 * 60 * atoi(val);
296 break;
297 case 'd' :
298 val[len-1] = '\0';
299 seconds = 24 * 60 * 60 * atoi(val);
300 break;
301 case 's' : /* s is the default suffix */
302 val[len-1] = '\0';
303 suffix = '0';
304 default :
305 if (suffix < '0' || suffix > '9') {
306 return "unknown suffix used in time value";
307 }
308 seconds = atoi(val);
309 break;
310 }
311
312 * (int *) ((char *) *config + offset) = seconds;
313 return NULL;
314 }
315 /* }}} */
316
fpm_conf_set_log_level(zval * value,void ** config,intptr_t offset)317 static char *fpm_conf_set_log_level(zval *value, void **config, intptr_t offset) /* {{{ */
318 {
319 char *val = Z_STRVAL_P(value);
320 int log_level;
321
322 if (!strcasecmp(val, "debug")) {
323 log_level = ZLOG_DEBUG;
324 } else if (!strcasecmp(val, "notice")) {
325 log_level = ZLOG_NOTICE;
326 } else if (!strcasecmp(val, "warning") || !strcasecmp(val, "warn")) {
327 log_level = ZLOG_WARNING;
328 } else if (!strcasecmp(val, "error")) {
329 log_level = ZLOG_ERROR;
330 } else if (!strcasecmp(val, "alert")) {
331 log_level = ZLOG_ALERT;
332 } else {
333 return "invalid value for 'log_level'";
334 }
335
336 * (int *) ((char *) *config + offset) = log_level;
337 return NULL;
338 }
339 /* }}} */
340
341 #ifdef HAVE_SYSLOG_H
fpm_conf_set_syslog_facility(zval * value,void ** config,intptr_t offset)342 static char *fpm_conf_set_syslog_facility(zval *value, void **config, intptr_t offset) /* {{{ */
343 {
344 char *val = Z_STRVAL_P(value);
345 int *conf = (int *) ((char *) *config + offset);
346
347 #ifdef LOG_AUTH
348 if (!strcasecmp(val, "AUTH")) {
349 *conf = LOG_AUTH;
350 return NULL;
351 }
352 #endif
353
354 #ifdef LOG_AUTHPRIV
355 if (!strcasecmp(val, "AUTHPRIV")) {
356 *conf = LOG_AUTHPRIV;
357 return NULL;
358 }
359 #endif
360
361 #ifdef LOG_CRON
362 if (!strcasecmp(val, "CRON")) {
363 *conf = LOG_CRON;
364 return NULL;
365 }
366 #endif
367
368 #ifdef LOG_DAEMON
369 if (!strcasecmp(val, "DAEMON")) {
370 *conf = LOG_DAEMON;
371 return NULL;
372 }
373 #endif
374
375 #ifdef LOG_FTP
376 if (!strcasecmp(val, "FTP")) {
377 *conf = LOG_FTP;
378 return NULL;
379 }
380 #endif
381
382 #ifdef LOG_KERN
383 if (!strcasecmp(val, "KERN")) {
384 *conf = LOG_KERN;
385 return NULL;
386 }
387 #endif
388
389 #ifdef LOG_LPR
390 if (!strcasecmp(val, "LPR")) {
391 *conf = LOG_LPR;
392 return NULL;
393 }
394 #endif
395
396 #ifdef LOG_MAIL
397 if (!strcasecmp(val, "MAIL")) {
398 *conf = LOG_MAIL;
399 return NULL;
400 }
401 #endif
402
403 #ifdef LOG_NEWS
404 if (!strcasecmp(val, "NEWS")) {
405 *conf = LOG_NEWS;
406 return NULL;
407 }
408 #endif
409
410 #ifdef LOG_SYSLOG
411 if (!strcasecmp(val, "SYSLOG")) {
412 *conf = LOG_SYSLOG;
413 return NULL;
414 }
415 #endif
416
417 #ifdef LOG_USER
418 if (!strcasecmp(val, "USER")) {
419 *conf = LOG_USER;
420 return NULL;
421 }
422 #endif
423
424 #ifdef LOG_UUCP
425 if (!strcasecmp(val, "UUCP")) {
426 *conf = LOG_UUCP;
427 return NULL;
428 }
429 #endif
430
431 #ifdef LOG_LOCAL0
432 if (!strcasecmp(val, "LOCAL0")) {
433 *conf = LOG_LOCAL0;
434 return NULL;
435 }
436 #endif
437
438 #ifdef LOG_LOCAL1
439 if (!strcasecmp(val, "LOCAL1")) {
440 *conf = LOG_LOCAL1;
441 return NULL;
442 }
443 #endif
444
445 #ifdef LOG_LOCAL2
446 if (!strcasecmp(val, "LOCAL2")) {
447 *conf = LOG_LOCAL2;
448 return NULL;
449 }
450 #endif
451
452 #ifdef LOG_LOCAL3
453 if (!strcasecmp(val, "LOCAL3")) {
454 *conf = LOG_LOCAL3;
455 return NULL;
456 }
457 #endif
458
459 #ifdef LOG_LOCAL4
460 if (!strcasecmp(val, "LOCAL4")) {
461 *conf = LOG_LOCAL4;
462 return NULL;
463 }
464 #endif
465
466 #ifdef LOG_LOCAL5
467 if (!strcasecmp(val, "LOCAL5")) {
468 *conf = LOG_LOCAL5;
469 return NULL;
470 }
471 #endif
472
473 #ifdef LOG_LOCAL6
474 if (!strcasecmp(val, "LOCAL6")) {
475 *conf = LOG_LOCAL6;
476 return NULL;
477 }
478 #endif
479
480 #ifdef LOG_LOCAL7
481 if (!strcasecmp(val, "LOCAL7")) {
482 *conf = LOG_LOCAL7;
483 return NULL;
484 }
485 #endif
486
487 return "invalid value";
488 }
489 /* }}} */
490 #endif
491
fpm_conf_set_rlimit_core(zval * value,void ** config,intptr_t offset)492 static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offset) /* {{{ */
493 {
494 char *val = Z_STRVAL_P(value);
495 int *ptr = (int *) ((char *) *config + offset);
496
497 if (!strcasecmp(val, "unlimited")) {
498 *ptr = -1;
499 } else {
500 int int_value;
501 void *subconf = &int_value;
502 char *error;
503
504 error = fpm_conf_set_integer(value, &subconf, 0);
505
506 if (error) {
507 return error;
508 }
509
510 if (int_value < 0) {
511 return "must be greater than zero or 'unlimited'";
512 }
513
514 *ptr = int_value;
515 }
516
517 return NULL;
518 }
519 /* }}} */
520
fpm_conf_set_pm(zval * value,void ** config,intptr_t offset)521 static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset) /* {{{ */
522 {
523 char *val = Z_STRVAL_P(value);
524 struct fpm_worker_pool_config_s *c = *config;
525 if (!strcasecmp(val, "static")) {
526 c->pm = PM_STYLE_STATIC;
527 } else if (!strcasecmp(val, "dynamic")) {
528 c->pm = PM_STYLE_DYNAMIC;
529 } else if (!strcasecmp(val, "ondemand")) {
530 c->pm = PM_STYLE_ONDEMAND;
531 } else {
532 return "invalid process manager (static, dynamic or ondemand)";
533 }
534 return NULL;
535 }
536 /* }}} */
537
fpm_conf_set_array(zval * key,zval * value,void ** config,int convert_to_bool)538 static char *fpm_conf_set_array(zval *key, zval *value, void **config, int convert_to_bool) /* {{{ */
539 {
540 struct key_value_s *kv;
541 struct key_value_s ***parent = (struct key_value_s ***) config;
542 int b;
543 void *subconf = &b;
544
545 kv = malloc(sizeof(*kv));
546
547 if (!kv) {
548 return "malloc() failed";
549 }
550
551 memset(kv, 0, sizeof(*kv));
552 kv->key = strdup(Z_STRVAL_P(key));
553
554 if (!kv->key) {
555 free(kv);
556 return "fpm_conf_set_array: strdup(key) failed";
557 }
558
559 if (convert_to_bool) {
560 char *err = fpm_conf_set_boolean(value, &subconf, 0);
561 if (err) {
562 free(kv->key);
563 free(kv);
564 return err;
565 }
566 kv->value = strdup(b ? "1" : "0");
567 } else {
568 kv->value = strdup(Z_STRVAL_P(value));
569 if (fpm_conf_expand_pool_name(&kv->value) == -1) {
570 return "Can't use '$pool' when the pool is not defined";
571 }
572 }
573
574 if (!kv->value) {
575 free(kv->key);
576 free(kv);
577 return "fpm_conf_set_array: strdup(value) failed";
578 }
579
580 kv->next = **parent;
581 **parent = kv;
582 return NULL;
583 }
584 /* }}} */
585
fpm_worker_pool_config_alloc()586 static void *fpm_worker_pool_config_alloc() /* {{{ */
587 {
588 struct fpm_worker_pool_s *wp;
589
590 wp = fpm_worker_pool_alloc();
591
592 if (!wp) {
593 return 0;
594 }
595
596 wp->config = malloc(sizeof(struct fpm_worker_pool_config_s));
597
598 if (!wp->config) {
599 fpm_worker_pool_free(wp);
600 return 0;
601 }
602
603 memset(wp->config, 0, sizeof(struct fpm_worker_pool_config_s));
604 wp->config->listen_backlog = FPM_BACKLOG_DEFAULT;
605 wp->config->pm_process_idle_timeout = 10; /* 10s by default */
606 wp->config->process_priority = 64; /* 64 means unset */
607 wp->config->clear_env = 1;
608
609 if (!fpm_worker_all_pools) {
610 fpm_worker_all_pools = wp;
611 } else {
612 struct fpm_worker_pool_s *tmp = fpm_worker_all_pools;
613 while (tmp) {
614 if (!tmp->next) {
615 tmp->next = wp;
616 break;
617 }
618 tmp = tmp->next;
619 }
620 }
621
622 current_wp = wp;
623 return wp->config;
624 }
625 /* }}} */
626
fpm_worker_pool_config_free(struct fpm_worker_pool_config_s * wpc)627 int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc) /* {{{ */
628 {
629 struct key_value_s *kv, *kv_next;
630
631 free(wpc->name);
632 free(wpc->prefix);
633 free(wpc->user);
634 free(wpc->group);
635 free(wpc->listen_address);
636 free(wpc->listen_owner);
637 free(wpc->listen_group);
638 free(wpc->listen_mode);
639 free(wpc->listen_allowed_clients);
640 free(wpc->pm_status_path);
641 free(wpc->ping_path);
642 free(wpc->ping_response);
643 free(wpc->access_log);
644 free(wpc->access_format);
645 free(wpc->slowlog);
646 free(wpc->chroot);
647 free(wpc->chdir);
648 free(wpc->security_limit_extensions);
649
650 for (kv = wpc->php_values; kv; kv = kv_next) {
651 kv_next = kv->next;
652 free(kv->key);
653 free(kv->value);
654 free(kv);
655 }
656 for (kv = wpc->php_admin_values; kv; kv = kv_next) {
657 kv_next = kv->next;
658 free(kv->key);
659 free(kv->value);
660 free(kv);
661 }
662 for (kv = wpc->env; kv; kv = kv_next) {
663 kv_next = kv->next;
664 free(kv->key);
665 free(kv->value);
666 free(kv);
667 }
668
669 return 0;
670 }
671 /* }}} */
672
fpm_evaluate_full_path(char ** path,struct fpm_worker_pool_s * wp,char * default_prefix,int expand)673 static int fpm_evaluate_full_path(char **path, struct fpm_worker_pool_s *wp, char *default_prefix, int expand) /* {{{ */
674 {
675 char *prefix = NULL;
676 char *full_path;
677
678 if (!path || !*path || **path == '/') {
679 return 0;
680 }
681
682 if (wp && wp->config) {
683 prefix = wp->config->prefix;
684 }
685
686 /* if the wp prefix is not set */
687 if (prefix == NULL) {
688 prefix = fpm_globals.prefix;
689 }
690
691 /* if the global prefix is not set */
692 if (prefix == NULL) {
693 prefix = default_prefix ? default_prefix : PHP_PREFIX;
694 }
695
696 if (expand) {
697 char *tmp;
698 tmp = strstr(*path, "$prefix");
699 if (tmp != NULL) {
700
701 if (tmp != *path) {
702 zlog(ZLOG_ERROR, "'$prefix' must be use at the beginning of the value");
703 return -1;
704 }
705
706 if (strlen(*path) > strlen("$prefix")) {
707 free(*path);
708 tmp = strdup((*path) + strlen("$prefix"));
709 *path = tmp;
710 } else {
711 free(*path);
712 *path = NULL;
713 }
714 }
715 }
716
717 if (*path) {
718 spprintf(&full_path, 0, "%s/%s", prefix, *path);
719 free(*path);
720 *path = strdup(full_path);
721 efree(full_path);
722 } else {
723 *path = strdup(prefix);
724 }
725
726 if (**path != '/' && wp != NULL && wp->config) {
727 return fpm_evaluate_full_path(path, NULL, default_prefix, expand);
728 }
729 return 0;
730 }
731 /* }}} */
732
fpm_conf_process_all_pools()733 static int fpm_conf_process_all_pools() /* {{{ */
734 {
735 struct fpm_worker_pool_s *wp, *wp2;
736
737 if (!fpm_worker_all_pools) {
738 zlog(ZLOG_ERROR, "No pool defined. at least one pool section must be specified in config file");
739 return -1;
740 }
741
742 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
743
744 /* prefix */
745 if (wp->config->prefix && *wp->config->prefix) {
746 fpm_evaluate_full_path(&wp->config->prefix, NULL, NULL, 0);
747
748 if (!fpm_conf_is_dir(wp->config->prefix)) {
749 zlog(ZLOG_ERROR, "[pool %s] the prefix '%s' does not exist or is not a directory", wp->config->name, wp->config->prefix);
750 return -1;
751 }
752 }
753
754 /* alert if user is not set only if we are not root*/
755 if (!wp->config->user && !geteuid()) {
756 zlog(ZLOG_ALERT, "[pool %s] user has not been defined", wp->config->name);
757 return -1;
758 }
759
760 /* listen */
761 if (wp->config->listen_address && *wp->config->listen_address) {
762 wp->listen_address_domain = fpm_sockets_domain_from_address(wp->config->listen_address);
763
764 if (wp->listen_address_domain == FPM_AF_UNIX && *wp->config->listen_address != '/') {
765 fpm_evaluate_full_path(&wp->config->listen_address, wp, NULL, 0);
766 }
767 } else {
768 zlog(ZLOG_ALERT, "[pool %s] no listen address have been defined!", wp->config->name);
769 return -1;
770 }
771
772 if (wp->config->process_priority != 64 && (wp->config->process_priority < -19 || wp->config->process_priority > 20)) {
773 zlog(ZLOG_ERROR, "[pool %s] process.priority must be included into [-19,20]", wp->config->name);
774 return -1;
775 }
776
777 /* pm */
778 if (wp->config->pm != PM_STYLE_STATIC && wp->config->pm != PM_STYLE_DYNAMIC && wp->config->pm != PM_STYLE_ONDEMAND) {
779 zlog(ZLOG_ALERT, "[pool %s] the process manager is missing (static, dynamic or ondemand)", wp->config->name);
780 return -1;
781 }
782
783 /* pm.max_children */
784 if (wp->config->pm_max_children < 1) {
785 zlog(ZLOG_ALERT, "[pool %s] pm.max_children must be a positive value", wp->config->name);
786 return -1;
787 }
788
789 /* pm.start_servers, pm.min_spare_servers, pm.max_spare_servers */
790 if (wp->config->pm == PM_STYLE_DYNAMIC) {
791 struct fpm_worker_pool_config_s *config = wp->config;
792
793 if (config->pm_min_spare_servers <= 0) {
794 zlog(ZLOG_ALERT, "[pool %s] pm.min_spare_servers(%d) must be a positive value", wp->config->name, config->pm_min_spare_servers);
795 return -1;
796 }
797
798 if (config->pm_max_spare_servers <= 0) {
799 zlog(ZLOG_ALERT, "[pool %s] pm.max_spare_servers(%d) must be a positive value", wp->config->name, config->pm_max_spare_servers);
800 return -1;
801 }
802
803 if (config->pm_min_spare_servers > config->pm_max_children ||
804 config->pm_max_spare_servers > config->pm_max_children) {
805 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);
806 return -1;
807 }
808
809 if (config->pm_max_spare_servers < config->pm_min_spare_servers) {
810 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);
811 return -1;
812 }
813
814 if (config->pm_start_servers <= 0) {
815 config->pm_start_servers = config->pm_min_spare_servers + ((config->pm_max_spare_servers - config->pm_min_spare_servers) / 2);
816 zlog(ZLOG_WARNING, "[pool %s] pm.start_servers is not set. It's been set to %d.", wp->config->name, config->pm_start_servers);
817
818 } else if (config->pm_start_servers < config->pm_min_spare_servers || config->pm_start_servers > config->pm_max_spare_servers) {
819 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);
820 return -1;
821 }
822 } else if (wp->config->pm == PM_STYLE_ONDEMAND) {
823 struct fpm_worker_pool_config_s *config = wp->config;
824
825 if (!fpm_event_support_edge_trigger()) {
826 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);
827 return -1;
828 }
829
830 if (config->pm_process_idle_timeout < 1) {
831 zlog(ZLOG_ALERT, "[pool %s] pm.process_idle_timeout(%ds) must be greater than 0s", wp->config->name, config->pm_process_idle_timeout);
832 return -1;
833 }
834
835 if (config->listen_backlog < FPM_BACKLOG_DEFAULT) {
836 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);
837 config->listen_backlog = FPM_BACKLOG_DEFAULT;
838 }
839
840 /* certainely useless but proper */
841 config->pm_start_servers = 0;
842 config->pm_min_spare_servers = 0;
843 config->pm_max_spare_servers = 0;
844 }
845
846 /* status */
847 if (wp->config->pm_status_path && *wp->config->pm_status_path) {
848 int i;
849 char *status = wp->config->pm_status_path;
850
851 if (*status != '/') {
852 zlog(ZLOG_ERROR, "[pool %s] the status path '%s' must start with a '/'", wp->config->name, status);
853 return -1;
854 }
855
856 if (strlen(status) < 2) {
857 zlog(ZLOG_ERROR, "[pool %s] the status path '%s' is not long enough", wp->config->name, status);
858 return -1;
859 }
860
861 for (i = 0; i < strlen(status); i++) {
862 if (!isalnum(status[i]) && status[i] != '/' && status[i] != '-' && status[i] != '_' && status[i] != '.') {
863 zlog(ZLOG_ERROR, "[pool %s] the status path '%s' must contain only the following characters '[alphanum]/_-.'", wp->config->name, status);
864 return -1;
865 }
866 }
867 }
868
869 /* ping */
870 if (wp->config->ping_path && *wp->config->ping_path) {
871 char *ping = wp->config->ping_path;
872 int i;
873
874 if (*ping != '/') {
875 zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' must start with a '/'", wp->config->name, ping);
876 return -1;
877 }
878
879 if (strlen(ping) < 2) {
880 zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' is not long enough", wp->config->name, ping);
881 return -1;
882 }
883
884 for (i = 0; i < strlen(ping); i++) {
885 if (!isalnum(ping[i]) && ping[i] != '/' && ping[i] != '-' && ping[i] != '_' && ping[i] != '.') {
886 zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' must containt only the following characters '[alphanum]/_-.'", wp->config->name, ping);
887 return -1;
888 }
889 }
890
891 if (!wp->config->ping_response) {
892 wp->config->ping_response = strdup("pong");
893 } else {
894 if (strlen(wp->config->ping_response) < 1) {
895 zlog(ZLOG_ERROR, "[pool %s] the ping response page '%s' is not long enough", wp->config->name, wp->config->ping_response);
896 return -1;
897 }
898 }
899 } else {
900 if (wp->config->ping_response) {
901 free(wp->config->ping_response);
902 wp->config->ping_response = NULL;
903 }
904 }
905
906 /* access.log, access.format */
907 if (wp->config->access_log && *wp->config->access_log) {
908 fpm_evaluate_full_path(&wp->config->access_log, wp, NULL, 0);
909 if (!wp->config->access_format) {
910 wp->config->access_format = strdup("%R - %u %t \"%m %r\" %s");
911 }
912 }
913
914 if (wp->config->request_terminate_timeout) {
915 fpm_globals.heartbeat = fpm_globals.heartbeat ? MIN(fpm_globals.heartbeat, (wp->config->request_terminate_timeout * 1000) / 3) : (wp->config->request_terminate_timeout * 1000) / 3;
916 }
917
918 /* slowlog */
919 if (wp->config->slowlog && *wp->config->slowlog) {
920 fpm_evaluate_full_path(&wp->config->slowlog, wp, NULL, 0);
921 }
922
923 /* request_slowlog_timeout */
924 if (wp->config->request_slowlog_timeout) {
925 #if HAVE_FPM_TRACE
926 if (! (wp->config->slowlog && *wp->config->slowlog)) {
927 zlog(ZLOG_ERROR, "[pool %s] 'slowlog' must be specified for use with 'request_slowlog_timeout'", wp->config->name);
928 return -1;
929 }
930 #else
931 static int warned = 0;
932
933 if (!warned) {
934 zlog(ZLOG_WARNING, "[pool %s] 'request_slowlog_timeout' is not supported on your system", wp->config->name);
935 warned = 1;
936 }
937
938 wp->config->request_slowlog_timeout = 0;
939 #endif
940
941 if (wp->config->slowlog && *wp->config->slowlog) {
942 int fd;
943
944 fd = open(wp->config->slowlog, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
945
946 if (0 > fd) {
947 zlog(ZLOG_SYSERROR, "Unable to create or open slowlog(%s)", wp->config->slowlog);
948 return -1;
949 }
950 close(fd);
951 }
952
953 fpm_globals.heartbeat = fpm_globals.heartbeat ? MIN(fpm_globals.heartbeat, (wp->config->request_slowlog_timeout * 1000) / 3) : (wp->config->request_slowlog_timeout * 1000) / 3;
954
955 if (wp->config->request_terminate_timeout && wp->config->request_slowlog_timeout > wp->config->request_terminate_timeout) {
956 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);
957 return -1;
958 }
959 }
960
961 /* chroot */
962 if (wp->config->chroot && *wp->config->chroot) {
963
964 fpm_evaluate_full_path(&wp->config->chroot, wp, NULL, 1);
965
966 if (*wp->config->chroot != '/') {
967 zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' must start with a '/'", wp->config->name, wp->config->chroot);
968 return -1;
969 }
970
971 if (!fpm_conf_is_dir(wp->config->chroot)) {
972 zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' does not exist or is not a directory", wp->config->name, wp->config->chroot);
973 return -1;
974 }
975 }
976
977 /* chdir */
978 if (wp->config->chdir && *wp->config->chdir) {
979
980 fpm_evaluate_full_path(&wp->config->chdir, wp, NULL, 0);
981
982 if (*wp->config->chdir != '/') {
983 zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' must start with a '/'", wp->config->name, wp->config->chdir);
984 return -1;
985 }
986
987 if (wp->config->chroot) {
988 char *buf;
989
990 spprintf(&buf, 0, "%s/%s", wp->config->chroot, wp->config->chdir);
991
992 if (!fpm_conf_is_dir(buf)) {
993 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);
994 efree(buf);
995 return -1;
996 }
997
998 efree(buf);
999 } else {
1000 if (!fpm_conf_is_dir(wp->config->chdir)) {
1001 zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' does not exist or is not a directory", wp->config->name, wp->config->chdir);
1002 return -1;
1003 }
1004 }
1005 }
1006
1007 /* security.limit_extensions */
1008 if (!wp->config->security_limit_extensions) {
1009 wp->config->security_limit_extensions = strdup(".php .phar");
1010 }
1011
1012 if (*wp->config->security_limit_extensions) {
1013 int nb_ext;
1014 char *ext;
1015 char *security_limit_extensions;
1016 char *limit_extensions;
1017
1018
1019 /* strdup because strtok(3) alters the string it parses */
1020 security_limit_extensions = strdup(wp->config->security_limit_extensions);
1021 limit_extensions = security_limit_extensions;
1022 nb_ext = 0;
1023
1024 /* find the number of extensions */
1025 while (strtok(limit_extensions, " \t")) {
1026 limit_extensions = NULL;
1027 nb_ext++;
1028 }
1029 free(security_limit_extensions);
1030
1031 /* if something found */
1032 if (nb_ext > 0) {
1033
1034 /* malloc the extension array */
1035 wp->limit_extensions = malloc(sizeof(char *) * (nb_ext + 1));
1036 if (!wp->limit_extensions) {
1037 zlog(ZLOG_ERROR, "[pool %s] unable to malloc extensions array", wp->config->name);
1038 return -1;
1039 }
1040
1041 /* strdup because strtok(3) alters the string it parses */
1042 security_limit_extensions = strdup(wp->config->security_limit_extensions);
1043 limit_extensions = security_limit_extensions;
1044 nb_ext = 0;
1045
1046 /* parse the string and save the extension in the array */
1047 while ((ext = strtok(limit_extensions, " \t"))) {
1048 limit_extensions = NULL;
1049 wp->limit_extensions[nb_ext++] = strdup(ext);
1050 }
1051
1052 /* end the array with NULL in order to parse it */
1053 wp->limit_extensions[nb_ext] = NULL;
1054 free(security_limit_extensions);
1055 }
1056 }
1057
1058 /* env[], php_value[], php_admin_values[] */
1059 if (!wp->config->chroot) {
1060 struct key_value_s *kv;
1061 char *options[] = FPM_PHP_INI_TO_EXPAND;
1062 char **p;
1063
1064 for (kv = wp->config->php_values; kv; kv = kv->next) {
1065 for (p = options; *p; p++) {
1066 if (!strcasecmp(kv->key, *p)) {
1067 fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
1068 }
1069 }
1070 }
1071 for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
1072 if (!strcasecmp(kv->key, "error_log") && !strcasecmp(kv->value, "syslog")) {
1073 continue;
1074 }
1075 for (p = options; *p; p++) {
1076 if (!strcasecmp(kv->key, *p)) {
1077 fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
1078 }
1079 }
1080 }
1081 }
1082 }
1083
1084 /* ensure 2 pools do not use the same listening address */
1085 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
1086 for (wp2 = fpm_worker_all_pools; wp2; wp2 = wp2->next) {
1087 if (wp == wp2) {
1088 continue;
1089 }
1090
1091 if (wp->config->listen_address && *wp->config->listen_address && wp2->config->listen_address && *wp2->config->listen_address && !strcmp(wp->config->listen_address, wp2->config->listen_address)) {
1092 zlog(ZLOG_ERROR, "[pool %s] unable to set listen address as it's already used in another pool '%s'", wp2->config->name, wp->config->name);
1093 return -1;
1094 }
1095 }
1096 }
1097 return 0;
1098 }
1099 /* }}} */
1100
fpm_conf_unlink_pid()1101 int fpm_conf_unlink_pid() /* {{{ */
1102 {
1103 if (fpm_global_config.pid_file) {
1104 if (0 > unlink(fpm_global_config.pid_file)) {
1105 zlog(ZLOG_SYSERROR, "Unable to remove the PID file (%s).", fpm_global_config.pid_file);
1106 return -1;
1107 }
1108 }
1109 return 0;
1110 }
1111 /* }}} */
1112
fpm_conf_write_pid()1113 int fpm_conf_write_pid() /* {{{ */
1114 {
1115 int fd;
1116
1117 if (fpm_global_config.pid_file) {
1118 char buf[64];
1119 int len;
1120
1121 unlink(fpm_global_config.pid_file);
1122 fd = creat(fpm_global_config.pid_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1123
1124 if (fd < 0) {
1125 zlog(ZLOG_SYSERROR, "Unable to create the PID file (%s).", fpm_global_config.pid_file);
1126 return -1;
1127 }
1128
1129 len = sprintf(buf, "%d", (int) fpm_globals.parent_pid);
1130
1131 if (len != write(fd, buf, len)) {
1132 zlog(ZLOG_SYSERROR, "Unable to write to the PID file.");
1133 close(fd);
1134 return -1;
1135 }
1136 close(fd);
1137 }
1138 return 0;
1139 }
1140 /* }}} */
1141
fpm_conf_post_process(int force_daemon TSRMLS_DC)1142 static int fpm_conf_post_process(int force_daemon TSRMLS_DC) /* {{{ */
1143 {
1144 struct fpm_worker_pool_s *wp;
1145
1146 if (fpm_global_config.pid_file) {
1147 fpm_evaluate_full_path(&fpm_global_config.pid_file, NULL, PHP_LOCALSTATEDIR, 0);
1148 }
1149
1150 if (force_daemon >= 0) {
1151 /* forced from command line options */
1152 fpm_global_config.daemonize = force_daemon;
1153 }
1154
1155 fpm_globals.log_level = fpm_global_config.log_level;
1156
1157 if (fpm_global_config.process_max < 0) {
1158 zlog(ZLOG_ERROR, "process_max can't be negative");
1159 return -1;
1160 }
1161
1162 if (fpm_global_config.process_priority != 64 && (fpm_global_config.process_priority < -19 || fpm_global_config.process_priority > 20)) {
1163 zlog(ZLOG_ERROR, "process.priority must be included into [-19,20]");
1164 return -1;
1165 }
1166
1167 if (!fpm_global_config.error_log) {
1168 fpm_global_config.error_log = strdup("log/php-fpm.log");
1169 }
1170
1171 #ifdef HAVE_SYSTEMD
1172 if (0 > fpm_systemd_conf()) {
1173 return -1;
1174 }
1175 #endif
1176
1177 #ifdef HAVE_SYSLOG_H
1178 if (!fpm_global_config.syslog_ident) {
1179 fpm_global_config.syslog_ident = strdup("php-fpm");
1180 }
1181
1182 if (fpm_global_config.syslog_facility < 0) {
1183 fpm_global_config.syslog_facility = LOG_DAEMON;
1184 }
1185
1186 if (strcasecmp(fpm_global_config.error_log, "syslog") != 0)
1187 #endif
1188 {
1189 fpm_evaluate_full_path(&fpm_global_config.error_log, NULL, PHP_LOCALSTATEDIR, 0);
1190 }
1191
1192 if (0 > fpm_stdio_open_error_log(0)) {
1193 return -1;
1194 }
1195
1196 if (0 > fpm_log_open(0)) {
1197 return -1;
1198 }
1199
1200 if (0 > fpm_event_pre_init(fpm_global_config.events_mechanism)) {
1201 return -1;
1202 }
1203
1204 if (0 > fpm_conf_process_all_pools()) {
1205 return -1;
1206 }
1207
1208 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
1209 if (!wp->config->access_log || !*wp->config->access_log) {
1210 continue;
1211 }
1212 if (0 > fpm_log_write(wp->config->access_format TSRMLS_CC)) {
1213 zlog(ZLOG_ERROR, "[pool %s] wrong format for access.format '%s'", wp->config->name, wp->config->access_format);
1214 return -1;
1215 }
1216 }
1217
1218 return 0;
1219 }
1220 /* }}} */
1221
fpm_conf_cleanup(int which,void * arg)1222 static void fpm_conf_cleanup(int which, void *arg) /* {{{ */
1223 {
1224 free(fpm_global_config.pid_file);
1225 free(fpm_global_config.error_log);
1226 free(fpm_global_config.events_mechanism);
1227 fpm_global_config.pid_file = 0;
1228 fpm_global_config.error_log = 0;
1229 #ifdef HAVE_SYSLOG_H
1230 free(fpm_global_config.syslog_ident);
1231 fpm_global_config.syslog_ident = 0;
1232 #endif
1233 free(fpm_globals.config);
1234 }
1235 /* }}} */
1236
fpm_conf_ini_parser_include(char * inc,void * arg TSRMLS_DC)1237 static void fpm_conf_ini_parser_include(char *inc, void *arg TSRMLS_DC) /* {{{ */
1238 {
1239 char *filename;
1240 int *error = (int *)arg;;
1241 #ifdef HAVE_GLOB
1242 glob_t g;
1243 #endif
1244 int i;
1245
1246 if (!inc || !arg) return;
1247 if (*error) return; /* We got already an error. Switch to the end. */
1248 spprintf(&filename, 0, "%s", ini_filename);
1249
1250 #ifdef HAVE_GLOB
1251 {
1252 g.gl_offs = 0;
1253 if ((i = glob(inc, GLOB_ERR | GLOB_MARK | GLOB_NOSORT, NULL, &g)) != 0) {
1254 #ifdef GLOB_NOMATCH
1255 if (i == GLOB_NOMATCH) {
1256 zlog(ZLOG_WARNING, "Nothing matches the include pattern '%s' from %s at line %d.", inc, filename, ini_lineno);
1257 efree(filename);
1258 return;
1259 }
1260 #endif /* GLOB_NOMATCH */
1261 zlog(ZLOG_ERROR, "Unable to globalize '%s' (ret=%d) from %s at line %d.", inc, i, filename, ini_lineno);
1262 *error = 1;
1263 efree(filename);
1264 return;
1265 }
1266
1267 for (i = 0; i < g.gl_pathc; i++) {
1268 int len = strlen(g.gl_pathv[i]);
1269 if (len < 1) continue;
1270 if (g.gl_pathv[i][len - 1] == '/') continue; /* don't parse directories */
1271 if (0 > fpm_conf_load_ini_file(g.gl_pathv[i] TSRMLS_CC)) {
1272 zlog(ZLOG_ERROR, "Unable to include %s from %s at line %d", g.gl_pathv[i], filename, ini_lineno);
1273 *error = 1;
1274 efree(filename);
1275 return;
1276 }
1277 }
1278 globfree(&g);
1279 }
1280 #else /* HAVE_GLOB */
1281 if (0 > fpm_conf_load_ini_file(inc TSRMLS_CC)) {
1282 zlog(ZLOG_ERROR, "Unable to include %s from %s at line %d", inc, filename, ini_lineno);
1283 *error = 1;
1284 efree(filename);
1285 return;
1286 }
1287 #endif /* HAVE_GLOB */
1288
1289 efree(filename);
1290 }
1291 /* }}} */
1292
fpm_conf_ini_parser_section(zval * section,void * arg TSRMLS_DC)1293 static void fpm_conf_ini_parser_section(zval *section, void *arg TSRMLS_DC) /* {{{ */
1294 {
1295 struct fpm_worker_pool_s *wp;
1296 struct fpm_worker_pool_config_s *config;
1297 int *error = (int *)arg;
1298
1299 /* switch to global conf */
1300 if (!strcasecmp(Z_STRVAL_P(section), "global")) {
1301 current_wp = NULL;
1302 return;
1303 }
1304
1305 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
1306 if (!wp->config) continue;
1307 if (!wp->config->name) continue;
1308 if (!strcasecmp(wp->config->name, Z_STRVAL_P(section))) {
1309 /* Found a wp with the same name. Bring it back */
1310 current_wp = wp;
1311 return;
1312 }
1313 }
1314
1315 /* it's a new pool */
1316 config = (struct fpm_worker_pool_config_s *)fpm_worker_pool_config_alloc();
1317 if (!current_wp || !config) {
1318 zlog(ZLOG_ERROR, "[%s:%d] Unable to alloc a new WorkerPool for worker '%s'", ini_filename, ini_lineno, Z_STRVAL_P(section));
1319 *error = 1;
1320 return;
1321 }
1322 config->name = strdup(Z_STRVAL_P(section));
1323 if (!config->name) {
1324 zlog(ZLOG_ERROR, "[%s:%d] Unable to alloc memory for configuration name for worker '%s'", ini_filename, ini_lineno, Z_STRVAL_P(section));
1325 *error = 1;
1326 return;
1327 }
1328 }
1329 /* }}} */
1330
fpm_conf_ini_parser_entry(zval * name,zval * value,void * arg TSRMLS_DC)1331 static void fpm_conf_ini_parser_entry(zval *name, zval *value, void *arg TSRMLS_DC) /* {{{ */
1332 {
1333 struct ini_value_parser_s *parser;
1334 void *config = NULL;
1335
1336 int *error = (int *)arg;
1337 if (!value) {
1338 zlog(ZLOG_ERROR, "[%s:%d] value is NULL for a ZEND_INI_PARSER_ENTRY", ini_filename, ini_lineno);
1339 *error = 1;
1340 return;
1341 }
1342
1343 if (!strcmp(Z_STRVAL_P(name), "include")) {
1344 if (ini_include) {
1345 zlog(ZLOG_ERROR, "[%s:%d] two includes at the same time !", ini_filename, ini_lineno);
1346 *error = 1;
1347 return;
1348 }
1349 ini_include = strdup(Z_STRVAL_P(value));
1350 return;
1351 }
1352
1353 if (!current_wp) { /* we are in the global section */
1354 parser = ini_fpm_global_options;
1355 config = &fpm_global_config;
1356 } else {
1357 parser = ini_fpm_pool_options;
1358 config = current_wp->config;
1359 }
1360
1361 for (; parser->name; parser++) {
1362 if (!strcasecmp(parser->name, Z_STRVAL_P(name))) {
1363 char *ret;
1364 if (!parser->parser) {
1365 zlog(ZLOG_ERROR, "[%s:%d] the parser for entry '%s' is not defined", ini_filename, ini_lineno, parser->name);
1366 *error = 1;
1367 return;
1368 }
1369
1370 ret = parser->parser(value, &config, parser->offset);
1371 if (ret) {
1372 zlog(ZLOG_ERROR, "[%s:%d] unable to parse value for entry '%s': %s", ini_filename, ini_lineno, parser->name, ret);
1373 *error = 1;
1374 return;
1375 }
1376
1377 /* all is good ! */
1378 return;
1379 }
1380 }
1381
1382 /* nothing has been found if we got here */
1383 zlog(ZLOG_ERROR, "[%s:%d] unknown entry '%s'", ini_filename, ini_lineno, Z_STRVAL_P(name));
1384 *error = 1;
1385 }
1386 /* }}} */
1387
fpm_conf_ini_parser_array(zval * name,zval * key,zval * value,void * arg TSRMLS_DC)1388 static void fpm_conf_ini_parser_array(zval *name, zval *key, zval *value, void *arg TSRMLS_DC) /* {{{ */
1389 {
1390 int *error = (int *)arg;
1391 char *err = NULL;
1392 void *config;
1393
1394 if (!Z_STRVAL_P(key) || !Z_STRVAL_P(value) || !*Z_STRVAL_P(key)) {
1395 zlog(ZLOG_ERROR, "[%s:%d] Misspelled array ?", ini_filename, ini_lineno);
1396 *error = 1;
1397 return;
1398 }
1399 if (!current_wp || !current_wp->config) {
1400 zlog(ZLOG_ERROR, "[%s:%d] Array are not allowed in the global section", ini_filename, ini_lineno);
1401 *error = 1;
1402 return;
1403 }
1404
1405 if (!strcmp("env", Z_STRVAL_P(name))) {
1406 if (!*Z_STRVAL_P(value)) {
1407 zlog(ZLOG_ERROR, "[%s:%d] empty value", ini_filename, ini_lineno);
1408 *error = 1;
1409 return;
1410 }
1411 config = (char *)current_wp->config + WPO(env);
1412 err = fpm_conf_set_array(key, value, &config, 0);
1413
1414 } else if (!strcmp("php_value", Z_STRVAL_P(name))) {
1415 config = (char *)current_wp->config + WPO(php_values);
1416 err = fpm_conf_set_array(key, value, &config, 0);
1417
1418 } else if (!strcmp("php_admin_value", Z_STRVAL_P(name))) {
1419 config = (char *)current_wp->config + WPO(php_admin_values);
1420 err = fpm_conf_set_array(key, value, &config, 0);
1421
1422 } else if (!strcmp("php_flag", Z_STRVAL_P(name))) {
1423 config = (char *)current_wp->config + WPO(php_values);
1424 err = fpm_conf_set_array(key, value, &config, 1);
1425
1426 } else if (!strcmp("php_admin_flag", Z_STRVAL_P(name))) {
1427 config = (char *)current_wp->config + WPO(php_admin_values);
1428 err = fpm_conf_set_array(key, value, &config, 1);
1429
1430 } else {
1431 zlog(ZLOG_ERROR, "[%s:%d] unknown directive '%s'", ini_filename, ini_lineno, Z_STRVAL_P(name));
1432 *error = 1;
1433 return;
1434 }
1435
1436 if (err) {
1437 zlog(ZLOG_ERROR, "[%s:%d] error while parsing '%s[%s]' : %s", ini_filename, ini_lineno, Z_STRVAL_P(name), Z_STRVAL_P(key), err);
1438 *error = 1;
1439 return;
1440 }
1441 }
1442 /* }}} */
1443
fpm_conf_ini_parser(zval * arg1,zval * arg2,zval * arg3,int callback_type,void * arg TSRMLS_DC)1444 static void fpm_conf_ini_parser(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC) /* {{{ */
1445 {
1446 int *error;
1447
1448 if (!arg1 || !arg) return;
1449 error = (int *)arg;
1450 if (*error) return; /* We got already an error. Switch to the end. */
1451
1452 switch(callback_type) {
1453 case ZEND_INI_PARSER_ENTRY:
1454 fpm_conf_ini_parser_entry(arg1, arg2, error TSRMLS_CC);
1455 break;;
1456 case ZEND_INI_PARSER_SECTION:
1457 fpm_conf_ini_parser_section(arg1, error TSRMLS_CC);
1458 break;;
1459 case ZEND_INI_PARSER_POP_ENTRY:
1460 fpm_conf_ini_parser_array(arg1, arg3, arg2, error TSRMLS_CC);
1461 break;;
1462 default:
1463 zlog(ZLOG_ERROR, "[%s:%d] Unknown INI syntax", ini_filename, ini_lineno);
1464 *error = 1;
1465 break;;
1466 }
1467 }
1468 /* }}} */
1469
fpm_conf_load_ini_file(char * filename TSRMLS_DC)1470 int fpm_conf_load_ini_file(char *filename TSRMLS_DC) /* {{{ */
1471 {
1472 int error = 0;
1473 char buf[1024+1];
1474 int fd, n;
1475 int nb_read = 1;
1476 char c = '*';
1477
1478 int ret = 1;
1479
1480 if (!filename || !filename[0]) {
1481 zlog(ZLOG_ERROR, "configuration filename is empty");
1482 return -1;
1483 }
1484
1485 fd = open(filename, O_RDONLY, 0);
1486 if (fd < 0) {
1487 zlog(ZLOG_SYSERROR, "failed to open configuration file '%s'", filename);
1488 return -1;
1489 }
1490
1491 if (ini_recursion++ > 4) {
1492 zlog(ZLOG_ERROR, "failed to include more than 5 files recusively");
1493 close(fd);
1494 return -1;
1495 }
1496
1497 ini_lineno = 0;
1498 while (nb_read > 0) {
1499 int tmp;
1500 memset(buf, 0, sizeof(char) * (1024 + 1));
1501 for (n = 0; n < 1024 && (nb_read = read(fd, &c, sizeof(char))) == sizeof(char) && c != '\n'; n++) {
1502 buf[n] = c;
1503 }
1504 buf[n++] = '\n';
1505 ini_lineno++;
1506 ini_filename = filename;
1507 tmp = zend_parse_ini_string(buf, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t)fpm_conf_ini_parser, &error TSRMLS_CC);
1508 ini_filename = filename;
1509 if (error || tmp == FAILURE) {
1510 if (ini_include) free(ini_include);
1511 ini_recursion--;
1512 close(fd);
1513 return -1;
1514 }
1515 if (ini_include) {
1516 char *tmp = ini_include;
1517 ini_include = NULL;
1518 fpm_evaluate_full_path(&tmp, NULL, NULL, 0);
1519 fpm_conf_ini_parser_include(tmp, &error TSRMLS_CC);
1520 if (error) {
1521 free(tmp);
1522 ini_recursion--;
1523 close(fd);
1524 return -1;
1525 }
1526 free(tmp);
1527 }
1528 }
1529
1530 ini_recursion--;
1531 close(fd);
1532 return ret;
1533
1534 }
1535 /* }}} */
1536
fpm_conf_dump()1537 static void fpm_conf_dump() /* {{{ */
1538 {
1539 struct fpm_worker_pool_s *wp;
1540
1541 /*
1542 * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
1543 */
1544 zlog(ZLOG_NOTICE, "[General]");
1545 zlog(ZLOG_NOTICE, "\tpid = %s", STR2STR(fpm_global_config.pid_file));
1546 zlog(ZLOG_NOTICE, "\terror_log = %s", STR2STR(fpm_global_config.error_log));
1547 #ifdef HAVE_SYSLOG_H
1548 zlog(ZLOG_NOTICE, "\tsyslog.ident = %s", STR2STR(fpm_global_config.syslog_ident));
1549 zlog(ZLOG_NOTICE, "\tsyslog.facility = %d", fpm_global_config.syslog_facility); /* FIXME: convert to string */
1550 #endif
1551 zlog(ZLOG_NOTICE, "\tlog_level = %s", zlog_get_level_name(fpm_globals.log_level));
1552 zlog(ZLOG_NOTICE, "\temergency_restart_interval = %ds", fpm_global_config.emergency_restart_interval);
1553 zlog(ZLOG_NOTICE, "\temergency_restart_threshold = %d", fpm_global_config.emergency_restart_threshold);
1554 zlog(ZLOG_NOTICE, "\tprocess_control_timeout = %ds", fpm_global_config.process_control_timeout);
1555 zlog(ZLOG_NOTICE, "\tprocess.max = %d", fpm_global_config.process_max);
1556 if (fpm_global_config.process_priority == 64) {
1557 zlog(ZLOG_NOTICE, "\tprocess.priority = undefined");
1558 } else {
1559 zlog(ZLOG_NOTICE, "\tprocess.priority = %d", fpm_global_config.process_priority);
1560 }
1561 zlog(ZLOG_NOTICE, "\tdaemonize = %s", BOOL2STR(fpm_global_config.daemonize));
1562 zlog(ZLOG_NOTICE, "\trlimit_files = %d", fpm_global_config.rlimit_files);
1563 zlog(ZLOG_NOTICE, "\trlimit_core = %d", fpm_global_config.rlimit_core);
1564 zlog(ZLOG_NOTICE, "\tevents.mechanism = %s", fpm_event_machanism_name());
1565 #ifdef HAVE_SYSTEMD
1566 zlog(ZLOG_NOTICE, "\tsystemd_interval = %ds", fpm_global_config.systemd_interval/1000);
1567 #endif
1568 zlog(ZLOG_NOTICE, " ");
1569
1570 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
1571 struct key_value_s *kv;
1572 if (!wp->config) continue;
1573 zlog(ZLOG_NOTICE, "[%s]", STR2STR(wp->config->name));
1574 zlog(ZLOG_NOTICE, "\tprefix = %s", STR2STR(wp->config->prefix));
1575 zlog(ZLOG_NOTICE, "\tuser = %s", STR2STR(wp->config->user));
1576 zlog(ZLOG_NOTICE, "\tgroup = %s", STR2STR(wp->config->group));
1577 zlog(ZLOG_NOTICE, "\tlisten = %s", STR2STR(wp->config->listen_address));
1578 zlog(ZLOG_NOTICE, "\tlisten.backlog = %d", wp->config->listen_backlog);
1579 zlog(ZLOG_NOTICE, "\tlisten.owner = %s", STR2STR(wp->config->listen_owner));
1580 zlog(ZLOG_NOTICE, "\tlisten.group = %s", STR2STR(wp->config->listen_group));
1581 zlog(ZLOG_NOTICE, "\tlisten.mode = %s", STR2STR(wp->config->listen_mode));
1582 zlog(ZLOG_NOTICE, "\tlisten.allowed_clients = %s", STR2STR(wp->config->listen_allowed_clients));
1583 if (wp->config->process_priority == 64) {
1584 zlog(ZLOG_NOTICE, "\tprocess.priority = undefined");
1585 } else {
1586 zlog(ZLOG_NOTICE, "\tprocess.priority = %d", wp->config->process_priority);
1587 }
1588 zlog(ZLOG_NOTICE, "\tpm = %s", PM2STR(wp->config->pm));
1589 zlog(ZLOG_NOTICE, "\tpm.max_children = %d", wp->config->pm_max_children);
1590 zlog(ZLOG_NOTICE, "\tpm.start_servers = %d", wp->config->pm_start_servers);
1591 zlog(ZLOG_NOTICE, "\tpm.min_spare_servers = %d", wp->config->pm_min_spare_servers);
1592 zlog(ZLOG_NOTICE, "\tpm.max_spare_servers = %d", wp->config->pm_max_spare_servers);
1593 zlog(ZLOG_NOTICE, "\tpm.process_idle_timeout = %d", wp->config->pm_process_idle_timeout);
1594 zlog(ZLOG_NOTICE, "\tpm.max_requests = %d", wp->config->pm_max_requests);
1595 zlog(ZLOG_NOTICE, "\tpm.status_path = %s", STR2STR(wp->config->pm_status_path));
1596 zlog(ZLOG_NOTICE, "\tping.path = %s", STR2STR(wp->config->ping_path));
1597 zlog(ZLOG_NOTICE, "\tping.response = %s", STR2STR(wp->config->ping_response));
1598 zlog(ZLOG_NOTICE, "\taccess.log = %s", STR2STR(wp->config->access_log));
1599 zlog(ZLOG_NOTICE, "\taccess.format = %s", STR2STR(wp->config->access_format));
1600 zlog(ZLOG_NOTICE, "\tslowlog = %s", STR2STR(wp->config->slowlog));
1601 zlog(ZLOG_NOTICE, "\trequest_slowlog_timeout = %ds", wp->config->request_slowlog_timeout);
1602 zlog(ZLOG_NOTICE, "\trequest_terminate_timeout = %ds", wp->config->request_terminate_timeout);
1603 zlog(ZLOG_NOTICE, "\trlimit_files = %d", wp->config->rlimit_files);
1604 zlog(ZLOG_NOTICE, "\trlimit_core = %d", wp->config->rlimit_core);
1605 zlog(ZLOG_NOTICE, "\tchroot = %s", STR2STR(wp->config->chroot));
1606 zlog(ZLOG_NOTICE, "\tchdir = %s", STR2STR(wp->config->chdir));
1607 zlog(ZLOG_NOTICE, "\tcatch_workers_output = %s", BOOL2STR(wp->config->catch_workers_output));
1608 zlog(ZLOG_NOTICE, "\tclear_env = %s", BOOL2STR(wp->config->clear_env));
1609 zlog(ZLOG_NOTICE, "\tsecurity.limit_extensions = %s", wp->config->security_limit_extensions);
1610
1611 for (kv = wp->config->env; kv; kv = kv->next) {
1612 zlog(ZLOG_NOTICE, "\tenv[%s] = %s", kv->key, kv->value);
1613 }
1614
1615 for (kv = wp->config->php_values; kv; kv = kv->next) {
1616 zlog(ZLOG_NOTICE, "\tphp_value[%s] = %s", kv->key, kv->value);
1617 }
1618
1619 for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
1620 zlog(ZLOG_NOTICE, "\tphp_admin_value[%s] = %s", kv->key, kv->value);
1621 }
1622 zlog(ZLOG_NOTICE, " ");
1623 }
1624 }
1625 /* }}} */
1626
fpm_conf_init_main(int test_conf,int force_daemon)1627 int fpm_conf_init_main(int test_conf, int force_daemon) /* {{{ */
1628 {
1629 int ret;
1630 TSRMLS_FETCH();
1631
1632 if (fpm_globals.prefix && *fpm_globals.prefix) {
1633 if (!fpm_conf_is_dir(fpm_globals.prefix)) {
1634 zlog(ZLOG_ERROR, "the global prefix '%s' does not exist or is not a directory", fpm_globals.prefix);
1635 return -1;
1636 }
1637 }
1638
1639 if (fpm_globals.pid && *fpm_globals.pid) {
1640 fpm_global_config.pid_file = strdup(fpm_globals.pid);
1641 }
1642
1643 if (fpm_globals.config == NULL) {
1644 char *tmp;
1645
1646 if (fpm_globals.prefix == NULL) {
1647 spprintf(&tmp, 0, "%s/php-fpm.conf", PHP_SYSCONFDIR);
1648 } else {
1649 spprintf(&tmp, 0, "%s/etc/php-fpm.conf", fpm_globals.prefix);
1650 }
1651
1652 if (!tmp) {
1653 zlog(ZLOG_SYSERROR, "spprintf() failed (tmp for fpm_globals.config)");
1654 return -1;
1655 }
1656
1657 fpm_globals.config = strdup(tmp);
1658 efree(tmp);
1659
1660 if (!fpm_globals.config) {
1661 zlog(ZLOG_SYSERROR, "spprintf() failed (fpm_globals.config)");
1662 return -1;
1663 }
1664 }
1665
1666 ret = fpm_conf_load_ini_file(fpm_globals.config TSRMLS_CC);
1667
1668 if (0 > ret) {
1669 zlog(ZLOG_ERROR, "failed to load configuration file '%s'", fpm_globals.config);
1670 return -1;
1671 }
1672
1673 if (0 > fpm_conf_post_process(force_daemon TSRMLS_CC)) {
1674 zlog(ZLOG_ERROR, "failed to post process the configuration");
1675 return -1;
1676 }
1677
1678 if (test_conf) {
1679 if (test_conf > 1) {
1680 fpm_conf_dump();
1681 }
1682 zlog(ZLOG_NOTICE, "configuration file %s test is successful\n", fpm_globals.config);
1683 fpm_globals.test_successful = 1;
1684 return -1;
1685 }
1686
1687 if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_conf_cleanup, 0)) {
1688 return -1;
1689 }
1690
1691 return 0;
1692 }
1693 /* }}} */
1694