This source file includes following definitions.
- fpm_children_cleanup
- fpm_child_alloc
- fpm_child_free
- fpm_child_close
- fpm_child_link
- fpm_child_unlink
- fpm_child_find
- fpm_child_init
- fpm_children_free
- fpm_children_bury
- fpm_resources_prepare
- fpm_resources_discard
- fpm_child_resources_use
- fpm_parent_resources_use
- fpm_children_make
- fpm_children_create_initial
- fpm_children_init_main
1
2
3
4
5 #include "fpm_config.h"
6
7 #include <sys/types.h>
8 #include <sys/wait.h>
9 #include <time.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <stdio.h>
13
14 #include "fpm.h"
15 #include "fpm_children.h"
16 #include "fpm_signals.h"
17 #include "fpm_worker_pool.h"
18 #include "fpm_sockets.h"
19 #include "fpm_process_ctl.h"
20 #include "fpm_php.h"
21 #include "fpm_conf.h"
22 #include "fpm_cleanup.h"
23 #include "fpm_events.h"
24 #include "fpm_clock.h"
25 #include "fpm_stdio.h"
26 #include "fpm_unix.h"
27 #include "fpm_env.h"
28 #include "fpm_scoreboard.h"
29 #include "fpm_status.h"
30 #include "fpm_log.h"
31
32 #include "zlog.h"
33
34 static time_t *last_faults;
35 static int fault;
36
37 static void fpm_children_cleanup(int which, void *arg)
38 {
39 free(last_faults);
40 }
41
42
43 static struct fpm_child_s *fpm_child_alloc()
44 {
45 struct fpm_child_s *ret;
46
47 ret = malloc(sizeof(struct fpm_child_s));
48
49 if (!ret) {
50 return 0;
51 }
52
53 memset(ret, 0, sizeof(*ret));
54 ret->scoreboard_i = -1;
55 return ret;
56 }
57
58
59 static void fpm_child_free(struct fpm_child_s *child)
60 {
61 free(child);
62 }
63
64
65 static void fpm_child_close(struct fpm_child_s *child, int in_event_loop)
66 {
67 if (child->fd_stdout != -1) {
68 if (in_event_loop) {
69 fpm_event_fire(&child->ev_stdout);
70 }
71 if (child->fd_stdout != -1) {
72 close(child->fd_stdout);
73 }
74 }
75
76 if (child->fd_stderr != -1) {
77 if (in_event_loop) {
78 fpm_event_fire(&child->ev_stderr);
79 }
80 if (child->fd_stderr != -1) {
81 close(child->fd_stderr);
82 }
83 }
84
85 fpm_child_free(child);
86 }
87
88
89 static void fpm_child_link(struct fpm_child_s *child)
90 {
91 struct fpm_worker_pool_s *wp = child->wp;
92
93 ++wp->running_children;
94 ++fpm_globals.running_children;
95
96 child->next = wp->children;
97 if (child->next) {
98 child->next->prev = child;
99 }
100 child->prev = 0;
101 wp->children = child;
102 }
103
104
105 static void fpm_child_unlink(struct fpm_child_s *child)
106 {
107 --child->wp->running_children;
108 --fpm_globals.running_children;
109
110 if (child->prev) {
111 child->prev->next = child->next;
112 } else {
113 child->wp->children = child->next;
114 }
115
116 if (child->next) {
117 child->next->prev = child->prev;
118 }
119 }
120
121
122 static struct fpm_child_s *fpm_child_find(pid_t pid)
123 {
124 struct fpm_worker_pool_s *wp;
125 struct fpm_child_s *child = 0;
126
127 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
128
129 for (child = wp->children; child; child = child->next) {
130 if (child->pid == pid) {
131 break;
132 }
133 }
134
135 if (child) break;
136 }
137
138 if (!child) {
139 return 0;
140 }
141
142 return child;
143 }
144
145
146 static void fpm_child_init(struct fpm_worker_pool_s *wp)
147 {
148 fpm_globals.max_requests = wp->config->pm_max_requests;
149
150 if (0 > fpm_stdio_init_child(wp) ||
151 0 > fpm_log_init_child(wp) ||
152 0 > fpm_status_init_child(wp) ||
153 0 > fpm_unix_init_child(wp) ||
154 0 > fpm_signals_init_child() ||
155 0 > fpm_env_init_child(wp) ||
156 0 > fpm_php_init_child(wp)) {
157
158 zlog(ZLOG_ERROR, "[pool %s] child failed to initialize", wp->config->name);
159 exit(FPM_EXIT_SOFTWARE);
160 }
161 }
162
163
164 int fpm_children_free(struct fpm_child_s *child)
165 {
166 struct fpm_child_s *next;
167
168 for (; child; child = next) {
169 next = child->next;
170 fpm_child_close(child, 0 );
171 }
172
173 return 0;
174 }
175
176
177 void fpm_children_bury()
178 {
179 int status;
180 pid_t pid;
181 struct fpm_child_s *child;
182
183 while ( (pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
184 char buf[128];
185 int severity = ZLOG_NOTICE;
186 int restart_child = 1;
187
188 child = fpm_child_find(pid);
189
190 if (WIFEXITED(status)) {
191
192 snprintf(buf, sizeof(buf), "with code %d", WEXITSTATUS(status));
193
194
195
196
197 if (child && child->idle_kill) {
198 restart_child = 0;
199 }
200
201 if (WEXITSTATUS(status) != FPM_EXIT_OK) {
202 severity = ZLOG_WARNING;
203 }
204
205 } else if (WIFSIGNALED(status)) {
206 const char *signame = fpm_signal_names[WTERMSIG(status)];
207 const char *have_core = WCOREDUMP(status) ? " - core dumped" : "";
208
209 if (signame == NULL) {
210 signame = "";
211 }
212
213 snprintf(buf, sizeof(buf), "on signal %d (%s%s)", WTERMSIG(status), signame, have_core);
214
215
216
217
218 if (child && child->idle_kill && WTERMSIG(status) == SIGQUIT) {
219 restart_child = 0;
220 }
221
222 if (WTERMSIG(status) != SIGQUIT) {
223 severity = ZLOG_WARNING;
224 }
225 } else if (WIFSTOPPED(status)) {
226
227 zlog(ZLOG_NOTICE, "child %d stopped for tracing", (int) pid);
228
229 if (child && child->tracer) {
230 child->tracer(child);
231 }
232
233 continue;
234 }
235
236 if (child) {
237 struct fpm_worker_pool_s *wp = child->wp;
238 struct timeval tv1, tv2;
239
240 fpm_child_unlink(child);
241
242 fpm_scoreboard_proc_free(wp->scoreboard, child->scoreboard_i);
243
244 fpm_clock_get(&tv1);
245
246 timersub(&tv1, &child->started, &tv2);
247
248 if (restart_child) {
249 if (!fpm_pctl_can_spawn_children()) {
250 severity = ZLOG_DEBUG;
251 }
252 zlog(severity, "[pool %s] child %d exited %s after %ld.%06d seconds from start", child->wp->config->name, (int) pid, buf, tv2.tv_sec, (int) tv2.tv_usec);
253 } else {
254 zlog(ZLOG_DEBUG, "[pool %s] child %d has been killed by the process management after %ld.%06d seconds from start", child->wp->config->name, (int) pid, tv2.tv_sec, (int) tv2.tv_usec);
255 }
256
257 fpm_child_close(child, 1 );
258
259 fpm_pctl_child_exited();
260
261 if (last_faults && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) {
262 time_t now = tv1.tv_sec;
263 int restart_condition = 1;
264 int i;
265
266 last_faults[fault++] = now;
267
268 if (fault == fpm_global_config.emergency_restart_threshold) {
269 fault = 0;
270 }
271
272 for (i = 0; i < fpm_global_config.emergency_restart_threshold; i++) {
273 if (now - last_faults[i] > fpm_global_config.emergency_restart_interval) {
274 restart_condition = 0;
275 break;
276 }
277 }
278
279 if (restart_condition) {
280
281 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);
282
283 fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
284 }
285 }
286
287 if (restart_child) {
288 fpm_children_make(wp, 1 , 1, 0);
289
290 if (fpm_globals.is_child) {
291 break;
292 }
293 }
294 } else {
295 zlog(ZLOG_ALERT, "oops, unknown child (%d) exited %s. Please open a bug report (https://bugs.php.net).", pid, buf);
296 }
297 }
298 }
299
300
301 static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp)
302 {
303 struct fpm_child_s *c;
304
305 c = fpm_child_alloc();
306
307 if (!c) {
308 zlog(ZLOG_ERROR, "[pool %s] unable to malloc new child", wp->config->name);
309 return 0;
310 }
311
312 c->wp = wp;
313 c->fd_stdout = -1; c->fd_stderr = -1;
314
315 if (0 > fpm_stdio_prepare_pipes(c)) {
316 fpm_child_free(c);
317 return 0;
318 }
319
320 if (0 > fpm_scoreboard_proc_alloc(wp->scoreboard, &c->scoreboard_i)) {
321 fpm_stdio_discard_pipes(c);
322 fpm_child_free(c);
323 return 0;
324 }
325
326 return c;
327 }
328
329
330 static void fpm_resources_discard(struct fpm_child_s *child)
331 {
332 fpm_scoreboard_proc_free(child->wp->scoreboard, child->scoreboard_i);
333 fpm_stdio_discard_pipes(child);
334 fpm_child_free(child);
335 }
336
337
338 static void fpm_child_resources_use(struct fpm_child_s *child)
339 {
340 struct fpm_worker_pool_s *wp;
341 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
342 if (wp == child->wp) {
343 continue;
344 }
345 fpm_scoreboard_free(wp->scoreboard);
346 }
347
348 fpm_scoreboard_child_use(child->wp->scoreboard, child->scoreboard_i, getpid());
349 fpm_stdio_child_use_pipes(child);
350 fpm_child_free(child);
351 }
352
353
354 static void fpm_parent_resources_use(struct fpm_child_s *child)
355 {
356 fpm_stdio_parent_use_pipes(child);
357 fpm_child_link(child);
358 }
359
360
361 int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug)
362 {
363 pid_t pid;
364 struct fpm_child_s *child;
365 int max;
366 static int warned = 0;
367
368 if (wp->config->pm == PM_STYLE_DYNAMIC) {
369 if (!in_event_loop) {
370 max = wp->config->pm_start_servers;
371 } else {
372 max = wp->running_children + nb_to_spawn;
373 }
374 } else if (wp->config->pm == PM_STYLE_ONDEMAND) {
375 if (!in_event_loop) {
376 max = 0;
377 } else {
378 max = wp->running_children + nb_to_spawn;
379 }
380 } else {
381 max = wp->config->pm_max_children;
382 }
383
384
385
386
387
388
389
390
391 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)) {
392
393 warned = 0;
394 child = fpm_resources_prepare(wp);
395
396 if (!child) {
397 return 2;
398 }
399
400 pid = fork();
401
402 switch (pid) {
403
404 case 0 :
405 fpm_child_resources_use(child);
406 fpm_globals.is_child = 1;
407 fpm_child_init(wp);
408 return 0;
409
410 case -1 :
411 zlog(ZLOG_SYSERROR, "fork() failed");
412
413 fpm_resources_discard(child);
414 return 2;
415
416 default :
417 child->pid = pid;
418 fpm_clock_get(&child->started);
419 fpm_parent_resources_use(child);
420
421 zlog(is_debug ? ZLOG_DEBUG : ZLOG_NOTICE, "[pool %s] child %d started", wp->config->name, (int) pid);
422 }
423
424 }
425
426 if (!warned && fpm_global_config.process_max > 0 && fpm_globals.running_children >= fpm_global_config.process_max) {
427 warned = 1;
428 zlog(ZLOG_WARNING, "The maximum number of processes has been reached. Please review your configuration and consider raising 'process.max'");
429 }
430
431 return 1;
432 }
433
434
435 int fpm_children_create_initial(struct fpm_worker_pool_s *wp)
436 {
437 if (wp->config->pm == PM_STYLE_ONDEMAND) {
438 wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s));
439
440 if (!wp->ondemand_event) {
441 zlog(ZLOG_ERROR, "[pool %s] unable to malloc the ondemand socket event", wp->config->name);
442
443 return 1;
444 }
445
446 memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s));
447 fpm_event_set(wp->ondemand_event, wp->listening_socket, FPM_EV_READ | FPM_EV_EDGE, fpm_pctl_on_socket_accept, wp);
448 wp->socket_event_set = 1;
449 fpm_event_add(wp->ondemand_event, 0);
450
451 return 1;
452 }
453 return fpm_children_make(wp, 0 , 0, 1);
454 }
455
456
457 int fpm_children_init_main()
458 {
459 if (fpm_global_config.emergency_restart_threshold &&
460 fpm_global_config.emergency_restart_interval) {
461
462 last_faults = malloc(sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
463
464 if (!last_faults) {
465 return -1;
466 }
467
468 memset(last_faults, 0, sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
469 }
470
471 if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_children_cleanup, 0)) {
472 return -1;
473 }
474
475 return 0;
476 }
477
478