/*
 * Logging related utility functions.
 *
 * Copyright (C) 2004-2007 Olaf Kirch <okir@suse.de>
 */

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>

#include <libisns/util.h>

static unsigned int	log_stdout = 1;
static unsigned int	debugging = 0;

/*
 * When backgrounding, any logging output should
 * go to syslog instead of stdout
 */
void
isns_log_background(void)
{
	log_stdout = 0;
}

/*
 * For output to syslog, sanitize the format string
 * by removing newlines.
 */
static const char *
sanitize_format(const char *fmt)
{
	static char	__fmt[1024];
	unsigned int	len;

	/* Don't bother unless there's a newline */
	if (!strchr(fmt, '\n'))
		return fmt;

	len = strlen(fmt);

	/* Decline if the buffer would overflow */
	if (len >= sizeof(__fmt))
		return fmt;

	strcpy(__fmt, fmt);
	while (len-- && __fmt[len] == '\n')
		__fmt[len] = '\0';

	while (len) {
		if (__fmt[len] == '\n')
			__fmt[len] = ' ';
		--len;
	}

	return __fmt;
}

/*
 * Output to stderr or syslog
 */
static void
voutput(int severity, const char *fmt, va_list ap)
{
	if (log_stdout) {
		switch (severity) {
		case LOG_ERR:
			fprintf(stderr, "Error: ");
			break;
		case LOG_WARNING:
			fprintf(stderr, "Warning: ");
			break;
		case LOG_DEBUG:
			fprintf(stderr, "   ");
			break;
		}
		vfprintf(stderr, fmt, ap);
	} else {
		fmt = sanitize_format(fmt);
		if (!fmt || !*fmt)
			return;
		vsyslog(severity, fmt, ap);
	}
}

void
isns_assert_failed(const char *condition, const char *file, unsigned int line)
{
	isns_error("Assertion failed (%s:%d): %s\n",
			file, line, condition);
	abort();
}

void
isns_fatal(const char *fmt, ...)
{
	va_list	ap;

	va_start(ap, fmt);
	if (log_stdout)
		fprintf(stderr, "** FATAL ERROR **\n");
	voutput(LOG_ERR, fmt, ap);
	va_end(ap);
	exit(1);
}

void
isns_error(const char *fmt, ...)
{
	va_list	ap;

	va_start(ap, fmt);
	voutput(LOG_WARNING, fmt, ap);
	va_end(ap);
}

void
isns_warning(const char *fmt, ...)
{
	va_list	ap;

	va_start(ap, fmt);
	voutput(LOG_NOTICE, fmt, ap);
	va_end(ap);
}

void
isns_notice(const char *fmt, ...)
{
	va_list	ap;

	va_start(ap, fmt);
	voutput(LOG_INFO, fmt, ap);
	va_end(ap);
}

void
isns_enable_debugging(const char *what)
{
	char	*copy, *s, *next;

	if (!strcmp(what, "all")) {
		debugging = ~0U;
		return;
	}
	
	copy = isns_strdup(what);
	
	for (s = copy; s; s = next) {
		if ((next = strchr(s, ',')) != NULL)
			*next++ = '\0';

		if (!strcmp(s, "general"))
			debugging |= (1 << DBG_GENERAL);
		else if (!strcmp(s, "socket"))
			debugging |= (1 << DBG_SOCKET);
		else if (!strcmp(s, "protocol"))
			debugging |= (1 << DBG_PROTOCOL);
		else if (!strcmp(s, "state"))
			debugging |= (1 << DBG_STATE);
		else if (!strcmp(s, "message"))
			debugging |= (1 << DBG_MESSAGE);
		else if (!strcmp(s, "auth"))
			debugging |= (1 << DBG_AUTH);
		else if (!strcmp(s, "scn"))
			debugging |= (1 << DBG_SCN);
		else if (!strcmp(s, "esi"))
			debugging |= (1 << DBG_ESI);
		else if (!strcmp(s, "all"))
			debugging = (unsigned int)-1;
		else {
			isns_error("Ignoring unknown isns_debug facility <<%s>>\n",
					s);
		}
	}
	isns_free(copy);
}

#define DEFINE_DEBUG_FUNC(name, NAME) \
void						\
isns_debug_##name(const char *fmt, ...)		\
{						\
	va_list	ap;				\
						\
	if (!(debugging & (1 << DBG_##NAME)))	\
		return;				\
						\
	va_start(ap, fmt);			\
	voutput(LOG_DEBUG, fmt, ap);		\
	va_end(ap);				\
}
DEFINE_DEBUG_FUNC(general,	GENERAL)
DEFINE_DEBUG_FUNC(socket,	SOCKET)
DEFINE_DEBUG_FUNC(protocol,	PROTOCOL)
DEFINE_DEBUG_FUNC(message,	MESSAGE)
DEFINE_DEBUG_FUNC(auth,		AUTH)
DEFINE_DEBUG_FUNC(state,	STATE)
DEFINE_DEBUG_FUNC(scn,		SCN)
DEFINE_DEBUG_FUNC(esi,		ESI)

int
isns_debug_enabled(int fac)
{
	return (debugging & (1 << fac)) != 0;
}

/*
 * Misc isns_print_fn_t implementations
 */
void
isns_print_stdout(const char *fmt, ...)
{
	va_list	ap;

	va_start(ap, fmt);
	vfprintf(stdout, fmt, ap);
	va_end(ap);
}

void
isns_print_stderr(const char *fmt, ...)
{
	va_list	ap;

	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
}
