/*
** SellaNMS
** $Id: daemon.cxx,v 1.91 2005/05/21 23:10:28 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

static char rcsid[] = "$Id: daemon.cxx,v 1.91 2005/05/21 23:10:28 sella Exp $";

#include "daemon.h"
#include "debug.h"
#include "nprocs.h"
#include <pwd.h>
#include <grp.h>
#include <stdio.h>
#include <ctype.h>
#include <dlfcn.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <getopt.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/resource.h>
#ifdef HAVE_NET_SNMP
	#include <net-snmp/net-snmp-config.h>
	#include <net-snmp/net-snmp-includes.h>
	#include <net-snmp/library/parse.h>
#else
	#include <ucd-snmp/ucd-snmp-config.h>
	#include <ucd-snmp/ucd-snmp-includes.h>
	#include <ucd-snmp/parse.h>
#endif

#ifdef HAVE_SETPROCTITLE
	#include "setproctitle.h"
#endif

Daemon::Daemon():daemon_running(true), test_flag(false), usage_flag(false), about_flag(false), version_flag(false), monitor_flag(false), discover_flag(false), foreground_flag(false), debug(0), nice(21), major_version(1), minor_version(0), revision(0), lastSignal(0) {

	this->name.append(" daemon");
	this->project = DGS_PROJECT;

	config_dso = DEFAULT_CONFIG_DSO;
	config_file = DEFAULT_CONFIG_FILE;

	has_execute = false;
	has_scheduler = false;

	openlog(NULL, LOG_PID | LOG_PERROR, DEFAULT_SYSLOG_FACILITY);
}

Daemon::~Daemon() {
	SAFE_DELETE(config);

	unload_all_mibs();

	snmp_shutdown(DGS_PROJECT);
}

RESULT Daemon::start(int argc, char** argv) {
	RESULT result = SUCCESS;
	pid_t pid;
	long daemon_count, i;
	pthread_t thread_id;

	if ((result = processCommandLine(argc, argv)) == FAILURE || this->usage_flag) {
		usage();

		return FAILURE;
	} else if (this->about_flag) {
		about();

		return SUCCESS;
	} else if (this->version_flag) {
		version();

		return SUCCESS;
	} else if ((result = checkEuidRoot()) == FAILURE) {
		fprintf(stderr, "ERROR: %s must be run as root.\n", this->project.c_str());

		return FAILURE;
	}

#ifdef HAVE_SETPROCTITLE
	initproctitle(argc, argv);
	setproctitle("sella_nms", "starting");
#endif

	if ((result = loadConfigModule()) == FAILURE) {
		fprintf(stderr, "ERROR: Failed to load configuration module '%s'. Check log for details.\n", config_dso.c_str());
	} else if (test_flag) {
		if ((result = getRootConfigNode()) == FAILURE) {
			fprintf(stderr, "Configuration FAILED\n");
		} else {
			fprintf(stderr, "Configuration PASSED\n");
		}
	} else if ((result = getRootConfigNode()) == FAILURE) {
			fprintf(stderr, "ERROR: Failed to read configuration file '%s'. Check log for details.\n", config_file.c_str());
	} else if ((result = processConfig()) == FAILURE) {
		fprintf(stderr, "ERROR: Failed to process configuration file '%s'. Check log for details.\n", config_file.c_str());
	} else if ((result = processDaemonOptions()) == FAILURE) {
		fprintf(stderr, "ERROR Failed to process daemon options. Check log for details.\n");
	} else if ((result = loadSupportLibraries()) == FAILURE) {
		LMSG("ERROR: Failed to load support libraries. Check log for details.");
	} else if (foreground_flag == false) {
		pid = fork();
		if (pid == -1) {
			fprintf(stderr, "ERROR: Failed to fork process. Check log for details.");
			daemon_running = false;

			return FAILURE;
		} else if (pid > 0) {
			daemon_running = false;

			return SUCCESS;
		}
		fclose(stdin); /* Will no longer accept input (ie: Ctrl-C) */
		fclose(stdout); /* Our output to stdout will go unheard. */
		fclose(stderr); /* Our output to stderr will go unheard. */

		LMSG("%s forked to background. PID is %d", this->project.c_str(), getpid());
	}

	/* Write the PID. */
	writePidFile();

	if (result == SUCCESS && !test_flag) {
		/* Announce that startup is complete. */
		LMSG("%s startup complete..", this->project.c_str());

		/* Drop priviledges globally for SellaNMS. */
		dropPriv();

		while (daemon_running) {
			LMSG("%s scheduler started..", this->project.c_str());

#ifdef HAVE_SETPROCTITLE
			setproctitle("sella_nms", "running");
#endif
			if ((result = scheduler()) != SUCCESS) {
				daemon_running = false;

				LMSG("ERROR: Execution of main thread failed.");
			} else {
				LMSG("%s scheduler stopped..", this->project.c_str());
				if (interrupt && daemon_running) {
					/* We're here due to a HUP. Re-read config and start back up again. */
					LMSG("%s re-reading configuration..", this->project.c_str());

					reloadConfigNode();
					interrupt = false;
					daemon_running = true;
				} else {
					/* If this wasn't a HUP, shutdown. */
					daemon_running = false;
				}
			}
		}
	}

	LMSG("%s unloading support libraries..", this->project.c_str());

	unloadSupportLibraries();

	/* Announce that shutdown is complete. */
	LMSG("%s shutdown complete..", this->project.c_str());

	return result;
}

RESULT Daemon::version(void) {
	fprintf(stdout, "%s v%d.%d.%d\n\n", this->project.c_str(), this->major_version, this->minor_version, this->revision);
	fprintf(stdout, "Copyright (c) 2000-2005, Digital Genesis Software.  All rights reserved.\n");
	fprintf(stdout, "This program is released under the GNU Public License, version 2.\n\n");

	return SUCCESS;
}

RESULT Daemon::about(void) {
	fprintf(stdout, "%s v%d.%d.%d\n\n", this->project.c_str(), this->major_version, this->minor_version, this->revision);
	fprintf(stdout, "Credits:\n");
	fprintf(stdout, "James M. Sella/DGS - Design, implementation and documentation.\n");
	fprintf(stdout, "William T. Sella/DGS - Design, implementation and documentation.\n\n");

	return SUCCESS;
}

RESULT Daemon::usage() {
	fprintf(stdout, "\n");
	fprintf(stdout, "Usage: %s [OPTIONS]\n", this->daemon.c_str());
	fprintf(stdout, "\n");
	fprintf(stdout, "  Informational\n");
	fprintf(stdout, "    -h, --help          Display this help screen\n");
	fprintf(stdout, "    -v, --version       Display version information\n");
	fprintf(stdout, "    -a, --about         Display credit information\n");
	fprintf(stdout, "\n");
	fprintf(stdout, "  Configuration\n");
	fprintf(stdout, "    -m, --module=DSO    Use DSO as the configuration reading module\n");
	fprintf(stdout, "    -c, --config=FILE   Use FILE as the configuration\n");
	fprintf(stdout, "    -t, --test          Test the configuration, but do not run\n");
	fprintf(stdout, "\n");
	fprintf(stdout, "  Daemon control\n");
	fprintf(stdout, "    -A, --all           Run all %s daemons\n", this->project.c_str());
	fprintf(stdout, "    -D, --discover      Run the network discovery daemon\n");
	fprintf(stdout, "    -M, --monitor       Run the network monitoring daemon\n");
	fprintf(stdout, "    -O, --output        Run the event output daemon\n");
	fprintf(stdout, "\n");
	fprintf(stdout, "  System\n");
	fprintf(stdout, "    -f, --foreground    Do not fork this process into the background\n");
	fprintf(stdout, "    -u, --user=STRING   Run as user STRING\n");
	fprintf(stdout, "    -g, --group=STRING  Run as group STRING\n");
	fprintf(stdout, "    -n, --nice=INTEGER  Run with a nice of INTEGER [-20,19]\n");
	fprintf(stdout, "    -p, --pidfile=FILE  Use FILE to save the daemon's process ID\n");
	fprintf(stdout, "    -s, --statfile=FILE Use FILE to save status information\n");
	fprintf(stdout, "\n");
	fprintf(stdout, "  Output\n");
	fprintf(stdout, "    -l, --logfile=FILE  Output logging information to FILE\n");
	fprintf(stdout, "    -d, --debug=INTEGER Set the debug level to INTEGER [0,9]\n");
	fprintf(stdout, "\n");
	fprintf(stdout, "To report bugs, email bugs@nms.digitalgenesis.com.");
	fprintf(stdout, "\n");
	
	return SUCCESS;
}

RESULT Daemon::checkEuidRoot() {
	if (geteuid() != 0) {
		return FAILURE;
	}
	return SUCCESS;
}

RESULT Daemon::processConfig() {
	RESULT result = SUCCESS;
	string dso;
	vector<string> configuration;

	if (!this->config->assignValues("system modules", configuration)) {
		LMSG("ERROR: No modules were listed to load in configuration.");
		result = FAILURE;
	} else {
		for (unsigned i = 0; i < configuration.size(); i++) {	
			if (!configuration[i].compare("discovery") && this->discover_flag) {
				if (this->config->assignValue("system modules discovery load", dso, DEFAULT_DISCOVERY_DSO)) {
					daemon_dso_v.insert(daemon_dso_v.begin(), dso);
				}
			}
			if (!configuration[i].compare("monitor") && this->monitor_flag) {
				if (this->config->assignValue("system modules monitor load", dso, DEFAULT_MONITOR_DSO)) {
					daemon_dso_v.insert(daemon_dso_v.begin(), dso);
				}
			}
		}
	}

	return result;
}

RESULT Daemon::loadConfigModule() {
	char *error;
	void *handle;
	Configuration* (*get_instance_function)(void) = NULL;

	if (!(handle = dlopen(config_dso.c_str(), RTLD_LAZY | RTLD_GLOBAL))) {
		if ((error = dlerror()) != NULL) {
			LMSG("WARNING: dlopen() returned error: %s", error);

			return FAILURE;
		}
	}
	get_instance_function = (Configuration* (*)(void))dlsym(handle, "getInstance");
	if ((error = dlerror()) != NULL) {
		LMSG("WARNING: dlsym returned error: %s", error);

		return FAILURE;
	}

	if ((config_mod = (*get_instance_function)()) == NULL) {
		LMSG("WARNING: Failed to get a Configuration instance using DSO: %s.", config_dso.c_str());

		return FAILURE;
	}

	/* Insert the library, so it can be unloaded later. */
	supportLibs.insert(supportLibs.end(), handle);

	return SUCCESS;
}

RESULT Daemon::processCommandLine(int argc, char **argv) {
	RESULT result = SUCCESS;

	extern char *optarg;
	unsigned long daemons = 0;

	int c;
	
	int longindex = 0;
	struct option longopts[] =
	{
		{"help",				0, 0, 'h'},
		{"version",			0, 0, 'v'},
		{"about",			0, 0, 'a'},
		{"module",			1, 0, 'm'},
		{"config",			1, 0, 'c'},
		{"test",				0, 0, 't'},
		{"foreground",		0, 0, 'f'},
		{"user",				1, 0, 'u'},
		{"group",			1, 0, 'g'},
		{"nice",				1, 0, 'n'},
		{"pidfile",			1, 0, 'p'},
		{"statfile",		1, 0, 's'},
		{"logfile",			1, 0, 'l'},
		{"debug",			1, 0, 'd'},
		{"discover",		0, 0, 'D'},
		{"monitor",			0, 0, 'M'},
		{"output",			0, 0, 'O'},
		{"all",				0, 0, 'A'},
		{0,					0, 0, 0}
	};

	this->daemon = basename(argv[0]);

	while ((c = getopt_long(argc, argv, "hvam:c:tfu:g:n:p:s:l:d:DMOA", longopts, &longindex)) != -1) {
		switch (c) {
			case 'h':
				this->usage_flag = true;
				break;

			case 'v':
				this->version_flag = true;
				break;

			case 'a':
				this->about_flag = true;
				break;

			case 'm':
				this->config_dso = optarg;
				break;

			case 'c':
				this->config_file = optarg;
				break;

			case 't':
				this->test_flag = true;
				break;

			case 'f':
				this->foreground_flag = true;
				break;

			case 'n':
				this->nice = atol(optarg);
				break;

			case 'p':
				this->pid_file = optarg;
				break;

			case 's':
				this->stat_file = optarg;
				break;

			case 'u':
				this->user = optarg;
				break;

			case 'g':
				this->group = optarg;
				break;

			case 'l':
				this->log = optarg;
				break;

			case 'd':
				this->debug = atol(optarg);
				break;

			case 'D':
				this->discover_flag = true;
				break;

			case 'M':
				this->monitor_flag = true;
				break;

			case 'O':
				this->output_flag = true;
				break;

			case 'A':
				this->discover_flag = true;
				this->monitor_flag = true;
				break;

			default:
				result = FAILURE;
		}
	}

	return result;
}

RESULT Daemon::loadSupportLibraries() {
	RESULT result = SUCCESS, check_result = SUCCESS;

	int library, bogus = 0;
	char *error;
	void *handle;

	Module* (*check_function)(void*, void*) = NULL;
	Module* (*get_library_function)(void*) = NULL;

	vector<string> v;
	vector<string>::iterator v_it;

	if (!config->assignValues("system modules load", v)) {
		result = FAILURE;
	} else {
		for (v_it = v.begin(); v_it != v.end() && result == SUCCESS; ++v_it) {
			handle = dlopen(v_it->c_str(), RTLD_LAZY | RTLD_GLOBAL);
			if (!handle) {
				if ((error = dlerror()) != NULL) {
					LMSG("WARNING: dlopen() returned error: %s", error);
				}
				result = FAILURE;
			} else {
				/* Look for a check function, incase we need to check the libary. */
				check_function = (Module* (*)(void*, void*))dlsym(handle, "check");
				if ((error = dlerror()) == NULL) {
					/* Identify the library, since most need different input. */
					get_library_function = (Module* (*)(void*))dlsym(handle, "getLibrary");
					if ((error = dlerror()) != NULL) {
						library = 0;
					} else {
						(*get_library_function)((void*) &library);
					}

					/* Perform the correct call for the specific library. */
					switch (library) {
						case LIBRARY_STORAGE:
							(*check_function)((void*) &result, (void*) config);
							break;
						default:
							(*check_function)((void*) &result, (void*) &bogus);
					}
				}

				/* Insert the library, so it can be unloaded later. */
				supportLibs.insert(supportLibs.end(), handle);
			}
		}
	}

	return result;
}

RESULT Daemon::unloadSupportLibraries() {
	RESULT result = SUCCESS;

	char *error;
	void *handle;

	slist<void*>::iterator l_it;

	for (l_it = supportLibs.begin(); l_it != supportLibs.end(); ++l_it) {
		if (dlclose(*l_it) != 0) {
			if ((error = dlerror()) != NULL) {
				LMSG("WARNING: dlclose() returned error: %s", error);
			}
			result = FAILURE;
		}
	}

	/* Wipe the list. */
	supportLibs.clear();

	return result;
}

RESULT Daemon::reloadConfigNode() {
	/* This doesn't do much currently, unless daemons children reload themselves picking up the new config */

	RESULT result;
	ConfigNode *oldConfig = NULL;
	ConfigNode *newConfig = NULL;

	assert(config_mod != NULL);

	result = config_mod->read(config_file);
	if (result == FAILURE) {
		LMSG("Failed to read configuration '%s', keeping active configuration.", config_file.c_str());
	} else {
		result = config_mod->get("", &(newConfig));
		if (result == FAILURE) {
			LMSG("Failed to get new config. Keeping active configuration.");
		} else {
			//We want to swap in the new config in one operation.
			oldConfig = this->config;
			this->config = newConfig;

			SAFE_DELETE(oldConfig);
			//Probably need to SETCONFIG for each of the storage modules here.
		}
	}

	return result;
}

RESULT Daemon::getRootConfigNode() {
	RESULT result;

	assert(config_mod != NULL);

	result = config_mod->read(config_file);
	if (result == SUCCESS) {
		result = config_mod->get("", &(this->config));
		if (result == FAILURE || this->config == NULL) {
			LMSG("WARNING: ConfNode == NULL - '%s' reading '%s'.", config_dso.c_str(), config_file.c_str());
		}
	} else {
		LMSG("WARNING: Unable to read config '%s' in library '%s'.", config_file.c_str(), config_dso.c_str());
	}
	return result;
}

RESULT Daemon::processDaemonOptions() {
	RESULT result = SUCCESS;
	FILE *fp;
	int fd;
	long val, core_dump;
	unsigned int mib_count;
	unsigned long mask;
	string str;
	struct rlimit rl;
	struct passwd *ent = NULL;
	vector<string> *v = NULL;
	vector<string>::iterator it;

	/* Setup our syslog connection */
	if (this->config->assignValue("system syslog facility", str)) {
		if (!str.compare("user")) {
			this->facility = LOG_USER;
		} else if (!str.compare("daemon")) {
			this->facility = LOG_DAEMON;
		} else if (!str.compare("local0")) {
			this->facility = LOG_LOCAL0;
		} else if (!str.compare("local1")) {
			this->facility = LOG_LOCAL1;
		} else if (!str.compare("local2")) {
			this->facility = LOG_LOCAL2;
		} else if (!str.compare("local3")) {
			this->facility = LOG_LOCAL3;
		} else if (!str.compare("local4")) {
			this->facility = LOG_LOCAL4;
		} else if (!str.compare("local5")) {
			this->facility = LOG_LOCAL5;
		} else if (!str.compare("local6")) {
			this->facility = LOG_LOCAL6;
		} else if (!str.compare("local7")) {
			this->facility = LOG_LOCAL7;
		} else if (!isdigit(str.at(0))) {
			this->facility = atol(str.c_str());
			if (this->facility < 0 || this->facility > 23) {
				this->facility = DEFAULT_SYSLOG_FACILITY;
			}
		}
	} else {
		this->facility = LOG_USER;
	}

	if (this->config->assignValue("system syslog severity", str)) {
		if (!str.compare("info") || !str.compare("information")) {
			this->severity = LOG_INFO;
		} else if (!str.compare("debug")) {
			this->severity = LOG_DEBUG;
		} else if (!str.compare("emerg") || !str.compare("emergency")) {
			this->severity = LOG_EMERG;
		} else if (!str.compare("alert")) {
			this->severity = LOG_ALERT;
		} else if (!str.compare("crit") || !str.compare("critical")) {
			this->severity = LOG_CRIT;
		} else if (!str.compare("err") || !str.compare("error")) {
			this->severity = LOG_ERR;
		} else if (!str.compare("warn") || !str.compare("warning")) {
			this->severity = LOG_WARNING;
		} else if (!str.compare("notice")) {
			this->severity = LOG_NOTICE;
		} else if (!isdigit(str.at(0))) {
			this->severity = atol(str.c_str());
			if (this->severity < 0 || this->severity > 7) {
				this->severity = LOG_INFO;
			}
		}
	} else {
		this->severity = LOG_INFO;
	}
	log_severity(this->severity);

	/* Open connection to syslog */
	val = LOG_PID | ((this->foreground_flag) ? LOG_PERROR : 0);
	closelog();
	openlog(this->daemon.c_str(), val, this->facility);

	/* Announce that we are beginning startup. */
	LMSG("%s v%d.%d.%d starting up...", this->project.c_str(), this->major_version, this->minor_version, this->revision);
	LMSG("%lu CPUs detected...", get_nprocs());

	/* Set our niceness */
	if (this->nice == 21) {
		this->config->assignValue("system process nice", val, 0);
		this->nice = val;
	}
	if (this->nice >= -20 && this->nice <= 19) {
		setpriority(PRIO_PROCESS, 0, this->nice);
	}

	/* If euid is root, and user wants a non-root user. */
	if (!geteuid() && this->config->assignValue("system security user", str) && str.compare("root")) {
		if (!(ent = getpwnam(str.c_str()))) {
			result = FAILURE;
			LMSG("user '%s' was not found in passwd file", str.c_str());
		} else {
			if (this->config->assignValue("system security group", str) && str.compare("root")) {
				if (initgroups(str.c_str(), ent->pw_gid)) {
					result = FAILURE;
					LMSG("group '%s' was not found in the group file", str.c_str());
				}
			}
			if ((result = setUid(ent->pw_uid)) == FAILURE) {
				LMSG("failed to set uid to %d", ent->pw_uid);
			} else if ((result = setGid(ent->pw_gid)) == FAILURE) {
				LMSG("failed to set gid to %d", ent->pw_gid);
			}
		}
	}

	/* Lock ourselves into a chroot jail, if requested. */
	if (this->config->assignValue("system security chroot", str)) {
		if (chroot(str.c_str()) < 1) {
			result = FAILURE;
			LMSG("WARNING: failed to chroot to '%s'", str.c_str());
		}
	}

	/* Create a pidfile */
	this->config->assignValue("system process pidfile", this->pid_file, DEFAULT_PID_FILE);

	/* Create a statfile */
	this->config->assignValue("system process statfile", this->stat_file, DEFAULT_STAT_FILE);
	if (!(fp = fopen(stat_file.c_str(), "w"))) {
		result = FAILURE;
		LMSG("failed to open statfile '%s'", stat_file.c_str());
	} else {
		if (ent != NULL && geteuid() == 0) {
			fd = fileno(fp);
			fchown(fd, geteuid(), ent->pw_gid);
			fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
		}
		if ((result = setStatFile(stat_file)) != SUCCESS) {
			result = FAILURE;
			LMSG("Failed to set stat_file");
		}
		fclose(fp);
	}

	/* Set our umask */
	if (this->config->assignValue("system security umask", mask)) {
		umask((mode_t) mask);
	}

	/* Set core dump size - */
	if (this->config->assignValue("system process core-dump", core_dump)) {
		if (core_dump < 0) {
			rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
		} else {
			rl.rlim_cur = rl.rlim_max = core_dump;
		}

		setrlimit(RLIMIT_CORE, &rl);
	}

	/* Setup the SNMP library */
	LMSG("Starting to load MIBs..");

	if (this->config->isNode("system snmp-mibs debug")) {
		snmp_set_do_debugging(1);
	}

	str.erase();
	if (this->config->assignValues("system snmp-mibs", v)) {
		/* Add additional MIB directories. */
		for (it = v->begin(); it != v->end(); ++it) {
			if (it->compare("debug")) {
				str.append(*it + ":");
			}
		}
		delete v;
	}

	setenv("MIBDIRS", str.c_str(), 0); /* setenv("MIBS", ":", 0); setenv("MIBFILES", ":", 0); */
#ifdef HAVE_NET_SNMP
	netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_MIBDIRS, str.c_str());
#endif

	init_snmp(DGS_PROJECT);

	if (this->config->isNode("system snmp-mibs debug")) {
		LMSG("Making call to init_mib()");
	}
	init_mib(); /* Setup internal MIB structures */

	if (this->config->isNode("system snmp-mibs debug")) {
		LMSG("Making call to read_all_mibs()");
	}
	read_all_mibs(); /* Actually reads in the MIBs at this point. */
	snmp_set_do_debugging(0);

	LMSG("Completed loading MIBs..");

	return result;
}

RESULT Daemon::writePidFile() {
	RESULT result = SUCCESS;
	FILE *fp;

	if (!(fp = fopen(this->pid_file.c_str(), "w"))) {
		result = FAILURE;
		LMSG("WARNING: Failed to open pidfile '%s'", this->pid_file.c_str());
	} else {
		if (fprintf(fp, "%d\n", getpid()) > 0) {
		} else {
			result = FAILURE;
			LMSG("WARNING: Failed write to pidfile '%s'", this->pid_file.c_str());
		}
		fclose(fp);
	}

	return result;
}

RESULT Daemon::processSignal(int sig) {
	/* Suppresses duplicate signal work. This occurs when someone signals multiple PIDs (ie: killall sella_nms). */
	if (lastSignal + 1 > time(NULL)) { /* No more than one signal every 2 seconds. */
		return SUCCESS;
	} else {
		lastSignal = time(NULL);
	}

	switch (sig) {
		case SIGHUP:	
			LMSG("%s signalled with SIGHUP (%s). Stopping modules for reload..", project.c_str(), name.c_str());

#ifdef HAVE_SETPROCTITLE
			setproctitle("sella_nms", "reloading");
#endif

			killThreadAll();
			break;

		case SIGTERM:
			if (daemon_running) {
				LMSG("%s signalled with SIGTERM (%s). Stopping modules for shutdown..", project.c_str(), name.c_str());
				daemon_running = false;

	#ifdef HAVE_SETPROCTITLE
				setproctitle("sella_nms", "shutdown");
	#endif

				killThreadAll();
				//kill(0, SIGUSR2); //Wake up any blocking select() calls.
			}
			break;

		case SIGUSR1:
			/* This isn't used. */
			if (this->debug < 9) {
				this->debug++;
			}

			LMSG("%s signalled with SIGUSR1 (%s). Debug level is now %d.", project.c_str(), name.c_str(), this->debug);
			break;

		case SIGUSR2:
			/* This isn't used. */
			if (this->debug > 0) {
				this->debug--;
			}

			LMSG("%s signalled with SIGUSR2 (%s). Debug level is now %d.", project.c_str(), name.c_str(), this->debug);
			break;

		default:
			LMSG("%s signalled with %d (%s). Its a trick, get an axe.", project.c_str(), sig, name.c_str());
	} 

	return SUCCESS;
}

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