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