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 free(x); 445 } 446 447 static void mod_accesslog_free_format_fields(format_fields * const ff) { 448 for (format_field *f = ff->ptr; f->type != FIELD_UNSET; ++f) 449 free(f->string.ptr); 450 free(ff->ts_accesslog_str.ptr); 451 free(ff); 452 } 453 454 FREE_FUNC(mod_accesslog_free) { 455 plugin_data * const p = p_d; 456 free(p->syslog_logbuffer.ptr); 457 if (NULL == p->cvlist) return; 458 /* (init i to 0 if global context; to 1 to skip empty global context) */ 459 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 460 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 461 for (; -1 != cpv->k_id; ++cpv) { 462 if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue; 463 switch (cpv->k_id) { 464 case 0: /* accesslog.filename */ 465 mod_accesslog_free_accesslog(cpv->v.v, p); 466 break; 467 case 1: /* accesslog.format */ 468 mod_accesslog_free_format_fields(cpv->v.v); 469 break; 470 default: 471 break; 472 } 473 } 474 } 475 476 if (NULL != p->default_format) { 477 mod_accesslog_free_format_fields(p->default_format); 478 } 479 } 480 481 static void mod_accesslog_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 482 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 483 case 0:{/* accesslog.filename */ 484 if (cpv->vtype != T_CONFIG_LOCAL) break; 485 accesslog_st * const x = cpv->v.v; 486 pconf->log_access_fd = x->log_access_fd; 487 pconf->piped_logger = x->piped_logger; 488 pconf->access_logfile = x->access_logfile; 489 pconf->access_logbuffer = &x->access_logbuffer; 490 break; 491 } 492 case 1:{/* accesslog.format */ 493 if (cpv->vtype != T_CONFIG_LOCAL) break; 494 pconf->parsed_format = cpv->v.v; 495 break; 496 } 497 case 2: /* accesslog.use-syslog */ 498 pconf->use_syslog = (int)cpv->v.u; 499 break; 500 case 3: /* accesslog.syslog-level */ 501 pconf->syslog_level = cpv->v.shrt; 502 break; 503 default:/* should not happen */ 504 return; 505 } 506 } 507 508 static void mod_accesslog_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 509 do { 510 mod_accesslog_merge_config_cpv(pconf, cpv); 511 } while ((++cpv)->k_id != -1); 512 } 513 514 static void mod_accesslog_patch_config(request_st * const r, plugin_data * const p) { 515 memcpy(&p->conf, &p->defaults, sizeof(plugin_config)); 516 for (int i = 1, used = p->nconfig; i < used; ++i) { 517 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 518 mod_accesslog_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 519 } 520 } 521 522 static format_fields * mod_accesslog_process_format(const char * const format, const size_t flen, server * const srv); 523 524 SETDEFAULTS_FUNC(mod_accesslog_set_defaults) { 525 static const config_plugin_keys_t cpk[] = { 526 { CONST_STR_LEN("accesslog.filename"), 527 T_CONFIG_STRING, 528 T_CONFIG_SCOPE_CONNECTION } 529 ,{ CONST_STR_LEN("accesslog.format"), 530 T_CONFIG_STRING, 531 T_CONFIG_SCOPE_CONNECTION } 532 ,{ CONST_STR_LEN("accesslog.use-syslog"), 533 T_CONFIG_BOOL, 534 T_CONFIG_SCOPE_CONNECTION } 535 ,{ CONST_STR_LEN("accesslog.syslog-level"), 536 T_CONFIG_SHORT, 537 T_CONFIG_SCOPE_CONNECTION } 538 ,{ NULL, 0, 539 T_CONFIG_UNSET, 540 T_CONFIG_SCOPE_UNSET } 541 }; 542 543 plugin_data * const p = p_d; 544 p->errh = srv->errh; 545 if (!config_plugin_values_init(srv, p, cpk, "mod_accesslog")) 546 return HANDLER_ERROR; 547 548 /* process and validate config directives 549 * (init i to 0 if global context; to 1 to skip empty global context) */ 550 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 551 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 552 int use_syslog = 0; 553 accesslog_st *x = NULL; 554 for (; -1 != cpv->k_id; ++cpv) { 555 switch (cpv->k_id) { 556 case 0: /* accesslog.filename */ 557 x = calloc(1, sizeof(accesslog_st)); 558 force_assert(x); 559 x->log_access_fd = -1; 560 x->piped_logger = (cpv->v.b->ptr[0] == '|'); 561 x->access_logfile = cpv->v.b; 562 cpv->vtype = T_CONFIG_LOCAL; 563 cpv->v.v = x; 564 break; 565 case 1: /* accesslog.format */ 566 if (NULL != strchr(cpv->v.b->ptr, '\\')) { 567 /* process basic backslash-escapes in format string */ 568 buffer *b; 569 *(const buffer **)&b = cpv->v.b; 570 char *t = b->ptr; 571 for (char *s = t; *s; ++s) { 572 if (s[0] != '\\') { *t++ = *s; continue; } 573 if (s[1] == '\0') continue; /*(ignore dangling '\\')*/ 574 switch (*++s) { 575 case 'a': *t++ = '\a'; break; /* bell */ 576 case 'b': *t++ = '\b'; break; /* backspace */ 577 case 'f': *t++ = '\f'; break; /* form feed */ 578 case 'n': *t++ = '\n'; break; /* newline */ 579 case 'r': *t++ = '\r'; break; /* carriage return */ 580 case 't': *t++ = '\t'; break; /* horizontal tab */ 581 case 'v': *t++ = '\v'; break; /* vertical tab */ 582 /*case '"':*/ 583 /*case '\\':*/ 584 default: *t++ = *s; break; /*(use literal char)*/ 585 } 586 } 587 buffer_string_set_length(b, (size_t)(t - b->ptr)); 588 } 589 cpv->v.v = 590 mod_accesslog_process_format(CONST_BUF_LEN(cpv->v.b), srv); 591 if (NULL == cpv->v.v) return HANDLER_ERROR; 592 cpv->vtype = T_CONFIG_LOCAL; 593 break; 594 case 2: /* accesslog.use-syslog */ 595 use_syslog = (int)cpv->v.u; 596 break; 597 case 3: /* accesslog.syslog-level */ 598 break; 599 default:/* should not happen */ 600 break; 601 } 602 } 603 604 if (srv->srvconf.preflight_check) continue; 605 606 if (use_syslog) continue; /* ignore the next checks */ 607 if (NULL == x || buffer_string_is_empty(x->access_logfile)) continue; 608 609 x->log_access_fd = fdevent_open_logger(x->access_logfile->ptr); 610 if (-1 == x->log_access_fd) { 611 log_perror(srv->errh, __FILE__, __LINE__, 612 "opening log '%s' failed", x->access_logfile->ptr); 613 return HANDLER_ERROR; 614 } 615 } 616 617 p->defaults.log_access_fd = -1; 618 p->defaults.syslog_level = LOG_INFO; 619 620 /* initialize p->defaults from global config context */ 621 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 622 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 623 if (-1 != cpv->k_id) 624 mod_accesslog_merge_config(&p->defaults, cpv); 625 } 626 627 if (NULL == p->defaults.parsed_format) { 628 /* (set default format even if p->use_syslog since 629 * some other condition might enable logfile) */ 630 static const char fmt[] = 631 "%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""; 632 p->defaults.parsed_format = p->default_format = 633 mod_accesslog_process_format(CONST_STR_LEN(fmt), srv); 634 if (NULL == p->default_format) return HANDLER_ERROR; 635 } 636 637 return HANDLER_GO_ON; 638 } 639 640 static format_fields * mod_accesslog_process_format(const char * const format, const size_t flen, server * const srv) { 641 format_fields * const parsed_format = 642 accesslog_parse_format(format, flen, srv->errh); 643 if (NULL == parsed_format) { 644 log_error(srv->errh, __FILE__, __LINE__, 645 "parsing accesslog-definition failed: %s", format); 646 return NULL; 647 } 648 649 uint32_t tcount = 0; 650 for (format_field *f = parsed_format->ptr; f->type != FIELD_UNSET; ++f) { 651 const buffer * const fstr = &f->string; 652 if (FIELD_FORMAT != f->type) continue; 653 if (FORMAT_TIMESTAMP == f->field) { 654 if (!buffer_string_is_empty(fstr)) { 655 const char *ptr = fstr->ptr; 656 if (0 == strncmp(ptr, "begin:", sizeof("begin:")-1)) { 657 f->opt |= FORMAT_FLAG_TIME_BEGIN; 658 ptr += sizeof("begin:")-1; 659 } else if (0 == strncmp(ptr, "end:", sizeof("end:")-1)) { 660 f->opt |= FORMAT_FLAG_TIME_END; 661 ptr += sizeof("end:")-1; 662 } 663 if (0 == strcmp(ptr, "sec")) f->opt |= FORMAT_FLAG_TIME_SEC; 664 else if (0 == strcmp(ptr, "msec")) f->opt |= FORMAT_FLAG_TIME_MSEC; 665 else if (0 == strcmp(ptr, "usec")) f->opt |= FORMAT_FLAG_TIME_USEC; 666 else if (0 == strcmp(ptr, "nsec")) f->opt |= FORMAT_FLAG_TIME_NSEC; 667 else if (0 == strcmp(ptr, "msec_frac")) f->opt |= FORMAT_FLAG_TIME_MSEC_FRAC; 668 else if (0 == strcmp(ptr, "usec_frac")) f->opt |= FORMAT_FLAG_TIME_USEC_FRAC; 669 else if (0 == strcmp(ptr, "nsec_frac")) f->opt |= FORMAT_FLAG_TIME_NSEC_FRAC; 670 else if (NULL == strchr(ptr, '%')) { 671 log_error(srv->errh, __FILE__, __LINE__, 672 "constant string for time format (misspelled token? or missing '%%'): %s", format); 673 mod_accesslog_free_format_fields(parsed_format); 674 return NULL; 675 } 676 } 677 678 /* make sure they didn't try to send the timestamp in twice 679 * (would invalidate pconf->parsed_format.ts_accesslog_str cache of timestamp str) */ 680 if (!(f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END|FORMAT_FLAG_TIME_SEC)) && ++tcount > 1) { 681 log_error(srv->errh, __FILE__, __LINE__, 682 "you may not use strftime timestamp format %%{}t twice in the same access log: %s", format); 683 mod_accesslog_free_format_fields(parsed_format); 684 return NULL; 685 } 686 687 if (f->opt & FORMAT_FLAG_TIME_BEGIN) srv->srvconf.high_precision_timestamps = 1; 688 } else if (FORMAT_TIME_USED_US == f->field) { 689 f->opt |= FORMAT_FLAG_TIME_USEC; 690 srv->srvconf.high_precision_timestamps = 1; 691 } else if (FORMAT_TIME_USED == f->field) { 692 if (buffer_string_is_empty(fstr) 693 || buffer_is_equal_string(fstr, CONST_STR_LEN("s")) 694 || buffer_is_equal_string(fstr, CONST_STR_LEN("sec"))) f->opt |= FORMAT_FLAG_TIME_SEC; 695 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("ms")) 696 || buffer_is_equal_string(fstr, CONST_STR_LEN("msec"))) f->opt |= FORMAT_FLAG_TIME_MSEC; 697 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("us")) 698 || buffer_is_equal_string(fstr, CONST_STR_LEN("usec"))) f->opt |= FORMAT_FLAG_TIME_USEC; 699 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("ns")) 700 || buffer_is_equal_string(fstr, CONST_STR_LEN("nsec"))) f->opt |= FORMAT_FLAG_TIME_NSEC; 701 else { 702 log_error(srv->errh, __FILE__, __LINE__, 703 "invalid time unit in %%{UNIT}T: %s", format); 704 mod_accesslog_free_format_fields(parsed_format); 705 return NULL; 706 } 707 708 if (f->opt & ~(FORMAT_FLAG_TIME_SEC)) srv->srvconf.high_precision_timestamps = 1; 709 } else if (FORMAT_COOKIE == f->field) { 710 if (buffer_string_is_empty(fstr)) f->type = FIELD_STRING; /*(blank)*/ 711 } else if (FORMAT_SERVER_PORT == f->field) { 712 if (buffer_string_is_empty(fstr)) 713 f->opt |= FORMAT_FLAG_PORT_LOCAL; 714 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("canonical"))) 715 f->opt |= FORMAT_FLAG_PORT_LOCAL; 716 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("local"))) 717 f->opt |= FORMAT_FLAG_PORT_LOCAL; 718 else if (buffer_is_equal_string(fstr, CONST_STR_LEN("remote"))) 719 f->opt |= FORMAT_FLAG_PORT_REMOTE; 720 else { 721 log_error(srv->errh, __FILE__, __LINE__, 722 "invalid format %%{canonical,local,remote}p: %s", format); 723 mod_accesslog_free_format_fields(parsed_format); 724 return NULL; 725 } 726 } else if (FORMAT_HEADER == f->field 727 || FORMAT_RESPONSE_HEADER == f->field) { 728 f->opt = http_header_hkey_get(CONST_BUF_LEN(fstr)); 729 } 730 } 731 732 return parsed_format; 733 } 734 735 static void log_access_flush(plugin_data * const p) { 736 /* future: might be slightly faster to have allocated array of open files 737 * rather than walking config, but only might matter with many directives */ 738 /* (init i to 0 if global context; to 1 to skip empty global context) */ 739 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 740 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 741 for (; -1 != cpv->k_id; ++cpv) { 742 switch (cpv->k_id) { 743 case 0: /* accesslog.filename */ 744 if (cpv->vtype == T_CONFIG_LOCAL && NULL != cpv->v.v) { 745 accesslog_st * const x = cpv->v.v; 746 if (buffer_string_is_empty(&x->access_logbuffer)) continue; 747 if (!accesslog_write_all(x->log_access_fd, 748 &x->access_logbuffer)) { 749 log_perror(p->errh, __FILE__, __LINE__, 750 "writing access log entry failed: %s", 751 x->access_logfile->ptr); 752 } 753 } 754 break; 755 default: 756 break; 757 } 758 } 759 } 760 } 761 762 TRIGGER_FUNC(log_access_periodic_flush) { 763 /* flush buffered access logs every 4 seconds */ 764 if (0 == (log_epoch_secs & 3)) log_access_flush((plugin_data *)p_d); 765 UNUSED(srv); 766 return HANDLER_GO_ON; 767 } 768 769 SIGHUP_FUNC(log_access_cycle) { 770 plugin_data * const p = p_d; 771 772 log_access_flush(p); 773 774 /* future: might be slightly faster to have allocated array of open files 775 * rather than walking config, but only might matter with many directives */ 776 /* (init i to 0 if global context; to 1 to skip empty global context) */ 777 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 778 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 779 for (; -1 != cpv->k_id; ++cpv) { 780 if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue; 781 switch (cpv->k_id) { 782 case 0:{/* accesslog.filename */ 783 accesslog_st * const x = cpv->v.v; 784 if (x->piped_logger) continue; 785 if (buffer_string_is_empty(x->access_logfile)) continue; 786 if (-1 == fdevent_cycle_logger(x->access_logfile->ptr, 787 &x->log_access_fd)) { 788 log_perror(srv->errh, __FILE__, __LINE__, 789 "cycling access log failed: %s", x->access_logfile->ptr); 790 } 791 /*(update p->defaults after cycling log)*/ 792 if (0 == i) p->defaults.log_access_fd = x->log_access_fd; 793 break; 794 } 795 default: 796 break; 797 } 798 } 799 } 800 801 return HANDLER_GO_ON; 802 } 803 804 static int log_access_record (const request_st * const r, buffer * const b, format_fields * const parsed_format) { 805 const connection * const con = r->con; 806 const buffer *vb; 807 struct timespec ts = { 0, 0 }; 808 int flush = 0; 809 810 for (const format_field *f = parsed_format->ptr; f->type != FIELD_UNSET; ++f) { 811 switch(f->type) { 812 case FIELD_STRING: 813 buffer_append_string_buffer(b, &f->string); 814 break; 815 case FIELD_FORMAT: 816 switch(f->field) { 817 case FORMAT_TIMESTAMP: 818 819 if (f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END)) { 820 if (f->opt & FORMAT_FLAG_TIME_SEC) { 821 time_t t = (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) ? log_epoch_secs : r->start_hp.tv_sec; 822 buffer_append_int(b, (intmax_t)t); 823 } else if (f->opt & (FORMAT_FLAG_TIME_MSEC|FORMAT_FLAG_TIME_USEC|FORMAT_FLAG_TIME_NSEC)) { 824 off_t t; /*(expected to be 64-bit since large file support enabled)*/ 825 long ns; 826 if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) { 827 if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); 828 t = (off_t)ts.tv_sec; 829 ns = ts.tv_nsec; 830 } else { 831 t = (off_t)r->start_hp.tv_sec; 832 ns = r->start_hp.tv_nsec; 833 } 834 if (f->opt & FORMAT_FLAG_TIME_MSEC) { 835 t *= 1000; 836 t += (ns + 999999) / 1000000; /* ceil */ 837 } else if (f->opt & FORMAT_FLAG_TIME_USEC) { 838 t *= 1000000; 839 t += (ns + 999) / 1000; /* ceil */ 840 } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC)*/ 841 t *= 1000000000; 842 t += ns; 843 } 844 buffer_append_int(b, (intmax_t)t); 845 } else { /*(FORMAT_FLAG_TIME_MSEC_FRAC|FORMAT_FLAG_TIME_USEC_FRAC|FORMAT_FLAG_TIME_NSEC_FRAC)*/ 846 long ns; 847 char *ptr; 848 if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) { 849 if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); 850 ns = ts.tv_nsec; 851 } else { 852 ns = r->start_hp.tv_nsec; 853 } 854 /*assert(t < 1000000000);*/ 855 if (f->opt & FORMAT_FLAG_TIME_MSEC_FRAC) { 856 ns += 999999; /* ceil */ 857 ns /= 1000000; 858 buffer_append_string_len(b, CONST_STR_LEN("000")); 859 } else if (f->opt & FORMAT_FLAG_TIME_USEC_FRAC) { 860 ns += 999; /* ceil */ 861 ns /= 1000; 862 buffer_append_string_len(b, CONST_STR_LEN("000000")); 863 } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC_FRAC)*/ 864 buffer_append_string_len(b, CONST_STR_LEN("000000000")); 865 } 866 for (ptr = b->ptr + buffer_string_length(b); ns > 0; ns /= 10) 867 *--ptr = (ns % 10) + '0'; 868 } 869 } else { 870 buffer * const ts_accesslog_str = &parsed_format->ts_accesslog_str; 871 /* cache the generated timestamp (only if ! FORMAT_FLAG_TIME_BEGIN) */ 872 struct tm *tmptr; 873 time_t t; 874 #if defined(HAVE_STRUCT_TM_GMTOFF) 875 # ifdef HAVE_LOCALTIME_R 876 struct tm tm; 877 # endif /* HAVE_LOCALTIME_R */ 878 #else /* HAVE_STRUCT_TM_GMTOFF */ 879 # ifdef HAVE_GMTIME_R 880 struct tm tm; 881 # endif /* HAVE_GMTIME_R */ 882 #endif /* HAVE_STRUCT_TM_GMTOFF */ 883 884 if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) { 885 const time_t cur_ts = log_epoch_secs; 886 if (parsed_format->last_generated_accesslog_ts == cur_ts) { 887 buffer_append_string_buffer(b, ts_accesslog_str); 888 break; 889 } 890 t = parsed_format->last_generated_accesslog_ts = cur_ts; 891 flush = 1; 892 } else { 893 t = r->start_hp.tv_sec; 894 } 895 896 #if defined(HAVE_STRUCT_TM_GMTOFF) 897 # ifdef HAVE_LOCALTIME_R 898 tmptr = localtime_r(&t, &tm); 899 # else /* HAVE_LOCALTIME_R */ 900 tmptr = localtime(&t); 901 # endif /* HAVE_LOCALTIME_R */ 902 #else /* HAVE_STRUCT_TM_GMTOFF */ 903 # ifdef HAVE_GMTIME_R 904 tmptr = gmtime_r(&t, &tm); 905 # else /* HAVE_GMTIME_R */ 906 tmptr = gmtime(&t); 907 # endif /* HAVE_GMTIME_R */ 908 #endif /* HAVE_STRUCT_TM_GMTOFF */ 909 910 buffer_clear(ts_accesslog_str); 911 912 if (buffer_string_is_empty(&f->string)) { 913 #if defined(HAVE_STRUCT_TM_GMTOFF) 914 long scd, hrs, min; 915 buffer_append_strftime(ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S ", tmptr); 916 buffer_append_string_len(ts_accesslog_str, tmptr->tm_gmtoff >= 0 ? "+" : "-", 1); 917 918 scd = labs(tmptr->tm_gmtoff); 919 hrs = scd / 3600; 920 min = (scd % 3600) / 60; 921 922 /* hours */ 923 if (hrs < 10) buffer_append_string_len(ts_accesslog_str, CONST_STR_LEN("0")); 924 buffer_append_int(ts_accesslog_str, hrs); 925 926 if (min < 10) buffer_append_string_len(ts_accesslog_str, CONST_STR_LEN("0")); 927 buffer_append_int(ts_accesslog_str, min); 928 buffer_append_string_len(ts_accesslog_str, CONST_STR_LEN("]")); 929 #else 930 buffer_append_strftime(ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S +0000]", tmptr); 931 #endif /* HAVE_STRUCT_TM_GMTOFF */ 932 } else { 933 buffer_append_strftime(ts_accesslog_str, f->string.ptr, tmptr); 934 } 935 936 buffer_append_string_buffer(b, ts_accesslog_str); 937 } 938 break; 939 case FORMAT_TIME_USED: 940 case FORMAT_TIME_USED_US: 941 if (f->opt & FORMAT_FLAG_TIME_SEC) { 942 buffer_append_int(b, log_epoch_secs - r->start_hp.tv_sec); 943 } else { 944 const struct timespec * const bs = &r->start_hp; 945 off_t tdiff; /*(expected to be 64-bit since large file support enabled)*/ 946 if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); 947 tdiff = (off_t)(ts.tv_sec - bs->tv_sec)*1000000000 + (ts.tv_nsec - bs->tv_nsec); 948 if (tdiff <= 0) { 949 /* sanity check for time moving backwards 950 * (daylight savings adjustment or leap seconds or ?) */ 951 tdiff = -1; 952 } else if (f->opt & FORMAT_FLAG_TIME_MSEC) { 953 tdiff += 999999; /* ceil */ 954 tdiff /= 1000000; 955 } else if (f->opt & FORMAT_FLAG_TIME_USEC) { 956 tdiff += 999; /* ceil */ 957 tdiff /= 1000; 958 } /* else (f->opt & FORMAT_FLAG_TIME_NSEC) */ 959 buffer_append_int(b, (intmax_t)tdiff); 960 } 961 break; 962 case FORMAT_REMOTE_ADDR: 963 case FORMAT_REMOTE_HOST: 964 buffer_append_string_buffer(b, con->dst_addr_buf); 965 break; 966 case FORMAT_REMOTE_IDENT: 967 /* ident */ 968 buffer_append_string_len(b, CONST_STR_LEN("-")); 969 break; 970 case FORMAT_REMOTE_USER: 971 if (NULL != (vb = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")))) { 972 accesslog_append_escaped(b, vb); 973 } else { 974 buffer_append_string_len(b, CONST_STR_LEN("-")); 975 } 976 break; 977 case FORMAT_REQUEST_LINE: 978 /*(attempt to reconstruct request line)*/ 979 http_method_append(b, r->http_method); 980 buffer_append_string_len(b, CONST_STR_LEN(" ")); 981 accesslog_append_escaped(b, &r->target_orig); 982 buffer_append_string_len(b, CONST_STR_LEN(" ")); 983 http_version_append(b, r->http_version); 984 break; 985 case FORMAT_STATUS: 986 buffer_append_int(b, r->http_status); 987 break; 988 989 case FORMAT_BYTES_OUT_NO_HEADER: 990 { 991 off_t bytes = r->http_version <= HTTP_VERSION_1_1 992 ? con->bytes_written - r->bytes_written_ckpt 993 : r->write_queue.bytes_out; 994 if (bytes > 0) { 995 bytes -= (off_t)r->resp_header_len; 996 buffer_append_int(b, bytes > 0 ? bytes : 0); 997 } else { 998 buffer_append_string_len(b, CONST_STR_LEN("-")); 999 } 1000 break; 1001 } 1002 case FORMAT_HEADER: 1003 if (NULL != (vb = http_header_request_get(r, f->opt, CONST_BUF_LEN(&f->string)))) { 1004 accesslog_append_escaped(b, vb); 1005 } else { 1006 buffer_append_string_len(b, CONST_STR_LEN("-")); 1007 } 1008 break; 1009 case FORMAT_RESPONSE_HEADER: 1010 if (NULL != (vb = http_header_response_get(r, f->opt, CONST_BUF_LEN(&f->string)))) { 1011 accesslog_append_escaped(b, vb); 1012 } else { 1013 buffer_append_string_len(b, CONST_STR_LEN("-")); 1014 } 1015 break; 1016 case FORMAT_ENV: 1017 case FORMAT_NOTE: 1018 if (NULL != (vb = http_header_env_get(r, CONST_BUF_LEN(&f->string)))) { 1019 accesslog_append_escaped(b, vb); 1020 } else { 1021 buffer_append_string_len(b, CONST_STR_LEN("-")); 1022 } 1023 break; 1024 case FORMAT_FILENAME: 1025 if (!buffer_string_is_empty(&r->physical.path)) { 1026 buffer_append_string_buffer(b, &r->physical.path); 1027 } else { 1028 buffer_append_string_len(b, CONST_STR_LEN("-")); 1029 } 1030 break; 1031 case FORMAT_BYTES_OUT: 1032 { 1033 off_t bytes = r->http_version <= HTTP_VERSION_1_1 1034 ? con->bytes_written - r->bytes_written_ckpt 1035 : r->write_queue.bytes_out; 1036 if (bytes > 0) { 1037 buffer_append_int(b, bytes); 1038 } else { 1039 buffer_append_string_len(b, CONST_STR_LEN("-")); 1040 } 1041 break; 1042 } 1043 case FORMAT_BYTES_IN: 1044 { 1045 off_t bytes = r->http_version <= HTTP_VERSION_1_1 1046 ? con->bytes_read - r->bytes_read_ckpt 1047 : r->read_queue.bytes_in + (off_t)r->rqst_header_len; 1048 if (bytes > 0) { 1049 buffer_append_int(b, bytes); 1050 } else { 1051 buffer_append_string_len(b, CONST_STR_LEN("-")); 1052 } 1053 break; 1054 } 1055 case FORMAT_SERVER_NAME: 1056 if (!buffer_string_is_empty(r->server_name)) { 1057 buffer_append_string_buffer(b, r->server_name); 1058 } else { 1059 buffer_append_string_len(b, CONST_STR_LEN("-")); 1060 } 1061 break; 1062 case FORMAT_HTTP_HOST: 1063 if (!buffer_string_is_empty(&r->uri.authority)) { 1064 accesslog_append_escaped(b, &r->uri.authority); 1065 } else { 1066 buffer_append_string_len(b, CONST_STR_LEN("-")); 1067 } 1068 break; 1069 case FORMAT_REQUEST_PROTOCOL: 1070 http_version_append(b, r->http_version); 1071 break; 1072 case FORMAT_REQUEST_METHOD: 1073 http_method_append(b, r->http_method); 1074 break; 1075 case FORMAT_PERCENT: 1076 buffer_append_string_len(b, CONST_STR_LEN("%")); 1077 break; 1078 case FORMAT_LOCAL_ADDR: 1079 { 1080 /* (perf: not using getsockname() and inet_ntop_cache_get_ip()) 1081 * (still useful if admin has configured explicit listen IPs) */ 1082 const char *colon; 1083 buffer *srvtoken = con->srv_socket->srv_token; 1084 if (srvtoken->ptr[0] == '[') { 1085 colon = strstr(srvtoken->ptr, "]:"); 1086 } else { 1087 colon = strchr(srvtoken->ptr, ':'); 1088 } 1089 if (colon) { 1090 buffer_append_string_len(b, srvtoken->ptr, (size_t)(colon - srvtoken->ptr)); 1091 } else { 1092 buffer_append_string_buffer(b, srvtoken); 1093 } 1094 } 1095 break; 1096 case FORMAT_SERVER_PORT: 1097 if (f->opt & FORMAT_FLAG_PORT_REMOTE) { 1098 buffer_append_int(b, sock_addr_get_port(&con->dst_addr)); 1099 } else { /* if (f->opt & FORMAT_FLAG_PORT_LOCAL) *//*(default)*/ 1100 const char *colon; 1101 buffer *srvtoken = ((server_socket*)(con->srv_socket))->srv_token; 1102 if (srvtoken->ptr[0] == '[') { 1103 colon = strstr(srvtoken->ptr, "]:"); 1104 } else { 1105 colon = strchr(srvtoken->ptr, ':'); 1106 } 1107 if (colon) { 1108 buffer_append_string(b, colon+1); 1109 } else { 1110 buffer_append_int(b, con->srv->srvconf.port); 1111 } 1112 } 1113 break; 1114 case FORMAT_QUERY_STRING: 1115 accesslog_append_escaped(b, &r->uri.query); 1116 break; 1117 case FORMAT_URL: 1118 { 1119 const uint32_t len = buffer_string_length(&r->target); 1120 const char * const qmark = memchr(r->target.ptr, '?', len); 1121 accesslog_append_escaped_str(b, r->target.ptr, qmark ? (uint32_t)(qmark - r->target.ptr) : len); 1122 } 1123 break; 1124 case FORMAT_CONNECTION_STATUS: 1125 if (r->state == CON_STATE_RESPONSE_END) { 1126 if (0 == r->keep_alive) { 1127 buffer_append_string_len(b, CONST_STR_LEN("-")); 1128 } else { 1129 buffer_append_string_len(b, CONST_STR_LEN("+")); 1130 } 1131 } else { /* CON_STATE_ERROR */ 1132 buffer_append_string_len(b, CONST_STR_LEN("X")); 1133 } 1134 break; 1135 case FORMAT_KEEPALIVE_COUNT: 1136 if (con->request_count > 1) { 1137 buffer_append_int(b, (intmax_t)(con->request_count-1)); 1138 } else { 1139 buffer_append_string_len(b, CONST_STR_LEN("0")); 1140 } 1141 break; 1142 case FORMAT_COOKIE: 1143 if (NULL != (vb = http_header_request_get(r, HTTP_HEADER_COOKIE, CONST_STR_LEN("Cookie")))) { 1144 char *str = vb->ptr; 1145 size_t len = buffer_string_length(&f->string); 1146 do { 1147 while (*str == ' ' || *str == '\t') ++str; 1148 if (0 == strncmp(str, f->string.ptr, len) && str[len] == '=') { 1149 char *v = str+len+1; 1150 for (str = v; *str != '\0' && *str != ';'; ++str) ; 1151 if (str == v) break; 1152 do { --str; } while (str > v && (*str == ' ' || *str == '\t')); 1153 accesslog_append_escaped_str(b, v, str - v + 1); 1154 break; 1155 } else { 1156 while (*str != ';' && *str != ' ' && *str != '\t' && *str != '\0') ++str; 1157 } 1158 while (*str == ' ' || *str == '\t') ++str; 1159 } while (*str++ == ';'); 1160 } 1161 break; 1162 default: 1163 break; 1164 } 1165 break; 1166 default: 1167 break; 1168 } 1169 } 1170 1171 return flush; 1172 } 1173 1174 REQUESTDONE_FUNC(log_access_write) { 1175 plugin_data * const p = p_d; 1176 mod_accesslog_patch_config(r, p); 1177 1178 /* No output device, nothing to do */ 1179 if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON; 1180 1181 buffer * const b = (p->conf.use_syslog) 1182 ? &p->syslog_logbuffer 1183 : p->conf.access_logbuffer; 1184 1185 const int flush = p->conf.piped_logger 1186 | log_access_record(r, b, p->conf.parsed_format); 1187 1188 if (p->conf.use_syslog) { /* syslog doesn't cache */ 1189 #ifdef HAVE_SYSLOG_H 1190 if (!buffer_string_is_empty(b)) { 1191 /*(syslog appends a \n on its own)*/ 1192 syslog(p->conf.syslog_level, "%s", b->ptr); 1193 } 1194 #endif 1195 buffer_clear(b); 1196 } 1197 else { 1198 buffer_append_string_len(b, CONST_STR_LEN("\n")); 1199 1200 if (flush || buffer_string_length(b) >= 8192) { 1201 if (!accesslog_write_all(p->conf.log_access_fd, b)) { 1202 log_perror(r->conf.errh, __FILE__, __LINE__, 1203 "writing access log entry failed: %s", 1204 p->conf.access_logfile->ptr); 1205 } 1206 } 1207 } 1208 1209 return HANDLER_GO_ON; 1210 } 1211 1212 1213 int mod_accesslog_plugin_init(plugin *p); 1214 int mod_accesslog_plugin_init(plugin *p) { 1215 p->version = LIGHTTPD_VERSION_ID; 1216 p->name = "accesslog"; 1217 1218 p->init = mod_accesslog_init; 1219 p->set_defaults= mod_accesslog_set_defaults; 1220 p->cleanup = mod_accesslog_free; 1221 1222 p->handle_request_done = log_access_write; 1223 p->handle_trigger = log_access_periodic_flush; 1224 p->handle_sighup = log_access_cycle; 1225 1226 return 0; 1227 } 1228