apache2 & mod_log_config-st & mod_log_mysql
Yarema
yds at CoolRat.org
Mon Jun 7 08:17:53 GMT 2004
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
-------------- next part --------------
--- 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 = apr_pcalloc(reslist->pool, sizeof(*res));
rv = reslist->constructor(&res->opaque, reslist->params, reslist->pool);
- if (rv != APR_SUCCESS) {
- return rv;
- }
*ret_res = res;
- return APR_SUCCESS;
+ return rv;
}
/**
@@ -132,14 +130,7 @@
*/
static apr_status_t destroy_resource(apr_reslist_t *reslist, apr_res_t *res)
{
- apr_status_t rv;
-
- rv = reslist->destructor(res->opaque, reslist->params, reslist->pool);
- if (rv != APR_SUCCESS) {
- return rv;
- }
-
- return APR_SUCCESS;
+ return reslist->destructor(res->opaque, reslist->params, reslist->pool);
}
static apr_status_t reslist_cleanup(void *data_)
@@ -187,6 +178,7 @@
/* Create the resource */
rv = create_resource(reslist, &res);
if (rv != 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 >= reslist->hmax
&& reslist->nidle <= 0) {
- apr_thread_cond_wait(reslist->avail, reslist->listlock);
+ if (reslist->timeout) {
+ if ((rv = apr_thread_cond_timedwait(reslist->avail,
+ reslist->listlock, reslist->timeout)) != 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 = create_resource(reslist, &res);
-
- if (rv != APR_SUCCESS) {
- apr_thread_mutex_unlock(reslist->listlock);
- return rv;
+ if (rv == APR_SUCCESS) {
+ reslist->ntotal++;
+ *resource = res->opaque;
}
-
- reslist->ntotal++;
- *resource = res->opaque;
free_container(reslist, res);
apr_thread_mutex_unlock(reslist->listlock);
- return APR_SUCCESS;
+ return rv;
}
}
@@ -356,6 +352,23 @@
apr_thread_mutex_unlock(reslist->listlock);
return reslist_maint(reslist);
+}
+
+APU_DECLARE(void) apr_reslist_timeout_set(apr_reslist_t *reslist,
+ apr_interval_time_t timeout)
+{
+ reslist->timeout = 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 = reslist->destructor(resource, reslist->params, reslist->pool);
+ reslist->ntotal--;
+ apr_thread_mutex_unlock(reslist->listlock);
+ return ret;
}
#endif /* APR_HAS_THREADS */
-------------- next part --------------
--- 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 at 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
+ * 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
+ * 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 @@
*
* 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=!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
+ * 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' = connection aborted before the response completed.
* '+' = connection may be kept alive after the response is sent.
* '-' = 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"
#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"
#if APR_HAVE_UNISTD_H
#include <unistd.h>
@@ -173,30 +190,35 @@
static int xfer_flags = (APR_WRITE | APR_APPEND | APR_CREATE);
static apr_fileperms_t xfer_perms = 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,
- const char **strs,
- int *strl,
- int nelts,
- apr_size_t len);
-static apr_status_t ap_buffered_log_writer(request_rec *r,
- void *handle,
- 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,
- const char* name);
-static void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s,
- 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 = ap_default_log_writer;
-static ap_log_writer_init *log_writer_init = 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,
+ const char* name);
+static apr_status_t ap_filepipe_log_ewriter(request_rec *r,
+ void *handle,
+ apr_array_header_t *data);
+
+static ap_log_writer *log_writer = NULL;
+static ap_log_writer_init *log_writer_init = NULL;
+
static int buffered_logs = 0; /* default unbuffered */
-static apr_array_header_t *all_buffered_logs = NULL;
/* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is
* guaranteed to be atomic when writing a pipe. And PIPE_BUF >= 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.
- * log_writer is NULL before the log file is opened and is
- * set to a opaque structure (usually a fd) after it is opened.
-
+ * 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;
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;
/*
@@ -267,168 +292,179 @@
*/
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;
-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 <= 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=stuff;
+ d->arg=NULL;
+ d->type=AP_LOG_EHANDLER_RETURN_CONST;
}
-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=(void *) ap_get_remote_host(r->connection,
+ r->per_dir_config,
+ REMOTE_NAME, NULL);
+ d->type=AP_LOG_EHANDLER_RETURN_STRING;
}
-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=r->connection->remote_ip;
+ d->type=AP_LOG_EHANDLER_RETURN_STRING;
}
-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=r->connection->local_ip;
+ d->type=AP_LOG_EHANDLER_RETURN_STRING;
}
-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=(void *) ap_get_remote_logname(r);
+ d->type=AP_LOG_EHANDLER_RETURN_STRING;
}
-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 = r->user;
-
- if (rvalue == NULL) {
+ d->data = r->user;
+ d->type=AP_LOG_EHANDLER_RETURN_STRING;
+
+ /*
+ if (d->data == NULL) {
rvalue = "-";
}
- else if (strlen(rvalue) == 0) {
+ else if (strlen(d->data) == 0) {
rvalue = "\"\"";
}
else {
rvalue = ap_escape_logitem(r->pool, rvalue);
}
-
- return rvalue;
+ */
}
-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 = (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 = AP_LOG_EHANDLER_RETURN_STRING;
}
-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 = r->filename;
+ d->type = 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 = r->uri;
+ d->type = 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 = r->unparsed_uri;
+ d->type = 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 = (void *)r->method;
+ d->type = 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 = r->protocol;
+ d->type = 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)
+{
+ if (r->args)
+ d->data=apr_pstrcat(r->pool, "?", r->args, NULL);
+ d->type=AP_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=apr_palloc(r->pool,sizeof(ap_log_unumber_t));
+ *((ap_log_unumber_t *) d->data)=r->status;
+ }
+ d->type=AP_LOG_EHANDLER_RETURN_UNUMBER;
}
-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="-";
}
else {
- return apr_off_t_toa(r->pool, r->bytes_sent);
+ d->data=apr_off_t_toa(r->pool, r->bytes_sent);
}
+ d->type=AP_LOG_EHANDLER_RETURN_STRING;
}
-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 == 0) {
+ d->data=apr_palloc(r->pool,sizeof(ap_log_unumber_t));
+ *((ap_log_unumber_t *) d->data)=r->bytes_sent;
}
+ d->type=AP_LOG_EHANDLER_RETURN_UNUMBER;
}
-
-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=(void *) apr_table_get(r->headers_in, a);
+ d->type=AP_LOG_EHANDLER_RETURN_STRING;
}
-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 = apr_table_get(r->headers_out, a);
- if (!strcasecmp(a, "Content-type") && r->content_type) {
- cp = 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)
+ d->data = ap_field_noparam(r->pool, r->content_type);
+ else
+ d->data = (void *) apr_table_get(r->headers_out, a);
+ if (! d->data)
+ d->data = (void *) apr_table_get(r->err_headers_out, a);
+ d->type=AP_LOG_EHANDLER_RETURN_STRING;
}
-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 = (void *) apr_table_get(r->notes, a);
+ d->type=AP_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 = (void *) apr_table_get(r->subprocess_env, a);
+ d->type=AP_LOG_EHANDLER_RETURN_STRING;
}
-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;
+ d->type=AP_LOG_EHANDLER_RETURN_STRING;
if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
if ((start_cookie = ap_strstr_c(cookies,a))) {
char *cookie, *end_cookie;
@@ -439,169 +475,94 @@
if (end_cookie) {
*end_cookie = '\0';
}
- return ap_escape_logitem(r->pool, cookie);
+ d->data=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)
+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 = apr_palloc(r->pool,
- sizeof(*cached_time));
+ d->type=AP_LOG_EHANDLER_RETURN_DATETIME;
+ d->data=apr_palloc(r->pool,sizeof(apr_time_t));
#ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
- apr_time_t request_time = apr_time_now();
+ *((apr_time_t *) d->data) = apr_time_now();
#else
- apr_time_t request_time = r->request_time;
+ *((apr_time_t *) d->data) = r->request_time;
#endif
- unsigned t_seconds = (unsigned)apr_time_sec(request_time);
- unsigned i = t_seconds & TIME_CACHE_MASK;
- memcpy(cached_time, &(request_time_cache[i]), sizeof(*cached_time));
- 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, r->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;
- memcpy(&(request_time_cache[i]), cached_time,
- sizeof(*cached_time));
- }
- return cached_time->timestr;
- }
}
-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 = apr_time_now() - r->request_time;
- return apr_psprintf(r->pool, "%" APR_TIME_T_FMT, apr_time_sec(duration));
+ d->data=apr_palloc(r->pool,sizeof(ap_log_unumber_t));
+ *((ap_log_unumber_t *) d->data) = apr_time_sec(apr_time_now() - r->request_time);
+ d->arg=a;
+ d->type=AP_LOG_EHANDLER_RETURN_UNUMBER;
}
-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,
- (apr_time_now() - r->request_time));
+ d->data=apr_palloc(r->pool,sizeof(ap_log_unumber_t));
+ *((ap_log_unumber_t *) d->data) = apr_time_now() - r->request_time;
+ d->arg=a;
+ d->type=AP_LOG_EHANDLER_RETURN_UNUMBER;
}
/* 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 = r->server->server_hostname;
+ d->arg=a;
+ d->type = AP_LOG_EHANDLER_RETURN_STRING;
}
-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=apr_palloc(r->pool,sizeof(ap_log_unumber_t));
+ *((ap_log_unumber_t *) d->data) = r->server->port ? r->server->port : ap_default_port(r);
+ d->arg=a;
+ d->type=AP_LOG_EHANDLER_RETURN_UNUMBER;
}
/* 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 = (void *) ap_get_server_name(r);
+ d->arg=a;
+ d->type = AP_LOG_EHANDLER_RETURN_STRING;
}
-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 == '\0' || !strcmp(a, "pid")) {
- return apr_psprintf(r->pool, "%" APR_PID_T_FMT, getpid());
+ d->arg=a;
+ if (!a || *a == '\0' || !strcmp(a, "pid")) {
+ d->data = apr_palloc(r->pool,sizeof(ap_log_unumber_t));
+ *((ap_log_unumber_t *) d->data) = getpid();
}
- else if (!strcmp(a, "tid")) {
#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, "%pT", &tid);
+ else if (!strcmp(a, "tid")) {
+ d->data = apr_palloc(r->pool,sizeof(ap_log_unumber_t));
+ *((ap_log_unumber_t *) d->data) = apr_os_thread_current();
}
- /* bogus format */
- return a;
+#endif
+ d->type = AP_LOG_EHANDLER_RETURN_UNUMBER;
}
-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 == AP_CONN_KEEPALIVE &&
+ d->data = "X";
+ else if (r->connection->keepalive == AP_CONN_KEEPALIVE &&
(!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))
+ d->data = "+";
+ else
+ d->data = "-";
+ d->arg=a;
+ d->type = AP_LOG_EHANDLER_RETURN_STRING;
}
/*****************************************************************
@@ -615,7 +576,7 @@
const char *s;
char *d;
- it->func = constant_item;
+ it->handler = (log_handler *)apr_hash_get(log_hash, "%", 1);
it->conditions = NULL;
s = *sa;
@@ -674,7 +635,6 @@
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);
@@ -686,14 +646,15 @@
if (*s == '%') {
it->arg = "%";
- it->func = constant_item;
+ it->handler = (log_handler *)apr_hash_get(log_hash, "%", 1);
+
*sa = ++s;
return NULL;
}
it->want_orig = -1;
- it->arg = ""; /* For safety's sake... */
+ it->arg = NULL; /* For safety's sake... */
while (*s) {
int i;
@@ -744,8 +705,8 @@
break;
default:
- handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1);
- if (!handler) {
+ it->handler = (log_handler *)apr_hash_get(log_hash, s++, 1);
+ if (!it->handler) {
char dummy[2];
dummy[0] = s[-1];
@@ -753,9 +714,8 @@
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;
+ it->want_orig = it->handler->want_orig_default;
}
*sa = s;
return NULL;
@@ -776,9 +736,6 @@
return NULL;
}
}
-
- s = APR_EOL_STR;
- parse_log_item(p, (log_format_item *) apr_array_push(a), &s);
return a;
}
@@ -787,11 +744,106 @@
* Actually logging.
*/
-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 = 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 = *t;
+#endif
+ unsigned t_seconds = (unsigned)apr_time_sec(request_time);
+ unsigned i = t_seconds & TIME_CACHE_MASK;
+ memcpy(cached_time, &(request_time_cache[i]), sizeof(*cached_time));
+ 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, *t);
+ 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));
+ cached_time->t_validate = t_seconds;
+ memcpy(&(request_time_cache[i]), cached_time,
+ sizeof(*cached_time));
+ }
+ return cached_time->timestr;
+ }
+}
+
+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... */
if (item->conditions && item->conditions->nelts != 0) {
@@ -808,14 +860,22 @@
if ((item->condition_sense && in_list)
|| (!item->condition_sense && !in_list)) {
- return "-";
+ d->type=AP_LOG_EHANDLER_RETURN_STRING;
+ return;
}
}
+
/* We do. Do it... */
- cp = (*item->func) (item->want_orig ? orig : r, item->arg);
- return cp ? cp : "-";
+ if (item->handler->oldstyle) {
+ if (! (d->data = (void *)(*item->handler->handler.func) (item->want_orig ? orig : r, item->arg)))
+ d->data = "-";
+ d->type = AP_LOG_EHANDLER_RETURN_OLDSTYLE;
+ }
+ else {
+ (*item->handler->handler.efunc) (item->want_orig ? orig : r, item->arg, d);
+ }
}
static void flush_log(buffered_log *buf)
@@ -837,9 +897,10 @@
int i;
apr_size_t len = 0;
apr_array_header_t *format;
- char *envar;
apr_status_t rv;
-
+ apr_array_header_t *data;
+ ap_log_ehandler_data *d;
+
if (cls->fname == NULL) {
return DECLINED;
}
@@ -849,25 +910,14 @@
* 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;
- }
+ if ((cls->condition_sense && apr_table_get(r->subprocess_env, cls->condition_var) != NULL)
+ ||
+ (!cls->condition_sense && apr_table_get(r->subprocess_env, cls->condition_var) == 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;
@@ -876,20 +926,84 @@
r = r->next;
}
- for (i = 0; i < format->nelts; ++i) {
- strs[i] = process_item(r, orig, &items[i]);
+ if (cls->conditions && cls->conditions->nelts != 0) {
+ int *conds = (int *) cls->conditions->elts;
+ int in_list = 0;
+
+ for (i = 0; i < cls->conditions->nelts; ++i) {
+ if (r->status == conds[i]) {
+ in_list = 1;
+ break;
+ }
+ }
+
+ if ((cls->condition_sense && in_list)
+ || (!cls->condition_sense && !in_list)) {
+ return DECLINED;
+ }
}
+ format = cls->format ? cls->format : default_format;
+ data = apr_array_make(r->pool, format->nelts, sizeof(ap_log_ehandler_data));
+ items = (log_format_item *) format->elts;
for (i = 0; i < format->nelts; ++i) {
- len += strl[i] = strlen(strs[i]);
+ d = (ap_log_ehandler_data *) apr_array_push(data);
+ d->data = NULL;
+ d->arg = 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;
+
+ if (cls->writer) { /* this is a new style writer */
+ rv = cls->writer->write(r, cls->writer_data, data);
+ }
+ else if (log_writer) { /* this is an old style writer */
+ strs = apr_palloc(r->pool, sizeof(char *) * (format->nelts));
+ strl = apr_palloc(r->pool, sizeof(int) * (format->nelts));
+ for (i = 0; i < data->nelts; ++i) {
+ d=&(((ap_log_ehandler_data*)(data->elts))[i]);
+ if ((d) && (d->data)) {
+ switch (d->type) {
+ case AP_LOG_EHANDLER_RETURN_OLDSTYLE:
+ strs[i] = d->data;
+ break;
+
+ case AP_LOG_EHANDLER_RETURN_CONST:
+ strs[i] = d->data;
+ break;
+
+ case AP_LOG_EHANDLER_RETURN_STRING:
+ if (strlen(d->data)==0)
+ strs[i] = "\"\"";
+ else
+ strs[i] = ap_escape_logitem(r->pool, d->data);
+ break;
+
+ case AP_LOG_EHANDLER_RETURN_NUMBER:
+ strs[i] = apr_psprintf(r->pool,"%" AP_LOG_NUMBER_T_FMT,*((ap_log_number_t *) d->data));
+ break;
+
+ case AP_LOG_EHANDLER_RETURN_UNUMBER:
+ strs[i] = apr_psprintf(r->pool,"%" AP_LOG_UNUMBER_T_FMT,*((ap_log_unumber_t *) d->data));
+ break;
+
+ case AP_LOG_EHANDLER_RETURN_DATETIME:
+ strs[i] = format_request_time(r,d->arg,d->data,NULL);
+ break;
+ }
+ }
+ else {
+ strs[i]="-";
+ }
+ }
+ for (i = 0; i < format->nelts; ++i) {
+ len += strl[i] = strlen(strs[i]);
+ }
+ rv = 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 = ap_filepipe_log_ewriter(r, cls->writer_data, data);
}
- rv = log_writer(r, cls->log_writer, strs, strl, format->nelts, len);
- /* xxx: do we return an error on log_writer? */
return OK;
}
@@ -1000,21 +1114,87 @@
multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
&log_config_module);
config_log_state *cls;
-
+ char *pos;
+ log_ewriter *writer;
+ int i;
+
cls = (config_log_state *) apr_array_push(mls->config_logs);
cls->condition_var = NULL;
+ cls->conditions = NULL;
if (envclause != NULL) {
- if (strncasecmp(envclause, "env=", 4) != 0) {
- return "error in condition clause";
+ if (strncasecmp(envclause, "env=", 4) == 0) {
+ i = 4;
+ if (cls->condition_sense = (envclause[i] == '!')) {
+ i++;
+ }
+ if (envclause[i] == '\0') {
+ return "missing environment variable name";
+ }
+ else {
+ cls->condition_var = apr_pstrdup(cmd->pool, &envclause[i]);
+ }
+ }
+ else if (strncasecmp(envclause, "status=", 7) == 0) {
+ i = 7;
+ if (cls->condition_sense = (envclause[i] == '!')) {
+ i++;
+ }
+ if (envclause[i] == '\0') {
+ return "missing status code(s)";
+ }
+ else {
+ pos = (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 = *pos - '0';
+ while (apr_isdigit(*++pos)) {
+ i = i * 10 + (*pos) - '0';
+ }
+ if (!cls->conditions) {
+ cls->conditions = apr_array_make(cmd->pool, 4, sizeof(int));
+ }
+ *(int *) apr_array_push(cls->conditions) = i;
+ break;
+
+ default:
+ return "illegal character within status code(s)";
+ }
+ }
+ }
}
- if ((envclause[4] == '\0')
- || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
- return "missing environment variable name";
+ else {
+ return "error in condition clause";
}
- cls->condition_var = apr_pstrdup(cmd->pool, &envclause[4]);
}
cls->fname = fn;
+ if ((pos = strchr(fn,':')) && (cls->writer = apr_hash_get(writer_hash, fn, pos-fn)) ) {
+ cls->fname=pos+1;
+ }
+ else {
+ cls->writer=NULL;
+ }
+ /* xxx Doesn't return an error if a key ("bla:/file") is given but no
+ * corresponding writer exists. The reason is backwards compatibility,
+ * to be able to fall back to the old file write mechanism -- but is
+ * this really necessary? Is there anybody (except fellow Amigans)
+ * who uses : in file system paths?
+ */
+
cls->format_string = fmt;
if (fmt == NULL) {
cls->format = NULL;
@@ -1022,8 +1202,7 @@
else {
cls->format = parse_log_string(cmd->pool, fmt, &err_string);
}
- cls->log_writer = NULL;
-
+ cls->writer_data = NULL;
return err_string;
}
@@ -1041,10 +1220,6 @@
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[] =
@@ -1067,16 +1242,25 @@
config_log_state *cls,
apr_array_header_t *default_format)
{
- if (cls->log_writer != NULL) {
+ if (cls->writer_data != 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)
+
+ if (cls->writer) { /* new style style writer */
+ cls->writer_data = cls->writer->setup(p, s, cls->fname);
+ }
+ else if (log_writer_init) { /* old style writer */
+ cls->writer_data = log_writer_init(p, s, cls->fname);
+ }
+ else { /* default, takes care of old "|pipe" as well as simple "file" syntax */
+ cls->writer_data = ap_old_log_writer_init(p, s, cls->fname);
+ }
+
+ if (cls->writer_data == NULL)
return NULL;
return cls;
@@ -1152,23 +1336,22 @@
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;
}
+ else {
+ log_list = NULL;
+ }
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);
+ if ((clsarray[i].writer) && (clsarray[i].writer->exit))
+ clsarray[i].writer->exit(s, clsarray[i].writer_data);
}
}
}
@@ -1178,17 +1361,10 @@
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
+ /* First, 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);
+ int res = open_multi_logs(s, p);
/* Then, virtual servers */
@@ -1201,39 +1377,30 @@
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);
+ multi_log_state *mls;
+ apr_array_header_t *log_list;
+ config_log_state *clsarray;
+ buffered_log *buf;
+ int i;
- 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;
+ apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs);
- 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;
+ for (; s; s = s->next) {
+ mls = ap_get_module_config(s->module_config, &log_config_module);
+ if (mls->config_logs->nelts) {
+ log_list = mls->config_logs;
+ }
+ else if (mls->server_config_logs) {
+ log_list = mls->server_config_logs;
+ }
+ else {
+ log_list = NULL;
+ }
+ if (log_list) {
+ clsarray = (config_log_state *) log_list->elts;
+ for (i = 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,
ap_log_handler_fn_t *handler, int def)
{
- ap_log_handler *log_struct = apr_palloc(p, sizeof(*log_struct));
- log_struct->func = handler;
+ log_handler *log_struct = apr_palloc(p, sizeof(*log_struct));
+ log_struct->handler.func = handler;
log_struct->want_orig_default = def;
+ log_struct->oldstyle = -1;
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,
+ ap_log_ehandler_fn_t *handler, int def)
{
- log_writer_init = handle;
+ log_handler *log_struct = apr_palloc(p, sizeof(*log_struct));
+ log_struct->handler.efunc = handler;
+ log_struct->want_orig_default = def;
+ log_struct->oldstyle = 0;
+
+ 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 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 = 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)
+static void ap_register_log_ewriter(apr_pool_t *p, char *key,
+ ap_log_ewriter_setup *setup,
+ ap_log_ewriter *write,
+ ap_log_ewriter_init *init,
+ ap_log_ewriter_exit *exit)
+{
+ log_ewriter *writer_struct = apr_palloc(p, sizeof(*writer_struct));
+ writer_struct->setup = setup;
+ writer_struct->write = write;
+ writer_struct->init = init;
+ writer_struct->exit = 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,
+ apr_array_header_t *data)
{
char *str;
char *s;
+ const char **strs;
+ int *strl;
int i;
+ int len = 0;
apr_status_t rv;
+ buffered_log *buf = (buffered_log*)handle;
+ ap_log_ehandler_data *d;
- str = apr_palloc(r->pool, len + 1);
+ strs = apr_palloc(r->pool, sizeof(char *) * (data->nelts));
+ strl = apr_palloc(r->pool, sizeof(int) * (data->nelts));
+ for (i = 0; i < data->nelts; ++i) {
+ d=&(((ap_log_ehandler_data*)(data->elts))[i]);
+ if ((d) && (d->data)) {
+ switch (d->type)
+ {
+ case AP_LOG_EHANDLER_RETURN_OLDSTYLE:
+ strs[i] = d->data;
+ break;
+
+ case AP_LOG_EHANDLER_RETURN_CONST:
+ strs[i] = d->data;
+ break;
+
+ case AP_LOG_EHANDLER_RETURN_STRING:
+ if (strlen(d->data)==0)
+ strs[i] = "\"\"";
+ else
+ strs[i] = ap_escape_logitem(r->pool, d->data);
+ break;
- for (i = 0, s = str; i < nelts; ++i) {
- memcpy(s, strs[i], strl[i]);
- s += strl[i];
- }
+ case AP_LOG_EHANDLER_RETURN_NUMBER:
+ strs[i] = apr_psprintf(r->pool,"%" AP_LOG_NUMBER_T_FMT,*((ap_log_number_t *) d->data));
+ break;
- rv = apr_file_write((apr_file_t*)handle, str, &len);
+ case AP_LOG_EHANDLER_RETURN_UNUMBER:
+ strs[i] = apr_psprintf(r->pool,"%" AP_LOG_UNUMBER_T_FMT,*((ap_log_unumber_t *) d->data));
+ break;
- return rv;
-}
-static void *ap_default_log_writer_init(apr_pool_t *p, server_rec *s,
- const char* name)
-{
- if (*name == '|') {
- piped_log *pl;
+ case AP_LOG_EHANDLER_RETURN_DATETIME:
+ strs[i] = format_request_time(r,d->arg,d->data,NULL);
+ break;
+ }
+ }
+ else {
+ strs[i]="-";
+ }
+ len += strl[i] = strlen(strs[i]);
+ }
+ len += strlen(APR_EOL_STR);
- pl = ap_open_piped_log(p, name + 1);
- if (pl == NULL) {
- return NULL;;
+ if (!buffered_logs) {
+ str = apr_palloc(r->pool, len + 1);
+ for (i = 0, s = str; i < data->nelts; ++i) {
+ memcpy(s, strs[i], strl[i]);
+ s += strl[i];
}
- return ap_piped_log_write_fd(pl);
+ memcpy(s, APR_EOL_STR, strlen(APR_EOL_STR));
+ rv = apr_file_write((apr_file_t*)handle, str, &len);
}
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;
+ buffered_log *buf = (buffered_log*)handle;
+
+ if (len + buf->outcnt > LOG_BUFSIZE) {
+ flush_log(buf);
}
- 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;
+ if (len >= LOG_BUFSIZE) {
+ apr_size_t w;
+
+ str = apr_palloc(r->pool, len + 1 );
+ for (i = 0, s = str; i < data->nelts; ++i) {
+ memcpy(s, strs[i], strl[i]);
+ s += strl[i];
+ }
+ memcpy(s, APR_EOL_STR, strlen(APR_EOL_STR));
+ w = len;
+ rv = apr_file_write(buf->handle, str, &w);
+ }
+ else {
+ for (i = 0, s = &buf->outbuf[buf->outcnt]; i < data->nelts; ++i) {
+ memcpy(s, strs[i], strl[i]);
+ s += strl[i];
+ }
+ memcpy(s, APR_EOL_STR, strlen(APR_EOL_STR));
+ buf->outcnt += len;
+ rv = APR_SUCCESS;
}
- return fd;
}
+
+ return rv;
}
-static void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s,
- const char* name)
+
+static void *init_buffered_logs(apr_pool_t *p, void *handle)
{
buffered_log *b;
- b = apr_pcalloc(p, sizeof(buffered_log));
- b->handle = ap_default_log_writer_init(p, s, name);
+ b = apr_palloc(p, sizeof(buffered_log));
+ b->handle = handle;
+ b->outcnt = 0;
- if (b->handle) {
- *(buffered_log **)apr_array_push(all_buffered_logs) = b;
+ if (b->handle)
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)
+static void *ap_file_log_writer_setup(apr_pool_t *p, server_rec *s,
+ const char* name)
{
- char *str;
- char *s;
- int i;
+ const char *fname = ap_server_root_relative(p, name);
+ apr_file_t *fd;
apr_status_t rv;
- buffered_log *buf = (buffered_log*)handle;
- if ((rv = APR_ANYLOCK_LOCK(&buf->mutex)) != 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 = 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;
}
- if (len >= LOG_BUFSIZE) {
- apr_size_t w;
+ if (buffered_logs)
+ return init_buffered_logs(p, fd);
+ else
+ return fd;
+}
- 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;
+static void *ap_pipe_log_writer_setup(apr_pool_t *p, server_rec *s,
+ const char* name)
+{
+ piped_log *pl;
+
+ pl = ap_open_piped_log(p, name);
+ if (pl == 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);
+}
- 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 == '|')
+ return ap_pipe_log_writer_setup(p, s, name + 1);
+ 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);
}
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);
+ static APR_OPTIONAL_FN_TYPE(ap_register_log_ehandler) *log_pfn_eregister;
- 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_ewriter,NULL,ap_filepipe_log_ewriter_exit);
+ ap_register_log_ewriter(p,"pipe",ap_pipe_log_writer_setup,ap_filepipe_log_ewriter,NULL,ap_filepipe_log_ewriter_exit);
+ log_pfn_eregister = 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;
}
@@ -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 = apr_hash_make(p);
+ APR_REGISTER_OPTIONAL_FN(ap_register_log_ewriter);
}
module AP_MODULE_DECLARE_DATA log_config_module =
@@ -1441,4 +1696,3 @@
config_log_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};
-
-------------- next part --------------
--- 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
/**
- * 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,
+ (apr_pool_t *p, char *tag, ap_log_handler_fn_t *func,
+ int def));
+APR_DECLARE_OPTIONAL_FN(void, ap_register_log_ehandler,
+ (apr_pool_t *p, char *tag, ap_log_ehandler_fn_t *func,
+ int def));
/**
- * 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,
const char *name);
+typedef void *ap_log_ewriter_setup(apr_pool_t *p, server_rec *s,
+ 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,
+ apr_array_header_t *data);
-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);
-APR_DECLARE_OPTIONAL_FN(void, ap_register_log_handler,
- (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
* in mod_log_config gets executed
- */
-APR_DECLARE_OPTIONAL_FN(void, ap_log_set_writer_init,(ap_log_writer_init *func));
-/**
* 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,
+ (apr_pool_t *p, char *key,
+ ap_log_ewriter_setup *setup,
+ ap_log_ewriter *write,
+ ap_log_ewriter_init *init,
+ ap_log_ewriter_exit *exit)
+ );
#endif /* MOD_LOG_CONFIG */
-------------- next part --------------
--- 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"
@@ -40,6 +39,7 @@
#include "http_config.h"
#include "http_connection.h"
#include "http_protocol.h"
+#include "http_log.h"
module AP_MODULE_DECLARE_DATA logio_module;
@@ -68,20 +68,23 @@
* Format items...
*/
-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 = ap_get_module_config(r->connection->conn_config,
&logio_module);
-
- return apr_off_t_toa(r->pool, cf->bytes_in);
+ d->type = AP_LOG_EHANDLER_RETURN_UNUMBER;
+ d->data = apr_palloc(r->pool, sizeof(ap_log_unumber_t));
+ *(ap_log_unumber_t*)d->data = cf->bytes_in;
}
-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 = ap_get_module_config(r->connection->conn_config,
&logio_module);
- return apr_off_t_toa(r->pool, cf->bytes_out);
+ d->type = AP_LOG_EHANDLER_RETURN_UNUMBER;
+ d->data = apr_palloc(r->pool, sizeof(ap_log_unumber_t));
+ *(ap_log_unumber_t*)d->data = cf->bytes_out;
}
/*
@@ -153,9 +156,9 @@
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;
- log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
+ log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_ehandler);
if (log_pfn_register) {
log_pfn_register(p, "I", log_bytes_in, 0);
More information about the freebsd-ports
mailing list