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