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 = '&uarr;';\n"
281 					   "  span.setAttribute('sortdir','up');\n"
282 					   "  rows.reverse();\n"
283 					   " } else {\n"
284 					   "  span.innerHTML = '&darr;';\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