File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_status.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:35:00 2016 UTC (7 years, 8 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_41p8, HEAD
lighttpd 1.4.41

    1: #include "first.h"
    2: 
    3: #include "server.h"
    4: #include "connections.h"
    5: #include "response.h"
    6: #include "connections.h"
    7: #include "log.h"
    8: 
    9: #include "plugin.h"
   10: 
   11: #include "inet_ntop_cache.h"
   12: 
   13: #include <sys/types.h>
   14: 
   15: #include <fcntl.h>
   16: #include <stdlib.h>
   17: #include <string.h>
   18: #include <unistd.h>
   19: #include <errno.h>
   20: #include <time.h>
   21: #include <stdio.h>
   22: 
   23: typedef struct {
   24: 	buffer *config_url;
   25: 	buffer *status_url;
   26: 	buffer *statistics_url;
   27: 
   28: 	int     sort;
   29: } plugin_config;
   30: 
   31: typedef struct {
   32: 	PLUGIN_DATA;
   33: 
   34: 	double traffic_out;
   35: 	double requests;
   36: 
   37: 	double mod_5s_traffic_out[5];
   38: 	double mod_5s_requests[5];
   39: 	size_t mod_5s_ndx;
   40: 
   41: 	double rel_traffic_out;
   42: 	double rel_requests;
   43: 
   44: 	double abs_traffic_out;
   45: 	double abs_requests;
   46: 
   47: 	double bytes_written;
   48: 
   49: 	buffer *module_list;
   50: 
   51: 	plugin_config **config_storage;
   52: 
   53: 	plugin_config conf;
   54: } plugin_data;
   55: 
   56: INIT_FUNC(mod_status_init) {
   57: 	plugin_data *p;
   58: 	size_t i;
   59: 
   60: 	p = calloc(1, sizeof(*p));
   61: 
   62: 	p->traffic_out = p->requests = 0;
   63: 	p->rel_traffic_out = p->rel_requests = 0;
   64: 	p->abs_traffic_out = p->abs_requests = 0;
   65: 	p->bytes_written = 0;
   66: 	p->module_list = buffer_init();
   67: 
   68: 	for (i = 0; i < 5; i++) {
   69: 		p->mod_5s_traffic_out[i] = p->mod_5s_requests[i] = 0;
   70: 	}
   71: 
   72: 	return p;
   73: }
   74: 
   75: FREE_FUNC(mod_status_free) {
   76: 	plugin_data *p = p_d;
   77: 
   78: 	UNUSED(srv);
   79: 
   80: 	if (!p) return HANDLER_GO_ON;
   81: 
   82: 	buffer_free(p->module_list);
   83: 
   84: 	if (p->config_storage) {
   85: 		size_t i;
   86: 		for (i = 0; i < srv->config_context->used; i++) {
   87: 			plugin_config *s = p->config_storage[i];
   88: 
   89: 			buffer_free(s->status_url);
   90: 			buffer_free(s->statistics_url);
   91: 			buffer_free(s->config_url);
   92: 
   93: 			free(s);
   94: 		}
   95: 		free(p->config_storage);
   96: 	}
   97: 
   98: 
   99: 	free(p);
  100: 
  101: 	return HANDLER_GO_ON;
  102: }
  103: 
  104: SETDEFAULTS_FUNC(mod_status_set_defaults) {
  105: 	plugin_data *p = p_d;
  106: 	size_t i;
  107: 
  108: 	config_values_t cv[] = {
  109: 		{ "status.status-url",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  110: 		{ "status.config-url",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  111: 		{ "status.enable-sort",          NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
  112: 		{ "status.statistics-url",       NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  113: 		{ NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
  114: 	};
  115: 
  116: 	if (!p) return HANDLER_ERROR;
  117: 
  118: 	p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
  119: 
  120: 	for (i = 0; i < srv->config_context->used; i++) {
  121: 		data_config const* config = (data_config const*)srv->config_context->data[i];
  122: 		plugin_config *s;
  123: 
  124: 		s = calloc(1, sizeof(plugin_config));
  125: 		s->config_url    = buffer_init();
  126: 		s->status_url    = buffer_init();
  127: 		s->sort          = 1;
  128: 		s->statistics_url    = buffer_init();
  129: 
  130: 		cv[0].destination = s->status_url;
  131: 		cv[1].destination = s->config_url;
  132: 		cv[2].destination = &(s->sort);
  133: 		cv[3].destination = s->statistics_url;
  134: 
  135: 		p->config_storage[i] = s;
  136: 
  137: 		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
  138: 			return HANDLER_ERROR;
  139: 		}
  140: 	}
  141: 
  142: 	return HANDLER_GO_ON;
  143: }
  144: 
  145: 
  146: 
  147: static int mod_status_row_append(buffer *b, const char *key, const char *value) {
  148: 	buffer_append_string_len(b, CONST_STR_LEN("   <tr>\n"));
  149: 	buffer_append_string_len(b, CONST_STR_LEN("    <td><b>"));
  150: 	buffer_append_string(b, key);
  151: 	buffer_append_string_len(b, CONST_STR_LEN("</b></td>\n"));
  152: 	buffer_append_string_len(b, CONST_STR_LEN("    <td>"));
  153: 	buffer_append_string(b, value);
  154: 	buffer_append_string_len(b, CONST_STR_LEN("</td>\n"));
  155: 	buffer_append_string_len(b, CONST_STR_LEN("   </tr>\n"));
  156: 
  157: 	return 0;
  158: }
  159: 
  160: static int mod_status_header_append(buffer *b, const char *key) {
  161: 	buffer_append_string_len(b, CONST_STR_LEN("   <tr>\n"));
  162: 	buffer_append_string_len(b, CONST_STR_LEN("    <th colspan=\"2\">"));
  163: 	buffer_append_string(b, key);
  164: 	buffer_append_string_len(b, CONST_STR_LEN("</th>\n"));
  165: 	buffer_append_string_len(b, CONST_STR_LEN("   </tr>\n"));
  166: 
  167: 	return 0;
  168: }
  169: 
  170: static int mod_status_header_append_sort(buffer *b, void *p_d, const char* key) {
  171: 	plugin_data *p = p_d;
  172: 
  173: 	if (p->conf.sort) {
  174: 		buffer_append_string_len(b, CONST_STR_LEN("<th class=\"status\"><a href=\"#\" class=\"sortheader\" onclick=\"resort(this);return false;\">"));
  175: 		buffer_append_string(b, key);
  176: 		buffer_append_string_len(b, CONST_STR_LEN("<span class=\"sortarrow\">:</span></a></th>\n"));
  177: 	} else {
  178: 		buffer_append_string_len(b, CONST_STR_LEN("<th class=\"status\">"));
  179: 		buffer_append_string(b, key);
  180: 		buffer_append_string_len(b, CONST_STR_LEN("</th>\n"));
  181: 	}
  182: 
  183: 	return 0;
  184: }
  185: 
  186: static int mod_status_get_multiplier(double *avg, char *multiplier, int size) {
  187: 	*multiplier = ' ';
  188: 
  189: 	if (*avg > size) { *avg /= size; *multiplier = 'k'; }
  190: 	if (*avg > size) { *avg /= size; *multiplier = 'M'; }
  191: 	if (*avg > size) { *avg /= size; *multiplier = 'G'; }
  192: 	if (*avg > size) { *avg /= size; *multiplier = 'T'; }
  193: 	if (*avg > size) { *avg /= size; *multiplier = 'P'; }
  194: 	if (*avg > size) { *avg /= size; *multiplier = 'E'; }
  195: 	if (*avg > size) { *avg /= size; *multiplier = 'Z'; }
  196: 	if (*avg > size) { *avg /= size; *multiplier = 'Y'; }
  197: 
  198: 	return 0;
  199: }
  200: 
  201: static handler_t mod_status_handle_server_status_html(server *srv, connection *con, void *p_d) {
  202: 	plugin_data *p = p_d;
  203: 	buffer *b = buffer_init();
  204: 	size_t j;
  205: 	double avg;
  206: 	char multiplier = '\0';
  207: 	char buf[32];
  208: 	time_t ts;
  209: 
  210: 	int days, hours, mins, seconds;
  211: 
  212: 	/*(CON_STATE_CLOSE must be last state in enum connection_state_t)*/
  213: 	int cstates[CON_STATE_CLOSE+3];
  214: 	memset(cstates, 0, sizeof(cstates));
  215: 
  216: 	buffer_copy_string_len(b, CONST_STR_LEN(
  217: 				 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
  218: 				 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
  219: 				 "         \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
  220: 				 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
  221: 				 " <head>\n"
  222: 				 "  <title>Status</title>\n"
  223: 
  224: 				   "  <style type=\"text/css\">\n"
  225: 				   "    table.status { border: black solid thin; }\n"
  226: 				   "    td { white-space: nowrap; }\n"
  227: 				   "    td.int { background-color: #f0f0f0; text-align: right }\n"
  228: 				   "    td.string { background-color: #f0f0f0; text-align: left }\n"
  229: 				   "    th.status { background-color: black; color: white; font-weight: bold; }\n"
  230: 				   "    a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n"
  231: 				   "    span.sortarrow { color: white; text-decoration: none; }\n"
  232: 				   "  </style>\n"));
  233: 
  234: 	if (!buffer_string_is_empty(con->uri.query) && 0 == memcmp(con->uri.query->ptr, CONST_STR_LEN("refresh="))) {
  235: 		/* Note: Refresh is an historical, but non-standard HTTP header
  236: 		 * References (meta http-equiv="refresh" use is deprecated):
  237: 		 *   https://www.w3.org/TR/WCAG10-HTML-TECHS/#meta-element
  238: 		 *   https://www.w3.org/TR/WCAG10-CORE-TECHS/#auto-page-refresh
  239: 		 *   https://www.w3.org/QA/Tips/reback
  240: 		 */
  241: 		const long refresh = strtol(con->uri.query->ptr+sizeof("refresh=")-1, NULL, 10);
  242: 		if (refresh > 0) {
  243: 			buffer_append_string_len(b, CONST_STR_LEN("<meta http-equiv=\"refresh\" content=\""));
  244: 			buffer_append_int(b, refresh < 604800 ? refresh : 604800);
  245: 			buffer_append_string_len(b, CONST_STR_LEN("\">\n"));
  246: 		}
  247: 	}
  248: 
  249: 	if (p->conf.sort) {
  250: 		buffer_append_string_len(b, CONST_STR_LEN(
  251: 					   "<script type=\"text/javascript\">\n"
  252: 					   "// <!--\n"
  253: 					   "var sort_column;\n"
  254: 					   "var prev_span = null;\n"
  255: 
  256: 					   "function get_inner_text(el) {\n"
  257: 					   " if((typeof el == 'string')||(typeof el == 'undefined'))\n"
  258: 					   "  return el;\n"
  259: 					   " if(el.innerText)\n"
  260: 					   "  return el.innerText;\n"
  261: 					   " else {\n"
  262: 					   "  var str = \"\";\n"
  263: 					   "  var cs = el.childNodes;\n"
  264: 					   "  var l = cs.length;\n"
  265: 					   "  for (i=0;i<l;i++) {\n"
  266: 					   "   if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n"
  267: 					   "   else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n"
  268: 					   "  }\n"
  269: 					   " }\n"
  270: 					   " return str;\n"
  271: 					   "}\n"
  272: 
  273: 					   "function sortfn(a,b) {\n"
  274: 					   " var at = get_inner_text(a.cells[sort_column]);\n"
  275: 					   " var bt = get_inner_text(b.cells[sort_column]);\n"
  276: 					   " if (a.cells[sort_column].className == 'int') {\n"
  277: 					   "  return parseInt(at)-parseInt(bt);\n"
  278: 					   " } else {\n"
  279: 					   "  aa = at.toLowerCase();\n"
  280: 					   "  bb = bt.toLowerCase();\n"
  281: 					   "  if (aa==bb) return 0;\n"
  282: 					   "  else if (aa<bb) return -1;\n"
  283: 					   "  else return 1;\n"
  284: 					   " }\n"
  285: 					   "}\n"
  286: 
  287: 					   "function resort(lnk) {\n"
  288: 					   " var span = lnk.childNodes[1];\n"
  289: 					   " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n"
  290: 					   " var rows = new Array();\n"
  291: 					   " for (j=1;j<table.rows.length;j++)\n"
  292: 					   "  rows[j-1] = table.rows[j];\n"
  293: 					   " sort_column = lnk.parentNode.cellIndex;\n"
  294: 					   " rows.sort(sortfn);\n"
  295: 
  296: 					   " if (prev_span != null) prev_span.innerHTML = '';\n"
  297: 					   " if (span.getAttribute('sortdir')=='down') {\n"
  298: 					   "  span.innerHTML = '&uarr;';\n"
  299: 					   "  span.setAttribute('sortdir','up');\n"
  300: 					   "  rows.reverse();\n"
  301: 					   " } else {\n"
  302: 					   "  span.innerHTML = '&darr;';\n"
  303: 					   "  span.setAttribute('sortdir','down');\n"
  304: 					   " }\n"
  305: 					   " for (i=0;i<rows.length;i++)\n"
  306: 					   "  table.tBodies[0].appendChild(rows[i]);\n"
  307: 					   " prev_span = span;\n"
  308: 					   "}\n"
  309: 					   "// -->\n"
  310: 					   "</script>\n"));
  311: 	}
  312: 
  313: 	buffer_append_string_len(b, CONST_STR_LEN(
  314: 				 " </head>\n"
  315: 				 " <body>\n"));
  316: 
  317: 
  318: 
  319: 	/* connection listing */
  320: 	buffer_append_string_len(b, CONST_STR_LEN("<h1>Server-Status ("));
  321: 	buffer_append_string_buffer(b, con->conf.server_tag);
  322: 	buffer_append_string_len(b, CONST_STR_LEN(")</h1>"));
  323: 
  324: 	buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">"));
  325: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Hostname</td><td class=\"string\">"));
  326: 	buffer_append_string_buffer(b, con->uri.authority);
  327: 	buffer_append_string_len(b, CONST_STR_LEN(" ("));
  328: 	buffer_append_string_buffer(b, con->server_name);
  329: 	buffer_append_string_len(b, CONST_STR_LEN(")</td></tr>\n"));
  330: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Uptime</td><td class=\"string\">"));
  331: 
  332: 	ts = srv->cur_ts - srv->startup_ts;
  333: 
  334: 	days = ts / (60 * 60 * 24);
  335: 	ts %= (60 * 60 * 24);
  336: 
  337: 	hours = ts / (60 * 60);
  338: 	ts %= (60 * 60);
  339: 
  340: 	mins = ts / (60);
  341: 	ts %= (60);
  342: 
  343: 	seconds = ts;
  344: 
  345: 	if (days) {
  346: 		buffer_append_int(b, days);
  347: 		buffer_append_string_len(b, CONST_STR_LEN(" days "));
  348: 	}
  349: 
  350: 	if (hours) {
  351: 		buffer_append_int(b, hours);
  352: 		buffer_append_string_len(b, CONST_STR_LEN(" hours "));
  353: 	}
  354: 
  355: 	if (mins) {
  356: 		buffer_append_int(b, mins);
  357: 		buffer_append_string_len(b, CONST_STR_LEN(" min "));
  358: 	}
  359: 
  360: 	buffer_append_int(b, seconds);
  361: 	buffer_append_string_len(b, CONST_STR_LEN(" s"));
  362: 
  363: 	buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));
  364: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Started at</td><td class=\"string\">"));
  365: 
  366: 	ts = srv->startup_ts;
  367: 
  368: 	strftime(buf, sizeof(buf) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts));
  369: 	buffer_append_string(b, buf);
  370: 	buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));
  371: 
  372: 
  373: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">absolute (since start)</th></tr>\n"));
  374: 
  375: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
  376: 	avg = p->abs_requests;
  377: 
  378: 	mod_status_get_multiplier(&avg, &multiplier, 1000);
  379: 
  380: 	buffer_append_int(b, avg);
  381: 	buffer_append_string_len(b, CONST_STR_LEN(" "));
  382: 	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
  383: 	buffer_append_string_len(b, CONST_STR_LEN("req</td></tr>\n"));
  384: 
  385: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
  386: 	avg = p->abs_traffic_out;
  387: 
  388: 	mod_status_get_multiplier(&avg, &multiplier, 1024);
  389: 
  390: 	snprintf(buf, sizeof(buf), "%.2f", avg);
  391: 	buffer_append_string(b, buf);
  392: 	buffer_append_string_len(b, CONST_STR_LEN(" "));
  393: 	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
  394: 	buffer_append_string_len(b, CONST_STR_LEN("byte</td></tr>\n"));
  395: 
  396: 
  397: 
  398: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (since start)</th></tr>\n"));
  399: 
  400: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
  401: 	avg = p->abs_requests / (srv->cur_ts - srv->startup_ts);
  402: 
  403: 	mod_status_get_multiplier(&avg, &multiplier, 1000);
  404: 
  405: 	buffer_append_int(b, avg);
  406: 	buffer_append_string_len(b, CONST_STR_LEN(" "));
  407: 	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
  408: 	buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n"));
  409: 
  410: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
  411: 	avg = p->abs_traffic_out / (srv->cur_ts - srv->startup_ts);
  412: 
  413: 	mod_status_get_multiplier(&avg, &multiplier, 1024);
  414: 
  415: 	snprintf(buf, sizeof(buf), "%.2f", avg);
  416: 	buffer_append_string(b, buf);
  417: 	buffer_append_string_len(b, CONST_STR_LEN(" "));
  418: 	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
  419: 	buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n"));
  420: 
  421: 
  422: 
  423: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n"));
  424: 	for (j = 0, avg = 0; j < 5; j++) {
  425: 		avg += p->mod_5s_requests[j];
  426: 	}
  427: 
  428: 	avg /= 5;
  429: 
  430: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">"));
  431: 
  432: 	mod_status_get_multiplier(&avg, &multiplier, 1000);
  433: 
  434: 	buffer_append_int(b, avg);
  435: 	buffer_append_string_len(b, CONST_STR_LEN(" "));
  436: 	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
  437: 
  438: 	buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n"));
  439: 
  440: 	for (j = 0, avg = 0; j < 5; j++) {
  441: 		avg += p->mod_5s_traffic_out[j];
  442: 	}
  443: 
  444: 	avg /= 5;
  445: 
  446: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">"));
  447: 
  448: 	mod_status_get_multiplier(&avg, &multiplier, 1024);
  449: 
  450: 	snprintf(buf, sizeof(buf), "%.2f", avg);
  451: 	buffer_append_string(b, buf);
  452: 	buffer_append_string_len(b, CONST_STR_LEN(" "));
  453: 	if (multiplier)	buffer_append_string_len(b, &multiplier, 1);
  454: 	buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n"));
  455: 
  456: 	buffer_append_string_len(b, CONST_STR_LEN("</table>\n"));
  457: 
  458: 	buffer_append_string_len(b, CONST_STR_LEN("<hr />\n<pre>\n"));
  459: 
  460: 	buffer_append_string_len(b, CONST_STR_LEN("<b>"));
  461: 	buffer_append_int(b, srv->conns->used);
  462: 	buffer_append_string_len(b, CONST_STR_LEN(" connections</b>\n"));
  463: 
  464: 	for (j = 0; j < srv->conns->used; j++) {
  465: 		connection *c = srv->conns->ptr[j];
  466: 		const char *state;
  467: 
  468: 		if (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) {
  469: 			state = "k";
  470: 			++cstates[CON_STATE_CLOSE+2];
  471: 		} else {
  472: 			state = connection_get_short_state(c->state);
  473: 			++cstates[(c->state <= CON_STATE_CLOSE ? c->state : CON_STATE_CLOSE+1)];
  474: 		}
  475: 
  476: 		buffer_append_string_len(b, state, 1);
  477: 
  478: 		if (((j + 1) % 50) == 0) {
  479: 			buffer_append_string_len(b, CONST_STR_LEN("\n"));
  480: 		}
  481: 	}
  482: 	buffer_append_string_len(b, CONST_STR_LEN("\n\n<table>\n"));
  483: 	buffer_append_string_len(b, CONST_STR_LEN("<tr><td style=\"text-align:right\">"));
  484: 	buffer_append_int(b, cstates[CON_STATE_CLOSE+2]);
  485: 	buffer_append_string_len(b, CONST_STR_LEN("<td>&nbsp;&nbsp;k = keep-alive</td></tr>\n"));
  486: 	for (j = 0; j < CON_STATE_CLOSE+2; ++j) {
  487: 		/*(skip "unknown" state if there are none; there should not be any unknown)*/
  488: 		if (0 == cstates[j] && j == CON_STATE_CLOSE+1) continue;
  489: 		buffer_append_string_len(b, CONST_STR_LEN("<tr><td style=\"text-align:right\">"));
  490: 		buffer_append_int(b, cstates[j]);
  491: 		buffer_append_string_len(b, CONST_STR_LEN("</td><td>&nbsp;&nbsp;"));
  492: 		buffer_append_string_len(b, connection_get_short_state(j), 1);
  493: 		buffer_append_string_len(b, CONST_STR_LEN(" = "));
  494: 		buffer_append_string(b, connection_get_state(j));
  495: 		buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));
  496: 	}
  497: 	buffer_append_string_len(b, CONST_STR_LEN("</table>"));
  498: 
  499: 	buffer_append_string_len(b, CONST_STR_LEN("\n</pre><hr />\n<h2>Connections</h2>\n"));
  500: 
  501: 	buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">\n"));
  502: 	buffer_append_string_len(b, CONST_STR_LEN("<tr>"));
  503: 	mod_status_header_append_sort(b, p_d, "Client IP");
  504: 	mod_status_header_append_sort(b, p_d, "Read");
  505: 	mod_status_header_append_sort(b, p_d, "Written");
  506: 	mod_status_header_append_sort(b, p_d, "State");
  507: 	mod_status_header_append_sort(b, p_d, "Time");
  508: 	mod_status_header_append_sort(b, p_d, "Host");
  509: 	mod_status_header_append_sort(b, p_d, "URI");
  510: 	mod_status_header_append_sort(b, p_d, "File");
  511: 	buffer_append_string_len(b, CONST_STR_LEN("</tr>\n"));
  512: 
  513: 	for (j = 0; j < srv->conns->used; j++) {
  514: 		connection *c = srv->conns->ptr[j];
  515: 
  516: 		buffer_append_string_len(b, CONST_STR_LEN("<tr><td class=\"string\">"));
  517: 
  518: 		buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(c->dst_addr)));
  519: 
  520: 		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
  521: 
  522: 		if (c->request.content_length) {
  523: 			buffer_append_int(b, c->request_content_queue->bytes_in);
  524: 			buffer_append_string_len(b, CONST_STR_LEN("/"));
  525: 			buffer_append_int(b, c->request.content_length);
  526: 		} else {
  527: 			buffer_append_string_len(b, CONST_STR_LEN("0/0"));
  528: 		}
  529: 
  530: 		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
  531: 
  532: 		buffer_append_int(b, c->write_queue->bytes_out);
  533: 		buffer_append_string_len(b, CONST_STR_LEN("/"));
  534: 		buffer_append_int(b, c->write_queue->bytes_out + chunkqueue_length(c->write_queue));
  535: 
  536: 		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
  537: 
  538: 		if (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) {
  539: 			buffer_append_string_len(b, CONST_STR_LEN("keep-alive"));
  540: 		} else {
  541: 			buffer_append_string(b, connection_get_state(c->state));
  542: 		}
  543: 
  544: 		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">"));
  545: 
  546: 		buffer_append_int(b, srv->cur_ts - c->request_start);
  547: 
  548: 		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
  549: 
  550: 		if (buffer_string_is_empty(c->server_name)) {
  551: 			buffer_append_string_buffer(b, c->uri.authority);
  552: 		}
  553: 		else {
  554: 			buffer_append_string_buffer(b, c->server_name);
  555: 		}
  556: 
  557: 		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
  558: 
  559: 		if (!buffer_string_is_empty(c->uri.path)) {
  560: 			buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML);
  561: 		}
  562: 
  563: 		if (!buffer_string_is_empty(c->uri.query)) {
  564: 			buffer_append_string_len(b, CONST_STR_LEN("?"));
  565: 			buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.query), ENCODING_HTML);
  566: 		}
  567: 
  568: 		if (!buffer_string_is_empty(c->request.orig_uri)) {
  569: 			buffer_append_string_len(b, CONST_STR_LEN(" ("));
  570: 			buffer_append_string_encoded(b, CONST_BUF_LEN(c->request.orig_uri), ENCODING_HTML);
  571: 			buffer_append_string_len(b, CONST_STR_LEN(")"));
  572: 		}
  573: 		buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">"));
  574: 
  575: 		buffer_append_string_buffer(b, c->physical.path);
  576: 
  577: 		buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n"));
  578: 	}
  579: 
  580: 
  581: 	buffer_append_string_len(b, CONST_STR_LEN(
  582: 		      "</table>\n"));
  583: 
  584: 
  585: 	buffer_append_string_len(b, CONST_STR_LEN(
  586: 		      " </body>\n"
  587: 		      "</html>\n"
  588: 		      ));
  589: 
  590: 	chunkqueue_append_buffer(con->write_queue, b);
  591: 	buffer_free(b);
  592: 
  593: 	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
  594: 
  595: 	return 0;
  596: }
  597: 
  598: 
  599: static handler_t mod_status_handle_server_status_text(server *srv, connection *con, void *p_d) {
  600: 	plugin_data *p = p_d;
  601: 	buffer *b = buffer_init();
  602: 	double avg;
  603: 	time_t ts;
  604: 	char buf[32];
  605: 	unsigned int k;
  606: 	unsigned int l;
  607: 
  608: 	/* output total number of requests */
  609: 	buffer_append_string_len(b, CONST_STR_LEN("Total Accesses: "));
  610: 	avg = p->abs_requests;
  611: 	snprintf(buf, sizeof(buf) - 1, "%.0f", avg);
  612: 	buffer_append_string(b, buf);
  613: 	buffer_append_string_len(b, CONST_STR_LEN("\n"));
  614: 
  615: 	/* output total traffic out in kbytes */
  616: 	buffer_append_string_len(b, CONST_STR_LEN("Total kBytes: "));
  617: 	avg = p->abs_traffic_out / 1024;
  618: 	snprintf(buf, sizeof(buf) - 1, "%.0f", avg);
  619: 	buffer_append_string(b, buf);
  620: 	buffer_append_string_len(b, CONST_STR_LEN("\n"));
  621: 
  622: 	/* output uptime */
  623: 	buffer_append_string_len(b, CONST_STR_LEN("Uptime: "));
  624: 	ts = srv->cur_ts - srv->startup_ts;
  625: 	buffer_append_int(b, ts);
  626: 	buffer_append_string_len(b, CONST_STR_LEN("\n"));
  627: 
  628: 	/* output busy servers */
  629: 	buffer_append_string_len(b, CONST_STR_LEN("BusyServers: "));
  630: 	buffer_append_int(b, srv->conns->used);
  631: 	buffer_append_string_len(b, CONST_STR_LEN("\n"));
  632: 
  633: 	buffer_append_string_len(b, CONST_STR_LEN("IdleServers: "));
  634: 	buffer_append_int(b, srv->conns->size - srv->conns->used);
  635: 	buffer_append_string_len(b, CONST_STR_LEN("\n"));
  636: 
  637: 	/* output scoreboard */
  638: 	buffer_append_string_len(b, CONST_STR_LEN("Scoreboard: "));
  639: 	for (k = 0; k < srv->conns->used; k++) {
  640: 		connection *c = srv->conns->ptr[k];
  641: 		const char *state =
  642: 		  (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri))
  643: 		    ? "k"
  644: 		    : connection_get_short_state(c->state);
  645: 		buffer_append_string_len(b, state, 1);
  646: 	}
  647: 	for (l = 0; l < srv->conns->size - srv->conns->used; l++) {
  648: 		buffer_append_string_len(b, CONST_STR_LEN("_"));
  649: 	}
  650: 	buffer_append_string_len(b, CONST_STR_LEN("\n"));
  651: 
  652: 	chunkqueue_append_buffer(con->write_queue, b);
  653: 	buffer_free(b);
  654: 
  655: 	/* set text/plain output */
  656: 	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
  657: 
  658: 	return 0;
  659: }
  660: 
  661: 
  662: static handler_t mod_status_handle_server_status_json(server *srv, connection *con, void *p_d) {
  663: 	plugin_data *p = p_d;
  664: 	buffer *b = buffer_init();
  665: 	double avg;
  666: 	time_t ts;
  667: 	char buf[32];
  668: 	size_t j;
  669: 	unsigned int jsonp = 0;
  670: 
  671: 	if (buffer_string_length(con->uri.query) >= sizeof("jsonp=")-1
  672: 	   && 0 == memcmp(con->uri.query->ptr, CONST_STR_LEN("jsonp="))) {
  673: 		/* not a full parse of query string for multiple parameters,
  674: 		* not URL-decoding param and not XML-encoding (XSS protection),
  675: 		* so simply ensure that json function name isalnum() or '_' */
  676: 		const char *f = con->uri.query->ptr + sizeof("jsonp=")-1;
  677: 		int len = 0;
  678: 		while (light_isalnum(f[len]) || f[len] == '_') ++len;
  679: 		if (0 != len && light_isalpha(f[0]) && f[len] == '\0') {
  680: 			buffer_append_string_len(b, f, len);
  681: 			buffer_append_string_len(b, CONST_STR_LEN("("));
  682: 			jsonp = 1;
  683: 		}
  684: 	}
  685: 
  686: 	/* output total number of requests */
  687: 	buffer_append_string_len(b, CONST_STR_LEN("{\n\t\"RequestsTotal\": "));
  688: 	avg = p->abs_requests;
  689: 	snprintf(buf, sizeof(buf) - 1, "%.0f", avg);
  690: 	buffer_append_string(b, buf);
  691: 	buffer_append_string_len(b, CONST_STR_LEN(",\n"));
  692: 
  693: 	/* output total traffic out in kbytes */
  694: 	buffer_append_string_len(b, CONST_STR_LEN("\t\"TrafficTotal\": "));
  695: 	avg = p->abs_traffic_out / 1024;
  696: 	snprintf(buf, sizeof(buf) - 1, "%.0f", avg);
  697: 	buffer_append_string(b, buf);
  698: 	buffer_append_string_len(b, CONST_STR_LEN(",\n"));
  699: 
  700: 	/* output uptime */
  701: 	buffer_append_string_len(b, CONST_STR_LEN("\t\"Uptime\": "));
  702: 	ts = srv->cur_ts - srv->startup_ts;
  703: 	buffer_append_int(b, ts);
  704: 	buffer_append_string_len(b, CONST_STR_LEN(",\n"));
  705: 
  706: 	/* output busy servers */
  707: 	buffer_append_string_len(b, CONST_STR_LEN("\t\"BusyServers\": "));
  708: 	buffer_append_int(b, srv->conns->used);
  709: 	buffer_append_string_len(b, CONST_STR_LEN(",\n"));
  710: 
  711: 	buffer_append_string_len(b, CONST_STR_LEN("\t\"IdleServers\": "));
  712: 	buffer_append_int(b, srv->conns->size - srv->conns->used);
  713: 	buffer_append_string_len(b, CONST_STR_LEN(",\n"));
  714: 
  715: 	for (j = 0, avg = 0; j < 5; j++) {
  716: 		avg += p->mod_5s_requests[j];
  717: 	}
  718: 
  719: 	avg /= 5;
  720: 
  721: 	buffer_append_string_len(b, CONST_STR_LEN("\t\"RequestAverage5s\":"));
  722: 	buffer_append_int(b, avg);
  723: 	buffer_append_string_len(b, CONST_STR_LEN(",\n"));
  724: 
  725: 	for (j = 0, avg = 0; j < 5; j++) {
  726: 		avg += p->mod_5s_traffic_out[j];
  727: 	}
  728: 
  729: 	avg /= 5;
  730: 
  731: 	buffer_append_string_len(b, CONST_STR_LEN("\t\"TrafficAverage5s\":"));
  732: 	buffer_append_int(b, avg / 1024); /* kbps */
  733: 	buffer_append_string_len(b, CONST_STR_LEN("\n}"));
  734: 
  735: 	if (jsonp) buffer_append_string_len(b, CONST_STR_LEN(");"));
  736: 
  737: 	chunkqueue_append_buffer(con->write_queue, b);
  738: 	buffer_free(b);
  739: 
  740: 	/* set text/plain output */
  741: 	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/javascript"));
  742: 
  743: 	return 0;
  744: }
  745: 
  746: 
  747: static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) {
  748: 	buffer *b;
  749: 	size_t i;
  750: 	array *st = srv->status;
  751: 	UNUSED(p_d);
  752: 
  753: 	if (0 == st->used) {
  754: 		/* we have nothing to send */
  755: 		con->http_status = 204;
  756: 		con->file_finished = 1;
  757: 
  758: 		return HANDLER_FINISHED;
  759: 	}
  760: 
  761: 	b = buffer_init();
  762: 	for (i = 0; i < st->used; i++) {
  763: 		size_t ndx = st->sorted[i];
  764: 
  765: 		buffer_append_string_buffer(b, st->data[ndx]->key);
  766: 		buffer_append_string_len(b, CONST_STR_LEN(": "));
  767: 		buffer_append_int(b, ((data_integer *)(st->data[ndx]))->value);
  768: 		buffer_append_string_len(b, CONST_STR_LEN("\n"));
  769: 	}
  770: 
  771: 	chunkqueue_append_buffer(con->write_queue, b);
  772: 	buffer_free(b);
  773: 
  774: 	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain"));
  775: 
  776: 	con->http_status = 200;
  777: 	con->file_finished = 1;
  778: 
  779: 	return HANDLER_FINISHED;
  780: }
  781: 
  782: 
  783: static handler_t mod_status_handle_server_status(server *srv, connection *con, void *p_d) {
  784: 
  785: 	if (buffer_is_equal_string(con->uri.query, CONST_STR_LEN("auto"))) {
  786: 		mod_status_handle_server_status_text(srv, con, p_d);
  787: 	} else if (buffer_string_length(con->uri.query) >= sizeof("json")-1
  788: 		   && 0 == memcmp(con->uri.query->ptr, CONST_STR_LEN("json"))) {
  789: 		mod_status_handle_server_status_json(srv, con, p_d);
  790: 	} else {
  791: 		mod_status_handle_server_status_html(srv, con, p_d);
  792: 	}
  793: 
  794: 	con->http_status = 200;
  795: 	con->file_finished = 1;
  796: 
  797: 	return HANDLER_FINISHED;
  798: }
  799: 
  800: 
  801: static handler_t mod_status_handle_server_config(server *srv, connection *con, void *p_d) {
  802: 	plugin_data *p = p_d;
  803: 	buffer *b = buffer_init();
  804: 	buffer *m = p->module_list;
  805: 	size_t i;
  806: 
  807: 	struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] =
  808: 	{
  809: 		/* - epoll is most reliable
  810: 		 * - select works everywhere
  811: 		 */
  812: #ifdef USE_LINUX_EPOLL
  813: 		{ FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" },
  814: #endif
  815: #ifdef USE_POLL
  816: 		{ FDEVENT_HANDLER_POLL,           "poll" },
  817: #endif
  818: #ifdef USE_SELECT
  819: 		{ FDEVENT_HANDLER_SELECT,         "select" },
  820: #endif
  821: #ifdef USE_LIBEV
  822: 		{ FDEVENT_HANDLER_LIBEV,          "libev" },
  823: #endif
  824: #ifdef USE_SOLARIS_DEVPOLL
  825: 		{ FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" },
  826: #endif
  827: #ifdef USE_SOLARIS_PORT
  828: 		{ FDEVENT_HANDLER_SOLARIS_PORT,   "solaris-eventports" },
  829: #endif
  830: #ifdef USE_FREEBSD_KQUEUE
  831: 		{ FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" },
  832: #endif
  833: 		{ FDEVENT_HANDLER_UNSET,          NULL }
  834: 	};
  835: 
  836: 	buffer_copy_string_len(b, CONST_STR_LEN(
  837: 			   "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
  838: 			   "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
  839: 			   "         \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
  840: 			   "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
  841: 			   " <head>\n"
  842: 			   "  <title>Status</title>\n"
  843: 			   " </head>\n"
  844: 			   " <body>\n"
  845: 			   "  <h1>"));
  846: 	buffer_append_string_buffer(b, con->conf.server_tag);
  847: 	buffer_append_string_len(b, CONST_STR_LEN(
  848: 			   "</h1>\n"
  849: 			   "  <table summary=\"status\" border=\"1\">\n"));
  850: 
  851: 	mod_status_header_append(b, "Server-Features");
  852: #ifdef HAVE_PCRE_H
  853: 	mod_status_row_append(b, "RegEx Conditionals", "enabled");
  854: #else
  855: 	mod_status_row_append(b, "RegEx Conditionals", "disabled - pcre missing");
  856: #endif
  857: 	mod_status_header_append(b, "Network Engine");
  858: 
  859: 	for (i = 0; event_handlers[i].name; i++) {
  860: 		if (event_handlers[i].et == srv->event_handler) {
  861: 			mod_status_row_append(b, "fd-Event-Handler", event_handlers[i].name);
  862: 			break;
  863: 		}
  864: 	}
  865: 
  866: 	mod_status_header_append(b, "Config-File-Settings");
  867: 
  868: 	for (i = 0; i < srv->plugins.used; i++) {
  869: 		plugin **ps = srv->plugins.ptr;
  870: 
  871: 		plugin *pl = ps[i];
  872: 
  873: 		if (i == 0) {
  874: 			buffer_copy_buffer(m, pl->name);
  875: 		} else {
  876: 			buffer_append_string_len(m, CONST_STR_LEN("<br />"));
  877: 			buffer_append_string_buffer(m, pl->name);
  878: 		}
  879: 	}
  880: 
  881: 	mod_status_row_append(b, "Loaded Modules", m->ptr);
  882: 
  883: 	buffer_append_string_len(b, CONST_STR_LEN("  </table>\n"));
  884: 
  885: 	buffer_append_string_len(b, CONST_STR_LEN(
  886: 		      " </body>\n"
  887: 		      "</html>\n"
  888: 		      ));
  889: 
  890: 	chunkqueue_append_buffer(con->write_queue, b);
  891: 	buffer_free(b);
  892: 
  893: 	response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
  894: 
  895: 	con->http_status = 200;
  896: 	con->file_finished = 1;
  897: 
  898: 	return HANDLER_FINISHED;
  899: }
  900: 
  901: #define PATCH(x) \
  902: 	p->conf.x = s->x;
  903: static int mod_status_patch_connection(server *srv, connection *con, plugin_data *p) {
  904: 	size_t i, j;
  905: 	plugin_config *s = p->config_storage[0];
  906: 
  907: 	PATCH(status_url);
  908: 	PATCH(config_url);
  909: 	PATCH(sort);
  910: 	PATCH(statistics_url);
  911: 
  912: 	/* skip the first, the global context */
  913: 	for (i = 1; i < srv->config_context->used; i++) {
  914: 		data_config *dc = (data_config *)srv->config_context->data[i];
  915: 		s = p->config_storage[i];
  916: 
  917: 		/* condition didn't match */
  918: 		if (!config_check_cond(srv, con, dc)) continue;
  919: 
  920: 		/* merge config */
  921: 		for (j = 0; j < dc->value->used; j++) {
  922: 			data_unset *du = dc->value->data[j];
  923: 
  924: 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.status-url"))) {
  925: 				PATCH(status_url);
  926: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.config-url"))) {
  927: 				PATCH(config_url);
  928: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.enable-sort"))) {
  929: 				PATCH(sort);
  930: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.statistics-url"))) {
  931: 				PATCH(statistics_url);
  932: 			}
  933: 		}
  934: 	}
  935: 
  936: 	return 0;
  937: }
  938: 
  939: static handler_t mod_status_handler(server *srv, connection *con, void *p_d) {
  940: 	plugin_data *p = p_d;
  941: 
  942: 	if (con->mode != DIRECT) return HANDLER_GO_ON;
  943: 
  944: 	mod_status_patch_connection(srv, con, p);
  945: 
  946: 	if (!buffer_string_is_empty(p->conf.status_url) &&
  947: 	    buffer_is_equal(p->conf.status_url, con->uri.path)) {
  948: 		return mod_status_handle_server_status(srv, con, p_d);
  949: 	} else if (!buffer_string_is_empty(p->conf.config_url) &&
  950: 	    buffer_is_equal(p->conf.config_url, con->uri.path)) {
  951: 		return mod_status_handle_server_config(srv, con, p_d);
  952: 	} else if (!buffer_string_is_empty(p->conf.statistics_url) &&
  953: 	    buffer_is_equal(p->conf.statistics_url, con->uri.path)) {
  954: 		return mod_status_handle_server_statistics(srv, con, p_d);
  955: 	}
  956: 
  957: 	return HANDLER_GO_ON;
  958: }
  959: 
  960: TRIGGER_FUNC(mod_status_trigger) {
  961: 	plugin_data *p = p_d;
  962: 	size_t i;
  963: 
  964: 	/* check all connections */
  965: 	for (i = 0; i < srv->conns->used; i++) {
  966: 		connection *c = srv->conns->ptr[i];
  967: 
  968: 		p->bytes_written += c->bytes_written_cur_second;
  969: 	}
  970: 
  971: 	/* a sliding average */
  972: 	p->mod_5s_traffic_out[p->mod_5s_ndx] = p->bytes_written;
  973: 	p->mod_5s_requests   [p->mod_5s_ndx] = p->requests;
  974: 
  975: 	p->mod_5s_ndx = (p->mod_5s_ndx+1) % 5;
  976: 
  977: 	p->abs_traffic_out += p->bytes_written;
  978: 	p->rel_traffic_out += p->bytes_written;
  979: 
  980: 	p->bytes_written = 0;
  981: 
  982: 	/* reset storage - second */
  983: 	p->traffic_out = 0;
  984: 	p->requests    = 0;
  985: 
  986: 	return HANDLER_GO_ON;
  987: }
  988: 
  989: REQUESTDONE_FUNC(mod_status_account) {
  990: 	plugin_data *p = p_d;
  991: 
  992: 	UNUSED(srv);
  993: 
  994: 	p->requests++;
  995: 	p->rel_requests++;
  996: 	p->abs_requests++;
  997: 
  998: 	p->bytes_written += con->bytes_written_cur_second;
  999: 
 1000: 	return HANDLER_GO_ON;
 1001: }
 1002: 
 1003: int mod_status_plugin_init(plugin *p);
 1004: int mod_status_plugin_init(plugin *p) {
 1005: 	p->version     = LIGHTTPD_VERSION_ID;
 1006: 	p->name        = buffer_init_string("status");
 1007: 
 1008: 	p->init        = mod_status_init;
 1009: 	p->cleanup     = mod_status_free;
 1010: 	p->set_defaults= mod_status_set_defaults;
 1011: 
 1012: 	p->handle_uri_clean    = mod_status_handler;
 1013: 	p->handle_trigger      = mod_status_trigger;
 1014: 	p->handle_request_done = mod_status_account;
 1015: 
 1016: 	p->data        = NULL;
 1017: 
 1018: 	return 0;
 1019: }

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