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