#include "../include/config.h"
#include "../include/common.h"
#include "../include/locations.h"
#include "../include/cgiutils.h"
#include "../include/getcgi.h"
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <stdarg.h>

#define CHARLEN 256
#define max(a,b) ((a)>(b))?(a):(b)

static int process_cgivars(void);

static char *strscpy(char *dest, const char *src);
static char *ssprintf(char *str, const char *fmt, ...);
static void terminate(int result, const char *fmt, ...);
static void get_expire_time_string(time_t *raw_time, char *buffer, int buffer_length);

int main(int argc, char **argv) {
	FILE *fp;
	char *status_file = NULL;
	char *lock_file = NULL;
	char *proc_file = NULL;
	char input_buffer[CHARLEN];
	int c, age, pid, testpid, found;
	int wt = -1;
	int ct = -1;
	struct stat statbuf;
	time_t current_time;

#ifdef DEFAULT_STATUS_FILE
	status_file = strscpy(status_file, DEFAULT_STATUS_FILE);
#else
	status_file = strscpy(status_file, "/usr/local/icinga/var/status.dat");
#endif

#ifdef DEFAULT_LOCK_FILE
	lock_file = strscpy(lock_file, DEFAULT_LOCK_FILE);
#else
	lock_file = strscpy(lock_file, "/usr/local/icinga/var/icinga.lock");
#endif

	if (getenv("REQUEST_METHOD")) {
		process_cgivars();
	} else { /* get arguments */

		while ((c = getopt(argc, argv, "+c:w:s:l:")) != EOF) {
			switch (c) {
			case 'c':
				ct = atoi(optarg);
				break;
			case 'w':
				wt = atoi(optarg);
				break;
			case 's':
				status_file = optarg;
				break;
			case 'l':
				lock_file = optarg;
				break;
			}
		}
	}

	/* find status file, get lastmod time */
	if (stat(status_file, &statbuf) == -1) {
		printf("ICINGA CRITICAL - could not find status log: %s\n", status_file);
		exit(STATE_CRITICAL);
	}

	time(&current_time);
	age = (int)(current_time - statbuf.st_mtime);

	/* find lock file.  get pid if it exists */
	if (stat(lock_file, &statbuf) == -1) {
		printf("ICINGA CRITICAL - could not find lock file: %s\n", lock_file);
		exit(STATE_CRITICAL);
	}

	fp = fopen(lock_file, "r");
	fscanf(fp, "%d", &pid);
	fclose(fp);
	proc_file = ssprintf(proc_file, "/proc/%d", pid);

	if (stat("/proc", &statbuf) == 0) {
		if (stat(proc_file, &statbuf) == -1) {
			printf("ICINGA CRITICAL - could not find proc file: %s\n", proc_file);
			exit(STATE_CRITICAL);
		}
	} else if (snprintf(proc_file, CHARLEN - 1, "/bin/ps -o pid -p %d", pid) && (fp = popen(proc_file, "r")) != NULL) {
		fgets(input_buffer, CHARLEN - 1, fp);
		fgets(input_buffer, CHARLEN - 1, fp);
		if (sscanf(input_buffer, "%d", &testpid) == 1) {
			if (testpid != pid) {
				printf("ICINGA CRITICAL - could not find process(1): %d\n", pid);
				exit(STATE_CRITICAL);
			}
		}
	} else if (snprintf(proc_file, CHARLEN - 1, "/bin/ps -eo pid") && (fp = popen(proc_file, "r")) != NULL) {
		found = FALSE;
		fgets(input_buffer, CHARLEN - 1, fp);
		while (fgets(input_buffer, CHARLEN - 1, fp)) {
			if (sscanf(input_buffer, "%d", &testpid) == 1)
				if (testpid == pid) found = TRUE;
		}
		if (!found) {
			printf("ICINGA CRITICAL - could not find process(2): %d\n", pid);
			exit(STATE_CRITICAL);
		}
	} else if (snprintf(proc_file, CHARLEN - 1, "/bin/ps -Ao pid") && (fp = popen(proc_file, "r")) != NULL) {
		found = FALSE;
		fgets(input_buffer, CHARLEN - 1, fp);
		while (fgets(input_buffer, CHARLEN - 1, fp)) {
			if (sscanf(input_buffer, "%d", &testpid) == 1)
				if (testpid == pid) found = TRUE;
		}
		if (!found) {
			printf("ICINGA CRITICAL - could not find process(2): %d\n", pid);
			exit(STATE_CRITICAL);
		}
	}

	if (ct > 0 && ct < age) {
		printf("ICINGA CRITICAL - status written %d seconds ago\n", age);
		exit(STATE_CRITICAL);
	} else if (wt > 0 && wt < age) {
		printf("ICINGA WARNING - status written %d seconds ago\n", age);
		exit(STATE_WARNING);
	} else {
		printf("ICINGA OK - status written %d seconds ago\n", age);
		exit(STATE_OK);
	}
}

static int process_cgivars(void) {
	char **variables;
	int error = FALSE;
	int x;

	variables = getcgivars();

	for (x = 0; variables[x] != NULL; x++) {

		/* do some basic length checking on the variable identifier to prevent buffer overflows */
		if (strlen(variables[x]) >= MAX_INPUT_BUFFER - 1) {
			x++;
			continue;
		}
	}
	return error;
}

/* get date/time string used in META tags for page expiration */
static void get_expire_time_string(time_t *raw_time, char *buffer, int buffer_length) {
	time_t t;
	struct tm *tm_ptr;
	int day;
	int hour;
	int minute;
	int second;
	int year;
	char *weekdays[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
	char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};

	if (raw_time == NULL)
		time(&t);
	else
		t = *raw_time;

	tm_ptr = gmtime(&t);

	hour = tm_ptr->tm_hour;
	minute = tm_ptr->tm_min;
	second = tm_ptr->tm_sec;
	day = tm_ptr->tm_mday;
	year = tm_ptr->tm_year + 1900;

	snprintf(buffer, buffer_length, "%s, %d %s %d %02d:%02d:%02d GMT", weekdays[tm_ptr->tm_wday], day, months[tm_ptr->tm_mon], year, hour, minute, second);
	buffer[buffer_length-1] = '\x0';

	return;
}

static char *strscpy(char *dest, const char *src) {
	int len;

	if (src != NULL)
		len = strlen(src) + 1;
	else
		return dest;

	if (dest == NULL || strlen(dest) < len)
		dest = realloc(dest, len);
	if (dest == NULL)
		terminate(STATE_UNKNOWN, "failed realloc in strscpy\n");

	strncpy(dest, src, len);

	return dest;
}

static char *ssprintf(char *str, const char *fmt, ...) {
	va_list ap;
	int nchars;
	int size;

	if (str == NULL)
		str = malloc(CHARLEN);
	if (str == NULL)
		terminate(STATE_UNKNOWN, "malloc failed in ssprintf");

	size = max(strlen(str), CHARLEN);

	va_start(ap, fmt);

	while (1) {

		nchars = vsnprintf(str, size, fmt, ap);

		if (nchars > -1)
			if (nchars < size) {
				va_end(ap);
				return str;
			} else {
				size = nchars + 1;
			}

		else
			size *= 2;

		str = realloc(str, nchars + 1);

		if (str == NULL)
			terminate(STATE_UNKNOWN, "realloc failed in ssprintf");
	}

}

static void terminate(int result, const char *fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	vprintf(fmt, ap);
	va_end(ap);
	exit(result);
}
