/*****************************************************************************
 *
 * XPDDEFAULT.C - Default performance data routines
 *
 *
 * 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/macros.h"
#include "../include/nagios.h"
#include "../include/workers.h"
#include "xpddefault.h"


static command *host_perfdata_command_ptr = NULL;
static command *service_perfdata_command_ptr = NULL;
static command *host_perfdata_file_processing_command_ptr = NULL;
static command *service_perfdata_file_processing_command_ptr = NULL;
static FILE    *host_perfdata_fp = NULL;
static FILE    *service_perfdata_fp = NULL;
static int     host_perfdata_fd = -1;
static int     service_perfdata_fd = -1;


/******************************************************************/
/************** INITIALIZATION & CLEANUP FUNCTIONS ****************/
/******************************************************************/

/* initializes performance data */
int xpddefault_initialize_performance_data(const char *cfgfile) {
	char *buffer = NULL;
	char *temp_buffer = NULL;
	char *temp_command_name = NULL;
	command *temp_command = NULL;
	time_t current_time;
	nagios_macros *mac;

	mac = get_global_macros();
	time(&current_time);

	/* reset vars */
	host_perfdata_command_ptr = NULL;
	service_perfdata_command_ptr = NULL;
	host_perfdata_file_processing_command_ptr = NULL;
	service_perfdata_file_processing_command_ptr = NULL;

	/* make sure we have some templates defined */
	if(host_perfdata_file_template == NULL)
		host_perfdata_file_template = (char *)strdup(DEFAULT_HOST_PERFDATA_FILE_TEMPLATE);
	if(service_perfdata_file_template == NULL)
		service_perfdata_file_template = (char *)strdup(DEFAULT_SERVICE_PERFDATA_FILE_TEMPLATE);

	/* process special chars in templates */
	xpddefault_preprocess_file_templates(host_perfdata_file_template);
	xpddefault_preprocess_file_templates(service_perfdata_file_template);

	/* open the performance data files */
	xpddefault_open_host_perfdata_file();
	xpddefault_open_service_perfdata_file();

	/* verify that performance data commands are valid */
	if(host_perfdata_command != NULL) {

		temp_buffer = (char *)strdup(host_perfdata_command);

		/* get the command name, leave any arguments behind */
		temp_command_name = my_strtok(temp_buffer, "!");

		if((temp_command = find_command(temp_command_name)) == NULL) {

			logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Host performance command '%s' was not found - host performance data will not be processed!\n", temp_command_name);

			my_free(host_perfdata_command);
			}

		my_free(temp_buffer);

		/* save the command pointer for later */
		host_perfdata_command_ptr = temp_command;
		}
	if(service_perfdata_command != NULL) {

		temp_buffer = (char *)strdup(service_perfdata_command);

		/* get the command name, leave any arguments behind */
		temp_command_name = my_strtok(temp_buffer, "!");

		if((temp_command = find_command(temp_command_name)) == NULL) {

			logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Service performance command '%s' was not found - service performance data will not be processed!\n", temp_command_name);

			my_free(service_perfdata_command);
			}

		/* free memory */
		my_free(temp_buffer);

		/* save the command pointer for later */
		service_perfdata_command_ptr = temp_command;
		}
	if(host_perfdata_file_processing_command != NULL) {

		temp_buffer = (char *)strdup(host_perfdata_file_processing_command);

		/* get the command name, leave any arguments behind */
		temp_command_name = my_strtok(temp_buffer, "!");

		if((temp_command = find_command(temp_command_name)) == NULL) {

			logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Host performance file processing command '%s' was not found - host performance data file will not be processed!\n", temp_command_name);

			my_free(host_perfdata_file_processing_command);
			}

		/* free memory */
		my_free(temp_buffer);

		/* save the command pointer for later */
		host_perfdata_file_processing_command_ptr = temp_command;
		}
	if(service_perfdata_file_processing_command != NULL) {

		temp_buffer = (char *)strdup(service_perfdata_file_processing_command);

		/* get the command name, leave any arguments behind */
		temp_command_name = my_strtok(temp_buffer, "!");

		if((temp_command = find_command(temp_command_name)) == NULL) {

			logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Service performance file processing command '%s' was not found - service performance data file will not be processed!\n", temp_command_name);

			my_free(service_perfdata_file_processing_command);
			}

		/* save the command pointer for later */
		service_perfdata_file_processing_command_ptr = temp_command;
		}

	/* periodically process the host perfdata file */
	if(host_perfdata_file_processing_interval > 0 && host_perfdata_file_processing_command != NULL)
		schedule_new_event(EVENT_USER_FUNCTION, TRUE, current_time + host_perfdata_file_processing_interval, TRUE, host_perfdata_file_processing_interval, NULL, TRUE, (void *)xpddefault_process_host_perfdata_file, NULL, 0);

	/* periodically process the service perfdata file */
	if(service_perfdata_file_processing_interval > 0 && service_perfdata_file_processing_command != NULL)
		schedule_new_event(EVENT_USER_FUNCTION, TRUE, current_time + service_perfdata_file_processing_interval, TRUE, service_perfdata_file_processing_interval, NULL, TRUE, (void *)xpddefault_process_service_perfdata_file, NULL, 0);

	/* save the host perf data file macro */
	my_free(mac->x[MACRO_HOSTPERFDATAFILE]);
	if(host_perfdata_file != NULL) {
		if((mac->x[MACRO_HOSTPERFDATAFILE] = (char *)strdup(host_perfdata_file)))
			strip(mac->x[MACRO_HOSTPERFDATAFILE]);
		}

	/* save the service perf data file macro */
	my_free(mac->x[MACRO_SERVICEPERFDATAFILE]);
	if(service_perfdata_file != NULL) {
		if((mac->x[MACRO_SERVICEPERFDATAFILE] = (char *)strdup(service_perfdata_file)))
			strip(mac->x[MACRO_SERVICEPERFDATAFILE]);
		}

	/* free memory */
	my_free(temp_buffer);
	my_free(buffer);

	return OK;
	}



/* cleans up performance data */
int xpddefault_cleanup_performance_data(void) {

	/* free memory */
	my_free(host_perfdata_command);
	my_free(service_perfdata_command);
	my_free(host_perfdata_file_template);
	my_free(service_perfdata_file_template);
	my_free(host_perfdata_file);
	my_free(service_perfdata_file);
	my_free(host_perfdata_file_processing_command);
	my_free(service_perfdata_file_processing_command);

	/* close the files */
	xpddefault_close_host_perfdata_file();
	xpddefault_close_service_perfdata_file();

	return OK;
	}



/******************************************************************/
/****************** PERFORMANCE DATA FUNCTIONS ********************/
/******************************************************************/


/* updates service performance data */
int xpddefault_update_service_performance_data(service *svc) {
	nagios_macros mac;
	host *hst;

	/*
	 * bail early if we've got nothing to do so we don't spend a lot
	 * of time calculating macros that never get used
	 * on distributed setups, empty perfdata results are required, so
	 * only drop out if demanded via configs.
	*/
	if(service_perfdata_process_empty_results==FALSE){
	        if(!svc || !svc->perf_data || !*svc->perf_data) {
		       return OK;
		}
	        if((!service_perfdata_fp || !service_perfdata_file_template) && !service_perfdata_command) {
		       return OK;
		}

	}
	/*
	 * we know we've got some work to do, so grab the necessary
	 * macros and get busy
	 */
	memset(&mac, 0, sizeof(mac));
	hst = find_host(svc->host_name);
	grab_host_macros_r(&mac, hst);
	grab_service_macros_r(&mac, svc);
	grab_argv_macros_r(&mac, svc->check_command);

	/* run the performance data command */
	xpddefault_run_service_performance_data_command(&mac, svc);

	/* update the performance data file */
	xpddefault_update_service_performance_data_file(&mac, svc);

	/* get rid of used memory we won't need anymore */
	clear_argv_macros_r(&mac);

	/* now free() it all */
	clear_volatile_macros_r(&mac);

	return OK;
	}


/* updates host performance data */
int xpddefault_update_host_performance_data(host *hst) {
	nagios_macros mac;


	/*
	 * bail early if we've got nothing to do so we don't spend a lot
	 * of time calculating macros that never get used
	 * on distributed setups, empty perfdata results are required, so
	 * only drop out if demanded via configs.
	 */
	if(host_perfdata_process_empty_results==FALSE){
		if(!hst || !hst->perf_data || !*hst->perf_data) {
			return OK;
			}
		if((!host_perfdata_fp || !host_perfdata_file_template) && !host_perfdata_command) {
			return OK;
			}
		}

	/* set up macros and get to work */
	memset(&mac, 0, sizeof(mac));
	grab_host_macros_r(&mac, hst);
	grab_argv_macros_r(&mac, hst->check_command);

	/* run the performance data command */
	xpddefault_run_host_performance_data_command(&mac, hst);

	/* no more commands to run, so we won't need this any more */
	clear_argv_macros_r(&mac);

	/* update the performance data file */
	xpddefault_update_host_performance_data_file(&mac, hst);

	/* free() all */
	clear_volatile_macros_r(&mac);

	return OK;
	}




/******************************************************************/
/************** PERFORMANCE DATA COMMAND FUNCTIONS ****************/
/******************************************************************/


/* runs the service performance data command */
int xpddefault_run_service_performance_data_command(nagios_macros *mac, service *svc) {
	char *raw_command_line = NULL;
	char *processed_command_line = NULL;
	int result = OK;
	int macro_options = STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS;

	log_debug_info(DEBUGL_FUNCTIONS, 0, "run_service_performance_data_command()\n");

	if(svc == NULL)
		return ERROR;

	/* we don't have a command */
	if(service_perfdata_command == NULL)
		return OK;

	/* get the raw command line */
	get_raw_command_line_r(mac, service_perfdata_command_ptr, service_perfdata_command, &raw_command_line, macro_options);
	if(raw_command_line == NULL)
		return ERROR;

	log_debug_info(DEBUGL_PERFDATA, 2, "Raw service performance data command line: %s\n", raw_command_line);

	/* process any macros in the raw command line */
	process_macros_r(mac, raw_command_line, &processed_command_line, macro_options);
	my_free(raw_command_line);
	if(processed_command_line == NULL)
		return ERROR;

	log_debug_info(DEBUGL_PERFDATA, 2, "Processed service performance data command line: %s\n", processed_command_line);

	/* run the command */
	wproc_run(WPJOB_SVC_PERFDATA, processed_command_line, perfdata_timeout, NULL);

	/* free memory */
	my_free(processed_command_line);

	return result;
	}


/* runs the host performance data command */
int xpddefault_run_host_performance_data_command(nagios_macros *mac, host *hst) {
	char *raw_command_line = NULL;
	char *processed_command_line = NULL;
	int result = OK;
	int macro_options = STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS;

	log_debug_info(DEBUGL_FUNCTIONS, 0, "run_host_performance_data_command()\n");

	if(hst == NULL)
		return ERROR;

	/* we don't have a command */
	if(host_perfdata_command == NULL)
		return OK;

	/* get the raw command line */
	get_raw_command_line_r(mac, host_perfdata_command_ptr, host_perfdata_command, &raw_command_line, macro_options);
	if(raw_command_line == NULL)
		return ERROR;

	log_debug_info(DEBUGL_PERFDATA, 2, "Raw host performance data command line: %s\n", raw_command_line);

	/* process any macros in the raw command line */
	process_macros_r(mac, raw_command_line, &processed_command_line, macro_options);
	my_free(raw_command_line);
	if (!processed_command_line)
		return ERROR;

	log_debug_info(DEBUGL_PERFDATA, 2, "Processed host performance data command line: %s\n", processed_command_line);

	/* run the command */
	wproc_run(WPJOB_HOST_PERFDATA, processed_command_line, perfdata_timeout, NULL);

	/* free memory */
	my_free(processed_command_line);

	return result;
	}



/******************************************************************/
/**************** FILE PERFORMANCE DATA FUNCTIONS *****************/
/******************************************************************/

/* open the host performance data file for writing */
int xpddefault_open_host_perfdata_file(void) {

	if(host_perfdata_file != NULL) {

		if(host_perfdata_file_pipe == TRUE) {
			/* must open read-write to avoid failure if the other end isn't ready yet */
			host_perfdata_fd = open(host_perfdata_file, O_NONBLOCK | O_RDWR | O_CREAT, 0644);
			host_perfdata_fp = fdopen(host_perfdata_fd, "w");
			}
		else
			host_perfdata_fp = fopen(host_perfdata_file, (host_perfdata_file_append == TRUE) ? "a" : "w");

		if(host_perfdata_fp == NULL) {

			logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: File '%s' could not be opened - host performance data will not be written to file!\n", host_perfdata_file);

			return ERROR;
			}
		}

	return OK;
	}


/* open the service performance data file for writing */
int xpddefault_open_service_perfdata_file(void) {

	if(service_perfdata_file != NULL) {
		if(service_perfdata_file_pipe == TRUE) {
			/* must open read-write to avoid failure if the other end isn't ready yet */
			service_perfdata_fd = open(service_perfdata_file, O_NONBLOCK | O_RDWR);
			service_perfdata_fp = fdopen(service_perfdata_fd, "w");
			}
		else
			service_perfdata_fp = fopen(service_perfdata_file, (service_perfdata_file_append == TRUE) ? "a" : "w");

		if(service_perfdata_fp == NULL) {

			logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: File '%s' could not be opened - service performance data will not be written to file!\n", service_perfdata_file);

			return ERROR;
			}
		}

	return OK;
	}


/* close the host performance data file */
int xpddefault_close_host_perfdata_file(void) {

	if(host_perfdata_fp != NULL)
		fclose(host_perfdata_fp);
	if(host_perfdata_fd >= 0) {
		close(host_perfdata_fd);
		host_perfdata_fd = -1;
		}

	return OK;
	}


/* close the service performance data file */
int xpddefault_close_service_perfdata_file(void) {

	if(service_perfdata_fp != NULL)
		fclose(service_perfdata_fp);
	if(service_perfdata_fd >= 0) {
		close(service_perfdata_fd);
		service_perfdata_fd = -1;
		}

	return OK;
	}


/* processes delimiter characters in templates */
int xpddefault_preprocess_file_templates(char *template) {
	char *tempbuf;
	unsigned int x, y;

	if(template == NULL)
		return OK;

	/* allocate temporary buffer */
	tempbuf = (char *)malloc(strlen(template) + 1);
	if(tempbuf == NULL)
		return ERROR;
	strcpy(tempbuf, "");

	for(x = 0, y = 0; x < strlen(template); x++, y++) {
		if(template[x] == '\\') {
			if(template[x + 1] == 't') {
				tempbuf[y] = '\t';
				x++;
				}
			else if(template[x + 1] == 'r') {
				tempbuf[y] = '\r';
				x++;
				}
			else if(template[x + 1] == 'n') {
				tempbuf[y] = '\n';
				x++;
				}
			else
				tempbuf[y] = template[x];
			}
		else
			tempbuf[y] = template[x];
		}
	tempbuf[y] = '\x0';

	strcpy(template, tempbuf);
	my_free(tempbuf);

	return OK;
	}


/* updates service performance data file */
int xpddefault_update_service_performance_data_file(nagios_macros *mac, service *svc) {
	char *raw_output = NULL;
	char *processed_output = NULL;
	int result = OK;

	log_debug_info(DEBUGL_FUNCTIONS, 0, "update_service_performance_data_file()\n");

	if(svc == NULL)
		return ERROR;

	/* we don't have a file to write to*/
	if(service_perfdata_fp == NULL || service_perfdata_file_template == NULL)
		return OK;

	/* get the raw line to write */
	raw_output = (char *)strdup(service_perfdata_file_template);

	log_debug_info(DEBUGL_PERFDATA, 2, "Raw service performance data file output: %s\n", raw_output);

	/* process any macros in the raw output line */
	process_macros_r(mac, raw_output, &processed_output, 0);
	if(processed_output == NULL)
		return ERROR;

	log_debug_info(DEBUGL_PERFDATA, 2, "Processed service performance data file output: %s\n", processed_output);

	/* write to host performance data file */
	fputs(processed_output, service_perfdata_fp);
	fputc('\n', service_perfdata_fp);
	fflush(service_perfdata_fp);

	/* free memory */
	my_free(raw_output);
	my_free(processed_output);

	return result;
	}


/* updates host performance data file */
int xpddefault_update_host_performance_data_file(nagios_macros *mac, host *hst) {
	char *raw_output = NULL;
	char *processed_output = NULL;
	int result = OK;

	log_debug_info(DEBUGL_FUNCTIONS, 0, "update_host_performance_data_file()\n");

	if(hst == NULL)
		return ERROR;

	/* we don't have a host perfdata file */
	if(host_perfdata_fp == NULL || host_perfdata_file_template == NULL)
		return OK;

	/* get the raw output */
	raw_output = (char *)strdup(host_perfdata_file_template);

	log_debug_info(DEBUGL_PERFDATA, 2, "Raw host performance file output: %s\n", raw_output);

	/* process any macros in the raw output */
	process_macros_r(mac, raw_output, &processed_output, 0);
	if(processed_output == NULL)
		return ERROR;

	log_debug_info(DEBUGL_PERFDATA, 2, "Processed host performance data file output: %s\n", processed_output);

	/* write to host performance data file */
	fputs(processed_output, host_perfdata_fp);
	fputc('\n', host_perfdata_fp);
	fflush(host_perfdata_fp);

	/* free memory */
	my_free(raw_output);
	my_free(processed_output);

	return result;
	}


/* periodically process the host perf data file */
int xpddefault_process_host_perfdata_file(void) {
	char *raw_command_line = NULL;
	char *processed_command_line = NULL;
	int early_timeout = FALSE;
	double exectime = 0.0;
	int result = OK;
	int macro_options = STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS;
	nagios_macros mac;

	log_debug_info(DEBUGL_FUNCTIONS, 0, "process_host_perfdata_file()\n");

	/* we don't have a command */
	if(host_perfdata_file_processing_command == NULL)
		return OK;

	/* init macros */
	memset(&mac, 0, sizeof(mac));

	/* get the raw command line */
	get_raw_command_line_r(&mac, host_perfdata_file_processing_command_ptr, host_perfdata_file_processing_command, &raw_command_line, macro_options);
	if(raw_command_line == NULL) {
		clear_volatile_macros_r(&mac);
		return ERROR;
		}

	log_debug_info(DEBUGL_PERFDATA, 2, "Raw host performance data file processing command line: %s\n", raw_command_line);

	/* process any macros in the raw command line */
	process_macros_r(&mac, raw_command_line, &processed_command_line, macro_options);
	my_free(raw_command_line);
	if(processed_command_line == NULL) {
		clear_volatile_macros_r(&mac);
		return ERROR;
		}

	log_debug_info(DEBUGL_PERFDATA, 2, "Processed host performance data file processing command line: %s\n", processed_command_line);

	/* close the performance data file */
	xpddefault_close_host_perfdata_file();

	/* run the command */
	my_system_r(&mac, processed_command_line, perfdata_timeout, &early_timeout, &exectime, NULL, 0);
	clear_volatile_macros_r(&mac);

	/* re-open the performance data file */
	xpddefault_open_host_perfdata_file();

	/* check to see if the command timed out */
	if(early_timeout == TRUE)
		logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Host performance data file processing command '%s' timed out after %d seconds\n", processed_command_line, perfdata_timeout);


	/* free memory */
	my_free(processed_command_line);

	return result;
	}


/* periodically process the service perf data file */
int xpddefault_process_service_perfdata_file(void) {
	char *raw_command_line = NULL;
	char *processed_command_line = NULL;
	int early_timeout = FALSE;
	double exectime = 0.0;
	int result = OK;
	int macro_options = STRIP_ILLEGAL_MACRO_CHARS | ESCAPE_MACRO_CHARS;
	nagios_macros mac;

	log_debug_info(DEBUGL_FUNCTIONS, 0, "process_service_perfdata_file()\n");

	/* we don't have a command */
	if(service_perfdata_file_processing_command == NULL)
		return OK;

	/* init macros */
	memset(&mac, 0, sizeof(mac));

	/* get the raw command line */
	get_raw_command_line_r(&mac, service_perfdata_file_processing_command_ptr, service_perfdata_file_processing_command, &raw_command_line, macro_options);
	if(raw_command_line == NULL) {
		clear_volatile_macros_r(&mac);
		return ERROR;
		}

	log_debug_info(DEBUGL_PERFDATA, 2, "Raw service performance data file processing command line: %s\n", raw_command_line);

	/* process any macros in the raw command line */
	process_macros_r(&mac, raw_command_line, &processed_command_line, macro_options);
	my_free(raw_command_line);
	if(processed_command_line == NULL) {
		clear_volatile_macros_r(&mac);
		return ERROR;
		}

	log_debug_info(DEBUGL_PERFDATA, 2, "Processed service performance data file processing command line: %s\n", processed_command_line);

	/* close the performance data file */
	xpddefault_close_service_perfdata_file();

	/* run the command */
	my_system_r(&mac, processed_command_line, perfdata_timeout, &early_timeout, &exectime, NULL, 0);

	/* re-open the performance data file */
	xpddefault_open_service_perfdata_file();

	clear_volatile_macros_r(&mac);

	/* check to see if the command timed out */
	if(early_timeout == TRUE)
		logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Service performance data file processing command '%s' timed out after %d seconds\n", processed_command_line, perfdata_timeout);

	/* free memory */
	my_free(processed_command_line);

	return result;
	}
