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
23typedef struct {
24	buffer *config_url;
25	buffer *status_url;
26	buffer *statistics_url;
27
28	int     sort;
29} plugin_config;
30
31typedef 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
56INIT_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
75FREE_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
104SETDEFAULTS_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
147static 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
160static 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
170static 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
186static 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
201static 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	buffer_copy_string_len(b, CONST_STR_LEN(
213				 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
214				 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
215				 "         \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
216				 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
217				 " <head>\n"
218				 "  <title>Status</title>\n"
219
220				   "  <style type=\"text/css\">\n"
221				   "    table.status { border: black solid thin; }\n"
222				   "    td { white-space: nowrap; }\n"
223				   "    td.int { background-color: #f0f0f0; text-align: right }\n"
224				   "    td.string { background-color: #f0f0f0; text-align: left }\n"
225				   "    th.status { background-color: black; color: white; font-weight: bold; }\n"
226				   "    a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n"
227				   "    span.sortarrow { color: white; text-decoration: none; }\n"
228				   "  </style>\n"));
229
230	if (p->conf.sort) {
231		buffer_append_string_len(b, CONST_STR_LEN(
232					   "<script type=\"text/javascript\">\n"
233					   "// <!--\n"
234					   "var sort_column;\n"
235					   "var prev_span = null;\n"
236
237					   "function get_inner_text(el) {\n"
238					   " if((typeof el == 'string')||(typeof el == 'undefined'))\n"
239					   "  return el;\n"
240					   " if(el.innerText)\n"
241					   "  return el.innerText;\n"
242					   " else {\n"
243					   "  var str = \"\";\n"
244					   "  var cs = el.childNodes;\n"
245					   "  var l = cs.length;\n"
246					   "  for (i=0;i<l;i++) {\n"
247					   "   if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n"
248					   "   else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n"
249					   "  }\n"
250					   " }\n"
251					   " return str;\n"
252					   "}\n"
253
254					   "function sortfn(a,b) {\n"
255					   " var at = get_inner_text(a.cells[sort_column]);\n"
256					   " var bt = get_inner_text(b.cells[sort_column]);\n"
257					   " if (a.cells[sort_column].className == 'int') {\n"
258					   "  return parseInt(at)-parseInt(bt);\n"
259					   " } else {\n"
260					   "  aa = at.toLowerCase();\n"
261					   "  bb = bt.toLowerCase();\n"
262					   "  if (aa==bb) return 0;\n"
263					   "  else if (aa<bb) return -1;\n"
264					   "  else return 1;\n"
265					   " }\n"
266					   "}\n"
267
268					   "function resort(lnk) {\n"
269					   " var span = lnk.childNodes[1];\n"
270					   " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n"
271					   " var rows = new Array();\n"
272					   " for (j=1;j<table.rows.length;j++)\n"
273					   "  rows[j-1] = table.rows[j];\n"
274					   " sort_column = lnk.parentNode.cellIndex;\n"
275					   " rows.sort(sortfn);\n"
276
277					   " if (prev_span != null) prev_span.innerHTML = '';\n"
278					   " if (span.getAttribute('sortdir')=='down') {\n"
279					   "  span.innerHTML = '&uarr;';\n"
280					   "  span.setAttribute('sortdir','up');\n"
281					   "  rows.reverse();\n"
282					   " } else {\n"
283					   "  span.innerHTML = '&darr;';\n"
284					   "  span.setAttribute('sortdir','down');\n"
285					   " }\n"
286					   " for (i=0;i<rows.length;i++)\n"
287					   "  table.tBodies[0].appendChild(rows[i]);\n"
288					   " prev_span = span;\n"
289					   "}\n"
290					   "// -->\n"
291					   "</script>\n"));
292	}
293
294	buffer_append_string_len(b, CONST_STR_LEN(
295				 " </head>\n"
296				 " <body>\n"));
297
298
299
300	/* connection listing */
301	buffer_append_string_len(b, CONST_STR_LEN("<h1>Server-Status (" PACKAGE_NAME " " PACKAGE_VERSION ")</h1>"));
302
303	buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">"));
304	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Hostname</td><td class=\"string\">"));
305	buffer_append_string_buffer(b, con->uri.authority);
306	buffer_append_string_len(b, CONST_STR_LEN(" ("));
307	buffer_append_string_buffer(b, con->server_name);
308	buffer_append_string_len(b, CONST_STR_LEN(")</td></tr>\n"));
309	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Uptime</td><td class=\"string\">"));
310
311	ts = srv->cur_ts - srv->startup_ts;
312
313	days = ts / (60 * 60 * 24);
314	ts %= (60 * 60 * 24);
315
316	hours = ts / (60 * 60);
317	ts %= (60 * 60);
318
319	mins = ts / (60);
320	ts %= (60);
321
322	seconds = ts;
323
324	if (days) {
325		buffer_append_int(b, days);
326		buffer_append_string_len(b, CONST_STR_LEN(" days "));
327	}
328
329	if (hours) {
330		buffer_append_int(b, hours);
331		buffer_append_string_len(b, CONST_STR_LEN(" hours "));
332	}
333
334	if (mins) {
335		buffer_append_int(b, mins);
336		buffer_append_string_len(b, CONST_STR_LEN(" min "));
337	}
338
339	buffer_append_int(b, seconds);
340	buffer_append_string_len(b, CONST_STR_LEN(" s"));
341
342	buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));
343	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Started at</td><td class=\"string\">"));
344
345	ts = srv->startup_ts;
346
347	strftime(buf, sizeof(buf) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts));
348	buffer_append_string(b, buf);
349	buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));
350
351
352	buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">absolute (since start)</th></tr>\n"));
353
354	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
355	avg = p->abs_requests;
356
357	mod_status_get_multiplier(&avg, &multiplier, 1000);
358
359	buffer_append_int(b, avg);
360	buffer_append_string_len(b, CONST_STR_LEN(" "));
361	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
362	buffer_append_string_len(b, CONST_STR_LEN("req</td></tr>\n"));
363
364	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
365	avg = p->abs_traffic_out;
366
367	mod_status_get_multiplier(&avg, &multiplier, 1024);
368
369	sprintf(buf, "%.2f", avg);
370	buffer_append_string(b, buf);
371	buffer_append_string_len(b, CONST_STR_LEN(" "));
372	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
373	buffer_append_string_len(b, CONST_STR_LEN("byte</td></tr>\n"));
374
375
376
377	buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (since start)</th></tr>\n"));
378
379	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
380	avg = p->abs_requests / (srv->cur_ts - srv->startup_ts);
381
382	mod_status_get_multiplier(&avg, &multiplier, 1000);
383
384	buffer_append_int(b, avg);
385	buffer_append_string_len(b, CONST_STR_LEN(" "));
386	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
387	buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n"));
388
389	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
390	avg = p->abs_traffic_out / (srv->cur_ts - srv->startup_ts);
391
392	mod_status_get_multiplier(&avg, &multiplier, 1024);
393
394	sprintf(buf, "%.2f", avg);
395	buffer_append_string(b, buf);
396	buffer_append_string_len(b, CONST_STR_LEN(" "));
397	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
398	buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n"));
399
400
401
402	buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n"));
403	for (j = 0, avg = 0; j < 5; j++) {
404		avg += p->mod_5s_requests[j];
405	}
406
407	avg /= 5;
408
409	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
410
411	mod_status_get_multiplier(&avg, &multiplier, 1000);
412
413	buffer_append_int(b, avg);
414	buffer_append_string_len(b, CONST_STR_LEN(" "));
415	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
416
417	buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n"));
418
419	for (j = 0, avg = 0; j < 5; j++) {
420		avg += p->mod_5s_traffic_out[j];
421	}
422
423	avg /= 5;
424
425	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
426
427	mod_status_get_multiplier(&avg, &multiplier, 1024);
428
429	sprintf(buf, "%.2f", avg);
430	buffer_append_string(b, buf);
431	buffer_append_string_len(b, CONST_STR_LEN(" "));
432	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
433	buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n"));
434
435	buffer_append_string_len(b, CONST_STR_LEN("</table>\n"));
436
437
438	buffer_append_string_len(b, CONST_STR_LEN(
439		"<hr />\n<pre><b>legend</b>\n"
440		". = connect, C = close, E = hard error, k = keep-alive\n"
441		"r = read, R = read-POST, W = write, h = handle-request\n"
442		"q = request-start,  Q = request-end\n"
443		"s = response-start, S = response-end\n"));
444
445	buffer_append_string_len(b, CONST_STR_LEN("<b>"));
446	buffer_append_int(b, srv->conns->used);
447	buffer_append_string_len(b, CONST_STR_LEN(" connections</b>\n"));
448
449	for (j = 0; j < srv->conns->used; j++) {
450		connection *c = srv->conns->ptr[j];
451		const char *state;
452
453		if (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) {
454			state = "k";
455		} else {
456			state = connection_get_short_state(c->state);
457		}
458
459		buffer_append_string_len(b, state, 1);
460
461		if (((j + 1) % 50) == 0) {
462			buffer_append_string_len(b, CONST_STR_LEN("\n"));
463		}
464	}
465
466	buffer_append_string_len(b, CONST_STR_LEN("\n</pre><hr />\n<h2>Connections</h2>\n"));
467
468	buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">\n"));
469	buffer_append_string_len(b, CONST_STR_LEN("<tr>"));
470	mod_status_header_append_sort(b, p_d, "Client IP");
471	mod_status_header_append_sort(b, p_d, "Read");
472	mod_status_header_append_sort(b, p_d, "Written");
473	mod_status_header_append_sort(b, p_d, "State");
474	mod_status_header_append_sort(b, p_d, "Time");
475	mod_status_header_append_sort(b, p_d, "Host");
476	mod_status_header_append_sort(b, p_d, "URI");
477	mod_status_header_append_sort(b, p_d, "File");
478	buffer_append_string_len(b, CONST_STR_LEN("</tr>\n"));
479
480	for (j = 0; j < srv->conns->used; j++) {
481		connection *c = srv->conns->ptr[j];
482
483		buffer_append_string_len(b, CONST_STR_LEN("<tr><td class=\"string\">"));
484
485		buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(c->dst_addr)));
486
487		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
488
489		if (c->request.content_length) {
490			buffer_append_int(b, c->request_content_queue->bytes_in);
491			buffer_append_string_len(b, CONST_STR_LEN("/"));
492			buffer_append_int(b, c->request.content_length);
493		} else {
494			buffer_append_string_len(b, CONST_STR_LEN("0/0"));
495		}
496
497		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
498
499		buffer_append_int(b, c->write_queue->bytes_out);
500		buffer_append_string_len(b, CONST_STR_LEN("/"));
501		buffer_append_int(b, c->write_queue->bytes_out + chunkqueue_length(c->write_queue));
502
503		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
504
505		if (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) {
506			buffer_append_string_len(b, CONST_STR_LEN("keep-alive"));
507		} else {
508			buffer_append_string(b, connection_get_state(c->state));
509		}
510
511		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
512
513		buffer_append_int(b, srv->cur_ts - c->request_start);
514
515		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
516
517		if (buffer_string_is_empty(c->server_name)) {
518			buffer_append_string_buffer(b, c->uri.authority);
519		}
520		else {
521			buffer_append_string_buffer(b, c->server_name);
522		}
523
524		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
525
526		if (!buffer_string_is_empty(c->uri.path)) {
527			buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML);
528		}
529
530		if (!buffer_string_is_empty(c->uri.query)) {
531			buffer_append_string_len(b, CONST_STR_LEN("?"));
532			buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.query), ENCODING_HTML);
533		}
534
535		if (!buffer_string_is_empty(c->request.orig_uri)) {
536			buffer_append_string_len(b, CONST_STR_LEN(" ("));
537			buffer_append_string_encoded(b, CONST_BUF_LEN(c->request.orig_uri), ENCODING_HTML);
538			buffer_append_string_len(b, CONST_STR_LEN(")"));
539		}
540		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
541
542		buffer_append_string_buffer(b, c->physical.path);
543
544		buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));
545	}
546
547
548	buffer_append_string_len(b, CONST_STR_LEN(
549		      "</table>\n"));
550
551
552	buffer_append_string_len(b, CONST_STR_LEN(
553		      " </body>\n"
554		      "</html>\n"
555		      ));
556
557	chunkqueue_append_buffer(con->write_queue, b);
558	buffer_free(b);
559
560	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
561
562	return 0;
563}
564
565
566static handler_t mod_status_handle_server_status_text(server *srv, connection *con, void *p_d) {
567	plugin_data *p = p_d;
568	buffer *b = buffer_init();
569	double avg;
570	time_t ts;
571	char buf[32];
572	unsigned int k;
573	unsigned int l;
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_int(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_int(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_int(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	chunkqueue_append_buffer(con->write_queue, b);
617	buffer_free(b);
618
619	/* set text/plain output */
620	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
621
622	return 0;
623}
624
625static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) {
626	buffer *b;
627	size_t i;
628	array *st = srv->status;
629	UNUSED(p_d);
630
631	if (0 == st->used) {
632		/* we have nothing to send */
633		con->http_status = 204;
634		con->file_finished = 1;
635
636		return HANDLER_FINISHED;
637	}
638
639	b = buffer_init();
640	for (i = 0; i < st->used; i++) {
641		size_t ndx = st->sorted[i];
642
643		buffer_append_string_buffer(b, st->data[ndx]->key);
644		buffer_append_string_len(b, CONST_STR_LEN(": "));
645		buffer_append_int(b, ((data_integer *)(st->data[ndx]))->value);
646		buffer_append_string_len(b, CONST_STR_LEN("\n"));
647	}
648
649	chunkqueue_append_buffer(con->write_queue, b);
650	buffer_free(b);
651
652	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
653
654	con->http_status = 200;
655	con->file_finished = 1;
656
657	return HANDLER_FINISHED;
658}
659
660
661static handler_t mod_status_handle_server_status(server *srv, connection *con, void *p_d) {
662
663	if (buffer_is_equal_string(con->uri.query, CONST_STR_LEN("auto"))) {
664		mod_status_handle_server_status_text(srv, con, p_d);
665	} else {
666		mod_status_handle_server_status_html(srv, con, p_d);
667	}
668
669	con->http_status = 200;
670	con->file_finished = 1;
671
672	return HANDLER_FINISHED;
673}
674
675
676static handler_t mod_status_handle_server_config(server *srv, connection *con, void *p_d) {
677	plugin_data *p = p_d;
678	buffer *b = buffer_init();
679	buffer *m = p->module_list;
680	size_t i;
681
682	struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] =
683	{
684		/* - epoll is most reliable
685		 * - select works everywhere
686		 */
687#ifdef USE_LINUX_EPOLL
688		{ FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" },
689#endif
690#ifdef USE_POLL
691		{ FDEVENT_HANDLER_POLL,           "poll" },
692#endif
693#ifdef USE_SELECT
694		{ FDEVENT_HANDLER_SELECT,         "select" },
695#endif
696#ifdef USE_LIBEV
697		{ FDEVENT_HANDLER_LIBEV,          "libev" },
698#endif
699#ifdef USE_SOLARIS_DEVPOLL
700		{ FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" },
701#endif
702#ifdef USE_SOLARIS_PORT
703		{ FDEVENT_HANDLER_SOLARIS_PORT,   "solaris-eventports" },
704#endif
705#ifdef USE_FREEBSD_KQUEUE
706		{ FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" },
707#endif
708		{ FDEVENT_HANDLER_UNSET,          NULL }
709	};
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_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	chunkqueue_append_buffer(con->write_queue, b);
763	buffer_free(b);
764
765	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
766
767	con->http_status = 200;
768	con->file_finished = 1;
769
770	return HANDLER_FINISHED;
771}
772
773#define PATCH(x) \
774	p->conf.x = s->x;
775static int mod_status_patch_connection(server *srv, connection *con, plugin_data *p) {
776	size_t i, j;
777	plugin_config *s = p->config_storage[0];
778
779	PATCH(status_url);
780	PATCH(config_url);
781	PATCH(sort);
782	PATCH(statistics_url);
783
784	/* skip the first, the global context */
785	for (i = 1; i < srv->config_context->used; i++) {
786		data_config *dc = (data_config *)srv->config_context->data[i];
787		s = p->config_storage[i];
788
789		/* condition didn't match */
790		if (!config_check_cond(srv, con, dc)) continue;
791
792		/* merge config */
793		for (j = 0; j < dc->value->used; j++) {
794			data_unset *du = dc->value->data[j];
795
796			if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.status-url"))) {
797				PATCH(status_url);
798			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.config-url"))) {
799				PATCH(config_url);
800			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.enable-sort"))) {
801				PATCH(sort);
802			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.statistics-url"))) {
803				PATCH(statistics_url);
804			}
805		}
806	}
807
808	return 0;
809}
810
811static handler_t mod_status_handler(server *srv, connection *con, void *p_d) {
812	plugin_data *p = p_d;
813
814	if (con->mode != DIRECT) return HANDLER_GO_ON;
815
816	mod_status_patch_connection(srv, con, p);
817
818	if (!buffer_string_is_empty(p->conf.status_url) &&
819	    buffer_is_equal(p->conf.status_url, con->uri.path)) {
820		return mod_status_handle_server_status(srv, con, p_d);
821	} else if (!buffer_string_is_empty(p->conf.config_url) &&
822	    buffer_is_equal(p->conf.config_url, con->uri.path)) {
823		return mod_status_handle_server_config(srv, con, p_d);
824	} else if (!buffer_string_is_empty(p->conf.statistics_url) &&
825	    buffer_is_equal(p->conf.statistics_url, con->uri.path)) {
826		return mod_status_handle_server_statistics(srv, con, p_d);
827	}
828
829	return HANDLER_GO_ON;
830}
831
832TRIGGER_FUNC(mod_status_trigger) {
833	plugin_data *p = p_d;
834	size_t i;
835
836	/* check all connections */
837	for (i = 0; i < srv->conns->used; i++) {
838		connection *c = srv->conns->ptr[i];
839
840		p->bytes_written += c->bytes_written_cur_second;
841	}
842
843	/* a sliding average */
844	p->mod_5s_traffic_out[p->mod_5s_ndx] = p->bytes_written;
845	p->mod_5s_requests   [p->mod_5s_ndx] = p->requests;
846
847	p->mod_5s_ndx = (p->mod_5s_ndx+1) % 5;
848
849	p->abs_traffic_out += p->bytes_written;
850	p->rel_traffic_out += p->bytes_written;
851
852	p->bytes_written = 0;
853
854	/* reset storage - second */
855	p->traffic_out = 0;
856	p->requests    = 0;
857
858	return HANDLER_GO_ON;
859}
860
861REQUESTDONE_FUNC(mod_status_account) {
862	plugin_data *p = p_d;
863
864	UNUSED(srv);
865
866	p->requests++;
867	p->rel_requests++;
868	p->abs_requests++;
869
870	p->bytes_written += con->bytes_written_cur_second;
871
872	return HANDLER_GO_ON;
873}
874
875int mod_status_plugin_init(plugin *p);
876int mod_status_plugin_init(plugin *p) {
877	p->version     = LIGHTTPD_VERSION_ID;
878	p->name        = buffer_init_string("status");
879
880	p->init        = mod_status_init;
881	p->cleanup     = mod_status_free;
882	p->set_defaults= mod_status_set_defaults;
883
884	p->handle_uri_clean    = mod_status_handler;
885	p->handle_trigger      = mod_status_trigger;
886	p->handle_request_done = mod_status_account;
887
888	p->data        = NULL;
889
890	return 0;
891}
892