/*
** SellaNMS
** $Id: log.cxx,v 1.23 2005/03/31 18:57:12 sella Exp $
** Copyright (c) 2001-2005 Digital Genesis Software, LLC. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
*/

#ifdef HAVE_CONFIG_H
	#include "config.h"
#endif

#include "log.h"
#include "debug.h"
#include <stdio.h>
#include <sys/stat.h>
#include <stl/string>
#include <stl/vector>

Log::Log() {
	this->name.append(" log");

	has_execute = true;
	has_scheduler = false;
}

Log::~Log() {
}

RESULT Log::execute() {
	RESULT result = SUCCESS;

	if ((result = readOutput("log")) != SUCCESS) {
		LMSG("NOTICE: Log failed to read output from storage.");
		result = FAILURE;
	} else {
		while (!outputEmpty()) {
			/* Default policy_input. */
			polInput.clear();
			polOutput.clear();

			/* Get next policy_input */
			if (result = policyInputNext(polInput) != SUCCESS) {
				LMSG("NOTICE: Failed to obtain next policy input.");
				break;
			}

			/* Run policy on input data. */
			if (outputPolicy.evaluate(polOutput, polInput) == ACTION_REJECT) {
				LMSG("NOTICE: Failed to evaluate policy.");
				result = FAILURE;
				break;
			}

			processPolicyOutput(polOutput);
		}
	}

	return result;
}

RESULT Log::readConfig() {
	RESULT result = SUCCESS;

	vector<string> v;

	/* Add and compile input policy. */
	if (config->assignValues("policy input", v)) {
		LMSG("NOTICE: Input policies are not implemented yet.");
		if (inputPolicy.addPolicies(config, v) == FAILURE) {
			LMSG("NOTICE: Failed to add/compile input policies.");
			result = FAILURE;
		}
	}

	/* Add and compile output policy. */
	if (config->assignValues("policy output", v)) {
		if (outputPolicy.addPolicies(config, v) == FAILURE) {
			LMSG("NOTICE: Failed to add/compile output policies.");
			result = FAILURE;
		}
	}

	/* Verify we have required policies. */
	if (outputPolicy.empty()) {
		LMSG("NOTICE: A output policy is required for this module.");
		result = FAILURE;
	}

	/* Large queue-depth results in increased RAM usage, during high event periods. An overwhelmed system could grow out of control. */
	if (config->assignValue("queue-depth", queueDepth, MODULE_OUTPUT_LOG_QUEUEDEPTH_DEFAULT)) {
		if (queueDepth < 1) {
			queueDepth = MODULE_OUTPUT_LOG_QUEUEDEPTH_DEFAULT;
		}
	}

   /* Configure Storage */
   storage.setConfig(*config);

	return result;
}

RESULT Log::printConfig() {
	LMSG("queue-depth: %lu", this->queueDepth);

	return Output::printConfig();
}

RESULT Log::processPolicyOutput(variable_binding_t &policy_output) {
	RESULT result = SUCCESS;

	int fd;
	FILE *fp;
	string file, entry;

	/* Build out log entry. */
	if (policy_output.find("entry") != policy_output.end()) {
		entry = policy_output["entry"];
	} else {
		if (policy_output.find("brief") != policy_output.end()) {
			entry = policy_output["brief"];
		}

		if (policy_output.find("detail") != policy_output.end()) {
			if (!entry.empty()) {
				entry.append(" - ");
			}
			entry.append(policy_output["detail"]);
		}
	}

	/* Check for log content. Exit if none. */
	if (entry.empty()) {
		LMSG("DEBUG: Policy contained no entry -- not writing to file.");
		return SUCCESS;
	}

	/* Add a newline. */
	entry.append("\n");

	/* Check for a file name. */
	if (policy_output.find("file") == policy_output.end()) {
		LMSG("DEBUG: Policy contained no filename -- not writing to file.");
		return SUCCESS;
	}

	/* Grab the log filename. */
	file = policy_output["file"];

	/* Rotate log, if needed. */
	//FIXME: Rotate is truncating the file each time.
	rotate(file, policy_output);

#if 0
	LMSG("DEUBG: Writing '%s' to file '%s'", entry.c_str(), file.c_str());
#endif
	fp = fopen(file.c_str(), "a"); /* Append only. */
	if (fp == NULL) {
		LMSG("WARNING: Failed to open log file '%s'", file.c_str());

		return FAILURE;
	}
	
	/* Write out log entry. */
	if (fwrite(entry.c_str(), 1, entry.size(), fp) < entry.size()) {
		LMSG("WARNING: Short write to log file '%s'", file.c_str());

		result = FAILURE;
	}

	/* Close off log. */
	fclose(fp);

	return result;
}

RESULT Log::rotate(string &file, variable_binding_t &policy_output) {
	RESULT result = SUCCESS;

	int fd;
	FILE *fp;
	char old_file[256], new_file[256];
	bool use_gzip = false, use_bzip2 = false;
	string str;
	struct stat file_stat;
	off_t file_size;
	unsigned long rotate;

	/* Check for file size. If non, no rotation is needed. */
	if (policy_output.find("size") == policy_output.end()) {
		return SUCCESS;
	}

	str = policy_output["size"];
	file_size = strtoul(str.c_str(), NULL, 10);

	if (stat(file.c_str(), &file_stat) != 0) {
		LMSG("WARNING: Failed to stat log file '%s'", file.c_str());
		
		return FAILURE;
	} else if (file_stat.st_size > file_size) {
		/* Chedk for rotate. Rotate log if needed. */
		if (policy_output.find("compress") != policy_output.end()) {
			str = policy_output["compress"];
			if (!str.compare("yes") || !str.compare("gzip")) {
				use_gzip = true;
			} else if (!str.compare("bzip2")) { 
				use_bzip2 = true;
			}
		}

		/* Chedk for rotate. Rotate log if needed. */
		if (policy_output.find("rotate") != policy_output.end()) {
			str = policy_output["rotate"];
			rotate = strtoul(str.c_str(), NULL, 10);
			if (rotate > 0) {
				for (unsigned long i = rotate; i > 0; i--) {
					/* Build new and old file names. */
					if (i > 1) {
						snprintf(old_file, 256, "%s.%lu", file.c_str(), i - 1);
					} else {
						snprintf(old_file, 256, "%s", file.c_str());
					}
					snprintf(new_file, 256, "%s.%lu", file.c_str(), i);

					/* Rename files. */
					if (rename(old_file, new_file) != 0) {
						LMSG("WARNING: Failed to rename file '%s' to '%s'.", old_file, new_file);
					}

					/* Compress if needed. */
					if (use_gzip) {
						/* FIXME: Also need to change old_file and new_file to have .gz extensions. */
						LMSG("NOTICE: Unable to compress '%s' with gzip. Not coded yet..", new_file);
					} else if (use_bzip2) {
						LMSG("NOTICE: Unable to compress '%s' with bzip2. Not coded yet..", new_file);
					}
				}
			} else {
				/* Truncate to 0. */
				if (truncate(file.c_str(), 0) != 0) {
					LMSG("WARNING: Failed to truncate log file '%s'.", file.c_str());

					result = FAILURE;
				}
			}
		} else {
			/* Truncate to 0. */
			if (truncate(file.c_str(), 0) != 0) {
				LMSG("WARNING: Failed to truncate log file '%s'.", file.c_str());

				result = FAILURE;
			}
		}
	}

	return result;
}

/*
** Local Variables:
** c-basic-offset: 3
** tab-width: 3
** End:
** vim600: fdm=marker
** vim: noet ts=3 sw=3
*/
