File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / sys / logfile.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:25:53 2012 UTC (13 years, 1 month ago) by misho
Branches: libpdel, MAIN
CVS tags: v0_5_3, HEAD
libpdel

    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>