/*
** SellaNMS
** $Id: syslog.cxx,v 1.27 2006/01/24 07:08:10 sella Exp $
** Copyright (c) 2001-2005 Digital Genesis Software, LLC. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
*/

static const char rcsid[] = "$Id: syslog.cxx,v 1.27 2006/01/24 07:08:10 sella Exp $";

#include "daemon.h"
#include "syslog.h"
#include "common/ip_util.h"
#include "storage/storage.h"
#include "state/state.h"
#include <fcntl.h>
#include <string.h>
#include <netdb.h>
#include <ctype.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <stl/vector>

#define BUF_SIZE 2024
#define WAIT_AND_KILL

extern ConfigNode* config;
extern bool interrupt;

Syslog::Syslog(): port(0), dupHist(NULL), dupHistCur(0), exitAfter(0), nextWarning(0) {
	pthread_mutex_init(&syslog_mutex, NULL);

	flag_init(syslog_flag);
}

Syslog::~Syslog() {
	pthread_mutex_destroy(&syslog_mutex);

	SAFE_DELETE_ARRAY(this->dupHist);
}

RESULT Syslog::readConfig() {
	RESULT result = SUCCESS;
	string str;
	vector<string> v;
	vector<string>::iterator v_it;
	struct servent *s_ent;
	struct in_addr ip;
	ConfigNode node;

	if (!config->assignNode(getConfigPath(), node)) {
		LMSG("No '%s' section found within the config file.", getConfigPath());

		return FAILURE;
	}

	if (node.assignValue("port", str)) {
		this->port = htons((unsigned short) strtoul(str.c_str(), NULL, 10));
	} else {
		s_ent = getservbyname("syslog", "udp");
		if (s_ent) {
			this->port = s_ent->s_port;
		} else {
			this->port = htons(MONITOR_SYSLOG_PORT_DEF);
		}
	}

	if (node.assignValue("source", str)) {
		if (inet_pton(AF_INET, str.c_str(), &ip) == 0) {
			this->source = htonl(INADDR_ANY);
		} else {
			this->source = ip.s_addr;
		}
	} else if (node.assignValue("interface", str)) {
		source_ip(str.c_str(), &ip);
		this->source = ip.s_addr;
	} else {
		this->source = htonl(INADDR_ANY);
	}

	node.assignValue("queue-depth", queueDepth, MONITOR_SYSLOG_QUEUEDEPTH_DEF, MONITOR_SYSLOG_QUEUEDEPTH_MIN, MONITOR_SYSLOG_QUEUEDEPTH_MAX);

	node.assignValue("duplicate-history-max", dupHistMax, MONITOR_SYSLOG_DUPHISTMAX_DEF);
	/* If needed, allocate memory and clear (sets as stale). */
	if (this->dupHistMax > 0) {
		this->dupHist = new dup_hist_t[this->dupHistMax];
		bzero(this->dupHist, sizeof(dup_hist_t) * this->dupHistMax);
	}

	node.assignValue("duplicate-history-age", dupHistAge, MONITOR_SYSLOG_DUPHISTAGE_DEF);

	node.assignValue("duplicate-fuzz-factor", dupFuzFactor, MONITOR_SYSLOG_DUPFUZFACTOR_DEF);
	/* Exists after X syslog messages are recieved. Useful for memory debugging. */
	node.assignValue("exit-after", exitAfter, MONITOR_SYSLOG_EXITAFTER_DEF);

	node.assignValue("rate-interval", rateInterval, MONITOR_SYSLOG_RATEINTERVAL_DEF, MONITOR_SYSLOG_RATEINTERVAL_MIN, MONITOR_SYSLOG_RATEINTERVAL_MAX);

	node.assignValue("rate-clean", rateClean, MONITOR_SYSLOG_RATECLEAN_DEF, MONITOR_SYSLOG_RATECLEAN_MIN, MONITOR_SYSLOG_RATECLEAN_MAX);

	/* Clear any existing policies. */
	inputPolicy.clear();
	outputPolicy.clear();

	/* Add and compile input policy. */
	if (node.assignValues("policy input", v)) {
		/* These are actually just added to the front of the output policies. */
		if (inputPolicy.addPolicies(config, v) == FAILURE) {
			LMSG("NOTICE: Failed to add/compile input policies.");

			return FAILURE;
		}
	}

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

			return FAILURE;
		}
	}

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

		return FAILURE;
	}

	/* Configure Storage */
	if (storage.setConfig(*config) != SUCCESS) {
		result = FAILURE;
	}

	printConfig();

	return result;
}

void Syslog::printConfig() {
	string str;
	vector<string> v;
	vector<string>::iterator v_it;

	struct in_addr ip;
	char buf[INET_ADDRSTRLEN];

	if (!TRACE_ISSET(TRACE_CONFIG)) {
		return;
	}

	TRACE(TRACE_CONFIG, "port: %lu", ntohs(this->port));
	TRACE(TRACE_CONFIG, "queue-depth: %lu", this->queueDepth);
	TRACE(TRACE_CONFIG, "duplicate-history-max: %lu entries", this->dupHistMax);
	TRACE(TRACE_CONFIG, "duplicate-history-age: %lu sec", this->dupHistAge);
	TRACE(TRACE_CONFIG, "duplicate-fuzz-factor: %lu", this->dupFuzFactor);
	TRACE(TRACE_CONFIG, "exit-after: %lu", this->exitAfter);
	TRACE(TRACE_CONFIG, "rate-interval: %lu", this->rateInterval);
	TRACE(TRACE_CONFIG, "rate-clean: %lu", this->rateClean);
	ip.s_addr = this->source;
	inet_ntop(AF_INET, &ip, buf, INET_ADDRSTRLEN);
	TRACE(TRACE_CONFIG, "source: %s", buf);

	if (config->assignValues("monitor syslog policy input", v)) {
		str.erase();
		for (v_it = v.begin(); v_it != v.end(); ++v_it) {
			str.append(*v_it + " ");
		}
		TRACE(TRACE_CONFIG, "policy input: %s", str.c_str());
	} else {
		TRACE(TRACE_CONFIG, "policy input: none");
	}

	if (config->assignValues("monitor syslog policy output", v)) {
		str.erase();
		for (v_it = v.begin(); v_it != v.end(); ++v_it) {
			str.append(*v_it + " ");
		}
		TRACE(TRACE_CONFIG, "policy output: %s", str.c_str());
	} else {
		TRACE(TRACE_CONFIG, "policy output: none");
	}

	return;
}

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

	int err;
	pthread_t thread_id = 0;

	slist<pthread_t> t_list;
	slist<pthread_t>::iterator it;

	TRACE(TRACE_THREAD, "Syslog::execute() starting");

	/* Create the storage thread */
	if ((err = pthread_create(&thread_id, NULL, (void* (*)(void*))Syslog::policy_thread, this)) == 0) {
		t_list.push_front(thread_id);
	}

	/* Create the receiver thread */
	if (err == 0) {
		if ((err = pthread_create(&thread_id, NULL, (void* (*)(void*))Syslog::receive_thread, this)) == 0) {
			t_list.push_front(thread_id);
		}
	}

	/* Join each thread to free its resources. */
	for (it = t_list.begin(); it != t_list.end(); ++it) {
		/* Wait for thread to complete. */
		if ((err = pthread_join(*it, NULL)) != 0) {
			LMSG("pthread_join returned %d", err);
		}
	}

	TRACE(TRACE_THREAD, "Syslog::execute() exiting");

	return result;
}

RESULT Syslog::receive_thread(void *args) {
	RESULT result = SUCCESS;	

	Syslog *instance = (Syslog*) args;

	received_queue_t *syslog_queue = &(instance->syslog_queue);
	pthread_mutex_t *syslog_mutex = &(instance->syslog_mutex);

	int s, n, err;
	struct protoent *protocol;
	struct sockaddr_in socketaddr;
	char buf[BUF_SIZE], buf2[INET_ADDRSTRLEN];
	pair<string, string> raw_msg;
	socklen_t len = 0;
	unsigned long rx_count = 0;

	TRACE(TRACE_THREAD, "Syslog::receive_thread() starting main loop");

	fd_set	sockets;
	timeval	timeout;

	bzero((char*) &socketaddr, sizeof(socketaddr));
	socketaddr.sin_family = AF_INET;
	socketaddr.sin_addr.s_addr = instance->source;
	socketaddr.sin_port = instance->port;

	/* Bind to socket. */
	grantPriv();

	if (!(protocol = getprotobyname("udp"))) {
		LMSG("failed to resolve protocol by name.");
		interrupt = true;
		result = FAILURE;
	} else if ((s = socket(PF_INET, SOCK_DGRAM, protocol->p_proto)) == -1) {
		LMSG("failed to open socket.");
		interrupt = true;
		result = FAILURE;
	} else if ((err = bind(s, (struct sockaddr*) &socketaddr, sizeof(socketaddr))) < 0) {
		inet_ntop(AF_INET, &socketaddr.sin_addr, buf2, INET_ADDRSTRLEN);
		strerror_r(err, buf, sizeof(buf));
		LMSG("failed to bind to %s:%d: %s", buf2, ntohs(instance->port), buf);
		interrupt = true;
		result = FAILURE;
	}

	dropPriv();

	/* Initialize the rate object */
	if (TRACE_ISSET(TRACE_DUMP)) {
		instance->rate.setDebug(true);
	}
	instance->rate.setInterval(instance->rateInterval);
	instance->rate.setClean(instance->rateClean);

	/* Main receive loop */
	while (!interrupt) {
		if (instance->exitAfter > 0 && rx_count >= instance->exitAfter) {
			LMSG("NOTICE: Syslog::recieve_thread exiting. We have now recieved %lu syslog messages (exit-after: %lu).", rx_count, instance->exitAfter);
			interrupt = true;
		} else {
			FD_ZERO(&sockets);
			FD_SET(s, &sockets);

			timeout.tv_sec = 1;
			timeout.tv_usec = 0;

			if (select(s + 1, &sockets, NULL, NULL, &timeout) == 1) {
				len = sizeof(struct sockaddr_in);
				if ((n = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*) &socketaddr, &len)) != -1) {
					buf[n] = '\0'; /* Remove trailing garbage. */
					rx_count++;

					if (instance->suppressDuplicate((u_int32_t*) &socketaddr.sin_addr.s_addr, buf, n) == false) {
						inet_ntop(AF_INET, &socketaddr.sin_addr, buf2, INET_ADDRSTRLEN);
						raw_msg.first = buf2;
						raw_msg.second = buf;

						TRACE(TRACE_INTERNAL, "Syslog::queueDepth == %lu, rx_count: %lu", instance->queueDepth, rx_count);

						if (syslog_queue->size() >= instance->queueDepth) {
							if (time(NULL) >= instance->nextWarning) {
								instance->nextWarning = time(NULL) + MONITOR_SYSLOG_NEXTWARN_DEF;
								LMSG("NOTICE: Syslog queue depth exceeded %lu. Dropping entry from %s: %s (Suppressing warning for %lus)", instance->queueDepth, raw_msg.first.c_str(), raw_msg.second.c_str(), MONITOR_SYSLOG_NEXTWARN_DEF);
							}
						} else {
							pthread_mutex_lock(syslog_mutex);
								syslog_queue->push(raw_msg);
							pthread_mutex_unlock(syslog_mutex);

							/* Set the syslog flag. */
							flag_set(instance->syslog_flag);
						}
					}
				} else {
					LMSG("WARNING: Error received from recvfrom()");
					interrupt = true;
				}
			}
		}
	}

	/* Close our socket. */
	close(s);

	TRACE(TRACE_THREAD, "Syslog::receive_thread() exiting main loop");

	pthread_exit(0);
}

bool Syslog::suppressDuplicate(u_int32_t *source, char* msg, int n) {
	bool ret_val = false;
	unsigned long signature = 0;
	struct in_addr ip;
	time_t stale = time(NULL) - dupHistAge;
	char buf[INET_ADDRSTRLEN];

	/* If no dup history, then no duplicates to detect. */
	if (dupHist == NULL) {
		return false;
	}

	/* Generate the signature. */
	for (int x = 0; x < n; x++) {
		signature += msg[x];
	}

	/* Check for the duplicate */
	for (int x = 0; x < dupHistMax; x++) {
		/* If the source is the same, and the entry isn't stale check for a duplicate. */
		if (dupHist[x].source == *source && dupHist[x].timestamp >= stale) {
			/* Is the entry close enough to be a duplicate? */
			if (dupHist[x].signature - dupFuzFactor <= signature && dupHist[x].signature + dupFuzFactor >= signature) {
				if (TRACE_ISSET(TRACE_DEBUG)) {
					ip.s_addr = *source;
					inet_ntop(AF_INET, &ip, buf, INET_ADDRSTRLEN);
					TRACE(TRACE_DEBUG, "Syslog duplicate detected (age: %d, var: %d) from %s: %s", time(NULL) - dupHist[x].timestamp, dupHist[x].signature - signature, buf, msg);
				}
				ret_val = true;
				break;
			}
		}
	}

	/* Insert new entry into history. */
	TRACE(TRACE_INTERNAL, "Syslog entry inserted into position %lu/%lu", dupHistCur, dupHistMax);

	dupHist[dupHistCur].source = *source;
	dupHist[dupHistCur].timestamp = time(NULL);
	dupHist[dupHistCur].signature = signature;

	/* Increment current entry. Ring buffer. */
	dupHistCur = (dupHistCur + 1 >= dupHistMax) ? 0 : dupHistCur + 1;

	return ret_val;
}

RESULT Syslog::policy_thread(void *args) {
	RESULT result = SUCCESS;
	ACTION action;

	Syslog *instance = (Syslog*) args;

	received_queue_t *syslog_queue = &(instance->syslog_queue);
	pthread_mutex_t *syslog_mutex = &(instance->syslog_mutex);

	variable_binding_t policy_input;
	variable_binding_t policy_output;

	char buf[BUF_SIZE], *ret_buf, *tok, *p, *ptr;
	const char regex[] = "^(<([0-9]{1,3})>)?(([A-Z][a-z][a-z] [ 0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]) )?(([A-Za-z0-9_\\.-]+) )?(([]\\[\\/a-zA-Z0-9_\\-\\.\\(\\)]+): )?(.+)$";
	string raw;
	syslog_t syslog;
	pair<string, string> raw_msg;
	StorageData data;
	time_t t;
	struct tm now;

	IPv4State *ipv4_state;
	InterfaceState *interface_state;
	ElementState *element_state;

	TRACE(TRACE_THREAD, "Syslog::policy_thread() starting main loop");

	// Until we are asked to stop, run syslog input thru policy.
	while (!interrupt) {
		/* Wait for the syslog flag to get set. */
		flag_wait(instance->syslog_flag, &(interrupt));

		while (!syslog_queue->empty() && !interrupt) {
			/* Clean policy structs. */
			policy_input.clear();
			policy_output.clear();

			/* Fetch Data from queue */
			pthread_mutex_lock(syslog_mutex);
				raw_msg = syslog_queue->front();
				syslog_queue->pop();
			pthread_mutex_unlock(syslog_mutex);

			t = time(NULL);
			localtime_r(&t, &now);
			raw = raw_msg.second;

			TRACE(TRACE_INTERNAL, "raw: %s", raw.c_str());

			/* Break down valid syslog message */
			if ((ret_buf = ereg_replace(regex, "\\2|\\4|\\6|\\8|\\9", raw.c_str())) == NULL) {
				LMSG("failed to parse: %s", raw.c_str());
			} else {
				TRACE(TRACE_INTERNAL, "parsed: %s", ret_buf);

				/* PRI */
				if ((tok = strtok_r(ret_buf, "|", &p)) != NULL) {
					if (tok[0] != '\0' && tok[0] != '\\') {
						TRACE(TRACE_INTERNAL, "pri: %s", tok);

						syslog.priority = atoi(tok);
					} else {
						syslog.priority = LOG_MAKEPRI(1, 5);
					}
				} else {
					syslog.priority = LOG_MAKEPRI(1, 5);
				}

				/* timestamp (header) */ 
				if ((tok = strtok_r(NULL, "|", &p)) != NULL) {
					if (tok[0] != '\0' && tok[0] != '\\') {
						TRACE(TRACE_INTERNAL, "timestamp: %s", tok);

						syslog.timestamp = tok;
					}
				}

				/* hostname (header) */ 
				if ((tok = strtok_r(NULL, "|", &p)) != NULL) {
					if (tok[0] != '\0' && tok[0] != '\\') {
						TRACE(TRACE_INTERNAL, "hostname: %s", tok);

						syslog.hostname = tok;
					}
				}

				/* tag and pid (msg) */ 
				if ((tok = strtok_r(NULL, "|", &p)) != NULL) {
					if (tok[0] != '\0' && tok[0] != '\\') {
						/* Locate the pid. */
						if ((ptr = strchr(tok, '[')) == NULL) {
							TRACE(TRACE_INTERNAL, "tag: %s", tok);

							syslog.tag = tok;
						} else {
							*ptr = '\0'; /* Seperate tag from pid. */
							syslog.tag = tok;

							ptr++; /* Move ptr onto pid. */
							syslog.pid = ptr;
							syslog.pid.erase(syslog.pid.size() - 1); /* consume ']' */

							TRACE(TRACE_INTERNAL, "tag: %s", tok);
							TRACE(TRACE_INTERNAL, "pid: %s", syslog.pid.c_str());
						}
					}
				}

				/* content (msg) */ 
				if ((tok = strtok_r(NULL, "|", &p)) != NULL) {
					if (tok[0] != '\0' && tok[0] != '\\') {
						/* Handle special case regex doesn't catch. */
						if (!syslog.hostname.empty() && syslog.tag.empty()) {
							/* First portion of content ends up in hostname. Fix it. */
							syslog.content.append(syslog.hostname + " " + tok);
							syslog.hostname.erase();

							TRACE(TRACE_INTERNAL, "content: %s (generated)", syslog.content.c_str());
						} else {
							TRACE(TRACE_INTERNAL, "content: %s", tok);

							syslog.content = tok;
						}
					}
				}

				/* If no timestamp was found, insert one */
				if (syslog.timestamp.empty()) {
					strftime(buf, sizeof(buf), "%b %e %T", &now);
					syslog.timestamp = buf;

					TRACE(TRACE_INTERNAL, "timestamp: %s (generated)", buf);
				}

				/* If no hostname was found, insert one */
				if (syslog.hostname.empty()) {
					syslog.hostname = raw_msg.first;

					TRACE(TRACE_INTERNAL, "hostname: %s (generated)", syslog.hostname.c_str());
				}

				/* Build header */
				syslog.header.append(syslog.timestamp + " " + syslog.hostname);
				TRACE(TRACE_INTERNAL, "header: %s (generated)", syslog.header.c_str());

				/* Build msg */
				if (syslog.tag.empty()) {
					syslog.msg = syslog.content;
				} else if (syslog.pid.empty()) {
					syslog.msg.append(syslog.tag + ": " + syslog.content);
				} else {
					syslog.msg.append(syslog.tag + "[" + syslog.pid + "]: " + syslog.content);
				}
				TRACE(TRACE_INTERNAL, "msg: %s (generated)", syslog.msg.c_str());

				SAFE_FREE(ret_buf);
			}

			/* Setup policy_input structure. */
			policy_input["protocol"] = "syslog";
			policy_input["origin"] = "network";
			policy_input["raw"] = raw_msg.second;
			policy_input["source"] = raw_msg.first;
			policy_input["source-except"] = raw_msg.first;
			policy_input["header"] = syslog.header;
			policy_input["timestamp"] = syslog.timestamp;
			policy_input["hostname"] = syslog.hostname;
			policy_input["tag"] = syslog.tag;
			policy_input["pid"] = syslog.pid;
			policy_input["content"] = syslog.content;
			policy_input["msg"] = syslog.msg;
			snprintf(buf, sizeof(buf), "%d", syslog.priority);
			policy_input["priority"] = buf;
			strftime(buf, sizeof(buf), "%T", &now);
			policy_input["time"] = buf;
			strftime(buf, sizeof(buf), "%A", &now);
			policy_input["day-of-week"] = buf;

			switch (LOG_PRI(syslog.priority)) {
				case LOG_EMERG:
					policy_input["severity"] = "emerg";
					break;

				case LOG_ALERT:
					policy_input["severity"] = "alert";
					break;

				case LOG_CRIT:
					policy_input["severity"] = "crit";
					break;

				case LOG_ERR:
					policy_input["severity"] = "err";
					break;

				case LOG_WARNING:
					policy_input["severity"] = "warning";
					break;

				case LOG_NOTICE:
					policy_input["severity"] = "notice";
					break;

				case LOG_INFO:
					policy_input["severity"] = "info";
					break;

				case LOG_DEBUG:
					policy_input["severity"] = "debug";
					break;

				default:
					snprintf(buf, sizeof(buf), "%d", LOG_PRI(syslog.priority));
					policy_input["severity"] = buf;
			}

			switch (LOG_FAC(syslog.priority) << 3) {
				case LOG_KERN:
					policy_input["facility"] = "kern";
					break;

				case LOG_USER:
					policy_input["facility"] = "user";
					break;

				case LOG_MAIL:
					policy_input["facility"] = "mail";
					break;

				case LOG_DAEMON:
					policy_input["facility"] = "daemon";
					break;

				case LOG_AUTH:
					policy_input["facility"] = "auth";
					break;

				case LOG_SYSLOG:
					policy_input["facility"] = "syslog";
					break;

				case LOG_LPR:
					policy_input["facility"] = "lrp";
					break;

				case LOG_NEWS:
					policy_input["facility"] = "news";
					break;

				case LOG_UUCP:
					policy_input["facility"] = "uucp";
					break;

				case LOG_CRON:
					policy_input["facility"] = "cron";
					break;

				case LOG_AUTHPRIV:
					policy_input["facility"] = "authpriv";
					break;

				case LOG_FTP:
					policy_input["facility"] = "ftp";
					break;

				case (12<<3):
					policy_input["facility"] = "ntp";
					break;

				case (13<<3):
					policy_input["facility"] = "audit";
					break;

				case (14<<3):
					policy_input["facility"] = "alert";
					break;

				case (15<<3):
					policy_input["facility"] = "at";
					break;

				case LOG_LOCAL0:
					policy_input["facility"] = "local0";
					break;

				case LOG_LOCAL1:
					policy_input["facility"] = "local1";
					break;

				case LOG_LOCAL2:
					policy_input["facility"] = "local2";
					break;

				case LOG_LOCAL3:
					policy_input["facility"] = "local3";
					break;

				case LOG_LOCAL4:
					policy_input["facility"] = "local4";
					break;

				case LOG_LOCAL5:
					policy_input["facility"] = "local5";
					break;

				case LOG_LOCAL6:
					policy_input["facility"] = "local6";
					break;

				case LOG_LOCAL7:
					policy_input["facility"] = "local7";
					break;

				default:
					snprintf(buf, sizeof(buf), "%d", LOG_FAC(syslog.priority));
					policy_input["facility"] = buf;
			}

			/* Default syslog structure. */
			syslog.priority = 0;
			syslog.header.erase();
			syslog.timestamp.erase();
			syslog.hostname.erase();
			syslog.msg.erase();
			syslog.tag.erase();
			syslog.pid.erase();
			syslog.content.erase();

			/* Rate code */
			if (instance->rateInterval > 0) {
				instance->rate.increment(policy_input["source"]);
				snprintf(buf, sizeof(buf), "%u", instance->rate.getRate(policy_input["source"]));
				policy_input["rate"] = buf;
				snprintf(buf, sizeof(buf), "%u", instance->rate.getRate(policy_input["source"], instance->rateInterval / 2));
				policy_input["half-rate"] = buf;
			}

			/* Run input policy over policy_input. */
			t = time(NULL);
			action = instance->inputPolicy.evaluate(policy_output, policy_input);
			TRACE(TRACE_TIMER, "inputPolicy.evaluate() ran %lu sec for raw: %s", time(NULL) - t, policy_input["raw"].c_str());

			if (action == ACTION_REJECT) {
				TRACE(TRACE_DEBUG, "Input policy rejected message from %s. Proceeding to next message.", policy_input["source"].c_str());

				continue;
			} else if (action == ACTION_FAILURE) {
				LMSG("Input policy evaluation failed on message from %s. Shutting down. raw: %s", policy_input["source"].c_str(), policy_input["raw"].c_str());

				interrupt = true;
				result = FAILURE;

				continue;
			}

			/* Copy data over for output policy. */
			policy_input = policy_output;
			policy_output.clear();

			/* Run output policy over policy_input. */
			t = time(NULL);
			action = instance->outputPolicy.evaluate(policy_output, policy_input);
			TRACE(TRACE_TIMER, "outputPolicy.evaluate() ran %lu sec for raw: %s", time(NULL) - t, policy_input["raw"].c_str());

			if (action == ACTION_REJECT) {
				TRACE(TRACE_DEBUG, "Output policy rejected message from %s. Proceeding to next message.", policy_input["source"].c_str());

				continue;
			} else if (action == ACTION_FAILURE) {
				LMSG("Output policy evaluation failed on message from %s. Shutting down. Raw: %s", policy_input["source"].c_str(), policy_input["raw"].c_str());

				interrupt = true;
				result = FAILURE;

				continue;
			}

			/*State code */
			if (policy_output.find("state") != policy_output.end()) {
				if (policy_output.find("ip-id") != policy_output.end()) {
					ipv4_state = new IPv4State(&(instance->storage), strtol(policy_output["ip-id"].c_str(), NULL, 10), "syslog");
					ipv4_state->set(policy_output["state"].c_str());
					SAFE_DELETE(ipv4_state);
				} else if (policy_output.find("interface-id") != policy_output.end()) {
					interface_state = new InterfaceState(&(instance->storage), strtol(policy_output["interface-id"].c_str(), NULL, 10), "syslog");
					interface_state->set(policy_output["state"].c_str());
					SAFE_DELETE(interface_state);
				} else if (policy_output.find("element-id") != policy_output.end()) {
					element_state = new ElementState(&(instance->storage), strtol(policy_output["element-id"].c_str(), NULL, 10), "syslog");
					element_state->set(policy_output["state"].c_str());
					SAFE_DELETE(element_state);
				}
			}
		}
	}

	TRACE(TRACE_THREAD, "Syslog::policy_thread() exiting main loop");

	pthread_exit(0);
}

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