php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
fpm_children.c
Go to the documentation of this file.
1 /* (c) 2007,2008 Andrei Nigmatulin */
2
3#include "fpm_config.h"
4
5#include <sys/types.h>
6#include <sys/wait.h>
7#include <time.h>
8#include <unistd.h>
9#include <string.h>
10#include <stdio.h>
11
12#include "fpm.h"
13#include "fpm_children.h"
14#include "fpm_signals.h"
15#include "fpm_worker_pool.h"
16#include "fpm_sockets.h"
17#include "fpm_process_ctl.h"
18#include "fpm_php.h"
19#include "fpm_conf.h"
20#include "fpm_cleanup.h"
21#include "fpm_events.h"
22#include "fpm_clock.h"
23#include "fpm_stdio.h"
24#include "fpm_unix.h"
25#include "fpm_env.h"
26#include "fpm_scoreboard.h"
27#include "fpm_status.h"
28#include "fpm_log.h"
29
30#include "zlog.h"
31
32static time_t *last_faults;
33static int fault;
34
35static void fpm_children_cleanup(int which, void *arg) /* {{{ */
36{
37 free(last_faults);
38}
39/* }}} */
40
41static struct fpm_child_s *fpm_child_alloc(void)
42{
43 struct fpm_child_s *ret;
44
45 ret = malloc(sizeof(struct fpm_child_s));
46
47 if (!ret) {
48 return 0;
49 }
50
51 memset(ret, 0, sizeof(*ret));
52 ret->scoreboard_i = -1;
53 return ret;
54}
55
56static void fpm_child_free(struct fpm_child_s *child) /* {{{ */
57{
58 if (child->log_stream) {
60 free(child->log_stream);
61 }
62 free(child);
63}
64/* }}} */
65
66static void fpm_postponed_child_free(struct fpm_event_s *ev, short which, void *arg)
67{
68 struct fpm_child_s *child = (struct fpm_child_s *) arg;
69
70 if (child->fd_stdout != -1) {
71 fpm_event_del(&child->ev_stdout);
72 close(child->fd_stdout);
73 }
74 if (child->fd_stderr != -1) {
75 fpm_event_del(&child->ev_stderr);
76 close(child->fd_stderr);
77 }
78
79 fpm_child_free((struct fpm_child_s *) child);
80}
81
82static void fpm_child_close(struct fpm_child_s *child, int in_event_loop) /* {{{ */
83{
84 if (child->fd_stdout != -1) {
85 if (in_event_loop) {
86 child->postponed_free = true;
87 fpm_event_fire(&child->ev_stdout);
88 }
89 if (child->fd_stdout != -1) {
90 close(child->fd_stdout);
91 }
92 }
93
94 if (child->fd_stderr != -1) {
95 if (in_event_loop) {
96 child->postponed_free = true;
97 fpm_event_fire(&child->ev_stderr);
98 }
99 if (child->fd_stderr != -1) {
100 close(child->fd_stderr);
101 }
102 }
103
104 if (in_event_loop && child->postponed_free) {
105 fpm_event_set_timer(&child->ev_free, 0, &fpm_postponed_child_free, child);
106 fpm_event_add(&child->ev_free, 1000);
107 } else {
108 fpm_child_free(child);
109 }
110}
111/* }}} */
112
113static void fpm_child_link(struct fpm_child_s *child) /* {{{ */
114{
115 struct fpm_worker_pool_s *wp = child->wp;
116
117 ++wp->running_children;
118 ++fpm_globals.running_children;
119
120 child->next = wp->children;
121 if (child->next) {
122 child->next->prev = child;
123 }
124 child->prev = 0;
125 wp->children = child;
126}
127/* }}} */
128
129static void fpm_child_unlink(struct fpm_child_s *child) /* {{{ */
130{
131 --child->wp->running_children;
132 --fpm_globals.running_children;
133
134 if (child->prev) {
135 child->prev->next = child->next;
136 } else {
137 child->wp->children = child->next;
138 }
139
140 if (child->next) {
141 child->next->prev = child->prev;
142 }
143}
144/* }}} */
145
146struct fpm_child_s *fpm_child_find(pid_t pid) /* {{{ */
147{
148 struct fpm_worker_pool_s *wp;
149 struct fpm_child_s *child = 0;
150
151 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
152
153 for (child = wp->children; child; child = child->next) {
154 if (child->pid == pid) {
155 break;
156 }
157 }
158
159 if (child) break;
160 }
161
162 if (!child) {
163 return 0;
164 }
165
166 return child;
167}
168/* }}} */
169
170static int fpm_child_cloexec(void)
171{
172 /* get listening socket attributes so it can be extended */
173 int attrs = fcntl(fpm_globals.listening_socket, F_GETFD);
174 if (0 > attrs) {
175 zlog(ZLOG_WARNING, "failed to get attributes of listening socket, errno: %d", errno);
176 return -1;
177 }
178
179 /* set CLOEXEC to prevent the descriptor leaking to child processes */
180 if (0 > fcntl(fpm_globals.listening_socket, F_SETFD, attrs | FD_CLOEXEC)) {
181 zlog(ZLOG_WARNING, "failed to change attribute of listening socket");
182 return -1;
183 }
184
185 return 0;
186}
187
188static void fpm_child_init(struct fpm_worker_pool_s *wp) /* {{{ */
189{
190 fpm_globals.max_requests = wp->config->pm_max_requests;
191 fpm_globals.listening_socket = dup(wp->listening_socket);
192
193 if (0 > fpm_stdio_init_child(wp) ||
194 0 > fpm_log_init_child(wp) ||
196 0 > fpm_unix_init_child(wp) ||
198 0 > fpm_env_init_child(wp) ||
199 0 > fpm_php_init_child(wp) ||
200 0 > fpm_child_cloexec()) {
201
202 zlog(ZLOG_ERROR, "[pool %s] child failed to initialize", wp->config->name);
204 }
205}
206/* }}} */
207
208int fpm_children_free(struct fpm_child_s *child) /* {{{ */
209{
210 struct fpm_child_s *next;
211
212 for (; child; child = next) {
213 next = child->next;
214 fpm_child_close(child, 0 /* in_event_loop */);
215 }
216
217 return 0;
218}
219/* }}} */
220
222{
223 int status;
224 pid_t pid;
225 struct fpm_child_s *child;
226
227 while ( (pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
228 char buf[128];
229 int severity = ZLOG_NOTICE;
230 int restart_child = 1;
231
232 child = fpm_child_find(pid);
233
234 if (WIFEXITED(status)) {
235
236 snprintf(buf, sizeof(buf), "with code %d", WEXITSTATUS(status));
237
238 /* if it's been killed because of dynamic process management
239 * don't restart it automatically
240 */
241 if (child && child->idle_kill) {
242 restart_child = 0;
243 }
244
245 if (WEXITSTATUS(status) != FPM_EXIT_OK) {
246 severity = ZLOG_WARNING;
247 }
248
249 } else if (WIFSIGNALED(status)) {
250 const char *signame = fpm_signal_names[WTERMSIG(status)];
251#ifdef WCOREDUMP
252 const char *have_core = WCOREDUMP(status) ? " - core dumped" : "";
253#else
254 const char* have_core = "";
255#endif
256
257 if (signame == NULL) {
258 signame = "";
259 }
260
261 snprintf(buf, sizeof(buf), "on signal %d (%s%s)", WTERMSIG(status), signame, have_core);
262
263 /* if it's been killed because of dynamic process management
264 * don't restart it automatically
265 */
266 if (child && child->idle_kill && WTERMSIG(status) == SIGQUIT) {
267 restart_child = 0;
268 }
269
270 if (WTERMSIG(status) != SIGQUIT) { /* possible request loss */
271 severity = ZLOG_WARNING;
272 }
273 } else if (WIFSTOPPED(status)) {
274
275 zlog(ZLOG_NOTICE, "child %d stopped for tracing", (int) pid);
276
277 if (child && child->tracer) {
278 child->tracer(child);
279 }
280
281 continue;
282 }
283
284 if (child) {
285 struct fpm_worker_pool_s *wp = child->wp;
286 struct timeval tv1, tv2;
287
288 fpm_child_unlink(child);
289
291
292 fpm_clock_get(&tv1);
293
294 timersub(&tv1, &child->started, &tv2);
295
296 if (restart_child) {
298 severity = ZLOG_DEBUG;
299 }
300 zlog(severity, "[pool %s] child %d exited %s after %ld.%06d seconds from start", wp->config->name, (int) pid, buf, (long)tv2.tv_sec, (int) tv2.tv_usec);
301 } else {
302 zlog(ZLOG_DEBUG, "[pool %s] child %d has been killed by the process management after %ld.%06d seconds from start", wp->config->name, (int) pid, (long)tv2.tv_sec, (int) tv2.tv_usec);
303 }
304
305 fpm_child_close(child, 1 /* in event_loop */);
306
308
309 if (last_faults && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) {
310 time_t now = tv1.tv_sec;
311 int restart_condition = 1;
312 int i;
313
314 last_faults[fault++] = now;
315
316 if (fault == fpm_global_config.emergency_restart_threshold) {
317 fault = 0;
318 }
319
320 for (i = 0; i < fpm_global_config.emergency_restart_threshold; i++) {
321 if (now - last_faults[i] > fpm_global_config.emergency_restart_interval) {
322 restart_condition = 0;
323 break;
324 }
325 }
326
327 if (restart_condition) {
328
329 zlog(ZLOG_WARNING, "failed processes threshold (%d in %d sec) is reached, initiating reload", fpm_global_config.emergency_restart_threshold, fpm_global_config.emergency_restart_interval);
330
332 }
333 }
334
335 if (restart_child) {
336 fpm_children_make(wp, 1 /* in event loop */, 1, 0);
337
338 if (fpm_globals.is_child) {
339 break;
340 }
341 }
342 } else if (fpm_globals.parent_pid == 1) {
343 zlog(ZLOG_DEBUG, "unknown child (%d) exited %s - most likely an orphan process (master process is the init process)", pid, buf);
344 } else {
345 zlog(ZLOG_WARNING, "unknown child (%d) exited %s - potentially a bug or pre exec child (e.g. s6-notifyoncheck)", pid, buf);
346 }
347 }
348}
349
350static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp) /* {{{ */
351{
352 struct fpm_child_s *c;
353
354 c = fpm_child_alloc();
355
356 if (!c) {
357 zlog(ZLOG_ERROR, "[pool %s] unable to malloc new child", wp->config->name);
358 return 0;
359 }
360
361 c->wp = wp;
362 c->fd_stdout = -1; c->fd_stderr = -1;
363
364 if (0 > fpm_stdio_prepare_pipes(c)) {
365 fpm_child_free(c);
366 return 0;
367 }
368
369 if (0 > fpm_scoreboard_proc_alloc(c)) {
371 fpm_child_free(c);
372 return 0;
373 }
374
375 return c;
376}
377/* }}} */
378
379static void fpm_resources_discard(struct fpm_child_s *child) /* {{{ */
380{
383 fpm_child_free(child);
384}
385/* }}} */
386
387static void fpm_child_resources_use(struct fpm_child_s *child) /* {{{ */
388{
389 struct fpm_worker_pool_s *wp;
390 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
391 if (wp == child->wp || wp == child->wp->shared) {
392 continue;
393 }
395 }
396
397 fpm_scoreboard_child_use(child, getpid());
399 fpm_child_free(child);
400}
401/* }}} */
402
403static void fpm_parent_resources_use(struct fpm_child_s *child) /* {{{ */
404{
406 fpm_child_link(child);
407}
408/* }}} */
409
410int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug) /* {{{ */
411{
412 pid_t pid;
413 struct fpm_child_s *child;
414 int max;
415 static int warned = 0;
416
417 if (wp->config->pm == PM_STYLE_DYNAMIC) {
418 if (!in_event_loop) { /* starting */
420 } else {
421 max = wp->running_children + nb_to_spawn;
422 }
423 } else if (wp->config->pm == PM_STYLE_ONDEMAND) {
424 if (!in_event_loop) { /* starting */
425 max = 0; /* do not create any child at startup */
426 } else {
427 max = wp->running_children + nb_to_spawn;
428 }
429 } else { /* PM_STYLE_STATIC */
431 }
432
433 /*
434 * fork children while:
435 * - fpm_pctl_can_spawn_children : FPM is running in a NORMAL state (aka not restart, stop or reload)
436 * - wp->running_children < max : there is less than the max process for the current pool
437 * - (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max):
438 * if fpm_global_config.process_max is set, FPM has not fork this number of processes (globally)
439 */
440 while (fpm_pctl_can_spawn_children() && wp->running_children < max && (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max)) {
441
442 warned = 0;
443 child = fpm_resources_prepare(wp);
444
445 if (!child) {
446 return 2;
447 }
448
449 zlog(ZLOG_DEBUG, "blocking signals before child birth");
450 if (0 > fpm_signals_child_block()) {
451 zlog(ZLOG_WARNING, "child may miss signals");
452 }
453
454 pid = fork();
455
456 switch (pid) {
457
458 case 0 :
459 fpm_child_resources_use(child);
460 fpm_globals.is_child = 1;
461 fpm_child_init(wp);
462 return 0;
463
464 case -1 :
465 zlog(ZLOG_DEBUG, "unblocking signals");
467 zlog(ZLOG_SYSERROR, "fork() failed");
468
469 fpm_resources_discard(child);
470 return 2;
471
472 default :
473 zlog(ZLOG_DEBUG, "unblocking signals, child born");
475 child->pid = pid;
476 fpm_clock_get(&child->started);
477 fpm_parent_resources_use(child);
478
479 zlog(is_debug ? ZLOG_DEBUG : ZLOG_NOTICE, "[pool %s] child %d started", wp->config->name, (int) pid);
480 }
481
482 }
483
484 if (!warned && fpm_global_config.process_max > 0 && fpm_globals.running_children >= fpm_global_config.process_max) {
485 if (wp->running_children < max) {
486 warned = 1;
487 zlog(ZLOG_WARNING, "The maximum number of processes has been reached. Please review your configuration and consider raising 'process.max'");
488 }
489 }
490
491 return 1; /* we are done */
492}
493/* }}} */
494
496{
497 if (wp->config->pm == PM_STYLE_ONDEMAND) {
498 wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s));
499
500 if (!wp->ondemand_event) {
501 zlog(ZLOG_ERROR, "[pool %s] unable to malloc the ondemand socket event", wp->config->name);
502 // FIXME handle crash
503 return 1;
504 }
505
506 memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s));
508 wp->socket_event_set = 1;
510
511 return 1;
512 }
513 return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1);
514}
515/* }}} */
516
518{
519 if (fpm_global_config.emergency_restart_threshold &&
520 fpm_global_config.emergency_restart_interval) {
521
522 last_faults = malloc(sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
523
524 if (!last_faults) {
525 return -1;
526 }
527
528 memset(last_faults, 0, sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
529 }
530
531 if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_children_cleanup, 0)) {
532 return -1;
533 }
534
535 return 0;
536}
DNS_STATUS status
Definition dns_win32.c:49
#define max(a, b)
Definition exif.c:60
zval * arg
Definition ffi.c:3975
memset(ptr, 0, type->size)
zend_ffi_ctype_name_buf buf
Definition ffi.c:4685
#define FD_CLOEXEC
Definition file.h:148
struct fpm_globals_s fpm_globals
Definition fpm.c:24
#define FPM_EXIT_SOFTWARE
Definition fpm.h:27
#define FPM_EXIT_OK
Definition fpm.h:15
struct fpm_child_s * fpm_child_find(pid_t pid)
int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug)
int fpm_children_create_initial(struct fpm_worker_pool_s *wp)
int fpm_children_free(struct fpm_child_s *child)
void fpm_children_bury(void)
int fpm_children_init_main(void)
int fpm_cleanup_add(int type, void(*cleanup)(int, void *), void *arg)
Definition fpm_cleanup.c:18
@ FPM_CLEANUP_ALL
Definition fpm_cleanup.h:15
int fpm_clock_get(struct timeval *tv)
Definition fpm_clock.c:110
struct fpm_global_config_s fpm_global_config
Definition fpm_conf.c:64
@ PM_STYLE_ONDEMAND
Definition fpm_conf.h:121
@ PM_STYLE_DYNAMIC
Definition fpm_conf.h:120
#define timersub(tvp, uvp, vvp)
Definition fpm_config.h:36
int fpm_env_init_child(struct fpm_worker_pool_s *wp)
Definition fpm_env.c:138
int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void(*callback)(struct fpm_event_s *, short, void *), void *arg)
Definition fpm_events.c:482
int fpm_event_add(struct fpm_event_s *ev, unsigned long int frequency)
Definition fpm_events.c:496
void fpm_event_fire(struct fpm_event_s *ev)
Definition fpm_events.c:472
int fpm_event_del(struct fpm_event_s *ev)
Definition fpm_events.c:538
#define FPM_EV_EDGE
Definition fpm_events.h:9
#define fpm_event_set_timer(ev, flags, cb, arg)
Definition fpm_events.h:11
#define FPM_EV_READ
Definition fpm_events.h:7
int fpm_log_init_child(struct fpm_worker_pool_s *wp)
Definition fpm_log.c:69
int fpm_php_init_child(struct fpm_worker_pool_s *wp)
Definition fpm_php.c:244
void fpm_pctl(int new_state, int action)
int fpm_pctl_can_spawn_children(void)
void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg)
int fpm_pctl_child_exited(void)
@ FPM_PCTL_STATE_RELOADING
@ FPM_PCTL_ACTION_SET
void fpm_scoreboard_free(struct fpm_worker_pool_s *wp)
void fpm_scoreboard_child_use(struct fpm_child_s *child, pid_t pid)
void fpm_scoreboard_proc_free(struct fpm_child_s *child)
int fpm_scoreboard_proc_alloc(struct fpm_child_s *child)
int fpm_signals_unblock(void)
int fpm_signals_init_child(void)
const char * fpm_signal_names[NSIG+1]
Definition fpm_signals.c:25
int fpm_signals_child_block(void)
int fpm_status_init_child(struct fpm_worker_pool_s *wp)
Definition fpm_status.c:24
int fpm_stdio_init_child(struct fpm_worker_pool_s *wp)
Definition fpm_stdio.c:140
int fpm_stdio_discard_pipes(struct fpm_child_s *child)
Definition fpm_stdio.c:339
void fpm_stdio_child_use_pipes(struct fpm_child_s *child)
Definition fpm_stdio.c:354
int fpm_stdio_prepare_pipes(struct fpm_child_s *child)
Definition fpm_stdio.c:288
int fpm_stdio_parent_use_pipes(struct fpm_child_s *child)
Definition fpm_stdio.c:318
int fpm_unix_init_child(struct fpm_worker_pool_s *wp)
Definition fpm_unix.c:424
struct fpm_worker_pool_s * fpm_worker_all_pools
#define NULL
Definition gdcache.h:45
#define next(ls)
Definition minilua.c:2661
const SIGQUIT
const WNOHANG
const SIGSEGV
const WUNTRACED
const SIGBUS
time_t now
Definition session.c:1281
struct timeval started
void(* tracer)(struct fpm_child_s *)
bool postponed_free
struct fpm_event_s ev_stdout ev_stderr ev_free
struct fpm_child_s * prev
struct fpm_child_s * next
struct fpm_worker_pool_s * wp
struct zlog_stream * log_stream
struct fpm_worker_pool_config_s * config
struct fpm_event_s * ondemand_event
struct fpm_worker_pool_s * shared
struct fpm_worker_pool_s * next
struct fpm_child_s * children
#define close(a)
#define errno
exit(string|int $status=0)
#define snprintf
zval * ret
zlog_bool zlog_stream_close(struct zlog_stream *stream)
Definition zlog.c:878
#define ZLOG_SYSERROR
Definition zlog.h:53
@ ZLOG_DEBUG
Definition zlog.h:42
@ ZLOG_ERROR
Definition zlog.h:45
@ ZLOG_NOTICE
Definition zlog.h:43
@ ZLOG_WARNING
Definition zlog.h:44
#define zlog(flags,...)
Definition zlog.h:9