#ifndef lint
static char *RCSid = "$Header: /afs/athena.mit.edu/astaff/project/xmit/Xcluster/snmp/RCS/main.c,v 1.5 97/03/03 09:19:50 root Exp $";
#endif

/*
 * $Log:	main.c,v $
 * Revision 1.5  97/03/03  09:19:50  root
 * *** empty log message ***
 * 
 * Revision 1.4  96/02/08  21:58:47  tom
 * *** empty log message ***
 * 
 * Revision 1.3  1994/10/13  09:58:33  nocuser
 * *** empty log message ***
 *
 * Revision 1.2  93/11/30  17:34:01  tom
 * changed longjmps to continues because sockets were already assigned..
 * no need to do it twice.. it breaks
 * 
 * Revision 1.1  93/08/03  12:58:08  tom
 * Initial revision
 * 
 * Revision 1.0  90/04/12 15:20:25  snmpdev
 * Initial revision
 * 
 */

/******************************************************************************
**
** 		main.c
**
** Main (top-level) function for the 'clustermon' application.
** Application is a daemon-like process that gathers statistics
** from SNMP agents. clustermon will first determine the number
** of interfaces and the network addresses associated with them,
** then gather statistics on each interface, at the intervals
** specified for the agent in the configuration file.
**
**
******************************************************************************/
#include "include.h"
#include <conf.h>
#include <snmp_hs.h>
#include "cmon.h"

char *progname;				/* name of program */

AGENT *first_agent = (AGENT *) NULL;

static jmp_buf env;


/*--------------main------------------*/

/* ARGSUSED */
void
main(argc,argv)
     int argc;
     char *argv[];
{ 
#ifdef SYSLOG
  long tloc;
#endif

  AGENT *a;
  struct itimerval interval, disable;	/* for timeouts */
  struct in_addr from;			/* source of packet */

  trptype mesg;				/* the incoming message */
  short mesg_type;			/* the type of the message received */
  char *id = COMMUNITY_T;		/* the session id */
  long r_id = REQ_ANY;			/* request id -- we'll take anything */
  char *svc = "snmp-trap";		/* service */
  short sidlen;				/* length of session id */
  
  short snmprecv();
  int trapintr();			/* deal with signal during recv */
  int trap_init();

  int selectbits = 0;

  struct timeval timeout;

  int pollsock;				/* struct to save polling socket */
  int trapsock;				/* struct to save trap socket */  
  FILE *f;

  trapsock = pollsock = -1;
  timeout.tv_sec  = 10;			/* make it a POLL  */
  timeout.tv_usec = 0;

  if ((f = fopen("/usr/cmon/cmon.pid", "w")) == NULL)
    {
      fprintf(stderr, "main: unable to create pid file\n");
    }
  else
    {
      int pid = getpid();
      fprintf(f, "%d\n", pid);;
      fclose(f);
    }

#ifdef SYSLOG
  openlog("cmon", LOG_PID, LOG_LOCAL7);

  (void) time(&tloc);
  syslog(LOG_INFO, "Start Cluster Monitor version %s at %s\n",
	 VERSION, (char *) ctime(&tloc));
#endif
  
  if ((progname = rindex(argv[0], '/')) == (char *) NULL)
    progname = argv[0];
  if (*progname == '/')
    progname++;

  /* read configuration file */
  if(configure() < 0)			/* could not configure? */
    exit(1);

  /* open TRAP socket */
  if ((trapsock = trap_init()) < 0) {
    syslog(LOG_ERR, "main: trap_init: can't open trap socket");
    fprintf(stderr,"Main: can't open snmp trap socket \n");
    quit();
  }

  selectbits |= 1 << trapsock;
  
/* initialize some preliminary stuff */
  interval.it_value.tv_sec = 1;	/* HARD CODE 1 sec interval for TRAPS */


/* do set up - discover workstation type of each agent */

  if(setup() < 0)
    exit(1);

  write_info();

  a = first_agent;		    /* start at the beginning of POLL list  */


/*--------------- MAIN LOOP -------------------------------------------------*/

  while (1)
    {
      int ibits;
      int n;
      
      ibits = selectbits;
      n = select(20, (int *)&ibits, (int *)0,(int *)0, &timeout);    
      if (n == 0) 
	{
	  /*---------- Only poll an agent on timeout----------------------*/
	  if( a != (AGENT *)NULL ) /* if agent valid */
	    {
	      /*if((a->status < 0) || (a->ntype == XXX ) */
		poll(a);		/* query for status */
	      a = a->nxt;		/* onto next agent */
	    }
	  else
	    {
	      fprintf(stderr,"Main: ran out of agents - RESET\n");
	      a = first_agent;		/* start at the beginning of list */ 
	    } 
	}      
      
      if (n > 0)
	{
	  if (ibits & (1 << trapsock)) 
	    {
	      /*----------- Select says a TRAP is here  ---------------------*/
	      pollsock = s;	/* save the poll socket  	*/
	      s = trapsock;	/* restore the trap socket 	*/
	      
	      if(setjmp(env))
		{
		  fprintf(stderr,"Main: TRAP giving up\n");
		  trapsock = s;	/* save the trap socket   		*/
		  s = pollsock;	/* restore polling socket 		*/
		  continue;
		}
	      else 
		{
		  (void) signal(SIGALRM,trapintr);
		  setitimer(ITIMER_REAL,&interval,(struct itimerval *)NULL);
		  
		  if((mesg_type = snmprecv(&from,svc,&r_id,s_id,&sidlen,
					   (char *)&mesg,sizeof(mesg))) < 0)
		    longjmp(env,1);
		  setitimer(ITIMER_REAL,&disable,(struct itimerval *)NULL);
		  
		  trapsock = s;		/* save the trap socket */
		  s = pollsock;		/* restore polling socket */
		  
		  if(bcmp(id,s_id,strlen(s_id)) != 0)
		    {
		      fprintf(stderr,
			      "Main: TRAP community wrong on TRAP recv\n");
		      continue;
		    }
		  
		  if (mesg_type != TRP) /* if this isn't a trap message */
		    {
		      fprintf(stderr,"Main: msg wasn't a trap\n");
		      continue;
		  }
		  
		  process_trp(&mesg);
		  write_info();
		}
	    }
	}
      
      /*----------- here on ERROR return from select, probably a SIGNAL -----*/
      if (n < 0)
	{
	  if (errno == EINVAL)
	    {
	      printf("main: select returned negative EINVAL \n");
	      syslog(LOG_ERR, "main: select: %m");
	      quit();
	    }
	  if (errno == EINTR)
	    {
	      printf("main: select returned negative SIGNAL\n");
	      syslog(LOG_ERR, "main: select: %m");
	      quit();
	    }
	}
    }
  
  exit(0);				/* NOTREACHED */
}

/*----------------------- FUNCTIONS ---------------------------------------*/

write_info()
{
  AGENT *a;
  int i, j, k;
  char outbuf[10];
  FILE *f;
  int status[MAXCLUSTERS][MAXMACHTYPE][3];

  if ((f = fopen("/usr/cmon/cmon.temp", "w+")) == NULL)
    {
      fprintf(stderr, "write_info: unable to create temporary file\n");
      return;
    }
    
  for(i=0; i < cluster; i++) 
    for(j=0; j < MAXMACHTYPE; j++) 
      for(k=0; k < 3; k++) 
	status[i][j][k] = 0;

  a = first_agent;			/* start at beginning of list */
  while(a != (AGENT *)NULL)		/* while there are still agents */
    {
      if( a->ntype >= 0 )		/* It's type is known */
	status[a->nloc][a->ntype][a->status+1]++; /* so count it */
      a = a->nxt;
    }
      
  for(i=0; i < cluster; i++) 
    {
      fputs(clusters[i], f);
      /* If you ever want to re-introduce the rios/finger hack you need to 
	 make this MAXMACHTYPE - 1 */ 
      for(j=0; j < MAXMACHTYPE ; j++) 
	for(k=1; k < 3; k++)
	  {
	    (void) sprintf(outbuf, " %3d", status[i][j][k]);
	    fputs(outbuf, f);
	  }
      fputs("\n", f);
    }

  (void) fclose(f);

  if (rename("/usr/cmon/cmon.temp", "/usr/cmon/cmon.status"))
    fprintf(stderr, "write_info: unable to rename temporary file\n");
}



trapintr()
{
  fprintf(stderr,"Main: TRAP receive timed out waiting for TRAP\n");
  return(0);
}


quit()
{
  syslog(LOG_ALERT, "Main: quit called, exiting...\n");
  exit(0);
}



/* Initialize the SNMP TRAP socket, port, etc. */
int trap_init()
{
  char hname[32];		/* temporary buffer for hostname */
  struct hostent *hp;		/* to get local host address */
  int sock;
  
  /* Get the  SNMP trap port. */
  bzero((char *)&local,sizeof(local));
  if(gethostname(hname,32) < 0)
    return(-1);
  do				/* get address of local host */
    hp = gethostbyname(hname);
  while(hp != NULL && hp->h_addrtype != AF_INET);
  if(hp == NULL)
    return(-2);
  bcopy((char *)(hp->h_addr),(char  *)&local.sin_addr,hp->h_length);
  local.sin_family = AF_INET;      /* want internet messages */
  
  /* get well-known port */
  local.sin_port = (short)htons((u_short)SNMPTRAP);
  
  /* create and bind local socket */
  if((sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
    return(-3);
#ifndef TEST
  if(bind(sock,(struct sockaddr *)&local,sizeof(local)) < 0)
    return(-4);
#endif

  return(sock);
}
