/*
**  GXSNMP -- An snmp management 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.
**
**  The host polling module
**
**  FIXME:  If you specify a variable in your list that is a subvariable
**          of another variable in the list, the program erroneously
**          returns 'not found' for the subvariable.
*/

#include <glib.h>

#include "main.h"
#include "config_file.h"
#include "event.h"
#include "poll.h"

/******************************************************************************
**
**  The following local data structure is used by the chain of foreach
**  processors, so that the inside poll processor receives all of the
**  data about the request.
**
******************************************************************************/

typedef struct _eventwork
{
  gchar       * hostid;		/* Host id from configuration file */
}
EVENTWORK;

/******************************************************************************
**
**  The actual event processor
**
**  This subroutine is called as a g_timeout event by the main loop for
**  each scheduled event.
**
**  Perform the desired operation, then schedule the next instance of the
**  event, and return to the main loop with a return code of FALSE to 
**  end the event.
**
******************************************************************************/

static gboolean
event_processor (gpointer user_data)
{
  EVENT       * event = user_data;
  CONFIG_NODE * node;
  gint          interval;
  GTimeVal      time;
  GTimeVal      timework;

  g_get_current_time (&time);
  g_print ("Event %s started for host %s ", event->eventid, event->hostid);
  g_print ("at %ld.%06ld\n", time.tv_sec, time.tv_usec);

/*
**  Perform the operation
*/

  poll_event (event);

/*
**  Schedule the next event
*/

  node = config_node_lookup (config, "host");
  if (!node)
    {
      g_print ("Cannot reschedule event %s for host %s because the "
	       "host table is gone!\n", event->eventid, event->hostid);
      return FALSE;
    }

  node = config_node_lookup (node, event->hostid);
  if (!node)
    {
      g_print ("Cannot reschedule event %s for host %s because the "
               "host entry has disappeared from the host table!\n",
		event->eventid, event->hostid);
      return FALSE;
    }

  node = config_node_lookup (node, "poll");
  if (!node)
    {
      g_print ("Cannot reschedule event %s for host %s because the "
               "poll table is gone from the host entry!\n", 
	   	event->eventid, event->hostid);
      return FALSE;
    }

  node = config_node_lookup (node, event->eventid);
  if (!node)
    {
      g_print ("Cannot reschedule event %s for host %s because the "
               "poll entry is gone from the poll table!\n",
		event->eventid, event->hostid);
      return FALSE;
    }

  node = config_node_lookup (node, "interval");
  if (!node)
    {
      g_print ("Cannot schedule request %s for host %s because "
               "no interval is specified in the poll request!\n",
		event->eventid, event->hostid);
      return FALSE;
    }

  interval = config_node_get_number (node);
  if (!interval)
    {
      g_print ("Cannot reschedule request %s for host %s because "
               "interval is either zero or invalid.\n",
                event->eventid, event->hostid);
      return FALSE;
    }

/*
**  Schedule the next timeout for this request.  The exact desired time
**  is maintained in the event block, and by carefully computing the 
**  exact amount of time for the timeout interval, we can effectively
**  avoid clock drift.  The timeouts don't fall right on the button, but
**  we don't lose any time over the long run.
*/

  event->time.tv_sec += interval;	    /* Set next exact event time */
  timework.tv_sec  = event->time.tv_sec;    /* make a working copy */
  timework.tv_usec = event->time.tv_usec;
  g_get_current_time (&time);		    /* Get the current time */
  if (time.tv_usec > timework.tv_usec)	    /* Adjust to avoid carry */
    {
      timework.tv_usec += 1000000;
      timework.tv_sec--;
    }
  timework.tv_usec -= time.tv_usec;	    /* Interval time = */
  timework.tv_sec  -= time.tv_sec;	    /* target time - current time */
  if (timework.tv_sec < 0)		    /* Don't want a negative */
    {					    /* time interval */
      timework.tv_sec = 0;
      timework.tv_usec = 0;
    }
  
  g_timeout_add (timework.tv_sec * 1000 +   /* Set the next timeout */
		 timework.tv_usec / 1000,
		 event_processor, 
		 event);
  return FALSE;				    /* Cancel this timeout */
}


/******************************************************************************
**
**  The host poll processor
**
**  This subroutine is called once (by the host scheduler, below), for 
**  each entry in each host's poll table.
**
**  The poll scheduler constructs an event block, then schedules an
**  immediate event timeout for the event.
**
******************************************************************************/

static void
poll_processor (gpointer key, gpointer value, gpointer user_data)
{
  gchar       * eventid     = key;
  EVENTWORK   * eventwork  = user_data;
  EVENT       * event;

  event          = g_malloc0 (sizeof (EVENT));
  event->hostid  = g_strdup (eventwork->hostid);
  event->eventid = g_strdup (eventid);
  g_get_current_time (&event->time);

  g_timeout_add (0, event_processor, event);
}

/******************************************************************************
**
**  The host scheduler
**
**  The host scheduler is called once (by the event scheduler, below) for
**  each host in the configuration file.
**
**  The host scheduler runs through the "poll" table for each host, and
**  calls the poll scheduler (above) for each entry in the poll table.
**
******************************************************************************/

static void
host_scheduler (gpointer key, gpointer value, gpointer user_data)
{
  gchar       * hostid     = key;
  CONFIG_NODE * host       = value;
  EVENTWORK   * eventwork  = user_data;
  CONFIG_NODE * node;

  eventwork->hostid = hostid;
  node = config_node_lookup (host, "poll");
  config_node_foreach (node, poll_processor, eventwork);
}

/******************************************************************************
**
**  The Event Scheduler.
**
**  The event scheduler runs through the configuration file, and calls the 
**  host processor (above) once for each host in the host table.
**
**  Exiting with a return code of FALSE causes the event scheduler to only
**  be run once on program startup.
**
******************************************************************************/

gboolean event_scheduler (gpointer unused)
{
  CONFIG_NODE * node;
  EVENTWORK     eventwork;

  node = config_node_lookup (config, "host");
  config_node_foreach (node, host_scheduler, &eventwork);
  return FALSE;				

}


