/*
 * Port. This file contains snmp querying routines.
 *
 * Copyright 1992 by the Massachusetts Institute of Technology.
 *
 * For copying and distribution information, please see the file
 * <mit-copyright.h>.
 *
 * Tom Coppeto
 * MIT Network Operations
 * 26 January 1992
 *
 *    $Source: /afs/.net/tools/src/port/RCS/snmpquery.c,v $
 *    $Author: tom $
 *    $Locker: tom $
 */

#ifndef lint
static char *rcsid = "$Header: /afs/.net/tools/src/port/RCS/snmpquery.c,v 1.3 1992/02/04 19:20:36 tom Exp tom $";
#endif

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


getreq reply;
static jmp_buf env;
static int retries;

static caddr_t *adjust_variables();
static void onintr();
static caddr_t *snmp_query();
static void (*previous_signal)();

static struct  itimerval disable;
static void cancel();

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(agent, obj)
     Agent *agent;
     objident *obj;
{
  caddr_t *v;
  getreq msg;
  int index;

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



caddr_t *
make_lots_o_snmp_queries(agent, obj)
     Agent *agent;
     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));
	      ++j;
	    }
	  ++i;
	}
      if(j == 0)
	return((caddr_t *) NULL);
      msg.reqid = getpid();
      msg.varlist.len = j;
      if(!(v = snmp_query(agent, &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(agent, obj)
     Agent *agent;
     objident *obj;
{
  caddr_t *v;
  getreq msg;
  int index;

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


caddr_t *
make_lots_o_snmp_nexts(agent, obj)
     Agent *agent;
     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));
      ++obj;
      ++i;
    }
  msg.reqid = getpid();
  msg.varlist.len = i;
  if((v = snmp_query(agent, &msg, NXT, &index)) == (caddr_t *) NULL)
    return(v);
  if(!*v)
    return((caddr_t *) NULL);
  else
    return(v);
}


static int reqid = 0;

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

  retries = 0;
  bzero(&reply, sizeof(reply));
  bzero(val, sizeof(val));
  msg->reqid = ++reqid;

  /*
   * save env
   */

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

  /*
   * send request
   */

  for(i = 0; i < the_burst; i++)
    if((rc = snmpsend(req, reqid, &(agent->addr), "snmp", msg, 
		      the_community, strlen(the_community), 
		      the_timeout)) < 0)
      {
	printf("error = %d\n", rc);
	return(val);
      }
    
  /*
   * prepare timeout
   */

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

  signal(SIGALRM, onintr);
  previous_signal = (void *) signal(SIGINT, cancel);
  setitimer(ITIMER_REAL, &interval, (struct itimerval *) NULL);

  /*
   * get reply
   */

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

  setitimer(ITIMER_REAL, &disable, (struct itimerval *) NULL);
  signal(SIGINT, previous_signal);

  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(agent, obj, val, type, noblock)
     Agent *agent;
     objident *obj;
     caddr_t val;
     int type;
     int noblock;
{
  getreq msg;
  char *s_id = (char *) NULL;
  struct sockaddr_in from;
  int sid_len = 0;
  int rc;
  struct itimerval interval;            
  int i;

  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 */
    }

  msg.reqid = ++reqid;

  /*
   * save env
   */

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


  /*
   * send message
   */


  for(i = 0; i < the_burst; ++i)
    if((rc = snmpsend(SET, reqid, &(agent->addr), "snmp", &msg, 
		      the_community, strlen(the_community), the_timeout)) < 0)
      {
	printf("error = %d\n", rc);
	return(ERROR);
      }


  if(noblock)
    return(SUCCESS);

  /*
   * prepare timeout
   */

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

  signal(SIGALRM, onintr);
  previous_signal = (void *) signal(SIGINT, cancel);
  setitimer(ITIMER_REAL, &interval, (struct itimerval *) NULL);

  /*
   * get reply
   */

  if((rc = snmprecv(&from, (char *) NULL, &reqid, s_id, &sid_len, &reply,
		    sizeof(getreq))) < 0)
    {
      signal(SIGINT, previous_signal);
      printf("error = %d\n", rc);
      return(ERROR);
    }
  setitimer(ITIMER_REAL, &disable, (struct itimerval *) NULL);
  signal(SIGINT, previous_signal);
  return(SUCCESS);
}


static void
cancel()
{
  setitimer(ITIMER_REAL, &disable, (struct itimerval *) NULL);
  signal(SIGINT, previous_signal);
  return;
}


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 void
onintr()
{
  signal(SIGINT, previous_signal);
  longjmp(env, 1);
  return;
}
