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