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 buffer *format;
128 unsigned short use_syslog;
129
130
131 int log_access_fd;
132 time_t last_generated_accesslog_ts;
133 time_t *last_generated_accesslog_ts_ptr;
134
135
136 buffer *access_logbuffer;
137 buffer *ts_accesslog_str;
138 buffer *ts_accesslog_fmt_str;
139 unsigned short append_tz_offset;
140
141 format_fields *parsed_format;
142 } plugin_config;
143
144 typedef struct {
145 PLUGIN_DATA;
146
147 plugin_config **config_storage;
148 plugin_config conf;
149 } plugin_data;
150
INIT_FUNC(mod_accesslog_init)151 INIT_FUNC(mod_accesslog_init) {
152 plugin_data *p;
153
154 p = calloc(1, sizeof(*p));
155
156 return p;
157 }
158
accesslog_append_escaped(buffer * dest,buffer * str)159 static void accesslog_append_escaped(buffer *dest, buffer *str) {
160 char *ptr, *start, *end;
161
162 /* replaces non-printable chars with \xHH where HH is the hex representation of the byte */
163 /* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
164 if (str->used == 0) return;
165 buffer_prepare_append(dest, str->used - 1);
166
167 for (ptr = start = str->ptr, end = str->ptr + str->used - 1; ptr < end; ptr++) {
168 char const c = *ptr;
169 if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
170 /* nothing to change, add later as one block */
171 } else {
172 /* copy previous part */
173 if (start < ptr) {
174 buffer_append_string_len(dest, start, ptr - start);
175 }
176 start = ptr + 1;
177
178 switch (c) {
179 case '"':
180 BUFFER_APPEND_STRING_CONST(dest, "\\\"");
181 break;
182 case '\\':
183 BUFFER_APPEND_STRING_CONST(dest, "\\\\");
184 break;
185 case '\b':
186 BUFFER_APPEND_STRING_CONST(dest, "\\b");
187 break;
188 case '\n':
189 BUFFER_APPEND_STRING_CONST(dest, "\\n");
190 break;
191 case '\r':
192 BUFFER_APPEND_STRING_CONST(dest, "\\r");
193 break;
194 case '\t':
195 BUFFER_APPEND_STRING_CONST(dest, "\\t");
196 break;
197 case '\v':
198 BUFFER_APPEND_STRING_CONST(dest, "\\v");
199 break;
200 default: {
201 /* non printable char => \xHH */
202 char hh[5] = {'\\','x',0,0,0};
203 char h = c / 16;
204 hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0');
205 h = c % 16;
206 hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0');
207 buffer_append_string_len(dest, &hh[0], 4);
208 }
209 break;
210 }
211 }
212 }
213
214 if (start < end) {
215 buffer_append_string_len(dest, start, end - start);
216 }
217 }
218
accesslog_parse_format(server * srv,format_fields * fields,buffer * format)219 static int accesslog_parse_format(server *srv, format_fields *fields, buffer *format) {
220 size_t i, j, k = 0, start = 0;
221
222 if (format->used == 0) return -1;
223
224 for (i = 0; i < format->used - 1; i++) {
225 switch(format->ptr[i]) {
226 case '%':
227 if (i > 0 && start != i) {
228 /* copy the string before this % */
229 if (fields->size == 0) {
230 fields->size = 16;
231 fields->used = 0;
232 fields->ptr = malloc(fields->size * sizeof(format_field * ));
233 } else if (fields->used == fields->size) {
234 fields->size += 16;
235 fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
236 }
237
238 fields->ptr[fields->used] = malloc(sizeof(format_field));
239 fields->ptr[fields->used]->type = FIELD_STRING;
240 fields->ptr[fields->used]->string = buffer_init();
241
242 buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
243
244 fields->used++;
245 }
246
247 /* we need a new field */
248
249 if (fields->size == 0) {
250 fields->size = 16;
251 fields->used = 0;
252 fields->ptr = malloc(fields->size * sizeof(format_field * ));
253 } else if (fields->used == fields->size) {
254 fields->size += 16;
255 fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
256 }
257
258 /* search for the terminating command */
259 switch (format->ptr[i+1]) {
260 case '>':
261 case '<':
262 /* after the } has to be a character */
263 if (format->ptr[i+2] == '\0') {
264 log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a format-specifier");
265 return -1;
266 }
267
268
269 for (j = 0; fmap[j].key != '\0'; j++) {
270 if (fmap[j].key != format->ptr[i+2]) continue;
271
272 /* found key */
273
274 fields->ptr[fields->used] = malloc(sizeof(format_field));
275 fields->ptr[fields->used]->type = FIELD_FORMAT;
276 fields->ptr[fields->used]->field = fmap[j].type;
277 fields->ptr[fields->used]->string = NULL;
278
279 fields->used++;
280
281 break;
282 }
283
284 if (fmap[j].key == '\0') {
285 log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a valid format-specifier");
286 return -1;
287 }
288
289 start = i + 3;
290 i = start - 1; /* skip the string */
291
292 break;
293 case '{':
294 /* go forward to } */
295
296 for (k = i+2; k < format->used - 1; k++) {
297 if (format->ptr[k] == '}') break;
298 }
299
300 if (k == format->used - 1) {
301 log_error_write(srv, __FILE__, __LINE__, "s", "%{ has to be terminated by a }");
302 return -1;
303 }
304
305 /* after the } has to be a character */
306 if (format->ptr[k+1] == '\0') {
307 log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a format-specifier");
308 return -1;
309 }
310
311 if (k == i + 2) {
312 log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be contain a string");
313 return -1;
314 }
315
316 for (j = 0; fmap[j].key != '\0'; j++) {
317 if (fmap[j].key != format->ptr[k+1]) continue;
318
319 /* found key */
320
321 fields->ptr[fields->used] = malloc(sizeof(format_field));
322 fields->ptr[fields->used]->type = FIELD_FORMAT;
323 fields->ptr[fields->used]->field = fmap[j].type;
324 fields->ptr[fields->used]->string = buffer_init();
325
326 buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + i + 2, k - (i + 2));
327
328 fields->used++;
329
330 break;
331 }
332
333 if (fmap[j].key == '\0') {
334 log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a valid format-specifier");
335 return -1;
336 }
337
338 start = k + 2;
339 i = start - 1; /* skip the string */
340
341 break;
342 default:
343 /* after the % has to be a character */
344 if (format->ptr[i+1] == '\0') {
345 log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a format-specifier");
346 return -1;
347 }
348
349 for (j = 0; fmap[j].key != '\0'; j++) {
350 if (fmap[j].key != format->ptr[i+1]) continue;
351
352 /* found key */
353
354 fields->ptr[fields->used] = malloc(sizeof(format_field));
355 fields->ptr[fields->used]->type = FIELD_FORMAT;
356 fields->ptr[fields->used]->field = fmap[j].type;
357 fields->ptr[fields->used]->string = NULL;
358
359 fields->used++;
360
361 break;
362 }
363
364 if (fmap[j].key == '\0') {
365 log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a valid format-specifier");
366 return -1;
367 }
368
369 start = i + 2;
370 i = start - 1; /* skip the string */
371
372 break;
373 }
374
375 break;
376 }
377 }
378
379 if (start < i) {
380 /* copy the string */
381 if (fields->size == 0) {
382 fields->size = 16;
383 fields->used = 0;
384 fields->ptr = malloc(fields->size * sizeof(format_field * ));
385 } else if (fields->used == fields->size) {
386 fields->size += 16;
387 fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
388 }
389
390 fields->ptr[fields->used] = malloc(sizeof(format_field));
391 fields->ptr[fields->used]->type = FIELD_STRING;
392 fields->ptr[fields->used]->string = buffer_init();
393
394 buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
395
396 fields->used++;
397 }
398
399 return 0;
400 }
401
FREE_FUNC(mod_accesslog_free)402 FREE_FUNC(mod_accesslog_free) {
403 plugin_data *p = p_d;
404 size_t i;
405
406 if (!p) return HANDLER_GO_ON;
407
408 if (p->config_storage) {
409
410 for (i = 0; i < srv->config_context->used; i++) {
411 plugin_config *s = p->config_storage[i];
412
413 if (!s) continue;
414
415 if (s->access_logbuffer->used) {
416 if (s->use_syslog) {
417 # ifdef HAVE_SYSLOG_H
418 if (s->access_logbuffer->used > 2) {
419 syslog(LOG_INFO, "%*s", (int) s->access_logbuffer->used - 2, s->access_logbuffer->ptr);
420 }
421 # endif
422 } else if (s->log_access_fd != -1) {
423 write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1);
424 }
425 }
426
427 if (s->log_access_fd != -1) close(s->log_access_fd);
428
429 buffer_free(s->ts_accesslog_str);
430 buffer_free(s->ts_accesslog_fmt_str);
431 buffer_free(s->access_logbuffer);
432 buffer_free(s->format);
433 buffer_free(s->access_logfile);
434
435 if (s->parsed_format) {
436 size_t j;
437 for (j = 0; j < s->parsed_format->used; j++) {
438 if (s->parsed_format->ptr[j]->string) buffer_free(s->parsed_format->ptr[j]->string);
439 free(s->parsed_format->ptr[j]);
440 }
441 free(s->parsed_format->ptr);
442 free(s->parsed_format);
443 }
444
445 free(s);
446 }
447
448 free(p->config_storage);
449 }
450
451 free(p);
452
453 return HANDLER_GO_ON;
454 }
455
SETDEFAULTS_FUNC(log_access_open)456 SETDEFAULTS_FUNC(log_access_open) {
457 plugin_data *p = p_d;
458 size_t i = 0;
459
460 config_values_t cv[] = {
461 { "accesslog.filename", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
462 { "accesslog.use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
463 { "accesslog.format", NULL, T_CONFIG_STRING, 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
484
485 cv[0].destination = s->access_logfile;
486 cv[1].destination = &(s->use_syslog);
487 cv[2].destination = s->format;
488
489 p->config_storage[i] = s;
490
491 if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
492 return HANDLER_ERROR;
493 }
494
495 if (i == 0 && buffer_is_empty(s->format)) {
496 /* set a default logfile string */
497
498 buffer_copy_string_len(s->format, CONST_STR_LEN("%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""));
499 }
500
501 /* parse */
502
503 if (s->format->used) {
504 size_t j, count;
505
506 s->parsed_format = calloc(1, sizeof(*(s->parsed_format)));
507
508 if (-1 == accesslog_parse_format(srv, s->parsed_format, s->format)) {
509
510 log_error_write(srv, __FILE__, __LINE__, "sb",
511 "parsing accesslog-definition failed:", s->format);
512
513 return HANDLER_ERROR;
514 }
515
516 /* make sure they didn't try to send the timestamp in twice...
517 * also, save the format string in a different variable (this
518 * will save a few conditionals later)
519 */
520 count = 0;
521 for (j = 0; j < s->parsed_format->used; j++) {
522 if (FIELD_FORMAT == s->parsed_format->ptr[j]->type) {
523 if (FORMAT_TIMESTAMP == s->parsed_format->ptr[j]->field) {
524 if (!buffer_is_empty(s->parsed_format->ptr[j]->string)) {
525 buffer_copy_string(s->ts_accesslog_fmt_str, s->parsed_format->ptr[j]->string->ptr);
526 }
527
528 if (++count > 1) {
529 log_error_write(srv, __FILE__, __LINE__, "sb",
530 "you may not use the timestamp twice in the same access log:", s->format);
531
532 return HANDLER_ERROR;
533 }
534 }
535 }
536 }
537
538 #if 0
539 /* debugging */
540 for (j = 0; j < s->parsed_format->used; j++) {
541 switch (s->parsed_format->ptr[j]->type) {
542 case FIELD_FORMAT:
543 log_error_write(srv, __FILE__, __LINE__, "ssds",
544 "config:", "format", s->parsed_format->ptr[j]->field,
545 s->parsed_format->ptr[j]->string ?
546 s->parsed_format->ptr[j]->string->ptr : "" );
547 break;
548 case FIELD_STRING:
549 log_error_write(srv, __FILE__, __LINE__, "ssbs", "config:", "string '", s->parsed_format->ptr[j]->string, "'");
550 break;
551 default:
552 break;
553 }
554 }
555 #endif
556 }
557
558 s->append_tz_offset = 0;
559 if (buffer_is_empty(s->ts_accesslog_fmt_str)) {
560 #if defined(HAVE_STRUCT_TM_GMTOFF)
561 BUFFER_COPY_STRING_CONST(s->ts_accesslog_fmt_str, "[%d/%b/%Y:%H:%M:%S ");
562 s->append_tz_offset = 1;
563 #else
564 BUFFER_COPY_STRING_CONST(s->ts_accesslog_fmt_str, "[%d/%b/%Y:%H:%M:%S +0000]");
565 #endif
566 }
567
568 if (s->use_syslog) {
569 /* ignore the next checks */
570 continue;
571 }
572
573 if (s->access_logfile->used < 2) continue;
574
575 if (-1 == (s->log_access_fd = open_logfile_or_pipe(srv, s->access_logfile->ptr)))
576 return HANDLER_ERROR;
577
578 }
579
580 return HANDLER_GO_ON;
581 }
582
SIGHUP_FUNC(log_access_cycle)583 SIGHUP_FUNC(log_access_cycle) {
584 plugin_data *p = p_d;
585 size_t i;
586
587 if (!p->config_storage) return HANDLER_GO_ON;
588
589 for (i = 0; i < srv->config_context->used; i++) {
590 plugin_config *s = p->config_storage[i];
591
592 if (s->access_logbuffer->used) {
593 if (s->use_syslog) {
594 #ifdef HAVE_SYSLOG_H
595 if (s->access_logbuffer->used > 2) {
596 /* syslog appends a \n on its own */
597 syslog(LOG_INFO, "%*s", (int) s->access_logbuffer->used - 2, s->access_logbuffer->ptr);
598 }
599 #endif
600 } else if (s->log_access_fd != -1) {
601 write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1);
602 }
603
604 buffer_reset(s->access_logbuffer);
605 }
606
607 if (s->use_syslog == 0 &&
608 s->access_logfile->used > 1 &&
609 s->access_logfile->ptr[0] != '|') {
610
611 close(s->log_access_fd);
612
613 if (-1 == (s->log_access_fd =
614 open(s->access_logfile->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) {
615
616 log_error_write(srv, __FILE__, __LINE__, "ss", "cycling access-log failed:", strerror(errno));
617
618 return HANDLER_ERROR;
619 }
620 #ifdef FD_CLOEXEC
621 fcntl(s->log_access_fd, F_SETFD, FD_CLOEXEC);
622 #endif
623 }
624 }
625
626 return HANDLER_GO_ON;
627 }
628
629 #define PATCH(x) \
630 p->conf.x = s->x;
mod_accesslog_patch_connection(server * srv,connection * con,plugin_data * p)631 static int mod_accesslog_patch_connection(server *srv, connection *con, plugin_data *p) {
632 size_t i, j;
633 plugin_config *s = p->config_storage[0];
634
635 PATCH(access_logfile);
636 PATCH(format);
637 PATCH(log_access_fd);
638 PATCH(last_generated_accesslog_ts_ptr);
639 PATCH(access_logbuffer);
640 PATCH(ts_accesslog_str);
641 PATCH(ts_accesslog_fmt_str);
642 PATCH(append_tz_offset);
643 PATCH(parsed_format);
644 PATCH(use_syslog);
645
646 /* skip the first, the global context */
647 for (i = 1; i < srv->config_context->used; i++) {
648 data_config *dc = (data_config *)srv->config_context->data[i];
649 s = p->config_storage[i];
650
651 /* condition didn't match */
652 if (!config_check_cond(srv, con, dc)) continue;
653
654 /* merge config */
655 for (j = 0; j < dc->value->used; j++) {
656 data_unset *du = dc->value->data[j];
657
658 if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.filename"))) {
659 PATCH(access_logfile);
660 PATCH(log_access_fd);
661 PATCH(access_logbuffer);
662 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.format"))) {
663 PATCH(format);
664 PATCH(parsed_format);
665 PATCH(last_generated_accesslog_ts_ptr);
666 PATCH(ts_accesslog_str);
667 PATCH(ts_accesslog_fmt_str);
668 PATCH(append_tz_offset);
669 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.use-syslog"))) {
670 PATCH(use_syslog);
671 PATCH(access_logbuffer);
672 }
673 }
674 }
675
676 return 0;
677 }
678 #undef PATCH
679
REQUESTDONE_FUNC(log_access_write)680 REQUESTDONE_FUNC(log_access_write) {
681 plugin_data *p = p_d;
682 buffer *b;
683 size_t j;
684
685 int newts = 0;
686 data_string *ds;
687
688 mod_accesslog_patch_connection(srv, con, p);
689
690 /* No output device, nothing to do */
691 if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON;
692
693 b = p->conf.access_logbuffer;
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 (con->authed_user->used > 1) {
769 buffer_append_string_buffer(b, con->authed_user);
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(LOG_INFO, "%*s", (int) b->used - 2, b->ptr);
915 }
916 #endif
917 } else if (p->conf.log_access_fd != -1) {
918 write(p->conf.log_access_fd, b->ptr, b->used - 1);
919 }
920 buffer_reset(b);
921 }
922
923 return HANDLER_GO_ON;
924 }
925
926
927 int mod_accesslog_plugin_init(plugin *p);
mod_accesslog_plugin_init(plugin * p)928 int mod_accesslog_plugin_init(plugin *p) {
929 p->version = LIGHTTPD_VERSION_ID;
930 p->name = buffer_init_string("accesslog");
931
932 p->init = mod_accesslog_init;
933 p->set_defaults= log_access_open;
934 p->cleanup = mod_accesslog_free;
935
936 p->handle_request_done = log_access_write;
937 p->handle_sighup = log_access_cycle;
938
939 p->data = NULL;
940
941 return 0;
942 }
943