10 #include <sys/types.h>
36 FORMAT_BYTES_OUT_NO_HEADER
,
45 FORMAT_REQUEST_PROTOCOL
,
46 FORMAT_REQUEST_METHOD
,
53 FORMAT_CONNECTION_STATUS
,
57 FORMAT_KEEPALIVE_COUNT
,
58 FORMAT_RESPONSE_HEADER
,
66 * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
70 static const format_mapping fmap
[] =
72 { '%', FORMAT_PERCENT
},
73 { 'h', FORMAT_REMOTE_HOST
},
74 { 'l', FORMAT_REMOTE_IDENT
},
75 { 'u', FORMAT_REMOTE_USER
},
76 { 't', FORMAT_TIMESTAMP
},
77 { 'r', FORMAT_REQUEST_LINE
},
78 { 's', FORMAT_STATUS
},
79 { 'b', FORMAT_BYTES_OUT_NO_HEADER
},
80 { 'i', FORMAT_HEADER
},
82 { 'a', FORMAT_REMOTE_ADDR
},
83 { 'A', FORMAT_LOCAL_ADDR
},
84 { 'B', FORMAT_BYTES_OUT_NO_HEADER
},
85 { 'C', FORMAT_COOKIE
},
86 { 'D', FORMAT_TIME_USED_US
},
88 { 'f', FORMAT_FILENAME
},
89 { 'H', FORMAT_REQUEST_PROTOCOL
},
90 { 'k', FORMAT_KEEPALIVE_COUNT
},
91 { 'm', FORMAT_REQUEST_METHOD
},
93 { 'p', FORMAT_SERVER_PORT
},
94 { 'P', FORMAT_UNSUPPORTED
}, /* we are only one process */
95 { 'q', FORMAT_QUERY_STRING
},
96 { 'T', FORMAT_TIME_USED
},
97 { 'U', FORMAT_URL
}, /* w/o querystring */
98 { 'v', FORMAT_SERVER_NAME
},
99 { 'V', FORMAT_HTTP_HOST
},
100 { 'X', FORMAT_CONNECTION_STATUS
},
101 { 'I', FORMAT_BYTES_IN
},
102 { 'O', FORMAT_BYTES_OUT
},
104 { 'o', FORMAT_RESPONSE_HEADER
},
106 { '\0', FORMAT_UNSET
}
110 enum e_optflags_time
{
111 /* format string is passed to strftime unless other format optflags set
112 * (besides FORMAT_FLAG_TIME_BEGIN or FORMAT_FLAG_TIME_END) */
113 FORMAT_FLAG_TIME_END
= 0x00,/* use request end time (default) */
114 FORMAT_FLAG_TIME_BEGIN
= 0x01,/* use request start time */
115 FORMAT_FLAG_TIME_SEC
= 0x02,/* request time as num sec since epoch */
116 FORMAT_FLAG_TIME_MSEC
= 0x04,/* request time as num msec since epoch */
117 FORMAT_FLAG_TIME_USEC
= 0x08,/* request time as num usec since epoch */
118 FORMAT_FLAG_TIME_NSEC
= 0x10,/* request time as num nsec since epoch */
119 FORMAT_FLAG_TIME_MSEC_FRAC
= 0x20,/* request time msec fraction */
120 FORMAT_FLAG_TIME_USEC_FRAC
= 0x40,/* request time usec fraction */
121 FORMAT_FLAG_TIME_NSEC_FRAC
= 0x80 /* request time nsec fraction */
126 enum { FIELD_UNSET
, FIELD_STRING
, FIELD_FORMAT
} type
;
141 buffer
*access_logfile
;
143 buffer
*access_logbuffer
; /* each logfile has a separate buffer */
145 unsigned short use_syslog
; /* syslog has global buffer */
146 unsigned short syslog_level
;
150 time_t last_generated_accesslog_ts
;
151 time_t *last_generated_accesslog_ts_ptr
;
153 buffer
*ts_accesslog_str
;
155 format_fields
*parsed_format
;
161 plugin_config
**config_storage
;
164 buffer
*syslog_logbuffer
; /* syslog has global buffer. no caching, always written directly */
167 INIT_FUNC(mod_accesslog_init
) {
170 p
= calloc(1, sizeof(*p
));
171 p
->syslog_logbuffer
= buffer_init();
176 static void accesslog_write_all(server
*srv
, const buffer
*filename
, int fd
, const void* buf
, size_t count
) {
177 if (-1 == write_all(fd
, buf
, count
)) {
178 log_error_write(srv
, __FILE__
, __LINE__
, "sbs",
179 "writing access log entry failed:", filename
, strerror(errno
));
183 static void accesslog_append_escaped(buffer
*dest
, buffer
*str
) {
184 char *ptr
, *start
, *end
;
186 /* replaces non-printable chars with \xHH where HH is the hex representation of the byte */
187 /* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
188 if (buffer_string_is_empty(str
)) return;
189 buffer_string_prepare_append(dest
, buffer_string_length(str
));
191 for (ptr
= start
= str
->ptr
, end
= str
->ptr
+ buffer_string_length(str
); ptr
< end
; ptr
++) {
192 unsigned char const c
= (unsigned char) *ptr
;
193 if (c
>= ' ' && c
<= '~' && c
!= '"' && c
!= '\\') {
194 /* nothing to change, add later as one block */
196 /* copy previous part */
198 buffer_append_string_len(dest
, start
, ptr
- start
);
204 BUFFER_APPEND_STRING_CONST(dest
, "\\\"");
207 BUFFER_APPEND_STRING_CONST(dest
, "\\\\");
210 BUFFER_APPEND_STRING_CONST(dest
, "\\b");
213 BUFFER_APPEND_STRING_CONST(dest
, "\\n");
216 BUFFER_APPEND_STRING_CONST(dest
, "\\r");
219 BUFFER_APPEND_STRING_CONST(dest
, "\\t");
222 BUFFER_APPEND_STRING_CONST(dest
, "\\v");
225 /* non printable char => \xHH */
226 char hh
[5] = {'\\','x',0,0,0};
228 hh
[2] = (h
> 9) ? (h
- 10 + 'A') : (h
+ '0');
230 hh
[3] = (h
> 9) ? (h
- 10 + 'A') : (h
+ '0');
231 buffer_append_string_len(dest
, &hh
[0], 4);
239 buffer_append_string_len(dest
, start
, end
- start
);
243 static int accesslog_parse_format(server
*srv
, format_fields
*fields
, buffer
*format
) {
244 size_t i
, j
, k
= 0, start
= 0;
246 if (buffer_is_empty(format
)) return -1;
248 for (i
= 0; i
< buffer_string_length(format
); i
++) {
249 switch(format
->ptr
[i
]) {
251 if (i
> 0 && start
!= i
) {
252 /* copy the string before this % */
253 if (fields
->size
== 0) {
256 fields
->ptr
= malloc(fields
->size
* sizeof(format_field
* ));
257 } else if (fields
->used
== fields
->size
) {
259 fields
->ptr
= realloc(fields
->ptr
, fields
->size
* sizeof(format_field
* ));
262 fields
->ptr
[fields
->used
] = malloc(sizeof(format_field
));
263 fields
->ptr
[fields
->used
]->type
= FIELD_STRING
;
264 fields
->ptr
[fields
->used
]->string
= buffer_init();
266 buffer_copy_string_len(fields
->ptr
[fields
->used
]->string
, format
->ptr
+ start
, i
- start
);
271 /* we need a new field */
273 if (fields
->size
== 0) {
276 fields
->ptr
= malloc(fields
->size
* sizeof(format_field
* ));
277 } else if (fields
->used
== fields
->size
) {
279 fields
->ptr
= realloc(fields
->ptr
, fields
->size
* sizeof(format_field
* ));
282 /* search for the terminating command */
283 switch (format
->ptr
[i
+1]) {
286 /* after the } has to be a character */
287 if (format
->ptr
[i
+2] == '\0') {
288 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%< and %> have to be followed by a format-specifier");
293 for (j
= 0; fmap
[j
].key
!= '\0'; j
++) {
294 if (fmap
[j
].key
!= format
->ptr
[i
+2]) continue;
298 fields
->ptr
[fields
->used
] = malloc(sizeof(format_field
));
299 fields
->ptr
[fields
->used
]->type
= FIELD_FORMAT
;
300 fields
->ptr
[fields
->used
]->field
= fmap
[j
].type
;
301 fields
->ptr
[fields
->used
]->string
= NULL
;
302 fields
->ptr
[fields
->used
]->opt
= 0;
309 if (fmap
[j
].key
== '\0') {
310 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%< and %> have to be followed by a valid format-specifier");
315 i
= start
- 1; /* skip the string */
319 /* go forward to } */
321 for (k
= i
+2; k
< buffer_string_length(format
); k
++) {
322 if (format
->ptr
[k
] == '}') break;
325 if (k
== buffer_string_length(format
)) {
326 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%{ has to be terminated by a }");
330 /* after the } has to be a character */
331 if (format
->ptr
[k
+1] == '\0') {
332 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%{...} has to be followed by a format-specifier");
337 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%{...} has to be contain a string");
341 for (j
= 0; fmap
[j
].key
!= '\0'; j
++) {
342 if (fmap
[j
].key
!= format
->ptr
[k
+1]) continue;
346 fields
->ptr
[fields
->used
] = malloc(sizeof(format_field
));
347 fields
->ptr
[fields
->used
]->type
= FIELD_FORMAT
;
348 fields
->ptr
[fields
->used
]->field
= fmap
[j
].type
;
349 fields
->ptr
[fields
->used
]->string
= buffer_init();
350 fields
->ptr
[fields
->used
]->opt
= 0;
352 buffer_copy_string_len(fields
->ptr
[fields
->used
]->string
, format
->ptr
+ i
+ 2, k
- (i
+ 2));
359 if (fmap
[j
].key
== '\0') {
360 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%{...} has to be followed by a valid format-specifier");
365 i
= start
- 1; /* skip the string */
369 /* after the % has to be a character */
370 if (format
->ptr
[i
+1] == '\0') {
371 log_error_write(srv
, __FILE__
, __LINE__
, "s", "% has to be followed by a format-specifier");
375 for (j
= 0; fmap
[j
].key
!= '\0'; j
++) {
376 if (fmap
[j
].key
!= format
->ptr
[i
+1]) continue;
380 fields
->ptr
[fields
->used
] = malloc(sizeof(format_field
));
381 fields
->ptr
[fields
->used
]->type
= FIELD_FORMAT
;
382 fields
->ptr
[fields
->used
]->field
= fmap
[j
].type
;
383 fields
->ptr
[fields
->used
]->string
= NULL
;
384 fields
->ptr
[fields
->used
]->opt
= 0;
391 if (fmap
[j
].key
== '\0') {
392 log_error_write(srv
, __FILE__
, __LINE__
, "s", "% has to be followed by a valid format-specifier");
397 i
= start
- 1; /* skip the string */
407 /* copy the string */
408 if (fields
->size
== 0) {
411 fields
->ptr
= malloc(fields
->size
* sizeof(format_field
* ));
412 } else if (fields
->used
== fields
->size
) {
414 fields
->ptr
= realloc(fields
->ptr
, fields
->size
* sizeof(format_field
* ));
417 fields
->ptr
[fields
->used
] = malloc(sizeof(format_field
));
418 fields
->ptr
[fields
->used
]->type
= FIELD_STRING
;
419 fields
->ptr
[fields
->used
]->string
= buffer_init();
421 buffer_copy_string_len(fields
->ptr
[fields
->used
]->string
, format
->ptr
+ start
, i
- start
);
429 FREE_FUNC(mod_accesslog_free
) {
430 plugin_data
*p
= p_d
;
433 if (!p
) return HANDLER_GO_ON
;
435 if (p
->config_storage
) {
437 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
438 plugin_config
*s
= p
->config_storage
[i
];
440 if (NULL
== s
) continue;
442 if (!buffer_string_is_empty(s
->access_logbuffer
)) {
443 if (s
->log_access_fd
!= -1) {
444 accesslog_write_all(srv
, s
->access_logfile
, s
->log_access_fd
, CONST_BUF_LEN(s
->access_logbuffer
));
448 if (s
->log_access_fd
!= -1) {
449 if (buffer_string_is_empty(s
->access_logfile
) || *s
->access_logfile
->ptr
!= '|') {
450 close(s
->log_access_fd
);
451 } /*(else piped loggers closed in fdevent_close_logger_pipes())*/
454 buffer_free(s
->ts_accesslog_str
);
455 buffer_free(s
->access_logbuffer
);
456 buffer_free(s
->format
);
457 buffer_free(s
->access_logfile
);
459 if (s
->parsed_format
) {
461 for (j
= 0; j
< s
->parsed_format
->used
; j
++) {
462 if (s
->parsed_format
->ptr
[j
]->string
) buffer_free(s
->parsed_format
->ptr
[j
]->string
);
463 free(s
->parsed_format
->ptr
[j
]);
465 free(s
->parsed_format
->ptr
);
466 free(s
->parsed_format
);
472 free(p
->config_storage
);
475 if (p
->syslog_logbuffer
) buffer_free(p
->syslog_logbuffer
);
478 return HANDLER_GO_ON
;
481 SETDEFAULTS_FUNC(log_access_open
) {
482 plugin_data
*p
= p_d
;
485 config_values_t cv
[] = {
486 { "accesslog.filename", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
487 { "accesslog.use-syslog", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
},
488 { "accesslog.format", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
489 { "accesslog.syslog-level", NULL
, T_CONFIG_SHORT
, T_CONFIG_SCOPE_CONNECTION
},
490 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
493 if (!p
) return HANDLER_ERROR
;
495 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
497 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
498 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
501 s
= calloc(1, sizeof(plugin_config
));
502 s
->access_logfile
= buffer_init();
503 s
->format
= buffer_init();
504 s
->access_logbuffer
= buffer_init();
505 s
->ts_accesslog_str
= buffer_init();
506 s
->log_access_fd
= -1;
507 s
->last_generated_accesslog_ts
= 0;
508 s
->last_generated_accesslog_ts_ptr
= &(s
->last_generated_accesslog_ts
);
509 s
->syslog_level
= LOG_INFO
;
512 cv
[0].destination
= s
->access_logfile
;
513 cv
[1].destination
= &(s
->use_syslog
);
514 cv
[2].destination
= s
->format
;
515 cv
[3].destination
= &(s
->syslog_level
);
517 p
->config_storage
[i
] = s
;
519 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
520 return HANDLER_ERROR
;
523 if (i
== 0 && buffer_string_is_empty(s
->format
)) {
524 /* set a default logfile string */
526 buffer_copy_string_len(s
->format
, CONST_STR_LEN("%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""));
531 if (!buffer_is_empty(s
->format
)) {
532 size_t j
, tcount
= 0;
534 s
->parsed_format
= calloc(1, sizeof(*(s
->parsed_format
)));
536 if (-1 == accesslog_parse_format(srv
, s
->parsed_format
, s
->format
)) {
538 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
539 "parsing accesslog-definition failed:", s
->format
);
541 return HANDLER_ERROR
;
544 for (j
= 0; j
< s
->parsed_format
->used
; ++j
) {
545 format_field
* const f
= s
->parsed_format
->ptr
[j
];
546 if (FIELD_FORMAT
!= f
->type
) continue;
547 if (FORMAT_TIMESTAMP
== f
->field
) {
548 if (!buffer_string_is_empty(f
->string
)) {
549 const char *ptr
= f
->string
->ptr
;
550 if (0 == strncmp(ptr
, "begin:", sizeof("begin:")-1)) {
551 f
->opt
|= FORMAT_FLAG_TIME_BEGIN
;
552 ptr
+= sizeof("begin:")-1;
553 } else if (0 == strncmp(ptr
, "end:", sizeof("end:")-1)) {
554 f
->opt
|= FORMAT_FLAG_TIME_END
;
555 ptr
+= sizeof("end:")-1;
557 if (0 == strcmp(ptr
, "sec")) f
->opt
|= FORMAT_FLAG_TIME_SEC
;
558 else if (0 == strcmp(ptr
, "msec")) f
->opt
|= FORMAT_FLAG_TIME_MSEC
;
559 else if (0 == strcmp(ptr
, "usec")) f
->opt
|= FORMAT_FLAG_TIME_USEC
;
560 else if (0 == strcmp(ptr
, "nsec")) f
->opt
|= FORMAT_FLAG_TIME_NSEC
;
561 else if (0 == strcmp(ptr
, "msec_frac")) f
->opt
|= FORMAT_FLAG_TIME_MSEC_FRAC
;
562 else if (0 == strcmp(ptr
, "usec_frac")) f
->opt
|= FORMAT_FLAG_TIME_USEC_FRAC
;
563 else if (0 == strcmp(ptr
, "nsec_frac")) f
->opt
|= FORMAT_FLAG_TIME_NSEC_FRAC
;
564 else if (NULL
== strchr(ptr
, '%')) {
565 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
566 "constant string for time format (misspelled token? or missing '%'):", s
->format
);
568 return HANDLER_ERROR
;
572 /* make sure they didn't try to send the timestamp in twice
573 * (would invalidate s->ts_accesslog_str cache of timestamp str) */
574 if (!(f
->opt
& ~(FORMAT_FLAG_TIME_BEGIN
|FORMAT_FLAG_TIME_END
|FORMAT_FLAG_TIME_SEC
)) && ++tcount
> 1) {
575 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
576 "you may not use strftime timestamp format %{}t twice in the same access log:", s
->format
);
578 return HANDLER_ERROR
;
581 if (f
->opt
& FORMAT_FLAG_TIME_BEGIN
) srv
->srvconf
.high_precision_timestamps
= 1;
582 } else if (FORMAT_TIME_USED_US
== f
->field
) {
583 f
->opt
|= FORMAT_FLAG_TIME_USEC
;
584 srv
->srvconf
.high_precision_timestamps
= 1;
585 } else if (FORMAT_TIME_USED
== f
->field
) {
586 if (buffer_string_is_empty(f
->string
)
587 || buffer_is_equal_string(f
->string
, CONST_STR_LEN("s"))
588 || buffer_is_equal_string(f
->string
, CONST_STR_LEN("sec"))) f
->opt
|= FORMAT_FLAG_TIME_SEC
;
589 else if (buffer_is_equal_string(f
->string
, CONST_STR_LEN("ms"))
590 || buffer_is_equal_string(f
->string
, CONST_STR_LEN("msec"))) f
->opt
|= FORMAT_FLAG_TIME_MSEC
;
591 else if (buffer_is_equal_string(f
->string
, CONST_STR_LEN("us"))
592 || buffer_is_equal_string(f
->string
, CONST_STR_LEN("usec"))) f
->opt
|= FORMAT_FLAG_TIME_USEC
;
593 else if (buffer_is_equal_string(f
->string
, CONST_STR_LEN("ns"))
594 || buffer_is_equal_string(f
->string
, CONST_STR_LEN("nsec"))) f
->opt
|= FORMAT_FLAG_TIME_NSEC
;
596 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
597 "invalid time unit in %{UNIT}T:", s
->format
);
599 return HANDLER_ERROR
;
602 if (f
->opt
& ~(FORMAT_FLAG_TIME_SEC
)) srv
->srvconf
.high_precision_timestamps
= 1;
603 } else if (FORMAT_COOKIE
== f
->field
) {
604 if (buffer_string_is_empty(f
->string
)) f
->type
= FIELD_STRING
; /*(blank)*/
610 for (j
= 0; j
< s
->parsed_format
->used
; j
++) {
611 switch (s
->parsed_format
->ptr
[j
]->type
) {
613 log_error_write(srv
, __FILE__
, __LINE__
, "ssds",
614 "config:", "format", s
->parsed_format
->ptr
[j
]->field
,
615 s
->parsed_format
->ptr
[j
]->string
?
616 s
->parsed_format
->ptr
[j
]->string
->ptr
: "" );
619 log_error_write(srv
, __FILE__
, __LINE__
, "ssbs", "config:", "string '", s
->parsed_format
->ptr
[j
]->string
, "'");
629 /* ignore the next checks */
633 if (buffer_string_is_empty(s
->access_logfile
)) continue;
635 if (srv
->srvconf
.preflight_check
) continue;
637 if (-1 == (s
->log_access_fd
= fdevent_open_logger(s
->access_logfile
->ptr
))) {
638 log_error_write(srv
, __FILE__
, __LINE__
, "SBSS",
639 "opening log '", s
->access_logfile
,
640 "' failed: ", strerror(errno
));
641 return HANDLER_ERROR
;
645 return HANDLER_GO_ON
;
649 #define O_LARGEFILE 0
652 static void log_access_flush(server
*srv
, void *p_d
) {
653 plugin_data
*p
= p_d
;
656 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
657 plugin_config
*s
= p
->config_storage
[i
];
659 if (!buffer_string_is_empty(s
->access_logbuffer
)) {
660 if (s
->log_access_fd
!= -1) {
661 accesslog_write_all(srv
, s
->access_logfile
, s
->log_access_fd
, CONST_BUF_LEN(s
->access_logbuffer
));
664 buffer_reset(s
->access_logbuffer
);
669 TRIGGER_FUNC(log_access_periodic_flush
) {
670 /* flush buffered access logs every 4 seconds */
671 if (0 == (srv
->cur_ts
& 3)) log_access_flush(srv
, p_d
);
672 return HANDLER_GO_ON
;
675 SIGHUP_FUNC(log_access_cycle
) {
676 plugin_data
*p
= p_d
;
679 if (!p
->config_storage
) return HANDLER_GO_ON
;
681 log_access_flush(srv
, p
);
683 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
684 plugin_config
*s
= p
->config_storage
[i
];
685 if (s
->use_syslog
== 0
686 && !buffer_string_is_empty(s
->access_logfile
)
687 && s
->access_logfile
->ptr
[0] != '|') {
689 if (-1 == fdevent_cycle_logger(s
->access_logfile
->ptr
, &s
->log_access_fd
)) {
690 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "cycling access-log failed:", strerror(errno
));
691 return HANDLER_ERROR
;
696 return HANDLER_GO_ON
;
701 static int mod_accesslog_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
703 plugin_config
*s
= p
->config_storage
[0];
705 PATCH(access_logfile
);
706 PATCH(log_access_fd
);
707 PATCH(last_generated_accesslog_ts_ptr
);
708 PATCH(access_logbuffer
);
709 PATCH(ts_accesslog_str
);
710 PATCH(parsed_format
);
714 /* skip the first, the global context */
715 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
716 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
717 s
= p
->config_storage
[i
];
719 /* condition didn't match */
720 if (!config_check_cond(srv
, con
, dc
)) continue;
723 for (j
= 0; j
< dc
->value
->used
; j
++) {
724 data_unset
*du
= dc
->value
->data
[j
];
726 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("accesslog.filename"))) {
727 PATCH(access_logfile
);
728 PATCH(log_access_fd
);
729 PATCH(access_logbuffer
);
730 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("accesslog.format"))) {
731 PATCH(parsed_format
);
732 PATCH(last_generated_accesslog_ts_ptr
);
733 PATCH(ts_accesslog_str
);
734 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("accesslog.use-syslog"))) {
736 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("accesslog.syslog-level"))) {
746 REQUESTDONE_FUNC(log_access_write
) {
747 plugin_data
*p
= p_d
;
753 struct timespec ts
= { 0, 0 };
755 mod_accesslog_patch_connection(srv
, con
, p
);
757 /* No output device, nothing to do */
758 if (!p
->conf
.use_syslog
&& p
->conf
.log_access_fd
== -1) return HANDLER_GO_ON
;
760 if (p
->conf
.use_syslog
) {
761 b
= p
->syslog_logbuffer
;
763 b
= p
->conf
.access_logbuffer
;
766 if (buffer_is_empty(b
)) {
767 buffer_string_set_length(b
, 0);
770 for (j
= 0; j
< p
->conf
.parsed_format
->used
; j
++) {
771 const format_field
* const f
= p
->conf
.parsed_format
->ptr
[j
];
774 buffer_append_string_buffer(b
, f
->string
);
778 case FORMAT_TIMESTAMP
:
780 if (f
->opt
& ~(FORMAT_FLAG_TIME_BEGIN
|FORMAT_FLAG_TIME_END
)) {
781 if (f
->opt
& FORMAT_FLAG_TIME_SEC
) {
782 time_t t
= (!(f
->opt
& FORMAT_FLAG_TIME_BEGIN
)) ? srv
->cur_ts
: con
->request_start
;
783 buffer_append_int(b
, (intmax_t)t
);
784 } else if (f
->opt
& (FORMAT_FLAG_TIME_MSEC
|FORMAT_FLAG_TIME_USEC
|FORMAT_FLAG_TIME_NSEC
)) {
785 off_t t
; /*(expected to be 64-bit since large file support enabled)*/
787 if (!(f
->opt
& FORMAT_FLAG_TIME_BEGIN
)) {
788 if (0 == ts
.tv_sec
) log_clock_gettime_realtime(&ts
);
789 t
= (off_t
)ts
.tv_sec
;
792 t
= (off_t
)con
->request_start_hp
.tv_sec
;
793 ns
= con
->request_start_hp
.tv_nsec
;
795 if (f
->opt
& FORMAT_FLAG_TIME_MSEC
) {
797 t
+= (ns
+ 999999) / 1000000; /* ceil */
798 } else if (f
->opt
& FORMAT_FLAG_TIME_USEC
) {
800 t
+= (ns
+ 999) / 1000; /* ceil */
801 } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC)*/
805 buffer_append_int(b
, (intmax_t)t
);
806 } else { /*(FORMAT_FLAG_TIME_MSEC_FRAC|FORMAT_FLAG_TIME_USEC_FRAC|FORMAT_FLAG_TIME_NSEC_FRAC)*/
809 if (!(f
->opt
& FORMAT_FLAG_TIME_BEGIN
)) {
810 if (0 == ts
.tv_sec
) log_clock_gettime_realtime(&ts
);
813 ns
= con
->request_start_hp
.tv_nsec
;
815 /*assert(t < 1000000000);*/
816 if (f
->opt
& FORMAT_FLAG_TIME_MSEC_FRAC
) {
817 ns
+= 999999; /* ceil */
819 buffer_append_string_len(b
, CONST_STR_LEN("000"));
820 } else if (f
->opt
& FORMAT_FLAG_TIME_USEC_FRAC
) {
821 ns
+= 999; /* ceil */
823 buffer_append_string_len(b
, CONST_STR_LEN("000000"));
824 } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC_FRAC)*/
825 buffer_append_string_len(b
, CONST_STR_LEN("000000000"));
827 for (ptr
= b
->ptr
+ buffer_string_length(b
); ns
> 0; ns
/= 10)
828 *--ptr
= (ns
% 10) + '0';
830 } else if (!(f
->opt
& FORMAT_FLAG_TIME_BEGIN
) && srv
->cur_ts
== *(p
->conf
.last_generated_accesslog_ts_ptr
)) {
831 buffer_append_string_buffer(b
, p
->conf
.ts_accesslog_str
);
833 /* cache the generated timestamp (only if ! FORMAT_FLAG_TIME_BEGIN) */
836 #if defined(HAVE_STRUCT_TM_GMTOFF)
837 # ifdef HAVE_LOCALTIME_R
839 # endif /* HAVE_LOCALTIME_R */
840 #else /* HAVE_STRUCT_TM_GMTOFF */
841 # ifdef HAVE_GMTIME_R
843 # endif /* HAVE_GMTIME_R */
844 #endif /* HAVE_STRUCT_TM_GMTOFF */
846 if (!(f
->opt
& FORMAT_FLAG_TIME_BEGIN
)) {
847 t
= *(p
->conf
.last_generated_accesslog_ts_ptr
) = srv
->cur_ts
;
850 t
= con
->request_start
;
853 #if defined(HAVE_STRUCT_TM_GMTOFF)
854 # ifdef HAVE_LOCALTIME_R
855 tmptr
= localtime_r(&t
, &tm
);
856 # else /* HAVE_LOCALTIME_R */
857 tmptr
= localtime(&t
);
858 # endif /* HAVE_LOCALTIME_R */
859 #else /* HAVE_STRUCT_TM_GMTOFF */
860 # ifdef HAVE_GMTIME_R
861 tmptr
= gmtime_r(&t
, &tm
);
862 # else /* HAVE_GMTIME_R */
864 # endif /* HAVE_GMTIME_R */
865 #endif /* HAVE_STRUCT_TM_GMTOFF */
867 buffer_string_prepare_copy(p
->conf
.ts_accesslog_str
, 255);
869 if (buffer_string_is_empty(f
->string
)) {
870 #if defined(HAVE_STRUCT_TM_GMTOFF)
872 buffer_append_strftime(p
->conf
.ts_accesslog_str
, "[%d/%b/%Y:%H:%M:%S ", tmptr
);
873 buffer_append_string_len(p
->conf
.ts_accesslog_str
, tmptr
->tm_gmtoff
>= 0 ? "+" : "-", 1);
875 scd
= labs(tmptr
->tm_gmtoff
);
877 min
= (scd
% 3600) / 60;
880 if (hrs
< 10) buffer_append_string_len(p
->conf
.ts_accesslog_str
, CONST_STR_LEN("0"));
881 buffer_append_int(p
->conf
.ts_accesslog_str
, hrs
);
883 if (min
< 10) buffer_append_string_len(p
->conf
.ts_accesslog_str
, CONST_STR_LEN("0"));
884 buffer_append_int(p
->conf
.ts_accesslog_str
, min
);
885 buffer_append_string_len(p
->conf
.ts_accesslog_str
, CONST_STR_LEN("]"));
887 buffer_append_strftime(p
->conf
.ts_accesslog_str
, "[%d/%b/%Y:%H:%M:%S +0000]", tmptr
);
888 #endif /* HAVE_STRUCT_TM_GMTOFF */
890 buffer_append_strftime(p
->conf
.ts_accesslog_str
, f
->string
->ptr
, tmptr
);
893 buffer_append_string_buffer(b
, p
->conf
.ts_accesslog_str
);
896 case FORMAT_TIME_USED
:
897 case FORMAT_TIME_USED_US
:
898 if (f
->opt
& FORMAT_FLAG_TIME_SEC
) {
899 buffer_append_int(b
, srv
->cur_ts
- con
->request_start
);
901 const struct timespec
* const bs
= &con
->request_start_hp
;
902 off_t tdiff
; /*(expected to be 64-bit since large file support enabled)*/
903 if (0 == ts
.tv_sec
) log_clock_gettime_realtime(&ts
);
904 tdiff
= (off_t
)(ts
.tv_sec
- bs
->tv_sec
)*1000000000 + (ts
.tv_nsec
- bs
->tv_nsec
);
906 /* sanity check for time moving backwards
907 * (daylight savings adjustment or leap seconds or ?) */
909 } else if (f
->opt
& FORMAT_FLAG_TIME_MSEC
) {
910 tdiff
+= 999999; /* ceil */
912 } else if (f
->opt
& FORMAT_FLAG_TIME_USEC
) {
913 tdiff
+= 999; /* ceil */
915 } /* else (f->opt & FORMAT_FLAG_TIME_NSEC) */
916 buffer_append_int(b
, (intmax_t)tdiff
);
919 case FORMAT_REMOTE_ADDR
:
920 case FORMAT_REMOTE_HOST
:
921 buffer_append_string_buffer(b
, con
->dst_addr_buf
);
923 case FORMAT_REMOTE_IDENT
:
925 buffer_append_string_len(b
, CONST_STR_LEN("-"));
927 case FORMAT_REMOTE_USER
:
928 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->environment
, "REMOTE_USER")) && !buffer_string_is_empty(ds
->value
)) {
929 accesslog_append_escaped(b
, ds
->value
);
931 buffer_append_string_len(b
, CONST_STR_LEN("-"));
934 case FORMAT_REQUEST_LINE
:
935 if (!buffer_string_is_empty(con
->request
.request_line
)) {
936 accesslog_append_escaped(b
, con
->request
.request_line
);
940 buffer_append_int(b
, con
->http_status
);
943 case FORMAT_BYTES_OUT_NO_HEADER
:
944 if (con
->bytes_written
> 0) {
946 con
->bytes_written
- con
->bytes_header
<= 0 ? 0 : con
->bytes_written
- con
->bytes_header
);
948 buffer_append_string_len(b
, CONST_STR_LEN("-"));
952 if (NULL
!= (ds
= (data_string
*)array_get_element_klen(con
->request
.headers
, CONST_BUF_LEN(f
->string
)))) {
953 accesslog_append_escaped(b
, ds
->value
);
955 buffer_append_string_len(b
, CONST_STR_LEN("-"));
958 case FORMAT_RESPONSE_HEADER
:
959 if (NULL
!= (ds
= (data_string
*)array_get_element_klen(con
->response
.headers
, CONST_BUF_LEN(f
->string
)))) {
960 accesslog_append_escaped(b
, ds
->value
);
962 buffer_append_string_len(b
, CONST_STR_LEN("-"));
967 if (NULL
!= (ds
= (data_string
*)array_get_element_klen(con
->environment
, CONST_BUF_LEN(f
->string
)))) {
968 accesslog_append_escaped(b
, ds
->value
);
970 buffer_append_string_len(b
, CONST_STR_LEN("-"));
973 case FORMAT_FILENAME
:
974 if (!buffer_string_is_empty(con
->physical
.path
)) {
975 buffer_append_string_buffer(b
, con
->physical
.path
);
977 buffer_append_string_len(b
, CONST_STR_LEN("-"));
980 case FORMAT_BYTES_OUT
:
981 if (con
->bytes_written
> 0) {
982 buffer_append_int(b
, con
->bytes_written
);
984 buffer_append_string_len(b
, CONST_STR_LEN("-"));
987 case FORMAT_BYTES_IN
:
988 if (con
->bytes_read
> 0) {
989 buffer_append_int(b
, con
->bytes_read
);
991 buffer_append_string_len(b
, CONST_STR_LEN("-"));
994 case FORMAT_SERVER_NAME
:
995 if (!buffer_string_is_empty(con
->server_name
)) {
996 buffer_append_string_buffer(b
, con
->server_name
);
998 buffer_append_string_len(b
, CONST_STR_LEN("-"));
1001 case FORMAT_HTTP_HOST
:
1002 if (!buffer_string_is_empty(con
->uri
.authority
)) {
1003 accesslog_append_escaped(b
, con
->uri
.authority
);
1005 buffer_append_string_len(b
, CONST_STR_LEN("-"));
1008 case FORMAT_REQUEST_PROTOCOL
:
1009 buffer_append_string_len(b
,
1010 con
->request
.http_version
== HTTP_VERSION_1_1
? "HTTP/1.1" : "HTTP/1.0", 8);
1012 case FORMAT_REQUEST_METHOD
:
1013 buffer_append_string(b
, get_http_method_name(con
->request
.http_method
));
1015 case FORMAT_PERCENT
:
1016 buffer_append_string_len(b
, CONST_STR_LEN("%"));
1018 case FORMAT_LOCAL_ADDR
:
1020 /* (perf: not using getsockname() and inet_ntop_cache_get_ip())
1021 * (still useful if admin has configured explicit listen IPs) */
1023 buffer
*srvtoken
= con
->srv_socket
->srv_token
;
1024 if (srvtoken
->ptr
[0] == '[') {
1025 colon
= strstr(srvtoken
->ptr
, "]:");
1027 colon
= strchr(srvtoken
->ptr
, ':');
1030 buffer_append_string_len(b
, srvtoken
->ptr
, (size_t)(colon
- srvtoken
->ptr
));
1032 buffer_append_string_buffer(b
, srvtoken
);
1036 case FORMAT_SERVER_PORT
:
1039 buffer
*srvtoken
= ((server_socket
*)(con
->srv_socket
))->srv_token
;
1040 if (srvtoken
->ptr
[0] == '[') {
1041 colon
= strstr(srvtoken
->ptr
, "]:");
1043 colon
= strchr(srvtoken
->ptr
, ':');
1046 buffer_append_string(b
, colon
+1);
1048 buffer_append_int(b
, srv
->srvconf
.port
);
1052 case FORMAT_QUERY_STRING
:
1053 accesslog_append_escaped(b
, con
->uri
.query
);
1056 accesslog_append_escaped(b
, con
->uri
.path_raw
);
1058 case FORMAT_CONNECTION_STATUS
:
1059 if (con
->state
== CON_STATE_RESPONSE_END
) {
1060 if (0 == con
->keep_alive
) {
1061 buffer_append_string_len(b
, CONST_STR_LEN("-"));
1063 buffer_append_string_len(b
, CONST_STR_LEN("+"));
1065 } else { /* CON_STATE_ERROR */
1066 buffer_append_string_len(b
, CONST_STR_LEN("X"));
1069 case FORMAT_KEEPALIVE_COUNT
:
1070 if (con
->request_count
> 1) {
1071 buffer_append_int(b
, (intmax_t)(con
->request_count
-1));
1073 buffer_append_string_len(b
, CONST_STR_LEN("0"));
1077 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->request
.headers
, "Cookie"))) {
1078 char *str
= ds
->value
->ptr
;
1079 size_t len
= buffer_string_length(f
->string
);
1081 while (*str
== ' ' || *str
== '\t') ++str
;
1082 if (0 == strncmp(str
, f
->string
->ptr
, len
) && str
[len
] == '=') {
1083 char *v
= str
+len
+1;
1085 for (str
= v
; *str
!= '\0' && *str
!= ';'; ++str
) ;
1086 if (str
== v
) break;
1087 do { --str
; } while (str
> v
&& (*str
== ' ' || *str
== '\t'));
1088 bstr
= buffer_init();
1089 buffer_copy_string_len(bstr
, v
, str
- v
+ 1);
1090 accesslog_append_escaped(b
, bstr
);
1094 do { ++str
; } while (*str
!= ' ' && *str
!= '\t' && *str
!= '\0');
1096 while (*str
== ' ' || *str
== '\t') ++str
;
1097 } while (*str
++ == ';');
1109 if (p
->conf
.use_syslog
) { /* syslog doesn't cache */
1110 #ifdef HAVE_SYSLOG_H
1111 if (!buffer_string_is_empty(b
)) {
1112 /*(syslog appends a \n on its own)*/
1113 syslog(p
->conf
.syslog_level
, "%s", b
->ptr
);
1117 return HANDLER_GO_ON
;
1120 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
1122 if ((!buffer_string_is_empty(p
->conf
.access_logfile
) && p
->conf
.access_logfile
->ptr
[0] == '|') || /* pipes don't cache */
1124 buffer_string_length(b
) >= BUFFER_MAX_REUSE_SIZE
) {
1125 if (p
->conf
.log_access_fd
>= 0) {
1126 accesslog_write_all(srv
, p
->conf
.access_logfile
, p
->conf
.log_access_fd
, CONST_BUF_LEN(b
));
1131 return HANDLER_GO_ON
;
1135 int mod_accesslog_plugin_init(plugin
*p
);
1136 int mod_accesslog_plugin_init(plugin
*p
) {
1137 p
->version
= LIGHTTPD_VERSION_ID
;
1138 p
->name
= buffer_init_string("accesslog");
1140 p
->init
= mod_accesslog_init
;
1141 p
->set_defaults
= log_access_open
;
1142 p
->cleanup
= mod_accesslog_free
;
1144 p
->handle_request_done
= log_access_write
;
1145 p
->handle_trigger
= log_access_periodic_flush
;
1146 p
->handle_sighup
= log_access_cycle
;