net-snmp 5.7
snmp_alarm.c
00001 /*
00002  * snmp_alarm.c:
00003  */
00004 /* Portions of this file are subject to the following copyright(s).  See
00005  * the Net-SNMP's COPYING file for more details and other copyrights
00006  * that may apply:
00007  */
00008 /*
00009  * Portions of this file are copyrighted by:
00010  * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
00011  * Use is subject to license terms specified in the COPYING file
00012  * distributed with the Net-SNMP package.
00013  */
00019 #include <net-snmp/net-snmp-config.h>
00020 
00021 #if HAVE_UNISTD_H
00022 #include <unistd.h>
00023 #endif
00024 #include <signal.h>
00025 #if HAVE_STDLIB_H
00026 #include <stdlib.h>
00027 #endif
00028 #include <sys/types.h>
00029 #if HAVE_NETINET_IN_H
00030 #include <netinet/in.h>
00031 #endif
00032 #if HAVE_STRING_H
00033 #include <string.h>
00034 #endif
00035 
00036 #if TIME_WITH_SYS_TIME
00037 # include <sys/time.h>
00038 # include <time.h>
00039 #else
00040 # if HAVE_SYS_TIME_H
00041 #  include <sys/time.h>
00042 # else
00043 #  include <time.h>
00044 # endif
00045 #endif
00046 
00047 #if HAVE_DMALLOC_H
00048 #include <dmalloc.h>
00049 #endif
00050 
00051 #include <net-snmp/types.h>
00052 #include <net-snmp/output_api.h>
00053 #include <net-snmp/config_api.h>
00054 #include <net-snmp/utilities.h>
00055 
00056 #include <net-snmp/library/snmp_api.h>
00057 #include <net-snmp/library/callback.h>
00058 #include <net-snmp/library/snmp_alarm.h>
00059 
00060 static struct snmp_alarm *thealarms = NULL;
00061 static int      start_alarms = 0;
00062 static unsigned int regnum = 1;
00063 
00064 int
00065 init_alarm_post_config(int majorid, int minorid, void *serverarg,
00066                        void *clientarg)
00067 {
00068     start_alarms = 1;
00069     set_an_alarm();
00070     return SNMPERR_SUCCESS;
00071 }
00072 
00073 void
00074 init_snmp_alarm(void)
00075 {
00076     start_alarms = 0;
00077     snmp_register_callback(SNMP_CALLBACK_LIBRARY,
00078                            SNMP_CALLBACK_POST_READ_CONFIG,
00079                            init_alarm_post_config, NULL);
00080 }
00081 
00082 void
00083 sa_update_entry(struct snmp_alarm *a)
00084 {
00085     if (a->t_last.tv_sec == 0 && a->t_last.tv_usec == 0) {
00086         struct timeval  t_now;
00087         /*
00088          * Never been called yet, call time `t' from now.  
00089          */
00090         gettimeofday(&t_now, NULL);
00091 
00092         a->t_last.tv_sec = t_now.tv_sec;
00093         a->t_last.tv_usec = t_now.tv_usec;
00094 
00095         NETSNMP_TIMERADD(&t_now, &a->t, &a->t_next);
00096     } else if (a->t_next.tv_sec == 0 && a->t_next.tv_usec == 0) {
00097         /*
00098          * We've been called but not reset for the next call.  
00099          */
00100         if (a->flags & SA_REPEAT) {
00101             if (a->t.tv_sec == 0 && a->t.tv_usec == 0) {
00102                 DEBUGMSGTL(("snmp_alarm",
00103                             "update_entry: illegal interval specified\n"));
00104                 snmp_alarm_unregister(a->clientreg);
00105                 return;
00106             }
00107 
00108             NETSNMP_TIMERADD(&a->t_last, &a->t, &a->t_next);
00109         } else {
00110             /*
00111              * Single time call, remove it.  
00112              */
00113             snmp_alarm_unregister(a->clientreg);
00114         }
00115     }
00116 }
00117 
00131 void
00132 snmp_alarm_unregister(unsigned int clientreg)
00133 {
00134     struct snmp_alarm *sa_ptr, **prevNext = &thealarms;
00135 
00136     for (sa_ptr = thealarms;
00137          sa_ptr != NULL && sa_ptr->clientreg != clientreg;
00138          sa_ptr = sa_ptr->next) {
00139         prevNext = &(sa_ptr->next);
00140     }
00141 
00142     if (sa_ptr != NULL) {
00143         *prevNext = sa_ptr->next;
00144         DEBUGMSGTL(("snmp_alarm", "unregistered alarm %d\n", 
00145                     sa_ptr->clientreg));
00146         /*
00147          * Note:  do not free the clientarg, its the clients responsibility 
00148          */
00149         free(sa_ptr);
00150     } else {
00151         DEBUGMSGTL(("snmp_alarm", "no alarm %d to unregister\n", clientreg));
00152     }
00153 }
00154 
00164 void
00165 snmp_alarm_unregister_all(void)
00166 {
00167   struct snmp_alarm *sa_ptr, *sa_tmp;
00168 
00169   for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_tmp) {
00170     sa_tmp = sa_ptr->next;
00171     free(sa_ptr);
00172   }
00173   DEBUGMSGTL(("snmp_alarm", "ALL alarms unregistered\n"));
00174   thealarms = NULL;
00175 }  
00176 
00177 struct snmp_alarm *
00178 sa_find_next(void)
00179 {
00180     struct snmp_alarm *a, *lowest = NULL;
00181     struct timeval  t_now;
00182 
00183     gettimeofday(&t_now, NULL);
00184 
00185     for (a = thealarms; a != NULL; a = a->next) {
00186         if (!(a->flags & SA_FIRED)) {
00187             /* check for time delta skew */
00188             if ((a->t_next.tv_sec - t_now.tv_sec) > a->t.tv_sec)
00189             {
00190                 DEBUGMSGTL(("time_skew", "Time delta too big (%ld seconds), should be %ld seconds - fixing\n",
00191                     (long)(a->t_next.tv_sec - t_now.tv_sec), (long)a->t.tv_sec));
00192                 a->t_next.tv_sec = t_now.tv_sec + a->t.tv_sec;
00193                 a->t_next.tv_usec = t_now.tv_usec + a->t.tv_usec;
00194            }
00195             if (lowest == NULL) {
00196                lowest = a;
00197             } else if (a->t_next.tv_sec == lowest->t_next.tv_sec) {
00198                 if (a->t_next.tv_usec < lowest->t_next.tv_usec) {
00199                     lowest = a;
00200                 }
00201             } else if (a->t_next.tv_sec < lowest->t_next.tv_sec) {
00202                lowest = a;
00203            }
00204        }
00205     }
00206     return lowest;
00207 }
00208 
00209 NETSNMP_IMPORT struct snmp_alarm *sa_find_specific(unsigned int clientreg);
00210 struct snmp_alarm *
00211 sa_find_specific(unsigned int clientreg)
00212 {
00213     struct snmp_alarm *sa_ptr;
00214     for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_ptr->next) {
00215         if (sa_ptr->clientreg == clientreg) {
00216             return sa_ptr;
00217         }
00218     }
00219     return NULL;
00220 }
00221 
00222 void
00223 run_alarms(void)
00224 {
00225     int             done = 0;
00226     struct snmp_alarm *a = NULL;
00227     unsigned int    clientreg;
00228     struct timeval  t_now;
00229 
00230     /*
00231      * Loop through everything we have repeatedly looking for the next thing to
00232      * call until all events are finally in the future again.  
00233      */
00234 
00235     while (!done) {
00236         if ((a = sa_find_next()) == NULL) {
00237             return;
00238         }
00239 
00240         gettimeofday(&t_now, NULL);
00241 
00242         if (timercmp(&a->t_next, &t_now, <)) {
00243             clientreg = a->clientreg;
00244             a->flags |= SA_FIRED;
00245             DEBUGMSGTL(("snmp_alarm", "run alarm %d\n", clientreg));
00246             (*(a->thecallback)) (clientreg, a->clientarg);
00247             DEBUGMSGTL(("snmp_alarm", "alarm %d completed\n", clientreg));
00248 
00249             if ((a = sa_find_specific(clientreg)) != NULL) {
00250                 a->t_last.tv_sec = t_now.tv_sec;
00251                 a->t_last.tv_usec = t_now.tv_usec;
00252                 a->t_next.tv_sec = 0;
00253                 a->t_next.tv_usec = 0;
00254                 a->flags &= ~SA_FIRED;
00255                 sa_update_entry(a);
00256             } else {
00257                 DEBUGMSGTL(("snmp_alarm", "alarm %d deleted itself\n",
00258                             clientreg));
00259             }
00260         } else {
00261             done = 1;
00262         }
00263     }
00264 }
00265 
00266 
00267 
00268 RETSIGTYPE
00269 alarm_handler(int a)
00270 {
00271     run_alarms();
00272     set_an_alarm();
00273 }
00274 
00275 
00276 
00277 int
00278 get_next_alarm_delay_time(struct timeval *delta)
00279 {
00280     struct snmp_alarm *sa_ptr;
00281     struct timeval  t_now;
00282 
00283     sa_ptr = sa_find_next();
00284 
00285     if (sa_ptr) {
00286         gettimeofday(&t_now, NULL);
00287 
00288         if (timercmp(&t_now, &sa_ptr->t_next, >)) {
00289             /*
00290              * Time has already passed.  Return the smallest possible amount of
00291              * time.  
00292              */
00293             delta->tv_sec = 0;
00294             delta->tv_usec = 1;
00295             return sa_ptr->clientreg;
00296         } else {
00297             /*
00298              * Time is still in the future.  
00299              */
00300             NETSNMP_TIMERSUB(&sa_ptr->t_next, &t_now, delta);
00301 
00302             return sa_ptr->clientreg;
00303         }
00304     }
00305 
00306     /*
00307      * Nothing Left.  
00308      */
00309     return 0;
00310 }
00311 
00312 
00313 void
00314 set_an_alarm(void)
00315 {
00316     struct timeval  delta;
00317     int             nextalarm = get_next_alarm_delay_time(&delta);
00318 
00319     /*
00320      * We don't use signals if they asked us nicely not to.  It's expected
00321      * they'll check the next alarm time and do their own calling of
00322      * run_alarms().  
00323      */
00324 
00325     if (nextalarm && !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
00326                                         NETSNMP_DS_LIB_ALARM_DONT_USE_SIG)) {
00327 #ifndef WIN32
00328 # ifdef HAVE_SETITIMER
00329         struct itimerval it;
00330 
00331         it.it_value.tv_sec = delta.tv_sec;
00332         it.it_value.tv_usec = delta.tv_usec;
00333         it.it_interval.tv_sec = 0;
00334         it.it_interval.tv_usec = 0;
00335 
00336         signal(SIGALRM, alarm_handler);
00337         setitimer(ITIMER_REAL, &it, NULL);
00338         DEBUGMSGTL(("snmp_alarm", "schedule alarm %d in %ld.%03ld seconds\n",
00339                     nextalarm, delta.tv_sec, (delta.tv_usec / 1000)));
00340 # else  /* HAVE_SETITIMER */
00341 #  ifdef SIGALRM
00342         signal(SIGALRM, alarm_handler);
00343         alarm(delta.tv_sec);
00344         DEBUGMSGTL(("snmp_alarm",
00345                     "schedule alarm %d in roughly %ld seconds\n", nextalarm,
00346                     delta.tv_sec));
00347 #  endif  /* SIGALRM */
00348 # endif  /* HAVE_SETITIMER */
00349 #endif  /* WIN32 */
00350 
00351     } else {
00352         DEBUGMSGTL(("snmp_alarm", "no alarms found to schedule\n"));
00353     }
00354 }
00355 
00356 
00388 unsigned int
00389 snmp_alarm_register(unsigned int when, unsigned int flags,
00390                     SNMPAlarmCallback * thecallback, void *clientarg)
00391 {
00392     struct timeval  t;
00393 
00394     if (0 == when) {
00395         t.tv_sec = 0;
00396         t.tv_usec = 1;
00397     } else {
00398         t.tv_sec = when;
00399         t.tv_usec = 0;
00400     }
00401 
00402     return snmp_alarm_register_hr(t, flags, thecallback, clientarg);
00403 }
00404 
00405 
00442 unsigned int
00443 snmp_alarm_register_hr(struct timeval t, unsigned int flags,
00444                        SNMPAlarmCallback * cb, void *cd)
00445 {
00446     struct snmp_alarm **s = NULL;
00447 
00448     for (s = &(thealarms); *s != NULL; s = &((*s)->next));
00449 
00450     *s = SNMP_MALLOC_STRUCT(snmp_alarm);
00451     if (*s == NULL) {
00452         return 0;
00453     }
00454 
00455     (*s)->t.tv_sec = t.tv_sec;
00456     (*s)->t.tv_usec = t.tv_usec;
00457     (*s)->flags = flags;
00458     (*s)->clientarg = cd;
00459     (*s)->thecallback = cb;
00460     (*s)->clientreg = regnum++;
00461     (*s)->next = NULL;
00462 
00463     sa_update_entry(*s);
00464 
00465     DEBUGMSGTL(("snmp_alarm",
00466                 "registered alarm %d, t = %ld.%03ld, flags=0x%02x\n",
00467                 (*s)->clientreg, (*s)->t.tv_sec, ((*s)->t.tv_usec / 1000),
00468                 (*s)->flags));
00469 
00470     if (start_alarms) {
00471         set_an_alarm();
00472     }
00473 
00474     return (*s)->clientreg;
00475 }