| /* snmp_alarm.c: generic library based alarm timers for various parts |
| of an application */ |
| |
| #include <net-snmp/net-snmp-config.h> |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include <signal.h> |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #include <sys/types.h> |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| |
| #if TIME_WITH_SYS_TIME |
| # ifdef WIN32 |
| # include <sys/timeb.h> |
| # else |
| # include <sys/time.h> |
| # endif |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| #if HAVE_WINSOCK_H |
| #include <winsock.h> |
| #endif |
| |
| #if HAVE_DMALLOC_H |
| #include <dmalloc.h> |
| #endif |
| |
| #include <net-snmp/asn1.h> |
| #include <net-snmp/snmp_api.h> |
| #include <net-snmp/snmp_debug.h> |
| #include <net-snmp/tools.h> |
| #include <net-snmp/default_store.h> |
| #include <net-snmp/callback.h> |
| #include <net-snmp/snmp_alarm.h> |
| |
| static struct snmp_alarm *thealarms; |
| 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 (a->t.tv_sec == 0 && a->t.tv_usec == 0) { |
| DEBUGMSGTL(("snmp_alarm", "update_entry: illegal interval specified\n")); |
| return; |
| } |
| |
| if (a->t_last.tv_sec == 0 && a->t_last.tv_usec == 0) { |
| struct timeval t_now; |
| /* Never been called yet, call time `t' from now. */ |
| gettimeofday(&t_now, NULL); |
| |
| a->t_last.tv_sec = t_now.tv_sec; |
| a->t_last.tv_usec = t_now.tv_usec; |
| |
| a->t_next.tv_sec = t_now.tv_sec + a->t.tv_sec; |
| a->t_next.tv_usec = t_now.tv_usec + a->t.tv_usec; |
| |
| while (a->t_next.tv_usec >= 1000000) { |
| a->t_next.tv_usec -= 1000000; |
| a->t_next.tv_sec += 1; |
| } |
| } else if (a->t_next.tv_sec == 0 && a->t_next.tv_usec == 0) { |
| /* We've been called but not reset for the next call. */ |
| if (a->flags & SA_REPEAT) { |
| a->t_next.tv_sec = a->t_last.tv_sec + a->t.tv_sec; |
| a->t_next.tv_usec = a->t_last.tv_usec + a->t.tv_usec; |
| |
| while (a->t_next.tv_usec >= 1000000) { |
| a->t_next.tv_usec -= 1000000; |
| a->t_next.tv_sec += 1; |
| } |
| } else { |
| /* Single time call, remove it. */ |
| snmp_alarm_unregister(a->clientreg); |
| } |
| } |
| } |
| |
| |
| |
| 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, its the clients responsibility */ |
| free(sa_ptr); |
| } else { |
| DEBUGMSGTL(("snmp_alarm", "no alarm %d to unregister\n", clientreg)); |
| } |
| } |
| |
| |
| |
| struct snmp_alarm * |
| sa_find_next(void) |
| { |
| struct snmp_alarm *a, *lowest = NULL; |
| |
| for(a = thealarms; a != NULL; a = a->next) { |
| if (lowest == NULL) { |
| lowest = a; |
| } else if (a->t_next.tv_sec == lowest->t_next.tv_sec) { |
| if (a->t_next.tv_usec < lowest->t_next.tv_usec) { |
| lowest = a; |
| } |
| } else if (a->t_next.tv_sec < lowest->t_next.tv_sec) { |
| lowest = a; |
| } |
| } |
| return lowest; |
| } |
| |
| |
| |
| 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) { |
| int done = 0; |
| struct snmp_alarm *a = NULL; |
| 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 (!done) { |
| if ((a = sa_find_next()) == NULL) { |
| return; |
| } |
| |
| gettimeofday(&t_now, NULL); |
| |
| if ((a->t_next.tv_sec < t_now.tv_sec) || |
| ((a->t_next.tv_sec == t_now.tv_sec) && |
| (a->t_next.tv_usec < t_now.tv_usec))) { |
| clientreg = a->clientreg; |
| DEBUGMSGTL(("snmp_alarm", "run alarm %d\n", clientreg)); |
| (*(a->thecallback))(clientreg, a->clientarg); |
| DEBUGMSGTL(("snmp_alarm", "alarm %d completed\n", clientreg)); |
| |
| if ((a = sa_find_specific(clientreg)) != NULL) { |
| a->t_last.tv_sec = t_now.tv_sec; |
| a->t_last.tv_usec = t_now.tv_usec; |
| a->t_next.tv_sec = 0; |
| a->t_next.tv_usec = 0; |
| sa_update_entry(a); |
| } else { |
| DEBUGMSGTL(("snmp_alarm", "alarm %d deleted itself\n", clientreg)); |
| } |
| } else { |
| done = 1; |
| } |
| } |
| } |
| |
| |
| |
| RETSIGTYPE |
| alarm_handler(int a) { |
| run_alarms(); |
| set_an_alarm(); |
| } |
| |
| |
| |
| int |
| get_next_alarm_delay_time(struct timeval *delta) |
| { |
| struct snmp_alarm *sa_ptr; |
| struct timeval t_diff, t_now; |
| |
| sa_ptr = sa_find_next(); |
| |
| if (sa_ptr) { |
| gettimeofday(&t_now, 0); |
| |
| if ((t_now.tv_sec > sa_ptr->t_next.tv_sec) || |
| ((t_now.tv_sec == sa_ptr->t_next.tv_sec) && |
| (t_now.tv_usec > sa_ptr->t_next.tv_usec))) { |
| /* Time has already passed. Return the smallest possible amount of |
| time. */ |
| delta->tv_sec = 0; |
| delta->tv_usec = 1; |
| return sa_ptr->clientreg; |
| } else { |
| /* Time is still in the future. */ |
| t_diff.tv_sec = sa_ptr->t_next.tv_sec - t_now.tv_sec; |
| t_diff.tv_usec = sa_ptr->t_next.tv_usec - t_now.tv_usec; |
| |
| while (t_diff.tv_usec < 0) { |
| t_diff.tv_sec -= 1; |
| t_diff.tv_usec += 1000000; |
| } |
| |
| delta->tv_sec = t_diff.tv_sec; |
| delta->tv_usec = t_diff.tv_usec; |
| return sa_ptr->clientreg; |
| } |
| } |
| |
| /* Nothing Left. */ |
| return 0; |
| } |
| |
| |
| 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 (!ds_get_boolean(DS_LIBRARY_ID, DS_LIB_ALARM_DONT_USE_SIG) && nextalarm) { |
| #ifndef WIN32 |
| # ifdef HAVE_SETITIMER |
| struct itimerval it; |
| |
| it.it_value.tv_sec = delta.tv_sec; |
| it.it_value.tv_usec = delta.tv_usec; |
| it.it_interval.tv_sec = 0; |
| it.it_interval.tv_usec = 0; |
| |
| signal(SIGALRM, alarm_handler); |
| setitimer(ITIMER_REAL, &it, NULL); |
| DEBUGMSGTL(("snmp_alarm", "schedule alarm %d in %d.%03d seconds\n", |
| nextalarm, delta.tv_sec, (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 %d seconds\n", |
| nextalarm, delta.tv_sec)); |
| # endif /* SIGALRM */ |
| # endif /* HAVE_SETITIMER */ |
| #endif /* WIN32 */ |
| |
| } else { |
| DEBUGMSGTL(("snmp_alarm", "no alarms found to schedule\n")); |
| } |
| } |
| |
| |
| |
| unsigned int |
| snmp_alarm_register(unsigned int when, unsigned int flags, |
| SNMPAlarmCallback *thecallback, void *clientarg) { |
| struct snmp_alarm **sa_pptr; |
| if (thealarms != NULL) { |
| for(sa_pptr = &thealarms; (*sa_pptr) != NULL; |
| sa_pptr = &((*sa_pptr)->next)); |
| } else { |
| sa_pptr = &thealarms; |
| } |
| |
| *sa_pptr = SNMP_MALLOC_STRUCT(snmp_alarm); |
| if (*sa_pptr == NULL) |
| return 0; |
| |
| (*sa_pptr)->t.tv_sec = when; |
| (*sa_pptr)->t.tv_usec = 0; |
| (*sa_pptr)->flags = flags; |
| (*sa_pptr)->clientarg = clientarg; |
| (*sa_pptr)->thecallback = thecallback; |
| (*sa_pptr)->clientreg = regnum++; |
| (*sa_pptr)->next = NULL; |
| sa_update_entry(*sa_pptr); |
| |
| DEBUGMSGTL(("snmp_alarm", "registered alarm %d, t = %d.%03d, flags=0x%02x\n", |
| (*sa_pptr)->clientreg, (*sa_pptr)->t.tv_sec, |
| ((*sa_pptr)->t.tv_usec / 1000), (*sa_pptr)->flags)); |
| |
| if (start_alarms) |
| set_an_alarm(); |
| return (*sa_pptr)->clientreg; |
| } |
| |
| |
| |
| 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.tv_sec = t.tv_sec; |
| (*s)->t.tv_usec = t.tv_usec; |
| (*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 = %d.%03d, flags=0x%02x\n", |
| (*s)->clientreg, (*s)->t.tv_sec, ((*s)->t.tv_usec / 1000), |
| (*s)->flags)); |
| |
| if (start_alarms) { |
| set_an_alarm(); |
| } |
| |
| return (*s)->clientreg; |
| } |