2005-02-20 14:27:00 +00:00
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include "base.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "buffer.h"
|
|
|
|
|
|
|
|
#include "plugin.h"
|
|
|
|
|
|
|
|
#include "inet_ntop_cache.h"
|
|
|
|
|
|
|
|
#include "sys-socket.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,
|
|
|
|
FORMAT_TIME_USED_MS,
|
|
|
|
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
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
FORMAT_RESPONSE_HEADER
|
|
|
|
} 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
|
|
|
*/
|
|
|
|
|
2006-10-04 13:26:23 +00:00
|
|
|
const format_mapping fmap[] =
|
|
|
|
{
|
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 },
|
|
|
|
{ 'D', FORMAT_TIME_USED_MS },
|
|
|
|
{ 'e', FORMAT_ENV },
|
|
|
|
{ 'f', FORMAT_FILENAME },
|
|
|
|
{ 'H', FORMAT_REQUEST_PROTOCOL },
|
|
|
|
{ 'm', FORMAT_REQUEST_METHOD },
|
|
|
|
{ 'n', FORMAT_UNSUPPORTED }, /* we have no notes */
|
|
|
|
{ '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 }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer *string;
|
|
|
|
int field;
|
|
|
|
} format_field;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
format_field **ptr;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
size_t used;
|
|
|
|
size_t size;
|
|
|
|
} format_fields;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
buffer *access_logfile;
|
|
|
|
buffer *format;
|
|
|
|
unsigned short use_syslog;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
int log_access_fd;
|
|
|
|
time_t last_generated_accesslog_ts;
|
|
|
|
time_t *last_generated_accesslog_ts_ptr;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer *access_logbuffer;
|
|
|
|
buffer *ts_accesslog_str;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
format_fields *parsed_format;
|
|
|
|
} plugin_config;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
PLUGIN_DATA;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
plugin_config **config_storage;
|
2006-10-04 13:26:23 +00:00
|
|
|
plugin_config conf;
|
2005-02-20 14:27:00 +00:00
|
|
|
} plugin_data;
|
|
|
|
|
|
|
|
INIT_FUNC(mod_accesslog_init) {
|
|
|
|
plugin_data *p;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
p = calloc(1, sizeof(*p));
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
int accesslog_parse_format(server *srv, format_fields *fields, buffer *format) {
|
|
|
|
size_t i, j, k = 0, start = 0;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-04-09 19:58:57 +00:00
|
|
|
if (format->used == 0) return -1;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-04-09 19:58:57 +00:00
|
|
|
for (i = 0; i < format->used - 1; i++) {
|
2005-02-20 14:27:00 +00:00
|
|
|
switch(format->ptr[i]) {
|
|
|
|
case '%':
|
2007-04-09 19:58:57 +00:00
|
|
|
if (i > 0 && start != i) {
|
|
|
|
/* copy the string before this % */
|
2005-02-20 14:27:00 +00:00
|
|
|
if (fields->size == 0) {
|
|
|
|
fields->size = 16;
|
|
|
|
fields->used = 0;
|
|
|
|
fields->ptr = malloc(fields->size * sizeof(format_fields * ));
|
|
|
|
} else if (fields->used == fields->size) {
|
|
|
|
fields->size += 16;
|
|
|
|
fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_fields * ));
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
fields->ptr[fields->used] = malloc(sizeof(format_fields));
|
|
|
|
fields->ptr[fields->used]->type = FIELD_STRING;
|
|
|
|
fields->ptr[fields->used]->string = buffer_init();
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
fields->used++;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* we need a new field */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (fields->size == 0) {
|
|
|
|
fields->size = 16;
|
|
|
|
fields->used = 0;
|
|
|
|
fields->ptr = malloc(fields->size * sizeof(format_fields * ));
|
|
|
|
} else if (fields->used == fields->size) {
|
|
|
|
fields->size += 16;
|
|
|
|
fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_fields * ));
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* search for the terminating command */
|
|
|
|
switch (format->ptr[i+1]) {
|
|
|
|
case '>':
|
|
|
|
case '<':
|
2007-04-09 19:58:57 +00:00
|
|
|
/* after the } has to be a character */
|
|
|
|
if (format->ptr[i+2] == '\0') {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a format-specifier");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (j = 0; fmap[j].key != '\0'; j++) {
|
|
|
|
if (fmap[j].key != format->ptr[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
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
fields->ptr[fields->used] = malloc(sizeof(format_fields));
|
|
|
|
fields->ptr[fields->used]->type = FIELD_FORMAT;
|
|
|
|
fields->ptr[fields->used]->field = fmap[j].type;
|
|
|
|
fields->ptr[fields->used]->string = NULL;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
fields->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') {
|
2007-04-09 19:58:57 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a valid format-specifier");
|
2005-02-20 14:27:00 +00:00
|
|
|
return -1;
|
|
|
|
}
|
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
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (k = i+2; k < format->used - 1; k++) {
|
|
|
|
if (format->ptr[k] == '}') break;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (k == format->used - 1) {
|
2007-04-09 19:58:57 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "%{ has to be terminated by a }");
|
2005-02-20 14:27:00 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2007-04-09 19:58:57 +00:00
|
|
|
|
|
|
|
/* after the } has to be a character */
|
2005-02-20 14:27:00 +00:00
|
|
|
if (format->ptr[k+1] == '\0') {
|
2007-04-09 19:58:57 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a format-specifier");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (k == i + 2) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be contain a string");
|
2005-02-20 14:27:00 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (j = 0; fmap[j].key != '\0'; j++) {
|
|
|
|
if (fmap[j].key != format->ptr[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
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
fields->ptr[fields->used] = malloc(sizeof(format_fields));
|
|
|
|
fields->ptr[fields->used]->type = FIELD_FORMAT;
|
|
|
|
fields->ptr[fields->used]->field = fmap[j].type;
|
|
|
|
fields->ptr[fields->used]->string = buffer_init();
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + i + 2, k - (i + 2));
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
fields->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') {
|
2007-04-09 19:58:57 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a valid format-specifier");
|
2005-02-20 14:27:00 +00:00
|
|
|
return -1;
|
|
|
|
}
|
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 */
|
|
|
|
if (format->ptr[i+1] == '\0') {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a format-specifier");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (j = 0; fmap[j].key != '\0'; j++) {
|
|
|
|
if (fmap[j].key != format->ptr[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
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
fields->ptr[fields->used] = malloc(sizeof(format_fields));
|
|
|
|
fields->ptr[fields->used]->type = FIELD_FORMAT;
|
|
|
|
fields->ptr[fields->used]->field = fmap[j].type;
|
|
|
|
fields->ptr[fields->used]->string = NULL;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
fields->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') {
|
2007-04-09 19:58:57 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a valid format-specifier");
|
2005-02-20 14:27:00 +00:00
|
|
|
return -1;
|
|
|
|
}
|
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
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (start < i) {
|
|
|
|
/* copy the string */
|
|
|
|
if (fields->size == 0) {
|
|
|
|
fields->size = 16;
|
|
|
|
fields->used = 0;
|
|
|
|
fields->ptr = malloc(fields->size * sizeof(format_fields * ));
|
|
|
|
} else if (fields->used == fields->size) {
|
|
|
|
fields->size += 16;
|
|
|
|
fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_fields * ));
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
fields->ptr[fields->used] = malloc(sizeof(format_fields));
|
|
|
|
fields->ptr[fields->used]->type = FIELD_STRING;
|
|
|
|
fields->ptr[fields->used]->string = buffer_init();
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
fields->used++;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
FREE_FUNC(mod_accesslog_free) {
|
|
|
|
plugin_data *p = p_d;
|
|
|
|
size_t i;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (!p) return HANDLER_GO_ON;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (p->config_storage) {
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (i = 0; i < srv->config_context->used; i++) {
|
|
|
|
plugin_config *s = p->config_storage[i];
|
2005-06-15 09:27:04 +00:00
|
|
|
|
|
|
|
if (!s) continue;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (s->access_logbuffer->used) {
|
|
|
|
if (s->use_syslog) {
|
|
|
|
# ifdef HAVE_SYSLOG_H
|
2006-01-31 12:09:58 +00:00
|
|
|
if (s->access_logbuffer->used > 2) {
|
|
|
|
syslog(LOG_INFO, "%*s", s->access_logbuffer->used - 2, s->access_logbuffer->ptr);
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
# endif
|
|
|
|
} else if (s->log_access_fd != -1) {
|
|
|
|
write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1);
|
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (s->log_access_fd != -1) close(s->log_access_fd);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer_free(s->ts_accesslog_str);
|
|
|
|
buffer_free(s->access_logbuffer);
|
|
|
|
buffer_free(s->format);
|
|
|
|
buffer_free(s->access_logfile);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (s->parsed_format) {
|
|
|
|
size_t j;
|
|
|
|
for (j = 0; j < s->parsed_format->used; j++) {
|
|
|
|
if (s->parsed_format->ptr[j]->string) buffer_free(s->parsed_format->ptr[j]->string);
|
|
|
|
free(s->parsed_format->ptr[j]);
|
|
|
|
}
|
|
|
|
free(s->parsed_format->ptr);
|
|
|
|
free(s->parsed_format);
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
free(s);
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
free(p->config_storage);
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
free(p);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
|
|
|
|
|
|
|
SETDEFAULTS_FUNC(log_access_open) {
|
|
|
|
plugin_data *p = p_d;
|
|
|
|
size_t i = 0;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
config_values_t cv[] = {
|
2005-02-20 14:27:00 +00:00
|
|
|
{ "accesslog.filename", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
|
|
|
|
{ "accesslog.use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
|
|
|
|
{ "accesslog.format", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
|
|
|
|
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
|
|
|
|
};
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (!p) return HANDLER_ERROR;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-06-27 19:25:55 +00:00
|
|
|
p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (i = 0; i < srv->config_context->used; i++) {
|
|
|
|
plugin_config *s;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
s = calloc(1, sizeof(plugin_config));
|
|
|
|
s->access_logfile = buffer_init();
|
|
|
|
s->format = buffer_init();
|
|
|
|
s->access_logbuffer = buffer_init();
|
|
|
|
s->ts_accesslog_str = buffer_init();
|
|
|
|
s->log_access_fd = -1;
|
|
|
|
s->last_generated_accesslog_ts = 0;
|
|
|
|
s->last_generated_accesslog_ts_ptr = &(s->last_generated_accesslog_ts);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
cv[0].destination = s->access_logfile;
|
|
|
|
cv[1].destination = &(s->use_syslog);
|
|
|
|
cv[2].destination = s->format;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
p->config_storage[i] = s;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (i == 0 && buffer_is_empty(s->format)) {
|
|
|
|
/* set a default logfile string */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-09-02 16:09:42 +00:00
|
|
|
buffer_copy_string(s->format, "%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"");
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* parse */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (s->format->used) {
|
|
|
|
s->parsed_format = calloc(1, sizeof(*(s->parsed_format)));
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (-1 == accesslog_parse_format(srv, s->parsed_format, s->format)) {
|
2005-06-27 19:25:55 +00:00
|
|
|
|
2006-10-04 13:26:23 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sb",
|
2005-06-27 19:25:55 +00:00
|
|
|
"parsing accesslog-definition failed:", s->format);
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
#if 0
|
2006-10-04 13:26:23 +00:00
|
|
|
/* debugging */
|
2005-02-20 14:27:00 +00:00
|
|
|
for (j = 0; j < s->parsed_format->used; j++) {
|
|
|
|
switch (s->parsed_format->ptr[j]->type) {
|
|
|
|
case FIELD_FORMAT:
|
2006-10-04 13:26:23 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ssds",
|
2005-02-20 14:27:00 +00:00
|
|
|
"config:", "format", s->parsed_format->ptr[j]->field,
|
2006-10-04 13:26:23 +00:00
|
|
|
s->parsed_format->ptr[j]->string ?
|
2005-02-20 14:27:00 +00:00
|
|
|
s->parsed_format->ptr[j]->string->ptr : "" );
|
|
|
|
break;
|
|
|
|
case FIELD_STRING:
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ssbs", "config:", "string '", s->parsed_format->ptr[j]->string, "'");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (s->use_syslog) {
|
|
|
|
/* ignore the next checks */
|
|
|
|
continue;
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2008-04-29 20:59:18 +00:00
|
|
|
if (s->access_logfile->used < 2) continue;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (s->access_logfile->ptr[0] == '|') {
|
|
|
|
#ifdef HAVE_FORK
|
|
|
|
/* create write pipe and spawn process */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
int to_log_fds[2];
|
|
|
|
pid_t pid;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (pipe(to_log_fds)) {
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno));
|
2005-03-01 23:01:12 +00:00
|
|
|
return HANDLER_ERROR;
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* fork, execve */
|
|
|
|
switch (pid = fork()) {
|
2006-10-04 13:26:23 +00:00
|
|
|
case 0:
|
2005-02-20 14:27:00 +00:00
|
|
|
/* child */
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
close(STDIN_FILENO);
|
|
|
|
dup2(to_log_fds[0], STDIN_FILENO);
|
|
|
|
close(to_log_fds[0]);
|
|
|
|
/* not needed */
|
|
|
|
close(to_log_fds[1]);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2008-04-29 20:59:39 +00:00
|
|
|
openDevNull(STDERR_FILENO);
|
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* we don't need the client socket */
|
|
|
|
for (i = 3; i < 256; i++) {
|
|
|
|
close(i);
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
/* exec the log-process (skip the | )
|
|
|
|
*
|
2005-02-20 14:27:00 +00:00
|
|
|
*/
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2007-06-15 15:04:56 +00:00
|
|
|
execl("/bin/sh", "sh", "-c", s->access_logfile->ptr + 1, (char *)NULL);
|
2005-02-20 14:27:00 +00:00
|
|
|
|
2006-10-04 13:26:23 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sss",
|
|
|
|
"spawning log-process failed: ", strerror(errno),
|
2005-02-20 14:27:00 +00:00
|
|
|
s->access_logfile->ptr + 1);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
exit(-1);
|
|
|
|
break;
|
|
|
|
case -1:
|
|
|
|
/* error */
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed: ", strerror(errno));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
close(to_log_fds[0]);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
s->log_access_fd = to_log_fds[1];
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
return -1;
|
|
|
|
#endif
|
2006-10-04 13:26:23 +00:00
|
|
|
} else if (-1 == (s->log_access_fd =
|
2005-02-20 14:27:00 +00:00
|
|
|
open(s->access_logfile->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) {
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ssb",
|
|
|
|
"opening access-log failed:",
|
2005-02-20 14:27:00 +00:00
|
|
|
strerror(errno), s->access_logfile);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
fcntl(s->log_access_fd, F_SETFD, FD_CLOEXEC);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
|
|
|
|
|
|
|
SIGHUP_FUNC(log_access_cycle) {
|
|
|
|
plugin_data *p = p_d;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!p->config_storage) return HANDLER_GO_ON;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
for (i = 0; i < srv->config_context->used; i++) {
|
|
|
|
plugin_config *s = p->config_storage[i];
|
|
|
|
|
2005-03-06 11:20:35 +00:00
|
|
|
if (s->access_logbuffer->used) {
|
2005-02-20 14:27:00 +00:00
|
|
|
if (s->use_syslog) {
|
|
|
|
#ifdef HAVE_SYSLOG_H
|
2006-01-31 12:09:58 +00:00
|
|
|
if (s->access_logbuffer->used > 2) {
|
|
|
|
/* syslog appends a \n on its own */
|
|
|
|
syslog(LOG_INFO, "%*s", s->access_logbuffer->used - 2, s->access_logbuffer->ptr);
|
|
|
|
}
|
2005-02-20 14:27:00 +00:00
|
|
|
#endif
|
|
|
|
} else if (s->log_access_fd != -1) {
|
|
|
|
write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1);
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-03-06 11:20:35 +00:00
|
|
|
buffer_reset(s->access_logbuffer);
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (s->use_syslog == 0 &&
|
2008-04-29 20:59:18 +00:00
|
|
|
s->access_logfile->used > 1 &&
|
2005-02-20 14:27:00 +00:00
|
|
|
s->access_logfile->ptr[0] != '|') {
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
close(s->log_access_fd);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
|
|
|
if (-1 == (s->log_access_fd =
|
2005-02-20 14:27:00 +00:00
|
|
|
open(s->access_logfile->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) {
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
log_error_write(srv, __FILE__, __LINE__, "ss", "cycling access-log failed:", strerror(errno));
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define PATCH(x) \
|
|
|
|
p->conf.x = s->x;
|
2005-08-08 10:27:07 +00:00
|
|
|
static int mod_accesslog_patch_connection(server *srv, connection *con, plugin_data *p) {
|
2005-02-20 14:27:00 +00:00
|
|
|
size_t i, j;
|
2005-08-08 10:27:07 +00:00
|
|
|
plugin_config *s = p->config_storage[0];
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-08-08 10:27:07 +00:00
|
|
|
PATCH(access_logfile);
|
|
|
|
PATCH(format);
|
|
|
|
PATCH(log_access_fd);
|
|
|
|
PATCH(last_generated_accesslog_ts_ptr);
|
|
|
|
PATCH(access_logbuffer);
|
|
|
|
PATCH(ts_accesslog_str);
|
|
|
|
PATCH(parsed_format);
|
|
|
|
PATCH(use_syslog);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* skip the first, the global context */
|
|
|
|
for (i = 1; i < srv->config_context->used; i++) {
|
|
|
|
data_config *dc = (data_config *)srv->config_context->data[i];
|
2005-08-08 10:27:07 +00:00
|
|
|
s = p->config_storage[i];
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* condition didn't match */
|
|
|
|
if (!config_check_cond(srv, con, dc)) continue;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* merge config */
|
|
|
|
for (j = 0; j < dc->value->used; j++) {
|
|
|
|
data_unset *du = dc->value->data[j];
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.filename"))) {
|
|
|
|
PATCH(access_logfile);
|
|
|
|
PATCH(log_access_fd);
|
|
|
|
PATCH(last_generated_accesslog_ts_ptr);
|
|
|
|
PATCH(access_logbuffer);
|
|
|
|
PATCH(ts_accesslog_str);
|
|
|
|
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.format"))) {
|
|
|
|
PATCH(format);
|
|
|
|
PATCH(parsed_format);
|
|
|
|
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.use-syslog"))) {
|
|
|
|
PATCH(use_syslog);
|
2007-04-09 19:20:00 +00:00
|
|
|
PATCH(last_generated_accesslog_ts_ptr);
|
|
|
|
PATCH(access_logbuffer);
|
|
|
|
PATCH(ts_accesslog_str);
|
2005-02-20 14:27:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#undef PATCH
|
|
|
|
|
|
|
|
REQUESTDONE_FUNC(log_access_write) {
|
|
|
|
plugin_data *p = p_d;
|
2005-09-02 16:09:42 +00:00
|
|
|
buffer *b;
|
2005-08-08 10:27:07 +00:00
|
|
|
size_t j;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
int newts = 0;
|
|
|
|
data_string *ds;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-08-08 10:27:07 +00:00
|
|
|
mod_accesslog_patch_connection(srv, con, p);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2008-04-29 20:59:18 +00:00
|
|
|
/* No output device, nothing to do */
|
|
|
|
if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON;
|
|
|
|
|
2005-09-02 16:09:42 +00:00
|
|
|
b = p->conf.access_logbuffer;
|
|
|
|
if (b->used == 0) {
|
|
|
|
buffer_copy_string(b, "");
|
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; j < p->conf.parsed_format->used; j++) {
|
|
|
|
switch(p->conf.parsed_format->ptr[j]->type) {
|
|
|
|
case FIELD_STRING:
|
2005-09-02 16:09:42 +00:00
|
|
|
buffer_append_string_buffer(b, p->conf.parsed_format->ptr[j]->string);
|
2005-02-20 14:27:00 +00:00
|
|
|
break;
|
|
|
|
case FIELD_FORMAT:
|
|
|
|
switch(p->conf.parsed_format->ptr[j]->field) {
|
|
|
|
case FORMAT_TIMESTAMP:
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* cache the generated timestamp */
|
|
|
|
if (srv->cur_ts != *(p->conf.last_generated_accesslog_ts_ptr)) {
|
|
|
|
struct tm tm;
|
|
|
|
#if defined(HAVE_STRUCT_TM_GMTOFF)
|
|
|
|
long scd, hrs, min;
|
|
|
|
#endif
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer_prepare_copy(p->conf.ts_accesslog_str, 255);
|
|
|
|
#if defined(HAVE_STRUCT_TM_GMTOFF)
|
|
|
|
# ifdef HAVE_LOCALTIME_R
|
|
|
|
localtime_r(&(srv->cur_ts), &tm);
|
|
|
|
strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S ", &tm);
|
|
|
|
# else
|
2008-07-30 16:42:14 +00:00
|
|
|
strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S ", localtime(&(srv->cur_ts)));
|
2005-02-20 14:27:00 +00:00
|
|
|
# endif
|
|
|
|
p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
buffer_append_string(p->conf.ts_accesslog_str, tm.tm_gmtoff >= 0 ? "+" : "-");
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
scd = abs(tm.tm_gmtoff);
|
|
|
|
hrs = scd / 3600;
|
|
|
|
min = (scd % 3600) / 60;
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
/* hours */
|
|
|
|
if (hrs < 10) buffer_append_string(p->conf.ts_accesslog_str, "0");
|
|
|
|
buffer_append_long(p->conf.ts_accesslog_str, hrs);
|
2006-10-04 13:26:23 +00:00
|
|
|
|
2005-02-20 14:27:00 +00:00
|
|
|
if (min < 10) buffer_append_string(p->conf.ts_accesslog_str, "0");
|
|
|
|
buffer_append_long(p->conf.ts_accesslog_str, min);
|
|
|
|
BUFFER_APPEND_STRING_CONST(p->conf.ts_accesslog_str, "]");
|
|
|
|
#else
|
|
|
|
#ifdef HAVE_GMTIME_R
|
|
|
|
gmtime_r(&(srv->cur_ts), &tm);
|
|
|
|
strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S +0000]", &tm);
|
|
|
|
#else
|
|
|
|
strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S +0000]", gmtime(&(srv->cur_ts)));
|
|
|
|
#endif
|
|
|
|
p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1;
|
|
|
|
#endif
|
2006-10-04 13:26:23 +00:00
|
|