File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_accesslog.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 10:32:48 2013 UTC (10 years, 8 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    1: #include "base.h"
    2: #include "log.h"
    3: #include "buffer.h"
    4: 
    5: #include "plugin.h"
    6: 
    7: #include "inet_ntop_cache.h"
    8: 
    9: #include "sys-socket.h"
   10: 
   11: #include <sys/types.h>
   12: #include <sys/stat.h>
   13: 
   14: #include <ctype.h>
   15: #include <stdlib.h>
   16: #include <string.h>
   17: #include <fcntl.h>
   18: #include <unistd.h>
   19: #include <errno.h>
   20: #include <time.h>
   21: 
   22: #include <stdio.h>
   23: 
   24: #ifdef HAVE_SYSLOG_H
   25: # include <syslog.h>
   26: #endif
   27: 
   28: typedef struct {
   29: 	char key;
   30: 	enum {
   31: 		FORMAT_UNSET,
   32: 			FORMAT_UNSUPPORTED,
   33: 			FORMAT_PERCENT,
   34: 			FORMAT_REMOTE_HOST,
   35: 			FORMAT_REMOTE_IDENT,
   36: 			FORMAT_REMOTE_USER,
   37: 			FORMAT_TIMESTAMP,
   38: 			FORMAT_REQUEST_LINE,
   39: 			FORMAT_STATUS,
   40: 			FORMAT_BYTES_OUT_NO_HEADER,
   41: 			FORMAT_HEADER,
   42: 
   43: 			FORMAT_REMOTE_ADDR,
   44: 			FORMAT_LOCAL_ADDR,
   45: 			FORMAT_COOKIE,
   46: 			FORMAT_TIME_USED_MS,
   47: 			FORMAT_ENV,
   48: 			FORMAT_FILENAME,
   49: 			FORMAT_REQUEST_PROTOCOL,
   50: 			FORMAT_REQUEST_METHOD,
   51: 			FORMAT_SERVER_PORT,
   52: 			FORMAT_QUERY_STRING,
   53: 			FORMAT_TIME_USED,
   54: 			FORMAT_URL,
   55: 			FORMAT_SERVER_NAME,
   56: 			FORMAT_HTTP_HOST,
   57: 			FORMAT_CONNECTION_STATUS,
   58: 			FORMAT_BYTES_IN,
   59: 			FORMAT_BYTES_OUT,
   60: 
   61: 			FORMAT_RESPONSE_HEADER
   62: 	} type;
   63: } format_mapping;
   64: 
   65: /**
   66:  *
   67:  *
   68:  * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
   69:  *
   70:  */
   71: 
   72: static const format_mapping fmap[] =
   73: {
   74: 	{ '%', FORMAT_PERCENT },
   75: 	{ 'h', FORMAT_REMOTE_HOST },
   76: 	{ 'l', FORMAT_REMOTE_IDENT },
   77: 	{ 'u', FORMAT_REMOTE_USER },
   78: 	{ 't', FORMAT_TIMESTAMP },
   79: 	{ 'r', FORMAT_REQUEST_LINE },
   80: 	{ 's', FORMAT_STATUS },
   81: 	{ 'b', FORMAT_BYTES_OUT_NO_HEADER },
   82: 	{ 'i', FORMAT_HEADER },
   83: 
   84: 	{ 'a', FORMAT_REMOTE_ADDR },
   85: 	{ 'A', FORMAT_LOCAL_ADDR },
   86: 	{ 'B', FORMAT_BYTES_OUT_NO_HEADER },
   87: 	{ 'C', FORMAT_COOKIE },
   88: 	{ 'D', FORMAT_TIME_USED_MS },
   89: 	{ 'e', FORMAT_ENV },
   90: 	{ 'f', FORMAT_FILENAME },
   91: 	{ 'H', FORMAT_REQUEST_PROTOCOL },
   92: 	{ 'm', FORMAT_REQUEST_METHOD },
   93: 	{ 'n', FORMAT_UNSUPPORTED }, /* we have no notes */
   94: 	{ 'p', FORMAT_SERVER_PORT },
   95: 	{ 'P', FORMAT_UNSUPPORTED }, /* we are only one process */
   96: 	{ 'q', FORMAT_QUERY_STRING },
   97: 	{ 'T', FORMAT_TIME_USED },
   98: 	{ 'U', FORMAT_URL }, /* w/o querystring */
   99: 	{ 'v', FORMAT_SERVER_NAME },
  100: 	{ 'V', FORMAT_HTTP_HOST },
  101: 	{ 'X', FORMAT_CONNECTION_STATUS },
  102: 	{ 'I', FORMAT_BYTES_IN },
  103: 	{ 'O', FORMAT_BYTES_OUT },
  104: 
  105: 	{ 'o', FORMAT_RESPONSE_HEADER },
  106: 
  107: 	{ '\0', FORMAT_UNSET }
  108: };
  109: 
  110: 
  111: typedef struct {
  112: 	enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type;
  113: 
  114: 	buffer *string;
  115: 	int field;
  116: } format_field;
  117: 
  118: typedef struct {
  119: 	format_field **ptr;
  120: 
  121: 	size_t used;
  122: 	size_t size;
  123: } format_fields;
  124: 
  125: typedef struct {
  126: 	buffer *access_logfile;
  127: 	int    log_access_fd;
  128: 	buffer *access_logbuffer; /* each logfile has a separate buffer */
  129: 
  130: 	unsigned short use_syslog; /* syslog has global buffer */
  131: 	unsigned short syslog_level;
  132: 
  133: 	buffer *format;
  134: 
  135: 	time_t last_generated_accesslog_ts;
  136: 	time_t *last_generated_accesslog_ts_ptr;
  137: 
  138: 	buffer *ts_accesslog_str;
  139: 	buffer *ts_accesslog_fmt_str;
  140: 	unsigned short append_tz_offset;
  141: 
  142: 	format_fields *parsed_format;
  143: } plugin_config;
  144: 
  145: typedef struct {
  146: 	PLUGIN_DATA;
  147: 
  148: 	plugin_config **config_storage;
  149: 	plugin_config conf;
  150: 
  151: 	buffer *syslog_logbuffer; /* syslog has global buffer. no caching, always written directly */
  152: } plugin_data;
  153: 
  154: INIT_FUNC(mod_accesslog_init) {
  155: 	plugin_data *p;
  156: 
  157: 	p = calloc(1, sizeof(*p));
  158: 	p->syslog_logbuffer = buffer_init();
  159: 
  160: 	return p;
  161: }
  162: 
  163: static void accesslog_append_escaped(buffer *dest, buffer *str) {
  164: 	char *ptr, *start, *end;
  165: 
  166: 	/* replaces non-printable chars with \xHH where HH is the hex representation of the byte */
  167: 	/* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
  168: 	if (str->used == 0) return;
  169: 	buffer_prepare_append(dest, str->used - 1);
  170: 
  171: 	for (ptr = start = str->ptr, end = str->ptr + str->used - 1; ptr < end; ptr++) {
  172: 		char const c = *ptr;
  173: 		if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
  174: 			/* nothing to change, add later as one block */
  175: 		} else {
  176: 			/* copy previous part */
  177: 			if (start < ptr) {
  178: 				buffer_append_string_len(dest, start, ptr - start);
  179: 			}
  180: 			start = ptr + 1;
  181: 
  182: 			switch (c) {
  183: 			case '"':
  184: 				BUFFER_APPEND_STRING_CONST(dest, "\\\"");
  185: 				break;
  186: 			case '\\':
  187: 				BUFFER_APPEND_STRING_CONST(dest, "\\\\");
  188: 				break;
  189: 			case '\b':
  190: 				BUFFER_APPEND_STRING_CONST(dest, "\\b");
  191: 				break;
  192: 			case '\n':
  193: 				BUFFER_APPEND_STRING_CONST(dest, "\\n");
  194: 				break;
  195: 			case '\r':
  196: 				BUFFER_APPEND_STRING_CONST(dest, "\\r");
  197: 				break;
  198: 			case '\t':
  199: 				BUFFER_APPEND_STRING_CONST(dest, "\\t");
  200: 				break;
  201: 			case '\v':
  202: 				BUFFER_APPEND_STRING_CONST(dest, "\\v");
  203: 				break;
  204: 			default: {
  205: 					/* non printable char => \xHH */
  206: 					char hh[5] = {'\\','x',0,0,0};
  207: 					char h = c / 16;
  208: 					hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0');
  209: 					h = c % 16;
  210: 					hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0');
  211: 					buffer_append_string_len(dest, &hh[0], 4);
  212: 				}
  213: 				break;
  214: 			}
  215: 		}
  216: 	}
  217: 
  218: 	if (start < end) {
  219: 		buffer_append_string_len(dest, start, end - start);
  220: 	}
  221: }
  222: 
  223: static int accesslog_parse_format(server *srv, format_fields *fields, buffer *format) {
  224: 	size_t i, j, k = 0, start = 0;
  225: 
  226: 	if (format->used == 0) return -1;
  227: 
  228: 	for (i = 0; i < format->used - 1; i++) {
  229: 		switch(format->ptr[i]) {
  230: 		case '%':
  231: 			if (i > 0 && start != i) {
  232: 				/* copy the string before this % */
  233: 				if (fields->size == 0) {
  234: 					fields->size = 16;
  235: 					fields->used = 0;
  236: 					fields->ptr = malloc(fields->size * sizeof(format_field * ));
  237: 				} else if (fields->used == fields->size) {
  238: 					fields->size += 16;
  239: 					fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
  240: 				}
  241: 
  242: 				fields->ptr[fields->used] = malloc(sizeof(format_field));
  243: 				fields->ptr[fields->used]->type = FIELD_STRING;
  244: 				fields->ptr[fields->used]->string = buffer_init();
  245: 
  246: 				buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
  247: 
  248: 				fields->used++;
  249: 			}
  250: 
  251: 			/* we need a new field */
  252: 
  253: 			if (fields->size == 0) {
  254: 				fields->size = 16;
  255: 				fields->used = 0;
  256: 				fields->ptr = malloc(fields->size * sizeof(format_field * ));
  257: 			} else if (fields->used == fields->size) {
  258: 				fields->size += 16;
  259: 				fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
  260: 			}
  261: 
  262: 			/* search for the terminating command */
  263: 			switch (format->ptr[i+1]) {
  264: 			case '>':
  265: 			case '<':
  266: 				/* after the } has to be a character */
  267: 				if (format->ptr[i+2] == '\0') {
  268: 					log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a format-specifier");
  269: 					return -1;
  270: 				}
  271: 
  272: 
  273: 				for (j = 0; fmap[j].key != '\0'; j++) {
  274: 					if (fmap[j].key != format->ptr[i+2]) continue;
  275: 
  276: 					/* found key */
  277: 
  278: 					fields->ptr[fields->used] = malloc(sizeof(format_field));
  279: 					fields->ptr[fields->used]->type = FIELD_FORMAT;
  280: 					fields->ptr[fields->used]->field = fmap[j].type;
  281: 					fields->ptr[fields->used]->string = NULL;
  282: 
  283: 					fields->used++;
  284: 
  285: 					break;
  286: 				}
  287: 
  288: 				if (fmap[j].key == '\0') {
  289: 					log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a valid format-specifier");
  290: 					return -1;
  291: 				}
  292: 
  293: 				start = i + 3;
  294: 				i = start - 1; /* skip the string */
  295: 
  296: 				break;
  297: 			case '{':
  298: 				/* go forward to } */
  299: 
  300: 				for (k = i+2; k < format->used - 1; k++) {
  301: 					if (format->ptr[k] == '}') break;
  302: 				}
  303: 
  304: 				if (k == format->used - 1) {
  305: 					log_error_write(srv, __FILE__, __LINE__, "s", "%{ has to be terminated by a }");
  306: 					return -1;
  307: 				}
  308: 
  309: 				/* after the } has to be a character */
  310: 				if (format->ptr[k+1] == '\0') {
  311: 					log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a format-specifier");
  312: 					return -1;
  313: 				}
  314: 
  315: 				if (k == i + 2) {
  316: 					log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be contain a string");
  317: 					return -1;
  318: 				}
  319: 
  320: 				for (j = 0; fmap[j].key != '\0'; j++) {
  321: 					if (fmap[j].key != format->ptr[k+1]) continue;
  322: 
  323: 					/* found key */
  324: 
  325: 					fields->ptr[fields->used] = malloc(sizeof(format_field));
  326: 					fields->ptr[fields->used]->type = FIELD_FORMAT;
  327: 					fields->ptr[fields->used]->field = fmap[j].type;
  328: 					fields->ptr[fields->used]->string = buffer_init();
  329: 
  330: 					buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + i + 2, k - (i + 2));
  331: 
  332: 					fields->used++;
  333: 
  334: 					break;
  335: 				}
  336: 
  337: 				if (fmap[j].key == '\0') {
  338: 					log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a valid format-specifier");
  339: 					return -1;
  340: 				}
  341: 
  342: 				start = k + 2;
  343: 				i = start - 1; /* skip the string */
  344: 
  345: 				break;
  346: 			default:
  347: 				/* after the % has to be a character */
  348: 				if (format->ptr[i+1] == '\0') {
  349: 					log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a format-specifier");
  350: 					return -1;
  351: 				}
  352: 
  353: 				for (j = 0; fmap[j].key != '\0'; j++) {
  354: 					if (fmap[j].key != format->ptr[i+1]) continue;
  355: 
  356: 					/* found key */
  357: 
  358: 					fields->ptr[fields->used] = malloc(sizeof(format_field));
  359: 					fields->ptr[fields->used]->type = FIELD_FORMAT;
  360: 					fields->ptr[fields->used]->field = fmap[j].type;
  361: 					fields->ptr[fields->used]->string = NULL;
  362: 
  363: 					fields->used++;
  364: 
  365: 					break;
  366: 				}
  367: 
  368: 				if (fmap[j].key == '\0') {
  369: 					log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a valid format-specifier");
  370: 					return -1;
  371: 				}
  372: 
  373: 				start = i + 2;
  374: 				i = start - 1; /* skip the string */
  375: 
  376: 				break;
  377: 			}
  378: 
  379: 			break;
  380: 		}
  381: 	}
  382: 
  383: 	if (start < i) {
  384: 		/* copy the string */
  385: 		if (fields->size == 0) {
  386: 			fields->size = 16;
  387: 			fields->used = 0;
  388: 			fields->ptr = malloc(fields->size * sizeof(format_field * ));
  389: 		} else if (fields->used == fields->size) {
  390: 			fields->size += 16;
  391: 			fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
  392: 		}
  393: 
  394: 		fields->ptr[fields->used] = malloc(sizeof(format_field));
  395: 		fields->ptr[fields->used]->type = FIELD_STRING;
  396: 		fields->ptr[fields->used]->string = buffer_init();
  397: 
  398: 		buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
  399: 
  400: 		fields->used++;
  401: 	}
  402: 
  403: 	return 0;
  404: }
  405: 
  406: FREE_FUNC(mod_accesslog_free) {
  407: 	plugin_data *p = p_d;
  408: 	size_t i;
  409: 
  410: 	if (!p) return HANDLER_GO_ON;
  411: 
  412: 	if (p->config_storage) {
  413: 
  414: 		for (i = 0; i < srv->config_context->used; i++) {
  415: 			plugin_config *s = p->config_storage[i];
  416: 
  417: 			if (!s) continue;
  418: 
  419: 			if (s->access_logbuffer->used) {
  420: 				if (s->log_access_fd != -1) {
  421: 					write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1);
  422: 				}
  423: 			}
  424: 
  425: 			if (s->log_access_fd != -1) close(s->log_access_fd);
  426: 
  427: 			buffer_free(s->ts_accesslog_str);
  428: 			buffer_free(s->ts_accesslog_fmt_str);
  429: 			buffer_free(s->access_logbuffer);
  430: 			buffer_free(s->format);
  431: 			buffer_free(s->access_logfile);
  432: 
  433: 			if (s->parsed_format) {
  434: 				size_t j;
  435: 				for (j = 0; j < s->parsed_format->used; j++) {
  436: 					if (s->parsed_format->ptr[j]->string) buffer_free(s->parsed_format->ptr[j]->string);
  437: 					free(s->parsed_format->ptr[j]);
  438: 				}
  439: 				free(s->parsed_format->ptr);
  440: 				free(s->parsed_format);
  441: 			}
  442: 
  443: 			free(s);
  444: 		}
  445: 
  446: 		free(p->config_storage);
  447: 	}
  448: 
  449: 	free(p);
  450: 	if (p->syslog_logbuffer) buffer_free(p->syslog_logbuffer);
  451: 
  452: 	return HANDLER_GO_ON;
  453: }
  454: 
  455: SETDEFAULTS_FUNC(log_access_open) {
  456: 	plugin_data *p = p_d;
  457: 	size_t i = 0;
  458: 
  459: 	config_values_t cv[] = {
  460: 		{ "accesslog.filename",             NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  461: 		{ "accesslog.use-syslog",           NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
  462: 		{ "accesslog.format",               NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
  463: 		{ "accesslog.syslog-level",         NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },
  464: 		{ NULL,                             NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
  465: 	};
  466: 
  467: 	if (!p) return HANDLER_ERROR;
  468: 
  469: 	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
  470: 
  471: 	for (i = 0; i < srv->config_context->used; i++) {
  472: 		plugin_config *s;
  473: 
  474: 		s = calloc(1, sizeof(plugin_config));
  475: 		s->access_logfile = buffer_init();
  476: 		s->format = buffer_init();
  477: 		s->access_logbuffer = buffer_init();
  478: 		s->ts_accesslog_str = buffer_init();
  479: 		s->ts_accesslog_fmt_str = buffer_init();
  480: 		s->log_access_fd = -1;
  481: 		s->last_generated_accesslog_ts = 0;
  482: 		s->last_generated_accesslog_ts_ptr = &(s->last_generated_accesslog_ts);
  483: 		s->syslog_level = LOG_INFO;
  484: 
  485: 
  486: 		cv[0].destination = s->access_logfile;
  487: 		cv[1].destination = &(s->use_syslog);
  488: 		cv[2].destination = s->format;
  489: 		cv[3].destination = &(s->syslog_level);
  490: 
  491: 		p->config_storage[i] = s;
  492: 
  493: 		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
  494: 			return HANDLER_ERROR;
  495: 		}
  496: 
  497: 		if (i == 0 && buffer_is_empty(s->format)) {
  498: 			/* set a default logfile string */
  499: 
  500: 			buffer_copy_string_len(s->format, CONST_STR_LEN("%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""));
  501: 		}
  502: 
  503: 		/* parse */
  504: 
  505: 		if (s->format->used) {
  506: 			size_t j, count;
  507: 
  508: 			s->parsed_format = calloc(1, sizeof(*(s->parsed_format)));
  509: 
  510: 			if (-1 == accesslog_parse_format(srv, s->parsed_format, s->format)) {
  511: 
  512: 				log_error_write(srv, __FILE__, __LINE__, "sb",
  513: 						"parsing accesslog-definition failed:", s->format);
  514: 
  515: 				return HANDLER_ERROR;
  516: 			}
  517: 
  518: 			/* make sure they didn't try to send the timestamp in twice...
  519: 			 * also, save the format string in a different variable (this
  520: 			 * will save a few conditionals later)
  521: 			 */
  522: 			count = 0;
  523: 			for (j = 0; j < s->parsed_format->used; j++) {
  524: 				if (FIELD_FORMAT == s->parsed_format->ptr[j]->type) {
  525: 					if (FORMAT_TIMESTAMP == s->parsed_format->ptr[j]->field) {
  526: 						if (!buffer_is_empty(s->parsed_format->ptr[j]->string)) {
  527: 							buffer_copy_string(s->ts_accesslog_fmt_str, s->parsed_format->ptr[j]->string->ptr);
  528: 						}
  529: 
  530: 						if (++count > 1) {
  531: 							log_error_write(srv, __FILE__, __LINE__, "sb",
  532: 								"you may not use the timestamp twice in the same access log:", s->format);
  533: 
  534: 							return HANDLER_ERROR;
  535: 						}
  536: 					}
  537: 				}
  538: 			}
  539: 
  540: #if 0
  541: 			/* debugging */
  542: 			for (j = 0; j < s->parsed_format->used; j++) {
  543: 				switch (s->parsed_format->ptr[j]->type) {
  544: 				case FIELD_FORMAT:
  545: 					log_error_write(srv, __FILE__, __LINE__, "ssds",
  546: 							"config:", "format", s->parsed_format->ptr[j]->field,
  547: 							s->parsed_format->ptr[j]->string ?
  548: 							s->parsed_format->ptr[j]->string->ptr : "" );
  549: 					break;
  550: 				case FIELD_STRING:
  551: 					log_error_write(srv, __FILE__, __LINE__, "ssbs", "config:", "string '", s->parsed_format->ptr[j]->string, "'");
  552: 					break;
  553: 				default:
  554: 					break;
  555: 				}
  556: 			}
  557: #endif
  558: 		}
  559: 
  560: 		s->append_tz_offset = 0;
  561: 		if (buffer_is_empty(s->ts_accesslog_fmt_str)) {
  562: #if defined(HAVE_STRUCT_TM_GMTOFF)
  563: 			BUFFER_COPY_STRING_CONST(s->ts_accesslog_fmt_str, "[%d/%b/%Y:%H:%M:%S ");
  564: 			s->append_tz_offset = 1;
  565: #else
  566: 			BUFFER_COPY_STRING_CONST(s->ts_accesslog_fmt_str, "[%d/%b/%Y:%H:%M:%S +0000]");
  567: #endif
  568: 		}
  569: 
  570: 		if (s->use_syslog) {
  571: 			/* ignore the next checks */
  572: 			continue;
  573: 		}
  574: 
  575: 		if (s->access_logfile->used < 2) continue;
  576: 
  577: 		if (-1 == (s->log_access_fd = open_logfile_or_pipe(srv, s->access_logfile->ptr)))
  578: 			return HANDLER_ERROR;
  579: 
  580: 	}
  581: 
  582: 	return HANDLER_GO_ON;
  583: }
  584: 
  585: SIGHUP_FUNC(log_access_cycle) {
  586: 	plugin_data *p = p_d;
  587: 	size_t i;
  588: 
  589: 	if (!p->config_storage) return HANDLER_GO_ON;
  590: 
  591: 	for (i = 0; i < srv->config_context->used; i++) {
  592: 		plugin_config *s = p->config_storage[i];
  593: 
  594: 		if (s->access_logbuffer->used) {
  595: 			if (s->log_access_fd != -1) {
  596: 				write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1);
  597: 			}
  598: 
  599: 			buffer_reset(s->access_logbuffer);
  600: 		}
  601: 
  602: 		if (s->use_syslog == 0 &&
  603: 		    s->access_logfile->used > 1 &&
  604: 		    s->access_logfile->ptr[0] != '|') {
  605: 
  606: 			close(s->log_access_fd);
  607: 
  608: 			if (-1 == (s->log_access_fd =
  609: 				   open(s->access_logfile->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) {
  610: 
  611: 				log_error_write(srv, __FILE__, __LINE__, "ss", "cycling access-log failed:", strerror(errno));
  612: 
  613: 				return HANDLER_ERROR;
  614: 			}
  615: #ifdef FD_CLOEXEC
  616: 			fcntl(s->log_access_fd, F_SETFD, FD_CLOEXEC);
  617: #endif
  618: 		}
  619: 	}
  620: 
  621: 	return HANDLER_GO_ON;
  622: }
  623: 
  624: #define PATCH(x) \
  625: 	p->conf.x = s->x;
  626: static int mod_accesslog_patch_connection(server *srv, connection *con, plugin_data *p) {
  627: 	size_t i, j;
  628: 	plugin_config *s = p->config_storage[0];
  629: 
  630: 	PATCH(access_logfile);
  631: 	PATCH(format);
  632: 	PATCH(log_access_fd);
  633: 	PATCH(last_generated_accesslog_ts_ptr);
  634: 	PATCH(access_logbuffer);
  635: 	PATCH(ts_accesslog_str);
  636: 	PATCH(ts_accesslog_fmt_str);
  637: 	PATCH(append_tz_offset);
  638: 	PATCH(parsed_format);
  639: 	PATCH(use_syslog);
  640: 	PATCH(syslog_level);
  641: 
  642: 	/* skip the first, the global context */
  643: 	for (i = 1; i < srv->config_context->used; i++) {
  644: 		data_config *dc = (data_config *)srv->config_context->data[i];
  645: 		s = p->config_storage[i];
  646: 
  647: 		/* condition didn't match */
  648: 		if (!config_check_cond(srv, con, dc)) continue;
  649: 
  650: 		/* merge config */
  651: 		for (j = 0; j < dc->value->used; j++) {
  652: 			data_unset *du = dc->value->data[j];
  653: 
  654: 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.filename"))) {
  655: 				PATCH(access_logfile);
  656: 				PATCH(log_access_fd);
  657: 				PATCH(access_logbuffer);
  658: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.format"))) {
  659: 				PATCH(format);
  660: 				PATCH(parsed_format);
  661: 				PATCH(last_generated_accesslog_ts_ptr);
  662: 				PATCH(ts_accesslog_str);
  663: 				PATCH(ts_accesslog_fmt_str);
  664: 				PATCH(append_tz_offset);
  665: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.use-syslog"))) {
  666: 				PATCH(use_syslog);
  667: 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.syslog-level"))) {
  668: 				PATCH(syslog_level);
  669: 			}
  670: 		}
  671: 	}
  672: 
  673: 	return 0;
  674: }
  675: #undef PATCH
  676: 
  677: REQUESTDONE_FUNC(log_access_write) {
  678: 	plugin_data *p = p_d;
  679: 	buffer *b;
  680: 	size_t j;
  681: 
  682: 	int newts = 0;
  683: 	data_string *ds;
  684: 
  685: 	mod_accesslog_patch_connection(srv, con, p);
  686: 
  687: 	/* No output device, nothing to do */
  688: 	if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON;
  689: 
  690: 	if (p->conf.use_syslog) {
  691: 		b = p->syslog_logbuffer;
  692: 	} else {
  693: 		b = p->conf.access_logbuffer;
  694: 	}
  695: 
  696: 	if (b->used == 0) {
  697: 		buffer_copy_string_len(b, CONST_STR_LEN(""));
  698: 	}
  699: 
  700: 	for (j = 0; j < p->conf.parsed_format->used; j++) {
  701: 		switch(p->conf.parsed_format->ptr[j]->type) {
  702: 		case FIELD_STRING:
  703: 			buffer_append_string_buffer(b, p->conf.parsed_format->ptr[j]->string);
  704: 			break;
  705: 		case FIELD_FORMAT:
  706: 			switch(p->conf.parsed_format->ptr[j]->field) {
  707: 			case FORMAT_TIMESTAMP:
  708: 
  709: 				/* cache the generated timestamp */
  710: 				if (srv->cur_ts != *(p->conf.last_generated_accesslog_ts_ptr)) {
  711: 					struct tm tm;
  712: #if defined(HAVE_STRUCT_TM_GMTOFF)
  713: 					long scd, hrs, min;
  714: #endif
  715: 
  716: 					buffer_prepare_copy(p->conf.ts_accesslog_str, 255);
  717: #if defined(HAVE_STRUCT_TM_GMTOFF)
  718: # ifdef HAVE_LOCALTIME_R
  719: 					localtime_r(&(srv->cur_ts), &tm);
  720: 					strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, &tm);
  721: # else /* HAVE_LOCALTIME_R */
  722: 					strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, localtime_r(&(srv->cur_ts)));
  723: # endif /* HAVE_LOCALTIME_R */
  724: 					p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1;
  725: 
  726: 					if (p->conf.append_tz_offset) {
  727: 						buffer_append_string_len(p->conf.ts_accesslog_str, tm.tm_gmtoff >= 0 ? "+" : "-", 1);
  728: 
  729: 						scd = abs(tm.tm_gmtoff);
  730: 						hrs = scd / 3600;
  731: 						min = (scd % 3600) / 60;
  732: 
  733: 						/* hours */
  734: 						if (hrs < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
  735: 						buffer_append_long(p->conf.ts_accesslog_str, hrs);
  736: 
  737: 						if (min < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
  738: 						buffer_append_long(p->conf.ts_accesslog_str, min);
  739: 						buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("]"));
  740: 					}
  741: #else /* HAVE_STRUCT_TM_GMTOFF */
  742: # ifdef HAVE_GMTIME_R
  743: 					gmtime_r(&(srv->cur_ts), &tm);
  744: 					strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, &tm);
  745: # else /* HAVE_GMTIME_R */
  746: 					strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, gmtime(&(srv->cur_ts)));
  747: # endif /* HAVE_GMTIME_R */
  748: 					p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1;
  749: #endif /* HAVE_STRUCT_TM_GMTOFF */
  750: 
  751: 					*(p->conf.last_generated_accesslog_ts_ptr) = srv->cur_ts;
  752: 					newts = 1;
  753: 				}
  754: 
  755: 				buffer_append_string_buffer(b, p->conf.ts_accesslog_str);
  756: 
  757: 				break;
  758: 			case FORMAT_REMOTE_HOST:
  759: 
  760: 				/* handle inet_ntop cache */
  761: 
  762: 				buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
  763: 
  764: 				break;
  765: 			case FORMAT_REMOTE_IDENT:
  766: 				/* ident */
  767: 				buffer_append_string_len(b, CONST_STR_LEN("-"));
  768: 				break;
  769: 			case FORMAT_REMOTE_USER:
  770: 				if (NULL != (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER")) && ds->value->used > 1) {
  771: 					accesslog_append_escaped(b, ds->value);
  772: 				} else {
  773: 					buffer_append_string_len(b, CONST_STR_LEN("-"));
  774: 				}
  775: 				break;
  776: 			case FORMAT_REQUEST_LINE:
  777: 				if (con->request.request_line->used) {
  778: 					accesslog_append_escaped(b, con->request.request_line);
  779: 				}
  780: 				break;
  781: 			case FORMAT_STATUS:
  782: 				buffer_append_long(b, con->http_status);
  783: 				break;
  784: 
  785: 			case FORMAT_BYTES_OUT_NO_HEADER:
  786: 				if (con->bytes_written > 0) {
  787: 					buffer_append_off_t(b,
  788: 							    con->bytes_written - con->bytes_header <= 0 ? 0 : con->bytes_written - con->bytes_header);
  789: 				} else {
  790: 					buffer_append_string_len(b, CONST_STR_LEN("-"));
  791: 				}
  792: 				break;
  793: 			case FORMAT_HEADER:
  794: 				if (NULL != (ds = (data_string *)array_get_element(con->request.headers, p->conf.parsed_format->ptr[j]->string->ptr))) {
  795: 					accesslog_append_escaped(b, ds->value);
  796: 				} else {
  797: 					buffer_append_string_len(b, CONST_STR_LEN("-"));
  798: 				}
  799: 				break;
  800: 			case FORMAT_RESPONSE_HEADER:
  801: 				if (NULL != (ds = (data_string *)array_get_element(con->response.headers, p->conf.parsed_format->ptr[j]->string->ptr))) {
  802: 					accesslog_append_escaped(b, ds->value);
  803: 				} else {
  804: 					buffer_append_string_len(b, CONST_STR_LEN("-"));
  805: 				}
  806: 				break;
  807: 			case FORMAT_ENV:
  808: 				if (NULL != (ds = (data_string *)array_get_element(con->environment, p->conf.parsed_format->ptr[j]->string->ptr))) {
  809: 					accesslog_append_escaped(b, ds->value);
  810: 				} else {
  811: 					buffer_append_string_len(b, CONST_STR_LEN("-"));
  812: 				}
  813: 				break;
  814: 			case FORMAT_FILENAME:
  815: 				if (con->physical.path->used > 1) {
  816: 					buffer_append_string_buffer(b, con->physical.path);
  817: 				} else {
  818: 					buffer_append_string_len(b, CONST_STR_LEN("-"));
  819: 				}
  820: 				break;
  821: 			case FORMAT_BYTES_OUT:
  822: 				if (con->bytes_written > 0) {
  823: 					buffer_append_off_t(b, con->bytes_written);
  824: 				} else {
  825: 					buffer_append_string_len(b, CONST_STR_LEN("-"));
  826: 				}
  827: 				break;
  828: 			case FORMAT_BYTES_IN:
  829: 				if (con->bytes_read > 0) {
  830: 					buffer_append_off_t(b, con->bytes_read);
  831: 				} else {
  832: 					buffer_append_string_len(b, CONST_STR_LEN("-"));
  833: 				}
  834: 				break;
  835: 			case FORMAT_TIME_USED:
  836: 				buffer_append_long(b, srv->cur_ts - con->request_start);
  837: 				break;
  838: 			case FORMAT_SERVER_NAME:
  839: 				if (con->server_name->used > 1) {
  840: 					buffer_append_string_buffer(b, con->server_name);
  841: 				} else {
  842: 					buffer_append_string_len(b, CONST_STR_LEN("-"));
  843: 				}
  844: 				break;
  845: 			case FORMAT_HTTP_HOST:
  846: 				if (con->uri.authority->used > 1) {
  847: 					accesslog_append_escaped(b, con->uri.authority);
  848: 				} else {
  849: 					buffer_append_string_len(b, CONST_STR_LEN("-"));
  850: 				}
  851: 				break;
  852: 			case FORMAT_REQUEST_PROTOCOL:
  853: 				buffer_append_string_len(b,
  854: 						     con->request.http_version == HTTP_VERSION_1_1 ? "HTTP/1.1" : "HTTP/1.0", 8);
  855: 				break;
  856: 			case FORMAT_REQUEST_METHOD:
  857: 				buffer_append_string(b, get_http_method_name(con->request.http_method));
  858: 				break;
  859: 			case FORMAT_PERCENT:
  860: 				buffer_append_string_len(b, CONST_STR_LEN("%"));
  861: 				break;
  862: 			case FORMAT_SERVER_PORT:
  863: 				{
  864: 					const char *colon;
  865: 					buffer *srvtoken = ((server_socket*)(con->srv_socket))->srv_token;
  866: 					if (srvtoken->ptr[0] == '[') {
  867: 						colon = strstr(srvtoken->ptr, "]:");
  868: 					} else {
  869: 						colon = strchr(srvtoken->ptr, ':');
  870: 					}
  871: 					if (colon) {
  872: 						buffer_append_string(b, colon+1);
  873: 					} else {
  874: 						buffer_append_long(b, srv->srvconf.port);
  875: 					}
  876: 				}
  877: 				break;
  878: 			case FORMAT_QUERY_STRING:
  879: 				accesslog_append_escaped(b, con->uri.query);
  880: 				break;
  881: 			case FORMAT_URL:
  882: 				accesslog_append_escaped(b, con->uri.path_raw);
  883: 				break;
  884: 			case FORMAT_CONNECTION_STATUS:
  885: 				switch(con->keep_alive) {
  886: 				case 0: buffer_append_string_len(b, CONST_STR_LEN("-")); break;
  887: 				default: buffer_append_string_len(b, CONST_STR_LEN("+")); break;
  888: 				}
  889: 				break;
  890: 			default:
  891: 				/*
  892: 				 { 'a', FORMAT_REMOTE_ADDR },
  893: 				 { 'A', FORMAT_LOCAL_ADDR },
  894: 				 { 'C', FORMAT_COOKIE },
  895: 				 { 'D', FORMAT_TIME_USED_MS },
  896: 				 */
  897: 
  898: 				break;
  899: 			}
  900: 			break;
  901: 		default:
  902: 			break;
  903: 		}
  904: 	}
  905: 
  906: 	buffer_append_string_len(b, CONST_STR_LEN("\n"));
  907: 
  908: 	if (p->conf.use_syslog ||  /* syslog doesn't cache */
  909: 	    (p->conf.access_logfile->used && p->conf.access_logfile->ptr[0] == '|') || /* pipes don't cache */
  910: 	    newts ||
  911: 	    b->used > BUFFER_MAX_REUSE_SIZE) {
  912: 		if (p->conf.use_syslog) {
  913: #ifdef HAVE_SYSLOG_H
  914: 			if (b->used > 2) {
  915: 				/* syslog appends a \n on its own */
  916: 				syslog(p->conf.syslog_level, "%*s", (int) b->used - 2, b->ptr);
  917: 			}
  918: #endif
  919: 		} else if (p->conf.log_access_fd != -1) {
  920: 			write(p->conf.log_access_fd, b->ptr, b->used - 1);
  921: 		}
  922: 		buffer_reset(b);
  923: 	}
  924: 
  925: 	return HANDLER_GO_ON;
  926: }
  927: 
  928: 
  929: int mod_accesslog_plugin_init(plugin *p);
  930: int mod_accesslog_plugin_init(plugin *p) {
  931: 	p->version     = LIGHTTPD_VERSION_ID;
  932: 	p->name        = buffer_init_string("accesslog");
  933: 
  934: 	p->init        = mod_accesslog_init;
  935: 	p->set_defaults= log_access_open;
  936: 	p->cleanup     = mod_accesslog_free;
  937: 
  938: 	p->handle_request_done  = log_access_write;
  939: 	p->handle_sighup        = log_access_cycle;
  940: 
  941: 	p->data        = NULL;
  942: 
  943: 	return 0;
  944: }

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