/*	*****************************************************************
	*	ProblemManager.c - Problem Management Routines		*
	*****************************************************************

	ProblemManager( Cmd, TypeProblem, nodename, UniqueID, StatusLine )
		Cmd - One of ADD_PROBLEM, ADD_PRIMARY_PROBLEM, DELETE_PROBLEM
		TypeProblem - Name of TestName that Failed
		nodename - Textual Name of Node ( Not-necesarily unique )
		UniqueId - Usually an IP UniqueIDess or otherwise unique Problem ID
		StatusLine - If Cmd is UPDATE_PROBLEM
*/

#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include "ProblemManager.h"	/* Problem Manager routine defines */
#include "version.h"
/*#define DEBUG 1 /* */

static char *DefaultPingkyDir=".";   /* Starting point in File System */
static char *PingkyDir;   		/* Starting point in File System */

static char Problem_File[100];        /* PROBLEM FILE path */
static long ProblemFileLastUpdated;    /* Last time PROBLEM.FILE was updated */
struct ProblemType 
	ProblemArray[MAXPROBLEMS];/* In Core version of existing problems*/
int NumProblems=0;

static char Log_File[100];             /* Where do we log things */

static char Config_File[100];          /* Where does User Comment Out Nodes */
static long ConfigFileLastUpdated;    /* Last time Config File was accessed */

static int ProblemFileChanged=0;

	/************************************************
	 * FORWARD DECLARATION OF STATIC ROUTINES	*
	 ************************************************/
static int Add_Problem_Entry();
static int AddProblem();
static int DelProblem();
static void ReadConfigFile();
static char *Comment();
#ifdef STANDALONE
char *programname;
#endif

/****************************************************************
 *   Problem_Manager: 						*
 *	Cmds:							*
 *		ADD_PROBLEM:  Add a problem to the list		*
 *				( If not already there )	*
 *		ADD_PRIMARY_PROBLEM: Add a problem deleteing	*
 *			all other problems with this node 	*
 *		DELETE_PROBLEM: Remove this problem		*
 *				( if there is one )		*
 *		UPDATE_PROBLEM: Update this problem's StatusLine*
 *				( if there is one )		*
 *		CLAIM_PROBLEM: Claim this problem		*
 *				( just like UPDATE PROBLEM )    *
 *  								*
 ****************************************************************/
int Problem_Manager(Cmd,TypeProblem,nodename,UniqueID,StatusLine)
int Cmd;		/* Add_Problem, Del_Problem, Add_Problem_Primary*/
char *TypeProblem;	/* Type of Problem - string text		*/
char *nodename;		/* nodename of node we are reporting about 	*/
char *UniqueID;		/* Unique Identifier for this node(like IP UniqueID)*/
char *StatusLine;	/* Status Line - if Cmd is UPDATE_PROBLEM	*/
{
int i,changed=0;
char buffer[100];
extern char *getenv();
extern char *programname;
long TimeNow;
static int virgin=1;

    ProblemFileChanged=0;  /* We haven't changed the PROBLEM.FILE this call */
    if ( virgin ) {	/* Set-Up Config Files we will access */
    	if ((PingkyDir=getenv("PINGKYDIR"))==NULL) 
		PingkyDir=DefaultPingkyDir;
    	strcpy(Problem_File,PingkyDir); strcat(Problem_File ,"/PROBLEM.FILE");
    	strcpy(Log_File,PingkyDir); strcat(Log_File,"/problemlog");
    	strcpy(Config_File,PingkyDir); strcat(Config_File,"/ProblemManager.config");
	/*sprintf(buffer,"%s: %s\n",programname,Version);
	printf(buffer);
	Log( buffer, Log_File );*/
	virgin=0;
   }

   /************ ReRead Config File if it has changed **********/
   if ( FileChanged( Config_File, &ConfigFileLastUpdated )     ) {
	ReadConfigFile( Config_File );
	FileChanged( Config_File, &ConfigFileLastUpdated );
   }
   /************* ReRead Problem File if it has changed **********/
   if ( FileChanged( Problem_File, &ProblemFileLastUpdated )     ) {
      NumProblems = ReadProblemFile( ProblemArray, Problem_File );
      FileChanged( Problem_File, &ProblemFileLastUpdated );
	ProblemFileChanged=1;
   }
   /****************** Handle Caller's Command *******************/
   switch( Cmd ) {
	case ADD_PRIMARY_PROBLEM: 
		changed=0;
   		  for (i=0; i<NumProblems ; i++)
       			if (strcmp(ProblemArray[i].UniqueID,UniqueID)==0)
          			if (strcmp(ProblemArray[i].TestName,TypeProblem)!=0){
				      ProblemArray[i].Name[0]='\0';
				       changed=1;
				}
		if ( changed ) {
		  WriteProblemFile( ProblemArray,Problem_File,NumProblems);
		  NumProblems=ReadProblemFile(ProblemArray,Problem_File);
		  ProblemFileChanged=1;
		}
	case ADD_PROBLEM: 
		  	NumProblems=AddProblem(nodename,UniqueID,TypeProblem,
					 ProblemArray,NumProblems);
		  	break;
	case DELETE_PROBLEM: 
		     	NumProblems=DelProblem(nodename,UniqueID,TypeProblem,
					 ProblemArray, NumProblems);
		  	break;
	case UPDATE_PROBLEM: 
	case CLAIM_PROBLEM: 
			i=Problem_Exists(UniqueID,TypeProblem,ProblemArray,NumProblems);

			if ( i != -1 ) {
				strncpy( ProblemArray[i].StatusLine, StatusLine, MAXSTATUSLINE-1 );
				ProblemArray[i].StatusLine[MAXSTATUSLINE-1]='\0';
			   	WriteProblemFile(ProblemArray,Problem_File,NumProblems); 
		  		NumProblems=ReadProblemFile(ProblemArray,Problem_File);
				if ( Cmd == UPDATE_PROBLEM )
         			sprintf(buffer," ^%s^%s^Operator Update: %s (%d mins elapsed)\n",
					ProblemArray[i].Name,ProblemArray[i].UniqueID,ProblemArray[i].StatusLine,(time(&TimeNow)-ProblemArray[i].TimeStamp)/60);
				else
         			sprintf(buffer," ^%s^%s^Operator Claim: %s (%d mins elapsed)\n",
					ProblemArray[i].Name,ProblemArray[i].UniqueID,ProblemArray[i].StatusLine,(time(&TimeNow)-ProblemArray[i].TimeStamp)/60);
         			Log(buffer,Log_File);
				ProblemFileChanged=1;

			}
		  	break;
	case CHECK:
			break;
	default:  
		Log("Problem_Manager- invalid cmd type!\n");
		panic("Problem_Manager- invalid cmd type!\n");
		  break;
   }
   return(ProblemFileChanged);
}


/**************************************************************************
 * Add_Problem_Entry:  Add a problem to the Problem structure and to disk *
 * To Do: Open this file with the append flag                             *
 **************************************************************************/ 
static int Add_Problem_Entry(Node,UniqueID,ProblemType,Problem, NumProblems)
char *Node,*UniqueID,*ProblemType;
struct ProblemType *Problem;
int NumProblems;
{
   time( &Problem[NumProblems].TimeStamp );
   strncpy( Problem[NumProblems].Name, Node, MAXNODENAME-1 ); 
	Problem[NumProblems].Name[MAXNODENAME-1]='\0';
   strncpy( Problem[NumProblems].UniqueID, UniqueID, MAXUNIQUEID-1 );
	Problem[NumProblems].UniqueID[MAXUNIQUEID-1]='\0';
   strncpy( Problem[NumProblems].TestName, ProblemType, MAXTESTNAME-1 );
	Problem[NumProblems].TestName[MAXTESTNAME-1]='\0';
   strncpy( Problem[NumProblems].StatusLine, Comment( Node ), MAXSTATUSLINE-1 );
	Problem[NumProblems].StatusLine[MAXSTATUSLINE-1]='\0';
/*   printf("Add_Problem_Entry: ADDING %lu %s %s %s_%s_%s %s\n",
		Problem[NumProblems].TimeStamp,_Day(Problem[NumProblems].TimeStamp),
		_Time(Problem[NumProblems].TimeStamp),
		Problem[NumProblems].Name,Problem[NumProblems].UniqueID,
	 	Problem[NumProblems].TestName, Problem[NumProblems].StatusLine);*/
   NumProblems++;
   WriteProblemFile( Problem , Problem_File, NumProblems );
   NumProblems=ReadProblemFile(ProblemArray,Problem_File);
   ProblemFileChanged=1;
   return( NumProblems );
}

/********************************************************************
 * AddProblem:   Add a problem to the Problem File if necessary     *
 *  								    *
 *  Variables:							    *
 *	Node: The name of the node that is having problems.	    *
 *  	UniqueID: The IP UniqueIDess of the node that is having the problem *
 *	Service: The service that is failing (PING,TELNET,etc)	    *
 *	Problem_File: Name of the Problem File containing Problems  *
 ********************************************************************/
static int AddProblem(Node,UniqueID,Service,Problem,NumProblems)
char *Node,*UniqueID;
char * Service;
struct ProblemType *Problem;
int NumProblems;
{
char Event[100];

   if ( TestNode( Node ) ) {
/*	printf("ProblemManager: %s is a test node - not added\n",Node);*/
	return( NumProblems );
   }
   if ( Problem_Exists(UniqueID,Service,Problem,NumProblems) == -1 ) {  
       	sprintf(Event,"ADDING PROBLEM %s_%s_%s_%s\n",
		Node,UniqueID,Service,Comment(Node));
       	Log(Event,Log_File);	
        NumProblems=Add_Problem_Entry(Node,UniqueID,Service,Problem,NumProblems);
   }
   return( NumProblems );
}

/********************************************************************
 * DelProblem:   Delete a problem in the Problem File if necessary  *
 *  								    *
 *  Variables:							    *
 *	Node: The name of the node that is now behaving.	    *
 *  	UniqueID: The IP UniqueIDess of the node that is behaving correctly *
 *	Service: The service that works now (PING,TELNET,etc)	    *
 *	Problem_File: Name of the Problem File containing Problems  *
 ********************************************************************/
static int DelProblem(Node,UniqueID,Service,Problem,NumProblems)
char *Node,*UniqueID;
char *Service;
struct ProblemType *Problem;
int NumProblems;
{
char Event[100];
int index;
long TimeNow;

   time(&TimeNow);

   if  ( (index=Problem_Exists(UniqueID,Service,Problem,NumProblems)) != -1 ) {
      	sprintf(Event,"DELETING PROBLEM %s_%s_%s (%d mins elapsed)\n",
		Problem[index].Name,
		Problem[index].UniqueID,
			Service,((TimeNow-Problem[index].TimeStamp)/60));
      Log(Event,Log_File);
					/* DELETE the problem */
      Problem[index].Name[0]='\0';    /* Mark this node invalid */
      WriteProblemFile(Problem,Problem_File,NumProblems);
      NumProblems=ReadProblemFile(Problem,Problem_File);
      ProblemFileChanged=1;
   }
   return( NumProblems );
}

/************************************************************************
 *	AUTOMATIC STATUS MESSAGES / TEST NODE SECTION			*
 ************************************************************************/
#define MAXNOTES MAXNODES
#define MAXCOMMENT 30

static int NumConfig=0;
struct ConfigType {
	char ICare;
	char Name[MAXNODENAME];
	char Comment[MAXCOMMENT];
} Config[MAXNOTES];

/****** ReadConfigFile - Default comments, and TestName Nodes ******/
/*    NODE COMMENT LINE FOR ALL TO SEE	BEV			*/
static void ReadConfigFile( Config_File )
char *Config_File;
{
FILE *stream;
char buffer[100];
int i=0,j,k;


   if ((stream=fopen(Config_File,"r")) == NULL) 
	panic("ReadConfig: config file unreadable!\n");

   while( ( fgets(buffer,sizeof(buffer),stream) != NULL) && ( i < MAXNOTES ) ) {
      if (buffer[0]=='#') 
		continue; 
	j=0;
	if ( buffer[j] == '*' ) {	/* '*' in Col 1 means IGNORE */
		Config[i].ICare=0;
		j++;
	}
	else Config[i].ICare=1;		/* No '*' means I Care &*/
      for(k=0; j<MAXNODENAME && !isspace(buffer[j]); j++,k++)
	Config[i].Name[k]=buffer[j];
      Config[i].Name[MAXNODENAME-1]='\0';

      for(j++,k=0; j<strlen(buffer) && k<MAXCOMMENT; k++,j++ )
	 if ( isprint( buffer[j] ) ) 
	 	Config[i].Comment[k]=buffer[j];
      Config[i].Comment[ MAXCOMMENT-1 ] = '\0';

/*	printf("Name[%d]=%s Comment=%s ICare=%d\n",i,Config[i].Name,Config[i].Comment,Config[i].ICare);*/
      i++;              /** Yea! We have a valid line **/
   }
   fclose(stream);
   NumConfig=i;
}

static int IsConfig( node )
char *node;
{
int i;

   if (FileChanged(Config_File,&ConfigFileLastUpdated)) {
      ReadConfigFile( Config_File );
      FileChanged(Config_File,&ConfigFileLastUpdated);
   }
	for( i=0; i<NumConfig; i++ ) {
		if ( strcmp( Config[i].Name , node ) == 0 )
			if ( Config[i].ICare == 0 ) {
				return( -1 );	/* TestNode Machine -I don't care */
			}
			else return( i );	/* INDEX OF NODE */
		else;
	}
	return( -2 ); /* NODE NOT FOUND */
}

int TestNode( node )
char *node;
{
	if ( IsConfig( node ) == -1 ) return( 1 );	/* TestNode */
	else return( 0 );	/* Non-TestNode */
}

static char *Comment( node )
char *node;
{
int i;

	if ( ( i=IsConfig( node ) ) == -2 ) {
		/*printf("No Comment for %s\n",node);*/
		return( "" ); 
	}
	else {
		/*printf("%s Comment=%s\n",node, Config[i].Comment );*/
		return( Config[i].Comment );
	}
}

#ifdef STANDALONE
main(argc, argv)
int argc;
char *argv[];
{
	programname=argv[0];
	Problem_Manager(ADD_PROBLEM,"PING","NODE1","NODE1ID",NULL);
	Problem_Manager(ADD_PROBLEM,"PING","NODE2","NODE2ID",NULL);
	Problem_Manager(ADD_PROBLEM,"PING","NODE3","NODE3ID",NULL);
	PrintProblems(NumProblems,ProblemArray);
	sleep(10);
	Problem_Manager(DELETE_PROBLEM,"PING","NODE2","NODE2ID",NULL);
	PrintProblems(NumProblems,ProblemArray);
	sleep(10);
	Problem_Manager(ADD_PRIMARY_PROBLEM,"DOWN","NODE1","NODE1ID",NULL);
	PrintProblems(NumProblems,ProblemArray);
	sleep(10);
	Problem_Manager(DELETE_PROBLEM,"PING","NODE3","NODE3ID",NULL);
	Problem_Manager(UPDATE_PROBLEM,"PING","NODE1","NODE1ID","AN UPDATE");
	PrintProblems(NumProblems,ProblemArray);
}
#endif
