/*
 * Copyright (C) 2001 Sistina Software (UK) Limited.
 *
 * This file is released under the LGPL.
 */

#include "lib.h"
#include "device.h"
#include "memlock.h"
#include "lvm-string.h"

#include <stdarg.h>
#include <syslog.h>

static FILE *_log_file;
static struct device _log_dev;
static struct str_list _log_dev_alias;

static int _verbose_level = 0;
static int _test = 0;
static int _partial = 0;
static int _pvmove = 0;
static int _debug_level = 0;
static int _syslog = 0;
static int _log_to_file = 0;
static int _log_direct = 0;
static int _log_while_suspended = 0;
static int _indent = 1;
static int _log_cmd_name = 0;
static int _log_suppress = 0;
static int _ignorelockingfailure = 0;
static char _cmd_name[30] = "";
static char _msg_prefix[30] = "  ";
static int _already_logging = 0;

void init_log_file(const char *log_file, int append)
{
	const char *open_mode = append ? "a" : "w";

	if (!(_log_file = fopen(log_file, open_mode))) {
		log_sys_error("fopen", log_file);
		return;
	}

	_log_to_file = 1;
}

void init_log_direct(const char *log_file, int append)
{
	const char *filename;
	int open_flags = append ? 0 : O_TRUNC;

	filename = dbg_strdup(log_file);
	dev_create_file(filename, &_log_dev, &_log_dev_alias);
	if (!dev_open_flags(&_log_dev, O_RDWR | O_CREAT | open_flags, 1, 0))
		return;

	_log_direct = 1;
}

void init_log_while_suspended(int log_while_suspended)
{
	_log_while_suspended = log_while_suspended;
}

void init_syslog(int facility)
{
	openlog("lvm", LOG_PID, facility);
	_syslog = 1;
}

void log_suppress(int suppress)
{
	_log_suppress = suppress;
}

void release_log_memory(void)
{
	dbg_free((char *) _log_dev_alias.str);
	_log_dev_alias.str = "activate_log file";
}

void fin_log(void)
{
	if (_log_direct) {
		dev_close(&_log_dev);
		_log_direct = 0;
	}

	if (_log_to_file) {
		fclose(_log_file);
		_log_to_file = 0;
	}
}

void fin_syslog()
{
	if (_syslog)
		closelog();
	_syslog = 0;
}

void init_verbose(int level)
{
	_verbose_level = level;
}

void init_test(int level)
{
	if (!_test && level)
		log_print("Test mode: Metadata will NOT be updated.");
	_test = level;
}

void init_partial(int level)
{
	_partial = level;
}

void init_pvmove(int level)
{
	_pvmove = level;
}

void init_ignorelockingfailure(int level)
{
	_ignorelockingfailure = level;
}

void init_cmd_name(int status)
{
	_log_cmd_name = status;
}

void set_cmd_name(const char *cmd)
{
	if (!_log_cmd_name)
		return;
	strncpy(_cmd_name, cmd, sizeof(_cmd_name));
	_cmd_name[sizeof(_cmd_name) - 1] = '\0';
}

void init_msg_prefix(const char *prefix)
{
	strncpy(_msg_prefix, prefix, sizeof(_msg_prefix));
	_msg_prefix[sizeof(_msg_prefix) - 1] = '\0';
}

void init_indent(int indent)
{
	_indent = indent;
}

int test_mode()
{
	return _test;
}

int partial_mode()
{
	return _partial;
}

int pvmove_mode()
{
	return _pvmove;
}

int ignorelockingfailure()
{
	return _ignorelockingfailure;
}

void init_debug(int level)
{
	_debug_level = level;
}

int debug_level()
{
	return _debug_level;
}

void print_log(int level, const char *file, int line, const char *format, ...)
{
	va_list ap;
	char buf[1024];
	int bufused, n;

	if (!_log_suppress) {
		va_start(ap, format);
		switch (level) {
		case _LOG_DEBUG:
			if (!strcmp("<backtrace>", format))
				break;
			if (_verbose_level > 2) {
				printf("%s%s", _cmd_name, _msg_prefix);
				if (_indent)
					printf("      ");
				vprintf(format, ap);
				putchar('\n');
			}
			break;

		case _LOG_INFO:
			if (_verbose_level > 1) {
				printf("%s%s", _cmd_name, _msg_prefix);
				if (_indent)
					printf("    ");
				vprintf(format, ap);
				putchar('\n');
			}
			break;
		case _LOG_NOTICE:
			if (_verbose_level) {
				printf("%s%s", _cmd_name, _msg_prefix);
				if (_indent)
					printf("  ");
				vprintf(format, ap);
				putchar('\n');
			}
			break;
		case _LOG_WARN:
			printf("%s%s", _cmd_name, _msg_prefix);
			vprintf(format, ap);
			putchar('\n');
			break;
		case _LOG_ERR:
			fprintf(stderr, "%s%s", _cmd_name, _msg_prefix);
			vfprintf(stderr, format, ap);
			fputc('\n', stderr);
			break;
		case _LOG_FATAL:
		default:
			fprintf(stderr, "%s%s", _cmd_name, _msg_prefix);
			vfprintf(stderr, format, ap);
			fputc('\n', stderr);
			break;
			;
		}
		va_end(ap);
	}

	if (level > _debug_level)
		return;

	if (_log_to_file && (_log_while_suspended || !memlock())) {
		fprintf(_log_file, "%s:%d %s%s", file, line, _cmd_name,
			_msg_prefix);

		va_start(ap, format);
		vfprintf(_log_file, format, ap);
		va_end(ap);

		fprintf(_log_file, "\n");
		fflush(_log_file);
	}

	if (_syslog && (_log_while_suspended || !memlock())) {
		va_start(ap, format);
		vsyslog(level, format, ap);
		va_end(ap);
	}

	/* FIXME This code is unfinished - pre-extend & condense. */
	if (!_already_logging && _log_direct && memlock()) {
		_already_logging = 1;
		memset(&buf, ' ', sizeof(buf));
		bufused = 0;
		if ((n = lvm_snprintf(buf, sizeof(buf) - bufused - 1,
				      "%s:%d %s%s", file, line, _cmd_name,
				      _msg_prefix)) == -1)
			goto done;

		bufused += n;

		va_start(ap, format);
		n = vsnprintf(buf + bufused - 1, sizeof(buf) - bufused - 1,
			      format, ap);
		va_end(ap);
		bufused += n;

	      done:
		buf[bufused - 1] = '\n';
		buf[bufused] = '\n';
		buf[sizeof(buf) - 1] = '\n';
		/* FIXME real size bufused */
		dev_append(&_log_dev, sizeof(buf), buf);
		_already_logging = 0;
	}
}
