/*
 * This is an auditd plugin for sending auditd data
 * to clickhouse DB.
 * Copyright (C) 2020 Aleksei Nikiforov <darktemplar@basealt.ru>
 *
 * 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 3 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 <https://www.gnu.org/licenses/>.
 *
 */

#include "logging.hpp"

#include <time.h>

#include <boost/scope_exit.hpp>

std::mutex Logger::s_mutex;

Logger::Logger()
	: m_initialized(false)
{
}

Logger::~Logger()
{
	dump_impl();
}

Logger& Logger::instance()
{
	static Logger s_instance;
	return s_instance;
}

void Logger::open(const char *filename)
{
	std::lock_guard<std::mutex> lock(s_mutex);
	instance().open_impl(filename);
}

void Logger::initialize()
{
	std::lock_guard<std::mutex> lock(s_mutex);
	instance().initialize_impl();
}

void Logger::write(const char *format, ...)
{
	std::lock_guard<std::mutex> lock(s_mutex);

	va_list args;

	va_start(args, format);
	instance().write_impl(format, args);
	va_end(args);
}

void Logger::dump()
{
	std::lock_guard<std::mutex> lock(s_mutex);
	instance().dump_impl();
}

void Logger::open_impl(const char *filename)
{
	m_logfile_name = filename;

	initialize_impl();
}

void Logger::initialize_impl()
{
	m_initialized = true;
}

void Logger::write_impl(const char *format, va_list args)
{
	FILE *outfile = NULL;

	std::string datestring = getdatestring();

	if (!m_logfile_name.empty())
	{
		outfile = fopen(m_logfile_name.c_str(), "at");
	}

	if (outfile != NULL)
	{
		fprintf(outfile, "%s: ", datestring.c_str());
		vfprintf(outfile, format, args);
		fprintf(outfile, "\n");

		fclose(outfile);
	}
	else
	{
		// save logged data for later use
		char *data = NULL;

		BOOST_SCOPE_EXIT(&data)
		{
			if (data != NULL)
			{
				free(data);
			}
		} BOOST_SCOPE_EXIT_END;

		int result = vasprintf(&data, format, args);

		if ((result >= 0) && (data != NULL))
		{
			m_lines.push_back(std::make_pair(datestring,data));
		}

		if (m_initialized)
		{
			dump_impl();
		}
	}
}

void Logger::dump_impl()
{
	FILE *outfile = NULL;

	if (!m_logfile_name.empty())
	{
		outfile = fopen(m_logfile_name.c_str(), "at");
	}

	for (auto iter = m_lines.begin(); iter != m_lines.end(); ++iter)
	{
		fprintf((outfile != NULL) ? outfile : stderr, "%s: %s\n", iter->first.c_str(), iter->second.c_str());
	}

	if (outfile != NULL)
	{
		fclose(outfile);
	}
	else
	{
		fflush(stderr);
	}

	m_lines.clear();
}

std::string Logger::getdatestring() const
{
	std::string result;

	time_t t = time(nullptr);
	struct tm tm_inst;

	auto tm_ptr = localtime_r(&t, &tm_inst);

	if (tm_ptr != nullptr)
	{
		char buffer[4096];

		auto buffer_res = strftime(buffer, sizeof(buffer) - 1, "%c %Z", tm_ptr);
		if (buffer_res > 0)
		{
			buffer[buffer_res] = 0;

			result = buffer;
		}
	}

	return result;
}