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