untangle overly complex control flow logic
[lighttpd.git] / src / mod_status.c
blob031fa7343e0af8beb0f9e58fa6e2b2481b6f81b7
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 #include "version.h"
25 typedef struct {
26 buffer *config_url;
27 buffer *status_url;
28 buffer *statistics_url;
30 int sort;
31 } plugin_config;
33 typedef struct {
34 PLUGIN_DATA;
36 double traffic_out;
37 double requests;
39 double mod_5s_traffic_out[5];
40 double mod_5s_requests[5];
41 size_t mod_5s_ndx;
43 double rel_traffic_out;
44 double rel_requests;
46 double abs_traffic_out;
47 double abs_requests;
49 double bytes_written;
51 buffer *module_list;
53 plugin_config **config_storage;
55 plugin_config conf;
56 } plugin_data;
58 INIT_FUNC(mod_status_init) {
59 plugin_data *p;
60 size_t i;
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;
67 p->bytes_written = 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;
74 return p;
77 FREE_FUNC(mod_status_free) {
78 plugin_data *p = p_d;
80 UNUSED(srv);
82 if (!p) return HANDLER_GO_ON;
84 buffer_free(p->module_list);
86 if (p->config_storage) {
87 size_t i;
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);
95 free(s);
97 free(p->config_storage);
101 free(p);
103 return HANDLER_GO_ON;
106 SETDEFAULTS_FUNC(mod_status_set_defaults) {
107 plugin_data *p = p_d;
108 size_t i;
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];
124 plugin_config *s;
126 s = calloc(1, sizeof(plugin_config));
127 s->config_url = buffer_init();
128 s->status_url = buffer_init();
129 s->sort = 1;
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"));
159 return 0;
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"));
169 return 0;
172 static int mod_status_header_append_sort(buffer *b, void *p_d, const char* key) {
173 plugin_data *p = p_d;
175 if (p->conf.sort) {
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"));
179 } else {
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"));
185 return 0;
188 static int mod_status_get_multiplier(double *avg, char *multiplier, int size) {
189 *multiplier = ' ';
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'; }
200 return 0;
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();
206 size_t j;
207 double avg;
208 char multiplier = '\0';
209 char buf[32];
210 time_t ts;
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"
223 " <head>\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"
234 " </style>\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);
244 if (refresh > 0) {
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"));
251 if (p->conf.sort) {
252 buffer_append_string_len(b, CONST_STR_LEN(
253 "<script type=\"text/javascript\">\n"
254 "// <!--\n"
255 "var sort_column;\n"
256 "var prev_span = null;\n"
258 "function get_inner_text(el) {\n"
259 " if((typeof el == 'string')||(typeof el == 'undefined'))\n"
260 " return el;\n"
261 " if(el.innerText)\n"
262 " return el.innerText;\n"
263 " else {\n"
264 " var str = \"\";\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"
270 " }\n"
271 " }\n"
272 " return str;\n"
273 "}\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"
280 " } else {\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"
285 " else return 1;\n"
286 " }\n"
287 "}\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 = '&uarr;';\n"
301 " span.setAttribute('sortdir','up');\n"
302 " rows.reverse();\n"
303 " } else {\n"
304 " span.innerHTML = '&darr;';\n"
305 " span.setAttribute('sortdir','down');\n"
306 " }\n"
307 " for (i=0;i<rows.length;i++)\n"
308 " table.tBodies[0].appendChild(rows[i]);\n"
309 " prev_span = span;\n"
310 "}\n"
311 "// -->\n"
312 "</script>\n"));
315 buffer_append_string_len(b, CONST_STR_LEN(
316 " </head>\n"
317 " <body>\n"));
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);
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 = 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);
650 buffer_free(b);
652 /* set text/plain output */
653 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
655 return 0;
658 static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) {
659 buffer *b;
660 size_t i;
661 array *st = srv->status;
662 UNUSED(p_d);
664 if (0 == st->used) {
665 /* we have nothing to send */
666 con->http_status = 204;
667 con->file_finished = 1;
669 return HANDLER_FINISHED;
672 b = buffer_init();
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);
683 buffer_free(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);
698 } else {
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;
713 size_t i;
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" },
722 #endif
723 #ifdef USE_POLL
724 { FDEVENT_HANDLER_POLL, "poll" },
725 #endif
726 #ifdef USE_SELECT
727 { FDEVENT_HANDLER_SELECT, "select" },
728 #endif
729 #ifdef USE_LIBEV
730 { FDEVENT_HANDLER_LIBEV, "libev" },
731 #endif
732 #ifdef USE_SOLARIS_DEVPOLL
733 { FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" },
734 #endif
735 #ifdef USE_SOLARIS_PORT
736 { FDEVENT_HANDLER_SOLARIS_PORT, "solaris-eventports" },
737 #endif
738 #ifdef USE_FREEBSD_KQUEUE
739 { FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" },
740 #endif
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"
749 " <head>\n"
750 " <title>Status</title>\n"
751 " </head>\n"
752 " <body>\n"
753 " <h1>" PACKAGE_DESC "</h1>\n"
754 " <table summary=\"status\" border=\"1\">\n"));
756 mod_status_header_append(b, "Server-Features");
757 #ifdef HAVE_PCRE_H
758 mod_status_row_append(b, "RegEx Conditionals", "enabled");
759 #else
760 mod_status_row_append(b, "RegEx Conditionals", "disabled - pcre missing");
761 #endif
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);
767 break;
771 mod_status_header_append(b, "Config-File-Settings");
773 for (i = 0; i < srv->plugins.used; i++) {
774 plugin **ps = srv->plugins.ptr;
776 plugin *pl = ps[i];
778 if (i == 0) {
779 buffer_copy_buffer(m, pl->name);
780 } else {
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(
791 " </body>\n"
792 "</html>\n"
795 chunkqueue_append_buffer(con->write_queue, b);
796 buffer_free(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;
806 #define PATCH(x) \
807 p->conf.x = s->x;
808 static int mod_status_patch_connection(server *srv, connection *con, plugin_data *p) {
809 size_t i, j;
810 plugin_config *s = p->config_storage[0];
812 PATCH(status_url);
813 PATCH(config_url);
814 PATCH(sort);
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;
825 /* merge config */
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"))) {
830 PATCH(status_url);
831 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.config-url"))) {
832 PATCH(config_url);
833 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.enable-sort"))) {
834 PATCH(sort);
835 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.statistics-url"))) {
836 PATCH(statistics_url);
841 return 0;
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;
867 size_t i;
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 */
888 p->traffic_out = 0;
889 p->requests = 0;
891 return HANDLER_GO_ON;
894 REQUESTDONE_FUNC(mod_status_account) {
895 plugin_data *p = p_d;
897 UNUSED(srv);
899 p->requests++;
900 p->rel_requests++;
901 p->abs_requests++;
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;
921 p->data = NULL;
923 return 0;