4 #include "connections.h"
6 #include "connections.h"
11 #include "inet_ntop_cache.h"
13 #include <sys/types.h>
28 buffer
*statistics_url
;
39 double mod_5s_traffic_out
[5];
40 double mod_5s_requests
[5];
43 double rel_traffic_out
;
46 double abs_traffic_out
;
53 plugin_config
**config_storage
;
58 INIT_FUNC(mod_status_init
) {
62 p
= calloc(1, sizeof(*p
));
64 p
->traffic_out
= p
->requests
= 0;
65 p
->rel_traffic_out
= p
->rel_requests
= 0;
66 p
->abs_traffic_out
= p
->abs_requests
= 0;
68 p
->module_list
= buffer_init();
70 for (i
= 0; i
< 5; i
++) {
71 p
->mod_5s_traffic_out
[i
] = p
->mod_5s_requests
[i
] = 0;
77 FREE_FUNC(mod_status_free
) {
82 if (!p
) return HANDLER_GO_ON
;
84 buffer_free(p
->module_list
);
86 if (p
->config_storage
) {
88 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
89 plugin_config
*s
= p
->config_storage
[i
];
91 buffer_free(s
->status_url
);
92 buffer_free(s
->statistics_url
);
93 buffer_free(s
->config_url
);
97 free(p
->config_storage
);
103 return HANDLER_GO_ON
;
106 SETDEFAULTS_FUNC(mod_status_set_defaults
) {
107 plugin_data
*p
= p_d
;
110 config_values_t cv
[] = {
111 { "status.status-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
112 { "status.config-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
113 { "status.enable-sort", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
},
114 { "status.statistics-url", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
115 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
118 if (!p
) return HANDLER_ERROR
;
120 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
122 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
123 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
126 s
= calloc(1, sizeof(plugin_config
));
127 s
->config_url
= buffer_init();
128 s
->status_url
= buffer_init();
130 s
->statistics_url
= buffer_init();
132 cv
[0].destination
= s
->status_url
;
133 cv
[1].destination
= s
->config_url
;
134 cv
[2].destination
= &(s
->sort
);
135 cv
[3].destination
= s
->statistics_url
;
137 p
->config_storage
[i
] = s
;
139 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
140 return HANDLER_ERROR
;
144 return HANDLER_GO_ON
;
149 static int mod_status_row_append(buffer
*b
, const char *key
, const char *value
) {
150 buffer_append_string_len(b
, CONST_STR_LEN(" <tr>\n"));
151 buffer_append_string_len(b
, CONST_STR_LEN(" <td><b>"));
152 buffer_append_string(b
, key
);
153 buffer_append_string_len(b
, CONST_STR_LEN("</b></td>\n"));
154 buffer_append_string_len(b
, CONST_STR_LEN(" <td>"));
155 buffer_append_string(b
, value
);
156 buffer_append_string_len(b
, CONST_STR_LEN("</td>\n"));
157 buffer_append_string_len(b
, CONST_STR_LEN(" </tr>\n"));
162 static int mod_status_header_append(buffer
*b
, const char *key
) {
163 buffer_append_string_len(b
, CONST_STR_LEN(" <tr>\n"));
164 buffer_append_string_len(b
, CONST_STR_LEN(" <th colspan=\"2\">"));
165 buffer_append_string(b
, key
);
166 buffer_append_string_len(b
, CONST_STR_LEN("</th>\n"));
167 buffer_append_string_len(b
, CONST_STR_LEN(" </tr>\n"));
172 static int mod_status_header_append_sort(buffer
*b
, void *p_d
, const char* key
) {
173 plugin_data
*p
= p_d
;
176 buffer_append_string_len(b
, CONST_STR_LEN("<th class=\"status\"><a href=\"#\" class=\"sortheader\" onclick=\"resort(this);return false;\">"));
177 buffer_append_string(b
, key
);
178 buffer_append_string_len(b
, CONST_STR_LEN("<span class=\"sortarrow\">:</span></a></th>\n"));
180 buffer_append_string_len(b
, CONST_STR_LEN("<th class=\"status\">"));
181 buffer_append_string(b
, key
);
182 buffer_append_string_len(b
, CONST_STR_LEN("</th>\n"));
188 static int mod_status_get_multiplier(double *avg
, char *multiplier
, int size
) {
191 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'k'; }
192 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'M'; }
193 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'G'; }
194 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'T'; }
195 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'P'; }
196 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'E'; }
197 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'Z'; }
198 if (*avg
> size
) { *avg
/= size
; *multiplier
= 'Y'; }
203 static handler_t
mod_status_handle_server_status_html(server
*srv
, connection
*con
, void *p_d
) {
204 plugin_data
*p
= p_d
;
205 buffer
*b
= buffer_init();
208 char multiplier
= '\0';
212 int days
, hours
, mins
, seconds
;
214 /*(CON_STATE_CLOSE must be last state in enum connection_state_t)*/
215 int cstates
[CON_STATE_CLOSE
+3];
216 memset(cstates
, 0, sizeof(cstates
));
218 buffer_copy_string_len(b
, CONST_STR_LEN(
219 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
220 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
221 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
222 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
224 " <title>Status</title>\n"
226 " <style type=\"text/css\">\n"
227 " table.status { border: black solid thin; }\n"
228 " td { white-space: nowrap; }\n"
229 " td.int { background-color: #f0f0f0; text-align: right }\n"
230 " td.string { background-color: #f0f0f0; text-align: left }\n"
231 " th.status { background-color: black; color: white; font-weight: bold; }\n"
232 " a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n"
233 " span.sortarrow { color: white; text-decoration: none; }\n"
236 if (!buffer_string_is_empty(con
->uri
.query
) && 0 == memcmp(con
->uri
.query
->ptr
, CONST_STR_LEN("refresh="))) {
237 /* Note: Refresh is an historical, but non-standard HTTP header
238 * References (meta http-equiv="refresh" use is deprecated):
239 * https://www.w3.org/TR/WCAG10-HTML-TECHS/#meta-element
240 * https://www.w3.org/TR/WCAG10-CORE-TECHS/#auto-page-refresh
241 * https://www.w3.org/QA/Tips/reback
243 const long refresh
= strtol(con
->uri
.query
->ptr
+sizeof("refresh=")-1, NULL
, 10);
245 buffer_append_string_len(b
, CONST_STR_LEN("<meta http-equiv=\"refresh\" content=\""));
246 buffer_append_int(b
, refresh
< 604800 ? refresh
: 604800);
247 buffer_append_string_len(b
, CONST_STR_LEN("\">\n"));
252 buffer_append_string_len(b
, CONST_STR_LEN(
253 "<script type=\"text/javascript\">\n"
256 "var prev_span = null;\n"
258 "function get_inner_text(el) {\n"
259 " if((typeof el == 'string')||(typeof el == 'undefined'))\n"
261 " if(el.innerText)\n"
262 " return el.innerText;\n"
265 " var cs = el.childNodes;\n"
266 " var l = cs.length;\n"
267 " for (i=0;i<l;i++) {\n"
268 " if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n"
269 " else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n"
275 "function sortfn(a,b) {\n"
276 " var at = get_inner_text(a.cells[sort_column]);\n"
277 " var bt = get_inner_text(b.cells[sort_column]);\n"
278 " if (a.cells[sort_column].className == 'int') {\n"
279 " return parseInt(at)-parseInt(bt);\n"
281 " aa = at.toLowerCase();\n"
282 " bb = bt.toLowerCase();\n"
283 " if (aa==bb) return 0;\n"
284 " else if (aa<bb) return -1;\n"
289 "function resort(lnk) {\n"
290 " var span = lnk.childNodes[1];\n"
291 " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n"
292 " var rows = new Array();\n"
293 " for (j=1;j<table.rows.length;j++)\n"
294 " rows[j-1] = table.rows[j];\n"
295 " sort_column = lnk.parentNode.cellIndex;\n"
296 " rows.sort(sortfn);\n"
298 " if (prev_span != null) prev_span.innerHTML = '';\n"
299 " if (span.getAttribute('sortdir')=='down') {\n"
300 " span.innerHTML = '↑';\n"
301 " span.setAttribute('sortdir','up');\n"
304 " span.innerHTML = '↓';\n"
305 " span.setAttribute('sortdir','down');\n"
307 " for (i=0;i<rows.length;i++)\n"
308 " table.tBodies[0].appendChild(rows[i]);\n"
309 " prev_span = span;\n"
315 buffer_append_string_len(b
, CONST_STR_LEN(
321 /* connection listing */
322 buffer_append_string_len(b
, CONST_STR_LEN("<h1>Server-Status (" PACKAGE_NAME
" " PACKAGE_VERSION
")</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"));
658 static handler_t
mod_status_handle_server_statistics(server
*srv
, connection
*con
, void *p_d
) {
661 array
*st
= srv
->status
;
665 /* we have nothing to send */
666 con
->http_status
= 204;
667 con
->file_finished
= 1;
669 return HANDLER_FINISHED
;
673 for (i
= 0; i
< st
->used
; i
++) {
674 size_t ndx
= st
->sorted
[i
];
676 buffer_append_string_buffer(b
, st
->data
[ndx
]->key
);
677 buffer_append_string_len(b
, CONST_STR_LEN(": "));
678 buffer_append_int(b
, ((data_integer
*)(st
->data
[ndx
]))->value
);
679 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
682 chunkqueue_append_buffer(con
->write_queue
, b
);
685 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
687 con
->http_status
= 200;
688 con
->file_finished
= 1;
690 return HANDLER_FINISHED
;
694 static handler_t
mod_status_handle_server_status(server
*srv
, connection
*con
, void *p_d
) {
696 if (buffer_is_equal_string(con
->uri
.query
, CONST_STR_LEN("auto"))) {
697 mod_status_handle_server_status_text(srv
, con
, p_d
);
699 mod_status_handle_server_status_html(srv
, con
, p_d
);
702 con
->http_status
= 200;
703 con
->file_finished
= 1;
705 return HANDLER_FINISHED
;
709 static handler_t
mod_status_handle_server_config(server
*srv
, connection
*con
, void *p_d
) {
710 plugin_data
*p
= p_d
;
711 buffer
*b
= buffer_init();
712 buffer
*m
= p
->module_list
;
715 struct ev_map
{ fdevent_handler_t et
; const char *name
; } event_handlers
[] =
717 /* - epoll is most reliable
718 * - select works everywhere
720 #ifdef USE_LINUX_EPOLL
721 { FDEVENT_HANDLER_LINUX_SYSEPOLL
, "linux-sysepoll" },
724 { FDEVENT_HANDLER_POLL
, "poll" },
727 { FDEVENT_HANDLER_SELECT
, "select" },
730 { FDEVENT_HANDLER_LIBEV
, "libev" },
732 #ifdef USE_SOLARIS_DEVPOLL
733 { FDEVENT_HANDLER_SOLARIS_DEVPOLL
,"solaris-devpoll" },
735 #ifdef USE_SOLARIS_PORT
736 { FDEVENT_HANDLER_SOLARIS_PORT
, "solaris-eventports" },
738 #ifdef USE_FREEBSD_KQUEUE
739 { FDEVENT_HANDLER_FREEBSD_KQUEUE
, "freebsd-kqueue" },
741 { FDEVENT_HANDLER_UNSET
, NULL
}
744 buffer_copy_string_len(b
, CONST_STR_LEN(
745 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
746 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
747 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
748 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
750 " <title>Status</title>\n"
753 " <h1>" PACKAGE_DESC
"</h1>\n"
754 " <table summary=\"status\" border=\"1\">\n"));
756 mod_status_header_append(b
, "Server-Features");
758 mod_status_row_append(b
, "RegEx Conditionals", "enabled");
760 mod_status_row_append(b
, "RegEx Conditionals", "disabled - pcre missing");
762 mod_status_header_append(b
, "Network Engine");
764 for (i
= 0; event_handlers
[i
].name
; i
++) {
765 if (event_handlers
[i
].et
== srv
->event_handler
) {
766 mod_status_row_append(b
, "fd-Event-Handler", event_handlers
[i
].name
);
771 mod_status_header_append(b
, "Config-File-Settings");
773 for (i
= 0; i
< srv
->plugins
.used
; i
++) {
774 plugin
**ps
= srv
->plugins
.ptr
;
779 buffer_copy_buffer(m
, pl
->name
);
781 buffer_append_string_len(m
, CONST_STR_LEN("<br />"));
782 buffer_append_string_buffer(m
, pl
->name
);
786 mod_status_row_append(b
, "Loaded Modules", m
->ptr
);
788 buffer_append_string_len(b
, CONST_STR_LEN(" </table>\n"));
790 buffer_append_string_len(b
, CONST_STR_LEN(
795 chunkqueue_append_buffer(con
->write_queue
, b
);
798 response_header_overwrite(srv
, con
, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
800 con
->http_status
= 200;
801 con
->file_finished
= 1;
803 return HANDLER_FINISHED
;
808 static int mod_status_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
810 plugin_config
*s
= p
->config_storage
[0];
815 PATCH(statistics_url
);
817 /* skip the first, the global context */
818 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
819 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
820 s
= p
->config_storage
[i
];
822 /* condition didn't match */
823 if (!config_check_cond(srv
, con
, dc
)) continue;
826 for (j
= 0; j
< dc
->value
->used
; j
++) {
827 data_unset
*du
= dc
->value
->data
[j
];
829 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.status-url"))) {
831 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.config-url"))) {
833 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.enable-sort"))) {
835 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("status.statistics-url"))) {
836 PATCH(statistics_url
);
844 static handler_t
mod_status_handler(server
*srv
, connection
*con
, void *p_d
) {
845 plugin_data
*p
= p_d
;
847 if (con
->mode
!= DIRECT
) return HANDLER_GO_ON
;
849 mod_status_patch_connection(srv
, con
, p
);
851 if (!buffer_string_is_empty(p
->conf
.status_url
) &&
852 buffer_is_equal(p
->conf
.status_url
, con
->uri
.path
)) {
853 return mod_status_handle_server_status(srv
, con
, p_d
);
854 } else if (!buffer_string_is_empty(p
->conf
.config_url
) &&
855 buffer_is_equal(p
->conf
.config_url
, con
->uri
.path
)) {
856 return mod_status_handle_server_config(srv
, con
, p_d
);
857 } else if (!buffer_string_is_empty(p
->conf
.statistics_url
) &&
858 buffer_is_equal(p
->conf
.statistics_url
, con
->uri
.path
)) {
859 return mod_status_handle_server_statistics(srv
, con
, p_d
);
862 return HANDLER_GO_ON
;
865 TRIGGER_FUNC(mod_status_trigger
) {
866 plugin_data
*p
= p_d
;
869 /* check all connections */
870 for (i
= 0; i
< srv
->conns
->used
; i
++) {
871 connection
*c
= srv
->conns
->ptr
[i
];
873 p
->bytes_written
+= c
->bytes_written_cur_second
;
876 /* a sliding average */
877 p
->mod_5s_traffic_out
[p
->mod_5s_ndx
] = p
->bytes_written
;
878 p
->mod_5s_requests
[p
->mod_5s_ndx
] = p
->requests
;
880 p
->mod_5s_ndx
= (p
->mod_5s_ndx
+1) % 5;
882 p
->abs_traffic_out
+= p
->bytes_written
;
883 p
->rel_traffic_out
+= p
->bytes_written
;
885 p
->bytes_written
= 0;
887 /* reset storage - second */
891 return HANDLER_GO_ON
;
894 REQUESTDONE_FUNC(mod_status_account
) {
895 plugin_data
*p
= p_d
;
903 p
->bytes_written
+= con
->bytes_written_cur_second
;
905 return HANDLER_GO_ON
;
908 int mod_status_plugin_init(plugin
*p
);
909 int mod_status_plugin_init(plugin
*p
) {
910 p
->version
= LIGHTTPD_VERSION_ID
;
911 p
->name
= buffer_init_string("status");
913 p
->init
= mod_status_init
;
914 p
->cleanup
= mod_status_free
;
915 p
->set_defaults
= mod_status_set_defaults
;
917 p
->handle_uri_clean
= mod_status_handler
;
918 p
->handle_trigger
= mod_status_trigger
;
919 p
->handle_request_done
= mod_status_account
;