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