1 /*
2  * snmp_alarm.c:
3  */
4 /* Portions of this file are subject to the following copyright(s).  See
5  * the Net-SNMP's COPYING file for more details and other copyrights
6  * that may apply:
7  */
8 /*
9  * Portions of this file are copyrighted by:
10  * Copyright � 2003 Sun Microsystems, Inc. All rights reserved.
11  * Use is subject to license terms specified in the COPYING file
12  * distributed with the Net-SNMP package.
13  */
14 /** @defgroup snmp_alarm  generic library based alarm timers for various parts of an application
15  *  @ingroup library
16  *
17  *  @{
18  */
19 #include <net-snmp/net-snmp-config.h>
20 
21 #if HAVE_UNISTD_H
22 #include <unistd.h>
23 #endif
24 #include <signal.h>
25 #if HAVE_STDLIB_H
26 #include <stdlib.h>
27 #endif
28 #include <sys/types.h>
29 #if HAVE_NETINET_IN_H
30 #include <netinet/in.h>
31 #endif
32 #if HAVE_STRING_H
33 #include <string.h>
34 #endif
35 
36 #if TIME_WITH_SYS_TIME
37 # include <sys/time.h>
38 # include <time.h>
39 #else
40 # if HAVE_SYS_TIME_H
41 #  include <sys/time.h>
42 # else
43 #  include <time.h>
44 # endif
45 #endif
46 
47 #include <net-snmp/types.h>
48 #include <net-snmp/output_api.h>
49 #include <net-snmp/config_api.h>
50 #include <net-snmp/utilities.h>
51 
52 #include <net-snmp/library/snmp_api.h>
53 #include <net-snmp/library/callback.h>
54 #include <net-snmp/library/snmp_alarm.h>
55 
56 static struct snmp_alarm *thealarms = NULL;
57 static int      start_alarms = 0;
58 static unsigned int regnum = 1;
59 
60 int
init_alarm_post_config(int majorid,int minorid,void * serverarg,void * clientarg)61 init_alarm_post_config(int majorid, int minorid, void *serverarg,
62                        void *clientarg)
63 {
64     start_alarms = 1;
65     set_an_alarm();
66     return SNMPERR_SUCCESS;
67 }
68 
69 void
init_snmp_alarm(void)70 init_snmp_alarm(void)
71 {
72     start_alarms = 0;
73     snmp_register_callback(SNMP_CALLBACK_LIBRARY,
74                            SNMP_CALLBACK_POST_READ_CONFIG,
75                            init_alarm_post_config, NULL);
76 }
77 
78 void
sa_update_entry(struct snmp_alarm * a)79 sa_update_entry(struct snmp_alarm *a)
80 {
81     if (!timerisset(&a->t_lastM)) {
82         /*
83          * First call of sa_update_entry() for alarm a: set t_lastM and t_nextM.
84          */
85         netsnmp_get_monotonic_clock(&a->t_lastM);
86         NETSNMP_TIMERADD(&a->t_lastM, &a->t, &a->t_nextM);
87     } else if (!timerisset(&a->t_nextM)) {
88         /*
89          * We've been called but not reset for the next call.
90          */
91         if (a->flags & SA_REPEAT) {
92             if (timerisset(&a->t)) {
93                 NETSNMP_TIMERADD(&a->t_lastM, &a->t, &a->t_nextM);
94             } else {
95                 DEBUGMSGTL(("snmp_alarm",
96                             "update_entry: illegal interval specified\n"));
97                 snmp_alarm_unregister(a->clientreg);
98             }
99         } else {
100             /*
101              * Single time call, remove it.
102              */
103             snmp_alarm_unregister(a->clientreg);
104         }
105     }
106 }
107 
108 /**
109  * This function removes the callback function from a list of registered
110  * alarms, unregistering the alarm.
111  *
112  * @param clientreg is a unique unsigned integer representing a registered
113  *	alarm which the client wants to unregister.
114  *
115  * @return void
116  *
117  * @see snmp_alarm_register
118  * @see snmp_alarm_register_hr
119  * @see snmp_alarm_unregister_all
120  */
121 void
snmp_alarm_unregister(unsigned int clientreg)122 snmp_alarm_unregister(unsigned int clientreg)
123 {
124     struct snmp_alarm *sa_ptr, **prevNext = &thealarms;
125 
126     for (sa_ptr = thealarms;
127          sa_ptr != NULL && sa_ptr->clientreg != clientreg;
128          sa_ptr = sa_ptr->next) {
129         prevNext = &(sa_ptr->next);
130     }
131 
132     if (sa_ptr != NULL) {
133         *prevNext = sa_ptr->next;
134         DEBUGMSGTL(("snmp_alarm", "unregistered alarm %d\n",
135 		    sa_ptr->clientreg));
136         /*
137          * Note: do not free the clientarg, it's the client's responsibility
138          */
139         free(sa_ptr);
140     } else {
141         DEBUGMSGTL(("snmp_alarm", "no alarm %d to unregister\n", clientreg));
142     }
143 }
144 
145 /**
146  * This function unregisters all alarms currently stored.
147  *
148  * @return void
149  *
150  * @see snmp_alarm_register
151  * @see snmp_alarm_register_hr
152  * @see snmp_alarm_unregister
153  */
154 void
snmp_alarm_unregister_all(void)155 snmp_alarm_unregister_all(void)
156 {
157   struct snmp_alarm *sa_ptr, *sa_tmp;
158 
159   for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_tmp) {
160     sa_tmp = sa_ptr->next;
161     free(sa_ptr);
162   }
163   DEBUGMSGTL(("snmp_alarm", "ALL alarms unregistered\n"));
164   thealarms = NULL;
165 }
166 
167 struct snmp_alarm *
sa_find_next(void)168 sa_find_next(void)
169 {
170     struct snmp_alarm *a, *lowest = NULL;
171 
172     for (a = thealarms; a != NULL; a = a->next)
173         if (!(a->flags & SA_FIRED)
174             && (lowest == NULL || timercmp(&a->t_nextM, &lowest->t_nextM, <)))
175             lowest = a;
176 
177     return lowest;
178 }
179 
180 NETSNMP_IMPORT struct snmp_alarm *sa_find_specific(unsigned int clientreg);
181 struct snmp_alarm *
sa_find_specific(unsigned int clientreg)182 sa_find_specific(unsigned int clientreg)
183 {
184     struct snmp_alarm *sa_ptr;
185     for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_ptr->next) {
186         if (sa_ptr->clientreg == clientreg) {
187             return sa_ptr;
188         }
189     }
190     return NULL;
191 }
192 
193 void
run_alarms(void)194 run_alarms(void)
195 {
196     struct snmp_alarm *a;
197     unsigned int    clientreg;
198     struct timeval  t_now;
199 
200     /*
201      * Loop through everything we have repeatedly looking for the next thing to
202      * call until all events are finally in the future again.
203      */
204 
205     while ((a = sa_find_next()) != NULL) {
206         netsnmp_get_monotonic_clock(&t_now);
207 
208         if (timercmp(&a->t_nextM, &t_now, >))
209             return;
210 
211         clientreg = a->clientreg;
212         a->flags |= SA_FIRED;
213         DEBUGMSGTL(("snmp_alarm", "run alarm %d\n", clientreg));
214         (*(a->thecallback)) (clientreg, a->clientarg);
215         DEBUGMSGTL(("snmp_alarm", "alarm %d completed\n", clientreg));
216 
217         a = sa_find_specific(clientreg);
218         if (a) {
219             a->t_lastM = t_now;
220             timerclear(&a->t_nextM);
221             a->flags &= ~SA_FIRED;
222             sa_update_entry(a);
223         } else {
224             DEBUGMSGTL(("snmp_alarm", "alarm %d deleted itself\n",
225                         clientreg));
226         }
227     }
228 }
229 
230 
231 
232 RETSIGTYPE
alarm_handler(int a)233 alarm_handler(int a)
234 {
235     run_alarms();
236     set_an_alarm();
237 }
238 
239 
240 
241 /**
242  * Look up the time at which the next alarm will fire.
243  *
244  * @param[out] alarm_tm Time at which the next alarm will fire.
245  * @param[in] now Earliest time that should be written into *alarm_tm.
246  *
247  * @return Zero if no alarms are scheduled; non-zero 'clientreg' value
248  *   identifying the first alarm that will fire if one or more alarms are
249  *   scheduled.
250  */
251 int
netsnmp_get_next_alarm_time(struct timeval * alarm_tm,const struct timeval * now)252 netsnmp_get_next_alarm_time(struct timeval *alarm_tm, const struct timeval *now)
253 {
254     struct snmp_alarm *sa_ptr;
255 
256     sa_ptr = sa_find_next();
257 
258     if (sa_ptr) {
259         netsnmp_assert(alarm_tm);
260         netsnmp_assert(timerisset(&sa_ptr->t_nextM));
261         if (timercmp(&sa_ptr->t_nextM, now, >))
262             *alarm_tm = sa_ptr->t_nextM;
263         else
264             *alarm_tm = *now;
265         return sa_ptr->clientreg;
266     } else {
267         return 0;
268     }
269 }
270 
271 /**
272  * Get the time until the next alarm will fire.
273  *
274  * @param[out] delta Time until the next alarm.
275  *
276  * @return Zero if no alarms are scheduled; non-zero 'clientreg' value
277  *   identifying the first alarm that will fire if one or more alarms are
278  *   scheduled.
279  */
280 int
get_next_alarm_delay_time(struct timeval * delta)281 get_next_alarm_delay_time(struct timeval *delta)
282 {
283     struct timeval t_now, alarm_tm;
284     int res;
285 
286     netsnmp_get_monotonic_clock(&t_now);
287     res = netsnmp_get_next_alarm_time(&alarm_tm, &t_now);
288     if (res)
289         NETSNMP_TIMERSUB(&alarm_tm, &t_now, delta);
290     return res;
291 }
292 
293 
294 void
set_an_alarm(void)295 set_an_alarm(void)
296 {
297     struct timeval  delta;
298     int             nextalarm = get_next_alarm_delay_time(&delta);
299 
300     /*
301      * We don't use signals if they asked us nicely not to.  It's expected
302      * they'll check the next alarm time and do their own calling of
303      * run_alarms().
304      */
305 
306     if (nextalarm && !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
307 					NETSNMP_DS_LIB_ALARM_DONT_USE_SIG)) {
308 #ifndef WIN32
309 # ifdef HAVE_SETITIMER
310         struct itimerval it;
311 
312         it.it_value = delta;
313         timerclear(&it.it_interval);
314 
315         signal(SIGALRM, alarm_handler);
316         setitimer(ITIMER_REAL, &it, NULL);
317         DEBUGMSGTL(("snmp_alarm", "schedule alarm %d in %ld.%03ld seconds\n",
318                     nextalarm, (long) delta.tv_sec, (long)(delta.tv_usec / 1000)));
319 # else  /* HAVE_SETITIMER */
320 #  ifdef SIGALRM
321         signal(SIGALRM, alarm_handler);
322         alarm(delta.tv_sec);
323         DEBUGMSGTL(("snmp_alarm",
324                     "schedule alarm %d in roughly %ld seconds\n", nextalarm,
325                     delta.tv_sec));
326 #  endif  /* SIGALRM */
327 # endif  /* HAVE_SETITIMER */
328 #endif  /* WIN32 */
329 
330     } else {
331         DEBUGMSGTL(("snmp_alarm", "no alarms found to schedule\n"));
332     }
333 }
334 
335 
336 /**
337  * This function registers function callbacks to occur at a specific time
338  * in the future.
339  *
340  * @param when is an unsigned integer specifying when the callback function
341  *             will be called in seconds.
342  *
343  * @param flags is an unsigned integer that specifies how frequent the callback
344  *	function is called in seconds.  Should be SA_REPEAT or 0.  If
345  *	flags  is  set with SA_REPEAT, then the registered callback function
346  *	will be called every SA_REPEAT seconds.  If flags is 0 then the
347  *	function will only be called once and then removed from the
348  *	registered alarm list.
349  *
350  * @param thecallback is a pointer SNMPAlarmCallback which is the callback
351  *	function being stored and registered.
352  *
353  * @param clientarg is a void pointer used by the callback function.  This
354  *	pointer is assigned to snmp_alarm->clientarg and passed into the
355  *	callback function for the client's specific needs.
356  *
357  * @return Returns a unique unsigned integer(which is also passed as the first
358  *	argument of each callback), which can then be used to remove the
359  *	callback from the list at a later point in the future using the
360  *	snmp_alarm_unregister() function.  If memory could not be allocated
361  *	for the snmp_alarm struct 0 is returned.
362  *
363  * @see snmp_alarm_unregister
364  * @see snmp_alarm_register_hr
365  * @see snmp_alarm_unregister_all
366  */
367 unsigned int
snmp_alarm_register(unsigned int when,unsigned int flags,SNMPAlarmCallback * thecallback,void * clientarg)368 snmp_alarm_register(unsigned int when, unsigned int flags,
369                     SNMPAlarmCallback * thecallback, void *clientarg)
370 {
371     struct timeval  t;
372 
373     if (0 == when) {
374         t.tv_sec = 0;
375         t.tv_usec = 1;
376     } else {
377         t.tv_sec = when;
378         t.tv_usec = 0;
379     }
380 
381     return snmp_alarm_register_hr(t, flags, thecallback, clientarg);
382 }
383 
384 
385 /**
386  * This function offers finer granularity as to when the callback
387  * function is called by making use of t->tv_usec value forming the
388  * "when" aspect of snmp_alarm_register().
389  *
390  * @param t is a timeval structure used to specify when the callback
391  *	function(alarm) will be called.  Adds the ability to specify
392  *	microseconds.  t.tv_sec and t.tv_usec are assigned
393  *	to snmp_alarm->tv_sec and snmp_alarm->tv_usec respectively internally.
394  *	The snmp_alarm_register function only assigns seconds(it's when
395  *	argument).
396  *
397  * @param flags is an unsigned integer that specifies how frequent the callback
398  *	function is called in seconds.  Should be SA_REPEAT or NULL.  If
399  *	flags  is  set with SA_REPEAT, then the registered callback function
400  *	will be called every SA_REPEAT seconds.  If flags is NULL then the
401  *	function will only be called once and then removed from the
402  *	registered alarm list.
403  *
404  * @param cb is a pointer SNMPAlarmCallback which is the callback
405  *	function being stored and registered.
406  *
407  * @param cd is a void pointer used by the callback function.  This
408  *	pointer is assigned to snmp_alarm->clientarg and passed into the
409  *	callback function for the client's specific needs.
410  *
411  * @return Returns a unique unsigned integer(which is also passed as the first
412  *	argument of each callback), which can then be used to remove the
413  *	callback from the list at a later point in the future using the
414  *	snmp_alarm_unregister() function.  If memory could not be allocated
415  *	for the snmp_alarm struct 0 is returned.
416  *
417  * @see snmp_alarm_register
418  * @see snmp_alarm_unregister
419  * @see snmp_alarm_unregister_all
420  */
421 unsigned int
snmp_alarm_register_hr(struct timeval t,unsigned int flags,SNMPAlarmCallback * cb,void * cd)422 snmp_alarm_register_hr(struct timeval t, unsigned int flags,
423                        SNMPAlarmCallback * cb, void *cd)
424 {
425     struct snmp_alarm **s = NULL;
426 
427     for (s = &(thealarms); *s != NULL; s = &((*s)->next));
428 
429     *s = SNMP_MALLOC_STRUCT(snmp_alarm);
430     if (*s == NULL) {
431         return 0;
432     }
433 
434     (*s)->t = t;
435     (*s)->flags = flags;
436     (*s)->clientarg = cd;
437     (*s)->thecallback = cb;
438     (*s)->clientreg = regnum++;
439     (*s)->next = NULL;
440 
441     sa_update_entry(*s);
442 
443     DEBUGMSGTL(("snmp_alarm",
444                 "registered alarm %d, t = %ld.%03ld, flags=0x%02x\n",
445                 (*s)->clientreg, (long) (*s)->t.tv_sec, (long)((*s)->t.tv_usec / 1000),
446                 (*s)->flags));
447 
448     if (start_alarms) {
449         set_an_alarm();
450     }
451 
452     return (*s)->clientreg;
453 }
454 
455 /**
456  * This function resets an existing alarm.
457  *
458  * @param clientreg is a unique unsigned integer representing a registered
459  *	alarm which the client wants to unregister.
460  *
461  * @return 0 on success, -1 if the alarm was not found
462  *
463  * @see snmp_alarm_register
464  * @see snmp_alarm_register_hr
465  * @see snmp_alarm_unregister
466  */
467 int
snmp_alarm_reset(unsigned int clientreg)468 snmp_alarm_reset(unsigned int clientreg)
469 {
470     struct snmp_alarm *a;
471     struct timeval  t_now;
472     if ((a = sa_find_specific(clientreg)) != NULL) {
473         netsnmp_get_monotonic_clock(&t_now);
474         a->t_lastM.tv_sec = t_now.tv_sec;
475         a->t_lastM.tv_usec = t_now.tv_usec;
476         a->t_nextM.tv_sec = 0;
477         a->t_nextM.tv_usec = 0;
478         NETSNMP_TIMERADD(&t_now, &a->t, &a->t_nextM);
479         return 0;
480     }
481     DEBUGMSGTL(("snmp_alarm_reset", "alarm %d not found\n",
482                 clientreg));
483     return -1;
484 }
485 /**  @} */
486