]> granicus.if.org Git - apache/blob - modules/loggers/mod_log_config.c
562ca457932a33fb1219c7477163ac1fd1d388e2
[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 - r->connection->keepalives) > 0)) {
538         return "+";
539     }
540     return "-";
541 }
542
543 /*****************************************************************
544  *
545  * Parsing the log format string
546  */
547
548 static char *parse_log_misc_string(apr_pool_t *p, log_format_item *it,
549                                    const char **sa)
550 {
551     const char *s;
552     char *d;
553
554     it->func = constant_item;
555     it->conditions = NULL;
556
557     s = *sa;
558     while (*s && *s != '%') {
559         s++;
560     }
561     /*
562      * This might allocate a few chars extra if there's a backslash
563      * escape in the format string.
564      */
565     it->arg = apr_palloc(p, s - *sa + 1);
566
567     d = it->arg;
568     s = *sa;
569     while (*s && *s != '%') {
570         if (*s != '\\') {
571             *d++ = *s++;
572         }
573         else {
574             s++;
575             switch (*s) {
576             case '\\':
577                 *d++ = '\\';
578                 s++;
579                 break;
580             case 'r':
581                 *d++ = '\r';
582                 s++;
583                 break;
584             case 'n':
585                 *d++ = '\n';
586                 s++;
587                 break;
588             case 't':   
589                 *d++ = '\t';
590                 s++;
591                 break;
592             default:
593                 /* copy verbatim */
594                 *d++ = '\\';
595                 /*
596                  * Allow the loop to deal with this *s in the normal
597                  * fashion so that it handles end of string etc.
598                  * properly.
599                  */
600                 break;
601             }
602         }
603     }
604     *d = '\0';
605
606     *sa = s;
607     return NULL;
608 }
609
610 static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char **sa)
611 {
612     const char *s = *sa;
613     ap_log_handler *handler;
614
615     if (*s != '%') {
616         return parse_log_misc_string(p, it, sa);
617     }
618
619     ++s;
620     it->condition_sense = 0;
621     it->conditions = NULL;
622     it->want_orig = -1;
623     it->arg = "";               /* For safety's sake... */
624
625     while (*s) {
626         int i;
627
628         switch (*s) {
629         case '!':
630             ++s;
631             it->condition_sense = !it->condition_sense;
632             break;
633
634         case '<':
635             ++s;
636             it->want_orig = 1;
637             break;
638
639         case '>':
640             ++s;
641             it->want_orig = 0;
642             break;
643
644         case ',':
645             ++s;
646             break;
647
648         case '{':
649             ++s;
650             it->arg = ap_getword(p, &s, '}');
651             break;
652
653         case '0':
654         case '1':
655         case '2':
656         case '3':
657         case '4':
658         case '5':
659         case '6':
660         case '7':
661         case '8':
662         case '9':
663             i = *s - '0';
664             while (apr_isdigit(*++s)) {
665                 i = i * 10 + (*s) - '0';
666             }
667             if (!it->conditions) {
668                 it->conditions = apr_array_make(p, 4, sizeof(int));
669             }
670             *(int *) apr_array_push(it->conditions) = i;
671             break;
672
673         default:
674             handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1);
675             if (!handler) {
676                 char dummy[2];
677
678                 dummy[0] = s[-1];
679                 dummy[1] = '\0';
680                 return apr_pstrcat(p, "Unrecognized LogFormat directive %",
681                                dummy, NULL);
682             }
683             it->func = handler->func;
684             if (it->want_orig == -1) {
685                 it->want_orig = handler->want_orig_default;
686             }
687             *sa = s;
688             return NULL;
689         }
690     }
691
692     return "Ran off end of LogFormat parsing args to some directive";
693 }
694
695 static apr_array_header_t *parse_log_string(apr_pool_t *p, const char *s, const char **err)
696 {
697     apr_array_header_t *a = apr_array_make(p, 30, sizeof(log_format_item));
698     char *res;
699
700     while (*s) {
701         if ((res = parse_log_item(p, (log_format_item *) apr_array_push(a), &s))) {
702             *err = res;
703             return NULL;
704         }
705     }
706
707     s = APR_EOL_STR;
708     parse_log_item(p, (log_format_item *) apr_array_push(a), &s);
709     return a;
710 }
711
712 /*****************************************************************
713  *
714  * Actually logging.
715  */
716
717 static const char *process_item(request_rec *r, request_rec *orig,
718                           log_format_item *item)
719 {
720     const char *cp;
721
722     /* First, see if we need to process this thing at all... */
723
724     if (item->conditions && item->conditions->nelts != 0) {
725         int i;
726         int *conds = (int *) item->conditions->elts;
727         int in_list = 0;
728
729         for (i = 0; i < item->conditions->nelts; ++i) {
730             if (r->status == conds[i]) {
731                 in_list = 1;
732                 break;
733             }
734         }
735
736         if ((item->condition_sense && in_list)
737             || (!item->condition_sense && !in_list)) {
738             return "-";
739         }
740     }
741
742     /* We do.  Do it... */
743
744     cp = (*item->func) (item->want_orig ? orig : r, item->arg);
745     return cp ? cp : "-";
746 }
747
748 #ifdef BUFFERED_LOGS
749 static void flush_log(config_log_state *cls)
750 {
751     if (cls->outcnt && cls->log_fd != NULL) {
752         apr_file_write(cls->log_fd, cls->outbuf, &cls->outcnt);
753         cls->outcnt = 0;
754     }
755 }
756 #endif
757
758 static int config_log_transaction(request_rec *r, config_log_state *cls,
759                                   apr_array_header_t *default_format)
760 {
761     log_format_item *items;
762     char *str, *s;
763     const char **strs;
764     int *strl;
765     request_rec *orig;
766     int i;
767     apr_size_t len = 0;
768     apr_array_header_t *format;
769     char *envar;
770
771     if (cls->fname == NULL) {
772         return DECLINED;
773     }
774
775     /*
776      * See if we've got any conditional envariable-controlled logging decisions
777      * to make.
778      */
779     if (cls->condition_var != NULL) {
780         envar = cls->condition_var;
781         if (*envar != '!') {
782             if (apr_table_get(r->subprocess_env, envar) == NULL) {
783                 return DECLINED;
784             }
785         }
786         else {
787             if (apr_table_get(r->subprocess_env, &envar[1]) != NULL) {
788                 return DECLINED;
789             }
790         }
791     }
792
793     format = cls->format ? cls->format : default_format;
794
795     strs = apr_palloc(r->pool, sizeof(char *) * (format->nelts));
796     strl = apr_palloc(r->pool, sizeof(int) * (format->nelts));
797     items = (log_format_item *) format->elts;
798
799     orig = r;
800     while (orig->prev) {
801         orig = orig->prev;
802     }
803     while (r->next) {
804         r = r->next;
805     }
806
807     for (i = 0; i < format->nelts; ++i) {
808         strs[i] = process_item(r, orig, &items[i]);
809     }
810
811     for (i = 0; i < format->nelts; ++i) {
812         len += strl[i] = strlen(strs[i]);
813     }
814
815 #ifdef BUFFERED_LOGS
816     if (len + cls->outcnt > LOG_BUFSIZE) {
817         flush_log(cls);
818     }
819     if (len >= LOG_BUFSIZE) {
820         apr_size_t w;
821
822         str = apr_palloc(r->pool, len + 1);
823         for (i = 0, s = str; i < format->nelts; ++i) {
824             memcpy(s, strs[i], strl[i]);
825             s += strl[i];
826         }
827         w = len;
828         apr_file_write(cls->log_fd, str, &w);
829     }
830     else {
831         for (i = 0, s = &cls->outbuf[cls->outcnt]; i < format->nelts; ++i) {
832             memcpy(s, strs[i], strl[i]);
833             s += strl[i];
834         }
835         cls->outcnt += len;
836     }
837 #else
838     str = apr_palloc(r->pool, len + 1);
839
840     for (i = 0, s = str; i < format->nelts; ++i) {
841         memcpy(s, strs[i], strl[i]);
842         s += strl[i];
843     }
844
845     apr_file_write(cls->log_fd, str, &len);
846 #endif
847
848     return OK;
849 }
850
851 static int multi_log_transaction(request_rec *r)
852 {
853     multi_log_state *mls = ap_get_module_config(r->server->module_config,
854                                                 &log_config_module);
855     config_log_state *clsarray;
856     int i;
857
858     /*
859      * Log this transaction..
860      */
861     if (mls->config_logs->nelts) {
862         clsarray = (config_log_state *) mls->config_logs->elts;
863         for (i = 0; i < mls->config_logs->nelts; ++i) {
864             config_log_state *cls = &clsarray[i];
865
866             config_log_transaction(r, cls, mls->default_format);
867         }
868     }
869     else if (mls->server_config_logs) {
870         clsarray = (config_log_state *) mls->server_config_logs->elts;
871         for (i = 0; i < mls->server_config_logs->nelts; ++i) {
872             config_log_state *cls = &clsarray[i];
873
874             config_log_transaction(r, cls, mls->default_format);
875         }
876     }
877
878     return OK;
879 }
880
881 /*****************************************************************
882  *
883  * Module glue...
884  */
885
886 static void *make_config_log_state(apr_pool_t *p, server_rec *s)
887 {
888     multi_log_state *mls;
889
890     mls = (multi_log_state *) apr_palloc(p, sizeof(multi_log_state));
891     mls->config_logs = apr_array_make(p, 1, sizeof(config_log_state));
892     mls->default_format_string = NULL;
893     mls->default_format = NULL;
894     mls->server_config_logs = NULL;
895     mls->formats = apr_table_make(p, 4);
896     apr_table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
897
898     return mls;
899 }
900
901 /*
902  * Use the merger to simply add a pointer from the vhost log state
903  * to the log of logs specified for the non-vhost configuration.  Make sure
904  * vhosts inherit any globally-defined format names.
905  */
906
907 static void *merge_config_log_state(apr_pool_t *p, void *basev, void *addv)
908 {
909     multi_log_state *base = (multi_log_state *) basev;
910     multi_log_state *add = (multi_log_state *) addv;
911
912     add->server_config_logs = base->config_logs;
913     if (!add->default_format) {
914         add->default_format_string = base->default_format_string;
915         add->default_format = base->default_format;
916     }
917     add->formats = apr_table_overlay(p, base->formats, add->formats);
918
919     return add;
920 }
921
922 /*
923  * Set the default logfile format, or define a nickname for a format string.
924  */
925 static const char *log_format(cmd_parms *cmd, void *dummy, const char *fmt,
926                               const char *name)
927 {
928     const char *err_string = NULL;
929     multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
930                                                 &log_config_module);
931
932     /*
933      * If we were given two arguments, the second is a name to be given to the
934      * format.  This syntax just defines the nickname - it doesn't actually
935      * make the format the default.
936      */
937     if (name != NULL) {
938         parse_log_string(cmd->pool, fmt, &err_string);
939         if (err_string == NULL) {
940             apr_table_setn(mls->formats, name, fmt);
941         }
942     }
943     else {
944         mls->default_format_string = fmt;
945         mls->default_format = parse_log_string(cmd->pool, fmt, &err_string);
946     }
947     return err_string;
948 }
949
950
951 static const char *add_custom_log(cmd_parms *cmd, void *dummy, const char *fn,
952                                   const char *fmt, const char *envclause)
953 {
954     const char *err_string = NULL;
955     multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
956                                                 &log_config_module);
957     config_log_state *cls;
958
959     cls = (config_log_state *) apr_array_push(mls->config_logs);
960     cls->condition_var = NULL;
961     if (envclause != NULL) {
962         if (strncasecmp(envclause, "env=", 4) != 0) {
963             return "error in condition clause";
964         }
965         if ((envclause[4] == '\0')
966             || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
967             return "missing environment variable name";
968         }
969         cls->condition_var = apr_pstrdup(cmd->pool, &envclause[4]);
970     }
971
972     cls->fname = fn;
973     cls->format_string = fmt;
974     if (fmt == NULL) {
975         cls->format = NULL;
976     }
977     else {
978         cls->format = parse_log_string(cmd->pool, fmt, &err_string);
979     }
980     cls->log_fd = NULL;
981
982     return err_string;
983 }
984
985 static const char *set_transfer_log(cmd_parms *cmd, void *dummy,
986                                     const char *fn)
987 {
988     return add_custom_log(cmd, dummy, fn, NULL, NULL);
989 }
990
991 static const char *set_cookie_log(cmd_parms *cmd, void *dummy, const char *fn)
992 {
993     return add_custom_log(cmd, dummy, fn, "%{Cookie}n \"%r\" %t", NULL);
994 }
995
996 static const command_rec config_log_cmds[] =
997 {
998 AP_INIT_TAKE23("CustomLog", add_custom_log, NULL, RSRC_CONF,
999      "a file name, a custom log format string or format name, "
1000      "and an optional \"env=\" clause (see docs)"),
1001 AP_INIT_TAKE1("TransferLog", set_transfer_log, NULL, RSRC_CONF,
1002      "the filename of the access log"),
1003 AP_INIT_TAKE12("LogFormat", log_format, NULL, RSRC_CONF,
1004      "a log format string (see docs) and an optional format name"),
1005 AP_INIT_TAKE1("CookieLog", set_cookie_log, NULL, RSRC_CONF,
1006      "the filename of the cookie log"),
1007     {NULL}
1008 };
1009
1010 static config_log_state *open_config_log(server_rec *s, apr_pool_t *p,
1011                                          config_log_state *cls,
1012                                          apr_array_header_t *default_format)
1013 {
1014     apr_status_t status;
1015
1016     if (cls->log_fd != NULL) {
1017         return cls;             /* virtual config shared w/main server */
1018     }
1019
1020     if (cls->fname == NULL) {
1021         return cls;             /* Leave it NULL to decline.  */
1022     }
1023
1024     if (*cls->fname == '|') {
1025         piped_log *pl;
1026
1027         pl = ap_open_piped_log(p, cls->fname + 1);
1028         if (pl == NULL) {
1029             exit(1);
1030         }
1031         cls->log_fd = ap_piped_log_write_fd(pl);
1032     }
1033     else {
1034         const char *fname = ap_server_root_relative(p, cls->fname);
1035         if ((status = apr_file_open(&cls->log_fd, fname, xfer_flags, xfer_perms, p)) 
1036             != APR_SUCCESS) {
1037             ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
1038                          "could not open transfer log file %s.", fname);
1039             exit(1);
1040         }
1041     }
1042 #ifdef BUFFERED_LOGS
1043     cls->outcnt = 0;
1044 #endif
1045
1046     return cls;
1047 }
1048
1049 static config_log_state *open_multi_logs(server_rec *s, apr_pool_t *p)
1050 {
1051     int i;
1052     multi_log_state *mls = ap_get_module_config(s->module_config,
1053                                              &log_config_module);
1054     config_log_state *clsarray;
1055     const char *dummy;
1056     const char *format;
1057
1058     if (mls->default_format_string) {
1059         format = apr_table_get(mls->formats, mls->default_format_string);
1060         if (format) {
1061             mls->default_format = parse_log_string(p, format, &dummy);
1062         }
1063     }    
1064
1065     if (!mls->default_format) {
1066         mls->default_format = parse_log_string(p, DEFAULT_LOG_FORMAT, &dummy);
1067     }
1068
1069     if (mls->config_logs->nelts) {
1070         clsarray = (config_log_state *) mls->config_logs->elts;
1071         for (i = 0; i < mls->config_logs->nelts; ++i) {
1072             config_log_state *cls = &clsarray[i];
1073
1074             if (cls->format_string) {
1075                 format = apr_table_get(mls->formats, cls->format_string);
1076                 if (format) {
1077                     cls->format = parse_log_string(p, format, &dummy);
1078                 }
1079             }
1080
1081             cls = open_config_log(s, p, cls, mls->default_format);
1082         }
1083     }
1084     else if (mls->server_config_logs) {
1085         clsarray = (config_log_state *) mls->server_config_logs->elts;
1086         for (i = 0; i < mls->server_config_logs->nelts; ++i) {
1087             config_log_state *cls = &clsarray[i];
1088
1089             if (cls->format_string) {
1090                 format = apr_table_get(mls->formats, cls->format_string);
1091                 if (format) {
1092                     cls->format = parse_log_string(p, format, &dummy);
1093                 }
1094             }
1095
1096             cls = open_config_log(s, p, cls, mls->default_format);
1097         }
1098     }
1099
1100     return NULL;
1101 }
1102
1103 #ifdef BUFFERED_LOGS
1104 static apr_status_t flush_all_logs(void *data)
1105 {
1106     server_rec *s = data;
1107     multi_log_state *mls;
1108     apr_array_header_t *log_list;
1109     config_log_state *clsarray;
1110     int i;
1111
1112     for (; s; s = s->next) {
1113         mls = ap_get_module_config(s->module_config, &log_config_module);
1114         log_list = NULL;
1115         if (mls->config_logs->nelts) {
1116             log_list = mls->config_logs;
1117         }
1118         else if (mls->server_config_logs) {
1119             log_list = mls->server_config_logs;
1120         }
1121         if (log_list) {
1122             clsarray = (config_log_state *) log_list->elts;
1123             for (i = 0; i < log_list->nelts; ++i) {
1124                 flush_log(&clsarray[i]);
1125             }
1126         }
1127     }
1128     return APR_SUCCESS;
1129 }
1130 #endif
1131
1132 static void init_config_log(apr_pool_t *pc, apr_pool_t *p, apr_pool_t *pt, server_rec *s)
1133 {
1134     /* First, do "physical" server, which gets default log fd and format
1135      * for the virtual servers, if they don't override...
1136      */
1137
1138     open_multi_logs(s, p);
1139
1140     /* Then, virtual servers */
1141
1142     for (s = s->next; s; s = s->next) {
1143         open_multi_logs(s, p);
1144     }
1145 }
1146
1147 static void init_child(apr_pool_t *p, server_rec *s)
1148 {
1149 #ifdef BUFFERED_LOGS
1150         /* Now register the last buffer flush with the cleanup engine */
1151         apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs);
1152 #endif
1153 }
1154
1155 static void ap_register_log_handler(apr_pool_t *p, char *tag, 
1156                                     ap_log_handler_fn_t *handler, int def)
1157 {
1158     ap_log_handler *log_struct = apr_palloc(p, sizeof(*log_struct));
1159     log_struct->func = handler;
1160     log_struct->want_orig_default = def;
1161
1162     apr_hash_set(log_hash, tag, 1, (const void *)log_struct);
1163 }
1164
1165 static void log_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
1166 {
1167     static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
1168
1169     log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
1170
1171     if (log_pfn_register) {
1172         log_pfn_register(p, "h", log_remote_host, 0);
1173         log_pfn_register(p, "a", log_remote_address, 0 );
1174         log_pfn_register(p, "A", log_local_address, 0 );
1175         log_pfn_register(p, "l", log_remote_logname, 0);
1176         log_pfn_register(p, "u", log_remote_user, 0);
1177         log_pfn_register(p, "t", log_request_time, 0);
1178         log_pfn_register(p, "f", log_request_file, 0);
1179         log_pfn_register(p, "b", clf_log_bytes_sent, 0);
1180         log_pfn_register(p, "B", log_bytes_sent, 0);
1181         log_pfn_register(p, "i", log_header_in, 0);
1182         log_pfn_register(p, "o", log_header_out, 0);
1183         log_pfn_register(p, "n", log_note, 0);
1184         log_pfn_register(p, "e", log_env_var, 0);
1185         log_pfn_register(p, "V", log_server_name, 0);
1186         log_pfn_register(p, "v", log_virtual_host, 0);
1187         log_pfn_register(p, "p", log_server_port, 0);
1188         log_pfn_register(p, "P", log_child_pid, 0);
1189         log_pfn_register(p, "H", log_request_protocol, 0);
1190         log_pfn_register(p, "m", log_request_method, 0);
1191         log_pfn_register(p, "q", log_request_query, 0);
1192         log_pfn_register(p, "c", log_connection_status, 0);
1193         log_pfn_register(p, "C", log_cookie, 0);
1194         log_pfn_register(p, "r", log_request_line, 1);
1195         log_pfn_register(p, "D", log_request_duration_microseconds, 1);
1196         log_pfn_register(p, "T", log_request_duration, 1);
1197         log_pfn_register(p, "U", log_request_uri, 1);
1198         log_pfn_register(p, "s", log_status, 1);
1199     }
1200 }
1201
1202 static void register_hooks(apr_pool_t *p)
1203 {
1204     ap_hook_pre_config(log_pre_config,NULL,NULL,APR_HOOK_REALLY_FIRST);
1205     ap_hook_child_init(init_child,NULL,NULL,APR_HOOK_MIDDLE);
1206     ap_hook_open_logs(init_config_log,NULL,NULL,APR_HOOK_MIDDLE);
1207     ap_hook_log_transaction(multi_log_transaction,NULL,NULL,APR_HOOK_MIDDLE);
1208
1209     /* Init log_hash before we register the optional function. It is 
1210      * possible for the optional function, ap_register_log_handler,
1211      * to be called before any other mod_log_config hooks are called.
1212      * As a policy, we should init everything required by an optional function
1213      * before calling APR_REGISTER_OPTIONAL_FN.
1214      */ 
1215     log_hash = apr_hash_make(p);
1216     APR_REGISTER_OPTIONAL_FN(ap_register_log_handler);
1217 }
1218
1219 module AP_MODULE_DECLARE_DATA log_config_module =
1220 {
1221     STANDARD20_MODULE_STUFF,
1222     NULL,                       /* create per-dir config */
1223     NULL,                       /* merge per-dir config */
1224     make_config_log_state,      /* server config */
1225     merge_config_log_state,     /* merge server config */
1226     config_log_cmds,            /* command apr_table_t */
1227     register_hooks              /* register hooks */
1228 };