2016-03-19 15:14:35 +00:00
|
|
|
#include "first.h"
|
|
|
|
|
2020-11-22 07:41:11 +00:00
|
|
|
#include "sys-time.h"
|
|
|
|
|
2009-10-11 14:31:42 +00:00
|
|
|
#include "base.h"
|
2017-03-28 04:04:31 +00:00
|
|
|
#include "fdevent.h"
|
2021-09-11 03:14:43 +00:00
|
|
|
#include "fdlog.h"
|
2009-10-11 14:31:42 +00:00
|
|
|
#include "log.h"
|
|
|
|
#include "buffer.h"
|
2018-09-09 05:50:33 +00:00
|
|
|
#include "http_header.h"
|
2017-11-18 03:09:51 +00:00
|
|
|
#include "sock_addr.h"
|
2009-10-11 14:31:42 +00:00
|
|
|
|
|
|
|
#include "plugin.h"
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_SYSLOG_H
|
|
|
|
# include <syslog.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char key;
|
2006-10-04 13:26:23 +00:00
|
|
|
enum {
|
2005-02-20 14:27:00 +00:00
|
|
|
FORMAT_UNSET,
|
|
|
|
FORMAT_UNSUPPORTED,
|
|
|
|
FORMAT_PERCENT,
|
|
|
|
FORMAT_REMOTE_HOST,
|
|
|
|
FORMAT_REMOTE_IDENT,
|
|
|
|
FORMAT_REMOTE_USER,
|
|
|
|
FORMAT_TIMESTAMP,
|
|
|
|
FORMAT_REQUEST_LINE,
|
|
|
|
FORMAT_STATUS,
|
|
|
|
FORMAT_BYTES_OUT_NO_HEADER,
|
|
|
|
FORMAT_HEADER,
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
FORMAT_REMOTE_ADDR,
|
|
|
|
FORMAT_LOCAL_ADDR,
|
|
|
|
FORMAT_COOKIE,
|
2016-07-13 00:41:53 +00:00
|
|
|
FORMAT_TIME_USED_US,
|
2005-02-20 14:27:00 +00:00
|
|
|
FORMAT_ENV,
|
|
|
|
FORMAT_FILENAME,
|
|
|
|
FORMAT_REQUEST_PROTOCOL,
|
|
|
|
FORMAT_REQUEST_METHOD,
|
|
|
|
FORMAT_SERVER_PORT,
|
|
|
|
FORMAT_QUERY_STRING,
|
|
|
|
FORMAT_TIME_USED,
|
|
|
|
FORMAT_URL,
|
|
|
|
FORMAT_SERVER_NAME,
|
2005-09-02 16:09:42 +00:00
|
|
|
FORMAT_HTTP_HOST,
|
2005-02-20 14:27:00 +00:00
|
|
|
FORMAT_CONNECTION_STATUS,
|
|
|
|
FORMAT_BYTES_IN,
|
|
|
|
FORMAT_BYTES_OUT,
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2016-07-13 00:41:53 +00:00
|
|
|
FORMAT_KEEPALIVE_COUNT,
|
2016-10-19 10:01:10 +00:00
|
|
|
FORMAT_RESPONSE_HEADER,
|
|
|
|
FORMAT_NOTE
|
2005-02-20 14:27:00 +00:00
|
|
|
} type;
|
|
|
|
} format_mapping;
|
|
|
|
|
|
|
|
/**
|
2006-10-04 13:26:23 +00:00
|
|
|
*
|
|
|
|
*
|
2005-02-20 14:27:00 +00:00
|
|
|
* "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
|
2006-10-04 13:26:23 +00:00
|
|
|
*
|
2005-02-20 14:27:00 +00:00
|
|
|
*/
|
|
|
|
|
2012-08-31 14:11:39 +00:00
|
|
|
static const format_mapping fmap[] =
|
2006-10-04 13:26:23 +00:00
|
|
|
{
|
2005-02-20 14:27:00 +00:00
|
|
|
{ '%', FORMAT_PERCENT },
|
|
|
|
{ 'h', FORMAT_REMOTE_HOST },
|
|
|
|
{ 'l', FORMAT_REMOTE_IDENT },
|
|
|
|
{ 'u', FORMAT_REMOTE_USER },
|
|
|
|
{ 't', FORMAT_TIMESTAMP },
|
|
|
|
{ 'r', FORMAT_REQUEST_LINE },
|
|
|
|
{ 's', FORMAT_STATUS },
|
|
|
|
{ 'b', FORMAT_BYTES_OUT_NO_HEADER },
|
|
|
|
{ 'i', FORMAT_HEADER },
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
{ 'a', FORMAT_REMOTE_ADDR },
|
|
|
|
{ 'A', FORMAT_LOCAL_ADDR },
|
|
|
|
{ 'B', FORMAT_BYTES_OUT_NO_HEADER },
|
|
|
|
{ 'C', FORMAT_COOKIE },
|
2016-07-13 00:41:53 +00:00
|
|
|
{ 'D', FORMAT_TIME_USED_US },
|
2005-02-20 14:27:00 +00:00
|
|
|
{ 'e', FORMAT_ENV },
|
|
|
|
{ 'f', FORMAT_FILENAME },
|
|
|
|
{ 'H', FORMAT_REQUEST_PROTOCOL },
|
2016-07-13 00:41:53 +00:00
|
|
|
{ 'k', FORMAT_KEEPALIVE_COUNT },
|
2005-02-20 14:27:00 +00:00
|
|
|
{ 'm', FORMAT_REQUEST_METHOD },
|
2016-10-19 10:01:10 +00:00
|
|
|
{ 'n', FORMAT_NOTE },
|
2005-02-20 14:27:00 +00:00
|
|
|
{ 'p', FORMAT_SERVER_PORT },
|
|
|
|
{ 'P', FORMAT_UNSUPPORTED }, /* we are only one process */
|
|
|
|
{ 'q', FORMAT_QUERY_STRING },
|
|
|
|
{ 'T', FORMAT_TIME_USED },
|
|
|
|
{ 'U', FORMAT_URL }, /* w/o querystring */
|
|
|
|
{ 'v', FORMAT_SERVER_NAME },
|
2005-09-02 16:09:42 +00:00
|
|
|
{ 'V', FORMAT_HTTP_HOST },
|
2005-02-20 14:27:00 +00:00
|
|
|
{ 'X', FORMAT_CONNECTION_STATUS },
|
|
|
|
{ 'I', FORMAT_BYTES_IN },
|
|
|
|
{ 'O', FORMAT_BYTES_OUT },
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
{ 'o', FORMAT_RESPONSE_HEADER },
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
{ '\0', FORMAT_UNSET }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-07-13 00:41:53 +00:00
|
|
|
enum e_optflags_time {
|
|
|
|
/* format string is passed to strftime unless other format optflags set
|
|
|
|
* (besides FORMAT_FLAG_TIME_BEGIN or FORMAT_FLAG_TIME_END) */
|
|
|
|
FORMAT_FLAG_TIME_END = 0x00,/* use request end time (default) */
|
|
|
|
FORMAT_FLAG_TIME_BEGIN = 0x01,/* use request start time */
|
|
|
|
FORMAT_FLAG_TIME_SEC = 0x02,/* request time as num sec since epoch */
|
|
|
|
FORMAT_FLAG_TIME_MSEC = 0x04,/* request time as num msec since epoch */
|
|
|
|
FORMAT_FLAG_TIME_USEC = 0x08,/* request time as num usec since epoch */
|
|
|
|
FORMAT_FLAG_TIME_NSEC = 0x10,/* request time as num nsec since epoch */
|
|
|
|
FORMAT_FLAG_TIME_MSEC_FRAC = 0x20,/* request time msec fraction */
|
|
|
|
FORMAT_FLAG_TIME_USEC_FRAC = 0x40,/* request time usec fraction */
|
|
|
|
FORMAT_FLAG_TIME_NSEC_FRAC = 0x80 /* request time nsec fraction */
|
|
|
|
};
|
|
|
|
|
2017-11-18 03:09:51 +00:00
|
|
|
enum e_optflags_port {
|
|
|
|
FORMAT_FLAG_PORT_LOCAL = 0x01,/* (default) */
|
|
|
|
FORMAT_FLAG_PORT_REMOTE = 0x02
|
|
|
|
};
|
|
|
|
|
2016-07-13 00:41:53 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
typedef struct {
|
2019-11-01 04:26:08 +00:00
|
|
|
enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type;
|
|
|
|
int field;
|
|
|
|
int opt;
|
|
|
|
buffer string;
|
2005-02-20 14:27:00 +00:00
|
|
|
} format_field;
|
|
|
|
|
|
|
|
typedef struct {
|
2021-07-12 18:46:49 +00:00
|
|
|
unix_time64_t last_generated_accesslog_ts;
|
2019-11-01 04:26:08 +00:00
|
|
|
buffer ts_accesslog_str;
|
|
|
|
#if defined(__STDC_VERSION__) && __STDC_VERSION__-0 >= 199901L /* C99 */
|
|
|
|
format_field ptr[]; /* C99 VLA */
|
|
|
|
#else
|
|
|
|
format_field ptr[1];
|
|
|
|
#endif
|
2005-02-20 14:27:00 +00:00
|
|
|
} format_fields;
|
|
|
|
|
|
|
|
typedef struct {
|
2021-09-12 23:13:44 +00:00
|
|
|
fdlog_st *fdlog;
|
2019-11-01 04:26:08 +00:00
|
|
|
char use_syslog; /* syslog has global buffer */
|
2013-08-30 14:13:43 +00:00
|
|
|
unsigned short syslog_level;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
format_fields *parsed_format;
|
|
|
|
} plugin_config;
|
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
typedef struct {
|
|
|
|
PLUGIN_DATA;
|
|
|
|
plugin_config defaults;
|
|
|
|
plugin_config conf;
|
2013-07-31 20:23:18 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
format_fields *default_format;/* allocated if default format */
|
2005-02-20 14:27:00 +00:00
|
|
|
} plugin_data;
|
|
|
|
|
|
|
|
INIT_FUNC(mod_accesslog_init) {
|
2019-11-01 04:26:08 +00:00
|
|
|
return calloc(1, sizeof(plugin_data));
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
|
2020-01-08 03:07:55 +00:00
|
|
|
static void accesslog_append_escaped_str(buffer * const dest, const char * const str, const size_t len) {
|
|
|
|
const char *ptr, *start, *end;
|
2010-07-11 17:18:54 +00:00
|
|
|
|
2009-10-16 16:43:28 +00:00
|
|
|
/* replaces non-printable chars with \xHH where HH is the hex representation of the byte */
|
|
|
|
/* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
|
2019-10-17 04:01:32 +00:00
|
|
|
if (0 == len) return;
|
|
|
|
buffer_string_prepare_append(dest, len);
|
2009-10-16 16:43:28 +00:00
|
|
|
|
2019-10-17 04:01:32 +00:00
|
|
|
for (ptr = start = str, end = str+len; ptr < end; ++ptr) {
|
2020-01-08 03:07:55 +00:00
|
|
|
unsigned char const c = *(const unsigned char *)ptr;
|
2012-04-19 13:02:08 +00:00
|
|
|
if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
|
2010-08-05 19:53:49 +00:00
|
|
|
/* nothing to change, add later as one block */
|
|
|
|
} else {
|
|
|
|
/* copy previous part */
|
|
|
|
if (start < ptr) {
|
|
|
|
buffer_append_string_len(dest, start, ptr - start);
|
|
|
|
}
|
|
|
|
start = ptr + 1;
|
|
|
|
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
const char *h2;
|
2012-04-19 13:02:08 +00:00
|
|
|
switch (c) {
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
case '"': h2 = "\\\""; break;
|
|
|
|
case '\\': h2 = "\\\\"; break;
|
|
|
|
case '\b': h2 = "\\b"; break;
|
|
|
|
case '\n': h2 = "\\n"; break;
|
|
|
|
case '\r': h2 = "\\r"; break;
|
|
|
|
case '\t': h2 = "\\t"; break;
|
|
|
|
case '\v': h2 = "\\v"; break;
|
2010-08-05 19:53:49 +00:00
|
|
|
default: {
|
|
|
|
/* non printable char => \xHH */
|
|
|
|
char hh[5] = {'\\','x',0,0,0};
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
char h = c >> 4;
|
2010-08-05 19:53:49 +00:00
|
|
|
hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0');
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
h = c & 0xFF;
|
2010-08-05 19:53:49 +00:00
|
|
|
hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0');
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
buffer_append_string_len(dest, hh, 4);
|
|
|
|
continue;
|
2010-08-05 19:53:49 +00:00
|
|
|
}
|
2009-10-16 16:43:28 +00:00
|
|
|
}
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
buffer_append_string_len(dest, h2, 2);
|
2009-10-16 16:43:28 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-05 19:53:49 +00:00
|
|
|
|
|
|
|
if (start < end) {
|
|
|
|
buffer_append_string_len(dest, start, end - start);
|
|
|
|
}
|
2009-10-16 16:43:28 +00:00
|
|
|
}
|
|
|
|
|
2019-10-17 04:01:32 +00:00
|
|
|
static void accesslog_append_escaped(buffer *dest, const buffer *str) {
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
accesslog_append_escaped_str(dest, BUF_PTR_LEN(str));
|
2019-10-17 04:01:32 +00:00
|
|
|
}
|
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
__attribute_cold__
|
2019-12-05 04:01:41 +00:00
|
|
|
static format_fields * accesslog_parse_format_err(log_error_st *errh, const char *file, unsigned int line, format_field *f, const char *msg) {
|
|
|
|
log_error(errh, file, line, "%s", msg);
|
2019-11-01 04:26:08 +00:00
|
|
|
for (; f->type != FIELD_UNSET; ++f) free(f->string.ptr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-05 04:01:41 +00:00
|
|
|
static format_fields * accesslog_parse_format(const char * const format, const size_t flen, log_error_st * const errh) {
|
2019-11-01 04:26:08 +00:00
|
|
|
/* common log format (the default) results in 18 elements,
|
|
|
|
* so 127 should be enough except for obscene custom usage */
|
2005-02-20 14:27:00 +00:00
|
|
|
size_t i, j, k = 0, start = 0;
|
2019-11-01 04:26:08 +00:00
|
|
|
uint32_t used = 0;
|
2019-12-08 05:42:23 +00:00
|
|
|
const uint32_t sz = 127;/* (sz+1 must match fptr[] num elts below) */
|
2019-11-01 04:26:08 +00:00
|
|
|
format_field *f;
|
2019-12-08 05:42:23 +00:00
|
|
|
format_field fptr[128]; /* (128 elements takes 4k on stack in 64-bit) */
|
2019-11-01 04:26:08 +00:00
|
|
|
memset(fptr, 0, sizeof(fptr));
|
|
|
|
if (0 != FIELD_UNSET) return NULL;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
if (0 == flen) return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < flen; ++i) {
|
|
|
|
if (format[i] != '%') continue;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-04-09 19:58:57 +00:00
|
|
|
if (i > 0 && start != i) {
|
|
|
|
/* copy the string before this % */
|
2019-11-01 04:26:08 +00:00
|
|
|
if (used == sz)
|
2019-12-05 04:01:41 +00:00
|
|
|
return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
|
2019-11-01 04:26:08 +00:00
|
|
|
"too many fields (>= 127) in accesslog.format");
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
f = fptr+used;
|
|
|
|
f->type = FIELD_STRING;
|
|
|
|
memset(&f->string, 0, sizeof(buffer));
|
|
|
|
buffer_copy_string_len(&f->string, format + start, i - start);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
++used;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* we need a new field */
|
2019-11-01 04:26:08 +00:00
|
|
|
if (used == sz)
|
2019-12-05 04:01:41 +00:00
|
|
|
return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
|
2019-11-01 04:26:08 +00:00
|
|
|
"too many fields (>= 127) in accesslog.format");
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* search for the terminating command */
|
2019-11-01 04:26:08 +00:00
|
|
|
switch (format[i+1]) {
|
2005-02-20 14:27:00 +00:00
|
|
|
case '>':
|
|
|
|
case '<':
|
2007-04-09 19:58:57 +00:00
|
|
|
/* after the } has to be a character */
|
2019-11-01 04:26:08 +00:00
|
|
|
if (format[i+2] == '\0') {
|
2019-12-05 04:01:41 +00:00
|
|
|
return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
|
2019-11-01 04:26:08 +00:00
|
|
|
"%< and %> have to be followed by a format-specifier");
|
2007-04-09 19:58:57 +00:00
|
|
|
}
|
|
|
|
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (j = 0; fmap[j].key != '\0'; j++) {
|
2019-11-01 04:26:08 +00:00
|
|
|
if (fmap[j].key != format[i+2]) continue;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* found key */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
f = fptr+used;
|
|
|
|
f->type = FIELD_FORMAT;
|
|
|
|
f->field = fmap[j].type;
|
|
|
|
f->opt = 0;
|
|
|
|
f->string.ptr = NULL;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
++used;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (fmap[j].key == '\0') {
|
2019-12-05 04:01:41 +00:00
|
|
|
return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
|
2019-11-01 04:26:08 +00:00
|
|
|
"%< and %> have to be followed by a valid format-specifier");
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
start = i + 3;
|
2007-04-09 19:58:57 +00:00
|
|
|
i = start - 1; /* skip the string */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
case '{':
|
|
|
|
/* go forward to } */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
for (k = i+2; k < flen; ++k) {
|
|
|
|
if (format[k] == '}') break;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
if (k == flen) {
|
2019-12-05 04:01:41 +00:00
|
|
|
return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
|
2019-11-01 04:26:08 +00:00
|
|
|
"%{ has to be terminated by a }");
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2007-04-09 19:58:57 +00:00
|
|
|
|
|
|
|
/* after the } has to be a character */
|
2019-11-01 04:26:08 +00:00
|
|
|
if (format[k+1] == '\0') {
|
2019-12-05 04:01:41 +00:00
|
|
|
return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
|
2019-11-01 04:26:08 +00:00
|
|
|
"%{...} has to be followed by a format-specifier");
|
2007-04-09 19:58:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (k == i + 2) {
|
2019-12-05 04:01:41 +00:00
|
|
|
return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
|
2019-11-01 04:26:08 +00:00
|
|
|
"%{...} has to contain a string");
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (j = 0; fmap[j].key != '\0'; j++) {
|
2019-11-01 04:26:08 +00:00
|
|
|
if (fmap[j].key != format[k+1]) continue;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* found key */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
f = fptr+used;
|
|
|
|
f->type = FIELD_FORMAT;
|
|
|
|
f->field = fmap[j].type;
|
|
|
|
f->opt = 0;
|
|
|
|
memset(&f->string, 0, sizeof(buffer));
|
|
|
|
buffer_copy_string_len(&f->string, format + i + 2, k - (i + 2));
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
++used;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (fmap[j].key == '\0') {
|
2019-12-05 04:01:41 +00:00
|
|
|
return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
|
2019-11-01 04:26:08 +00:00
|
|
|
"%{...} has to be followed by a valid format-specifier");
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
start = k + 2;
|
2007-04-09 19:58:57 +00:00
|
|
|
i = start - 1; /* skip the string */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
default:
|
2007-04-09 19:58:57 +00:00
|
|
|
/* after the % has to be a character */
|
2019-11-01 04:26:08 +00:00
|
|
|
if (format[i+1] == '\0') {
|
2019-12-05 04:01:41 +00:00
|
|
|
return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
|
2019-11-01 04:26:08 +00:00
|
|
|
"% has to be followed by a format-specifier");
|
2007-04-09 19:58:57 +00:00
|
|
|
}
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (j = 0; fmap[j].key != '\0'; j++) {
|
2019-11-01 04:26:08 +00:00
|
|
|
if (fmap[j].key != format[i+1]) continue;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* found key */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
f = fptr+used;
|
|
|
|
f->type = FIELD_FORMAT;
|
|
|
|
f->field = fmap[j].type;
|
|
|
|
f->string.ptr = NULL;
|
|
|
|
f->opt = 0;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
++used;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (fmap[j].key == '\0') {
|
2019-12-05 04:01:41 +00:00
|
|
|
return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
|
2019-11-01 04:26:08 +00:00
|
|
|
"% has to be followed by a valid format-specifier");
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
start = i + 2;
|
2007-04-09 19:58:57 +00:00
|
|
|
i = start - 1; /* skip the string */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (start < i) {
|
|
|
|
/* copy the string */
|
2019-11-01 04:26:08 +00:00
|
|
|
if (used == sz)
|
2019-12-05 04:01:41 +00:00
|
|
|
return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr,
|
2019-11-01 04:26:08 +00:00
|
|
|
"too many fields (>= 127) in accesslog.format");
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
f = fptr+used;
|
|
|
|
f->type = FIELD_STRING;
|
|
|
|
memset(&f->string, 0, sizeof(buffer));
|
|
|
|
buffer_copy_string_len(&f->string, format + start, i - start);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
++used;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
format_fields * const fields =
|
|
|
|
malloc(sizeof(format_fields) + ((used+1) * sizeof(format_field)));
|
|
|
|
force_assert(fields);
|
|
|
|
memset(fields, 0, sizeof(format_fields));
|
|
|
|
memcpy(fields->ptr, fptr, (used+1) * sizeof(format_field));
|
|
|
|
return fields;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
static void mod_accesslog_free_format_fields(format_fields * const ff) {
|
|
|
|
for (format_field *f = ff->ptr; f->type != FIELD_UNSET; ++f)
|
|
|
|
free(f->string.ptr);
|
|
|
|
free(ff->ts_accesslog_str.ptr);
|
|
|
|
free(ff);
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-19 08:39:40 +00:00
|
|
|
FREE_FUNC(mod_accesslog_free) {
|
|
|
|
plugin_data * const p = p_d;
|
2019-11-01 04:26:08 +00:00
|
|
|
if (NULL == p->cvlist) return;
|
|
|
|
/* (init i to 0 if global context; to 1 to skip empty global context) */
|
|
|
|
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
|
|
|
|
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
|
|
|
|
for (; -1 != cpv->k_id; ++cpv) {
|
|
|
|
if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue;
|
|
|
|
switch (cpv->k_id) {
|
|
|
|
case 0: /* accesslog.filename */
|
2021-09-12 23:13:44 +00:00
|
|
|
/*(handled by fdlog_closeall())*/
|
2019-11-01 04:26:08 +00:00
|
|
|
break;
|
|
|
|
case 1: /* accesslog.format */
|
|
|
|
mod_accesslog_free_format_fields(cpv->v.v);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != p->default_format) {
|
|
|
|
mod_accesslog_free_format_fields(p->default_format);
|
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
static void mod_accesslog_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
|
|
|
|
switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
|
|
|
|
case 0:{/* accesslog.filename */
|
|
|
|
if (cpv->vtype != T_CONFIG_LOCAL) break;
|
2021-09-12 23:13:44 +00:00
|
|
|
pconf->fdlog = cpv->v.v;
|
2019-11-01 04:26:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1:{/* accesslog.format */
|
|
|
|
if (cpv->vtype != T_CONFIG_LOCAL) break;
|
|
|
|
pconf->parsed_format = cpv->v.v;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2: /* accesslog.use-syslog */
|
|
|
|
pconf->use_syslog = (int)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 3: /* accesslog.syslog-level */
|
|
|
|
pconf->syslog_level = cpv->v.shrt;
|
|
|
|
break;
|
|
|
|
default:/* should not happen */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2009-10-12 10:13:01 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
static void mod_accesslog_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
|
|
|
|
do {
|
|
|
|
mod_accesslog_merge_config_cpv(pconf, cpv);
|
|
|
|
} while ((++cpv)->k_id != -1);
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2020-01-13 02:51:12 +00:00
|
|
|
static void mod_accesslog_patch_config(request_st * const r, plugin_data * const p) {
|
2019-11-01 04:26:08 +00:00
|
|
|
memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
|
|
|
|
for (int i = 1, used = p->nconfig; i < used; ++i) {
|
2020-01-13 02:51:12 +00:00
|
|
|
if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
|
2019-11-01 04:26:08 +00:00
|
|
|
mod_accesslog_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
|
|
|
|
}
|
|
|
|
}
|
2005-06-27 19:25:55 +00:00
|
|
|
|
2019-12-05 04:01:41 +00:00
|
|
|
static format_fields * mod_accesslog_process_format(const char * const format, const size_t flen, server * const srv);
|
2019-11-01 04:26:08 +00:00
|
|
|
|
|
|
|
SETDEFAULTS_FUNC(mod_accesslog_set_defaults) {
|
|
|
|
static const config_plugin_keys_t cpk[] = {
|
|
|
|
{ CONST_STR_LEN("accesslog.filename"),
|
|
|
|
T_CONFIG_STRING,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("accesslog.format"),
|
|
|
|
T_CONFIG_STRING,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("accesslog.use-syslog"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("accesslog.syslog-level"),
|
|
|
|
T_CONFIG_SHORT,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ NULL, 0,
|
|
|
|
T_CONFIG_UNSET,
|
|
|
|
T_CONFIG_SCOPE_UNSET }
|
|
|
|
};
|
|
|
|
|
|
|
|
plugin_data * const p = p_d;
|
|
|
|
if (!config_plugin_values_init(srv, p, cpk, "mod_accesslog"))
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
|
|
|
|
/* process and validate config directives
|
|
|
|
* (init i to 0 if global context; to 1 to skip empty global context) */
|
|
|
|
for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
|
|
|
|
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
|
|
|
|
int use_syslog = 0;
|
2021-09-12 23:13:44 +00:00
|
|
|
config_plugin_value_t *cpvfile = NULL;
|
2019-11-01 04:26:08 +00:00
|
|
|
for (; -1 != cpv->k_id; ++cpv) {
|
|
|
|
switch (cpv->k_id) {
|
|
|
|
case 0: /* accesslog.filename */
|
2021-09-12 23:13:44 +00:00
|
|
|
if (!buffer_is_blank(cpv->v.b))
|
|
|
|
cpvfile = cpv;
|
|
|
|
else {
|
|
|
|
cpv->v.v = NULL;
|
|
|
|
cpv->vtype = T_CONFIG_LOCAL;
|
|
|
|
}
|
2019-11-01 04:26:08 +00:00
|
|
|
break;
|
|
|
|
case 1: /* accesslog.format */
|
2020-03-14 06:00:00 +00:00
|
|
|
if (NULL != strchr(cpv->v.b->ptr, '\\')) {
|
|
|
|
/* process basic backslash-escapes in format string */
|
|
|
|
buffer *b;
|
|
|
|
*(const buffer **)&b = cpv->v.b;
|
|
|
|
char *t = b->ptr;
|
|
|
|
for (char *s = t; *s; ++s) {
|
|
|
|
if (s[0] != '\\') { *t++ = *s; continue; }
|
|
|
|
if (s[1] == '\0') continue; /*(ignore dangling '\\')*/
|
|
|
|
switch (*++s) {
|
|
|
|
case 'a': *t++ = '\a'; break; /* bell */
|
|
|
|
case 'b': *t++ = '\b'; break; /* backspace */
|
|
|
|
case 'f': *t++ = '\f'; break; /* form feed */
|
|
|
|
case 'n': *t++ = '\n'; break; /* newline */
|
|
|
|
case 'r': *t++ = '\r'; break; /* carriage return */
|
|
|
|
case 't': *t++ = '\t'; break; /* horizontal tab */
|
|
|
|
case 'v': *t++ = '\v'; break; /* vertical tab */
|
|
|
|
/*case '"':*/
|
|
|
|
/*case '\\':*/
|
|
|
|
default: *t++ = *s; break; /*(use literal char)*/
|
|
|
|
}
|
|
|
|
}
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
buffer_truncate(b, (size_t)(t - b->ptr));
|
2020-03-14 06:00:00 +00:00
|
|
|
}
|
2019-11-01 04:26:08 +00:00
|
|
|
cpv->v.v =
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
mod_accesslog_process_format(BUF_PTR_LEN(cpv->v.b), srv);
|
2019-11-01 04:26:08 +00:00
|
|
|
if (NULL == cpv->v.v) return HANDLER_ERROR;
|
|
|
|
cpv->vtype = T_CONFIG_LOCAL;
|
|
|
|
break;
|
|
|
|
case 2: /* accesslog.use-syslog */
|
|
|
|
use_syslog = (int)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 3: /* accesslog.syslog-level */
|
|
|
|
break;
|
|
|
|
default:/* should not happen */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (srv->srvconf.preflight_check) continue;
|
|
|
|
|
|
|
|
if (use_syslog) continue; /* ignore the next checks */
|
2021-09-12 23:13:44 +00:00
|
|
|
cpv = cpvfile; /* accesslog.filename handled after preflight_check */
|
|
|
|
if (NULL == cpv) continue;
|
|
|
|
const char * const fn = cpv->v.b->ptr;
|
|
|
|
cpv->v.v = fdlog_open(fn);
|
|
|
|
cpv->vtype = T_CONFIG_LOCAL;
|
|
|
|
if (NULL == cpv->v.v) {
|
2019-11-01 04:26:08 +00:00
|
|
|
log_perror(srv->errh, __FILE__, __LINE__,
|
2021-09-12 23:13:44 +00:00
|
|
|
"opening log '%s' failed", fn);
|
2019-11-01 04:26:08 +00:00
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p->defaults.syslog_level = LOG_INFO;
|
|
|
|
|
|
|
|
/* initialize p->defaults from global config context */
|
|
|
|
if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
|
|
|
|
const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
|
|
|
|
if (-1 != cpv->k_id)
|
|
|
|
mod_accesslog_merge_config(&p->defaults, cpv);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL == p->defaults.parsed_format) {
|
|
|
|
/* (set default format even if p->use_syslog since
|
|
|
|
* some other condition might enable logfile) */
|
|
|
|
static const char fmt[] =
|
|
|
|
"%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"";
|
|
|
|
p->defaults.parsed_format = p->default_format =
|
2019-12-05 04:01:41 +00:00
|
|
|
mod_accesslog_process_format(CONST_STR_LEN(fmt), srv);
|
2019-11-01 04:26:08 +00:00
|
|
|
if (NULL == p->default_format) return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
2005-06-27 19:25:55 +00:00
|
|
|
|
2019-12-05 04:01:41 +00:00
|
|
|
static format_fields * mod_accesslog_process_format(const char * const format, const size_t flen, server * const srv) {
|
2019-11-01 04:26:08 +00:00
|
|
|
format_fields * const parsed_format =
|
2019-12-05 04:01:41 +00:00
|
|
|
accesslog_parse_format(format, flen, srv->errh);
|
2019-11-01 04:26:08 +00:00
|
|
|
if (NULL == parsed_format) {
|
|
|
|
log_error(srv->errh, __FILE__, __LINE__,
|
|
|
|
"parsing accesslog-definition failed: %s", format);
|
|
|
|
return NULL;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2009-10-12 10:13:01 +00:00
|
|
|
|
2019-11-01 04:26:08 +00:00
|
|
|
uint32_t tcount = 0;
|
|
|
|
for (format_field *f = parsed_format->ptr; f->type != FIELD_UNSET; ++f) {
|
|
|
|
const buffer * const fstr = &f->string;
|
2016-07-13 00:41:53 +00:00
|
|
|
if (FIELD_FORMAT != f->type) continue;
|
|
|
|
if (FORMAT_TIMESTAMP == f->field) {
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
2021-06-09 02:57:36 +00:00
|
|
|
if (!buffer_is_blank(fstr)) {
|
2019-11-01 04:26:08 +00:00
|
|
|
const char *ptr = fstr->ptr;
|
2016-07-13 00:41:53 +00:00
|
|
|
if (0 == strncmp(ptr, "begin:", sizeof("begin:")-1)) {
|
|
|
|
f->opt |= FORMAT_FLAG_TIME_BEGIN;
|
|
|
|
ptr += sizeof("begin:")-1;
|
|
|
|
} else if (0 == strncmp(ptr, "end:", sizeof("end:")-1)) {
|
|
|
|
f->opt |= FORMAT_FLAG_TIME_END;
|
|
|
|
ptr += sizeof("end:")-1;
|
2009-10-12 10:13:01 +00:00
|
|
|
}
|
2016-07-13 00:41:53 +00:00
|
|
|
if (0 == strcmp(ptr, "sec")) f->opt |= FORMAT_FLAG_TIME_SEC;
|
|
|
|
else if (0 == strcmp(ptr, "msec")) f->opt |= FORMAT_FLAG_TIME_MSEC;
|
|
|
|
else if (0 == strcmp(ptr, "usec")) f->opt |= FORMAT_FLAG_TIME_USEC;
|
|
|
|
else if (0 == strcmp(ptr, "nsec")) f->opt |= FORMAT_FLAG_TIME_NSEC;
|
|
|
|
else if (0 == strcmp(ptr, "msec_frac")) f->opt |= FORMAT_FLAG_TIME_MSEC_FRAC;
|
|
|
|
else if (0 == strcmp(ptr, "usec_frac")) f->opt |= FORMAT_FLAG_TIME_USEC_FRAC;
|
|
|
|
else if (0 == strcmp(ptr, "nsec_frac")) f->opt |= FORMAT_FLAG_TIME_NSEC_FRAC;
|
|
|
|
else if (NULL == strchr(ptr, '%')) {
|
2019-11-01 04:26:08 +00:00
|
|
|
log_error(srv->errh, __FILE__, __LINE__,
|
|
|
|
"constant string for time format (misspelled token? or missing '%%'): %s", format);
|
|
|
|
mod_accesslog_free_format_fields(parsed_format);
|
|
|
|
return NULL;
|
2009-10-12 10:13:01 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-13 00:41:53 +00:00
|
|
|
|
|
|
|
/* make sure they didn't try to send the timestamp in twice
|
2019-11-01 04:26:08 +00:00
|
|
|
* (would invalidate pconf->parsed_format.ts_accesslog_str cache of timestamp str) */
|
2016-07-13 00:41:53 +00:00
|
|
|
if (!(f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END|FORMAT_FLAG_TIME_SEC)) && ++tcount > 1) {
|
|