/*
 * DEBUG - debugging functions
 *
 * Author:
 * Emile van Bergen, emile@evbergen.xs4all.nl
 *
 * Permission to redistribute an original or modified version of this program
 * in source, intermediate or object code form is hereby granted exclusively
 * under the terms of the GNU General Public License, version 2. Please see the
 * file COPYING for details, or refer to http://www.gnu.org/copyleft/gpl.html.
 *
 * History:
 * 2000/12/02 - EvB - Created
 * 2001/02/06 - EvB - Made dbg_cvtstr NULL-proof
 * 2001/11/01 - EvB - Added standard logging functions
 * 2002/02/06 - EvB - Added msg_line interface and made meta_printav
 * 		      and hexdump use it instead of writing to stderr
 * 2005/06/20 - EvB - Add tag if we run as a module
 */

char debug_id[] = "DEBUG - Copyright (C) 2000 Emile van Bergen.";


/*
 * INCLUDES & DEFINES
 */


#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>	/* For vsprintf / vsnprintf */

#include <misc.h>	/* For MIN */
#include <metatype.h> 	/* For meta_atoprt() and meta_ordtoa() */

#include <debug.h>
#include <platform.h>


/*
 * GLOBALS
 */


int debuglevel = 0;		/* Backwards compatibility only */

int msg_syslogfac = -1;		/* Syslog facility to which we output all */
int msg_nostderr = 0;		/* Be quiet on stderr */
char *msg_tag = 0;		/* Extra tag to add to each line */
int msg_tagl = 0;

int msg_thresh[F_CNT] = {	/* Priority threshold (only lower-numbered */
	L_ERR, L_ERR,		/* or equal gets through */
	L_ERR, L_ERR, 
	L_ERR, L_ERR
};


char *msg_f_names[F_CNT+1] = {	/* Names for our own internal facilities */

	"misc",		/* miscellaneous */
	"text",		/* textfile handling (dictionary) */
	"lang",		/* language */
	"proc",		/* subprocess events */
	"recv",		/* packet reception */
	"send",		/* packet sending */
	0		/* not found */
};


#ifdef HAVE_SYSLOG

#include <syslog.h>

#define SYSLOG_F_CNT	18	/* Syslog facilities */

struct {
	char *name;
	int fac;
} syslog_f_map[SYSLOG_F_CNT + 1] = {
	{ "auth",	LOG_AUTH },
	{ "cron",	LOG_CRON },
	{ "daemon",	LOG_DAEMON },
	{ "kern",	LOG_KERN },
	{ "lpr",	LOG_LPR },
	{ "mail",	LOG_MAIL },
	{ "news",	LOG_NEWS },
	{ "syslog",	LOG_SYSLOG },
	{ "user",	LOG_USER },
	{ "uucp",	LOG_UUCP },
	{ "local0",	LOG_LOCAL0 },
	{ "local1",	LOG_LOCAL1 },
	{ "local2",	LOG_LOCAL2 },
	{ "local3",	LOG_LOCAL3 },
	{ "local4",	LOG_LOCAL4 },
	{ "local5",	LOG_LOCAL5 },
	{ "local6",	LOG_LOCAL6 },
	{ "local7",	LOG_LOCAL7 },
	{ 0,		-1 },		/* not found */
};

#ifndef LOG_MAKEPRI
#define LOG_MAKEPRI(f, p)	((f) | (p))
#endif

#endif


/*
 * FUNCTIONS
 */


void msg_init(int syslogfac, int nostderr)
{
	msg_syslogfac = syslogfac;
	msg_nostderr = nostderr;

#ifdef HAVE_SYSLOG
	if (msg_syslogfac != -1) openlog("openradius", 0, msg_syslogfac);
#endif
}


void msg_setthresh(int ourfac, int level)
{
	if (ourfac >= 0 && ourfac < F_CNT) msg_thresh[ourfac] = level;
}


void msg_settag(char *tag, int tagl)
{
	if (tagl < 32 && tag) { msg_tag = tag; msg_tagl = tagl; }
}


int msg_getfacbyname(char *name)
{
	int ret;

	for(ret = 0; 
	    ret < F_CNT && strcmp(name, msg_f_names[ret]); 
	    ret++);

	return ret;
}

#if 0
char *msg_getfacbynr(int ourfac)
{
	return (ourfac >= 0 && ourfac < F_CNT) ? msg_f_names[ourfac] : "****";
}
#endif


int msg_getsyslogfacbyname(char *name)
{
#ifdef HAVE_SYSLOG
	int ret;

	for(ret = 0; 
	    ret < SYSLOG_F_CNT && strcmp(name, syslog_f_map[ret].name); 
	    ret++);

	return syslog_f_map[ret].fac;
#else
	return -1;
#endif
}


void msg(int ourfac, int level, char *fmt, ...)
{
	static char logline[4096];
	char *o;
	int n;
	va_list ap;

	/* Return if level is not high (numerically low) enough */
	if (level > msg_thresh[ourfac]) return;

	/* Init logline */
	o = logline;
	if (msg_tagl && msg_tag) { 
		*o++ = '[';
		memcpy(o, msg_tag, msg_tagl); o += msg_tagl;
		*o++ = ']';
		*o++ = ' ';
	}
	*o++ = '[';
	memcpy(o, msg_f_names[ourfac], 4); o += 4;
	*o ++ = ']';
	*o ++ = ' ';

	/* Prepare arglist and create formatted line */
	va_start(ap, fmt);
	n = logline + sizeof(logline) - 16 - o;
#ifdef HAVE_VSNPRINTF
	vsnprintf(o, n, fmt, ap);
#else
	if (vsprintf(o, fmt, ap) >= n) _exit(254);	/* safest */
#endif
	msg_line(level, logline);
	va_end(ap);
}


void msg_line(int level, char *logline)
{
	/* Write line to stderr if we haven't been told to be quiet */
	if (!msg_nostderr) 
		write(2, logline, strlen(logline));

#ifdef HAVE_SYSLOG
	/* Write line to syslog if facility is not none */
	if (msg_syslogfac != -1) 
		syslog(LOG_MAKEPRI(msg_syslogfac, level), "%s", logline);
#endif
}


char *dbg_cvtstr(char *s, int slen)
{
	static char dbg_cvtbuf[DBG_CVTBUFLEN];
	int len;

	len = s ? meta_atoprt(s,slen, 0,0,0,0, dbg_cvtbuf,DBG_CVTBUFLEN-1) : 0;
	dbg_cvtbuf[len] = 0;

	return dbg_cvtbuf;
}


#define DBG_HEXBUFLEN	(4 + 2 + 16 * 3 + 1 + 16 + 1 + 1)

void hexdump(char *s, int slen)
{
	static char hexbuf[DBG_HEXBUFLEN];
	char *o;
	int ofs, i;	

	for(ofs = 0; ofs < slen; ofs += 16, s += 16) {
		o = hexbuf;
		memset(o, ' ', DBG_HEXBUFLEN - 1);
		o += meta_ordtoa(o, 4, 4, 16, ofs) + 2;
		for(i = 0; i < MIN(slen - ofs, 16); i++) 
			o += meta_ordtoa(o, 2, 2, 16, s[i] & 0xff) + 1;
		o = hexbuf + 4 + 2 + 16 * 3 + 1;
		for(i = 0; i < MIN(slen - ofs, 16); i++) 
			*o++ = s[i] > 31 && s[i] < 127 ? s[i] : '.';
		hexbuf[4 + 2 + 16 * 3 + 1 + 16] = '\n';
		hexbuf[DBG_HEXBUFLEN - 1] = 0;
		msg_line(L_DEBUG, hexbuf);
	}
}



syntax highlighted by Code2HTML, v. 0.9.1