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

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

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