Annotation of embedaddon/lighttpd/src/mod_accesslog.c, revision 1.1
1.1 ! misho 1: #include "base.h"
! 2: #include "log.h"
! 3: #include "buffer.h"
! 4:
! 5: #include "plugin.h"
! 6:
! 7: #include "inet_ntop_cache.h"
! 8:
! 9: #include "sys-socket.h"
! 10:
! 11: #include <sys/types.h>
! 12: #include <sys/stat.h>
! 13:
! 14: #include <ctype.h>
! 15: #include <stdlib.h>
! 16: #include <string.h>
! 17: #include <fcntl.h>
! 18: #include <unistd.h>
! 19: #include <errno.h>
! 20: #include <time.h>
! 21:
! 22: #include <stdio.h>
! 23:
! 24: #ifdef HAVE_SYSLOG_H
! 25: # include <syslog.h>
! 26: #endif
! 27:
! 28: typedef struct {
! 29: char key;
! 30: enum {
! 31: FORMAT_UNSET,
! 32: FORMAT_UNSUPPORTED,
! 33: FORMAT_PERCENT,
! 34: FORMAT_REMOTE_HOST,
! 35: FORMAT_REMOTE_IDENT,
! 36: FORMAT_REMOTE_USER,
! 37: FORMAT_TIMESTAMP,
! 38: FORMAT_REQUEST_LINE,
! 39: FORMAT_STATUS,
! 40: FORMAT_BYTES_OUT_NO_HEADER,
! 41: FORMAT_HEADER,
! 42:
! 43: FORMAT_REMOTE_ADDR,
! 44: FORMAT_LOCAL_ADDR,
! 45: FORMAT_COOKIE,
! 46: FORMAT_TIME_USED_MS,
! 47: FORMAT_ENV,
! 48: FORMAT_FILENAME,
! 49: FORMAT_REQUEST_PROTOCOL,
! 50: FORMAT_REQUEST_METHOD,
! 51: FORMAT_SERVER_PORT,
! 52: FORMAT_QUERY_STRING,
! 53: FORMAT_TIME_USED,
! 54: FORMAT_URL,
! 55: FORMAT_SERVER_NAME,
! 56: FORMAT_HTTP_HOST,
! 57: FORMAT_CONNECTION_STATUS,
! 58: FORMAT_BYTES_IN,
! 59: FORMAT_BYTES_OUT,
! 60:
! 61: FORMAT_RESPONSE_HEADER
! 62: } type;
! 63: } format_mapping;
! 64:
! 65: /**
! 66: *
! 67: *
! 68: * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
! 69: *
! 70: */
! 71:
! 72: static const format_mapping fmap[] =
! 73: {
! 74: { '%', FORMAT_PERCENT },
! 75: { 'h', FORMAT_REMOTE_HOST },
! 76: { 'l', FORMAT_REMOTE_IDENT },
! 77: { 'u', FORMAT_REMOTE_USER },
! 78: { 't', FORMAT_TIMESTAMP },
! 79: { 'r', FORMAT_REQUEST_LINE },
! 80: { 's', FORMAT_STATUS },
! 81: { 'b', FORMAT_BYTES_OUT_NO_HEADER },
! 82: { 'i', FORMAT_HEADER },
! 83:
! 84: { 'a', FORMAT_REMOTE_ADDR },
! 85: { 'A', FORMAT_LOCAL_ADDR },
! 86: { 'B', FORMAT_BYTES_OUT_NO_HEADER },
! 87: { 'C', FORMAT_COOKIE },
! 88: { 'D', FORMAT_TIME_USED_MS },
! 89: { 'e', FORMAT_ENV },
! 90: { 'f', FORMAT_FILENAME },
! 91: { 'H', FORMAT_REQUEST_PROTOCOL },
! 92: { 'm', FORMAT_REQUEST_METHOD },
! 93: { 'n', FORMAT_UNSUPPORTED }, /* we have no notes */
! 94: { 'p', FORMAT_SERVER_PORT },
! 95: { 'P', FORMAT_UNSUPPORTED }, /* we are only one process */
! 96: { 'q', FORMAT_QUERY_STRING },
! 97: { 'T', FORMAT_TIME_USED },
! 98: { 'U', FORMAT_URL }, /* w/o querystring */
! 99: { 'v', FORMAT_SERVER_NAME },
! 100: { 'V', FORMAT_HTTP_HOST },
! 101: { 'X', FORMAT_CONNECTION_STATUS },
! 102: { 'I', FORMAT_BYTES_IN },
! 103: { 'O', FORMAT_BYTES_OUT },
! 104:
! 105: { 'o', FORMAT_RESPONSE_HEADER },
! 106:
! 107: { '\0', FORMAT_UNSET }
! 108: };
! 109:
! 110:
! 111: typedef struct {
! 112: enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type;
! 113:
! 114: buffer *string;
! 115: int field;
! 116: } format_field;
! 117:
! 118: typedef struct {
! 119: format_field **ptr;
! 120:
! 121: size_t used;
! 122: size_t size;
! 123: } format_fields;
! 124:
! 125: typedef struct {
! 126: buffer *access_logfile;
! 127: int log_access_fd;
! 128: buffer *access_logbuffer; /* each logfile has a separate buffer */
! 129:
! 130: unsigned short use_syslog; /* syslog has global buffer */
! 131: unsigned short syslog_level;
! 132:
! 133: buffer *format;
! 134:
! 135: time_t last_generated_accesslog_ts;
! 136: time_t *last_generated_accesslog_ts_ptr;
! 137:
! 138: buffer *ts_accesslog_str;
! 139: buffer *ts_accesslog_fmt_str;
! 140: unsigned short append_tz_offset;
! 141:
! 142: format_fields *parsed_format;
! 143: } plugin_config;
! 144:
! 145: typedef struct {
! 146: PLUGIN_DATA;
! 147:
! 148: plugin_config **config_storage;
! 149: plugin_config conf;
! 150:
! 151: buffer *syslog_logbuffer; /* syslog has global buffer. no caching, always written directly */
! 152: } plugin_data;
! 153:
! 154: INIT_FUNC(mod_accesslog_init) {
! 155: plugin_data *p;
! 156:
! 157: p = calloc(1, sizeof(*p));
! 158: p->syslog_logbuffer = buffer_init();
! 159:
! 160: return p;
! 161: }
! 162:
! 163: static void accesslog_append_escaped(buffer *dest, buffer *str) {
! 164: char *ptr, *start, *end;
! 165:
! 166: /* replaces non-printable chars with \xHH where HH is the hex representation of the byte */
! 167: /* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
! 168: if (str->used == 0) return;
! 169: buffer_prepare_append(dest, str->used - 1);
! 170:
! 171: for (ptr = start = str->ptr, end = str->ptr + str->used - 1; ptr < end; ptr++) {
! 172: char const c = *ptr;
! 173: if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
! 174: /* nothing to change, add later as one block */
! 175: } else {
! 176: /* copy previous part */
! 177: if (start < ptr) {
! 178: buffer_append_string_len(dest, start, ptr - start);
! 179: }
! 180: start = ptr + 1;
! 181:
! 182: switch (c) {
! 183: case '"':
! 184: BUFFER_APPEND_STRING_CONST(dest, "\\\"");
! 185: break;
! 186: case '\\':
! 187: BUFFER_APPEND_STRING_CONST(dest, "\\\\");
! 188: break;
! 189: case '\b':
! 190: BUFFER_APPEND_STRING_CONST(dest, "\\b");
! 191: break;
! 192: case '\n':
! 193: BUFFER_APPEND_STRING_CONST(dest, "\\n");
! 194: break;
! 195: case '\r':
! 196: BUFFER_APPEND_STRING_CONST(dest, "\\r");
! 197: break;
! 198: case '\t':
! 199: BUFFER_APPEND_STRING_CONST(dest, "\\t");
! 200: break;
! 201: case '\v':
! 202: BUFFER_APPEND_STRING_CONST(dest, "\\v");
! 203: break;
! 204: default: {
! 205: /* non printable char => \xHH */
! 206: char hh[5] = {'\\','x',0,0,0};
! 207: char h = c / 16;
! 208: hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0');
! 209: h = c % 16;
! 210: hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0');
! 211: buffer_append_string_len(dest, &hh[0], 4);
! 212: }
! 213: break;
! 214: }
! 215: }
! 216: }
! 217:
! 218: if (start < end) {
! 219: buffer_append_string_len(dest, start, end - start);
! 220: }
! 221: }
! 222:
! 223: static int accesslog_parse_format(server *srv, format_fields *fields, buffer *format) {
! 224: size_t i, j, k = 0, start = 0;
! 225:
! 226: if (format->used == 0) return -1;
! 227:
! 228: for (i = 0; i < format->used - 1; i++) {
! 229: switch(format->ptr[i]) {
! 230: case '%':
! 231: if (i > 0 && start != i) {
! 232: /* copy the string before this % */
! 233: if (fields->size == 0) {
! 234: fields->size = 16;
! 235: fields->used = 0;
! 236: fields->ptr = malloc(fields->size * sizeof(format_field * ));
! 237: } else if (fields->used == fields->size) {
! 238: fields->size += 16;
! 239: fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
! 240: }
! 241:
! 242: fields->ptr[fields->used] = malloc(sizeof(format_field));
! 243: fields->ptr[fields->used]->type = FIELD_STRING;
! 244: fields->ptr[fields->used]->string = buffer_init();
! 245:
! 246: buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
! 247:
! 248: fields->used++;
! 249: }
! 250:
! 251: /* we need a new field */
! 252:
! 253: if (fields->size == 0) {
! 254: fields->size = 16;
! 255: fields->used = 0;
! 256: fields->ptr = malloc(fields->size * sizeof(format_field * ));
! 257: } else if (fields->used == fields->size) {
! 258: fields->size += 16;
! 259: fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
! 260: }
! 261:
! 262: /* search for the terminating command */
! 263: switch (format->ptr[i+1]) {
! 264: case '>':
! 265: case '<':
! 266: /* after the } has to be a character */
! 267: if (format->ptr[i+2] == '\0') {
! 268: log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a format-specifier");
! 269: return -1;
! 270: }
! 271:
! 272:
! 273: for (j = 0; fmap[j].key != '\0'; j++) {
! 274: if (fmap[j].key != format->ptr[i+2]) continue;
! 275:
! 276: /* found key */
! 277:
! 278: fields->ptr[fields->used] = malloc(sizeof(format_field));
! 279: fields->ptr[fields->used]->type = FIELD_FORMAT;
! 280: fields->ptr[fields->used]->field = fmap[j].type;
! 281: fields->ptr[fields->used]->string = NULL;
! 282:
! 283: fields->used++;
! 284:
! 285: break;
! 286: }
! 287:
! 288: if (fmap[j].key == '\0') {
! 289: log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a valid format-specifier");
! 290: return -1;
! 291: }
! 292:
! 293: start = i + 3;
! 294: i = start - 1; /* skip the string */
! 295:
! 296: break;
! 297: case '{':
! 298: /* go forward to } */
! 299:
! 300: for (k = i+2; k < format->used - 1; k++) {
! 301: if (format->ptr[k] == '}') break;
! 302: }
! 303:
! 304: if (k == format->used - 1) {
! 305: log_error_write(srv, __FILE__, __LINE__, "s", "%{ has to be terminated by a }");
! 306: return -1;
! 307: }
! 308:
! 309: /* after the } has to be a character */
! 310: if (format->ptr[k+1] == '\0') {
! 311: log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a format-specifier");
! 312: return -1;
! 313: }
! 314:
! 315: if (k == i + 2) {
! 316: log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be contain a string");
! 317: return -1;
! 318: }
! 319:
! 320: for (j = 0; fmap[j].key != '\0'; j++) {
! 321: if (fmap[j].key != format->ptr[k+1]) continue;
! 322:
! 323: /* found key */
! 324:
! 325: fields->ptr[fields->used] = malloc(sizeof(format_field));
! 326: fields->ptr[fields->used]->type = FIELD_FORMAT;
! 327: fields->ptr[fields->used]->field = fmap[j].type;
! 328: fields->ptr[fields->used]->string = buffer_init();
! 329:
! 330: buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + i + 2, k - (i + 2));
! 331:
! 332: fields->used++;
! 333:
! 334: break;
! 335: }
! 336:
! 337: if (fmap[j].key == '\0') {
! 338: log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a valid format-specifier");
! 339: return -1;
! 340: }
! 341:
! 342: start = k + 2;
! 343: i = start - 1; /* skip the string */
! 344:
! 345: break;
! 346: default:
! 347: /* after the % has to be a character */
! 348: if (format->ptr[i+1] == '\0') {
! 349: log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a format-specifier");
! 350: return -1;
! 351: }
! 352:
! 353: for (j = 0; fmap[j].key != '\0'; j++) {
! 354: if (fmap[j].key != format->ptr[i+1]) continue;
! 355:
! 356: /* found key */
! 357:
! 358: fields->ptr[fields->used] = malloc(sizeof(format_field));
! 359: fields->ptr[fields->used]->type = FIELD_FORMAT;
! 360: fields->ptr[fields->used]->field = fmap[j].type;
! 361: fields->ptr[fields->used]->string = NULL;
! 362:
! 363: fields->used++;
! 364:
! 365: break;
! 366: }
! 367:
! 368: if (fmap[j].key == '\0') {
! 369: log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a valid format-specifier");
! 370: return -1;
! 371: }
! 372:
! 373: start = i + 2;
! 374: i = start - 1; /* skip the string */
! 375:
! 376: break;
! 377: }
! 378:
! 379: break;
! 380: }
! 381: }
! 382:
! 383: if (start < i) {
! 384: /* copy the string */
! 385: if (fields->size == 0) {
! 386: fields->size = 16;
! 387: fields->used = 0;
! 388: fields->ptr = malloc(fields->size * sizeof(format_field * ));
! 389: } else if (fields->used == fields->size) {
! 390: fields->size += 16;
! 391: fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
! 392: }
! 393:
! 394: fields->ptr[fields->used] = malloc(sizeof(format_field));
! 395: fields->ptr[fields->used]->type = FIELD_STRING;
! 396: fields->ptr[fields->used]->string = buffer_init();
! 397:
! 398: buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
! 399:
! 400: fields->used++;
! 401: }
! 402:
! 403: return 0;
! 404: }
! 405:
! 406: FREE_FUNC(mod_accesslog_free) {
! 407: plugin_data *p = p_d;
! 408: size_t i;
! 409:
! 410: if (!p) return HANDLER_GO_ON;
! 411:
! 412: if (p->config_storage) {
! 413:
! 414: for (i = 0; i < srv->config_context->used; i++) {
! 415: plugin_config *s = p->config_storage[i];
! 416:
! 417: if (!s) continue;
! 418:
! 419: if (s->access_logbuffer->used) {
! 420: if (s->log_access_fd != -1) {
! 421: write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1);
! 422: }
! 423: }
! 424:
! 425: if (s->log_access_fd != -1) close(s->log_access_fd);
! 426:
! 427: buffer_free(s->ts_accesslog_str);
! 428: buffer_free(s->ts_accesslog_fmt_str);
! 429: buffer_free(s->access_logbuffer);
! 430: buffer_free(s->format);
! 431: buffer_free(s->access_logfile);
! 432:
! 433: if (s->parsed_format) {
! 434: size_t j;
! 435: for (j = 0; j < s->parsed_format->used; j++) {
! 436: if (s->parsed_format->ptr[j]->string) buffer_free(s->parsed_format->ptr[j]->string);
! 437: free(s->parsed_format->ptr[j]);
! 438: }
! 439: free(s->parsed_format->ptr);
! 440: free(s->parsed_format);
! 441: }
! 442:
! 443: free(s);
! 444: }
! 445:
! 446: free(p->config_storage);
! 447: }
! 448:
! 449: free(p);
! 450: if (p->syslog_logbuffer) buffer_free(p->syslog_logbuffer);
! 451:
! 452: return HANDLER_GO_ON;
! 453: }
! 454:
! 455: SETDEFAULTS_FUNC(log_access_open) {
! 456: plugin_data *p = p_d;
! 457: size_t i = 0;
! 458:
! 459: config_values_t cv[] = {
! 460: { "accesslog.filename", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
! 461: { "accesslog.use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
! 462: { "accesslog.format", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
! 463: { "accesslog.syslog-level", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },
! 464: { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
! 465: };
! 466:
! 467: if (!p) return HANDLER_ERROR;
! 468:
! 469: p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
! 470:
! 471: for (i = 0; i < srv->config_context->used; i++) {
! 472: plugin_config *s;
! 473:
! 474: s = calloc(1, sizeof(plugin_config));
! 475: s->access_logfile = buffer_init();
! 476: s->format = buffer_init();
! 477: s->access_logbuffer = buffer_init();
! 478: s->ts_accesslog_str = buffer_init();
! 479: s->ts_accesslog_fmt_str = buffer_init();
! 480: s->log_access_fd = -1;
! 481: s->last_generated_accesslog_ts = 0;
! 482: s->last_generated_accesslog_ts_ptr = &(s->last_generated_accesslog_ts);
! 483: s->syslog_level = LOG_INFO;
! 484:
! 485:
! 486: cv[0].destination = s->access_logfile;
! 487: cv[1].destination = &(s->use_syslog);
! 488: cv[2].destination = s->format;
! 489: cv[3].destination = &(s->syslog_level);
! 490:
! 491: p->config_storage[i] = s;
! 492:
! 493: if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
! 494: return HANDLER_ERROR;
! 495: }
! 496:
! 497: if (i == 0 && buffer_is_empty(s->format)) {
! 498: /* set a default logfile string */
! 499:
! 500: buffer_copy_string_len(s->format, CONST_STR_LEN("%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""));
! 501: }
! 502:
! 503: /* parse */
! 504:
! 505: if (s->format->used) {
! 506: size_t j, count;
! 507:
! 508: s->parsed_format = calloc(1, sizeof(*(s->parsed_format)));
! 509:
! 510: if (-1 == accesslog_parse_format(srv, s->parsed_format, s->format)) {
! 511:
! 512: log_error_write(srv, __FILE__, __LINE__, "sb",
! 513: "parsing accesslog-definition failed:", s->format);
! 514:
! 515: return HANDLER_ERROR;
! 516: }
! 517:
! 518: /* make sure they didn't try to send the timestamp in twice...
! 519: * also, save the format string in a different variable (this
! 520: * will save a few conditionals later)
! 521: */
! 522: count = 0;
! 523: for (j = 0; j < s->parsed_format->used; j++) {
! 524: if (FIELD_FORMAT == s->parsed_format->ptr[j]->type) {
! 525: if (FORMAT_TIMESTAMP == s->parsed_format->ptr[j]->field) {
! 526: if (!buffer_is_empty(s->parsed_format->ptr[j]->string)) {
! 527: buffer_copy_string(s->ts_accesslog_fmt_str, s->parsed_format->ptr[j]->string->ptr);
! 528: }
! 529:
! 530: if (++count > 1) {
! 531: log_error_write(srv, __FILE__, __LINE__, "sb",
! 532: "you may not use the timestamp twice in the same access log:", s->format);
! 533:
! 534: return HANDLER_ERROR;
! 535: }
! 536: }
! 537: }
! 538: }
! 539:
! 540: #if 0
! 541: /* debugging */
! 542: for (j = 0; j < s->parsed_format->used; j++) {
! 543: switch (s->parsed_format->ptr[j]->type) {
! 544: case FIELD_FORMAT:
! 545: log_error_write(srv, __FILE__, __LINE__, "ssds",
! 546: "config:", "format", s->parsed_format->ptr[j]->field,
! 547: s->parsed_format->ptr[j]->string ?
! 548: s->parsed_format->ptr[j]->string->ptr : "" );
! 549: break;
! 550: case FIELD_STRING:
! 551: log_error_write(srv, __FILE__, __LINE__, "ssbs", "config:", "string '", s->parsed_format->ptr[j]->string, "'");
! 552: break;
! 553: default:
! 554: break;
! 555: }
! 556: }
! 557: #endif
! 558: }
! 559:
! 560: s->append_tz_offset = 0;
! 561: if (buffer_is_empty(s->ts_accesslog_fmt_str)) {
! 562: #if defined(HAVE_STRUCT_TM_GMTOFF)
! 563: BUFFER_COPY_STRING_CONST(s->ts_accesslog_fmt_str, "[%d/%b/%Y:%H:%M:%S ");
! 564: s->append_tz_offset = 1;
! 565: #else
! 566: BUFFER_COPY_STRING_CONST(s->ts_accesslog_fmt_str, "[%d/%b/%Y:%H:%M:%S +0000]");
! 567: #endif
! 568: }
! 569:
! 570: if (s->use_syslog) {
! 571: /* ignore the next checks */
! 572: continue;
! 573: }
! 574:
! 575: if (s->access_logfile->used < 2) continue;
! 576:
! 577: if (-1 == (s->log_access_fd = open_logfile_or_pipe(srv, s->access_logfile->ptr)))
! 578: return HANDLER_ERROR;
! 579:
! 580: }
! 581:
! 582: return HANDLER_GO_ON;
! 583: }
! 584:
! 585: SIGHUP_FUNC(log_access_cycle) {
! 586: plugin_data *p = p_d;
! 587: size_t i;
! 588:
! 589: if (!p->config_storage) return HANDLER_GO_ON;
! 590:
! 591: for (i = 0; i < srv->config_context->used; i++) {
! 592: plugin_config *s = p->config_storage[i];
! 593:
! 594: if (s->access_logbuffer->used) {
! 595: if (s->log_access_fd != -1) {
! 596: write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1);
! 597: }
! 598:
! 599: buffer_reset(s->access_logbuffer);
! 600: }
! 601:
! 602: if (s->use_syslog == 0 &&
! 603: s->access_logfile->used > 1 &&
! 604: s->access_logfile->ptr[0] != '|') {
! 605:
! 606: close(s->log_access_fd);
! 607:
! 608: if (-1 == (s->log_access_fd =
! 609: open(s->access_logfile->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) {
! 610:
! 611: log_error_write(srv, __FILE__, __LINE__, "ss", "cycling access-log failed:", strerror(errno));
! 612:
! 613: return HANDLER_ERROR;
! 614: }
! 615: #ifdef FD_CLOEXEC
! 616: fcntl(s->log_access_fd, F_SETFD, FD_CLOEXEC);
! 617: #endif
! 618: }
! 619: }
! 620:
! 621: return HANDLER_GO_ON;
! 622: }
! 623:
! 624: #define PATCH(x) \
! 625: p->conf.x = s->x;
! 626: static int mod_accesslog_patch_connection(server *srv, connection *con, plugin_data *p) {
! 627: size_t i, j;
! 628: plugin_config *s = p->config_storage[0];
! 629:
! 630: PATCH(access_logfile);
! 631: PATCH(format);
! 632: PATCH(log_access_fd);
! 633: PATCH(last_generated_accesslog_ts_ptr);
! 634: PATCH(access_logbuffer);
! 635: PATCH(ts_accesslog_str);
! 636: PATCH(ts_accesslog_fmt_str);
! 637: PATCH(append_tz_offset);
! 638: PATCH(parsed_format);
! 639: PATCH(use_syslog);
! 640: PATCH(syslog_level);
! 641:
! 642: /* skip the first, the global context */
! 643: for (i = 1; i < srv->config_context->used; i++) {
! 644: data_config *dc = (data_config *)srv->config_context->data[i];
! 645: s = p->config_storage[i];
! 646:
! 647: /* condition didn't match */
! 648: if (!config_check_cond(srv, con, dc)) continue;
! 649:
! 650: /* merge config */
! 651: for (j = 0; j < dc->value->used; j++) {
! 652: data_unset *du = dc->value->data[j];
! 653:
! 654: if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.filename"))) {
! 655: PATCH(access_logfile);
! 656: PATCH(log_access_fd);
! 657: PATCH(access_logbuffer);
! 658: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.format"))) {
! 659: PATCH(format);
! 660: PATCH(parsed_format);
! 661: PATCH(last_generated_accesslog_ts_ptr);
! 662: PATCH(ts_accesslog_str);
! 663: PATCH(ts_accesslog_fmt_str);
! 664: PATCH(append_tz_offset);
! 665: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.use-syslog"))) {
! 666: PATCH(use_syslog);
! 667: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.syslog-level"))) {
! 668: PATCH(syslog_level);
! 669: }
! 670: }
! 671: }
! 672:
! 673: return 0;
! 674: }
! 675: #undef PATCH
! 676:
! 677: REQUESTDONE_FUNC(log_access_write) {
! 678: plugin_data *p = p_d;
! 679: buffer *b;
! 680: size_t j;
! 681:
! 682: int newts = 0;
! 683: data_string *ds;
! 684:
! 685: mod_accesslog_patch_connection(srv, con, p);
! 686:
! 687: /* No output device, nothing to do */
! 688: if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON;
! 689:
! 690: if (p->conf.use_syslog) {
! 691: b = p->syslog_logbuffer;
! 692: } else {
! 693: b = p->conf.access_logbuffer;
! 694: }
! 695:
! 696: if (b->used == 0) {
! 697: buffer_copy_string_len(b, CONST_STR_LEN(""));
! 698: }
! 699:
! 700: for (j = 0; j < p->conf.parsed_format->used; j++) {
! 701: switch(p->conf.parsed_format->ptr[j]->type) {
! 702: case FIELD_STRING:
! 703: buffer_append_string_buffer(b, p->conf.parsed_format->ptr[j]->string);
! 704: break;
! 705: case FIELD_FORMAT:
! 706: switch(p->conf.parsed_format->ptr[j]->field) {
! 707: case FORMAT_TIMESTAMP:
! 708:
! 709: /* cache the generated timestamp */
! 710: if (srv->cur_ts != *(p->conf.last_generated_accesslog_ts_ptr)) {
! 711: struct tm tm;
! 712: #if defined(HAVE_STRUCT_TM_GMTOFF)
! 713: long scd, hrs, min;
! 714: #endif
! 715:
! 716: buffer_prepare_copy(p->conf.ts_accesslog_str, 255);
! 717: #if defined(HAVE_STRUCT_TM_GMTOFF)
! 718: # ifdef HAVE_LOCALTIME_R
! 719: localtime_r(&(srv->cur_ts), &tm);
! 720: strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, &tm);
! 721: # else /* HAVE_LOCALTIME_R */
! 722: strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, localtime_r(&(srv->cur_ts)));
! 723: # endif /* HAVE_LOCALTIME_R */
! 724: p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1;
! 725:
! 726: if (p->conf.append_tz_offset) {
! 727: buffer_append_string_len(p->conf.ts_accesslog_str, tm.tm_gmtoff >= 0 ? "+" : "-", 1);
! 728:
! 729: scd = abs(tm.tm_gmtoff);
! 730: hrs = scd / 3600;
! 731: min = (scd % 3600) / 60;
! 732:
! 733: /* hours */
! 734: if (hrs < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
! 735: buffer_append_long(p->conf.ts_accesslog_str, hrs);
! 736:
! 737: if (min < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
! 738: buffer_append_long(p->conf.ts_accesslog_str, min);
! 739: buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("]"));
! 740: }
! 741: #else /* HAVE_STRUCT_TM_GMTOFF */
! 742: # ifdef HAVE_GMTIME_R
! 743: gmtime_r(&(srv->cur_ts), &tm);
! 744: strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, &tm);
! 745: # else /* HAVE_GMTIME_R */
! 746: strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, p->conf.ts_accesslog_fmt_str->ptr, gmtime(&(srv->cur_ts)));
! 747: # endif /* HAVE_GMTIME_R */
! 748: p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1;
! 749: #endif /* HAVE_STRUCT_TM_GMTOFF */
! 750:
! 751: *(p->conf.last_generated_accesslog_ts_ptr) = srv->cur_ts;
! 752: newts = 1;
! 753: }
! 754:
! 755: buffer_append_string_buffer(b, p->conf.ts_accesslog_str);
! 756:
! 757: break;
! 758: case FORMAT_REMOTE_HOST:
! 759:
! 760: /* handle inet_ntop cache */
! 761:
! 762: buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
! 763:
! 764: break;
! 765: case FORMAT_REMOTE_IDENT:
! 766: /* ident */
! 767: buffer_append_string_len(b, CONST_STR_LEN("-"));
! 768: break;
! 769: case FORMAT_REMOTE_USER:
! 770: if (NULL != (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER")) && ds->value->used > 1) {
! 771: accesslog_append_escaped(b, ds->value);
! 772: } else {
! 773: buffer_append_string_len(b, CONST_STR_LEN("-"));
! 774: }
! 775: break;
! 776: case FORMAT_REQUEST_LINE:
! 777: if (con->request.request_line->used) {
! 778: accesslog_append_escaped(b, con->request.request_line);
! 779: }
! 780: break;
! 781: case FORMAT_STATUS:
! 782: buffer_append_long(b, con->http_status);
! 783: break;
! 784:
! 785: case FORMAT_BYTES_OUT_NO_HEADER:
! 786: if (con->bytes_written > 0) {
! 787: buffer_append_off_t(b,
! 788: con->bytes_written - con->bytes_header <= 0 ? 0 : con->bytes_written - con->bytes_header);
! 789: } else {
! 790: buffer_append_string_len(b, CONST_STR_LEN("-"));
! 791: }
! 792: break;
! 793: case FORMAT_HEADER:
! 794: if (NULL != (ds = (data_string *)array_get_element(con->request.headers, p->conf.parsed_format->ptr[j]->string->ptr))) {
! 795: accesslog_append_escaped(b, ds->value);
! 796: } else {
! 797: buffer_append_string_len(b, CONST_STR_LEN("-"));
! 798: }
! 799: break;
! 800: case FORMAT_RESPONSE_HEADER:
! 801: if (NULL != (ds = (data_string *)array_get_element(con->response.headers, p->conf.parsed_format->ptr[j]->string->ptr))) {
! 802: accesslog_append_escaped(b, ds->value);
! 803: } else {
! 804: buffer_append_string_len(b, CONST_STR_LEN("-"));
! 805: }
! 806: break;
! 807: case FORMAT_ENV:
! 808: if (NULL != (ds = (data_string *)array_get_element(con->environment, p->conf.parsed_format->ptr[j]->string->ptr))) {
! 809: accesslog_append_escaped(b, ds->value);
! 810: } else {
! 811: buffer_append_string_len(b, CONST_STR_LEN("-"));
! 812: }
! 813: break;
! 814: case FORMAT_FILENAME:
! 815: if (con->physical.path->used > 1) {
! 816: buffer_append_string_buffer(b, con->physical.path);
! 817: } else {
! 818: buffer_append_string_len(b, CONST_STR_LEN("-"));
! 819: }
! 820: break;
! 821: case FORMAT_BYTES_OUT:
! 822: if (con->bytes_written > 0) {
! 823: buffer_append_off_t(b, con->bytes_written);
! 824: } else {
! 825: buffer_append_string_len(b, CONST_STR_LEN("-"));
! 826: }
! 827: break;
! 828: case FORMAT_BYTES_IN:
! 829: if (con->bytes_read > 0) {
! 830: buffer_append_off_t(b, con->bytes_read);
! 831: } else {
! 832: buffer_append_string_len(b, CONST_STR_LEN("-"));
! 833: }
! 834: break;
! 835: case FORMAT_TIME_USED:
! 836: buffer_append_long(b, srv->cur_ts - con->request_start);
! 837: break;
! 838: case FORMAT_SERVER_NAME:
! 839: if (con->server_name->used > 1) {
! 840: buffer_append_string_buffer(b, con->server_name);
! 841: } else {
! 842: buffer_append_string_len(b, CONST_STR_LEN("-"));
! 843: }
! 844: break;
! 845: case FORMAT_HTTP_HOST:
! 846: if (con->uri.authority->used > 1) {
! 847: accesslog_append_escaped(b, con->uri.authority);
! 848: } else {
! 849: buffer_append_string_len(b, CONST_STR_LEN("-"));
! 850: }
! 851: break;
! 852: case FORMAT_REQUEST_PROTOCOL:
! 853: buffer_append_string_len(b,
! 854: con->request.http_version == HTTP_VERSION_1_1 ? "HTTP/1.1" : "HTTP/1.0", 8);
! 855: break;
! 856: case FORMAT_REQUEST_METHOD:
! 857: buffer_append_string(b, get_http_method_name(con->request.http_method));
! 858: break;
! 859: case FORMAT_PERCENT:
! 860: buffer_append_string_len(b, CONST_STR_LEN("%"));
! 861: break;
! 862: case FORMAT_SERVER_PORT:
! 863: {
! 864: const char *colon;
! 865: buffer *srvtoken = ((server_socket*)(con->srv_socket))->srv_token;
! 866: if (srvtoken->ptr[0] == '[') {
! 867: colon = strstr(srvtoken->ptr, "]:");
! 868: } else {
! 869: colon = strchr(srvtoken->ptr, ':');
! 870: }
! 871: if (colon) {
! 872: buffer_append_string(b, colon+1);
! 873: } else {
! 874: buffer_append_long(b, srv->srvconf.port);
! 875: }
! 876: }
! 877: break;
! 878: case FORMAT_QUERY_STRING:
! 879: accesslog_append_escaped(b, con->uri.query);
! 880: break;
! 881: case FORMAT_URL:
! 882: accesslog_append_escaped(b, con->uri.path_raw);
! 883: break;
! 884: case FORMAT_CONNECTION_STATUS:
! 885: switch(con->keep_alive) {
! 886: case 0: buffer_append_string_len(b, CONST_STR_LEN("-")); break;
! 887: default: buffer_append_string_len(b, CONST_STR_LEN("+")); break;
! 888: }
! 889: break;
! 890: default:
! 891: /*
! 892: { 'a', FORMAT_REMOTE_ADDR },
! 893: { 'A', FORMAT_LOCAL_ADDR },
! 894: { 'C', FORMAT_COOKIE },
! 895: { 'D', FORMAT_TIME_USED_MS },
! 896: */
! 897:
! 898: break;
! 899: }
! 900: break;
! 901: default:
! 902: break;
! 903: }
! 904: }
! 905:
! 906: buffer_append_string_len(b, CONST_STR_LEN("\n"));
! 907:
! 908: if (p->conf.use_syslog || /* syslog doesn't cache */
! 909: (p->conf.access_logfile->used && p->conf.access_logfile->ptr[0] == '|') || /* pipes don't cache */
! 910: newts ||
! 911: b->used > BUFFER_MAX_REUSE_SIZE) {
! 912: if (p->conf.use_syslog) {
! 913: #ifdef HAVE_SYSLOG_H
! 914: if (b->used > 2) {
! 915: /* syslog appends a \n on its own */
! 916: syslog(p->conf.syslog_level, "%*s", (int) b->used - 2, b->ptr);
! 917: }
! 918: #endif
! 919: } else if (p->conf.log_access_fd != -1) {
! 920: write(p->conf.log_access_fd, b->ptr, b->used - 1);
! 921: }
! 922: buffer_reset(b);
! 923: }
! 924:
! 925: return HANDLER_GO_ON;
! 926: }
! 927:
! 928:
! 929: int mod_accesslog_plugin_init(plugin *p);
! 930: int mod_accesslog_plugin_init(plugin *p) {
! 931: p->version = LIGHTTPD_VERSION_ID;
! 932: p->name = buffer_init_string("accesslog");
! 933:
! 934: p->init = mod_accesslog_init;
! 935: p->set_defaults= log_access_open;
! 936: p->cleanup = mod_accesslog_free;
! 937:
! 938: p->handle_request_done = log_access_write;
! 939: p->handle_sighup = log_access_cycle;
! 940:
! 941: p->data = NULL;
! 942:
! 943: return 0;
! 944: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>