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>