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