mod_log_config.c revision 5c05c1f29be5bc37b22794737ee63a5f567053b5
f743002678eb67b99bbc29fee116b65d9530fec0wrowe/* Licensed to the Apache Software Foundation (ASF) under one or more
80833bb9a1bf25dcf19e814438a4b311d2e1f4cffuankg * contributor license agreements. See the NOTICE file distributed with
09c87c777bed1655621bb20e1c46cb6b1a63279dcovener * this work for additional information regarding copyright ownership.
3060ce7f798fbda7999cd4ddf89b525d2b294185covener * The ASF licenses this file to You under the Apache License, Version 2.0
3060ce7f798fbda7999cd4ddf89b525d2b294185covener * (the "License"); you may not use this file except in compliance with
3060ce7f798fbda7999cd4ddf89b525d2b294185covener * the License. You may obtain a copy of the License at
3060ce7f798fbda7999cd4ddf89b525d2b294185covener *
09c87c777bed1655621bb20e1c46cb6b1a63279dcovener * http://www.apache.org/licenses/LICENSE-2.0
09c87c777bed1655621bb20e1c46cb6b1a63279dcovener *
09c87c777bed1655621bb20e1c46cb6b1a63279dcovener * Unless required by applicable law or agreed to in writing, software
c85eff31536e6bfef1537b2435564d48665435d3rpluem * distributed under the License is distributed on an "AS IS" BASIS,
c85eff31536e6bfef1537b2435564d48665435d3rpluem * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
c85eff31536e6bfef1537b2435564d48665435d3rpluem * See the License for the specific language governing permissions and
c85eff31536e6bfef1537b2435564d48665435d3rpluem * limitations under the License.
c2051ade794269f23194ec06842dc225d082763arpluem */
c2051ade794269f23194ec06842dc225d082763arpluem
c2051ade794269f23194ec06842dc225d082763arpluem/*
c1a63b8fad09c419c1a64f75993feb8a343a6801ylavic * Modified by djm@va.pubnix.com:
c1a63b8fad09c419c1a64f75993feb8a343a6801ylavic * If no TransferLog is given explicitly, decline to log.
c1a63b8fad09c419c1a64f75993feb8a343a6801ylavic *
8eb78a55c83fee3383fd2862f66a3ab20d059283rjung * This is module implements the TransferLog directive (same as the
8eb78a55c83fee3383fd2862f66a3ab20d059283rjung * common log module), and additional directives, LogFormat and CustomLog.
8eb78a55c83fee3383fd2862f66a3ab20d059283rjung *
017999c1606011d16d7fb6789a82634c6d504498rjung *
017999c1606011d16d7fb6789a82634c6d504498rjung * Syntax:
393e1bb47b60cf97d521c49cf929740f32b95758kbrand *
393e1bb47b60cf97d521c49cf929740f32b95758kbrand * TransferLog fn Logs transfers to fn in standard log format, unless
393e1bb47b60cf97d521c49cf929740f32b95758kbrand * a custom format is set with LogFormat
393e1bb47b60cf97d521c49cf929740f32b95758kbrand * LogFormat format Set a log format from TransferLog files
8af5a3bd941a25b28ff9c84c513d6aa9f48f2cdcrjung * CustomLog fn format
8af5a3bd941a25b28ff9c84c513d6aa9f48f2cdcrjung * Log to file fn with format given by the format
8af5a3bd941a25b28ff9c84c513d6aa9f48f2cdcrjung * argument
42b6ba421855a65673ad46844a0be899e4ad9405rjung *
42b6ba421855a65673ad46844a0be899e4ad9405rjung * CookieLog fn For backwards compatability with old Cookie
d41624899afd4656a24fa839431ade65da56c4a5rjung * logging module - now deprecated.
d41624899afd4656a24fa839431ade65da56c4a5rjung *
d41624899afd4656a24fa839431ade65da56c4a5rjung * There can be any number of TransferLog and CustomLog
e6b4bd1113567627ab6bb6c6a7105e1e01a7d889jailletc * commands. Each request will be logged to _ALL_ the
e6b4bd1113567627ab6bb6c6a7105e1e01a7d889jailletc * named files, in the appropriate format.
e466c40e1801982602ee0200c9e8b61cc148742djailletc *
e466c40e1801982602ee0200c9e8b61cc148742djailletc * If no TransferLog or CustomLog directive appears in a VirtualHost,
457468b82e59d01eba00dd9d0817309c8f5e414ejim * the request will be logged to the log file(s) defined outside
457468b82e59d01eba00dd9d0817309c8f5e414ejim * the virtual host section. If a TransferLog or CustomLog directive
457468b82e59d01eba00dd9d0817309c8f5e414ejim * appears in the VirtualHost section, the log files defined outside
04983e3bd1754764eec7d6bb772fe3b0bf391771jorton * the VirtualHost will _not_ be used. This makes this module compatable
04983e3bd1754764eec7d6bb772fe3b0bf391771jorton * with the CLF and config log modules, where the use of TransferLog
15890c9306ba98f6fc243e15a3c4778ddc7d773erpluem * inside the VirtualHost section overrides its use outside.
15660979a30d251681463de2e0584853890082accovener *
15660979a30d251681463de2e0584853890082accovener * Examples:
15660979a30d251681463de2e0584853890082accovener *
15660979a30d251681463de2e0584853890082accovener * TransferLog logs/access_log
cfd9415521847b2f9394fad04fb701cfb955f503rjung * <VirtualHost>
cfd9415521847b2f9394fad04fb701cfb955f503rjung * LogFormat "... custom format ..."
cfd9415521847b2f9394fad04fb701cfb955f503rjung * TransferLog log/virtual_only
28c31fb73c1264bd1d0ff932573677030b024c7dwrowe * CustomLog log/virtual_useragents "%t %{user-agent}i"
28c31fb73c1264bd1d0ff932573677030b024c7dwrowe * </VirtualHost>
28c31fb73c1264bd1d0ff932573677030b024c7dwrowe *
28c31fb73c1264bd1d0ff932573677030b024c7dwrowe * This will log using CLF to access_log any requests handled by the
28c31fb73c1264bd1d0ff932573677030b024c7dwrowe * main server, while any requests to the virtual host will be logged
8491e0600f69b0405e156ea8a419653c065c645bcovener * with the "... custom format..." to virtual_only _AND_ using
63b9f1f5880391261705f696d7d65507bbe9ace3covener * the custom user-agent log to virtual_useragents.
63b9f1f5880391261705f696d7d65507bbe9ace3covener *
63b9f1f5880391261705f696d7d65507bbe9ace3covener * Note that the NCSA referer and user-agent logs are easily added with
87a26948305eab2bab8a4fb3f2a21f6725055790covener * CustomLog:
87a26948305eab2bab8a4fb3f2a21f6725055790covener * CustomLog logs/referer "%{referer}i -> %U"
87a26948305eab2bab8a4fb3f2a21f6725055790covener * CustomLog logs/agent "%{user-agent}i"
4efd27d2bd53a819a194f8a942f8881c1927755eylavic *
4efd27d2bd53a819a194f8a942f8881c1927755eylavic * RefererIgnore functionality can be obtained with conditional
4efd27d2bd53a819a194f8a942f8881c1927755eylavic * logging (SetEnvIf and CustomLog ... env=!VAR).
4efd27d2bd53a819a194f8a942f8881c1927755eylavic *
983528026996668ea295be95aedb9c7a346af470ylavic * But using this method allows much easier modification of the
983528026996668ea295be95aedb9c7a346af470ylavic * log format, e.g. to log hosts along with UA:
983528026996668ea295be95aedb9c7a346af470ylavic * CustomLog logs/referer "%{referer}i %U %h"
249ab52ef73a2b33446ae07904e3526b57251411ylavic *
249ab52ef73a2b33446ae07904e3526b57251411ylavic * The argument to LogFormat and CustomLog is a string, which can include
249ab52ef73a2b33446ae07904e3526b57251411ylavic * literal characters copied into the log files, and '%' directives as
1f0836d4b1a203c7b375daae691beb95f6036205ylavic * follows:
1f0836d4b1a203c7b375daae691beb95f6036205ylavic *
1f0836d4b1a203c7b375daae691beb95f6036205ylavic * %...B: bytes sent, excluding HTTP headers.
3b11e6ec1c5273d6a8968460db650e7ca99c49c0ylavic * %...b: bytes sent, excluding HTTP headers in CLF format, i.e. a '-'
3b11e6ec1c5273d6a8968460db650e7ca99c49c0ylavic * when no bytes where sent (rather than a '0'.
3b11e6ec1c5273d6a8968460db650e7ca99c49c0ylavic * %...{FOOBAR}C: The contents of the HTTP cookie FOOBAR
01402a0fbec8bd11f6c10d8ef9c9cceac68bb787ylavic * %...{FOOBAR}e: The contents of the environment variable FOOBAR
01402a0fbec8bd11f6c10d8ef9c9cceac68bb787ylavic * %...f: filename
01402a0fbec8bd11f6c10d8ef9c9cceac68bb787ylavic * %...h: remote host
49dacedb6c387b786b7911082ff35121a45f414bcovener * %...a: remote IP-address
49dacedb6c387b786b7911082ff35121a45f414bcovener * %...A: local IP-address
49dacedb6c387b786b7911082ff35121a45f414bcovener * %...{Foobar}i: The contents of Foobar: header line(s) in the request
49dacedb6c387b786b7911082ff35121a45f414bcovener * sent to the client.
3c990331fc6702119e4f5b8ba9eae3021aea5265jim * %...k: number of requests served over this connection
3c990331fc6702119e4f5b8ba9eae3021aea5265jim * %...l: remote logname (from identd, if supplied)
3c990331fc6702119e4f5b8ba9eae3021aea5265jim * %...{Foobar}n: The contents of note "Foobar" from another module.
3c990331fc6702119e4f5b8ba9eae3021aea5265jim * %...{Foobar}o: The contents of Foobar: header line(s) in the reply.
fc42512879dd0504532f52fe5d0d0383dda96a1eniq * %...p: the canonical port for the server
fc42512879dd0504532f52fe5d0d0383dda96a1eniq * %...{format}p: the canonical port for the server, or the actual local
fc42512879dd0504532f52fe5d0d0383dda96a1eniq * or remote port
0451df5dc50fa5d8b3e07d92ee6a92e36a1181a5niq * %...P: the process ID of the child that serviced the request.
0451df5dc50fa5d8b3e07d92ee6a92e36a1181a5niq * %...{format}P: the process ID or thread ID of the child/thread that
0451df5dc50fa5d8b3e07d92ee6a92e36a1181a5niq * serviced the request
da0442c0440caef34706e2c2f3af05cb65921cc0jailletc * %...r: first line of request
983528026996668ea295be95aedb9c7a346af470ylavic * %...s: status. For requests that got internally redirected, this
da0442c0440caef34706e2c2f3af05cb65921cc0jailletc * is status of the *original* request --- %...>s for the last.
da0442c0440caef34706e2c2f3af05cb65921cc0jailletc * %...t: time, in common log format time format
06b8f183140c8e02e0974e938a05078b511d1603covener * %...{format}t: The time, in the form given by format, which should
06b8f183140c8e02e0974e938a05078b511d1603covener * be in strftime(3) format.
06b8f183140c8e02e0974e938a05078b511d1603covener * %...T: the time taken to serve the request, in seconds.
15890c9306ba98f6fc243e15a3c4778ddc7d773erpluem * %...D: the time taken to serve the request, in micro seconds.
259878293a997ff49f5ddfc53d3739cbdc25444ecovener * %...u: remote user (from auth; may be bogus if return status (%s) is 401)
259878293a997ff49f5ddfc53d3739cbdc25444ecovener * %...U: the URL path requested.
259878293a997ff49f5ddfc53d3739cbdc25444ecovener * %...v: the configured name of the server (i.e. which virtual host?)
259878293a997ff49f5ddfc53d3739cbdc25444ecovener * %...V: the server name according to the UseCanonicalName setting
15890c9306ba98f6fc243e15a3c4778ddc7d773erpluem * %...m: the request method
b54b024c06a19926832d77d40ba35ad8c41e4d3dminfrin * %...H: the request protocol
b54b024c06a19926832d77d40ba35ad8c41e4d3dminfrin * %...q: the query string prepended by "?", or empty if no query string
b54b024c06a19926832d77d40ba35ad8c41e4d3dminfrin * %...X: Status of the connection.
65967d05f839dbf27cf91d91fa79585eeae19660minfrin * 'X' = connection aborted before the response completed.
65967d05f839dbf27cf91d91fa79585eeae19660minfrin * '+' = connection may be kept alive after the response is sent.
65967d05f839dbf27cf91d91fa79585eeae19660minfrin * '-' = connection will be closed after the response is sent.
65967d05f839dbf27cf91d91fa79585eeae19660minfrin (This directive was %...c in late versions of Apache 1.3, but
8152945ae46857b170cb227e79bb799f4fc7710dminfrin this conflicted with the historical ssl %...{var}c syntax.)
8152945ae46857b170cb227e79bb799f4fc7710dminfrin*
8152945ae46857b170cb227e79bb799f4fc7710dminfrin * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can
8152945ae46857b170cb227e79bb799f4fc7710dminfrin * indicate conditions for inclusion of the item (which will cause it
75f5c2db254c0167a0e396254460de09b775d203trawick * to be replaced with '-' if the condition is not met). Note that
75f5c2db254c0167a0e396254460de09b775d203trawick * there is no escaping performed on the strings from %r, %...i and
75f5c2db254c0167a0e396254460de09b775d203trawick * %...o; some with long memories may remember that I thought this was
11f1871b90149f8af3bf4e884dcc404436686967ylavic * a bad idea, once upon a time, and I'm still not comfortable with
11f1871b90149f8af3bf4e884dcc404436686967ylavic * it, but it is difficult to see how to "do the right thing" with all
11f1871b90149f8af3bf4e884dcc404436686967ylavic * of '%..i', unless we URL-escape everything and break with CLF.
11f1871b90149f8af3bf4e884dcc404436686967ylavic *
4f0358189bfa57b8e75bd6b94db264302a8f336amrumph * The forms of condition are a list of HTTP status codes, which may
4f0358189bfa57b8e75bd6b94db264302a8f336amrumph * or may not be preceded by '!'. Thus, '%400,501{User-agent}i' logs
4f0358189bfa57b8e75bd6b94db264302a8f336amrumph * User-agent: on 400 errors and 501 errors (Bad Request, Not
5716f9c6daa92dde5f2f9d11ed63f7c9549c223atrawick * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all
5716f9c6daa92dde5f2f9d11ed63f7c9549c223atrawick * requests which did *not* return some sort of normal status.
5716f9c6daa92dde5f2f9d11ed63f7c9549c223atrawick *
5716f9c6daa92dde5f2f9d11ed63f7c9549c223atrawick * The default LogFormat reproduces CLF; see below.
54d750a84a175d8e338880514d440773eb986b50covener *
54d750a84a175d8e338880514d440773eb986b50covener * The way this is supposed to work with virtual hosts is as follows:
54d750a84a175d8e338880514d440773eb986b50covener * a virtual host can have its own LogFormat, or its own TransferLog.
54d750a84a175d8e338880514d440773eb986b50covener * If it doesn't have its own LogFormat, it inherits from the main
54d750a84a175d8e338880514d440773eb986b50covener * server. If it doesn't have its own TransferLog, it writes to the
54d750a84a175d8e338880514d440773eb986b50covener * same descriptor (meaning the same process for "| ...").
54d750a84a175d8e338880514d440773eb986b50covener *
54d750a84a175d8e338880514d440773eb986b50covener * --- rst */
7a3aa12f0eda24793ee26d6a179bd53132e9dae8covener
54d750a84a175d8e338880514d440773eb986b50covener#include "apr_strings.h"
54d750a84a175d8e338880514d440773eb986b50covener#include "apr_lib.h"
83b50288fa7d306324bba68832011ea08f5c7832covener#include "apr_hash.h"
4e30ef014533a7e93c92d88306291f5e49c9692ftrawick#include "apr_optional.h"
83b50288fa7d306324bba68832011ea08f5c7832covener#include "apr_anylock.h"
5f066f496cd9f20a2a701255bc67d44e7cb46daetrawick
5f066f496cd9f20a2a701255bc67d44e7cb46daetrawick#define APR_WANT_STRFUNC
5f066f496cd9f20a2a701255bc67d44e7cb46daetrawick#include "apr_want.h"
2e15620d724fb8e3a5be183b917359a2fd6e9468covener
2e15620d724fb8e3a5be183b917359a2fd6e9468covener#include "ap_config.h"
2e15620d724fb8e3a5be183b917359a2fd6e9468covener#include "mod_log_config.h"
2e15620d724fb8e3a5be183b917359a2fd6e9468covener#include "httpd.h"
1b988c41ee505962781d110a3e4c2c90f1ea0aa4covener#include "http_config.h"
1b988c41ee505962781d110a3e4c2c90f1ea0aa4covener#include "http_core.h" /* For REMOTE_NAME */
1b988c41ee505962781d110a3e4c2c90f1ea0aa4covener#include "http_log.h"
1b988c41ee505962781d110a3e4c2c90f1ea0aa4covener#include "http_protocol.h"
b8efdc95bec9cf089aa1be0bfd07d46aa1137a7acovener#include "util_time.h"
b8efdc95bec9cf089aa1be0bfd07d46aa1137a7acovener#include "ap_mpm.h"
b8efdc95bec9cf089aa1be0bfd07d46aa1137a7acovener
f06e7c4b1bce6b6491e5de0b7998d3f5696b293dchrisd#if APR_HAVE_UNISTD_H
f06e7c4b1bce6b6491e5de0b7998d3f5696b293dchrisd#include <unistd.h>
f06e7c4b1bce6b6491e5de0b7998d3f5696b293dchrisd#endif
179565be4043d7e5f9161aa75271fa0a001866d9covener#ifdef HAVE_LIMITS_H
179565be4043d7e5f9161aa75271fa0a001866d9covener#include <limits.h>
179565be4043d7e5f9161aa75271fa0a001866d9covener#endif
111436a32ba1254291e4883292fb116d15fe8f64covener
fce4949fb0b309a5744afcd503c6ed2d35621ee2covener#define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %>s %b"
fce4949fb0b309a5744afcd503c6ed2d35621ee2covener
fce4949fb0b309a5744afcd503c6ed2d35621ee2covenermodule AP_MODULE_DECLARE_DATA log_config_module;
fce4949fb0b309a5744afcd503c6ed2d35621ee2covener
7b7430e701e9a31ce809da7c220bb8dfcf68c86etrawick
7b7430e701e9a31ce809da7c220bb8dfcf68c86etrawickstatic int xfer_flags = (APR_WRITE | APR_APPEND | APR_CREATE | APR_LARGEFILE);
7b7430e701e9a31ce809da7c220bb8dfcf68c86etrawickstatic apr_fileperms_t xfer_perms = APR_OS_DEFAULT;
ccc20788c1e5fc973f36df634399c89acb70deaejerenkrantzstatic apr_hash_t *log_hash;
ccc20788c1e5fc973f36df634399c89acb70deaejerenkrantzstatic apr_status_t ap_default_log_writer(request_rec *r,
ccc20788c1e5fc973f36df634399c89acb70deaejerenkrantz void *handle,
273e512f20f262e5e2aa8e0e83371d1929fb76adjkaluza const char **strs,
273e512f20f262e5e2aa8e0e83371d1929fb76adjkaluza int *strl,
273e512f20f262e5e2aa8e0e83371d1929fb76adjkaluza int nelts,
efe780dcf13b2b95effabf897d694d8f23feac74trawick apr_size_t len);
fe83f60b41477b14a37edcfcd1f7f5c5a1ebfe44minfrinstatic apr_status_t ap_buffered_log_writer(request_rec *r,
fe83f60b41477b14a37edcfcd1f7f5c5a1ebfe44minfrin void *handle,
fe83f60b41477b14a37edcfcd1f7f5c5a1ebfe44minfrin const char **strs,
993d1261a278d7322bccef219101220b7b4fb8c5jkaluza int *strl,
993d1261a278d7322bccef219101220b7b4fb8c5jkaluza int nelts,
993d1261a278d7322bccef219101220b7b4fb8c5jkaluza apr_size_t len);
ba050a6f942b9fa0e81ed73437588005c569655ccovenerstatic void *ap_default_log_writer_init(apr_pool_t *p, server_rec *s,
ba050a6f942b9fa0e81ed73437588005c569655ccovener const char* name);
ba050a6f942b9fa0e81ed73437588005c569655ccovenerstatic void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s,
ba050a6f942b9fa0e81ed73437588005c569655ccovener const char* name);
135ddda3a989215d2bedbcf1529bfb269c3eda23niq
135ddda3a989215d2bedbcf1529bfb269c3eda23niqstatic ap_log_writer_init* ap_log_set_writer_init(ap_log_writer_init *handle);
135ddda3a989215d2bedbcf1529bfb269c3eda23niqstatic ap_log_writer* ap_log_set_writer(ap_log_writer *handle);
001a44c352f89c9ec332ffd3e0a6927dcd19432chumbedoohstatic ap_log_writer *log_writer = ap_default_log_writer;
001a44c352f89c9ec332ffd3e0a6927dcd19432chumbedoohstatic ap_log_writer_init *log_writer_init = ap_default_log_writer_init;
001a44c352f89c9ec332ffd3e0a6927dcd19432chumbedoohstatic int buffered_logs = 0; /* default unbuffered */
efe780dcf13b2b95effabf897d694d8f23feac74trawickstatic apr_array_header_t *all_buffered_logs = NULL;
793214f67dede32edfd9ee96c664ead04d175cbbjfclere
cc5a4a08dc9783fcbc52ce86f11e01c281a43810minfrin/* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is
33124689065ade0dfc8c54d8ebb734f9439cb89btrawick * guaranteed to be atomic when writing a pipe. And PIPE_BUF >= 512
33124689065ade0dfc8c54d8ebb734f9439cb89btrawick * is guaranteed. So we'll just guess 512 in the event the system
33124689065ade0dfc8c54d8ebb734f9439cb89btrawick * doesn't have this. Now, for file writes there is actually no limit,
9b0076ddd1103e5fa9c1f9bafde4b06ce244fbaecovener * the entire write is atomic. Whether all systems implement this
9b0076ddd1103e5fa9c1f9bafde4b06ce244fbaecovener * correctly is another question entirely ... so we'll just use PIPE_BUF
9b0076ddd1103e5fa9c1f9bafde4b06ce244fbaecovener * because it's probably a good guess as to what is implemented correctly
249d09d51808cb7981af99762c3b3736ca126cd5jkaluza * everywhere.
249d09d51808cb7981af99762c3b3736ca126cd5jkaluza */
249d09d51808cb7981af99762c3b3736ca126cd5jkaluza#ifdef PIPE_BUF
249d09d51808cb7981af99762c3b3736ca126cd5jkaluza#define LOG_BUFSIZE PIPE_BUF
56589be3d7a3e9343370df240010c6928cc78b39jkaluza#else
56589be3d7a3e9343370df240010c6928cc78b39jkaluza#define LOG_BUFSIZE (512)
56589be3d7a3e9343370df240010c6928cc78b39jkaluza#endif
77ca16c5676da23155311e13cee61e7eaba9fa3ejailletc
77ca16c5676da23155311e13cee61e7eaba9fa3ejailletc/*
77ca16c5676da23155311e13cee61e7eaba9fa3ejailletc * multi_log_state is our per-(virtual)-server configuration. We store
77ca16c5676da23155311e13cee61e7eaba9fa3ejailletc * an array of the logs we are going to use, each of type config_log_state.
f87299dab99bc04b51a6b8cad51b6795db862c0atrawick * If a default log format is given by LogFormat, store in default_format
f87299dab99bc04b51a6b8cad51b6795db862c0atrawick * (backward compat. with mod_log_config). We also store for each virtual
f87299dab99bc04b51a6b8cad51b6795db862c0atrawick * server a pointer to the logs specified for the main server, so that if this
4d12805e6c18253040223ea637acd6b3b3c18f60jorton * vhost has no logs defined, we can use the main server's logs instead.
4d12805e6c18253040223ea637acd6b3b3c18f60jorton *
4d12805e6c18253040223ea637acd6b3b3c18f60jorton * So, for the main server, config_logs contains a list of the log files
4d12805e6c18253040223ea637acd6b3b3c18f60jorton * and server_config_logs is empty. For a vhost, server_config_logs
4d12805e6c18253040223ea637acd6b3b3c18f60jorton * points to the same array as config_logs in the main server, and
e5d909f2b06bd880fb3675cd49363df981caa631trawick * config_logs points to the array of logs defined inside this vhost,
a4df2cd1e1391575a327c2a90ba4315f805a0a78covener * which might be empty.
a4df2cd1e1391575a327c2a90ba4315f805a0a78covener */
a4df2cd1e1391575a327c2a90ba4315f805a0a78covener
cb666b29f81df1d11d65002250153353568021fccovenertypedef struct {
cb666b29f81df1d11d65002250153353568021fccovener const char *default_format_string;
cb666b29f81df1d11d65002250153353568021fccovener apr_array_header_t *default_format;
6a80c3c6f4b8ea7ba5e89402b8b779b09ce020e0covener apr_array_header_t *config_logs;
1c2cab00d988fc48cbe59032cf76cc0bab20d6f7covener apr_array_header_t *server_config_logs;
6a80c3c6f4b8ea7ba5e89402b8b779b09ce020e0covener apr_table_t *formats;
75a230a728338d84dcfe81edd375352f34de22d0covener} multi_log_state;
75a230a728338d84dcfe81edd375352f34de22d0covener
75a230a728338d84dcfe81edd375352f34de22d0covener/*
1f50dc34ae069adeed20b2986e5ffdefa5c410e0covener * config_log_state holds the status of a single log file. fname might
1f50dc34ae069adeed20b2986e5ffdefa5c410e0covener * be NULL, which means this module does no logging for this
1f50dc34ae069adeed20b2986e5ffdefa5c410e0covener * request. format might be NULL, in which case the default_format
63a5ea80bddcc84a462e40f402b4f330e0e05411covener * from the multi_log_state should be used, or if that is NULL as
63a5ea80bddcc84a462e40f402b4f330e0e05411covener * well, use the CLF.
63a5ea80bddcc84a462e40f402b4f330e0e05411covener * log_writer is NULL before the log file is opened and is
63a5ea80bddcc84a462e40f402b4f330e0e05411covener * set to a opaque structure (usually a fd) after it is opened.
65a4e663b82f8bce28ac22ab2edfd7502de36998sf
65a4e663b82f8bce28ac22ab2edfd7502de36998sf */
65a4e663b82f8bce28ac22ab2edfd7502de36998sftypedef struct {
65a4e663b82f8bce28ac22ab2edfd7502de36998sf apr_file_t *handle;
c7de1955eb0eaeabf7042902476397692672d549sf apr_size_t outcnt;
74e7f6c55fd67b10cb400b3f6d1dc718a303d944minfrin char outbuf[LOG_BUFSIZE];
74e7f6c55fd67b10cb400b3f6d1dc718a303d944minfrin apr_anylock_t mutex;
74e7f6c55fd67b10cb400b3f6d1dc718a303d944minfrin} buffered_log;
74e7f6c55fd67b10cb400b3f6d1dc718a303d944minfrin
a511a29faf2ff7ead3b67680154a624effb31aafminfrintypedef struct {
a511a29faf2ff7ead3b67680154a624effb31aafminfrin const char *fname;
a511a29faf2ff7ead3b67680154a624effb31aafminfrin const char *format_string;
a511a29faf2ff7ead3b67680154a624effb31aafminfrin apr_array_header_t *format;
a511a29faf2ff7ead3b67680154a624effb31aafminfrin void *log_writer;
63921358ef93fcb41bc71d9894221ba3d7fbb87bminfrin char *condition_var;
63921358ef93fcb41bc71d9894221ba3d7fbb87bminfrin} config_log_state;
63921358ef93fcb41bc71d9894221ba3d7fbb87bminfrin
deec48c67d4786bc77112ffbf3a4e70b931097edminfrin/*
6d601599d3d65df0410eae6e573e75b2dbfb1fb4minfrin * Format items...
6d601599d3d65df0410eae6e573e75b2dbfb1fb4minfrin * Note that many of these could have ap_sprintfs replaced with static buffers.
6d601599d3d65df0410eae6e573e75b2dbfb1fb4minfrin */
6d601599d3d65df0410eae6e573e75b2dbfb1fb4minfrin
684e0cfc200f66287a93bbd1708d1dd8a92a7eefcovenertypedef struct {
684e0cfc200f66287a93bbd1708d1dd8a92a7eefcovener ap_log_handler_fn_t *func;
5c43d2fb853f84497b5ece2d414ef9484aa87e5fsf char *arg;
05a5a9c3e16f21566e1b61f4bd68025ce1b741ccjoes int condition_sense;
05a5a9c3e16f21566e1b61f4bd68025ce1b741ccjoes int want_orig;
ef82e8fa164e0a1f8b813f7deb6b7ead96018c94niq apr_array_header_t *conditions;
26c5829347f6a355c00f1ba0301d575056b69536niq} log_format_item;
ef82e8fa164e0a1f8b813f7deb6b7ead96018c94niq
ef82e8fa164e0a1f8b813f7deb6b7ead96018c94niqstatic char *pfmt(apr_pool_t *p, int i)
ef82e8fa164e0a1f8b813f7deb6b7ead96018c94niq{
ef82e8fa164e0a1f8b813f7deb6b7ead96018c94niq if (i <= 0) {
ef82e8fa164e0a1f8b813f7deb6b7ead96018c94niq return "-";
ef82e8fa164e0a1f8b813f7deb6b7ead96018c94niq }
413ee814748f37be168ff12407fa6dba0ceeabe6trawick else {
c12917da693bae4028a1d5a5e8224bceed8c739dsf return apr_itoa(p, i);
c12917da693bae4028a1d5a5e8224bceed8c739dsf }
eafcc0ebf263d0ba69855b6e10958c4c1a2361bdsf}
eafcc0ebf263d0ba69855b6e10958c4c1a2361bdsf
eafcc0ebf263d0ba69855b6e10958c4c1a2361bdsfstatic const char *constant_item(request_rec *dummy, char *stuff)
eafcc0ebf263d0ba69855b6e10958c4c1a2361bdsf{
eafcc0ebf263d0ba69855b6e10958c4c1a2361bdsf return stuff;
d7ffd2da16d58b1a0de212e4d56f7aebb72bef26sf}
d7ffd2da16d58b1a0de212e4d56f7aebb72bef26sf
d7ffd2da16d58b1a0de212e4d56f7aebb72bef26sfstatic const char *log_remote_host(request_rec *r, char *a)
4576c1a9ef54cd1e5555ee07d016a7f559f80338sf{
4576c1a9ef54cd1e5555ee07d016a7f559f80338sf return ap_escape_logitem(r->pool, ap_get_remote_host(r->connection,
4576c1a9ef54cd1e5555ee07d016a7f559f80338sf r->per_dir_config,
9811aed12bbc71783d2e544ccb5fecd193843eadsf REMOTE_NAME, NULL));
9811aed12bbc71783d2e544ccb5fecd193843eadsf}
9811aed12bbc71783d2e544ccb5fecd193843eadsf
88fac54d9d64f85bbdab5d7010816f4377f95bd7rjungstatic const char *log_remote_address(request_rec *r, char *a)
88fac54d9d64f85bbdab5d7010816f4377f95bd7rjung{
bd3f5647b96d378d9c75c954e3f13582af32c643sf return r->connection->remote_ip;
bd3f5647b96d378d9c75c954e3f13582af32c643sf}
bd3f5647b96d378d9c75c954e3f13582af32c643sf
bd3f5647b96d378d9c75c954e3f13582af32c643sfstatic const char *log_local_address(request_rec *r, char *a)
bd3f5647b96d378d9c75c954e3f13582af32c643sf{
2a7beea91d46beb41f043a84eaad060047ee04aafabien return r->connection->local_ip;
2a7beea91d46beb41f043a84eaad060047ee04aafabien}
2a7beea91d46beb41f043a84eaad060047ee04aafabien
2a7beea91d46beb41f043a84eaad060047ee04aafabienstatic const char *log_remote_logname(request_rec *r, char *a)
584a85dd4047e38d3ed3a29b6662fcc9d100ae4csf{
584a85dd4047e38d3ed3a29b6662fcc9d100ae4csf return ap_escape_logitem(r->pool, ap_get_remote_logname(r));
584a85dd4047e38d3ed3a29b6662fcc9d100ae4csf}
f21e9e3d0bfb7a507ecc5bc963f2159d693503d1sf
f21e9e3d0bfb7a507ecc5bc963f2159d693503d1sfstatic const char *log_remote_user(request_rec *r, char *a)
f21e9e3d0bfb7a507ecc5bc963f2159d693503d1sf{
f6b9c755a0b793e8a3a3aebd327ca20a86478117sf char *rvalue = r->user;
f6b9c755a0b793e8a3a3aebd327ca20a86478117sf
f6b9c755a0b793e8a3a3aebd327ca20a86478117sf if (rvalue == NULL) {
132ee6ac1c26d6e8953836316ba50734eefab47bsf rvalue = "-";
132ee6ac1c26d6e8953836316ba50734eefab47bsf }
132ee6ac1c26d6e8953836316ba50734eefab47bsf else if (strlen(rvalue) == 0) {
85eacfc96a04547ef25aabbc06440039715084c2jorton rvalue = "\"\"";
85eacfc96a04547ef25aabbc06440039715084c2jorton }
85eacfc96a04547ef25aabbc06440039715084c2jorton else {
536d2e7cd1fdec1255b8c3bdf41fdc714c506a54trawick rvalue = ap_escape_logitem(r->pool, rvalue);
536d2e7cd1fdec1255b8c3bdf41fdc714c506a54trawick }
536d2e7cd1fdec1255b8c3bdf41fdc714c506a54trawick
536d2e7cd1fdec1255b8c3bdf41fdc714c506a54trawick return rvalue;
79c5787b92ac5f0e1cc82393816c77a006399316trawick}
79c5787b92ac5f0e1cc82393816c77a006399316trawick
79c5787b92ac5f0e1cc82393816c77a006399316trawickstatic const char *log_request_line(request_rec *r, char *a)
79c5787b92ac5f0e1cc82393816c77a006399316trawick{
c967bf3bc89e8aa60dbd30d9da388e448ddc1cc4trawick /* NOTE: If the original request contained a password, we
79c5787b92ac5f0e1cc82393816c77a006399316trawick * re-write the request line here to contain XXXXXX instead:
79c5787b92ac5f0e1cc82393816c77a006399316trawick * (note the truncation before the protocol string for HTTP/0.9 requests)
79c5787b92ac5f0e1cc82393816c77a006399316trawick * (note also that r->the_request contains the unmodified request)
79c5787b92ac5f0e1cc82393816c77a006399316trawick */
79c5787b92ac5f0e1cc82393816c77a006399316trawick return ap_escape_logitem(r->pool,
7b395e4e878c28a4784919cfd2e704ddd14a3390jorton (r->parsed_uri.password)
7b395e4e878c28a4784919cfd2e704ddd14a3390jorton ? apr_pstrcat(r->pool, r->method, " ",
7b395e4e878c28a4784919cfd2e704ddd14a3390jorton apr_uri_unparse(r->pool,
7b395e4e878c28a4784919cfd2e704ddd14a3390jorton &r->parsed_uri, 0),
536e48c08d674acac5d44929318f2ad928edc361jorton r->assbackwards ? NULL : " ",
536e48c08d674acac5d44929318f2ad928edc361jorton r->protocol, NULL)
e81785da447b469da66f218b3f0244aab507958djorton : r->the_request);
e81785da447b469da66f218b3f0244aab507958djorton}
3e4e54d4e3fc0123c63d57aa84ac7ad7a8c73ff8jorton
3e4e54d4e3fc0123c63d57aa84ac7ad7a8c73ff8jortonstatic const char *log_request_file(request_rec *r, char *a)
3e4e54d4e3fc0123c63d57aa84ac7ad7a8c73ff8jorton{
53e9b27aba029b18be814df40bcf6f0428771d1efuankg return ap_escape_logitem(r->pool, r->filename);
53e9b27aba029b18be814df40bcf6f0428771d1efuankg}
53e9b27aba029b18be814df40bcf6f0428771d1efuankgstatic const char *log_request_uri(request_rec *r, char *a)
53e9b27aba029b18be814df40bcf6f0428771d1efuankg{
53e9b27aba029b18be814df40bcf6f0428771d1efuankg return ap_escape_logitem(r->pool, r->uri);
6bb524f1895f30265a1431afc460977d391cb36bsf}
6bb524f1895f30265a1431afc460977d391cb36bsfstatic const char *log_request_method(request_rec *r, char *a)
ca61ccd0c306c2c72df153688ba1b49f3eceed80sf{
6bb524f1895f30265a1431afc460977d391cb36bsf return ap_escape_logitem(r->pool, r->method);
e6dd71992459d05a676b98b7963423dc5dc1e24aminfrin}
e6dd71992459d05a676b98b7963423dc5dc1e24aminfrinstatic const char *log_request_protocol(request_rec *r, char *a)
e6dd71992459d05a676b98b7963423dc5dc1e24aminfrin{
e6dd71992459d05a676b98b7963423dc5dc1e24aminfrin return ap_escape_logitem(r->pool, r->protocol);
23f1535d6a60817d2846bac0aea230ea475d7dccminfrin}
23f1535d6a60817d2846bac0aea230ea475d7dccminfrinstatic const char *log_request_query(request_rec *r, char *a)
23f1535d6a60817d2846bac0aea230ea475d7dccminfrin{
23f1535d6a60817d2846bac0aea230ea475d7dccminfrin return (r->args) ? apr_pstrcat(r->pool, "?",
ec7520b24cd80d34d82bbcaca153cbb23cc04bc0rjung ap_escape_logitem(r->pool, r->args), NULL)
ec7520b24cd80d34d82bbcaca153cbb23cc04bc0rjung : "";
ec7520b24cd80d34d82bbcaca153cbb23cc04bc0rjung}
ec7520b24cd80d34d82bbcaca153cbb23cc04bc0rjungstatic const char *log_status(request_rec *r, char *a)
ec7520b24cd80d34d82bbcaca153cbb23cc04bc0rjung{
ec7520b24cd80d34d82bbcaca153cbb23cc04bc0rjung return pfmt(r->pool, r->status);
ec7520b24cd80d34d82bbcaca153cbb23cc04bc0rjung}
ec7520b24cd80d34d82bbcaca153cbb23cc04bc0rjung
6249dfa569d3b4f1f539665b979a80c6e335d93etrawickstatic const char *clf_log_bytes_sent(request_rec *r, char *a)
6249dfa569d3b4f1f539665b979a80c6e335d93etrawick{
0827cb14e550f6f65018431c22c2c913631c8f25kbrand if (!r->sent_bodyct || !r->bytes_sent) {
6249dfa569d3b4f1f539665b979a80c6e335d93etrawick return "-";
ae600ca541efc686b34f8b1f21bd3d0741d37674covener }
6249dfa569d3b4f1f539665b979a80c6e335d93etrawick else {
cfa64348224b66dd1c9979b809406c4d15b1c137fielding return apr_off_t_toa(r->pool, r->bytes_sent);
74499a117b3b2cd9666715a14f90c0e5d1a4ee8ajim }
cfa64348224b66dd1c9979b809406c4d15b1c137fielding}
74499a117b3b2cd9666715a14f90c0e5d1a4ee8ajim
cfa64348224b66dd1c9979b809406c4d15b1c137fieldingstatic const char *log_bytes_sent(request_rec *r, char *a)
74499a117b3b2cd9666715a14f90c0e5d1a4ee8ajim{
cfa64348224b66dd1c9979b809406c4d15b1c137fielding if (!r->sent_bodyct || !r->bytes_sent) {
74499a117b3b2cd9666715a14f90c0e5d1a4ee8ajim return "0";
cfa64348224b66dd1c9979b809406c4d15b1c137fielding }
else {
return apr_off_t_toa(r->pool, r->bytes_sent);
}
}
static const char *log_header_in(request_rec *r, char *a)
{
return ap_escape_logitem(r->pool, apr_table_get(r->headers_in, a));
}
static APR_INLINE char *find_multiple_headers(apr_pool_t *pool,
const apr_table_t *table,
const char *key)
{
const apr_array_header_t *elts;
const apr_table_entry_t *t_elt;
const apr_table_entry_t *t_end;
apr_size_t len;
struct sle {
struct sle *next;
const char *value;
apr_size_t len;
} *result_list, *rp;
elts = apr_table_elts(table);
if (!elts->nelts) {
return NULL;
}
t_elt = (const apr_table_entry_t *)elts->elts;
t_end = t_elt + elts->nelts;
len = 1; /* \0 */
result_list = rp = NULL;
do {
if (!strcasecmp(t_elt->key, key)) {
if (!result_list) {
result_list = rp = apr_palloc(pool, sizeof(*rp));
}
else {
rp = rp->next = apr_palloc(pool, sizeof(*rp));
len += 2; /* ", " */
}
rp->next = NULL;
rp->value = t_elt->val;
rp->len = strlen(rp->value);
len += rp->len;
}
++t_elt;
} while (t_elt < t_end);
if (result_list) {
char *result = apr_palloc(pool, len);
char *cp = result;
rp = result_list;
while (rp) {
if (rp != result_list) {
*cp++ = ',';
*cp++ = ' ';
}
memcpy(cp, rp->value, rp->len);
cp += rp->len;
rp = rp->next;
}
*cp = '\0';
return result;
}
return NULL;
}
static const char *log_header_out(request_rec *r, char *a)
{
const char *cp = NULL;
if (!strcasecmp(a, "Content-type") && r->content_type) {
cp = ap_field_noparam(r->pool, r->content_type);
}
else if (!strcasecmp(a, "Set-Cookie")) {
cp = find_multiple_headers(r->pool, r->headers_out, a);
}
else {
cp = apr_table_get(r->headers_out, a);
}
return ap_escape_logitem(r->pool, cp);
}
static const char *log_note(request_rec *r, char *a)
{
return ap_escape_logitem(r->pool, apr_table_get(r->notes, a));
}
static const char *log_env_var(request_rec *r, char *a)
{
return ap_escape_logitem(r->pool, apr_table_get(r->subprocess_env, a));
}
static const char *log_cookie(request_rec *r, char *a)
{
const char *cookies;
const char *start_cookie;
if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
if ((start_cookie = ap_strstr_c(cookies,a))) {
char *cookie, *end_cookie;
start_cookie += strlen(a) + 1; /* cookie_name + '=' */
cookie = apr_pstrdup(r->pool, start_cookie);
/* kill everything in cookie after ';' */
end_cookie = strchr(cookie, ';');
if (end_cookie) {
*end_cookie = '\0';
}
return ap_escape_logitem(r->pool, cookie);
}
}
return NULL;
}
static const char *log_request_time_custom(request_rec *r, char *a,
apr_time_exp_t *xt)
{
apr_size_t retcode;
char tstr[MAX_STRING_LEN];
apr_strftime(tstr, &retcode, sizeof(tstr), a, xt);
return apr_pstrdup(r->pool, tstr);
}
#define DEFAULT_REQUEST_TIME_SIZE 32
typedef struct {
unsigned t;
char timestr[DEFAULT_REQUEST_TIME_SIZE];
unsigned t_validate;
} cached_request_time;
#define TIME_CACHE_SIZE 4
#define TIME_CACHE_MASK 3
static cached_request_time request_time_cache[TIME_CACHE_SIZE];
static const char *log_request_time(request_rec *r, char *a)
{
apr_time_exp_t xt;
/* ### I think getting the time again at the end of the request
* just for logging is dumb. i know it's "required" for CLF.
* folks writing log parsing tools don't realise that out of order
* times have always been possible (consider what happens if one
* process calculates the time to log, but then there's a context
* switch before it writes and before that process is run again the
* log rotation occurs) and they should just fix their tools rather
* than force the server to pay extra cpu cycles. if you've got
* a problem with this, you can set the define. -djg
*/
if (a && *a) { /* Custom format */
/* The custom time formatting uses a very large temp buffer
* on the stack. To avoid using so much stack space in the
* common case where we're not using a custom format, the code
* for the custom format in a separate function. (That's why
* log_request_time_custom is not inlined right here.)
*/
#ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
ap_explode_recent_localtime(&xt, apr_time_now());
#else
ap_explode_recent_localtime(&xt, r->request_time);
#endif
return log_request_time_custom(r, a, &xt);
}
else { /* CLF format */
/* This code uses the same technique as ap_explode_recent_localtime():
* optimistic caching with logic to detect and correct race conditions.
* See the comments in server/util_time.c for more information.
*/
cached_request_time* cached_time = apr_palloc(r->pool,
sizeof(*cached_time));
#ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
apr_time_t request_time = apr_time_now();
#else
apr_time_t request_time = r->request_time;
#endif
unsigned t_seconds = (unsigned)apr_time_sec(request_time);
unsigned i = t_seconds & TIME_CACHE_MASK;
*cached_time = request_time_cache[i];
if ((t_seconds != cached_time->t) ||
(t_seconds != cached_time->t_validate)) {
/* Invalid or old snapshot, so compute the proper time string
* and store it in the cache
*/
char sign;
int timz;
ap_explode_recent_localtime(&xt, request_time);
timz = xt.tm_gmtoff;
if (timz < 0) {
timz = -timz;
sign = '-';
}
else {
sign = '+';
}
cached_time->t = t_seconds;
apr_snprintf(cached_time->timestr, DEFAULT_REQUEST_TIME_SIZE,
"[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]",
xt.tm_mday, apr_month_snames[xt.tm_mon],
xt.tm_year+1900, xt.tm_hour, xt.tm_min, xt.tm_sec,
sign, timz / (60*60), (timz % (60*60)) / 60);
cached_time->t_validate = t_seconds;
request_time_cache[i] = *cached_time;
}
return cached_time->timestr;
}
}
static const char *log_request_duration(request_rec *r, char *a)
{
apr_time_t duration = apr_time_now() - r->request_time;
return apr_psprintf(r->pool, "%" APR_TIME_T_FMT, apr_time_sec(duration));
}
static const char *log_request_duration_microseconds(request_rec *r, char *a)
{
return apr_psprintf(r->pool, "%" APR_TIME_T_FMT,
(apr_time_now() - r->request_time));
}
/* These next two routines use the canonical name:port so that log
* parsers don't need to duplicate all the vhost parsing crud.
*/
static const char *log_virtual_host(request_rec *r, char *a)
{
return ap_escape_logitem(r->pool, r->server->server_hostname);
}
static const char *log_server_port(request_rec *r, char *a)
{
apr_port_t port;
if (*a == '\0' || !strcasecmp(a, "canonical")) {
port = r->server->port ? r->server->port : ap_default_port(r);
}
else if (!strcasecmp(a, "remote")) {
port = r->connection->remote_addr->port;
}
else if (!strcasecmp(a, "local")) {
port = r->connection->local_addr->port;
}
else {
/* bogus format */
return a;
}
return apr_itoa(r->pool, (int)port);
}
/* This respects the setting of UseCanonicalName so that
* the dynamic mass virtual hosting trick works better.
*/
static const char *log_server_name(request_rec *r, char *a)
{
return ap_escape_logitem(r->pool, ap_get_server_name(r));
}
static const char *log_pid_tid(request_rec *r, char *a)
{
if (*a == '\0' || !strcasecmp(a, "pid")) {
return ap_append_pid(r->pool, "", "");
}
else if (!strcasecmp(a, "tid") || !strcasecmp(a, "hextid")) {
#if APR_HAS_THREADS
apr_os_thread_t tid = apr_os_thread_current();
#else
int tid = 0; /* APR will format "0" anyway but an arg is needed */
#endif
return apr_psprintf(r->pool,
#if APR_MAJOR_VERSION > 1 || (APR_MAJOR_VERSION == 1 && APR_MINOR_VERSION >= 2)
/* APR can format a thread id in hex */
*a == 'h' ? "%pt" : "%pT",
#else
/* APR is missing the feature, so always use decimal */
"%pT",
#endif
&tid);
}
/* bogus format */
return a;
}
static const char *log_connection_status(request_rec *r, char *a)
{
if (r->connection->aborted)
return "X";
if (r->connection->keepalive == AP_CONN_KEEPALIVE &&
(!r->server->keep_alive_max ||
(r->server->keep_alive_max - r->connection->keepalives) > 0)) {
return "+";
}
return "-";
}
static const char *log_requests_on_connection(request_rec *r, char *a)
{
return apr_itoa(r->pool, r->connection->keepalives);
}
/*****************************************************************
*
* Parsing the log format string
*/
static char *parse_log_misc_string(apr_pool_t *p, log_format_item *it,
const char **sa)
{
const char *s;
char *d;
it->func = constant_item;
it->conditions = NULL;
s = *sa;
while (*s && *s != '%') {
s++;
}
/*
* This might allocate a few chars extra if there's a backslash
* escape in the format string.
*/
it->arg = apr_palloc(p, s - *sa + 1);
d = it->arg;
s = *sa;
while (*s && *s != '%') {
if (*s != '\\') {
*d++ = *s++;
}
else {
s++;
switch (*s) {
case '\\':
*d++ = '\\';
s++;
break;
case 'r':
*d++ = '\r';
s++;
break;
case 'n':
*d++ = '\n';
s++;
break;
case 't':
*d++ = '\t';
s++;
break;
default:
/* copy verbatim */
*d++ = '\\';
/*
* Allow the loop to deal with this *s in the normal
* fashion so that it handles end of string etc.
* properly.
*/
break;
}
}
}
*d = '\0';
*sa = s;
return NULL;
}
static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char **sa)
{
const char *s = *sa;
ap_log_handler *handler;
if (*s != '%') {
return parse_log_misc_string(p, it, sa);
}
++s;
it->condition_sense = 0;
it->conditions = NULL;
if (*s == '%') {
it->arg = "%";
it->func = constant_item;
*sa = ++s;
return NULL;
}
it->want_orig = -1;
it->arg = ""; /* For safety's sake... */
while (*s) {
int i;
switch (*s) {
case '!':
++s;
it->condition_sense = !it->condition_sense;
break;
case '<':
++s;
it->want_orig = 1;
break;
case '>':
++s;
it->want_orig = 0;
break;
case ',':
++s;
break;
case '{':
++s;
it->arg = ap_getword(p, &s, '}');
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
i = *s - '0';
while (apr_isdigit(*++s)) {
i = i * 10 + (*s) - '0';
}
if (!it->conditions) {
it->conditions = apr_array_make(p, 4, sizeof(int));
}
*(int *) apr_array_push(it->conditions) = i;
break;
default:
handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1);
if (!handler) {
char dummy[2];
dummy[0] = s[-1];
dummy[1] = '\0';
return apr_pstrcat(p, "Unrecognized LogFormat directive %",
dummy, NULL);
}
it->func = handler->func;
if (it->want_orig == -1) {
it->want_orig = handler->want_orig_default;
}
*sa = s;
return NULL;
}
}
return "Ran off end of LogFormat parsing args to some directive";
}
static apr_array_header_t *parse_log_string(apr_pool_t *p, const char *s, const char **err)
{
apr_array_header_t *a = apr_array_make(p, 30, sizeof(log_format_item));
char *res;
while (*s) {
if ((res = parse_log_item(p, (log_format_item *) apr_array_push(a), &s))) {
*err = res;
return NULL;
}
}
s = APR_EOL_STR;
parse_log_item(p, (log_format_item *) apr_array_push(a), &s);
return a;
}
/*****************************************************************
*
* Actually logging.
*/
static const char *process_item(request_rec *r, request_rec *orig,
log_format_item *item)
{
const char *cp;
/* First, see if we need to process this thing at all... */
if (item->conditions && item->conditions->nelts != 0) {
int i;
int *conds = (int *) item->conditions->elts;
int in_list = 0;
for (i = 0; i < item->conditions->nelts; ++i) {
if (r->status == conds[i]) {
in_list = 1;
break;
}
}
if ((item->condition_sense && in_list)
|| (!item->condition_sense && !in_list)) {
return "-";
}
}
/* We do. Do it... */
cp = (*item->func) (item->want_orig ? orig : r, item->arg);
return cp ? cp : "-";
}
static void flush_log(buffered_log *buf)
{
if (buf->outcnt && buf->handle != NULL) {
apr_file_write(buf->handle, buf->outbuf, &buf->outcnt);
buf->outcnt = 0;
}
}
static int config_log_transaction(request_rec *r, config_log_state *cls,
apr_array_header_t *default_format)
{
log_format_item *items;
const char **strs;
int *strl;
request_rec *orig;
int i;
apr_size_t len = 0;
apr_array_header_t *format;
char *envar;
apr_status_t rv;
if (cls->fname == NULL) {
return DECLINED;
}
/*
* See if we've got any conditional envariable-controlled logging decisions
* to make.
*/
if (cls->condition_var != NULL) {
envar = cls->condition_var;
if (*envar != '!') {
if (apr_table_get(r->subprocess_env, envar) == NULL) {
return DECLINED;
}
}
else {
if (apr_table_get(r->subprocess_env, &envar[1]) != NULL) {
return DECLINED;
}
}
}
format = cls->format ? cls->format : default_format;
strs = apr_palloc(r->pool, sizeof(char *) * (format->nelts));
strl = apr_palloc(r->pool, sizeof(int) * (format->nelts));
items = (log_format_item *) format->elts;
orig = r;
while (orig->prev) {
orig = orig->prev;
}
while (r->next) {
r = r->next;
}
for (i = 0; i < format->nelts; ++i) {
strs[i] = process_item(r, orig, &items[i]);
}
for (i = 0; i < format->nelts; ++i) {
len += strl[i] = strlen(strs[i]);
}
if (!log_writer) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r,
"log writer isn't correctly setup");
return HTTP_INTERNAL_SERVER_ERROR;
}
rv = log_writer(r, cls->log_writer, strs, strl, format->nelts, len);
/* xxx: do we return an error on log_writer? */
return OK;
}
static int multi_log_transaction(request_rec *r)
{
multi_log_state *mls = ap_get_module_config(r->server->module_config,
&log_config_module);
config_log_state *clsarray;
int i;
/*
* Log this transaction..
*/
if (mls->config_logs->nelts) {
clsarray = (config_log_state *) mls->config_logs->elts;
for (i = 0; i < mls->config_logs->nelts; ++i) {
config_log_state *cls = &clsarray[i];
config_log_transaction(r, cls, mls->default_format);
}
}
else if (mls->server_config_logs) {
clsarray = (config_log_state *) mls->server_config_logs->elts;
for (i = 0; i < mls->server_config_logs->nelts; ++i) {
config_log_state *cls = &clsarray[i];
config_log_transaction(r, cls, mls->default_format);
}
}
return OK;
}
/*****************************************************************
*
* Module glue...
*/
static void *make_config_log_state(apr_pool_t *p, server_rec *s)
{
multi_log_state *mls;
mls = (multi_log_state *) apr_palloc(p, sizeof(multi_log_state));
mls->config_logs = apr_array_make(p, 1, sizeof(config_log_state));
mls->default_format_string = NULL;
mls->default_format = NULL;
mls->server_config_logs = NULL;
mls->formats = apr_table_make(p, 4);
apr_table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
return mls;
}
/*
* Use the merger to simply add a pointer from the vhost log state
* to the log of logs specified for the non-vhost configuration. Make sure
* vhosts inherit any globally-defined format names.
*/
static void *merge_config_log_state(apr_pool_t *p, void *basev, void *addv)
{
multi_log_state *base = (multi_log_state *) basev;
multi_log_state *add = (multi_log_state *) addv;
add->server_config_logs = base->config_logs;
if (!add->default_format) {
add->default_format_string = base->default_format_string;
add->default_format = base->default_format;
}
add->formats = apr_table_overlay(p, base->formats, add->formats);
return add;
}
/*
* Set the default logfile format, or define a nickname for a format string.
*/
static const char *log_format(cmd_parms *cmd, void *dummy, const char *fmt,
const char *name)
{
const char *err_string = NULL;
multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
&log_config_module);
/*
* If we were given two arguments, the second is a name to be given to the
* format. This syntax just defines the nickname - it doesn't actually
* make the format the default.
*/
if (name != NULL) {
parse_log_string(cmd->pool, fmt, &err_string);
if (err_string == NULL) {
apr_table_setn(mls->formats, name, fmt);
}
}
else {
mls->default_format_string = fmt;
mls->default_format = parse_log_string(cmd->pool, fmt, &err_string);
}
return err_string;
}
static const char *add_custom_log(cmd_parms *cmd, void *dummy, const char *fn,
const char *fmt, const char *envclause)
{
const char *err_string = NULL;
multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
&log_config_module);
config_log_state *cls;
cls = (config_log_state *) apr_array_push(mls->config_logs);
cls->condition_var = NULL;
if (envclause != NULL) {
if (strncasecmp(envclause, "env=", 4) != 0) {
return "error in condition clause";
}
if ((envclause[4] == '\0')
|| ((envclause[4] == '!') && (envclause[5] == '\0'))) {
return "missing environment variable name";
}
cls->condition_var = apr_pstrdup(cmd->pool, &envclause[4]);
}
cls->fname = fn;
cls->format_string = fmt;
if (fmt == NULL) {
cls->format = NULL;
}
else {
cls->format = parse_log_string(cmd->pool, fmt, &err_string);
}
cls->log_writer = NULL;
return err_string;
}
static const char *set_transfer_log(cmd_parms *cmd, void *dummy,
const char *fn)
{
return add_custom_log(cmd, dummy, fn, NULL, NULL);
}
static const char *set_cookie_log(cmd_parms *cmd, void *dummy, const char *fn)
{
return add_custom_log(cmd, dummy, fn, "%{Cookie}n \"%r\" %t", NULL);
}
static const char *set_buffered_logs_on(cmd_parms *parms, void *dummy, int flag)
{
buffered_logs = flag;
if (buffered_logs) {
ap_log_set_writer_init(ap_buffered_log_writer_init);
ap_log_set_writer(ap_buffered_log_writer);
}
return NULL;
}
static const command_rec config_log_cmds[] =
{
AP_INIT_TAKE23("CustomLog", add_custom_log, NULL, RSRC_CONF,
"a file name, a custom log format string or format name, "
"and an optional \"env=\" clause (see docs)"),
AP_INIT_TAKE1("TransferLog", set_transfer_log, NULL, RSRC_CONF,
"the filename of the access log"),
AP_INIT_TAKE12("LogFormat", log_format, NULL, RSRC_CONF,
"a log format string (see docs) and an optional format name"),
AP_INIT_TAKE1("CookieLog", set_cookie_log, NULL, RSRC_CONF,
"the filename of the cookie log"),
AP_INIT_FLAG("BufferedLogs", set_buffered_logs_on, NULL, RSRC_CONF,
"Enable Buffered Logging (experimental)"),
{NULL}
};
static config_log_state *open_config_log(server_rec *s, apr_pool_t *p,
config_log_state *cls,
apr_array_header_t *default_format)
{
if (cls->log_writer != NULL) {
return cls; /* virtual config shared w/main server */
}
if (cls->fname == NULL) {
return cls; /* Leave it NULL to decline. */
}
cls->log_writer = log_writer_init(p, s, cls->fname);
if (cls->log_writer == NULL)
return NULL;
return cls;
}
static int open_multi_logs(server_rec *s, apr_pool_t *p)
{
int i;
multi_log_state *mls = ap_get_module_config(s->module_config,
&log_config_module);
config_log_state *clsarray;
const char *dummy;
const char *format;
if (mls->default_format_string) {
format = apr_table_get(mls->formats, mls->default_format_string);
if (format) {
mls->default_format = parse_log_string(p, format, &dummy);
}
}
if (!mls->default_format) {
mls->default_format = parse_log_string(p, DEFAULT_LOG_FORMAT, &dummy);
}
if (mls->config_logs->nelts) {
clsarray = (config_log_state *) mls->config_logs->elts;
for (i = 0; i < mls->config_logs->nelts; ++i) {
config_log_state *cls = &clsarray[i];
if (cls->format_string) {
format = apr_table_get(mls->formats, cls->format_string);
if (format) {
cls->format = parse_log_string(p, format, &dummy);
}
}
if (!open_config_log(s, p, cls, mls->default_format)) {
/* Failure already logged by open_config_log */
return DONE;
}
}
}
else if (mls->server_config_logs) {
clsarray = (config_log_state *) mls->server_config_logs->elts;
for (i = 0; i < mls->server_config_logs->nelts; ++i) {
config_log_state *cls = &clsarray[i];
if (cls->format_string) {
format = apr_table_get(mls->formats, cls->format_string);
if (format) {
cls->format = parse_log_string(p, format, &dummy);
}
}
if (!open_config_log(s, p, cls, mls->default_format)) {
/* Failure already logged by open_config_log */
return DONE;
}
}
}
return OK;
}
static apr_status_t flush_all_logs(void *data)
{
server_rec *s = data;
multi_log_state *mls;
apr_array_header_t *log_list;
config_log_state *clsarray;
buffered_log *buf;
int i;
if (!buffered_logs)
return APR_SUCCESS;
for (; s; s = s->next) {
mls = ap_get_module_config(s->module_config, &log_config_module);
log_list = NULL;
if (mls->config_logs->nelts) {
log_list = mls->config_logs;
}
else if (mls->server_config_logs) {
log_list = mls->server_config_logs;
}
if (log_list) {
clsarray = (config_log_state *) log_list->elts;
for (i = 0; i < log_list->nelts; ++i) {
buf = clsarray[i].log_writer;
flush_log(buf);
}
}
}
return APR_SUCCESS;
}
static int init_config_log(apr_pool_t *pc, apr_pool_t *p, apr_pool_t *pt, server_rec *s)
{
int res;
/* First init the buffered logs array, which is needed when opening the logs. */
if (buffered_logs) {
all_buffered_logs = apr_array_make(p, 5, sizeof(buffered_log *));
}
/* Next, do "physical" server, which gets default log fd and format
* for the virtual servers, if they don't override...
*/
res = open_multi_logs(s, p);
/* Then, virtual servers */
for (s = s->next; (res == OK) && s; s = s->next) {
res = open_multi_logs(s, p);
}
return res;
}
static void init_child(apr_pool_t *p, server_rec *s)
{
int mpm_threads;
ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
/* Now register the last buffer flush with the cleanup engine */
if (buffered_logs) {
int i;
buffered_log **array = (buffered_log **)all_buffered_logs->elts;
apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs);
for (i = 0; i < all_buffered_logs->nelts; i++) {
buffered_log *this = array[i];
#if APR_HAS_THREADS
if (mpm_threads > 1) {
apr_status_t rv;
this->mutex.type = apr_anylock_threadmutex;
rv = apr_thread_mutex_create(&this->mutex.lock.tm,
APR_THREAD_MUTEX_DEFAULT,
p);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"could not initialize buffered log mutex, "
"transfer log may become corrupted");
this->mutex.type = apr_anylock_none;
}
}
else
#endif
{
this->mutex.type = apr_anylock_none;
}
}
}
}
static void ap_register_log_handler(apr_pool_t *p, char *tag,
ap_log_handler_fn_t *handler, int def)
{
ap_log_handler *log_struct = apr_palloc(p, sizeof(*log_struct));
log_struct->func = handler;
log_struct->want_orig_default = def;
apr_hash_set(log_hash, tag, 1, (const void *)log_struct);
}
static ap_log_writer_init* ap_log_set_writer_init(ap_log_writer_init *handle)
{
ap_log_writer_init *old = log_writer_init;
log_writer_init = handle;
return old;
}
static ap_log_writer *ap_log_set_writer(ap_log_writer *handle)
{
ap_log_writer *old = log_writer;
log_writer = handle;
return old;
}
static apr_status_t ap_default_log_writer( request_rec *r,
void *handle,
const char **strs,
int *strl,
int nelts,
apr_size_t len)
{
char *str;
char *s;
int i;
apr_status_t rv;
str = apr_palloc(r->pool, len + 1);
for (i = 0, s = str; i < nelts; ++i) {
memcpy(s, strs[i], strl[i]);
s += strl[i];
}
rv = apr_file_write((apr_file_t*)handle, str, &len);
return rv;
}
static void *ap_default_log_writer_init(apr_pool_t *p, server_rec *s,
const char* name)
{
if (*name == '|') {
piped_log *pl;
pl = ap_open_piped_log(p, name + 1);
if (pl == NULL) {
return NULL;;
}
return ap_piped_log_write_fd(pl);
}
else {
const char *fname = ap_server_root_relative(p, name);
apr_file_t *fd;
apr_status_t rv;
if (!fname) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
"invalid transfer log path %s.", name);
return NULL;
}
rv = apr_file_open(&fd, fname, xfer_flags, xfer_perms, p);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"could not open transfer log file %s.", fname);
return NULL;
}
return fd;
}
}
static void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s,
const char* name)
{
buffered_log *b;
b = apr_pcalloc(p, sizeof(buffered_log));
b->handle = ap_default_log_writer_init(p, s, name);
if (b->handle) {
*(buffered_log **)apr_array_push(all_buffered_logs) = b;
return b;
}
else
return NULL;
}
static apr_status_t ap_buffered_log_writer(request_rec *r,
void *handle,
const char **strs,
int *strl,
int nelts,
apr_size_t len)
{
char *str;
char *s;
int i;
apr_status_t rv;
buffered_log *buf = (buffered_log*)handle;
if ((rv = APR_ANYLOCK_LOCK(&buf->mutex)) != APR_SUCCESS) {
return rv;
}
if (len + buf->outcnt > LOG_BUFSIZE) {
flush_log(buf);
}
if (len >= LOG_BUFSIZE) {
apr_size_t w;
str = apr_palloc(r->pool, len + 1);
for (i = 0, s = str; i < nelts; ++i) {
memcpy(s, strs[i], strl[i]);
s += strl[i];
}
w = len;
rv = apr_file_write(buf->handle, str, &w);
}
else {
for (i = 0, s = &buf->outbuf[buf->outcnt]; i < nelts; ++i) {
memcpy(s, strs[i], strl[i]);
s += strl[i];
}
buf->outcnt += len;
rv = APR_SUCCESS;
}
APR_ANYLOCK_UNLOCK(&buf->mutex);
return rv;
}
static int log_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
{
static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
if (log_pfn_register) {
log_pfn_register(p, "h", log_remote_host, 0);
log_pfn_register(p, "a", log_remote_address, 0 );
log_pfn_register(p, "A", log_local_address, 0 );
log_pfn_register(p, "l", log_remote_logname, 0);
log_pfn_register(p, "u", log_remote_user, 0);
log_pfn_register(p, "t", log_request_time, 0);
log_pfn_register(p, "f", log_request_file, 0);
log_pfn_register(p, "b", clf_log_bytes_sent, 0);
log_pfn_register(p, "B", log_bytes_sent, 0);
log_pfn_register(p, "i", log_header_in, 0);
log_pfn_register(p, "o", log_header_out, 0);
log_pfn_register(p, "n", log_note, 0);
log_pfn_register(p, "e", log_env_var, 0);
log_pfn_register(p, "V", log_server_name, 0);
log_pfn_register(p, "v", log_virtual_host, 0);
log_pfn_register(p, "p", log_server_port, 0);
log_pfn_register(p, "P", log_pid_tid, 0);
log_pfn_register(p, "H", log_request_protocol, 0);
log_pfn_register(p, "m", log_request_method, 0);
log_pfn_register(p, "q", log_request_query, 0);
log_pfn_register(p, "X", log_connection_status, 0);
log_pfn_register(p, "C", log_cookie, 0);
log_pfn_register(p, "k", log_requests_on_connection, 0);
log_pfn_register(p, "r", log_request_line, 1);
log_pfn_register(p, "D", log_request_duration_microseconds, 1);
log_pfn_register(p, "T", log_request_duration, 1);
log_pfn_register(p, "U", log_request_uri, 1);
log_pfn_register(p, "s", log_status, 1);
}
return OK;
}
static void register_hooks(apr_pool_t *p)
{
ap_hook_pre_config(log_pre_config,NULL,NULL,APR_HOOK_REALLY_FIRST);
ap_hook_child_init(init_child,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_open_logs(init_config_log,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_log_transaction(multi_log_transaction,NULL,NULL,APR_HOOK_MIDDLE);
/* Init log_hash before we register the optional function. It is
* possible for the optional function, ap_register_log_handler,
* to be called before any other mod_log_config hooks are called.
* As a policy, we should init everything required by an optional function
* before calling APR_REGISTER_OPTIONAL_FN.
*/
log_hash = apr_hash_make(p);
APR_REGISTER_OPTIONAL_FN(ap_register_log_handler);
APR_REGISTER_OPTIONAL_FN(ap_log_set_writer_init);
APR_REGISTER_OPTIONAL_FN(ap_log_set_writer);
}
module AP_MODULE_DECLARE_DATA log_config_module =
{
STANDARD20_MODULE_STUFF,
NULL, /* create per-dir config */
NULL, /* merge per-dir config */
make_config_log_state, /* server config */
merge_config_log_state, /* merge server config */
config_log_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};