7 #include "inet_ntop_cache.h"
9 #include "sys-socket.h"
11 #include <sys/types.h>
40 FORMAT_BYTES_OUT_NO_HEADER
,
49 FORMAT_REQUEST_PROTOCOL
,
50 FORMAT_REQUEST_METHOD
,
57 FORMAT_CONNECTION_STATUS
,
61 FORMAT_RESPONSE_HEADER
68 * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
72 static const format_mapping fmap
[] =
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
},
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
},
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
},
105 { 'o', FORMAT_RESPONSE_HEADER
},
107 { '\0', FORMAT_UNSET
}
112 enum { FIELD_UNSET
, FIELD_STRING
, FIELD_FORMAT
} type
;
126 buffer
*access_logfile
;
128 buffer
*access_logbuffer
; /* each logfile has a separate buffer */
130 unsigned short use_syslog
; /* syslog has global buffer */
131 unsigned short syslog_level
;
135 time_t last_generated_accesslog_ts
;
136 time_t *last_generated_accesslog_ts_ptr
;
138 buffer
*ts_accesslog_str
;
139 buffer
*ts_accesslog_fmt_str
;
140 unsigned short append_tz_offset
;
142 format_fields
*parsed_format
;
148 plugin_config
**config_storage
;
151 buffer
*syslog_logbuffer
; /* syslog has global buffer. no caching, always written directly */
154 INIT_FUNC(mod_accesslog_init
) {
157 p
= calloc(1, sizeof(*p
));
158 p
->syslog_logbuffer
= buffer_init();
163 static void accesslog_append_escaped(buffer
*dest
, buffer
*str
) {
164 char *ptr
, *start
, *end
;
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);
171 for (ptr
= start
= str
->ptr
, end
= str
->ptr
+ str
->used
- 1; ptr
< end
; ptr
++) {
173 if (c
>= ' ' && c
<= '~' && c
!= '"' && c
!= '\\') {
174 /* nothing to change, add later as one block */
176 /* copy previous part */
178 buffer_append_string_len(dest
, start
, ptr
- start
);
184 BUFFER_APPEND_STRING_CONST(dest
, "\\\"");
187 BUFFER_APPEND_STRING_CONST(dest
, "\\\\");
190 BUFFER_APPEND_STRING_CONST(dest
, "\\b");
193 BUFFER_APPEND_STRING_CONST(dest
, "\\n");
196 BUFFER_APPEND_STRING_CONST(dest
, "\\r");
199 BUFFER_APPEND_STRING_CONST(dest
, "\\t");
202 BUFFER_APPEND_STRING_CONST(dest
, "\\v");
205 /* non printable char => \xHH */
206 char hh
[5] = {'\\','x',0,0,0};
208 hh
[2] = (h
> 9) ? (h
- 10 + 'A') : (h
+ '0');
210 hh
[3] = (h
> 9) ? (h
- 10 + 'A') : (h
+ '0');
211 buffer_append_string_len(dest
, &hh
[0], 4);
219 buffer_append_string_len(dest
, start
, end
- start
);
223 static int accesslog_parse_format(server
*srv
, format_fields
*fields
, buffer
*format
) {
224 size_t i
, j
, k
= 0, start
= 0;
226 if (format
->used
== 0) return -1;
228 for (i
= 0; i
< format
->used
- 1; i
++) {
229 switch(format
->ptr
[i
]) {
231 if (i
> 0 && start
!= i
) {
232 /* copy the string before this % */
233 if (fields
->size
== 0) {
236 fields
->ptr
= malloc(fields
->size
* sizeof(format_field
* ));
237 } else if (fields
->used
== fields
->size
) {
239 fields
->ptr
= realloc(fields
->ptr
, fields
->size
* sizeof(format_field
* ));
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();
246 buffer_copy_string_len(fields
->ptr
[fields
->used
]->string
, format
->ptr
+ start
, i
- start
);
251 /* we need a new field */
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 /* search for the terminating command */
263 switch (format
->ptr
[i
+1]) {
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");
273 for (j
= 0; fmap
[j
].key
!= '\0'; j
++) {
274 if (fmap
[j
].key
!= format
->ptr
[i
+2]) continue;
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
;
288 if (fmap
[j
].key
== '\0') {
289 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%< and %> have to be followed by a valid format-specifier");
294 i
= start
- 1; /* skip the string */
298 /* go forward to } */
300 for (k
= i
+2; k
< format
->used
- 1; k
++) {
301 if (format
->ptr
[k
] == '}') break;
304 if (k
== format
->used
- 1) {
305 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%{ has to be terminated by a }");
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");
316 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%{...} has to be contain a string");
320 for (j
= 0; fmap
[j
].key
!= '\0'; j
++) {
321 if (fmap
[j
].key
!= format
->ptr
[k
+1]) continue;
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();
330 buffer_copy_string_len(fields
->ptr
[fields
->used
]->string
, format
->ptr
+ i
+ 2, k
- (i
+ 2));
337 if (fmap
[j
].key
== '\0') {
338 log_error_write(srv
, __FILE__
, __LINE__
, "s", "%{...} has to be followed by a valid format-specifier");
343 i
= start
- 1; /* skip the string */
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");
353 for (j
= 0; fmap
[j
].key
!= '\0'; j
++) {
354 if (fmap
[j
].key
!= format
->ptr
[i
+1]) continue;
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
;
368 if (fmap
[j
].key
== '\0') {
369 log_error_write(srv
, __FILE__
, __LINE__
, "s", "% has to be followed by a valid format-specifier");
374 i
= start
- 1; /* skip the string */
384 /* copy the string */
385 if (fields
->size
== 0) {
388 fields
->ptr
= malloc(fields
->size
* sizeof(format_field
* ));
389 } else if (fields
->used
== fields
->size
) {
391 fields
->ptr
= realloc(fields
->ptr
, fields
->size
* sizeof(format_field
* ));
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();
398 buffer_copy_string_len(fields
->ptr
[fields
->used
]->string
, format
->ptr
+ start
, i
- start
);
406 FREE_FUNC(mod_accesslog_free
) {
407 plugin_data
*p
= p_d
;
410 if (!p
) return HANDLER_GO_ON
;
412 if (p
->config_storage
) {
414 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
415 plugin_config
*s
= p
->config_storage
[i
];
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);
425 if (s
->log_access_fd
!= -1) close(s
->log_access_fd
);
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
);
433 if (s
->parsed_format
) {
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
]);
439 free(s
->parsed_format
->ptr
);
440 free(s
->parsed_format
);
446 free(p
->config_storage
);
449 if (p
->syslog_logbuffer
) buffer_free(p
->syslog_logbuffer
);
452 return HANDLER_GO_ON
;
455 SETDEFAULTS_FUNC(log_access_open
) {
456 plugin_data
*p
= p_d
;
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
}
467 if (!p
) return HANDLER_ERROR
;
469 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
471 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
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
;
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
);
491 p
->config_storage
[i
] = s
;
493 if (0 != config_insert_values_global(srv
, ((data_config
*)srv
->config_context
->data
[i
])->value
, cv
)) {
494 return HANDLER_ERROR
;
497 if (i
== 0 && buffer_is_empty(s
->format
)) {
498 /* set a default logfile string */
500 buffer_copy_string_len(s
->format
, CONST_STR_LEN("%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""));
505 if (s
->format
->used
) {
508 s
->parsed_format
= calloc(1, sizeof(*(s
->parsed_format
)));
510 if (-1 == accesslog_parse_format(srv
, s
->parsed_format
, s
->format
)) {
512 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
513 "parsing accesslog-definition failed:", s
->format
);
515 return HANDLER_ERROR
;
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)
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
);
531 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
532 "you may not use the timestamp twice in the same access log:", s
->format
);
534 return HANDLER_ERROR
;
542 for (j
= 0; j
< s
->parsed_format
->used
; j
++) {
543 switch (s
->parsed_format
->ptr
[j
]->type
) {
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
: "" );
551 log_error_write(srv
, __FILE__
, __LINE__
, "ssbs", "config:", "string '", s
->parsed_format
->ptr
[j
]->string
, "'");
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;
566 BUFFER_COPY_STRING_CONST(s
->ts_accesslog_fmt_str
, "[%d/%b/%Y:%H:%M:%S +0000]");
571 /* ignore the next checks */
575 if (s
->access_logfile
->used
< 2) continue;
577 if (-1 == (s
->log_access_fd
= open_logfile_or_pipe(srv
, s
->access_logfile
->ptr
)))
578 return HANDLER_ERROR
;
582 return HANDLER_GO_ON
;
585 SIGHUP_FUNC(log_access_cycle
) {
586 plugin_data
*p
= p_d
;
589 if (!p
->config_storage
) return HANDLER_GO_ON
;
591 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
592 plugin_config
*s
= p
->config_storage
[i
];
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);
599 buffer_reset(s
->access_logbuffer
);
602 if (s
->use_syslog
== 0 &&
603 s
->access_logfile
->used
> 1 &&
604 s
->access_logfile
->ptr
[0] != '|') {
606 if (-1 != s
->log_access_fd
) close(s
->log_access_fd
);
608 if (-1 == (s
->log_access_fd
=
609 open(s
->access_logfile
->ptr
, O_APPEND
| O_WRONLY
| O_CREAT
| O_LARGEFILE
, 0644))) {
611 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "cycling access-log failed:", strerror(errno
));
613 return HANDLER_ERROR
;
615 fd_close_on_exec(s
->log_access_fd
);
619 return HANDLER_GO_ON
;
624 static int mod_accesslog_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
626 plugin_config
*s
= p
->config_storage
[0];
628 PATCH(access_logfile
);
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
);
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
];
645 /* condition didn't match */
646 if (!config_check_cond(srv
, con
, dc
)) continue;
649 for (j
= 0; j
< dc
->value
->used
; j
++) {
650 data_unset
*du
= dc
->value
->data
[j
];
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"))) {
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"))) {
665 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("accesslog.syslog-level"))) {
675 REQUESTDONE_FUNC(log_access_write
) {
676 plugin_data
*p
= p_d
;
683 mod_accesslog_patch_connection(srv
, con
, p
);
685 /* No output device, nothing to do */
686 if (!p
->conf
.use_syslog
&& p
->conf
.log_access_fd
== -1) return HANDLER_GO_ON
;
688 if (p
->conf
.use_syslog
) {
689 b
= p
->syslog_logbuffer
;
691 b
= p
->conf
.access_logbuffer
;
695 buffer_copy_string_len(b
, CONST_STR_LEN(""));
698 for (j
= 0; j
< p
->conf
.parsed_format
->used
; j
++) {
699 switch(p
->conf
.parsed_format
->ptr
[j
]->type
) {
701 buffer_append_string_buffer(b
, p
->conf
.parsed_format
->ptr
[j
]->string
);
704 switch(p
->conf
.parsed_format
->ptr
[j
]->field
) {
705 case FORMAT_TIMESTAMP
:
707 /* cache the generated timestamp */
708 if (srv
->cur_ts
!= *(p
->conf
.last_generated_accesslog_ts_ptr
)) {
710 #if defined(HAVE_STRUCT_TM_GMTOFF)
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;
724 if (p
->conf
.append_tz_offset
) {
725 buffer_append_string_len(p
->conf
.ts_accesslog_str
, tm
.tm_gmtoff
>= 0 ? "+" : "-", 1);
727 scd
= abs(tm
.tm_gmtoff
);
729 min
= (scd
% 3600) / 60;
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
);
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("]"));
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 */
749 *(p
->conf
.last_generated_accesslog_ts_ptr
) = srv
->cur_ts
;
753 buffer_append_string_buffer(b
, p
->conf
.ts_accesslog_str
);
756 case FORMAT_REMOTE_HOST
:
758 /* handle inet_ntop cache */
760 buffer_append_string(b
, inet_ntop_cache_get_ip(srv
, &(con
->dst_addr
)));
763 case FORMAT_REMOTE_IDENT
:
765 buffer_append_string_len(b
, CONST_STR_LEN("-"));
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
);
771 buffer_append_string_len(b
, CONST_STR_LEN("-"));
774 case FORMAT_REQUEST_LINE
:
775 if (con
->request
.request_line
->used
) {
776 accesslog_append_escaped(b
, con
->request
.request_line
);
780 buffer_append_long(b
, con
->http_status
);
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
);
788 buffer_append_string_len(b
, CONST_STR_LEN("-"));
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
);
795 buffer_append_string_len(b
, CONST_STR_LEN("-"));
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
);
802 buffer_append_string_len(b
, CONST_STR_LEN("-"));
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
);
809 buffer_append_string_len(b
, CONST_STR_LEN("-"));
812 case FORMAT_FILENAME
:
813 if (con
->physical
.path
->used
> 1) {
814 buffer_append_string_buffer(b
, con
->physical
.path
);
816 buffer_append_string_len(b
, CONST_STR_LEN("-"));
819 case FORMAT_BYTES_OUT
:
820 if (con
->bytes_written
> 0) {
821 buffer_append_off_t(b
, con
->bytes_written
);
823 buffer_append_string_len(b
, CONST_STR_LEN("-"));
826 case FORMAT_BYTES_IN
:
827 if (con
->bytes_read
> 0) {
828 buffer_append_off_t(b
, con
->bytes_read
);
830 buffer_append_string_len(b
, CONST_STR_LEN("-"));
833 case FORMAT_TIME_USED
:
834 buffer_append_long(b
, srv
->cur_ts
- con
->request_start
);
836 case FORMAT_SERVER_NAME
:
837 if (con
->server_name
->used
> 1) {
838 buffer_append_string_buffer(b
, con
->server_name
);
840 buffer_append_string_len(b
, CONST_STR_LEN("-"));
843 case FORMAT_HTTP_HOST
:
844 if (con
->uri
.authority
->used
> 1) {
845 accesslog_append_escaped(b
, con
->uri
.authority
);
847 buffer_append_string_len(b
, CONST_STR_LEN("-"));
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);
854 case FORMAT_REQUEST_METHOD
:
855 buffer_append_string(b
, get_http_method_name(con
->request
.http_method
));
858 buffer_append_string_len(b
, CONST_STR_LEN("%"));
860 case FORMAT_SERVER_PORT
:
863 buffer
*srvtoken
= ((server_socket
*)(con
->srv_socket
))->srv_token
;
864 if (srvtoken
->ptr
[0] == '[') {
865 colon
= strstr(srvtoken
->ptr
, "]:");
867 colon
= strchr(srvtoken
->ptr
, ':');
870 buffer_append_string(b
, colon
+1);
872 buffer_append_long(b
, srv
->srvconf
.port
);
876 case FORMAT_QUERY_STRING
:
877 accesslog_append_escaped(b
, con
->uri
.query
);
880 accesslog_append_escaped(b
, con
->uri
.path_raw
);
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;
890 { 'a', FORMAT_REMOTE_ADDR },
891 { 'A', FORMAT_LOCAL_ADDR },
892 { 'C', FORMAT_COOKIE },
893 { 'D', FORMAT_TIME_USED_MS },
904 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
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 */
909 b
->used
> BUFFER_MAX_REUSE_SIZE
) {
910 if (p
->conf
.use_syslog
) {
913 /* syslog appends a \n on its own */
914 syslog(p
->conf
.syslog_level
, "%*s", (int) b
->used
- 2, b
->ptr
);
917 } else if (p
->conf
.log_access_fd
!= -1) {
918 write(p
->conf
.log_access_fd
, b
->ptr
, b
->used
- 1);
923 return HANDLER_GO_ON
;
927 int 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");
932 p
->init
= mod_accesslog_init
;
933 p
->set_defaults
= log_access_open
;
934 p
->cleanup
= mod_accesslog_free
;
936 p
->handle_request_done
= log_access_write
;
937 p
->handle_sighup
= log_access_cycle
;