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