xref: /php-src/sapi/fpm/fpm/fpm_events.c (revision d0056d63)
1 	/* (c) 2007,2008 Andrei Nigmatulin */
2 
3 #include "fpm_config.h"
4 
5 #include <unistd.h>
6 #include <errno.h>
7 #include <stdlib.h> /* for putenv */
8 #include <string.h>
9 
10 #include <php.h>
11 
12 #include "fpm.h"
13 #include "fpm_process_ctl.h"
14 #include "fpm_events.h"
15 #include "fpm_cleanup.h"
16 #include "fpm_stdio.h"
17 #include "fpm_signals.h"
18 #include "fpm_children.h"
19 #include "zlog.h"
20 #include "fpm_clock.h"
21 #include "fpm_log.h"
22 
23 #include "events/select.h"
24 #include "events/poll.h"
25 #include "events/epoll.h"
26 #include "events/devpoll.h"
27 #include "events/port.h"
28 #include "events/kqueue.h"
29 
30 #ifdef HAVE_SYSTEMD
31 #include "fpm_systemd.h"
32 #endif
33 
34 #define fpm_event_set_timeout(ev, now) timeradd(&(now), &(ev)->frequency, &(ev)->timeout);
35 
36 static void fpm_event_cleanup(int which, void *arg);
37 static void fpm_postponed_children_bury(struct fpm_event_s *ev, short which, void *arg);
38 static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg);
39 static struct fpm_event_s *fpm_event_queue_isset(struct fpm_event_queue_s *queue, struct fpm_event_s *ev);
40 static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_event_s *ev);
41 static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_event_s *ev);
42 static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue);
43 
44 static struct fpm_event_module_s *module;
45 static struct fpm_event_queue_s *fpm_event_queue_timer = NULL;
46 static struct fpm_event_queue_s *fpm_event_queue_fd = NULL;
47 static struct fpm_event_s children_bury_timer;
48 
fpm_event_cleanup(int which,void * arg)49 static void fpm_event_cleanup(int which, void *arg) /* {{{ */
50 {
51 	fpm_event_queue_destroy(&fpm_event_queue_timer);
52 	fpm_event_queue_destroy(&fpm_event_queue_fd);
53 }
54 /* }}} */
55 
fpm_postponed_children_bury(struct fpm_event_s * ev,short which,void * arg)56 static void fpm_postponed_children_bury(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
57 {
58 	fpm_children_bury();
59 }
60 /* }}} */
61 
fpm_got_signal(struct fpm_event_s * ev,short which,void * arg)62 static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
63 {
64 	char c;
65 	int res, ret;
66 	int fd = ev->fd;
67 
68 	do {
69 		do {
70 			res = read(fd, &c, 1);
71 		} while (res == -1 && errno == EINTR);
72 
73 		if (res <= 0) {
74 			if (res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
75 				zlog(ZLOG_SYSERROR, "unable to read from the signal pipe");
76 			}
77 			return;
78 		}
79 
80 		switch (c) {
81 			case 'C' :                  /* SIGCHLD */
82 				zlog(ZLOG_DEBUG, "received SIGCHLD");
83 				/* epoll_wait() may report signal fd before read events for a finished child
84 				 * in the same bunch of events. Prevent immediate free of the child structure
85 				 * and so the fpm_event_s instance. Otherwise use after free happens during
86 				 * attempt to process following read event. */
87 				fpm_event_set_timer(&children_bury_timer, 0, &fpm_postponed_children_bury, NULL);
88 				fpm_event_add(&children_bury_timer, 0);
89 				break;
90 			case 'I' :                  /* SIGINT  */
91 				zlog(ZLOG_DEBUG, "received SIGINT");
92 				zlog(ZLOG_NOTICE, "Terminating ...");
93 				fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
94 				break;
95 			case 'T' :                  /* SIGTERM */
96 				zlog(ZLOG_DEBUG, "received SIGTERM");
97 				zlog(ZLOG_NOTICE, "Terminating ...");
98 				fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
99 				break;
100 			case 'Q' :                  /* SIGQUIT */
101 				zlog(ZLOG_DEBUG, "received SIGQUIT");
102 				zlog(ZLOG_NOTICE, "Finishing ...");
103 				fpm_pctl(FPM_PCTL_STATE_FINISHING, FPM_PCTL_ACTION_SET);
104 				break;
105 			case '1' :                  /* SIGUSR1 */
106 				zlog(ZLOG_DEBUG, "received SIGUSR1");
107 
108 				/* fpm_stdio_init_final tied STDERR fd with error_log fd. This affects logging to the
109 				 * access.log if it was configured to write to the stderr. Check #8885. */
110 				fpm_stdio_restore_original_stderr(0);
111 
112 				if (0 == fpm_stdio_open_error_log(1)) {
113 					zlog(ZLOG_NOTICE, "error log file re-opened");
114 				} else {
115 					zlog(ZLOG_ERROR, "unable to re-opened error log file");
116 				}
117 
118 				ret = fpm_log_open(1);
119 				if (ret == 0) {
120 					zlog(ZLOG_NOTICE, "access log file re-opened");
121 				} else if (ret == -1) {
122 					zlog(ZLOG_ERROR, "unable to re-opened access log file");
123 				}
124 				/* else no access log are set */
125 
126 				/* We need to tie stderr with error_log in the master process after log files reload. Check #8885. */
127 				fpm_stdio_redirect_stderr_to_error_log();
128 
129 				break;
130 			case '2' :                  /* SIGUSR2 */
131 				zlog(ZLOG_DEBUG, "received SIGUSR2");
132 				zlog(ZLOG_NOTICE, "Reloading in progress ...");
133 				fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
134 				break;
135 		}
136 
137 		if (fpm_globals.is_child) {
138 			break;
139 		}
140 	} while (1);
141 	return;
142 }
143 /* }}} */
144 
fpm_event_queue_isset(struct fpm_event_queue_s * queue,struct fpm_event_s * ev)145 static struct fpm_event_s *fpm_event_queue_isset(struct fpm_event_queue_s *queue, struct fpm_event_s *ev) /* {{{ */
146 {
147 	if (!ev) {
148 		return NULL;
149 	}
150 
151 	while (queue) {
152 		if (queue->ev == ev) {
153 			return ev;
154 		}
155 		queue = queue->next;
156 	}
157 
158 	return NULL;
159 }
160 /* }}} */
161 
fpm_event_queue_add(struct fpm_event_queue_s ** queue,struct fpm_event_s * ev)162 static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_event_s *ev) /* {{{ */
163 {
164 	struct fpm_event_queue_s *elt;
165 
166 	if (!queue || !ev) {
167 		return -1;
168 	}
169 
170 	if (fpm_event_queue_isset(*queue, ev)) {
171 		return 0;
172 	}
173 
174 	if (!(elt = malloc(sizeof(struct fpm_event_queue_s)))) {
175 		zlog(ZLOG_SYSERROR, "Unable to add the event to queue: malloc() failed");
176 		return -1;
177 	}
178 	elt->prev = NULL;
179 	elt->next = NULL;
180 	elt->ev = ev;
181 
182 	if (*queue) {
183 		(*queue)->prev = elt;
184 		elt->next = *queue;
185 	}
186 	*queue = elt;
187 
188 	/* ask the event module to add the fd from its own queue */
189 	if (*queue == fpm_event_queue_fd && module->add) {
190 		module->add(ev);
191 	}
192 
193 	return 0;
194 }
195 /* }}} */
196 
fpm_event_queue_del(struct fpm_event_queue_s ** queue,struct fpm_event_s * ev)197 static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_event_s *ev) /* {{{ */
198 {
199 	struct fpm_event_queue_s *q;
200 	if (!queue || !ev) {
201 		return -1;
202 	}
203 	q = *queue;
204 	while (q) {
205 		if (q->ev == ev) {
206 			if (q->prev) {
207 				q->prev->next = q->next;
208 			}
209 			if (q->next) {
210 				q->next->prev = q->prev;
211 			}
212 			if (q == *queue) {
213 				*queue = q->next;
214 				if (*queue) {
215 					(*queue)->prev = NULL;
216 				}
217 			}
218 
219 			/* ask the event module to remove the fd from its own queue */
220 			if (*queue == fpm_event_queue_fd && module->remove) {
221 				module->remove(ev);
222 			}
223 
224 			free(q);
225 			return 0;
226 		}
227 		q = q->next;
228 	}
229 	return -1;
230 }
231 /* }}} */
232 
fpm_event_queue_destroy(struct fpm_event_queue_s ** queue)233 static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */
234 {
235 	struct fpm_event_queue_s *q, *tmp;
236 
237 	if (!queue) {
238 		return;
239 	}
240 
241 	if (*queue == fpm_event_queue_fd && module->clean) {
242 		module->clean();
243 	}
244 
245 	q = *queue;
246 	while (q) {
247 		tmp = q;
248 		q = q->next;
249 		/* q->prev = NULL */
250 		free(tmp);
251 	}
252 	*queue = NULL;
253 }
254 /* }}} */
255 
fpm_event_pre_init(char * mechanism)256 int fpm_event_pre_init(char *mechanism) /* {{{ */
257 {
258 	/* kqueue */
259 	module = fpm_event_kqueue_module();
260 	if (module) {
261 		if (!mechanism || strcasecmp(module->name, mechanism) == 0) {
262 			return 0;
263 		}
264 	}
265 
266 	/* port */
267 	module = fpm_event_port_module();
268 	if (module) {
269 		if (!mechanism || strcasecmp(module->name, mechanism) == 0) {
270 			return 0;
271 		}
272 	}
273 
274 	/* epoll */
275 	module = fpm_event_epoll_module();
276 	if (module) {
277 		if (!mechanism || strcasecmp(module->name, mechanism) == 0) {
278 			return 0;
279 		}
280 	}
281 
282 	/* /dev/poll */
283 	module = fpm_event_devpoll_module();
284 	if (module) {
285 		if (!mechanism || strcasecmp(module->name, mechanism) == 0) {
286 			return 0;
287 		}
288 	}
289 
290 	/* poll */
291 	module = fpm_event_poll_module();
292 	if (module) {
293 		if (!mechanism || strcasecmp(module->name, mechanism) == 0) {
294 			return 0;
295 		}
296 	}
297 
298 	/* select */
299 	module = fpm_event_select_module();
300 	if (module) {
301 		if (!mechanism || strcasecmp(module->name, mechanism) == 0) {
302 			return 0;
303 		}
304 	}
305 
306 	if (mechanism) {
307 		zlog(ZLOG_ERROR, "event mechanism '%s' is not available on this system", mechanism);
308 	} else {
309 		zlog(ZLOG_ERROR, "unable to find a suitable event mechanism on this system");
310 	}
311 	return -1;
312 }
313 /* }}} */
314 
fpm_event_mechanism_name(void)315 const char *fpm_event_mechanism_name(void)
316 {
317 	return module ? module->name : NULL;
318 }
319 
fpm_event_support_edge_trigger(void)320 int fpm_event_support_edge_trigger(void)
321 {
322 	return module ? module->support_edge_trigger : 0;
323 }
324 
fpm_event_init_main(void)325 int fpm_event_init_main(void)
326 {
327 	struct fpm_worker_pool_s *wp;
328 	int max;
329 
330 	if (!module) {
331 		zlog(ZLOG_ERROR, "no event module found");
332 		return -1;
333 	}
334 
335 	if (!module->wait) {
336 		zlog(ZLOG_ERROR, "Incomplete event implementation. Please open a bug report on https://github.com/php/php-src/issues.");
337 		return -1;
338 	}
339 
340 	/* count the max number of necessary fds for polling */
341 	max = 1; /* only one FD is necessary at startup for the master process signal pipe */
342 	for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
343 		if (!wp->config) continue;
344 		if (wp->config->catch_workers_output && wp->config->pm_max_children > 0) {
345 			max += (wp->config->pm_max_children * 2);
346 		}
347 	}
348 
349 	if (module->init(max) < 0) {
350 		zlog(ZLOG_ERROR, "Unable to initialize the event module %s", module->name);
351 		return -1;
352 	}
353 
354 	zlog(ZLOG_DEBUG, "event module is %s and %d fds have been reserved", module->name, max);
355 
356 	if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_event_cleanup, NULL)) {
357 		return -1;
358 	}
359 	return 0;
360 }
361 
fpm_event_loop(int err)362 void fpm_event_loop(int err) /* {{{ */
363 {
364 	static struct fpm_event_s signal_fd_event;
365 
366 	/* sanity check */
367 	if (fpm_globals.parent_pid != getpid()) {
368 		return;
369 	}
370 
371 	fpm_event_set(&signal_fd_event, fpm_signals_get_fd(), FPM_EV_READ, &fpm_got_signal, NULL);
372 	fpm_event_add(&signal_fd_event, 0);
373 
374 	/* add timers */
375 	if (fpm_globals.heartbeat > 0) {
376 		fpm_pctl_heartbeat(NULL, 0, NULL);
377 	}
378 
379 	if (!err) {
380 		fpm_pctl_perform_idle_server_maintenance_heartbeat(NULL, 0, NULL);
381 
382 		zlog(ZLOG_DEBUG, "%zu bytes have been reserved in SHM", fpm_shm_get_size_allocated());
383 		zlog(ZLOG_NOTICE, "ready to handle connections");
384 
385 #ifdef HAVE_SYSTEMD
386 		fpm_systemd_heartbeat(NULL, 0, NULL);
387 #endif
388 	}
389 
390 	while (1) {
391 		struct fpm_event_queue_s *q, *q2;
392 		struct timeval ms;
393 		struct timeval tmp;
394 		struct timeval now;
395 		unsigned long int timeout;
396 		int ret;
397 
398 		/* sanity check */
399 		if (fpm_globals.parent_pid != getpid()) {
400 			return;
401 		}
402 
403 		fpm_clock_get(&now);
404 		timerclear(&ms);
405 
406 		/* search in the timeout queue for the next timer to trigger */
407 		q = fpm_event_queue_timer;
408 		while (q) {
409 			if (!timerisset(&ms)) {
410 				ms = q->ev->timeout;
411 			} else {
412 				if (timercmp(&q->ev->timeout, &ms, <)) {
413 					ms = q->ev->timeout;
414 				}
415 			}
416 			q = q->next;
417 		}
418 
419 		/* 1s timeout if none has been set */
420 		if (!timerisset(&ms) || timercmp(&ms, &now, <) || timercmp(&ms, &now, ==)) {
421 			timeout = 1000;
422 		} else {
423 			timersub(&ms, &now, &tmp);
424 			timeout = (tmp.tv_sec * 1000) + (tmp.tv_usec / 1000) + 1;
425 		}
426 
427 		ret = module->wait(fpm_event_queue_fd, timeout);
428 
429 		/* is a child, nothing to do here */
430 		if (ret == -2) {
431 			return;
432 		}
433 
434 		if (ret > 0) {
435 			zlog(ZLOG_DEBUG, "event module triggered %d events", ret);
436 		}
437 
438 		/* trigger timers */
439 		q = fpm_event_queue_timer;
440 		while (q) {
441 			struct fpm_event_queue_s *next = q->next;
442 			fpm_clock_get(&now);
443 			if (q->ev) {
444 				if (timercmp(&now, &q->ev->timeout, >) || timercmp(&now, &q->ev->timeout, ==)) {
445 					struct fpm_event_s *ev = q->ev;
446 					if (ev->flags & FPM_EV_PERSIST) {
447 						fpm_event_set_timeout(ev, now);
448 					} else {
449 						/* Delete the event. Make sure this happens before it is fired,
450 						 * so that the event callback may register the same timer again. */
451 						q2 = q;
452 						if (q->prev) {
453 							q->prev->next = q->next;
454 						}
455 						if (q->next) {
456 							q->next->prev = q->prev;
457 						}
458 						if (q == fpm_event_queue_timer) {
459 							fpm_event_queue_timer = q->next;
460 							if (fpm_event_queue_timer) {
461 								fpm_event_queue_timer->prev = NULL;
462 							}
463 						}
464 						free(q2);
465 					}
466 
467 					fpm_event_fire(ev);
468 
469 					/* sanity check */
470 					if (fpm_globals.parent_pid != getpid()) {
471 						return;
472 					}
473 				}
474 			}
475 			q = next;
476 		}
477 	}
478 }
479 /* }}} */
480 
fpm_event_fire(struct fpm_event_s * ev)481 void fpm_event_fire(struct fpm_event_s *ev) /* {{{ */
482 {
483 	if (!ev || !ev->callback) {
484 		return;
485 	}
486 
487 	(*ev->callback)( (struct fpm_event_s *) ev, ev->which, ev->arg);
488 }
489 /* }}} */
490 
fpm_event_set(struct fpm_event_s * ev,int fd,int flags,void (* callback)(struct fpm_event_s *,short,void *),void * arg)491 int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void (*callback)(struct fpm_event_s *, short, void *), void *arg) /* {{{ */
492 {
493 	if (!ev || !callback || fd < -1) {
494 		return -1;
495 	}
496 	memset(ev, 0, sizeof(struct fpm_event_s));
497 	ev->fd = fd;
498 	ev->callback = callback;
499 	ev->arg = arg;
500 	ev->flags = flags;
501 	return 0;
502 }
503 /* }}} */
504 
fpm_event_add(struct fpm_event_s * ev,unsigned long int frequency)505 int fpm_event_add(struct fpm_event_s *ev, unsigned long int frequency) /* {{{ */
506 {
507 	struct timeval now;
508 	struct timeval tmp;
509 
510 	if (!ev) {
511 		return -1;
512 	}
513 
514 	ev->index = -1;
515 
516 	/* it's a triggered event on incoming data */
517 	if (ev->flags & FPM_EV_READ) {
518 		ev->which = FPM_EV_READ;
519 		if (fpm_event_queue_add(&fpm_event_queue_fd, ev) != 0) {
520 			return -1;
521 		}
522 		return 0;
523 	}
524 
525 	/* it's a timer event */
526 	ev->which = FPM_EV_TIMEOUT;
527 
528 	fpm_clock_get(&now);
529 	if (frequency >= 1000) {
530 		tmp.tv_sec = frequency / 1000;
531 		tmp.tv_usec = (frequency % 1000) * 1000;
532 	} else {
533 		tmp.tv_sec = 0;
534 		tmp.tv_usec = frequency * 1000;
535 	}
536 	ev->frequency = tmp;
537 	fpm_event_set_timeout(ev, now);
538 
539 	if (fpm_event_queue_add(&fpm_event_queue_timer, ev) != 0) {
540 		return -1;
541 	}
542 
543 	return 0;
544 }
545 /* }}} */
546 
fpm_event_del(struct fpm_event_s * ev)547 int fpm_event_del(struct fpm_event_s *ev) /* {{{ */
548 {
549 	if (ev->index >= 0 && fpm_event_queue_del(&fpm_event_queue_fd, ev) != 0) {
550 		return -1;
551 	}
552 
553 	if (ev->index < 0 && fpm_event_queue_del(&fpm_event_queue_timer, ev) != 0) {
554 		return -1;
555 	}
556 
557 	return 0;
558 }
559 /* }}} */
560