4 #include "connections.h"
6 #include "connections.h"
11 #include "inet_ntop_cache.h"
13 #include <sys/types.h>
26 buffer
*statistics_url
;
37 double mod_5s_traffic_out
[5];
38 double mod_5s_requests
[5];
41 double rel_traffic_out
;
44 double abs_traffic_out
;
51 plugin_config
**config_storage
;
56 INIT_FUNC(mod_status_init
) {
60 p
= calloc(1, sizeof(*p
));
62 p
->traffic_out
= p
->requests
= 0;
63 p
->rel_traffic_out
= p
->rel_requests
= 0;
64 p
->abs_traffic_out
= p
->abs_requests
= 0;
66 p
->module_list
= buffer_init();
68 for (i
= 0; i
< 5; i
++) {
69 p
->mod_5s_traffic_out
[i
] = p
->mod_5s_requests
[i
] = 0;
75 FREE_FUNC(mod_status_free
) {
80 if (!p
) return HANDLER_GO_ON
;
82 buffer_free(p
->module_list
);
84 if (p
->config_storage
) {
86 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
87 plugin_config
*s
= p
->config_storage
[i
];
88 if (NULL
== s
) continue;
90 buffer_free(s
->status_url
);
91 buffer_free(s
->statistics_url
);
92 buffer_free(s
->config_url
);
96 free(p
->config_storage
);
102 return HANDLER_GO_ON
;
105 SETDEFAULTS_FUNC(mod_status_set_defaults
) {
106 plugin_data
*p
= p_d
;
109 config_values_t cv
[] = {
110 { "status.status-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
111 { "status.config-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
112 { "status.enable-sort", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
},
113 { "status.statistics-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
114 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
117 if (!p
) return HANDLER_ERROR
;
119 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
121 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
122 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
125 s
= calloc(1, sizeof(plugin_config
));
126 s
->config_url
= buffer_init();
127 s
->status_url
= buffer_init();
129 s
->statistics_url
= buffer_init();
131 cv
[0].destination
= s
->status_url
;
132 cv
[1].destination
= s
->config_url
;
133 cv
[2].destination
= &(s
->sort
);
134 cv
[3].destination
= s
->statistics_url
;
136 p
->config_storage
[i
] = s
;
138 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
139 return HANDLER_ERROR
;
143 return HANDLER_GO_ON
;
148 static int mod_status_row_append(buffer
*b
, const char *key
, const char *value
) {
149 buffer_append_string_len(b
, CONST_STR_LEN(" <tr>\n"));
150 buffer_append_string_len(b
, CONST_STR_LEN(" <td><b>"));
151 buffer_append_string(b
, key
);
152 buffer_append_string_len(b
, CONST_STR_LEN("</b></td>\n"));
153 buffer_append_string_len(b
, CONST_STR_LEN(" <td>"));
154 buffer_append_string(b
, value
);
155 buffer_append_string_len(b
, CONST_STR_LEN("</td>\n"));
156 buffer_append_string_len(b
, CONST_STR_LEN(" </tr>\n"));
161 static int mod_status_header_append(buffer
*b
, const char *key
) {
162 buffer_append_string_len(b
, CONST_STR_LEN(" <tr>\n"));
163 buffer_append_string_len(b
, CONST_STR_LEN(" <th colspan=\"2\">"));
164 buffer_append_string(b
, key
);
165 buffer_append_string_len(b
, CONST_STR_LEN("</th>\n"));
166 buffer_append_string_len(b
, CONST_STR_LEN(" </tr>\n"));
171 static int mod_status_header_append_sort(buffer
*b
, void *p_d
, const char* key
) {
172 plugin_data
*p
= p_d
;
175 buffer_append_string_len(b
, CONST_STR_LEN("<th class=\"status\"><a href=\"#\" class=\"sortheader\" onclick=\"resort(this);return false;\">"));
176 buffer_append_string(b
, key
);
177 buffer_append_string_len(b
, CONST_STR_LEN("<span class=\"sortarrow\">:</span></a></th>\n"));
179 buffer_append_string_len(b
, CONST_STR_LEN("<th class=\"status\">"));
180 buffer_append_string(b
, key
);
181 buffer_append_string_len(b
, CONST_STR_LEN("</th>\n"));
187 static int mod_status_get_multiplier(double *avg
, char *multiplier
, int size
) {
190 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'k'; }
191 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'M'; }
192 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'G'; }
193 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'T'; }
194 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'P'; }
195 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'E'; }
196 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'Z'; }
197 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'Y'; }
202 static handler_t
mod_status_handle_server_status_html(server
*srv
, connection
*con
, void *p_d
) {
203 plugin_data
*p
= p_d
;
204 buffer
*b
= buffer_init();
207 char multiplier
= '\0';
211 int days
, hours
, mins
, seconds
;
213 /*(CON_STATE_CLOSE must be last state in enum connection_state_t)*/
214 int cstates
[CON_STATE_CLOSE
+3];
215 memset(cstates
, 0, sizeof(cstates
));
217 buffer_copy_string_len(b
, CONST_STR_LEN(
218 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
219 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
220 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
221 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
223 " <title>Status</title>\n"
225 " <style type=\"text/css\">\n"
226 " table.status { border: black solid thin; }\n"
227 " td { white-space: nowrap; }\n"
228 " td.int { background-color: #f0f0f0; text-align: right }\n"
229 " td.string { background-color: #f0f0f0; text-align: left }\n"
230 " th.status { background-color: black; color: white; font-weight: bold; }\n"
231 " a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n"
232 " span.sortarrow { color: white; text-decoration: none; }\n"
235 if (!buffer_string_is_empty(con
->uri
.query
) && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("refresh="))) {
236 /* Note: Refresh is an historical, but non-standard HTTP header
237 * References (meta http-equiv="refresh" use is deprecated):
238 * https://www.w3.org/TR/WCAG10-HTML-TECHS/#meta-element
239 * https://www.w3.org/TR/WCAG10-CORE-TECHS/#auto-page-refresh
240 * https://www.w3.org/QA/Tips/reback
242 const long refresh
= strtol(con
->uri
.query
->ptr
+sizeof("refresh=")-1, NULL
, 10);
244 buffer_append_string_len(b
, CONST_STR_LEN("<meta http-equiv=\"refresh\" content=\""));
245 buffer_append_int(b
, refresh
< 604800 ? refresh
: 604800);
246 buffer_append_string_len(b
, CONST_STR_LEN("\">\n"));
251 buffer_append_string_len(b
, CONST_STR_LEN(
252 "<script type=\"text/javascript\">\n"
255 "var prev_span = null;\n"
257 "function get_inner_text(el) {\n"
258 " if((typeof el == 'string')||(typeof el == 'undefined'))\n"
260 " if(el.innerText)\n"
261 " return el.innerText;\n"
264 " var cs = el.childNodes;\n"
265 " var l = cs.length;\n"
266 " for (i=0;i<l;i++) {\n"
267 " if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n"
268 " else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n"
274 "function sortfn(a,b) {\n"
275 " var at = get_inner_text(a.cells[sort_column]);\n"
276 " var bt = get_inner_text(b.cells[sort_column]);\n"
277 " if (a.cells[sort_column].className == 'int') {\n"
278 " return parseInt(at)-parseInt(bt);\n"
280 " aa = at.toLowerCase();\n"
281 " bb = bt.toLowerCase();\n"
282 " if (aa==bb) return 0;\n"
283 " else if (aa<bb) return -1;\n"
288 "function resort(lnk) {\n"
289 " var span = lnk.childNodes[1];\n"
290 " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n"
291 " var rows = new Array();\n"
292 " for (j=1;j<table.rows.length;j++)\n"
293 " rows[j-1] = table.rows[j];\n"
294 " sort_column = lnk.parentNode.cellIndex;\n"
295 " rows.sort(sortfn);\n"
297 " if (prev_span != null) prev_span.innerHTML = '';\n"
298 " if (span.getAttribute('sortdir')=='down') {\n"
299 " span.innerHTML = '↑';\n"
300 " span.setAttribute('sortdir','up');\n"
303 " span.innerHTML = '↓';\n"
304 " span.setAttribute('sortdir','down');\n"
306 " for (i=0;i<rows.length;i++)\n"
307 " table.tBodies[0].appendChild(rows[i]);\n"
308 " prev_span = span;\n"
314 buffer_append_string_len(b
, CONST_STR_LEN(
320 /* connection listing */
321 buffer_append_string_len(b
, CONST_STR_LEN("<h1>Server-Status ("));
322 buffer_append_string_buffer(b
, con
->conf
.server_tag
);
323 buffer_append_string_len(b
, CONST_STR_LEN(")</h1>"));
325 buffer_append_string_len(b
, CONST_STR_LEN("<table summary=\"status\" class=\"status\">"));
326 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Hostname</td><td class=\"string\">"));
327 buffer_append_string_buffer(b
, con
->uri
.authority
);
328 buffer_append_string_len(b
, CONST_STR_LEN(" ("));
329 buffer_append_string_buffer(b
, con
->server_name
);
330 buffer_append_string_len(b
, CONST_STR_LEN(")</td></tr>\n"));
331 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Uptime</td><td class=\"string\">"));
333 ts
= srv
->cur_ts
- srv
->startup_ts
;
335 days
= ts
/ (60 * 60 * 24);
336 ts
%= (60 * 60 * 24);
338 hours
= ts
/ (60 * 60);
347 buffer_append_int(b
, days
);
348 buffer_append_string_len(b
, CONST_STR_LEN(" days "));
352 buffer_append_int(b
, hours
);
353 buffer_append_string_len(b
, CONST_STR_LEN(" hours "));
357 buffer_append_int(b
, mins
);
358 buffer_append_string_len(b
, CONST_STR_LEN(" min "));
361 buffer_append_int(b
, seconds
);
362 buffer_append_string_len(b
, CONST_STR_LEN(" s"));
364 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
365 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Started at</td><td class=\"string\">"));
367 ts
= srv
->startup_ts
;
369 strftime(buf
, sizeof(buf
) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts
));
370 buffer_append_string(b
, buf
);
371 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
374 buffer_append_string_len(b
, CONST_STR_LEN("<tr><th colspan=\"2\">absolute (since start)</th></tr>\n"));
376 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
377 avg
= p
->abs_requests
;
379 mod_status_get_multiplier(&avg
, &multiplier
, 1000);
381 buffer_append_int(b
, avg
);
382 buffer_append_string_len(b
, CONST_STR_LEN(" "));
383 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
384 buffer_append_string_len(b
, CONST_STR_LEN("req</td></tr>\n"));
386 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
387 avg
= p
->abs_traffic_out
;
389 mod_status_get_multiplier(&avg
, &multiplier
, 1024);
391 snprintf(buf
, sizeof(buf
), "%.2f", avg
);
392 buffer_append_string(b
, buf
);
393 buffer_append_string_len(b
, CONST_STR_LEN(" "));
394 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
395 buffer_append_string_len(b
, CONST_STR_LEN("byte</td></tr>\n"));
399 buffer_append_string_len(b
, CONST_STR_LEN("<tr><th colspan=\"2\">average (since start)</th></tr>\n"));
401 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
402 avg
= p
->abs_requests
/ (srv
->cur_ts
- srv
->startup_ts
);
404 mod_status_get_multiplier(&avg
, &multiplier
, 1000);
406 buffer_append_int(b
, avg
);
407 buffer_append_string_len(b
, CONST_STR_LEN(" "));
408 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
409 buffer_append_string_len(b
, CONST_STR_LEN("req/s</td></tr>\n"));
411 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
412 avg
= p
->abs_traffic_out
/ (srv
->cur_ts
- srv
->startup_ts
);
414 mod_status_get_multiplier(&avg
, &multiplier
, 1024);
416 snprintf(buf
, sizeof(buf
), "%.2f", avg
);
417 buffer_append_string(b
, buf
);
418 buffer_append_string_len(b
, CONST_STR_LEN(" "));
419 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
420 buffer_append_string_len(b
, CONST_STR_LEN("byte/s</td></tr>\n"));
424 buffer_append_string_len(b
, CONST_STR_LEN("<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n"));
425 for (j
= 0, avg
= 0; j
< 5; j
++) {
426 avg
+= p
->mod_5s_requests
[j
];
431 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
433 mod_status_get_multiplier(&avg
, &multiplier
, 1000);
435 buffer_append_int(b
, avg
);
436 buffer_append_string_len(b
, CONST_STR_LEN(" "));
437 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
439 buffer_append_string_len(b
, CONST_STR_LEN("req/s</td></tr>\n"));
441 for (j
= 0, avg
= 0; j
< 5; j
++) {
442 avg
+= p
->mod_5s_traffic_out
[j
];
447 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
449 mod_status_get_multiplier(&avg
, &multiplier
, 1024);
451 snprintf(buf
, sizeof(buf
), "%.2f", avg
);
452 buffer_append_string(b
, buf
);
453 buffer_append_string_len(b
, CONST_STR_LEN(" "));
454 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
455 buffer_append_string_len(b
, CONST_STR_LEN("byte/s</td></tr>\n"));
457 buffer_append_string_len(b
, CONST_STR_LEN("</table>\n"));
459 buffer_append_string_len(b
, CONST_STR_LEN("<hr />\n<pre>\n"));
461 buffer_append_string_len(b
, CONST_STR_LEN("<b>"));
462 buffer_append_int(b
, srv
->conns
->used
);
463 buffer_append_string_len(b
, CONST_STR_LEN(" connections</b>\n"));
465 for (j
= 0; j
< srv
->conns
->used
; j
++) {
466 connection
*c
= srv
->conns
->ptr
[j
];
469 if (CON_STATE_READ
== c
->state
&& !buffer_string_is_empty(c
->request
.orig_uri
)) {
471 ++cstates
[CON_STATE_CLOSE
+2];
473 state
= connection_get_short_state(c
->state
);
474 ++cstates
[(c
->state
<= CON_STATE_CLOSE
? c
->state
: CON_STATE_CLOSE
+1)];
477 buffer_append_string_len(b
, state
, 1);
479 if (((j
+ 1) % 50) == 0) {
480 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
483 buffer_append_string_len(b
, CONST_STR_LEN("\n\n<table>\n"));
484 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td style=\"text-align:right\">"));
485 buffer_append_int(b
, cstates
[CON_STATE_CLOSE
+2]);
486 buffer_append_string_len(b
, CONST_STR_LEN("<td> k = keep-alive</td></tr>\n"));
487 for (j
= 0; j
< CON_STATE_CLOSE
+2; ++j
) {
488 /*(skip "unknown" state if there are none; there should not be any unknown)*/
489 if (0 == cstates
[j
] && j
== CON_STATE_CLOSE
+1) continue;
490 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td style=\"text-align:right\">"));
491 buffer_append_int(b
, cstates
[j
]);
492 buffer_append_string_len(b
, CONST_STR_LEN("</td><td> "));
493 buffer_append_string_len(b
, connection_get_short_state(j
), 1);
494 buffer_append_string_len(b
, CONST_STR_LEN(" = "));
495 buffer_append_string(b
, connection_get_state(j
));
496 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
498 buffer_append_string_len(b
, CONST_STR_LEN("</table>"));
500 buffer_append_string_len(b
, CONST_STR_LEN("\n</pre><hr />\n<h2>Connections</h2>\n"));
502 buffer_append_string_len(b
, CONST_STR_LEN("<table summary=\"status\" class=\"status\">\n"));
503 buffer_append_string_len(b
, CONST_STR_LEN("<tr>"));
504 mod_status_header_append_sort(b
, p_d
, "Client IP");
505 mod_status_header_append_sort(b
, p_d
, "Read");
506 mod_status_header_append_sort(b
, p_d
, "Written");
507 mod_status_header_append_sort(b
, p_d
, "State");
508 mod_status_header_append_sort(b
, p_d
, "Time");
509 mod_status_header_append_sort(b
, p_d
, "Host");
510 mod_status_header_append_sort(b
, p_d
, "URI");
511 mod_status_header_append_sort(b
, p_d
, "File");
512 buffer_append_string_len(b
, CONST_STR_LEN("</tr>\n"));
514 for (j
= 0; j
< srv
->conns
->used
; j
++) {
515 connection
*c
= srv
->conns
->ptr
[j
];
517 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td class=\"string\">"));
519 buffer_append_string(b
, inet_ntop_cache_get_ip(srv
, &(c
->dst_addr
)));
521 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"int\">"));
523 if (c
->request
.content_length
) {
524 buffer_append_int(b
, c
->request_content_queue
->bytes_in
);
525 buffer_append_string_len(b
, CONST_STR_LEN("/"));
526 buffer_append_int(b
, c
->request
.content_length
);
528 buffer_append_string_len(b
, CONST_STR_LEN("0/0"));
531 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"int\">"));
533 buffer_append_int(b
, c
->write_queue
->bytes_out
);
534 buffer_append_string_len(b
, CONST_STR_LEN("/"));
535 buffer_append_int(b
, c
->write_queue
->bytes_out
+ chunkqueue_length(c
->write_queue
));
537 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
539 if (CON_STATE_READ
== c
->state
&& !buffer_string_is_empty(c
->request
.orig_uri
)) {
540 buffer_append_string_len(b
, CONST_STR_LEN("keep-alive"));
542 buffer_append_string(b
, connection_get_state(c
->state
));
545 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"int\">"));
547 buffer_append_int(b
, srv
->cur_ts
- c
->request_start
);
549 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
551 if (buffer_string_is_empty(c
->server_name
)) {
552 buffer_append_string_buffer(b
, c
->uri
.authority
);
555 buffer_append_string_buffer(b
, c
->server_name
);
558 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
560 if (!buffer_string_is_empty(c
->uri
.path
)) {
561 buffer_append_string_encoded(b
, CONST_BUF_LEN(c
->uri
.path
), ENCODING_HTML
);
564 if (!buffer_string_is_empty(c
->uri
.query
)) {
565 buffer_append_string_len(b
, CONST_STR_LEN("?"));
566 buffer_append_string_encoded(b
, CONST_BUF_LEN(c
->uri
.query
), ENCODING_HTML
);
569 if (!buffer_string_is_empty(c
->request
.orig_uri
)) {
570 buffer_append_string_len(b
, CONST_STR_LEN(" ("));
571 buffer_append_string_encoded(b
, CONST_BUF_LEN(c
->request
.orig_uri
), ENCODING_HTML
);
572 buffer_append_string_len(b
, CONST_STR_LEN(")"));
574 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
576 buffer_append_string_buffer(b
, c
->physical
.path
);
578 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
582 buffer_append_string_len(b
, CONST_STR_LEN(
586 buffer_append_string_len(b
, CONST_STR_LEN(
591 chunkqueue_append_buffer(con
->write_queue
, b
);
594 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
600 static handler_t
mod_status_handle_server_status_text(server
*srv
, connection
*con
, void *p_d
) {
601 plugin_data
*p
= p_d
;
602 buffer
*b
= buffer_init();
609 /* output total number of requests */
610 buffer_append_string_len(b
, CONST_STR_LEN("Total Accesses: "));
611 avg
= p
->abs_requests
;
612 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
613 buffer_append_string(b
, buf
);
614 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
616 /* output total traffic out in kbytes */
617 buffer_append_string_len(b
, CONST_STR_LEN("Total kBytes: "));
618 avg
= p
->abs_traffic_out
/ 1024;
619 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
620 buffer_append_string(b
, buf
);
621 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
624 buffer_append_string_len(b
, CONST_STR_LEN("Uptime: "));
625 ts
= srv
->cur_ts
- srv
->startup_ts
;
626 buffer_append_int(b
, ts
);
627 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
629 /* output busy servers */
630 buffer_append_string_len(b
, CONST_STR_LEN("BusyServers: "));
631 buffer_append_int(b
, srv
->conns
->used
);
632 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
634 buffer_append_string_len(b
, CONST_STR_LEN("IdleServers: "));
635 buffer_append_int(b
, srv
->conns
->size
- srv
->conns
->used
);
636 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
638 /* output scoreboard */
639 buffer_append_string_len(b
, CONST_STR_LEN("Scoreboard: "));
640 for (k
= 0; k
< srv
->conns
->used
; k
++) {
641 connection
*c
= srv
->conns
->ptr
[k
];
643 (CON_STATE_READ
== c
->state
&& !buffer_string_is_empty(c
->request
.orig_uri
))
645 : connection_get_short_state(c
->state
);
646 buffer_append_string_len(b
, state
, 1);
648 for (l
= 0; l
< srv
->conns
->size
- srv
->conns
->used
; l
++) {
649 buffer_append_string_len(b
, CONST_STR_LEN("_"));
651 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
653 chunkqueue_append_buffer(con
->write_queue
, b
);
656 /* set text/plain output */
657 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
663 static handler_t
mod_status_handle_server_status_json(server
*srv
, connection
*con
, void *p_d
) {
664 plugin_data
*p
= p_d
;
665 buffer
*b
= buffer_init();
670 unsigned int jsonp
= 0;
672 if (buffer_string_length(con
->uri
.query
) >= sizeof("jsonp=")-1
673 && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("jsonp="))) {
674 /* not a full parse of query string for multiple parameters,
675 * not URL-decoding param and not XML-encoding (XSS protection),
676 * so simply ensure that json function name isalnum() or '_' */
677 const char *f
= con
->uri
.query
->ptr
+ sizeof("jsonp=")-1;
679 while (light_isalnum(f
[len
]) || f
[len
] == '_') ++len
;
680 if (0 != len
&& light_isalpha(f
[0]) && f
[len
] == '\0') {
681 buffer_append_string_len(b
, f
, len
);
682 buffer_append_string_len(b
, CONST_STR_LEN("("));
687 /* output total number of requests */
688 buffer_append_string_len(b
, CONST_STR_LEN("{\n\t\"RequestsTotal\": "));
689 avg
= p
->abs_requests
;
690 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
691 buffer_append_string(b
, buf
);
692 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
694 /* output total traffic out in kbytes */
695 buffer_append_string_len(b
, CONST_STR_LEN("\t\"TrafficTotal\": "));
696 avg
= p
->abs_traffic_out
/ 1024;
697 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
698 buffer_append_string(b
, buf
);
699 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
702 buffer_append_string_len(b
, CONST_STR_LEN("\t\"Uptime\": "));
703 ts
= srv
->cur_ts
- srv
->startup_ts
;
704 buffer_append_int(b
, ts
);
705 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
707 /* output busy servers */
708 buffer_append_string_len(b
, CONST_STR_LEN("\t\"BusyServers\": "));
709 buffer_append_int(b
, srv
->conns
->used
);
710 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
712 buffer_append_string_len(b
, CONST_STR_LEN("\t\"IdleServers\": "));
713 buffer_append_int(b
, srv
->conns
->size
- srv
->conns
->used
);
714 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
716 for (j
= 0, avg
= 0; j
< 5; j
++) {
717 avg
+= p
->mod_5s_requests
[j
];
722 buffer_append_string_len(b
, CONST_STR_LEN("\t\"RequestAverage5s\":"));
723 buffer_append_int(b
, avg
);
724 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
726 for (j
= 0, avg
= 0; j
< 5; j
++) {
727 avg
+= p
->mod_5s_traffic_out
[j
];
732 buffer_append_string_len(b
, CONST_STR_LEN("\t\"TrafficAverage5s\":"));
733 buffer_append_int(b
, avg
/ 1024); /* kbps */
734 buffer_append_string_len(b
, CONST_STR_LEN("\n}"));
736 if (jsonp
) buffer_append_string_len(b
, CONST_STR_LEN(");"));
738 chunkqueue_append_buffer(con
->write_queue
, b
);
741 /* set text/plain output */
742 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/javascript"));
748 static handler_t
mod_status_handle_server_statistics(server
*srv
, connection
*con
, void *p_d
) {
751 array
*st
= srv
->status
;
755 /* we have nothing to send */
756 con
->http_status
= 204;
757 con
->file_finished
= 1;
759 return HANDLER_FINISHED
;
763 for (i
= 0; i
< st
->used
; i
++) {
764 size_t ndx
= st
->sorted
[i
];
766 buffer_append_string_buffer(b
, st
->data
[ndx
]->key
);
767 buffer_append_string_len(b
, CONST_STR_LEN(": "));
768 buffer_append_int(b
, ((data_integer
*)(st
->data
[ndx
]))->value
);
769 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
772 chunkqueue_append_buffer(con
->write_queue
, b
);
775 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
777 con
->http_status
= 200;
778 con
->file_finished
= 1;
780 return HANDLER_FINISHED
;
784 static handler_t
mod_status_handle_server_status(server
*srv
, connection
*con
, void *p_d
) {
786 if (buffer_is_equal_string(con
->uri
.query
, CONST_STR_LEN("auto"))) {
787 mod_status_handle_server_status_text(srv
, con
, p_d
);
788 } else if (buffer_string_length(con
->uri
.query
) >= sizeof("json")-1
789 && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("json"))) {
790 mod_status_handle_server_status_json(srv
, con
, p_d
);
792 mod_status_handle_server_status_html(srv
, con
, p_d
);
795 con
->http_status
= 200;
796 con
->file_finished
= 1;
798 return HANDLER_FINISHED
;
802 static handler_t
mod_status_handle_server_config(server
*srv
, connection
*con
, void *p_d
) {
803 plugin_data
*p
= p_d
;
804 buffer
*b
= buffer_init();
805 buffer
*m
= p
->module_list
;
808 struct ev_map
{ fdevent_handler_t et
; const char *name
; } event_handlers
[] =
810 /* - epoll is most reliable
811 * - select works everywhere
813 #ifdef USE_LINUX_EPOLL
814 { FDEVENT_HANDLER_LINUX_SYSEPOLL
, "linux-sysepoll" },
817 { FDEVENT_HANDLER_POLL
, "poll" },
820 { FDEVENT_HANDLER_SELECT
, "select" },
823 { FDEVENT_HANDLER_LIBEV
, "libev" },
825 #ifdef USE_SOLARIS_DEVPOLL
826 { FDEVENT_HANDLER_SOLARIS_DEVPOLL
,"solaris-devpoll" },
828 #ifdef USE_SOLARIS_PORT
829 { FDEVENT_HANDLER_SOLARIS_PORT
, "solaris-eventports" },
831 #ifdef USE_FREEBSD_KQUEUE
832 { FDEVENT_HANDLER_FREEBSD_KQUEUE
, "freebsd-kqueue" },
834 { FDEVENT_HANDLER_UNSET
, NULL
}
837 buffer_copy_string_len(b
, CONST_STR_LEN(
838 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
839 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
840 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
841 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
843 " <title>Status</title>\n"
847 buffer_append_string_buffer(b
, con
->conf
.server_tag
);
848 buffer_append_string_len(b
, CONST_STR_LEN(
850 " <table summary=\"status\" border=\"1\">\n"));
852 mod_status_header_append(b
, "Server-Features");
854 mod_status_row_append(b
, "RegEx Conditionals", "enabled");
856 mod_status_row_append(b
, "RegEx Conditionals", "disabled - pcre missing");
858 mod_status_header_append(b
, "Network Engine");
860 for (i
= 0; event_handlers
[i
].name
; i
++) {
861 if (event_handlers
[i
].et
== srv
->event_handler
) {
862 mod_status_row_append(b
, "fd-Event-Handler", event_handlers
[i
].name
);
867 mod_status_header_append(b
, "Config-File-Settings");
869 for (i
= 0; i
< srv
->plugins
.used
; i
++) {
870 plugin
**ps
= srv
->plugins
.ptr
;
875 buffer_copy_buffer(m
, pl
->name
);
877 buffer_append_string_len(m
, CONST_STR_LEN("<br />"));
878 buffer_append_string_buffer(m
, pl
->name
);
882 mod_status_row_append(b
, "Loaded Modules", m
->ptr
);
884 buffer_append_string_len(b
, CONST_STR_LEN(" </table>\n"));
886 buffer_append_string_len(b
, CONST_STR_LEN(
891 chunkqueue_append_buffer(con
->write_queue
, b
);
894 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
896 con
->http_status
= 200;
897 con
->file_finished
= 1;
899 return HANDLER_FINISHED
;
904 static int mod_status_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
906 plugin_config
*s
= p
->config_storage
[0];
911 PATCH(statistics_url
);
913 /* skip the first, the global context */
914 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
915 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
916 s
= p
->config_storage
[i
];
918 /* condition didn't match */
919 if (!config_check_cond(srv
, con
, dc
)) continue;
922 for (j
= 0; j
< dc
->value
->used
; j
++) {
923 data_unset
*du
= dc
->value
->data
[j
];
925 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.status-url"))) {
927 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.config-url"))) {
929 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.enable-sort"))) {
931 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.statistics-url"))) {
932 PATCH(statistics_url
);
940 static handler_t
mod_status_handler(server
*srv
, connection
*con
, void *p_d
) {
941 plugin_data
*p
= p_d
;
943 if (con
->mode
!= DIRECT
) return HANDLER_GO_ON
;
945 mod_status_patch_connection(srv
, con
, p
);
947 if (!buffer_string_is_empty(p
->conf
.status_url
) &&
948 buffer_is_equal(p
->conf
.status_url
, con
->uri
.path
)) {
949 return mod_status_handle_server_status(srv
, con
, p_d
);
950 } else if (!buffer_string_is_empty(p
->conf
.config_url
) &&
951 buffer_is_equal(p
->conf
.config_url
, con
->uri
.path
)) {
952 return mod_status_handle_server_config(srv
, con
, p_d
);
953 } else if (!buffer_string_is_empty(p
->conf
.statistics_url
) &&
954 buffer_is_equal(p
->conf
.statistics_url
, con
->uri
.path
)) {
955 return mod_status_handle_server_statistics(srv
, con
, p_d
);
958 return HANDLER_GO_ON
;
961 TRIGGER_FUNC(mod_status_trigger
) {
962 plugin_data
*p
= p_d
;
965 /* check all connections */
966 for (i
= 0; i
< srv
->conns
->used
; i
++) {
967 connection
*c
= srv
->conns
->ptr
[i
];
969 p
->bytes_written
+= c
->bytes_written_cur_second
;
972 /* a sliding average */
973 p
->mod_5s_traffic_out
[p
->mod_5s_ndx
] = p
->bytes_written
;
974 p
->mod_5s_requests
[p
->mod_5s_ndx
] = p
->requests
;
976 p
->mod_5s_ndx
= (p
->mod_5s_ndx
+1) % 5;
978 p
->abs_traffic_out
+= p
->bytes_written
;
979 p
->rel_traffic_out
+= p
->bytes_written
;
981 p
->bytes_written
= 0;
983 /* reset storage - second */
987 return HANDLER_GO_ON
;
990 REQUESTDONE_FUNC(mod_status_account
) {
991 plugin_data
*p
= p_d
;
999 p
->bytes_written
+= con
->bytes_written_cur_second
;
1001 return HANDLER_GO_ON
;
1004 int mod_status_plugin_init(plugin
*p
);
1005 int mod_status_plugin_init(plugin
*p
) {
1006 p
->version
= LIGHTTPD_VERSION_ID
;
1007 p
->name
= buffer_init_string("status");
1009 p
->init
= mod_status_init
;
1010 p
->cleanup
= mod_status_free
;
1011 p
->set_defaults
= mod_status_set_defaults
;
1013 p
->handle_uri_clean
= mod_status_handler
;
1014 p
->handle_trigger
= mod_status_trigger
;
1015 p
->handle_request_done
= mod_status_account
;