xref: /PHP-5.3/sapi/fpm/fpm/fpm_conf.c (revision 331540d2)
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