home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / modules / standard / mod_log_config.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-01  |  30.1 KB  |  1,050 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995-1999 The Apache Group.  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 Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" 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 Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``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 GROUP 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 Group 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 Group 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.  * Except: no RefererIgnore functionality
  110.  *         logs '-' if no Referer or User-Agent instead of nothing
  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.  * %...{Foobar}i:  The contents of Foobar: header line(s) in the request
  126.  *                 sent to the client.
  127.  * %...l:  remote logname (from identd, if supplied)
  128.  * %...{Foobar}n:  The contents of note "Foobar" from another module.
  129.  * %...{Foobar}o:  The contents of Foobar: header line(s) in the reply.
  130.  * %...p:  the port the request was served to
  131.  * %...P:  the process ID of the child that serviced the request.
  132.  * %...r:  first line of request
  133.  * %...s:  status.  For requests that got internally redirected, this
  134.  *         is status of the *original* request --- %...>s for the last.
  135.  * %...t:  time, in common log format time format
  136.  * %...{format}t:  The time, in the form given by format, which should
  137.  *                 be in strftime(3) format.
  138.  * %...T:  the time taken to serve the request, in seconds.
  139.  * %...u:  remote user (from auth; may be bogus if return status (%s) is 401)
  140.  * %...U:  the URL path requested.
  141.  * %...v:  the name of the server (i.e. which virtual host?)
  142.  *
  143.  * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can
  144.  * indicate conditions for inclusion of the item (which will cause it
  145.  * to be replaced with '-' if the condition is not met).  Note that
  146.  * there is no escaping performed on the strings from %r, %...i and
  147.  * %...o; some with long memories may remember that I thought this was
  148.  * a bad idea, once upon a time, and I'm still not comfortable with
  149.  * it, but it is difficult to see how to "do the right thing" with all
  150.  * of '%..i', unless we URL-escape everything and break with CLF.
  151.  *
  152.  * The forms of condition are a list of HTTP status codes, which may
  153.  * or may not be preceded by '!'.  Thus, '%400,501{User-agent}i' logs
  154.  * User-agent: on 400 errors and 501 errors (Bad Request, Not
  155.  * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all
  156.  * requests which did *not* return some sort of normal status.
  157.  *
  158.  * The default LogFormat reproduces CLF; see below.
  159.  *
  160.  * The way this is supposed to work with virtual hosts is as follows:
  161.  * a virtual host can have its own LogFormat, or its own TransferLog.
  162.  * If it doesn't have its own LogFormat, it inherits from the main
  163.  * server.  If it doesn't have its own TransferLog, it writes to the
  164.  * same descriptor (meaning the same process for "| ...").
  165.  *
  166.  * --- rst */
  167.  
  168. #define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %>s %b"
  169.  
  170. #include "httpd.h"
  171. #include "http_config.h"
  172. #include "http_core.h"          /* For REMOTE_NAME */
  173. #include "http_log.h"
  174. #include <limits.h>
  175.  
  176. module MODULE_VAR_EXPORT config_log_module;
  177.  
  178. static int xfer_flags = (O_WRONLY | O_APPEND | O_CREAT);
  179. #if defined(OS2) || defined(WIN32)
  180. /* OS/2 dosen't support users and groups */
  181. static mode_t xfer_mode = (S_IREAD | S_IWRITE);
  182. #else
  183. static mode_t xfer_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  184. #endif
  185.  
  186. /* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is
  187.  * guaranteed to be atomic when writing a pipe.  And PIPE_BUF >= 512
  188.  * is guaranteed.  So we'll just guess 512 in the event the system
  189.  * doesn't have this.  Now, for file writes there is actually no limit,
  190.  * the entire write is atomic.  Whether all systems implement this
  191.  * correctly is another question entirely ... so we'll just use PIPE_BUF
  192.  * because it's probably a good guess as to what is implemented correctly
  193.  * everywhere.
  194.  */
  195. #ifdef PIPE_BUF
  196. #define LOG_BUFSIZE     PIPE_BUF
  197. #else
  198. #define LOG_BUFSIZE     (512)
  199. #endif
  200.  
  201. /*
  202.  * multi_log_state is our per-(virtual)-server configuration. We store
  203.  * an array of the logs we are going to use, each of type config_log_state.
  204.  * If a default log format is given by LogFormat, store in default_format
  205.  * (backward compat. with mod_log_config).  We also store for each virtual
  206.  * server a pointer to the logs specified for the main server, so that if this
  207.  * vhost has no logs defined, we can use the main server's logs instead.
  208.  *
  209.  * So, for the main server, config_logs contains a list of the log files
  210.  * and server_config_logs in empty. For a vhost, server_config_logs
  211.  * points to the same array as config_logs in the main server, and
  212.  * config_logs points to the array of logs defined inside this vhost,
  213.  * which might be empty.
  214.  */
  215.  
  216. typedef struct {
  217.     char *default_format_string;
  218.     array_header *default_format;
  219.     array_header *config_logs;
  220.     array_header *server_config_logs;
  221.     table *formats;
  222. } multi_log_state;
  223.  
  224. /*
  225.  * config_log_state holds the status of a single log file. fname might
  226.  * be NULL, which means this module does no logging for this
  227.  * request. format might be NULL, in which case the default_format
  228.  * from the multi_log_state should be used, or if that is NULL as
  229.  * well, use the CLF. log_fd is -1 before the log file is opened and
  230.  * set to a valid fd after it is opened.
  231.  */
  232.  
  233. typedef struct {
  234.     char *fname;
  235.     char *format_string;
  236.     array_header *format;
  237.     int log_fd;
  238. #ifdef BUFFERED_LOGS
  239.     int outcnt;
  240.     char outbuf[LOG_BUFSIZE];
  241. #endif
  242. } config_log_state;
  243.  
  244. /*
  245.  * Format items...
  246.  * Note that many of these could have ap_sprintfs replaced with static buffers.
  247.  */
  248.  
  249. typedef const char *(*item_key_func) (request_rec *, char *);
  250.  
  251. typedef struct {
  252.     item_key_func func;
  253.     char *arg;
  254.     int condition_sense;
  255.     int want_orig;
  256.     array_header *conditions;
  257. } log_format_item;
  258.  
  259. static char *format_integer(pool *p, int i)
  260. {
  261.     return ap_psprintf(p, "%d", i);
  262. }
  263.  
  264. static char *pfmt(pool *p, int i)
  265. {
  266.     if (i <= 0) {
  267.         return "-";
  268.     }
  269.     else {
  270.         return format_integer(p, i);
  271.     }
  272. }
  273.  
  274. static const char *constant_item(request_rec *dummy, char *stuff)
  275. {
  276.     return stuff;
  277. }
  278.  
  279. static const char *log_remote_host(request_rec *r, char *a)
  280. {
  281.     return ap_get_remote_host(r->connection, r->per_dir_config,
  282.                                     REMOTE_NAME);
  283. }
  284.  
  285. static const char *log_remote_address(request_rec *r, char *a)
  286. {
  287.     return r->connection->remote_ip;
  288. }
  289.  
  290. static const char *log_remote_logname(request_rec *r, char *a)
  291. {
  292.     return ap_get_remote_logname(r);
  293. }
  294.  
  295. static const char *log_remote_user(request_rec *r, char *a)
  296. {
  297.     char *rvalue = r->connection->user;
  298.  
  299.     if (rvalue == NULL) {
  300.         rvalue = "-";
  301.     }
  302.     else if (strlen(rvalue) == 0) {
  303.         rvalue = "\"\"";
  304.     }
  305.     return rvalue;
  306. }
  307.  
  308. static const char *log_request_line(request_rec *r, char *a)
  309. {
  310.         /* NOTE: If the original request contained a password, we
  311.          * re-write the request line here to contain XXXXXX instead:
  312.          * (note the truncation before the protocol string for HTTP/0.9 requests)
  313.          * (note also that r->the_request contains the unmodified request)
  314.          */
  315.     return (r->parsed_uri.password) ? ap_pstrcat(r->pool, r->method, " ",
  316.                      ap_unparse_uri_components(r->pool, &r->parsed_uri, 0),
  317.                      r->assbackwards ? NULL : " ", r->protocol, NULL)
  318.                     : r->the_request;
  319. }
  320.  
  321. static const char *log_request_file(request_rec *r, char *a)
  322. {
  323.     return r->filename;
  324. }
  325. static const char *log_request_uri(request_rec *r, char *a)
  326. {
  327.     return r->uri;
  328. }
  329. static const char *log_status(request_rec *r, char *a)
  330. {
  331.     return pfmt(r->pool, r->status);
  332. }
  333.  
  334. static const char *log_bytes_sent(request_rec *r, char *a)
  335. {
  336.     if (!r->sent_bodyct) {
  337.         return "-";
  338.     }
  339.     else {
  340.         long int bs;
  341.         ap_bgetopt(r->connection->client, BO_BYTECT, &bs);
  342.     return ap_psprintf(r->pool, "%ld", bs);
  343.     }
  344. }
  345.  
  346. static const char *log_header_in(request_rec *r, char *a)
  347. {
  348.     return ap_table_get(r->headers_in, a);
  349. }
  350.  
  351. static const char *log_header_out(request_rec *r, char *a)
  352. {
  353.     const char *cp = ap_table_get(r->headers_out, a);
  354.     if (!strcasecmp(a, "Content-type") && r->content_type) {
  355.         cp = r->content_type;
  356.     }
  357.     if (cp) {
  358.         return cp;
  359.     }
  360.     return ap_table_get(r->err_headers_out, a);
  361. }
  362.  
  363. static const char *log_note(request_rec *r, char *a)
  364. {
  365.     return ap_table_get(r->notes, a);
  366. }
  367. static const char *log_env_var(request_rec *r, char *a)
  368. {
  369.     return ap_table_get(r->subprocess_env, a);
  370. }
  371.  
  372. static const char *log_request_time(request_rec *r, char *a)
  373. {
  374.     int timz;
  375.     struct tm *t;
  376.     char tstr[MAX_STRING_LEN];
  377.  
  378.     t = ap_get_gmtoff(&timz);
  379.  
  380.     if (a && *a) {              /* Custom format */
  381.         strftime(tstr, MAX_STRING_LEN, a, t);
  382.     }
  383.     else {                      /* CLF format */
  384.         char sign = (timz < 0 ? '-' : '+');
  385.     size_t l;
  386.  
  387.         if (timz < 0) {
  388.             timz = -timz;
  389.         }
  390.  
  391.         strftime(tstr, MAX_STRING_LEN, "[%d/%b/%Y:%H:%M:%S ", t);
  392.     l = strlen(tstr);
  393.         ap_snprintf(tstr + l, sizeof(tstr) - l,
  394.                     "%c%.2d%.2d]", sign, timz / 60, timz % 60);
  395.     }
  396.  
  397.     return ap_pstrdup(r->pool, tstr);
  398. }
  399.  
  400. static const char *log_request_duration(request_rec *r, char *a)
  401. {
  402.     return ap_psprintf(r->pool, "%ld", time(NULL) - r->request_time);
  403. }
  404.  
  405. /* These next two routines use the canonical name:port so that log
  406.  * parsers don't need to duplicate all the vhost parsing crud.
  407.  */
  408. static const char *log_virtual_host(request_rec *r, char *a)
  409. {
  410.     return r->server->server_hostname;
  411. }
  412.  
  413. static const char *log_server_port(request_rec *r, char *a)
  414. {
  415.     return ap_psprintf(r->pool, "%u",
  416.     r->server->port ? r->server->port : ap_default_port(r));
  417. }
  418.  
  419. static const char *log_child_pid(request_rec *r, char *a)
  420. {
  421.     return ap_psprintf(r->pool, "%ld", (long) getpid());
  422. }
  423.  
  424. /*****************************************************************
  425.  *
  426.  * Parsing the log format string
  427.  */
  428.  
  429. static struct log_item_list {
  430.     char ch;
  431.     item_key_func func;
  432.     int want_orig_default;
  433. } log_item_keys[] = {
  434.  
  435.     {
  436.         'h', log_remote_host, 0
  437.     },
  438.     {   
  439.         'a', log_remote_address, 0 
  440.     },
  441.     {
  442.         'l', log_remote_logname, 0
  443.     },
  444.     {
  445.         'u', log_remote_user, 0
  446.     },
  447.     {
  448.         't', log_request_time, 0
  449.     },
  450.     {
  451.         'T', log_request_duration, 1
  452.     },
  453.     {
  454.         'r', log_request_line, 1
  455.     },
  456.     {
  457.         'f', log_request_file, 0
  458.     },
  459.     {
  460.         'U', log_request_uri, 1
  461.     },
  462.     {
  463.         's', log_status, 1
  464.     },
  465.     {
  466.         'b', log_bytes_sent, 0
  467.     },
  468.     {
  469.         'i', log_header_in, 0
  470.     },
  471.     {
  472.         'o', log_header_out, 0
  473.     },
  474.     {
  475.         'n', log_note, 0
  476.     },
  477.     {
  478.         'e', log_env_var, 0
  479.     },
  480.     {
  481.         'v', log_virtual_host, 0
  482.     },
  483.     {
  484.         'p', log_server_port, 0
  485.     },
  486.     {
  487.         'P', log_child_pid, 0
  488.     },
  489.     {
  490.         '\0'
  491.     }
  492. };
  493.  
  494. static struct log_item_list *find_log_func(char k)
  495. {
  496.     int i;
  497.  
  498.     for (i = 0; log_item_keys[i].ch; ++i)
  499.         if (k == log_item_keys[i].ch) {
  500.             return &log_item_keys[i];
  501.         }
  502.  
  503.     return NULL;
  504. }
  505.  
  506. static char *log_format_substring(pool *p, const char *start,
  507.                                   const char *end)
  508. {
  509.     char *res = ap_palloc(p, end - start + 1);
  510.  
  511.     strncpy(res, start, end - start);
  512.     res[end - start] = '\0';
  513.     return res;
  514. }
  515.  
  516. static char *parse_log_misc_string(pool *p, log_format_item *it,
  517.                                    const char **sa)
  518. {
  519.     const char *s = *sa;
  520.  
  521.     it->func = constant_item;
  522.     it->conditions = NULL;
  523.  
  524.     while (*s && *s != '%') {
  525.         ++s;
  526.     }
  527.     it->arg = log_format_substring(p, *sa, s);
  528.     *sa = s;
  529.  
  530.     return NULL;
  531. }
  532.  
  533. static char *parse_log_item(pool *p, log_format_item *it, const char **sa)
  534. {
  535.     const char *s = *sa;
  536.  
  537.     if (*s != '%') {
  538.         return parse_log_misc_string(p, it, sa);
  539.     }
  540.  
  541.     ++s;
  542.     it->condition_sense = 0;
  543.     it->conditions = NULL;
  544.     it->want_orig = -1;
  545.     it->arg = "";               /* For safety's sake... */
  546.  
  547.     while (*s) {
  548.         int i;
  549.         struct log_item_list *l;
  550.  
  551.         switch (*s) {
  552.         case '!':
  553.             ++s;
  554.             it->condition_sense = !it->condition_sense;
  555.             break;
  556.  
  557.         case '<':
  558.             ++s;
  559.             it->want_orig = 1;
  560.             break;
  561.  
  562.         case '>':
  563.             ++s;
  564.             it->want_orig = 0;
  565.             break;
  566.  
  567.         case ',':
  568.             ++s;
  569.             break;
  570.  
  571.         case '{':
  572.             ++s;
  573.             it->arg = ap_getword(p, &s, '}');
  574.             break;
  575.  
  576.         case '0':
  577.         case '1':
  578.         case '2':
  579.         case '3':
  580.         case '4':
  581.         case '5':
  582.         case '6':
  583.         case '7':
  584.         case '8':
  585.         case '9':
  586.             i = *s - '0';
  587.             while (ap_isdigit(*++s)) {
  588.                 i = i * 10 + (*s) - '0';
  589.             }
  590.             if (!it->conditions) {
  591.                 it->conditions = ap_make_array(p, 4, sizeof(int));
  592.             }
  593.             *(int *) ap_push_array(it->conditions) = i;
  594.             break;
  595.  
  596.         default:
  597.             l = find_log_func(*s++);
  598.             if (!l) {
  599.                 char dummy[2];
  600.  
  601.                 dummy[0] = s[-1];
  602.                 dummy[1] = '\0';
  603.                 return ap_pstrcat(p, "Unrecognized LogFormat directive %",
  604.                                dummy, NULL);
  605.             }
  606.             it->func = l->func;
  607.             if (it->want_orig == -1) {
  608.                 it->want_orig = l->want_orig_default;
  609.             }
  610.             *sa = s;
  611.             return NULL;
  612.         }
  613.     }
  614.  
  615.     return "Ran off end of LogFormat parsing args to some directive";
  616. }
  617.  
  618. static array_header *parse_log_string(pool *p, const char *s, const char **err)
  619. {
  620.     array_header *a = ap_make_array(p, 30, sizeof(log_format_item));
  621.     char *res;
  622.  
  623.     while (*s) {
  624.         if ((res = parse_log_item(p, (log_format_item *) ap_push_array(a), &s))) {
  625.             *err = res;
  626.             return NULL;
  627.         }
  628.     }
  629.  
  630.     s = "\n";
  631.     parse_log_item(p, (log_format_item *) ap_push_array(a), &s);
  632.     return a;
  633. }
  634.  
  635. /*****************************************************************
  636.  *
  637.  * Actually logging.
  638.  */
  639.  
  640. static const char *process_item(request_rec *r, request_rec *orig,
  641.                           log_format_item *item)
  642. {
  643.     const char *cp;
  644.  
  645.     /* First, see if we need to process this thing at all... */
  646.  
  647.     if (item->conditions && item->conditions->nelts != 0) {
  648.         int i;
  649.         int *conds = (int *) item->conditions->elts;
  650.         int in_list = 0;
  651.  
  652.         for (i = 0; i < item->conditions->nelts; ++i) {
  653.             if (r->status == conds[i]) {
  654.                 in_list = 1;
  655.                 break;
  656.             }
  657.         }
  658.  
  659.         if ((item->condition_sense && in_list)
  660.             || (!item->condition_sense && !in_list)) {
  661.             return "-";
  662.         }
  663.     }
  664.  
  665.     /* We do.  Do it... */
  666.  
  667.     cp = (*item->func) (item->want_orig ? orig : r, item->arg);
  668.     return cp ? cp : "-";
  669. }
  670.  
  671. #ifdef BUFFERED_LOGS
  672. static void flush_log(config_log_state *cls)
  673. {
  674.     if (cls->outcnt && cls->log_fd != -1) {
  675.         write(cls->log_fd, cls->outbuf, cls->outcnt);
  676.         cls->outcnt = 0;
  677.     }
  678. }
  679. #endif
  680.  
  681. static int config_log_transaction(request_rec *r, config_log_state *cls,
  682.                                   array_header *default_format)
  683. {
  684.     log_format_item *items;
  685.     char *str, *s;
  686.     const char **strs;
  687.     int *strl;
  688.     request_rec *orig;
  689.     int i;
  690.     int len = 0;
  691.     array_header *format;
  692.  
  693.     if (cls->fname == NULL) {
  694.         return DECLINED;
  695.     }
  696.  
  697.     format = cls->format ? cls->format : default_format;
  698.  
  699.     strs = ap_palloc(r->pool, sizeof(char *) * (format->nelts));
  700.     strl = ap_palloc(r->pool, sizeof(int) * (format->nelts));
  701.     items = (log_format_item *) format->elts;
  702.  
  703.     orig = r;
  704.     while (orig->prev) {
  705.         orig = orig->prev;
  706.     }
  707.     while (r->next) {
  708.         r = r->next;
  709.     }
  710.  
  711.     for (i = 0; i < format->nelts; ++i) {
  712.         strs[i] = process_item(r, orig, &items[i]);
  713.     }
  714.  
  715.     for (i = 0; i < format->nelts; ++i) {
  716.         len += strl[i] = strlen(strs[i]);
  717.     }
  718.  
  719. #ifdef BUFFERED_LOGS
  720.     if (len + cls->outcnt > LOG_BUFSIZE) {
  721.         flush_log(cls);
  722.     }
  723.     if (len >= LOG_BUFSIZE) {
  724.         str = ap_palloc(r->pool, len + 1);
  725.         for (i = 0, s = str; i < format->nelts; ++i) {
  726.             memcpy(s, strs[i], strl[i]);
  727.             s += strl[i];
  728.         }
  729.         write(cls->log_fd, str, len);
  730.     }
  731.     else {
  732.         for (i = 0, s = &cls->outbuf[cls->outcnt]; i < format->nelts; ++i) {
  733.             memcpy(s, strs[i], strl[i]);
  734.             s += strl[i];
  735.         }
  736.         cls->outcnt += len;
  737.     }
  738. #else
  739.     str = ap_palloc(r->pool, len + 1);
  740.  
  741.     for (i = 0, s = str; i < format->nelts; ++i) {
  742.         memcpy(s, strs[i], strl[i]);
  743.         s += strl[i];
  744.     }
  745.  
  746.     write(cls->log_fd, str, len);
  747. #endif
  748.  
  749.     return OK;
  750. }
  751.  
  752. static int multi_log_transaction(request_rec *r)
  753. {
  754.     multi_log_state *mls = ap_get_module_config(r->server->module_config,
  755.                                              &config_log_module);
  756.     config_log_state *clsarray;
  757.     int i;
  758.  
  759.     if (mls->config_logs->nelts) {
  760.         clsarray = (config_log_state *) mls->config_logs->elts;
  761.         for (i = 0; i < mls->config_logs->nelts; ++i) {
  762.             config_log_state *cls = &clsarray[i];
  763.  
  764.             config_log_transaction(r, cls, mls->default_format);
  765.         }
  766.     }
  767.     else if (mls->server_config_logs) {
  768.         clsarray = (config_log_state *) mls->server_config_logs->elts;
  769.         for (i = 0; i < mls->server_config_logs->nelts; ++i) {
  770.             config_log_state *cls = &clsarray[i];
  771.  
  772.             config_log_transaction(r, cls, mls->default_format);
  773.         }
  774.     }
  775.  
  776.     return OK;
  777. }
  778.  
  779. /*****************************************************************
  780.  *
  781.  * Module glue...
  782.  */
  783.  
  784. static void *make_config_log_state(pool *p, server_rec *s)
  785. {
  786.     multi_log_state *mls = (multi_log_state *) ap_palloc(p, sizeof(multi_log_state));
  787.  
  788.     mls->config_logs = ap_make_array(p, 1, sizeof(config_log_state));
  789.     mls->default_format_string = NULL;
  790.     mls->default_format = NULL;
  791.     mls->server_config_logs = NULL;
  792.     mls->formats = ap_make_table(p, 4);
  793.     ap_table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
  794.  
  795.     return mls;
  796. }
  797.  
  798. /*
  799.  * Use the merger to simply add a pointer from the vhost log state
  800.  * to the log of logs specified for the non-vhost configuration.  Make sure
  801.  * vhosts inherit any globally-defined format names.
  802.  */
  803.  
  804. static void *merge_config_log_state(pool *p, void *basev, void *addv)
  805. {
  806.     multi_log_state *base = (multi_log_state *) basev;
  807.     multi_log_state *add = (multi_log_state *) addv;
  808.  
  809.     add->server_config_logs = base->config_logs;
  810.     if (!add->default_format) {
  811.         add->default_format_string = base->default_format_string;
  812.         add->default_format = base->default_format;
  813.     }
  814.     add->formats = ap_overlay_tables(p, base->formats, add->formats);
  815.  
  816.     return add;
  817. }
  818.  
  819. /*
  820.  * Set the default logfile format, or define a nickname for a format string.
  821.  */
  822. static const char *log_format(cmd_parms *cmd, void *dummy, char *fmt,
  823.                               char *name)
  824. {
  825.     const char *err_string = NULL;
  826.     multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
  827.                                              &config_log_module);
  828.  
  829.     /*
  830.      * If we were given two arguments, the second is a name to be given to the
  831.      * format.  This syntax just defines the nickname - it doesn't actually
  832.      * make the format the default.
  833.      */
  834.     if (name != NULL) {
  835.         parse_log_string(cmd->pool, fmt, &err_string);
  836.         if (err_string == NULL) {
  837.             ap_table_setn(mls->formats, name, fmt);
  838.         }
  839.     }
  840.     else {
  841.         mls->default_format_string = fmt;
  842.         mls->default_format = parse_log_string(cmd->pool, fmt, &err_string);
  843.     }
  844.     return err_string;
  845. }
  846.  
  847. static const char *add_custom_log(cmd_parms *cmd, void *dummy, char *fn,
  848.                                   char *fmt)
  849. {
  850.     const char *err_string = NULL;
  851.     multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
  852.                                              &config_log_module);
  853.     config_log_state *cls;
  854.  
  855.     cls = (config_log_state *) ap_push_array(mls->config_logs);
  856.     cls->fname = fn;
  857.     cls->format_string = fmt;
  858.     if (!fmt) {
  859.         cls->format = NULL;
  860.     }
  861.     else {
  862.         cls->format = parse_log_string(cmd->pool, fmt, &err_string);
  863.     }
  864.     cls->log_fd = -1;
  865.  
  866.     return err_string;
  867. }
  868.  
  869. static const char *set_transfer_log(cmd_parms *cmd, void *dummy, char *fn)
  870. {
  871.     return add_custom_log(cmd, dummy, fn, NULL);
  872. }
  873.  
  874. static const char *set_cookie_log(cmd_parms *cmd, void *dummy, char *fn)
  875. {
  876.     return add_custom_log(cmd, dummy, fn, "%{Cookie}n \"%r\" %t");
  877. }
  878.  
  879. static const command_rec config_log_cmds[] =
  880. {
  881.     {"CustomLog", add_custom_log, NULL, RSRC_CONF, TAKE2,
  882.      "a file name and a custom log format string or format name"},
  883.     {"TransferLog", set_transfer_log, NULL, RSRC_CONF, TAKE1,
  884.      "the filename of the access log"},
  885.     {"LogFormat", log_format, NULL, RSRC_CONF, TAKE12,
  886.      "a log format string (see docs) and an optional format name"},
  887.     {"CookieLog", set_cookie_log, NULL, RSRC_CONF, TAKE1,
  888.      "the filename of the cookie log"},
  889.     {NULL}
  890. };
  891.  
  892. static config_log_state *open_config_log(server_rec *s, pool *p,
  893.                                          config_log_state *cls,
  894.                                          array_header *default_format)
  895. {
  896.     if (cls->log_fd > 0) {
  897.         return cls;             /* virtual config shared w/main server */
  898.     }
  899.  
  900.     if (cls->fname == NULL) {
  901.         return cls;             /* Leave it NULL to decline.  */
  902.     }
  903.  
  904.     if (*cls->fname == '|') {
  905.         piped_log *pl;
  906.  
  907.         pl = ap_open_piped_log(p, cls->fname + 1);
  908.         if (pl == NULL) {
  909.             exit(1);
  910.         }
  911.         cls->log_fd = ap_piped_log_write_fd(pl);
  912.     }
  913.     else {
  914.         char *fname = ap_server_root_relative(p, cls->fname);
  915.         if ((cls->log_fd = ap_popenf(p, fname, xfer_flags, xfer_mode)) < 0) {
  916.             ap_log_error(APLOG_MARK, APLOG_ERR, s,
  917.                          "could not open transfer log file %s.", fname);
  918.             exit(1);
  919.         }
  920.     }
  921. #ifdef BUFFERED_LOGS
  922.     cls->outcnt = 0;
  923. #endif
  924.  
  925.     return cls;
  926. }
  927.  
  928. static config_log_state *open_multi_logs(server_rec *s, pool *p)
  929. {
  930.     int i;
  931.     multi_log_state *mls = ap_get_module_config(s->module_config,
  932.                                              &config_log_module);
  933.     config_log_state *clsarray;
  934.     const char *dummy;
  935.     const char *format;
  936.  
  937.     if (mls->default_format_string) {
  938.     format = ap_table_get(mls->formats, mls->default_format_string);
  939.     if (format) {
  940.         mls->default_format = parse_log_string(p, format, &dummy);
  941.     }
  942.     }    
  943.  
  944.     if (!mls->default_format) {
  945.         mls->default_format = parse_log_string(p, DEFAULT_LOG_FORMAT, &dummy);
  946.     }
  947.  
  948.     if (mls->config_logs->nelts) {
  949.         clsarray = (config_log_state *) mls->config_logs->elts;
  950.         for (i = 0; i < mls->config_logs->nelts; ++i) {
  951.             config_log_state *cls = &clsarray[i];
  952.  
  953.         if (cls->format_string) {
  954.         format = ap_table_get(mls->formats, cls->format_string);
  955.         if (format) {
  956.             cls->format = parse_log_string(p, format, &dummy);
  957.         }
  958.         }
  959.  
  960.             cls = open_config_log(s, p, cls, mls->default_format);
  961.         }
  962.     }
  963.     else if (mls->server_config_logs) {
  964.         clsarray = (config_log_state *) mls->server_config_logs->elts;
  965.         for (i = 0; i < mls->server_config_logs->nelts; ++i) {
  966.             config_log_state *cls = &clsarray[i];
  967.  
  968.         if (cls->format_string) {
  969.         format = ap_table_get(mls->formats, cls->format_string);
  970.         if (format) {
  971.             cls->format = parse_log_string(p, format, &dummy);
  972.         }
  973.         }
  974.  
  975.             cls = open_config_log(s, p, cls, mls->default_format);
  976.         }
  977.     }
  978.  
  979.     return NULL;
  980. }
  981.  
  982. static void init_config_log(server_rec *s, pool *p)
  983. {
  984.     /* First, do "physical" server, which gets default log fd and format
  985.      * for the virtual servers, if they don't override...
  986.      */
  987.  
  988.     open_multi_logs(s, p);
  989.  
  990.     /* Then, virtual servers */
  991.  
  992.     for (s = s->next; s; s = s->next) {
  993.         open_multi_logs(s, p);
  994.     }
  995. }
  996.  
  997. #ifdef BUFFERED_LOGS
  998. static void flush_all_logs(server_rec *s, pool *p)
  999. {
  1000.     multi_log_state *mls;
  1001.     array_header *log_list;
  1002.     config_log_state *clsarray;
  1003.     int i;
  1004.  
  1005.     for (; s; s = s->next) {
  1006.         mls = ap_get_module_config(s->module_config, &config_log_module);
  1007.         log_list = NULL;
  1008.         if (mls->config_logs->nelts) {
  1009.             log_list = mls->config_logs;
  1010.         }
  1011.         else if (mls->server_config_logs) {
  1012.             log_list = mls->server_config_logs;
  1013.         }
  1014.         if (log_list) {
  1015.             clsarray = (config_log_state *) log_list->elts;
  1016.             for (i = 0; i < log_list->nelts; ++i) {
  1017.                 flush_log(&clsarray[i]);
  1018.             }
  1019.         }
  1020.     }
  1021. }
  1022. #endif
  1023.  
  1024. module MODULE_VAR_EXPORT config_log_module =
  1025. {
  1026.     STANDARD_MODULE_STUFF,
  1027.     init_config_log,            /* initializer */
  1028.     NULL,                       /* create per-dir config */
  1029.     NULL,                       /* merge per-dir config */
  1030.     make_config_log_state,      /* server config */
  1031.     merge_config_log_state,     /* merge server config */
  1032.     config_log_cmds,            /* command table */
  1033.     NULL,                       /* handlers */
  1034.     NULL,                       /* filename translation */
  1035.     NULL,                       /* check_user_id */
  1036.     NULL,                       /* check auth */
  1037.     NULL,                       /* check access */
  1038.     NULL,                       /* type_checker */
  1039.     NULL,                       /* fixups */
  1040.     multi_log_transaction,      /* logger */
  1041.     NULL,                       /* header parser */
  1042.     NULL,                       /* child_init */
  1043. #ifdef BUFFERED_LOGS
  1044.     flush_all_logs,             /* child_exit */
  1045. #else
  1046.     NULL,
  1047. #endif
  1048.     NULL                        /* post read-request */
  1049. };
  1050.