1 #include "server.h"
2 #include "connections.h"
3 #include "response.h"
4 #include "connections.h"
5 #include "log.h"
6
7 #include "plugin.h"
8
9 #include "inet_ntop_cache.h"
10
11 #include <sys/types.h>
12
13 #include <fcntl.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <errno.h>
18 #include <time.h>
19 #include <stdio.h>
20
21 #include "version.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
INIT_FUNC(mod_status_init)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
FREE_FUNC(mod_status_free)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
SETDEFAULTS_FUNC(mod_status_set_defaults)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(specific_config *));
119
120 for (i = 0; i < srv->config_context->used; i++) {
121 plugin_config *s;
122
123 s = calloc(1, sizeof(plugin_config));
124 s->config_url = buffer_init();
125 s->status_url = buffer_init();
126 s->sort = 1;
127 s->statistics_url = buffer_init();
128
129 cv[0].destination = s->status_url;
130 cv[1].destination = s->config_url;
131 cv[2].destination = &(s->sort);
132 cv[3].destination = s->statistics_url;
133
134 p->config_storage[i] = s;
135
136 if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
137 return HANDLER_ERROR;
138 }
139 }
140
141 return HANDLER_GO_ON;
142 }
143
144
145
mod_status_row_append(buffer * b,const char * key,const char * value)146 static int mod_status_row_append(buffer *b, const char *key, const char *value) {
147 buffer_append_string_len(b, CONST_STR_LEN(" <tr>\n"));
148 buffer_append_string_len(b, CONST_STR_LEN(" <td><b>"));
149 buffer_append_string(b, key);
150 buffer_append_string_len(b, CONST_STR_LEN("</b></td>\n"));
151 buffer_append_string_len(b, CONST_STR_LEN(" <td>"));
152 buffer_append_string(b, value);
153 buffer_append_string_len(b, CONST_STR_LEN("</td>\n"));
154 buffer_append_string_len(b, CONST_STR_LEN(" </tr>\n"));
155
156 return 0;
157 }
158
mod_status_header_append(buffer * b,const char * key)159 static int mod_status_header_append(buffer *b, const char *key) {
160 buffer_append_string_len(b, CONST_STR_LEN(" <tr>\n"));
161 buffer_append_string_len(b, CONST_STR_LEN(" <th colspan=\"2\">"));
162 buffer_append_string(b, key);
163 buffer_append_string_len(b, CONST_STR_LEN("</th>\n"));
164 buffer_append_string_len(b, CONST_STR_LEN(" </tr>\n"));
165
166 return 0;
167 }
168
mod_status_header_append_sort(buffer * b,void * p_d,const char * key)169 static int mod_status_header_append_sort(buffer *b, void *p_d, const char* key) {
170 plugin_data *p = p_d;
171
172 if (p->conf.sort) {
173 buffer_append_string_len(b, CONST_STR_LEN("<th class=\"status\"><a href=\"#\" class=\"sortheader\" onclick=\"resort(this);return false;\">"));
174 buffer_append_string(b, key);
175 buffer_append_string_len(b, CONST_STR_LEN("<span class=\"sortarrow\">:</span></a></th>\n"));
176 } else {
177 buffer_append_string_len(b, CONST_STR_LEN("<th class=\"status\">"));
178 buffer_append_string(b, key);
179 buffer_append_string_len(b, CONST_STR_LEN("</th>\n"));
180 }
181
182 return 0;
183 }
184
mod_status_get_multiplier(double * avg,char * multiplier,int size)185 static int mod_status_get_multiplier(double *avg, char *multiplier, int size) {
186 *multiplier = ' ';
187
188 if (*avg > size) { *avg /= size; *multiplier = 'k'; }
189 if (*avg > size) { *avg /= size; *multiplier = 'M'; }
190 if (*avg > size) { *avg /= size; *multiplier = 'G'; }
191 if (*avg > size) { *avg /= size; *multiplier = 'T'; }
192 if (*avg > size) { *avg /= size; *multiplier = 'P'; }
193 if (*avg > size) { *avg /= size; *multiplier = 'E'; }
194 if (*avg > size) { *avg /= size; *multiplier = 'Z'; }
195 if (*avg > size) { *avg /= size; *multiplier = 'Y'; }
196
197 return 0;
198 }
199
mod_status_handle_server_status_html(server * srv,connection * con,void * p_d)200 static handler_t mod_status_handle_server_status_html(server *srv, connection *con, void *p_d) {
201 plugin_data *p = p_d;
202 buffer *b;
203 size_t j;
204 double avg;
205 char multiplier = '\0';
206 char buf[32];
207 time_t ts;
208
209 int days, hours, mins, seconds;
210
211 b = chunkqueue_get_append_buffer(con->write_queue);
212
213 buffer_copy_string_len(b, CONST_STR_LEN(
214 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
215 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
216 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
217 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
218 " <head>\n"
219 " <title>Status</title>\n"
220
221 " <style type=\"text/css\">\n"
222 " table.status { border: black solid thin; }\n"
223 " td { white-space: nowrap; }\n"
224 " td.int { background-color: #f0f0f0; text-align: right }\n"
225 " td.string { background-color: #f0f0f0; text-align: left }\n"
226 " th.status { background-color: black; color: white; font-weight: bold; }\n"
227 " a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n"
228 " span.sortarrow { color: white; text-decoration: none; }\n"
229 " </style>\n"));
230
231 if (p->conf.sort) {
232 buffer_append_string_len(b, CONST_STR_LEN(
233 "<script type=\"text/javascript\">\n"
234 "// <!--\n"
235 "var sort_column;\n"
236 "var prev_span = null;\n"
237
238 "function get_inner_text(el) {\n"
239 " if((typeof el == 'string')||(typeof el == 'undefined'))\n"
240 " return el;\n"
241 " if(el.innerText)\n"
242 " return el.innerText;\n"
243 " else {\n"
244 " var str = \"\";\n"
245 " var cs = el.childNodes;\n"
246 " var l = cs.length;\n"
247 " for (i=0;i<l;i++) {\n"
248 " if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n"
249 " else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n"
250 " }\n"
251 " }\n"
252 " return str;\n"
253 "}\n"
254
255 "function sortfn(a,b) {\n"
256 " var at = get_inner_text(a.cells[sort_column]);\n"
257 " var bt = get_inner_text(b.cells[sort_column]);\n"
258 " if (a.cells[sort_column].className == 'int') {\n"
259 " return parseInt(at)-parseInt(bt);\n"
260 " } else {\n"
261 " aa = at.toLowerCase();\n"
262 " bb = bt.toLowerCase();\n"
263 " if (aa==bb) return 0;\n"
264 " else if (aa<bb) return -1;\n"
265 " else return 1;\n"
266 " }\n"
267 "}\n"
268
269 "function resort(lnk) {\n"
270 " var span = lnk.childNodes[1];\n"
271 " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n"
272 " var rows = new Array();\n"
273 " for (j=1;j<table.rows.length;j++)\n"
274 " rows[j-1] = table.rows[j];\n"
275 " sort_column = lnk.parentNode.cellIndex;\n"
276 " rows.sort(sortfn);\n"
277
278 " if (prev_span != null) prev_span.innerHTML = '';\n"
279 " if (span.getAttribute('sortdir')=='down') {\n"
280 " span.innerHTML = '↑';\n"
281 " span.setAttribute('sortdir','up');\n"
282 " rows.reverse();\n"
283 " } else {\n"
284 " span.innerHTML = '↓';\n"
285 " span.setAttribute('sortdir','down');\n"
286 " }\n"
287 " for (i=0;i<rows.length;i++)\n"
288 " table.tBodies[0].appendChild(rows[i]);\n"
289 " prev_span = span;\n"
290 "}\n"
291 "// -->\n"
292 "</script>\n"));
293 }
294
295 buffer_append_string_len(b, CONST_STR_LEN(
296 " </head>\n"
297 " <body>\n"));
298
299
300
301 /* connection listing */
302 buffer_append_string_len(b, CONST_STR_LEN("<h1>Server-Status (" PACKAGE_NAME " " PACKAGE_VERSION ")</h1>"));
303
304 buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">"));
305 buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Hostname</td><td class=\"string\">"));
306 buffer_append_string_buffer(b, con->uri.authority);
307 buffer_append_string_len(b, CONST_STR_LEN(" ("));
308 buffer_append_string_buffer(b, con->server_name);
309 buffer_append_string_len(b, CONST_STR_LEN(")</td></tr>\n"));
310 buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Uptime</td><td class=\"string\">"));
311
312 ts = srv->cur_ts - srv->startup_ts;
313
314 days = ts / (60 * 60 * 24);
315 ts %= (60 * 60 * 24);
316
317 hours = ts / (60 * 60);
318 ts %= (60 * 60);
319
320 mins = ts / (60);
321 ts %= (60);
322
323 seconds = ts;
324
325 if (days) {
326 buffer_append_long(b, days);
327 buffer_append_string_len(b, CONST_STR_LEN(" days "));
328 }
329
330 if (hours) {
331 buffer_append_long(b, hours);
332 buffer_append_string_len(b, CONST_STR_LEN(" hours "));
333 }
334
335 if (mins) {
336 buffer_append_long(b, mins);
337 buffer_append_string_len(b, CONST_STR_LEN(" min "));
338 }
339
340 buffer_append_long(b, seconds);
341 buffer_append_string_len(b, CONST_STR_LEN(" s"));
342
343 buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));
344 buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Started at</td><td class=\"string\">"));
345
346 ts = srv->startup_ts;
347
348 strftime(buf, sizeof(buf) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts));
349 buffer_append_string(b, buf);
350 buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));
351
352
353 buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">absolute (since start)</th></tr>\n"));
354
355 buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
356 avg = p->abs_requests;
357
358 mod_status_get_multiplier(&avg, &multiplier, 1000);
359
360 buffer_append_long(b, avg);
361 buffer_append_string_len(b, CONST_STR_LEN(" "));
362 if (multiplier) buffer_append_string_len(b, &multiplier, 1);
363 buffer_append_string_len(b, CONST_STR_LEN("req</td></tr>\n"));
364
365 buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
366 avg = p->abs_traffic_out;
367
368 mod_status_get_multiplier(&avg, &multiplier, 1024);
369
370 sprintf(buf, "%.2f", avg);
371 buffer_append_string(b, buf);
372 buffer_append_string_len(b, CONST_STR_LEN(" "));
373 if (multiplier) buffer_append_string_len(b, &multiplier, 1);
374 buffer_append_string_len(b, CONST_STR_LEN("byte</td></tr>\n"));
375
376
377
378 buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (since start)</th></tr>\n"));
379
380 buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
381 avg = p->abs_requests / (srv->cur_ts - srv->startup_ts);
382
383 mod_status_get_multiplier(&avg, &multiplier, 1000);
384
385 buffer_append_long(b, avg);
386 buffer_append_string_len(b, CONST_STR_LEN(" "));
387 if (multiplier) buffer_append_string_len(b, &multiplier, 1);
388 buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n"));
389
390 buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
391 avg = p->abs_traffic_out / (srv->cur_ts - srv->startup_ts);
392
393 mod_status_get_multiplier(&avg, &multiplier, 1024);
394
395 sprintf(buf, "%.2f", avg);
396 buffer_append_string(b, buf);
397 buffer_append_string_len(b, CONST_STR_LEN(" "));
398 if (multiplier) buffer_append_string_len(b, &multiplier, 1);
399 buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n"));
400
401
402
403 buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n"));
404 for (j = 0, avg = 0; j < 5; j++) {
405 avg += p->mod_5s_requests[j];
406 }
407
408 avg /= 5;
409
410 buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
411
412 mod_status_get_multiplier(&avg, &multiplier, 1000);
413
414 buffer_append_long(b, avg);
415 buffer_append_string_len(b, CONST_STR_LEN(" "));
416 if (multiplier) buffer_append_string_len(b, &multiplier, 1);
417
418 buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n"));
419
420 for (j = 0, avg = 0; j < 5; j++) {
421 avg += p->mod_5s_traffic_out[j];
422 }
423
424 avg /= 5;
425
426 buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
427
428 mod_status_get_multiplier(&avg, &multiplier, 1024);
429
430 sprintf(buf, "%.2f", avg);
431 buffer_append_string(b, buf);
432 buffer_append_string_len(b, CONST_STR_LEN(" "));
433 if (multiplier) buffer_append_string_len(b, &multiplier, 1);
434 buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n"));
435
436 buffer_append_string_len(b, CONST_STR_LEN("</table>\n"));
437
438
439 buffer_append_string_len(b, CONST_STR_LEN(
440 "<hr />\n<pre><b>legend</b>\n"
441 ". = connect, C = close, E = hard error, k = keep-alive\n"
442 "r = read, R = read-POST, W = write, h = handle-request\n"
443 "q = request-start, Q = request-end\n"
444 "s = response-start, S = response-end\n"));
445
446 buffer_append_string_len(b, CONST_STR_LEN("<b>"));
447 buffer_append_long(b, srv->conns->used);
448 buffer_append_string_len(b, CONST_STR_LEN(" connections</b>\n"));
449
450 for (j = 0; j < srv->conns->used; j++) {
451 connection *c = srv->conns->ptr[j];
452 const char *state;
453
454 if (CON_STATE_READ == c->state && c->request.orig_uri->used > 0) {
455 state = "k";
456 } else {
457 state = connection_get_short_state(c->state);
458 }
459
460 buffer_append_string_len(b, state, 1);
461
462 if (((j + 1) % 50) == 0) {
463 buffer_append_string_len(b, CONST_STR_LEN("\n"));
464 }
465 }
466
467 buffer_append_string_len(b, CONST_STR_LEN("\n</pre><hr />\n<h2>Connections</h2>\n"));
468
469 buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">\n"));
470 buffer_append_string_len(b, CONST_STR_LEN("<tr>"));
471 mod_status_header_append_sort(b, p_d, "Client IP");
472 mod_status_header_append_sort(b, p_d, "Read");
473 mod_status_header_append_sort(b, p_d, "Written");
474 mod_status_header_append_sort(b, p_d, "State");
475 mod_status_header_append_sort(b, p_d, "Time");
476 mod_status_header_append_sort(b, p_d, "Host");
477 mod_status_header_append_sort(b, p_d, "URI");
478 mod_status_header_append_sort(b, p_d, "File");
479 buffer_append_string_len(b, CONST_STR_LEN("</tr>\n"));
480
481 for (j = 0; j < srv->conns->used; j++) {
482 connection *c = srv->conns->ptr[j];
483
484 buffer_append_string_len(b, CONST_STR_LEN("<tr><td class=\"string\">"));
485
486 buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(c->dst_addr)));
487
488 buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
489
490 if (c->request.content_length) {
491 buffer_append_long(b, c->request_content_queue->bytes_in);
492 buffer_append_string_len(b, CONST_STR_LEN("/"));
493 buffer_append_long(b, c->request.content_length);
494 } else {
495 buffer_append_string_len(b, CONST_STR_LEN("0/0"));
496 }
497
498 buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
499
500 buffer_append_off_t(b, chunkqueue_written(c->write_queue));
501 buffer_append_string_len(b, CONST_STR_LEN("/"));
502 buffer_append_off_t(b, chunkqueue_length(c->write_queue));
503
504 buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
505
506 if (CON_STATE_READ == c->state && c->request.orig_uri->used > 0) {
507 buffer_append_string_len(b, CONST_STR_LEN("keep-alive"));
508 } else {
509 buffer_append_string(b, connection_get_state(c->state));
510 }
511
512 buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
513
514 buffer_append_long(b, srv->cur_ts - c->request_start);
515
516 buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
517
518 if (buffer_is_empty(c->server_name)) {
519 buffer_append_string_buffer(b, c->uri.authority);
520 }
521 else {
522 buffer_append_string_buffer(b, c->server_name);
523 }
524
525 buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
526
527 if (!buffer_is_empty(c->uri.path)) {
528 buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML);
529 }
530
531 if (!buffer_is_empty(c->uri.query)) {
532 buffer_append_string_len(b, CONST_STR_LEN("?"));
533 buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.query), ENCODING_HTML);
534 }
535
536 if (!buffer_is_empty(c->request.orig_uri)) {
537 buffer_append_string_len(b, CONST_STR_LEN(" ("));
538 buffer_append_string_encoded(b, CONST_BUF_LEN(c->request.orig_uri), ENCODING_HTML);
539 buffer_append_string_len(b, CONST_STR_LEN(")"));
540 }
541 buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
542
543 buffer_append_string_buffer(b, c->physical.path);
544
545 buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));
546 }
547
548
549 buffer_append_string_len(b, CONST_STR_LEN(
550 "</table>\n"));
551
552
553 buffer_append_string_len(b, CONST_STR_LEN(
554 " </body>\n"
555 "</html>\n"
556 ));
557
558 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
559
560 return 0;
561 }
562
563
mod_status_handle_server_status_text(server * srv,connection * con,void * p_d)564 static handler_t mod_status_handle_server_status_text(server *srv, connection *con, void *p_d) {
565 plugin_data *p = p_d;
566 buffer *b;
567 double avg;
568 time_t ts;
569 char buf[32];
570 unsigned int k;
571 unsigned int l;
572
573 b = chunkqueue_get_append_buffer(con->write_queue);
574
575 /* output total number of requests */
576 buffer_append_string_len(b, CONST_STR_LEN("Total Accesses: "));
577 avg = p->abs_requests;
578 snprintf(buf, sizeof(buf) - 1, "%.0f", avg);
579 buffer_append_string(b, buf);
580 buffer_append_string_len(b, CONST_STR_LEN("\n"));
581
582 /* output total traffic out in kbytes */
583 buffer_append_string_len(b, CONST_STR_LEN("Total kBytes: "));
584 avg = p->abs_traffic_out / 1024;
585 snprintf(buf, sizeof(buf) - 1, "%.0f", avg);
586 buffer_append_string(b, buf);
587 buffer_append_string_len(b, CONST_STR_LEN("\n"));
588
589 /* output uptime */
590 buffer_append_string_len(b, CONST_STR_LEN("Uptime: "));
591 ts = srv->cur_ts - srv->startup_ts;
592 buffer_append_long(b, ts);
593 buffer_append_string_len(b, CONST_STR_LEN("\n"));
594
595 /* output busy servers */
596 buffer_append_string_len(b, CONST_STR_LEN("BusyServers: "));
597 buffer_append_long(b, srv->conns->used);
598 buffer_append_string_len(b, CONST_STR_LEN("\n"));
599
600 buffer_append_string_len(b, CONST_STR_LEN("IdleServers: "));
601 buffer_append_long(b, srv->conns->size - srv->conns->used);
602 buffer_append_string_len(b, CONST_STR_LEN("\n"));
603
604 /* output scoreboard */
605 buffer_append_string_len(b, CONST_STR_LEN("Scoreboard: "));
606 for (k = 0; k < srv->conns->used; k++) {
607 connection *c = srv->conns->ptr[k];
608 const char *state = connection_get_short_state(c->state);
609 buffer_append_string_len(b, state, 1);
610 }
611 for (l = 0; l < srv->conns->size - srv->conns->used; l++) {
612 buffer_append_string_len(b, CONST_STR_LEN("_"));
613 }
614 buffer_append_string_len(b, CONST_STR_LEN("\n"));
615
616 /* set text/plain output */
617
618 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
619
620 return 0;
621 }
622
mod_status_handle_server_statistics(server * srv,connection * con,void * p_d)623 static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) {
624 buffer *b;
625 size_t i;
626 array *st = srv->status;
627 UNUSED(p_d);
628
629 if (0 == st->used) {
630 /* we have nothing to send */
631 con->http_status = 204;
632 con->file_finished = 1;
633
634 return HANDLER_FINISHED;
635 }
636
637 b = chunkqueue_get_append_buffer(con->write_queue);
638
639 for (i = 0; i < st->used; i++) {
640 size_t ndx = st->sorted[i];
641
642 buffer_append_string_buffer(b, st->data[ndx]->key);
643 buffer_append_string_len(b, CONST_STR_LEN(": "));
644 buffer_append_long(b, ((data_integer *)(st->data[ndx]))->value);
645 buffer_append_string_len(b, CONST_STR_LEN("\n"));
646 }
647
648 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
649
650 con->http_status = 200;
651 con->file_finished = 1;
652
653 return HANDLER_FINISHED;
654 }
655
656
mod_status_handle_server_status(server * srv,connection * con,void * p_d)657 static handler_t mod_status_handle_server_status(server *srv, connection *con, void *p_d) {
658
659 if (buffer_is_equal_string(con->uri.query, CONST_STR_LEN("auto"))) {
660 mod_status_handle_server_status_text(srv, con, p_d);
661 } else {
662 mod_status_handle_server_status_html(srv, con, p_d);
663 }
664
665 con->http_status = 200;
666 con->file_finished = 1;
667
668 return HANDLER_FINISHED;
669 }
670
671
mod_status_handle_server_config(server * srv,connection * con,void * p_d)672 static handler_t mod_status_handle_server_config(server *srv, connection *con, void *p_d) {
673 plugin_data *p = p_d;
674 buffer *b, *m = p->module_list;
675 size_t i;
676
677 struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] =
678 {
679 /* - epoll is most reliable
680 * - select works everywhere
681 */
682 #ifdef USE_LINUX_EPOLL
683 { FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" },
684 #endif
685 #ifdef USE_POLL
686 { FDEVENT_HANDLER_POLL, "poll" },
687 #endif
688 #ifdef USE_SELECT
689 { FDEVENT_HANDLER_SELECT, "select" },
690 #endif
691 #ifdef USE_LIBEV
692 { FDEVENT_HANDLER_LIBEV, "libev" },
693 #endif
694 #ifdef USE_SOLARIS_DEVPOLL
695 { FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" },
696 #endif
697 #ifdef USE_SOLARIS_PORT
698 { FDEVENT_HANDLER_SOLARIS_PORT, "solaris-eventports" },
699 #endif
700 #ifdef USE_FREEBSD_KQUEUE
701 { FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" },
702 #endif
703 #ifdef USE_MTCP
704 { FDEVENT_HANDLER_LIBMTCP, "mtcp-epoll" },
705 #endif
706 { FDEVENT_HANDLER_UNSET, NULL }
707 };
708
709 b = chunkqueue_get_append_buffer(con->write_queue);
710
711 buffer_copy_string_len(b, CONST_STR_LEN(
712 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
713 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
714 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
715 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
716 " <head>\n"
717 " <title>Status</title>\n"
718 " </head>\n"
719 " <body>\n"
720 " <h1>" PACKAGE_DESC "</h1>\n"
721 " <table summary=\"status\" border=\"1\">\n"));
722
723 mod_status_header_append(b, "Server-Features");
724 #ifdef HAVE_PCRE_H
725 mod_status_row_append(b, "RegEx Conditionals", "enabled");
726 #else
727 mod_status_row_append(b, "RegEx Conditionals", "disabled - pcre missing");
728 #endif
729 mod_status_header_append(b, "Network Engine");
730
731 for (i = 0; event_handlers[i].name; i++) {
732 if (event_handlers[i].et == srv->event_handler) {
733 mod_status_row_append(b, "fd-Event-Handler", event_handlers[i].name);
734 break;
735 }
736 }
737
738 mod_status_header_append(b, "Config-File-Settings");
739
740 for (i = 0; i < srv->plugins.used; i++) {
741 plugin **ps = srv->plugins.ptr;
742
743 plugin *pl = ps[i];
744
745 if (i == 0) {
746 buffer_copy_string_buffer(m, pl->name);
747 } else {
748 buffer_append_string_len(m, CONST_STR_LEN("<br />"));
749 buffer_append_string_buffer(m, pl->name);
750 }
751 }
752
753 mod_status_row_append(b, "Loaded Modules", m->ptr);
754
755 buffer_append_string_len(b, CONST_STR_LEN(" </table>\n"));
756
757 buffer_append_string_len(b, CONST_STR_LEN(
758 " </body>\n"
759 "</html>\n"
760 ));
761
762 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
763
764 con->http_status = 200;
765 con->file_finished = 1;
766
767 return HANDLER_FINISHED;
768 }
769
770 #define PATCH(x) \
771 p->conf.x = s->x;
mod_status_patch_connection(server * srv,connection * con,plugin_data * p)772 static int mod_status_patch_connection(server *srv, connection *con, plugin_data *p) {
773 size_t i, j;
774 plugin_config *s = p->config_storage[0];
775
776 PATCH(status_url);
777 PATCH(config_url);
778 PATCH(sort);
779 PATCH(statistics_url);
780
781 /* skip the first, the global context */
782 for (i = 1; i < srv->config_context->used; i++) {
783 data_config *dc = (data_config *)srv->config_context->data[i];
784 s = p->config_storage[i];
785
786 /* condition didn't match */
787 if (!config_check_cond(srv, con, dc)) continue;
788
789 /* merge config */
790 for (j = 0; j < dc->value->used; j++) {
791 data_unset *du = dc->value->data[j];
792
793 if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.status-url"))) {
794 PATCH(status_url);
795 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.config-url"))) {
796 PATCH(config_url);
797 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.enable-sort"))) {
798 PATCH(sort);
799 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.statistics-url"))) {
800 PATCH(statistics_url);
801 }
802 }
803 }
804
805 return 0;
806 }
807
mod_status_handler(server * srv,connection * con,void * p_d)808 static handler_t mod_status_handler(server *srv, connection *con, void *p_d) {
809 plugin_data *p = p_d;
810
811 if (con->mode != DIRECT) return HANDLER_GO_ON;
812
813 mod_status_patch_connection(srv, con, p);
814
815 if (!buffer_is_empty(p->conf.status_url) &&
816 buffer_is_equal(p->conf.status_url, con->uri.path)) {
817 return mod_status_handle_server_status(srv, con, p_d);
818 } else if (!buffer_is_empty(p->conf.config_url) &&
819 buffer_is_equal(p->conf.config_url, con->uri.path)) {
820 return mod_status_handle_server_config(srv, con, p_d);
821 } else if (!buffer_is_empty(p->conf.statistics_url) &&
822 buffer_is_equal(p->conf.statistics_url, con->uri.path)) {
823 return mod_status_handle_server_statistics(srv, con, p_d);
824 }
825
826 return HANDLER_GO_ON;
827 }
828
TRIGGER_FUNC(mod_status_trigger)829 TRIGGER_FUNC(mod_status_trigger) {
830 plugin_data *p = p_d;
831 size_t i;
832
833 /* check all connections */
834 for (i = 0; i < srv->conns->used; i++) {
835 connection *c = srv->conns->ptr[i];
836
837 p->bytes_written += c->bytes_written_cur_second;
838 }
839
840 /* a sliding average */
841 p->mod_5s_traffic_out[p->mod_5s_ndx] = p->bytes_written;
842 p->mod_5s_requests [p->mod_5s_ndx] = p->requests;
843
844 p->mod_5s_ndx = (p->mod_5s_ndx+1) % 5;
845
846 p->abs_traffic_out += p->bytes_written;
847 p->rel_traffic_out += p->bytes_written;
848
849 p->bytes_written = 0;
850
851 /* reset storage - second */
852 p->traffic_out = 0;
853 p->requests = 0;
854
855 return HANDLER_GO_ON;
856 }
857
REQUESTDONE_FUNC(mod_status_account)858 REQUESTDONE_FUNC(mod_status_account) {
859 plugin_data *p = p_d;
860
861 UNUSED(srv);
862
863 p->requests++;
864 p->rel_requests++;
865 p->abs_requests++;
866
867 p->bytes_written += con->bytes_written_cur_second;
868
869 return HANDLER_GO_ON;
870 }
871
872 int mod_status_plugin_init(plugin *p);
mod_status_plugin_init(plugin * p)873 int mod_status_plugin_init(plugin *p) {
874 p->version = LIGHTTPD_VERSION_ID;
875 p->name = buffer_init_string("status");
876
877 p->init = mod_status_init;
878 p->cleanup = mod_status_free;
879 p->set_defaults= mod_status_set_defaults;
880
881 p->handle_uri_clean = mod_status_handler;
882 p->handle_trigger = mod_status_trigger;
883 p->handle_request_done = mod_status_account;
884
885 p->data = NULL;
886
887 return 0;
888 }
889