/*
 * GXSNMP -- An snmp mangament application
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <time.h>
#include <stdio.h>
#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#endif HAVE_GETOPT_LONG
#include <strings.h>
#include <signal.h>
#include <errno.h>
#include <malloc.h>
#include <sys/time.h>
#include <sys/socket.h>

#include "collector.h"
#include "g_snmp.h"

/* 
 * Compile time default setting. They are only used if no defaults are 
 * available in the configuration file.
 */

static char *def_community = "public";
static int def_domain = AF_INET;
static int def_port = 161;
static int def_retries = 10;
static int def_timeout = 5;
static int def_defer = 900;
static int def_interval = 900;

/*
 * Lists holding table and groups for configuration
 */

static GSList *tables = NULL;
static GSList *groups = NULL;

/* The 3 queues representing the 3 states a SNMP variable group can be in:
 * - w_queue: Currently waiting until a large enough PDU slot gets available.
 * - s_queue: Currently waiting for an answer from the SNMP agent.
 * - i_queue: Currently idle, waiting for the next time for a request.
 */

struct vector {
  char    * fqdname;
  char    * community;
  guint     interval;
  guint     timeout;
  guint     domain;
  guint     port;
  guint     retries;
  guint     defer;
  guint     type;
  GSList *  oids;
  GSList *  output;
};

static GSList *w_queue = NULL;
static GSList *i_queue = NULL;
static GSList *s_queue = NULL;

/* Global counter of active PDUs waiting for a reply */

static int pducnt = 0;

/*
 * Structs holding group and table configuration during build
 * of SNMP groups
 */

struct snmp_group {
  char   * name;
  GSList * oids;
  int      type;
};

struct snmp_table {
  char   * name;
  gulong * index;
  long     indexlen;
  GSList * oids;
  int      type;
};

struct oid {
  char   * name;
  char   * type;
  gulong * value;
  long     valuelen;
};

/*
 * Struct for fe_oid_scan
 */

struct cbdata {
  GSList ** oids;
  char    * type;
};

struct defs {
  char    * fqdname;
  char    * community;
  guint     interval;
  guint     timeout;
  guint     domain;
  guint     port;
  guint     retries;
  guint     defer;
};

static void 
fe_group_build(gpointer key, gpointer mvalue, gpointer user_data)
{
  struct defs   * defs;
  char          * group;
  struct slist  * val;
  struct slist  * value;
  GSList        * mygroups;
  struct snmp_group * sgroup;
  struct vector * vector;

  val   = (struct slist *) mvalue;
  defs  = (struct defs *) user_data;
  group = (char *) key;

  if (val->type != SLIST_LIST)
    g_error ("group has wrong type.");

  /* Check if a named group really exists */

  mygroups = groups;

  while (mygroups)
    {
      sgroup = (struct snmp_group *) (mygroups->data);

      if (!strcmp(sgroup->name, group))
        goto found;
      printf("No %s\n", sgroup->name);
      mygroups = mygroups->next;
    }
  g_error ("No such group %s\n", group);
  return;

found:
  /* Now add the vector to the wait queue */

  vector = (struct vector *) g_malloc(sizeof(struct vector));

  vector->fqdname   = defs->fqdname;

  vector->community = defs->community;
  vector->interval  = defs->interval;
  vector->timeout   = defs->timeout;
  vector->domain    = defs->domain;
  vector->port      = defs->port;
  vector->retries   = defs->retries;
  vector->defer     = defs->defer;
  vector->type      = sgroup->type;
  vector->oids      = sgroup->oids;

  if ((value = g_hash_table_lookup (val->value.list, "output")))
    {
      if (value->type != SLIST_LIST)
        g_error ("output wrong type");
      vector->output = value->value.list;
    }
  else vector->output = NULL;

  w_queue = g_slist_append(w_queue, vector);
  
}

static void
get_next(char *def, int *start, int *end)
{
  char *p, *q;

  if((p = index(def, ',')))
    *p++ = '\0';

  if((q = index(def, '-')))
    *q++ = '\0';

  *start = atoi(def);
  if (q)
    *end = atoi(q);
  else
    *end = *start; 

  *def = '\0';

  if (p)
    while ((*def++ = *p++));
};

static void 
fe_table_build(gpointer key, gpointer mvalue, gpointer user_data)
{
  struct defs   * defs;
  char          * table;
  char          * instances;
  struct slist  * val;
  struct slist  * value;
  GSList        * mytables;
  GSList        * output;
  struct snmp_table * stable;
  struct vector * vector;
  int             start, end;

  val   = (struct slist *) mvalue;
  defs  = (struct defs *) user_data;
  table = (char *) key;

  if (val->type != SLIST_LIST)
    g_error ("table has wrong type.");

  /* Check if a named group really exists */

  mytables = tables;

  while (mytables)
    {
      stable = (struct snmp_table *) (mytables->data);

      if (!strcmp(stable->name, table))
        goto found;
      printf("No %s\n", stable->name);
      mytables = mytables->next;
    }
  g_error ("No such table %s\n", table);
  return;

found:
  /* Now add the vector to the wait queue */

  if ((value = g_hash_table_lookup (val->value.list, "instances")))
    {
      if (value->type != SLIST_STRING)
        g_error ("instances wrong type");
      instances = g_strdup(value->value.string);
    }
  else g_error ("no instances defined for table.");

  if ((value = g_hash_table_lookup (val->value.list, "output")))
    {
      if (value->type != SLIST_LIST)
        g_error ("output wrong type");
      output = value->value.list;
    }
  else output = NULL;

  /* instances might be list of ranges like "1,3,5-9,11" */
  while(strcmp(instances,""))
    {
      get_next(instances, &start, &end);
      while (start <= end)
        {
          vector = (struct vector *) g_malloc(sizeof(struct vector));

          vector->fqdname   = defs->fqdname;
          vector->community = defs->community;
          vector->interval  = defs->interval;
          vector->timeout   = defs->timeout;
          vector->domain    = defs->domain;
          vector->port      = defs->port;
          vector->retries   = defs->retries;
          vector->defer     = defs->defer;
          vector->type      = stable->type;
          vector->output    = output;
/*          vector->oids      = add_instance(stable->oids, start); */
          printf("Adding instance %d\n", start);
          start++;
          w_queue = g_slist_append(w_queue, vector);
        }
    }
}

static void
host_setup(GHashTable *config, char *hostname)
{
  struct slist  * value;
  struct defs     defs;

  if ((value = g_hash_table_lookup (config, "name")))
    {
      if (value->type != SLIST_STRING)
        g_error ("Name %s wrong type", hostname);
      defs.fqdname = value->value.string;
    }
  else defs.fqdname = hostname;

  if ((value = g_hash_table_lookup (config, "community")))
    {
      if (value->type != SLIST_STRING)
        g_error ("Community %s wrong type", hostname);
      defs.community = value->value.string;
    }
  else defs.community = def_community;

  if ((value = g_hash_table_lookup (config, "interval")))
    {
      if (value->type != SLIST_NUM)
        g_error ("Interval %s wrong type", hostname);
      defs.interval = value->value.number;
    }
  else defs.interval = def_interval;

  if ((value = g_hash_table_lookup (config, "timeout")))
    {
      if (value->type != SLIST_NUM)
        g_error ("Timeout %s wrong type", hostname);
      defs.timeout = value->value.number;
    }
  else defs.timeout = def_timeout;

  if ((value = g_hash_table_lookup (config, "domain")))
    {
      if (value->type == SLIST_STRING)
	{
	  /* Translate symbolic transport domain name to numeric name */
	  /* This should somehow be supported in the Transport object */
#ifdef HAVE_INET
	  if (!strcmp (value->value.string, "TCP/IP"))
	    defs.domain = AF_INET;
	  else
#endif
#ifdef HAVE_IPX
	  if (!strcmp (value->value.string, "IPX"))
	    defs.domain = AF_IPX;
	  else
#endif
#ifdef HAVE_INET6
	  if (!strcmp (value->value.string, "IPv6"))
	    def_domain = AF_INET6;
	  else
#endif
	    g_warning ("No such domain - %s", value->value.string);
        }
      else if (value->type == SLIST_NUM)
        defs.domain = value->value.number;
      else 
        g_error ("Domain %s wrong type", hostname);
    }
  else defs.domain = def_domain;

  if ((value = g_hash_table_lookup (config, "port")))
    {
      if (value->type != SLIST_NUM)
        g_error ("Port %s wrong type", hostname);
      defs.port = value->value.number;
    }
  else defs.port = def_port;

  if ((value = g_hash_table_lookup (config, "retries")))
    {
      if (value->type != SLIST_NUM)
        g_error ("Retries %s wrong type", hostname);
      defs.retries = value->value.number;
    }
  else defs.retries = def_retries;

  if ((value = g_hash_table_lookup (config, "defer")))
    {
      if (value->type != SLIST_NUM)
        g_error ("Defer %s wrong type", hostname);
      defs.defer = value->value.number;
    }
  else defs.defer = def_defer;

  printf("Defining host %s community %s\n", defs.fqdname, defs.community);

  if ((value = g_hash_table_lookup (config, "group")))
    {
      if (value->type != SLIST_LIST)
        g_error ("Group %s wrong type", hostname);
      g_hash_table_foreach(value->value.list, fe_group_build, &defs);
    }
  if ((value = g_hash_table_lookup (config, "table")))
    {
      if (value->type != SLIST_LIST)
        g_error ("Table %s wrong type", hostname);
      g_hash_table_foreach(value->value.list, fe_table_build, &defs);
    }
}

static void
fe_oid_scan(gpointer key, gpointer mvalue, gpointer user_data)
{
  struct oid    * oid;
  struct slist  * value;
  struct slist  * val = (struct slist *) mvalue;
  struct cbdata * cbd = (struct cbdata *) user_data;
  int            i;
  char         * p;

  if (val->type != SLIST_LIST)
    return;

  if ((value = g_hash_table_lookup (val->value.list, "oid")))
    {
      if (value->type != SLIST_STRING)
        g_error ("Oid %s wrong type", (char *)key);
      
      oid = g_malloc(sizeof(struct oid));
      oid->name  = (char *) key;
      oid->value = g_malloc(1024 * sizeof(gulong));

#if 0
      read_objid(value->value.string, oid->value, &(oid->valuelen));
#else
      i = 0;
      p = strtok(value->value.string, ".");
      while ((i < 1024) && p)
        {
          oid->value[i++] = atoi(p);
          p = strtok(NULL, ".");
        }
      oid->valuelen = i;
#endif
      if ((value = g_hash_table_lookup (val->value.list, "type")))
        {
          if (value->type != SLIST_STRING)
            g_error ("Type %s wrong type", (char *)key);
          oid->type = value->value.string;
        }
      *(cbd->oids) = g_slist_append(*(cbd->oids), oid);
    }
}

static void
fe_initgroup(gpointer key, gpointer mvalue, gpointer user_data)
{
  struct slist      *group, *value;
  struct snmp_group *mygroup;
  struct cbdata     cbd;

  group = (struct slist *) mvalue;
  if (group->type != SLIST_LIST)
    g_error ("Illegal snmp group %s definition", (char *) key);
  
  mygroup = g_malloc(sizeof(struct snmp_group));
  mygroup->name = (char *) key;
  mygroup->oids = NULL;

  cbd.oids = &mygroup->oids;
  cbd.type = NULL;

  if ((value = g_hash_table_lookup (group->value.list, "type")))
    {
      if (value->type != SLIST_STRING)
        g_error ("Snmp group %s wrong type", (char *)key);
      cbd.type = value->value.string;
    }
  g_hash_table_foreach(group->value.list, fe_oid_scan, &cbd);

  groups = g_slist_append(groups, mygroup);
}

static void
fe_inittable(gpointer key, gpointer mvalue, gpointer user_data)
{
  struct slist *table, *value;
  struct snmp_table *mytable;
  struct cbdata     cbd;
  int            i;
  char         * p;

  table = (struct slist *) mvalue;
  if (table->type != SLIST_LIST)
    g_error ("Illegal snmp table %s definition", (char *) key);
  
  mytable = g_malloc(sizeof(struct snmp_table));
  mytable->name = (char *) key;
  mytable->oids = NULL;
  mytable->index = NULL;
  mytable->indexlen = 0;

  cbd.oids = &mytable->oids;
  cbd.type = NULL;

  if ((value = g_hash_table_lookup (table->value.list, "type")))
    {
      if (value->type != SLIST_STRING)
        g_error ("Snmp group %s wrong type", (char *)key);
      cbd.type = value->value.string;
    }

  g_hash_table_foreach(table->value.list, fe_oid_scan, &cbd);
  if ((value = g_hash_table_lookup (table->value.list, "index")))
    {
      if (value->type != SLIST_STRING)
        g_error ("Snmp table %s wrong index", (char *)key);
      mytable->index = g_malloc(1024 * sizeof(gulong));
#if 0
      read_objid(value->value.string, mytable->index, &(mytable->indexlen));
#else
      i = 0;
      p = strtok(value->value.string, ".");
      while ((i < 1024) && p)
        {
          mytable->index[i++] = atoi(p);
          p = strtok(NULL, ".");
        }
      mytable->indexlen = i;
#endif
    }
  tables = g_slist_append(tables, mytable);
}

void
snmp_init (GHashTable * mconfig)
{
  struct slist *snmp, *config, *value;

  snmp = g_hash_table_lookup (mconfig, "snmp");
  if (!snmp)
    return;
  if (snmp->type != SLIST_LIST)
    g_error ("Illegal snmp definition");

  if ((config = g_hash_table_lookup (snmp->value.list, "config")))
    {
      if (config->type != SLIST_LIST)
	g_error ("Illegal snmp config definition");

      if ((value = g_hash_table_lookup (config->value.list, "community")))
	{
	  if (value->type != SLIST_STRING)
	    g_error ("Illegal snmp config community definition");
	  def_community = value->value.string;
	}

      if ((value = g_hash_table_lookup (config->value.list, "domain")))
	{
	  if (value->type == SLIST_STRING)
	    {
	      /* Translate symbolic transport domain name to numeric name */
	      /* This should somehow be supported in the Transport object */
#ifdef HAVE_INET
	      if (!strcmp (value->value.string, "TCP/IP"))
		def_domain = AF_INET;
	      else
#endif
#ifdef HAVE_IPX
	      if (!strcmp (value->value.string, "IPX"))
		def_domain = AF_IPX;
	      else
#endif
#ifdef HAVE_INET6
	      if (!strcmp (value->value.string, "IPv6"))
		def_domain = AF_INET6;
	      else
#endif
		g_warning ("No such domain - %s", value->value.string);
	    }
	  else if (value->type == SLIST_NUM)
	    def_domain = value->value.number;
	  else
	    g_error ("Illegal snmp config domain definition");

	}

      if ((value = g_hash_table_lookup (config->value.list, "port")))
	{
	  if (value->type != SLIST_NUM)
	    g_error ("Illegal snmp config port definition");
	  def_port = value->value.number;
	}

      if ((value = g_hash_table_lookup (config->value.list, "retries")))
	{
	  if (value->type != SLIST_NUM)
	    g_error ("Illegal snmp config retries definition");
	  def_retries = value->value.number;
	}

      if ((value = g_hash_table_lookup (config->value.list, "timeout")))
	{
	  if (value->type != SLIST_NUM)
	    g_error ("Illegal snmp config timeout definition");
	  def_timeout = value->value.number;
	}

      if ((value = g_hash_table_lookup (config->value.list, "defer")))
	{
	  if (value->type != SLIST_NUM)
	    g_error ("Illegal snmp config defer definition");
	  def_defer = value->value.number;
	}

      if ((value = g_hash_table_lookup (config->value.list, "interval")))
	{
	  if (value->type != SLIST_NUM)
	    g_error ("Illegal snmp config interval definition");
	  def_interval = value->value.number;
	}
    }
  if (debug)
    fprintf (stderr, "Default values used by snmp module:\n"
	     "community:   %s\n"
	     "domain:      %d\n"
	     "port:        %d\n"
	     "retries:     %d\n"
	     "timeout:     %d\n"
	     "defer:       %d\n"
	     "interval:    %d\n",
	     def_community, def_domain, def_port, def_retries,
	     def_timeout, def_defer, def_interval);
  if ((config = g_hash_table_lookup (snmp->value.list, "table")))
    {
      if (config->type != SLIST_LIST)
	g_error ("Illegal snmp table definition");
      g_hash_table_foreach(config->value.list, fe_inittable, NULL);
    }
  if ((config = g_hash_table_lookup (snmp->value.list, "group")))
    {
      if (config->type != SLIST_LIST)
	g_error ("Illegal snmp group definition");
      g_hash_table_foreach(config->value.list, fe_initgroup, NULL);
    }
/* Register host specific setup with main */
  register_host_function("snmp", host_setup);
}

#if 0
/* Encode a request to the SNMP agent. Multiple variables can be queried. */

static void
insert_oid (gpointer data, gpointer lptr)
{
  SNMP_OBJECT *objs;
  struct _oid *myoid = (struct _oid *) data;
  GSList **olist = (GSList **) lptr;

  objs = g_malloc (sizeof (SNMP_OBJECT));

  objs->request = 0;
  memcpy (&objs->id, &myoid->name, myoid->name_length * sizeof (glong));
  objs->id_len = myoid->name_length;
  objs->type = SNMP_NULL;
  objs->syntax_len = 0;

  *olist = g_slist_append (*olist, objs);
}

static struct _pdu *
build_pdu (GSList * oids, struct _host *host)
{
  struct _pdu *mypdu;
  struct _oid *myoid;
  GSList *olist;
  SNMP_PDU spdu;
  SNMP_AUTH auth;
  gint len;
  guchar buffer[MAX_DGRAM_SIZE], *ptr;

  mypdu = (struct _pdu *) g_malloc (sizeof (struct _pdu));

  mypdu->oids = oids;
  mypdu->host = host;
  mypdu->retry_count = retry;
  mypdu->id = ++id;

  ptr = buffer;
  olist = NULL;

  g_slist_foreach (oids, insert_oid, &olist);

  spdu.request.type = SNMP_PDU_GET;
  spdu.request.id = mypdu->id;
  spdu.request.error_status = 0;
  spdu.request.error_index = 0;

  len = sizeof (buffer);

  myoid = oids->data;

  strcpy (auth.name, host->community);
  auth.nlen = strlen (host->community);
  auth.type = AUTH_COMMUNITY;
  g_snmp_encode (&ptr, &len, &spdu,
		 &auth,
		 SNMP_V1,
		 olist);

  g_slist_free (olist);
  mypdu->buffer = malloc (len);
  memcpy (mypdu->buffer, ptr, len);
  mypdu->length = len;

  return mypdu;
}

static void
set_vars (gpointer data, gpointer lptr)
{
  struct _pdu *mypdu = (struct _pdu *) lptr;
  SNMP_OBJECT *obj = (SNMP_OBJECT *) data;

  GSList *id = mypdu->oids;

  struct _oid *myoid;

  while (id)
    {
      myoid = id->data;
      id = g_slist_next (id);
      if (!memcmp (obj->id, myoid->name, myoid->name_length * sizeof (glong)))
	{
	  output (myoid, obj, mypdu->now);
	  if (myoid->duration)
	    myoid->time = myoid->duration + mypdu->now;
	  else
	    myoid->time = 0;
	  return;
	}
    }
}

void
insert_oids (GSList * oids)
{
  while (oids)
    {
      i_queue = g_slist_append (i_queue, oids->data);
      oids = g_slist_remove (oids, oids->data);
    }
}

static void
snmp_receive ()
{
  unsigned char buffer[MAX_DGRAM_SIZE];
  int adrsize, len, objlen, comsize, i, version;
  struct sockaddr_in address;
  GSList *objs;
  SNMP_PDU spdu;
  SNMP_AUTH auth;
  GSList *id;
  struct _pdu *mypdu;

  /*
   * need to init adrsize properly 
   */
  adrsize = sizeof (address);
  len = recvfrom (fd, buffer, sizeof (buffer), 0, (struct sockaddr *) &address,
		  &adrsize);
  /*
   * Need to check the return val of recvfrom
   */
  if (debug)
    g_print ("recvfrom len %d\n", len);

  g_snmp_decode (buffer, len, &spdu, &auth, &version,
		 &objs);

  if (debug)
    printf ("Response: reqid = %d, op=%d\n", spdu.request.id,
	    spdu.request.type);

  id = s_queue;

  while (id)
    {
      mypdu = id->data;
      if (mypdu->id == spdu.request.id)
	break;
      id = g_slist_next (id);
    }
  mypdu->now = time (NULL);
  if (!id)
    {
      if (debug)
	printf ("No corresponding request outstanding\n");
      g_slist_free (objs);
      return;
    }
  if (memcmp (auth.name, mypdu->host->community, auth.nlen))
    {
      if (debug)
	printf ("Wrong community\n");
      g_slist_free (objs);
      return;
    }
  /*
   * On my machine the &address and mypdu->host->address never matches
   * but by looking at the code it appears you just want to compare 
   * IP addresses.. The modifications I've made do just that.
   * Before I altered it I was getting the following
   * Strange Sender IP Address 130.205.65.21 130.205.65.21
   * hmm...
   */
  if (memcmp (&address.sin_addr, &mypdu->host->address.sin_addr,
	      sizeof (struct in_addr)))
    {
      if (debug)
	printf ("Strange Sender IP Address %s %s\n",
		inet_ntoa (address.sin_addr),
		inet_ntoa (mypdu->host->address.sin_addr));
      g_slist_free (objs);
      return;
    }
  if (debug)
    printf ("Success!\n");
  g_slist_foreach (objs, set_vars, mypdu);

  insert_oids (mypdu->oids);
  g_slist_free (objs);
  s_queue = g_slist_remove (s_queue, mypdu);
  return;
}

static
get (GSList * oid, struct _host *host)
{
  struct _pdu *mypdu;

  mypdu = build_pdu (oid, host);
  send_get (mypdu);
  s_queue = g_slist_append (s_queue, mypdu);
}

void
process_table ()
{
  int numfds;
  fd_set fdset;
  int count, block;
  struct timeval to, *tv;
  time_t now;
  GSList *tmplist;
  GSList *oidlist;
  struct _oid *myoid, *myoid2;
  struct _pdu *mypdu;

/* This is the main loop of this collector. In short words, do the following:
   - check for i_queue entries with expired time and move them to w_queue.
   - check if there are any entries in w_queue.
   - If yes, check if an SNMP slot if free (maxpdu)
   - If yes, build PDU and send it
   - If no, get time of nearest i_queue entry and prepare select with block=0
   - call select()
   - start all over again
 */

  i_queue = NULL;
  s_queue = NULL;
  fd = socket (AF_INET, SOCK_DGRAM, 0);
  while (1)
    {
      block = 1;
      now = time (NULL);

/* Search through all i_queue entries */

      tmplist = i_queue;
      while (tmplist)
	{
	  myoid = tmplist->data;
	  tmplist = g_slist_next (tmplist);

	  if (myoid->time)
	    {
	      if (myoid->time <= now)
		{
		  w_queue = g_slist_append (w_queue, myoid);
		  i_queue = g_slist_remove (i_queue, myoid);
		}
	      else
		{
		  if (block)
		    {
		      block = 0;
		      to.tv_sec = MAX (myoid->time - now, 0);
		    }
		  else
		    {
		      if (myoid->time - now < to.tv_sec)
			to.tv_sec = MAX (myoid->time - now, 0);
		    }
		}
	    }
	}

/* Search through all s_queue entries */

      tmplist = s_queue;
      while (tmplist)
	{
	  mypdu = tmplist->data;
	  tmplist = g_slist_next (tmplist);

	  if (mypdu->time <= now)
	    {
	      if (mypdu->retry_count)
		{
		  mypdu->retry_count--;
		  send_get (mypdu);
		}
	      else
		{
		  oidlist = mypdu->oids;
		  while (oidlist)
		    {
		      myoid = oidlist->data;
		      myoid->time = now + defer;
		      oidlist = g_slist_next (oidlist);
		    }
		  insert_oids (mypdu->oids);
		  s_queue = g_slist_remove (s_queue, mypdu);
		  g_free (mypdu);
		}
	    }
	  else
	    {
	      if (block)
		{
		  block = 0;
		  to.tv_sec = MAX (mypdu->time - now, 0);
		}
	      else
		{
		  if (mypdu->time - now < to.tv_sec)
		    to.tv_sec = MAX (mypdu->time - now, 0);
		}
	    }
	}

      while (w_queue && (pducnt < maxreq))
	{
	  oidlist = NULL;
	  myoid = w_queue->data;

	  oidlist = g_slist_append (oidlist, myoid);
	  w_queue = g_slist_remove (w_queue, myoid);
	  count = 1;

	  tmplist = w_queue;
	  while (tmplist && count < pdusize)
	    {
	      myoid2 = tmplist->data;
	      tmplist = g_slist_next (tmplist);
	      if (!strcmp (myoid2->host->name, myoid->host->name))
		{
		  w_queue = g_slist_remove (w_queue, myoid2);
		  oidlist = g_slist_append (oidlist, myoid2);
		  count++;
		}
	    }
	  if (debug)
	    printf ("Getting %d variables\n", count);
	  get (oidlist, myoid->host);
	}

      numfds = fd + 1;
      FD_ZERO (&fdset);
      FD_SET (fd, &fdset);
      to.tv_usec = 0;
      if (block)
	to.tv_sec = 10;

      if (debug)
	printf ("Select sleeps for %d seconds\n", to.tv_sec);
      count = select (numfds, &fdset, 0, 0, &to);
      if (debug)
	printf ("Select returned with count=%d\n", count);
      if (count > 0)
	{
	  snmp_receive ();
	}
      else
	switch (count)
	  {
	  case 0:
	    break;
	  case -1:
	    if (errno == EINTR)
	      {
		continue;
	      }
	    else
	      {
		exit (1);
	      }
	  default:
	    exit (2);
	  }
    }
}
#endif
