Annotation of embedaddon/libpdel/sys/logfile.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (c) 2001-2002 Packet Design, LLC.
                      4:  * All rights reserved.
                      5:  * 
                      6:  * Subject to the following obligations and disclaimer of warranty,
                      7:  * use and redistribution of this software, in source or object code
                      8:  * forms, with or without modifications are expressly permitted by
                      9:  * Packet Design; provided, however, that:
                     10:  * 
                     11:  *    (i)  Any and all reproductions of the source or object code
                     12:  *         must include the copyright notice above and the following
                     13:  *         disclaimer of warranties; and
                     14:  *    (ii) No rights are granted, in any manner or form, to use
                     15:  *         Packet Design trademarks, including the mark "PACKET DESIGN"
                     16:  *         on advertising, endorsements, or otherwise except as such
                     17:  *         appears in the above copyright notice or in the software.
                     18:  * 
                     19:  * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
                     20:  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
                     21:  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
                     22:  * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
                     23:  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
                     24:  * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
                     25:  * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
                     26:  * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
                     27:  * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
                     28:  * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
                     29:  * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
                     30:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
                     31:  * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
                     32:  * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
                     33:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     34:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
                     35:  * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
                     36:  * THE POSSIBILITY OF SUCH DAMAGE.
                     37:  *
                     38:  * Author: Archie Cobbs <archie@freebsd.org>
                     39:  */
                     40: 
                     41: #include <sys/types.h>
                     42: #include <sys/param.h>
                     43: #include <sys/stat.h>
                     44: #include <sys/mman.h>
                     45: #include <netinet/in.h>
                     46: 
                     47: #include <stdio.h>
                     48: #include <stdlib.h>
                     49: #include <stdarg.h>
                     50: #include <syslog.h>
                     51: #include <unistd.h>
                     52: #include <errno.h>
                     53: #include <assert.h>
                     54: #include <string.h>
                     55: #include <pthread.h>
                     56: #include <fcntl.h>
                     57: 
                     58: #include "structs/structs.h"
                     59: #include "structs/type/array.h"
                     60: #include "sys/logfile.h"
                     61: #include "sys/alog.h"
                     62: #include "util/typed_mem.h"
                     63: 
                     64: #define LOGFILE_MEM_TYPE       "logfile"
                     65: #define LOGFILE_MAGIC          0x476ea198
                     66: 
                     67: #define MAX_ENTRIES            (1 << 20)
                     68: #define MAX_DATA               (1 << 24)
                     69: 
                     70: /* Structure passed back to client */
                     71: struct logfile {
                     72:        struct loghead  *head;                  /* mmap'd file region */
                     73:        pthread_mutex_t mutex;                  /* mutex lock */
                     74:        u_int32_t       headlen;                /* length of head + entries */
                     75:        int             fd;                     /* file descriptor for file */
                     76: };
                     77: 
                     78: struct logent {
                     79:        u_int32_t       offset;
                     80:        u_int32_t       length;
                     81: };
                     82: 
                     83: /* Initial part of a log file; all fields in host order */
                     84: struct loghead {
                     85:        u_int32_t       magic;                  /* magic number */
                     86:        u_int32_t       maxent;                 /* max # entries */
                     87:        u_int32_t       maxdata;                /* max data area length */
                     88:        u_int32_t       num;                    /* number of valid entries */
                     89:        u_int32_t       next;                   /* next entry index */
                     90:        struct logent   ents[0];                /* maxent entries */
                     91: };
                     92: 
                     93: /*
                     94:  * Open/create a new logfile.
                     95:  */
                     96: struct logfile *
                     97: logfile_open(const char *path, int flags, u_int32_t maxent, u_int32_t maxdata)
                     98: {
                     99:        struct logfile *lf;
                    100:        struct loghead head;
                    101:        int initialize;
                    102:        int esave;
                    103: 
                    104:        /* Get and sanity check flags */
                    105:        switch (flags) {
                    106:        case 0:
                    107: #ifdef O_SHLOCK
                    108:        case O_SHLOCK:
                    109:        case O_EXLOCK:
                    110: #endif
                    111:                break;
                    112:        default:
                    113:                errno = EINVAL;
                    114:                return (NULL);
                    115:        }
                    116: #ifdef O_SHLOCK
                    117:        if ((flags & (O_SHLOCK|O_EXLOCK)) != 0)
                    118:                flags |= O_NONBLOCK;
                    119: #endif
                    120: 
                    121:        /* Create object and open file */
                    122:        if ((lf = MALLOC(LOGFILE_MEM_TYPE, sizeof(*lf))) == NULL)
                    123:                return (NULL);
                    124:        memset(lf, 0, sizeof(*lf));
                    125:        if (path != NULL) {
                    126:                if ((lf->fd = open(path, O_CREAT|O_RDWR|flags, 0644)) == -1)
                    127:                        goto fail;
                    128:                (void)fcntl(lf->fd, F_SETFD, 1);
                    129:        } else
                    130:                lf->fd = -1;
                    131: 
                    132:        /* See if file already existed */
                    133:        if (lf->fd != -1) {
                    134:                struct stat sb;
                    135: 
                    136:                if (fstat(lf->fd, &sb) == -1)
                    137:                        goto fail;
                    138:                if (!(initialize = (sb.st_size == 0))) {
                    139:                        int r;
                    140: 
                    141:                        if ((r = read(lf->fd,
                    142:                            &head, sizeof(head))) != sizeof(head)) {
                    143:                                if (r != -1)
                    144:                                        errno = EINVAL;
                    145:                                goto fail;
                    146:                        }
                    147:                        if (head.magic != LOGFILE_MAGIC) {
                    148:                                errno = EINVAL;
                    149:                                goto fail;
                    150:                        }
                    151:                        maxdata = head.maxdata;
                    152:                        maxent = head.maxent;
                    153:                }
                    154:        } else
                    155:                initialize = 1;
                    156: 
                    157:        /* Sanity check parameters */
                    158:        if (maxent == 0 || maxdata == 0
                    159:            || maxent > MAX_ENTRIES || maxdata > MAX_DATA) {
                    160:                errno = EINVAL;
                    161:                goto fail;
                    162:        }
                    163: 
                    164:        /* Compute size of header */
                    165:        lf->headlen = sizeof(*lf->head) + (maxent * sizeof(*lf->head->ents));
                    166: 
                    167:        /* Set file length */
                    168:        if (lf->fd != -1 && ftruncate(lf->fd, lf->headlen + maxdata) == -1)
                    169:                goto fail;
                    170: 
                    171:        /* Memory map file */
                    172:        if ((lf->head = mmap(NULL, lf->headlen + maxdata, PROT_READ|PROT_WRITE,
                    173:            path == NULL ? MAP_ANON : MAP_SHARED, lf->fd, 0)) == MAP_FAILED)
                    174:                goto fail;
                    175: 
                    176:        /* For new file, write header and initialize entries */
                    177:        if (initialize) {
                    178:                lf->head->magic = LOGFILE_MAGIC;
                    179:                lf->head->maxdata = maxdata;
                    180:                lf->head->maxent = maxent;
                    181:                lf->head->num = 0;
                    182:                lf->head->next = 0;
                    183:                memset(lf->head->ents, 0, maxent * sizeof(*lf->head->ents));
                    184:                (void)msync(lf->head, 0, MS_ASYNC);
                    185:        }
                    186: 
                    187:        /* Sanitize header fields */
                    188:        if (lf->head->num > lf->head->maxent)
                    189:                lf->head->num = lf->head->maxent;
                    190:        lf->head->next %= lf->head->maxent;
                    191: 
                    192:        /* Initialize mutex */
                    193:        if ((errno = pthread_mutex_init(&lf->mutex, NULL)) != 0)
                    194:                goto fail;
                    195: 
                    196:        /* Done */
                    197:        return (lf);
                    198: 
                    199: fail:
                    200:        esave = errno;
                    201:        if (lf->fd != -1)
                    202:                (void)close(lf->fd);
                    203:        if (lf->head != NULL)
                    204:                (void)munmap(lf->head, lf->headlen + maxdata);
                    205:        FREE(LOGFILE_MEM_TYPE, lf);
                    206:        errno = esave;
                    207:        return (NULL);
                    208: }
                    209: 
                    210: /*
                    211:  * Close a logfile.
                    212:  */
                    213: void
                    214: logfile_close(struct logfile **lfp)
                    215: {
                    216:        struct logfile *const lf = *lfp;
                    217: 
                    218:        /* Check for NULL */
                    219:        if (lf == NULL)
                    220:                return;
                    221:        *lfp = NULL;
                    222: 
                    223:        /* Close up shop */
                    224:        (void)msync(lf->head, 0, MS_SYNC);
                    225:        (void)munmap(lf->head, lf->headlen + lf->head->maxdata);
                    226:        if (lf->fd != -1)
                    227:                (void)close(lf->fd);
                    228:        pthread_mutex_destroy(&lf->mutex);
                    229:        FREE(LOGFILE_MEM_TYPE, lf);
                    230: }
                    231: 
                    232: /*
                    233:  * Get the number of valid entries in a logfile.
                    234:  */
                    235: u_int32_t
                    236: logfile_num_entries(struct logfile *lf)
                    237: {
                    238:        u_int32_t num;
                    239:        int r;
                    240: 
                    241:        r = pthread_mutex_lock(&lf->mutex);
                    242:        assert(r == 0);
                    243:        num = lf->head->num;
                    244:        r = pthread_mutex_unlock(&lf->mutex);
                    245:        assert(r == 0);
                    246:        return (num);
                    247: }
                    248: 
                    249: /*
                    250:  * Trim the number of stored entries.
                    251:  */
                    252: void
                    253: logfile_trim(struct logfile *lf, int num)
                    254: {
                    255:        int r;
                    256: 
                    257:        r = pthread_mutex_lock(&lf->mutex);
                    258:        assert(r == 0);
                    259:        if (lf->head->num > num)
                    260:                lf->head->num = num;
                    261:        r = pthread_mutex_unlock(&lf->mutex);
                    262:        assert(r == 0);
                    263: }
                    264: 
                    265: /*
                    266:  * Retrieve an entry.
                    267:  */
                    268: const void *
                    269: logfile_get(struct logfile *lf, int which, int *lenp)
                    270: {
                    271:        struct loghead *const head = lf->head;
                    272:        struct logent *ent;
                    273:        const void *rtn;
                    274:        int r;
                    275: 
                    276:        /* Lock logfile */
                    277:        r = pthread_mutex_lock(&lf->mutex);
                    278:        assert(r == 0);
                    279: 
                    280:        /* Find entry */
                    281:        if (which >= 0 || which < -head->num) {
                    282:                r = pthread_mutex_unlock(&lf->mutex);
                    283:                assert(r == 0);
                    284:                errno = ENOENT;
                    285:                return (NULL);
                    286:        }
                    287:        ent = &head->ents[(head->next + head->maxent + which) % head->maxent];
                    288: 
                    289:        /* Sanity check it */
                    290:        if (ent->offset > head->maxdata
                    291:            || ent->length > head->maxdata
                    292:            || ent->offset + ent->length > head->maxdata) {
                    293:                r = pthread_mutex_unlock(&lf->mutex);
                    294:                assert(r == 0);
                    295:                errno = EINVAL;
                    296:                return (NULL);
                    297:        }
                    298: 
                    299:        /* Get data and length */
                    300:        if (lenp != NULL)
                    301:                *lenp = ent->length;
                    302:        rtn = (u_char *)lf->head + lf->headlen + ent->offset;
                    303: 
                    304:        /* Unlock logfile */
                    305:        r = pthread_mutex_unlock(&lf->mutex);
                    306:        assert(r == 0);
                    307: 
                    308:        /* Done */
                    309:        return (rtn);
                    310: }
                    311: 
                    312: /*
                    313:  * Put an entry.
                    314:  */
                    315: int
                    316: logfile_put(struct logfile *lf, const void *data, int len)
                    317: {
                    318:        struct loghead *const head = lf->head;
                    319:        struct logent *ent;
                    320:        u_int32_t start;
                    321:        int wrap = 0;
                    322:        int r;
                    323: 
                    324:        if (len < 0) {
                    325:                errno = EINVAL;
                    326:                return (-1);
                    327:        }
                    328:        if (len > head->maxdata) {
                    329:                errno = EMSGSIZE;
                    330:                return (-1);
                    331:        }
                    332: 
                    333:        /* Lock logfile */
                    334:        r = pthread_mutex_lock(&lf->mutex);
                    335:        assert(r == 0);
                    336: 
                    337:        /* Figure out where this entry's data will go */
                    338:        if (head->num > 0) {
                    339:                ent = &head->ents[(head->next
                    340:                    + head->maxent - 1) % head->maxent];
                    341:                start = ALIGN(ent->offset + ent->length);
                    342:                if (start + len > head->maxdata) {      /* won't fit, wrap it */
                    343:                        wrap = start;   /* point where we were forced to wrap */
                    344:                        start = 0;
                    345:                }
                    346:        } else {
                    347:                head->next = 0;
                    348:                start = 0;
                    349:        }
                    350: 
                    351:        /* Remove all entries whose data overlaps the new guy's data */
                    352:        for ( ; head->num > 0; head->num--) {
                    353:                ent = &head->ents[(head->next
                    354:                    + head->maxent - head->num) % head->maxent];
                    355:                if (wrap != 0) {        /* clear out end region we skipped */
                    356:                        if (ent->offset >= wrap)
                    357:                                continue;
                    358:                        wrap = 0;
                    359:                }
                    360:                if (ent->offset + ent->length <= start
                    361:                    || ent->offset >= start + len)
                    362:                        break;
                    363:        }
                    364: 
                    365:        /* Save entry */
                    366:        ent = &head->ents[head->next];
                    367:        ent->offset = start;
                    368:        ent->length = len;
                    369:        memcpy((u_char *)lf->head + lf->headlen + ent->offset, data, len);
                    370:        if (head->num < head->maxent)
                    371:                head->num++;
                    372:        head->next = (head->next + 1) % head->maxent;
                    373: 
                    374:        /* Unlock logfile */
                    375:        r = pthread_mutex_unlock(&lf->mutex);
                    376:        assert(r == 0);
                    377: 
                    378:        /* Done */
                    379:        return (0);
                    380: }
                    381: 
                    382: /*
                    383:  * Sync logfile to disk.
                    384:  */
                    385: void
                    386: logfile_sync(struct logfile *lf)
                    387: {
                    388:        int r;
                    389: 
                    390:        r = pthread_mutex_lock(&lf->mutex);
                    391:        assert(r == 0);
                    392:        (void)msync(lf->head, 0, MS_SYNC);
                    393:        r = pthread_mutex_unlock(&lf->mutex);
                    394:        assert(r == 0);
                    395: }
                    396: 
                    397: #ifdef LOGFILE_TEST
                    398: 
                    399: #include <err.h>
                    400: 
                    401: int
                    402: main(int ac, char **av)
                    403: {
                    404:        const time_t now = time(NULL);
                    405:        struct logfile *lf;
                    406:        int maxent = 0;
                    407:        int maxdata = 0;
                    408:        int readonly = 0;
                    409:        int alog_ents = 0;
                    410:        long seed = 0;
                    411:        int num = -1;
                    412:        char *path;
                    413:        int i;
                    414:        int ch;
                    415: 
                    416:        srandomdev();
                    417: 
                    418:        while ((ch = getopt(ac, av, "s:n:ra")) != -1) {
                    419:                switch (ch) {
                    420:                case 's':
                    421:                        seed = atol(optarg);
                    422:                        break;
                    423:                case 'n':
                    424:                        num = atol(optarg);
                    425:                        break;
                    426:                case 'a':
                    427:                        alog_ents = 1;
                    428:                        break;
                    429:                case 'r':
                    430:                        readonly = 1;
                    431:                        break;
                    432:                default:
                    433:                        goto usage;
                    434:                }
                    435:        }
                    436:        ac -= optind;
                    437:        av += optind;
                    438: 
                    439:        /* Sanity */
                    440:        if (!readonly)
                    441:                alog_ents = 0;
                    442: 
                    443:        if (!readonly && seed == 0) {
                    444:                seed = random();
                    445:                printf("Seed is %ld.\n", seed);
                    446:        }
                    447:        srandom(seed);
                    448: 
                    449:        switch (ac) {
                    450:        case 3:
                    451:                maxent = atoi(av[1]);
                    452:                maxdata = atoi(av[2]);
                    453:                /* fall through */
                    454:        case 1:
                    455:                path = av[0];
                    456:                break;
                    457:        default:
                    458: usage:         fprintf(stderr, "usage: logfile [-r] [-s seed] [-n num]"
                    459:                    " path [maxent maxdata]\n");
                    460:                exit(1);
                    461:        }
                    462: 
                    463:        /* Open log */
                    464:        if ((lf = logfile_open(strcmp(path, "-") == 0 ? NULL : path,
                    465:            maxent, maxdata)) == NULL)
                    466:                err(1, "%s", path);
                    467: 
                    468:        /* Read only? */
                    469:        if (readonly)
                    470:                goto readit;
                    471: 
                    472:        /* Write some entries into it */
                    473:        printf("Logfile \"%s\" has %d entries.\n",
                    474:            path, logfile_num_entries(lf));
                    475:        if (num == -1)
                    476:                num = random() % 64;
                    477:        printf("Writing %d entries...\n", num);
                    478:        for (i = 0; i < num; i++) {
                    479:                char buf[128];
                    480:                int nex;
                    481:                int j;
                    482: 
                    483:                snprintf(buf, sizeof(buf), "%03ld:%03d ", now % 1000, i);
                    484:                nex = random() % 32;
                    485:                for (j = 0; j < nex; j++) {
                    486:                        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
                    487:                            "%02lx", random() % 0x100);
                    488:                }
                    489:                strlcat(buf, "\n", sizeof(buf));
                    490:                if (logfile_put(lf, buf, strlen(buf) + 1) == -1)
                    491:                        warn("logfile_put: \"%s\"", buf);
                    492:        }
                    493: 
                    494: readit:
                    495:        num = logfile_num_entries(lf);
                    496:        printf("Logfile \"%s\" now has %d entries\n", path, num);
                    497:        printf("\t maxent=%u\n", lf->head->maxent);
                    498:        printf("\tmaxdata=%u\n", lf->head->maxdata);
                    499:        printf("\t   next=%u\n", lf->head->next);
                    500:        for (i = -num; i < 0; i++) {
                    501:                const void *e;
                    502:                int len;
                    503: 
                    504:                printf("%4d: ", i + num);
                    505:                if ((e = logfile_get(lf, i, &len)) == NULL) {
                    506:                        warn("logfile_get(%d)", i);
                    507:                        continue;
                    508:                }
                    509:                if (alog_ents) {
                    510:                        const struct alog_entry *const ent = e;
                    511:                        struct tm tm;
                    512:                        char tbuf[64];
                    513: 
                    514:                        strftime(tbuf, sizeof(tbuf),
                    515:                            "%b %e %T", localtime_r(&ent->when, &tm));
                    516:                        printf("%s [%d] %s\n", tbuf, ent->sev, ent->msg);
                    517:                } else
                    518:                        printf("(%2d) %s", len, (const char *)e);
                    519:        }
                    520:        printf("Closing logfile...\n");
                    521:        logfile_close(&lf);
                    522:        return (0);
                    523: }
                    524: 
                    525: #endif /* LOGFILE_TEST */
                    526: 
                    527: 

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