xref: /php-src/sapi/fpm/fpm/fpm_status.c (revision 065bde1e)
1 	/* (c) 2009 Jerome Loyet */
2 
3 #include "php.h"
4 #include "zend_long.h"
5 #include "SAPI.h"
6 #include <stdio.h>
7 
8 #include "fpm_config.h"
9 #include "fpm_scoreboard.h"
10 #include "fpm_status.h"
11 #include "fpm_clock.h"
12 #include "zlog.h"
13 #include "fpm_atomic.h"
14 #include "fpm_conf.h"
15 #include "fpm_php.h"
16 #include "ext/standard/html.h"
17 #include "ext/json/php_json.h"
18 
19 static char *fpm_status_uri = NULL;
20 static char *fpm_status_ping_uri = NULL;
21 static char *fpm_status_ping_response = NULL;
22 
23 
fpm_status_init_child(struct fpm_worker_pool_s * wp)24 int fpm_status_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
25 {
26 	if (!wp || !wp->config) {
27 		zlog(ZLOG_ERROR, "unable to init fpm_status because conf structure is NULL");
28 		return -1;
29 	}
30 
31 	if (wp->config->pm_status_path) {
32 		fpm_status_uri = strdup(wp->config->pm_status_path);
33 	}
34 
35 	if (wp->config->ping_path) {
36 		if (!wp->config->ping_response) {
37 			zlog(ZLOG_ERROR, "[pool %s] ping is set (%s) but ping.response is not set.", wp->config->name, wp->config->ping_path);
38 			return -1;
39 		}
40 		fpm_status_ping_uri = strdup(wp->config->ping_path);
41 		fpm_status_ping_response = strdup(wp->config->ping_response);
42 	}
43 
44 	return 0;
45 }
46 /* }}} */
47 
fpm_status_export_to_zval(zval * status)48 int fpm_status_export_to_zval(zval *status)
49 {
50 	struct fpm_scoreboard_s scoreboard, *scoreboard_p;
51 	zval fpm_proc_stats, fpm_proc_stat;
52 	time_t now_epoch;
53 	struct timeval duration, now;
54 	double cpu;
55 	int i;
56 
57 	scoreboard_p = fpm_scoreboard_acquire(NULL, 1);
58 	if (!scoreboard_p) {
59 		zlog(ZLOG_NOTICE, "[pool (unknown)] status: scoreboard already in use.");
60 		return -1;
61 	}
62 
63 	/* copy the scoreboard not to bother other processes */
64 	scoreboard = *scoreboard_p;
65 	struct fpm_scoreboard_proc_s *procs = safe_emalloc(
66 			sizeof(struct fpm_scoreboard_proc_s), scoreboard.nprocs, 0);
67 
68 	struct fpm_scoreboard_proc_s *proc_p;
69 	for(i=0; i<scoreboard.nprocs; i++) {
70 		proc_p = fpm_scoreboard_proc_acquire(scoreboard_p, i, 1);
71 		if (!proc_p){
72 			procs[i].used=-1;
73 			continue;
74 		}
75 		procs[i] = *proc_p;
76 		fpm_scoreboard_proc_release(proc_p);
77 	}
78 	fpm_scoreboard_release(scoreboard_p);
79 
80 	now_epoch = time(NULL);
81 	fpm_clock_get(&now);
82 
83 	array_init(status);
84 	add_assoc_string(status, "pool", scoreboard.pool);
85 	add_assoc_string(status, "process-manager", PM2STR(scoreboard.pm));
86 	add_assoc_long(status, "start-time", scoreboard.start_epoch);
87 	add_assoc_long(status, "start-since", now_epoch - scoreboard.start_epoch);
88 	add_assoc_long(status, "accepted-conn", scoreboard.requests);
89 	add_assoc_long(status, "listen-queue", scoreboard.lq);
90 	add_assoc_long(status, "max-listen-queue", scoreboard.lq_max);
91 	add_assoc_long(status, "listen-queue-len", scoreboard.lq_len);
92 	add_assoc_long(status, "idle-processes", scoreboard.idle);
93 	add_assoc_long(status, "active-processes", scoreboard.active);
94 	add_assoc_long(status, "total-processes", scoreboard.idle + scoreboard.active);
95 	add_assoc_long(status, "max-active-processes", scoreboard.active_max);
96 	add_assoc_long(status, "max-children-reached", scoreboard.max_children_reached);
97 	add_assoc_long(status, "slow-requests", scoreboard.slow_rq);
98 	add_assoc_long(status, "memory-peak", scoreboard.memory_peak);
99 
100 	array_init(&fpm_proc_stats);
101 	for(i=0; i<scoreboard.nprocs; i++) {
102 		if (!procs[i].used) {
103 			continue;
104 		}
105 		proc_p = &procs[i];
106 		/* prevent NaN */
107 		if (procs[i].cpu_duration.tv_sec == 0 && procs[i].cpu_duration.tv_usec == 0) {
108 			cpu = 0.;
109 		} else {
110 			cpu = (procs[i].last_request_cpu.tms_utime + procs[i].last_request_cpu.tms_stime + procs[i].last_request_cpu.tms_cutime + procs[i].last_request_cpu.tms_cstime) / fpm_scoreboard_get_tick() / (procs[i].cpu_duration.tv_sec + procs[i].cpu_duration.tv_usec / 1000000.) * 100.;
111 		}
112 
113 		array_init(&fpm_proc_stat);
114 		add_assoc_long(&fpm_proc_stat, "pid", procs[i].pid);
115 		add_assoc_string(&fpm_proc_stat, "state", fpm_request_get_stage_name(procs[i].request_stage));
116 		add_assoc_long(&fpm_proc_stat, "start-time", procs[i].start_epoch);
117 		add_assoc_long(&fpm_proc_stat, "start-since", now_epoch - procs[i].start_epoch);
118 		add_assoc_long(&fpm_proc_stat, "requests", procs[i].requests);
119 		if (procs[i].request_stage == FPM_REQUEST_ACCEPTING) {
120 			duration = procs[i].duration;
121 		} else {
122 			timersub(&now, &procs[i].accepted, &duration);
123 		}
124 		add_assoc_long(&fpm_proc_stat, "request-duration", duration.tv_sec * 1000000UL + duration.tv_usec);
125 		add_assoc_string(&fpm_proc_stat, "request-method", procs[i].request_method[0] != '\0' ? procs[i].request_method : "-");
126 		add_assoc_string(&fpm_proc_stat, "request-uri", procs[i].request_uri);
127 		add_assoc_string(&fpm_proc_stat, "query-string", procs[i].query_string);
128 		add_assoc_long(&fpm_proc_stat, "request-length", procs[i].content_length);
129 		add_assoc_string(&fpm_proc_stat, "user", procs[i].auth_user[0] != '\0' ? procs[i].auth_user : "-");
130 		add_assoc_string(&fpm_proc_stat, "script", procs[i].script_filename[0] != '\0' ? procs[i].script_filename : "-");
131 		add_assoc_double(&fpm_proc_stat, "last-request-cpu", procs[i].request_stage == FPM_REQUEST_ACCEPTING ? cpu : 0.);
132 		add_assoc_long(&fpm_proc_stat, "last-request-memory", procs[i].request_stage == FPM_REQUEST_ACCEPTING ? procs[i].memory : 0);
133 		add_next_index_zval(&fpm_proc_stats, &fpm_proc_stat);
134 	}
135 	add_assoc_zval(status, "procs", &fpm_proc_stats);
136 	efree(procs);
137 
138 	return 0;
139 }
140 /* }}} */
141 
fpm_status_handle_request(void)142 int fpm_status_handle_request(void) /* {{{ */
143 {
144 	struct fpm_scoreboard_s *scoreboard_p;
145 	struct fpm_scoreboard_proc_s *proc;
146 	char *buffer, *time_format, time_buffer[64];
147 	time_t now_epoch;
148 	int full, has_start_time;
149 	bool encode_html, encode_json;
150 	char *short_syntax, *short_post;
151 	char *full_pre, *full_syntax, *full_post, *full_separator;
152 
153 	if (!SG(request_info).request_uri) {
154 		return 0;
155 	}
156 
157 	/* PING */
158 	if (fpm_status_ping_uri && fpm_status_ping_response && !strcmp(fpm_status_ping_uri, SG(request_info).request_uri)) {
159 		fpm_request_executing();
160 		sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1);
161 		sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1);
162 		sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1);
163 		SG(sapi_headers).http_response_code = 200;
164 
165 		/* handle HEAD */
166 		if (SG(request_info).headers_only) {
167 			return 1;
168 		}
169 
170 		PUTS(fpm_status_ping_response);
171 		return 1;
172 	}
173 
174 	/* STATUS */
175 	if (fpm_status_uri && !strcmp(fpm_status_uri, SG(request_info).request_uri)) {
176 		zend_string *_GET_str;
177 
178 		fpm_request_executing();
179 
180 		/* full status ? */
181 		_GET_str = ZSTR_INIT_LITERAL("_GET", 0);
182 		full = fpm_php_is_key_in_table(_GET_str, ZEND_STRL("full"));
183 		short_syntax = short_post = NULL;
184 		full_separator = full_pre = full_syntax = full_post = NULL;
185 		encode_html = false;
186 		encode_json = false;
187 		has_start_time = 1;
188 
189 		scoreboard_p = fpm_scoreboard_get();
190 		if (scoreboard_p) {
191 			scoreboard_p = fpm_scoreboard_copy(scoreboard_p->shared ? scoreboard_p->shared : scoreboard_p, full);
192 		}
193 		if (!scoreboard_p) {
194 			zlog(ZLOG_ERROR, "status: unable to find or access status shared memory");
195 			SG(sapi_headers).http_response_code = 500;
196 			sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1);
197 			sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1);
198 			sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1);
199 			PUTS("Internal error. Please review log file for errors.");
200 			return 1;
201 		}
202 
203 		if (scoreboard_p->idle < 0 || scoreboard_p->active < 0) {
204 			fpm_scoreboard_free_copy(scoreboard_p);
205 			zlog(ZLOG_ERROR, "[pool %s] invalid status values", scoreboard_p->pool);
206 			SG(sapi_headers).http_response_code = 500;
207 			sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1);
208 			sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1);
209 			sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1);
210 			PUTS("Internal error. Please review log file for errors.");
211 			return 1;
212 		}
213 
214 		/* send common headers */
215 		sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1);
216 		sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1);
217 		SG(sapi_headers).http_response_code = 200;
218 
219 		/* handle HEAD */
220 		if (SG(request_info).headers_only) {
221 			fpm_scoreboard_free_copy(scoreboard_p);
222 			return 1;
223 		}
224 
225 		/* HTML */
226 		if (fpm_php_is_key_in_table(_GET_str, ZEND_STRL("html"))) {
227 			sapi_add_header_ex(ZEND_STRL("Content-Type: text/html"), 1, 1);
228 			time_format = "%d/%b/%Y:%H:%M:%S %z";
229 			encode_html = true;
230 
231 			short_syntax =
232 				"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
233 				"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
234 				"<head><title>PHP-FPM Status Page</title></head>\n"
235 				"<body>\n"
236 				"<table>\n"
237 					"<tr><th>pool</th><td>%s</td></tr>\n"
238 					"<tr><th>process manager</th><td>%s</td></tr>\n"
239 					"<tr><th>start time</th><td>%s</td></tr>\n"
240 					"<tr><th>start since</th><td>%lu</td></tr>\n"
241 					"<tr><th>accepted conn</th><td>%lu</td></tr>\n"
242 					"<tr><th>listen queue</th><td>%d</td></tr>\n"
243 					"<tr><th>max listen queue</th><td>%d</td></tr>\n"
244 					"<tr><th>listen queue len</th><td>%u</td></tr>\n"
245 					"<tr><th>idle processes</th><td>%d</td></tr>\n"
246 					"<tr><th>active processes</th><td>%d</td></tr>\n"
247 					"<tr><th>total processes</th><td>%d</td></tr>\n"
248 					"<tr><th>max active processes</th><td>%d</td></tr>\n"
249 					"<tr><th>max children reached</th><td>%u</td></tr>\n"
250 					"<tr><th>slow requests</th><td>%lu</td></tr>\n"
251 					"<tr><th>memory peak</th><td>%zu</td></tr>\n"
252 				"</table>\n";
253 
254 			if (!full) {
255 				short_post = "</body></html>";
256 			} else {
257 				full_pre =
258 					"<table border=\"1\">\n"
259 					"<tr>"
260 						"<th>pid</th>"
261 						"<th>state</th>"
262 						"<th>start time</th>"
263 						"<th>start since</th>"
264 						"<th>requests</th>"
265 						"<th>request duration</th>"
266 						"<th>request method</th>"
267 						"<th>request uri</th>"
268 						"<th>content length</th>"
269 						"<th>user</th>"
270 						"<th>script</th>"
271 						"<th>last request cpu</th>"
272 						"<th>last request memory</th>"
273 					"</tr>\n";
274 
275 				full_syntax =
276 					"<tr>"
277 						"<td>%d</td>"
278 						"<td>%s</td>"
279 						"<td>%s</td>"
280 						"<td>%lu</td>"
281 						"<td>%lu</td>"
282 						"<td>%lu</td>"
283 						"<td>%s</td>"
284 						"<td>%s%s%s</td>"
285 						"<td>%zu</td>"
286 						"<td>%s</td>"
287 						"<td>%s</td>"
288 						"<td>%.2f</td>"
289 						"<td>%zu</td>"
290 					"</tr>\n";
291 
292 				full_post = "</table></body></html>";
293 			}
294 
295 		/* XML */
296 		} else if (fpm_php_is_key_in_table(_GET_str, ZEND_STRL("xml"))) {
297 			sapi_add_header_ex(ZEND_STRL("Content-Type: text/xml"), 1, 1);
298 			time_format = "%s";
299 			encode_html = true;
300 
301 			short_syntax =
302 				"<?xml version=\"1.0\" ?>\n"
303 				"<status>\n"
304 				"<pool>%s</pool>\n"
305 				"<process-manager>%s</process-manager>\n"
306 				"<start-time>%s</start-time>\n"
307 				"<start-since>%lu</start-since>\n"
308 				"<accepted-conn>%lu</accepted-conn>\n"
309 				"<listen-queue>%d</listen-queue>\n"
310 				"<max-listen-queue>%d</max-listen-queue>\n"
311 				"<listen-queue-len>%u</listen-queue-len>\n"
312 				"<idle-processes>%d</idle-processes>\n"
313 				"<active-processes>%d</active-processes>\n"
314 				"<total-processes>%d</total-processes>\n"
315 				"<max-active-processes>%d</max-active-processes>\n"
316 				"<max-children-reached>%u</max-children-reached>\n"
317 				"<slow-requests>%lu</slow-requests>\n"
318 				"<memory-peak>%zu</memory-peak>\n";
319 
320 				if (!full) {
321 					short_post = "</status>";
322 				} else {
323 					full_pre = "<processes>\n";
324 					full_syntax =
325 						"<process>"
326 							"<pid>%d</pid>"
327 							"<state>%s</state>"
328 							"<start-time>%s</start-time>"
329 							"<start-since>%lu</start-since>"
330 							"<requests>%lu</requests>"
331 							"<request-duration>%lu</request-duration>"
332 							"<request-method>%s</request-method>"
333 							"<request-uri>%s%s%s</request-uri>"
334 							"<content-length>%zu</content-length>"
335 							"<user>%s</user>"
336 							"<script>%s</script>"
337 							"<last-request-cpu>%.2f</last-request-cpu>"
338 							"<last-request-memory>%zu</last-request-memory>"
339 						"</process>\n"
340 					;
341 					full_post = "</processes>\n</status>";
342 				}
343 
344 			/* JSON */
345 		} else if (fpm_php_is_key_in_table(_GET_str, ZEND_STRL("json"))) {
346 			sapi_add_header_ex(ZEND_STRL("Content-Type: application/json"), 1, 1);
347 			time_format = "%s";
348 
349 			encode_json = true;
350 
351 			short_syntax =
352 				"{"
353 				"\"pool\":\"%s\","
354 				"\"process manager\":\"%s\","
355 				"\"start time\":%s,"
356 				"\"start since\":%lu,"
357 				"\"accepted conn\":%lu,"
358 				"\"listen queue\":%d,"
359 				"\"max listen queue\":%d,"
360 				"\"listen queue len\":%u,"
361 				"\"idle processes\":%d,"
362 				"\"active processes\":%d,"
363 				"\"total processes\":%d,"
364 				"\"max active processes\":%d,"
365 				"\"max children reached\":%u,"
366 				"\"slow requests\":%lu,"
367 				"\"memory peak\":%zu";
368 
369 			if (!full) {
370 				short_post = "}";
371 			} else {
372 				full_separator = ",";
373 				full_pre = ", \"processes\":[";
374 
375 				full_syntax = "{"
376 					"\"pid\":%d,"
377 					"\"state\":\"%s\","
378 					"\"start time\":%s,"
379 					"\"start since\":%lu,"
380 					"\"requests\":%lu,"
381 					"\"request duration\":%lu,"
382 					"\"request method\":\"%s\","
383 					"\"request uri\":\"%s%s%s\","
384 					"\"content length\":%zu,"
385 					"\"user\":\"%s\","
386 					"\"script\":\"%s\","
387 					"\"last request cpu\":%.2f,"
388 					"\"last request memory\":%zu"
389 					"}";
390 
391 				full_post = "]}";
392 			}
393 
394 			/* OpenMetrics */
395 		} else if (fpm_php_is_key_in_table(_GET_str, ZEND_STRL("openmetrics"))) {
396 			sapi_add_header_ex(ZEND_STRL("Content-Type: application/openmetrics-text; version=1.0.0; charset=utf-8"), 1, 1);
397 			time_format = "%s";
398 
399 			short_syntax =
400 				"# HELP phpfpm_up Could pool %s using a %s PM on PHP-FPM be reached?\n"
401 				"# TYPE phpfpm_up gauge\n"
402 				"phpfpm_up 1\n"
403 				"# HELP phpfpm_start_since The number of seconds since FPM has started.\n"
404 				"# TYPE phpfpm_start_since counter\n"
405 				"phpfpm_start_since %lu\n"
406 				"# HELP phpfpm_accepted_connections The number of requests accepted by the pool.\n"
407 				"# TYPE phpfpm_accepted_connections counter\n"
408 				"phpfpm_accepted_connections %lu\n"
409 				"# HELP phpfpm_listen_queue The number of requests in the queue of pending connections.\n"
410 				"# TYPE phpfpm_listen_queue gauge\n"
411 				"phpfpm_listen_queue %d\n"
412 				"# HELP phpfpm_max_listen_queue The maximum number of requests in the queue of pending connections since FPM has started.\n"
413 				"# TYPE phpfpm_max_listen_queue counter\n"
414 				"phpfpm_max_listen_queue %d\n"
415 				"# TYPE phpfpm_listen_queue_length gauge\n"
416 				"# HELP phpfpm_listen_queue_length The size of the socket queue of pending connections.\n"
417 				"phpfpm_listen_queue_length %u\n"
418 				"# HELP phpfpm_idle_processes The number of idle processes.\n"
419 				"# TYPE phpfpm_idle_processes gauge\n"
420 				"phpfpm_idle_processes %d\n"
421 				"# HELP phpfpm_active_processes The number of active processes.\n"
422 				"# TYPE phpfpm_active_processes gauge\n"
423 				"phpfpm_active_processes %d\n"
424 				"# HELP phpfpm_total_processes The number of idle + active processes.\n"
425 				"# TYPE phpfpm_total_processes gauge\n"
426 				"phpfpm_total_processes %d\n"
427 				"# HELP phpfpm_max_active_processes The maximum number of active processes since FPM has started.\n"
428 				"# TYPE phpfpm_max_active_processes counter\n"
429 				"phpfpm_max_active_processes %d\n"
430 				"# HELP phpfpm_max_children_reached The number of times, the process limit has been reached, when pm tries to start more children (works only for pm 'dynamic' and 'ondemand').\n"
431 				"# TYPE phpfpm_max_children_reached counter\n"
432 				"phpfpm_max_children_reached %u\n"
433 				"# HELP phpfpm_slow_requests The number of requests that exceeded your 'request_slowlog_timeout' value.\n"
434 				"# TYPE phpfpm_slow_requests counter\n"
435 				"phpfpm_slow_requests %lu\n"
436 				"# HELP phpfpm_memory_peak The memory usage peak since FPM has started.\n"
437 				"# TYPE phpfpm_memory_peak gauge\n"
438 				"phpfpm_memory_peak %zu\n"
439 				"# EOF\n";
440 
441 			has_start_time = 0;
442 			if (!full) {
443 				short_post = "";
444 			} else {
445 				full_separator = "";
446 				full_pre = "";
447 				full_syntax = "";
448 				full_post = "";
449 			}
450 
451 		/* TEXT */
452 		} else {
453 			sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1);
454 			time_format = "%d/%b/%Y:%H:%M:%S %z";
455 
456 			short_syntax =
457 				"pool:                 %s\n"
458 				"process manager:      %s\n"
459 				"start time:           %s\n"
460 				"start since:          %lu\n"
461 				"accepted conn:        %lu\n"
462 				"listen queue:         %d\n"
463 				"max listen queue:     %d\n"
464 				"listen queue len:     %u\n"
465 				"idle processes:       %d\n"
466 				"active processes:     %d\n"
467 				"total processes:      %d\n"
468 				"max active processes: %d\n"
469 				"max children reached: %u\n"
470 				"slow requests:        %lu\n"
471 				"memory peak:          %zu\n";
472 
473 				if (full) {
474 					full_syntax =
475 						"\n"
476 						"************************\n"
477 						"pid:                  %d\n"
478 						"state:                %s\n"
479 						"start time:           %s\n"
480 						"start since:          %lu\n"
481 						"requests:             %lu\n"
482 						"request duration:     %lu\n"
483 						"request method:       %s\n"
484 						"request URI:          %s%s%s\n"
485 						"content length:       %zu\n"
486 						"user:                 %s\n"
487 						"script:               %s\n"
488 						"last request cpu:     %.2f\n"
489 						"last request memory:  %zu\n";
490 				}
491 		}
492 
493 		now_epoch = time(NULL);
494 		if (has_start_time) {
495 			strftime(time_buffer, sizeof(time_buffer) - 1, time_format, localtime(&scoreboard_p->start_epoch));
496 			spprintf(&buffer, 0, short_syntax,
497 					scoreboard_p->pool,
498 					PM2STR(scoreboard_p->pm),
499 					time_buffer,
500 					(unsigned long) (now_epoch - scoreboard_p->start_epoch),
501 					scoreboard_p->requests,
502 					scoreboard_p->lq,
503 					scoreboard_p->lq_max,
504 					scoreboard_p->lq_len,
505 					scoreboard_p->idle,
506 					scoreboard_p->active,
507 					scoreboard_p->idle + scoreboard_p->active,
508 					scoreboard_p->active_max,
509 					scoreboard_p->max_children_reached,
510 					scoreboard_p->slow_rq,
511 					scoreboard_p->memory_peak);
512 		} else {
513 			spprintf(&buffer, 0, short_syntax,
514 					scoreboard_p->pool,
515 					PM2STR(scoreboard_p->pm),
516 					(unsigned long) (now_epoch - scoreboard_p->start_epoch),
517 					scoreboard_p->requests,
518 					scoreboard_p->lq,
519 					scoreboard_p->lq_max,
520 					scoreboard_p->lq_len,
521 					scoreboard_p->idle,
522 					scoreboard_p->active,
523 					scoreboard_p->idle + scoreboard_p->active,
524 					scoreboard_p->active_max,
525 					scoreboard_p->max_children_reached,
526 					scoreboard_p->slow_rq,
527 					scoreboard_p->memory_peak);
528 		}
529 
530 		PUTS(buffer);
531 		efree(buffer);
532 		zend_string_release_ex(_GET_str, 0);
533 
534 		if (short_post) {
535 			PUTS(short_post);
536 		}
537 
538 		/* no need to test the var 'full' */
539 		if (full_syntax) {
540 			unsigned int i;
541 			int first;
542 			zend_string *tmp_query_string;
543 			char *query_string;
544 			struct timeval duration, now;
545 			float cpu;
546 
547 			fpm_clock_get(&now);
548 
549 			if (full_pre) {
550 				PUTS(full_pre);
551 			}
552 
553 			first = 1;
554 			for (i=0; i<scoreboard_p->nprocs; i++) {
555 				if (!scoreboard_p->procs[i].used) {
556 					continue;
557 				}
558 				proc = &scoreboard_p->procs[i];
559 
560 				if (first) {
561 					first = 0;
562 				} else {
563 					if (full_separator) {
564 						PUTS(full_separator);
565 					}
566 				}
567 
568 				query_string = NULL;
569 				tmp_query_string = NULL;
570 				if (proc->query_string[0] != '\0') {
571 					if (encode_html) {
572 						tmp_query_string = php_escape_html_entities_ex(
573 								(const unsigned char *) proc->query_string,
574 								strlen(proc->query_string), 1, ENT_HTML_IGNORE_ERRORS & ENT_COMPAT,
575 								NULL, /* double_encode */ 1, /* quiet */ 0);
576 					} else if (encode_json) {
577 						tmp_query_string = php_json_encode_string(proc->query_string,
578 								strlen(proc->query_string), PHP_JSON_INVALID_UTF8_IGNORE);
579 					} else {
580 						query_string = proc->query_string;
581 					}
582 					if (tmp_query_string) {
583 						query_string = ZSTR_VAL(tmp_query_string);
584 						/* remove quotes around the string */
585 						if (encode_json && ZSTR_LEN(tmp_query_string) >= 2) {
586 							query_string[ZSTR_LEN(tmp_query_string) - 1] = '\0';
587 							++query_string;
588 						}
589 					}
590 				}
591 
592 				/* prevent NaN */
593 				if (proc->cpu_duration.tv_sec == 0 && proc->cpu_duration.tv_usec == 0) {
594 					cpu = 0.;
595 				} else {
596 					cpu = (proc->last_request_cpu.tms_utime + proc->last_request_cpu.tms_stime + proc->last_request_cpu.tms_cutime + proc->last_request_cpu.tms_cstime) / fpm_scoreboard_get_tick() / (proc->cpu_duration.tv_sec + proc->cpu_duration.tv_usec / 1000000.) * 100.;
597 				}
598 
599 				if (proc->request_stage == FPM_REQUEST_ACCEPTING) {
600 					duration = proc->duration;
601 				} else {
602 					timersub(&now, &proc->accepted, &duration);
603 				}
604 				strftime(time_buffer, sizeof(time_buffer) - 1, time_format, localtime(&proc->start_epoch));
605 				spprintf(&buffer, 0, full_syntax,
606 					(int) proc->pid,
607 					fpm_request_get_stage_name(proc->request_stage),
608 					time_buffer,
609 					(unsigned long) (now_epoch - proc->start_epoch),
610 					proc->requests,
611 					duration.tv_sec * 1000000UL + duration.tv_usec,
612 					proc->request_method[0] != '\0' ? proc->request_method : "-",
613 					proc->request_uri[0] != '\0' ? proc->request_uri : "-",
614 					query_string ? "?" : "",
615 					query_string ? query_string : "",
616 					proc->content_length,
617 					proc->auth_user[0] != '\0' ? proc->auth_user : "-",
618 					proc->script_filename[0] != '\0' ? proc->script_filename : "-",
619 					proc->request_stage == FPM_REQUEST_ACCEPTING ? cpu : 0.,
620 					proc->request_stage == FPM_REQUEST_ACCEPTING ? proc->memory : 0);
621 				PUTS(buffer);
622 				efree(buffer);
623 
624 				if (tmp_query_string) {
625 					zend_string_free(tmp_query_string);
626 				}
627 			}
628 
629 			if (full_post) {
630 				PUTS(full_post);
631 			}
632 		}
633 
634 		fpm_scoreboard_free_copy(scoreboard_p);
635 		return 1;
636 	}
637 
638 	return 0;
639 }
640 /* }}} */
641