9 #include "inet_ntop_cache.h"
11 #include "sys-socket.h"
13 #include <sys/types.h>
42 FORMAT_BYTES_OUT_NO_HEADER
,
51 FORMAT_REQUEST_PROTOCOL
,
52 FORMAT_REQUEST_METHOD
,
59 FORMAT_CONNECTION_STATUS
,
63 FORMAT_RESPONSE_HEADER
70 * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
74 static const format_mapping fmap
[] =
76 { '%', FORMAT_PERCENT
},
77 { 'h', FORMAT_REMOTE_HOST
},
78 { 'l', FORMAT_REMOTE_IDENT
},
79 { 'u', FORMAT_REMOTE_USER
},
80 { 't', FORMAT_TIMESTAMP
},
81 { 'r', FORMAT_REQUEST_LINE
},
82 { 's', FORMAT_STATUS
},
83 { 'b', FORMAT_BYTES_OUT_NO_HEADER
},
84 { 'i', FORMAT_HEADER
},
86 { 'a', FORMAT_REMOTE_ADDR
},
87 { 'A', FORMAT_LOCAL_ADDR
},
88 { 'B', FORMAT_BYTES_OUT_NO_HEADER
},
89 { 'C', FORMAT_COOKIE
},
90 { 'D', FORMAT_TIME_USED_MS
},
92 { 'f', FORMAT_FILENAME
},
93 { 'H', FORMAT_REQUEST_PROTOCOL
},
94 { 'm', FORMAT_REQUEST_METHOD
},
95 { 'n', FORMAT_UNSUPPORTED
}, /* we have no notes */
96 { 'p', FORMAT_SERVER_PORT
},
97 { 'P', FORMAT_UNSUPPORTED
}, /* we are only one process */
98 { 'q', FORMAT_QUERY_STRING
},
99 { 'T', FORMAT_TIME_USED
},
100 { 'U', FORMAT_URL
}, /* w/o querystring */
101 { 'v', FORMAT_SERVER_NAME
},
102 { 'V', FORMAT_HTTP_HOST
},
103 { 'X', FORMAT_CONNECTION_STATUS
},
104 { 'I', FORMAT_BYTES_IN
},
105 { 'O', FORMAT_BYTES_OUT
},
107 { 'o', FORMAT_RESPONSE_HEADER
},
109 { '\0', FORMAT_UNSET
}
114 enum { FIELD_UNSET
, FIELD_STRING
, FIELD_FORMAT
} type
;
128 buffer
*access_logfile
;
130 buffer
*access_logbuffer
; /* each logfile has a separate buffer */
132 unsigned short use_syslog
; /* syslog has global buffer */
133 unsigned short syslog_level
;
137 time_t last_generated_accesslog_ts
;
138 time_t *last_generated_accesslog_ts_ptr
;
140 buffer
*ts_accesslog_str
;
141 buffer
*ts_accesslog_fmt_str
;
142 unsigned short append_tz_offset
;
144 format_fields
*parsed_format
;
150 plugin_config
**config_storage
;
153 buffer
*syslog_logbuffer
; /* syslog has global buffer. no caching, always written directly */
156 INIT_FUNC(mod_accesslog_init
) {
159 p
= calloc(1, sizeof(*p
));
160 p
->syslog_logbuffer
= buffer_init();
165 static void accesslog_write_all(server
*srv
, const buffer
*filename
, int fd
, const void* buf
, size_t count
) {
166 if (-1 == write_all(fd
, buf
, count
)) {
167 log_error_write(srv
, __FILE__
, __LINE__
, "sbs",
168 "writing access log entry failed:", filename
, strerror(errno
));
172 static void accesslog_append_escaped(buffer
*dest
, buffer
*str
) {
173 char *ptr
, *start
, *end
;
175 /* replaces non-printable chars with \xHH where HH is the hex representation of the byte */
176 /* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
177 if (buffer_string_is_empty(str
)) return;
178 buffer_string_prepare_append(dest
, buffer_string_length(str
));
180 for (ptr
= start
= str
->ptr
, end
= str
->ptr
+ buffer_string_length(str
); ptr
< end
; ptr
++) {
181 unsigned char const c
= (unsigned char) *ptr
;
182 if (c
>= ' ' && c
<= '~' && c
!= '"' && c
!= '\\') {
183 /* nothing to change, add later as one block */
185 /* copy previous part */
187 buffer_append_string_len(dest
, start
, ptr
- start
);
193 BUFFER_APPEND_STRING_CONST(dest
, "\\\"");
196 BUFFER_APPEND_STRING_CONST(dest
, "\\\\");
199 BUFFER_APPEND_STRING_CONST(dest
, "\\b");
202 BUFFER_APPEND_STRING_CONST(dest
, "\\n");
205 BUFFER_APPEND_STRING_CONST(dest
, "\\r");
208 BUFFER_APPEND_STRING_CONST(dest
, "\\t");
211 BUFFER_APPEND_STRING_CONST(dest
, "\\v");
214 /* non printable char => \xHH */
215 char hh
[5] = {'\\','x',0,0,0};
217 hh
[2] = (h
> 9) ? (h
- 10 + 'A') : (h
+ '0');
219 hh
[3] = (h
> 9) ? (h
- 10 + 'A') : (h
+ '0');
220 buffer_append_string_len(dest
, &hh
[0], 4);
228 buffer_append_string_len(dest
, start
, end
- start
);
232 static int accesslog_parse_format(server
*srv
, format_fields
*fields
, buffer
*format
) {
233 size_t i
, j
, k
= 0, start
= 0;
235 if (buffer_is_empty(format
)) return -1;
237 for (i
= 0; i
< buffer_string_length(format
); i
++) {
238 switch(format
->ptr
[i
]) {
240 if (i
> 0 && start
!= i
) {
241 /* copy the string before this % */
242 if (fields
->size
== 0) {
245 fields
->ptr
= malloc(fields
->size
* sizeof(format_field
* ));
246 } else if (fields
->used
== fields
->size
) {
248 fields
->ptr
= realloc(fields
->ptr
, fields
->size
* sizeof(format_field
* ));
251 fields
->ptr
[fields
->used
] = malloc(sizeof(format_field
));
252 fields
->ptr
[fields
->used
]->type
= FIELD_STRING
;
253 fields
->ptr
[fields
->used
]->string
= buffer_init();
255 buffer_copy_string_len(fields
->ptr
[fields
->used
]->string
, format
->ptr
+ start
, i
- start
);
260 /* we need a new field */
262 if (fields
->size
== 0) {
265 fields
->ptr
= malloc(fields
->size
* sizeof(format_field
* ));
266 } else if (fields
->used
== fields
->size
) {
268 fields
->ptr
= realloc(fields
->ptr
, fields
->size
* sizeof(format_field
* ));
271 /* search for the terminating command */
272 switch (format
->ptr
[i
+1]) {
275 /* after the } has to be a character */
276 if (format
->ptr
[i
+2] == '\0') {
277 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%< and %> have to be followed by a format-specifier");
282 for (j
= 0; fmap
[j
].key
!= '\0'; j
++) {
283 if (fmap
[j
].key
!= format
->ptr
[i
+2]) continue;
287 fields
->ptr
[fields
->used
] = malloc(sizeof(format_field
));
288 fields
->ptr
[fields
->used
]->type
= FIELD_FORMAT
;
289 fields
->ptr
[fields
->used
]->field
= fmap
[j
].type
;
290 fields
->ptr
[fields
->used
]->string
= NULL
;
297 if (fmap
[j
].key
== '\0') {
298 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%< and %> have to be followed by a valid format-specifier");
303 i
= start
- 1; /* skip the string */
307 /* go forward to } */
309 for (k
= i
+2; k
< buffer_string_length(format
); k
++) {
310 if (format
->ptr
[k
] == '}') break;
313 if (k
== buffer_string_length(format
)) {
314 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%{ has to be terminated by a }");
318 /* after the } has to be a character */
319 if (format
->ptr
[k
+1] == '\0') {
320 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%{...} has to be followed by a format-specifier");
325 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%{...} has to be contain a string");
329 for (j
= 0; fmap
[j
].key
!= '\0'; j
++) {
330 if (fmap
[j
].key
!= format
->ptr
[k
+1]) continue;
334 fields
->ptr
[fields
->used
] = malloc(sizeof(format_field
));
335 fields
->ptr
[fields
->used
]->type
= FIELD_FORMAT
;
336 fields
->ptr
[fields
->used
]->field
= fmap
[j
].type
;
337 fields
->ptr
[fields
->used
]->string
= buffer_init();
339 buffer_copy_string_len(fields
->ptr
[fields
->used
]->string
, format
->ptr
+ i
+ 2, k
- (i
+ 2));
346 if (fmap
[j
].key
== '\0') {
347 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%{...} has to be followed by a valid format-specifier");
352 i
= start
- 1; /* skip the string */
356 /* after the % has to be a character */
357 if (format
->ptr
[i
+1] == '\0') {
358 log_error_write(srv
, __FILE__
, __LINE__
, "s", "% has to be followed by a format-specifier");
362 for (j
= 0; fmap
[j
].key
!= '\0'; j
++) {
363 if (fmap
[j
].key
!= format
->ptr
[i
+1]) continue;
367 fields
->ptr
[fields
->used
] = malloc(sizeof(format_field
));
368 fields
->ptr
[fields
->used
]->type
= FIELD_FORMAT
;
369 fields
->ptr
[fields
->used
]->field
= fmap
[j
].type
;
370 fields
->ptr
[fields
->used
]->string
= NULL
;
377 if (fmap
[j
].key
== '\0') {
378 log_error_write(srv
, __FILE__
, __LINE__
, "s", "% has to be followed by a valid format-specifier");
383 i
= start
- 1; /* skip the string */
393 /* copy the string */
394 if (fields
->size
== 0) {
397 fields
->ptr
= malloc(fields
->size
* sizeof(format_field
* ));
398 } else if (fields
->used
== fields
->size
) {
400 fields
->ptr
= realloc(fields
->ptr
, fields
->size
* sizeof(format_field
* ));
403 fields
->ptr
[fields
->used
] = malloc(sizeof(format_field
));
404 fields
->ptr
[fields
->used
]->type
= FIELD_STRING
;
405 fields
->ptr
[fields
->used
]->string
= buffer_init();
407 buffer_copy_string_len(fields
->ptr
[fields
->used
]->string
, format
->ptr
+ start
, i
- start
);
415 FREE_FUNC(mod_accesslog_free
) {
416 plugin_data
*p
= p_d
;
419 if (!p
) return HANDLER_GO_ON
;
421 if (p
->config_storage
) {
423 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
424 plugin_config
*s
= p
->config_storage
[i
];
426 if (NULL
== s
) continue;
428 if (!buffer_string_is_empty(s
->access_logbuffer
)) {
429 if (s
->log_access_fd
!= -1) {
430 accesslog_write_all(srv
, s
->access_logfile
, s
->log_access_fd
, CONST_BUF_LEN(s
->access_logbuffer
));
434 if (s
->log_access_fd
!= -1) close(s
->log_access_fd
);
436 buffer_free(s
->ts_accesslog_str
);
437 buffer_free(s
->ts_accesslog_fmt_str
);
438 buffer_free(s
->access_logbuffer
);
439 buffer_free(s
->format
);
440 buffer_free(s
->access_logfile
);
442 if (s
->parsed_format
) {
444 for (j
= 0; j
< s
->parsed_format
->used
; j
++) {
445 if (s
->parsed_format
->ptr
[j
]->string
) buffer_free(s
->parsed_format
->ptr
[j
]->string
);
446 free(s
->parsed_format
->ptr
[j
]);
448 free(s
->parsed_format
->ptr
);
449 free(s
->parsed_format
);
455 free(p
->config_storage
);
458 if (p
->syslog_logbuffer
) buffer_free(p
->syslog_logbuffer
);
461 return HANDLER_GO_ON
;
464 SETDEFAULTS_FUNC(log_access_open
) {
465 plugin_data
*p
= p_d
;
468 config_values_t cv
[] = {
469 { "accesslog.filename", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
470 { "accesslog.use-syslog", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
},
471 { "accesslog.format", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
472 { "accesslog.syslog-level", NULL
, T_CONFIG_SHORT
, T_CONFIG_SCOPE_CONNECTION
},
473 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
476 if (!p
) return HANDLER_ERROR
;
478 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
480 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
481 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
484 s
= calloc(1, sizeof(plugin_config
));
485 s
->access_logfile
= buffer_init();
486 s
->format
= buffer_init();
487 s
->access_logbuffer
= buffer_init();
488 s
->ts_accesslog_str
= buffer_init();
489 s
->ts_accesslog_fmt_str
= buffer_init();
490 s
->log_access_fd
= -1;
491 s
->last_generated_accesslog_ts
= 0;
492 s
->last_generated_accesslog_ts_ptr
= &(s
->last_generated_accesslog_ts
);
493 s
->syslog_level
= LOG_INFO
;
496 cv
[0].destination
= s
->access_logfile
;
497 cv
[1].destination
= &(s
->use_syslog
);
498 cv
[2].destination
= s
->format
;
499 cv
[3].destination
= &(s
->syslog_level
);
501 p
->config_storage
[i
] = s
;
503 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
504 return HANDLER_ERROR
;
507 if (i
== 0 && buffer_string_is_empty(s
->format
)) {
508 /* set a default logfile string */
510 buffer_copy_string_len(s
->format
, CONST_STR_LEN("%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""));
515 if (!buffer_is_empty(s
->format
)) {
518 s
->parsed_format
= calloc(1, sizeof(*(s
->parsed_format
)));
520 if (-1 == accesslog_parse_format(srv
, s
->parsed_format
, s
->format
)) {
522 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
523 "parsing accesslog-definition failed:", s
->format
);
525 return HANDLER_ERROR
;
528 /* make sure they didn't try to send the timestamp in twice...
529 * also, save the format string in a different variable (this
530 * will save a few conditionals later)
533 for (j
= 0; j
< s
->parsed_format
->used
; j
++) {
534 if (FIELD_FORMAT
== s
->parsed_format
->ptr
[j
]->type
) {
535 if (FORMAT_TIMESTAMP
== s
->parsed_format
->ptr
[j
]->field
) {
536 if (!buffer_string_is_empty(s
->parsed_format
->ptr
[j
]->string
)) {
537 buffer_copy_string(s
->ts_accesslog_fmt_str
, s
->parsed_format
->ptr
[j
]->string
->ptr
);
541 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
542 "you may not use the timestamp twice in the same access log:", s
->format
);
544 return HANDLER_ERROR
;
552 for (j
= 0; j
< s
->parsed_format
->used
; j
++) {
553 switch (s
->parsed_format
->ptr
[j
]->type
) {
555 log_error_write(srv
, __FILE__
, __LINE__
, "ssds",
556 "config:", "format", s
->parsed_format
->ptr
[j
]->field
,
557 s
->parsed_format
->ptr
[j
]->string
?
558 s
->parsed_format
->ptr
[j
]->string
->ptr
: "" );
561 log_error_write(srv
, __FILE__
, __LINE__
, "ssbs", "config:", "string '", s
->parsed_format
->ptr
[j
]->string
, "'");
570 s
->append_tz_offset
= 0;
571 if (buffer_string_is_empty(s
->ts_accesslog_fmt_str
)) {
572 #if defined(HAVE_STRUCT_TM_GMTOFF)
573 BUFFER_COPY_STRING_CONST(s
->ts_accesslog_fmt_str
, "[%d/%b/%Y:%H:%M:%S ");
574 s
->append_tz_offset
= 1;
576 BUFFER_COPY_STRING_CONST(s
->ts_accesslog_fmt_str
, "[%d/%b/%Y:%H:%M:%S +0000]");
581 /* ignore the next checks */
585 if (buffer_string_is_empty(s
->access_logfile
)) continue;
587 if (srv
->srvconf
.preflight_check
) continue;
589 if (-1 == (s
->log_access_fd
= open_logfile_or_pipe(srv
, s
->access_logfile
->ptr
)))
590 return HANDLER_ERROR
;
594 return HANDLER_GO_ON
;
597 SIGHUP_FUNC(log_access_cycle
) {
598 plugin_data
*p
= p_d
;
601 if (!p
->config_storage
) return HANDLER_GO_ON
;
603 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
604 plugin_config
*s
= p
->config_storage
[i
];
606 if (!buffer_string_is_empty(s
->access_logbuffer
)) {
607 if (s
->log_access_fd
!= -1) {
608 accesslog_write_all(srv
, s
->access_logfile
, s
->log_access_fd
, CONST_BUF_LEN(s
->access_logbuffer
));
611 buffer_reset(s
->access_logbuffer
);
614 if (s
->use_syslog
== 0
615 && !buffer_string_is_empty(s
->access_logfile
)
616 && s
->access_logfile
->ptr
[0] != '|') {
618 if (-1 != s
->log_access_fd
) close(s
->log_access_fd
);
620 if (-1 == (s
->log_access_fd
=
621 open(s
->access_logfile
->ptr
, O_APPEND
| O_WRONLY
| O_CREAT
| O_LARGEFILE
, 0644))) {
623 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "cycling access-log failed:", strerror(errno
));
625 return HANDLER_ERROR
;
627 fd_close_on_exec(s
->log_access_fd
);
631 return HANDLER_GO_ON
;
636 static int mod_accesslog_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
638 plugin_config
*s
= p
->config_storage
[0];
640 PATCH(access_logfile
);
642 PATCH(log_access_fd
);
643 PATCH(last_generated_accesslog_ts_ptr
);
644 PATCH(access_logbuffer
);
645 PATCH(ts_accesslog_str
);
646 PATCH(ts_accesslog_fmt_str
);
647 PATCH(append_tz_offset
);
648 PATCH(parsed_format
);
652 /* skip the first, the global context */
653 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
654 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
655 s
= p
->config_storage
[i
];
657 /* condition didn't match */
658 if (!config_check_cond(srv
, con
, dc
)) continue;
661 for (j
= 0; j
< dc
->value
->used
; j
++) {
662 data_unset
*du
= dc
->value
->data
[j
];
664 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("accesslog.filename"))) {
665 PATCH(access_logfile
);
666 PATCH(log_access_fd
);
667 PATCH(access_logbuffer
);
668 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("accesslog.format"))) {
670 PATCH(parsed_format
);
671 PATCH(last_generated_accesslog_ts_ptr
);
672 PATCH(ts_accesslog_str
);
673 PATCH(ts_accesslog_fmt_str
);
674 PATCH(append_tz_offset
);
675 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("accesslog.use-syslog"))) {
677 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("accesslog.syslog-level"))) {
687 REQUESTDONE_FUNC(log_access_write
) {
688 plugin_data
*p
= p_d
;
695 mod_accesslog_patch_connection(srv
, con
, p
);
697 /* No output device, nothing to do */
698 if (!p
->conf
.use_syslog
&& p
->conf
.log_access_fd
== -1) return HANDLER_GO_ON
;
700 if (p
->conf
.use_syslog
) {
701 b
= p
->syslog_logbuffer
;
703 b
= p
->conf
.access_logbuffer
;
706 if (buffer_is_empty(b
)) {
707 buffer_string_set_length(b
, 0);
710 for (j
= 0; j
< p
->conf
.parsed_format
->used
; j
++) {
711 switch(p
->conf
.parsed_format
->ptr
[j
]->type
) {
713 buffer_append_string_buffer(b
, p
->conf
.parsed_format
->ptr
[j
]->string
);
716 switch(p
->conf
.parsed_format
->ptr
[j
]->field
) {
717 case FORMAT_TIMESTAMP
:
719 /* cache the generated timestamp */
720 if (srv
->cur_ts
!= *(p
->conf
.last_generated_accesslog_ts_ptr
)) {
722 #if defined(HAVE_STRUCT_TM_GMTOFF)
726 buffer_string_prepare_copy(p
->conf
.ts_accesslog_str
, 255);
727 #if defined(HAVE_STRUCT_TM_GMTOFF)
728 # ifdef HAVE_LOCALTIME_R
729 localtime_r(&(srv
->cur_ts
), &tm
);
730 buffer_append_strftime(p
->conf
.ts_accesslog_str
, p
->conf
.ts_accesslog_fmt_str
->ptr
, &tm
);
731 # else /* HAVE_LOCALTIME_R */
732 buffer_append_strftime(p
->conf
.ts_accesslog_str
, p
->conf
.ts_accesslog_fmt_str
->ptr
, localtime(&(srv
->cur_ts
)));
733 # endif /* HAVE_LOCALTIME_R */
735 if (p
->conf
.append_tz_offset
) {
736 buffer_append_string_len(p
->conf
.ts_accesslog_str
, tm
.tm_gmtoff
>= 0 ? "+" : "-", 1);
738 scd
= abs(tm
.tm_gmtoff
);
740 min
= (scd
% 3600) / 60;
743 if (hrs
< 10) buffer_append_string_len(p
->conf
.ts_accesslog_str
, CONST_STR_LEN("0"));
744 buffer_append_int(p
->conf
.ts_accesslog_str
, hrs
);
746 if (min
< 10) buffer_append_string_len(p
->conf
.ts_accesslog_str
, CONST_STR_LEN("0"));
747 buffer_append_int(p
->conf
.ts_accesslog_str
, min
);
748 buffer_append_string_len(p
->conf
.ts_accesslog_str
, CONST_STR_LEN("]"));
750 #else /* HAVE_STRUCT_TM_GMTOFF */
751 # ifdef HAVE_GMTIME_R
752 gmtime_r(&(srv
->cur_ts
), &tm
);
753 buffer_append_strftime(p
->conf
.ts_accesslog_str
, p
->conf
.ts_accesslog_fmt_str
->ptr
, &tm
);
754 # else /* HAVE_GMTIME_R */
755 buffer_append_strftime(p
->conf
.ts_accesslog_str
, p
->conf
.ts_accesslog_fmt_str
->ptr
, gmtime(&(srv
->cur_ts
)));
756 # endif /* HAVE_GMTIME_R */
757 #endif /* HAVE_STRUCT_TM_GMTOFF */
759 *(p
->conf
.last_generated_accesslog_ts_ptr
) = srv
->cur_ts
;
763 buffer_append_string_buffer(b
, p
->conf
.ts_accesslog_str
);
766 case FORMAT_REMOTE_HOST
:
768 /* handle inet_ntop cache */
770 buffer_append_string(b
, inet_ntop_cache_get_ip(srv
, &(con
->dst_addr
)));
773 case FORMAT_REMOTE_IDENT
:
775 buffer_append_string_len(b
, CONST_STR_LEN("-"));
777 case FORMAT_REMOTE_USER
:
778 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->environment
, "REMOTE_USER")) && !buffer_string_is_empty(ds
->value
)) {
779 accesslog_append_escaped(b
, ds
->value
);
781 buffer_append_string_len(b
, CONST_STR_LEN("-"));
784 case FORMAT_REQUEST_LINE
:
785 if (!buffer_string_is_empty(con
->request
.request_line
)) {
786 accesslog_append_escaped(b
, con
->request
.request_line
);
790 buffer_append_int(b
, con
->http_status
);
793 case FORMAT_BYTES_OUT_NO_HEADER
:
794 if (con
->bytes_written
> 0) {
796 con
->bytes_written
- con
->bytes_header
<= 0 ? 0 : con
->bytes_written
- con
->bytes_header
);
798 buffer_append_string_len(b
, CONST_STR_LEN("-"));
802 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->request
.headers
, p
->conf
.parsed_format
->ptr
[j
]->string
->ptr
))) {
803 accesslog_append_escaped(b
, ds
->value
);
805 buffer_append_string_len(b
, CONST_STR_LEN("-"));
808 case FORMAT_RESPONSE_HEADER
:
809 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->response
.headers
, p
->conf
.parsed_format
->ptr
[j
]->string
->ptr
))) {
810 accesslog_append_escaped(b
, ds
->value
);
812 buffer_append_string_len(b
, CONST_STR_LEN("-"));
816 if (NULL
!= (ds
= (data_string
*)array_get_element(con
->environment
, p
->conf
.parsed_format
->ptr
[j
]->string
->ptr
))) {
817 accesslog_append_escaped(b
, ds
->value
);
819 buffer_append_string_len(b
, CONST_STR_LEN("-"));
822 case FORMAT_FILENAME
:
823 if (!buffer_string_is_empty(con
->physical
.path
)) {
824 buffer_append_string_buffer(b
, con
->physical
.path
);
826 buffer_append_string_len(b
, CONST_STR_LEN("-"));
829 case FORMAT_BYTES_OUT
:
830 if (con
->bytes_written
> 0) {
831 buffer_append_int(b
, con
->bytes_written
);
833 buffer_append_string_len(b
, CONST_STR_LEN("-"));
836 case FORMAT_BYTES_IN
:
837 if (con
->bytes_read
> 0) {
838 buffer_append_int(b
, con
->bytes_read
);
840 buffer_append_string_len(b
, CONST_STR_LEN("-"));
843 case FORMAT_TIME_USED
:
844 buffer_append_int(b
, srv
->cur_ts
- con
->request_start
);
846 case FORMAT_SERVER_NAME
:
847 if (!buffer_string_is_empty(con
->server_name
)) {
848 buffer_append_string_buffer(b
, con
->server_name
);
850 buffer_append_string_len(b
, CONST_STR_LEN("-"));
853 case FORMAT_HTTP_HOST
:
854 if (!buffer_string_is_empty(con
->uri
.authority
)) {
855 accesslog_append_escaped(b
, con
->uri
.authority
);
857 buffer_append_string_len(b
, CONST_STR_LEN("-"));
860 case FORMAT_REQUEST_PROTOCOL
:
861 buffer_append_string_len(b
,
862 con
->request
.http_version
== HTTP_VERSION_1_1
? "HTTP/1.1" : "HTTP/1.0", 8);
864 case FORMAT_REQUEST_METHOD
:
865 buffer_append_string(b
, get_http_method_name(con
->request
.http_method
));
868 buffer_append_string_len(b
, CONST_STR_LEN("%"));
870 case FORMAT_SERVER_PORT
:
873 buffer
*srvtoken
= ((server_socket
*)(con
->srv_socket
))->srv_token
;
874 if (srvtoken
->ptr
[0] == '[') {
875 colon
= strstr(srvtoken
->ptr
, "]:");
877 colon
= strchr(srvtoken
->ptr
, ':');
880 buffer_append_string(b
, colon
+1);
882 buffer_append_int(b
, srv
->srvconf
.port
);
886 case FORMAT_QUERY_STRING
:
887 accesslog_append_escaped(b
, con
->uri
.query
);
890 accesslog_append_escaped(b
, con
->uri
.path_raw
);
892 case FORMAT_CONNECTION_STATUS
:
893 switch(con
->keep_alive
) {
894 case 0: buffer_append_string_len(b
, CONST_STR_LEN("-")); break;
895 default: buffer_append_string_len(b
, CONST_STR_LEN("+")); break;
900 { 'a', FORMAT_REMOTE_ADDR },
901 { 'A', FORMAT_LOCAL_ADDR },
902 { 'C', FORMAT_COOKIE },
903 { 'D', FORMAT_TIME_USED_MS },
914 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
916 if (p
->conf
.use_syslog
|| /* syslog doesn't cache */
917 (!buffer_string_is_empty(p
->conf
.access_logfile
) && p
->conf
.access_logfile
->ptr
[0] == '|') || /* pipes don't cache */
919 buffer_string_length(b
) >= BUFFER_MAX_REUSE_SIZE
) {
920 if (p
->conf
.use_syslog
) {
922 if (!buffer_string_is_empty(b
)) {
923 /* syslog appends a \n on its own */
924 buffer_string_set_length(b
, buffer_string_length(b
) - 1);
925 syslog(p
->conf
.syslog_level
, "%s", b
->ptr
);
928 } else if (p
->conf
.log_access_fd
!= -1) {
929 accesslog_write_all(srv
, p
->conf
.access_logfile
, p
->conf
.log_access_fd
, CONST_BUF_LEN(b
));
934 return HANDLER_GO_ON
;
938 int mod_accesslog_plugin_init(plugin
*p
);
939 int mod_accesslog_plugin_init(plugin
*p
) {
940 p
->version
= LIGHTTPD_VERSION_ID
;
941 p
->name
= buffer_init_string("accesslog");
943 p
->init
= mod_accesslog_init
;
944 p
->set_defaults
= log_access_open
;
945 p
->cleanup
= mod_accesslog_free
;
947 p
->handle_request_done
= log_access_write
;
948 p
->handle_sighup
= log_access_cycle
;