/*
 * kmod - log infrastructure
 *
 * Copyright (C) 2012-2013  ProFUSION embedded systems
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>

#include <libkmod/libkmod.h>

#include "kmod.h"

#define PRIO_MAX_SIZE 32

static bool log_use_syslog;
static int log_priority = LOG_WARNING;

static const char *prio_to_str(char buf[static PRIO_MAX_SIZE], int prio)
{
	const char *prioname;

	switch (prio) {
	case LOG_CRIT:
		prioname = "FATAL";
		break;
	case LOG_ERR:
		prioname = "ERROR";
		break;
	case LOG_WARNING:
		prioname = "WARNING";
		break;
	case LOG_NOTICE:
		prioname = "NOTICE";
		break;
	case LOG_INFO:
		prioname = "INFO";
		break;
	case LOG_DEBUG:
		prioname = "DEBUG";
		break;
	default:
		snprintf(buf, PRIO_MAX_SIZE, "LOG-%03d", prio);
		prioname = buf;
	}

	return prioname;
}

_printf_format_(6, 0)
static void log_kmod(void *data, int priority, const char *file, int line,
		     const char *fn, const char *format, va_list args)
{
	char buf[PRIO_MAX_SIZE];
	const char *prioname;
	char *str;

	prioname = prio_to_str(buf, priority);

	if (vasprintf(&str, format, args) < 0)
		return;

	if (log_use_syslog) {
#ifdef ENABLE_DEBUG
		syslog(priority, "%s: %s:%d %s() %s", prioname, file, line,
		       fn, str);
#else
		syslog(priority, "%s: %s", prioname, str);
#endif
	} else {
#ifdef ENABLE_DEBUG
		fprintf(stderr, "%s: %s: %s:%d %s() %s",
			program_invocation_short_name, prioname, file, line,
			fn, str);
#else
		fprintf(stderr, "%s: %s: %s", program_invocation_short_name,
			prioname, str);
#endif
	}

	free(str);
	(void)data;
}
void log_open(bool use_syslog)
{
	log_use_syslog = use_syslog;

	if (log_use_syslog)
		openlog(program_invocation_short_name, LOG_CONS, LOG_DAEMON);
}

void log_close(void)
{
	if (log_use_syslog)
		closelog();
}

void log_printf(int prio, const char *fmt, ...)
{
	char buf[PRIO_MAX_SIZE];
	const char *prioname;
	char *msg;
	va_list args;

	if (prio > log_priority)
		return;

	va_start(args, fmt);
	if (vasprintf(&msg, fmt, args) < 0)
		msg = NULL;
	va_end(args);
	if (msg == NULL)
		return;

	prioname = prio_to_str(buf, prio);

	if (log_use_syslog)
		syslog(prio, "%s: %s", prioname, msg);
	else
		fprintf(stderr, "%s: %s: %s", program_invocation_short_name,
			prioname, msg);
	free(msg);

	if (prio <= LOG_CRIT)
		exit(EXIT_FAILURE);
}

void log_setup_kmod_log(struct kmod_ctx *ctx, int priority)
{
	log_priority = priority;

	kmod_set_log_priority(ctx, log_priority);
	kmod_set_log_fn(ctx, log_kmod, NULL);
}