Date: Mon, 07 Jun 2004 04:17:24 -0400 From: Yarema <yds@CoolRat.org> To: Clement Laforet <clement@FreeBSD.org> Cc: Ports FreeBSD <ports@FreeBSD.org> Subject: apache2 & mod_log_config-st & mod_log_mysql Message-ID: <61670F38EE92BEC06E2BE009@volyn.coolrat.org>
next in thread | raw e-mail | index | archive | help
--==========B6C94F11EFDA6764AC7E========== Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Content-Disposition: inline Hello Clement, I have a production setup using apache+mod_ssl with mod_log_sql. (The latter leaves something to be desired.) So I was very happy to discover mod_log_mysql for apache2, both of which you maintain. While combing through the docs for mod_log_mysql at <http://bitbrook.de/software/mod_log_mysql/> a few things struck me as somewhat incomplete. The docs list as one of the requirements to have a fixed apr_reslist.c -- which is missing from the way you implemented the mod_log_mysql -> mod_log_config-st -> apache2 ports dependencies. Then there's the whole issue of having mod_log_config-st in a separate port, itself missing the modified mod_logio.c, which returns i/o counts as numbers to mod_mod_log_config, not as strings as the original would do. So here's what I propose. How about dropping the mod_log_config-st entirely and mod_log_mysql dependency on it. And replacing the whole mess with the four additional patches to the apache2 port that I'm attaching with this email. So far as I can tell this does not break anything and does fix the two omissions mentioned above. Not to mention just being simpler IMHO. The patches were derived from the sources in <http://bitbrook.de/software/mod_log_mysql/mod_log_sources.tar.bz2> except for apr_reslist.c, version 1.9 of which was downloaded from <http://cvs.apache.org/viewcvs.cgi/apr-util/misc/apr_reslist.c> Seems like the Right Thing (TM) to me. :) What do you think? -- Yarema http://yds.CoolRat.org --==========B6C94F11EFDA6764AC7E========== Content-Type: text/plain; charset=iso-8859-1; name="patch-srclib:apr-util:misc:apr_reslist.c" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="patch-srclib:apr-util:misc:apr_reslist.c"; size=3583 --- srclib/apr-util/misc/apr_reslist.c.orig Fri Feb 13 04:52:43 2004 +++ srclib/apr-util/misc/apr_reslist.c Mon Mar 15 08:21:26 2004 @@ -49,6 +49,7 @@ int smax; /* soft maximum on the total number of resources */ int hmax; /* hard maximum on the total number of resources */ apr_interval_time_t ttl; /* TTL when we have too many resources */ + apr_interval_time_t timeout; /* Timeout for waiting on resource */ apr_reslist_constructor constructor; apr_reslist_destructor destructor; void *params; /* opaque data passed to constructor and destructor = calls */ @@ -118,12 +119,9 @@ res =3D apr_pcalloc(reslist->pool, sizeof(*res)); =20 rv =3D reslist->constructor(&res->opaque, reslist->params, = reslist->pool); - if (rv !=3D APR_SUCCESS) { - return rv; - } =20 *ret_res =3D res; - return APR_SUCCESS; + return rv; } =20 /** @@ -132,14 +130,7 @@ */ static apr_status_t destroy_resource(apr_reslist_t *reslist, apr_res_t = *res) { - apr_status_t rv; - - rv =3D reslist->destructor(res->opaque, reslist->params, = reslist->pool); - if (rv !=3D APR_SUCCESS) { - return rv; - } - - return APR_SUCCESS; + return reslist->destructor(res->opaque, reslist->params, = reslist->pool); } =20 static apr_status_t reslist_cleanup(void *data_) @@ -187,6 +178,7 @@ /* Create the resource */ rv =3D create_resource(reslist, &res); if (rv !=3D APR_SUCCESS) { + free_container(reslist, res); apr_thread_mutex_unlock(reslist->listlock); return rv; } @@ -313,7 +305,15 @@ * a new one, or something becomes free. */ else while (reslist->ntotal >=3D reslist->hmax && reslist->nidle <=3D 0) { - apr_thread_cond_wait(reslist->avail, reslist->listlock); + if (reslist->timeout) { + if ((rv =3D apr_thread_cond_timedwait(reslist->avail,=20 + reslist->listlock, reslist->timeout)) !=3D APR_SUCCESS) { + apr_thread_mutex_unlock(reslist->listlock); + return rv; + } + } + else + apr_thread_cond_wait(reslist->avail, reslist->listlock); } /* If we popped out of the loop, first try to see if there * are new resources available for immediate use. */ @@ -329,17 +329,13 @@ * a resource to fill the slot and use it. */ else { rv =3D create_resource(reslist, &res); - - if (rv !=3D APR_SUCCESS) { - apr_thread_mutex_unlock(reslist->listlock); - return rv; + if (rv =3D=3D APR_SUCCESS) { + reslist->ntotal++; + *resource =3D res->opaque; } - - reslist->ntotal++; - *resource =3D res->opaque; free_container(reslist, res); apr_thread_mutex_unlock(reslist->listlock); - return APR_SUCCESS; + return rv; } } =20 @@ -356,6 +352,23 @@ apr_thread_mutex_unlock(reslist->listlock); =20 return reslist_maint(reslist); +} + +APU_DECLARE(void) apr_reslist_timeout_set(apr_reslist_t *reslist, + apr_interval_time_t timeout) +{ + reslist->timeout =3D timeout; +} + +APU_DECLARE(apr_status_t) apr_reslist_invalidate(apr_reslist_t *reslist, + void *resource) +{ + apr_status_t ret; + apr_thread_mutex_lock(reslist->listlock); + ret =3D reslist->destructor(resource, reslist->params, reslist->pool); + reslist->ntotal--; + apr_thread_mutex_unlock(reslist->listlock); + return ret; } =20 #endif /* APR_HAS_THREADS */ --==========B6C94F11EFDA6764AC7E========== Content-Type: text/plain; charset=iso-8859-1; name="patch-modules:loggers:mod_log_config.c" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="patch-modules:loggers:mod_log_config.c"; size=61326 --- modules/loggers/mod_log_config.c.orig Wed Mar 3 06:07:50 2004 +++ modules/loggers/mod_log_config.c Fri Oct 17 04:58:07 2003 @@ -17,7 +17,12 @@ * Modified by djm@va.pubnix.com: * If no TransferLog is given explicitly, decline to log. * - * This is module implements the TransferLog directive (same as the + * Modified by st: + * Added logic to give other log writers a more detailed view of the + * data to be logged. + * Added item %R, which returns the unmodified URL (this is not %U). + * + * This module implements the TransferLog directive (same as the * common log module), and additional directives, LogFormat and CustomLog. * * @@ -27,12 +32,25 @@ * a custom format is set with LogFormat * LogFormat format Set a log format from TransferLog files * CustomLog fn format - * Log to file fn with format given by the format + * Log to fn with format given by the format * argument * * CookieLog fn For backwards compatability with old Cookie * logging module - now deprecated. * + * fn is a URL-like descriptor of the form "writer:path". The exact format + * of "path" depends on the log writer to use. + * This module implements two default log writers, "file" and "pipe". + * + * For backwards compatibility: fn may also be a simple filesystem path=20 + * without the "writer:"-part, and possibly prepended by a | to indicate + * logging into a pipe. These fn will be handled by the build-in file or + * pipe log writer. + * Furthermore, if a "writer:"-part exists in fn but no matching log = writer=20 + * was found, this module will fall back to log into a file named [fn]. In = + * other words: No error will be posted upon missing log writer, the = default + * file log writer will be used. + * * There can be any number of TransferLog and CustomLog * commands. Each request will be logged to _ALL_ the * named files, in the appropriate format. @@ -47,11 +65,11 @@ *=20 * Examples: * - * TransferLog logs/access_log + * TransferLog file:logs/access_log * <VirtualHost> * LogFormat "... custom format ..." - * TransferLog log/virtual_only - * CustomLog log/virtual_useragents "%t %{user-agent}i" + * TransferLog file:log/virtual_only + * CustomLog file:log/virtual_useragents "%t %{user-agent}i" * </VirtualHost> * * This will log using CLF to access_log any requests handled by the @@ -61,15 +79,15 @@ * * Note that the NCSA referer and user-agent logs are easily added with * CustomLog: - * CustomLog logs/referer "%{referer}i -> %U" - * CustomLog logs/agent "%{user-agent}i" + * CustomLog file:logs/referer "%{referer}i -> %U" + * CustomLog file:logs/agent "%{user-agent}i" * * RefererIgnore functionality can be obtained with conditional * logging (SetEnvIf and CustomLog ... env=3D!VAR). * * But using this method allows much easier modification of the * log format, e.g. to log hosts along with UA: - * CustomLog logs/referer "%{referer}i %U %h" + * CustomLog file:logs/referer "%{referer}i %U %h" * * The argument to LogFormat and CustomLog is a string, which can include * literal characters copied into the log files, and '%' directives as @@ -91,8 +109,8 @@ * %...{Foobar}o: The contents of Foobar: header line(s) in the reply. * %...p: the port the request was served to * %...P: the process ID of the child that serviced the request. - * %...{format}P: the process ID or thread ID of the child/thread that - * serviced the request + * %...{format}P: the process ID (pid) or thread ID (tid) of the=20 + * child/thread that serviced the request * %...r: first line of request * %...s: status. For requests that got internally redirected, this * is status of the *original* request --- %...>s for the last. @@ -103,6 +121,7 @@ * %...D: the time taken to serve the request, in micro seconds. * %...u: remote user (from auth; may be bogus if return status (%s) is = 401) * %...U: the URL path requested. + * %...R: the URL requested, unmodified * %...v: the configured name of the server (i.e. which virtual host?) * %...V: the server name according to the UseCanonicalName setting * %...m: the request method @@ -112,9 +131,9 @@ * 'X' =3D connection aborted before the response completed. * '+' =3D connection may be kept alive after the response is = sent. * '-' =3D connection will be closed after the response is sent. - (This directive was %...c in late versions of Apache 1.3, but - this conflicted with the historical ssl %...{var}c syntax.) -* + * (This directive was %...c in late versions of Apache 1.3, but + * this conflicted with the historical ssl %...{var}c syntax.) + * * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can * indicate conditions for inclusion of the item (which will cause it * to be replaced with '-' if the condition is not met). Note that @@ -144,7 +163,6 @@ #include "apr_lib.h" #include "apr_hash.h" #include "apr_optional.h" -#include "apr_anylock.h" =20 #define APR_WANT_STRFUNC #include "apr_want.h" @@ -157,7 +175,6 @@ #include "http_log.h" #include "http_protocol.h" #include "util_time.h" -#include "ap_mpm.h" =20 #if APR_HAVE_UNISTD_H #include <unistd.h> @@ -173,30 +190,35 @@ =20 static int xfer_flags =3D (APR_WRITE | APR_APPEND | APR_CREATE); static apr_fileperms_t xfer_perms =3D APR_OS_DEFAULT; + +typedef struct { + union { + ap_log_handler_fn_t *func; + ap_log_ehandler_fn_t *efunc; + } handler; + int oldstyle; + int want_orig_default; +} log_handler; static apr_hash_t *log_hash; -static apr_status_t ap_default_log_writer(request_rec *r, - void *handle,=20 - const char **strs, - int *strl, - int nelts, - apr_size_t len); -static apr_status_t ap_buffered_log_writer(request_rec *r, - void *handle,=20 - const char **strs, - int *strl, - int nelts, - apr_size_t len); -static void *ap_default_log_writer_init(apr_pool_t *p, server_rec *s,=20 - const char* name); -static void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s,=20 - const char* name); - -static void ap_log_set_writer_init(ap_log_writer_init *handle); -static void ap_log_set_writer(ap_log_writer *handle); -static ap_log_writer *log_writer =3D ap_default_log_writer; -static ap_log_writer_init *log_writer_init =3D ap_default_log_writer_init; + +typedef struct { + ap_log_ewriter_setup *setup; + ap_log_ewriter *write; + ap_log_ewriter_init *init; + ap_log_ewriter_exit *exit; +} log_ewriter; +static apr_hash_t *writer_hash; + +static void *ap_old_log_writer_init(apr_pool_t *p, server_rec *s,=20 + const char* name); +static apr_status_t ap_filepipe_log_ewriter(request_rec *r, + void *handle,=20 + apr_array_header_t *data); + +static ap_log_writer *log_writer =3D NULL; +static ap_log_writer_init *log_writer_init =3D NULL; + static int buffered_logs =3D 0; /* default unbuffered */ -static apr_array_header_t *all_buffered_logs =3D NULL; =20 /* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is * guaranteed to be atomic when writing a pipe. And PIPE_BUF >=3D 512 @@ -242,23 +264,26 @@ * request. format might be NULL, in which case the default_format * from the multi_log_state should be used, or if that is NULL as * well, use the CLF.=20 - * log_writer is NULL before the log file is opened and is - * set to a opaque structure (usually a fd) after it is opened. -=20 + * writer_data is NULL before the log file is opened and is + * set to an opaque structure (usually a fd) after it is opened. */ + typedef struct { apr_file_t *handle; apr_size_t outcnt; char outbuf[LOG_BUFSIZE]; - apr_anylock_t mutex; } buffered_log; =20 typedef struct { const char *fname; const char *format_string; apr_array_header_t *format; - void *log_writer; + log_ewriter *writer; + void *writer_data; + + int condition_sense; char *condition_var; + apr_array_header_t *conditions; } config_log_state; =20 /* @@ -267,168 +292,179 @@ */ =20 typedef struct { - ap_log_handler_fn_t *func; + log_handler *handler; char *arg; int condition_sense; int want_orig; apr_array_header_t *conditions; } log_format_item; =20 -static char *format_integer(apr_pool_t *p, int i) -{ - return apr_itoa(p, i); -} - -static char *pfmt(apr_pool_t *p, int i) -{ - if (i <=3D 0) { - return "-"; - } - else { - return format_integer(p, i); - } -} - -static const char *constant_item(request_rec *dummy, char *stuff) +static void *constant_item(request_rec *r, char *stuff, = ap_log_ehandler_data *d) { - return stuff; + d->data=3Dstuff; + d->arg=3DNULL; + d->type=3DAP_LOG_EHANDLER_RETURN_CONST; } =20 -static const char *log_remote_host(request_rec *r, char *a) +static void *log_remote_host(request_rec *r, char *a, ap_log_ehandler_data = *d) { - return ap_escape_logitem(r->pool, ap_get_remote_host(r->connection, - = r->per_dir_config, - REMOTE_NAME, = NULL)); + d->data=3D(void *) ap_get_remote_host(r->connection, + r->per_dir_config, + REMOTE_NAME, NULL); + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; } =20 -static const char *log_remote_address(request_rec *r, char *a) +static void *log_remote_address(request_rec *r, char *a, = ap_log_ehandler_data *d) { - return r->connection->remote_ip; + d->data=3Dr->connection->remote_ip; + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; } =20 -static const char *log_local_address(request_rec *r, char *a) +static void *log_local_address(request_rec *r, char *a, = ap_log_ehandler_data *d) { - return r->connection->local_ip; + d->data=3Dr->connection->local_ip; + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; } =20 -static const char *log_remote_logname(request_rec *r, char *a) +static void *log_remote_logname(request_rec *r, char *a, = ap_log_ehandler_data *d) { - return ap_escape_logitem(r->pool, ap_get_remote_logname(r)); + d->data=3D(void *) ap_get_remote_logname(r); + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; } =20 -static const char *log_remote_user(request_rec *r, char *a) +static void *log_remote_user(request_rec *r, char *a, ap_log_ehandler_data = *d) { - char *rvalue =3D r->user; - - if (rvalue =3D=3D NULL) { + d->data =3D r->user; + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; + =20 + /* + if (d->data =3D=3D NULL) { rvalue =3D "-"; } - else if (strlen(rvalue) =3D=3D 0) { + else if (strlen(d->data) =3D=3D 0) { rvalue =3D "\"\""; } else { rvalue =3D ap_escape_logitem(r->pool, rvalue); } - - return rvalue; + */ } =20 -static const char *log_request_line(request_rec *r, char *a) +static void *log_request_line(request_rec *r, char *a, = ap_log_ehandler_data *d) { /* NOTE: If the original request contained a password, we * re-write the request line here to contain XXXXXX instead: * (note the truncation before the protocol string for HTTP/0.9 = requests) * (note also that r->the_request contains the unmodified request) */ - return ap_escape_logitem(r->pool, - (r->parsed_uri.password) - ? apr_pstrcat(r->pool, r->method, " ", - apr_uri_unparse(r->pool, - = &r->parsed_uri, 0), - r->assbackwards ? NULL : " ", - r->protocol, NULL) - : r->the_request); + d->data =3D (r->parsed_uri.password) + ? apr_pstrcat(r->pool, r->method, " ", + apr_uri_unparse(r->pool, + &r->parsed_uri, 0), + r->assbackwards ? NULL : " ", + r->protocol, NULL) + : r->the_request; + d->type =3D AP_LOG_EHANDLER_RETURN_STRING; } =20 -static const char *log_request_file(request_rec *r, char *a) +static void *log_request_file(request_rec *r, char *a, = ap_log_ehandler_data *d) { - return ap_escape_logitem(r->pool, r->filename); + d->data =3D r->filename; + d->type =3D AP_LOG_EHANDLER_RETURN_STRING; } -static const char *log_request_uri(request_rec *r, char *a) + +static void *log_request_uri(request_rec *r, char *a, ap_log_ehandler_data = *d) { - return ap_escape_logitem(r->pool, r->uri); + d->data =3D r->uri; + d->type =3D AP_LOG_EHANDLER_RETURN_STRING; } -static const char *log_request_method(request_rec *r, char *a) + +static void *log_unparsed_request_uri(request_rec *r, char *a, = ap_log_ehandler_data *d) { - return ap_escape_logitem(r->pool, r->method); + d->data =3D r->unparsed_uri; + d->type =3D AP_LOG_EHANDLER_RETURN_STRING; } -static const char *log_request_protocol(request_rec *r, char *a) + +static void *log_request_method(request_rec *r, char *a, = ap_log_ehandler_data *d) { - return ap_escape_logitem(r->pool, r->protocol); + d->data =3D (void *)r->method; + d->type =3D AP_LOG_EHANDLER_RETURN_STRING; } -static const char *log_request_query(request_rec *r, char *a) +static void *log_request_protocol(request_rec *r, char *a, = ap_log_ehandler_data *d) { - return (r->args) ? apr_pstrcat(r->pool, "?", - ap_escape_logitem(r->pool, r->args), = NULL) - : ""; + d->data =3D r->protocol; + d->type =3D AP_LOG_EHANDLER_RETURN_STRING; } -static const char *log_status(request_rec *r, char *a) +static void *log_request_query(request_rec *r, char *a, = ap_log_ehandler_data *d) +{=20 + if (r->args) + d->data=3Dapr_pstrcat(r->pool, "?", r->args, NULL); + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; +} +static void *log_status(request_rec *r, char *a, ap_log_ehandler_data *d) { - return pfmt(r->pool, r->status); + if (r->status > 0) { + d->data=3Dapr_palloc(r->pool,sizeof(ap_log_unumber_t)); + *((ap_log_unumber_t *) d->data)=3Dr->status; + } + d->type=3DAP_LOG_EHANDLER_RETURN_UNUMBER; } =20 -static const char *clf_log_bytes_sent(request_rec *r, char *a) +static void *clf_log_bytes_sent(request_rec *r, char *a, = ap_log_ehandler_data *d) { if (!r->sent_bodyct || !r->bytes_sent) { - return "-"; + d->data=3D"-"; } else { - return apr_off_t_toa(r->pool, r->bytes_sent); + d->data=3Dapr_off_t_toa(r->pool, r->bytes_sent); } + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; } =20 -static const char *log_bytes_sent(request_rec *r, char *a) +static void *log_bytes_sent(request_rec *r, char *a, ap_log_ehandler_data = *d) { - if (!r->sent_bodyct || !r->bytes_sent) { - return "0"; - } - else { - return apr_psprintf(r->pool, "%" APR_OFF_T_FMT, r->bytes_sent); + if (r->header_only =3D=3D 0) { + d->data=3Dapr_palloc(r->pool,sizeof(ap_log_unumber_t)); + *((ap_log_unumber_t *) d->data)=3Dr->bytes_sent; } + d->type=3DAP_LOG_EHANDLER_RETURN_UNUMBER; } =20 - -static const char *log_header_in(request_rec *r, char *a) +static void *log_header_in(request_rec *r, char *a, ap_log_ehandler_data = *d) { - return ap_escape_logitem(r->pool, apr_table_get(r->headers_in, a)); + d->data=3D(void *) apr_table_get(r->headers_in, a); + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; } =20 -static const char *log_header_out(request_rec *r, char *a) +static void *log_header_out(request_rec *r, char *a, ap_log_ehandler_data = *d) { - const char *cp =3D apr_table_get(r->headers_out, a); - if (!strcasecmp(a, "Content-type") && r->content_type) { - cp =3D ap_field_noparam(r->pool, r->content_type); - } - if (cp) { - return ap_escape_logitem(r->pool, cp); - } - return ap_escape_logitem(r->pool, apr_table_get(r->err_headers_out, = a)); + if (!strcasecmp(a, "Content-type") && r->content_type)=20 + d->data =3D ap_field_noparam(r->pool, r->content_type); + else + d->data =3D (void *) apr_table_get(r->headers_out, a); + if (! d->data)=20 + d->data =3D (void *) apr_table_get(r->err_headers_out, a); + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; } =20 -static const char *log_note(request_rec *r, char *a) +static void *log_note(request_rec *r, char *a, ap_log_ehandler_data *d) { - return ap_escape_logitem(r->pool, apr_table_get(r->notes, a)); + d->data =3D (void *) apr_table_get(r->notes, a); + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; } -static const char *log_env_var(request_rec *r, char *a) + +static void *log_env_var(request_rec *r, char *a, ap_log_ehandler_data *d) { - return ap_escape_logitem(r->pool, apr_table_get(r->subprocess_env, = a)); + d->data =3D (void *) apr_table_get(r->subprocess_env, a); + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; } =20 -static const char *log_cookie(request_rec *r, char *a) +static void *log_cookie(request_rec *r, char *a, ap_log_ehandler_data *d) { const char *cookies; const char *start_cookie; =20 + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; if ((cookies =3D apr_table_get(r->headers_in, "Cookie"))) { if ((start_cookie =3D ap_strstr_c(cookies,a))) { char *cookie, *end_cookie; @@ -439,169 +475,94 @@ if (end_cookie) { *end_cookie =3D '\0'; } - return ap_escape_logitem(r->pool, cookie); + d->data=3Dcookie; } } - 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); } =20 -#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) +static void *log_request_time(request_rec *r, char *a, = ap_log_ehandler_data *d) { - 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 =3D apr_palloc(r->pool, - = sizeof(*cached_time)); + d->type=3DAP_LOG_EHANDLER_RETURN_DATETIME; + d->data=3Dapr_palloc(r->pool,sizeof(apr_time_t)); #ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE - apr_time_t request_time =3D apr_time_now(); + *((apr_time_t *) d->data) =3D apr_time_now(); #else - apr_time_t request_time =3D r->request_time; + *((apr_time_t *) d->data) =3D r->request_time; #endif - unsigned t_seconds =3D (unsigned)apr_time_sec(request_time); - unsigned i =3D t_seconds & TIME_CACHE_MASK; - memcpy(cached_time, &(request_time_cache[i]), = sizeof(*cached_time)); - if ((t_seconds !=3D cached_time->t) || - (t_seconds !=3D 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, r->request_time); - timz =3D xt.tm_gmtoff; - if (timz < 0) { - timz =3D -timz; - sign =3D '-'; - } - else { - sign =3D '+'; - } - cached_time->t =3D 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 =3D t_seconds; - memcpy(&(request_time_cache[i]), cached_time, - sizeof(*cached_time)); - } - return cached_time->timestr; - } } =20 -static const char *log_request_duration(request_rec *r, char *a) +static void *log_request_duration(request_rec *r, char *a, = ap_log_ehandler_data *d) { - apr_time_t duration =3D apr_time_now() - r->request_time; - return apr_psprintf(r->pool, "%" APR_TIME_T_FMT, = apr_time_sec(duration)); + d->data=3Dapr_palloc(r->pool,sizeof(ap_log_unumber_t)); + *((ap_log_unumber_t *) d->data) =3D apr_time_sec(apr_time_now() - = r->request_time); + d->arg=3Da; + d->type=3DAP_LOG_EHANDLER_RETURN_UNUMBER; } =20 -static const char *log_request_duration_microseconds(request_rec *r, char = *a) +static void *log_request_duration_microseconds(request_rec *r, char *a, = ap_log_ehandler_data *d) { - return apr_psprintf(r->pool, "%" APR_TIME_T_FMT,=20 - (apr_time_now() - r->request_time)); + d->data=3Dapr_palloc(r->pool,sizeof(ap_log_unumber_t)); + *((ap_log_unumber_t *) d->data) =3D apr_time_now() - r->request_time; + d->arg=3Da; + d->type=3DAP_LOG_EHANDLER_RETURN_UNUMBER; } =20 /* 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) +static void *log_virtual_host(request_rec *r, char *a, = ap_log_ehandler_data *d) { - return ap_escape_logitem(r->pool, r->server->server_hostname); + d->data =3D r->server->server_hostname; + d->arg=3Da; + d->type =3D AP_LOG_EHANDLER_RETURN_STRING; } =20 -static const char *log_server_port(request_rec *r, char *a) +static void *log_server_port(request_rec *r, char *a, ap_log_ehandler_data = *d) { - return apr_psprintf(r->pool, "%u", - r->server->port ? r->server->port : = ap_default_port(r)); + d->data=3Dapr_palloc(r->pool,sizeof(ap_log_unumber_t)); + *((ap_log_unumber_t *) d->data) =3D r->server->port ? r->server->port = : ap_default_port(r); + d->arg=3Da; + d->type=3DAP_LOG_EHANDLER_RETURN_UNUMBER; } =20 /* 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) +static void *log_server_name(request_rec *r, char *a, ap_log_ehandler_data = *d) { - return ap_escape_logitem(r->pool, ap_get_server_name(r)); + d->data =3D (void *) ap_get_server_name(r); + d->arg=3Da; + d->type =3D AP_LOG_EHANDLER_RETURN_STRING; } =20 -static const char *log_pid_tid(request_rec *r, char *a) +static void *log_pid_tid(request_rec *r, char *a, ap_log_ehandler_data *d) { - if (*a =3D=3D '\0' || !strcmp(a, "pid")) { - return apr_psprintf(r->pool, "%" APR_PID_T_FMT, getpid()); + d->arg=3Da; + if (!a || *a =3D=3D '\0' || !strcmp(a, "pid")) { + d->data =3D apr_palloc(r->pool,sizeof(ap_log_unumber_t)); + *((ap_log_unumber_t *) d->data) =3D getpid(); } - else if (!strcmp(a, "tid")) { #if APR_HAS_THREADS - apr_os_thread_t tid =3D apr_os_thread_current(); -#else - int tid =3D 0; /* APR will format "0" anyway but an arg is needed = */ -#endif - return apr_psprintf(r->pool, "%pT", &tid); + else if (!strcmp(a, "tid")) { + d->data =3D apr_palloc(r->pool,sizeof(ap_log_unumber_t)); + *((ap_log_unumber_t *) d->data) =3D apr_os_thread_current(); } - /* bogus format */ - return a; +#endif + d->type =3D AP_LOG_EHANDLER_RETURN_UNUMBER; } =20 -static const char *log_connection_status(request_rec *r, char *a) +static void *log_connection_status(request_rec *r, char *a, = ap_log_ehandler_data *d) { if (r->connection->aborted) - return "X"; - - if (r->connection->keepalive =3D=3D AP_CONN_KEEPALIVE &&=20 + d->data =3D "X"; + else if (r->connection->keepalive =3D=3D AP_CONN_KEEPALIVE &&=20 (!r->server->keep_alive_max || - (r->server->keep_alive_max - r->connection->keepalives) > 0)) { - return "+"; - } - return "-"; + (r->server->keep_alive_max - r->connection->keepalives) > 0))=20 + d->data =3D "+"; + else + d->data =3D "-"; + d->arg=3Da; + d->type =3D AP_LOG_EHANDLER_RETURN_STRING; } =20 /***************************************************************** @@ -615,7 +576,7 @@ const char *s; char *d; =20 - it->func =3D constant_item; + it->handler =3D (log_handler *)apr_hash_get(log_hash, "%", 1); it->conditions =3D NULL; =20 s =3D *sa; @@ -674,7 +635,6 @@ static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char = **sa) { const char *s =3D *sa; - ap_log_handler *handler; =20 if (*s !=3D '%') { return parse_log_misc_string(p, it, sa); @@ -686,14 +646,15 @@ =20 if (*s =3D=3D '%') { it->arg =3D "%"; - it->func =3D constant_item; + it->handler =3D (log_handler *)apr_hash_get(log_hash, "%", 1); + *sa =3D ++s; =20 return NULL; } =20 it->want_orig =3D -1; - it->arg =3D ""; /* For safety's sake... */ + it->arg =3D NULL; /* For safety's sake... */ =20 while (*s) { int i; @@ -744,8 +705,8 @@ break; =20 default: - handler =3D (ap_log_handler *)apr_hash_get(log_hash, s++, 1); - if (!handler) { + it->handler =3D (log_handler *)apr_hash_get(log_hash, s++, 1); + if (!it->handler) { char dummy[2]; =20 dummy[0] =3D s[-1]; @@ -753,9 +714,8 @@ return apr_pstrcat(p, "Unrecognized LogFormat directive = %", dummy, NULL); } - it->func =3D handler->func; if (it->want_orig =3D=3D -1) { - it->want_orig =3D handler->want_orig_default; + it->want_orig =3D it->handler->want_orig_default;=20 } *sa =3D s; return NULL; @@ -776,9 +736,6 @@ return NULL; } } - - s =3D APR_EOL_STR; - parse_log_item(p, (log_format_item *) apr_array_push(a), &s); return a; } =20 @@ -787,11 +744,106 @@ * Actually logging. */ =20 -static const char *process_item(request_rec *r, request_rec *orig, - log_format_item *item) +static const char *format_request_time_custom(request_rec *r, char *a, + apr_time_exp_t *xt) { - const char *cp; + 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 *format_request_time(request_rec *r, char *a, apr_time_t = *t, cached_request_time *cache) +{ + 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, *t); +#endif + return format_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 =3D apr_palloc(r->pool, + = sizeof(*cached_time)); +#ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE + apr_time_t request_time =3D apr_time_now(); +#else + apr_time_t request_time =3D *t; +#endif + unsigned t_seconds =3D (unsigned)apr_time_sec(request_time); + unsigned i =3D t_seconds & TIME_CACHE_MASK; + memcpy(cached_time, &(request_time_cache[i]), = sizeof(*cached_time)); + if ((t_seconds !=3D cached_time->t) || + (t_seconds !=3D 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, *t); + timz =3D xt.tm_gmtoff; + if (timz < 0) { + timz =3D -timz; + sign =3D '-'; + } + else { + sign =3D '+'; + } + cached_time->t =3D 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)); + cached_time->t_validate =3D t_seconds; + memcpy(&(request_time_cache[i]), cached_time, + sizeof(*cached_time)); + } + return cached_time->timestr; + } +} + =20 +static void process_item(request_rec *r, request_rec *orig, + log_format_item *item, + ap_log_ehandler_data *d) +{ /* First, see if we need to process this thing at all... */ =20 if (item->conditions && item->conditions->nelts !=3D 0) { @@ -808,14 +860,22 @@ =20 if ((item->condition_sense && in_list) || (!item->condition_sense && !in_list)) { - return "-"; + d->type=3DAP_LOG_EHANDLER_RETURN_STRING; + return; } } =20 + /* We do. Do it... */ =20 - cp =3D (*item->func) (item->want_orig ? orig : r, item->arg); - return cp ? cp : "-"; + if (item->handler->oldstyle) { + if (! (d->data =3D (void *)(*item->handler->handler.func) = (item->want_orig ? orig : r, item->arg))) + d->data =3D "-"; + d->type =3D AP_LOG_EHANDLER_RETURN_OLDSTYLE; + } + else { + (*item->handler->handler.efunc) (item->want_orig ? orig : r, = item->arg, d); + } } =20 static void flush_log(buffered_log *buf) @@ -837,9 +897,10 @@ int i; apr_size_t len =3D 0; apr_array_header_t *format; - char *envar; apr_status_t rv; - + apr_array_header_t *data; + ap_log_ehandler_data *d; + =20 if (cls->fname =3D=3D NULL) { return DECLINED; } @@ -849,25 +910,14 @@ * to make. */ if (cls->condition_var !=3D NULL) { - envar =3D cls->condition_var; - if (*envar !=3D '!') { - if (apr_table_get(r->subprocess_env, envar) =3D=3D NULL) { - return DECLINED; - } - } - else { - if (apr_table_get(r->subprocess_env, &envar[1]) !=3D NULL) { - return DECLINED; - } + if ((cls->condition_sense && apr_table_get(r->subprocess_env, = cls->condition_var) !=3D NULL) + ||=20 + (!cls->condition_sense && apr_table_get(r->subprocess_env, = cls->condition_var) =3D=3D NULL)) + { + return DECLINED; } } =20 - format =3D cls->format ? cls->format : default_format; - - strs =3D apr_palloc(r->pool, sizeof(char *) * (format->nelts)); - strl =3D apr_palloc(r->pool, sizeof(int) * (format->nelts)); - items =3D (log_format_item *) format->elts; - orig =3D r; while (orig->prev) { orig =3D orig->prev; @@ -876,20 +926,84 @@ r =3D r->next; } =20 - for (i =3D 0; i < format->nelts; ++i) { - strs[i] =3D process_item(r, orig, &items[i]); + if (cls->conditions && cls->conditions->nelts !=3D 0) { + int *conds =3D (int *) cls->conditions->elts; + int in_list =3D 0; + + for (i =3D 0; i < cls->conditions->nelts; ++i) { + if (r->status =3D=3D conds[i]) { + in_list =3D 1; + break; + } + } + + if ((cls->condition_sense && in_list) + || (!cls->condition_sense && !in_list)) { + return DECLINED; + } } =20 + format =3D cls->format ? cls->format : default_format; + data =3D apr_array_make(r->pool, format->nelts, = sizeof(ap_log_ehandler_data)); + items =3D (log_format_item *) format->elts; for (i =3D 0; i < format->nelts; ++i) { - len +=3D strl[i] =3D strlen(strs[i]); + d =3D (ap_log_ehandler_data *) apr_array_push(data); + d->data =3D NULL; + d->arg =3D items[i].arg; + process_item(r, orig, &items[i], d); } - if (!log_writer) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, - "log writer isn't correctly setup"); - return HTTP_INTERNAL_SERVER_ERROR; + =20 + if (cls->writer) { /* this is a new style writer */ + rv =3D cls->writer->write(r, cls->writer_data, data); + } + else if (log_writer) { /* this is an old style writer */ + strs =3D apr_palloc(r->pool, sizeof(char *) * (format->nelts)); + strl =3D apr_palloc(r->pool, sizeof(int) * (format->nelts)); + for (i =3D 0; i < data->nelts; ++i) { + d=3D&(((ap_log_ehandler_data*)(data->elts))[i]); + if ((d) && (d->data)) { + switch (d->type) { + case AP_LOG_EHANDLER_RETURN_OLDSTYLE: + strs[i] =3D d->data; + break; + =20 + case AP_LOG_EHANDLER_RETURN_CONST: + strs[i] =3D d->data; + break; + =20 + case AP_LOG_EHANDLER_RETURN_STRING: + if (strlen(d->data)=3D=3D0) + strs[i] =3D "\"\""; + else + strs[i] =3D ap_escape_logitem(r->pool, d->data); + break; + =20 + case AP_LOG_EHANDLER_RETURN_NUMBER: + strs[i] =3D apr_psprintf(r->pool,"%" = AP_LOG_NUMBER_T_FMT,*((ap_log_number_t *) d->data)); + break; + =20 + case AP_LOG_EHANDLER_RETURN_UNUMBER: + strs[i] =3D apr_psprintf(r->pool,"%" = AP_LOG_UNUMBER_T_FMT,*((ap_log_unumber_t *) d->data)); + break; + =20 + case AP_LOG_EHANDLER_RETURN_DATETIME: + strs[i] =3D format_request_time(r,d->arg,d->data,NULL); + break; + } + } + else { + strs[i]=3D"-"; + } + } + for (i =3D 0; i < format->nelts; ++i) { + len +=3D strl[i] =3D strlen(strs[i]); + } + rv =3D log_writer(r, cls->writer_data, strs, strl, format->nelts, = len); + /* xxx: do we return an error on log_writer? */ + } + else { /* no writer at all, use our file writer as default */ + rv =3D ap_filepipe_log_ewriter(r, cls->writer_data, data); } - rv =3D log_writer(r, cls->log_writer, strs, strl, format->nelts, len); - /* xxx: do we return an error on log_writer? */ return OK; } =20 @@ -1000,21 +1114,87 @@ multi_log_state *mls =3D = ap_get_module_config(cmd->server->module_config, &log_config_module); config_log_state *cls; - + char *pos; + log_ewriter *writer; + int i; + =20 cls =3D (config_log_state *) apr_array_push(mls->config_logs); cls->condition_var =3D NULL; + cls->conditions =3D NULL; if (envclause !=3D NULL) { - if (strncasecmp(envclause, "env=3D", 4) !=3D 0) { - return "error in condition clause"; + if (strncasecmp(envclause, "env=3D", 4) =3D=3D 0) { + i =3D 4; + if (cls->condition_sense =3D (envclause[i] =3D=3D '!')) { + i++; + } + if (envclause[i] =3D=3D '\0') { + return "missing environment variable name"; + } + else { + cls->condition_var =3D apr_pstrdup(cmd->pool, = &envclause[i]); + } + } + else if (strncasecmp(envclause, "status=3D", 7) =3D=3D 0) { + i =3D 7; + if (cls->condition_sense =3D (envclause[i] =3D=3D '!')) { + i++; + } + if (envclause[i] =3D=3D '\0') { + return "missing status code(s)"; + } + else { + pos =3D (char*)&envclause[i]; + while (*pos) { + switch (*pos) { + case ',': + ++pos; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + i =3D *pos - '0'; + while (apr_isdigit(*++pos)) { + i =3D i * 10 + (*pos) - '0'; + } + if (!cls->conditions) { + cls->conditions =3D apr_array_make(cmd->pool, = 4, sizeof(int)); + } + *(int *) apr_array_push(cls->conditions) =3D i; + break; + =20 + default: + return "illegal character within status code(s)"; + } + } + } } - if ((envclause[4] =3D=3D '\0') - || ((envclause[4] =3D=3D '!') && (envclause[5] =3D=3D '\0'))) = { - return "missing environment variable name"; + else { + return "error in condition clause"; } - cls->condition_var =3D apr_pstrdup(cmd->pool, &envclause[4]); } =20 cls->fname =3D fn; + if ((pos =3D strchr(fn,':')) && (cls->writer =3D = apr_hash_get(writer_hash, fn, pos-fn)) ) { + cls->fname=3Dpos+1; + } + else { + cls->writer=3DNULL; + } + /* xxx Doesn't return an error if a key ("bla:/file") is given but no=20 + * corresponding writer exists. The reason is backwards compatibility, + * to be able to fall back to the old file write mechanism -- but is=20 + * this really necessary? Is there anybody (except fellow Amigans) + * who uses : in file system paths? + */ + =20 cls->format_string =3D fmt; if (fmt =3D=3D NULL) { cls->format =3D NULL; @@ -1022,8 +1202,7 @@ else { cls->format =3D parse_log_string(cmd->pool, fmt, &err_string); } - cls->log_writer =3D NULL; - + cls->writer_data =3D NULL; return err_string; } =20 @@ -1041,10 +1220,6 @@ static const char *set_buffered_logs_on(cmd_parms *parms, void *dummy, int = flag) { buffered_logs =3D 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[] =3D @@ -1067,16 +1242,25 @@ config_log_state *cls, apr_array_header_t = *default_format) { - if (cls->log_writer !=3D NULL) { + if (cls->writer_data !=3D NULL) { return cls; /* virtual config shared w/main server */ } =20 if (cls->fname =3D=3D NULL) { return cls; /* Leave it NULL to decline. */ } - =20 - cls->log_writer =3D log_writer_init(p, s, cls->fname); - if (cls->log_writer =3D=3D NULL) + + if (cls->writer) { /* new style style writer */ + cls->writer_data =3D cls->writer->setup(p, s, cls->fname); + } + else if (log_writer_init) { /* old style writer */ + cls->writer_data =3D log_writer_init(p, s, cls->fname); + } + else { /* default, takes care of old "|pipe" as well as simple "file" = syntax */ + cls->writer_data =3D ap_old_log_writer_init(p, s, cls->fname); + } + =20 + if (cls->writer_data =3D=3D NULL) return NULL;=20 =20 return cls; @@ -1152,23 +1336,22 @@ buffered_log *buf; int i; =20 - if (!buffered_logs) - return APR_SUCCESS; - =20 for (; s; s =3D s->next) { mls =3D ap_get_module_config(s->module_config, = &log_config_module); - log_list =3D NULL; if (mls->config_logs->nelts) { log_list =3D mls->config_logs; } else if (mls->server_config_logs) { log_list =3D mls->server_config_logs; } + else { + log_list =3D NULL; + } if (log_list) { clsarray =3D (config_log_state *) log_list->elts; for (i =3D 0; i < log_list->nelts; ++i) { - buf =3D clsarray[i].log_writer; - flush_log(buf); + if ((clsarray[i].writer) && (clsarray[i].writer->exit)) + clsarray[i].writer->exit(s, clsarray[i].writer_data); } } } @@ -1178,17 +1361,10 @@ =20 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 =3D apr_array_make(p, 5, sizeof(buffered_log = *)); - } - - /* Next, do "physical" server, which gets default log fd and format + /* First, do "physical" server, which gets default log fd and format * for the virtual servers, if they don't override... */ - res =3D open_multi_logs(s, p); + int res =3D open_multi_logs(s, p); =20 /* Then, virtual servers */ =20 @@ -1201,39 +1377,30 @@ =20 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 =3D (buffered_log **)all_buffered_logs->elts; - =20 - apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs); + multi_log_state *mls; + apr_array_header_t *log_list; + config_log_state *clsarray; + buffered_log *buf; + int i; =20 - for (i =3D 0; i < all_buffered_logs->nelts; i++) { - buffered_log *this =3D array[i]; - =20 -#if APR_HAS_THREADS - if (mpm_threads > 1) { - apr_status_t rv; + apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs); =20 - this->mutex.type =3D apr_anylock_threadmutex; - rv =3D apr_thread_mutex_create(&this->mutex.lock.tm, - APR_THREAD_MUTEX_DEFAULT, - p); - if (rv !=3D 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 =3D apr_anylock_none; - } - } - else -#endif - { - this->mutex.type =3D apr_anylock_none; + for (; s; s =3D s->next) { + mls =3D ap_get_module_config(s->module_config, = &log_config_module); + if (mls->config_logs->nelts) { + log_list =3D mls->config_logs; + } + else if (mls->server_config_logs) { + log_list =3D mls->server_config_logs; + } + else { + log_list =3D NULL; + } + if (log_list) { + clsarray =3D (config_log_state *) log_list->elts; + for (i =3D 0; i < log_list->nelts; ++i) { + if ((clsarray[i].writer) && (clsarray[i].writer->init)) + clsarray[i].writer->init(p, s, = clsarray[i].writer_data); } } } @@ -1242,173 +1409,257 @@ static void ap_register_log_handler(apr_pool_t *p, char *tag,=20 ap_log_handler_fn_t *handler, int def) { - ap_log_handler *log_struct =3D apr_palloc(p, sizeof(*log_struct)); - log_struct->func =3D handler; + log_handler *log_struct =3D apr_palloc(p, sizeof(*log_struct)); + log_struct->handler.func =3D handler; log_struct->want_orig_default =3D def; + log_struct->oldstyle =3D -1; =20 apr_hash_set(log_hash, tag, 1, (const void *)log_struct); } -static void ap_log_set_writer_init(ap_log_writer_init *handle) +static void ap_register_log_ehandler(apr_pool_t *p, char *tag,=20 + ap_log_ehandler_fn_t *handler, int = def) { - log_writer_init =3D handle; + log_handler *log_struct =3D apr_palloc(p, sizeof(*log_struct)); + log_struct->handler.efunc =3D handler; + log_struct->want_orig_default =3D def; + log_struct->oldstyle =3D 0; +=20 + apr_hash_set(log_hash, tag, 1, (const void *)log_struct); +} =20 +static ap_log_writer_init* ap_log_set_writer_init(ap_log_writer_init = *handle) +{ + ap_log_writer_init *old =3D log_writer_init; + log_writer_init =3D handle; + =20 + return old; } -static void ap_log_set_writer(ap_log_writer *handle) +static ap_log_writer* ap_log_set_writer(ap_log_writer *handle) { + ap_log_writer *old =3D log_writer; log_writer =3D handle; + =20 + return old; } =20 -static apr_status_t ap_default_log_writer( request_rec *r, - void *handle,=20 - const char **strs, - int *strl, - int nelts, - apr_size_t len) +static void ap_register_log_ewriter(apr_pool_t *p, char *key,=20 + ap_log_ewriter_setup *setup, + ap_log_ewriter *write, + ap_log_ewriter_init *init, + ap_log_ewriter_exit *exit) +{ + log_ewriter *writer_struct =3D apr_palloc(p, sizeof(*writer_struct)); + writer_struct->setup =3D setup; + writer_struct->write =3D write; + writer_struct->init =3D init; + writer_struct->exit =3D exit; + apr_hash_set(writer_hash, key, APR_HASH_KEY_STRING, (const void = *)writer_struct); +} + +static apr_status_t ap_filepipe_log_ewriter(request_rec *r, + void *handle,=20 + apr_array_header_t *data) =20 { char *str; char *s; + const char **strs; + int *strl; int i; + int len =3D 0; apr_status_t rv; + buffered_log *buf =3D (buffered_log*)handle; + ap_log_ehandler_data *d; =20 - str =3D apr_palloc(r->pool, len + 1); + strs =3D apr_palloc(r->pool, sizeof(char *) * (data->nelts)); + strl =3D apr_palloc(r->pool, sizeof(int) * (data->nelts)); + for (i =3D 0; i < data->nelts; ++i) { + d=3D&(((ap_log_ehandler_data*)(data->elts))[i]); + if ((d) && (d->data)) { + switch (d->type) + { + case AP_LOG_EHANDLER_RETURN_OLDSTYLE: + strs[i] =3D d->data; + break; + =20 + case AP_LOG_EHANDLER_RETURN_CONST: + strs[i] =3D d->data; + break; + =20 + case AP_LOG_EHANDLER_RETURN_STRING: + if (strlen(d->data)=3D=3D0) + strs[i] =3D "\"\""; + else + strs[i] =3D ap_escape_logitem(r->pool, d->data); + break; =20 - for (i =3D 0, s =3D str; i < nelts; ++i) { - memcpy(s, strs[i], strl[i]); - s +=3D strl[i]; - } + case AP_LOG_EHANDLER_RETURN_NUMBER: + strs[i] =3D apr_psprintf(r->pool,"%" = AP_LOG_NUMBER_T_FMT,*((ap_log_number_t *) d->data)); + break; =20 - rv =3D apr_file_write((apr_file_t*)handle, str, &len); + case AP_LOG_EHANDLER_RETURN_UNUMBER: + strs[i] =3D apr_psprintf(r->pool,"%" = AP_LOG_UNUMBER_T_FMT,*((ap_log_unumber_t *) d->data)); + break; =20 - return rv; -} -static void *ap_default_log_writer_init(apr_pool_t *p, server_rec *s,=20 - const char* name) -{ - if (*name =3D=3D '|') { - piped_log *pl; + case AP_LOG_EHANDLER_RETURN_DATETIME: + strs[i] =3D format_request_time(r,d->arg,d->data,NULL); + break; + } + } + else { + strs[i]=3D"-"; + } + len +=3D strl[i] =3D strlen(strs[i]); + } + len +=3D strlen(APR_EOL_STR); =20 - pl =3D ap_open_piped_log(p, name + 1); - if (pl =3D=3D NULL) { - return NULL;; + if (!buffered_logs) { + str =3D apr_palloc(r->pool, len + 1); + for (i =3D 0, s =3D str; i < data->nelts; ++i) { + memcpy(s, strs[i], strl[i]); + s +=3D strl[i]; } - return ap_piped_log_write_fd(pl); + memcpy(s, APR_EOL_STR, strlen(APR_EOL_STR)); + rv =3D apr_file_write((apr_file_t*)handle, str, &len); } else { - const char *fname =3D 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; + buffered_log *buf =3D (buffered_log*)handle; + + if (len + buf->outcnt > LOG_BUFSIZE) { + flush_log(buf); } - rv =3D apr_file_open(&fd, fname, xfer_flags, xfer_perms, p); - if (rv !=3D APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, - "could not open transfer log file %s.", = fname); - return NULL; + if (len >=3D LOG_BUFSIZE) { + apr_size_t w; + =20 + str =3D apr_palloc(r->pool, len + 1 ); + for (i =3D 0, s =3D str; i < data->nelts; ++i) { + memcpy(s, strs[i], strl[i]); + s +=3D strl[i]; + } + memcpy(s, APR_EOL_STR, strlen(APR_EOL_STR)); + w =3D len; + rv =3D apr_file_write(buf->handle, str, &w); + } + else { + for (i =3D 0, s =3D &buf->outbuf[buf->outcnt]; i < = data->nelts; ++i) { + memcpy(s, strs[i], strl[i]); + s +=3D strl[i]; + } + memcpy(s, APR_EOL_STR, strlen(APR_EOL_STR)); + buf->outcnt +=3D len; + rv =3D APR_SUCCESS; } - return fd; } + + return rv; } -static void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s,=20 - const char* name) + +static void *init_buffered_logs(apr_pool_t *p, void *handle)=20 { buffered_log *b; - b =3D apr_pcalloc(p, sizeof(buffered_log)); - b->handle =3D ap_default_log_writer_init(p, s, name); + b =3D apr_palloc(p, sizeof(buffered_log)); + b->handle =3D handle; + b->outcnt =3D 0; =20 - if (b->handle) { - *(buffered_log **)apr_array_push(all_buffered_logs) =3D b; + if (b->handle) return b; - } else return NULL; } -static apr_status_t ap_buffered_log_writer(request_rec *r, - void *handle,=20 - const char **strs, - int *strl, - int nelts, - apr_size_t len) =20 +static void *ap_file_log_writer_setup(apr_pool_t *p, server_rec *s,=20 + const char* name) { - char *str; - char *s; - int i; + const char *fname =3D ap_server_root_relative(p, name); + apr_file_t *fd; apr_status_t rv; - buffered_log *buf =3D (buffered_log*)handle; =20 - if ((rv =3D APR_ANYLOCK_LOCK(&buf->mutex)) !=3D APR_SUCCESS) { - return rv; + if (!fname) { + ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s, + "invalid transfer log path %s.", name); + return NULL; } - - if (len + buf->outcnt > LOG_BUFSIZE) { - flush_log(buf); + rv =3D apr_file_open(&fd, fname, xfer_flags, xfer_perms, p); + if (rv !=3D APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, + "could not open transfer log file %s.", fname); + return NULL; } - if (len >=3D LOG_BUFSIZE) { - apr_size_t w; + if (buffered_logs) + return init_buffered_logs(p, fd); + else + return fd; +} =20 - str =3D apr_palloc(r->pool, len + 1); - for (i =3D 0, s =3D str; i < nelts; ++i) { - memcpy(s, strs[i], strl[i]); - s +=3D strl[i]; - } - w =3D len; - rv =3D apr_file_write(buf->handle, str, &w); - =20 - } - else { - for (i =3D 0, s =3D &buf->outbuf[buf->outcnt]; i < nelts; ++i) { - memcpy(s, strs[i], strl[i]); - s +=3D strl[i]; - } - buf->outcnt +=3D len; - rv =3D APR_SUCCESS; +static void *ap_pipe_log_writer_setup(apr_pool_t *p, server_rec *s,=20 + const char* name) +{ + piped_log *pl; + + pl =3D ap_open_piped_log(p, name); + if (pl =3D=3D NULL) { + return NULL;; } + if (buffered_logs) + return init_buffered_logs(p, ap_piped_log_write_fd(pl)); + else + return ap_piped_log_write_fd(pl); +} =20 - APR_ANYLOCK_UNLOCK(&buf->mutex); - return rv; +static void *ap_old_log_writer_init(apr_pool_t *p, server_rec *s, + const char* name) +{ + if (*name =3D=3D '|') + return ap_pipe_log_writer_setup(p, s, name + 1); =20 + else + return ap_file_log_writer_setup(p, s, name); +} + +static void ap_filepipe_log_ewriter_exit(server_rec *s, void *data) +{ + if (buffered_logs) + flush_log((buffered_log*)data); } =20 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; - =20 - log_pfn_register =3D = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler); + static APR_OPTIONAL_FN_TYPE(ap_register_log_ehandler) = *log_pfn_eregister; =20 - 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, "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); - } + = ap_register_log_ewriter(p,"file",ap_file_log_writer_setup,ap_filepipe_log_ew= riter,NULL,ap_filepipe_log_ewriter_exit); + = ap_register_log_ewriter(p,"pipe",ap_pipe_log_writer_setup,ap_filepipe_log_ew= riter,NULL,ap_filepipe_log_ewriter_exit); =20 + log_pfn_eregister =3D = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_ehandler); + if (log_pfn_eregister) { + log_pfn_eregister(p, "%", constant_item, 0); + log_pfn_eregister(p, "h", log_remote_host, 0); + log_pfn_eregister(p, "a", log_remote_address, 0 ); + log_pfn_eregister(p, "A", log_local_address, 0 ); + log_pfn_eregister(p, "l", log_remote_logname, 0); + log_pfn_eregister(p, "r", log_request_line, 1); + log_pfn_eregister(p, "u", log_remote_user, 0); + log_pfn_eregister(p, "s", log_status, 1); + log_pfn_eregister(p, "f", log_request_file, 0); + log_pfn_eregister(p, "U", log_request_uri, 1); + log_pfn_eregister(p, "m", log_request_method, 0); + log_pfn_eregister(p, "H", log_request_protocol, 0); + log_pfn_eregister(p, "q", log_request_query, 0); + log_pfn_eregister(p, "b", clf_log_bytes_sent, 0); + log_pfn_eregister(p, "B", log_bytes_sent, 0); + log_pfn_eregister(p, "i", log_header_in, 0); + log_pfn_eregister(p, "o", log_header_out, 0); + log_pfn_eregister(p, "n", log_note, 0); + log_pfn_eregister(p, "e", log_env_var, 0); + log_pfn_eregister(p, "C", log_cookie, 0); + log_pfn_eregister(p, "V", log_server_name, 0); + log_pfn_eregister(p, "v", log_virtual_host, 0); + log_pfn_eregister(p, "p", log_server_port, 0); + log_pfn_eregister(p, "D", log_request_duration_microseconds, 1); + log_pfn_eregister(p, "P", log_pid_tid, 0); + log_pfn_eregister(p, "R", log_unparsed_request_uri, 1); + log_pfn_eregister(p, "t", log_request_time, 0); + log_pfn_eregister(p, "T", log_request_duration, 1); + log_pfn_eregister(p, "X", log_connection_status, 0); + } return OK; } =20 @@ -1429,6 +1680,10 @@ 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); + + APR_REGISTER_OPTIONAL_FN(ap_register_log_ehandler); + writer_hash =3D apr_hash_make(p); + APR_REGISTER_OPTIONAL_FN(ap_register_log_ewriter); } =20 module AP_MODULE_DECLARE_DATA log_config_module =3D @@ -1441,4 +1696,3 @@ config_log_cmds, /* command apr_table_t */ register_hooks /* register hooks */ }; - --==========B6C94F11EFDA6764AC7E========== Content-Type: text/plain; charset=iso-8859-1; name="patch-modules:loggers:mod_log_config.h" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="patch-modules:loggers:mod_log_config.h"; size=4615 --- modules/loggers/mod_log_config.h.orig Mon Feb 9 15:53:18 2004 +++ modules/loggers/mod_log_config.h Sun Oct 5 07:08:08 2003 @@ -21,17 +21,49 @@ #define _MOD_LOG_CONFIG_H 1 =20 /**=20 - * callback function prototype for a external log handler + * callback function prototype for an external log handler */ + +/* types of returned data */ +#define AP_LOG_EHANDLER_RETURN_CONST -1 /* the text between %-items */ +#define AP_LOG_EHANDLER_RETURN_OLDSTYLE 0 /* ap_escape_logitem() string = */ +#define AP_LOG_EHANDLER_RETURN_STRING 1 /* raw string, not escaped */ +#define AP_LOG_EHANDLER_RETURN_NUMBER 2 /* signed number, = ap_log_number_t */ +#define AP_LOG_EHANDLER_RETURN_UNUMBER 3 /* unsigned number, = ap_log_unumber_t */ +#define AP_LOG_EHANDLER_RETURN_DATETIME 4 /* time, apr_time_t */ + +#define ap_log_number_t apr_int64_t +#define ap_log_unumber_t apr_uint64_t +#define AP_LOG_NUMBER_T_FMT APR_INT64_T_FMT +#define AP_LOG_UNUMBER_T_FMT APR_UINT64_T_FMT + +/* struct to hold returned data */ +typedef struct ap_log_ehandler_data { + int type; /* AP_LOG_EHANDLER_RETURN_*, see above */ + char *arg; /* log item argument, as given in e.g. %{arg}t */ + void *data; /* pointer to data or null if n/a */ +} ap_log_ehandler_data; + typedef const char *ap_log_handler_fn_t(request_rec *r, char *a); +typedef void *ap_log_ehandler_fn_t(request_rec *r, char *a, = ap_log_ehandler_data *d); + +APR_DECLARE_OPTIONAL_FN(void, ap_register_log_handler,=20 + (apr_pool_t *p, char *tag, ap_log_handler_fn_t = *func, + int def)); +APR_DECLARE_OPTIONAL_FN(void, ap_register_log_ehandler,=20 + (apr_pool_t *p, char *tag, ap_log_ehandler_fn_t = *func, + int def)); =20 /** - * callback function prototype for a external writer initilization. + * callback function prototype for an external writer's initialization. */ typedef void *ap_log_writer_init(apr_pool_t *p, server_rec *s,=20 const char *name); +typedef void *ap_log_ewriter_setup(apr_pool_t *p, server_rec *s,=20 + const char *name); + /** - * callback which gets called where there is a log line to write. + * callback which gets called when there is a log line to write. */ typedef apr_status_t ap_log_writer( request_rec *r, @@ -40,23 +72,43 @@ int *lengths, int nelts, apr_size_t len); +typedef apr_status_t ap_log_ewriter( + request_rec *r, + void *handle,=20 + apr_array_header_t *data); =20 -typedef struct ap_log_handler { - ap_log_handler_fn_t *func; - int want_orig_default; -} ap_log_handler; +/** + * callback function prototypes for each child's external writer init and = exit + */ +typedef void ap_log_ewriter_init(apr_pool_t *p, server_rec *s, void = *data); +typedef void ap_log_ewriter_exit(server_rec *s, void *data); =20 -APR_DECLARE_OPTIONAL_FN(void, ap_register_log_handler,=20 - (apr_pool_t *p, char *tag, ap_log_handler_fn_t = *func, - int def)); /** * you will need to set your init handler *BEFORE* the open_logs=20 * in mod_log_config gets executed - */ -APR_DECLARE_OPTIONAL_FN(void, ap_log_set_writer_init,(ap_log_writer_init = *func)); -/**=20 * you should probably set the writer at the same time (ie..before = open_logs) + * + * ap_log_set_writer() deprecated, better use ap_register_log_ewriter() + */ + +APR_DECLARE_OPTIONAL_FN(ap_log_writer_init*, = ap_log_set_writer_init,(ap_log_writer_init *func)); +APR_DECLARE_OPTIONAL_FN(ap_log_writer*, ap_log_set_writer, (ap_log_writer* = func)); + +/* + * Register a new log writer. + * p pool, you get this as argument to your pre_config hook + * key the scheme part of a CustomLog uri, identifies this log writer + * write write a line of log data + * setup called during ap_hook_open_logs + * init called during ap_hook_child_init + * exit called upon child exit (registered as pool cleanup from = ap_hook_child_init()) */ -APR_DECLARE_OPTIONAL_FN(void, ap_log_set_writer, (ap_log_writer* func)); +APR_DECLARE_OPTIONAL_FN(void, ap_register_log_ewriter,=20 + (apr_pool_t *p, char *key,=20 + ap_log_ewriter_setup *setup, + ap_log_ewriter *write, + ap_log_ewriter_init *init, + ap_log_ewriter_exit *exit) + ); =20 #endif /* MOD_LOG_CONFIG */ --==========B6C94F11EFDA6764AC7E========== Content-Type: text/plain; charset=iso-8859-1; name="patch-modules:loggers:mod_logio.c" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="patch-modules:loggers:mod_logio.c"; size=1974 --- modules/loggers/mod_logio.c.orig Mon Feb 9 15:53:18 2004 +++ modules/loggers/mod_logio.c Sun Oct 5 07:08:08 2003 @@ -29,7 +29,6 @@ #include "apr_lib.h" #include "apr_hash.h" #include "apr_optional.h" - #define APR_WANT_STRFUNC #include "apr_want.h" =20 @@ -40,6 +39,7 @@ #include "http_config.h" #include "http_connection.h" #include "http_protocol.h" +#include "http_log.h" =20 module AP_MODULE_DECLARE_DATA logio_module; =20 @@ -68,20 +68,23 @@ * Format items... */ =20 -static const char *log_bytes_in(request_rec *r, char *a) +static void *log_bytes_in(request_rec *r, char *a, ap_log_ehandler_data = *d) { logio_config_t *cf =3D = ap_get_module_config(r->connection->conn_config, &logio_module); - - return apr_off_t_toa(r->pool, cf->bytes_in); + d->type =3D AP_LOG_EHANDLER_RETURN_UNUMBER; + d->data =3D apr_palloc(r->pool, sizeof(ap_log_unumber_t)); + *(ap_log_unumber_t*)d->data =3D cf->bytes_in; } =20 -static const char *log_bytes_out(request_rec *r, char *a) +static void *log_bytes_out(request_rec *r, char *a, ap_log_ehandler_data = *d) { logio_config_t *cf =3D = ap_get_module_config(r->connection->conn_config, &logio_module); =20 - return apr_off_t_toa(r->pool, cf->bytes_out); + d->type =3D AP_LOG_EHANDLER_RETURN_UNUMBER; + d->data =3D apr_palloc(r->pool, sizeof(ap_log_unumber_t)); + *(ap_log_unumber_t*)d->data =3D cf->bytes_out; } =20 /* @@ -153,9 +156,9 @@ =20 static int logio_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; + static APR_OPTIONAL_FN_TYPE(ap_register_log_ehandler) = *log_pfn_register; =20 - log_pfn_register =3D = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler); + log_pfn_register =3D = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_ehandler); =20 if (log_pfn_register) { log_pfn_register(p, "I", log_bytes_in, 0); --==========B6C94F11EFDA6764AC7E==========--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?61670F38EE92BEC06E2BE009>