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>