4 #include "connections.h"
6 #include "http_header.h"
11 #include <sys/types.h>
23 buffer
*statistics_url
;
34 double mod_5s_traffic_out
[5];
35 double mod_5s_requests
[5];
38 double rel_traffic_out
;
41 double abs_traffic_out
;
48 plugin_config
**config_storage
;
53 INIT_FUNC(mod_status_init
) {
57 p
= calloc(1, sizeof(*p
));
59 p
->traffic_out
= p
->requests
= 0;
60 p
->rel_traffic_out
= p
->rel_requests
= 0;
61 p
->abs_traffic_out
= p
->abs_requests
= 0;
63 p
->module_list
= buffer_init();
65 for (i
= 0; i
< 5; i
++) {
66 p
->mod_5s_traffic_out
[i
] = p
->mod_5s_requests
[i
] = 0;
72 FREE_FUNC(mod_status_free
) {
77 if (!p
) return HANDLER_GO_ON
;
79 buffer_free(p
->module_list
);
81 if (p
->config_storage
) {
83 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
84 plugin_config
*s
= p
->config_storage
[i
];
85 if (NULL
== s
) continue;
87 buffer_free(s
->status_url
);
88 buffer_free(s
->statistics_url
);
89 buffer_free(s
->config_url
);
93 free(p
->config_storage
);
102 SETDEFAULTS_FUNC(mod_status_set_defaults
) {
103 plugin_data
*p
= p_d
;
106 config_values_t cv
[] = {
107 { "status.status-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
108 { "status.config-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
109 { "status.enable-sort", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
},
110 { "status.statistics-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
111 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
114 if (!p
) return HANDLER_ERROR
;
116 p
->config_storage
= calloc(srv
->config_context
->used
, sizeof(plugin_config
*));
118 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
119 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
122 s
= calloc(1, sizeof(plugin_config
));
123 s
->config_url
= buffer_init();
124 s
->status_url
= buffer_init();
126 s
->statistics_url
= buffer_init();
128 cv
[0].destination
= s
->status_url
;
129 cv
[1].destination
= s
->config_url
;
130 cv
[2].destination
= &(s
->sort
);
131 cv
[3].destination
= s
->statistics_url
;
133 p
->config_storage
[i
] = s
;
135 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
136 return HANDLER_ERROR
;
140 return HANDLER_GO_ON
;
145 static int mod_status_row_append(buffer
*b
, const char *key
, const char *value
) {
146 buffer_append_string_len(b
, CONST_STR_LEN(" <tr>\n"));
147 buffer_append_string_len(b
, CONST_STR_LEN(" <td><b>"));
148 buffer_append_string(b
, key
);
149 buffer_append_string_len(b
, CONST_STR_LEN("</b></td>\n"));
150 buffer_append_string_len(b
, CONST_STR_LEN(" <td>"));
151 buffer_append_string(b
, value
);
152 buffer_append_string_len(b
, CONST_STR_LEN("</td>\n"));
153 buffer_append_string_len(b
, CONST_STR_LEN(" </tr>\n"));
158 static int mod_status_header_append(buffer
*b
, const char *key
) {
159 buffer_append_string_len(b
, CONST_STR_LEN(" <tr>\n"));
160 buffer_append_string_len(b
, CONST_STR_LEN(" <th colspan=\"2\">"));
161 buffer_append_string(b
, key
);
162 buffer_append_string_len(b
, CONST_STR_LEN("</th>\n"));
163 buffer_append_string_len(b
, CONST_STR_LEN(" </tr>\n"));
168 static int mod_status_header_append_sort(buffer
*b
, void *p_d
, const char* key
) {
169 plugin_data
*p
= p_d
;
172 buffer_append_string_len(b
, CONST_STR_LEN("<th class=\"status\"><a href=\"#\" class=\"sortheader\" onclick=\"resort(this);return false;\">"));
173 buffer_append_string(b
, key
);
174 buffer_append_string_len(b
, CONST_STR_LEN("<span class=\"sortarrow\">:</span></a></th>\n"));
176 buffer_append_string_len(b
, CONST_STR_LEN("<th class=\"status\">"));
177 buffer_append_string(b
, key
);
178 buffer_append_string_len(b
, CONST_STR_LEN("</th>\n"));
184 static int mod_status_get_multiplier(double *avg
, char *multiplier
, int size
) {
187 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'k'; }
188 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'M'; }
189 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'G'; }
190 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'T'; }
191 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'P'; }
192 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'E'; }
193 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'Z'; }
194 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'Y'; }
199 static handler_t
mod_status_handle_server_status_html(server
*srv
, connection
*con
, void *p_d
) {
200 plugin_data
*p
= p_d
;
201 buffer
*b
= chunkqueue_append_buffer_open(con
->write_queue
);
204 char multiplier
= '\0';
208 int days
, hours
, mins
, seconds
;
210 /*(CON_STATE_CLOSE must be last state in enum connection_state_t)*/
211 int cstates
[CON_STATE_CLOSE
+3];
212 memset(cstates
, 0, sizeof(cstates
));
214 buffer_copy_string_len(b
, CONST_STR_LEN(
215 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
216 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
217 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
218 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
220 " <title>Status</title>\n"
222 " <style type=\"text/css\">\n"
223 " table.status { border: black solid thin; }\n"
224 " td { white-space: nowrap; }\n"
225 " td.int { background-color: #f0f0f0; text-align: right }\n"
226 " td.string { background-color: #f0f0f0; text-align: left }\n"
227 " th.status { background-color: black; color: white; font-weight: bold; }\n"
228 " a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n"
229 " span.sortarrow { color: white; text-decoration: none; }\n"
232 if (!buffer_string_is_empty(con
->uri
.query
) && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("refresh="))) {
233 /* Note: Refresh is an historical, but non-standard HTTP header
234 * References (meta http-equiv="refresh" use is deprecated):
235 * https://www.w3.org/TR/WCAG10-HTML-TECHS/#meta-element
236 * https://www.w3.org/TR/WCAG10-CORE-TECHS/#auto-page-refresh
237 * https://www.w3.org/QA/Tips/reback
239 const long refresh
= strtol(con
->uri
.query
->ptr
+sizeof("refresh=")-1, NULL
, 10);
241 buffer_append_string_len(b
, CONST_STR_LEN("<meta http-equiv=\"refresh\" content=\""));
242 buffer_append_int(b
, refresh
< 604800 ? refresh
: 604800);
243 buffer_append_string_len(b
, CONST_STR_LEN("\">\n"));
248 buffer_append_string_len(b
, CONST_STR_LEN(
249 "<script type=\"text/javascript\">\n"
252 "var prev_span = null;\n"
254 "function get_inner_text(el) {\n"
255 " if((typeof el == 'string')||(typeof el == 'undefined'))\n"
257 " if(el.innerText)\n"
258 " return el.innerText;\n"
261 " var cs = el.childNodes;\n"
262 " var l = cs.length;\n"
263 " for (i=0;i<l;i++) {\n"
264 " if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n"
265 " else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n"
271 "function sortfn(a,b) {\n"
272 " var at = get_inner_text(a.cells[sort_column]);\n"
273 " var bt = get_inner_text(b.cells[sort_column]);\n"
274 " if (a.cells[sort_column].className == 'int') {\n"
275 " return parseInt(at)-parseInt(bt);\n"
277 " aa = at.toLowerCase();\n"
278 " bb = bt.toLowerCase();\n"
279 " if (aa==bb) return 0;\n"
280 " else if (aa<bb) return -1;\n"
285 "function resort(lnk) {\n"
286 " var span = lnk.childNodes[1];\n"
287 " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n"
288 " var rows = new Array();\n"
289 " for (j=1;j<table.rows.length;j++)\n"
290 " rows[j-1] = table.rows[j];\n"
291 " sort_column = lnk.parentNode.cellIndex;\n"
292 " rows.sort(sortfn);\n"
294 " if (prev_span != null) prev_span.innerHTML = '';\n"
295 " if (span.getAttribute('sortdir')=='down') {\n"
296 " span.innerHTML = '↑';\n"
297 " span.setAttribute('sortdir','up');\n"
300 " span.innerHTML = '↓';\n"
301 " span.setAttribute('sortdir','down');\n"
303 " for (i=0;i<rows.length;i++)\n"
304 " table.tBodies[0].appendChild(rows[i]);\n"
305 " prev_span = span;\n"
311 buffer_append_string_len(b
, CONST_STR_LEN(
317 /* connection listing */
318 buffer_append_string_len(b
, CONST_STR_LEN("<h1>Server-Status ("));
319 buffer_append_string_buffer(b
, con
->conf
.server_tag
);
320 buffer_append_string_len(b
, CONST_STR_LEN(")</h1>"));
322 buffer_append_string_len(b
, CONST_STR_LEN("<table summary=\"status\" class=\"status\">"));
323 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Hostname</td><td class=\"string\">"));
324 buffer_append_string_buffer(b
, con
->uri
.authority
);
325 buffer_append_string_len(b
, CONST_STR_LEN(" ("));
326 buffer_append_string_buffer(b
, con
->server_name
);
327 buffer_append_string_len(b
, CONST_STR_LEN(")</td></tr>\n"));
328 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Uptime</td><td class=\"string\">"));
330 ts
= srv
->cur_ts
- srv
->startup_ts
;
332 days
= ts
/ (60 * 60 * 24);
333 ts
%= (60 * 60 * 24);
335 hours
= ts
/ (60 * 60);
344 buffer_append_int(b
, days
);
345 buffer_append_string_len(b
, CONST_STR_LEN(" days "));
349 buffer_append_int(b
, hours
);
350 buffer_append_string_len(b
, CONST_STR_LEN(" hours "));
354 buffer_append_int(b
, mins
);
355 buffer_append_string_len(b
, CONST_STR_LEN(" min "));
358 buffer_append_int(b
, seconds
);
359 buffer_append_string_len(b
, CONST_STR_LEN(" s"));
361 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
362 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Started at</td><td class=\"string\">"));
364 ts
= srv
->startup_ts
;
366 strftime(buf
, sizeof(buf
) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts
));
367 buffer_append_string(b
, buf
);
368 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
371 buffer_append_string_len(b
, CONST_STR_LEN("<tr><th colspan=\"2\">absolute (since start)</th></tr>\n"));
373 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
374 avg
= p
->abs_requests
;
376 mod_status_get_multiplier(&avg
, &multiplier
, 1000);
378 buffer_append_int(b
, avg
);
379 buffer_append_string_len(b
, CONST_STR_LEN(" "));
380 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
381 buffer_append_string_len(b
, CONST_STR_LEN("req</td></tr>\n"));
383 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
384 avg
= p
->abs_traffic_out
;
386 mod_status_get_multiplier(&avg
, &multiplier
, 1024);
388 snprintf(buf
, sizeof(buf
), "%.2f", avg
);
389 buffer_append_string(b
, buf
);
390 buffer_append_string_len(b
, CONST_STR_LEN(" "));
391 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
392 buffer_append_string_len(b
, CONST_STR_LEN("byte</td></tr>\n"));
396 buffer_append_string_len(b
, CONST_STR_LEN("<tr><th colspan=\"2\">average (since start)</th></tr>\n"));
398 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
399 avg
= p
->abs_requests
/ (srv
->cur_ts
- srv
->startup_ts
);
401 mod_status_get_multiplier(&avg
, &multiplier
, 1000);
403 buffer_append_int(b
, avg
);
404 buffer_append_string_len(b
, CONST_STR_LEN(" "));
405 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
406 buffer_append_string_len(b
, CONST_STR_LEN("req/s</td></tr>\n"));
408 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
409 avg
= p
->abs_traffic_out
/ (srv
->cur_ts
- srv
->startup_ts
);
411 mod_status_get_multiplier(&avg
, &multiplier
, 1024);
413 snprintf(buf
, sizeof(buf
), "%.2f", avg
);
414 buffer_append_string(b
, buf
);
415 buffer_append_string_len(b
, CONST_STR_LEN(" "));
416 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
417 buffer_append_string_len(b
, CONST_STR_LEN("byte/s</td></tr>\n"));
421 buffer_append_string_len(b
, CONST_STR_LEN("<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n"));
422 for (j
= 0, avg
= 0; j
< 5; j
++) {
423 avg
+= p
->mod_5s_requests
[j
];
428 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
430 mod_status_get_multiplier(&avg
, &multiplier
, 1000);
432 buffer_append_int(b
, avg
);
433 buffer_append_string_len(b
, CONST_STR_LEN(" "));
434 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
436 buffer_append_string_len(b
, CONST_STR_LEN("req/s</td></tr>\n"));
438 for (j
= 0, avg
= 0; j
< 5; j
++) {
439 avg
+= p
->mod_5s_traffic_out
[j
];
444 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
446 mod_status_get_multiplier(&avg
, &multiplier
, 1024);
448 snprintf(buf
, sizeof(buf
), "%.2f", avg
);
449 buffer_append_string(b
, buf
);
450 buffer_append_string_len(b
, CONST_STR_LEN(" "));
451 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
452 buffer_append_string_len(b
, CONST_STR_LEN("byte/s</td></tr>\n"));
454 buffer_append_string_len(b
, CONST_STR_LEN("</table>\n"));
456 buffer_append_string_len(b
, CONST_STR_LEN("<hr />\n<pre>\n"));
458 buffer_append_string_len(b
, CONST_STR_LEN("<b>"));
459 buffer_append_int(b
, srv
->conns
->used
);
460 buffer_append_string_len(b
, CONST_STR_LEN(" connections</b>\n"));
462 for (j
= 0; j
< srv
->conns
->used
; j
++) {
463 connection
*c
= srv
->conns
->ptr
[j
];
466 if (CON_STATE_READ
== c
->state
&& !buffer_string_is_empty(c
->request
.orig_uri
)) {
468 ++cstates
[CON_STATE_CLOSE
+2];
470 state
= connection_get_short_state(c
->state
);
471 ++cstates
[(c
->state
<= CON_STATE_CLOSE
? c
->state
: CON_STATE_CLOSE
+1)];
474 buffer_append_string_len(b
, state
, 1);
476 if (((j
+ 1) % 50) == 0) {
477 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
480 buffer_append_string_len(b
, CONST_STR_LEN("\n\n<table>\n"));
481 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td style=\"text-align:right\">"));
482 buffer_append_int(b
, cstates
[CON_STATE_CLOSE
+2]);
483 buffer_append_string_len(b
, CONST_STR_LEN("<td> k = keep-alive</td></tr>\n"));
484 for (j
= 0; j
< CON_STATE_CLOSE
+2; ++j
) {
485 /*(skip "unknown" state if there are none; there should not be any unknown)*/
486 if (0 == cstates
[j
] && j
== CON_STATE_CLOSE
+1) continue;
487 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td style=\"text-align:right\">"));
488 buffer_append_int(b
, cstates
[j
]);
489 buffer_append_string_len(b
, CONST_STR_LEN("</td><td> "));
490 buffer_append_string_len(b
, connection_get_short_state(j
), 1);
491 buffer_append_string_len(b
, CONST_STR_LEN(" = "));
492 buffer_append_string(b
, connection_get_state(j
));
493 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
495 buffer_append_string_len(b
, CONST_STR_LEN("</table>"));
497 buffer_append_string_len(b
, CONST_STR_LEN("\n</pre><hr />\n<h2>Connections</h2>\n"));
499 buffer_append_string_len(b
, CONST_STR_LEN("<table summary=\"status\" class=\"status\">\n"));
500 buffer_append_string_len(b
, CONST_STR_LEN("<tr>"));
501 mod_status_header_append_sort(b
, p_d
, "Client IP");
502 mod_status_header_append_sort(b
, p_d
, "Read");
503 mod_status_header_append_sort(b
, p_d
, "Written");
504 mod_status_header_append_sort(b
, p_d
, "State");
505 mod_status_header_append_sort(b
, p_d
, "Time");
506 mod_status_header_append_sort(b
, p_d
, "Host");
507 mod_status_header_append_sort(b
, p_d
, "URI");
508 mod_status_header_append_sort(b
, p_d
, "File");
509 buffer_append_string_len(b
, CONST_STR_LEN("</tr>\n"));
511 for (j
= 0; j
< srv
->conns
->used
; j
++) {
512 connection
*c
= srv
->conns
->ptr
[j
];
514 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td class=\"string\">"));
516 buffer_append_string_buffer(b
, c
->dst_addr_buf
);
518 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"int\">"));
520 if (c
->request
.content_length
) {
521 buffer_append_int(b
, c
->request_content_queue
->bytes_in
);
522 buffer_append_string_len(b
, CONST_STR_LEN("/"));
523 buffer_append_int(b
, c
->request
.content_length
);
525 buffer_append_string_len(b
, CONST_STR_LEN("0/0"));
528 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"int\">"));
530 buffer_append_int(b
, c
->write_queue
->bytes_out
);
531 buffer_append_string_len(b
, CONST_STR_LEN("/"));
532 buffer_append_int(b
, c
->write_queue
->bytes_out
+ chunkqueue_length(c
->write_queue
));
534 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
536 if (CON_STATE_READ
== c
->state
&& !buffer_string_is_empty(c
->request
.orig_uri
)) {
537 buffer_append_string_len(b
, CONST_STR_LEN("keep-alive"));
539 buffer_append_string(b
, connection_get_state(c
->state
));
542 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"int\">"));
544 buffer_append_int(b
, srv
->cur_ts
- c
->request_start
);
546 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
548 if (buffer_string_is_empty(c
->server_name
)) {
549 buffer_append_string_buffer(b
, c
->uri
.authority
);
552 buffer_append_string_buffer(b
, c
->server_name
);
555 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
557 if (!buffer_string_is_empty(c
->uri
.path
)) {
558 buffer_append_string_encoded(b
, CONST_BUF_LEN(c
->uri
.path
), ENCODING_HTML
);
561 if (!buffer_string_is_empty(c
->uri
.query
)) {
562 buffer_append_string_len(b
, CONST_STR_LEN("?"));
563 buffer_append_string_encoded(b
, CONST_BUF_LEN(c
->uri
.query
), ENCODING_HTML
);
566 if (!buffer_string_is_empty(c
->request
.orig_uri
)) {
567 buffer_append_string_len(b
, CONST_STR_LEN(" ("));
568 buffer_append_string_encoded(b
, CONST_BUF_LEN(c
->request
.orig_uri
), ENCODING_HTML
);
569 buffer_append_string_len(b
, CONST_STR_LEN(")"));
571 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
573 buffer_append_string_buffer(b
, c
->physical
.path
);
575 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
579 buffer_append_string_len(b
, CONST_STR_LEN(
583 buffer_append_string_len(b
, CONST_STR_LEN(
588 chunkqueue_append_buffer_commit(con
->write_queue
);
590 http_header_response_set(con
, HTTP_HEADER_CONTENT_TYPE
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
596 static handler_t
mod_status_handle_server_status_text(server
*srv
, connection
*con
, void *p_d
) {
597 plugin_data
*p
= p_d
;
598 buffer
*b
= chunkqueue_append_buffer_open(con
->write_queue
);
605 /* output total number of requests */
606 buffer_append_string_len(b
, CONST_STR_LEN("Total Accesses: "));
607 avg
= p
->abs_requests
;
608 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
609 buffer_append_string(b
, buf
);
610 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
612 /* output total traffic out in kbytes */
613 buffer_append_string_len(b
, CONST_STR_LEN("Total kBytes: "));
614 avg
= p
->abs_traffic_out
/ 1024;
615 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
616 buffer_append_string(b
, buf
);
617 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
620 buffer_append_string_len(b
, CONST_STR_LEN("Uptime: "));
621 ts
= srv
->cur_ts
- srv
->startup_ts
;
622 buffer_append_int(b
, ts
);
623 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
625 /* output busy servers */
626 buffer_append_string_len(b
, CONST_STR_LEN("BusyServers: "));
627 buffer_append_int(b
, srv
->conns
->used
);
628 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
630 buffer_append_string_len(b
, CONST_STR_LEN("IdleServers: "));
631 buffer_append_int(b
, srv
->conns
->size
- srv
->conns
->used
);
632 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
634 /* output scoreboard */
635 buffer_append_string_len(b
, CONST_STR_LEN("Scoreboard: "));
636 for (k
= 0; k
< srv
->conns
->used
; k
++) {
637 connection
*c
= srv
->conns
->ptr
[k
];
639 (CON_STATE_READ
== c
->state
&& !buffer_string_is_empty(c
->request
.orig_uri
))
641 : connection_get_short_state(c
->state
);
642 buffer_append_string_len(b
, state
, 1);
644 for (l
= 0; l
< srv
->conns
->size
- srv
->conns
->used
; l
++) {
645 buffer_append_string_len(b
, CONST_STR_LEN("_"));
647 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
649 chunkqueue_append_buffer_commit(con
->write_queue
);
651 /* set text/plain output */
652 http_header_response_set(con
, HTTP_HEADER_CONTENT_TYPE
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
658 static handler_t
mod_status_handle_server_status_json(server
*srv
, connection
*con
, void *p_d
) {
659 plugin_data
*p
= p_d
;
660 buffer
*b
= chunkqueue_append_buffer_open(con
->write_queue
);
665 unsigned int jsonp
= 0;
667 if (buffer_string_length(con
->uri
.query
) >= sizeof("jsonp=")-1
668 && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("jsonp="))) {
669 /* not a full parse of query string for multiple parameters,
670 * not URL-decoding param and not XML-encoding (XSS protection),
671 * so simply ensure that json function name isalnum() or '_' */
672 const char *f
= con
->uri
.query
->ptr
+ sizeof("jsonp=")-1;
674 while (light_isalnum(f
[len
]) || f
[len
] == '_') ++len
;
675 if (0 != len
&& light_isalpha(f
[0]) && f
[len
] == '\0') {
676 buffer_append_string_len(b
, f
, len
);
677 buffer_append_string_len(b
, CONST_STR_LEN("("));
682 /* output total number of requests */
683 buffer_append_string_len(b
, CONST_STR_LEN("{\n\t\"RequestsTotal\": "));
684 avg
= p
->abs_requests
;
685 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
686 buffer_append_string(b
, buf
);
687 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
689 /* output total traffic out in kbytes */
690 buffer_append_string_len(b
, CONST_STR_LEN("\t\"TrafficTotal\": "));
691 avg
= p
->abs_traffic_out
/ 1024;
692 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
693 buffer_append_string(b
, buf
);
694 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
697 buffer_append_string_len(b
, CONST_STR_LEN("\t\"Uptime\": "));
698 ts
= srv
->cur_ts
- srv
->startup_ts
;
699 buffer_append_int(b
, ts
);
700 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
702 /* output busy servers */
703 buffer_append_string_len(b
, CONST_STR_LEN("\t\"BusyServers\": "));
704 buffer_append_int(b
, srv
->conns
->used
);
705 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
707 buffer_append_string_len(b
, CONST_STR_LEN("\t\"IdleServers\": "));
708 buffer_append_int(b
, srv
->conns
->size
- srv
->conns
->used
);
709 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
711 for (j
= 0, avg
= 0; j
< 5; j
++) {
712 avg
+= p
->mod_5s_requests
[j
];
717 buffer_append_string_len(b
, CONST_STR_LEN("\t\"RequestAverage5s\":"));
718 buffer_append_int(b
, avg
);
719 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
721 for (j
= 0, avg
= 0; j
< 5; j
++) {
722 avg
+= p
->mod_5s_traffic_out
[j
];
727 buffer_append_string_len(b
, CONST_STR_LEN("\t\"TrafficAverage5s\":"));
728 buffer_append_int(b
, avg
/ 1024); /* kbps */
729 buffer_append_string_len(b
, CONST_STR_LEN("\n}"));
731 if (jsonp
) buffer_append_string_len(b
, CONST_STR_LEN(");"));
733 chunkqueue_append_buffer_commit(con
->write_queue
);
735 /* set text/plain output */
736 http_header_response_set(con
, HTTP_HEADER_CONTENT_TYPE
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/javascript"));
742 static handler_t
mod_status_handle_server_statistics(server
*srv
, connection
*con
, void *p_d
) {
745 array
*st
= srv
->status
;
749 /* we have nothing to send */
750 con
->http_status
= 204;
751 con
->file_finished
= 1;
753 return HANDLER_FINISHED
;
756 b
= chunkqueue_append_buffer_open(con
->write_queue
);
757 for (i
= 0; i
< st
->used
; i
++) {
758 size_t ndx
= st
->sorted
[i
];
760 buffer_append_string_buffer(b
, st
->data
[ndx
]->key
);
761 buffer_append_string_len(b
, CONST_STR_LEN(": "));
762 buffer_append_int(b
, ((data_integer
*)(st
->data
[ndx
]))->value
);
763 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
765 chunkqueue_append_buffer_commit(con
->write_queue
);
767 http_header_response_set(con
, HTTP_HEADER_CONTENT_TYPE
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
769 con
->http_status
= 200;
770 con
->file_finished
= 1;
772 return HANDLER_FINISHED
;
776 static handler_t
mod_status_handle_server_status(server
*srv
, connection
*con
, void *p_d
) {
778 if (buffer_is_equal_string(con
->uri
.query
, CONST_STR_LEN("auto"))) {
779 mod_status_handle_server_status_text(srv
, con
, p_d
);
780 } else if (buffer_string_length(con
->uri
.query
) >= sizeof("json")-1
781 && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("json"))) {
782 mod_status_handle_server_status_json(srv
, con
, p_d
);
784 mod_status_handle_server_status_html(srv
, con
, p_d
);
787 con
->http_status
= 200;
788 con
->file_finished
= 1;
790 return HANDLER_FINISHED
;
794 static handler_t
mod_status_handle_server_config(server
*srv
, connection
*con
, void *p_d
) {
795 plugin_data
*p
= p_d
;
796 buffer
*b
= chunkqueue_append_buffer_open(con
->write_queue
);
797 buffer
*m
= p
->module_list
;
800 buffer_copy_string_len(b
, CONST_STR_LEN(
801 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
802 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
803 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
804 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
806 " <title>Status</title>\n"
810 buffer_append_string_buffer(b
, con
->conf
.server_tag
);
811 buffer_append_string_len(b
, CONST_STR_LEN(
813 " <table summary=\"status\" border=\"1\">\n"));
815 mod_status_header_append(b
, "Server-Features");
817 mod_status_row_append(b
, "RegEx Conditionals", "enabled");
819 mod_status_row_append(b
, "RegEx Conditionals", "disabled - pcre missing");
821 mod_status_header_append(b
, "Network Engine");
823 mod_status_row_append(b
, "fd-Event-Handler", srv
->srvconf
.event_handler
->ptr
);
825 mod_status_header_append(b
, "Config-File-Settings");
827 for (i
= 0; i
< srv
->plugins
.used
; i
++) {
828 plugin
**ps
= srv
->plugins
.ptr
;
833 buffer_copy_buffer(m
, pl
->name
);
835 buffer_append_string_len(m
, CONST_STR_LEN("<br />"));
836 buffer_append_string_buffer(m
, pl
->name
);
840 mod_status_row_append(b
, "Loaded Modules", m
->ptr
);
842 buffer_append_string_len(b
, CONST_STR_LEN(" </table>\n"));
844 buffer_append_string_len(b
, CONST_STR_LEN(
849 chunkqueue_append_buffer_commit(con
->write_queue
);
851 http_header_response_set(con
, HTTP_HEADER_CONTENT_TYPE
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
853 con
->http_status
= 200;
854 con
->file_finished
= 1;
856 return HANDLER_FINISHED
;
861 static int mod_status_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
863 plugin_config
*s
= p
->config_storage
[0];
868 PATCH(statistics_url
);
870 /* skip the first, the global context */
871 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
872 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
873 s
= p
->config_storage
[i
];
875 /* condition didn't match */
876 if (!config_check_cond(srv
, con
, dc
)) continue;
879 for (j
= 0; j
< dc
->value
->used
; j
++) {
880 data_unset
*du
= dc
->value
->data
[j
];
882 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.status-url"))) {
884 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.config-url"))) {
886 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.enable-sort"))) {
888 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.statistics-url"))) {
889 PATCH(statistics_url
);
897 static handler_t
mod_status_handler(server
*srv
, connection
*con
, void *p_d
) {
898 plugin_data
*p
= p_d
;
900 if (con
->mode
!= DIRECT
) return HANDLER_GO_ON
;
902 mod_status_patch_connection(srv
, con
, p
);
904 if (!buffer_string_is_empty(p
->conf
.status_url
) &&
905 buffer_is_equal(p
->conf
.status_url
, con
->uri
.path
)) {
906 return mod_status_handle_server_status(srv
, con
, p_d
);
907 } else if (!buffer_string_is_empty(p
->conf
.config_url
) &&
908 buffer_is_equal(p
->conf
.config_url
, con
->uri
.path
)) {
909 return mod_status_handle_server_config(srv
, con
, p_d
);
910 } else if (!buffer_string_is_empty(p
->conf
.statistics_url
) &&
911 buffer_is_equal(p
->conf
.statistics_url
, con
->uri
.path
)) {
912 return mod_status_handle_server_statistics(srv
, con
, p_d
);
915 return HANDLER_GO_ON
;
918 TRIGGER_FUNC(mod_status_trigger
) {
919 plugin_data
*p
= p_d
;
922 /* check all connections */
923 for (i
= 0; i
< srv
->conns
->used
; i
++) {
924 connection
*c
= srv
->conns
->ptr
[i
];
926 p
->bytes_written
+= c
->bytes_written_cur_second
;
929 /* a sliding average */
930 p
->mod_5s_traffic_out
[p
->mod_5s_ndx
] = p
->bytes_written
;
931 p
->mod_5s_requests
[p
->mod_5s_ndx
] = p
->requests
;
933 p
->mod_5s_ndx
= (p
->mod_5s_ndx
+1) % 5;
935 p
->abs_traffic_out
+= p
->bytes_written
;
936 p
->rel_traffic_out
+= p
->bytes_written
;
938 p
->bytes_written
= 0;
940 /* reset storage - second */
944 return HANDLER_GO_ON
;
947 REQUESTDONE_FUNC(mod_status_account
) {
948 plugin_data
*p
= p_d
;
956 p
->bytes_written
+= con
->bytes_written_cur_second
;
958 return HANDLER_GO_ON
;
961 int mod_status_plugin_init(plugin
*p
);
962 int mod_status_plugin_init(plugin
*p
) {
963 p
->version
= LIGHTTPD_VERSION_ID
;
964 p
->name
= buffer_init_string("status");
966 p
->init
= mod_status_init
;
967 p
->cleanup
= mod_status_free
;
968 p
->set_defaults
= mod_status_set_defaults
;
970 p
->handle_uri_clean
= mod_status_handler
;
971 p
->handle_trigger
= mod_status_trigger
;
972 p
->handle_request_done
= mod_status_account
;