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