1 /* $Id: fpm_conf.c,v 1.33.2.3 2008/12/13 03:50:29 anight Exp $ */
2 /* (c) 2007,2008 Andrei Nigmatulin */
3
4 #include "fpm_config.h"
5
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <stddef.h>
12 #include <string.h>
13 #if HAVE_INTTYPES_H
14 # include <inttypes.h>
15 #else
16 # include <stdint.h>
17 #endif
18 #ifdef HAVE_GLOB
19 # ifndef PHP_WIN32
20 # include <glob.h>
21 # else
22 # include "win32/glob.h"
23 # endif
24 #endif
25
26 #include <stdio.h>
27 #include <unistd.h>
28
29 #include "php.h"
30 #include "zend_ini_scanner.h"
31 #include "zend_globals.h"
32 #include "zend_stream.h"
33 #include "php_syslog.h"
34
35 #include "fpm.h"
36 #include "fpm_conf.h"
37 #include "fpm_stdio.h"
38 #include "fpm_worker_pool.h"
39 #include "fpm_cleanup.h"
40 #include "fpm_php.h"
41 #include "fpm_sockets.h"
42 #include "fpm_shm.h"
43 #include "fpm_status.h"
44 #include "fpm_log.h"
45 #include "fpm_events.h"
46 #include "zlog.h"
47 #ifdef HAVE_SYSTEMD
48 #include "fpm_systemd.h"
49 #endif
50
51
52 #define STR2STR(a) (a ? a : "undefined")
53 #define BOOL2STR(a) (a ? "yes" : "no")
54 #define GO(field) offsetof(struct fpm_global_config_s, field)
55 #define WPO(field) offsetof(struct fpm_worker_pool_config_s, field)
56
57 static int fpm_conf_load_ini_file(char *filename);
58 static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset);
59 #if 0 /* not used for now */
60 static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset);
61 #endif
62 static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset);
63 static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset);
64 static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset);
65 static char *fpm_conf_set_log_level(zval *value, void **config, intptr_t offset);
66 static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offset);
67 static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset);
68 #ifdef HAVE_SYSLOG_H
69 static char *fpm_conf_set_syslog_facility(zval *value, void **config, intptr_t offset);
70 #endif
71
72 struct fpm_global_config_s fpm_global_config = {
73 .daemonize = 1,
74 #ifdef HAVE_SYSLOG_H
75 .syslog_facility = -1,
76 #endif
77 .process_max = 0,
78 .process_priority = 64, /* 64 means unset */
79 #ifdef HAVE_SYSTEMD
80 .systemd_watchdog = 0,
81 .systemd_interval = -1, /* -1 means not set */
82 #endif
83 };
84 static struct fpm_worker_pool_s *current_wp = NULL;
85 static int ini_recursion = 0;
86 static char *ini_filename = NULL;
87 static int ini_lineno = 0;
88 static char *ini_include = NULL;
89
90 /*
91 * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
92 */
93 static struct ini_value_parser_s ini_fpm_global_options[] = {
94 { "pid", &fpm_conf_set_string, GO(pid_file) },
95 { "error_log", &fpm_conf_set_string, GO(error_log) },
96 #ifdef HAVE_SYSLOG_H
97 { "syslog.ident", &fpm_conf_set_string, GO(syslog_ident) },
98 { "syslog.facility", &fpm_conf_set_syslog_facility, GO(syslog_facility) },
99 #endif
100 { "log_level", &fpm_conf_set_log_level, GO(log_level) },
101 { "emergency_restart_threshold", &fpm_conf_set_integer, GO(emergency_restart_threshold) },
102 { "emergency_restart_interval", &fpm_conf_set_time, GO(emergency_restart_interval) },
103 { "process_control_timeout", &fpm_conf_set_time, GO(process_control_timeout) },
104 { "process.max", &fpm_conf_set_integer, GO(process_max) },
105 { "process.priority", &fpm_conf_set_integer, GO(process_priority) },
106 { "daemonize", &fpm_conf_set_boolean, GO(daemonize) },
107 { "rlimit_files", &fpm_conf_set_integer, GO(rlimit_files) },
108 { "rlimit_core", &fpm_conf_set_rlimit_core, GO(rlimit_core) },
109 { "events.mechanism", &fpm_conf_set_string, GO(events_mechanism) },
110 #ifdef HAVE_SYSTEMD
111 { "systemd_interval", &fpm_conf_set_time, GO(systemd_interval) },
112 #endif
113 { 0, 0, 0 }
114 };
115
116 /*
117 * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
118 */
119 static struct ini_value_parser_s ini_fpm_pool_options[] = {
120 { "prefix", &fpm_conf_set_string, WPO(prefix) },
121 { "user", &fpm_conf_set_string, WPO(user) },
122 { "group", &fpm_conf_set_string, WPO(group) },
123 { "listen", &fpm_conf_set_string, WPO(listen_address) },
124 { "listen.backlog", &fpm_conf_set_integer, WPO(listen_backlog) },
125 #ifdef HAVE_FPM_ACL
126 { "listen.acl_users", &fpm_conf_set_string, WPO(listen_acl_users) },
127 { "listen.acl_groups", &fpm_conf_set_string, WPO(listen_acl_groups) },
128 #endif
129 { "listen.owner", &fpm_conf_set_string, WPO(listen_owner) },
130 { "listen.group", &fpm_conf_set_string, WPO(listen_group) },
131 { "listen.mode", &fpm_conf_set_string, WPO(listen_mode) },
132 { "listen.allowed_clients", &fpm_conf_set_string, WPO(listen_allowed_clients) },
133 { "process.priority", &fpm_conf_set_integer, WPO(process_priority) },
134 { "process.dumpable", &fpm_conf_set_boolean, WPO(process_dumpable) },
135 { "pm", &fpm_conf_set_pm, WPO(pm) },
136 { "pm.max_children", &fpm_conf_set_integer, WPO(pm_max_children) },
137 { "pm.start_servers", &fpm_conf_set_integer, WPO(pm_start_servers) },
138 { "pm.min_spare_servers", &fpm_conf_set_integer, WPO(pm_min_spare_servers) },
139 { "pm.max_spare_servers", &fpm_conf_set_integer, WPO(pm_max_spare_servers) },
140 { "pm.process_idle_timeout", &fpm_conf_set_time, WPO(pm_process_idle_timeout) },
141 { "pm.max_requests", &fpm_conf_set_integer, WPO(pm_max_requests) },
142 { "pm.status_path", &fpm_conf_set_string, WPO(pm_status_path) },
143 { "ping.path", &fpm_conf_set_string, WPO(ping_path) },
144 { "ping.response", &fpm_conf_set_string, WPO(ping_response) },
145 { "access.log", &fpm_conf_set_string, WPO(access_log) },
146 { "access.format", &fpm_conf_set_string, WPO(access_format) },
147 { "slowlog", &fpm_conf_set_string, WPO(slowlog) },
148 { "request_slowlog_timeout", &fpm_conf_set_time, WPO(request_slowlog_timeout) },
149 { "request_slowlog_trace_depth", &fpm_conf_set_integer, WPO(request_slowlog_trace_depth) },
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 size_t 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] != '.' && 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 size_t 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] != '.' && 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 /* request_slowlog_trace_depth */
976 if (wp->config->request_slowlog_trace_depth) {
977 #if HAVE_FPM_TRACE
978 if (! (wp->config->slowlog && *wp->config->slowlog)) {
979 zlog(ZLOG_ERROR, "[pool %s] 'slowlog' must be specified for use with 'request_slowlog_trace_depth'", wp->config->name);
980 return -1;
981 }
982 #else
983 static int warned = 0;
984
985 if (!warned) {
986 zlog(ZLOG_WARNING, "[pool %s] 'request_slowlog_trace_depth' is not supported on your system", wp->config->name);
987 warned = 1;
988 }
989 #endif
990
991 if (wp->config->request_slowlog_trace_depth <= 0) {
992 zlog(ZLOG_ERROR, "[pool %s] 'request_slowlog_trace_depth' (%d) must be a positive value", wp->config->name, wp->config->request_slowlog_trace_depth);
993 return -1;
994 }
995 } else {
996 wp->config->request_slowlog_trace_depth = 20;
997 }
998
999 /* chroot */
1000 if (wp->config->chroot && *wp->config->chroot) {
1001
1002 fpm_evaluate_full_path(&wp->config->chroot, wp, NULL, 1);
1003
1004 if (*wp->config->chroot != '/') {
1005 zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' must start with a '/'", wp->config->name, wp->config->chroot);
1006 return -1;
1007 }
1008
1009 if (!fpm_conf_is_dir(wp->config->chroot)) {
1010 zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' does not exist or is not a directory", wp->config->name, wp->config->chroot);
1011 return -1;
1012 }
1013 }
1014
1015 /* chdir */
1016 if (wp->config->chdir && *wp->config->chdir) {
1017
1018 fpm_evaluate_full_path(&wp->config->chdir, wp, NULL, 0);
1019
1020 if (*wp->config->chdir != '/') {
1021 zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' must start with a '/'", wp->config->name, wp->config->chdir);
1022 return -1;
1023 }
1024
1025 if (wp->config->chroot) {
1026 char *buf;
1027
1028 spprintf(&buf, 0, "%s/%s", wp->config->chroot, wp->config->chdir);
1029
1030 if (!fpm_conf_is_dir(buf)) {
1031 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);
1032 efree(buf);
1033 return -1;
1034 }
1035
1036 efree(buf);
1037 } else {
1038 if (!fpm_conf_is_dir(wp->config->chdir)) {
1039 zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' does not exist or is not a directory", wp->config->name, wp->config->chdir);
1040 return -1;
1041 }
1042 }
1043 }
1044
1045 /* security.limit_extensions */
1046 if (!wp->config->security_limit_extensions) {
1047 wp->config->security_limit_extensions = strdup(".php .phar");
1048 }
1049
1050 if (*wp->config->security_limit_extensions) {
1051 int nb_ext;
1052 char *ext;
1053 char *security_limit_extensions;
1054 char *limit_extensions;
1055
1056
1057 /* strdup because strtok(3) alters the string it parses */
1058 security_limit_extensions = strdup(wp->config->security_limit_extensions);
1059 limit_extensions = security_limit_extensions;
1060 nb_ext = 0;
1061
1062 /* find the number of extensions */
1063 while (strtok(limit_extensions, " \t")) {
1064 limit_extensions = NULL;
1065 nb_ext++;
1066 }
1067 free(security_limit_extensions);
1068
1069 /* if something found */
1070 if (nb_ext > 0) {
1071
1072 /* malloc the extension array */
1073 wp->limit_extensions = malloc(sizeof(char *) * (nb_ext + 1));
1074 if (!wp->limit_extensions) {
1075 zlog(ZLOG_ERROR, "[pool %s] unable to malloc extensions array", wp->config->name);
1076 return -1;
1077 }
1078
1079 /* strdup because strtok(3) alters the string it parses */
1080 security_limit_extensions = strdup(wp->config->security_limit_extensions);
1081 limit_extensions = security_limit_extensions;
1082 nb_ext = 0;
1083
1084 /* parse the string and save the extension in the array */
1085 while ((ext = strtok(limit_extensions, " \t"))) {
1086 limit_extensions = NULL;
1087 wp->limit_extensions[nb_ext++] = strdup(ext);
1088 }
1089
1090 /* end the array with NULL in order to parse it */
1091 wp->limit_extensions[nb_ext] = NULL;
1092 free(security_limit_extensions);
1093 }
1094 }
1095
1096 /* env[], php_value[], php_admin_values[] */
1097 if (!wp->config->chroot) {
1098 struct key_value_s *kv;
1099 char *options[] = FPM_PHP_INI_TO_EXPAND;
1100 char **p;
1101
1102 for (kv = wp->config->php_values; kv; kv = kv->next) {
1103 for (p = options; *p; p++) {
1104 if (!strcasecmp(kv->key, *p)) {
1105 fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
1106 }
1107 }
1108 }
1109 for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
1110 if (!strcasecmp(kv->key, "error_log") && !strcasecmp(kv->value, "syslog")) {
1111 continue;
1112 }
1113 for (p = options; *p; p++) {
1114 if (!strcasecmp(kv->key, *p)) {
1115 fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
1116 }
1117 }
1118 }
1119 }
1120 }
1121
1122 /* ensure 2 pools do not use the same listening address */
1123 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
1124 for (wp2 = fpm_worker_all_pools; wp2; wp2 = wp2->next) {
1125 if (wp == wp2) {
1126 continue;
1127 }
1128
1129 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)) {
1130 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);
1131 return -1;
1132 }
1133 }
1134 }
1135 return 0;
1136 }
1137 /* }}} */
1138
fpm_conf_unlink_pid()1139 int fpm_conf_unlink_pid() /* {{{ */
1140 {
1141 if (fpm_global_config.pid_file) {
1142 if (0 > unlink(fpm_global_config.pid_file)) {
1143 zlog(ZLOG_SYSERROR, "Unable to remove the PID file (%s).", fpm_global_config.pid_file);
1144 return -1;
1145 }
1146 }
1147 return 0;
1148 }
1149 /* }}} */
1150
fpm_conf_write_pid()1151 int fpm_conf_write_pid() /* {{{ */
1152 {
1153 int fd;
1154
1155 if (fpm_global_config.pid_file) {
1156 char buf[64];
1157 int len;
1158
1159 unlink(fpm_global_config.pid_file);
1160 fd = creat(fpm_global_config.pid_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1161
1162 if (fd < 0) {
1163 zlog(ZLOG_SYSERROR, "Unable to create the PID file (%s).", fpm_global_config.pid_file);
1164 return -1;
1165 }
1166
1167 len = sprintf(buf, "%d", (int) fpm_globals.parent_pid);
1168
1169 if (len != write(fd, buf, len)) {
1170 zlog(ZLOG_SYSERROR, "Unable to write to the PID file.");
1171 close(fd);
1172 return -1;
1173 }
1174 close(fd);
1175 }
1176 return 0;
1177 }
1178 /* }}} */
1179
fpm_conf_post_process(int force_daemon)1180 static int fpm_conf_post_process(int force_daemon) /* {{{ */
1181 {
1182 struct fpm_worker_pool_s *wp;
1183
1184 if (fpm_global_config.pid_file) {
1185 fpm_evaluate_full_path(&fpm_global_config.pid_file, NULL, PHP_LOCALSTATEDIR, 0);
1186 }
1187
1188 if (force_daemon >= 0) {
1189 /* forced from command line options */
1190 fpm_global_config.daemonize = force_daemon;
1191 }
1192
1193 fpm_globals.log_level = fpm_global_config.log_level;
1194 zlog_set_level(fpm_globals.log_level);
1195
1196 if (fpm_global_config.process_max < 0) {
1197 zlog(ZLOG_ERROR, "process_max can't be negative");
1198 return -1;
1199 }
1200
1201 if (fpm_global_config.process_priority != 64 && (fpm_global_config.process_priority < -19 || fpm_global_config.process_priority > 20)) {
1202 zlog(ZLOG_ERROR, "process.priority must be included into [-19,20]");
1203 return -1;
1204 }
1205
1206 if (!fpm_global_config.error_log) {
1207 fpm_global_config.error_log = strdup("log/php-fpm.log");
1208 }
1209
1210 #ifdef HAVE_SYSTEMD
1211 if (0 > fpm_systemd_conf()) {
1212 return -1;
1213 }
1214 #endif
1215
1216 #ifdef HAVE_SYSLOG_H
1217 if (!fpm_global_config.syslog_ident) {
1218 fpm_global_config.syslog_ident = strdup("php-fpm");
1219 }
1220
1221 if (fpm_global_config.syslog_facility < 0) {
1222 fpm_global_config.syslog_facility = LOG_DAEMON;
1223 }
1224
1225 if (strcasecmp(fpm_global_config.error_log, "syslog") != 0)
1226 #endif
1227 {
1228 fpm_evaluate_full_path(&fpm_global_config.error_log, NULL, PHP_LOCALSTATEDIR, 0);
1229 }
1230
1231 if (0 > fpm_stdio_open_error_log(0)) {
1232 return -1;
1233 }
1234
1235 if (0 > fpm_event_pre_init(fpm_global_config.events_mechanism)) {
1236 return -1;
1237 }
1238
1239 if (0 > fpm_conf_process_all_pools()) {
1240 return -1;
1241 }
1242
1243 if (0 > fpm_log_open(0)) {
1244 return -1;
1245 }
1246
1247 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
1248 if (!wp->config->access_log || !*wp->config->access_log) {
1249 continue;
1250 }
1251 if (0 > fpm_log_write(wp->config->access_format)) {
1252 zlog(ZLOG_ERROR, "[pool %s] wrong format for access.format '%s'", wp->config->name, wp->config->access_format);
1253 return -1;
1254 }
1255 }
1256
1257 return 0;
1258 }
1259 /* }}} */
1260
fpm_conf_cleanup(int which,void * arg)1261 static void fpm_conf_cleanup(int which, void *arg) /* {{{ */
1262 {
1263 free(fpm_global_config.pid_file);
1264 free(fpm_global_config.error_log);
1265 free(fpm_global_config.events_mechanism);
1266 fpm_global_config.pid_file = 0;
1267 fpm_global_config.error_log = 0;
1268 #ifdef HAVE_SYSLOG_H
1269 free(fpm_global_config.syslog_ident);
1270 fpm_global_config.syslog_ident = 0;
1271 #endif
1272 free(fpm_globals.config);
1273 }
1274 /* }}} */
1275
fpm_conf_ini_parser_include(char * inc,void * arg)1276 static void fpm_conf_ini_parser_include(char *inc, void *arg) /* {{{ */
1277 {
1278 char *filename;
1279 int *error = (int *)arg;
1280 #ifdef HAVE_GLOB
1281 glob_t g;
1282 #endif
1283 size_t i;
1284
1285 if (!inc || !arg) return;
1286 if (*error) return; /* We got already an error. Switch to the end. */
1287 spprintf(&filename, 0, "%s", ini_filename);
1288
1289 #ifdef HAVE_GLOB
1290 {
1291 g.gl_offs = 0;
1292 if ((i = glob(inc, GLOB_ERR | GLOB_MARK, NULL, &g)) != 0) {
1293 #ifdef GLOB_NOMATCH
1294 if (i == GLOB_NOMATCH) {
1295 zlog(ZLOG_WARNING, "Nothing matches the include pattern '%s' from %s at line %d.", inc, filename, ini_lineno);
1296 efree(filename);
1297 return;
1298 }
1299 #endif /* GLOB_NOMATCH */
1300 zlog(ZLOG_ERROR, "Unable to globalize '%s' (ret=%zd) from %s at line %d.", inc, i, filename, ini_lineno);
1301 *error = 1;
1302 efree(filename);
1303 return;
1304 }
1305
1306 for (i = 0; i < g.gl_pathc; i++) {
1307 int len = strlen(g.gl_pathv[i]);
1308 if (len < 1) continue;
1309 if (g.gl_pathv[i][len - 1] == '/') continue; /* don't parse directories */
1310 if (0 > fpm_conf_load_ini_file(g.gl_pathv[i])) {
1311 zlog(ZLOG_ERROR, "Unable to include %s from %s at line %d", g.gl_pathv[i], filename, ini_lineno);
1312 *error = 1;
1313 efree(filename);
1314 return;
1315 }
1316 }
1317 globfree(&g);
1318 }
1319 #else /* HAVE_GLOB */
1320 if (0 > fpm_conf_load_ini_file(inc)) {
1321 zlog(ZLOG_ERROR, "Unable to include %s from %s at line %d", inc, filename, ini_lineno);
1322 *error = 1;
1323 efree(filename);
1324 return;
1325 }
1326 #endif /* HAVE_GLOB */
1327
1328 efree(filename);
1329 }
1330 /* }}} */
1331
fpm_conf_ini_parser_section(zval * section,void * arg)1332 static void fpm_conf_ini_parser_section(zval *section, void *arg) /* {{{ */
1333 {
1334 struct fpm_worker_pool_s *wp;
1335 struct fpm_worker_pool_config_s *config;
1336 int *error = (int *)arg;
1337
1338 /* switch to global conf */
1339 if (!strcasecmp(Z_STRVAL_P(section), "global")) {
1340 current_wp = NULL;
1341 return;
1342 }
1343
1344 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
1345 if (!wp->config) continue;
1346 if (!wp->config->name) continue;
1347 if (!strcasecmp(wp->config->name, Z_STRVAL_P(section))) {
1348 /* Found a wp with the same name. Bring it back */
1349 current_wp = wp;
1350 return;
1351 }
1352 }
1353
1354 /* it's a new pool */
1355 config = (struct fpm_worker_pool_config_s *)fpm_worker_pool_config_alloc();
1356 if (!current_wp || !config) {
1357 zlog(ZLOG_ERROR, "[%s:%d] Unable to alloc a new WorkerPool for worker '%s'", ini_filename, ini_lineno, Z_STRVAL_P(section));
1358 *error = 1;
1359 return;
1360 }
1361 config->name = strdup(Z_STRVAL_P(section));
1362 if (!config->name) {
1363 zlog(ZLOG_ERROR, "[%s:%d] Unable to alloc memory for configuration name for worker '%s'", ini_filename, ini_lineno, Z_STRVAL_P(section));
1364 *error = 1;
1365 return;
1366 }
1367 }
1368 /* }}} */
1369
fpm_conf_ini_parser_entry(zval * name,zval * value,void * arg)1370 static void fpm_conf_ini_parser_entry(zval *name, zval *value, void *arg) /* {{{ */
1371 {
1372 struct ini_value_parser_s *parser;
1373 void *config = NULL;
1374
1375 int *error = (int *)arg;
1376 if (!value) {
1377 zlog(ZLOG_ERROR, "[%s:%d] value is NULL for a ZEND_INI_PARSER_ENTRY", ini_filename, ini_lineno);
1378 *error = 1;
1379 return;
1380 }
1381
1382 if (!strcmp(Z_STRVAL_P(name), "include")) {
1383 if (ini_include) {
1384 zlog(ZLOG_ERROR, "[%s:%d] two includes at the same time !", ini_filename, ini_lineno);
1385 *error = 1;
1386 return;
1387 }
1388 ini_include = strdup(Z_STRVAL_P(value));
1389 return;
1390 }
1391
1392 if (!current_wp) { /* we are in the global section */
1393 parser = ini_fpm_global_options;
1394 config = &fpm_global_config;
1395 } else {
1396 parser = ini_fpm_pool_options;
1397 config = current_wp->config;
1398 }
1399
1400 for (; parser->name; parser++) {
1401 if (!strcasecmp(parser->name, Z_STRVAL_P(name))) {
1402 char *ret;
1403 if (!parser->parser) {
1404 zlog(ZLOG_ERROR, "[%s:%d] the parser for entry '%s' is not defined", ini_filename, ini_lineno, parser->name);
1405 *error = 1;
1406 return;
1407 }
1408
1409 ret = parser->parser(value, &config, parser->offset);
1410 if (ret) {
1411 zlog(ZLOG_ERROR, "[%s:%d] unable to parse value for entry '%s': %s", ini_filename, ini_lineno, parser->name, ret);
1412 *error = 1;
1413 return;
1414 }
1415
1416 /* all is good ! */
1417 return;
1418 }
1419 }
1420
1421 /* nothing has been found if we got here */
1422 zlog(ZLOG_ERROR, "[%s:%d] unknown entry '%s'", ini_filename, ini_lineno, Z_STRVAL_P(name));
1423 *error = 1;
1424 }
1425 /* }}} */
1426
fpm_conf_ini_parser_array(zval * name,zval * key,zval * value,void * arg)1427 static void fpm_conf_ini_parser_array(zval *name, zval *key, zval *value, void *arg) /* {{{ */
1428 {
1429 int *error = (int *)arg;
1430 char *err = NULL;
1431 void *config;
1432
1433 if (!Z_STRVAL_P(key) || !Z_STRVAL_P(value) || !*Z_STRVAL_P(key)) {
1434 zlog(ZLOG_ERROR, "[%s:%d] Misspelled array ?", ini_filename, ini_lineno);
1435 *error = 1;
1436 return;
1437 }
1438 if (!current_wp || !current_wp->config) {
1439 zlog(ZLOG_ERROR, "[%s:%d] Array are not allowed in the global section", ini_filename, ini_lineno);
1440 *error = 1;
1441 return;
1442 }
1443
1444 if (!strcmp("env", Z_STRVAL_P(name))) {
1445 if (!*Z_STRVAL_P(value)) {
1446 zlog(ZLOG_ERROR, "[%s:%d] empty value", ini_filename, ini_lineno);
1447 *error = 1;
1448 return;
1449 }
1450 config = (char *)current_wp->config + WPO(env);
1451 err = fpm_conf_set_array(key, value, &config, 0);
1452
1453 } else if (!strcmp("php_value", Z_STRVAL_P(name))) {
1454 config = (char *)current_wp->config + WPO(php_values);
1455 err = fpm_conf_set_array(key, value, &config, 0);
1456
1457 } else if (!strcmp("php_admin_value", Z_STRVAL_P(name))) {
1458 config = (char *)current_wp->config + WPO(php_admin_values);
1459 err = fpm_conf_set_array(key, value, &config, 0);
1460
1461 } else if (!strcmp("php_flag", Z_STRVAL_P(name))) {
1462 config = (char *)current_wp->config + WPO(php_values);
1463 err = fpm_conf_set_array(key, value, &config, 1);
1464
1465 } else if (!strcmp("php_admin_flag", Z_STRVAL_P(name))) {
1466 config = (char *)current_wp->config + WPO(php_admin_values);
1467 err = fpm_conf_set_array(key, value, &config, 1);
1468
1469 } else {
1470 zlog(ZLOG_ERROR, "[%s:%d] unknown directive '%s'", ini_filename, ini_lineno, Z_STRVAL_P(name));
1471 *error = 1;
1472 return;
1473 }
1474
1475 if (err) {
1476 zlog(ZLOG_ERROR, "[%s:%d] error while parsing '%s[%s]' : %s", ini_filename, ini_lineno, Z_STRVAL_P(name), Z_STRVAL_P(key), err);
1477 *error = 1;
1478 return;
1479 }
1480 }
1481 /* }}} */
1482
fpm_conf_ini_parser(zval * arg1,zval * arg2,zval * arg3,int callback_type,void * arg)1483 static void fpm_conf_ini_parser(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg) /* {{{ */
1484 {
1485 int *error;
1486
1487 if (!arg1 || !arg) return;
1488 error = (int *)arg;
1489 if (*error) return; /* We got already an error. Switch to the end. */
1490
1491 switch(callback_type) {
1492 case ZEND_INI_PARSER_ENTRY:
1493 fpm_conf_ini_parser_entry(arg1, arg2, error);
1494 break;
1495 case ZEND_INI_PARSER_SECTION:
1496 fpm_conf_ini_parser_section(arg1, error);
1497 break;
1498 case ZEND_INI_PARSER_POP_ENTRY:
1499 fpm_conf_ini_parser_array(arg1, arg3, arg2, error);
1500 break;
1501 default:
1502 zlog(ZLOG_ERROR, "[%s:%d] Unknown INI syntax", ini_filename, ini_lineno);
1503 *error = 1;
1504 break;
1505 }
1506 }
1507 /* }}} */
1508
fpm_conf_load_ini_file(char * filename)1509 int fpm_conf_load_ini_file(char *filename) /* {{{ */
1510 {
1511 int error = 0;
1512 char *buf = NULL, *newbuf = NULL;
1513 int bufsize = 0;
1514 int fd, n;
1515 int nb_read = 1;
1516 char c = '*';
1517
1518 int ret = 1;
1519
1520 if (!filename || !filename[0]) {
1521 zlog(ZLOG_ERROR, "configuration filename is empty");
1522 return -1;
1523 }
1524
1525 fd = open(filename, O_RDONLY, 0);
1526 if (fd < 0) {
1527 zlog(ZLOG_SYSERROR, "failed to open configuration file '%s'", filename);
1528 return -1;
1529 }
1530
1531 if (ini_recursion++ > 4) {
1532 zlog(ZLOG_ERROR, "failed to include more than 5 files recusively");
1533 close(fd);
1534 return -1;
1535 }
1536
1537 ini_lineno = 0;
1538 while (nb_read > 0) {
1539 int tmp;
1540 ini_lineno++;
1541 ini_filename = filename;
1542 for (n = 0; (nb_read = read(fd, &c, sizeof(char))) == sizeof(char) && c != '\n'; n++) {
1543 if (n == bufsize) {
1544 bufsize += 1024;
1545 newbuf = (char*) realloc(buf, sizeof(char) * (bufsize + 2));
1546 if (newbuf == NULL) {
1547 ini_recursion--;
1548 close(fd);
1549 free(buf);
1550 return -1;
1551 }
1552 buf = newbuf;
1553 }
1554
1555 buf[n] = c;
1556 }
1557 if (n == 0) {
1558 continue;
1559 }
1560 /* always append newline and null terminate */
1561 buf[n++] = '\n';
1562 buf[n] = '\0';
1563 tmp = zend_parse_ini_string(buf, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t)fpm_conf_ini_parser, &error);
1564 ini_filename = filename;
1565 if (error || tmp == FAILURE) {
1566 if (ini_include) free(ini_include);
1567 ini_recursion--;
1568 close(fd);
1569 free(buf);
1570 return -1;
1571 }
1572 if (ini_include) {
1573 char *tmp = ini_include;
1574 ini_include = NULL;
1575 fpm_evaluate_full_path(&tmp, NULL, NULL, 0);
1576 fpm_conf_ini_parser_include(tmp, &error);
1577 if (error) {
1578 free(tmp);
1579 ini_recursion--;
1580 close(fd);
1581 free(buf);
1582 return -1;
1583 }
1584 free(tmp);
1585 }
1586 }
1587 free(buf);
1588
1589 ini_recursion--;
1590 close(fd);
1591 return ret;
1592 }
1593 /* }}} */
1594
fpm_conf_dump()1595 static void fpm_conf_dump() /* {{{ */
1596 {
1597 struct fpm_worker_pool_s *wp;
1598
1599 /*
1600 * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
1601 */
1602 zlog(ZLOG_NOTICE, "[global]");
1603 zlog(ZLOG_NOTICE, "\tpid = %s", STR2STR(fpm_global_config.pid_file));
1604 zlog(ZLOG_NOTICE, "\terror_log = %s", STR2STR(fpm_global_config.error_log));
1605 #ifdef HAVE_SYSLOG_H
1606 zlog(ZLOG_NOTICE, "\tsyslog.ident = %s", STR2STR(fpm_global_config.syslog_ident));
1607 zlog(ZLOG_NOTICE, "\tsyslog.facility = %d", fpm_global_config.syslog_facility); /* FIXME: convert to string */
1608 #endif
1609 zlog(ZLOG_NOTICE, "\tlog_level = %s", zlog_get_level_name(fpm_globals.log_level));
1610 zlog(ZLOG_NOTICE, "\temergency_restart_interval = %ds", fpm_global_config.emergency_restart_interval);
1611 zlog(ZLOG_NOTICE, "\temergency_restart_threshold = %d", fpm_global_config.emergency_restart_threshold);
1612 zlog(ZLOG_NOTICE, "\tprocess_control_timeout = %ds", fpm_global_config.process_control_timeout);
1613 zlog(ZLOG_NOTICE, "\tprocess.max = %d", fpm_global_config.process_max);
1614 if (fpm_global_config.process_priority == 64) {
1615 zlog(ZLOG_NOTICE, "\tprocess.priority = undefined");
1616 } else {
1617 zlog(ZLOG_NOTICE, "\tprocess.priority = %d", fpm_global_config.process_priority);
1618 }
1619 zlog(ZLOG_NOTICE, "\tdaemonize = %s", BOOL2STR(fpm_global_config.daemonize));
1620 zlog(ZLOG_NOTICE, "\trlimit_files = %d", fpm_global_config.rlimit_files);
1621 zlog(ZLOG_NOTICE, "\trlimit_core = %d", fpm_global_config.rlimit_core);
1622 zlog(ZLOG_NOTICE, "\tevents.mechanism = %s", fpm_event_machanism_name());
1623 #ifdef HAVE_SYSTEMD
1624 zlog(ZLOG_NOTICE, "\tsystemd_interval = %ds", fpm_global_config.systemd_interval/1000);
1625 #endif
1626 zlog(ZLOG_NOTICE, " ");
1627
1628 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
1629 struct key_value_s *kv;
1630 if (!wp->config) continue;
1631 zlog(ZLOG_NOTICE, "[%s]", STR2STR(wp->config->name));
1632 zlog(ZLOG_NOTICE, "\tprefix = %s", STR2STR(wp->config->prefix));
1633 zlog(ZLOG_NOTICE, "\tuser = %s", STR2STR(wp->config->user));
1634 zlog(ZLOG_NOTICE, "\tgroup = %s", STR2STR(wp->config->group));
1635 zlog(ZLOG_NOTICE, "\tlisten = %s", STR2STR(wp->config->listen_address));
1636 zlog(ZLOG_NOTICE, "\tlisten.backlog = %d", wp->config->listen_backlog);
1637 #ifdef HAVE_FPM_ACL
1638 zlog(ZLOG_NOTICE, "\tlisten.acl_users = %s", STR2STR(wp->config->listen_acl_users));
1639 zlog(ZLOG_NOTICE, "\tlisten.acl_groups = %s", STR2STR(wp->config->listen_acl_groups));
1640 #endif
1641 zlog(ZLOG_NOTICE, "\tlisten.owner = %s", STR2STR(wp->config->listen_owner));
1642 zlog(ZLOG_NOTICE, "\tlisten.group = %s", STR2STR(wp->config->listen_group));
1643 zlog(ZLOG_NOTICE, "\tlisten.mode = %s", STR2STR(wp->config->listen_mode));
1644 zlog(ZLOG_NOTICE, "\tlisten.allowed_clients = %s", STR2STR(wp->config->listen_allowed_clients));
1645 if (wp->config->process_priority == 64) {
1646 zlog(ZLOG_NOTICE, "\tprocess.priority = undefined");
1647 } else {
1648 zlog(ZLOG_NOTICE, "\tprocess.priority = %d", wp->config->process_priority);
1649 }
1650 zlog(ZLOG_NOTICE, "\tprocess.dumpable = %s", BOOL2STR(wp->config->process_dumpable));
1651 zlog(ZLOG_NOTICE, "\tpm = %s", PM2STR(wp->config->pm));
1652 zlog(ZLOG_NOTICE, "\tpm.max_children = %d", wp->config->pm_max_children);
1653 zlog(ZLOG_NOTICE, "\tpm.start_servers = %d", wp->config->pm_start_servers);
1654 zlog(ZLOG_NOTICE, "\tpm.min_spare_servers = %d", wp->config->pm_min_spare_servers);
1655 zlog(ZLOG_NOTICE, "\tpm.max_spare_servers = %d", wp->config->pm_max_spare_servers);
1656 zlog(ZLOG_NOTICE, "\tpm.process_idle_timeout = %d", wp->config->pm_process_idle_timeout);
1657 zlog(ZLOG_NOTICE, "\tpm.max_requests = %d", wp->config->pm_max_requests);
1658 zlog(ZLOG_NOTICE, "\tpm.status_path = %s", STR2STR(wp->config->pm_status_path));
1659 zlog(ZLOG_NOTICE, "\tping.path = %s", STR2STR(wp->config->ping_path));
1660 zlog(ZLOG_NOTICE, "\tping.response = %s", STR2STR(wp->config->ping_response));
1661 zlog(ZLOG_NOTICE, "\taccess.log = %s", STR2STR(wp->config->access_log));
1662 zlog(ZLOG_NOTICE, "\taccess.format = %s", STR2STR(wp->config->access_format));
1663 zlog(ZLOG_NOTICE, "\tslowlog = %s", STR2STR(wp->config->slowlog));
1664 zlog(ZLOG_NOTICE, "\trequest_slowlog_timeout = %ds", wp->config->request_slowlog_timeout);
1665 zlog(ZLOG_NOTICE, "\trequest_slowlog_trace_depth = %d", wp->config->request_slowlog_trace_depth);
1666 zlog(ZLOG_NOTICE, "\trequest_terminate_timeout = %ds", wp->config->request_terminate_timeout);
1667 zlog(ZLOG_NOTICE, "\trlimit_files = %d", wp->config->rlimit_files);
1668 zlog(ZLOG_NOTICE, "\trlimit_core = %d", wp->config->rlimit_core);
1669 zlog(ZLOG_NOTICE, "\tchroot = %s", STR2STR(wp->config->chroot));
1670 zlog(ZLOG_NOTICE, "\tchdir = %s", STR2STR(wp->config->chdir));
1671 zlog(ZLOG_NOTICE, "\tcatch_workers_output = %s", BOOL2STR(wp->config->catch_workers_output));
1672 zlog(ZLOG_NOTICE, "\tclear_env = %s", BOOL2STR(wp->config->clear_env));
1673 zlog(ZLOG_NOTICE, "\tsecurity.limit_extensions = %s", wp->config->security_limit_extensions);
1674
1675 for (kv = wp->config->env; kv; kv = kv->next) {
1676 zlog(ZLOG_NOTICE, "\tenv[%s] = %s", kv->key, kv->value);
1677 }
1678
1679 for (kv = wp->config->php_values; kv; kv = kv->next) {
1680 zlog(ZLOG_NOTICE, "\tphp_value[%s] = %s", kv->key, kv->value);
1681 }
1682
1683 for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
1684 zlog(ZLOG_NOTICE, "\tphp_admin_value[%s] = %s", kv->key, kv->value);
1685 }
1686 zlog(ZLOG_NOTICE, " ");
1687 }
1688 }
1689 /* }}} */
1690
fpm_conf_init_main(int test_conf,int force_daemon)1691 int fpm_conf_init_main(int test_conf, int force_daemon) /* {{{ */
1692 {
1693 int ret;
1694
1695 if (fpm_globals.prefix && *fpm_globals.prefix) {
1696 if (!fpm_conf_is_dir(fpm_globals.prefix)) {
1697 zlog(ZLOG_ERROR, "the global prefix '%s' does not exist or is not a directory", fpm_globals.prefix);
1698 return -1;
1699 }
1700 }
1701
1702 if (fpm_globals.pid && *fpm_globals.pid) {
1703 fpm_global_config.pid_file = strdup(fpm_globals.pid);
1704 }
1705
1706 if (fpm_globals.config == NULL) {
1707 char *tmp;
1708
1709 if (fpm_globals.prefix == NULL) {
1710 spprintf(&tmp, 0, "%s/php-fpm.conf", PHP_SYSCONFDIR);
1711 } else {
1712 spprintf(&tmp, 0, "%s/etc/php-fpm.conf", fpm_globals.prefix);
1713 }
1714
1715 if (!tmp) {
1716 zlog(ZLOG_SYSERROR, "spprintf() failed (tmp for fpm_globals.config)");
1717 return -1;
1718 }
1719
1720 fpm_globals.config = strdup(tmp);
1721 efree(tmp);
1722
1723 if (!fpm_globals.config) {
1724 zlog(ZLOG_SYSERROR, "spprintf() failed (fpm_globals.config)");
1725 return -1;
1726 }
1727 }
1728
1729 ret = fpm_conf_load_ini_file(fpm_globals.config);
1730
1731 if (0 > ret) {
1732 zlog(ZLOG_ERROR, "failed to load configuration file '%s'", fpm_globals.config);
1733 return -1;
1734 }
1735
1736 if (0 > fpm_conf_post_process(force_daemon)) {
1737 zlog(ZLOG_ERROR, "failed to post process the configuration");
1738 return -1;
1739 }
1740
1741 if (test_conf) {
1742 if (test_conf > 1) {
1743 fpm_conf_dump();
1744 }
1745 zlog(ZLOG_NOTICE, "configuration file %s test is successful\n", fpm_globals.config);
1746 fpm_globals.test_successful = 1;
1747 return -1;
1748 }
1749
1750 if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_conf_cleanup, 0)) {
1751 return -1;
1752 }
1753
1754 return 0;
1755 }
1756 /* }}} */
1757