/*
 * This file contains snmp querying routines.
 *
 * Copyright 1990 by the Massachusetts Institute of Technology.
 *
 * For copying and distribution information, please see the file
 * <mit-copyright.h>.
 *
 * Tom Coppeto
 * MIT Network Services
 * 8 August 1990
 *
 *    $Source: /afs/.net.mit.edu/tools/src/xinterface/RCS/snmpquery.c,v $
 *    $Author: tom $
 *    $Locker:  $
 *    $Log:	snmpquery.c,v $
 * Revision 1.7  94/07/25  10:47:47  tom
 * *** empty log message ***
 * 
 * Revision 1.6  92/01/28  11:41:29  tom
 * fixed bcopy bug
 * 
 * Revision 1.5  91/05/21  11:07:44  tom
 * fixes null ref bug
 * 
 * Revision 1.4  91/02/21  13:46:53  tom
 * check errindex
 * 
 * Revision 1.3  90/10/29  09:18:27  tom
 * can deal with agents that don't suuport all variables in a query...
 * have to delete bad variable and retry
 * 
 * Revision 1.2  90/08/19  16:19:04  tom
 * *** empty log message ***
 * 
 * Revision 1.1  90/08/15  01:13:10  tom
 * Initial revision
 * 
 */

#ifndef lint
static char *rcsid = "$Header: /afs/.net.mit.edu/tools/src/xinterface/RCS/snmpquery.c,v 1.7 94/07/25 10:47:47 tom Exp $";
#endif

#include "xport.h"
#include <sys/time.h>
#include <signal.h>
#include <setjmp.h>
#include <mit-copyright.h>

#define TIMEOUT   10
#define RETRIES   2

getreq reply;
static jmp_buf env;
static int retries;

static caddr_t *adjust_variables();
static int onintr();
static caddr_t *snmp_query();

objident skip;
int zero = -1;

/*
 *   Function:    make_snmp_query()
 *   Description: makes an snmp query. This function will block and retry
 *                RETRIES after TIMEOUT.
 *   Arguments:   objident obj: variable to query
 *   Returns:     ptr to value.
 */


caddr_t
make_snmp_query(obj)
     objident *obj;
{
  caddr_t *v;
  getreq msg;
  int index;

  bzero(&msg, sizeof(msg)); 
  bcopy(obj, &msg.varlist.elem[0].name, sizeof(objident));
  msg.varlist.elem[0].val.type = EMPTY;
  msg.reqid = getpid();
  msg.varlist.len = 1;
  v = snmp_query(&msg, REQ, &index);
  return(*v);
}



caddr_t *
make_lots_o_snmp_queries(obj)
     objident **obj;
{
  getreq msg;
  int i = 0;
  int j = 0;
  caddr_t *v;
  int index;

  while(1)
    {
      i = 0;
      j = 0;
      index = 0;
      bzero(&msg, sizeof(msg)); 
      while(obj[i])
	{
	  if(obj[i] != &skip)
	    {
	      bcopy(obj[i], &msg.varlist.elem[j].name, sizeof(objident));
	      msg.varlist.elem[j].val.type = EMPTY;
	      ++j;
	    }
	  ++i;
	}
      if(j == 0)
	return((caddr_t *) NULL);
      msg.reqid = getpid();
      msg.varlist.len = j;
      if(!(v = snmp_query(&msg, REQ, &index)))
	if(!index)
	  return(v);
	else
	  {
	    i = 0;
	    while(obj[i] != (objident *) NULL)
	      {
		if(oidncmp(obj[i], &(msg.varlist.elem[index - 1].name),
			   msg.varlist.elem[index - 1].name.ncmp) == 0)
		  obj[i] = &skip;
		++i;
	      }
	    continue;
	  }
      if(!*v)
	return((caddr_t *) NULL);
      
      i = 0;
      while(v[i++]);
      if((i - 1) != j)
	return((caddr_t *) NULL);
      
      v = adjust_variables(v, obj);
      return(v);
    }
}




      
caddr_t
make_snmp_next(obj)
     objident *obj;
{
  caddr_t *v;
  getreq msg;
  int index;

  bzero(&msg, sizeof(msg)); 
  bcopy(obj, &msg.varlist.elem[0].name, sizeof(objident), &index);
  msg.varlist.elem[0].val.type = EMPTY;
  msg.reqid = getpid();
  msg.varlist.len = 1;
  v = snmp_query(&msg, NXT, &index);
  return(*v);
}


caddr_t *
make_lots_o_snmp_nexts(obj)
     objident **obj;
{
  getreq msg;
  int i = 0;
  caddr_t *v;
  int index; 

  
  bzero(&msg, sizeof(msg)); 
  while(*obj)
    {
      bcopy(*obj, &msg.varlist.elem[i].name, sizeof(objident));
      msg.varlist.elem[i].val.type = EMPTY;
      ++obj;
      ++i;
    }
  msg.reqid = getpid();
  msg.varlist.len = i;
  if((v = snmp_query(&msg, NXT, &index)) == (caddr_t *) NULL)
    return(v);
  if(!*v)
    return((caddr_t *) NULL);
  else
    return(v);
}



static caddr_t *
snmp_query(msg, req, index)
     getreq *msg;
     int req;
     int *index;
{
  char    *s_id = (char *) NULL;
  struct  sockaddr_in from;
  int     sid_len;
  int     reqid;
  int     rc;
  struct  itimerval interval;            
  struct  itimerval disable;
  static  caddr_t val[SNMPMAXVARS];
  int     i;

  retries = 0;
  bzero(&reply, sizeof(reply));
  bzero(val, sizeof(val));
  reqid = REQ_ANY;

  /*
   * save env
   */

  if(setjmp(env))
    {
      if(retries < RETRIES) 
	retries++;       
      else         
	return(val);
    }

  /*
   * send request
   */

  if((rc = snmpsend(req, msg->reqid, &addr, "snmp", msg, community, 
		    strlen(community), TIMEOUT)) < 0)
    {
      printf("error = %d\n", rc);
      return(val);
    }

  /*
   * prepare timeout
   */

  bzero(&interval, sizeof(interval));
  bzero(&disable,  sizeof(disable));
  interval.it_value.tv_sec = TIMEOUT; 

  signal(SIGALRM, onintr);
  setitimer(ITIMER_REAL, &interval, (struct itimerval *) NULL);

  /*
   * get reply
   */

  if((rc = snmprecv(&from, (char *) NULL, &reqid, s_id, &sid_len, &reply,
		    sizeof(getreq))) < 0)
    {
      printf("error = %d\n", rc);
      return(val);
    }

  setitimer(ITIMER_REAL, &disable, (struct itimerval *) NULL);
  if(reply.errindex)
    {
      *index = reply.errindex;
      return(val);
    }

  /*
   * return value 
   */

  for(i = 0; i < reply.varlist.len; i++)
    {
      switch(reply.varlist.elem[i].val.type)
	{
	case STR:
	  val[i] = (caddr_t) reply.varlist.elem[i].val.value.str.str;
	  break;
	case IPADD:
	  val[i] = (caddr_t) &(reply.varlist.elem[i].val.value.ipadd);
	  break;
	case INT:
	  val[i] = (caddr_t) &(reply.varlist.elem[i].val.value.intgr);
	  break;
	case GAUGE:
	  val[i] = (caddr_t) &(reply.varlist.elem[i].val.value.gauge);
	  break;
	case CNTR:
	  val[i] = (caddr_t) &(reply.varlist.elem[i].val.value.cntr);
	  break;
	case OBJ:
	  val[i] = (caddr_t) &(reply.varlist.elem[i].val.value.obj);
	  break;
	case TIME:
	  val[i] = (caddr_t) &(reply.varlist.elem[i].val.value.time);
	  break;
	}
    }
  return(val);
}



/*   
 * Function:    make_snmp_set()
 *   Description: set an snmp value. This function will block and retry
 *                once after TIMEOUT. 
 *   Arguments:   objident obj: variable to query
 *                caddr_t val:  value
 *                int type:     type of vaue
 *   Returns:     ptr to value.
 */

make_snmp_set(obj, val, type)
     objident *obj;
     caddr_t val;
     int type;
{
  getreq msg;
  char *s_id = (char *) NULL;
  struct sockaddr_in from;
  int sid_len;
  int reqid;
  int rc;
  struct itimerval interval;            
  struct itimerval disable;

  retries = 0; 

  /*
   * prepare message
   */

  bzero(&msg, sizeof(msg));
  bcopy(obj, &msg.varlist.elem[0].name, sizeof(objident));
  msg.reqid = getpid();
  msg.varlist.len = 1;
  msg.varlist.elem[0].val.type = type;
  switch(type)
    {
    case INT: 
      bcopy(val, &(msg.varlist.elem[0].val.value.intgr), sizeof(int));
      break;
    case STR:
      msg.varlist.elem[0].val.value.str.str = val;
      msg.varlist.elem[0].val.value.str.len = strlen(val);
      break;
    default:
      return (ERROR); /* too lazy */
    }

  reqid = REQ_ANY;

  /*
   * save env
   */

  if(setjmp(env))
    {
      if(retries < RETRIES) 
	retries++;       
      else         
	return(ERROR);
    }


  /*
   * send message
   */

  if((rc = snmpsend(SET, msg.reqid, &addr, "snmp", &msg, community, 
		    strlen(community), TIMEOUT)) < 0)
    {
      printf("error = %d\n", rc);
      return(ERROR);
    }

  /*
   * prepare timeout
   */

  bzero(&interval, sizeof(interval));
  bzero(&disable,  sizeof(disable));
  interval.it_value.tv_sec = TIMEOUT; 

  signal(SIGALRM, onintr);
  setitimer(ITIMER_REAL, &interval, (struct itimerval *) NULL);

  /*
   * get reply
   */

  if((rc = snmprecv(&from, (char *) NULL, &reqid, s_id, &sid_len, &reply,
		    sizeof(getreq))) < 0)
    {
      printf("error = %d\n", rc);
      return(ERROR);
    }

  setitimer(ITIMER_REAL, &disable, (struct itimerval *) NULL);
  return(SUCCESS);
}



static caddr_t *
adjust_variables(r, o)
     objident **o;
     caddr_t  *r;
{
  static caddr_t ret[SNMPMAXVARS];
  int i, j;

  bzero(ret, sizeof(ret));
  for(i = 0, j = 0; o[i] != (objident *) NULL; i++)
    if(o[i] != &skip)
      ret[i] = r[j++];
    else
      ret[i] = (caddr_t) &zero;

  return(ret);
}




static int
onintr()
{
  longjmp(env, 1);
}
