Annotation of embedaddon/lighttpd/src/mod_accesslog.c, revision 1.1.1.2
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: if (p->syslog_logbuffer) buffer_free(p->syslog_logbuffer);
1.1.1.2 ! misho 450: free(p);
1.1 misho 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:
1.1.1.2 ! misho 469: p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1 misho 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:
1.1.1.2 ! misho 606: if (-1 != s->log_access_fd) close(s->log_access_fd);
1.1 misho 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: }
1.1.1.2 ! misho 615: fd_close_on_exec(s->log_access_fd);
1.1 misho 616: }
617: }
618:
619: return HANDLER_GO_ON;
620: }
621:
622: #define PATCH(x) \
623: p->conf.x = s->x;
624: static int mod_accesslog_patch_connection(server *srv, connection *con, plugin_data *p) {
625: size_t i, j;
626: plugin_config *s = p->config_storage[0];
627:
628: PATCH(access_logfile);
629: PATCH(format);
630: PATCH(log_access_fd);
631: PATCH(last_generated_accesslog_ts_ptr);
632: PATCH(access_logbuffer);
633: PATCH(ts_accesslog_str);
634: PATCH(ts_accesslog_fmt_str);
635: PATCH(append_tz_offset);
636: PATCH(parsed_format);
637: PATCH(use_syslog);
638: PATCH(syslog_level);
639:
640: /* skip the first, the global context */
641: for (i = 1; i < srv->config_context->used; i++) {
642: data_config *dc = (data_config *)srv->config_context->data[i];
643: s = p->config_storage[i];
644:
645: /* condition didn't match */
646: if (!config_check_cond(srv, con, dc)) continue;
647:
648: /* merge config */
649: for (j = 0; j < dc->value->used; j++) {
650: data_unset *du = dc->value->data[j];
651:
652: if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.filename"))) {
653: PATCH(access_logfile);
654: PATCH(log_access_fd);
655: PATCH(access_logbuffer);
656: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.format"))) {
657: PATCH(format);
658: PATCH(parsed_format);
659: PATCH(last_generated_accesslog_ts_ptr);
660: PATCH(ts_accesslog_str);
661: PATCH(ts_accesslog_fmt_str);
662: PATCH(append_tz_offset);
663: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.use-syslog"))) {
664: PATCH(use_syslog);
665: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.syslog-level"))) {
666: PATCH(syslog_level);
667: }
668: }
669: }
670:
671: return 0;
672: }
673: #undef PATCH
674:
675: REQUESTDONE_FUNC(log_access_write) {
676: plugin_data *p = p_d;
677: buffer *b;
678: size_t j;
679:
680: int newts = 0;
681: data_string *ds;
682:
683: mod_accesslog_patch_connection(srv, con, p);
684:
685: /* No output device, nothing to do */
686: if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON;
687:
688: if (p->conf.use_syslog) {
689: b = p->syslog_logbuffer;
690: } else {
691: b = p->conf.access_logbuffer;
692: }
693:
694: if (b->used == 0) {
695: buffer_copy_string_len(b, CONST_STR_LEN(""));
696: }
697:
698: for (j = 0; j < p->conf.parsed_format->used; j++) {
699: switch(p->conf.parsed_format->ptr[j]->type) {
700: case FIELD_STRING:
701: buffer_append_string_buffer(b, p->conf.parsed_format->ptr[j]->string);
702: break;
703: case FIELD_FORMAT:
704: switch(p->conf.parsed_format->ptr[j]->field) {
705: case FORMAT_TIMESTAMP:
706:
707: /* cache the generated timestamp */
708: if (srv->cur_ts != *(p->conf.last_generated_accesslog_ts_ptr)) {
709: struct tm tm;
710: #if defined(HAVE_STRUCT_TM_GMTOFF)
711: long scd, hrs, min;
712: #endif
713:
714: buffer_prepare_copy(p->conf.ts_accesslog_str, 255);
715: #if defined(HAVE_STRUCT_TM_GMTOFF)
716: # ifdef HAVE_LOCALTIME_R
717: localtime_r(&(srv->cur_ts), &tm);
718: strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, &tm);
719: # else /* HAVE_LOCALTIME_R */
720: 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)));
721: # endif /* HAVE_LOCALTIME_R */
722: p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1;
723:
724: if (p->conf.append_tz_offset) {
725: buffer_append_string_len(p->conf.ts_accesslog_str, tm.tm_gmtoff >= 0 ? "+" : "-", 1);
726:
727: scd = abs(tm.tm_gmtoff);
728: hrs = scd / 3600;
729: min = (scd % 3600) / 60;
730:
731: /* hours */
732: if (hrs < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
733: buffer_append_long(p->conf.ts_accesslog_str, hrs);
734:
735: if (min < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
736: buffer_append_long(p->conf.ts_accesslog_str, min);
737: buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("]"));
738: }
739: #else /* HAVE_STRUCT_TM_GMTOFF */
740: # ifdef HAVE_GMTIME_R
741: gmtime_r(&(srv->cur_ts), &tm);
742: strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, &tm);
743: # else /* HAVE_GMTIME_R */
744: 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)));
745: # endif /* HAVE_GMTIME_R */
746: p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1;
747: #endif /* HAVE_STRUCT_TM_GMTOFF */
748:
749: *(p->conf.last_generated_accesslog_ts_ptr) = srv->cur_ts;
750: newts = 1;
751: }
752:
753: buffer_append_string_buffer(b, p->conf.ts_accesslog_str);
754:
755: break;
756: case FORMAT_REMOTE_HOST:
757:
758: /* handle inet_ntop cache */
759:
760: buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
761:
762: break;
763: case FORMAT_REMOTE_IDENT:
764: /* ident */
765: buffer_append_string_len(b, CONST_STR_LEN("-"));
766: break;
767: case FORMAT_REMOTE_USER:
768: if (NULL != (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER")) && ds->value->used > 1) {
769: accesslog_append_escaped(b, ds->value);
770: } else {
771: buffer_append_string_len(b, CONST_STR_LEN("-"));
772: }
773: break;
774: case FORMAT_REQUEST_LINE:
775: if (con->request.request_line->used) {
776: accesslog_append_escaped(b, con->request.request_line);
777: }
778: break;
779: case FORMAT_STATUS:
780: buffer_append_long(b, con->http_status);
781: break;
782:
783: case FORMAT_BYTES_OUT_NO_HEADER:
784: if (con->bytes_written > 0) {
785: buffer_append_off_t(b,
786: con->bytes_written - con->bytes_header <= 0 ? 0 : con->bytes_written - con->bytes_header);
787: } else {
788: buffer_append_string_len(b, CONST_STR_LEN("-"));
789: }
790: break;
791: case FORMAT_HEADER:
792: if (NULL != (ds = (data_string *)array_get_element(con->request.headers, p->conf.parsed_format->ptr[j]->string->ptr))) {
793: accesslog_append_escaped(b, ds->value);
794: } else {
795: buffer_append_string_len(b, CONST_STR_LEN("-"));
796: }
797: break;
798: case FORMAT_RESPONSE_HEADER:
799: if (NULL != (ds = (data_string *)array_get_element(con->response.headers, p->conf.parsed_format->ptr[j]->string->ptr))) {
800: accesslog_append_escaped(b, ds->value);
801: } else {
802: buffer_append_string_len(b, CONST_STR_LEN("-"));
803: }
804: break;
805: case FORMAT_ENV:
806: if (NULL != (ds = (data_string *)array_get_element(con->environment, p->conf.parsed_format->ptr[j]->string->ptr))) {
807: accesslog_append_escaped(b, ds->value);
808: } else {
809: buffer_append_string_len(b, CONST_STR_LEN("-"));
810: }
811: break;
812: case FORMAT_FILENAME:
813: if (con->physical.path->used > 1) {
814: buffer_append_string_buffer(b, con->physical.path);
815: } else {
816: buffer_append_string_len(b, CONST_STR_LEN("-"));
817: }
818: break;
819: case FORMAT_BYTES_OUT:
820: if (con->bytes_written > 0) {
821: buffer_append_off_t(b, con->bytes_written);
822: } else {
823: buffer_append_string_len(b, CONST_STR_LEN("-"));
824: }
825: break;
826: case FORMAT_BYTES_IN:
827: if (con->bytes_read > 0) {
828: buffer_append_off_t(b, con->bytes_read);
829: } else {
830: buffer_append_string_len(b, CONST_STR_LEN("-"));
831: }
832: break;
833: case FORMAT_TIME_USED:
834: buffer_append_long(b, srv->cur_ts - con->request_start);
835: break;
836: case FORMAT_SERVER_NAME:
837: if (con->server_name->used > 1) {
838: buffer_append_string_buffer(b, con->server_name);
839: } else {
840: buffer_append_string_len(b, CONST_STR_LEN("-"));
841: }
842: break;
843: case FORMAT_HTTP_HOST:
844: if (con->uri.authority->used > 1) {
845: accesslog_append_escaped(b, con->uri.authority);
846: } else {
847: buffer_append_string_len(b, CONST_STR_LEN("-"));
848: }
849: break;
850: case FORMAT_REQUEST_PROTOCOL:
851: buffer_append_string_len(b,
852: con->request.http_version == HTTP_VERSION_1_1 ? "HTTP/1.1" : "HTTP/1.0", 8);
853: break;
854: case FORMAT_REQUEST_METHOD:
855: buffer_append_string(b, get_http_method_name(con->request.http_method));
856: break;
857: case FORMAT_PERCENT:
858: buffer_append_string_len(b, CONST_STR_LEN("%"));
859: break;
860: case FORMAT_SERVER_PORT:
861: {
862: const char *colon;
863: buffer *srvtoken = ((server_socket*)(con->srv_socket))->srv_token;
864: if (srvtoken->ptr[0] == '[') {
865: colon = strstr(srvtoken->ptr, "]:");
866: } else {
867: colon = strchr(srvtoken->ptr, ':');
868: }
869: if (colon) {
870: buffer_append_string(b, colon+1);
871: } else {
872: buffer_append_long(b, srv->srvconf.port);
873: }
874: }
875: break;
876: case FORMAT_QUERY_STRING:
877: accesslog_append_escaped(b, con->uri.query);
878: break;
879: case FORMAT_URL:
880: accesslog_append_escaped(b, con->uri.path_raw);
881: break;
882: case FORMAT_CONNECTION_STATUS:
883: switch(con->keep_alive) {
884: case 0: buffer_append_string_len(b, CONST_STR_LEN("-")); break;
885: default: buffer_append_string_len(b, CONST_STR_LEN("+")); break;
886: }
887: break;
888: default:
889: /*
890: { 'a', FORMAT_REMOTE_ADDR },
891: { 'A', FORMAT_LOCAL_ADDR },
892: { 'C', FORMAT_COOKIE },
893: { 'D', FORMAT_TIME_USED_MS },
894: */
895:
896: break;
897: }
898: break;
899: default:
900: break;
901: }
902: }
903:
904: buffer_append_string_len(b, CONST_STR_LEN("\n"));
905:
906: if (p->conf.use_syslog || /* syslog doesn't cache */
907: (p->conf.access_logfile->used && p->conf.access_logfile->ptr[0] == '|') || /* pipes don't cache */
908: newts ||
909: b->used > BUFFER_MAX_REUSE_SIZE) {
910: if (p->conf.use_syslog) {
911: #ifdef HAVE_SYSLOG_H
912: if (b->used > 2) {
913: /* syslog appends a \n on its own */
914: syslog(p->conf.syslog_level, "%*s", (int) b->used - 2, b->ptr);
915: }
916: #endif
917: } else if (p->conf.log_access_fd != -1) {
1.1.1.2 ! misho 918: force_assert(b->used > 0);
1.1 misho 919: write(p->conf.log_access_fd, b->ptr, b->used - 1);
920: }
921: buffer_reset(b);
922: }
923:
924: return HANDLER_GO_ON;
925: }
926:
927:
928: int mod_accesslog_plugin_init(plugin *p);
929: int mod_accesslog_plugin_init(plugin *p) {
930: p->version = LIGHTTPD_VERSION_ID;
931: p->name = buffer_init_string("accesslog");
932:
933: p->init = mod_accesslog_init;
934: p->set_defaults= log_access_open;
935: p->cleanup = mod_accesslog_free;
936:
937: p->handle_request_done = log_access_write;
938: p->handle_sighup = log_access_cycle;
939:
940: p->data = NULL;
941:
942: return 0;
943: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>