Annotation of embedaddon/libpdel/sys/logfile.c, revision 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>