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
];
642 (CON_STATE_READ
== c
->state
&& !buffer_string_is_empty(c
->request
.orig_uri
))
644 : connection_get_short_state(c
->state
);
645 buffer_append_string_len(b
, state
, 1);
647 for (l
= 0; l
< srv
->conns
->size
- srv
->conns
->used
; l
++) {
648 buffer_append_string_len(b
, CONST_STR_LEN("_"));
650 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
652 chunkqueue_append_buffer(con
->write_queue
, b
);
655 /* set text/plain output */
656 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
662 static handler_t
mod_status_handle_server_status_json(server
*srv
, connection
*con
, void *p_d
) {
663 plugin_data
*p
= p_d
;
664 buffer
*b
= buffer_init();
669 unsigned int jsonp
= 0;
671 if (buffer_string_length(con
->uri
.query
) >= sizeof("jsonp=")-1
672 && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("jsonp="))) {
673 /* not a full parse of query string for multiple parameters,
674 * not URL-decoding param and not XML-encoding (XSS protection),
675 * so simply ensure that json function name isalnum() or '_' */
676 const char *f
= con
->uri
.query
->ptr
+ sizeof("jsonp=")-1;
678 while (light_isalnum(f
[len
]) || f
[len
] == '_') ++len
;
679 if (0 != len
&& light_isalpha(f
[0]) && f
[len
] == '\0') {
680 buffer_append_string_len(b
, f
, len
);
681 buffer_append_string_len(b
, CONST_STR_LEN("("));
686 /* output total number of requests */
687 buffer_append_string_len(b
, CONST_STR_LEN("{\n\t\"RequestsTotal\": "));
688 avg
= p
->abs_requests
;
689 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
690 buffer_append_string(b
, buf
);
691 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
693 /* output total traffic out in kbytes */
694 buffer_append_string_len(b
, CONST_STR_LEN("\t\"TrafficTotal\": "));
695 avg
= p
->abs_traffic_out
/ 1024;
696 snprintf(buf
, sizeof(buf
) - 1, "%.0f", avg
);
697 buffer_append_string(b
, buf
);
698 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
701 buffer_append_string_len(b
, CONST_STR_LEN("\t\"Uptime\": "));
702 ts
= srv
->cur_ts
- srv
->startup_ts
;
703 buffer_append_int(b
, ts
);
704 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
706 /* output busy servers */
707 buffer_append_string_len(b
, CONST_STR_LEN("\t\"BusyServers\": "));
708 buffer_append_int(b
, srv
->conns
->used
);
709 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
711 buffer_append_string_len(b
, CONST_STR_LEN("\t\"IdleServers\": "));
712 buffer_append_int(b
, srv
->conns
->size
- srv
->conns
->used
);
713 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
715 for (j
= 0, avg
= 0; j
< 5; j
++) {
716 avg
+= p
->mod_5s_requests
[j
];
721 buffer_append_string_len(b
, CONST_STR_LEN("\t\"RequestAverage5s\":"));
722 buffer_append_int(b
, avg
);
723 buffer_append_string_len(b
, CONST_STR_LEN(",\n"));
725 for (j
= 0, avg
= 0; j
< 5; j
++) {
726 avg
+= p
->mod_5s_traffic_out
[j
];
731 buffer_append_string_len(b
, CONST_STR_LEN("\t\"TrafficAverage5s\":"));
732 buffer_append_int(b
, avg
/ 1024); /* kbps */
733 buffer_append_string_len(b
, CONST_STR_LEN("\n}"));
735 if (jsonp
) buffer_append_string_len(b
, CONST_STR_LEN(");"));
737 chunkqueue_append_buffer(con
->write_queue
, b
);
740 /* set text/plain output */
741 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/javascript"));
747 static handler_t
mod_status_handle_server_statistics(server
*srv
, connection
*con
, void *p_d
) {
750 array
*st
= srv
->status
;
754 /* we have nothing to send */
755 con
->http_status
= 204;
756 con
->file_finished
= 1;
758 return HANDLER_FINISHED
;
762 for (i
= 0; i
< st
->used
; i
++) {
763 size_t ndx
= st
->sorted
[i
];
765 buffer_append_string_buffer(b
, st
->data
[ndx
]->key
);
766 buffer_append_string_len(b
, CONST_STR_LEN(": "));
767 buffer_append_int(b
, ((data_integer
*)(st
->data
[ndx
]))->value
);
768 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
771 chunkqueue_append_buffer(con
->write_queue
, b
);
774 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
776 con
->http_status
= 200;
777 con
->file_finished
= 1;
779 return HANDLER_FINISHED
;
783 static handler_t
mod_status_handle_server_status(server
*srv
, connection
*con
, void *p_d
) {
785 if (buffer_is_equal_string(con
->uri
.query
, CONST_STR_LEN("auto"))) {
786 mod_status_handle_server_status_text(srv
, con
, p_d
);
787 } else if (buffer_string_length(con
->uri
.query
) >= sizeof("json")-1
788 && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("json"))) {
789 mod_status_handle_server_status_json(srv
, con
, p_d
);
791 mod_status_handle_server_status_html(srv
, con
, p_d
);
794 con
->http_status
= 200;
795 con
->file_finished
= 1;
797 return HANDLER_FINISHED
;
801 static handler_t
mod_status_handle_server_config(server
*srv
, connection
*con
, void *p_d
) {
802 plugin_data
*p
= p_d
;
803 buffer
*b
= buffer_init();
804 buffer
*m
= p
->module_list
;
807 struct ev_map
{ fdevent_handler_t et
; const char *name
; } event_handlers
[] =
809 /* - epoll is most reliable
810 * - select works everywhere
812 #ifdef USE_LINUX_EPOLL
813 { FDEVENT_HANDLER_LINUX_SYSEPOLL
, "linux-sysepoll" },
816 { FDEVENT_HANDLER_POLL
, "poll" },
819 { FDEVENT_HANDLER_SELECT
, "select" },
822 { FDEVENT_HANDLER_LIBEV
, "libev" },
824 #ifdef USE_SOLARIS_DEVPOLL
825 { FDEVENT_HANDLER_SOLARIS_DEVPOLL
,"solaris-devpoll" },
827 #ifdef USE_SOLARIS_PORT
828 { FDEVENT_HANDLER_SOLARIS_PORT
, "solaris-eventports" },
830 #ifdef USE_FREEBSD_KQUEUE
831 { FDEVENT_HANDLER_FREEBSD_KQUEUE
, "freebsd-kqueue" },
833 { FDEVENT_HANDLER_UNSET
, NULL
}
836 buffer_copy_string_len(b
, CONST_STR_LEN(
837 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
838 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
839 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
840 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
842 " <title>Status</title>\n"
846 buffer_append_string_buffer(b
, con
->conf
.server_tag
);
847 buffer_append_string_len(b
, CONST_STR_LEN(
849 " <table summary=\"status\" border=\"1\">\n"));
851 mod_status_header_append(b
, "Server-Features");
853 mod_status_row_append(b
, "RegEx Conditionals", "enabled");
855 mod_status_row_append(b
, "RegEx Conditionals", "disabled - pcre missing");
857 mod_status_header_append(b
, "Network Engine");
859 for (i
= 0; event_handlers
[i
].name
; i
++) {
860 if (event_handlers
[i
].et
== srv
->event_handler
) {
861 mod_status_row_append(b
, "fd-Event-Handler", event_handlers
[i
].name
);
866 mod_status_header_append(b
, "Config-File-Settings");
868 for (i
= 0; i
< srv
->plugins
.used
; i
++) {
869 plugin
**ps
= srv
->plugins
.ptr
;
874 buffer_copy_buffer(m
, pl
->name
);
876 buffer_append_string_len(m
, CONST_STR_LEN("<br />"));
877 buffer_append_string_buffer(m
, pl
->name
);
881 mod_status_row_append(b
, "Loaded Modules", m
->ptr
);
883 buffer_append_string_len(b
, CONST_STR_LEN(" </table>\n"));
885 buffer_append_string_len(b
, CONST_STR_LEN(
890 chunkqueue_append_buffer(con
->write_queue
, b
);
893 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
895 con
->http_status
= 200;
896 con
->file_finished
= 1;
898 return HANDLER_FINISHED
;
903 static int mod_status_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
905 plugin_config
*s
= p
->config_storage
[0];
910 PATCH(statistics_url
);
912 /* skip the first, the global context */
913 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
914 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
915 s
= p
->config_storage
[i
];
917 /* condition didn't match */
918 if (!config_check_cond(srv
, con
, dc
)) continue;
921 for (j
= 0; j
< dc
->value
->used
; j
++) {
922 data_unset
*du
= dc
->value
->data
[j
];
924 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.status-url"))) {
926 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.config-url"))) {
928 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.enable-sort"))) {
930 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.statistics-url"))) {
931 PATCH(statistics_url
);
939 static handler_t
mod_status_handler(server
*srv
, connection
*con
, void *p_d
) {
940 plugin_data
*p
= p_d
;
942 if (con
->mode
!= DIRECT
) return HANDLER_GO_ON
;
944 mod_status_patch_connection(srv
, con
, p
);
946 if (!buffer_string_is_empty(p
->conf
.status_url
) &&
947 buffer_is_equal(p
->conf
.status_url
, con
->uri
.path
)) {
948 return mod_status_handle_server_status(srv
, con
, p_d
);
949 } else if (!buffer_string_is_empty(p
->conf
.config_url
) &&
950 buffer_is_equal(p
->conf
.config_url
, con
->uri
.path
)) {
951 return mod_status_handle_server_config(srv
, con
, p_d
);
952 } else if (!buffer_string_is_empty(p
->conf
.statistics_url
) &&
953 buffer_is_equal(p
->conf
.statistics_url
, con
->uri
.path
)) {
954 return mod_status_handle_server_statistics(srv
, con
, p_d
);
957 return HANDLER_GO_ON
;
960 TRIGGER_FUNC(mod_status_trigger
) {
961 plugin_data
*p
= p_d
;
964 /* check all connections */
965 for (i
= 0; i
< srv
->conns
->used
; i
++) {
966 connection
*c
= srv
->conns
->ptr
[i
];
968 p
->bytes_written
+= c
->bytes_written_cur_second
;
971 /* a sliding average */
972 p
->mod_5s_traffic_out
[p
->mod_5s_ndx
] = p
->bytes_written
;
973 p
->mod_5s_requests
[p
->mod_5s_ndx
] = p
->requests
;
975 p
->mod_5s_ndx
= (p
->mod_5s_ndx
+1) % 5;
977 p
->abs_traffic_out
+= p
->bytes_written
;
978 p
->rel_traffic_out
+= p
->bytes_written
;
980 p
->bytes_written
= 0;
982 /* reset storage - second */
986 return HANDLER_GO_ON
;
989 REQUESTDONE_FUNC(mod_status_account
) {
990 plugin_data
*p
= p_d
;
998 p
->bytes_written
+= con
->bytes_written_cur_second
;
1000 return HANDLER_GO_ON
;
1003 int mod_status_plugin_init(plugin
*p
);
1004 int mod_status_plugin_init(plugin
*p
) {
1005 p
->version
= LIGHTTPD_VERSION_ID
;
1006 p
->name
= buffer_init_string("status");
1008 p
->init
= mod_status_init
;
1009 p
->cleanup
= mod_status_free
;
1010 p
->set_defaults
= mod_status_set_defaults
;
1012 p
->handle_uri_clean
= mod_status_handler
;
1013 p
->handle_trigger
= mod_status_trigger
;
1014 p
->handle_request_done
= mod_status_account
;