xref: /lighttpd1.4/src/mod_accesslog.c (revision 1fad70f4)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "fdevent.h"
5 #include "log.h"
6 #include "buffer.h"
7 #include "http_header.h"
8 #include "sock_addr.h"
9 
10 #include "plugin.h"
11 
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 
15 #include <stdlib.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <time.h>
20 
21 #ifdef HAVE_SYSLOG_H
22 # include <syslog.h>
23 #endif
24 
25 typedef struct {
26 	char key;
27 	enum {
28 		FORMAT_UNSET,
29 			FORMAT_UNSUPPORTED,
30 			FORMAT_PERCENT,
31 			FORMAT_REMOTE_HOST,
32 			FORMAT_REMOTE_IDENT,
33 			FORMAT_REMOTE_USER,
34 			FORMAT_TIMESTAMP,
35 			FORMAT_REQUEST_LINE,
36 			FORMAT_STATUS,
37 			FORMAT_BYTES_OUT_NO_HEADER,
38 			FORMAT_HEADER,
39 
40 			FORMAT_REMOTE_ADDR,
41 			FORMAT_LOCAL_ADDR,
42 			FORMAT_COOKIE,
43 			FORMAT_TIME_USED_US,
44 			FORMAT_ENV,
45 			FORMAT_FILENAME,
46 			FORMAT_REQUEST_PROTOCOL,
47 			FORMAT_REQUEST_METHOD,
48 			FORMAT_SERVER_PORT,
49 			FORMAT_QUERY_STRING,
50 			FORMAT_TIME_USED,
51 			FORMAT_URL,
52 			FORMAT_SERVER_NAME,
53 			FORMAT_HTTP_HOST,
54 			FORMAT_CONNECTION_STATUS,
55 			FORMAT_BYTES_IN,
56 			FORMAT_BYTES_OUT,
57 
58 			FORMAT_KEEPALIVE_COUNT,
59 			FORMAT_RESPONSE_HEADER,
60 			FORMAT_NOTE
61 	} type;
62 } format_mapping;
63 
64 /**
65  *
66  *
67  * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
68  *
69  */
70 
71 static const format_mapping fmap[] =
72 {
73 	{ '%', FORMAT_PERCENT },
74 	{ 'h', FORMAT_REMOTE_HOST },
75 	{ 'l', FORMAT_REMOTE_IDENT },
76 	{ 'u', FORMAT_REMOTE_USER },
77 	{ 't', FORMAT_TIMESTAMP },
78 	{ 'r', FORMAT_REQUEST_LINE },
79 	{ 's', FORMAT_STATUS },
80 	{ 'b', FORMAT_BYTES_OUT_NO_HEADER },
81 	{ 'i', FORMAT_HEADER },
82 
83 	{ 'a', FORMAT_REMOTE_ADDR },
84 	{ 'A', FORMAT_LOCAL_ADDR },
85 	{ 'B', FORMAT_BYTES_OUT_NO_HEADER },
86 	{ 'C', FORMAT_COOKIE },
87 	{ 'D', FORMAT_TIME_USED_US },
88 	{ 'e', FORMAT_ENV },
89 	{ 'f', FORMAT_FILENAME },
90 	{ 'H', FORMAT_REQUEST_PROTOCOL },
91 	{ 'k', FORMAT_KEEPALIVE_COUNT },
92 	{ 'm', FORMAT_REQUEST_METHOD },
93 	{ 'n', FORMAT_NOTE },
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 enum e_optflags_time {
112 	/* format string is passed to strftime unless other format optflags set
113 	 * (besides FORMAT_FLAG_TIME_BEGIN or FORMAT_FLAG_TIME_END) */
114 	FORMAT_FLAG_TIME_END       = 0x00,/* use request end time (default) */
115 	FORMAT_FLAG_TIME_BEGIN     = 0x01,/* use request start time */
116 	FORMAT_FLAG_TIME_SEC       = 0x02,/* request time as num  sec since epoch */
117 	FORMAT_FLAG_TIME_MSEC      = 0x04,/* request time as num msec since epoch */
118 	FORMAT_FLAG_TIME_USEC      = 0x08,/* request time as num usec since epoch */
119 	FORMAT_FLAG_TIME_NSEC      = 0x10,/* request time as num nsec since epoch */
120 	FORMAT_FLAG_TIME_MSEC_FRAC = 0x20,/* request time msec fraction */
121 	FORMAT_FLAG_TIME_USEC_FRAC = 0x40,/* request time usec fraction */
122 	FORMAT_FLAG_TIME_NSEC_FRAC = 0x80 /* request time nsec fraction */
123 };
124 
125 enum e_optflags_port {
126 	FORMAT_FLAG_PORT_LOCAL     = 0x01,/* (default) */
127 	FORMAT_FLAG_PORT_REMOTE    = 0x02
128 };
129 
130 
131 typedef struct {
132     enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type;
133     int field;
134     int opt;
135     buffer string;
136 } format_field;
137 
138 typedef struct {
139     time_t last_generated_accesslog_ts;
140     buffer ts_accesslog_str;
141   #if defined(__STDC_VERSION__) && __STDC_VERSION__-0 >= 199901L /* C99 */
142     format_field ptr[];  /* C99 VLA */
143   #else
144     format_field ptr[1];
145   #endif
146 } format_fields;
147 
148 typedef struct {
149 	int    log_access_fd;
150 	char use_syslog; /* syslog has global buffer */
151 	char piped_logger;
152 	unsigned short syslog_level;
153 	buffer *access_logbuffer; /* each logfile has a separate buffer */
154 	const buffer *access_logfile;
155 
156 	format_fields *parsed_format;
157 } plugin_config;
158 
159 typedef struct {
160     int log_access_fd;
161     char piped_logger;
162     const buffer *access_logfile;
163     buffer access_logbuffer; /* each logfile has a separate buffer */
164 } accesslog_st;
165 
166 typedef struct {
167     PLUGIN_DATA;
168     plugin_config defaults;
169     plugin_config conf;
170 
171     buffer syslog_logbuffer; /* syslog has global buffer. no caching, always written directly */
172     log_error_st *errh; /* copy of srv->errh */
173     format_fields *default_format;/* allocated if default format */
174 } plugin_data;
175 
176 INIT_FUNC(mod_accesslog_init) {
177     return calloc(1, sizeof(plugin_data));
178 }
179 
180 static int accesslog_write_all(const int fd, buffer * const b) {
181     const ssize_t wr = (fd >= 0) ? write_all(fd, CONST_BUF_LEN(b)) : 0;
182     buffer_clear(b); /*(clear buffer, even if fd < 0)*/
183     return (-1 != wr);
184 }
185 
186 static void accesslog_append_escaped_str(buffer * const dest, const char * const str, const size_t len) {
187 	const char *ptr, *start, *end;
188 
189 	/* replaces non-printable chars with \xHH where HH is the hex representation of the byte */
190 	/* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
191 	if (0 == len) return;
192 	buffer_string_prepare_append(dest, len);
193 
194 	for (ptr = start = str, end = str+len; ptr < end; ++ptr) {
195 		unsigned char const c = *(const unsigned char *)ptr;
196 		if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
197 			/* nothing to change, add later as one block */
198 		} else {
199 			/* copy previous part */
200 			if (start < ptr) {
201 				buffer_append_string_len(dest, start, ptr - start);
202 			}
203 			start = ptr + 1;
204 
205 			switch (c) {
206 			case '"':
207 				BUFFER_APPEND_STRING_CONST(dest, "\\\"");
208 				break;
209 			case '\\':
210 				BUFFER_APPEND_STRING_CONST(dest, "\\\\");
211 				break;
212 			case '\b':
213 				BUFFER_APPEND_STRING_CONST(dest, "\\b");
214 				break;
215 			case '\n':
216 				BUFFER_APPEND_STRING_CONST(dest, "\\n");
217 				break;
218 			case '\r':
219 				BUFFER_APPEND_STRING_CONST(dest, "\\r");
220 				break;
221 			case '\t':
222 				BUFFER_APPEND_STRING_CONST(dest, "\\t");
223 				break;
224 			case '\v':
225 				BUFFER_APPEND_STRING_CONST(dest, "\\v");
226 				break;
227 			default: {
228 					/* non printable char => \xHH */
229 					char hh[5] = {'\\','x',0,0,0};
230 					char h = c / 16;
231 					hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0');
232 					h = c % 16;
233 					hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0');
234 					buffer_append_string_len(dest, &hh[0], 4);
235 				}
236 				break;
237 			}
238 		}
239 	}
240 
241 	if (start < end) {
242 		buffer_append_string_len(dest, start, end - start);
243 	}
244 }
245 
246 static void accesslog_append_escaped(buffer *dest, const buffer *str) {
247 	accesslog_append_escaped_str(dest, CONST_BUF_LEN(str));
248 }
249 
250 __attribute_cold__
251 static format_fields * accesslog_parse_format_err(log_error_st *errh, const char *file, unsigned int line, format_field *f, const char *msg) {
252     log_error(errh, file, line, "%s", msg);
253     for (; f->type != FIELD_UNSET; ++f) free(f->string.ptr);
254     return NULL;
255 }
256 
257 static format_fields * accesslog_parse_format(const char * const format, const size_t flen, log_error_st * const errh) {
258 	/* common log format (the default) results in 18 elements,
259 	 * so 127 should be enough except for obscene custom usage */
260 	size_t i, j, k = 0, start = 0;
261 	uint32_t used = 0;
262 	const uint32_t sz = 127;/* (sz+1 must match fptr[] num elts below) */
263 	format_field *f;
264 	format_field fptr[128]; /* (128 elements takes 4k on stack in 64-bit) */
265 	memset(fptr, 0, sizeof(fptr));
266 	if (0 != FIELD_UNSET) return NULL;
267 
268 	if (0 == flen) return NULL;
269 
270 	for (i = 0; i < flen; ++i) {
271 		if (format[i] != '%') continue;
272 
273 			if (i > 0 && start != i) {
274 				/* copy the string before this % */
275 				if (used == sz)
276 					return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
277 					         "too many fields (>= 127) in accesslog.format");
278 
279 				f = fptr+used;
280 				f->type = FIELD_STRING;
281 				memset(&f->string, 0, sizeof(buffer));
282 				buffer_copy_string_len(&f->string, format + start, i - start);
283 
284 				++used;
285 			}
286 
287 			/* we need a new field */
288 			if (used == sz)
289 				return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
290 				         "too many fields (>= 127) in accesslog.format");
291 
292 			/* search for the terminating command */
293 			switch (format[i+1]) {
294 			case '>':
295 			case '<':
296 				/* after the } has to be a character */
297 				if (format[i+2] == '\0') {
298 					return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
299 					         "%< and %> have to be followed by a format-specifier");
300 				}
301 
302 
303 				for (j = 0; fmap[j].key != '\0'; j++) {
304 					if (fmap[j].key != format[i+2]) continue;
305 
306 					/* found key */
307 
308 					f = fptr+used;
309 					f->type = FIELD_FORMAT;
310 					f->field = fmap[j].type;
311 					f->opt = 0;
312 					f->string.ptr = NULL;
313 
314 					++used;
315 
316 					break;
317 				}
318 
319 				if (fmap[j].key == '\0') {
320 					return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
321 					         "%< and %> have to be followed by a valid format-specifier");
322 				}
323 
324 				start = i + 3;
325 				i = start - 1; /* skip the string */
326 
327 				break;
328 			case '{':
329 				/* go forward to } */
330 
331 				for (k = i+2; k < flen; ++k) {
332 					if (format[k] == '}') break;
333 				}
334 
335 				if (k == flen) {
336 					return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
337 					         "%{ has to be terminated by a }");
338 				}
339 
340 				/* after the } has to be a character */
341 				if (format[k+1] == '\0') {
342 					return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
343 					         "%{...} has to be followed by a format-specifier");
344 				}
345 
346 				if (k == i + 2) {
347 					return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
348 					         "%{...} has to contain a string");
349 				}
350 
351 				for (j = 0; fmap[j].key != '\0'; j++) {
352 					if (fmap[j].key != format[k+1]) continue;
353 
354 					/* found key */
355 
356 					f = fptr+used;
357 					f->type = FIELD_FORMAT;
358 					f->field = fmap[j].type;
359 					f->opt = 0;
360 					memset(&f->string, 0, sizeof(buffer));
361 					buffer_copy_string_len(&f->string, format + i + 2, k - (i + 2));
362 
363 					++used;
364 
365 					break;
366 				}
367 
368 				if (fmap[j].key == '\0') {
369 					return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
370 					         "%{...} has to be followed by a valid format-specifier");
371 				}
372 
373 				start = k + 2;
374 				i = start - 1; /* skip the string */
375 
376 				break;
377 			default:
378 				/* after the % has to be a character */
379 				if (format[i+1] == '\0') {
380 					return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
381 					         "% has to be followed by a format-specifier");
382 				}
383 
384 				for (j = 0; fmap[j].key != '\0'; j++) {
385 					if (fmap[j].key != format[i+1]) continue;
386 
387 					/* found key */
388 
389 					f = fptr+used;
390 					f->type = FIELD_FORMAT;
391 					f->field = fmap[j].type;
392 					f->string.ptr = NULL;
393 					f->opt = 0;
394 
395 					++used;
396 
397 					break;
398 				}
399 
400 				if (fmap[j].key == '\0') {
401 					return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
402 					         "% has to be followed by a valid format-specifier");
403 				}
404 
405 				start = i + 2;
406 				i = start - 1; /* skip the string */
407 
408 				break;
409 			}
410 	}
411 
412 	if (start < i) {
413 		/* copy the string */
414 		if (used == sz)
415 			return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
416 			         "too many fields (>= 127) in accesslog.format");
417 
418 		f = fptr+used;
419 		f->type = FIELD_STRING;
420 		memset(&f->string, 0, sizeof(buffer));
421 		buffer_copy_string_len(&f->string, format + start, i - start);
422 
423 		++used;
424 	}
425 
426 	format_fields * const fields =
427 	  malloc(sizeof(format_fields) + ((used+1) * sizeof(format_field)));
428 	force_assert(fields);
429 	memset(fields, 0, sizeof(format_fields));
430 	memcpy(fields->ptr, fptr, (used+1) * sizeof(format_field));
431 	return fields;
432 }
433 
434 static void mod_accesslog_free_accesslog(accesslog_st * const x, plugin_data *p) {
435     /*(piped loggers are closed in fdevent_close_logger_pipes())*/
436     if (!x->piped_logger && -1 != x->log_access_fd) {
437         if (!accesslog_write_all(x->log_access_fd, &x->access_logbuffer)) {
438             log_perror(p->errh, __FILE__, __LINE__,
439               "writing access log entry failed: %s", x->access_logfile->ptr);
440         }
441         close(x->log_access_fd);
442     }
443     free(x->access_logbuffer.ptr);
444     free(x);
445 }
446 
447 static void mod_accesslog_free_format_fields(format_fields * const ff) {
448     for (format_field *f = ff->ptr; f->type != FIELD_UNSET; ++f)
449         free(f->string.ptr);
450     free(ff->ts_accesslog_str.ptr);
451     free(ff);
452 }
453 
454 FREE_FUNC(mod_accesslog_free) {
455     plugin_data * const p = p_d;
456     free(p->syslog_logbuffer.ptr);
457     if (NULL == p->cvlist) return;
458     /* (init i to 0 if global context; to 1 to skip empty global context) */
459     for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
460         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
461         for (; -1 != cpv->k_id; ++cpv) {
462             if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue;
463             switch (cpv->k_id) {
464               case 0: /* accesslog.filename */
465                 mod_accesslog_free_accesslog(cpv->v.v, p);
466                 break;
467               case 1: /* accesslog.format */
468                 mod_accesslog_free_format_fields(cpv->v.v);
469                 break;
470               default:
471                 break;
472             }
473         }
474     }
475 
476     if (NULL != p->default_format) {
477         mod_accesslog_free_format_fields(p->default_format);
478     }
479 }
480 
481 static void mod_accesslog_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
482     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
483       case 0:{/* accesslog.filename */
484         if (cpv->vtype != T_CONFIG_LOCAL) break;
485         accesslog_st * const x = cpv->v.v;
486         pconf->log_access_fd    = x->log_access_fd;
487         pconf->piped_logger     = x->piped_logger;
488         pconf->access_logfile   = x->access_logfile;
489         pconf->access_logbuffer = &x->access_logbuffer;
490         break;
491       }
492       case 1:{/* accesslog.format */
493         if (cpv->vtype != T_CONFIG_LOCAL) break;
494         pconf->parsed_format = cpv->v.v;
495         break;
496       }
497       case 2: /* accesslog.use-syslog */
498         pconf->use_syslog = (int)cpv->v.u;
499         break;
500       case 3: /* accesslog.syslog-level */
501         pconf->syslog_level = cpv->v.shrt;
502         break;
503       default:/* should not happen */
504         return;
505     }
506 }
507 
508 static void mod_accesslog_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
509     do {
510         mod_accesslog_merge_config_cpv(pconf, cpv);
511     } while ((++cpv)->k_id != -1);
512 }
513 
514 static void mod_accesslog_patch_config(request_st * const r, plugin_data * const p) {
515     memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
516     for (int i = 1, used = p->nconfig; i < used; ++i) {
517         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
518             mod_accesslog_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
519     }
520 }
521 
522 static format_fields * mod_accesslog_process_format(const char * const format, const size_t flen, server * const srv);
523 
524 SETDEFAULTS_FUNC(mod_accesslog_set_defaults) {
525     static const config_plugin_keys_t cpk[] = {
526       { CONST_STR_LEN("accesslog.filename"),
527         T_CONFIG_STRING,
528         T_CONFIG_SCOPE_CONNECTION }
529      ,{ CONST_STR_LEN("accesslog.format"),
530         T_CONFIG_STRING,
531         T_CONFIG_SCOPE_CONNECTION }
532      ,{ CONST_STR_LEN("accesslog.use-syslog"),
533         T_CONFIG_BOOL,
534         T_CONFIG_SCOPE_CONNECTION }
535      ,{ CONST_STR_LEN("accesslog.syslog-level"),
536         T_CONFIG_SHORT,
537         T_CONFIG_SCOPE_CONNECTION }
538      ,{ NULL, 0,
539         T_CONFIG_UNSET,
540         T_CONFIG_SCOPE_UNSET }
541     };
542 
543     plugin_data * const p = p_d;
544     p->errh = srv->errh;
545     if (!config_plugin_values_init(srv, p, cpk, "mod_accesslog"))
546         return HANDLER_ERROR;
547 
548     /* process and validate config directives
549      * (init i to 0 if global context; to 1 to skip empty global context) */
550     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
551         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
552         int use_syslog = 0;
553         accesslog_st *x = NULL;
554         for (; -1 != cpv->k_id; ++cpv) {
555             switch (cpv->k_id) {
556               case 0: /* accesslog.filename */
557                 x = calloc(1, sizeof(accesslog_st));
558                 force_assert(x);
559                 x->log_access_fd = -1;
560                 x->piped_logger = (cpv->v.b->ptr[0] == '|');
561                 x->access_logfile = cpv->v.b;
562                 cpv->vtype = T_CONFIG_LOCAL;
563                 cpv->v.v = x;
564                 break;
565               case 1: /* accesslog.format */
566                 if (NULL != strchr(cpv->v.b->ptr, '\\')) {
567                     /* process basic backslash-escapes in format string */
568                     buffer *b;
569                     *(const buffer **)&b = cpv->v.b;
570                     char *t = b->ptr;
571                     for (char *s = t; *s; ++s) {
572                         if (s[0] != '\\') { *t++ = *s; continue; }
573                         if (s[1] == '\0') continue; /*(ignore dangling '\\')*/
574                         switch (*++s) {
575                           case 'a': *t++ = '\a'; break; /* bell */
576                           case 'b': *t++ = '\b'; break; /* backspace */
577                           case 'f': *t++ = '\f'; break; /* form feed */
578                           case 'n': *t++ = '\n'; break; /* newline */
579                           case 'r': *t++ = '\r'; break; /* carriage return */
580                           case 't': *t++ = '\t'; break; /* horizontal tab */
581                           case 'v': *t++ = '\v'; break; /* vertical tab */
582                           /*case '"':*/
583                           /*case '\\':*/
584                           default:  *t++ = *s;   break; /*(use literal char)*/
585                         }
586                     }
587                     buffer_string_set_length(b, (size_t)(t - b->ptr));
588                 }
589                 cpv->v.v =
590                   mod_accesslog_process_format(CONST_BUF_LEN(cpv->v.b), srv);
591                 if (NULL == cpv->v.v) return HANDLER_ERROR;
592                 cpv->vtype = T_CONFIG_LOCAL;
593                 break;
594               case 2: /* accesslog.use-syslog */
595                 use_syslog = (int)cpv->v.u;
596                 break;
597               case 3: /* accesslog.syslog-level */
598                 break;
599               default:/* should not happen */
600                 break;
601             }
602         }
603 
604         if (srv->srvconf.preflight_check) continue;
605 
606         if (use_syslog) continue; /* ignore the next checks */
607         if (NULL == x || buffer_string_is_empty(x->access_logfile)) continue;
608 
609         x->log_access_fd = fdevent_open_logger(x->access_logfile->ptr);
610         if (-1 == x->log_access_fd) {
611             log_perror(srv->errh, __FILE__, __LINE__,
612               "opening log '%s' failed", x->access_logfile->ptr);
613             return HANDLER_ERROR;
614         }
615     }
616 
617     p->defaults.log_access_fd = -1;
618     p->defaults.syslog_level = LOG_INFO;
619 
620     /* initialize p->defaults from global config context */
621     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
622         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
623         if (-1 != cpv->k_id)
624             mod_accesslog_merge_config(&p->defaults, cpv);
625     }
626 
627     if (NULL == p->defaults.parsed_format) {
628         /* (set default format even if p->use_syslog since
629          *  some other condition might enable logfile) */
630         static const char fmt[] =
631           "%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"";
632         p->defaults.parsed_format = p->default_format =
633           mod_accesslog_process_format(CONST_STR_LEN(fmt), srv);
634         if (NULL == p->default_format) return HANDLER_ERROR;
635     }
636 
637     return HANDLER_GO_ON;
638 }
639 
640 static format_fields * mod_accesslog_process_format(const char * const format, const size_t flen, server * const srv) {
641 			format_fields * const parsed_format =
642 			  accesslog_parse_format(format, flen, srv->errh);
643 			if (NULL == parsed_format) {
644 				log_error(srv->errh, __FILE__, __LINE__,
645 					"parsing accesslog-definition failed: %s", format);
646 				return NULL;
647 			}
648 
649 			uint32_t tcount = 0;
650 			for (format_field *f = parsed_format->ptr; f->type != FIELD_UNSET; ++f) {
651 				const buffer * const fstr = &f->string;
652 				if (FIELD_FORMAT != f->type) continue;
653 				if (FORMAT_TIMESTAMP == f->field) {
654 					if (!buffer_string_is_empty(fstr)) {
655 						const char *ptr = fstr->ptr;
656 						if (0 == strncmp(ptr, "begin:", sizeof("begin:")-1)) {
657 							f->opt |= FORMAT_FLAG_TIME_BEGIN;
658 							ptr += sizeof("begin:")-1;
659 						} else if (0 == strncmp(ptr, "end:", sizeof("end:")-1)) {
660 							f->opt |= FORMAT_FLAG_TIME_END;
661 							ptr += sizeof("end:")-1;
662 						}
663 						if      (0 == strcmp(ptr, "sec"))       f->opt |= FORMAT_FLAG_TIME_SEC;
664 						else if (0 == strcmp(ptr, "msec"))      f->opt |= FORMAT_FLAG_TIME_MSEC;
665 						else if (0 == strcmp(ptr, "usec"))      f->opt |= FORMAT_FLAG_TIME_USEC;
666 						else if (0 == strcmp(ptr, "nsec"))      f->opt |= FORMAT_FLAG_TIME_NSEC;
667 						else if (0 == strcmp(ptr, "msec_frac")) f->opt |= FORMAT_FLAG_TIME_MSEC_FRAC;
668 						else if (0 == strcmp(ptr, "usec_frac")) f->opt |= FORMAT_FLAG_TIME_USEC_FRAC;
669 						else if (0 == strcmp(ptr, "nsec_frac")) f->opt |= FORMAT_FLAG_TIME_NSEC_FRAC;
670 						else if (NULL == strchr(ptr, '%')) {
671 							log_error(srv->errh, __FILE__, __LINE__,
672 								"constant string for time format (misspelled token? or missing '%%'): %s", format);
673 							mod_accesslog_free_format_fields(parsed_format);
674 							return NULL;
675 						}
676 					}
677 
678 					/* make sure they didn't try to send the timestamp in twice
679 					 * (would invalidate pconf->parsed_format.ts_accesslog_str cache of timestamp str) */
680 					if (!(f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END|FORMAT_FLAG_TIME_SEC)) && ++tcount > 1) {
681 						log_error(srv->errh, __FILE__, __LINE__,
682 							"you may not use strftime timestamp format %%{}t twice in the same access log: %s", format);
683 						mod_accesslog_free_format_fields(parsed_format);
684 						return NULL;
685 					}
686 
687 					if (f->opt & FORMAT_FLAG_TIME_BEGIN) srv->srvconf.high_precision_timestamps = 1;
688 				} else if (FORMAT_TIME_USED_US == f->field) {
689 					f->opt |= FORMAT_FLAG_TIME_USEC;
690 					srv->srvconf.high_precision_timestamps = 1;
691 				} else if (FORMAT_TIME_USED == f->field) {
692 					if (buffer_string_is_empty(fstr)
693 					      || buffer_is_equal_string(fstr, CONST_STR_LEN("s"))
694 					      || buffer_is_equal_string(fstr, CONST_STR_LEN("sec")))  f->opt |= FORMAT_FLAG_TIME_SEC;
695 					else if (buffer_is_equal_string(fstr, CONST_STR_LEN("ms"))
696 					      || buffer_is_equal_string(fstr, CONST_STR_LEN("msec"))) f->opt |= FORMAT_FLAG_TIME_MSEC;
697 					else if (buffer_is_equal_string(fstr, CONST_STR_LEN("us"))
698 					      || buffer_is_equal_string(fstr, CONST_STR_LEN("usec"))) f->opt |= FORMAT_FLAG_TIME_USEC;
699 					else if (buffer_is_equal_string(fstr, CONST_STR_LEN("ns"))
700 					      || buffer_is_equal_string(fstr, CONST_STR_LEN("nsec"))) f->opt |= FORMAT_FLAG_TIME_NSEC;
701 					else {
702 						log_error(srv->errh, __FILE__, __LINE__,
703 							"invalid time unit in %%{UNIT}T: %s", format);
704 						mod_accesslog_free_format_fields(parsed_format);
705 						return NULL;
706 					}
707 
708 					if (f->opt & ~(FORMAT_FLAG_TIME_SEC)) srv->srvconf.high_precision_timestamps = 1;
709 				} else if (FORMAT_COOKIE == f->field) {
710 					if (buffer_string_is_empty(fstr)) f->type = FIELD_STRING; /*(blank)*/
711 				} else if (FORMAT_SERVER_PORT == f->field) {
712 					if (buffer_string_is_empty(fstr))
713 						f->opt |= FORMAT_FLAG_PORT_LOCAL;
714 					else if (buffer_is_equal_string(fstr, CONST_STR_LEN("canonical")))
715 						f->opt |= FORMAT_FLAG_PORT_LOCAL;
716 					else if (buffer_is_equal_string(fstr, CONST_STR_LEN("local")))
717 						f->opt |= FORMAT_FLAG_PORT_LOCAL;
718 					else if (buffer_is_equal_string(fstr, CONST_STR_LEN("remote")))
719 						f->opt |= FORMAT_FLAG_PORT_REMOTE;
720 					else {
721 						log_error(srv->errh, __FILE__, __LINE__,
722 							"invalid format %%{canonical,local,remote}p: %s", format);
723 						mod_accesslog_free_format_fields(parsed_format);
724 						return NULL;
725 					}
726 				} else if (FORMAT_HEADER == f->field
727 				           || FORMAT_RESPONSE_HEADER == f->field) {
728 					f->opt = http_header_hkey_get(CONST_BUF_LEN(fstr));
729 				}
730 			}
731 
732 			return parsed_format;
733 }
734 
735 static void log_access_flush(plugin_data * const p) {
736     /* future: might be slightly faster to have allocated array of open files
737      * rather than walking config, but only might matter with many directives */
738     /* (init i to 0 if global context; to 1 to skip empty global context) */
739     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
740         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
741         for (; -1 != cpv->k_id; ++cpv) {
742             switch (cpv->k_id) {
743               case 0: /* accesslog.filename */
744                 if (cpv->vtype == T_CONFIG_LOCAL && NULL != cpv->v.v) {
745                     accesslog_st * const x = cpv->v.v;
746                     if (buffer_string_is_empty(&x->access_logbuffer)) continue;
747                     if (!accesslog_write_all(x->log_access_fd,
748                                              &x->access_logbuffer)) {
749                         log_perror(p->errh, __FILE__, __LINE__,
750                           "writing access log entry failed: %s",
751                           x->access_logfile->ptr);
752                     }
753                 }
754                 break;
755               default:
756                 break;
757             }
758         }
759     }
760 }
761 
762 TRIGGER_FUNC(log_access_periodic_flush) {
763     /* flush buffered access logs every 4 seconds */
764     if (0 == (log_epoch_secs & 3)) log_access_flush((plugin_data *)p_d);
765     UNUSED(srv);
766     return HANDLER_GO_ON;
767 }
768 
769 SIGHUP_FUNC(log_access_cycle) {
770     plugin_data * const p = p_d;
771 
772     log_access_flush(p);
773 
774     /* future: might be slightly faster to have allocated array of open files
775      * rather than walking config, but only might matter with many directives */
776     /* (init i to 0 if global context; to 1 to skip empty global context) */
777     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
778         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
779         for (; -1 != cpv->k_id; ++cpv) {
780             if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue;
781             switch (cpv->k_id) {
782               case 0:{/* accesslog.filename */
783                 accesslog_st * const x = cpv->v.v;
784                 if (x->piped_logger) continue;
785                 if (buffer_string_is_empty(x->access_logfile)) continue;
786                 if (-1 == fdevent_cycle_logger(x->access_logfile->ptr,
787                                                &x->log_access_fd)) {
788                     log_perror(srv->errh, __FILE__, __LINE__,
789                       "cycling access log failed: %s", x->access_logfile->ptr);
790                 }
791                 /*(update p->defaults after cycling log)*/
792                 if (0 == i) p->defaults.log_access_fd = x->log_access_fd;
793                 break;
794               }
795               default:
796                 break;
797             }
798         }
799     }
800 
801     return HANDLER_GO_ON;
802 }
803 
804 static int log_access_record (const request_st * const r, buffer * const b, format_fields * const parsed_format) {
805 	const connection * const con = r->con;
806 	const buffer *vb;
807 	struct timespec ts = { 0, 0 };
808 	int flush = 0;
809 
810 	for (const format_field *f = parsed_format->ptr; f->type != FIELD_UNSET; ++f) {
811 		switch(f->type) {
812 		case FIELD_STRING:
813 			buffer_append_string_buffer(b, &f->string);
814 			break;
815 		case FIELD_FORMAT:
816 			switch(f->field) {
817 			case FORMAT_TIMESTAMP:
818 
819 				if (f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END)) {
820 					if (f->opt & FORMAT_FLAG_TIME_SEC) {
821 						time_t t = (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) ? log_epoch_secs : r->start_hp.tv_sec;
822 						buffer_append_int(b, (intmax_t)t);
823 					} else if (f->opt & (FORMAT_FLAG_TIME_MSEC|FORMAT_FLAG_TIME_USEC|FORMAT_FLAG_TIME_NSEC)) {
824 						off_t t; /*(expected to be 64-bit since large file support enabled)*/
825 						long ns;
826 						if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
827 							if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts);
828 							t = (off_t)ts.tv_sec;
829 							ns = ts.tv_nsec;
830 						} else {
831 							t = (off_t)r->start_hp.tv_sec;
832 							ns = r->start_hp.tv_nsec;
833 						}
834 						if (f->opt & FORMAT_FLAG_TIME_MSEC) {
835 							t *= 1000;
836 							t += (ns + 999999) / 1000000; /* ceil */
837 						} else if (f->opt & FORMAT_FLAG_TIME_USEC) {
838 							t *= 1000000;
839 							t += (ns + 999) / 1000; /* ceil */
840 						} else {/*(f->opt & FORMAT_FLAG_TIME_NSEC)*/
841 							t *= 1000000000;
842 							t += ns;
843 						}
844 						buffer_append_int(b, (intmax_t)t);
845 					} else { /*(FORMAT_FLAG_TIME_MSEC_FRAC|FORMAT_FLAG_TIME_USEC_FRAC|FORMAT_FLAG_TIME_NSEC_FRAC)*/
846 						long ns;
847 						char *ptr;
848 						if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
849 							if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts);
850 							ns = ts.tv_nsec;
851 						} else {
852 							ns = r->start_hp.tv_nsec;
853 						}
854 						/*assert(t < 1000000000);*/
855 						if (f->opt & FORMAT_FLAG_TIME_MSEC_FRAC) {
856 							ns +=  999999; /* ceil */
857 							ns /= 1000000;
858 							buffer_append_string_len(b, CONST_STR_LEN("000"));
859 						} else if (f->opt & FORMAT_FLAG_TIME_USEC_FRAC) {
860 							ns +=  999; /* ceil */
861 							ns /= 1000;
862 							buffer_append_string_len(b, CONST_STR_LEN("000000"));
863 						} else {/*(f->opt & FORMAT_FLAG_TIME_NSEC_FRAC)*/
864 							buffer_append_string_len(b, CONST_STR_LEN("000000000"));
865 						}
866 						for (ptr = b->ptr + buffer_string_length(b); ns > 0; ns /= 10)
867 							*--ptr = (ns % 10) + '0';
868 					}
869 				} else {
870 					buffer * const ts_accesslog_str = &parsed_format->ts_accesslog_str;
871 					/* cache the generated timestamp (only if ! FORMAT_FLAG_TIME_BEGIN) */
872 					struct tm *tmptr;
873 					time_t t;
874 				      #if defined(HAVE_STRUCT_TM_GMTOFF)
875 				      # ifdef HAVE_LOCALTIME_R
876 					struct tm tm;
877 				      # endif /* HAVE_LOCALTIME_R */
878 				      #else /* HAVE_STRUCT_TM_GMTOFF */
879 				      # ifdef HAVE_GMTIME_R
880 					struct tm tm;
881 				      # endif /* HAVE_GMTIME_R */
882 				      #endif /* HAVE_STRUCT_TM_GMTOFF */
883 
884 					if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
885 						const time_t cur_ts = log_epoch_secs;
886 						if (parsed_format->last_generated_accesslog_ts == cur_ts) {
887 							buffer_append_string_buffer(b, ts_accesslog_str);
888 							break;
889 						}
890 						t = parsed_format->last_generated_accesslog_ts = cur_ts;
891 						flush = 1;
892 					} else {
893 						t = r->start_hp.tv_sec;
894 					}
895 
896 				      #if defined(HAVE_STRUCT_TM_GMTOFF)
897 				      # ifdef HAVE_LOCALTIME_R
898 					tmptr = localtime_r(&t, &tm);
899 				      # else /* HAVE_LOCALTIME_R */
900 					tmptr = localtime(&t);
901 				      # endif /* HAVE_LOCALTIME_R */
902 				      #else /* HAVE_STRUCT_TM_GMTOFF */
903 				      # ifdef HAVE_GMTIME_R
904 					tmptr = gmtime_r(&t, &tm);
905 				      # else /* HAVE_GMTIME_R */
906 					tmptr = gmtime(&t);
907 				      # endif /* HAVE_GMTIME_R */
908 				      #endif /* HAVE_STRUCT_TM_GMTOFF */
909 
910 					buffer_clear(ts_accesslog_str);
911 
912 					if (buffer_string_is_empty(&f->string)) {
913 					      #if defined(HAVE_STRUCT_TM_GMTOFF)
914 						long scd, hrs, min;
915 						buffer_append_strftime(ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S ", tmptr);
916 						buffer_append_string_len(ts_accesslog_str, tmptr->tm_gmtoff >= 0 ? "+" : "-", 1);
917 
918 						scd = labs(tmptr->tm_gmtoff);
919 						hrs = scd / 3600;
920 						min = (scd % 3600) / 60;
921 
922 						/* hours */
923 						if (hrs < 10) buffer_append_string_len(ts_accesslog_str, CONST_STR_LEN("0"));
924 						buffer_append_int(ts_accesslog_str, hrs);
925 
926 						if (min < 10) buffer_append_string_len(ts_accesslog_str, CONST_STR_LEN("0"));
927 						buffer_append_int(ts_accesslog_str, min);
928 						buffer_append_string_len(ts_accesslog_str, CONST_STR_LEN("]"));
929 					      #else
930 						buffer_append_strftime(ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S +0000]", tmptr);
931 					      #endif /* HAVE_STRUCT_TM_GMTOFF */
932 					} else {
933 						buffer_append_strftime(ts_accesslog_str, f->string.ptr, tmptr);
934 					}
935 
936 					buffer_append_string_buffer(b, ts_accesslog_str);
937 				}
938 				break;
939 			case FORMAT_TIME_USED:
940 			case FORMAT_TIME_USED_US:
941 				if (f->opt & FORMAT_FLAG_TIME_SEC) {
942 					buffer_append_int(b, log_epoch_secs - r->start_hp.tv_sec);
943 				} else {
944 					const struct timespec * const bs = &r->start_hp;
945 					off_t tdiff; /*(expected to be 64-bit since large file support enabled)*/
946 					if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts);
947 					tdiff = (off_t)(ts.tv_sec - bs->tv_sec)*1000000000 + (ts.tv_nsec - bs->tv_nsec);
948 					if (tdiff <= 0) {
949 						/* sanity check for time moving backwards
950 						 * (daylight savings adjustment or leap seconds or ?) */
951 						tdiff  = -1;
952 					} else if (f->opt & FORMAT_FLAG_TIME_MSEC) {
953 						tdiff +=  999999; /* ceil */
954 						tdiff /= 1000000;
955 					} else if (f->opt & FORMAT_FLAG_TIME_USEC) {
956 						tdiff +=  999; /* ceil */
957 						tdiff /= 1000;
958 					} /* else (f->opt & FORMAT_FLAG_TIME_NSEC) */
959 					buffer_append_int(b, (intmax_t)tdiff);
960 				}
961 				break;
962 			case FORMAT_REMOTE_ADDR:
963 			case FORMAT_REMOTE_HOST:
964 				buffer_append_string_buffer(b, con->dst_addr_buf);
965 				break;
966 			case FORMAT_REMOTE_IDENT:
967 				/* ident */
968 				buffer_append_string_len(b, CONST_STR_LEN("-"));
969 				break;
970 			case FORMAT_REMOTE_USER:
971 				if (NULL != (vb = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")))) {
972 					accesslog_append_escaped(b, vb);
973 				} else {
974 					buffer_append_string_len(b, CONST_STR_LEN("-"));
975 				}
976 				break;
977 			case FORMAT_REQUEST_LINE:
978 				/*(attempt to reconstruct request line)*/
979 				http_method_append(b, r->http_method);
980 				buffer_append_string_len(b, CONST_STR_LEN(" "));
981 				accesslog_append_escaped(b, &r->target_orig);
982 				buffer_append_string_len(b, CONST_STR_LEN(" "));
983 				http_version_append(b, r->http_version);
984 				break;
985 			case FORMAT_STATUS:
986 				buffer_append_int(b, r->http_status);
987 				break;
988 
989 			case FORMAT_BYTES_OUT_NO_HEADER:
990 			{
991 				off_t bytes = r->http_version <= HTTP_VERSION_1_1
992 				  ? con->bytes_written - r->bytes_written_ckpt
993 				  : r->write_queue.bytes_out;
994 				if (bytes > 0) {
995 					bytes -= (off_t)r->resp_header_len;
996 					buffer_append_int(b, bytes > 0 ? bytes : 0);
997 				} else {
998 					buffer_append_string_len(b, CONST_STR_LEN("-"));
999 				}
1000 				break;
1001 			}
1002 			case FORMAT_HEADER:
1003 				if (NULL != (vb = http_header_request_get(r, f->opt, CONST_BUF_LEN(&f->string)))) {
1004 					accesslog_append_escaped(b, vb);
1005 				} else {
1006 					buffer_append_string_len(b, CONST_STR_LEN("-"));
1007 				}
1008 				break;
1009 			case FORMAT_RESPONSE_HEADER:
1010 				if (NULL != (vb = http_header_response_get(r, f->opt, CONST_BUF_LEN(&f->string)))) {
1011 					accesslog_append_escaped(b, vb);
1012 				} else {
1013 					buffer_append_string_len(b, CONST_STR_LEN("-"));
1014 				}
1015 				break;
1016 			case FORMAT_ENV:
1017 			case FORMAT_NOTE:
1018 				if (NULL != (vb = http_header_env_get(r, CONST_BUF_LEN(&f->string)))) {
1019 					accesslog_append_escaped(b, vb);
1020 				} else {
1021 					buffer_append_string_len(b, CONST_STR_LEN("-"));
1022 				}
1023 				break;
1024 			case FORMAT_FILENAME:
1025 				if (!buffer_string_is_empty(&r->physical.path)) {
1026 					buffer_append_string_buffer(b, &r->physical.path);
1027 				} else {
1028 					buffer_append_string_len(b, CONST_STR_LEN("-"));
1029 				}
1030 				break;
1031 			case FORMAT_BYTES_OUT:
1032 			{
1033 				off_t bytes = r->http_version <= HTTP_VERSION_1_1
1034 				  ? con->bytes_written - r->bytes_written_ckpt
1035 				  : r->write_queue.bytes_out;
1036 				if (bytes > 0) {
1037 					buffer_append_int(b, bytes);
1038 				} else {
1039 					buffer_append_string_len(b, CONST_STR_LEN("-"));
1040 				}
1041 				break;
1042 			}
1043 			case FORMAT_BYTES_IN:
1044 			{
1045 				off_t bytes = r->http_version <= HTTP_VERSION_1_1
1046 				  ? con->bytes_read - r->bytes_read_ckpt
1047 				  : r->read_queue.bytes_in + (off_t)r->rqst_header_len;
1048 				if (bytes > 0) {
1049 					buffer_append_int(b, bytes);
1050 				} else {
1051 					buffer_append_string_len(b, CONST_STR_LEN("-"));
1052 				}
1053 				break;
1054 			}
1055 			case FORMAT_SERVER_NAME:
1056 				if (!buffer_string_is_empty(r->server_name)) {
1057 					buffer_append_string_buffer(b, r->server_name);
1058 				} else {
1059 					buffer_append_string_len(b, CONST_STR_LEN("-"));
1060 				}
1061 				break;
1062 			case FORMAT_HTTP_HOST:
1063 				if (!buffer_string_is_empty(&r->uri.authority)) {
1064 					accesslog_append_escaped(b, &r->uri.authority);
1065 				} else {
1066 					buffer_append_string_len(b, CONST_STR_LEN("-"));
1067 				}
1068 				break;
1069 			case FORMAT_REQUEST_PROTOCOL:
1070 				http_version_append(b, r->http_version);
1071 				break;
1072 			case FORMAT_REQUEST_METHOD:
1073 				http_method_append(b, r->http_method);
1074 				break;
1075 			case FORMAT_PERCENT:
1076 				buffer_append_string_len(b, CONST_STR_LEN("%"));
1077 				break;
1078 			case FORMAT_LOCAL_ADDR:
1079 				{
1080 					/* (perf: not using getsockname() and inet_ntop_cache_get_ip())
1081 					 * (still useful if admin has configured explicit listen IPs) */
1082 					const char *colon;
1083 					buffer *srvtoken = con->srv_socket->srv_token;
1084 					if (srvtoken->ptr[0] == '[') {
1085 						colon = strstr(srvtoken->ptr, "]:");
1086 					} else {
1087 						colon = strchr(srvtoken->ptr, ':');
1088 					}
1089 					if (colon) {
1090 						buffer_append_string_len(b, srvtoken->ptr, (size_t)(colon - srvtoken->ptr));
1091 					} else {
1092 						buffer_append_string_buffer(b, srvtoken);
1093 					}
1094 				}
1095 				break;
1096 			case FORMAT_SERVER_PORT:
1097 				if (f->opt & FORMAT_FLAG_PORT_REMOTE) {
1098 					buffer_append_int(b, sock_addr_get_port(&con->dst_addr));
1099 				} else { /* if (f->opt & FORMAT_FLAG_PORT_LOCAL) *//*(default)*/
1100 					const char *colon;
1101 					buffer *srvtoken = ((server_socket*)(con->srv_socket))->srv_token;
1102 					if (srvtoken->ptr[0] == '[') {
1103 						colon = strstr(srvtoken->ptr, "]:");
1104 					} else {
1105 						colon = strchr(srvtoken->ptr, ':');
1106 					}
1107 					if (colon) {
1108 						buffer_append_string(b, colon+1);
1109 					} else {
1110 						buffer_append_int(b, con->srv->srvconf.port);
1111 					}
1112 				}
1113 				break;
1114 			case FORMAT_QUERY_STRING:
1115 				accesslog_append_escaped(b, &r->uri.query);
1116 				break;
1117 			case FORMAT_URL:
1118 				{
1119 					const uint32_t len = buffer_string_length(&r->target);
1120 					const char * const qmark = memchr(r->target.ptr, '?', len);
1121 					accesslog_append_escaped_str(b, r->target.ptr, qmark ? (uint32_t)(qmark - r->target.ptr) : len);
1122 				}
1123 				break;
1124 			case FORMAT_CONNECTION_STATUS:
1125 				if (r->state == CON_STATE_RESPONSE_END) {
1126 					if (0 == r->keep_alive) {
1127 						buffer_append_string_len(b, CONST_STR_LEN("-"));
1128 					} else {
1129 						buffer_append_string_len(b, CONST_STR_LEN("+"));
1130 					}
1131 				} else { /* CON_STATE_ERROR */
1132 					buffer_append_string_len(b, CONST_STR_LEN("X"));
1133 				}
1134 				break;
1135 			case FORMAT_KEEPALIVE_COUNT:
1136 				if (con->request_count > 1) {
1137 					buffer_append_int(b, (intmax_t)(con->request_count-1));
1138 				} else {
1139 					buffer_append_string_len(b, CONST_STR_LEN("0"));
1140 				}
1141 				break;
1142 			case FORMAT_COOKIE:
1143 				if (NULL != (vb = http_header_request_get(r, HTTP_HEADER_COOKIE, CONST_STR_LEN("Cookie")))) {
1144 					char *str = vb->ptr;
1145 					size_t len = buffer_string_length(&f->string);
1146 					do {
1147 						while (*str == ' ' || *str == '\t') ++str;
1148 						if (0 == strncmp(str, f->string.ptr, len) && str[len] == '=') {
1149 							char *v = str+len+1;
1150 							for (str = v; *str != '\0' && *str != ';'; ++str) ;
1151 							if (str == v) break;
1152 							do { --str; } while (str > v && (*str == ' ' || *str == '\t'));
1153 							accesslog_append_escaped_str(b, v, str - v + 1);
1154 							break;
1155 						} else {
1156 							while (*str != ';' && *str != ' ' && *str != '\t' && *str != '\0') ++str;
1157 						}
1158 						while (*str == ' ' || *str == '\t') ++str;
1159 					} while (*str++ == ';');
1160 				}
1161 				break;
1162 			default:
1163 				break;
1164 			}
1165 			break;
1166 		default:
1167 			break;
1168 		}
1169 	}
1170 
1171 	return flush;
1172 }
1173 
1174 REQUESTDONE_FUNC(log_access_write) {
1175 	plugin_data * const p = p_d;
1176 	mod_accesslog_patch_config(r, p);
1177 
1178 	/* No output device, nothing to do */
1179 	if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON;
1180 
1181 	buffer * const b = (p->conf.use_syslog)
1182 	  ? &p->syslog_logbuffer
1183 	  : p->conf.access_logbuffer;
1184 
1185 	const int flush = p->conf.piped_logger
1186 	                | log_access_record(r, b, p->conf.parsed_format);
1187 
1188 	if (p->conf.use_syslog) { /* syslog doesn't cache */
1189 #ifdef HAVE_SYSLOG_H
1190 		if (!buffer_string_is_empty(b)) {
1191 			/*(syslog appends a \n on its own)*/
1192 			syslog(p->conf.syslog_level, "%s", b->ptr);
1193 		}
1194 #endif
1195 		buffer_clear(b);
1196 	}
1197 	else {
1198 		buffer_append_string_len(b, CONST_STR_LEN("\n"));
1199 
1200 		if (flush || buffer_string_length(b) >= 8192) {
1201 			if (!accesslog_write_all(p->conf.log_access_fd, b)) {
1202 				log_perror(r->conf.errh, __FILE__, __LINE__,
1203 				  "writing access log entry failed: %s",
1204 				  p->conf.access_logfile->ptr);
1205 			}
1206 		}
1207 	}
1208 
1209 	return HANDLER_GO_ON;
1210 }
1211 
1212 
1213 int mod_accesslog_plugin_init(plugin *p);
1214 int mod_accesslog_plugin_init(plugin *p) {
1215 	p->version     = LIGHTTPD_VERSION_ID;
1216 	p->name        = "accesslog";
1217 
1218 	p->init        = mod_accesslog_init;
1219 	p->set_defaults= mod_accesslog_set_defaults;
1220 	p->cleanup     = mod_accesslog_free;
1221 
1222 	p->handle_request_done  = log_access_write;
1223 	p->handle_trigger       = log_access_periodic_flush;
1224 	p->handle_sighup        = log_access_cycle;
1225 
1226 	return 0;
1227 }
1228