/*----------------------------------------------------------------------------*/
/* Xymon RRD graph overview generator.                                        */
/*                                                                            */
/* This is a standalone tool for generating data for the trends column.       */
/* All of the data stored in RRD files for a host end up as graphs. Some of   */
/* these are displayed together with the corresponding status display, but    */
/* others (e.g. from "data" messages) are not. This generates a "trends"      */
/* column that contains all of the graphs for a host.                         */
/*                                                                            */
/* Copyright (C) 2002-2011 Henrik Storner <henrik@storner.dk>                 */
/*                                                                            */
/* This program is released under the GNU General Public License (GPL),       */
/* version 2. See the file "COPYING" for details.                             */
/*                                                                            */
/*----------------------------------------------------------------------------*/

static char rcsid[] = "$Id: svcstatus-trends.c 8069 2019-07-23 15:29:06Z jccleaver $";

#include <limits.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <utime.h>
#include <errno.h>

#include "libxymon.h"

typedef struct graph_t {
	xymongraph_t *gdef;
	int count;
	struct graph_t *next;
} graph_t;

typedef struct dirstack_t {
	char *dirname;
	DIR *rrddir;
	struct dirstack_t *next;
} dirstack_t;
dirstack_t *dirs = NULL;

static dirstack_t *stack_opendir(char *dirname)
{
	dirstack_t *newdir;
	DIR *d;

	d = opendir(dirname);
	if (d == NULL) return NULL;

	newdir = (dirstack_t *)malloc(sizeof(dirstack_t));
	newdir->dirname = strdup(dirname);
	newdir->rrddir = d;
	newdir->next = NULL;

	if (dirs == NULL) {
		dirs = newdir;
	}
	else {
		newdir->next = dirs;
		dirs = newdir;
	}

	return newdir;
}

static void stack_closedir(void)
{
	dirstack_t *tmp = dirs;

	if (dirs && dirs->rrddir) {
		dirs = dirs->next;

		closedir(tmp->rrddir);
		xfree(tmp->dirname);
		xfree(tmp);
	}
}

static char *stack_readdir(void)
{
	static char fname[PATH_MAX];
	struct dirent *d;
	struct stat st;

	if (dirs == NULL) return NULL;

	do {
		d = readdir(dirs->rrddir);
		if (d == NULL) {
			stack_closedir();
		}
		else if (*(d->d_name) == '.') {
			d = NULL;
		}
		else {
			snprintf(fname, sizeof(fname), "%s/%s", dirs->dirname, d->d_name);
			if ((stat(fname, &st) == 0) && (S_ISDIR(st.st_mode))) {
				stack_opendir(fname);
				d = NULL;
			}
		}
	} while (dirs && (d == NULL));

	if (d == NULL) return NULL;

	if (strncmp(fname, "./", 2) == 0) return (fname + 2); else return fname;
}


static char *rrdlink_text(void *host, graph_t *rrd, hg_link_t wantmeta, time_t starttime, time_t endtime)
{
	STATIC_SBUF_DEFINE(rrdlink);
	char *graphdef, *p;
	char *hostdisplayname, *hostrrdgraphs;

	hostdisplayname = xmh_item(host, XMH_DISPLAYNAME);
	hostrrdgraphs = xmh_item(host, XMH_TRENDS);

	dbgprintf("rrdlink_text: host %s, rrd %s\n", xmh_item(host, XMH_HOSTNAME), rrd->gdef->xymonrrdname);

	/* If no rrdgraphs definition, include all with default links */
	if (hostrrdgraphs == NULL) {
		dbgprintf("rrdlink_text: Standard URL (no rrdgraphs)\n");
		return xymon_graph_data(xmh_item(host, XMH_HOSTNAME), hostdisplayname, NULL, -1, rrd->gdef, rrd->count, 
					 HG_WITH_STALE_RRDS, wantmeta, 0, starttime, endtime);
	}

	/* Find this rrd definition in the rrdgraphs */
	graphdef = strstr(hostrrdgraphs, rrd->gdef->xymonrrdname);
	if (graphdef) {
		char *endp;
		int namelength;
		namelength = strlen(rrd->gdef->xymonrrdname);
		while (graphdef) {
			/* 
			 * Search for string, but eliminate partial matches.
			 * Must start and bol or after a comma or !,
			 *  and end with colon, comma, or eol.
			*/
			endp = graphdef + namelength;
			if (((*endp == '\0') || (*endp == ':') || (*endp == ',')) && ((graphdef == hostrrdgraphs) || (*(graphdef-1) == ',') || (*(graphdef-1) == '!') )) break;
			/* no match, keep looking */
			graphdef = strstr(endp, rrd->gdef->xymonrrdname);
		}
	}

	/* If not found ... */
	if (graphdef == NULL) {
		dbgprintf("rrdlink_text: NULL graphdef\n");

		/* Do we include all by default ? */
		if (*(hostrrdgraphs) == '*') {
			dbgprintf("rrdlink_text: Default URL included\n");

			/* Yes, return default link for this RRD */
			return xymon_graph_data(xmh_item(host, XMH_HOSTNAME), hostdisplayname, NULL, -1, rrd->gdef, rrd->count, 
						 HG_WITH_STALE_RRDS, wantmeta, 0, starttime, endtime);
		}
		else {
			dbgprintf("rrdlink_text: Default URL NOT included\n");
			/* No, return empty string */
			return "";
		}
	}

	/* We now know that rrdgraphs explicitly define what to do with this RRD */

	/* Does he want to explicitly exclude this RRD ? */
	if ((graphdef > hostrrdgraphs) && (*(graphdef-1) == '!')) {
		dbgprintf("rrdlink_text: This graph is explicitly excluded\n");
		return "";
	}

	/* It must be included. */
	if (rrdlink == NULL) {
		SBUF_MALLOC(rrdlink, 4096);
	}
	*rrdlink = '\0';

	p = graphdef + strlen(rrd->gdef->xymonrrdname);
	if (*p == ':') {
		/* There is an explicit list of graphs to add for this RRD. */
		char savechar;
		char *enddef;
		graph_t *myrrd;
		char *partlink;

		myrrd = (graph_t *) malloc(sizeof(graph_t));
		myrrd->gdef = (xymongraph_t *) calloc(1, sizeof(xymongraph_t));

		/* First, null-terminate this graph definition so we only look at the active RRD */
		enddef = strchr(graphdef, ',');
		if (enddef) *enddef = '\0';

		graphdef = (p+1);
		do {
			p = strchr(graphdef, '|');			/* Ends at '|' ? */
			if (p == NULL) p = graphdef + strlen(graphdef);	/* Ends at end of string */
			savechar = *p; *p = '\0'; 

			myrrd->gdef->xymonrrdname = graphdef;
			myrrd->gdef->xymonpartname = NULL;
			myrrd->gdef->maxgraphs = 0;
			myrrd->count = rrd->count;
			myrrd->next = NULL;
			partlink = xymon_graph_data(xmh_item(host, XMH_HOSTNAME), hostdisplayname, NULL, -1, myrrd->gdef, myrrd->count, 
						     HG_WITH_STALE_RRDS, wantmeta, 0, starttime, endtime);
			if ((strlen(rrdlink) + strlen(partlink) + 1) >= rrdlink_buflen) {
				SBUF_REALLOC(rrdlink, rrdlink_buflen + strlen(partlink) + 4096);
			}
			strncat(rrdlink, partlink, (rrdlink_buflen - strlen(rrdlink)));
			*p = savechar;

			graphdef = p;
			if (*graphdef != '\0') graphdef++;

		} while (*graphdef);

		if (enddef) *enddef = ',';
		xfree(myrrd->gdef);
		xfree(myrrd);

		return rrdlink;
	}
	else {
		/* It is included with the default graph */
		return xymon_graph_data(xmh_item(host, XMH_HOSTNAME), hostdisplayname, NULL, -1, rrd->gdef, rrd->count, 
					 HG_WITH_STALE_RRDS, wantmeta, 0, starttime, endtime);
	}

	return "";
}


char *generate_trends(char *hostname, time_t starttime, time_t endtime)
{
	void *myhost;
	char hostrrddir[PATH_MAX];
	char *fn;
	int anyrrds = 0;
	xymongraph_t *graph;
	graph_t *rwalk;
	SBUF_DEFINE(allrrdlinks);
	char *allrrdlinksend;

	myhost = hostinfo(hostname);
	if (!myhost) return NULL;

	snprintf(hostrrddir, sizeof(hostrrddir), "%s/%s", xgetenv("XYMONRRDS"), hostname);
	if (chdir(hostrrddir) != 0) {
		errprintf("Cannot chdir to %s: %s\n", hostrrddir, strerror(errno));
		return NULL;
	}
	stack_opendir(".");

	while ((fn = stack_readdir())) {
		/* Check if the filename ends in ".rrd", and we know how to handle this RRD */
		if ((strlen(fn) <= 4) || (strcmp(fn+strlen(fn)-4, ".rrd") != 0)) continue;
		graph = find_xymon_graph(fn); if (!graph) continue;

		dbgprintf("Got RRD %s\n", fn);
		anyrrds++;

		for (rwalk = (graph_t *)xmh_item(myhost, XMH_DATA); (rwalk && (rwalk->gdef != graph)); rwalk = rwalk->next) ;
		if (rwalk == NULL) {
			graph_t *newrrd = (graph_t *) malloc(sizeof(graph_t));

			newrrd->gdef = graph;
			newrrd->count = 1;
			newrrd->next = (graph_t *)xmh_item(myhost, XMH_DATA);
			xmh_set_item(myhost, XMH_DATA, newrrd);
			rwalk = newrrd;
			dbgprintf("New rrd for host:%s, rrd:%s\n", hostname, graph->xymonrrdname);
		}
		else {
			rwalk->count++;

			dbgprintf("Extra RRD for host %s, rrd %s   count:%d\n", 
				hostname, 
				rwalk->gdef->xymonrrdname, rwalk->count);
		}
	}
	stack_closedir();

	if (!anyrrds) return NULL;

	SBUF_MALLOC(allrrdlinks, 16384);
	*allrrdlinks = '\0';
	allrrdlinksend = allrrdlinks;

	graph = xymongraphs;
	while (graph->xymonrrdname) {
		for (rwalk = (graph_t *)xmh_item(myhost, XMH_DATA); (rwalk && (rwalk->gdef->xymonrrdname != graph->xymonrrdname)); rwalk = rwalk->next) ;
		if (rwalk) {
			int buflen;
			char *onelink;

			buflen = (allrrdlinksend - allrrdlinks);
			onelink = rrdlink_text(myhost, rwalk, 0, starttime, endtime);
			if ((buflen + strlen(onelink)) >= allrrdlinks_buflen) {
				SBUF_REALLOC(allrrdlinks, allrrdlinks_buflen+4096);
				allrrdlinksend = allrrdlinks + buflen;
			}
			allrrdlinksend += snprintf(allrrdlinksend, (allrrdlinks_buflen - (allrrdlinksend - allrrdlinks)), "%s", onelink);
		}

		graph++;
	}

	return allrrdlinks;
}

