net-snmp  5.4.1
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 # ifdef WIN32
00038 #  include <sys/timeb.h>
00039 # else
00040 #  include <sys/time.h>
00041 # endif
00042 # include <time.h>
00043 #else
00044 # if HAVE_SYS_TIME_H
00045 #  include <sys/time.h>
00046 # else
00047 #  include <time.h>
00048 # endif
00049 #endif
00050 #if HAVE_WINSOCK_H
00051 #include <winsock.h>
00052 #endif
00053 
00054 #if HAVE_DMALLOC_H
00055 #include <dmalloc.h>
00056 #endif
00057 
00058 #include <net-snmp/types.h>
00059 #include <net-snmp/output_api.h>
00060 #include <net-snmp/config_api.h>
00061 #include <net-snmp/utilities.h>
00062 
00063 #include <net-snmp/library/snmp_api.h>
00064 #include <net-snmp/library/callback.h>
00065 #include <net-snmp/library/snmp_alarm.h>
00066 
00067 static struct snmp_alarm *thealarms = NULL;
00068 static int      start_alarms = 0;
00069 static unsigned int regnum = 1;
00070 
00071 int
00072 init_alarm_post_config(int majorid, int minorid, void *serverarg,
00073                        void *clientarg)
00074 {
00075     start_alarms = 1;
00076     set_an_alarm();
00077     return SNMPERR_SUCCESS;
00078 }
00079 
00080 void
00081 init_snmp_alarm(void)
00082 {
00083     start_alarms = 0;
00084     snmp_register_callback(SNMP_CALLBACK_LIBRARY,
00085                            SNMP_CALLBACK_POST_READ_CONFIG,
00086                            init_alarm_post_config, NULL);
00087 }
00088 
00089 void
00090 sa_update_entry(struct snmp_alarm *a)
00091 {
00092     if (a->t_last.tv_sec == 0 && a->t_last.tv_usec == 0) {
00093         struct timeval  t_now;
00094         /*
00095          * Never been called yet, call time `t' from now.  
00096          */
00097         gettimeofday(&t_now, NULL);
00098 
00099         a->t_last.tv_sec = t_now.tv_sec;
00100         a->t_last.tv_usec = t_now.tv_usec;
00101 
00102         a->t_next.tv_sec = t_now.tv_sec + a->t.tv_sec;
00103         a->t_next.tv_usec = t_now.tv_usec + a->t.tv_usec;
00104 
00105         while (a->t_next.tv_usec >= 1000000) {
00106             a->t_next.tv_usec -= 1000000;
00107             a->t_next.tv_sec += 1;
00108         }
00109     } else if (a->t_next.tv_sec == 0 && a->t_next.tv_usec == 0) {
00110         /*
00111          * We've been called but not reset for the next call.  
00112          */
00113         if (a->flags & SA_REPEAT) {
00114             if (a->t.tv_sec == 0 && a->t.tv_usec == 0) {
00115                 DEBUGMSGTL(("snmp_alarm",
00116                             "update_entry: illegal interval specified\n"));
00117                 snmp_alarm_unregister(a->clientreg);
00118                 return;
00119             }
00120 
00121             a->t_next.tv_sec = a->t_last.tv_sec + a->t.tv_sec;
00122             a->t_next.tv_usec = a->t_last.tv_usec + a->t.tv_usec;
00123 
00124             while (a->t_next.tv_usec >= 1000000) {
00125                 a->t_next.tv_usec -= 1000000;
00126                 a->t_next.tv_sec += 1;
00127             }
00128         } else {
00129             /*
00130              * Single time call, remove it.  
00131              */
00132             snmp_alarm_unregister(a->clientreg);
00133         }
00134     }
00135 }
00136 
00150 void
00151 snmp_alarm_unregister(unsigned int clientreg)
00152 {
00153     struct snmp_alarm *sa_ptr, **prevNext = &thealarms;
00154 
00155     for (sa_ptr = thealarms;
00156          sa_ptr != NULL && sa_ptr->clientreg != clientreg;
00157          sa_ptr = sa_ptr->next) {
00158         prevNext = &(sa_ptr->next);
00159     }
00160 
00161     if (sa_ptr != NULL) {
00162         *prevNext = sa_ptr->next;
00163         DEBUGMSGTL(("snmp_alarm", "unregistered alarm %d\n", 
00164                     sa_ptr->clientreg));
00165         /*
00166          * Note:  do not free the clientarg, its the clients responsibility 
00167          */
00168         free(sa_ptr);
00169     } else {
00170         DEBUGMSGTL(("snmp_alarm", "no alarm %d to unregister\n", clientreg));
00171     }
00172 }
00173 
00183 void
00184 snmp_alarm_unregister_all(void)
00185 {
00186   struct snmp_alarm *sa_ptr, *sa_tmp;
00187 
00188   for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_tmp) {
00189     sa_tmp = sa_ptr->next;
00190     free(sa_ptr);
00191   }
00192   DEBUGMSGTL(("snmp_alarm", "ALL alarms unregistered\n"));
00193   thealarms = NULL;
00194 }  
00195 
00196 struct snmp_alarm *
00197 sa_find_next(void)
00198 {
00199     struct snmp_alarm *a, *lowest = NULL;
00200 
00201     for (a = thealarms; a != NULL; a = a->next) {
00202         if (lowest == NULL) {
00203             lowest = a;
00204         } else if (a->t_next.tv_sec == lowest->t_next.tv_sec) {
00205             if (a->t_next.tv_usec < lowest->t_next.tv_usec) {
00206                 lowest = a;
00207             }
00208         } else if (a->t_next.tv_sec < lowest->t_next.tv_sec) {
00209             lowest = a;
00210         }
00211     }
00212     return lowest;
00213 }
00214 
00215 struct snmp_alarm *
00216 sa_find_specific(unsigned int clientreg)
00217 {
00218     struct snmp_alarm *sa_ptr;
00219     for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_ptr->next) {
00220         if (sa_ptr->clientreg == clientreg) {
00221             return sa_ptr;
00222         }
00223     }
00224     return NULL;
00225 }
00226 
00227 void
00228 run_alarms(void)
00229 {
00230     int             done = 0;
00231     struct snmp_alarm *a = NULL;
00232     unsigned int    clientreg;
00233     struct timeval  t_now;
00234 
00235     /*
00236      * Loop through everything we have repeatedly looking for the next thing to
00237      * call until all events are finally in the future again.  
00238      */
00239 
00240     while (!done) {
00241         if ((a = sa_find_next()) == NULL) {
00242             return;
00243         }
00244 
00245         gettimeofday(&t_now, NULL);
00246 
00247         if ((a->t_next.tv_sec < t_now.tv_sec) ||
00248             ((a->t_next.tv_sec == t_now.tv_sec) &&
00249              (a->t_next.tv_usec < t_now.tv_usec))) {
00250             clientreg = a->clientreg;
00251             DEBUGMSGTL(("snmp_alarm", "run alarm %d\n", clientreg));
00252             (*(a->thecallback)) (clientreg, a->clientarg);
00253             DEBUGMSGTL(("snmp_alarm", "alarm %d completed\n", clientreg));
00254 
00255             if ((a = sa_find_specific(clientreg)) != NULL) {
00256                 a->t_last.tv_sec = t_now.tv_sec;
00257                 a->t_last.tv_usec = t_now.tv_usec;
00258                 a->t_next.tv_sec = 0;
00259                 a->t_next.tv_usec = 0;
00260                 sa_update_entry(a);
00261             } else {
00262                 DEBUGMSGTL(("snmp_alarm", "alarm %d deleted itself\n",
00263                             clientreg));
00264             }
00265         } else {
00266             done = 1;
00267         }
00268     }
00269 }
00270 
00271 
00272 
00273 RETSIGTYPE
00274 alarm_handler(int a)
00275 {
00276     run_alarms();
00277     set_an_alarm();
00278 }
00279 
00280 
00281 
00282 int
00283 get_next_alarm_delay_time(struct timeval *delta)
00284 {
00285     struct snmp_alarm *sa_ptr;
00286     struct timeval  t_diff, t_now;
00287 
00288     sa_ptr = sa_find_next();
00289 
00290     if (sa_ptr) {
00291         gettimeofday(&t_now, 0);
00292 
00293         if ((t_now.tv_sec > sa_ptr->t_next.tv_sec) ||
00294             ((t_now.tv_sec == sa_ptr->t_next.tv_sec) &&
00295              (t_now.tv_usec > sa_ptr->t_next.tv_usec))) {
00296             /*
00297              * Time has already passed.  Return the smallest possible amount of
00298              * time.  
00299              */
00300             delta->tv_sec = 0;
00301             delta->tv_usec = 1;
00302             return sa_ptr->clientreg;
00303         } else {
00304             /*
00305              * Time is still in the future.  
00306              */
00307             t_diff.tv_sec = sa_ptr->t_next.tv_sec - t_now.tv_sec;
00308             t_diff.tv_usec = sa_ptr->t_next.tv_usec - t_now.tv_usec;
00309 
00310             while (t_diff.tv_usec < 0) {
00311                 t_diff.tv_sec -= 1;
00312                 t_diff.tv_usec += 1000000;
00313             }
00314 
00315             delta->tv_sec = t_diff.tv_sec;
00316             delta->tv_usec = t_diff.tv_usec;
00317             return sa_ptr->clientreg;
00318         }
00319     }
00320 
00321     /*
00322      * Nothing Left.  
00323      */
00324     return 0;
00325 }
00326 
00327 
00328 void
00329 set_an_alarm(void)
00330 {
00331     struct timeval  delta;
00332     int             nextalarm = get_next_alarm_delay_time(&delta);
00333 
00334     /*
00335      * We don't use signals if they asked us nicely not to.  It's expected
00336      * they'll check the next alarm time and do their own calling of
00337      * run_alarms().  
00338      */
00339 
00340     if (nextalarm && !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
00341                                         NETSNMP_DS_LIB_ALARM_DONT_USE_SIG)) {
00342 #ifndef WIN32
00343 # ifdef HAVE_SETITIMER
00344         struct itimerval it;
00345 
00346         it.it_value.tv_sec = delta.tv_sec;
00347         it.it_value.tv_usec = delta.tv_usec;
00348         it.it_interval.tv_sec = 0;
00349         it.it_interval.tv_usec = 0;
00350 
00351         signal(SIGALRM, alarm_handler);
00352         setitimer(ITIMER_REAL, &it, NULL);
00353         DEBUGMSGTL(("snmp_alarm", "schedule alarm %d in %d.%03d seconds\n",
00354                     nextalarm, delta.tv_sec, (delta.tv_usec / 1000)));
00355 # else  /* HAVE_SETITIMER */
00356 #  ifdef SIGALRM
00357         signal(SIGALRM, alarm_handler);
00358         alarm(delta.tv_sec);
00359         DEBUGMSGTL(("snmp_alarm",
00360                     "schedule alarm %d in roughly %d seconds\n", nextalarm,
00361                     delta.tv_sec));
00362 #  endif  /* SIGALRM */
00363 # endif  /* HAVE_SETITIMER */
00364 #endif  /* WIN32 */
00365 
00366     } else {
00367         DEBUGMSGTL(("snmp_alarm", "no alarms found to schedule\n"));
00368     }
00369 }
00370 
00371 
00403 unsigned int
00404 snmp_alarm_register(unsigned int when, unsigned int flags,
00405                     SNMPAlarmCallback * thecallback, void *clientarg)
00406 {
00407     struct snmp_alarm **sa_pptr;
00408     if (thealarms != NULL) {
00409         for (sa_pptr = &thealarms; (*sa_pptr) != NULL;
00410              sa_pptr = &((*sa_pptr)->next));
00411     } else {
00412         sa_pptr = &thealarms;
00413     }
00414 
00415     *sa_pptr = SNMP_MALLOC_STRUCT(snmp_alarm);
00416     if (*sa_pptr == NULL)
00417         return 0;
00418 
00419     if (0 == when) {
00420         (*sa_pptr)->t.tv_sec = 0;
00421         (*sa_pptr)->t.tv_usec = 1;
00422     } else {
00423         (*sa_pptr)->t.tv_sec = when;
00424         (*sa_pptr)->t.tv_usec = 0;
00425     }
00426     (*sa_pptr)->flags = flags;
00427     (*sa_pptr)->clientarg = clientarg;
00428     (*sa_pptr)->thecallback = thecallback;
00429     (*sa_pptr)->clientreg = regnum++;
00430     (*sa_pptr)->next = NULL;
00431     sa_update_entry(*sa_pptr);
00432 
00433     DEBUGMSGTL(("snmp_alarm",
00434                 "registered alarm %d, t = %d.%03d, flags=0x%02x\n",
00435                 (*sa_pptr)->clientreg, (*sa_pptr)->t.tv_sec,
00436                 ((*sa_pptr)->t.tv_usec / 1000), (*sa_pptr)->flags));
00437 
00438     if (start_alarms)
00439         set_an_alarm();
00440     return (*sa_pptr)->clientreg;
00441 }
00442 
00443 
00480 unsigned int
00481 snmp_alarm_register_hr(struct timeval t, unsigned int flags,
00482                        SNMPAlarmCallback * cb, void *cd)
00483 {
00484     struct snmp_alarm **s = NULL;
00485 
00486     for (s = &(thealarms); *s != NULL; s = &((*s)->next));
00487 
00488     *s = SNMP_MALLOC_STRUCT(snmp_alarm);
00489     if (*s == NULL) {
00490         return 0;
00491     }
00492 
00493     (*s)->t.tv_sec = t.tv_sec;
00494     (*s)->t.tv_usec = t.tv_usec;
00495     (*s)->flags = flags;
00496     (*s)->clientarg = cd;
00497     (*s)->thecallback = cb;
00498     (*s)->clientreg = regnum++;
00499     (*s)->next = NULL;
00500 
00501     sa_update_entry(*s);
00502 
00503     DEBUGMSGTL(("snmp_alarm",
00504                 "registered alarm %d, t = %d.%03d, flags=0x%02x\n",
00505                 (*s)->clientreg, (*s)->t.tv_sec, ((*s)->t.tv_usec / 1000),
00506                 (*s)->flags));
00507 
00508     if (start_alarms) {
00509         set_an_alarm();
00510     }
00511 
00512     return (*s)->clientreg;
00513 }