/*
 * support/nfs/xlog.c
 *
 * This module handles the logging of requests.
 *
 * TODO:	Merge the two "XXX_log() calls.
 *
 * Authors:	Donald J. Becker, <becker@super.org>
 *		Rick Sladkey, <jrs@world.std.com>
 *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
 *		Olaf Kirch, <okir@monad.swb.de>
 *
 *		This software maybe be used for any purpose provided
 *		the above copyright notice is retained.  It is supplied
 *		as is, with no warranty expressed or implied.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#include "nfslib.h"
#include "conffile.h"

#undef	VERBOSE_PRINTF

#pragma GCC visibility push(hidden)

static int  log_stderr = 1;
static int  log_syslog = 1;
static int  logging = 0;		/* enable/disable DEBUG logs	*/
static int  logmask = 0;		/* What will be logged		*/
static char log_name[256];		/* name of this program		*/
static int  log_pid = -1;		/* PID of this program		*/

int export_errno = 0;

static void	xlog_toggle(int sig);
static struct xlog_debugfac	debugnames[] = {
	{ "general",	D_GENERAL, },
	{ "call",	D_CALL, },
	{ "auth",	D_AUTH, },
	{ "parse",	D_PARSE, },
	{ "all",	D_ALL, },
	{ NULL,		0, },
};

void
xlog_open(char *progname)
{
	openlog(progname, LOG_PID, LOG_DAEMON);

	strncpy(log_name, progname, sizeof (log_name) - 1);
	log_name [sizeof (log_name) - 1] = '\0';
	log_pid = getpid();

	signal(SIGUSR1, xlog_toggle);
	signal(SIGUSR2, xlog_toggle);
}

void
xlog_stderr(int on)
{
	log_stderr = on;
}

void
xlog_syslog(int on)
{
	log_syslog = on;
}

static void
xlog_toggle(int sig)
{
	unsigned int	tmp, i;

	if (sig == SIGUSR1) {
		if ((logmask & D_ALL) && !logging) {
			xlog(D_GENERAL, "turned on logging");
			logging = 1;
			return;
		}
		tmp = ~logmask;
		logmask |= ((logmask & D_ALL) << 1) | D_GENERAL;
		for (i = -1, tmp &= logmask; tmp; tmp >>= 1, i++)
			if (tmp & 1)
				xlog(D_GENERAL,
					"turned on logging level %d", i);
	} else {
		xlog(D_GENERAL, "turned off logging");
		logging = 0;
	}
	signal(sig, xlog_toggle);
}

void
xlog_config(int fac, int on)
{
	if (on)
		logmask |= fac;
	else
		logmask &= ~fac;
	if (on)
		logging = 1;
}

void
xlog_sconfig(char *kind, int on)
{
	struct xlog_debugfac	*tbl = debugnames;

	while (tbl->df_name != NULL && strcasecmp(tbl->df_name, kind)) 
		tbl++;
	if (!tbl->df_name) {
		xlog (L_WARNING, "Invalid debug facility: %s\n", kind);
		return;
	}
	xlog_config(tbl->df_fac, on);
}

void
xlog_set_debug(char *service)
{
	struct conf_list *kinds;
	struct conf_list_node *n;

	kinds = conf_get_list(service, "debug");
	if (!kinds || !kinds->cnt) {
		free(kinds);
		return;
	}
	TAILQ_FOREACH(n, &(kinds->fields), link)
		xlog_sconfig(n->field, 1);

	conf_free_list(kinds);
}

int
xlog_enabled(int fac)
{
	return (logging && (fac & logmask));
}


/* Write something to the system logfile and/or stderr */
void
xlog_backend(int kind, const char *fmt, va_list args)
{
	if (!(kind & (L_ALL)) && !(logging && (kind & logmask)))
		return;

	if (log_stderr) {
		va_list		args2;
#ifdef VERBOSE_PRINTF
		time_t		now;
		struct tm	*tm;

		time(&now);
		tm = localtime(&now);
		fprintf(stderr, "%s[%d] %04d-%02d-%02d %02d:%02d:%02d ",
				log_name, log_pid,
				tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday,
				tm->tm_hour, tm->tm_min, tm->tm_sec);
#else
		fprintf(stderr, "%s: ", log_name);
#endif
		va_copy(args2, args);
		vfprintf(stderr, fmt, args2);
		fprintf(stderr, "\n");
		va_end(args2);
	}

	if (log_syslog) {
		switch (kind) {
		case L_FATAL:
			vsyslog(LOG_ERR, fmt, args);
			break;
		case L_ERROR:
			vsyslog(LOG_ERR, fmt, args);
			break;
		case L_WARNING:
			vsyslog(LOG_WARNING, fmt, args);
			break;
		case L_NOTICE:
			vsyslog(LOG_NOTICE, fmt, args);
			break;
		default:
			if (!log_stderr)
				vsyslog(LOG_INFO, fmt, args);
			break;
		}
	}

	if (kind == L_FATAL)
		exit(1);
}

void
xlog(int kind, const char* fmt, ...)
{
	va_list args;

	if (kind & (L_ERROR|D_GENERAL))
		export_errno = 1;

	va_start(args, fmt);
	xlog_backend(kind, fmt, args);
	va_end(args);
}

void
xlog_warn(const char* fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	xlog_backend(L_WARNING, fmt, args);
	va_end(args);
}


void
xlog_err(const char* fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	xlog_backend(L_FATAL, fmt, args);
	va_end(args);
}

void
xlog_errno(int err, const char *fmt, ...)
{
	va_list args;

	errno = err;
	va_start(args, fmt);
	xlog_backend(L_FATAL, fmt, args);
	va_end(args);
}
