Annotation of embedaddon/lighttpd/src/mod_accesslog.c, revision 1.1.1.1
1.1 misho 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>