/*****************************************************************************
 *
 * 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

