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/socket.h>
44: #include <sys/uio.h>
45: #include <sys/queue.h>
46:
47: #include <netinet/in.h>
48:
49: #include <stdlib.h>
50: #include <stddef.h>
51: #include <stdio.h>
52: #include <syslog.h>
53: #include <assert.h>
54: #include <string.h>
55: #include <stdarg.h>
56: #include <unistd.h>
57: #include <fcntl.h>
58: #include <errno.h>
59: #include <regex.h>
60: #include <pthread.h>
61:
62: #include "structs/structs.h"
63: #include "structs/type/array.h"
64: #include "structs/type/int.h"
65: #include "structs/type/ip4.h"
66: #include "structs/type/pointer.h"
67: #include "structs/type/string.h"
68: #include "structs/type/struct.h"
69: #include "structs/type/time.h"
70: #include "sys/alog.h"
71: #include "sys/logfile.h"
72: #include "util/typed_mem.h"
73:
74: /* Memory allocation types */
75: #define MEM_TYPE "alog"
76: #define MEM_TYPE_ENTRY "alog_entry"
77: #define MEM_TYPE_HISTORY "alog_history"
78: #define MEM_TYPE_CURCHAN "alog_curchan"
79:
80: /* Last message timeout functions */
81: #define LASTMSG_TIMEOUT_INITIAL 5
82: #define LASTMSG_TIMEOUT_1(t, d) ((t) + (d) + 1 / 2)
83: #define LASTMSG_TIMEOUT_2(t, d) ((t) + (d) + 1 / 2)
84: #define LASTMSG_TIMEOUT_3(t) (t)
85:
86: #define LASTMSG_REPEATED_FMT "last message repeated %u times"
87:
88: /* Misc defs */
89: #define SYSLOG_PORT 514 /* syslogd udp port */
90: #define MAX_NAME 64 /* max identifier length */
91: #define EST_ENTRY_SIZE 100 /* estimated avg. size per entry */
92:
93: enum alog_type {
94: ALOG_NONE = 0, /* not initialized */
95: ALOG_NULL, /* log to nowhere */
96: ALOG_STDERR, /* log to stderr */
97: ALOG_SYSLOG_LOCAL, /* log to local machine syslog */
98: ALOG_SYSLOG_REMOTE /* log to remote machine syslog */
99: };
100:
101: /* Current alog channel (thread-specific variable) */
102: struct alog_curchan {
103: int channel;
104: };
105:
106: /* Stolen from syslog.h */
107: struct nameval {
108: const char *name;
109: int val;
110: };
111:
112: /* 'Last message' state for a channel */
113: struct alog_lastmsg {
114: char *msg; /* previously logged message */
115: int sev; /* severity for 'msg' */
116: time_t when; /* timestamp for 'msg' */
117: u_int repeat; /* number of unlogged repeats */
118: u_int timeout; /* timeout for logging repeat */
119: time_t timer_expiry; /* timer running & expiry */
120: };
121:
122: /* Structure describing one channel */
123: struct alog_channel {
124: char name[MAX_NAME]; /* local syslog(3) identifier */
125: struct logfile *logfile; /* history logfile, or NULL */
126: enum alog_type type; /* channel configuration type */
127: int sock; /* remote syslog(3) socket */
128: int facility; /* channel facility */
129: int min_severity; /* min channel severity */
130: u_char debug; /* debug enabled flag */
131: pthread_mutex_t mutex; /* mutex, if not ALOG_NONE */
132: struct alog_lastmsg last; /* 'last message' info */
133: };
134:
135: /*
136: * Internal variables
137: */
138: static struct alog_channel alog_channels[ALOG_MAX_CHANNELS];
139: static pthread_key_t alog_current_channel;
140:
141: /* Stolen from syslog.h */
142: static struct nameval prioritynames[] = {
143: { "alert", LOG_ALERT, },
144: { "crit", LOG_CRIT, },
145: { "debug", LOG_DEBUG, },
146: { "emerg", LOG_EMERG, },
147: { "err", LOG_ERR, },
148: { "error", LOG_ERR, }, /* DEPRECATED */
149: { "info", LOG_INFO, },
150: { "notice", LOG_NOTICE, },
151: { "panic", LOG_EMERG, }, /* DEPRECATED */
152: { "warn", LOG_WARNING, }, /* DEPRECATED */
153: { "warning", LOG_WARNING, },
154: { NULL, -1, }
155: };
156:
157: /* Stolen from syslog.h */
158: static struct nameval facilitynames[] = {
159: { "auth", LOG_AUTH, },
160: { "authpriv", LOG_AUTHPRIV, },
161: { "cron", LOG_CRON, },
162: { "daemon", LOG_DAEMON, },
163: { "ftp", LOG_FTP, },
164: { "kern", LOG_KERN, },
165: { "lpr", LOG_LPR, },
166: { "mail", LOG_MAIL, },
167: { "news", LOG_NEWS, },
168: #ifdef LOG_NTP
169: { "ntp", LOG_NTP, },
170: #endif
171: #ifdef LOG_SECURITY
172: { "security", LOG_SECURITY, },
173: #endif
174: { "syslog", LOG_SYSLOG, },
175: { "user", LOG_USER, },
176: { "uucp", LOG_UUCP, },
177: { "local0", LOG_LOCAL0, },
178: { "local1", LOG_LOCAL1, },
179: { "local2", LOG_LOCAL2, },
180: { "local3", LOG_LOCAL3, },
181: { "local4", LOG_LOCAL4, },
182: { "local5", LOG_LOCAL5, },
183: { "local6", LOG_LOCAL6, },
184: { "local7", LOG_LOCAL7, },
185: { NULL, -1, }
186: };
187:
188: /*
189: * Internal functions
190: */
191: static void alog_write(struct alog_channel *ch,
192: int sev, time_t when, const char *msg);
193: static void alog_flush_lastmsg(struct alog_channel *ch, u_int timeout);
194: static void alog_last_check(struct alog_channel *ch);
195: static int alog_get_channel(void);
196: static struct alog_curchan *alog_get_current_channel(int create);
197: static void alog_init_current_channel(void);
198: static void alog_curchan_destroy(void *arg);
199:
200: /*
201: * Initialize or reconfigure a logging channel.
202: */
203: int
204: alog_configure(int channel, const struct alog_config *conf)
205: {
206: struct alog_channel *const ch = &alog_channels[channel];
207: struct sockaddr_in sin;
208: int init_mutex = 0;
209: int esave;
210: int debug;
211:
212: /* Sanity check */
213: if (channel < 0 || channel >= ALOG_MAX_CHANNELS) {
214: errno = EINVAL;
215: return (-1);
216: }
217:
218: /* If already initialized, shut it down */
219: if (ch->type != ALOG_NONE)
220: alog_shutdown(channel);
221:
222: /* Initialize channel */
223: debug = ch->debug;
224: memset(ch, 0, sizeof(*ch));
225: ch->sock = -1;
226: ch->debug = debug;
227: if (conf->name != NULL)
228: strlcpy(ch->name, conf->name, sizeof(ch->name));
229: ch->facility = (ch->debug || conf->facility == NULL) ?
230: -1 : alog_facility(conf->facility);
231: ch->min_severity = conf->min_severity;
232:
233: /* Open logfile */
234: if (conf->histlen > 0) {
235: if ((ch->logfile = logfile_open(conf->path, 0,
236: conf->histlen, conf->histlen * EST_ENTRY_SIZE)) == NULL)
237: goto fail;
238: }
239:
240: /* Initialize mutex */
241: if ((errno = pthread_mutex_init(&ch->mutex, NULL)) != 0)
242: goto fail;
243: init_mutex = 1;
244:
245: /* Handle stderr case */
246: if (ch->facility == -1) {
247: ch->type = ALOG_STDERR;
248: return (0);
249: }
250:
251: /* Handle NULL case */
252: if (conf->name == NULL) {
253: ch->type = ALOG_NULL;
254: return (0);
255: }
256:
257: /* Handle local syslog case */
258: if (conf->remote_server.s_addr == 0) {
259: ch->type = ALOG_SYSLOG_LOCAL;
260: return (0);
261: }
262:
263: /* Handle remote syslog case */
264: if ((ch->sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
265: goto fail;
266: (void)fcntl(ch->sock, F_SETFD, 1);
267: if (shutdown(ch->sock, SHUT_RD) == -1)
268: goto fail;
269: memset(&sin, 0, sizeof(sin));
270: #ifndef __linux__
271: sin.sin_len = sizeof(sin);
272: #endif
273: sin.sin_family = AF_INET;
274: sin.sin_port = htons(SYSLOG_PORT);
275: sin.sin_addr = conf->remote_server;
276: if (connect(ch->sock, (struct sockaddr *)&sin, sizeof(sin)) == -1)
277: goto fail;
278: ch->type = ALOG_SYSLOG_REMOTE;
279: return (0);
280:
281: fail:
282: /* Clean up after failure */
283: esave = errno;
284: logfile_close(&ch->logfile); /* ok if null */
285: if (ch->sock != -1) {
286: (void)close(ch->sock);
287: ch->sock = -1;
288: }
289: if (init_mutex)
290: pthread_mutex_destroy(&ch->mutex);
291: errno = esave;
292: return (-1);
293: }
294:
295: /*
296: * Shutdown one logging channel.
297: */
298: int
299: alog_shutdown(int channel)
300: {
301: struct alog_channel *const ch = &alog_channels[channel];
302:
303: /* Sanity check */
304: if (channel < 0 || channel >= ALOG_MAX_CHANNELS) {
305: errno = EINVAL;
306: return (-1);
307: }
308:
309: /* Cancel timer */
310: ch->last.timer_expiry = 0;
311:
312: /* Flush any repeated message */
313: alog_flush_lastmsg(ch, 0);
314:
315: /* Shut down channel */
316: switch (ch->type) {
317: case ALOG_SYSLOG_REMOTE:
318: (void)close(ch->sock);
319: ch->sock = -1;
320: pthread_mutex_destroy(&ch->mutex);
321: break;
322: case ALOG_SYSLOG_LOCAL:
323: closelog();
324: pthread_mutex_destroy(&ch->mutex);
325: break;
326: case ALOG_NULL:
327: case ALOG_STDERR:
328: pthread_mutex_destroy(&ch->mutex);
329: break;
330: case ALOG_NONE:
331: break;
332: }
333: ch->type = ALOG_NONE;
334:
335: /* Free saved last message */
336: FREE(MEM_TYPE, ch->last.msg);
337: ch->last.msg = NULL;
338:
339: /* Close logfile */
340: logfile_close(&ch->logfile); /* ok if null */
341:
342: /* Done */
343: return (0);
344: }
345:
346: /*
347: * Get current channel.
348: */
349: static int
350: alog_get_channel(void)
351: {
352: struct alog_curchan *cc;
353:
354: if ((cc = alog_get_current_channel(0)) == NULL)
355: return (0);
356: return (cc->channel);
357: }
358:
359: /*
360: * Set active channel.
361: */
362: int
363: alog_set_channel(int channel)
364: {
365: struct alog_curchan *cc;
366:
367: /* Sanity check */
368: if (channel < 0 || channel >= ALOG_MAX_CHANNELS) {
369: errno = EINVAL;
370: return (-1);
371: }
372:
373: /* Compare with current channel */
374: if ((cc = alog_get_current_channel(channel != 0)) == NULL) {
375: if (channel != 0)
376: return (-1);
377: return (0);
378: }
379: if (channel == cc->channel)
380: return (0);
381:
382: /* Set to new channel, even if it's uninitialized */
383: cc->channel = channel;
384:
385: /* Done */
386: return (0);
387: }
388:
389: /*
390: * Get current channel per-thread variable.
391: */
392: static struct alog_curchan *
393: alog_get_current_channel(int create)
394: {
395: static pthread_once_t init_channel_once = PTHREAD_ONCE_INIT;
396: struct alog_curchan *cc;
397:
398: /* Initialize per-thread variable (once) */
399: if ((errno = pthread_once(&init_channel_once,
400: alog_init_current_channel)) != 0) {
401: fprintf(stderr, "%s: %s: %s\n",
402: "alog", "pthread_once", strerror(errno));
403: return (NULL);
404: }
405:
406: /* Get instance for this thread; create if necessary */
407: if ((cc = pthread_getspecific(alog_current_channel)) == NULL) {
408: if (!create)
409: return (NULL);
410: if ((cc = MALLOC(MEM_TYPE_CURCHAN, sizeof(*cc))) == NULL) {
411: fprintf(stderr, "%s: %s: %s\n",
412: "alog", "malloc", strerror(errno));
413: return (NULL);
414: }
415: cc->channel = 0;
416: if ((errno = pthread_setspecific(alog_current_channel,
417: cc)) != 0) {
418: fprintf(stderr, "%s: %s: %s\n",
419: "alog", "pthread_setspecific", strerror(errno));
420: FREE(MEM_TYPE_CURCHAN, cc);
421: return (NULL);
422: }
423: }
424:
425: /* Return current channel */
426: return (cc);
427: }
428:
429: /*
430: * Initialize "current channel" per-thread variable.
431: */
432: static void
433: alog_init_current_channel(void)
434: {
435: int err;
436:
437: if ((err = pthread_key_create(&alog_current_channel,
438: alog_curchan_destroy)) != 0) {
439: fprintf(stderr, "%s: %s: %s\n",
440: "alog", "pthread_key_create", strerror(err));
441: assert(0);
442: }
443: }
444:
445: /*
446: * Destroy an instance of the per-thread current channel variable.
447: */
448: static void
449: alog_curchan_destroy(void *arg)
450: {
451: struct alog_curchan *const cc = arg;
452:
453: FREE(MEM_TYPE_CURCHAN, cc);
454: }
455:
456: /*
457: * Enable debugging on a channel.
458: */
459: void
460: alog_set_debug(int channel, int enabled)
461: {
462: struct alog_channel *const ch = &alog_channels[channel];
463:
464: ch->debug = !!enabled;
465: }
466:
467: /*
468: * Log something to the currently selected channel. Preserves errno.
469: */
470: void
471: alog(int sev, const char *fmt, ...)
472: {
473: va_list args;
474:
475: va_start(args, fmt);
476: valog(sev, fmt, args);
477: va_end(args);
478: }
479:
480: /*
481: * Log something to the currently selected channel. Preserves errno.
482: */
483: void
484: valog(int sev, const char *fmt, va_list args)
485: {
486: const int errno_save = errno;
487: struct alog_channel *const ch = &alog_channels[alog_get_channel()];
488: char fmtbuf[1024];
489: time_t now;
490: char *msg;
491: int r;
492:
493: /* Lock channel */
494: if (ch->type != ALOG_NONE) {
495: r = pthread_mutex_lock(&ch->mutex);
496: assert(r == 0);
497: }
498:
499: /* Check last message timer */
500: alog_last_check(ch);
501:
502: /* Filter out unwanted verbosity */
503: if (ch->type != ALOG_NONE && sev > ch->min_severity)
504: goto done;
505:
506: /* Allow for things like ``alog(LOG_DEBUG + 1, ...)'' */
507: if (sev >= LOG_DEBUG)
508: sev = LOG_DEBUG;
509:
510: /* Build the message */
511: alog_expand(fmt, errno_save, fmtbuf, sizeof(fmtbuf));
512: VASPRINTF(MEM_TYPE, &msg, fmtbuf, args);
513: if (msg == NULL) {
514: fprintf(stderr, "%s: %s: %s\n",
515: __FUNCTION__, "vasprintf", strerror(errno));
516: goto done;
517: }
518:
519: /* Get current time */
520: now = time(NULL);
521:
522: /* If not configured, don't do the repeated messages thing */
523: if (ch->type == ALOG_NONE) {
524: alog_write(ch, sev, now, msg);
525: FREE(MEM_TYPE, msg);
526: goto done;
527: }
528:
529: /* Handle repeated messages */
530: if (ch->last.msg == NULL || strcmp(msg, ch->last.msg) != 0) {
531: alog_flush_lastmsg(ch, LASTMSG_TIMEOUT_INITIAL);
532: FREE(MEM_TYPE, ch->last.msg);
533: ch->last.msg = msg;
534: ch->last.sev = sev;
535: ch->last.when = now;
536: alog_write(ch, sev, now, msg);
537: } else {
538: time_t delay;
539:
540: delay = MAX(now - ch->last.when, 0);
541: if (++ch->last.repeat == 1) {
542: ch->last.timeout = LASTMSG_TIMEOUT_1(
543: ch->last.timeout, delay);
544: } else {
545: ch->last.timeout = LASTMSG_TIMEOUT_2(
546: ch->last.timeout, delay);
547: }
548: ch->last.when = now;
549: ch->last.timer_expiry = now + ch->last.timeout;
550: FREE(MEM_TYPE, msg);
551: }
552:
553: done:
554: /* Unlock channel and restore errno */
555: if (ch->type != ALOG_NONE) {
556: r = pthread_mutex_unlock(&ch->mutex);
557: assert(r == 0);
558: }
559: errno = errno_save;
560: }
561:
562: /*
563: * Check for expiration of 'last mesasge' timer.
564: *
565: * This assumes the channel is locked.
566: */
567: static void
568: alog_last_check(struct alog_channel *ch)
569: {
570: /* Is the timer "running"? */
571: if (ch->last.timer_expiry == 0)
572: return;
573:
574: /* Has the timer "expired"? */
575: if (ch->last.timer_expiry > time(NULL))
576: return;
577:
578: /* "Stop" timer */
579: ch->last.timer_expiry = 0;
580:
581: /* Flush last message */
582: alog_flush_lastmsg(ch, LASTMSG_TIMEOUT_3(ch->last.timeout));
583: }
584:
585: /*
586: * Log repeated message(s).
587: *
588: * This assumes the channel is locked.
589: */
590: static void
591: alog_flush_lastmsg(struct alog_channel *ch, u_int timeout)
592: {
593: switch (ch->last.repeat) {
594: case 0:
595: break;
596: case 1:
597: if (ch->last.msg != NULL) {
598: alog_write(ch, ch->last.sev,
599: ch->last.when, ch->last.msg);
600: }
601: break;
602: default:
603: {
604: char buf[sizeof(LASTMSG_REPEATED_FMT) + 32];
605:
606: snprintf(buf, sizeof(buf),
607: LASTMSG_REPEATED_FMT, ch->last.repeat);
608: alog_write(ch, ch->last.sev, ch->last.when, buf);
609: break;
610: }
611: }
612: ch->last.repeat = 0;
613: ch->last.timeout = timeout;
614: ch->last.timer_expiry = 0;
615: }
616:
617: /*
618: * Write a message to the log.
619: *
620: * This assumes the channel is locked.
621: */
622: static void
623: alog_write(struct alog_channel *ch, int sev, time_t when, const char *msg)
624: {
625: /* Log it to wherever */
626: switch (ch->type) {
627: case ALOG_NONE:
628: case ALOG_STDERR:
629: fprintf(stderr, "%s: %s\n",
630: alog_severity_name(LOG_PRI(sev)), msg);
631: break;
632: case ALOG_NULL:
633: break;
634: case ALOG_SYSLOG_LOCAL:
635: openlog(ch->name, 0, ch->facility); /* XXX race condition */
636: syslog(sev, "%s", msg); /* XXX race condition */
637: break;
638: case ALOG_SYSLOG_REMOTE:
639: {
640: char ascbuf[32];
641: struct tm tm;
642: char *netmsg;
643: int len;
644:
645: if (ch->sock == -1)
646: break;
647: gmtime_r(&when, &tm);
648: asctime_r(&tm, ascbuf);
649: len = ASPRINTF(TYPED_MEM_TEMP, &netmsg, "<%d>%.15s %s",
650: LOG_MAKEPRI(ch->facility, sev), ascbuf + 4, msg);
651: if (netmsg != NULL)
652: write(ch->sock, netmsg, len);
653: FREE(TYPED_MEM_TEMP, netmsg);
654: break;
655: }
656: }
657:
658: /* Save message in history buffer */
659: if (ch->logfile != NULL) {
660: struct alog_entry *ent;
661: int mlen;
662:
663: /* Setup entry */
664: mlen = strlen(msg) + 1;
665: if ((ent = MALLOC(MEM_TYPE, sizeof(*ent) + mlen)) == NULL) {
666: fprintf(stderr, "%s: %s: %s\n",
667: __FUNCTION__, "malloc", strerror(errno));
668: return;
669: }
670: ent->when = when;
671: ent->sev = LOG_PRI(sev);
672: memcpy(ent->msg, msg, mlen);
673:
674: /* Add to history file */
675: if (logfile_put(ch->logfile, ent, sizeof(*ent) + mlen) == -1) {
676: fprintf(stderr, "%s: %s: %s\n",
677: __FUNCTION__, "logfile_put", strerror(errno));
678: }
679: FREE(MEM_TYPE, ent);
680: }
681: }
682:
683: /*
684: * Get channel history
685: */
686: int
687: alog_get_history(int channel, int min_sev, int max_num, time_t max_age,
688: const regex_t *preg, struct alog_history *list)
689: {
690: struct alog_channel *const ch = &alog_channels[channel];
691: struct alog_entry *copy;
692: regmatch_t pmatch;
693: u_int num_ent;
694: int rtn = -1;
695: time_t now;
696: int i, j;
697: u_int num;
698: int r;
699:
700: /* Sanity check */
701: if (channel < 0 || channel >= ALOG_MAX_CHANNELS) {
702: errno = EINVAL;
703: return (-1);
704: }
705:
706: /* Lock channel */
707: if (ch->type != ALOG_NONE) {
708: r = pthread_mutex_lock(&ch->mutex);
709: assert(r == 0);
710: }
711:
712: /* Check last message timer */
713: alog_last_check(ch);
714:
715: /* Initialize array */
716: memset(list, 0, sizeof(*list));
717:
718: /* Nothing to return? */
719: if (ch->logfile == NULL
720: || max_num == 0
721: || (num_ent = logfile_num_entries(ch->logfile)) == 0) {
722: rtn = 0;
723: goto done;
724: }
725:
726: /* Get current time */
727: now = time(NULL);
728:
729: #define MATCH(sev, when, msg, msglen) \
730: (sev <= min_sev && (now - when) <= max_age && (preg == NULL \
731: || (pmatch.rm_so = 0, pmatch.rm_eo = msglen, \
732: regexec(preg, msg, 0, &pmatch, REG_STARTEND) == 0)))
733:
734: /* Count how many entries we'll be returning */
735: num = 0;
736: if (ch->last.repeat > 0
737: && ch->last.msg != NULL
738: && MATCH(ch->last.sev, ch->last.when,
739: ch->last.msg, strlen(ch->last.msg)))
740: num++;
741: for (i = -1; num < max_num && i >= -num_ent; i--) {
742: const struct alog_entry *ent;
743: int len;
744:
745: /* Get entry, check severity and age */
746: if ((ent = logfile_get(ch->logfile, i, &len)) != NULL
747: && len >= sizeof(*ent) + 1
748: && MATCH(ent->sev, ent->when,
749: ent->msg, len - sizeof(*ent) - 1))
750: num++;
751: }
752: if (num == 0) {
753: rtn = 0;
754: goto done;
755: }
756:
757: /* Allocate array of pointers */
758: if ((list->elems = MALLOC(MEM_TYPE_HISTORY,
759: num * sizeof(*list->elems))) == NULL)
760: goto done;
761:
762: /* Fill array, starting with the most recent first */
763: j = num;
764: if (ch->last.repeat > 0
765: && ch->last.msg != NULL
766: && MATCH(ch->last.sev, ch->last.when,
767: ch->last.msg, strlen(ch->last.msg))) {
768: char buf[sizeof(LASTMSG_REPEATED_FMT) + 32];
769: const char *s;
770:
771: if (ch->last.repeat > 1) {
772: snprintf(buf, sizeof(buf),
773: LASTMSG_REPEATED_FMT, ch->last.repeat);
774: s = buf;
775: } else
776: s = ch->last.msg;
777: if ((copy = MALLOC(MEM_TYPE_ENTRY,
778: sizeof(*copy) + strlen(s) + 1)) != NULL) {
779: copy->when = ch->last.when;
780: copy->sev = ch->last.sev;
781: strcpy(copy->msg, s);
782: list->elems[--j] = copy;
783: }
784: }
785: for (i = -1; j > 0 && i >= -num_ent; i--) {
786: const struct alog_entry *ent;
787: int len;
788:
789: /* Copy it if it matches */
790: if ((ent = logfile_get(ch->logfile, i, &len)) != NULL
791: && len >= sizeof(*ent) + 1
792: && MATCH(ent->sev, ent->when,
793: ent->msg, len - sizeof(*ent) - 1)) {
794: if ((copy = MALLOC(MEM_TYPE_ENTRY, len)) != NULL) {
795: memcpy(copy, ent, len - 1);
796: ((char *)copy)[len - 1] = '\0'; /* safety */
797: list->elems[--j] = copy;
798: }
799: }
800: }
801:
802: /* Collapse entries dropped because of memory problems or whatever */
803: if (j > 0) {
804: num -= j;
805: memcpy(list->elems, list->elems + j,
806: num * sizeof(*list->elems));
807: }
808: list->length = num;
809: rtn = 0;
810:
811: done:
812: /* Unlock channel and return */
813: if (ch->type != ALOG_NONE) {
814: r = pthread_mutex_unlock(&ch->mutex);
815: assert(r == 0);
816: }
817: return (rtn);
818: }
819:
820: /*
821: * Clear log history.
822: */
823: int
824: alog_clear_history(int channel)
825: {
826: struct alog_channel *const ch = &alog_channels[channel];
827: int r;
828:
829: /* Sanity check */
830: if (channel < 0 || channel >= ALOG_MAX_CHANNELS) {
831: errno = EINVAL;
832: return (-1);
833: }
834:
835: /* Lock channel */
836: if (ch->type != ALOG_NONE) {
837: r = pthread_mutex_lock(&ch->mutex);
838: assert(r == 0);
839: }
840:
841: /* Reset last message state */
842: ch->last.timer_expiry = 0;
843: FREE(MEM_TYPE, ch->last.msg);
844: memset(&ch->last, 0, sizeof(ch->last));
845:
846: /* Zero out logfile */
847: if (ch->logfile != NULL)
848: logfile_trim(ch->logfile, 0);
849:
850: /* Unlock channel */
851: if (ch->type != ALOG_NONE) {
852: r = pthread_mutex_unlock(&ch->mutex);
853: assert(r == 0);
854: }
855:
856: /* Done */
857: return (0);
858: }
859:
860: /*
861: * Decode syslog facility name
862: */
863: int
864: alog_facility(const char *name)
865: {
866: int i;
867:
868: for (i = 0; facilitynames[i].name != NULL; i++) {
869: if (strcmp(name, facilitynames[i].name) == 0)
870: return (facilitynames[i].val);
871: }
872: return (-1);
873: }
874:
875: /*
876: * Decode syslog facility name
877: */
878: const char *
879: alog_facility_name(int facility)
880: {
881: int i;
882:
883: for (i = 0; facilitynames[i].name != NULL; i++) {
884: if (facilitynames[i].val == facility)
885: return (facilitynames[i].name);
886: }
887: return (NULL);
888: }
889:
890: /*
891: * Decode syslog severity name
892: */
893: int
894: alog_severity(const char *name)
895: {
896: char *eptr;
897: int i;
898:
899: for (i = 0; prioritynames[i].name != NULL; i++) {
900: if (strcmp(name, prioritynames[i].name) == 0)
901: return (prioritynames[i].val);
902: }
903: i = (int)strtol(name, &eptr, 0);
904: if (*name != '\0' && *eptr == '\0')
905: return (i);
906: return (-1);
907: }
908:
909: /*
910: * Decode syslog severity name
911: */
912: const char *
913: alog_severity_name(int severity)
914: {
915: int i;
916:
917: for (i = 0; prioritynames[i].name != NULL; i++) {
918: if (prioritynames[i].val == severity)
919: return (prioritynames[i].name);
920: }
921: return (NULL);
922: }
923:
924: /*
925: * Kludge to deal with %m format when not using syslog
926: */
927: void
928: alog_expand(const char *fmt, int errnum, char *buf, size_t bufsize)
929: {
930: const char *errstr;
931: const char *s;
932: int errlen;
933: int i;
934:
935: /* Find "%m" which is usually at the end */
936: for (s = fmt + strlen(fmt) - 2;
937: s >= fmt && !(s[0] == '%' && s[1] == 'm');
938: s--);
939:
940: /* Check if we should bother doing anything */
941: if (s < fmt || (i = s - fmt) > bufsize - 2) {
942: strlcpy(buf, fmt, bufsize);
943: return;
944: }
945:
946: /* Convert "%m" to error string */
947: errstr = strerror(errnum);
948: errlen = strlen(errstr);
949: strlcpy(buf, fmt, i + 1);
950: strlcpy(buf + i, errstr, bufsize - i);
951: strlcpy(buf + i + errlen, fmt + i + 2, bufsize - i - errlen);
952: }
953:
954: /*********************************************************************
955: STRUCTS TYPE DEFINITIONS
956: *********************************************************************/
957:
958: /* Type for "path" and "name" strings in struct alog_config */
959: static const struct structs_type alog_path_type
960: = STRUCTS_STRING_TYPE("alog_config.path", 1);
961: static const struct structs_type alog_name_type
962: = STRUCTS_STRING_TYPE("alog_config.name", 1);
963:
964: /* Fields list for struct alog_config */
965: static const struct structs_field alog_config_fields[] = {
966: STRUCTS_STRUCT_FIELD(alog_config, path, &alog_path_type),
967: STRUCTS_STRUCT_FIELD(alog_config, name, &alog_name_type),
968: STRUCTS_STRUCT_FIELD(alog_config, facility, &alog_facility_type),
969: STRUCTS_STRUCT_FIELD(alog_config, remote_server, &structs_type_ip4),
970: STRUCTS_STRUCT_FIELD(alog_config, min_severity, &alog_severity_type),
971: STRUCTS_STRUCT_FIELD(alog_config, histlen, &structs_type_int),
972: STRUCTS_STRUCT_FIELD_END
973: };
974:
975: /* Type for struct alog_config */
976: const struct structs_type alog_config_type
977: = STRUCTS_STRUCT_TYPE(alog_config, &alog_config_fields);
978:
979: /* Override method for alog_facility_type */
980: static structs_init_t alog_facility_type_init;
981: static structs_binify_t alog_facility_type_binify;
982:
983: /* Type for syslog facility, which we store as a string. */
984: const struct structs_type alog_facility_type = {
985: sizeof(char *),
986: "alog",
987: STRUCTS_TYPE_PRIMITIVE,
988: alog_facility_type_init,
989: structs_ascii_copy,
990: structs_string_equal,
991: structs_string_ascify,
992: alog_facility_type_binify,
993: structs_string_encode,
994: structs_string_decode,
995: structs_string_free,
996: { { (void *)"alog_config.facility" }, { (void *)1 } }
997: };
998:
999: /*
1000: * Initializer for alog_facility_type
1001: */
1002: static int
1003: alog_facility_type_init(const struct structs_type *type, void *data)
1004: {
1005: return (structs_string_binify(type, "daemon", data, NULL, 0));
1006: }
1007:
1008: /*
1009: * Binifier for alog_facility_type
1010: */
1011: static int
1012: alog_facility_type_binify(const struct structs_type *type,
1013: const char *ascii, void *data, char *ebuf, size_t emax)
1014: {
1015: if (*ascii != '\0' && alog_facility(ascii) == -1) {
1016: strlcpy(ebuf, "invalid syslog facility", emax);
1017: errno = EINVAL;
1018: return (-1);
1019: }
1020: return (structs_string_binify(type, ascii, data, NULL, 0));
1021: }
1022:
1023: /* Override methods for alog_severity_type */
1024: static structs_init_t alog_severity_init;
1025: static structs_ascify_t alog_severity_ascify;
1026: static structs_binify_t alog_severity_binify;
1027:
1028: /* Type for severity, which defaults to LOG_INFO. The input can be either
1029: a symbolic name or a numeric value. */
1030: const struct structs_type alog_severity_type = {
1031: sizeof(int),
1032: "int",
1033: STRUCTS_TYPE_PRIMITIVE,
1034: alog_severity_init,
1035: structs_region_copy,
1036: structs_region_equal,
1037: alog_severity_ascify,
1038: alog_severity_binify,
1039: structs_region_encode_netorder,
1040: structs_region_decode_netorder,
1041: structs_nothing_free,
1042: { { (void *)2 }, { (void *)1 } }
1043: };
1044:
1045: /*
1046: * Initializer for severity
1047: */
1048: static int
1049: alog_severity_init(const struct structs_type *type, void *data)
1050: {
1051: *((int *)data) = LOG_INFO;
1052: return (0);
1053: }
1054:
1055: /*
1056: * Ascifier for severity
1057: */
1058: static char *
1059: alog_severity_ascify(const struct structs_type *type,
1060: const char *mtype, const void *data)
1061: {
1062: const int sev = *((int *)data);
1063: const char *s;
1064: char buf[32];
1065:
1066: if ((s = alog_severity_name(sev)) == NULL) {
1067: snprintf(buf, sizeof(buf), "%d", sev);
1068: s = buf;
1069: }
1070: return (STRDUP(mtype, s));
1071: }
1072:
1073: /*
1074: * Binifier for severity
1075: */
1076: static int
1077: alog_severity_binify(const struct structs_type *type,
1078: const char *ascii, void *data, char *ebuf, size_t emax)
1079: {
1080: int sev;
1081:
1082: if ((sev = alog_severity(ascii)) == -1
1083: && structs_int_binify(type, ascii, data, NULL, 0) == -1) {
1084: strlcpy(ebuf, "invalid syslog severity", emax);
1085: return (-1);
1086: }
1087: *((int *)data) = sev;
1088: return (0);
1089: }
1090:
1091: /*
1092: * Ascifier for log entry message.
1093: */
1094: static char *
1095: alog_entry_msg_ascify(const struct structs_type *type,
1096: const char *mtype, const void *data)
1097: {
1098: return (STRDUP(mtype, data));
1099: }
1100:
1101: /* Type for log entry message. This is a "read only" type. */
1102: static const struct structs_type alog_entry_msg_type = {
1103: 0,
1104: "alog_entry_msg_type",
1105: STRUCTS_TYPE_PRIMITIVE,
1106: structs_notsupp_init,
1107: structs_notsupp_copy,
1108: structs_notsupp_equal,
1109: alog_entry_msg_ascify,
1110: structs_notsupp_binify,
1111: structs_notsupp_encode,
1112: structs_notsupp_decode,
1113: structs_nothing_free,
1114: };
1115:
1116: /* Type for history list: an array of pointers to log entries */
1117: static const struct structs_field alog_entry_fields[] = {
1118: STRUCTS_STRUCT_FIELD(alog_entry, when, &structs_type_time_abs),
1119: STRUCTS_STRUCT_FIELD(alog_entry, sev, &alog_severity_type),
1120: STRUCTS_STRUCT_FIELD(alog_entry, msg, &alog_entry_msg_type),
1121: STRUCTS_STRUCT_FIELD_END
1122: };
1123: static const struct structs_type alog_entry_type
1124: = STRUCTS_STRUCT_TYPE(alog_entry, &alog_entry_fields);
1125: static const struct structs_type alog_history_ptr_type
1126: = STRUCTS_POINTER_TYPE(&alog_entry_type, MEM_TYPE_ENTRY);
1127: const struct structs_type alog_history_type
1128: = STRUCTS_ARRAY_TYPE(&alog_history_ptr_type, MEM_TYPE_HISTORY, "entry");
1129:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>