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