/* 

HAPM - High Availability Port Monitor

by Alexandre Antonio Antunes de Almeida (BR Army)
   Joao Eriberto Mota Filho (BR Army)
   Rosemeri Dantas de Oliveira (BR Army)

High Availability Port Monitor (HAPM) is a local port status check. It is a
simple, light and fast daemon to check TCP/UDP ports. If one or more monitored
ports (per IP) downs then the Heartbeat will be killed by HAPM.
   
This is a Brazilian project under GNU/GPL License!
URL: http://hapm.sourceforge.net

Please check the /etc/ha.d/hapm.conf file.

*/
   
#include "hapm.h" 
#include <stdarg.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <syslog.h>
#include <string.h>
#define MAX 100
//treatment of buffer overflow
#define BUFFMAXSIZEIP 16  //max size of IP number (+1: \n)
#define BUFFMAXSIZEPORT 6 //max size of port

// Print out the version number
void PrintVersion()
{
    printf("\n\nHigh Availability Port Monitor - HAPM v%0.1f",NUMBER_VERSION);
    printf("\nhttp://hapm.sourceforge.net\n\n");
}

void PrintUsage()
{
    PrintVersion();
    
    printf("Usage:\n");
    printf("----------------------------\n");
    printf("hapm <args>\n");
    printf("  -v  Print version and exit\n");
    printf("----------------------------\n\n");
    printf("Example:\n");
    printf("  hapm -v\n\n");
}

int main(int parc, char *parv[])
{
    if(parc>1)
    {	
        if(parc>2)
	{
    	    PrintUsage();
	    exit(0);
	}

	if(strncmp("-v",parv[1],3)==0)
        {
    	    PrintVersion();
	    exit(0);
	} else {
    	    printf("Couldn't understand command line, quitting\n\n");
	    exit(0);
	}
    }
    
    /* Creating Structure that will include IP address and Port.  
     eternal looping. */
    typedef struct	
    {
	char address_ip[BUFFMAXSIZEIP];  
	char number_port[BUFFMAXSIZEPORT];
    } addressport;  

    int 	fd;
    char	ip[BUFFMAXSIZEIP]; 
    char 	port[BUFFMAXSIZEPORT];	
    struct 	sockaddr_in serv_addr;
    addressport matriz_addressport[MAX]; 
       
    /* Reading configuration file */
    FILE *conf,*pid;
    char linha[MAX];
    char vsocket[]="socket=";
    char vtime[]="time=";
    char vgraceful_time[]="graceful_time=";
    char vheartbeat[]="heartbeat=";
    char vheartbeatpid[]="heartbeatpid=";
    char heartbeat_path[MAX];
    char heartbeatpib_path[MAX];
    int  vpid=getpid();
    int  inicioPosicaoPorta;
    char time_sleep[5];
    char graceful_time[5];
    int  i,j,k,h,s,t,p,g;
    int	 interval;
    char cmd[MAX]; //Store the String of system command
    char cmdstop[MAX];//Store  "stop" command
    char cmdrm[MAX];//Store string of remove command of the pid file
    char cmdpid[MAX];//Store rm command
    
    // LOG: HAPM Started
    syslog(LOG_NOTICE,"HAPM Started");

    //Reading config file (hapm.conf) and initialization of variables.
    conf = fopen("/etc/ha.d/hapm.conf","r");

    if (!conf){
	printf("HAPM: ERROR: fail when openning configuration file. \n");

	// LOG: HAPM: ERROR: fail when openning configuration file
	syslog(LOG_NOTICE,"ERROR: fail when openning configuration file.");    
	exit(0);
    }
    
    fgets(linha, MAX, conf);
    
    /* Processing of configuration file. Here, we'll get:
       - IP adresses
       - Number Ports
       - Heartbeat path to stop service
       - Time to sleep
    */
    
    /* Initialize vector to mount the  ADDRESSPORT structure */
    s=0;

    /* Beginning of process */
    while(!feof(conf))
    {
        /* Does not read comment lines with the charactere '#'
	   and empty lines */

	if(strncmp("#",linha,1)!=0 & strlen(linha)!=1)
	{
	    // Mount  ADDRESSPORT structure
	    if(strncmp(vsocket,linha,7)==0)
	    {
		// Locate the comma position in line of file
    		for(i=0;i<=25;i++)
		{
    		    if (linha[i]==':')
			inicioPosicaoPorta=i+1; // comma
		}    	
    		//Locate the Port
		//Clear  port vector
		for(j=0;j<=9;j++)
		{
    		    port[j]='\0';
		}

    		j=0;
		for(i=inicioPosicaoPorta;linha[i]!='\0';i++)
		{
		    port[j++]=linha[i];
		}
		j=0;

		//Clear IP vector
    		for(j=0;j<=15;j++)
    		    ip[j]='\0';
	
		//Locate IP and store in IP variable
    		k=0;	
		for(i=7;i<=(inicioPosicaoPorta-2);i++)
		{
		    ip[k++]=linha[i];
		}

		// Store ip and  port from  ADDRESSPORT
		strncpy(matriz_addressport[s].address_ip,ip,sizeof(ip));
		strncpy(matriz_addressport[s].number_port,port,sizeof(port));
		s++;
	    }
	    
    	    // Locate heartbeat path in configuration file
	    if(strncmp(vheartbeat,linha,10)==0)
	    {
		h=0;
		for(i=10;i<=strlen(linha);i++)
		{
		    heartbeat_path[h++]=linha[i];
		}	    
	    }

    	    // Locate heartbeat.pid path in configuration file
	    if(strncmp(vheartbeatpid,linha,13)==0)
	    {
		p=0;
		for(i=13;i<=strlen(linha);i++)
		{
		    heartbeatpib_path[p++]=linha[i];
		}	    
	    }
	    
	    // Read the "time to sleep" of process
	    if(strncmp(vtime,linha,5)==0)
	    {
		t=0;
		for(i=5;linha[i]!='\0';i++)
		{
		    time_sleep[t++]=linha[i];
		}
	    }

	    // Time in seconds used by HAPM to confirm the die of the process
	    // This time prevents a falses positives caused by restart of process
	    if(strncmp(vgraceful_time,linha,14)==0)
	    {
		g=0;
		for(i=14;linha[i]!='\0';i++)
		{
		    graceful_time[g++]=linha[i];
		}
	    }

	}    
        // Read next line of configuration file
        fgets(linha,MAX, conf);
    }
    // Close configuration file
    fclose(conf);

    //Initialize the command string:
    for (i=0;i<=h-3;i++)
    {
	cmd[i] = heartbeat_path[i];
    }
    strncpy(cmd,heartbeat_path,h-4);
    strncpy(cmdstop," stop",6);
    strncat(cmd,cmdstop,sizeof(cmdstop)+1);
    
    //Initialize the command string of remove heartbeat.pid file
    for (i=0;i<=p-3;i++)
    {
	cmdrm[i] = heartbeatpib_path[i];
    }
    strncpy(cmdpid,"rm -f ",7);
    strncat(cmdpid,cmdrm,sizeof(cmdrm)+1);

    // Validate the "time to sleep"
    if(atoi(time_sleep)<=0 | atoi(time_sleep)>3600)
    {
	printf("HAPM: ERROR: The time range must be 0 to 3600 seconds. \n");

        // LOG: HAPM: ERROR: The time range must be 0 to 3600 seconds.
	syslog(LOG_NOTICE,"ERROR: The time range must be 0 to 3600 seconds.");

        exit(0);
     }     
     
    // Create  PID file
    pid=fopen("/var/run/hapm.pid","w");
    fprintf(pid,"%d", vpid);
    fclose(pid);

    // Eternal process
    interval=58;
    while(1==1)
    {
	sleep(atoi(time_sleep));
	for(i=0;i<=(s-1);i++)
	{  
//	    strncpy(ip,matriz_addressport[i].address_ip,sizeof(matriz_addressport[i].address_ip)+1);
//	    strncpy(port,matriz_addressport[i].number_port,sizeof(matriz_addressport[i].number_port)+1);
	    /* 	Creating the socket: 
		AF_NET: says that protocol will be used by socket is TCP/IP family	
		SOCK_STREAM: socket type
	        0: the protocol is adjusted to 0 (zero). So, the socket()
		   will choice the correct protocol according type parameter
	    */
	    // Check error
	    fd = socket(AF_INET, SOCK_STREAM, 0);

//    	    if (fd < 0)
//	        printf("Can't create a new socket");

	    /*
	      serv_addr.sin_family: host byte order
	      serv_addr.sin_port> converte em short a porta pesquisada
	      serv_addr.sin_addr.s_addr=inet_addr(ip)
	        OBS.: serv_addr.sin_addr.s_addr=INADDR_ANY: utiliza o endereço IP da máquina,
	              utilizando INADDR_ANY o sistema usa o endereço IP da máquina onde o
		      processo está rodando
	      memset(&(serv_addr.sin_zero),'\0',8): zera o resto da estrutura
	    */
	    bzero((char *) &serv_addr, sizeof(serv_addr));
	    serv_addr.sin_family = AF_INET;
//	    serv_addr.sin_port = htons(atoi(port));
//	    serv_addr.sin_addr.s_addr = inet_addr(ip);
	    serv_addr.sin_port = htons(atoi(matriz_addressport[i].number_port));
	    serv_addr.sin_addr.s_addr = inet_addr(matriz_addressport[i].address_ip);
	    memset(&(serv_addr.sin_zero),'\0',8);

	    /*
	      bind() return -1 when port are being used
	    */
	    if (bind(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) >= 0)
	    { 
//		It does not work:
//	    	printf("Is down the port%s? I will wait the graceful time...",port);
		sleep(atoi(graceful_time));
		close(fd);
		fd = socket(AF_INET, SOCK_STREAM, 0);
		bzero((char *) &serv_addr, sizeof(serv_addr));
		serv_addr.sin_family = AF_INET;
//		serv_addr.sin_port = htons(atoi(port));
//		serv_addr.sin_addr.s_addr = inet_addr(ip);
		serv_addr.sin_port = htons(atoi(matriz_addressport[i].number_port));
		serv_addr.sin_addr.s_addr = inet_addr(matriz_addressport[i].address_ip);
		memset(&(serv_addr.sin_zero),'\0',8);

		
		if (bind(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) >= 0)
		{
	    	    if (fopen("/var/run/heartbeat.pid","r"))
	    	    {
    			// LOG: HAPM: Port $porta is down.
			//syslog(LOG_NOTICE,"Port %sis down.",port);
			//printf("\n\nHAPM: Port down: %s",port);
			//printf("\n\nHAPM: Port %s is down.",port);
			syslog(LOG_NOTICE,"Port %sis down.",matriz_addressport[i].number_port);
			printf("\n\nHAPM: Port down: %s",matriz_addressport[i].number_port);

			// LOG: HAPM: Stopping Heartbeat.
			syslog(LOG_NOTICE,"Stopping Heartbeat.");
			printf("HAPM: Stopping Heartbeat. \n\n");

			//Stop in Heartbeat - parameter hapm.conf
    			system(cmd);
		    
			//Erase heartbeat.pid file 
			system(cmdpid);
		    } else
		    {
			interval++;
			if (interval==60)
			{
			    // Report Heartbeat status in log and screen periodcly
			    syslog(LOG_NOTICE,"WARNING: HEARTBEAT IS DOWN.");
			
			    printf("HAPM WARNING: HEARTBEAT IS DOWN. CHECK THE LOG. \n");
			    interval=0;
			}
		    }
		}  
	    }  
	    //Close socket
	    close(fd);
	}
    }
}
