/*
** CBLM v3
** $Id: prefs.c,v 1.40 2008/07/14 00:25:08 sella Exp $
** Copyright (c) 2004-2008 James M. Sella. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
** 
** Cheap Bastard Latency Monitor v3
*/

static const char rcsid[] = "$Id: prefs.c,v 1.40 2008/07/14 00:25:08 sella Exp $";

#include "prefs.h"
#include "db.h"

#include <snmptrap.h>
#include <configuration.h>

#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <getopt.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/resource.h>
#ifdef HAVE_SYS_PRCTL_H
	#include <sys/prctl.h>
#endif
#ifdef HAVE_SYS_CAPABILITY_H
	#include <sys/capability.h>
#endif

/* Global Variables */
struct prefs_t prefs;

/* External Global Variables */
extern struct args_t args;
extern pthread_mutex_t probe_mutex, db_mutex;
extern MYSQL mysql;

/* Getopt Variables */
char const short_options[] = "s:p:I:e:l:Z:R::r:M:N:Q:m:a:k:z:x:fL:H:S:D:U:P:CX:Y:T:WEc:u:g:t:o::n:d::vh";
struct option const long_options[] = {
	{"source", required_argument, NULL, 's'},
	{"port", required_argument, NULL, 'p'},
	{"interface", required_argument, NULL, 'I'},
	{"name", required_argument, NULL, 'e'},
	{"location", required_argument, NULL, 'l'},
	{"description", required_argument, NULL, 'Z'},
	{"repeat", optional_argument, NULL, 'R'},
	{"rate", required_argument, NULL, 'r'},
	{"dscp", required_argument, NULL, 'M'},
	{"precedence", required_argument, NULL, 'N'},
	{"tos", required_argument, NULL, 'Q'},
	{"maxott", required_argument, NULL, 'm'},
	{"maxage", required_argument, NULL, 'a'},
	{"key", required_argument, NULL, 'k'},
	{"size", required_argument, NULL, 'z'},
	{"pattern", required_argument, NULL, 'x'},
	{"fragment", no_argument, NULL, 'f'},
	{"ttl", required_argument, NULL, 'L'},
	{"dbhost", required_argument, NULL, 'H'},
	{"dbsock", required_argument, NULL, 'S'},
	{"dbname", required_argument, NULL, 'D'},
	{"dbuser", required_argument, NULL, 'U'},
	{"dbpass", required_argument, NULL, 'P'},
	{"dbcomp", no_argument, NULL, 'C'},
	{"disable", required_argument, NULL, 'X'},
	{"threshold", required_argument, NULL, 'Y'},
	{"snmptrap", required_argument, NULL, 'T'},
	{"write", no_argument, NULL, 'W'},
	{"print", no_argument, NULL, 'E'},
	{"config", required_argument, NULL, 'c'},
	{"user", required_argument, NULL, 'u'},
	{"group", required_argument, NULL, 'g'},
	{"chroot", required_argument, NULL, 't'},
	{"stats", optional_argument, NULL, 'o'},
	{"nice", required_argument, NULL, 'n'},
	{"debug", optional_argument, NULL, 'd'},
	{"version", no_argument, NULL, 'v'},
	{"help", no_argument, NULL, 'h'},
	{NULL, 0, NULL, 0}
};

int get_args(int argc, char** argv) {
	int c, i, len, opt_index = 0, warn = 0;
	char *ptr, buf[512];
	snmptrap_peer_t **s;

	/* Default our settings. */
	memset(&args, 0, sizeof(struct args_t));
	memset(&prefs, 0, sizeof(struct prefs_t));
	args.source.s_addr = htonl(INADDR_ANY);
	args.interface.s_addr = htonl(INADDR_ANY);
	args.nice = DEF_NICENESS;

	/* Set passed arguments. */
	while ((c = getopt_long(argc, argv, short_options, long_options, &opt_index)) != -1) {
		switch (c) {
			case 's':
				/* Try converting as if its an IP address. */
				if (inet_aton(optarg, &args.source) == 0) {
					/* Try finding the IP, assuming its an interface name. */
					source_ip(optarg, &args.source);
				}

				break;

			case 'p':
				prefs.port = atoi(optarg);

				if (prefs.port > 0) {
					prefs.port = htons(prefs.port);
				} else {
					prefs.port = htons(DEF_PORT);
				}

				break;
				
			case 'I':
				/* Try converting as if its an IP address. */
				if (inet_aton(optarg, &args.interface) == 0) {
					/* Try finding the IP, assuming its an interface name. */
					source_ip(optarg, &args.interface);
				}

				break;

			case 'e':
				if (args.name == NULL) {
					args.name = (char*) strndup(optarg, 64);
				}

				break;

			case 'l':
				args.location = (char*) strndup(optarg, 132);

				break;

			case 'Z':
				args.description = (char*) strndup(optarg, 255);

				break;

			case 'R':
				if (optarg) {
					prefs.repeat = atoi(optarg);
					if (prefs.repeat < 0 || prefs.repeat > MAX_REPEAT) {
						prefs.repeat = DEF_REPEAT;
					}
				} else {
					prefs.repeat = DEF_REPEAT;
				}

				break;

			case 'r':
				prefs.rate = atoi(optarg);

				if (prefs.rate > MAX_RATE) {
					prefs.rate = DEF_RATE;
				}

				break;
				
			case 'M':
				add_tos(optarg, 2);

				break;

			case 'N':
				add_tos(optarg, 5);

				break;

			case 'Q':
				add_tos(optarg, 0);

				break;

			case 'm':
				prefs.maxott = atoi(optarg);

				if (prefs.maxott > MAX_MAXOTT) {
					prefs.maxott = DEF_MAXOTT;
				}

				break;
				
			case 'a':
				prefs.maxage = atoi(optarg);

				if (prefs.maxage > MAX_MAXAGE || prefs.maxage < MIN_MAXAGE) {
					prefs.maxage = DEF_MAXAGE;
				}

				break;
				
			case 'k':
				strncpy(prefs.key, optarg, sizeof(prefs.key));
				memset(optarg, 'x', strlen(optarg) < sizeof(prefs.key) ? strlen(optarg) : sizeof(prefs.key));

				break;

			case 'z':
				prefs.size = atoi(optarg);

				if (prefs.size > MAX_SIZE) {
					prefs.size = DEF_SIZE;
				}

				break;
				
			case 'x':
				prefs.pattern = (char*) strndup(optarg, 255);

				break;

			case 'f':
				prefs.fragment = 1;

				break;

			case 'L':
				prefs.ttl = atoi(optarg);

				if (prefs.ttl > MAX_TTL || prefs.ttl < 1) {
					prefs.maxott = DEF_TTL;
				}

				break;

			case 'H':
				ptr = NULL;

				args.db_host = (char*) strndup(optarg, 132);

				for (i = 0; i < strlen(args.db_host); i++) {
					if (args.db_host[i] == ':') {
						args.db_host[i] = '\0';
						ptr = args.db_host + i + 1;
					}
				}

				if (ptr) {
					args.db_port = atoi(ptr);
				} else {
					args.db_port = DEF_DBPORT;
				}

				break;

			case 'S':
				args.db_sock = (char*) strndup(optarg, 255);

				break;

			case 'D':
				args.db_name = (char*) strndup(optarg, 132);

				break;

			case 'U':
				args.db_user = (char*) strndup(optarg, 132);

				break;

			case 'P':
				args.db_pass = (char*) strndup(optarg, 132);
				memset(optarg, 'x', strlen(optarg) < 132 ? strlen(optarg) : 132);

				break;

			case 'C':
				args.db_comp = 1;

				break;

			case 'X':
				if (strncasecmp(optarg, "ott", 3) == 0) {
					prefs.trk_ott = -1;
				} else if (strncasecmp(optarg, "jtr", 3) == 0) {
					prefs.trk_jtr = -1;
				} else if (strncasecmp(optarg, "tos", 3) == 0) {
					prefs.trk_tos = -1;
				} else if (strncasecmp(optarg, "pri", 3) == 0) {
					prefs.trk_pri = -1;
				} else if (strncasecmp(optarg, "ooo", 3) == 0) {
					prefs.trk_ooo = -1;
				} else if (strncasecmp(optarg, "dup", 3) == 0) {
					prefs.trk_dup = -1;
				} else {
					fprintf(stderr, "--disable supports the following arguments; OTT, JTR, TOS, PRI, OOO, DUP\n");
				}

				break;

			case 'Y':
				ptr = NULL;

				for (i = 0; i < strlen(optarg); i++) {
					if (optarg[i] == ':') {
						optarg[i] = '\0';
						ptr = optarg + i + 1;
					}
				}

				if (ptr == NULL) {
					fprintf(stderr, "--threshold argument must be of the form FEATURE:NUMBER (eg: --threshold=LOSS:3)\n");

					break;
				} else {
					errno = 0;
					strtol(ptr, (char **) NULL, 10);
					if (errno != 0) {
						fprintf(stderr, "--threshold argument must be of the form FEATURE:NUMBER (eg: --threshold=LOSS:3)\n");

						break;
					}
				}

				if (strncasecmp(optarg, "loss", 4) == 0) {
					prefs.trap_loss = atoi(ptr);
				} else if (strncasecmp(optarg, "rtt", 3) == 0) {
					prefs.trap_rtt = atoi(ptr);
				} else if (strncasecmp(optarg, "ott", 3) == 0) {
					prefs.trap_ott = atoi(ptr);
				} else if (strncasecmp(optarg, "jtr", 3) == 0) {
					prefs.trap_jtr = atoi(ptr);
				} else if (strncasecmp(optarg, "tos", 3) == 0) {
					prefs.trap_tos = atoi(ptr);
				} else if (strncasecmp(optarg, "pri", 3) == 0) {
					prefs.trap_pri = atoi(ptr);
				} else if (strncasecmp(optarg, "ooo", 3) == 0) {
					prefs.trap_ooo = atoi(ptr);
				} else if (strncasecmp(optarg, "dup", 3) == 0) {
					prefs.trap_dup = atoi(ptr);
				} else {
					fprintf(stderr, "--threshold supports the following arguments; LOSS, RTT, OTT, JTR, TOS, PRI, OOO, DUP\n");
				}

				break;

			case 'T': /* --snmptrap=HOST[:PORT][:COMMUNITY[:VERSION[:INFORM]]] */
#ifdef HAVE_NET_SNMP
				s = &(prefs.peer);
				while ((*s) != NULL) s = &((*s)->next); /* Move to the end of peer list */
				*s = malloc(sizeof(snmptrap_peer_t));
				memset(*s, 0, sizeof(snmptrap_peer_t));
				(*s)->version = (*s)->inform = -1;

				ptr = strtok(optarg, ":");
				while (ptr != NULL) {
					if ((*s)->peername[0] == '\0') { /* HOST */
						strncpy((*s)->peername, ptr, sizeof((*s)->peername));

						/* Check if PORT is following hostname */
						if ((ptr = strtok(NULL, ":")) != NULL) { 
							/* Assume it's a PORT if its only numbers */
							snprintf(buf, sizeof(buf), "%u", atoi(ptr));
							if (strcmp(ptr, buf) == 0) { /* PORT */
								if (atoi(ptr) >= 1 && atoi(ptr) <= 65535) { /* Ignore invalid range */
									snprintf(buf, sizeof(buf), "%s:%u", (*s)->peername, atoi(ptr));
									strncpy((*s)->peername, buf, sizeof((*s)->peername));
								} else {
									fprintf(stderr, "Specified port '%s' for '--snmptrap' is invalid and will be ignored.\n", ptr);
								}
							} else {
								continue;
							}
						}
					} else if ((*s)->community[0] == '\0') { /* COMMUNITY */
						strncpy((*s)->community, ptr, sizeof((*s)->community));
					} else if ((*s)->version == -1) { /* VERSION */
						if (strcasecmp(ptr, "1") == 0 || strcasecmp(ptr, "v1") == 0) {
							(*s)->version = SNMP_VERSION_1;
						} else if (strcasecmp(ptr, "3") == 0 || strcasecmp(ptr, "v3") == 0) {
							(*s)->version = SNMP_VERSION_3;
						} else {
							(*s)->version = SNMP_VERSION_2c;
						}
					} else if ((*s)->inform == -1) { /* INFORM */
						if (strncasecmp(ptr, "y", 1) == 0 || strcasecmp(ptr, "on") == 0 || strcasecmp(ptr, "1") == 0) { /* Yes, On or 1 */
							(*s)->inform = 1;
						} else {
							(*s)->inform = 0;
						}
					} else {
						break;
					}

					/* Next token */
					ptr = strtok(NULL, ":");
				}

				/* Set COMMUNITY default, if needed */
				if ((*s)->community[0] == '\0') {
					strncpy((*s)->community, "public", sizeof((*s)->community));
				}

				/* Set VERSION default, if needed */
				if ((*s)->version == -1) {
					(*s)->version = SNMP_VERSION_2c;
				}

				/* Set INFORM default, if needed */
				if ((*s)->inform == -1) {
					(*s)->inform = 0;
				}
#else
				if (warn == 0) {
					fprintf(stderr, "Support for '--snmptrap' is not compiled into the binary.\n");
					warn = 1;
				}
#endif /* HAVE_NET_SNMP */

				break;

			case 'W':
				args.write = 1;

				if (args.debug == 0) {
					args.debug = DEBUG_ERROR;
				}

				break;

			case 'E':
				args.print = 1;

				if (args.debug == 0) {
					args.debug = DEBUG_ERROR;
				}

				break;

			case 'c':
				args.config = (char*) strndup(optarg, 32);

				break;

			case 'u':
				args.user = (char*) strndup(optarg, 32);

				break;

			case 'g':
				args.group = (char*) strndup(optarg, 32);

				break;

			case 't':
				args.chroot = (char*) strndup(optarg, 255);

				break;

			case 'o':
				if (optarg) {
					args.stats = (char*) strndup(optarg, 255);
				} else {
					args.stats = (char*) strndup(DEF_STATS, 255);
				}

				break;

			case 'n':
				if (optarg) {
					args.nice = atoi(optarg);
				}

				if (args.nice < -19 || args.nice > 20) {
					args.nice = DEF_NICENESS;
				}

				break;
				
			case 'd':
				if (optarg) {
					args.debug = atoi(optarg);
					if (args.debug < 0 || args.debug > DEBUG_MAXVAL) {
						args.debug = DEBUG_ERROR;
					}
				} else {
					args.debug = DEBUG_ERROR;
				}

				break;

			case 'v':
				version(0);

				return EXIT_FAILURE;

			default:
			case 'h':
				usage();
				version(1);

				return EXIT_FAILURE;
		}
	}

	return EXIT_SUCCESS;
}

void usage() {
	printf("Usage: %s [OPTION]\n", PROGRAM_NAME);
	printf("A high performance active latency, loss and jitter probe.\n");
	printf("\nPreferences:\n");
	printf("  -s, --source=IP              Source traffic from specified IP (first found)\n");
	printf("  -p, --port=PORT              Listen on specified port (%d)\n", DEF_PORT);
	printf("  -I, --interface=INTERFACE    Listen on specified interface (all)\n");
	printf("  -e, --name=STRING            Probe descriptive name (IP)\n");
	printf("  -l, --location=STRING        Probe location (none)\n");
	printf("  -Z, --description=STRING     Probe description (none)\n");
	printf("  -R, --repeat[=SEC]           Repeat threshold snmptraps every SEC seconds (%lu)\n", DEF_REPEAT);
	printf("  -r, --rate=NUM               Rate to transmit packets at (NUM/min) (%lu/min)\n", DEF_RATE);
	printf("  -M, --dscp=DSCP[:PRI]        Transmit with specified DSCP and priority (%s)\n", strlen(DEF_TOS) > 1 ? DEF_TOS : "0:0");
	printf("  -N, --precedence=PREC[:PRI]  Transmit with specified precedence and priority (%s)\n", strlen(DEF_TOS) > 1 ? DEF_TOS : "0:0");
	printf("  -Q, --tos=TOS[:PRI]          Transmit with specified ToS and priority (%s)\n", strlen(DEF_TOS) > 1 ? DEF_TOS : "0:0");
	printf("  -m, --maxott=MSEC            Maximum allowed one way trip time (ms) between probes (%lu)\n", DEF_MAXOTT);
	printf("  -a, --maxage=SEC             Maximum age (sec) of probe before it is assumed dead (%lu)\n", DEF_MAXAGE);
	printf("  -k, --key=STRING             Key used for authorizing packets (%lu char max) (%s)\n", sizeof(prefs.key), strlen(DEF_KEY) > 1 ? DEF_KEY : "none");
	printf("  -z, --size=BYTES             Size of packet payload. (%lu)\n", DEF_SIZE);
	printf("  -x, --pattern=STRING         Pattern to load into packets (%s)\n", strlen(DEF_PATTERN) > 1 ? DEF_PATTERN : "none");
	printf("  -f, --fragment               Allow packet fragementation (disable DF) (off)\n");
	printf("  -L, --ttl=NUM                Set time to live (TTL) for outgoing packets (%u)\n", DEF_TTL);
	printf("  -X, --disable=FEATURE        Disable feature OTT, JTR, TOS, PRI, OOO, DUP (none)\n");
	printf("  -Y, --threshold=FEATURE:NUM  Set snmptrap threshold LOSS, RTT, OTT, JTR, TOS, PRI, OOO, DUP\n");
	printf("  -T, --snmptrap=HOST[:OPTION] Send snmptraps to host (none)\n");
	printf("\nDatabase:\n");
	printf("  -H, --dbhost=HOST[:PORT]     MySQL host for data storage and communication (%s)\n", strlen(DEF_DBHOST) > 1 ? DEF_DBHOST : "none");
	printf("  -S, --dbsock=SOCKET          MySQL socket for data storage and communication (%s)\n", strlen(DEF_DBSOCK) > 1 ? DEF_DBSOCK : "none");
	printf("  -D, --dbname=DATABASE        MySQL database to store data within (%s)\n", strlen(DEF_DBNAME) > 1 ? DEF_DBNAME : "none");
	printf("  -U, --dbuser=USER            Use specified username for connecting to the database (%s)\n", strlen(DEF_DBUSER) > 1 ? DEF_DBUSER : "none");
	printf("  -P, --dbpass=PASSWORD        Use specified password for connecting to the database (%s)\n", strlen(DEF_DBPASS) > 1 ? DEF_DBPASS : "none");
	printf("  -C, --dbcomp                 Use compression protocol to communicate with the database\n");
	printf("  -W, --write                  Write locally specified preferences into the database and exit\n");
	printf("  -E, --print                  Print probe preferences stored within the database and exit\n");
	printf("\nProcess:\n");
	printf("  -c, --config=FILE            Use configuration FILE (none)\n");
	printf("  -u, --user=USER              Run process as USER (current)\n");
	printf("  -g, --group=GROUP            Run process as GROUP (current)\n");
	printf("  -t, --chroot=DIR             Change root to DIR (none)\n");
	printf("  -o, --stats[=FILE]           Write stats to FILE (%s)\n", strlen(DEF_STATS) > 1 ? DEF_STATS : "none");
	printf("  -n, --nice=+/-NUM            Niceness at which to run process (%d)\n", DEF_NICENESS);
	printf("  -d, --debug[=LEVEL]          Enable debug and remain in foreground\n");
	printf("\nGeneral:\n");
	printf("  -v, --version                Displays version information\n");
	printf("  -h, --help                   Displays this help information\n\n");
	printf("Examples:\n\n");
	printf("  %s -H sql.domain.com -P mypass\n", PROGRAM_NAME);
	printf("  %s -H sql.domain.com:3306 -U cblm -P mypass\n", PROGRAM_NAME);
	printf("  %s -H sql.domain.com -P mypass -M 0:0 -M 40:5 -x '%s' --write\n", PROGRAM_NAME, DEF_PATTERN);
	printf("  %s -H sql.domain.com -P mypass --print\n", PROGRAM_NAME);
	printf("\n");
}

void version(int flag) {
	if (flag == 0 || flag == 2) {
		printf("%s v%s\n", PROGRAM_NAME, PROGRAM_VERS);
	}
	printf("Written by James M. Sella <sella@digitalgenesis.com>\n");
	if (!flag) {
		printf("\nCopyright (c) 2004-2008 James M. Sella. All rights reserved.\n");
		printf("This is free software; see the source for copying conditions. There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
	}
}

int use_args() {
	char buf[512];
	struct passwd *ent;
	struct group *gent;
#ifdef HAVE_SYS_CAPABILITY_H
	cap_t cap;
#endif

	/* At least one output must be used. */
	if (args.db_host == NULL && args.db_sock == NULL) {
		fprintf(stderr, "Database not configured via the command line or configuration file.\n");
		fprintf(stderr, "Usage: %s [OPTION] [-H HOST[:PORT]] [-c FILE]\n", PROGRAM_NAME);
		fprintf(stderr, "Use 'cblmd --help' for more information.\n");
		return EXIT_FAILURE;
	}

	if (args.db_name == NULL || strlen(args.db_name) < 1) {
		if (args.db_name) free(args.db_name);
		args.db_name = (char*) strndup(DEF_DBNAME, 132);
	}

	if (args.db_user == NULL || strlen(args.db_user) < 1) {
		if (args.db_user) free(args.db_pass);
		args.db_user = (char*) strndup(DEF_DBUSER, 132);
	}

	if (args.db_pass == NULL || strlen(args.db_pass) < 1) {
		if (args.db_pass) free(args.db_pass);
		args.db_pass = (char*) strndup(DEF_DBPASS, 132);
	}

	// FIXME: We should walk the list of interfaces to find an appropriate source rather than guess.
	if (args.source.s_addr == htonl(INADDR_ANY)) {
		source_ip("eth0", &args.source);
		if (args.source.s_addr == htonl(INADDR_ANY)) {
			source_ip("eth1", &args.source);
			if (args.source.s_addr == htonl(INADDR_ANY)) {
				source_ip("bond0", &args.source);
			}
		}

		if (args.source.s_addr == htonl(INADDR_ANY)) {
			fprintf(stderr, "Failed to find a source address to transmit from. Please specify with -s.\n");
			return EXIT_FAILURE;
		}
	}

	if (args.name == NULL || strlen(args.name) < 1 || strncmp("short", args.name, 5) == 0) {
		if (gethostname(buf, sizeof(buf) > 64 ? 64 : sizeof(buf)) != -1) {
			if (args.name && strncmp("short", args.name, 5) == 0) {
				if (index(buf, '.') != NULL) {
					*(index(buf, '.')) = '\0';
				}
			}

			if (args.name) free(args.name);
			args.name = (char*) strndup(buf, strlen(buf));
		} else { /* Use source IP as hostname */
			args.name = (char*) strndup(inet_ntoa(args.source), 64);
		}
	}

	/* Set our niceness */
	if (args.nice >= 0 || geteuid() == 0) {
		setpriority(PRIO_PROCESS, 0, args.nice);
	}

	/* If euid is root, we can change the owner, group and root dir. */
	if (geteuid() != 0 && (args.group || args.user || args.chroot)) {
		fprintf(stderr, "Only root may change the user, group or root directory of a process.\n");
	} else {
		ent = NULL;
		gent = NULL;

		/* Read in the group, if needed. */
		if (args.group) {
			if (!(gent = getgrnam(args.group))) {
				fprintf(stderr, "Group '%s' was not found.\n", args.group);
			}
		}

		/* Read in the user, if needed. */
		if (args.user) {
			if (!(ent = getpwnam(args.user))) {
				fprintf(stderr, "User '%s' was not found.\n", args.user);
			}
		}

		/* Lock ourselves into a chroot jail, if requested. */
		if (args.chroot) {
			if (chroot(args.chroot) == -1) {
				fprintf(stderr, "Failed to chroot to '%s'\n", args.chroot);
			}
		}

		/* Set our new group. */
		if (gent && setegid(gent->gr_gid)) {
			fprintf(stderr, "Failed to set GID to '%d'.\n", gent->gr_gid);
		}

		/* Set our new user. */
		if (ent) {
#ifdef HAVE_SYS_PRCTL_H
			if (prctl(PR_SET_KEEPCAPS, 1 ,0 ,0 ,0) < 0) {
				fprintf(stderr, "Failed to keep capabilities - prctl(): %s\n", strerror(errno));
			}
#endif

#ifdef HAVE_SYS_CAPABILITY_H
			if (seteuid(ent->pw_uid)) {
				fprintf(stderr, "Failed to set UID to '%d'.\n", ent->pw_uid);
			}

			cap = cap_from_text("cap_net_admin=ep");
			if (cap_set_proc(cap) < 0) {
				fprintf(stderr, "Failed to keep CAP_NET_ADMIN and CAP_NET_RAW - cap_set_proc(): %s\n", strerror(errno));
			} 
			cap_free(cap);

			if (args.debug >= DEBUG_INTERNAL) {
				cap = cap_get_proc();
				fprintf(stderr, "Running with uid %d and capabilities '%s'\n", getuid(), cap_to_text(cap, NULL));
				cap_free(cap);
			}
#endif

		}
	}

	return EXIT_SUCCESS;
}

int read_prefs(int overwrite) {
	int first = 1;
	MYSQL_RES *res;
	MYSQL_ROW row;

	pthread_mutex_lock(&probe_mutex);
	pthread_mutex_lock(&db_mutex);

	prefs.last = 0; /* Matches last_prefs() return value if no 'last' set. */

	if (db_query(&res, "SELECT var, val FROM prefs") == 0) {
		while (row = mysql_fetch_row(res)) {
			if (args.debug >= DEBUG_DUMP) {
				syslog(LOG_DEBUG, "read val: %s, var: %s", row[0], row[1]);
			}

			if (overwrite == 1) {
				if (!strncmp(row[0], "port", 4)) {
					prefs.port = htons(atoi(row[1]));
				} else if (!strncmp(row[0], "repeat", 6)) {
					prefs.repeat = atoi(row[1]);
				} else if (!strncmp(row[0], "rate", 4)) {
					prefs.rate = atoi(row[1]);
				} else if (!strncmp(row[0], "size", 4)) {
					prefs.size = atoi(row[1]);
				} else if (!strncmp(row[0], "fragment", 8)) {
					prefs.fragment = atoi(row[1]);
				} else if (!strncmp(row[0], "ttl", 3)) {
					prefs.ttl = atoi(row[1]);
				} else if (!strncmp(row[0], "maxott", 6)) {
					prefs.maxott = atoi(row[1]);
				} else if (!strncmp(row[0], "maxage", 6)) {
					prefs.maxage = atoi(row[1]);
				} else if (!strncmp(row[0], "key", 3)) {
					strncpy(prefs.key, row[1], sizeof(prefs.key));
				} else if (!strncmp(row[0], "tos", 3)) {
					if (first) {
						free_tos();
						first = 0;
					}
					add_tos(row[1], 0);
				} else if (!strncmp(row[0], "pattern", 7)) {
					prefs.pattern = (char*) strndup(row[1], 255);
				} else if (!strncmp(row[0], "last", 4)) {
					prefs.last = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_ott", 7)) {
					prefs.trk_ott = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_jtr", 7)) {
					prefs.trk_jtr = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_tos", 7)) {
					prefs.trk_tos = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_pri", 7)) {
					prefs.trk_pri = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_ooo", 7)) {
					prefs.trk_ooo = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_dup", 7)) {
					prefs.trk_dup = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_loss", 8)) {
					prefs.trap_loss = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_rtt", 7)) {
					prefs.trap_rtt = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_ott", 7)) {
					prefs.trap_ott = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_jtr", 7)) {
					prefs.trap_jtr = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_tos", 7)) {
					prefs.trap_tos = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_pri", 7)) {
					prefs.trap_pri = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_ooo", 7)) {
					prefs.trap_ooo = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_dup", 7)) {
					prefs.trap_dup = atoi(row[1]);
				}
			} else {
				if (!strncmp(row[0], "port", 4) && prefs.port == 0) {
					prefs.port = htons(atoi(row[1]));
				} else if (!strncmp(row[0], "repeat", 6) && prefs.repeat == 0) {
					prefs.repeat = atoi(row[1]);
				} else if (!strncmp(row[0], "rate", 4) && prefs.rate == 0) {
					prefs.rate = atoi(row[1]);
				} else if (!strncmp(row[0], "size", 4) && prefs.size == 0) {
					prefs.size = atoi(row[1]);
				} else if (!strncmp(row[0], "fragment", 8) && prefs.fragment == 0) {
					prefs.fragment = atoi(row[1]);
				} else if (!strncmp(row[0], "ttl", 3) && prefs.ttl == 0) {
					prefs.ttl = atoi(row[1]);
				} else if (!strncmp(row[0], "maxott", 6) && prefs.maxott == 0) {
					prefs.maxott = atoi(row[1]);
				} else if (!strncmp(row[0], "maxage", 6) && prefs.maxage == 0) {
					prefs.maxage = atoi(row[1]);
				} else if (!strncmp(row[0], "key", 3) && strlen(prefs.key) < 1) {
					strncpy(prefs.key, row[1], sizeof(prefs.key));
				} else if (!strncmp(row[0], "tos", 3) && (prefs.tos == NULL || !first)) {
					if (first) {
						free_tos();
						first = 0;
					}
					add_tos(row[1], 0);
				} else if (!strncmp(row[0], "pattern", 7) && prefs.pattern == NULL) {
					prefs.pattern = (char*) strndup(row[1], 255);
				} else if (!strncmp(row[0], "last", 4) && prefs.last == 0) {
					prefs.last = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_ott", 7) && prefs.trk_ott == 0) {
					prefs.trk_ott = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_jtr", 7) && prefs.trk_jtr == 0) {
					prefs.trk_jtr = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_tos", 7) && prefs.trk_tos == 0) {
					prefs.trk_tos = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_pri", 7) && prefs.trk_pri == 0) {
					prefs.trk_pri = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_ooo", 7) && prefs.trk_ooo == 0) {
					prefs.trk_ooo = atoi(row[1]);
				} else if (!strncmp(row[0], "trk_dup", 7) && prefs.trk_dup == 0) {
					prefs.trk_dup = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_loss", 8) && prefs.trap_loss == 0) {
					prefs.trap_loss = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_rtt", 7) && prefs.trap_rtt == 0) {
					prefs.trap_rtt = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_ott", 7) && prefs.trap_ott == 0) {
					prefs.trap_ott = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_jtr", 7) && prefs.trap_jtr == 0) {
					prefs.trap_jtr = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_tos", 7) && prefs.trap_tos == 0) {
					prefs.trap_tos = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_pri", 7) && prefs.trap_pri == 0) {
					prefs.trap_pri = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_ooo", 7) && prefs.trap_ooo == 0) {
					prefs.trap_ooo = atoi(row[1]);
				} else if (!strncmp(row[0], "trap_dup", 7) && prefs.trap_dup == 0) {
					prefs.trap_dup = atoi(row[1]);
				}
			}
		}
		mysql_free_result(res);
	}

	pthread_mutex_unlock(&db_mutex);
	pthread_mutex_unlock(&probe_mutex);

	/* Set defaults if nothing is set. */
	if (prefs.port == 0) {
		prefs.port = htons(DEF_PORT);
	}

	if (prefs.rate == 0) {
		prefs.rate = DEF_RATE;
	}

	if (prefs.size == 0) {
		prefs.size = DEF_SIZE;
	}

	if (prefs.ttl == 0) {
		prefs.ttl = DEF_TTL;
	}

	if (prefs.maxott == 0) {
		prefs.maxott = DEF_MAXOTT;
	}

	if (prefs.maxage == 0) {
		prefs.maxage = DEF_MAXAGE;
	}

	if (strlen(prefs.key) < 1) {
		strncpy(prefs.key, DEF_KEY, sizeof(prefs.key));
	}

	if (prefs.tos == NULL) {
		add_tos(DEF_TOS, 0);
	}

	if (prefs.pattern == NULL) {
		prefs.pattern = (char*) malloc(strlen(DEF_PATTERN) + 1);
		strcpy(prefs.pattern, DEF_PATTERN);
	}

	if (prefs.fragment == 0) {
		prefs.fragment = -1;
	}

	if (prefs.trk_ott == 0) {
		prefs.trk_ott = 1;
	}

	if (prefs.trk_jtr == 0) {
		prefs.trk_jtr = 1;
	}

	if (prefs.trk_tos == 0) {
		prefs.trk_tos = 1;
	}

	if (prefs.trk_pri == 0) {
		prefs.trk_pri = 1;
	}

	if (prefs.trk_ooo == 0) {
		prefs.trk_ooo = 1;
	}

	if (prefs.trk_dup == 0) {
		prefs.trk_dup = 1;
	}

	if (prefs.trap_loss == 0) {
		prefs.trap_loss = DEF_THRESHOLD_LOSS;
	}

	if (prefs.trap_rtt == 0) {
		prefs.trap_rtt = DEF_THRESHOLD_RTT;
	}

	if (prefs.trap_ott == 0) {
		prefs.trap_ott = DEF_THRESHOLD_OTT;
	}

	if (prefs.trap_jtr == 0) {
		prefs.trap_jtr = DEF_THRESHOLD_JTR;
	}

	if (prefs.trap_tos == 0) {
		prefs.trap_tos = DEF_THRESHOLD_TOS;
	}

	if (prefs.trap_pri == 0) {
		prefs.trap_pri = DEF_THRESHOLD_PRI;
	}

	if (prefs.trap_ooo == 0) {
		prefs.trap_ooo = DEF_THRESHOLD_OOO;
	}

	if (prefs.trap_dup == 0) {
		prefs.trap_dup = DEF_THRESHOLD_DUP;
	}

	return 0;
}

int write_prefs() {
	struct tos_t* cur;
	int ret_val = 1;
	char sql[132], front[] = "INSERT INTO prefs (var,val) VALUES(";
	MYSQL_RES *res;
	MYSQL_ROW row;

	pthread_mutex_lock(&db_mutex);

	if (db_query(&res, "LOCK TABLE prefs WRITE") != 0) {
		goto exit;
	}
	mysql_free_result(res);

	if (db_query(&res, "DELETE FROM prefs") != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'port',%d)", front, ntohs(prefs.port));
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'repeat',%lu)", front, prefs.repeat);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'rate',%lu)", front, prefs.rate);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'size',%lu)", front, prefs.size);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'fragment',%lu)", front, prefs.fragment);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'ttl',%lu)", front, prefs.ttl);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'maxott',%lu)", front, prefs.maxott);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'maxage',%lu)", front, prefs.maxage);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'key','%s')", front, prefs.key);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	cur = prefs.tos;
	while (cur != NULL) {
		snprintf(sql, sizeof(sql), "%s'tos','%d:%d')", front, cur->tos, cur->pri);
		if (db_query(&res, sql) != 0) {
			goto unlock;
		}
		mysql_free_result(res);
		cur = cur->next;
	}

	snprintf(sql, sizeof(sql), "%s'pattern','%s')", front, prefs.pattern);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	prefs.last = time(NULL);
	snprintf(sql, sizeof(sql), "%s'last',%lu)", front, prefs.last);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trk_ott','%d')", front, prefs.trk_ott);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trk_jtr','%d')", front, prefs.trk_jtr);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trk_tos','%d')", front, prefs.trk_tos);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trk_pri','%d')", front, prefs.trk_pri);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trk_ooo','%d')", front, prefs.trk_ooo);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trk_dup','%d')", front, prefs.trk_dup);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trap_loss','%d')", front, prefs.trap_loss);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trap_rtt','%d')", front, prefs.trap_rtt);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trap_ott','%d')", front, prefs.trap_ott);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trap_jtr','%d')", front, prefs.trap_jtr);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trap_tos','%d')", front, prefs.trap_tos);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trap_pri','%d')", front, prefs.trap_pri);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trap_ooo','%d')", front, prefs.trap_ooo);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	snprintf(sql, sizeof(sql), "%s'trap_dup','%d')", front, prefs.trap_dup);
	if (db_query(&res, sql) != 0) {
		goto unlock;
	}
	mysql_free_result(res);

	/* Successful. */
	ret_val = 0;

unlock:
	db_query(&res, "UNLOCK TABLES");
	mysql_free_result(res);

exit:
	pthread_mutex_unlock(&db_mutex);

	return ret_val;
}

uint64_t last_prefs() {
	MYSQL_RES *res;
	MYSQL_ROW row;
	uint64_t last = 0;

	pthread_mutex_lock(&probe_mutex);
	pthread_mutex_lock(&db_mutex);

	if (db_query(&res, "SELECT val FROM prefs WHERE var = 'last'") == 0) {
		while (row = mysql_fetch_row(res)) {
			if (args.debug >= DEBUG_DUMP) {
				syslog(LOG_DEBUG, "read val: %s (last)", row[0]);
			}
			last = atoi(row[0]);
		}

		mysql_free_result(res);
	}

	pthread_mutex_unlock(&db_mutex);
	pthread_mutex_unlock(&probe_mutex);

	return last;
}

void add_tos(const char* str, int shift) {
	/* NOTE: Allows duplicate entries and converts garbage input to 0. */
	int i, len;
	char buf[132];
	struct tos_t *node, **cur;

	assert(str != NULL);

	/* Zero buf and copy input. */
	memset(buf, 0, sizeof(buf));
	strncpy(buf, str, sizeof(buf) - 1);

	/* Build new tos node */
	len = strlen(buf);
	for (i = 0; i <= len; i++) {
		if (buf[i] == ':' || buf[i] == '\0') {
			/* Allocate tos_t struct. */
			node = malloc(sizeof(struct tos_t));
			node->next = NULL;

			/* Set ToS and Priority */
			node->tos = atoi(buf) << shift;
			if (buf[i + 1] == '\0') {
				node->pri = node->tos >> 5;
			} else {
				node->pri = atoi(buf + i + 1);
			}

			break;
		}
	}

	/* Don't add entries with invalid tos or pri. */
	if (node->tos > 255 || node->pri > 7) {
		free(node);

		return;
	}

	/* Find end of list and check for duplicates. */
	cur = &(prefs.tos);
	while (*cur != NULL) {
		/* Don't add duplicate entries. */
		if ((*cur)->tos == node->tos && (*cur)->pri == node->pri) {
			free(node);

			return;
		}
		cur = &(*cur)->next;
	}

	/* Add node to end of list. */
	*cur = node;
}

void free_args() {
	if (args.db_host) free(args.db_host);
	if (args.db_sock) free(args.db_sock);
	if (args.db_name) free(args.db_name);
	if (args.db_user) free(args.db_user);
	if (args.db_pass) free(args.db_pass);
	if (args.name) free(args.name);
	if (args.location) free(args.location);
	if (args.description) free(args.description);
	if (args.config) free(args.config);
	if (args.user) free(args.user);
	if (args.group) free(args.group);
	if (args.chroot) free(args.chroot);
	if (args.stats) free(args.stats);

	memset(&args, 0, sizeof(args));
}

void free_prefs() {
	snmptrap_peer_t *s;

	free_tos();

	if (prefs.pattern) free(prefs.pattern);

	while (prefs.peer) {
		s = prefs.peer;
		prefs.peer = s->next;
		free(s);
	}

	memset(&prefs, 0, sizeof(prefs));
}

void free_tos() {
	struct tos_t *cur, *prev;

	cur = prefs.tos;
	while (cur != NULL) {
		prev = cur;
		cur = cur->next;
		free(prev);
	}
	prefs.tos = NULL;
}

void display_args() {
	char *tmp;

	syslog(LOG_DEBUG, "[Arguments]");

	syslog(LOG_DEBUG, " DB Host: %s:%lu", (args.db_host) ? args.db_host : "none", args.db_port);
	syslog(LOG_DEBUG, " DB Sock: %s", (args.db_sock) ? args.db_sock : "none");
	syslog(LOG_DEBUG, " DB Name: %s", (args.db_name) ? args.db_name : "none");
	syslog(LOG_DEBUG, " DB User: %s", (args.db_user) ? args.db_user : "none");
	syslog(LOG_DEBUG, " DB Pass: %s", (args.db_pass) ? args.db_pass : "none");
	syslog(LOG_DEBUG, " DB Comp: %s", (args.db_comp) ? "true" : "false");

	syslog(LOG_DEBUG, " Write: %s", (args.write) ? "true" : "false");
	syslog(LOG_DEBUG, " Print: %s", (args.print) ? "true" : "false");

	tmp = inet_ntoa(args.source);
	syslog(LOG_DEBUG, " Source: %s", tmp);

	tmp = inet_ntoa(args.interface);
	syslog(LOG_DEBUG, " Interface: %s", tmp);

	syslog(LOG_DEBUG, " Name: %s", args.name);

	if (args.location) {
		syslog(LOG_DEBUG, " Location: %s", args.location);
	} else {
		syslog(LOG_DEBUG, " Location: none");
	}

	if (args.description) {
		syslog(LOG_DEBUG, " Description: %s", args.description);
	} else {
		syslog(LOG_DEBUG, " Description: none");
	}

	if (args.config) {
		syslog(LOG_DEBUG, " Configuration: %s", args.config);
	} else {
		syslog(LOG_DEBUG, " Configuration: none");
	}

	if (args.user) {
		syslog(LOG_DEBUG, " User: %s", args.user);
	} else {
		syslog(LOG_DEBUG, " User: current");
	}

	if (args.group) {
		syslog(LOG_DEBUG, " Group: %s", args.group);
	} else {
		syslog(LOG_DEBUG, " Group: current");
	}

	if (args.stats) {
		syslog(LOG_DEBUG, " Stats: %s", args.stats);
	} else {
		syslog(LOG_DEBUG, " Stats: disabled");
	}

	if (args.chroot) {
		syslog(LOG_DEBUG, " Chroot: %s", args.chroot);
	} else {
		syslog(LOG_DEBUG, " Chroot: disabled");
	}

	syslog(LOG_DEBUG, " Nice: %d", args.nice);

	if (args.debug > 0) {
		syslog(LOG_DEBUG, " Debug: %d", args.debug);
	} else {
		syslog(LOG_DEBUG, " Debug: disabled");
	}
}

void display_prefs(int tty) {
	char buf[132], buf2[32];
	struct tos_t* cur;
	snmptrap_peer_t *s;

	if (tty == 1) {
		fprintf(stderr, "\n[Preferences]\n");

		fprintf(stderr, " Port: %d\n", ntohs(prefs.port));

		fprintf(stderr, " Repeat: %d sec\n", prefs.repeat);

		fprintf(stderr, " Rate: %d/min\n", prefs.rate);

		fprintf(stderr, " Size: %d bytes\n", prefs.size);

		fprintf(stderr, " TTL: %d\n", prefs.ttl);

		fprintf(stderr, " MaxOTT: %d msec\n", prefs.maxott);

		fprintf(stderr, " MaxAge: %d sec\n", prefs.maxage);

		fprintf(stderr, " Key: %s\n", prefs.key);

		cur = prefs.tos;
		while (cur != NULL) {
			fprintf(stderr, " ToS: %d:%d (dscp: %d, prec: %d)\n", cur->tos, cur->pri, cur->tos >> 2, cur->tos >> 5);
			cur = cur->next;
		}

		fprintf(stderr, " Pattern: %s\n", prefs.pattern);

		fprintf(stderr, " Fragment: %s\n", (prefs.fragment == 1) ? "true" : "false");

		fprintf(stderr, " Track OTT: %s\n", (prefs.trk_ott == 1) ? "true" : "false");

		fprintf(stderr, " Track JTR: %s\n", (prefs.trk_jtr == 1) ? "true" : "false");

		fprintf(stderr, " Track TOS %s\n", (prefs.trk_tos == 1) ? "true" : "false");

		fprintf(stderr, " Track PRI %s\n", (prefs.trk_pri == 1) ? "true" : "false");

		fprintf(stderr, " Track OOO: %s\n", (prefs.trk_ooo == 1) ? "true" : "false");

		fprintf(stderr, " Track DUP: %s\n", (prefs.trk_dup == 1) ? "true" : "false");

		fprintf(stderr, " Trap LOSS: %d\n", (prefs.trap_ott == MAXINT) ? 0 : prefs.trap_loss);

		fprintf(stderr, " Trap RTT: %d\n", (prefs.trap_rtt == MAXINT) ? 0 : prefs.trap_rtt);

		fprintf(stderr, " Trap OTT: %d\n", (prefs.trap_ott == MAXINT) ? 0 : prefs.trap_ott);

		fprintf(stderr, " Trap JTR: %d\n", (prefs.trap_jtr == MAXINT) ? 0 : prefs.trap_jtr);

		fprintf(stderr, " Trap TOS %d\n", (prefs.trap_tos == MAXINT) ? 0 : prefs.trap_tos);

		fprintf(stderr, " Trap PRI %d\n", (prefs.trap_pri == MAXINT) ? 0 : prefs.trap_pri);

		fprintf(stderr, " Trap OOO: %d\n", (prefs.trap_ooo == MAXINT) ? 0 : prefs.trap_ooo);

		fprintf(stderr, " Trap DUP: %d\n", (prefs.trap_dup == MAXINT) ? 0 : prefs.trap_dup);

		if ((s = prefs.peer) != NULL) {
			while (s != NULL) {
				fprintf(stderr, " SNMPTrap: %s:%s:%s:%d (host[:port]:community:version:inform)\n", s->peername, s->community, (s->version == SNMP_VERSION_1) ? "v1" : (s->version == SNMP_VERSION_3) ? "v3" : "v2c", s->inform);
				s = s->next;
			}
		} else {
			fprintf(stderr, " SNMPTrap: none\n");
		}
		fprintf(stderr, "\n");
	} else {
		syslog(LOG_DEBUG, "[Preferences]");

		syslog(LOG_DEBUG, " Port: %d", ntohs(prefs.port));

		syslog(LOG_DEBUG, " Repeat: %d sec", prefs.repeat);

		syslog(LOG_DEBUG, " Rate: %d/min", prefs.rate);

		syslog(LOG_DEBUG, " Size: %d bytes", prefs.size);

		syslog(LOG_DEBUG, " TTL: %d", prefs.ttl);

		syslog(LOG_DEBUG, " MaxOTT: %d msec", prefs.maxott);

		syslog(LOG_DEBUG, " MaxAge: %d sec", prefs.maxage);

		syslog(LOG_DEBUG, " Key: %s", prefs.key);

		cur = prefs.tos;
		while (cur != NULL) {
			syslog(LOG_DEBUG, " ToS: %d:%d (dscp: %d, prec: %d)\n", cur->tos, cur->pri, cur->tos >> 2, cur->tos >> 5);
			cur = cur->next;
		}

		syslog(LOG_DEBUG, " Pattern: %s", prefs.pattern);

		syslog(LOG_DEBUG, " Fragment: %s", (prefs.fragment == 1) ? "true" : "false");

		syslog(LOG_DEBUG, " Track OTT: %s", (prefs.trk_ott == 1) ? "true" : "false");

		syslog(LOG_DEBUG, " Track JTR: %s", (prefs.trk_jtr == 1) ? "true" : "false");

		syslog(LOG_DEBUG, " Track TOS %s", (prefs.trk_tos == 1) ? "true" : "false");

		syslog(LOG_DEBUG, " Track PRI %s", (prefs.trk_pri == 1) ? "true" : "false");

		syslog(LOG_DEBUG, " Track OOO: %s", (prefs.trk_ooo == 1) ? "true" : "false");

		syslog(LOG_DEBUG, " Track DUP: %s", (prefs.trk_dup == 1) ? "true" : "false");

		syslog(LOG_DEBUG, " Trap LOSS: %d\n", (prefs.trap_ott == MAXINT) ? 0 : prefs.trap_loss);

		syslog(LOG_DEBUG, " Trap RTT: %d\n", (prefs.trap_rtt == MAXINT) ? 0 : prefs.trap_rtt);

		syslog(LOG_DEBUG, " Trap OTT: %d\n", (prefs.trap_ott == MAXINT) ? 0 : prefs.trap_ott);

		syslog(LOG_DEBUG, " Trap JTR: %d\n", (prefs.trap_jtr == MAXINT) ? 0 : prefs.trap_jtr);

		syslog(LOG_DEBUG, " Trap TOS %d\n", (prefs.trap_tos == MAXINT) ? 0 : prefs.trap_tos);

		syslog(LOG_DEBUG, " Trap PRI %d\n", (prefs.trap_pri == MAXINT) ? 0 : prefs.trap_pri);

		syslog(LOG_DEBUG, " Trap OOO: %d\n", (prefs.trap_ooo == MAXINT) ? 0 : prefs.trap_ooo);

		syslog(LOG_DEBUG, " Trap DUP: %d\n", (prefs.trap_dup == MAXINT) ? 0 : prefs.trap_dup);

		if ((s = prefs.peer) != NULL) {
			while (s != NULL) {
				syslog(LOG_DEBUG, " snmptrap: %s:%s:%s:%d (host[:port]:community:version:inform)", s->peername, s->community, (s->version == SNMP_VERSION_1) ? "v1" : (s->version == SNMP_VERSION_3) ? "v3" : "v2c", s->inform);
				s = s->next;
			}
		} else {
			syslog(LOG_DEBUG, " snmptrap: none");
		}
	}
}

int process_config(struct config_node *node) {
	int i, warn = 0, valid;
	char buf[512], *ptr, *snmpport = NULL;
	snmptrap_peer_t **s;
	struct in_addr ip;
	struct config_node *n;

	while (node != NULL) {
		/* Source */
		if (strcasecmp(node->var, "source") == 0 && node->val != NULL) {
			if (args.source.s_addr == htonl(INADDR_ANY)) {
				/* Try converting as if its an IP address. */
				if (inet_aton(node->val, &args.source) == 0) {
					/* Try finding the IP, assuming its an source name. */
					source_ip(node->val, &args.source);
				}
			}

		/* Interface */
		} else if (strcasecmp(node->var, "interface") == 0 && node->val != NULL) {
			if (args.interface.s_addr == htonl(INADDR_ANY)) {
				/* Try converting as if its an IP address. */
				if (inet_aton(node->val, &args.interface) == 0) {
					/* Try finding the IP, assuming its an interface name. */
					source_ip(node->val, &args.interface);
				}
			}

		/* Port */
		} else if (strcasecmp(node->var, "port") == 0 && node->val != NULL) {
			if (prefs.port == 0) {
				prefs.port = atoi(node->val);

				if (prefs.port > 0) {
					prefs.port = htons(prefs.port);
				} else {
					prefs.port = htons(DEF_PORT);
				}
			}

		} else if (strcasecmp(node->var, "name") == 0 && node->val != NULL) {
			if (args.name == NULL) {
				args.name = (char*) strndup(node->val, 64);
			}

		} else if (strcasecmp(node->var, "location") == 0 && node->val != NULL) {
			if (args.location == NULL) {
				args.location = (char*) strndup(node->val, 132);
			}

		} else if (strcasecmp(node->var, "description") == 0 && node->val != NULL) {
			if (args.description == NULL) {
				args.description = (char*) strndup(node->val, 255);
			}

		/* Repeat */
		} else if (strcasecmp(node->var, "repeat") == 0 && node->val != NULL) {
			if (prefs.repeat == 0) {
				prefs.repeat = atoi(node->val);

				if (prefs.repeat > MAX_REPEAT) {
					prefs.repeat = DEF_REPEAT;
				}
			}

		/* Rate */
		} else if (strcasecmp(node->var, "rate") == 0 && node->val != NULL) {
			if (prefs.rate == 0) {
				prefs.rate = atoi(node->val);

				if (prefs.rate > MAX_RATE) {
					prefs.rate = DEF_RATE;
				}
			}

		} else if (strcasecmp(node->var, "dscp") == 0) {
			if (node->val != NULL) {
				add_tos(node->val, 2);
			}

			n = node->sub;
			while (n != NULL) {
				add_tos(n->var, 2);
				n = n->next;
			}
		} else if (strcasecmp(node->var, "precedence") == 0) {
			if (node->val != NULL) {
				add_tos(node->val, 5);
			}

			n = node->sub;
			while (n != NULL) {
				add_tos(n->var, 5);
				n = n->next;
			}
		} else if (strcasecmp(node->var, "tos") == 0) {
			if (node->val != NULL) {
				add_tos(node->val, 0);
			}

			n = node->sub;
			while (n != NULL) {
				add_tos(n->var, 0);
				n = n->next;
			}

		} else if (strcasecmp(node->var, "maxott") == 0 && node->val != NULL) {
			if (prefs.maxott == 0) {
				prefs.maxott = atoi(node->val);

				if (prefs.maxott > MAX_MAXOTT) {
					prefs.maxott = DEF_MAXOTT;
				}
			}

		} else if (strcasecmp(node->var, "maxage") == 0 && node->val != NULL) {
			if (prefs.maxage == 0) {
				prefs.maxage = atoi(node->val);

				if (prefs.maxage > MAX_MAXAGE) {
					prefs.maxage = DEF_MAXAGE;
				}
			}

		} else if (strcasecmp(node->var, "key") == 0 && node->val != NULL) {
			if (prefs.key[0] == '\0') {
				strncpy(prefs.key, node->val, sizeof(prefs.key));
			}

		} else if (strcasecmp(node->var, "size") == 0 && node->val != NULL) {
			if (prefs.size == 0) {
				prefs.size = atoi(node->val);

				if (prefs.size > MAX_SIZE) {
					prefs.size = DEF_SIZE;
				}
			}

		} else if (strcasecmp(node->var, "pattern") == 0 && node->val != NULL) {
			if (prefs.pattern == NULL) {
				prefs.pattern = (char*) strndup(node->val, 255);
			}

		} else if (strcasecmp(node->var, "fragment") == 0) {
			if (prefs.fragment == 0) {
				if (n->val == NULL || strcasecmp(n->var, "yes") == 0 || strcasecmp(n->var, "on") == 0 || strcasecmp(n->var, "1") == 0) {
					prefs.fragment = 1;
				}
			}

		} else if (strcasecmp(node->var, "ttl") == 0 && node->val != NULL) {
			if (prefs.ttl == 0) {
				prefs.ttl = atoi(node->val);

           if (prefs.ttl > MAX_TTL || prefs.ttl < 1) {
					prefs.ttl = DEF_TTL;
				}
			}

		} else if (strcasecmp(node->var, "snmptrap") == 0) {
#ifdef HAVE_NET_SNMP
			s = &(prefs.peer);
			while ((*s) != NULL) s = &((*s)->next); /* Move to the end of peer list */
			*s = malloc(sizeof(snmptrap_peer_t));
			memset(*s, 0, sizeof(snmptrap_peer_t));

			strncpy((*s)->community, "public", sizeof((*s)->community));
			(*s)->version = SNMP_VERSION_2c;

			/* Capture peername (if available) */
			if (node->val != NULL) {
				strncpy((*s)->peername, node->val, sizeof((*s)->peername));
			}

			n = node->sub;
			while (n != NULL) {
				if (strcasecmp(n->var, "host") == 0 && n->val != NULL) {
					strncpy((*s)->peername, n->val, sizeof((*s)->peername));
				} else if (strcasecmp(n->var, "port") == 0 && n->val != NULL) {
					if (atoi(n->val) >= 1 && atoi(n->val) <= 65535) { /* Ignore invalid range */
						if (snmpport) free(snmpport);
						snmpport = (char*) strndup(n->val, 32);
					}
				} else if (strcasecmp(n->var, "community") == 0 && n->val != NULL) {
					strncpy((*s)->community, n->val, sizeof((*s)->community));
				} else if (strcasecmp(n->var, "version") == 0 && n->val != NULL) {
					if (strcasecmp(n->val, "1") == 0 || strcasecmp(n->val, "v1") == 0) {
						(*s)->version = SNMP_VERSION_1;
					} else if (strcasecmp(n->val, "3") == 0 || strcasecmp(n->val, "v3") == 0) {
						(*s)->version = SNMP_VERSION_3;
					} else {
						(*s)->version = SNMP_VERSION_2c;
					}
				} else if (strcasecmp(n->var, "inform") == 0) {
					if (n->val == NULL) { /* Keyword only - enabled */
						(*s)->inform = 1;
					} else if (strncasecmp(n->val, "y", 1) == 0 || strcasecmp(n->val, "on") == 0 || strcasecmp(n->val, "1") == 0) { /* Yes, On or 1 */
						(*s)->inform = 1;
					} else {
						(*s)->inform = 0;
					}
				} else {
					syslog(LOG_INFO, "invalid option: <%s> %s", node->var, n->var);
				}

				n = n->next;
			}

			/* Remove peer if not valid */
			if ((*s)->peername[0] == '\0') {
				syslog(LOG_INFO, "invalid option: <%s> (missing 'host')", node->var);
				free(*s);
				*s = NULL;
			} else if (snmpport != NULL && strchr((*s)->peername, ':') == NULL) {
				snprintf(buf, sizeof(buf), "%s:%u", (*s)->peername, atoi(snmpport));
				strncpy((*s)->peername, buf, sizeof((*s)->peername));
			}

			if (snmpport) {
				free(snmpport);
				snmpport = NULL;
			}
#else
			if (warn == 0) {
				fprintf(stderr, "Support for '--snmptrap' is not compiled into the binary.\n");
				warn = 1;
			}
#endif /* HAVE_NET_SNMP */

		} else if (strcasecmp(node->var, "database") == 0) {
			if (args.db_host == NULL) {
				/* Capture database host (if available) */
				if (node->val != NULL) {
					args.db_host = (char*) strndup(node->val, 132);
				}

				n = node->sub;
				while (n != NULL) {
					if (strncasecmp(n->var, "host", 4) == 0 && n->val != NULL) {
						if (args.db_host == NULL) {
							args.db_host = (char*) strndup(n->val, 132);
						}

					} else if (strcasecmp(n->var, "port") == 0 && n->val != NULL) {
						if (args.db_port == 0 && atoi(n->val) >= 1 && atoi(n->val) <= 65535) { /* Ignore invalid range */
							args.db_port = atoi(n->val);
						}

					} else if (strncasecmp(n->var, "sock", 4) == 0 && n->val != NULL) {
						if (args.db_sock == NULL) {
							args.db_sock = (char*) strndup(n->val, 255);
						}

					} else if (strcasecmp(n->var, "name") == 0 && n->val != NULL) {
						if (args.db_name == NULL) {
							args.db_name = (char*) strndup(n->val, 132);
						}

					} else if (strncasecmp(n->var, "user", 4) == 0 && n->val != NULL) {
						if (args.db_user == NULL) {
							args.db_user = (char*) strndup(n->val, 132);
						}

					} else if (strncasecmp(n->var, "pass", 4) == 0 && n->val != NULL) {
						if (args.db_pass == NULL) {
							args.db_pass = (char*) strndup(n->val, 132);
						}

					} else if (strncasecmp(n->var, "comp", 4) == 0) {
						if (args.db_comp == 0) {
							if (n->val == NULL || strcasecmp(n->var, "yes") == 0 || strcasecmp(n->var, "on") == 0 || strcasecmp(n->var, "1") == 0) {
								args.db_comp = 1;
							}
						}

					} else {
						syslog(LOG_INFO, "invalid option: <%s> %s", node->var, n->var);
					}

					n = n->next;
				}
			}

		} else if (strcasecmp(node->var, "user") == 0 && node->val != NULL) {
			if (args.user == NULL) {
				args.user = (char*) strndup(node->val, 32);
			}

		} else if (strcasecmp(node->var, "group") == 0 && node->val != NULL) {
			if (args.group == NULL) {
				args.group = (char*) strndup(node->val, 32);
			}

		} else if (strcasecmp(node->var, "chroot") == 0 && node->val != NULL) {
			if (args.chroot == NULL) {
				args.chroot = (char*) strndup(node->val, 255);
			}

		} else if (strcasecmp(node->var, "stats") == 0) {
			if (args.stats == NULL) {
				if (node->val) {
					args.stats = (char*) strndup(node->val, 255);
				} else {
					args.stats = (char*) strndup(DEF_STATS, 255);
				}
			}

		} else if (strcasecmp(node->var, "nice") == 0 && node->val != NULL) {
			if (args.nice == DEF_NICENESS) {
				args.nice = atoi(node->val);

				if (args.nice < -19 || args.nice > 20) {
					args.nice = DEF_NICENESS;
				}
			}

		} else if (strcasecmp(node->var, "debug") == 0) {
			if (args.debug == 0) {
				if (node->val != NULL) {
					args.debug = strtoul(node->val, NULL, 10);

					if (args.debug > DEBUG_MAXVAL) {
						args.debug = DEBUG_MAXVAL;
					}
				} else {
					args.debug = DEBUG_ERROR;
				}
			}

		} else {
			syslog(LOG_INFO, "invalid option: %s", node->var);
		}

		node = node->next;
	}

	return 0;
}

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