/*****************************************************************************
 *
 * STATUSDATA.C - External status data for Nagios CGIs
 *
 * Copyright (c) 2000-2006 Ethan Galstad (egalstad@nagios.org)
 * Last Modified:   10-19-2006
 *
 * License:
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *****************************************************************************/

/*********** COMMON HEADER FILES ***********/

#include "../include/config.h"
#include "../include/common.h"
#include "../include/objects.h"
#include "../include/statusdata.h"

#ifdef NSCORE
#include "../include/nagios.h"
#include "../include/broker.h"
#endif
#ifdef NSCGI
#include "../include/cgiutils.h"
#endif

/**** IMPLEMENTATION SPECIFIC HEADER FILES ****/

#ifdef USE_XSDDEFAULT
#include "../xdata/xsddefault.h"		/* default routines */
#endif


#ifdef NSCORE
extern int      aggregate_status_updates;
#endif

#ifdef NSCGI
hoststatus      *hoststatus_list = NULL;
hoststatus      *hoststatus_list_tail = NULL;
servicestatus   *servicestatus_list = NULL;
servicestatus   *servicestatus_list_tail = NULL;

hoststatus      **hoststatus_hashlist = NULL;
servicestatus   **servicestatus_hashlist = NULL;

extern int      use_pending_states;
#endif



#ifdef NSCORE

/******************************************************************/
/****************** TOP-LEVEL OUTPUT FUNCTIONS ********************/
/******************************************************************/

/* initializes status data at program start */
int initialize_status_data(char *config_file) {
	int result = OK;

	/**** IMPLEMENTATION-SPECIFIC CALLS ****/
#ifdef USE_XSDDEFAULT
	result = xsddefault_initialize_status_data(config_file);
#endif

	return result;
	}


/* update all status data (aggregated dump) */
int update_all_status_data(void) {
	int result = OK;

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_aggregated_status_data(NEBTYPE_AGGREGATEDSTATUS_STARTDUMP, NEBFLAG_NONE, NEBATTR_NONE, NULL);
#endif

	/**** IMPLEMENTATION-SPECIFIC CALLS ****/
#ifdef USE_XSDDEFAULT
	result = xsddefault_save_status_data();
#endif

#ifdef USE_EVENT_BROKER
	/* send data to event broker */
	broker_aggregated_status_data(NEBTYPE_AGGREGATEDSTATUS_ENDDUMP, NEBFLAG_NONE, NEBATTR_NONE, NULL);
#endif

	if(result != OK)
		return ERROR;

	return OK;
	}


/* cleans up status data before program termination */
int cleanup_status_data(char *config_file, int delete_status_data) {
	int result = OK;

	/**** IMPLEMENTATION-SPECIFIC CALLS ****/
#ifdef USE_XSDDEFAULT
	result = xsddefault_cleanup_status_data(config_file, delete_status_data);
#endif

	return result;
	}



/* updates program status info */
int update_program_status(int aggregated_dump) {

#ifdef USE_EVENT_BROKER
	/* send data to event broker (non-aggregated dumps only) */
	if(aggregated_dump == FALSE)
		broker_program_status(NEBTYPE_PROGRAMSTATUS_UPDATE, NEBFLAG_NONE, NEBATTR_NONE, NULL);
#endif

	/* currently a noop if aggregated updates is TRUE */

	/* update all status data if we're not aggregating updates */
	if(aggregate_status_updates == FALSE)
		update_all_status_data();

	return OK;
	}



/* updates host status info */
int update_host_status(host *hst, int aggregated_dump) {

#ifdef USE_EVENT_BROKER
	/* send data to event broker (non-aggregated dumps only) */
	if(aggregated_dump == FALSE)
		broker_host_status(NEBTYPE_HOSTSTATUS_UPDATE, NEBFLAG_NONE, NEBATTR_NONE, hst, NULL);
#endif

	/* currently a noop if aggregated updates is TRUE */

	/* update all status data if we're not aggregating updates */
	if(aggregate_status_updates == FALSE)
		update_all_status_data();

	return OK;
	}



/* updates service status info */
int update_service_status(service *svc, int aggregated_dump) {

#ifdef USE_EVENT_BROKER
	/* send data to event broker (non-aggregated dumps only) */
	if(aggregated_dump == FALSE)
		broker_service_status(NEBTYPE_SERVICESTATUS_UPDATE, NEBFLAG_NONE, NEBATTR_NONE, svc, NULL);
#endif

	/* currently a noop if aggregated updates is TRUE */

	/* update all status data if we're not aggregating updates */
	if(aggregate_status_updates == FALSE)
		update_all_status_data();

	return OK;
	}



/* updates contact status info */
int update_contact_status(contact *cntct, int aggregated_dump) {

#ifdef USE_EVENT_BROKER
	/* send data to event broker (non-aggregated dumps only) */
	if(aggregated_dump == FALSE)
		broker_contact_status(NEBTYPE_CONTACTSTATUS_UPDATE, NEBFLAG_NONE, NEBATTR_NONE, cntct, NULL);
#endif

	/* currently a noop if aggregated updates is TRUE */

	/* update all status data if we're not aggregating updates */
	if(aggregate_status_updates == FALSE)
		update_all_status_data();

	return OK;
	}
#endif





#ifdef NSCGI

/******************************************************************/
/******************* TOP-LEVEL INPUT FUNCTIONS ********************/
/******************************************************************/


/* reads in all status data */
int read_status_data(char *config_file, int options) {
	int result = OK;

	/**** IMPLEMENTATION-SPECIFIC CALLS ****/
#ifdef USE_XSDDEFAULT
	result = xsddefault_read_status_data(config_file, options);
#endif
#ifdef USE_XSDDB
	result = xsddb_read_status_data(config_file, options);
#endif

	return result;
	}



/******************************************************************/
/****************** CHAINED HASH FUNCTIONS ************************/
/******************************************************************/

/* adds hoststatus to hash list in memory */
int add_hoststatus_to_hashlist(hoststatus *new_hoststatus) {
	hoststatus *temp_hoststatus = NULL;
	hoststatus *lastpointer = NULL;
	int hashslot = 0;
	int i = 0;

	/* initialize hash list */
	if(hoststatus_hashlist == NULL) {

		hoststatus_hashlist = (hoststatus **)malloc(sizeof(hoststatus *) * HOSTSTATUS_HASHSLOTS);
		if(hoststatus_hashlist == NULL)
			return 0;

		for(i = 0; i < HOSTSTATUS_HASHSLOTS; i++)
			hoststatus_hashlist[i] = NULL;
		}

	if(!new_hoststatus)
		return 0;

	hashslot = hashfunc(new_hoststatus->host_name, NULL, HOSTSTATUS_HASHSLOTS);
	lastpointer = NULL;
	for(temp_hoststatus = hoststatus_hashlist[hashslot]; temp_hoststatus && compare_hashdata(temp_hoststatus->host_name, NULL, new_hoststatus->host_name, NULL) < 0; temp_hoststatus = temp_hoststatus->nexthash)
		lastpointer = temp_hoststatus;

	if(!temp_hoststatus || (compare_hashdata(temp_hoststatus->host_name, NULL, new_hoststatus->host_name, NULL) != 0)) {
		if(lastpointer)
			lastpointer->nexthash = new_hoststatus;
		else
			hoststatus_hashlist[hashslot] = new_hoststatus;
		new_hoststatus->nexthash = temp_hoststatus;

		return 1;
		}

	/* else already exists */
	return 0;
	}


int add_servicestatus_to_hashlist(servicestatus *new_servicestatus) {
	servicestatus *temp_servicestatus = NULL, *lastpointer = NULL;
	int hashslot = 0;
	int i = 0;

	/* initialize hash list */
	if(servicestatus_hashlist == NULL) {

		servicestatus_hashlist = (servicestatus **)malloc(sizeof(servicestatus *) * SERVICESTATUS_HASHSLOTS);
		if(servicestatus_hashlist == NULL)
			return 0;

		for(i = 0; i < SERVICESTATUS_HASHSLOTS; i++)
			servicestatus_hashlist[i] = NULL;
		}

	if(!new_servicestatus)
		return 0;

	hashslot = hashfunc(new_servicestatus->host_name, new_servicestatus->description, SERVICESTATUS_HASHSLOTS);
	lastpointer = NULL;
	for(temp_servicestatus = servicestatus_hashlist[hashslot]; temp_servicestatus && compare_hashdata(temp_servicestatus->host_name, temp_servicestatus->description, new_servicestatus->host_name, new_servicestatus->description) < 0; temp_servicestatus = temp_servicestatus->nexthash)
		lastpointer = temp_servicestatus;

	if(!temp_servicestatus || (compare_hashdata(temp_servicestatus->host_name, temp_servicestatus->description, new_servicestatus->host_name, new_servicestatus->description) != 0)) {
		if(lastpointer)
			lastpointer->nexthash = new_servicestatus;
		else
			servicestatus_hashlist[hashslot] = new_servicestatus;
		new_servicestatus->nexthash = temp_servicestatus;


		return 1;
		}

	/* else already exists */
	return 0;
	}



/******************************************************************/
/********************** ADDITION FUNCTIONS ************************/
/******************************************************************/


/* adds a host status entry to the list in memory */
int add_host_status(hoststatus *new_hoststatus) {
	char date_string[MAX_DATETIME_LENGTH];

	/* make sure we have what we need */
	if(new_hoststatus == NULL)
		return ERROR;
	if(new_hoststatus->host_name == NULL)
		return ERROR;

	/* massage host status a bit */
	if(new_hoststatus != NULL) {
		switch(new_hoststatus->status) {
			case 0:
				new_hoststatus->status = HOST_UP;
				break;
			case 1:
				new_hoststatus->status = HOST_DOWN;
				break;
			case 2:
				new_hoststatus->status = HOST_UNREACHABLE;
				break;
			default:
				new_hoststatus->status = HOST_UP;
				break;
			}
		if(new_hoststatus->has_been_checked == FALSE) {
			if(use_pending_states == TRUE)
				new_hoststatus->status = HOST_PENDING;
			my_free(new_hoststatus->plugin_output);
			if(new_hoststatus->should_be_scheduled == TRUE) {
				get_time_string(&new_hoststatus->next_check, date_string, sizeof(date_string), LONG_DATE_TIME);
				asprintf(&new_hoststatus->plugin_output, "Host check scheduled for %s", date_string);
				}
			else {
				/* passive-only hosts that have just been scheduled for a forced check */
				if(new_hoststatus->checks_enabled == FALSE && new_hoststatus->next_check != (time_t)0L && (new_hoststatus->check_options & CHECK_OPTION_FORCE_EXECUTION)) {
					get_time_string(&new_hoststatus->next_check, date_string, sizeof(date_string), LONG_DATE_TIME);
					asprintf(&new_hoststatus->plugin_output, "Forced host check scheduled for %s", date_string);
					}
				/* passive-only hosts not scheduled to be checked */
				else
					new_hoststatus->plugin_output = (char *)strdup("Host is not scheduled to be checked...");
				}
			}
		}

	new_hoststatus->next = NULL;
	new_hoststatus->nexthash = NULL;

	/* add new hoststatus to hoststatus chained hash list */
	if(!add_hoststatus_to_hashlist(new_hoststatus))
		return ERROR;

	/* object cache file is already sorted, so just add new items to end of list */
	if(hoststatus_list == NULL) {
		hoststatus_list = new_hoststatus;
		hoststatus_list_tail = new_hoststatus;
		}
	else {
		hoststatus_list_tail->next = new_hoststatus;
		hoststatus_list_tail = new_hoststatus;
		}

	return OK;
	}


/* adds a service status entry to the list in memory */
int add_service_status(servicestatus *new_svcstatus) {
	char date_string[MAX_DATETIME_LENGTH];

	/* make sure we have what we need */
	if(new_svcstatus == NULL)
		return ERROR;
	if(new_svcstatus->host_name == NULL || new_svcstatus->description == NULL)
		return ERROR;


	/* massage service status a bit */
	if(new_svcstatus != NULL) {
		switch(new_svcstatus->status) {
			case 0:
				new_svcstatus->status = SERVICE_OK;
				break;
			case 1:
				new_svcstatus->status = SERVICE_WARNING;
				break;
			case 2:
				new_svcstatus->status = SERVICE_CRITICAL;
				break;
			case 3:
				new_svcstatus->status = SERVICE_UNKNOWN;
				break;
			default:
				new_svcstatus->status = SERVICE_OK;
				break;
			}
		if(new_svcstatus->has_been_checked == FALSE) {
			if(use_pending_states == TRUE)
				new_svcstatus->status = SERVICE_PENDING;
			my_free(new_svcstatus->plugin_output);
			if(new_svcstatus->should_be_scheduled == TRUE) {
				get_time_string(&new_svcstatus->next_check, date_string, sizeof(date_string), LONG_DATE_TIME);
				asprintf(&new_svcstatus->plugin_output, "Service check scheduled for %s", date_string);
				}
			else {
				/* passive-only services that have just been scheduled for a forced check */
				if(new_svcstatus->checks_enabled == FALSE && new_svcstatus->next_check != (time_t)0L && (new_svcstatus->check_options & CHECK_OPTION_FORCE_EXECUTION)) {
					get_time_string(&new_svcstatus->next_check, date_string, sizeof(date_string), LONG_DATE_TIME);
					asprintf(&new_svcstatus->plugin_output, "Forced service check scheduled for %s", date_string);
					}
				/* passive-only services not scheduled to be checked */
				else
					new_svcstatus->plugin_output = (char *)strdup("Service is not scheduled to be checked...");
				}
			}
		}

	new_svcstatus->next = NULL;
	new_svcstatus->nexthash = NULL;

	/* add new servicestatus to servicestatus chained hash list */
	if(!add_servicestatus_to_hashlist(new_svcstatus))
		return ERROR;

	/* object cache file is already sorted, so just add new items to end of list */
	if(servicestatus_list == NULL) {
		servicestatus_list = new_svcstatus;
		servicestatus_list_tail = new_svcstatus;
		}
	else {
		servicestatus_list_tail->next = new_svcstatus;
		servicestatus_list_tail = new_svcstatus;
		}

	return OK;
	}





/******************************************************************/
/*********************** CLEANUP FUNCTIONS ************************/
/******************************************************************/


/* free all memory for status data */
void free_status_data(void) {
	hoststatus *this_hoststatus = NULL;
	hoststatus *next_hoststatus = NULL;
	servicestatus *this_svcstatus = NULL;
	servicestatus *next_svcstatus = NULL;

	/* free memory for the host status list */
	for(this_hoststatus = hoststatus_list; this_hoststatus != NULL; this_hoststatus = next_hoststatus) {
		next_hoststatus = this_hoststatus->next;
		my_free(this_hoststatus->host_name);
		my_free(this_hoststatus->plugin_output);
		my_free(this_hoststatus->long_plugin_output);
		my_free(this_hoststatus->perf_data);
		my_free(this_hoststatus);
		}

	/* free memory for the service status list */
	for(this_svcstatus = servicestatus_list; this_svcstatus != NULL; this_svcstatus = next_svcstatus) {
		next_svcstatus = this_svcstatus->next;
		my_free(this_svcstatus->host_name);
		my_free(this_svcstatus->description);
		my_free(this_svcstatus->plugin_output);
		my_free(this_svcstatus->long_plugin_output);
		my_free(this_svcstatus->perf_data);
		my_free(this_svcstatus);
		}

	/* free hash lists reset list pointers */
	my_free(hoststatus_hashlist);
	my_free(servicestatus_hashlist);
	hoststatus_list = NULL;
	servicestatus_list = NULL;

	return;
	}




/******************************************************************/
/************************ SEARCH FUNCTIONS ************************/
/******************************************************************/


/* find a host status entry */
hoststatus *find_hoststatus(char *host_name) {
	hoststatus *temp_hoststatus = NULL;

	if(host_name == NULL || hoststatus_hashlist == NULL)
		return NULL;

	for(temp_hoststatus = hoststatus_hashlist[hashfunc(host_name, NULL, HOSTSTATUS_HASHSLOTS)]; temp_hoststatus && compare_hashdata(temp_hoststatus->host_name, NULL, host_name, NULL) < 0; temp_hoststatus = temp_hoststatus->nexthash);

	if(temp_hoststatus && (compare_hashdata(temp_hoststatus->host_name, NULL, host_name, NULL) == 0))
		return temp_hoststatus;

	return NULL;
	}


/* find a service status entry */
servicestatus *find_servicestatus(char *host_name, char *svc_desc) {
	servicestatus *temp_servicestatus = NULL;

	if(host_name == NULL || svc_desc == NULL || servicestatus_hashlist == NULL)
		return NULL;

	for(temp_servicestatus = servicestatus_hashlist[hashfunc(host_name, svc_desc, SERVICESTATUS_HASHSLOTS)]; temp_servicestatus && compare_hashdata(temp_servicestatus->host_name, temp_servicestatus->description, host_name, svc_desc) < 0; temp_servicestatus = temp_servicestatus->nexthash);

	if(temp_servicestatus && (compare_hashdata(temp_servicestatus->host_name, temp_servicestatus->description, host_name, svc_desc) == 0))
		return temp_servicestatus;

	return NULL;
	}




/******************************************************************/
/*********************** UTILITY FUNCTIONS ************************/
/******************************************************************/


/* gets the total number of services of a certain state for a specific host */
int get_servicestatus_count(char *host_name, int type) {
	servicestatus *temp_status = NULL;
	int count = 0;

	if(host_name == NULL)
		return 0;

	for(temp_status = servicestatus_list; temp_status != NULL; temp_status = temp_status->next) {
		if(temp_status->status & type) {
			if(!strcmp(host_name, temp_status->host_name))
				count++;
			}
		}

	return count;
	}



#endif