Blame snmplib/snmp_alarm.c

Packit fcad23
/*
Packit fcad23
 * snmp_alarm.c:
Packit fcad23
 */
Packit fcad23
/* Portions of this file are subject to the following copyright(s).  See
Packit fcad23
 * the Net-SNMP's COPYING file for more details and other copyrights
Packit fcad23
 * that may apply:
Packit fcad23
 */
Packit fcad23
/*
Packit fcad23
 * Portions of this file are copyrighted by:
Packit fcad23
 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
Packit fcad23
 * Use is subject to license terms specified in the COPYING file
Packit fcad23
 * distributed with the Net-SNMP package.
Packit fcad23
 */
Packit fcad23
/** @defgroup snmp_alarm  generic library based alarm timers for various parts of an application 
Packit fcad23
 *  @ingroup library
Packit fcad23
 * 
Packit fcad23
 *  @{
Packit fcad23
 */
Packit fcad23
#include <net-snmp/net-snmp-config.h>
Packit fcad23
Packit fcad23
#if HAVE_UNISTD_H
Packit fcad23
#include <unistd.h>
Packit fcad23
#endif
Packit fcad23
#include <signal.h>
Packit fcad23
#if HAVE_STDLIB_H
Packit fcad23
#include <stdlib.h>
Packit fcad23
#endif
Packit fcad23
#include <sys/types.h>
Packit fcad23
#if HAVE_NETINET_IN_H
Packit fcad23
#include <netinet/in.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_STRING_H
Packit fcad23
#include <string.h>
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#if TIME_WITH_SYS_TIME
Packit fcad23
# include <sys/time.h>
Packit fcad23
# include <time.h>
Packit fcad23
#else
Packit fcad23
# if HAVE_SYS_TIME_H
Packit fcad23
#  include <sys/time.h>
Packit fcad23
# else
Packit fcad23
#  include <time.h>
Packit fcad23
# endif
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#if HAVE_DMALLOC_H
Packit fcad23
#include <dmalloc.h>
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#include <net-snmp/types.h>
Packit fcad23
#include <net-snmp/output_api.h>
Packit fcad23
#include <net-snmp/config_api.h>
Packit fcad23
#include <net-snmp/utilities.h>
Packit fcad23
Packit fcad23
#include <net-snmp/library/snmp_api.h>
Packit fcad23
#include <net-snmp/library/callback.h>
Packit fcad23
#include <net-snmp/library/snmp_alarm.h>
Packit fcad23
Packit fcad23
static struct snmp_alarm *thealarms = NULL;
Packit fcad23
static int      start_alarms = 0;
Packit fcad23
static unsigned int regnum = 1;
Packit fcad23
Packit fcad23
int
Packit fcad23
init_alarm_post_config(int majorid, int minorid, void *serverarg,
Packit fcad23
                       void *clientarg)
Packit fcad23
{
Packit fcad23
    start_alarms = 1;
Packit fcad23
    set_an_alarm();
Packit fcad23
    return SNMPERR_SUCCESS;
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
init_snmp_alarm(void)
Packit fcad23
{
Packit fcad23
    start_alarms = 0;
Packit fcad23
    snmp_register_callback(SNMP_CALLBACK_LIBRARY,
Packit fcad23
                           SNMP_CALLBACK_POST_READ_CONFIG,
Packit fcad23
                           init_alarm_post_config, NULL);
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
sa_update_entry(struct snmp_alarm *a)
Packit fcad23
{
Packit fcad23
    if (!timerisset(&a->t_lastM)) {
Packit fcad23
        /*
Packit fcad23
         * First call of sa_update_entry() for alarm a: set t_lastM and t_nextM.
Packit fcad23
         */
Packit fcad23
        netsnmp_get_monotonic_clock(&a->t_lastM);
Packit fcad23
        NETSNMP_TIMERADD(&a->t_lastM, &a->t, &a->t_nextM);
Packit fcad23
    } else if (!timerisset(&a->t_nextM)) {
Packit fcad23
        /*
Packit fcad23
         * We've been called but not reset for the next call.  
Packit fcad23
         */
Packit fcad23
        if (a->flags & SA_REPEAT) {
Packit fcad23
            if (timerisset(&a->t)) {
Packit fcad23
                NETSNMP_TIMERADD(&a->t_lastM, &a->t, &a->t_nextM);
Packit fcad23
            } else {
Packit fcad23
                DEBUGMSGTL(("snmp_alarm",
Packit fcad23
                            "update_entry: illegal interval specified\n"));
Packit fcad23
                snmp_alarm_unregister(a->clientreg);
Packit fcad23
            }
Packit fcad23
        } else {
Packit fcad23
            /*
Packit fcad23
             * Single time call, remove it.  
Packit fcad23
             */
Packit fcad23
            snmp_alarm_unregister(a->clientreg);
Packit fcad23
        }
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
/**
Packit fcad23
 * This function removes the callback function from a list of registered
Packit fcad23
 * alarms, unregistering the alarm.
Packit fcad23
 *
Packit fcad23
 * @param clientreg is a unique unsigned integer representing a registered
Packit fcad23
 *	alarm which the client wants to unregister.
Packit fcad23
 *
Packit fcad23
 * @return void
Packit fcad23
 *
Packit fcad23
 * @see snmp_alarm_register
Packit fcad23
 * @see snmp_alarm_register_hr
Packit fcad23
 * @see snmp_alarm_unregister_all
Packit fcad23
 */
Packit fcad23
void
Packit fcad23
snmp_alarm_unregister(unsigned int clientreg)
Packit fcad23
{
Packit fcad23
    struct snmp_alarm *sa_ptr, **prevNext = &thealarms;
Packit fcad23
Packit fcad23
    for (sa_ptr = thealarms;
Packit fcad23
         sa_ptr != NULL && sa_ptr->clientreg != clientreg;
Packit fcad23
         sa_ptr = sa_ptr->next) {
Packit fcad23
        prevNext = &(sa_ptr->next);
Packit fcad23
    }
Packit fcad23
Packit fcad23
    if (sa_ptr != NULL) {
Packit fcad23
        *prevNext = sa_ptr->next;
Packit fcad23
        DEBUGMSGTL(("snmp_alarm", "unregistered alarm %d\n", 
Packit fcad23
		    sa_ptr->clientreg));
Packit fcad23
        /*
Packit fcad23
         * Note: do not free the clientarg, it's the client's responsibility 
Packit fcad23
         */
Packit fcad23
        free(sa_ptr);
Packit fcad23
    } else {
Packit fcad23
        DEBUGMSGTL(("snmp_alarm", "no alarm %d to unregister\n", clientreg));
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
/**
Packit fcad23
 * This function unregisters all alarms currently stored.
Packit fcad23
 *
Packit fcad23
 * @return void
Packit fcad23
 *
Packit fcad23
 * @see snmp_alarm_register
Packit fcad23
 * @see snmp_alarm_register_hr
Packit fcad23
 * @see snmp_alarm_unregister
Packit fcad23
 */
Packit fcad23
void
Packit fcad23
snmp_alarm_unregister_all(void)
Packit fcad23
{
Packit fcad23
  struct snmp_alarm *sa_ptr, *sa_tmp;
Packit fcad23
Packit fcad23
  for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_tmp) {
Packit fcad23
    sa_tmp = sa_ptr->next;
Packit fcad23
    free(sa_ptr);
Packit fcad23
  }
Packit fcad23
  DEBUGMSGTL(("snmp_alarm", "ALL alarms unregistered\n"));
Packit fcad23
  thealarms = NULL;
Packit fcad23
}  
Packit fcad23
Packit fcad23
struct snmp_alarm *
Packit fcad23
sa_find_next(void)
Packit fcad23
{
Packit fcad23
    struct snmp_alarm *a, *lowest = NULL;
Packit fcad23
Packit fcad23
    for (a = thealarms; a != NULL; a = a->next)
Packit fcad23
        if (!(a->flags & SA_FIRED)
Packit fcad23
            && (lowest == NULL || timercmp(&a->t_nextM, &lowest->t_nextM, <)))
Packit fcad23
            lowest = a;
Packit fcad23
Packit fcad23
    return lowest;
Packit fcad23
}
Packit fcad23
Packit fcad23
NETSNMP_IMPORT struct snmp_alarm *sa_find_specific(unsigned int clientreg);
Packit fcad23
struct snmp_alarm *
Packit fcad23
sa_find_specific(unsigned int clientreg)
Packit fcad23
{
Packit fcad23
    struct snmp_alarm *sa_ptr;
Packit fcad23
    for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_ptr->next) {
Packit fcad23
        if (sa_ptr->clientreg == clientreg) {
Packit fcad23
            return sa_ptr;
Packit fcad23
        }
Packit fcad23
    }
Packit fcad23
    return NULL;
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
run_alarms(void)
Packit fcad23
{
Packit fcad23
    struct snmp_alarm *a;
Packit fcad23
    unsigned int    clientreg;
Packit fcad23
    struct timeval  t_now;
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * Loop through everything we have repeatedly looking for the next thing to
Packit fcad23
     * call until all events are finally in the future again.  
Packit fcad23
     */
Packit fcad23
Packit fcad23
    while ((a = sa_find_next()) != NULL) {
Packit fcad23
        netsnmp_get_monotonic_clock(&t_now);
Packit fcad23
Packit fcad23
        if (timercmp(&a->t_nextM, &t_now, >))
Packit fcad23
            return;
Packit fcad23
Packit fcad23
        clientreg = a->clientreg;
Packit fcad23
        a->flags |= SA_FIRED;
Packit fcad23
        DEBUGMSGTL(("snmp_alarm", "run alarm %d\n", clientreg));
Packit fcad23
        (*(a->thecallback)) (clientreg, a->clientarg);
Packit fcad23
        DEBUGMSGTL(("snmp_alarm", "alarm %d completed\n", clientreg));
Packit fcad23
Packit fcad23
        a = sa_find_specific(clientreg);
Packit fcad23
        if (a) {
Packit fcad23
            a->t_lastM = t_now;
Packit fcad23
            timerclear(&a->t_nextM);
Packit fcad23
            a->flags &= ~SA_FIRED;
Packit fcad23
            sa_update_entry(a);
Packit fcad23
        } else {
Packit fcad23
            DEBUGMSGTL(("snmp_alarm", "alarm %d deleted itself\n",
Packit fcad23
                        clientreg));
Packit fcad23
        }
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
Packit fcad23
Packit fcad23
RETSIGTYPE
Packit fcad23
alarm_handler(int a)
Packit fcad23
{
Packit fcad23
    run_alarms();
Packit fcad23
    set_an_alarm();
Packit fcad23
}
Packit fcad23
Packit fcad23
Packit fcad23
Packit fcad23
/**
Packit fcad23
 * Look up the time at which the next alarm will fire.
Packit fcad23
 *
Packit fcad23
 * @param[out] alarm_tm Time at which the next alarm will fire.
Packit fcad23
 * @param[in] now Earliest time that should be written into *alarm_tm.
Packit fcad23
 *
Packit fcad23
 * @return Zero if no alarms are scheduled; non-zero 'clientreg' value
Packit fcad23
 *   identifying the first alarm that will fire if one or more alarms are
Packit fcad23
 *   scheduled.
Packit fcad23
 */
Packit fcad23
int
Packit fcad23
netsnmp_get_next_alarm_time(struct timeval *alarm_tm, const struct timeval *now)
Packit fcad23
{
Packit fcad23
    struct snmp_alarm *sa_ptr;
Packit fcad23
Packit fcad23
    sa_ptr = sa_find_next();
Packit fcad23
Packit fcad23
    if (sa_ptr) {
Packit fcad23
        netsnmp_assert(alarm_tm);
Packit fcad23
        netsnmp_assert(timerisset(&sa_ptr->t_nextM));
Packit fcad23
        if (timercmp(&sa_ptr->t_nextM, now, >))
Packit fcad23
            *alarm_tm = sa_ptr->t_nextM;
Packit fcad23
        else
Packit fcad23
            *alarm_tm = *now;
Packit fcad23
        return sa_ptr->clientreg;
Packit fcad23
    } else {
Packit fcad23
        return 0;
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
/**
Packit fcad23
 * Get the time until the next alarm will fire.
Packit fcad23
 *
Packit fcad23
 * @param[out] delta Time until the next alarm.
Packit fcad23
 *
Packit fcad23
 * @return Zero if no alarms are scheduled; non-zero 'clientreg' value
Packit fcad23
 *   identifying the first alarm that will fire if one or more alarms are
Packit fcad23
 *   scheduled.
Packit fcad23
 */
Packit fcad23
int
Packit fcad23
get_next_alarm_delay_time(struct timeval *delta)
Packit fcad23
{
Packit fcad23
    struct timeval t_now, alarm_tm;
Packit fcad23
    int res;
Packit fcad23
Packit fcad23
    netsnmp_get_monotonic_clock(&t_now);
Packit fcad23
    res = netsnmp_get_next_alarm_time(&alarm_tm, &t_now);
Packit fcad23
    if (res)
Packit fcad23
        NETSNMP_TIMERSUB(&alarm_tm, &t_now, delta);
Packit fcad23
    return res;
Packit fcad23
}
Packit fcad23
Packit fcad23
Packit fcad23
void
Packit fcad23
set_an_alarm(void)
Packit fcad23
{
Packit fcad23
    struct timeval  delta;
Packit fcad23
    int             nextalarm = get_next_alarm_delay_time(&delta);
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * We don't use signals if they asked us nicely not to.  It's expected
Packit fcad23
     * they'll check the next alarm time and do their own calling of
Packit fcad23
     * run_alarms().  
Packit fcad23
     */
Packit fcad23
Packit fcad23
    if (nextalarm && !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
Packit fcad23
					NETSNMP_DS_LIB_ALARM_DONT_USE_SIG)) {
Packit fcad23
#ifndef WIN32
Packit fcad23
# ifdef HAVE_SETITIMER
Packit fcad23
        struct itimerval it;
Packit fcad23
Packit fcad23
        it.it_value = delta;
Packit fcad23
        timerclear(&it.it_interval);
Packit fcad23
Packit fcad23
        signal(SIGALRM, alarm_handler);
Packit fcad23
        setitimer(ITIMER_REAL, &it, NULL);
Packit fcad23
        DEBUGMSGTL(("snmp_alarm", "schedule alarm %d in %ld.%03ld seconds\n",
Packit fcad23
                    nextalarm, (long) delta.tv_sec, (long)(delta.tv_usec / 1000)));
Packit fcad23
# else  /* HAVE_SETITIMER */
Packit fcad23
#  ifdef SIGALRM
Packit fcad23
        signal(SIGALRM, alarm_handler);
Packit fcad23
        alarm(delta.tv_sec);
Packit fcad23
        DEBUGMSGTL(("snmp_alarm",
Packit fcad23
                    "schedule alarm %d in roughly %ld seconds\n", nextalarm,
Packit fcad23
                    delta.tv_sec));
Packit fcad23
#  endif  /* SIGALRM */
Packit fcad23
# endif  /* HAVE_SETITIMER */
Packit fcad23
#endif  /* WIN32 */
Packit fcad23
Packit fcad23
    } else {
Packit fcad23
        DEBUGMSGTL(("snmp_alarm", "no alarms found to schedule\n"));
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
Packit fcad23
/**
Packit fcad23
 * This function registers function callbacks to occur at a specific time
Packit fcad23
 * in the future.
Packit fcad23
 *
Packit fcad23
 * @param when is an unsigned integer specifying when the callback function
Packit fcad23
 *             will be called in seconds.
Packit fcad23
 *
Packit fcad23
 * @param flags is an unsigned integer that specifies how frequent the callback
Packit fcad23
 *	function is called in seconds.  Should be SA_REPEAT or 0.  If  
Packit fcad23
 *	flags  is  set with SA_REPEAT, then the registered callback function
Packit fcad23
 *	will be called every SA_REPEAT seconds.  If flags is 0 then the 
Packit fcad23
 *	function will only be called once and then removed from the 
Packit fcad23
 *	registered alarm list.
Packit fcad23
 *
Packit fcad23
 * @param thecallback is a pointer SNMPAlarmCallback which is the callback 
Packit fcad23
 *	function being stored and registered.
Packit fcad23
 *
Packit fcad23
 * @param clientarg is a void pointer used by the callback function.  This 
Packit fcad23
 *	pointer is assigned to snmp_alarm->clientarg and passed into the
Packit fcad23
 *	callback function for the client's specific needs.
Packit fcad23
 *
Packit fcad23
 * @return Returns a unique unsigned integer(which is also passed as the first 
Packit fcad23
 *	argument of each callback), which can then be used to remove the
Packit fcad23
 *	callback from the list at a later point in the future using the
Packit fcad23
 *	snmp_alarm_unregister() function.  If memory could not be allocated
Packit fcad23
 *	for the snmp_alarm struct 0 is returned.
Packit fcad23
 *
Packit fcad23
 * @see snmp_alarm_unregister
Packit fcad23
 * @see snmp_alarm_register_hr
Packit fcad23
 * @see snmp_alarm_unregister_all
Packit fcad23
 */
Packit fcad23
unsigned int
Packit fcad23
snmp_alarm_register(unsigned int when, unsigned int flags,
Packit fcad23
                    SNMPAlarmCallback * thecallback, void *clientarg)
Packit fcad23
{
Packit fcad23
    struct timeval  t;
Packit fcad23
Packit fcad23
    if (0 == when) {
Packit fcad23
        t.tv_sec = 0;
Packit fcad23
        t.tv_usec = 1;
Packit fcad23
    } else {
Packit fcad23
        t.tv_sec = when;
Packit fcad23
        t.tv_usec = 0;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    return snmp_alarm_register_hr(t, flags, thecallback, clientarg);
Packit fcad23
}
Packit fcad23
Packit fcad23
Packit fcad23
/**
Packit fcad23
 * This function offers finer granularity as to when the callback 
Packit fcad23
 * function is called by making use of t->tv_usec value forming the 
Packit fcad23
 * "when" aspect of snmp_alarm_register().
Packit fcad23
 *
Packit fcad23
 * @param t is a timeval structure used to specify when the callback 
Packit fcad23
 *	function(alarm) will be called.  Adds the ability to specify
Packit fcad23
 *	microseconds.  t.tv_sec and t.tv_usec are assigned
Packit fcad23
 *	to snmp_alarm->tv_sec and snmp_alarm->tv_usec respectively internally.
Packit fcad23
 *	The snmp_alarm_register function only assigns seconds(it's when 
Packit fcad23
 *	argument).
Packit fcad23
 *
Packit fcad23
 * @param flags is an unsigned integer that specifies how frequent the callback
Packit fcad23
 *	function is called in seconds.  Should be SA_REPEAT or NULL.  If  
Packit fcad23
 *	flags  is  set with SA_REPEAT, then the registered callback function
Packit fcad23
 *	will be called every SA_REPEAT seconds.  If flags is NULL then the 
Packit fcad23
 *	function will only be called once and then removed from the 
Packit fcad23
 *	registered alarm list.
Packit fcad23
 *
Packit fcad23
 * @param cb is a pointer SNMPAlarmCallback which is the callback 
Packit fcad23
 *	function being stored and registered.
Packit fcad23
 *
Packit fcad23
 * @param cd is a void pointer used by the callback function.  This 
Packit fcad23
 *	pointer is assigned to snmp_alarm->clientarg and passed into the
Packit fcad23
 *	callback function for the client's specific needs.
Packit fcad23
 *
Packit fcad23
 * @return Returns a unique unsigned integer(which is also passed as the first 
Packit fcad23
 *	argument of each callback), which can then be used to remove the
Packit fcad23
 *	callback from the list at a later point in the future using the
Packit fcad23
 *	snmp_alarm_unregister() function.  If memory could not be allocated
Packit fcad23
 *	for the snmp_alarm struct 0 is returned.
Packit fcad23
 *
Packit fcad23
 * @see snmp_alarm_register
Packit fcad23
 * @see snmp_alarm_unregister
Packit fcad23
 * @see snmp_alarm_unregister_all
Packit fcad23
 */
Packit fcad23
unsigned int
Packit fcad23
snmp_alarm_register_hr(struct timeval t, unsigned int flags,
Packit fcad23
                       SNMPAlarmCallback * cb, void *cd)
Packit fcad23
{
Packit fcad23
    struct snmp_alarm **s = NULL;
Packit fcad23
Packit fcad23
    for (s = &(thealarms); *s != NULL; s = &((*s)->next));
Packit fcad23
Packit fcad23
    *s = SNMP_MALLOC_STRUCT(snmp_alarm);
Packit fcad23
    if (*s == NULL) {
Packit fcad23
        return 0;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    (*s)->t = t;
Packit fcad23
    (*s)->flags = flags;
Packit fcad23
    (*s)->clientarg = cd;
Packit fcad23
    (*s)->thecallback = cb;
Packit fcad23
    (*s)->clientreg = regnum++;
Packit fcad23
    (*s)->next = NULL;
Packit fcad23
Packit fcad23
    sa_update_entry(*s);
Packit fcad23
Packit fcad23
    DEBUGMSGTL(("snmp_alarm",
Packit fcad23
                "registered alarm %d, t = %ld.%03ld, flags=0x%02x\n",
Packit fcad23
                (*s)->clientreg, (long) (*s)->t.tv_sec, (long)((*s)->t.tv_usec / 1000),
Packit fcad23
                (*s)->flags));
Packit fcad23
Packit fcad23
    if (start_alarms) {
Packit fcad23
        set_an_alarm();
Packit fcad23
    }
Packit fcad23
Packit fcad23
    return (*s)->clientreg;
Packit fcad23
}
Packit fcad23
Packit fcad23
/**
Packit fcad23
 * This function resets an existing alarm.
Packit fcad23
 *
Packit fcad23
 * @param clientreg is a unique unsigned integer representing a registered
Packit fcad23
 *	alarm which the client wants to unregister.
Packit fcad23
 *
Packit fcad23
 * @return 0 on success, -1 if the alarm was not found
Packit fcad23
 *
Packit fcad23
 * @see snmp_alarm_register
Packit fcad23
 * @see snmp_alarm_register_hr
Packit fcad23
 * @see snmp_alarm_unregister
Packit fcad23
 */
Packit fcad23
int
Packit fcad23
snmp_alarm_reset(unsigned int clientreg)
Packit fcad23
{
Packit fcad23
    struct snmp_alarm *a;
Packit fcad23
    struct timeval  t_now;
Packit fcad23
    if ((a = sa_find_specific(clientreg)) != NULL) {
Packit fcad23
        netsnmp_get_monotonic_clock(&t_now);
Packit fcad23
        a->t_lastM.tv_sec = t_now.tv_sec;
Packit fcad23
        a->t_lastM.tv_usec = t_now.tv_usec;
Packit fcad23
        a->t_nextM.tv_sec = 0;
Packit fcad23
        a->t_nextM.tv_usec = 0;
Packit fcad23
        NETSNMP_TIMERADD(&t_now, &a->t, &a->t_nextM);
Packit fcad23
        return 0;
Packit fcad23
    }
Packit fcad23
    DEBUGMSGTL(("snmp_alarm_reset", "alarm %d not found\n",
Packit fcad23
                clientreg));
Packit fcad23
    return -1;
Packit fcad23
}
Packit fcad23
/**  @} */