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
];
89 buffer_free(s
->status_url
);
90 buffer_free(s
->statistics_url
);
91 buffer_free(s
->config_url
);
95 free(p
->config_storage
);
101 return HANDLER_GO_ON
;
104 SETDEFAULTS_FUNC(mod_status_set_defaults
) {
105 plugin_data
*p
= p_d
;
108 config_values_t cv
[] = {
109 { "status.status-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
110 { "status.config-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
111 { "status.enable-sort", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
},
112 { "status.statistics-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
113 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
116 if (!p
) return HANDLER_ERROR
;
118 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
120 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
121 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
124 s
= calloc(1, sizeof(plugin_config
));
125 s
->config_url
= buffer_init();
126 s
->status_url
= buffer_init();
128 s
->statistics_url
= buffer_init();
130 cv
[0].destination
= s
->status_url
;
131 cv
[1].destination
= s
->config_url
;
132 cv
[2].destination
= &(s
->sort
);
133 cv
[3].destination
= s
->statistics_url
;
135 p
->config_storage
[i
] = s
;
137 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
138 return HANDLER_ERROR
;
142 return HANDLER_GO_ON
;
147 static int mod_status_row_append(buffer
*b
, const char *key
, const char *value
) {
148 buffer_append_string_len(b
, CONST_STR_LEN(" <tr>\n"));
149 buffer_append_string_len(b
, CONST_STR_LEN(" <td><b>"));
150 buffer_append_string(b
, key
);
151 buffer_append_string_len(b
, CONST_STR_LEN("</b></td>\n"));
152 buffer_append_string_len(b
, CONST_STR_LEN(" <td>"));
153 buffer_append_string(b
, value
);
154 buffer_append_string_len(b
, CONST_STR_LEN("</td>\n"));
155 buffer_append_string_len(b
, CONST_STR_LEN(" </tr>\n"));
160 static int mod_status_header_append(buffer
*b
, const char *key
) {
161 buffer_append_string_len(b
, CONST_STR_LEN(" <tr>\n"));
162 buffer_append_string_len(b
, CONST_STR_LEN(" <th colspan=\"2\">"));
163 buffer_append_string(b
, key
);
164 buffer_append_string_len(b
, CONST_STR_LEN("</th>\n"));
165 buffer_append_string_len(b
, CONST_STR_LEN(" </tr>\n"));
170 static int mod_status_header_append_sort(buffer
*b
, void *p_d
, const char* key
) {
171 plugin_data
*p
= p_d
;
174 buffer_append_string_len(b
, CONST_STR_LEN("<th class=\"status\"><a href=\"#\" class=\"sortheader\" onclick=\"resort(this);return false;\">"));
175 buffer_append_string(b
, key
);
176 buffer_append_string_len(b
, CONST_STR_LEN("<span class=\"sortarrow\">:</span></a></th>\n"));
178 buffer_append_string_len(b
, CONST_STR_LEN("<th class=\"status\">"));
179 buffer_append_string(b
, key
);
180 buffer_append_string_len(b
, CONST_STR_LEN("</th>\n"));
186 static int mod_status_get_multiplier(double *avg
, char *multiplier
, int size
) {
189 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'k'; }
190 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'M'; }
191 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'G'; }
192 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'T'; }
193 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'P'; }
194 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'E'; }
195 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'Z'; }
196 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'Y'; }
201 static handler_t
mod_status_handle_server_status_html(server
*srv
, connection
*con
, void *p_d
) {
202 plugin_data
*p
= p_d
;
203 buffer
*b
= buffer_init();
206 char multiplier
= '\0';
210 int days
, hours
, mins
, seconds
;
212 /*(CON_STATE_CLOSE must be last state in enum connection_state_t)*/
213 int cstates
[CON_STATE_CLOSE
+3];
214 memset(cstates
, 0, sizeof(cstates
));
216 buffer_copy_string_len(b
, CONST_STR_LEN(
217 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
218 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
219 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
220 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
222 " <title>Status</title>\n"
224 " <style type=\"text/css\">\n"
225 " table.status { border: black solid thin; }\n"
226 " td { white-space: nowrap; }\n"
227 " td.int { background-color: #f0f0f0; text-align: right }\n"
228 " td.string { background-color: #f0f0f0; text-align: left }\n"
229 " th.status { background-color: black; color: white; font-weight: bold; }\n"
230 " a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n"
231 " span.sortarrow { color: white; text-decoration: none; }\n"
234 if (!buffer_string_is_empty(con
->uri
.query
) && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("refresh="))) {
235 /* Note: Refresh is an historical, but non-standard HTTP header
236 * References (meta http-equiv="refresh" use is deprecated):
237 * https://www.w3.org/TR/WCAG10-HTML-TECHS/#meta-element
238 * https://www.w3.org/TR/WCAG10-CORE-TECHS/#auto-page-refresh
239 * https://www.w3.org/QA/Tips/reback
241 const long refresh
= strtol(con
->uri
.query
->ptr
+sizeof("refresh=")-1, NULL
, 10);
243 buffer_append_string_len(b
, CONST_STR_LEN("<meta http-equiv=\"refresh\" content=\""));
244 buffer_append_int(b
, refresh
< 604800 ? refresh
: 604800);
245 buffer_append_string_len(b
, CONST_STR_LEN("\">\n"));
250 buffer_append_string_len(b
, CONST_STR_LEN(
251 "<script type=\"text/javascript\">\n"
254 "var prev_span = null;\n"
256 "function get_inner_text(el) {\n"
257 " if((typeof el == 'string')||(typeof el == 'undefined'))\n"
259 " if(el.innerText)\n"
260 " return el.innerText;\n"
263 " var cs = el.childNodes;\n"
264 " var l = cs.length;\n"
265 " for (i=0;i<l;i++) {\n"
266 " if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n"
267 " else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n"
273 "function sortfn(a,b) {\n"
274 " var at = get_inner_text(a.cells[sort_column]);\n"
275 " var bt = get_inner_text(b.cells[sort_column]);\n"
276 " if (a.cells[sort_column].className == 'int') {\n"
277 " return parseInt(at)-parseInt(bt);\n"
279 " aa = at.toLowerCase();\n"
280 " bb = bt.toLowerCase();\n"
281 " if (aa==bb) return 0;\n"
282 " else if (aa<bb) return -1;\n"
287 "function resort(lnk) {\n"
288 " var span = lnk.childNodes[1];\n"
289 " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n"
290 " var rows = new Array();\n"
291 " for (j=1;j<table.rows.length;j++)\n"
292 " rows[j-1] = table.rows[j];\n"
293 " sort_column = lnk.parentNode.cellIndex;\n"
294 " rows.sort(sortfn);\n"
296 " if (prev_span != null) prev_span.innerHTML = '';\n"
297 " if (span.getAttribute('sortdir')=='down') {\n"
298 " span.innerHTML = '↑';\n"
299 " span.setAttribute('sortdir','up');\n"
302 " span.innerHTML = '↓';\n"
303 " span.setAttribute('sortdir','down');\n"
305 " for (i=0;i<rows.length;i++)\n"
306 " table.tBodies[0].appendChild(rows[i]);\n"
307 " prev_span = span;\n"
313 buffer_append_string_len(b
, CONST_STR_LEN(
319 /* connection listing */
320 buffer_append_string_len(b
, CONST_STR_LEN("<h1>Server-Status ("));
321 buffer_append_string_buffer(b
, con
->conf
.server_tag
);
322 buffer_append_string_len(b
, CONST_STR_LEN(")</h1>"));
324 buffer_append_string_len(b
, CONST_STR_LEN("<table summary=\"status\" class=\"status\">"));
325 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Hostname</td><td class=\"string\">"));
326 buffer_append_string_buffer(b
, con
->uri
.authority
);
327 buffer_append_string_len(b
, CONST_STR_LEN(" ("));
328 buffer_append_string_buffer(b
, con
->server_name
);
329 buffer_append_string_len(b
, CONST_STR_LEN(")</td></tr>\n"));
330 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Uptime</td><td class=\"string\">"));
332 ts
= srv
->cur_ts
- srv
->startup_ts
;
334 days
= ts
/ (60 * 60 * 24);
335 ts
%= (60 * 60 * 24);
337 hours
= ts
/ (60 * 60);
346 buffer_append_int(b
, days
);
347 buffer_append_string_len(b
, CONST_STR_LEN(" days "));
351 buffer_append_int(b
, hours
);
352 buffer_append_string_len(b
, CONST_STR_LEN(" hours "));
356 buffer_append_int(b
, mins
);
357 buffer_append_string_len(b
, CONST_STR_LEN(" min "));
360 buffer_append_int(b
, seconds
);
361 buffer_append_string_len(b
, CONST_STR_LEN(" s"));
363 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
364 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Started at</td><td class=\"string\">"));
366 ts
= srv
->startup_ts
;
368 strftime(buf
, sizeof(buf
) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts
));
369 buffer_append_string(b
, buf
);
370 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
373 buffer_append_string_len(b
, CONST_STR_LEN("<tr><th colspan=\"2\">absolute (since start)</th></tr>\n"));
375 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
376 avg
= p
->abs_requests
;
378 mod_status_get_multiplier(&avg
, &multiplier
, 1000);
380 buffer_append_int(b
, avg
);
381 buffer_append_string_len(b
, CONST_STR_LEN(" "));
382 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
383 buffer_append_string_len(b
, CONST_STR_LEN("req</td></tr>\n"));
385 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
386 avg
= p
->abs_traffic_out
;
388 mod_status_get_multiplier(&avg
, &multiplier
, 1024);
390 snprintf(buf
, sizeof(buf
), "%.2f", avg
);
391 buffer_append_string(b
, buf
);
392 buffer_append_string_len(b
, CONST_STR_LEN(" "));
393 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
394 buffer_append_string_len(b
, CONST_STR_LEN("byte</td></tr>\n"));
398 buffer_append_string_len(b
, CONST_STR_LEN("<tr><th colspan=\"2\">average (since start)</th></tr>\n"));
400 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
401 avg
= p
->abs_requests
/ (srv
->cur_ts
- srv
->startup_ts
);
403 mod_status_get_multiplier(&avg
, &multiplier
, 1000);
405 buffer_append_int(b
, avg
);
406 buffer_append_string_len(b
, CONST_STR_LEN(" "));
407 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
408 buffer_append_string_len(b
, CONST_STR_LEN("req/s</td></tr>\n"));
410 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
411 avg
= p
->abs_traffic_out
/ (srv
->cur_ts
- srv
->startup_ts
);
413 mod_status_get_multiplier(&avg
, &multiplier
, 1024);
415 snprintf(buf
, sizeof(buf
), "%.2f", avg
);
416 buffer_append_string(b
, buf
);
417 buffer_append_string_len(b
, CONST_STR_LEN(" "));
418 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
419 buffer_append_string_len(b
, CONST_STR_LEN("byte/s</td></tr>\n"));
423 buffer_append_string_len(b
, CONST_STR_LEN("<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n"));
424 for (j
= 0, avg
= 0; j
< 5; j
++) {
425 avg
+= p
->mod_5s_requests
[j
];
430 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
432 mod_status_get_multiplier(&avg
, &multiplier
, 1000);
434 buffer_append_int(b
, avg
);
435 buffer_append_string_len(b
, CONST_STR_LEN(" "));
436 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
438 buffer_append_string_len(b
, CONST_STR_LEN("req/s</td></tr>\n"));
440 for (j
= 0, avg
= 0; j
< 5; j
++) {
441 avg
+= p
->mod_5s_traffic_out
[j
];
446 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
448 mod_status_get_multiplier(&avg
, &multiplier
, 1024);
450 snprintf(buf
, sizeof(buf
), "%.2f", avg
);
451 buffer_append_string(b
, buf
);
452 buffer_append_string_len(b
, CONST_STR_LEN(" "));
453 if (multiplier
) buffer_append_string_len(b
, &multiplier
, 1);
454 buffer_append_string_len(b
, CONST_STR_LEN("byte/s</td></tr>\n"));
456 buffer_append_string_len(b
, CONST_STR_LEN("</table>\n"));
458 buffer_append_string_len(b
, CONST_STR_LEN("<hr />\n<pre>\n"));
460 buffer_append_string_len(b
, CONST_STR_LEN("<b>"));
461 buffer_append_int(b
, srv
->conns
->used
);
462 buffer_append_string_len(b
, CONST_STR_LEN(" connections</b>\n"));
464 for (j
= 0; j
< srv
->conns
->used
; j
++) {
465 connection
*c
= srv
->conns
->ptr
[j
];
468 if (CON_STATE_READ
== c
->state
&& !buffer_string_is_empty(c
->request
.orig_uri
)) {
470 ++cstates
[CON_STATE_CLOSE
+2];
472 state
= connection_get_short_state(c
->state
);
473 ++cstates
[(c
->state
<= CON_STATE_CLOSE
? c
->state
: CON_STATE_CLOSE
+1)];
476 buffer_append_string_len(b
, state
, 1);
478 if (((j
+ 1) % 50) == 0) {
479 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
482 buffer_append_string_len(b
, CONST_STR_LEN("\n\n<table>\n"));
483 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td style=\"text-align:right\">"));
484 buffer_append_int(b
, cstates
[CON_STATE_CLOSE
+2]);
485 buffer_append_string_len(b
, CONST_STR_LEN("<td> k = keep-alive</td></tr>\n"));
486 for (j
= 0; j
< CON_STATE_CLOSE
+2; ++j
) {
487 /*(skip "unknown" state if there are none; there should not be any unknown)*/
488 if (0 == cstates
[j
] && j
== CON_STATE_CLOSE
+1) continue;
489 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td style=\"text-align:right\">"));
490 buffer_append_int(b
, cstates
[j
]);
491 buffer_append_string_len(b
, CONST_STR_LEN("</td><td> "));
492 buffer_append_string_len(b
, connection_get_short_state(j
), 1);
493 buffer_append_string_len(b
, CONST_STR_LEN(" = "));
494 buffer_append_string(b
, connection_get_state(j
));
495 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
497 buffer_append_string_len(b
, CONST_STR_LEN("</table>"));
499 buffer_append_string_len(b
, CONST_STR_LEN("\n</pre><hr />\n<h2>Connections</h2>\n"));
501 buffer_append_string_len(b
, CONST_STR_LEN("<table summary=\"status\" class=\"status\">\n"));
502 buffer_append_string_len(b
, CONST_STR_LEN("<tr>"));
503 mod_status_header_append_sort(b
, p_d
, "Client IP");
504 mod_status_header_append_sort(b
, p_d
, "Read");
505 mod_status_header_append_sort(b
, p_d
, "Written");
506 mod_status_header_append_sort(b
, p_d
, "State");
507 mod_status_header_append_sort(b
, p_d
, "Time");
508 mod_status_header_append_sort(b
, p_d
, "Host");
509 mod_status_header_append_sort(b
, p_d
, "URI");
510 mod_status_header_append_sort(b
, p_d
, "File");
511 buffer_append_string_len(b
, CONST_STR_LEN("</tr>\n"));
513 for (j
= 0; j
< srv
->conns
->used
; j
++) {
514 connection
*c
= srv
->conns
->ptr
[j
];
516 buffer_append_string_len(b
, CONST_STR_LEN("<tr><td class=\"string\">"));
518 buffer_append_string(b
, inet_ntop_cache_get_ip(srv
, &(c
->dst_addr
)));
520 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"int\">"));
522 if (c
->request
.content_length
) {
523 buffer_append_int(b
, c
->request_content_queue
->bytes_in
);
524 buffer_append_string_len(b
, CONST_STR_LEN("/"));
525 buffer_append_int(b
, c
->request
.content_length
);
527 buffer_append_string_len(b
, CONST_STR_LEN("0/0"));
530 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"int\">"));
532 buffer_append_int(b
, c
->write_queue
->bytes_out
);
533 buffer_append_string_len(b
, CONST_STR_LEN("/"));
534 buffer_append_int(b
, c
->write_queue
->bytes_out
+ chunkqueue_length(c
->write_queue
));
536 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
538 if (CON_STATE_READ
== c
->state
&& !buffer_string_is_empty(c
->request
.orig_uri
)) {
539 buffer_append_string_len(b
, CONST_STR_LEN("keep-alive"));
541 buffer_append_string(b
, connection_get_state(c
->state
));
544 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"int\">"));
546 buffer_append_int(b
, srv
->cur_ts
- c
->request_start
);
548 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
550 if (buffer_string_is_empty(c
->server_name
)) {
551 buffer_append_string_buffer(b
, c
->uri
.authority
);
554 buffer_append_string_buffer(b
, c
->server_name
);
557 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
559 if (!buffer_string_is_empty(c
->uri
.path
)) {
560 buffer_append_string_encoded(b
, CONST_BUF_LEN(c
->uri
.path
), ENCODING_HTML
);
563 if (!buffer_string_is_empty(c
->uri
.query
)) {
564 buffer_append_string_len(b
, CONST_STR_LEN("?"));
565 buffer_append_string_encoded(b
, CONST_BUF_LEN(c
->uri
.query
), ENCODING_HTML
);
568 if (!buffer_string_is_empty(c
->request
.orig_uri
)) {
569 buffer_append_string_len(b
, CONST_STR_LEN(" ("));
570 buffer_append_string_encoded(b
, CONST_BUF_LEN(c
->request
.orig_uri
), ENCODING_HTML
);
571 buffer_append_string_len(b
, CONST_STR_LEN(")"));
573 buffer_append_string_len(b
, CONST_STR_LEN("</td><td class=\"string\">"));
575 buffer_append_string_buffer(b
, c
->physical
.path
);
577 buffer_append_string_len(b
, CONST_STR_LEN("</td></tr>\n"));
581 buffer_append_string_len(b
, CONST_STR_LEN(
585 buffer_append_string_len(b
, CONST_STR_LEN(
590 chunkqueue_append_buffer(con
->write_queue
, b
);
593 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
599 static handler_t
mod_status_handle_server_status_text(server
*srv
, connection
*con
, void *p_d
) {
600 plugin_data
*p
= p_d
;
601 buffer
*b
= buffer_init();
608 /* output total number of requests */
609 buffer_append_string_len(b
, CONST_STR_LEN("Total Accesses: "));
610 avg
= p
->abs_requests
;
611 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
612 buffer_append_string(b
, buf
);
613 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
615 /* output total traffic out in kbytes */
616 buffer_append_string_len(b
, CONST_STR_LEN("Total kBytes: "));
617 avg
= p
->abs_traffic_out
/ 1024;
618 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
619 buffer_append_string(b
, buf
);
620 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
623 buffer_append_string_len(b
, CONST_STR_LEN("Uptime: "));
624 ts
= srv
->cur_ts
- srv
->startup_ts
;
625 buffer_append_int(b
, ts
);
626 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
628 /* output busy servers */
629 buffer_append_string_len(b
, CONST_STR_LEN("BusyServers: "));
630 buffer_append_int(b
, srv
->conns
->used
);
631 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
633 buffer_append_string_len(b
, CONST_STR_LEN("IdleServers: "));
634 buffer_append_int(b
, srv
->conns
->size
- srv
->conns
->used
);
635 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
637 /* output scoreboard */
638 buffer_append_string_len(b
, CONST_STR_LEN("Scoreboard: "));
639 for (k
= 0; k
< srv
->conns
->used
; k
++) {
640 connection
*c
= srv
->conns
->ptr
[k
];
641 const char *state
= 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(con
->write_queue
, b
);
652 /* set text/plain output */
653 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
659 static handler_t
mod_status_handle_server_status_json(server
*srv
, connection
*con
, void *p_d
) {
660 plugin_data
*p
= p_d
;
661 buffer
*b
= buffer_init();
666 unsigned int jsonp
= 0;
668 if (buffer_string_length(con
->uri
.query
) >= sizeof("jsonp=")-1
669 && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("jsonp="))) {
670 /* not a full parse of query string for multiple parameters,
671 * not URL-decoding param and not XML-encoding (XSS protection),
672 * so simply ensure that json function name isalnum() or '_' */
673 const char *f
= con
->uri
.query
->ptr
+ sizeof("jsonp=")-1;
675 while (light_isalnum(f
[len
]) || f
[len
] == '_') ++len
;
676 if (0 != len
&& light_isalpha(f
[0]) && f
[len
] == '\0') {
677 buffer_append_string_len(b
, f
, len
);
678 buffer_append_string_len(b
, CONST_STR_LEN("("));
683 /* output total number of requests */
684 buffer_append_string_len(b
, CONST_STR_LEN("{\n\t\"RequestsTotal\": "));
685 avg
= p
->abs_requests
;
686 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
687 buffer_append_string(b
, buf
);
688 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
690 /* output total traffic out in kbytes */
691 buffer_append_string_len(b
, CONST_STR_LEN("\t\"TrafficTotal\": "));
692 avg
= p
->abs_traffic_out
/ 1024;
693 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
694 buffer_append_string(b
, buf
);
695 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
698 buffer_append_string_len(b
, CONST_STR_LEN("\t\"Uptime\": "));
699 ts
= srv
->cur_ts
- srv
->startup_ts
;
700 buffer_append_int(b
, ts
);
701 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
703 /* output busy servers */
704 buffer_append_string_len(b
, CONST_STR_LEN("\t\"BusyServers\": "));
705 buffer_append_int(b
, srv
->conns
->used
);
706 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
708 buffer_append_string_len(b
, CONST_STR_LEN("\t\"IdleServers\": "));
709 buffer_append_int(b
, srv
->conns
->size
- srv
->conns
->used
);
710 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
712 for (j
= 0, avg
= 0; j
< 5; j
++) {
713 avg
+= p
->mod_5s_requests
[j
];
718 buffer_append_string_len(b
, CONST_STR_LEN("\t\"RequestAverage5s\":"));
719 buffer_append_int(b
, avg
);
720 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
722 for (j
= 0, avg
= 0; j
< 5; j
++) {
723 avg
+= p
->mod_5s_traffic_out
[j
];
728 buffer_append_string_len(b
, CONST_STR_LEN("\t\"TrafficAverage5s\":"));
729 buffer_append_int(b
, avg
/ 1024); /* kbps */
730 buffer_append_string_len(b
, CONST_STR_LEN("\n}"));
732 if (jsonp
) buffer_append_string_len(b
, CONST_STR_LEN(");"));
734 chunkqueue_append_buffer(con
->write_queue
, b
);
737 /* set text/plain output */
738 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/javascript"));
744 static handler_t
mod_status_handle_server_statistics(server
*srv
, connection
*con
, void *p_d
) {
747 array
*st
= srv
->status
;
751 /* we have nothing to send */
752 con
->http_status
= 204;
753 con
->file_finished
= 1;
755 return HANDLER_FINISHED
;
759 for (i
= 0; i
< st
->used
; i
++) {
760 size_t ndx
= st
->sorted
[i
];
762 buffer_append_string_buffer(b
, st
->data
[ndx
]->key
);
763 buffer_append_string_len(b
, CONST_STR_LEN(": "));
764 buffer_append_int(b
, ((data_integer
*)(st
->data
[ndx
]))->value
);
765 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
768 chunkqueue_append_buffer(con
->write_queue
, b
);
771 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
773 con
->http_status
= 200;
774 con
->file_finished
= 1;
776 return HANDLER_FINISHED
;
780 static handler_t
mod_status_handle_server_status(server
*srv
, connection
*con
, void *p_d
) {
782 if (buffer_is_equal_string(con
->uri
.query
, CONST_STR_LEN("auto"))) {
783 mod_status_handle_server_status_text(srv
, con
, p_d
);
784 } else if (buffer_string_length(con
->uri
.query
) >= sizeof("json")-1
785 && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("json"))) {
786 mod_status_handle_server_status_json(srv
, con
, p_d
);
788 mod_status_handle_server_status_html(srv
, con
, p_d
);
791 con
->http_status
= 200;
792 con
->file_finished
= 1;
794 return HANDLER_FINISHED
;
798 static handler_t
mod_status_handle_server_config(server
*srv
, connection
*con
, void *p_d
) {
799 plugin_data
*p
= p_d
;
800 buffer
*b
= buffer_init();
801 buffer
*m
= p
->module_list
;
804 struct ev_map
{ fdevent_handler_t et
; const char *name
; } event_handlers
[] =
806 /* - epoll is most reliable
807 * - select works everywhere
809 #ifdef USE_LINUX_EPOLL
810 { FDEVENT_HANDLER_LINUX_SYSEPOLL
, "linux-sysepoll" },
813 { FDEVENT_HANDLER_POLL
, "poll" },
816 { FDEVENT_HANDLER_SELECT
, "select" },
819 { FDEVENT_HANDLER_LIBEV
, "libev" },
821 #ifdef USE_SOLARIS_DEVPOLL
822 { FDEVENT_HANDLER_SOLARIS_DEVPOLL
,"solaris-devpoll" },
824 #ifdef USE_SOLARIS_PORT
825 { FDEVENT_HANDLER_SOLARIS_PORT
, "solaris-eventports" },
827 #ifdef USE_FREEBSD_KQUEUE
828 { FDEVENT_HANDLER_FREEBSD_KQUEUE
, "freebsd-kqueue" },
830 { FDEVENT_HANDLER_UNSET
, NULL
}
833 buffer_copy_string_len(b
, CONST_STR_LEN(
834 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
835 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
836 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
837 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
839 " <title>Status</title>\n"
843 buffer_append_string_buffer(b
, con
->conf
.server_tag
);
844 buffer_append_string_len(b
, CONST_STR_LEN(
846 " <table summary=\"status\" border=\"1\">\n"));
848 mod_status_header_append(b
, "Server-Features");
850 mod_status_row_append(b
, "RegEx Conditionals", "enabled");
852 mod_status_row_append(b
, "RegEx Conditionals", "disabled - pcre missing");
854 mod_status_header_append(b
, "Network Engine");
856 for (i
= 0; event_handlers
[i
].name
; i
++) {
857 if (event_handlers
[i
].et
== srv
->event_handler
) {
858 mod_status_row_append(b
, "fd-Event-Handler", event_handlers
[i
].name
);
863 mod_status_header_append(b
, "Config-File-Settings");
865 for (i
= 0; i
< srv
->plugins
.used
; i
++) {
866 plugin
**ps
= srv
->plugins
.ptr
;
871 buffer_copy_buffer(m
, pl
->name
);
873 buffer_append_string_len(m
, CONST_STR_LEN("<br />"));
874 buffer_append_string_buffer(m
, pl
->name
);
878 mod_status_row_append(b
, "Loaded Modules", m
->ptr
);
880 buffer_append_string_len(b
, CONST_STR_LEN(" </table>\n"));
882 buffer_append_string_len(b
, CONST_STR_LEN(
887 chunkqueue_append_buffer(con
->write_queue
, b
);
890 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
892 con
->http_status
= 200;
893 con
->file_finished
= 1;
895 return HANDLER_FINISHED
;
900 static int mod_status_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
902 plugin_config
*s
= p
->config_storage
[0];
907 PATCH(statistics_url
);
909 /* skip the first, the global context */
910 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
911 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
912 s
= p
->config_storage
[i
];
914 /* condition didn't match */
915 if (!config_check_cond(srv
, con
, dc
)) continue;
918 for (j
= 0; j
< dc
->value
->used
; j
++) {
919 data_unset
*du
= dc
->value
->data
[j
];
921 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.status-url"))) {
923 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.config-url"))) {
925 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.enable-sort"))) {
927 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.statistics-url"))) {
928 PATCH(statistics_url
);
936 static handler_t
mod_status_handler(server
*srv
, connection
*con
, void *p_d
) {
937 plugin_data
*p
= p_d
;
939 if (con
->mode
!= DIRECT
) return HANDLER_GO_ON
;
941 mod_status_patch_connection(srv
, con
, p
);
943 if (!buffer_string_is_empty(p
->conf
.status_url
) &&
944 buffer_is_equal(p
->conf
.status_url
, con
->uri
.path
)) {
945 return mod_status_handle_server_status(srv
, con
, p_d
);
946 } else if (!buffer_string_is_empty(p
->conf
.config_url
) &&
947 buffer_is_equal(p
->conf
.config_url
, con
->uri
.path
)) {
948 return mod_status_handle_server_config(srv
, con
, p_d
);
949 } else if (!buffer_string_is_empty(p
->conf
.statistics_url
) &&
950 buffer_is_equal(p
->conf
.statistics_url
, con
->uri
.path
)) {
951 return mod_status_handle_server_statistics(srv
, con
, p_d
);
954 return HANDLER_GO_ON
;
957 TRIGGER_FUNC(mod_status_trigger
) {
958 plugin_data
*p
= p_d
;
961 /* check all connections */
962 for (i
= 0; i
< srv
->conns
->used
; i
++) {
963 connection
*c
= srv
->conns
->ptr
[i
];
965 p
->bytes_written
+= c
->bytes_written_cur_second
;
968 /* a sliding average */
969 p
->mod_5s_traffic_out
[p
->mod_5s_ndx
] = p
->bytes_written
;
970 p
->mod_5s_requests
[p
->mod_5s_ndx
] = p
->requests
;
972 p
->mod_5s_ndx
= (p
->mod_5s_ndx
+1) % 5;
974 p
->abs_traffic_out
+= p
->bytes_written
;
975 p
->rel_traffic_out
+= p
->bytes_written
;
977 p
->bytes_written
= 0;
979 /* reset storage - second */
983 return HANDLER_GO_ON
;
986 REQUESTDONE_FUNC(mod_status_account
) {
987 plugin_data
*p
= p_d
;
995 p
->bytes_written
+= con
->bytes_written_cur_second
;
997 return HANDLER_GO_ON
;
1000 int mod_status_plugin_init(plugin
*p
);
1001 int mod_status_plugin_init(plugin
*p
) {
1002 p
->version
= LIGHTTPD_VERSION_ID
;
1003 p
->name
= buffer_init_string("status");
1005 p
->init
= mod_status_init
;
1006 p
->cleanup
= mod_status_free
;
1007 p
->set_defaults
= mod_status_set_defaults
;
1009 p
->handle_uri_clean
= mod_status_handler
;
1010 p
->handle_trigger
= mod_status_trigger
;
1011 p
->handle_request_done
= mod_status_account
;