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