/*
 * logging	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.
 */

#include "system.h"
#include "logging.h"

#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#else
#define LOG_FILE "/var/tmp/%s.log"
#endif

static int logging = 0;			       /* enable/disable DEBUG logs    */
static int dbg_mask = D_GENERAL;	       /* What will be logged          */
static char log_name[NAME_MAX + 1];	       /* name of this program         */
static FILE *log_fp = (FILE *) NULL;	       /* fp for the log file          */

void
log_open(char *progname, int foreground)
{
#ifdef HAVE_SYSLOG_H
	openlog(progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
	if (foreground) {
		log_fp = stderr;
	}
#else
	if (foreground) {
		log_fp = stderr;
	} else {
		char path[PATH_MAX + 1];

		snprintf(path, PATH_MAX, LOG_FILE, progname);
		if ((log_fp = fopen(path, "a")) == NULL)
			return;
	}
#endif /* HAVE_SYSLOG_H */

	if (log_fp != NULL) {
		setbuf(log_fp, (char *) NULL);
	}

	snprintf(log_name, NAME_MAX, "%s[%d]", progname, (int) getpid());
}

void
log_close(void)
{
	if (log_fp) {
		fclose(log_fp);
		log_fp = 0;
	}
}

void
log_set_background(void)
{
	if (log_fp == stderr) {
		log_fp = NULL;
	}
}

void
log_toggle(int sig)
{
	dbg_printf(__FILE__, __LINE__, D_GENERAL, "turned off logging\n");
	logging = 1 - logging;
	dbg_printf(__FILE__, __LINE__, D_GENERAL, "turned on logging\n");
}

void
log_enable(char *kind)
{
	if ('a' == *kind && !strcmp(kind, "auth")) {
		dbg_mask |= D_AUTH;
	} else if ('a' == *kind && !strcmp(kind, "all")) {
		dbg_mask |= D_ALL;
	} else if ('c' == *kind && !strcmp(kind, "call")) {
		dbg_mask |= D_CALL;
	} else if ('d' == *kind && !strcmp(kind, "devtab")) {
		dbg_mask |= D_DEVTAB;
	} else if ('f' == *kind && !strcmp(kind, "fhcache")) {
		dbg_mask |= D_FHCACHE;
	} else if ('f' == *kind && !strcmp(kind, "fhtrace")) {
		dbg_mask |= D_FHTRACE;
	} else if ('g' == *kind && !strcmp(kind, "general")) {
		dbg_mask |= D_GENERAL;
	} else if ('m' == *kind && !strcmp(kind, "most")) {
		dbg_mask |= D_ALL & ~(D_UGID); 
	} else if ('r' == *kind && !strcmp(kind, "rmtab")) {
		dbg_mask |= D_RMTAB;
	} else if ('s' == *kind && !strcmp(kind, "stale")) {
		dbg_mask |= D_AUTH | D_CALL | D_FHCACHE | D_FHTRACE;
	} else if ('u' == *kind && !strcmp(kind, "ugid")) {
		dbg_mask |= D_UGID;
	} else {
		fprintf(stderr, "Invalid debug facility: %s\n", kind);
	}

	logging = 1;
}

int
log_level_enabled(int level)
{
	return (logging && (level & dbg_mask));
}

/* Write something to the system logfile. */
void
dbg_printf(const char *file, int line, int kind, const char *fmt, ...)
{
	char buff[2048];
	va_list args;
	time_t now;
	struct tm *tm;

	if (!(kind & (L_FATAL | L_ERROR | L_WARNING | L_NOTICE))
	    && !(logging && (kind & dbg_mask)))
		return;

	va_start(args, fmt);
#ifdef HAVE_VPRINTF
	buff[0] = '\0';
	vsnprintf(buff, sizeof(buff) - 1, fmt, args);
#else
	/* Figure out how to use _doprnt here. */
#endif
	va_end(args);

#ifdef HAVE_SYSLOG_H
	if (kind & (L_FATAL | L_ERROR)) {
		(void) syslog(LOG_ERR, "%s", buff);
	} else if (kind & L_WARNING) {
		(void) syslog(LOG_WARNING, "%s", buff);
	} else if (kind & L_NOTICE) {
		(void) syslog(LOG_NOTICE, "%s", buff);
	} else if (log_fp == NULL) {
		(void) syslog(LOG_DEBUG, "%s", buff);
	}
#endif
	if (log_fp != (FILE *) NULL) {
		(void) time(&now);
		tm = localtime(&now);
		fprintf(log_fp, "%s %02d/%02d/%02d %02d:%02d %s %d : %s",
			log_name, tm->tm_mon + 1, tm->tm_mday, tm->tm_year,
			tm->tm_hour, tm->tm_min, file, line, buff);
		if (strchr(buff, '\n') == NULL)
			fputc('\n', log_fp);
	}

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

/*
 * Log an incoming call.
 */
void
log_call(const char* file, int line, struct svc_req *rqstp, char *xname, char *arg)
{
	char buffer[4096];
	size_t i, len, total;

	if (!logging || !(dbg_mask & D_CALL))
		return;

	total = sizeof(buffer);
	snprintf(buffer, total, "%s [%d ", xname, rqstp->rq_cred.oa_flavor);
	len = strlen(buffer);

	if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
		struct authunix_parms *unix_cred;
		struct tm *tm;

		unix_cred = (struct authunix_parms *) rqstp->rq_clntcred;
		tm = localtime(&unix_cred->aup_time);
		snprintf(buffer + len, total - len,
			 "%d/%d/%d %02d:%02d:%02d %s %d.%d",
			 tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
			 tm->tm_hour, tm->tm_min, tm->tm_sec,
			 unix_cred->aup_machname,
			 unix_cred->aup_uid, unix_cred->aup_gid);
		len = strlen(buffer);
		if ((int) unix_cred->aup_len > 0) {
			snprintf(buffer + len, total - len,
				 "+%d", unix_cred->aup_gids[0]);
			len = strlen(buffer);
			for (i = 1; i < unix_cred->aup_len; i++) {
				snprintf(buffer + len, total - len,
					 ",%d", unix_cred->aup_gids[i]);
				len = strlen(buffer);
			}
		}
	}
	dbg_printf(file, line, D_CALL, "%s]\n", buffer);
	if (arg && *arg)
		dbg_printf(file, line, D_CALL, "\t%s\n", arg);
}
