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