1 #include "first.h" 2 3 #include "base.h" 4 #include "fdevent.h" 5 #include "log.h" 6 #include "buffer.h" 7 #include "http_header.h" 8 #include "sock_addr.h" 9 10 #include "plugin.h" 11 12 #include <sys/types.h> 13 #include <sys/stat.h> 14 15 #include <stdlib.h> 16 #include <string.h> 17 #include <fcntl.h> 18 #include <unistd.h> 19 #include <time.h> 20 21 #ifdef HAVE_SYSLOG_H 22 # include <syslog.h> 23 #endif 24 25 typedef struct { 26 char key; 27 enum { 28 FORMAT_UNSET, 29 FORMAT_UNSUPPORTED, 30 FORMAT_PERCENT, 31 FORMAT_REMOTE_HOST, 32 FORMAT_REMOTE_IDENT, 33 FORMAT_REMOTE_USER, 34 FORMAT_TIMESTAMP, 35 FORMAT_REQUEST_LINE, 36 FORMAT_STATUS, 37 FORMAT_BYTES_OUT_NO_HEADER, 38 FORMAT_HEADER, 39 40 FORMAT_REMOTE_ADDR, 41 FORMAT_LOCAL_ADDR, 42 FORMAT_COOKIE, 43 FORMAT_TIME_USED_US, 44 FORMAT_ENV, 45 FORMAT_FILENAME, 46 FORMAT_REQUEST_PROTOCOL, 47 FORMAT_REQUEST_METHOD, 48 FORMAT_SERVER_PORT, 49 FORMAT_QUERY_STRING, 50 FORMAT_TIME_USED, 51 FORMAT_URL, 52 FORMAT_SERVER_NAME, 53 FORMAT_HTTP_HOST, 54 FORMAT_CONNECTION_STATUS, 55 FORMAT_BYTES_IN, 56 FORMAT_BYTES_OUT, 57 58 FORMAT_KEEPALIVE_COUNT, 59 FORMAT_RESPONSE_HEADER, 60 FORMAT_NOTE 61 } type; 62 } format_mapping; 63 64 /** 65 * 66 * 67 * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" 68 * 69 */ 70 71 static const format_mapping fmap[] = 72 { 73 { '%', FORMAT_PERCENT }, 74 { 'h', FORMAT_REMOTE_HOST }, 75 { 'l', FORMAT_REMOTE_IDENT }, 76 { 'u', FORMAT_REMOTE_USER }, 77 { 't', FORMAT_TIMESTAMP }, 78 { 'r', FORMAT_REQUEST_LINE }, 79 { 's', FORMAT_STATUS }, 80 { 'b', FORMAT_BYTES_OUT_NO_HEADER }, 81 { 'i', FORMAT_HEADER }, 82 83 { 'a', FORMAT_REMOTE_ADDR }, 84 { 'A', FORMAT_LOCAL_ADDR }, 85 { 'B', FORMAT_BYTES_OUT_NO_HEADER }, 86 { 'C', FORMAT_COOKIE }, 87 { 'D', FORMAT_TIME_USED_US }, 88 { 'e', FORMAT_ENV }, 89 { 'f', FORMAT_FILENAME }, 90 { 'H', FORMAT_REQUEST_PROTOCOL }, 91 { 'k', FORMAT_KEEPALIVE_COUNT }, 92 { 'm', FORMAT_REQUEST_METHOD }, 93 { 'n', FORMAT_NOTE }, 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 enum e_optflags_time { 112 /* format string is passed to strftime unless other format optflags set 113 * (besides FORMAT_FLAG_TIME_BEGIN or FORMAT_FLAG_TIME_END) */ 114 FORMAT_FLAG_TIME_END = 0x00,/* use request end time (default) */ 115 FORMAT_FLAG_TIME_BEGIN = 0x01,/* use request start time */ 116 FORMAT_FLAG_TIME_SEC = 0x02,/* request time as num sec since epoch */ 117 FORMAT_FLAG_TIME_MSEC = 0x04,/* request time as num msec since epoch */ 118 FORMAT_FLAG_TIME_USEC = 0x08,/* request time as num usec since epoch */ 119 FORMAT_FLAG_TIME_NSEC = 0x10,/* request time as num nsec since epoch */ 120 FORMAT_FLAG_TIME_MSEC_FRAC = 0x20,/* request time msec fraction */ 121 FORMAT_FLAG_TIME_USEC_FRAC = 0x40,/* request time usec fraction */ 122 FORMAT_FLAG_TIME_NSEC_FRAC = 0x80 /* request time nsec fraction */ 123 }; 124 125 enum e_optflags_port { 126 FORMAT_FLAG_PORT_LOCAL = 0x01,/* (default) */ 127 FORMAT_FLAG_PORT_REMOTE = 0x02 128 }; 129 130 131 typedef struct { 132 enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type; 133 int field; 134 int opt; 135 buffer string; 136 } format_field; 137 138 typedef struct { 139 time_t last_generated_accesslog_ts; 140 buffer ts_accesslog_str; 141 #if defined(__STDC_VERSION__) && __STDC_VERSION__-0 >= 199901L /* C99 */ 142 format_field ptr[]; /* C99 VLA */ 143 #else 144 format_field ptr[1]; 145 #endif 146 } format_fields; 147 148 typedef struct { 149 int log_access_fd; 150 char use_syslog; /* syslog has global buffer */ 151 char piped_logger; 152 unsigned short syslog_level; 153 buffer *access_logbuffer; /* each logfile has a separate buffer */ 154 const buffer *access_logfile; 155 156 format_fields *parsed_format; 157 } plugin_config; 158 159 typedef struct { 160 int log_access_fd; 161 char piped_logger; 162 const buffer *access_logfile; 163 buffer access_logbuffer; /* each logfile has a separate buffer */ 164 } accesslog_st; 165 166 typedef struct { 167 PLUGIN_DATA; 168 plugin_config defaults; 169 plugin_config conf; 170 171 buffer syslog_logbuffer; /* syslog has global buffer. no caching, always written directly */ 172 log_error_st *errh; /* copy of srv->errh */ 173 format_fields *default_format;/* allocated if default format */ 174 } plugin_data; 175 176 INIT_FUNC(mod_accesslog_init) { 177 return calloc(1, sizeof(plugin_data)); 178 } 179 180 static int accesslog_write_all(const int fd, buffer * const b) { 181 const ssize_t wr = (fd >= 0) ? write_all(fd, CONST_BUF_LEN(b)) : 0; 182 buffer_clear(b); /*(clear buffer, even if fd < 0)*/ 183 return (-1 != wr); 184 } 185 186 static void accesslog_append_escaped_str(buffer * const dest, const char * const str, const size_t len) { 187 const char *ptr, *start, *end; 188 189 /* replaces non-printable chars with \xHH where HH is the hex representation of the byte */ 190 /* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */ 191 if (0 == len) return; 192 buffer_string_prepare_append(dest, len); 193 194 for (ptr = start = str, end = str+len; ptr < end; ++ptr) { 195 unsigned char const c = *(const unsigned char *)ptr; 196 if (c >= ' ' && c <= '~' && c != '"' && c != '\\') { 197 /* nothing to change, add later as one block */ 198 } else { 199 /* copy previous part */ 200 if (start < ptr) { 201 buffer_append_string_len(dest, start, ptr - start); 202 } 203 start = ptr + 1; 204 205 switch (c) { 206 case '"': 207 BUFFER_APPEND_STRING_CONST(dest, "\\\""); 208 break; 209 case '\\': 210 BUFFER_APPEND_STRING_CONST(dest, "\\\\"); 211 break; 212 case '\b': 213 BUFFER_APPEND_STRING_CONST(dest, "\\b"); 214 break; 215 case '\n': 216 BUFFER_APPEND_STRING_CONST(dest, "\\n"); 217 break; 218 case '\r': 219 BUFFER_APPEND_STRING_CONST(dest, "\\r"); 220 break; 221 case '\t': 222 BUFFER_APPEND_STRING_CONST(dest, "\\t"); 223 break; 224 case '\v': 225 BUFFER_APPEND_STRING_CONST(dest, "\\v"); 226 break; 227 default: { 228 /* non printable char => \xHH */ 229 char hh[5] = {'\\','x',0,0,0}; 230 char h = c / 16; 231 hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0'); 232 h = c % 16; 233 hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0'); 234 buffer_append_string_len(dest, &hh[0], 4); 235 } 236 break; 237 } 238 } 239 } 240 241 if (start < end) { 242 buffer_append_string_len(dest, start, end - start); 243 } 244 } 245 246 static void accesslog_append_escaped(buffer *dest, const buffer *str) { 247 accesslog_append_escaped_str(dest, CONST_BUF_LEN(str)); 248 } 249 250 __attribute_cold__ 251 static format_fields * accesslog_parse_format_err(log_error_st *errh, const char *file, unsigned int line, format_field *f, const char *msg) { 252 log_error(errh, file, line, "%s", msg); 253 for (; f->type != FIELD_UNSET; ++f) free(f->string.ptr); 254 return NULL; 255 } 256 257 static format_fields * accesslog_parse_format(const char * const format, const size_t flen, log_error_st * const errh) { 258 /* common log format (the default) results in 18 elements, 259 * so 127 should be enough except for obscene custom usage */ 260 size_t i, j, k = 0, start = 0; 261 uint32_t used = 0; 262 const uint32_t sz = 127;/* (sz+1 must match fptr[] num elts below) */ 263 format_field *f; 264 format_field fptr[128]; /* (128 elements takes 4k on stack in 64-bit) */ 265 memset(fptr, 0, sizeof(fptr)); 266 if (0 != FIELD_UNSET) return NULL; 267 268 if (0 == flen) return NULL; 269 270 for (i = 0; i < flen; ++i) { 271 if (format[i] != '%') continue; 272 273 if (i > 0 && start != i) { 274 /* copy the string before this % */ 275 if (used == sz) 276 return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr, 277 "too many fields (>= 127) in accesslog.format"); 278 279 f = fptr+used; 280 f->type = FIELD_STRING; 281 memset(&f->string, 0, sizeof(buffer)); 282 buffer_copy_string_len(&f->string, format + start, i - start); 283 284 ++used; 285 } 286 287 /* we need a new field */ 288 if (used == sz) 289 return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr, 290 "too many fields (>= 127) in accesslog.format"); 291 292 /* search for the terminating command */ 293 switch (format[i+1]) { 294 case '>': 295 case '<': 296 /* after the } has to be a character */ 297 if (format[i+2] == '\0') { 298 return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr, 299 "%< and %> have to be followed by a format-specifier"); 300 } 301 302 303 for (j = 0; fmap[j].key != '\0'; j++) { 304 if (fmap[j].key != format[i+2]) continue; 305 306 /* found key */ 307 308 f = fptr+used; 309 f->type = FIELD_FORMAT; 310 f->field = fmap[j].type; 311 f->opt = 0; 312 f->string.ptr = NULL; 313 314 ++used; 315 316 break; 317 } 318 319 if (fmap[j].key == '\0') { 320 return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr, 321 "%< and %> have to be followed by a valid format-specifier"); 322 } 323 324 start = i + 3; 325 i = start - 1; /* skip the string */ 326 327 break; 328 case '{': 329 /* go forward to } */ 330 331 for (k = i+2; k < flen; ++k) { 332 if (format[k] == '}') break; 333 } 334 335 if (k == flen) { 336 return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr, 337 "%{ has to be terminated by a }"); 338 } 339 340 /* after the } has to be a character */ 341 if (format[k+1] == '\0') { 342 return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr, 343 "%{...} has to be followed by a format-specifier"); 344 } 345 346 if (k == i + 2) { 347 return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr, 348 "%{...} has to contain a string"); 349 } 350 351 for (j = 0; fmap[j].key != '\0'; j++) { 352 if (fmap[j].key != format[k+1]) continue; 353 354 /* found key */ 355 356 f = fptr+used; 357 f->type = FIELD_FORMAT; 358 f->field = fmap[j].type; 359 f->opt = 0; 360 memset(&f->string, 0, sizeof(buffer)); 361 buffer_copy_string_len(&f->string, format + i + 2, k - (i + 2)); 362 363 ++used; 364 365 break; 366 } 367 368 if (fmap[j].key == '\0') { 369 return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr, 370 "%{...} has to be followed by a valid format-specifier"); 371 } 372 373 start = k + 2; 374 i = start - 1; /* skip the string */ 375 376 break; 377 default: 378 /* after the % has to be a character */ 379 if (format[i+1] == '\0') { 380 return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr, 381 "% has to be followed by a format-specifier"); 382 } 383 384 for (j = 0; fmap[j].key != '\0'; j++) { 385 if (fmap[j].key != format[i+1]) continue; 386 387 /* found key */ 388 389 f = fptr+used; 390 f->type = FIELD_FORMAT; 391 f->field = fmap[j].type; 392 f->string.ptr = NULL; 393 f->opt = 0; 394 395 ++used; 396 397 break; 398 } 399 400 if (fmap[j].key == '\0') { 401 return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr, 402 "% has to be followed by a valid format-specifier"); 403 } 404 405 start = i + 2; 406 i = start - 1; /* skip the string */ 407 408 break; 409 } 410 } 411 412 if (start < i) { 413 /* copy the string */ 414 if (used == sz) 415 return accesslog_parse_format_err(errh, __FILE__, __LINE__, fptr, 416 "too many fields (>= 127) in accesslog.format"); 417 418 f = fptr+used; 419 f->type = FIELD_STRING; 420 memset(&f->string, 0, sizeof(buffer)); 421 buffer_copy_string_len(&f->string, format + start, i - start); 422 423 ++used; 424 } 425 426 format_fields * const fields = 427 malloc(sizeof(format_fields) + ((used+1) * sizeof(format_field))); 428 force_assert(fields); 429 memset(fields, 0, sizeof(format_fields)); 430 memcpy(fields->ptr, fptr, (used+1) * sizeof(format_field)); 431 return fields; 432 } 433 434 static void mod_accesslog_free_accesslog(accesslog_st * const x, plugin_data *p) { 435 /*(piped loggers are closed in fdevent_close_logger_pipes())*/ 436 if (!x->piped_logger && -1 != x->log_access_fd) { 437 if (!accesslog_write_all(x->log_access_fd, &x->access_logbuffer)) { 438 log_perror(p->errh, __FILE__, __LINE__, 439 "writing access log entry failed: %s", x->access_logfile->ptr); 440 } 441 close(x->log_access_fd); 442 } 443 free(x->access_logbuffer.ptr); 444 } 445 446 static void mod_accesslog_free_format_fields(format_fields * const ff) { 447 for (format_field *f = ff->ptr; f->type != FIELD_UNSET; ++f) 448 free(f->string.ptr); 449 free(ff->ts_accesslog_str.ptr); 450 free(ff); 451 } 452 453 FREE_FUNC(mod_accesslog_free) { 454 plugin_data * const p = p_d; 455 free(p->syslog_logbuffer.ptr); 456 if (NULL == p->cvlist) return; 457 /* (init i to 0 if global context; to 1 to skip empty global context) */ 458 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 459 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 460 for (; -1 != cpv->k_id; ++cpv) { 461 if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue; 462 switch (cpv->k_id) { 463 case 0: /* accesslog.filename */ 464 mod_accesslog_free_accesslog(cpv->v.v, p); 465 break; 466 case 1: /* accesslog.format */ 467 mod_accesslog_free_format_fields(cpv->v.v); 468 break; 469 default: 470 break; 471 } 472 } 473 } 474 475 if (NULL != p->default_format) { 476 mod_accesslog_free_format_fields(p->default_format); 477 } 478 } 479 480 static void mod_accesslog_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 481 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 482 case 0:{/* accesslog.filename */ 483 if (cpv->vtype != T_CONFIG_LOCAL) break; 484 accesslog_st * const x = cpv->v.v; 485 pconf->log_access_fd = x->log_access_fd; 486 pconf->piped_logger = x->piped_logger; 487 pconf->access_logfile = x->access_logfile; 488 pconf->access_logbuffer = &x->access_logbuffer; 489 break; 490 } 491 case 1:{/* accesslog.format */ 492 if (cpv->vtype != T_CONFIG_LOCAL) break; 493 pconf->parsed_format = cpv->v.v; 494 break; 495 } 496 case 2: /* accesslog.use-syslog */ 497 pconf->use_syslog = (int)cpv->v.u; 498 break; 499 case 3: /* accesslog.syslog-level */ 500 pconf->syslog_level = cpv->v.shrt; 501 break; 502 default:/* should not happen */ 503 return; 504 } 505 } 506 507 static void mod_accesslog_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 508 do { 509 mod_accesslog_merge_config_cpv(pconf, cpv); 510 } while ((++cpv)->k_id != -1); 511 } 512 513 static void mod_accesslog_patch_config(request_st * const r, plugin_data * const p) { 514 memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); 515 for (int i = 1, used = p->nconfig; i < used; ++i) { 516 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 517 mod_accesslog_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 518 } 519 } 520 521 static format_fields * mod_accesslog_process_format(const char * const format, const size_t flen, server * const srv); 522 523 SETDEFAULTS_FUNC(mod_accesslog_set_defaults) { 524 static const config_plugin_keys_t cpk[] = { 525 { CONST_STR_LEN("accesslog.filename"), 526 T_CONFIG_STRING, 527 T_CONFIG_SCOPE_CONNECTION } 528 ,{ CONST_STR_LEN("accesslog.format"), 529 T_CONFIG_STRING, 530 T_CONFIG_SCOPE_CONNECTION } 531 ,{ CONST_STR_LEN("accesslog.use-syslog"), 532 T_CONFIG_BOOL, 533 T_CONFIG_SCOPE_CONNECTION } 534 ,{ CONST_STR_LEN("accesslog.syslog-level"), 535 T_CONFIG_SHORT, 536 T_CONFIG_SCOPE_CONNECTION } 537 ,{ NULL, 0, 538 T_CONFIG_UNSET, 539 T_CONFIG_SCOPE_UNSET } 540 }; 541 542 plugin_data * const p = p_d; 543 p->errh = srv->errh; 544 if (!config_plugin_values_init(srv, p, cpk, "mod_accesslog")) 545 return HANDLER_ERROR; 546 547 /* process and validate config directives 548 * (init i to 0 if global context; to 1 to skip empty global context) */ 549 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 550 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 551 int use_syslog = 0; 552 accesslog_st *x = NULL; 553 for (; -1 != cpv->k_id; ++cpv) { 554 switch (cpv->k_id) { 555 case 0: /* accesslog.filename */ 556 x = calloc(1, sizeof(accesslog_st)); 557 force_assert(x); 558 x->log_access_fd = -1; 559 x->piped_logger = (cpv->v.b->ptr[0] == '|'); 560 x->access_logfile = cpv->v.b; 561 cpv->vtype = T_CONFIG_LOCAL; 562 cpv->v.v = x; 563 break; 564 case 1: /* accesslog.format */ 565 if (NULL != strchr(cpv->v.b->ptr, '\\')) { 566 /* process basic backslash-escapes in format string */ 567 buffer *b; 568 *(const buffer **)&b = cpv->v.b; 569 char *t = b->ptr; 570 for (char *s = t; *s; ++s) { 571 if (s[0] != '\\') { *t++ = *s; continue; } 572 if (s[1] == '\0') continue; /*(ignore dangling '\\')*/ 573 switch (*++s) { 574 case 'a': *t++ = '\a'; break; /* bell */ 575 case 'b': *t++ = '\b'; break; /* backspace */ 576 case 'f': *t++ = '\f'; break; /* form feed */ 577 case 'n': *t++ = '\n'; break; /* newline */ 578 case 'r': *t++ = '\r'; break; /* carriage return */ 579 case 't': *t++ = '\t'; break; /* horizontal tab */ 580 case 'v': *t++ = '\v'; break; /* vertical tab */ 581 /*case '"':*/ 582 /*case '\\':*/ 583 default: *t++ = *s; break; /*(use literal char)*/ 584 } 585 } 586 buffer_string_set_length(b, (size_t)(t - b->ptr)); 587 } 588 cpv->v.v = 589 mod_accesslog_process_format(CONST_BUF_LEN(cpv->v.b), srv); 590 if (NULL == cpv->v.v) return HANDLER_ERROR; 591 cpv->vtype = T_CONFIG_LOCAL; 592 break; 593 case 2: /* accesslog.use-syslog */ 594 use_syslog = (int)cpv->v.u; 595 break; 596 case 3: /* accesslog.syslog-level */ 597 break; 598 default:/* should not happen */ 599 break; 600 } 601 } 602 603 if (srv->srvconf.preflight_check) continue; 604 605 if (use_syslog) continue; /* ignore the next checks */ 606 if (NULL == x || buffer_string_is_empty(x->access_logfile)) continue; 607 608 x->log_access_fd = fdevent_open_logger(x->access_logfile->ptr); 609 if (-1 == x->log_access_fd) { 610 log_perror(srv->errh, __FILE__, __LINE__, 611 "opening log '%s' failed", x->access_logfile->ptr); 612 return HANDLER_ERROR; 613 } 614 } 615 616 p->defaults.log_access_fd = -1; 617 p->defaults.syslog_level = LOG_INFO; 618 619 /* initialize p->defaults from global config context */ 620 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 621 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 622 if (-1 != cpv->k_id) 623 mod_accesslog_merge_config(&p->defaults, cpv); 624 } 625 626 if (NULL == p->defaults.parsed_format) { 627 /* (set default format even if p->use_syslog since 628 * some other condition might enable logfile) */ 629 static const char fmt[] = 630 "%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""; 631 p->defaults.parsed_format = p->default_format = 632 mod_accesslog_process_format(CONST_STR_LEN(fmt), srv); 633 if (NULL == p->default_format) return HANDLER_ERROR; 634 } 635 636 return HANDLER_GO_ON; 637 } 638 639 static format_fields * mod_accesslog_process_format(const char * const format, const size_t flen, server * const srv) { 640 format_fields * const parsed_format = 641 accesslog_parse_format(format, flen, srv->errh); 642 if (NULL == parsed_format) { 643 log_error(srv->errh, __FILE__, __LINE__, 644 "parsing accesslog-definition failed: %s", format); 645 return NULL; 646 } 647 648 uint32_t tcount = 0; 649 for (format_field *f = parsed_format->ptr; f->type != FIELD_UNSET; ++f) { 650 const buffer * const fstr = &f->string; 651 if (FIELD_FORMAT != f->type) continue; 652 if (FORMAT_TIMESTAMP == f->field) { 653 if (!buffer_string_is_empty(fstr)) { 654 const char *ptr = fstr->ptr; 655 if (0 == strncmp(ptr, "begin:", sizeof("begin:")-1)) { 656 f->opt |= FORMAT_FLAG_TIME_BEGIN; 657 ptr += sizeof("begin:")-1; 658 } else if (0 == strncmp(ptr, "end:", sizeof("end:")-1)) { 659 f->opt |= FORMAT_FLAG_TIME_END; 660 ptr += sizeof("end:")-1; 661 } 662 if (0 == strcmp(ptr, "sec")) f->opt |= FORMAT_FLAG_TIME_SEC; 663 else if (0 == strcmp(ptr, "msec")) f->opt |= FORMAT_FLAG_TIME_MSEC; 664 else if (0 == strcmp(ptr, "usec")) f->opt |= FORMAT_FLAG_TIME_USEC; 665 else if (0 == strcmp(ptr, "nsec")) f->opt |= FORMAT_FLAG_TIME_NSEC; 666 else if (0 == strcmp(ptr, "msec_frac")) f->opt |= FORMAT_FLAG_TIME_MSEC_FRAC; 667 else if (0 == strcmp(ptr, "usec_frac")) f->opt |= FORMAT_FLAG_TIME_USEC_FRAC; 668 else if (0 == strcmp(ptr, "nsec_frac")) f->opt |= FORMAT_FLAG_TIME_NSEC_FRAC; 669 else if (NULL == strchr(ptr, '%')) { 670 log_error(srv->errh, __FILE__, __LINE__, 671 "constant string for time format (misspelled token? or missing '%%'): %s", format); 672 mod_accesslog_free_format_fields(parsed_format); 673 return NULL; 674 } 675 } 676 677 /* make sure they didn't try to send the timestamp in twice 678 * (would invalidate pconf->parsed_format.ts_accesslog_str cache of timestamp str) */ 679 if (!(f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END|FORMAT_FLAG_TIME_SEC)) && ++tcount > 1) { 680 log_error(srv->errh, __FILE__, __LINE__, 681 "you may not use strftime timestamp format %%{}t twice in the same access log: %s", format); 682 mod_accesslog_free_format_fields(parsed_format); 683 return NULL; 684 } 685 686 if (f->opt & FORMAT_FLAG_TIME_BEGIN) srv->srvconf.high_precision_timestamps = 1; 687 } else if (FORMAT_TIME_USED_US == f->field) { 688 f->opt |= FORMAT_FLAG_TIME_USEC; 689 srv->srvconf.high_precision_timestamps = 1; 690 } else if (FORMAT_TIME_USED == f->field) { 691 if (buffer_string_is_empty(fstr) 692 || buffer_is_equal_string(fstr, CONST_STR_LEN("s")) 693 || buffer_is_equal_string(fstr, CONST_STR_LEN("sec"))) f->opt |= FORMAT_FLAG_TIME_SEC; 694 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("ms")) 695 || buffer_is_equal_string(fstr, CONST_STR_LEN("msec"))) f->opt |= FORMAT_FLAG_TIME_MSEC; 696 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("us")) 697 || buffer_is_equal_string(fstr, CONST_STR_LEN("usec"))) f->opt |= FORMAT_FLAG_TIME_USEC; 698 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("ns")) 699 || buffer_is_equal_string(fstr, CONST_STR_LEN("nsec"))) f->opt |= FORMAT_FLAG_TIME_NSEC; 700 else { 701 log_error(srv->errh, __FILE__, __LINE__, 702 "invalid time unit in %%{UNIT}T: %s", format); 703 mod_accesslog_free_format_fields(parsed_format); 704 return NULL; 705 } 706 707 if (f->opt & ~(FORMAT_FLAG_TIME_SEC)) srv->srvconf.high_precision_timestamps = 1; 708 } else if (FORMAT_COOKIE == f->field) { 709 if (buffer_string_is_empty(fstr)) f->type = FIELD_STRING; /*(blank)*/ 710 } else if (FORMAT_SERVER_PORT == f->field) { 711 if (buffer_string_is_empty(fstr)) 712 f->opt |= FORMAT_FLAG_PORT_LOCAL; 713 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("canonical"))) 714 f->opt |= FORMAT_FLAG_PORT_LOCAL; 715 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("local"))) 716 f->opt |= FORMAT_FLAG_PORT_LOCAL; 717 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("remote"))) 718 f->opt |= FORMAT_FLAG_PORT_REMOTE; 719 else { 720 log_error(srv->errh, __FILE__, __LINE__, 721 "invalid format %%{canonical,local,remote}p: %s", format); 722 mod_accesslog_free_format_fields(parsed_format); 723 return NULL; 724 } 725 } 726 } 727 728 return parsed_format; 729 } 730 731 static void log_access_flush(plugin_data * const p) { 732 /* future: might be slightly faster to have allocated array of open files 733 * rather than walking config, but only might matter with many directives */ 734 /* (init i to 0 if global context; to 1 to skip empty global context) */ 735 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 736 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 737 for (; -1 != cpv->k_id; ++cpv) { 738 switch (cpv->k_id) { 739 case 0: /* accesslog.filename */ 740 if (cpv->vtype == T_CONFIG_LOCAL && NULL != cpv->v.v) { 741 accesslog_st * const x = cpv->v.v; 742 if (buffer_string_is_empty(&x->access_logbuffer)) continue; 743 if (!accesslog_write_all(x->log_access_fd, 744 &x->access_logbuffer)) { 745 log_perror(p->errh, __FILE__, __LINE__, 746 "writing access log entry failed: %s", 747 x->access_logfile->ptr); 748 } 749 } 750 break; 751 default: 752 break; 753 } 754 } 755 } 756 } 757 758 TRIGGER_FUNC(log_access_periodic_flush) { 759 /* flush buffered access logs every 4 seconds */ 760 if (0 == (log_epoch_secs & 3)) log_access_flush((plugin_data *)p_d); 761 UNUSED(srv); 762 return HANDLER_GO_ON; 763 } 764 765 SIGHUP_FUNC(log_access_cycle) { 766 plugin_data * const p = p_d; 767 768 log_access_flush(p); 769 770 /* future: might be slightly faster to have allocated array of open files 771 * rather than walking config, but only might matter with many directives */ 772 /* (init i to 0 if global context; to 1 to skip empty global context) */ 773 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 774 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 775 for (; -1 != cpv->k_id; ++cpv) { 776 if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue; 777 switch (cpv->k_id) { 778 case 0:{/* accesslog.filename */ 779 accesslog_st * const x = cpv->v.v; 780 if (x->piped_logger) continue; 781 if (buffer_string_is_empty(x->access_logfile)) continue; 782 if (-1 == fdevent_cycle_logger(x->access_logfile->ptr, 783 &x->log_access_fd)) { 784 log_perror(srv->errh, __FILE__, __LINE__, 785 "cycling access log failed: %s", x->access_logfile->ptr); 786 } 787 break; 788 } 789 default: 790 break; 791 } 792 } 793 } 794 795 return HANDLER_GO_ON; 796 } 797 798 static int log_access_record (const request_st * const r, buffer * const b, format_fields * const parsed_format) { 799 const connection * const con = r->con; 800 const buffer *vb; 801 struct timespec ts = { 0, 0 }; 802 int flush = 0; 803 804 for (const format_field *f = parsed_format->ptr; f->type != FIELD_UNSET; ++f) { 805 switch(f->type) { 806 case FIELD_STRING: 807 buffer_append_string_buffer(b, &f->string); 808 break; 809 case FIELD_FORMAT: 810 switch(f->field) { 811 case FORMAT_TIMESTAMP: 812 813 if (f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END)) { 814 if (f->opt & FORMAT_FLAG_TIME_SEC) { 815 time_t t = (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) ? log_epoch_secs : r->start_ts; 816 buffer_append_int(b, (intmax_t)t); 817 } else if (f->opt & (FORMAT_FLAG_TIME_MSEC|FORMAT_FLAG_TIME_USEC|FORMAT_FLAG_TIME_NSEC)) { 818 off_t t; /*(expected to be 64-bit since large file support enabled)*/ 819 long ns; 820 if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) { 821 if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); 822 t = (off_t)ts.tv_sec; 823 ns = ts.tv_nsec; 824 } else { 825 t = (off_t)r->start_hp.tv_sec; 826 ns = r->start_hp.tv_nsec; 827 } 828 if (f->opt & FORMAT_FLAG_TIME_MSEC) { 829 t *= 1000; 830 t += (ns + 999999) / 1000000; /* ceil */ 831 } else if (f->opt & FORMAT_FLAG_TIME_USEC) { 832 t *= 1000000; 833 t += (ns + 999) / 1000; /* ceil */ 834 } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC)*/ 835 t *= 1000000000; 836 t += ns; 837 } 838 buffer_append_int(b, (intmax_t)t); 839 } else { /*(FORMAT_FLAG_TIME_MSEC_FRAC|FORMAT_FLAG_TIME_USEC_FRAC|FORMAT_FLAG_TIME_NSEC_FRAC)*/ 840 long ns; 841 char *ptr; 842 if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) { 843 if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); 844 ns = ts.tv_nsec; 845 } else { 846 ns = r->start_hp.tv_nsec; 847 } 848 /*assert(t < 1000000000);*/ 849 if (f->opt & FORMAT_FLAG_TIME_MSEC_FRAC) { 850 ns += 999999; /* ceil */ 851 ns /= 1000000; 852 buffer_append_string_len(b, CONST_STR_LEN("000")); 853 } else if (f->opt & FORMAT_FLAG_TIME_USEC_FRAC) { 854 ns += 999; /* ceil */ 855 ns /= 1000; 856 buffer_append_string_len(b, CONST_STR_LEN("000000")); 857 } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC_FRAC)*/ 858 buffer_append_string_len(b, CONST_STR_LEN("000000000")); 859 } 860 for (ptr = b->ptr + buffer_string_length(b); ns > 0; ns /= 10) 861 *--ptr = (ns % 10) + '0'; 862 } 863 } else { 864 buffer * const ts_accesslog_str = &parsed_format->ts_accesslog_str; 865 /* cache the generated timestamp (only if ! FORMAT_FLAG_TIME_BEGIN) */ 866 struct tm *tmptr; 867 time_t t; 868 #if defined(HAVE_STRUCT_TM_GMTOFF) 869 # ifdef HAVE_LOCALTIME_R 870 struct tm tm; 871 # endif /* HAVE_LOCALTIME_R */ 872 #else /* HAVE_STRUCT_TM_GMTOFF */ 873 # ifdef HAVE_GMTIME_R 874 struct tm tm; 875 # endif /* HAVE_GMTIME_R */ 876 #endif /* HAVE_STRUCT_TM_GMTOFF */ 877 878 if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) { 879 const time_t cur_ts = log_epoch_secs; 880 if (parsed_format->last_generated_accesslog_ts == cur_ts) { 881 buffer_append_string_buffer(b, ts_accesslog_str); 882 break; 883 } 884 t = parsed_format->last_generated_accesslog_ts = cur_ts; 885 flush = 1; 886 } else { 887 t = r->start_ts; 888 } 889 890 #if defined(HAVE_STRUCT_TM_GMTOFF) 891 # ifdef HAVE_LOCALTIME_R 892 tmptr = localtime_r(&t, &tm); 893 # else /* HAVE_LOCALTIME_R */ 894 tmptr = localtime(&t); 895 # endif /* HAVE_LOCALTIME_R */ 896 #else /* HAVE_STRUCT_TM_GMTOFF */ 897 # ifdef HAVE_GMTIME_R 898 tmptr = gmtime_r(&t, &tm); 899 # else /* HAVE_GMTIME_R */ 900 tmptr = gmtime(&t); 901 # endif /* HAVE_GMTIME_R */ 902 #endif /* HAVE_STRUCT_TM_GMTOFF */ 903 904 buffer_clear(ts_accesslog_str); 905 906 if (buffer_string_is_empty(&f->string)) { 907 #if defined(HAVE_STRUCT_TM_GMTOFF) 908 long scd, hrs, min; 909 buffer_append_strftime(ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S ", tmptr); 910 buffer_append_string_len(ts_accesslog_str, tmptr->tm_gmtoff >= 0 ? "+" : "-", 1); 911 912 scd = labs(tmptr->tm_gmtoff); 913 hrs = scd / 3600; 914 min = (scd % 3600) / 60; 915 916 /* hours */ 917 if (hrs < 10) buffer_append_string_len(ts_accesslog_str, CONST_STR_LEN("0")); 918 buffer_append_int(ts_accesslog_str, hrs); 919 920 if (min < 10) buffer_append_string_len(ts_accesslog_str, CONST_STR_LEN("0")); 921 buffer_append_int(ts_accesslog_str, min); 922 buffer_append_string_len(ts_accesslog_str, CONST_STR_LEN("]")); 923 #else 924 buffer_append_strftime(ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S +0000]", tmptr); 925 #endif /* HAVE_STRUCT_TM_GMTOFF */ 926 } else { 927 buffer_append_strftime(ts_accesslog_str, f->string.ptr, tmptr); 928 } 929 930 buffer_append_string_buffer(b, ts_accesslog_str); 931 } 932 break; 933 case FORMAT_TIME_USED: 934 case FORMAT_TIME_USED_US: 935 if (f->opt & FORMAT_FLAG_TIME_SEC) { 936 buffer_append_int(b, log_epoch_secs - r->start_ts); 937 } else { 938 const struct timespec * const bs = &r->start_hp; 939 off_t tdiff; /*(expected to be 64-bit since large file support enabled)*/ 940 if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); 941 tdiff = (off_t)(ts.tv_sec - bs->tv_sec)*1000000000 + (ts.tv_nsec - bs->tv_nsec); 942 if (tdiff <= 0) { 943 /* sanity check for time moving backwards 944 * (daylight savings adjustment or leap seconds or ?) */ 945 tdiff = -1; 946 } else if (f->opt & FORMAT_FLAG_TIME_MSEC) { 947 tdiff += 999999; /* ceil */ 948 tdiff /= 1000000; 949 } else if (f->opt & FORMAT_FLAG_TIME_USEC) { 950 tdiff += 999; /* ceil */ 951 tdiff /= 1000; 952 } /* else (f->opt & FORMAT_FLAG_TIME_NSEC) */ 953 buffer_append_int(b, (intmax_t)tdiff); 954 } 955 break; 956 case FORMAT_REMOTE_ADDR: 957 case FORMAT_REMOTE_HOST: 958 buffer_append_string_buffer(b, con->dst_addr_buf); 959 break; 960 case FORMAT_REMOTE_IDENT: 961 /* ident */ 962 buffer_append_string_len(b, CONST_STR_LEN("-")); 963 break; 964 case FORMAT_REMOTE_USER: 965 if (NULL != (vb = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")))) { 966 accesslog_append_escaped(b, vb); 967 } else { 968 buffer_append_string_len(b, CONST_STR_LEN("-")); 969 } 970 break; 971 case FORMAT_REQUEST_LINE: 972 /*(attempt to reconstruct request line)*/ 973 http_method_append(b, r->http_method); 974 buffer_append_string_len(b, CONST_STR_LEN(" ")); 975 accesslog_append_escaped(b, &r->target_orig); 976 buffer_append_string_len(b, CONST_STR_LEN(" ")); 977 http_version_append(b, r->http_version); 978 break; 979 case FORMAT_STATUS: 980 buffer_append_int(b, r->http_status); 981 break; 982 983 case FORMAT_BYTES_OUT_NO_HEADER: 984 { 985 off_t bytes = r->http_version <= HTTP_VERSION_1_1 986 ? con->bytes_written - r->bytes_written_ckpt 987 : r->write_queue->bytes_out; 988 if (bytes > 0) { 989 bytes -= (off_t)r->resp_header_len; 990 buffer_append_int(b, bytes > 0 ? bytes : 0); 991 } else { 992 buffer_append_string_len(b, CONST_STR_LEN("-")); 993 } 994 break; 995 } 996 case FORMAT_HEADER: 997 if (NULL != (vb = http_header_request_get(r, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(&f->string)))) { 998 accesslog_append_escaped(b, vb); 999 } else { 1000 buffer_append_string_len(b, CONST_STR_LEN("-")); 1001 } 1002 break; 1003 case FORMAT_RESPONSE_HEADER: 1004 if (NULL != (vb = http_header_response_get(r, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(&f->string)))) { 1005 accesslog_append_escaped(b, vb); 1006 } else { 1007 buffer_append_string_len(b, CONST_STR_LEN("-")); 1008 } 1009 break; 1010 case FORMAT_ENV: 1011 case FORMAT_NOTE: 1012 if (NULL != (vb = http_header_env_get(r, CONST_BUF_LEN(&f->string)))) { 1013 accesslog_append_escaped(b, vb); 1014 } else { 1015 buffer_append_string_len(b, CONST_STR_LEN("-")); 1016 } 1017 break; 1018 case FORMAT_FILENAME: 1019 if (!buffer_string_is_empty(&r->physical.path)) { 1020 buffer_append_string_buffer(b, &r->physical.path); 1021 } else { 1022 buffer_append_string_len(b, CONST_STR_LEN("-")); 1023 } 1024 break; 1025 case FORMAT_BYTES_OUT: 1026 { 1027 off_t bytes = r->http_version <= HTTP_VERSION_1_1 1028 ? con->bytes_written - r->bytes_written_ckpt 1029 : r->write_queue->bytes_out; 1030 if (bytes > 0) { 1031 buffer_append_int(b, bytes); 1032 } else { 1033 buffer_append_string_len(b, CONST_STR_LEN("-")); 1034 } 1035 break; 1036 } 1037 case FORMAT_BYTES_IN: 1038 { 1039 off_t bytes = r->http_version <= HTTP_VERSION_1_1 1040 ? con->bytes_read - r->bytes_read_ckpt 1041 : r->read_queue->bytes_in + (off_t)r->rqst_header_len; 1042 if (bytes > 0) { 1043 buffer_append_int(b, bytes); 1044 } else { 1045 buffer_append_string_len(b, CONST_STR_LEN("-")); 1046 } 1047 break; 1048 } 1049 case FORMAT_SERVER_NAME: 1050 if (!buffer_string_is_empty(r->server_name)) { 1051 buffer_append_string_buffer(b, r->server_name); 1052 } else { 1053 buffer_append_string_len(b, CONST_STR_LEN("-")); 1054 } 1055 break; 1056 case FORMAT_HTTP_HOST: 1057 if (!buffer_string_is_empty(&r->uri.authority)) { 1058 accesslog_append_escaped(b, &r->uri.authority); 1059 } else { 1060 buffer_append_string_len(b, CONST_STR_LEN("-")); 1061 } 1062 break; 1063 case FORMAT_REQUEST_PROTOCOL: 1064 http_version_append(b, r->http_version); 1065 break; 1066 case FORMAT_REQUEST_METHOD: 1067 http_method_append(b, r->http_method); 1068 break; 1069 case FORMAT_PERCENT: 1070 buffer_append_string_len(b, CONST_STR_LEN("%")); 1071 break; 1072 case FORMAT_LOCAL_ADDR: 1073 { 1074 /* (perf: not using getsockname() and inet_ntop_cache_get_ip()) 1075 * (still useful if admin has configured explicit listen IPs) */ 1076 const char *colon; 1077 buffer *srvtoken = con->srv_socket->srv_token; 1078 if (srvtoken->ptr[0] == '[') { 1079 colon = strstr(srvtoken->ptr, "]:"); 1080 } else { 1081 colon = strchr(srvtoken->ptr, ':'); 1082 } 1083 if (colon) { 1084 buffer_append_string_len(b, srvtoken->ptr, (size_t)(colon - srvtoken->ptr)); 1085 } else { 1086 buffer_append_string_buffer(b, srvtoken); 1087 } 1088 } 1089 break; 1090 case FORMAT_SERVER_PORT: 1091 if (f->opt & FORMAT_FLAG_PORT_REMOTE) { 1092 buffer_append_int(b, sock_addr_get_port(&con->dst_addr)); 1093 } else { /* if (f->opt & FORMAT_FLAG_PORT_LOCAL) *//*(default)*/ 1094 const char *colon; 1095 buffer *srvtoken = ((server_socket*)(con->srv_socket))->srv_token; 1096 if (srvtoken->ptr[0] == '[') { 1097 colon = strstr(srvtoken->ptr, "]:"); 1098 } else { 1099 colon = strchr(srvtoken->ptr, ':'); 1100 } 1101 if (colon) { 1102 buffer_append_string(b, colon+1); 1103 } else { 1104 buffer_append_int(b, con->srv->srvconf.port); 1105 } 1106 } 1107 break; 1108 case FORMAT_QUERY_STRING: 1109 accesslog_append_escaped(b, &r->uri.query); 1110 break; 1111 case FORMAT_URL: 1112 { 1113 const uint32_t len = buffer_string_length(&r->target); 1114 const char * const qmark = memchr(r->target.ptr, '?', len); 1115 accesslog_append_escaped_str(b, r->target.ptr, qmark ? (uint32_t)(qmark - r->target.ptr) : len); 1116 } 1117 break; 1118 case FORMAT_CONNECTION_STATUS: 1119 if (r->state == CON_STATE_RESPONSE_END) { 1120 if (0 == r->keep_alive) { 1121 buffer_append_string_len(b, CONST_STR_LEN("-")); 1122 } else { 1123 buffer_append_string_len(b, CONST_STR_LEN("+")); 1124 } 1125 } else { /* CON_STATE_ERROR */ 1126 buffer_append_string_len(b, CONST_STR_LEN("X")); 1127 } 1128 break; 1129 case FORMAT_KEEPALIVE_COUNT: 1130 if (con->request_count > 1) { 1131 buffer_append_int(b, (intmax_t)(con->request_count-1)); 1132 } else { 1133 buffer_append_string_len(b, CONST_STR_LEN("0")); 1134 } 1135 break; 1136 case FORMAT_COOKIE: 1137 if (NULL != (vb = http_header_request_get(r, HTTP_HEADER_COOKIE, CONST_STR_LEN("Cookie")))) { 1138 char *str = vb->ptr; 1139 size_t len = buffer_string_length(&f->string); 1140 do { 1141 while (*str == ' ' || *str == '\t') ++str; 1142 if (0 == strncmp(str, f->string.ptr, len) && str[len] == '=') { 1143 char *v = str+len+1; 1144 for (str = v; *str != '\0' && *str != ';'; ++str) ; 1145 if (str == v) break; 1146 do { --str; } while (str > v && (*str == ' ' || *str == '\t')); 1147 accesslog_append_escaped_str(b, v, str - v + 1); 1148 break; 1149 } else { 1150 while (*str != ';' && *str != ' ' && *str != '\t' && *str != '\0') ++str; 1151 } 1152 while (*str == ' ' || *str == '\t') ++str; 1153 } while (*str++ == ';'); 1154 } 1155 break; 1156 default: 1157 break; 1158 } 1159 break; 1160 default: 1161 break; 1162 } 1163 } 1164 1165 return flush; 1166 } 1167 1168 REQUESTDONE_FUNC(log_access_write) { 1169 plugin_data * const p = p_d; 1170 mod_accesslog_patch_config(r, p); 1171 1172 /* No output device, nothing to do */ 1173 if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON; 1174 1175 buffer * const b = (p->conf.use_syslog) 1176 ? &p->syslog_logbuffer 1177 : p->conf.access_logbuffer; 1178 1179 const int flush = p->conf.piped_logger 1180 | log_access_record(r, b, p->conf.parsed_format); 1181 1182 if (p->conf.use_syslog) { /* syslog doesn't cache */ 1183 #ifdef HAVE_SYSLOG_H 1184 if (!buffer_string_is_empty(b)) { 1185 /*(syslog appends a \n on its own)*/ 1186 syslog(p->conf.syslog_level, "%s", b->ptr); 1187 } 1188 #endif 1189 buffer_clear(b); 1190 } 1191 else { 1192 buffer_append_string_len(b, CONST_STR_LEN("\n")); 1193 1194 if (flush || buffer_string_length(b) >= 8192) { 1195 if (!accesslog_write_all(p->conf.log_access_fd, b)) { 1196 log_perror(r->conf.errh, __FILE__, __LINE__, 1197 "writing access log entry failed: %s", 1198 p->conf.access_logfile->ptr); 1199 } 1200 } 1201 } 1202 1203 return HANDLER_GO_ON; 1204 } 1205 1206 1207 int mod_accesslog_plugin_init(plugin *p); 1208 int mod_accesslog_plugin_init(plugin *p) { 1209 p->version = LIGHTTPD_VERSION_ID; 1210 p->name = "accesslog"; 1211 1212 p->init = mod_accesslog_init; 1213 p->set_defaults= mod_accesslog_set_defaults; 1214 p->cleanup = mod_accesslog_free; 1215 1216 p->handle_request_done = log_access_write; 1217 p->handle_trigger = log_access_periodic_flush; 1218 p->handle_sighup = log_access_cycle; 1219 1220 return 0; 1221 } 1222