File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / sys / alog.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/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>