1: #include "first.h"
2:
3: #include "server.h"
4: #include "connections.h"
5: #include "response.h"
6: #include "connections.h"
7: #include "log.h"
8:
9: #include "plugin.h"
10:
11: #include "inet_ntop_cache.h"
12:
13: #include <sys/types.h>
14:
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>
22:
23: typedef struct {
24: buffer *config_url;
25: buffer *status_url;
26: buffer *statistics_url;
27:
28: int sort;
29: } plugin_config;
30:
31: typedef struct {
32: PLUGIN_DATA;
33:
34: double traffic_out;
35: double requests;
36:
37: double mod_5s_traffic_out[5];
38: double mod_5s_requests[5];
39: size_t mod_5s_ndx;
40:
41: double rel_traffic_out;
42: double rel_requests;
43:
44: double abs_traffic_out;
45: double abs_requests;
46:
47: double bytes_written;
48:
49: buffer *module_list;
50:
51: plugin_config **config_storage;
52:
53: plugin_config conf;
54: } plugin_data;
55:
56: INIT_FUNC(mod_status_init) {
57: plugin_data *p;
58: size_t i;
59:
60: p = calloc(1, sizeof(*p));
61:
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();
67:
68: for (i = 0; i < 5; i++) {
69: p->mod_5s_traffic_out[i] = p->mod_5s_requests[i] = 0;
70: }
71:
72: return p;
73: }
74:
75: FREE_FUNC(mod_status_free) {
76: plugin_data *p = p_d;
77:
78: UNUSED(srv);
79:
80: if (!p) return HANDLER_GO_ON;
81:
82: buffer_free(p->module_list);
83:
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];
88:
89: buffer_free(s->status_url);
90: buffer_free(s->statistics_url);
91: buffer_free(s->config_url);
92:
93: free(s);
94: }
95: free(p->config_storage);
96: }
97:
98:
99: free(p);
100:
101: return HANDLER_GO_ON;
102: }
103:
104: SETDEFAULTS_FUNC(mod_status_set_defaults) {
105: plugin_data *p = p_d;
106: size_t i;
107:
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 }
114: };
115:
116: if (!p) return HANDLER_ERROR;
117:
118: p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
119:
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;
123:
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();
129:
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;
134:
135: p->config_storage[i] = s;
136:
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;
139: }
140: }
141:
142: return HANDLER_GO_ON;
143: }
144:
145:
146:
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"));
156:
157: return 0;
158: }
159:
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"));
166:
167: return 0;
168: }
169:
170: static int mod_status_header_append_sort(buffer *b, void *p_d, const char* key) {
171: plugin_data *p = p_d;
172:
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"));
181: }
182:
183: return 0;
184: }
185:
186: static int mod_status_get_multiplier(double *avg, char *multiplier, int size) {
187: *multiplier = ' ';
188:
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'; }
197:
198: return 0;
199: }
200:
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;
209:
210: int days, hours, mins, seconds;
211:
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));
215:
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"
223:
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"));
233:
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
240: */
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"));
246: }
247: }
248:
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"
255:
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"
272:
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"
286:
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"
295:
296: " if (prev_span != null) prev_span.innerHTML = '';\n"
297: " if (span.getAttribute('sortdir')=='down') {\n"
298: " span.innerHTML = '↑';\n"
299: " span.setAttribute('sortdir','up');\n"
300: " rows.reverse();\n"
301: " } else {\n"
302: " span.innerHTML = '↓';\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"));
311: }
312:
313: buffer_append_string_len(b, CONST_STR_LEN(
314: " </head>\n"
315: " <body>\n"));
316:
317:
318:
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>"));
323:
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\">"));
331:
332: ts = srv->cur_ts - srv->startup_ts;
333:
334: days = ts / (60 * 60 * 24);
335: ts %= (60 * 60 * 24);
336:
337: hours = ts / (60 * 60);
338: ts %= (60 * 60);
339:
340: mins = ts / (60);
341: ts %= (60);
342:
343: seconds = ts;
344:
345: if (days) {
346: buffer_append_int(b, days);
347: buffer_append_string_len(b, CONST_STR_LEN(" days "));
348: }
349:
350: if (hours) {
351: buffer_append_int(b, hours);
352: buffer_append_string_len(b, CONST_STR_LEN(" hours "));
353: }
354:
355: if (mins) {
356: buffer_append_int(b, mins);
357: buffer_append_string_len(b, CONST_STR_LEN(" min "));
358: }
359:
360: buffer_append_int(b, seconds);
361: buffer_append_string_len(b, CONST_STR_LEN(" s"));
362:
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\">"));
365:
366: ts = srv->startup_ts;
367:
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"));
371:
372:
373: buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">absolute (since start)</th></tr>\n"));
374:
375: buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
376: avg = p->abs_requests;
377:
378: mod_status_get_multiplier(&avg, &multiplier, 1000);
379:
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"));
384:
385: buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
386: avg = p->abs_traffic_out;
387:
388: mod_status_get_multiplier(&avg, &multiplier, 1024);
389:
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"));
395:
396:
397:
398: buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (since start)</th></tr>\n"));
399:
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);
402:
403: mod_status_get_multiplier(&avg, &multiplier, 1000);
404:
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"));
409:
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);
412:
413: mod_status_get_multiplier(&avg, &multiplier, 1024);
414:
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"));
420:
421:
422:
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];
426: }
427:
428: avg /= 5;
429:
430: buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
431:
432: mod_status_get_multiplier(&avg, &multiplier, 1000);
433:
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);
437:
438: buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n"));
439:
440: for (j = 0, avg = 0; j < 5; j++) {
441: avg += p->mod_5s_traffic_out[j];
442: }
443:
444: avg /= 5;
445:
446: buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
447:
448: mod_status_get_multiplier(&avg, &multiplier, 1024);
449:
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"));
455:
456: buffer_append_string_len(b, CONST_STR_LEN("</table>\n"));
457:
458: buffer_append_string_len(b, CONST_STR_LEN("<hr />\n<pre>\n"));
459:
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"));
463:
464: for (j = 0; j < srv->conns->used; j++) {
465: connection *c = srv->conns->ptr[j];
466: const char *state;
467:
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)];
474: }
475:
476: buffer_append_string_len(b, state, 1);
477:
478: if (((j + 1) % 50) == 0) {
479: buffer_append_string_len(b, CONST_STR_LEN("\n"));
480: }
481: }
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"));
496: }
497: buffer_append_string_len(b, CONST_STR_LEN("</table>"));
498:
499: buffer_append_string_len(b, CONST_STR_LEN("\n</pre><hr />\n<h2>Connections</h2>\n"));
500:
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"));
512:
513: for (j = 0; j < srv->conns->used; j++) {
514: connection *c = srv->conns->ptr[j];
515:
516: buffer_append_string_len(b, CONST_STR_LEN("<tr><td class=\"string\">"));
517:
518: buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(c->dst_addr)));
519:
520: buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
521:
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"));
528: }
529:
530: buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
531:
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));
535:
536: buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
537:
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));
542: }
543:
544: buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
545:
546: buffer_append_int(b, srv->cur_ts - c->request_start);
547:
548: buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
549:
550: if (buffer_string_is_empty(c->server_name)) {
551: buffer_append_string_buffer(b, c->uri.authority);
552: }
553: else {
554: buffer_append_string_buffer(b, c->server_name);
555: }
556:
557: buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
558:
559: if (!buffer_string_is_empty(c->uri.path)) {
560: buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML);
561: }
562:
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);
566: }
567:
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(")"));
572: }
573: buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
574:
575: buffer_append_string_buffer(b, c->physical.path);
576:
577: buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));
578: }
579:
580:
581: buffer_append_string_len(b, CONST_STR_LEN(
582: "</table>\n"));
583:
584:
585: buffer_append_string_len(b, CONST_STR_LEN(
586: " </body>\n"
587: "</html>\n"
588: ));
589:
590: chunkqueue_append_buffer(con->write_queue, b);
591: buffer_free(b);
592:
593: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
594:
595: return 0;
596: }
597:
598:
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;
607:
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"));
614:
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"));
621:
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"));
627:
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"));
632:
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"));
636:
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);
646: }
647: for (l = 0; l < srv->conns->size - srv->conns->used; l++) {
648: buffer_append_string_len(b, CONST_STR_LEN("_"));
649: }
650: buffer_append_string_len(b, CONST_STR_LEN("\n"));
651:
652: chunkqueue_append_buffer(con->write_queue, b);
653: buffer_free(b);
654:
655: /* set text/plain output */
656: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
657:
658: return 0;
659: }
660:
661:
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;
670:
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;
683: }
684: }
685:
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"));
692:
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"));
699:
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"));
705:
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"));
710:
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"));
714:
715: for (j = 0, avg = 0; j < 5; j++) {
716: avg += p->mod_5s_requests[j];
717: }
718:
719: avg /= 5;
720:
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"));
724:
725: for (j = 0, avg = 0; j < 5; j++) {
726: avg += p->mod_5s_traffic_out[j];
727: }
728:
729: avg /= 5;
730:
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}"));
734:
735: if (jsonp) buffer_append_string_len(b, CONST_STR_LEN(");"));
736:
737: chunkqueue_append_buffer(con->write_queue, b);
738: buffer_free(b);
739:
740: /* set text/plain output */
741: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/javascript"));
742:
743: return 0;
744: }
745:
746:
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);
752:
753: if (0 == st->used) {
754: /* we have nothing to send */
755: con->http_status = 204;
756: con->file_finished = 1;
757:
758: return HANDLER_FINISHED;
759: }
760:
761: b = buffer_init();
762: for (i = 0; i < st->used; i++) {
763: size_t ndx = st->sorted[i];
764:
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"));
769: }
770:
771: chunkqueue_append_buffer(con->write_queue, b);
772: buffer_free(b);
773:
774: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
775:
776: con->http_status = 200;
777: con->file_finished = 1;
778:
779: return HANDLER_FINISHED;
780: }
781:
782:
783: static handler_t mod_status_handle_server_status(server *srv, connection *con, void *p_d) {
784:
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);
792: }
793:
794: con->http_status = 200;
795: con->file_finished = 1;
796:
797: return HANDLER_FINISHED;
798: }
799:
800:
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;
806:
807: struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] =
808: {
809: /* - epoll is most reliable
810: * - select works everywhere
811: */
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 }
834: };
835:
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"));
850:
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");
858:
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;
863: }
864: }
865:
866: mod_status_header_append(b, "Config-File-Settings");
867:
868: for (i = 0; i < srv->plugins.used; i++) {
869: plugin **ps = srv->plugins.ptr;
870:
871: plugin *pl = ps[i];
872:
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);
878: }
879: }
880:
881: mod_status_row_append(b, "Loaded Modules", m->ptr);
882:
883: buffer_append_string_len(b, CONST_STR_LEN(" </table>\n"));
884:
885: buffer_append_string_len(b, CONST_STR_LEN(
886: " </body>\n"
887: "</html>\n"
888: ));
889:
890: chunkqueue_append_buffer(con->write_queue, b);
891: buffer_free(b);
892:
893: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
894:
895: con->http_status = 200;
896: con->file_finished = 1;
897:
898: return HANDLER_FINISHED;
899: }
900:
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];
906:
907: PATCH(status_url);
908: PATCH(config_url);
909: PATCH(sort);
910: PATCH(statistics_url);
911:
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];
916:
917: /* condition didn't match */
918: if (!config_check_cond(srv, con, dc)) continue;
919:
920: /* merge config */
921: for (j = 0; j < dc->value->used; j++) {
922: data_unset *du = dc->value->data[j];
923:
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);
932: }
933: }
934: }
935:
936: return 0;
937: }
938:
939: static handler_t mod_status_handler(server *srv, connection *con, void *p_d) {
940: plugin_data *p = p_d;
941:
942: if (con->mode != DIRECT) return HANDLER_GO_ON;
943:
944: mod_status_patch_connection(srv, con, p);
945:
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);
955: }
956:
957: return HANDLER_GO_ON;
958: }
959:
960: TRIGGER_FUNC(mod_status_trigger) {
961: plugin_data *p = p_d;
962: size_t i;
963:
964: /* check all connections */
965: for (i = 0; i < srv->conns->used; i++) {
966: connection *c = srv->conns->ptr[i];
967:
968: p->bytes_written += c->bytes_written_cur_second;
969: }
970:
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;
974:
975: p->mod_5s_ndx = (p->mod_5s_ndx+1) % 5;
976:
977: p->abs_traffic_out += p->bytes_written;
978: p->rel_traffic_out += p->bytes_written;
979:
980: p->bytes_written = 0;
981:
982: /* reset storage - second */
983: p->traffic_out = 0;
984: p->requests = 0;
985:
986: return HANDLER_GO_ON;
987: }
988:
989: REQUESTDONE_FUNC(mod_status_account) {
990: plugin_data *p = p_d;
991:
992: UNUSED(srv);
993:
994: p->requests++;
995: p->rel_requests++;
996: p->abs_requests++;
997:
998: p->bytes_written += con->bytes_written_cur_second;
999:
1000: return HANDLER_GO_ON;
1001: }
1002:
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");
1007:
1008: p->init = mod_status_init;
1009: p->cleanup = mod_status_free;
1010: p->set_defaults= mod_status_set_defaults;
1011:
1012: p->handle_uri_clean = mod_status_handler;
1013: p->handle_trigger = mod_status_trigger;
1014: p->handle_request_done = mod_status_account;
1015:
1016: p->data = NULL;
1017:
1018: return 0;
1019: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>