]> granicus.if.org Git - apache/blob - modules/loggers/mod_log_config.c
2213b6c8525b38ce7c05ebf3b2e8941bbd0aea4b
[apache] / modules / loggers / mod_log_config.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /*
60  * Modified by djm@va.pubnix.com:
61  * If no TransferLog is given explicitly, decline to log.
62  *
63  * This is module implements the TransferLog directive (same as the
64  * common log module), and additional directives, LogFormat and CustomLog.
65  *
66  *
67  * Syntax:
68  *
69  *    TransferLog fn      Logs transfers to fn in standard log format, unless
70  *                        a custom format is set with LogFormat
71  *    LogFormat format    Set a log format from TransferLog files
72  *    CustomLog fn format
73  *                        Log to file fn with format given by the format
74  *                        argument
75  *
76  *    CookieLog fn        For backwards compatability with old Cookie
77  *                        logging module - now deprecated.
78  *
79  * There can be any number of TransferLog and CustomLog
80  * commands. Each request will be logged to _ALL_ the
81  * named files, in the appropriate format.
82  *
83  * If no TransferLog or CustomLog directive appears in a VirtualHost,
84  * the request will be logged to the log file(s) defined outside
85  * the virtual host section. If a TransferLog or CustomLog directive
86  * appears in the VirtualHost section, the log files defined outside
87  * the VirtualHost will _not_ be used. This makes this module compatable
88  * with the CLF and config log modules, where the use of TransferLog
89  * inside the VirtualHost section overrides its use outside.
90  * 
91  * Examples:
92  *
93  *    TransferLog    logs/access_log
94  *    <VirtualHost>
95  *    LogFormat      "... custom format ..."
96  *    TransferLog    log/virtual_only
97  *    CustomLog      log/virtual_useragents "%t %{user-agent}i"
98  *    </VirtualHost>
99  *
100  * This will log using CLF to access_log any requests handled by the
101  * main server, while any requests to the virtual host will be logged
102  * with the "... custom format..." to virtual_only _AND_ using
103  * the custom user-agent log to virtual_useragents.
104  *
105  * Note that the NCSA referer and user-agent logs are easily added with
106  * CustomLog:
107  *   CustomLog   logs/referer  "%{referer}i -> %U"
108  *   CustomLog   logs/agent    "%{user-agent}i"
109  *
110  * RefererIgnore functionality can be obtained with conditional
111  * logging (SetEnvIf and CustomLog ... env=!VAR).
112  *
113  * But using this method allows much easier modification of the
114  * log format, e.g. to log hosts along with UA:
115  *   CustomLog   logs/referer "%{referer}i %U %h"
116  *
117  * The argument to LogFormat and CustomLog is a string, which can include
118  * literal characters copied into the log files, and '%' directives as
119  * follows:
120  *
121  * %...B:  bytes sent, excluding HTTP headers.
122  * %...b:  bytes sent, excluding HTTP headers in CLF format, i.e. a '-'
123  *         when no bytes where sent (rather than a '0'.
124  * %...{FOOBAR}C:  The contents of the HTTP cookie FOOBAR
125  * %...{FOOBAR}e:  The contents of the environment variable FOOBAR
126  * %...f:  filename
127  * %...h:  remote host
128  * %...a:  remote IP-address
129  * %...A:  local IP-address
130  * %...{Foobar}i:  The contents of Foobar: header line(s) in the request
131  *                 sent to the client.
132  * %...l:  remote logname (from identd, if supplied)
133  * %...{Foobar}n:  The contents of note "Foobar" from another module.
134  * %...{Foobar}o:  The contents of Foobar: header line(s) in the reply.
135  * %...p:  the port the request was served to
136  * %...P:  the process ID of the child that serviced the request.
137  * %...r:  first line of request
138  * %...s:  status.  For requests that got internally redirected, this
139  *         is status of the *original* request --- %...>s for the last.
140  * %...t:  time, in common log format time format
141  * %...{format}t:  The time, in the form given by format, which should
142  *                 be in strftime(3) format.
143  * %...T:  the time taken to serve the request, in seconds.
144  * %...D:  the time taken to serve the request, in micro seconds.
145  * %...u:  remote user (from auth; may be bogus if return status (%s) is 401)
146  * %...U:  the URL path requested.
147  * %...v:  the configured name of the server (i.e. which virtual host?)
148  * %...V:  the server name according to the UseCanonicalName setting
149  * %...m:  the request method
150  * %...H:  the request protocol
151  * %...q:  the query string prepended by "?", or empty if no query string
152  * %...X:  Status of the connection.
153  *         'X' = connection aborted before the response completed.
154  *         '+' = connection may be kept alive after the response is sent.
155  *         '-' = connection will be closed after the response is sent.
156            (This directive was %...c in late versions of Apache 1.3, but
157             this conflicted with the historical ssl %...{var}c syntax.)
158 *
159  * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can
160  * indicate conditions for inclusion of the item (which will cause it
161  * to be replaced with '-' if the condition is not met).  Note that
162  * there is no escaping performed on the strings from %r, %...i and
163  * %...o; some with long memories may remember that I thought this was
164  * a bad idea, once upon a time, and I'm still not comfortable with
165  * it, but it is difficult to see how to "do the right thing" with all
166  * of '%..i', unless we URL-escape everything and break with CLF.
167  *
168  * The forms of condition are a list of HTTP status codes, which may
169  * or may not be preceded by '!'.  Thus, '%400,501{User-agent}i' logs
170  * User-agent: on 400 errors and 501 errors (Bad Request, Not
171  * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all
172  * requests which did *not* return some sort of normal status.
173  *
174  * The default LogFormat reproduces CLF; see below.
175  *
176  * The way this is supposed to work with virtual hosts is as follows:
177  * a virtual host can have its own LogFormat, or its own TransferLog.
178  * If it doesn't have its own LogFormat, it inherits from the main
179  * server.  If it doesn't have its own TransferLog, it writes to the
180  * same descriptor (meaning the same process for "| ...").
181  *
182  * --- rst */
183
184 #include "apr_strings.h"
185 #include "apr_lib.h"
186 #include "apr_hash.h"
187 #include "apr_optional.h"
188
189 #define APR_WANT_STRFUNC
190 #include "apr_want.h"
191
192 #include "ap_config.h"
193 #include "mod_log_config.h"
194 #include "httpd.h"
195 #include "http_config.h"
196 #include "http_core.h"          /* For REMOTE_NAME */
197 #include "http_log.h"
198 #include "http_protocol.h"
199 #include "util_time.h"
200
201 #if APR_HAVE_UNISTD_H
202 #include <unistd.h>
203 #endif
204 #ifdef HAVE_LIMITS_H
205 #include <limits.h>
206 #endif
207
208 #define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %>s %b"
209
210 module AP_MODULE_DECLARE_DATA log_config_module;
211
212 static int xfer_flags = (APR_WRITE | APR_APPEND | APR_CREATE);
213 static apr_fileperms_t xfer_perms = APR_OS_DEFAULT;
214 static apr_hash_t *log_hash;
215
216 /* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is
217  * guaranteed to be atomic when writing a pipe.  And PIPE_BUF >= 512
218  * is guaranteed.  So we'll just guess 512 in the event the system
219  * doesn't have this.  Now, for file writes there is actually no limit,
220  * the entire write is atomic.  Whether all systems implement this
221  * correctly is another question entirely ... so we'll just use PIPE_BUF
222  * because it's probably a good guess as to what is implemented correctly
223  * everywhere.
224  */
225 #ifdef PIPE_BUF
226 #define LOG_BUFSIZE     PIPE_BUF
227 #else
228 #define LOG_BUFSIZE     (512)
229 #endif
230
231 /*
232  * multi_log_state is our per-(virtual)-server configuration. We store
233  * an array of the logs we are going to use, each of type config_log_state.
234  * If a default log format is given by LogFormat, store in default_format
235  * (backward compat. with mod_log_config).  We also store for each virtual
236  * server a pointer to the logs specified for the main server, so that if this
237  * vhost has no logs defined, we can use the main server's logs instead.
238  *
239  * So, for the main server, config_logs contains a list of the log files
240  * and server_config_logs is empty. For a vhost, server_config_logs
241  * points to the same array as config_logs in the main server, and
242  * config_logs points to the array of logs defined inside this vhost,
243  * which might be empty.
244  */
245
246 typedef struct {
247     const char *default_format_string;
248     apr_array_header_t *default_format;
249     apr_array_header_t *config_logs;
250     apr_array_header_t *server_config_logs;
251     apr_table_t *formats;
252 } multi_log_state;
253
254 /*
255  * config_log_state holds the status of a single log file. fname might
256  * be NULL, which means this module does no logging for this
257  * request. format might be NULL, in which case the default_format
258  * from the multi_log_state should be used, or if that is NULL as
259  * well, use the CLF. log_fd is NULL before the log file is opened and
260  * set to a valid fd after it is opened.
261  */
262
263 typedef struct {
264     const char *fname;
265     const char *format_string;
266     apr_array_header_t *format;
267     apr_file_t *log_fd;
268     char *condition_var;
269 #ifdef BUFFERED_LOGS
270     apr_size_t outcnt;
271     char outbuf[LOG_BUFSIZE];
272 #endif
273 } config_log_state;
274
275 /*
276  * Format items...
277  * Note that many of these could have ap_sprintfs replaced with static buffers.
278  */
279
280 typedef struct {
281     ap_log_handler_fn_t *func;
282     char *arg;
283     int condition_sense;
284     int want_orig;
285     apr_array_header_t *conditions;
286 } log_format_item;
287
288 static char *format_integer(apr_pool_t *p, int i)
289 {
290     return apr_itoa(p, i);
291 }
292
293 static char *pfmt(apr_pool_t *p, int i)
294 {
295     if (i <= 0) {
296         return "-";
297     }
298     else {
299         return format_integer(p, i);
300     }
301 }
302
303 static const char *constant_item(request_rec *dummy, char *stuff)
304 {
305     return stuff;
306 }
307
308 static const char *log_remote_host(request_rec *r, char *a)
309 {
310     return ap_get_remote_host(r->connection, r->per_dir_config,
311                                     REMOTE_NAME, NULL);
312 }
313
314 static const char *log_remote_address(request_rec *r, char *a)
315 {
316     return r->connection->remote_ip;
317 }
318
319 static const char *log_local_address(request_rec *r, char *a)
320 {
321     return r->connection->local_ip;
322 }
323
324 static const char *log_remote_logname(request_rec *r, char *a)
325 {
326     return ap_get_remote_logname(r);
327 }
328
329 static const char *log_remote_user(request_rec *r, char *a)
330 {
331     char *rvalue = r->user;
332
333     if (rvalue == NULL) {
334         rvalue = "-";
335     }
336     else if (strlen(rvalue) == 0) {
337         rvalue = "\"\"";
338     }
339     return rvalue;
340 }
341
342 static const char *log_request_line(request_rec *r, char *a)
343 {
344             /* NOTE: If the original request contained a password, we
345              * re-write the request line here to contain XXXXXX instead:
346              * (note the truncation before the protocol string for HTTP/0.9 requests)
347              * (note also that r->the_request contains the unmodified request)
348              */
349     return (r->parsed_uri.password) ? apr_pstrcat(r->pool, r->method, " ",
350                                          apr_uri_unparse(r->pool, &r->parsed_uri, 0),
351                                          r->assbackwards ? NULL : " ", r->protocol, NULL)
352                                         : r->the_request;
353 }
354
355 static const char *log_request_file(request_rec *r, char *a)
356 {
357     return r->filename;
358 }
359 static const char *log_request_uri(request_rec *r, char *a)
360 {
361     return r->uri;
362 }
363 static const char *log_request_method(request_rec *r, char *a)
364 {
365     return r->method;
366 }
367 static const char *log_request_protocol(request_rec *r, char *a)
368 {
369     return r->protocol;
370 }
371 static const char *log_request_query(request_rec *r, char *a)
372 {
373     return (r->args != NULL) ? apr_pstrcat(r->pool, "?", r->args, NULL)
374                              : "";
375 }
376 static const char *log_status(request_rec *r, char *a)
377 {
378     return pfmt(r->pool, r->status);
379 }
380
381 static const char *clf_log_bytes_sent(request_rec *r, char *a)
382 {
383     if (!r->sent_bodyct) {
384         return "-";
385     }
386     else {
387         return apr_off_t_toa(r->pool, r->bytes_sent);
388     }
389 }
390
391 static const char *log_bytes_sent(request_rec *r, char *a)
392 {
393     if (!r->sent_bodyct) {
394         return "0";
395     }
396     else {
397         return apr_psprintf(r->pool, "%" APR_OFF_T_FMT, r->bytes_sent);
398     }
399 }
400
401
402 static const char *log_header_in(request_rec *r, char *a)
403 {
404     return apr_table_get(r->headers_in, a);
405 }
406
407 static const char *log_header_out(request_rec *r, char *a)
408 {
409     const char *cp = apr_table_get(r->headers_out, a);
410     if (!strcasecmp(a, "Content-type") && r->content_type) {
411         cp = ap_field_noparam(r->pool, r->content_type);
412     }
413     if (cp) {
414         return cp;
415     }
416     return apr_table_get(r->err_headers_out, a);
417 }
418
419 static const char *log_note(request_rec *r, char *a)
420 {
421     return apr_table_get(r->notes, a);
422 }
423 static const char *log_env_var(request_rec *r, char *a)
424 {
425     return apr_table_get(r->subprocess_env, a);
426 }
427
428 static const char *log_cookie(request_rec *r, char *a)
429 {
430     const char *cookies;
431     const char *start_cookie;
432
433     if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
434         if ((start_cookie = ap_strstr_c(cookies,a))) {
435             char *cookie, *end_cookie;
436             start_cookie += strlen(a) + 1; /* cookie_name + '=' */
437             cookie = apr_pstrdup(r->pool, start_cookie);
438             /* kill everything in cookie after ';' */
439             end_cookie = strchr(cookie, ';'); 
440             if (end_cookie) {
441                 *end_cookie = '\0';
442             }
443             return cookie;
444         }
445     }
446     return NULL;
447 }
448
449 static const char *log_request_time(request_rec *r, char *a)
450 {
451     apr_exploded_time_t xt;
452     apr_size_t retcode;
453     char tstr[MAX_STRING_LEN];
454
455     /*
456         hi.  i think getting the time again at the end of the request
457         just for logging is dumb.  i know it's "required" for CLF.
458         folks writing log parsing tools don't realise that out of order
459         times have always been possible (consider what happens if one
460         process calculates the time to log, but then there's a context
461         switch before it writes and before that process is run again the
462         log rotation occurs) and they should just fix their tools rather
463         than force the server to pay extra cpu cycles.  if you've got
464         a problem with this, you can set the define.  -djg
465     */
466 #ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
467     ap_explode_recent_localtime(&xt, apr_time_now());
468 #else
469     ap_explode_recent_localtime(&xt, r->request_time);
470 #endif
471     if (a && *a) {              /* Custom format */
472         apr_strftime(tstr, &retcode, MAX_STRING_LEN, a, &xt);
473     }
474     else {                      /* CLF format */
475         char sign;
476         int timz;
477
478         timz = xt.tm_gmtoff;
479         if (timz < 0) {
480             timz = -timz;
481             sign = '-';
482         }
483         else {
484             sign = '+';
485         }
486
487         apr_snprintf(tstr, sizeof(tstr), "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]",
488                 xt.tm_mday, apr_month_snames[xt.tm_mon], xt.tm_year+1900,
489                 xt.tm_hour, xt.tm_min, xt.tm_sec,
490                 sign, timz / (60*60), timz % (60*60));
491     }
492
493     return apr_pstrdup(r->pool, tstr);
494 }
495
496 static const char *log_request_duration(request_rec *r, char *a)
497 {
498     return apr_psprintf(r->pool, "%qd", (apr_time_now() - r->request_time) 
499                                              / APR_USEC_PER_SEC);
500 }
501
502 static const char *log_request_duration_microseconds(request_rec *r, char *a)
503 {
504     return apr_psprintf(r->pool, "%qd", (apr_time_now() - r->request_time));
505 }
506
507 /* These next two routines use the canonical name:port so that log
508  * parsers don't need to duplicate all the vhost parsing crud.
509  */
510 static const char *log_virtual_host(request_rec *r, char *a)
511 {
512     return r->server->server_hostname;
513 }
514
515 static const char *log_server_port(request_rec *r, char *a)
516 {
517     return apr_psprintf(r->pool, "%u",
518         r->server->port ? r->server->port : ap_default_port(r));
519 }
520
521 /* This respects the setting of UseCanonicalName so that
522  * the dynamic mass virtual hosting trick works better.
523  */
524 static const char *log_server_name(request_rec *r, char *a)
525 {
526     return ap_get_server_name(r);
527 }
528
529 static const char *log_child_pid(request_rec *r, char *a)
530 {
531     return apr_psprintf(r->pool, "%ld", (long) getpid());
532 }
533
534 static const char *log_connection_status(request_rec *r, char *a)
535 {
536     if (r->connection->aborted)
537         return "X";
538
539     if (r->connection->keepalive && 
540         (!r->server->keep_alive_max ||
541          (r->server->keep_alive_max - r->connection->keepalives) > 0)) {
542         return "+";
543     }
544     return "-";
545 }
546
547 /*****************************************************************
548  *
549  * Parsing the log format string
550  */
551
552 static char *parse_log_misc_string(apr_pool_t *p, log_format_item *it,
553                                    const char **sa)
554 {
555     const char *s;
556     char *d;
557
558     it->func = constant_item;
559     it->conditions = NULL;
560
561     s = *sa;
562     while (*s && *s != '%') {
563         s++;
564     }
565     /*
566      * This might allocate a few chars extra if there's a backslash
567      * escape in the format string.
568      */
569     it->arg = apr_palloc(p, s - *sa + 1);
570
571     d = it->arg;
572     s = *sa;
573     while (*s && *s != '%') {
574         if (*s != '\\') {
575             *d++ = *s++;
576         }
577         else {
578             s++;
579             switch (*s) {
580             case '\\':
581                 *d++ = '\\';
582                 s++;
583                 break;
584             case 'r':
585                 *d++ = '\r';
586                 s++;
587                 break;
588             case 'n':
589                 *d++ = '\n';
590                 s++;
591                 break;
592             case 't':   
593                 *d++ = '\t';
594                 s++;
595                 break;
596             default:
597                 /* copy verbatim */
598                 *d++ = '\\';
599                 /*
600                  * Allow the loop to deal with this *s in the normal
601                  * fashion so that it handles end of string etc.
602                  * properly.
603                  */
604                 break;
605             }
606         }
607     }
608     *d = '\0';
609
610     *sa = s;
611     return NULL;
612 }
613
614 static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char **sa)
615 {
616     const char *s = *sa;
617     ap_log_handler *handler;
618
619     if (*s != '%') {
620         return parse_log_misc_string(p, it, sa);
621     }
622
623     ++s;
624     it->condition_sense = 0;
625     it->conditions = NULL;
626     it->want_orig = -1;
627     it->arg = "";               /* For safety's sake... */
628
629     while (*s) {
630         int i;
631
632         switch (*s) {
633         case '!':
634             ++s;
635             it->condition_sense = !it->condition_sense;
636             break;
637
638         case '<':
639             ++s;
640             it->want_orig = 1;
641             break;
642
643         case '>':
644             ++s;
645             it->want_orig = 0;
646             break;
647
648         case ',':
649             ++s;
650             break;
651
652         case '{':
653             ++s;
654             it->arg = ap_getword(p, &s, '}');
655             break;
656
657         case '0':
658         case '1':
659         case '2':
660         case '3':
661         case '4':
662         case '5':
663         case '6':
664         case '7':
665         case '8':
666         case '9':
667             i = *s - '0';
668             while (apr_isdigit(*++s)) {
669                 i = i * 10 + (*s) - '0';
670             }
671             if (!it->conditions) {
672                 it->conditions = apr_array_make(p, 4, sizeof(int));
673             }
674             *(int *) apr_array_push(it->conditions) = i;
675             break;
676
677         default:
678             handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1);
679             if (!handler) {
680                 char dummy[2];
681
682                 dummy[0] = s[-1];
683                 dummy[1] = '\0';
684                 return apr_pstrcat(p, "Unrecognized LogFormat directive %",
685                                dummy, NULL);
686             }
687             it->func = handler->func;
688             if (it->want_orig == -1) {
689                 it->want_orig = handler->want_orig_default;
690             }
691             *sa = s;
692             return NULL;
693         }
694     }
695
696     return "Ran off end of LogFormat parsing args to some directive";
697 }
698
699 static apr_array_header_t *parse_log_string(apr_pool_t *p, const char *s, const char **err)
700 {
701     apr_array_header_t *a = apr_array_make(p, 30, sizeof(log_format_item));
702     char *res;
703
704     while (*s) {
705         if ((res = parse_log_item(p, (log_format_item *) apr_array_push(a), &s))) {
706             *err = res;
707             return NULL;
708         }
709     }
710
711     s = APR_EOL_STR;
712     parse_log_item(p, (log_format_item *) apr_array_push(a), &s);
713     return a;
714 }
715
716 /*****************************************************************
717  *
718  * Actually logging.
719  */
720
721 static const char *process_item(request_rec *r, request_rec *orig,
722                           log_format_item *item)
723 {
724     const char *cp;
725
726     /* First, see if we need to process this thing at all... */
727
728     if (item->conditions && item->conditions->nelts != 0) {
729         int i;
730         int *conds = (int *) item->conditions->elts;
731         int in_list = 0;
732
733         for (i = 0; i < item->conditions->nelts; ++i) {
734             if (r->status == conds[i]) {
735                 in_list = 1;
736                 break;
737             }
738         }
739
740         if ((item->condition_sense && in_list)
741             || (!item->condition_sense && !in_list)) {
742             return "-";
743         }
744     }
745
746     /* We do.  Do it... */
747
748     cp = (*item->func) (item->want_orig ? orig : r, item->arg);
749     return cp ? cp : "-";
750 }
751
752 #ifdef BUFFERED_LOGS
753 static void flush_log(config_log_state *cls)
754 {
755     if (cls->outcnt && cls->log_fd != NULL) {
756         apr_file_write(cls->log_fd, cls->outbuf, &cls->outcnt);
757         cls->outcnt = 0;
758     }
759 }
760 #endif
761
762 static int config_log_transaction(request_rec *r, config_log_state *cls,
763                                   apr_array_header_t *default_format)
764 {
765     log_format_item *items;
766     char *str, *s;
767     const char **strs;
768     int *strl;
769     request_rec *orig;
770     int i;
771     apr_size_t len = 0;
772     apr_array_header_t *format;
773     char *envar;
774
775     if (cls->fname == NULL) {
776         return DECLINED;
777     }
778
779     /*
780      * See if we've got any conditional envariable-controlled logging decisions
781      * to make.
782      */
783     if (cls->condition_var != NULL) {
784         envar = cls->condition_var;
785         if (*envar != '!') {
786             if (apr_table_get(r->subprocess_env, envar) == NULL) {
787                 return DECLINED;
788             }
789         }
790         else {
791             if (apr_table_get(r->subprocess_env, &envar[1]) != NULL) {
792                 return DECLINED;
793             }
794         }
795     }
796
797     format = cls->format ? cls->format : default_format;
798
799     strs = apr_palloc(r->pool, sizeof(char *) * (format->nelts));
800     strl = apr_palloc(r->pool, sizeof(int) * (format->nelts));
801     items = (log_format_item *) format->elts;
802
803     orig = r;
804     while (orig->prev) {
805         orig = orig->prev;
806     }
807     while (r->next) {
808         r = r->next;
809     }
810
811     for (i = 0; i < format->nelts; ++i) {
812         strs[i] = process_item(r, orig, &items[i]);
813     }
814
815     for (i = 0; i < format->nelts; ++i) {
816         len += strl[i] = strlen(strs[i]);
817     }
818
819 #ifdef BUFFERED_LOGS
820     if (len + cls->outcnt > LOG_BUFSIZE) {
821         flush_log(cls);
822     }
823     if (len >= LOG_BUFSIZE) {
824         apr_size_t w;
825
826         str = apr_palloc(r->pool, len + 1);
827         for (i = 0, s = str; i < format->nelts; ++i) {
828             memcpy(s, strs[i], strl[i]);
829             s += strl[i];
830         }
831         w = len;
832         apr_file_write(cls->log_fd, str, &w);
833     }
834     else {
835         for (i = 0, s = &cls->outbuf[cls->outcnt]; i < format->nelts; ++i) {
836             memcpy(s, strs[i], strl[i]);
837             s += strl[i];
838         }
839         cls->outcnt += len;
840     }
841 #else
842     str = apr_palloc(r->pool, len + 1);
843
844     for (i = 0, s = str; i < format->nelts; ++i) {
845         memcpy(s, strs[i], strl[i]);
846         s += strl[i];
847     }
848
849     apr_file_write(cls->log_fd, str, &len);
850 #endif
851
852     return OK;
853 }
854
855 static int multi_log_transaction(request_rec *r)
856 {
857     multi_log_state *mls = ap_get_module_config(r->server->module_config,
858                                                 &log_config_module);
859     config_log_state *clsarray;
860     int i;
861
862     /*
863      * Log this transaction..
864      */
865     if (mls->config_logs->nelts) {
866         clsarray = (config_log_state *) mls->config_logs->elts;
867         for (i = 0; i < mls->config_logs->nelts; ++i) {
868             config_log_state *cls = &clsarray[i];
869
870             config_log_transaction(r, cls, mls->default_format);
871         }
872     }
873     else if (mls->server_config_logs) {
874         clsarray = (config_log_state *) mls->server_config_logs->elts;
875         for (i = 0; i < mls->server_config_logs->nelts; ++i) {
876             config_log_state *cls = &clsarray[i];
877
878             config_log_transaction(r, cls, mls->default_format);
879         }
880     }
881
882     return OK;
883 }
884
885 /*****************************************************************
886  *
887  * Module glue...
888  */
889
890 static void *make_config_log_state(apr_pool_t *p, server_rec *s)
891 {
892     multi_log_state *mls;
893
894     mls = (multi_log_state *) apr_palloc(p, sizeof(multi_log_state));
895     mls->config_logs = apr_array_make(p, 1, sizeof(config_log_state));
896     mls->default_format_string = NULL;
897     mls->default_format = NULL;
898     mls->server_config_logs = NULL;
899     mls->formats = apr_table_make(p, 4);
900     apr_table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
901
902     return mls;
903 }
904
905 /*
906  * Use the merger to simply add a pointer from the vhost log state
907  * to the log of logs specified for the non-vhost configuration.  Make sure
908  * vhosts inherit any globally-defined format names.
909  */
910
911 static void *merge_config_log_state(apr_pool_t *p, void *basev, void *addv)
912 {
913     multi_log_state *base = (multi_log_state *) basev;
914     multi_log_state *add = (multi_log_state *) addv;
915
916     add->server_config_logs = base->config_logs;
917     if (!add->default_format) {
918         add->default_format_string = base->default_format_string;
919         add->default_format = base->default_format;
920     }
921     add->formats = apr_table_overlay(p, base->formats, add->formats);
922
923     return add;
924 }
925
926 /*
927  * Set the default logfile format, or define a nickname for a format string.
928  */
929 static const char *log_format(cmd_parms *cmd, void *dummy, const char *fmt,
930                               const char *name)
931 {
932     const char *err_string = NULL;
933     multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
934                                                 &log_config_module);
935
936     /*
937      * If we were given two arguments, the second is a name to be given to the
938      * format.  This syntax just defines the nickname - it doesn't actually
939      * make the format the default.
940      */
941     if (name != NULL) {
942         parse_log_string(cmd->pool, fmt, &err_string);
943         if (err_string == NULL) {
944             apr_table_setn(mls->formats, name, fmt);
945         }
946     }
947     else {
948         mls->default_format_string = fmt;
949         mls->default_format = parse_log_string(cmd->pool, fmt, &err_string);
950     }
951     return err_string;
952 }
953
954
955 static const char *add_custom_log(cmd_parms *cmd, void *dummy, const char *fn,
956                                   const char *fmt, const char *envclause)
957 {
958     const char *err_string = NULL;
959     multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
960                                                 &log_config_module);
961     config_log_state *cls;
962
963     cls = (config_log_state *) apr_array_push(mls->config_logs);
964     cls->condition_var = NULL;
965     if (envclause != NULL) {
966         if (strncasecmp(envclause, "env=", 4) != 0) {
967             return "error in condition clause";
968         }
969         if ((envclause[4] == '\0')
970             || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
971             return "missing environment variable name";
972         }
973         cls->condition_var = apr_pstrdup(cmd->pool, &envclause[4]);
974     }
975
976     cls->fname = fn;
977     cls->format_string = fmt;
978     if (fmt == NULL) {
979         cls->format = NULL;
980     }
981     else {
982         cls->format = parse_log_string(cmd->pool, fmt, &err_string);
983     }
984     cls->log_fd = NULL;
985
986     return err_string;
987 }
988
989 static const char *set_transfer_log(cmd_parms *cmd, void *dummy,
990                                     const char *fn)
991 {
992     return add_custom_log(cmd, dummy, fn, NULL, NULL);
993 }
994
995 static const char *set_cookie_log(cmd_parms *cmd, void *dummy, const char *fn)
996 {
997     return add_custom_log(cmd, dummy, fn, "%{Cookie}n \"%r\" %t", NULL);
998 }
999
1000 static const command_rec config_log_cmds[] =
1001 {
1002 AP_INIT_TAKE23("CustomLog", add_custom_log, NULL, RSRC_CONF,
1003      "a file name, a custom log format string or format name, "
1004      "and an optional \"env=\" clause (see docs)"),
1005 AP_INIT_TAKE1("TransferLog", set_transfer_log, NULL, RSRC_CONF,
1006      "the filename of the access log"),
1007 AP_INIT_TAKE12("LogFormat", log_format, NULL, RSRC_CONF,
1008      "a log format string (see docs) and an optional format name"),
1009 AP_INIT_TAKE1("CookieLog", set_cookie_log, NULL, RSRC_CONF,
1010      "the filename of the cookie log"),
1011     {NULL}
1012 };
1013
1014 static config_log_state *open_config_log(server_rec *s, apr_pool_t *p,
1015                                          config_log_state *cls,
1016                                          apr_array_header_t *default_format)
1017 {
1018     apr_status_t status;
1019     void *data;
1020     const char *userdata_key = "open_config_log";
1021
1022     /* Skip opening the log the first time through. It's really
1023      * good to avoid starting the piped log process during preflight.
1024      */
1025     apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1026     if (!data) {
1027         apr_pool_userdata_setn((const void *)1, userdata_key,
1028                                NULL, s->process->pool);
1029         /* If logging for the first time after a restart, keep going. */
1030         if (!ap_my_generation) {
1031             return cls;
1032         }
1033     }
1034
1035     if (cls->log_fd != NULL) {
1036         return cls;             /* virtual config shared w/main server */
1037     }
1038
1039     if (cls->fname == NULL) {
1040         return cls;             /* Leave it NULL to decline.  */
1041     }
1042
1043     if (*cls->fname == '|') {
1044         piped_log *pl;
1045
1046         pl = ap_open_piped_log(p, cls->fname + 1);
1047         if (pl == NULL) {
1048             exit(1);
1049         }
1050         cls->log_fd = ap_piped_log_write_fd(pl);
1051     }
1052     else {
1053         const char *fname = ap_server_root_relative(p, cls->fname);
1054         if ((status = apr_file_open(&cls->log_fd, fname, xfer_flags, xfer_perms, p)) 
1055             != APR_SUCCESS) {
1056             ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
1057                          "could not open transfer log file %s.", fname);
1058             exit(1);
1059         }
1060         apr_file_set_inherit(cls->log_fd);
1061     }
1062 #ifdef BUFFERED_LOGS
1063     cls->outcnt = 0;
1064 #endif
1065
1066     return cls;
1067 }
1068
1069 static config_log_state *open_multi_logs(server_rec *s, apr_pool_t *p)
1070 {
1071     int i;
1072     multi_log_state *mls = ap_get_module_config(s->module_config,
1073                                              &log_config_module);
1074     config_log_state *clsarray;
1075     const char *dummy;
1076     const char *format;
1077
1078     if (mls->default_format_string) {
1079         format = apr_table_get(mls->formats, mls->default_format_string);
1080         if (format) {
1081             mls->default_format = parse_log_string(p, format, &dummy);
1082         }
1083     }    
1084
1085     if (!mls->default_format) {
1086         mls->default_format = parse_log_string(p, DEFAULT_LOG_FORMAT, &dummy);
1087     }
1088
1089     if (mls->config_logs->nelts) {
1090         clsarray = (config_log_state *) mls->config_logs->elts;
1091         for (i = 0; i < mls->config_logs->nelts; ++i) {
1092             config_log_state *cls = &clsarray[i];
1093
1094             if (cls->format_string) {
1095                 format = apr_table_get(mls->formats, cls->format_string);
1096                 if (format) {
1097                     cls->format = parse_log_string(p, format, &dummy);
1098                 }
1099             }
1100
1101             cls = open_config_log(s, p, cls, mls->default_format);
1102         }
1103     }
1104     else if (mls->server_config_logs) {
1105         clsarray = (config_log_state *) mls->server_config_logs->elts;
1106         for (i = 0; i < mls->server_config_logs->nelts; ++i) {
1107             config_log_state *cls = &clsarray[i];
1108
1109             if (cls->format_string) {
1110                 format = apr_table_get(mls->formats, cls->format_string);
1111                 if (format) {
1112                     cls->format = parse_log_string(p, format, &dummy);
1113                 }
1114             }
1115
1116             cls = open_config_log(s, p, cls, mls->default_format);
1117         }
1118     }
1119
1120     return NULL;
1121 }
1122
1123 #ifdef BUFFERED_LOGS
1124 static apr_status_t flush_all_logs(void *data)
1125 {
1126     server_rec *s = data;
1127     multi_log_state *mls;
1128     apr_array_header_t *log_list;
1129     config_log_state *clsarray;
1130     int i;
1131
1132     for (; s; s = s->next) {
1133         mls = ap_get_module_config(s->module_config, &log_config_module);
1134         log_list = NULL;
1135         if (mls->config_logs->nelts) {
1136             log_list = mls->config_logs;
1137         }
1138         else if (mls->server_config_logs) {
1139             log_list = mls->server_config_logs;
1140         }
1141         if (log_list) {
1142             clsarray = (config_log_state *) log_list->elts;
1143             for (i = 0; i < log_list->nelts; ++i) {
1144                 flush_log(&clsarray[i]);
1145             }
1146         }
1147     }
1148     return APR_SUCCESS;
1149 }
1150 #endif
1151
1152 static int init_config_log(apr_pool_t *pc, apr_pool_t *p, apr_pool_t *pt, server_rec *s)
1153 {
1154     /* First, do "physical" server, which gets default log fd and format
1155      * for the virtual servers, if they don't override...
1156      */
1157
1158     open_multi_logs(s, p);
1159
1160     /* Then, virtual servers */
1161
1162     for (s = s->next; s; s = s->next) {
1163         open_multi_logs(s, p);
1164     }
1165     return OK;
1166 }
1167
1168 static void init_child(apr_pool_t *p, server_rec *s)
1169 {
1170 #ifdef BUFFERED_LOGS
1171         /* Now register the last buffer flush with the cleanup engine */
1172         apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs);
1173 #endif
1174 }
1175
1176 static void ap_register_log_handler(apr_pool_t *p, char *tag, 
1177                                     ap_log_handler_fn_t *handler, int def)
1178 {
1179     ap_log_handler *log_struct = apr_palloc(p, sizeof(*log_struct));
1180     log_struct->func = handler;
1181     log_struct->want_orig_default = def;
1182
1183     apr_hash_set(log_hash, tag, 1, (const void *)log_struct);
1184 }
1185
1186 static void log_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
1187 {
1188     static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
1189
1190     log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
1191
1192     if (log_pfn_register) {
1193         log_pfn_register(p, "h", log_remote_host, 0);
1194         log_pfn_register(p, "a", log_remote_address, 0 );
1195         log_pfn_register(p, "A", log_local_address, 0 );
1196         log_pfn_register(p, "l", log_remote_logname, 0);
1197         log_pfn_register(p, "u", log_remote_user, 0);
1198         log_pfn_register(p, "t", log_request_time, 0);
1199         log_pfn_register(p, "f", log_request_file, 0);
1200         log_pfn_register(p, "b", clf_log_bytes_sent, 0);
1201         log_pfn_register(p, "B", log_bytes_sent, 0);
1202         log_pfn_register(p, "i", log_header_in, 0);
1203         log_pfn_register(p, "o", log_header_out, 0);
1204         log_pfn_register(p, "n", log_note, 0);
1205         log_pfn_register(p, "e", log_env_var, 0);
1206         log_pfn_register(p, "V", log_server_name, 0);
1207         log_pfn_register(p, "v", log_virtual_host, 0);
1208         log_pfn_register(p, "p", log_server_port, 0);
1209         log_pfn_register(p, "P", log_child_pid, 0);
1210         log_pfn_register(p, "H", log_request_protocol, 0);
1211         log_pfn_register(p, "m", log_request_method, 0);
1212         log_pfn_register(p, "q", log_request_query, 0);
1213         log_pfn_register(p, "X", log_connection_status, 0);
1214         log_pfn_register(p, "C", log_cookie, 0);
1215         log_pfn_register(p, "r", log_request_line, 1);
1216         log_pfn_register(p, "D", log_request_duration_microseconds, 1);
1217         log_pfn_register(p, "T", log_request_duration, 1);
1218         log_pfn_register(p, "U", log_request_uri, 1);
1219         log_pfn_register(p, "s", log_status, 1);
1220     }
1221 }
1222
1223 static void register_hooks(apr_pool_t *p)
1224 {
1225     ap_hook_pre_config(log_pre_config,NULL,NULL,APR_HOOK_REALLY_FIRST);
1226     ap_hook_child_init(init_child,NULL,NULL,APR_HOOK_MIDDLE);
1227     ap_hook_open_logs(init_config_log,NULL,NULL,APR_HOOK_MIDDLE);
1228     ap_hook_log_transaction(multi_log_transaction,NULL,NULL,APR_HOOK_MIDDLE);
1229
1230     /* Init log_hash before we register the optional function. It is 
1231      * possible for the optional function, ap_register_log_handler,
1232      * to be called before any other mod_log_config hooks are called.
1233      * As a policy, we should init everything required by an optional function
1234      * before calling APR_REGISTER_OPTIONAL_FN.
1235      */ 
1236     log_hash = apr_hash_make(p);
1237     APR_REGISTER_OPTIONAL_FN(ap_register_log_handler);
1238 }
1239
1240 module AP_MODULE_DECLARE_DATA log_config_module =
1241 {
1242     STANDARD20_MODULE_STUFF,
1243     NULL,                       /* create per-dir config */
1244     NULL,                       /* merge per-dir config */
1245     make_config_log_state,      /* server config */
1246     merge_config_log_state,     /* merge server config */
1247     config_log_cmds,            /* command apr_table_t */
1248     register_hooks              /* register hooks */
1249 };