File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_status.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:20:06 2014 UTC (10 years ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_35p0, v1_4_35, HEAD
lighttpd 1.4.35

    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: 
   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: 		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: 
  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: 
  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: 
  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: 
  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: 
  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: 
  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: 
  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: 
  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: 
  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: 		{ FDEVENT_HANDLER_UNSET,          NULL }
  704: 	};
  705: 
  706: 	b = chunkqueue_get_append_buffer(con->write_queue);
  707: 
  708: 	buffer_copy_string_len(b, CONST_STR_LEN(
  709: 			   "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
  710: 			   "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
  711: 			   "         \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
  712: 			   "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
  713: 			   " <head>\n"
  714: 			   "  <title>Status</title>\n"
  715: 			   " </head>\n"
  716: 			   " <body>\n"
  717: 			   "  <h1>" PACKAGE_DESC "</h1>\n"
  718: 			   "  <table summary=\"status\" border=\"1\">\n"));
  719: 
  720: 	mod_status_header_append(b, "Server-Features");
  721: #ifdef HAVE_PCRE_H
  722: 	mod_status_row_append(b, "RegEx Conditionals", "enabled");
  723: #else
  724: 	mod_status_row_append(b, "RegEx Conditionals", "disabled - pcre missing");
  725: #endif
  726: 	mod_status_header_append(b, "Network Engine");
  727: 
  728: 	for (i = 0; event_handlers[i].name; i++) {
  729: 		if (event_handlers[i].et == srv->event_handler) {
  730: 			mod_status_row_append(b, "fd-Event-Handler", event_handlers[i].name);
  731: 			break;
  732: 		}
  733: 	}
  734: 
  735: 	mod_status_header_append(b, "Config-File-Settings");
  736: 
  737: 	for (i = 0; i < srv->plugins.used; i++) {
  738: 		plugin **ps = srv->plugins.ptr;
  739: 
  740: 		plugin *pl = ps[i];
  741: 
  742: 		if (i == 0) {
  743: 			buffer_copy_string_buffer(m, pl->name);
  744: 		} else {
  745: 			buffer_append_string_len(m, CONST_STR_LEN("<br />"));
  746: 			buffer_append_string_buffer(m, pl->name);
  747: 		}
  748: 	}
  749: 
  750: 	mod_status_row_append(b, "Loaded Modules", m->ptr);
  751: 
  752: 	buffer_append_string_len(b, CONST_STR_LEN("  </table>\n"));
  753: 
  754: 	buffer_append_string_len(b, CONST_STR_LEN(
  755: 		      " </body>\n"
  756: 		      "</html>\n"
  757: 		      ));
  758: 
  759: 	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
  760: 
  761: 	con->http_status = 200;
  762: 	con->file_finished = 1;
  763: 
  764: 	return HANDLER_FINISHED;
  765: }
  766: 
  767: #define PATCH(x) \
  768: 	p->conf.x = s->x;
  769: static int mod_status_patch_connection(server *srv, connection *con, plugin_data *p) {
  770: 	size_t i, j;
  771: 	plugin_config *s = p->config_storage[0];
  772: 
  773: 	PATCH(status_url);
  774: 	PATCH(config_url);
  775: 	PATCH(sort);
  776: 	PATCH(statistics_url);
  777: 
  778: 	/* skip the first, the global context */
  779: 	for (i = 1; i < srv->config_context->used; i++) {
  780: 		data_config *dc = (data_config *)srv->config_context->data[i];
  781: 		s = p->config_storage[i];
  782: 
  783: 		/* condition didn't match */
  784: 		if (!config_check_cond(srv, con, dc)) continue;
  785: 
  786: 		/* merge config */
  787: 		for (j = 0; j < dc->value->used; j++) {
  788: 			data_unset *du = dc->value->data[j];
  789: 
  790: 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.status-url"))) {
  791: 				PATCH(status_url);
  792: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.config-url"))) {
  793: 				PATCH(config_url);
  794: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.enable-sort"))) {
  795: 				PATCH(sort);
  796: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.statistics-url"))) {
  797: 				PATCH(statistics_url);
  798: 			}
  799: 		}
  800: 	}
  801: 
  802: 	return 0;
  803: }
  804: 
  805: static handler_t mod_status_handler(server *srv, connection *con, void *p_d) {
  806: 	plugin_data *p = p_d;
  807: 
  808: 	if (con->mode != DIRECT) return HANDLER_GO_ON;
  809: 
  810: 	mod_status_patch_connection(srv, con, p);
  811: 
  812: 	if (!buffer_is_empty(p->conf.status_url) &&
  813: 	    buffer_is_equal(p->conf.status_url, con->uri.path)) {
  814: 		return mod_status_handle_server_status(srv, con, p_d);
  815: 	} else if (!buffer_is_empty(p->conf.config_url) &&
  816: 	    buffer_is_equal(p->conf.config_url, con->uri.path)) {
  817: 		return mod_status_handle_server_config(srv, con, p_d);
  818: 	} else if (!buffer_is_empty(p->conf.statistics_url) &&
  819: 	    buffer_is_equal(p->conf.statistics_url, con->uri.path)) {
  820: 		return mod_status_handle_server_statistics(srv, con, p_d);
  821: 	}
  822: 
  823: 	return HANDLER_GO_ON;
  824: }
  825: 
  826: TRIGGER_FUNC(mod_status_trigger) {
  827: 	plugin_data *p = p_d;
  828: 	size_t i;
  829: 
  830: 	/* check all connections */
  831: 	for (i = 0; i < srv->conns->used; i++) {
  832: 		connection *c = srv->conns->ptr[i];
  833: 
  834: 		p->bytes_written += c->bytes_written_cur_second;
  835: 	}
  836: 
  837: 	/* a sliding average */
  838: 	p->mod_5s_traffic_out[p->mod_5s_ndx] = p->bytes_written;
  839: 	p->mod_5s_requests   [p->mod_5s_ndx] = p->requests;
  840: 
  841: 	p->mod_5s_ndx = (p->mod_5s_ndx+1) % 5;
  842: 
  843: 	p->abs_traffic_out += p->bytes_written;
  844: 	p->rel_traffic_out += p->bytes_written;
  845: 
  846: 	p->bytes_written = 0;
  847: 
  848: 	/* reset storage - second */
  849: 	p->traffic_out = 0;
  850: 	p->requests    = 0;
  851: 
  852: 	return HANDLER_GO_ON;
  853: }
  854: 
  855: REQUESTDONE_FUNC(mod_status_account) {
  856: 	plugin_data *p = p_d;
  857: 
  858: 	UNUSED(srv);
  859: 
  860: 	p->requests++;
  861: 	p->rel_requests++;
  862: 	p->abs_requests++;
  863: 
  864: 	p->bytes_written += con->bytes_written_cur_second;
  865: 
  866: 	return HANDLER_GO_ON;
  867: }
  868: 
  869: int mod_status_plugin_init(plugin *p);
  870: int mod_status_plugin_init(plugin *p) {
  871: 	p->version     = LIGHTTPD_VERSION_ID;
  872: 	p->name        = buffer_init_string("status");
  873: 
  874: 	p->init        = mod_status_init;
  875: 	p->cleanup     = mod_status_free;
  876: 	p->set_defaults= mod_status_set_defaults;
  877: 
  878: 	p->handle_uri_clean    = mod_status_handler;
  879: 	p->handle_trigger      = mod_status_trigger;
  880: 	p->handle_request_done = mod_status_account;
  881: 
  882: 	p->data        = NULL;
  883: 
  884: 	return 0;
  885: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>