Annotation of embedaddon/lighttpd/src/mod_accesslog.c, revision 1.1.1.3

1.1.1.3 ! misho       1: #include "first.h"
        !             2: 
1.1       misho       3: #include "base.h"
                      4: #include "log.h"
                      5: #include "buffer.h"
                      6: 
                      7: #include "plugin.h"
                      8: 
                      9: #include <sys/types.h>
                     10: #include <sys/stat.h>
                     11: 
                     12: #include <ctype.h>
                     13: #include <stdlib.h>
                     14: #include <string.h>
                     15: #include <fcntl.h>
                     16: #include <unistd.h>
                     17: #include <errno.h>
                     18: #include <time.h>
                     19: 
                     20: #include <stdio.h>
                     21: 
                     22: #ifdef HAVE_SYSLOG_H
                     23: # include <syslog.h>
                     24: #endif
                     25: 
                     26: typedef struct {
                     27:        char key;
                     28:        enum {
                     29:                FORMAT_UNSET,
                     30:                        FORMAT_UNSUPPORTED,
                     31:                        FORMAT_PERCENT,
                     32:                        FORMAT_REMOTE_HOST,
                     33:                        FORMAT_REMOTE_IDENT,
                     34:                        FORMAT_REMOTE_USER,
                     35:                        FORMAT_TIMESTAMP,
                     36:                        FORMAT_REQUEST_LINE,
                     37:                        FORMAT_STATUS,
                     38:                        FORMAT_BYTES_OUT_NO_HEADER,
                     39:                        FORMAT_HEADER,
                     40: 
                     41:                        FORMAT_REMOTE_ADDR,
                     42:                        FORMAT_LOCAL_ADDR,
                     43:                        FORMAT_COOKIE,
1.1.1.3 ! misho      44:                        FORMAT_TIME_USED_US,
1.1       misho      45:                        FORMAT_ENV,
                     46:                        FORMAT_FILENAME,
                     47:                        FORMAT_REQUEST_PROTOCOL,
                     48:                        FORMAT_REQUEST_METHOD,
                     49:                        FORMAT_SERVER_PORT,
                     50:                        FORMAT_QUERY_STRING,
                     51:                        FORMAT_TIME_USED,
                     52:                        FORMAT_URL,
                     53:                        FORMAT_SERVER_NAME,
                     54:                        FORMAT_HTTP_HOST,
                     55:                        FORMAT_CONNECTION_STATUS,
                     56:                        FORMAT_BYTES_IN,
                     57:                        FORMAT_BYTES_OUT,
                     58: 
1.1.1.3 ! misho      59:                        FORMAT_KEEPALIVE_COUNT,
1.1       misho      60:                        FORMAT_RESPONSE_HEADER
                     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 },
1.1.1.3 ! misho      87:        { 'D', FORMAT_TIME_USED_US },
1.1       misho      88:        { 'e', FORMAT_ENV },
                     89:        { 'f', FORMAT_FILENAME },
                     90:        { 'H', FORMAT_REQUEST_PROTOCOL },
1.1.1.3 ! misho      91:        { 'k', FORMAT_KEEPALIVE_COUNT },
1.1       misho      92:        { 'm', FORMAT_REQUEST_METHOD },
                     93:        { 'n', FORMAT_UNSUPPORTED }, /* we have no notes */
                     94:        { 'p', FORMAT_SERVER_PORT },
                     95:        { 'P', FORMAT_UNSUPPORTED }, /* we are only one process */
                     96:        { 'q', FORMAT_QUERY_STRING },
                     97:        { 'T', FORMAT_TIME_USED },
                     98:        { 'U', FORMAT_URL }, /* w/o querystring */
                     99:        { 'v', FORMAT_SERVER_NAME },
                    100:        { 'V', FORMAT_HTTP_HOST },
                    101:        { 'X', FORMAT_CONNECTION_STATUS },
                    102:        { 'I', FORMAT_BYTES_IN },
                    103:        { 'O', FORMAT_BYTES_OUT },
                    104: 
                    105:        { 'o', FORMAT_RESPONSE_HEADER },
                    106: 
                    107:        { '\0', FORMAT_UNSET }
                    108: };
                    109: 
                    110: 
1.1.1.3 ! misho     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: 
1.1       misho     126: typedef struct {
                    127:        enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type;
                    128: 
                    129:        buffer *string;
                    130:        int field;
1.1.1.3 ! misho     131:        int opt;
1.1       misho     132: } format_field;
                    133: 
                    134: typedef struct {
                    135:        format_field **ptr;
                    136: 
                    137:        size_t used;
                    138:        size_t size;
                    139: } format_fields;
                    140: 
                    141: typedef struct {
                    142:        buffer *access_logfile;
                    143:        int    log_access_fd;
                    144:        buffer *access_logbuffer; /* each logfile has a separate buffer */
                    145: 
                    146:        unsigned short use_syslog; /* syslog has global buffer */
                    147:        unsigned short syslog_level;
                    148: 
                    149:        buffer *format;
                    150: 
                    151:        time_t last_generated_accesslog_ts;
                    152:        time_t *last_generated_accesslog_ts_ptr;
                    153: 
                    154:        buffer *ts_accesslog_str;
                    155: 
                    156:        format_fields *parsed_format;
                    157: } plugin_config;
                    158: 
                    159: typedef struct {
                    160:        PLUGIN_DATA;
                    161: 
                    162:        plugin_config **config_storage;
                    163:        plugin_config conf;
                    164: 
                    165:        buffer *syslog_logbuffer; /* syslog has global buffer. no caching, always written directly */
                    166: } plugin_data;
                    167: 
                    168: INIT_FUNC(mod_accesslog_init) {
                    169:        plugin_data *p;
                    170: 
                    171:        p = calloc(1, sizeof(*p));
                    172:        p->syslog_logbuffer = buffer_init();
                    173: 
                    174:        return p;
                    175: }
                    176: 
1.1.1.3 ! misho     177: static void accesslog_write_all(server *srv, const buffer *filename, int fd, const void* buf, size_t count) {
        !           178:        if (-1 == write_all(fd, buf, count)) {
        !           179:                log_error_write(srv, __FILE__, __LINE__, "sbs",
        !           180:                        "writing access log entry failed:", filename, strerror(errno));
        !           181:        }
        !           182: }
        !           183: 
1.1       misho     184: static void accesslog_append_escaped(buffer *dest, buffer *str) {
                    185:        char *ptr, *start, *end;
                    186: 
                    187:        /* replaces non-printable chars with \xHH where HH is the hex representation of the byte */
                    188:        /* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
1.1.1.3 ! misho     189:        if (buffer_string_is_empty(str)) return;
        !           190:        buffer_string_prepare_append(dest, buffer_string_length(str));
1.1       misho     191: 
1.1.1.3 ! misho     192:        for (ptr = start = str->ptr, end = str->ptr + buffer_string_length(str); ptr < end; ptr++) {
        !           193:                unsigned char const c = (unsigned char) *ptr;
1.1       misho     194:                if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
                    195:                        /* nothing to change, add later as one block */
                    196:                } else {
                    197:                        /* copy previous part */
                    198:                        if (start < ptr) {
                    199:                                buffer_append_string_len(dest, start, ptr - start);
                    200:                        }
                    201:                        start = ptr + 1;
                    202: 
                    203:                        switch (c) {
                    204:                        case '"':
                    205:                                BUFFER_APPEND_STRING_CONST(dest, "\\\"");
                    206:                                break;
                    207:                        case '\\':
                    208:                                BUFFER_APPEND_STRING_CONST(dest, "\\\\");
                    209:                                break;
                    210:                        case '\b':
                    211:                                BUFFER_APPEND_STRING_CONST(dest, "\\b");
                    212:                                break;
                    213:                        case '\n':
                    214:                                BUFFER_APPEND_STRING_CONST(dest, "\\n");
                    215:                                break;
                    216:                        case '\r':
                    217:                                BUFFER_APPEND_STRING_CONST(dest, "\\r");
                    218:                                break;
                    219:                        case '\t':
                    220:                                BUFFER_APPEND_STRING_CONST(dest, "\\t");
                    221:                                break;
                    222:                        case '\v':
                    223:                                BUFFER_APPEND_STRING_CONST(dest, "\\v");
                    224:                                break;
                    225:                        default: {
                    226:                                        /* non printable char => \xHH */
                    227:                                        char hh[5] = {'\\','x',0,0,0};
                    228:                                        char h = c / 16;
                    229:                                        hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0');
                    230:                                        h = c % 16;
                    231:                                        hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0');
                    232:                                        buffer_append_string_len(dest, &hh[0], 4);
                    233:                                }
                    234:                                break;
                    235:                        }
                    236:                }
                    237:        }
                    238: 
                    239:        if (start < end) {
                    240:                buffer_append_string_len(dest, start, end - start);
                    241:        }
                    242: }
                    243: 
                    244: static int accesslog_parse_format(server *srv, format_fields *fields, buffer *format) {
                    245:        size_t i, j, k = 0, start = 0;
                    246: 
1.1.1.3 ! misho     247:        if (buffer_is_empty(format)) return -1;
1.1       misho     248: 
1.1.1.3 ! misho     249:        for (i = 0; i < buffer_string_length(format); i++) {
1.1       misho     250:                switch(format->ptr[i]) {
                    251:                case '%':
                    252:                        if (i > 0 && start != i) {
                    253:                                /* copy the string before this % */
                    254:                                if (fields->size == 0) {
                    255:                                        fields->size = 16;
                    256:                                        fields->used = 0;
                    257:                                        fields->ptr = malloc(fields->size * sizeof(format_field * ));
                    258:                                } else if (fields->used == fields->size) {
                    259:                                        fields->size += 16;
                    260:                                        fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
                    261:                                }
                    262: 
                    263:                                fields->ptr[fields->used] = malloc(sizeof(format_field));
                    264:                                fields->ptr[fields->used]->type = FIELD_STRING;
                    265:                                fields->ptr[fields->used]->string = buffer_init();
                    266: 
                    267:                                buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
                    268: 
                    269:                                fields->used++;
                    270:                        }
                    271: 
                    272:                        /* we need a new field */
                    273: 
                    274:                        if (fields->size == 0) {
                    275:                                fields->size = 16;
                    276:                                fields->used = 0;
                    277:                                fields->ptr = malloc(fields->size * sizeof(format_field * ));
                    278:                        } else if (fields->used == fields->size) {
                    279:                                fields->size += 16;
                    280:                                fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
                    281:                        }
                    282: 
                    283:                        /* search for the terminating command */
                    284:                        switch (format->ptr[i+1]) {
                    285:                        case '>':
                    286:                        case '<':
                    287:                                /* after the } has to be a character */
                    288:                                if (format->ptr[i+2] == '\0') {
                    289:                                        log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a format-specifier");
                    290:                                        return -1;
                    291:                                }
                    292: 
                    293: 
                    294:                                for (j = 0; fmap[j].key != '\0'; j++) {
                    295:                                        if (fmap[j].key != format->ptr[i+2]) continue;
                    296: 
                    297:                                        /* found key */
                    298: 
                    299:                                        fields->ptr[fields->used] = malloc(sizeof(format_field));
                    300:                                        fields->ptr[fields->used]->type = FIELD_FORMAT;
                    301:                                        fields->ptr[fields->used]->field = fmap[j].type;
                    302:                                        fields->ptr[fields->used]->string = NULL;
1.1.1.3 ! misho     303:                                        fields->ptr[fields->used]->opt = 0;
1.1       misho     304: 
                    305:                                        fields->used++;
                    306: 
                    307:                                        break;
                    308:                                }
                    309: 
                    310:                                if (fmap[j].key == '\0') {
                    311:                                        log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a valid format-specifier");
                    312:                                        return -1;
                    313:                                }
                    314: 
                    315:                                start = i + 3;
                    316:                                i = start - 1; /* skip the string */
                    317: 
                    318:                                break;
                    319:                        case '{':
                    320:                                /* go forward to } */
                    321: 
1.1.1.3 ! misho     322:                                for (k = i+2; k < buffer_string_length(format); k++) {
1.1       misho     323:                                        if (format->ptr[k] == '}') break;
                    324:                                }
                    325: 
1.1.1.3 ! misho     326:                                if (k == buffer_string_length(format)) {
1.1       misho     327:                                        log_error_write(srv, __FILE__, __LINE__, "s", "%{ has to be terminated by a }");
                    328:                                        return -1;
                    329:                                }
                    330: 
                    331:                                /* after the } has to be a character */
                    332:                                if (format->ptr[k+1] == '\0') {
                    333:                                        log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a format-specifier");
                    334:                                        return -1;
                    335:                                }
                    336: 
                    337:                                if (k == i + 2) {
                    338:                                        log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be contain a string");
                    339:                                        return -1;
                    340:                                }
                    341: 
                    342:                                for (j = 0; fmap[j].key != '\0'; j++) {
                    343:                                        if (fmap[j].key != format->ptr[k+1]) continue;
                    344: 
                    345:                                        /* found key */
                    346: 
                    347:                                        fields->ptr[fields->used] = malloc(sizeof(format_field));
                    348:                                        fields->ptr[fields->used]->type = FIELD_FORMAT;
                    349:                                        fields->ptr[fields->used]->field = fmap[j].type;
                    350:                                        fields->ptr[fields->used]->string = buffer_init();
1.1.1.3 ! misho     351:                                        fields->ptr[fields->used]->opt = 0;
1.1       misho     352: 
                    353:                                        buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + i + 2, k - (i + 2));
                    354: 
                    355:                                        fields->used++;
                    356: 
                    357:                                        break;
                    358:                                }
                    359: 
                    360:                                if (fmap[j].key == '\0') {
                    361:                                        log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a valid format-specifier");
                    362:                                        return -1;
                    363:                                }
                    364: 
                    365:                                start = k + 2;
                    366:                                i = start - 1; /* skip the string */
                    367: 
                    368:                                break;
                    369:                        default:
                    370:                                /* after the % has to be a character */
                    371:                                if (format->ptr[i+1] == '\0') {
                    372:                                        log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a format-specifier");
                    373:                                        return -1;
                    374:                                }
                    375: 
                    376:                                for (j = 0; fmap[j].key != '\0'; j++) {
                    377:                                        if (fmap[j].key != format->ptr[i+1]) continue;
                    378: 
                    379:                                        /* found key */
                    380: 
                    381:                                        fields->ptr[fields->used] = malloc(sizeof(format_field));
                    382:                                        fields->ptr[fields->used]->type = FIELD_FORMAT;
                    383:                                        fields->ptr[fields->used]->field = fmap[j].type;
                    384:                                        fields->ptr[fields->used]->string = NULL;
1.1.1.3 ! misho     385:                                        fields->ptr[fields->used]->opt = 0;
1.1       misho     386: 
                    387:                                        fields->used++;
                    388: 
                    389:                                        break;
                    390:                                }
                    391: 
                    392:                                if (fmap[j].key == '\0') {
                    393:                                        log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a valid format-specifier");
                    394:                                        return -1;
                    395:                                }
                    396: 
                    397:                                start = i + 2;
                    398:                                i = start - 1; /* skip the string */
                    399: 
                    400:                                break;
                    401:                        }
                    402: 
                    403:                        break;
                    404:                }
                    405:        }
                    406: 
                    407:        if (start < i) {
                    408:                /* copy the string */
                    409:                if (fields->size == 0) {
                    410:                        fields->size = 16;
                    411:                        fields->used = 0;
                    412:                        fields->ptr = malloc(fields->size * sizeof(format_field * ));
                    413:                } else if (fields->used == fields->size) {
                    414:                        fields->size += 16;
                    415:                        fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * ));
                    416:                }
                    417: 
                    418:                fields->ptr[fields->used] = malloc(sizeof(format_field));
                    419:                fields->ptr[fields->used]->type = FIELD_STRING;
                    420:                fields->ptr[fields->used]->string = buffer_init();
                    421: 
                    422:                buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start);
                    423: 
                    424:                fields->used++;
                    425:        }
                    426: 
                    427:        return 0;
                    428: }
                    429: 
                    430: FREE_FUNC(mod_accesslog_free) {
                    431:        plugin_data *p = p_d;
                    432:        size_t i;
                    433: 
                    434:        if (!p) return HANDLER_GO_ON;
                    435: 
                    436:        if (p->config_storage) {
                    437: 
                    438:                for (i = 0; i < srv->config_context->used; i++) {
                    439:                        plugin_config *s = p->config_storage[i];
                    440: 
1.1.1.3 ! misho     441:                        if (NULL == s) continue;
1.1       misho     442: 
1.1.1.3 ! misho     443:                        if (!buffer_string_is_empty(s->access_logbuffer)) {
1.1       misho     444:                                if (s->log_access_fd != -1) {
1.1.1.3 ! misho     445:                                        accesslog_write_all(srv, s->access_logfile, s->log_access_fd, CONST_BUF_LEN(s->access_logbuffer));
1.1       misho     446:                                }
                    447:                        }
                    448: 
                    449:                        if (s->log_access_fd != -1) close(s->log_access_fd);
                    450: 
                    451:                        buffer_free(s->ts_accesslog_str);
                    452:                        buffer_free(s->access_logbuffer);
                    453:                        buffer_free(s->format);
                    454:                        buffer_free(s->access_logfile);
                    455: 
                    456:                        if (s->parsed_format) {
                    457:                                size_t j;
                    458:                                for (j = 0; j < s->parsed_format->used; j++) {
                    459:                                        if (s->parsed_format->ptr[j]->string) buffer_free(s->parsed_format->ptr[j]->string);
                    460:                                        free(s->parsed_format->ptr[j]);
                    461:                                }
                    462:                                free(s->parsed_format->ptr);
                    463:                                free(s->parsed_format);
                    464:                        }
                    465: 
                    466:                        free(s);
                    467:                }
                    468: 
                    469:                free(p->config_storage);
                    470:        }
                    471: 
                    472:        if (p->syslog_logbuffer) buffer_free(p->syslog_logbuffer);
1.1.1.2   misho     473:        free(p);
1.1       misho     474: 
                    475:        return HANDLER_GO_ON;
                    476: }
                    477: 
                    478: SETDEFAULTS_FUNC(log_access_open) {
                    479:        plugin_data *p = p_d;
                    480:        size_t i = 0;
                    481: 
                    482:        config_values_t cv[] = {
                    483:                { "accesslog.filename",             NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
                    484:                { "accesslog.use-syslog",           NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
                    485:                { "accesslog.format",               NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
                    486:                { "accesslog.syslog-level",         NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },
                    487:                { NULL,                             NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
                    488:        };
                    489: 
                    490:        if (!p) return HANDLER_ERROR;
                    491: 
1.1.1.2   misho     492:        p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1       misho     493: 
                    494:        for (i = 0; i < srv->config_context->used; i++) {
1.1.1.3 ! misho     495:                data_config const* config = (data_config const*)srv->config_context->data[i];
1.1       misho     496:                plugin_config *s;
                    497: 
                    498:                s = calloc(1, sizeof(plugin_config));
                    499:                s->access_logfile = buffer_init();
                    500:                s->format = buffer_init();
                    501:                s->access_logbuffer = buffer_init();
                    502:                s->ts_accesslog_str = buffer_init();
                    503:                s->log_access_fd = -1;
                    504:                s->last_generated_accesslog_ts = 0;
                    505:                s->last_generated_accesslog_ts_ptr = &(s->last_generated_accesslog_ts);
                    506:                s->syslog_level = LOG_INFO;
                    507: 
                    508: 
                    509:                cv[0].destination = s->access_logfile;
                    510:                cv[1].destination = &(s->use_syslog);
                    511:                cv[2].destination = s->format;
                    512:                cv[3].destination = &(s->syslog_level);
                    513: 
                    514:                p->config_storage[i] = s;
                    515: 
1.1.1.3 ! misho     516:                if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
1.1       misho     517:                        return HANDLER_ERROR;
                    518:                }
                    519: 
1.1.1.3 ! misho     520:                if (i == 0 && buffer_string_is_empty(s->format)) {
1.1       misho     521:                        /* set a default logfile string */
                    522: 
                    523:                        buffer_copy_string_len(s->format, CONST_STR_LEN("%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""));
                    524:                }
                    525: 
                    526:                /* parse */
                    527: 
1.1.1.3 ! misho     528:                if (!buffer_is_empty(s->format)) {
        !           529:                        size_t j, tcount = 0;
1.1       misho     530: 
                    531:                        s->parsed_format = calloc(1, sizeof(*(s->parsed_format)));
                    532: 
                    533:                        if (-1 == accesslog_parse_format(srv, s->parsed_format, s->format)) {
                    534: 
                    535:                                log_error_write(srv, __FILE__, __LINE__, "sb",
                    536:                                                "parsing accesslog-definition failed:", s->format);
                    537: 
                    538:                                return HANDLER_ERROR;
                    539:                        }
                    540: 
1.1.1.3 ! misho     541:                        for (j = 0; j < s->parsed_format->used; ++j) {
        !           542:                                format_field * const f = s->parsed_format->ptr[j];
        !           543:                                if (FIELD_FORMAT != f->type) continue;
        !           544:                                if (FORMAT_TIMESTAMP == f->field) {
        !           545:                                        if (!buffer_string_is_empty(f->string)) {
        !           546:                                                const char *ptr = f->string->ptr;
        !           547:                                                if (0 == strncmp(ptr, "begin:", sizeof("begin:")-1)) {
        !           548:                                                        f->opt |= FORMAT_FLAG_TIME_BEGIN;
        !           549:                                                        ptr += sizeof("begin:")-1;
        !           550:                                                } else if (0 == strncmp(ptr, "end:", sizeof("end:")-1)) {
        !           551:                                                        f->opt |= FORMAT_FLAG_TIME_END;
        !           552:                                                        ptr += sizeof("end:")-1;
1.1       misho     553:                                                }
1.1.1.3 ! misho     554:                                                if      (0 == strcmp(ptr, "sec"))       f->opt |= FORMAT_FLAG_TIME_SEC;
        !           555:                                                else if (0 == strcmp(ptr, "msec"))      f->opt |= FORMAT_FLAG_TIME_MSEC;
        !           556:                                                else if (0 == strcmp(ptr, "usec"))      f->opt |= FORMAT_FLAG_TIME_USEC;
        !           557:                                                else if (0 == strcmp(ptr, "nsec"))      f->opt |= FORMAT_FLAG_TIME_NSEC;
        !           558:                                                else if (0 == strcmp(ptr, "msec_frac")) f->opt |= FORMAT_FLAG_TIME_MSEC_FRAC;
        !           559:                                                else if (0 == strcmp(ptr, "usec_frac")) f->opt |= FORMAT_FLAG_TIME_USEC_FRAC;
        !           560:                                                else if (0 == strcmp(ptr, "nsec_frac")) f->opt |= FORMAT_FLAG_TIME_NSEC_FRAC;
        !           561:                                                else if (NULL == strchr(ptr, '%')) {
1.1       misho     562:                                                        log_error_write(srv, __FILE__, __LINE__, "sb",
1.1.1.3 ! misho     563:                                                                "constant string for time format (misspelled token? or missing '%'):", s->format);
1.1       misho     564: 
                    565:                                                        return HANDLER_ERROR;
                    566:                                                }
                    567:                                        }
1.1.1.3 ! misho     568: 
        !           569:                                        /* make sure they didn't try to send the timestamp in twice
        !           570:                                         * (would invalidate s->ts_accesslog_str cache of timestamp str) */
        !           571:                                        if (!(f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END|FORMAT_FLAG_TIME_SEC)) && ++tcount > 1) {
        !           572:                                                log_error_write(srv, __FILE__, __LINE__, "sb",
        !           573:                                                        "you may not use strftime timestamp format %{}t twice in the same access log:", s->format);
        !           574: 
        !           575:                                                return HANDLER_ERROR;
        !           576:                                        }
        !           577: 
        !           578:                                        if (f->opt & FORMAT_FLAG_TIME_BEGIN) srv->srvconf.high_precision_timestamps = 1;
        !           579:                                } else if (FORMAT_TIME_USED_US == f->field) {
        !           580:                                        f->opt |= FORMAT_FLAG_TIME_USEC;
        !           581:                                        srv->srvconf.high_precision_timestamps = 1;
        !           582:                                } else if (FORMAT_TIME_USED == f->field) {
        !           583:                                        if (f->opt & ~(FORMAT_FLAG_TIME_SEC)) srv->srvconf.high_precision_timestamps = 1;
        !           584: 
        !           585:                                        if (buffer_string_is_empty(f->string)
        !           586:                                              || buffer_is_equal_string(f->string, CONST_STR_LEN("s"))
        !           587:                                              || buffer_is_equal_string(f->string, CONST_STR_LEN("sec")))  f->opt |= FORMAT_FLAG_TIME_SEC;
        !           588:                                        else if (buffer_is_equal_string(f->string, CONST_STR_LEN("ms"))
        !           589:                                              || buffer_is_equal_string(f->string, CONST_STR_LEN("msec"))) f->opt |= FORMAT_FLAG_TIME_MSEC;
        !           590:                                        else if (buffer_is_equal_string(f->string, CONST_STR_LEN("us"))
        !           591:                                              || buffer_is_equal_string(f->string, CONST_STR_LEN("usec"))) f->opt |= FORMAT_FLAG_TIME_USEC;
        !           592:                                        else if (buffer_is_equal_string(f->string, CONST_STR_LEN("ns"))
        !           593:                                              || buffer_is_equal_string(f->string, CONST_STR_LEN("nsec"))) f->opt |= FORMAT_FLAG_TIME_NSEC;
        !           594:                                        else {
        !           595:                                                log_error_write(srv, __FILE__, __LINE__, "sb",
        !           596:                                                        "invalid time unit in %{UNIT}T:", s->format);
        !           597: 
        !           598:                                                return HANDLER_ERROR;
        !           599:                                        }
        !           600:                                } else if (FORMAT_COOKIE == f->field) {
        !           601:                                        if (buffer_string_is_empty(f->string)) f->type = FIELD_STRING; /*(blank)*/
1.1       misho     602:                                }
                    603:                        }
                    604: 
                    605: #if 0
                    606:                        /* debugging */
                    607:                        for (j = 0; j < s->parsed_format->used; j++) {
                    608:                                switch (s->parsed_format->ptr[j]->type) {
                    609:                                case FIELD_FORMAT:
                    610:                                        log_error_write(srv, __FILE__, __LINE__, "ssds",
                    611:                                                        "config:", "format", s->parsed_format->ptr[j]->field,
                    612:                                                        s->parsed_format->ptr[j]->string ?
                    613:                                                        s->parsed_format->ptr[j]->string->ptr : "" );
                    614:                                        break;
                    615:                                case FIELD_STRING:
                    616:                                        log_error_write(srv, __FILE__, __LINE__, "ssbs", "config:", "string '", s->parsed_format->ptr[j]->string, "'");
                    617:                                        break;
                    618:                                default:
                    619:                                        break;
                    620:                                }
                    621:                        }
                    622: #endif
                    623:                }
                    624: 
                    625:                if (s->use_syslog) {
                    626:                        /* ignore the next checks */
                    627:                        continue;
                    628:                }
                    629: 
1.1.1.3 ! misho     630:                if (buffer_string_is_empty(s->access_logfile)) continue;
        !           631: 
        !           632:                if (srv->srvconf.preflight_check) continue;
1.1       misho     633: 
                    634:                if (-1 == (s->log_access_fd = open_logfile_or_pipe(srv, s->access_logfile->ptr)))
                    635:                        return HANDLER_ERROR;
                    636: 
                    637:        }
                    638: 
                    639:        return HANDLER_GO_ON;
                    640: }
                    641: 
                    642: SIGHUP_FUNC(log_access_cycle) {
                    643:        plugin_data *p = p_d;
                    644:        size_t i;
                    645: 
                    646:        if (!p->config_storage) return HANDLER_GO_ON;
                    647: 
                    648:        for (i = 0; i < srv->config_context->used; i++) {
                    649:                plugin_config *s = p->config_storage[i];
                    650: 
1.1.1.3 ! misho     651:                if (!buffer_string_is_empty(s->access_logbuffer)) {
1.1       misho     652:                        if (s->log_access_fd != -1) {
1.1.1.3 ! misho     653:                                accesslog_write_all(srv, s->access_logfile, s->log_access_fd, CONST_BUF_LEN(s->access_logbuffer));
1.1       misho     654:                        }
                    655: 
                    656:                        buffer_reset(s->access_logbuffer);
                    657:                }
                    658: 
1.1.1.3 ! misho     659:                if (s->use_syslog == 0
        !           660:                        && !buffer_string_is_empty(s->access_logfile)
        !           661:                        && s->access_logfile->ptr[0] != '|') {
1.1       misho     662: 
1.1.1.2   misho     663:                        if (-1 != s->log_access_fd) close(s->log_access_fd);
1.1       misho     664: 
                    665:                        if (-1 == (s->log_access_fd =
                    666:                                   open(s->access_logfile->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) {
                    667: 
                    668:                                log_error_write(srv, __FILE__, __LINE__, "ss", "cycling access-log failed:", strerror(errno));
                    669: 
                    670:                                return HANDLER_ERROR;
                    671:                        }
1.1.1.2   misho     672:                        fd_close_on_exec(s->log_access_fd);
1.1       misho     673:                }
                    674:        }
                    675: 
                    676:        return HANDLER_GO_ON;
                    677: }
                    678: 
                    679: #define PATCH(x) \
                    680:        p->conf.x = s->x;
                    681: static int mod_accesslog_patch_connection(server *srv, connection *con, plugin_data *p) {
                    682:        size_t i, j;
                    683:        plugin_config *s = p->config_storage[0];
                    684: 
                    685:        PATCH(access_logfile);
                    686:        PATCH(log_access_fd);
                    687:        PATCH(last_generated_accesslog_ts_ptr);
                    688:        PATCH(access_logbuffer);
                    689:        PATCH(ts_accesslog_str);
                    690:        PATCH(parsed_format);
                    691:        PATCH(use_syslog);
                    692:        PATCH(syslog_level);
                    693: 
                    694:        /* skip the first, the global context */
                    695:        for (i = 1; i < srv->config_context->used; i++) {
                    696:                data_config *dc = (data_config *)srv->config_context->data[i];
                    697:                s = p->config_storage[i];
                    698: 
                    699:                /* condition didn't match */
                    700:                if (!config_check_cond(srv, con, dc)) continue;
                    701: 
                    702:                /* merge config */
                    703:                for (j = 0; j < dc->value->used; j++) {
                    704:                        data_unset *du = dc->value->data[j];
                    705: 
                    706:                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.filename"))) {
                    707:                                PATCH(access_logfile);
                    708:                                PATCH(log_access_fd);
                    709:                                PATCH(access_logbuffer);
                    710:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.format"))) {
                    711:                                PATCH(parsed_format);
                    712:                                PATCH(last_generated_accesslog_ts_ptr);
                    713:                                PATCH(ts_accesslog_str);
                    714:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.use-syslog"))) {
                    715:                                PATCH(use_syslog);
                    716:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.syslog-level"))) {
                    717:                                PATCH(syslog_level);
                    718:                        }
                    719:                }
                    720:        }
                    721: 
                    722:        return 0;
                    723: }
                    724: #undef PATCH
                    725: 
                    726: REQUESTDONE_FUNC(log_access_write) {
                    727:        plugin_data *p = p_d;
                    728:        buffer *b;
                    729:        size_t j;
                    730: 
                    731:        int newts = 0;
                    732:        data_string *ds;
1.1.1.3 ! misho     733:        struct timespec ts = { 0, 0 };
1.1       misho     734: 
                    735:        mod_accesslog_patch_connection(srv, con, p);
                    736: 
                    737:        /* No output device, nothing to do */
                    738:        if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON;
                    739: 
                    740:        if (p->conf.use_syslog) {
                    741:                b = p->syslog_logbuffer;
                    742:        } else {
                    743:                b = p->conf.access_logbuffer;
                    744:        }
                    745: 
1.1.1.3 ! misho     746:        if (buffer_is_empty(b)) {
        !           747:                buffer_string_set_length(b, 0);
1.1       misho     748:        }
                    749: 
                    750:        for (j = 0; j < p->conf.parsed_format->used; j++) {
1.1.1.3 ! misho     751:                const format_field * const f = p->conf.parsed_format->ptr[j];
        !           752:                switch(f->type) {
1.1       misho     753:                case FIELD_STRING:
1.1.1.3 ! misho     754:                        buffer_append_string_buffer(b, f->string);
1.1       misho     755:                        break;
                    756:                case FIELD_FORMAT:
1.1.1.3 ! misho     757:                        switch(f->field) {
1.1       misho     758:                        case FORMAT_TIMESTAMP:
                    759: 
1.1.1.3 ! misho     760:                                if (f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END)) {
        !           761:                                        if (f->opt & FORMAT_FLAG_TIME_SEC) {
        !           762:                                                time_t t = (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) ? srv->cur_ts : con->request_start;
        !           763:                                                buffer_append_int(b, (intmax_t)t);
        !           764:                                        } else if (f->opt & (FORMAT_FLAG_TIME_MSEC|FORMAT_FLAG_TIME_USEC|FORMAT_FLAG_TIME_NSEC)) {
        !           765:                                                off_t t; /*(expected to be 64-bit since large file support enabled)*/
        !           766:                                                long ns;
        !           767:                                                if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
        !           768:                                                        if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts);
        !           769:                                                        t = (off_t)ts.tv_sec;
        !           770:                                                        ns = ts.tv_nsec;
        !           771:                                                } else {
        !           772:                                                        t = (off_t)con->request_start_hp.tv_sec;
        !           773:                                                        ns = con->request_start_hp.tv_nsec;
        !           774:                                                }
        !           775:                                                if (f->opt & FORMAT_FLAG_TIME_MSEC) {
        !           776:                                                        t *= 1000;
        !           777:                                                        t += (ns + 999999) / 1000000; /* ceil */
        !           778:                                                } else if (f->opt & FORMAT_FLAG_TIME_USEC) {
        !           779:                                                        t *= 1000000;
        !           780:                                                        t += (ns + 999) / 1000; /* ceil */
        !           781:                                                } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC)*/
        !           782:                                                        t *= 1000000000;
        !           783:                                                        t += ns;
        !           784:                                                }
        !           785:                                                buffer_append_int(b, (intmax_t)t);
        !           786:                                        } else { /*(FORMAT_FLAG_TIME_MSEC_FRAC|FORMAT_FLAG_TIME_USEC_FRAC|FORMAT_FLAG_TIME_NSEC_FRAC)*/
        !           787:                                                long ns;
        !           788:                                                char *ptr;
        !           789:                                                if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
        !           790:                                                        if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts);
        !           791:                                                        ns = ts.tv_nsec;
        !           792:                                                } else {
        !           793:                                                        ns = con->request_start_hp.tv_nsec;
        !           794:                                                }
        !           795:                                                /*assert(t < 1000000000);*/
        !           796:                                                if (f->opt & FORMAT_FLAG_TIME_MSEC_FRAC) {
        !           797:                                                        ns +=  999999; /* ceil */
        !           798:                                                        ns /= 1000000;
        !           799:                                                        buffer_append_string_len(b, CONST_STR_LEN("000"));
        !           800:                                                } else if (f->opt & FORMAT_FLAG_TIME_USEC_FRAC) {
        !           801:                                                        ns +=  999; /* ceil */
        !           802:                                                        ns /= 1000;
        !           803:                                                        buffer_append_string_len(b, CONST_STR_LEN("000000"));
        !           804:                                                } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC_FRAC)*/
        !           805:                                                        buffer_append_string_len(b, CONST_STR_LEN("000000000"));
        !           806:                                                }
        !           807:                                                for (ptr = b->ptr + buffer_string_length(b); ns > 0; ns /= 10)
        !           808:                                                        *--ptr = (ns % 10) + '0';
        !           809:                                        }
        !           810:                                } else if (!(f->opt & FORMAT_FLAG_TIME_BEGIN) && srv->cur_ts == *(p->conf.last_generated_accesslog_ts_ptr)) {
        !           811:                                        buffer_append_string_buffer(b, p->conf.ts_accesslog_str);
        !           812:                                } else {
        !           813:                                        /* cache the generated timestamp (only if ! FORMAT_FLAG_TIME_BEGIN) */
        !           814:                                        struct tm *tmptr;
        !           815:                                        time_t t;
        !           816:                                      #if defined(HAVE_STRUCT_TM_GMTOFF)
        !           817:                                      # ifdef HAVE_LOCALTIME_R
1.1       misho     818:                                        struct tm tm;
1.1.1.3 ! misho     819:                                      # endif /* HAVE_LOCALTIME_R */
        !           820:                                      #else /* HAVE_STRUCT_TM_GMTOFF */
        !           821:                                      # ifdef HAVE_GMTIME_R
        !           822:                                        struct tm tm;
        !           823:                                      # endif /* HAVE_GMTIME_R */
        !           824:                                      #endif /* HAVE_STRUCT_TM_GMTOFF */
1.1       misho     825: 
1.1.1.3 ! misho     826:                                        if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
        !           827:                                                t = *(p->conf.last_generated_accesslog_ts_ptr) = srv->cur_ts;
        !           828:                                                newts = 1;
        !           829:                                        } else {
        !           830:                                                t = con->request_start;
        !           831:                                        }
1.1       misho     832: 
1.1.1.3 ! misho     833:                                      #if defined(HAVE_STRUCT_TM_GMTOFF)
        !           834:                                      # ifdef HAVE_LOCALTIME_R
        !           835:                                        tmptr = localtime_r(&t, &tm);
        !           836:                                      # else /* HAVE_LOCALTIME_R */
        !           837:                                        tmptr = localtime(&t);
        !           838:                                      # endif /* HAVE_LOCALTIME_R */
        !           839:                                      #else /* HAVE_STRUCT_TM_GMTOFF */
        !           840:                                      # ifdef HAVE_GMTIME_R
        !           841:                                        tmptr = gmtime_r(&t, &tm);
        !           842:                                      # else /* HAVE_GMTIME_R */
        !           843:                                        tmptr = gmtime(&t);
        !           844:                                      # endif /* HAVE_GMTIME_R */
        !           845:                                      #endif /* HAVE_STRUCT_TM_GMTOFF */
        !           846: 
        !           847:                                        buffer_string_prepare_copy(p->conf.ts_accesslog_str, 255);
        !           848: 
        !           849:                                        if (buffer_string_is_empty(f->string)) {
        !           850:                                              #if defined(HAVE_STRUCT_TM_GMTOFF)
        !           851:                                                long scd, hrs, min;
        !           852:                                                buffer_append_strftime(p->conf.ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S ", tmptr);
        !           853:                                                buffer_append_string_len(p->conf.ts_accesslog_str, tmptr->tm_gmtoff >= 0 ? "+" : "-", 1);
1.1       misho     854: 
1.1.1.3 ! misho     855:                                                scd = labs(tmptr->tm_gmtoff);
1.1       misho     856:                                                hrs = scd / 3600;
                    857:                                                min = (scd % 3600) / 60;
                    858: 
                    859:                                                /* hours */
                    860:                                                if (hrs < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
1.1.1.3 ! misho     861:                                                buffer_append_int(p->conf.ts_accesslog_str, hrs);
1.1       misho     862: 
                    863:                                                if (min < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0"));
1.1.1.3 ! misho     864:                                                buffer_append_int(p->conf.ts_accesslog_str, min);
1.1       misho     865:                                                buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("]"));
1.1.1.3 ! misho     866:                                              #else
        !           867:                                                buffer_append_strftime(p->conf.ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S +0000]", tmptr);
        !           868:                                              #endif /* HAVE_STRUCT_TM_GMTOFF */
        !           869:                                        } else {
        !           870:                                                buffer_append_strftime(p->conf.ts_accesslog_str, f->string->ptr, tmptr);
1.1       misho     871:                                        }
                    872: 
1.1.1.3 ! misho     873:                                        buffer_append_string_buffer(b, p->conf.ts_accesslog_str);
        !           874:                                }
        !           875:                                break;
        !           876:                        case FORMAT_TIME_USED:
        !           877:                        case FORMAT_TIME_USED_US:
        !           878:                                if (f->opt & FORMAT_FLAG_TIME_SEC) {
        !           879:                                        buffer_append_int(b, srv->cur_ts - con->request_start);
        !           880:                                } else {
        !           881:                                        const struct timespec * const bs = &con->request_start_hp;
        !           882:                                        off_t tdiff; /*(expected to be 64-bit since large file support enabled)*/
        !           883:                                        if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts);
        !           884:                                        tdiff = (off_t)(ts.tv_sec - bs->tv_sec)*1000000000 + (ts.tv_nsec - bs->tv_nsec);
        !           885:                                        if (tdiff <= 0) {
        !           886:                                                /* sanity check for time moving backwards
        !           887:                                                 * (daylight savings adjustment or leap seconds or ?) */
        !           888:                                                tdiff  = -1;
        !           889:                                        } else if (f->opt & FORMAT_FLAG_TIME_MSEC) {
        !           890:                                                tdiff +=  999999; /* ceil */
        !           891:                                                tdiff /= 1000000;
        !           892:                                        } else if (f->opt & FORMAT_FLAG_TIME_USEC) {
        !           893:                                                tdiff +=  999; /* ceil */
        !           894:                                                tdiff /= 1000;
        !           895:                                        } /* else (f->opt & FORMAT_FLAG_TIME_NSEC) */
        !           896:                                        buffer_append_int(b, (intmax_t)tdiff);
1.1       misho     897:                                }
                    898:                                break;
1.1.1.3 ! misho     899:                        case FORMAT_REMOTE_ADDR:
1.1       misho     900:                        case FORMAT_REMOTE_HOST:
1.1.1.3 ! misho     901:                                buffer_append_string_buffer(b, con->dst_addr_buf);
1.1       misho     902:                                break;
                    903:                        case FORMAT_REMOTE_IDENT:
                    904:                                /* ident */
                    905:                                buffer_append_string_len(b, CONST_STR_LEN("-"));
                    906:                                break;
                    907:                        case FORMAT_REMOTE_USER:
1.1.1.3 ! misho     908:                                if (NULL != (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER")) && !buffer_string_is_empty(ds->value)) {
1.1       misho     909:                                        accesslog_append_escaped(b, ds->value);
                    910:                                } else {
                    911:                                        buffer_append_string_len(b, CONST_STR_LEN("-"));
                    912:                                }
                    913:                                break;
                    914:                        case FORMAT_REQUEST_LINE:
1.1.1.3 ! misho     915:                                if (!buffer_string_is_empty(con->request.request_line)) {
1.1       misho     916:                                        accesslog_append_escaped(b, con->request.request_line);
                    917:                                }
                    918:                                break;
                    919:                        case FORMAT_STATUS:
1.1.1.3 ! misho     920:                                buffer_append_int(b, con->http_status);
1.1       misho     921:                                break;
                    922: 
                    923:                        case FORMAT_BYTES_OUT_NO_HEADER:
                    924:                                if (con->bytes_written > 0) {
1.1.1.3 ! misho     925:                                        buffer_append_int(b,
1.1       misho     926:                                                            con->bytes_written - con->bytes_header <= 0 ? 0 : con->bytes_written - con->bytes_header);
                    927:                                } else {
                    928:                                        buffer_append_string_len(b, CONST_STR_LEN("-"));
                    929:                                }
                    930:                                break;
                    931:                        case FORMAT_HEADER:
1.1.1.3 ! misho     932:                                if (NULL != (ds = (data_string *)array_get_element(con->request.headers, f->string->ptr))) {
1.1       misho     933:                                        accesslog_append_escaped(b, ds->value);
                    934:                                } else {
                    935:                                        buffer_append_string_len(b, CONST_STR_LEN("-"));
                    936:                                }
                    937:                                break;
                    938:                        case FORMAT_RESPONSE_HEADER:
1.1.1.3 ! misho     939:                                if (NULL != (ds = (data_string *)array_get_element(con->response.headers, f->string->ptr))) {
1.1       misho     940:                                        accesslog_append_escaped(b, ds->value);
                    941:                                } else {
                    942:                                        buffer_append_string_len(b, CONST_STR_LEN("-"));
                    943:                                }
                    944:                                break;
                    945:                        case FORMAT_ENV:
1.1.1.3 ! misho     946:                                if (NULL != (ds = (data_string *)array_get_element(con->environment, f->string->ptr))) {
1.1       misho     947:                                        accesslog_append_escaped(b, ds->value);
                    948:                                } else {
                    949:                                        buffer_append_string_len(b, CONST_STR_LEN("-"));
                    950:                                }
                    951:                                break;
                    952:                        case FORMAT_FILENAME:
1.1.1.3 ! misho     953:                                if (!buffer_string_is_empty(con->physical.path)) {
1.1       misho     954:                                        buffer_append_string_buffer(b, con->physical.path);
                    955:                                } else {
                    956:                                        buffer_append_string_len(b, CONST_STR_LEN("-"));
                    957:                                }
                    958:                                break;
                    959:                        case FORMAT_BYTES_OUT:
                    960:                                if (con->bytes_written > 0) {
1.1.1.3 ! misho     961:                                        buffer_append_int(b, con->bytes_written);
1.1       misho     962:                                } else {
                    963:                                        buffer_append_string_len(b, CONST_STR_LEN("-"));
                    964:                                }
                    965:                                break;
                    966:                        case FORMAT_BYTES_IN:
                    967:                                if (con->bytes_read > 0) {
1.1.1.3 ! misho     968:                                        buffer_append_int(b, con->bytes_read);
1.1       misho     969:                                } else {
                    970:                                        buffer_append_string_len(b, CONST_STR_LEN("-"));
                    971:                                }
                    972:                                break;
                    973:                        case FORMAT_SERVER_NAME:
1.1.1.3 ! misho     974:                                if (!buffer_string_is_empty(con->server_name)) {
1.1       misho     975:                                        buffer_append_string_buffer(b, con->server_name);
                    976:                                } else {
                    977:                                        buffer_append_string_len(b, CONST_STR_LEN("-"));
                    978:                                }
                    979:                                break;
                    980:                        case FORMAT_HTTP_HOST:
1.1.1.3 ! misho     981:                                if (!buffer_string_is_empty(con->uri.authority)) {
1.1       misho     982:                                        accesslog_append_escaped(b, con->uri.authority);
                    983:                                } else {
                    984:                                        buffer_append_string_len(b, CONST_STR_LEN("-"));
                    985:                                }
                    986:                                break;
                    987:                        case FORMAT_REQUEST_PROTOCOL:
                    988:                                buffer_append_string_len(b,
1.1.1.3 ! misho     989:                                        con->request.http_version == HTTP_VERSION_1_1 ? "HTTP/1.1" : "HTTP/1.0", 8);
1.1       misho     990:                                break;
                    991:                        case FORMAT_REQUEST_METHOD:
                    992:                                buffer_append_string(b, get_http_method_name(con->request.http_method));
                    993:                                break;
                    994:                        case FORMAT_PERCENT:
                    995:                                buffer_append_string_len(b, CONST_STR_LEN("%"));
                    996:                                break;
1.1.1.3 ! misho     997:                        case FORMAT_LOCAL_ADDR:
        !           998:                                {
        !           999:                                        /* (perf: not using getsockname() and inet_ntop_cache_get_ip())
        !          1000:                                         * (still useful if admin has configured explicit listen IPs) */
        !          1001:                                        const char *colon;
        !          1002:                                        buffer *srvtoken = con->srv_socket->srv_token;
        !          1003:                                        if (srvtoken->ptr[0] == '[') {
        !          1004:                                                colon = strstr(srvtoken->ptr, "]:");
        !          1005:                                        } else {
        !          1006:                                                colon = strchr(srvtoken->ptr, ':');
        !          1007:                                        }
        !          1008:                                        if (colon) {
        !          1009:                                                buffer_append_string_len(b, srvtoken->ptr, (size_t)(colon - srvtoken->ptr));
        !          1010:                                        } else {
        !          1011:                                                buffer_append_string_buffer(b, srvtoken);
        !          1012:                                        }
        !          1013:                                }
        !          1014:                                break;
1.1       misho    1015:                        case FORMAT_SERVER_PORT:
                   1016:                                {
                   1017:                                        const char *colon;
                   1018:                                        buffer *srvtoken = ((server_socket*)(con->srv_socket))->srv_token;
                   1019:                                        if (srvtoken->ptr[0] == '[') {
                   1020:                                                colon = strstr(srvtoken->ptr, "]:");
                   1021:                                        } else {
                   1022:                                                colon = strchr(srvtoken->ptr, ':');
                   1023:                                        }
                   1024:                                        if (colon) {
                   1025:                                                buffer_append_string(b, colon+1);
                   1026:                                        } else {
1.1.1.3 ! misho    1027:                                                buffer_append_int(b, srv->srvconf.port);
1.1       misho    1028:                                        }
                   1029:                                }
                   1030:                                break;
                   1031:                        case FORMAT_QUERY_STRING:
                   1032:                                accesslog_append_escaped(b, con->uri.query);
                   1033:                                break;
                   1034:                        case FORMAT_URL:
                   1035:                                accesslog_append_escaped(b, con->uri.path_raw);
                   1036:                                break;
                   1037:                        case FORMAT_CONNECTION_STATUS:
1.1.1.3 ! misho    1038:                                if (con->state == CON_STATE_RESPONSE_END) {
        !          1039:                                        if (0 == con->keep_alive) {
        !          1040:                                                buffer_append_string_len(b, CONST_STR_LEN("-"));
        !          1041:                                        } else {
        !          1042:                                                buffer_append_string_len(b, CONST_STR_LEN("+"));
        !          1043:                                        }
        !          1044:                                } else { /* CON_STATE_ERROR */
        !          1045:                                        buffer_append_string_len(b, CONST_STR_LEN("X"));
        !          1046:                                }
        !          1047:                                break;
        !          1048:                        case FORMAT_KEEPALIVE_COUNT:
        !          1049:                                if (con->request_count > 1) {
        !          1050:                                        buffer_append_int(b, (intmax_t)(con->request_count-1));
        !          1051:                                } else {
        !          1052:                                        buffer_append_string_len(b, CONST_STR_LEN("0"));
        !          1053:                                }
        !          1054:                                break;
        !          1055:                        case FORMAT_COOKIE:
        !          1056:                                if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) {
        !          1057:                                        char *str = ds->value->ptr;
        !          1058:                                        size_t len = buffer_string_length(f->string);
        !          1059:                                        do {
        !          1060:                                                while (*str == ' ' || *str == '\t') ++str;
        !          1061:                                                if (0 == strncmp(str, f->string->ptr, len) && str[len] == '=') {
        !          1062:                                                        char *v = str+len+1;
        !          1063:                                                        buffer *bstr;
        !          1064:                                                        for (str = v; *str != '\0' && *str != ';'; ++str) ;
        !          1065:                                                        if (str == v) break;
        !          1066:                                                        do { --str; } while (str > v && (*str == ' ' || *str == '\t'));
        !          1067:                                                        bstr = buffer_init();
        !          1068:                                                        buffer_copy_string_len(bstr, v, str - v + 1);
        !          1069:                                                        accesslog_append_escaped(b, bstr);
        !          1070:                                                        buffer_free(bstr);
        !          1071:                                                        break;
        !          1072:                                                } else {
        !          1073:                                                        do { ++str; } while (*str != ' ' && *str != '\t' && *str != '\0');
        !          1074:                                                }
        !          1075:                                                while (*str == ' ' || *str == '\t') ++str;
        !          1076:                                        } while (*str++ == ';');
1.1       misho    1077:                                }
                   1078:                                break;
                   1079:                        default:
                   1080:                                break;
                   1081:                        }
                   1082:                        break;
                   1083:                default:
                   1084:                        break;
                   1085:                }
                   1086:        }
                   1087: 
1.1.1.3 ! misho    1088:        if (p->conf.use_syslog) { /* syslog doesn't cache */
        !          1089: #ifdef HAVE_SYSLOG_H
        !          1090:                if (!buffer_string_is_empty(b)) {
        !          1091:                        /*(syslog appends a \n on its own)*/
        !          1092:                        syslog(p->conf.syslog_level, "%s", b->ptr);
        !          1093:                        buffer_reset(b);
        !          1094:                }
        !          1095: #endif
        !          1096:                return HANDLER_GO_ON;
        !          1097:        }
        !          1098: 
1.1       misho    1099:        buffer_append_string_len(b, CONST_STR_LEN("\n"));
                   1100: 
1.1.1.3 ! misho    1101:        if ((!buffer_string_is_empty(p->conf.access_logfile) && p->conf.access_logfile->ptr[0] == '|') || /* pipes don't cache */
1.1       misho    1102:            newts ||
1.1.1.3 ! misho    1103:            buffer_string_length(b) >= BUFFER_MAX_REUSE_SIZE) {
        !          1104:                if (p->conf.log_access_fd >= 0) {
        !          1105:                        accesslog_write_all(srv, p->conf.access_logfile, p->conf.log_access_fd, CONST_BUF_LEN(b));
1.1       misho    1106:                }
                   1107:                buffer_reset(b);
                   1108:        }
                   1109: 
                   1110:        return HANDLER_GO_ON;
                   1111: }
                   1112: 
                   1113: 
                   1114: int mod_accesslog_plugin_init(plugin *p);
                   1115: int mod_accesslog_plugin_init(plugin *p) {
                   1116:        p->version     = LIGHTTPD_VERSION_ID;
                   1117:        p->name        = buffer_init_string("accesslog");
                   1118: 
                   1119:        p->init        = mod_accesslog_init;
                   1120:        p->set_defaults= log_access_open;
                   1121:        p->cleanup     = mod_accesslog_free;
                   1122: 
                   1123:        p->handle_request_done  = log_access_write;
                   1124:        p->handle_sighup        = log_access_cycle;
                   1125: 
                   1126:        p->data        = NULL;
                   1127: 
                   1128:        return 0;
                   1129: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>