do not set REDIRECT_URI in mod_magnet, mod_rewrite (#2738)
[lighttpd.git] / src / mod_status.c
blob647f4037155dd9788c2f05f9a2d04108f8c1247e
1 #include "first.h"
3 #include "server.h"
4 #include "connections.h"
5 #include "response.h"
6 #include "connections.h"
7 #include "log.h"
9 #include "plugin.h"
11 #include "inet_ntop_cache.h"
13 #include <sys/types.h>
15 #include <fcntl.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <time.h>
21 #include <stdio.h>
23 typedef struct {
24 buffer *config_url;
25 buffer *status_url;
26 buffer *statistics_url;
28 int sort;
29 } plugin_config;
31 typedef struct {
32 PLUGIN_DATA;
34 double traffic_out;
35 double requests;
37 double mod_5s_traffic_out[5];
38 double mod_5s_requests[5];
39 size_t mod_5s_ndx;
41 double rel_traffic_out;
42 double rel_requests;
44 double abs_traffic_out;
45 double abs_requests;
47 double bytes_written;
49 buffer *module_list;
51 plugin_config **config_storage;
53 plugin_config conf;
54 } plugin_data;
56 INIT_FUNC(mod_status_init) {
57 plugin_data *p;
58 size_t i;
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;
65 p->bytes_written = 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;
72 return p;
75 FREE_FUNC(mod_status_free) {
76 plugin_data *p = p_d;
78 UNUSED(srv);
80 if (!p) return HANDLER_GO_ON;
82 buffer_free(p->module_list);
84 if (p->config_storage) {
85 size_t i;
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);
93 free(s);
95 free(p->config_storage);
99 free(p);
101 return HANDLER_GO_ON;
104 SETDEFAULTS_FUNC(mod_status_set_defaults) {
105 plugin_data *p = p_d;
106 size_t i;
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];
122 plugin_config *s;
124 s = calloc(1, sizeof(plugin_config));
125 s->config_url = buffer_init();
126 s->status_url = buffer_init();
127 s->sort = 1;
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"));
157 return 0;
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"));
167 return 0;
170 static int mod_status_header_append_sort(buffer *b, void *p_d, const char* key) {
171 plugin_data *p = p_d;
173 if (p->conf.sort) {
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"));
177 } else {
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"));
183 return 0;
186 static int mod_status_get_multiplier(double *avg, char *multiplier, int size) {
187 *multiplier = ' ';
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'; }
198 return 0;
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();
204 size_t j;
205 double avg;
206 char multiplier = '\0';
207 char buf[32];
208 time_t ts;
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"
221 " <head>\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"
232 " </style>\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);
242 if (refresh > 0) {
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"));
249 if (p->conf.sort) {
250 buffer_append_string_len(b, CONST_STR_LEN(
251 "<script type=\"text/javascript\">\n"
252 "// <!--\n"
253 "var sort_column;\n"
254 "var prev_span = null;\n"
256 "function get_inner_text(el) {\n"
257 " if((typeof el == 'string')||(typeof el == 'undefined'))\n"
258 " return el;\n"
259 " if(el.innerText)\n"
260 " return el.innerText;\n"
261 " else {\n"
262 " var str = \"\";\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"
268 " }\n"
269 " }\n"
270 " return str;\n"
271 "}\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"
278 " } else {\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"
283 " else return 1;\n"
284 " }\n"
285 "}\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 = '&uarr;';\n"
299 " span.setAttribute('sortdir','up');\n"
300 " rows.reverse();\n"
301 " } else {\n"
302 " span.innerHTML = '&darr;';\n"
303 " span.setAttribute('sortdir','down');\n"
304 " }\n"
305 " for (i=0;i<rows.length;i++)\n"
306 " table.tBodies[0].appendChild(rows[i]);\n"
307 " prev_span = span;\n"
308 "}\n"
309 "// -->\n"
310 "</script>\n"));
313 buffer_append_string_len(b, CONST_STR_LEN(
314 " </head>\n"
315 " <body>\n"));
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);
338 ts %= (60 * 60);
340 mins = ts / (60);
341 ts %= (60);
343 seconds = ts;
345 if (days) {
346 buffer_append_int(b, days);
347 buffer_append_string_len(b, CONST_STR_LEN(" days "));
350 if (hours) {
351 buffer_append_int(b, hours);
352 buffer_append_string_len(b, CONST_STR_LEN(" hours "));
355 if (mins) {
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];
428 avg /= 5;
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];
444 avg /= 5;
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];
466 const char *state;
468 if (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) {
469 state = "k";
470 ++cstates[CON_STATE_CLOSE+2];
471 } else {
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>&nbsp;&nbsp;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>&nbsp;&nbsp;"));
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);
526 } else {
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"));
540 } else {
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);
553 else {
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(
582 "</table>\n"));
585 buffer_append_string_len(b, CONST_STR_LEN(
586 " </body>\n"
587 "</html>\n"
590 chunkqueue_append_buffer(con->write_queue, b);
591 buffer_free(b);
593 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
595 return 0;
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();
602 double avg;
603 time_t ts;
604 char buf[32];
605 unsigned int k;
606 unsigned int l;
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"));
622 /* output uptime */
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 =
642 (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri))
643 ? "k"
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);
653 buffer_free(b);
655 /* set text/plain output */
656 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
658 return 0;
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();
665 double avg;
666 time_t ts;
667 char buf[32];
668 size_t j;
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;
677 int len = 0;
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("("));
682 jsonp = 1;
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"));
700 /* output uptime */
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];
719 avg /= 5;
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];
729 avg /= 5;
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);
738 buffer_free(b);
740 /* set text/plain output */
741 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/javascript"));
743 return 0;
747 static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) {
748 buffer *b;
749 size_t i;
750 array *st = srv->status;
751 UNUSED(p_d);
753 if (0 == st->used) {
754 /* we have nothing to send */
755 con->http_status = 204;
756 con->file_finished = 1;
758 return HANDLER_FINISHED;
761 b = buffer_init();
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);
772 buffer_free(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);
790 } else {
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;
805 size_t i;
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" },
814 #endif
815 #ifdef USE_POLL
816 { FDEVENT_HANDLER_POLL, "poll" },
817 #endif
818 #ifdef USE_SELECT
819 { FDEVENT_HANDLER_SELECT, "select" },
820 #endif
821 #ifdef USE_LIBEV
822 { FDEVENT_HANDLER_LIBEV, "libev" },
823 #endif
824 #ifdef USE_SOLARIS_DEVPOLL
825 { FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" },
826 #endif
827 #ifdef USE_SOLARIS_PORT
828 { FDEVENT_HANDLER_SOLARIS_PORT, "solaris-eventports" },
829 #endif
830 #ifdef USE_FREEBSD_KQUEUE
831 { FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" },
832 #endif
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"
841 " <head>\n"
842 " <title>Status</title>\n"
843 " </head>\n"
844 " <body>\n"
845 " <h1>"));
846 buffer_append_string_buffer(b, con->conf.server_tag);
847 buffer_append_string_len(b, CONST_STR_LEN(
848 "</h1>\n"
849 " <table summary=\"status\" border=\"1\">\n"));
851 mod_status_header_append(b, "Server-Features");
852 #ifdef HAVE_PCRE_H
853 mod_status_row_append(b, "RegEx Conditionals", "enabled");
854 #else
855 mod_status_row_append(b, "RegEx Conditionals", "disabled - pcre missing");
856 #endif
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);
862 break;
866 mod_status_header_append(b, "Config-File-Settings");
868 for (i = 0; i < srv->plugins.used; i++) {
869 plugin **ps = srv->plugins.ptr;
871 plugin *pl = ps[i];
873 if (i == 0) {
874 buffer_copy_buffer(m, pl->name);
875 } else {
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(
886 " </body>\n"
887 "</html>\n"
890 chunkqueue_append_buffer(con->write_queue, b);
891 buffer_free(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;
901 #define PATCH(x) \
902 p->conf.x = s->x;
903 static int mod_status_patch_connection(server *srv, connection *con, plugin_data *p) {
904 size_t i, j;
905 plugin_config *s = p->config_storage[0];
907 PATCH(status_url);
908 PATCH(config_url);
909 PATCH(sort);
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;
920 /* merge config */
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"))) {
925 PATCH(status_url);
926 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.config-url"))) {
927 PATCH(config_url);
928 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.enable-sort"))) {
929 PATCH(sort);
930 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.statistics-url"))) {
931 PATCH(statistics_url);
936 return 0;
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;
962 size_t i;
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 */
983 p->traffic_out = 0;
984 p->requests = 0;
986 return HANDLER_GO_ON;
989 REQUESTDONE_FUNC(mod_status_account) {
990 plugin_data *p = p_d;
992 UNUSED(srv);
994 p->requests++;
995 p->rel_requests++;
996 p->abs_requests++;
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;
1016 p->data = NULL;
1018 return 0;