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