/*****************************************************************************
*                  Copyright (c) 1997 Lucent Technologies                    *
*                            All Rights Reserved                             *
*                                                                            *
*   This is unpublished proprietary source code of Lucent Technologies.      *
*         The copyright notice above does not evidence any actual            *
*               or intended publication of such source code.                 *
*****************************************************************************/

/****************************************************************************/
/* File:        SAinit.c 
** Description: 
**		This file contains all the functions of initializing the
**		SNMP Agent.
** Functions: 
**		void SAinit(int argc, char **argv);
**		void SAset_defaults();
**		void SAinit_UX(int argc, char **argv);
**		void SAprep_netinfo();
**		void SAget_args(int argc, char **argv);
**		void SAprint_info();
**		void SAset_server_socket();
**		static void SAtermfunc(UXSIGNAL sig);
**		void SAsig_handler(int sig);
** History: 	
**		Initial Version. Beth Zhang 8/1/97 
*****************************************************************************/ 

#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/stropts.h>
#include <sys/param.h>
#include <netdb.h>
#include <netinet/in.h>
#include <snmp.h>
#include <snmpdefs.h>
#include <unistd.h>
#include <errno.h>
#include "ux/UXmacros.h"
#include "mg/MGapports.h"
#include "cm/CMpcrid.h"
#include "rdbu/RUmacros.h"
#include "util/UTrccIntfc.h"
#include "event/EVmac.h"
#include "SAdefs.h"
#include "SAext.h"

/* Variables ***************************************************************/
int SAehhb_intvl;
int SAtrap_intvl;
int SAplog_intvl;
int SArcchb_intvl;
int SAnumber_traps;
int SAsize_trapqueue;

SATRAPQ	SAtrapQ;

u_short         SAsnmp_port;		/* snmp port */
u_long		SAtrapd_net_addr;	/* trap host IP address */
u_short         SAtrapd_port;		/* trap port */
char 		SAtrapd_host[MAXHOSTNAMELEN+1];

int SAsnmpsock;

int SAehdeath_alrmsvrt;

char SAgetCommunity[SA_MAX_COMMUNITY_LEN+1];
char SAsetCommunity[SA_MAX_COMMUNITY_LEN+1];
char SAtrapCommunity[SA_MAX_COMMUNITY_LEN+1];
 
long SAstart_time;

static char *Usage = "\
Usage:     APsnmpagent	[ -h EMShostname ]\n\
                     	[ -p snmpPort ]\n\
                     	[ -t trapdPort ]\n\
                     	[ -b EHHBinterval ]\n\
                     	[ -a EHalarmseverity ]\n\
                     	[ -i trapsendinterval ]\n\
			[ -l ploginterval ]\n\
                     	[ -g getcommunitystring ]\n\
                     	[ -s setcommunitystring ]\n\
                     	[ -n numbertraponce ]\n\
                     	[ -q sizetrapqueue ]\n\
where:\n\
      -h EMShostname            hostname of EMS (destination for traps)\n\
      -p localPort              local port to which agent should bind\n\
      -t trapdPort              port number of the trap daemon at EMS host\n\
      -b EHHBinterval           interval(milisecond) for HB with Event Handler\n\
      -a EHalarmseverity[0...5] alarm severity for EHdeath alarm trap\n\
      -i trapsendinterval       interval(milisecond) for sending next traps\n\
      -l ploginterval		interval(second) for log performance data\n\
      -g getcommunitystring     community string for SNMP GET authenication\n\
      -s setcommunitystring     community string for SNMP SET authenication\n\
      -n numbertraponce         number of traps will be sent at once\n\
      -q sizetrapqueue          maximum traps waiting in the queue\n\
\n";
 
/* SAprevSetReqId stores the previous setRequest request id. If a duplicate 
** request id is received from EMS, that setRequest will be rejected */
INT_32_T SAprevSetReqId = -1;

/* global variables to keep ap active alarms in SNMP agent for improve 
** performance of the agent */
INT_32_T SAnumAlarms = 0;
INT_32_T SAalarmSeq[EV_MAX_ALARMS];

/************************************************************************/
/* Name:        SAsig_handler
** Description:
**              This is a signal handler.
** Inputs:
**              sig:     signal
** Returns:
**              None
** History:
**		Initial Version: Beth Zhang 8/1/97
**************************************************************************/
void 
SAsig_handler(int sig) 
{
	UX_D3LOG("Entering SAsig_handler()\n");
        switch (sig) 
	{
                case SIGPOLL:
                        /* set flag. reading socket and processing client 
			   request will be handled by SAhandle_client_request
                           in main loop. */
			SAsocket_has_msg = TRUE;
                        break;
                default:
			UX_ELOG("Unknown signal: %d\n", sig);
                        break;
        } /* end of switch */
	UX_D3LOG("Exiting SAsig_handler()\n");
}


/************************************************************************/
/* Name:        SAtermfunc
** Description:
**		This function is called when the process is asked to terminate 
**		via a SIGTERM signal. The address of the function is passed
** 		to UXSPINIT as a parameter.	
** Inputs:
**		sig: signal;
** Returns:
**              None
** History:
**		Initial Version: Beth Zhang 8/1/97
****************************************************************************/
static void 
SAtermfunc(UXSIGNAL sig) 
{
        UX_D3LOG("Entering SAtermfunc()\n");

        /*  Detach from the RDBU shared memory segment. */
        RUSHMDETACH();

	/* Detach from NEST shard memory segment */
	EVDETACH_FROM_NEST;

	/* close socket */
	close(SAsnmpsock);

	if (sig == UXNOSIG) 
		UX_D3LOG("Terminating: Process called UXSPCU\n");
	else
		UX_D3LOG("Fatal signal %s received\n", UXSIGRET(sig));

        UX_D3LOG("Exiting SAtermfunc()\n");
}


/************************************************************************/
/* Name:        SAset_server_socket
** Description:   
**              This function creates socket, binds the socket to the
**              Agent snmp port, sets up the server to listen for
**              connection requests from the client, and sets up 
**              the descriptor so that it will get a SIGPOLL signal when
**              there is something to read from the socket.
** Inputs:        
**              None
** Returns:
**		None
** History:
**		Initial Version: Beth Zhang 8/1/97     
****************************************************************************/
void 
SAset_server_socket() 
{
	int retval;
	struct sockaddr_in snmpsvr;  /* internet socket address structure */
	int flag = 1;
	pid_t pid = getpid();

	UX_D3LOG("Entering SAset_server_socket()\n");

        /* Create socket */
        if ((SAsnmpsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 
	{
		UX_ELOG("Create socket fail: %s. Exit.\n", strerror(errno));
                UXSPCU(UXNOSIG); 
        } 
 
        /* prepare for snmp server */             
        snmpsvr.sin_port = htons(SAsnmp_port);
        snmpsvr.sin_family = AF_INET;
	snmpsvr.sin_addr.s_addr = htonl(INADDR_ANY);

        /* Bind the socket to the local address specified in snmp server. This
        ** establishes a local address for the socket. */
        if ((retval = bind( SAsnmpsock, 
			    (struct sockaddr *)(&snmpsvr),
                            sizeof(snmpsvr))) < 0) 
	{
		UX_ELOG("Bind socket fail: %s. Exit.\n", strerror(errno));
                UXSPCU(UXNOSIG);
        } 

	/* Call UTfd_async() function to perform the control functions on
	   socket accessed via the file descriptor that are needed for
 	   asynchronous input processing. */
	if (UTfd_async(SAsnmpsock) == -1) /* return 1 on success */
					 /* return -1 on failure */
	{
		UX_ELOG("UTfd_async() returns failed.\n");
		UXSPCU(UXNOSIG);
	}

	UX_D3LOG("Exiting SAset_server_socket()\n");

}


/************************************************************************/
/* Name:        SAprint_info
** Description:
**              This function processes command-line arguments.
** Inputs:
**		None
** Returns:
**              None
** History:
**              Initial Version: Beth Zhang 9/5/97
****************************************************************************/
void 
SAprint_info()
{
        UX_D1LOG("Host Network Information:\n");
        UX_D1LOG("\tsnmp port is set to %d\n", SAsnmp_port);
        UX_D1LOG("\ttrap port is set to %d\n", SAtrapd_port);
        UX_D1LOG("\ttrap host is set to %s\n", SAtrapd_host);
        UX_D1LOG("    Event Handler Information:\n");
        UX_D1LOG("\tEvent Handler heartbeat interval is set to %d\n",
                        SAehhb_intvl);
        UX_D1LOG("\tEvent Handler death alarm severity is set to %d\n",
                        SAehdeath_alrmsvrt);
        UX_D1LOG("\tPerformance LOG interval is set to %d\n",
                        (int)SAplog_intvl/(60*1000));
	UX_D1LOG("\tRCC HB interval is set to %d miliseconds\n", SArcchb_intvl);
        UX_D1LOG("    Trap Throttling Information:\n");
        UX_D1LOG("\ttrap interval is set to %d\n", SAtrap_intvl);
        UX_D1LOG("\tnumber of traps is set to %d\n", SAnumber_traps);
        UX_D1LOG("\tsize of trap queue is set to %d\n", SAsize_trapqueue);
 
}


/************************************************************************/
/* Name:        SAget_args
** Description: 
**		This function processes command-line arguments.
** Inputs:
**              argc    from main
**              argv    from main
** Returns:
**              None
** History:
**		Initial Version: Beth Zhang 8/1/97
****************************************************************************/
void 
SAget_args(int argc, char **argv) 
{
	int opt;
	int tmp_int;
	
	UX_D3LOG("Entering SAget_args()\n");

	opterr = 0;
        while ((opt = getopt(argc, argv, "h:p:t:b:a:i:g:s:n:q:d:?")) != EOF) 
	{
            switch (opt) 
	    {
                case 'h': /* ems host name */
                        if (gethostbyname((char *)optarg) == NULL)
			{
				UX_ELOG("Unknown EMS host: %s. Use the default: %s\n", 
					optarg,  SA_TRAP_HOSTNAME);
                        }
			else /* overwrite default */ 
			{
				strcpy(SAtrapd_host, optarg);
			} 
                        break;
                case 'p': /* ap snmp port */
                        if ((sscanf(optarg, "%d", &tmp_int) != 1) 
					|| (tmp_int <= 0)) 
			{
				UX_ELOG("Invalid snmp port: %d. Use the default: %d\n", 
					tmp_int, SA_DFLT_SNMP_SVC_PORT);
                        } 
			else  /* overwrite default port */
			{
                                SAsnmp_port = (u_short) tmp_int;
			}
                        break;
                case 't': /* EMS trapd port */
                        if ((sscanf(optarg, "%d", &tmp_int) != 1) 
					|| (tmp_int <= 0)) 
			{
				UX_ELOG("Invalid trapd port: %d. Use the default: %d\n", 
					tmp_int, SA_DFLT_TRAPD_SVC_PORT);
                        } 
			else /* overwrite default port */
			{
                                SAtrapd_port = (u_short) tmp_int;
			}
                        break;
                case '?':
                        printf("%s", Usage);
                        UXSPCU(UXNOSIG);
                case 'b': /* EH HB interval in miliseconds */
                        if ((sscanf(optarg, "%d", &tmp_int) !=1)
                                        || (tmp_int <= 0)) 
			{
				UX_ELOG("Invalid EH Heartbeat interval: %d. Use the default: %d\n", 
					tmp_int, SA_EHHB_INTVL);
                        } 
			else 
			{
                                SAehhb_intvl = tmp_int;
			}
                        break;
                case 'l': /* PLOG interval in minutes*/
                        if ((sscanf(optarg, "%d", &tmp_int) !=1)
                                        || (tmp_int <= 0))
                        {
                                UX_ELOG("Invalid PLOG interval: %d. Use the default: %d\n",
                                        tmp_int, (int)SA_PLOG_INTVL/(60*1000));
                        }
                        else
                        {   
                                SAplog_intvl = tmp_int * 60 * 1000;
                        }
                        break;   
                case 'a': /* EH death alarm severity */
                        if ((sscanf(optarg, "%d", &tmp_int) != 1)
                           || (tmp_int < SA_MIB_SEVERITY_INDETERMINATE)
                           || (tmp_int >= SA_MIB_SEVERITY_CLEARED)) 
			{
				UX_ELOG("Invalid alarm severity: %d. Use the default: %d\n",
					tmp_int, SA_EHDEATH_ALARM_SEV);
                        } 
			else 
			{
                                SAehdeath_alrmsvrt = tmp_int;
			}
                        break;
                case 'i': /* trap interval in miliseconds */
                        if ((sscanf(optarg, "%d", &tmp_int) != 1)
                                        || (tmp_int <= 0)) 
			{
				UX_ELOG("Invalid trap interval: %d. Use the default: %d\n", 
					tmp_int, SA_TRAP_INTVL);
                        } 
			else 
			{
                                SAtrap_intvl = tmp_int;
			}
                        break;
                case 'g': /* get community string name */
                        strcpy(SAgetCommunity, optarg);
                        break;
                case 's': /* set community string name */
                        strcpy(SAsetCommunity, optarg);
                        break;  
                case 'n': /* number of traps will be sent at once */
                        if ((sscanf(optarg, "%d", &tmp_int) != 1)
                                        || (tmp_int <= 0)) 
			{
				UX_ELOG("Invalid number of traps: %d. Use the default: %d\n",
					tmp_int, SA_NUM_TRAPS);
                        } 
			else 
			{
                                SAnumber_traps = tmp_int;
			}
                        break;
                case 'q': /* size of trap queue */
                        if ((sscanf(optarg, "%d", &tmp_int) != 1)
                                        || (tmp_int <= 0)) 
			{
				UX_ELOG("Invalid size of trap queue: %d. Use the default: %d\n",
					tmp_int, SA_SIZE_TRAP_QUEUE);
                        } 
			else 
			{
                                SAsize_trapqueue = tmp_int;
			}
                        break;
		case 'd': /* debug level */
			if ((sscanf(optarg, "%d", &tmp_int) != 1)
					|| (tmp_int < UX_DBG1) 
					|| (tmp_int > UX_DBG3))
			{
				UX_ELOG("Invalid debug level: %d\n", tmp_int);
			}
			else
			{
				UX_DEBUG_ON = TRUE;
				UX_DEBUG_LVL = tmp_int;
			}
			break;
            } /* end of switch */
        } /* end of while */

        UX_D3LOG("Exiting SAget_args()\n");
}
 

/************************************************************************/
/* Name:        SAprep_netinfo()
** Description:
**              This function gets host address and service port for both
**              Agent and EMS. If we found a service entry, use the port
**              specified in that entry. Otherwise, use the default.
** Inputs:
**              None
** Returns:
**		None
** History:
**              Initial Version:        Beth Zhang 8/5/97
****************************************************************************/
void 
SAprep_netinfo() 
{
	struct servent *tmp_srvport; /* temporory host port */

	UX_D3LOG("Entering SAprep_netinfo()\n");

	/* Get snmp service port */
	tmp_srvport = getservbyname(SA_SNMP_SERVICE, "udp");
	if (tmp_srvport == NULL) 
	{
		UX_ELOG("Unknown snmp service port: %s. Port set to default: %d\n",
			SA_SNMP_SERVICE, SA_DFLT_SNMP_SVC_PORT);
		SAsnmp_port = SA_DFLT_SNMP_SVC_PORT;
	} 
	else
		SAsnmp_port = tmp_srvport->s_port;

	/* Get EMS host name */
	memset(SAtrapd_host, '\0', sizeof(SAtrapd_host));
	strcpy(SAtrapd_host, SA_TRAP_HOSTNAME);
 
	/* Get trapd service port */
        tmp_srvport = getservbyname(SA_TRAPD_SERVICE, "udp"); 
        if (tmp_srvport == NULL) 
	{
		UX_ELOG("Unknown trapd service port: %s. Port set to default: %d\n",
			SA_TRAPD_SERVICE, SA_DFLT_TRAPD_SVC_PORT);
               	SAtrapd_port = SA_DFLT_TRAPD_SVC_PORT; 
	} 
	else
               	SAtrapd_port = tmp_srvport->s_port; 

	UX_D3LOG("Exiting SAprep_netinfo()\n");
}
 
/***************************************************************************/
/* Name:        SAinit_UX
** Description:
**              This function calls UXSPINIT for UX System Process
**              initialization.
** Inputs:
**              argc from main
**              argv from main
** Returns:
**		None
** History:
**		Initial Version: Beth Zhang 8/1/97
****************************************************************************/
void 
SAinit_UX(int argc, char **argv) 
{
	UXRETVAL uxretval;
 
        /* Do UX initialization. */
        uxretval = UXSPINIT(MGAPPT_SNMPAGENT, SAtermfunc, argc, argv); 
 
        switch (uxretval) 
	{
                case UXSUCCESS:
                case UXRESPAWN:
                case UXNOTERMFUNC:
                case UXNOSPCU:
                        break;

                case UXECONN:  
			break;
                default:         
                        /* some strange thing happened, print error and exit*/
			UX_ELOG("UXSPINIT() failure: %s\n",
					UXRETSTRING(uxretval));

			/* Sleep for a bit before exiting so we don't cause the
			** process to be respawned too quickly. */
			sleep(SA_SLEEP_TIME);
			/* Some strange error has occurred, terminate the 
			** process */
			exit(1);
			break;

        }    /* end of switch */
 
	UX_D3LOG("SAinit_UX success. Exiting SAinit_UX()\n");
}


/***************************************************************************/
/* Name:        SAset_defaults
** Description:
**              This function sets default values for SNMP get/set community
**		string, time interval for checking Event Handler death, 
**		Event Handler death alarm severity, timer interval for 
**		sending traps from trap queue, the number of traps that will 
**		be sent to EMS at once, the size of trap queue. Finaly this
**		function calls SAprep_netinfo for getting network related
**		information.
** Inputs:
**		None
** Returns:
**              None
** History:
**              Initial Version: Beth Zhang 8/1/97
****************************************************************************/
void 
SAset_defaults()
{
	UX_D3LOG("Entering SAset_defaults()\n");

	/* Community string for GetRequests and SetRequests */
        memset(SAgetCommunity, '\0', sizeof(SAgetCommunity));
        memset(SAsetCommunity, '\0', sizeof(SAsetCommunity));
        memset(SAtrapCommunity, '\0', sizeof(SAtrapCommunity));
        memcpy(SAgetCommunity, SA_GET_COMMUNITY_STR, 
			strlen(SA_GET_COMMUNITY_STR));
        memcpy(SAsetCommunity, SA_SET_COMMUNITY_STR, 
			strlen(SA_SET_COMMUNITY_STR));
        memcpy(SAtrapCommunity, SA_TRAP_COMMUNITY_STR, 
			strlen(SA_TRAP_COMMUNITY_STR));

	/* default values related to Event Handler */
        SAehhb_intvl = SA_EHHB_INTVL;
        SAehdeath_alrmsvrt = SA_EHDEATH_ALARM_SEV;

	/* default values related to trap throttling */
        SAtrap_intvl = SA_TRAP_INTVL;
        SAnumber_traps = SA_NUM_TRAPS;
        SAsize_trapqueue = SA_SIZE_TRAP_QUEUE;
	memset(&SAtrapQ, 0, sizeof(SAtrapQ));

	/* initialize snmp_stats */
	snmp_stats.snmpOutTraps = 0;
	snmp_stats.snmpOutPkts = 0;
	SAplog_intvl = SA_PLOG_INTVL;

	SArcchb_intvl =  (int) ((SA_RCCHB_INTVL / 3) * 1000);

	/* default values related network information */
        SAprep_netinfo();

	UX_D3LOG("Exiting SAset_defaults()\n");

}


/***************************************************************************/
/* Name:	SAinit         
** Description:   
**		This function is called by main() function to initialize
**		the SNMP Agent.
** Inputs:	
**		argc		from main
**		argv 		from main 
** Returns:	
**		None
** History:     
**		Initial Version: Beth Zhang 8/1/97
****************************************************************************/ 
void 
SAinit(int argc, char **argv) 
{
	long retval;
	struct timeval      now;
        struct hostent  *SAtrapd_addr_p;        /* trap host structure */

        SAinit_UX(argc, argv);

        /* Initialize with RCC */
        if (!UXPRIVATE)
        {
                if ((retval = UT_RCCINIT(SA_RCCHB_INTVL)) != UXSUCCESS)
                {
                        UX_ELOG("Fatal error: RCC initialization failed %s\n",
                                UXRETSTRING(retval));
                        UXSPCU(UXNOSIG);
                }
        }

        /* Attach to RDBU shared-memory segment */
        UX_D1LOG("SAinit: initializing RDBU; wait up to %d seconds\n",
          SA_RDBU_WAIT / 1000);
        retval = RUSHMINIT(SA_RDBU_WAIT);
	UX_D1LOG("SAinit: return from RUSHMINIT()\n");

        if (retval == ITGFAIL) {
                UX_ELOG("Attach to RDBU shared-memory segment failed: %d\n", retval);
                UXSPCU(UXNOSIG);
        }

        (void) sigset(SIGPOLL, SAsig_handler);
	SAset_defaults();
	SAget_args(argc, argv);
	SAprint_info();

        /* get EMS IP address for later sending traps to. */
        if ((SAtrapd_addr_p = gethostbyname(SAtrapd_host)) == NULL)
        {
                UX_ELOG("Unknown host: %s. Exit.\n", SAtrapd_host);
                UXSPCU(UXNOSIG);
        }

        /* get trapd network address */
        memcpy(&SAtrapd_net_addr, SAtrapd_addr_p->h_addr_list[0],
                                  SAtrapd_addr_p->h_length);

	SAset_server_socket();

        /* initialize timer and flags for trap throttling and
        ** heartbeating with Event Handler and RCC */
        SAreset_for_next_traps();
        SAreset_for_EHHB();
	SAreset_for_plog();
	SAreset_for_rcchb();
 
	SAget_actAlarmSeqTable();

        (void) gettimeofday(&now,0);
        SAstart_time = now.tv_sec;

	/* Send coldStart trap to the EMS */
	SAsend_traps(SA_COLD_START_TRAP);

	UX_D3LOG("Exiting SAinit()\n");
}


