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