/src/net-snmp/snmplib/snmp_alarm.c
Line | Count | Source (jump to first uncovered line) |
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 | | #ifdef HAVE_UNISTD_H |
22 | | #include <unistd.h> |
23 | | #endif |
24 | | #include <signal.h> |
25 | | #ifdef HAVE_STDLIB_H |
26 | | #include <stdlib.h> |
27 | | #endif |
28 | | #include <sys/types.h> |
29 | | #ifdef HAVE_NETINET_IN_H |
30 | | #include <netinet/in.h> |
31 | | #endif |
32 | | #ifdef HAVE_STRING_H |
33 | | #include <string.h> |
34 | | #endif |
35 | | |
36 | | #ifdef TIME_WITH_SYS_TIME |
37 | | # include <sys/time.h> |
38 | | # include <time.h> |
39 | | #else |
40 | | # ifdef 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 |
61 | | init_alarm_post_config(int majorid, int minorid, void *serverarg, |
62 | | void *clientarg) |
63 | 0 | { |
64 | 0 | start_alarms = 1; |
65 | 0 | set_an_alarm(); |
66 | 0 | return SNMPERR_SUCCESS; |
67 | 0 | } |
68 | | |
69 | | void |
70 | | init_snmp_alarm(void) |
71 | 0 | { |
72 | 0 | start_alarms = 0; |
73 | 0 | snmp_register_callback(SNMP_CALLBACK_LIBRARY, |
74 | 0 | SNMP_CALLBACK_POST_READ_CONFIG, |
75 | 0 | init_alarm_post_config, NULL); |
76 | 0 | } |
77 | | |
78 | | void |
79 | | sa_update_entry(struct snmp_alarm *a) |
80 | 0 | { |
81 | 0 | if (!timerisset(&a->t_lastM)) { |
82 | | /* |
83 | | * First call of sa_update_entry() for alarm a: set t_lastM and t_nextM. |
84 | | */ |
85 | 0 | netsnmp_get_monotonic_clock(&a->t_lastM); |
86 | 0 | NETSNMP_TIMERADD(&a->t_lastM, &a->t, &a->t_nextM); |
87 | 0 | } else if (!timerisset(&a->t_nextM)) { |
88 | | /* |
89 | | * We've been called but not reset for the next call. |
90 | | */ |
91 | 0 | if (a->flags & SA_REPEAT) { |
92 | 0 | if (timerisset(&a->t)) { |
93 | 0 | NETSNMP_TIMERADD(&a->t_lastM, &a->t, &a->t_nextM); |
94 | 0 | } else { |
95 | 0 | DEBUGMSGTL(("snmp_alarm", |
96 | 0 | "update_entry: illegal interval specified\n")); |
97 | 0 | snmp_alarm_unregister(a->clientreg); |
98 | 0 | } |
99 | 0 | } else { |
100 | | /* |
101 | | * Single time call, remove it. |
102 | | */ |
103 | 0 | snmp_alarm_unregister(a->clientreg); |
104 | 0 | } |
105 | 0 | } |
106 | 0 | } |
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 |
122 | | snmp_alarm_unregister(unsigned int clientreg) |
123 | 0 | { |
124 | 0 | struct snmp_alarm *sa_ptr, **prevNext = &thealarms; |
125 | |
|
126 | 0 | for (sa_ptr = thealarms; |
127 | 0 | sa_ptr != NULL && sa_ptr->clientreg != clientreg; |
128 | 0 | sa_ptr = sa_ptr->next) { |
129 | 0 | prevNext = &(sa_ptr->next); |
130 | 0 | } |
131 | |
|
132 | 0 | if (sa_ptr != NULL) { |
133 | 0 | *prevNext = sa_ptr->next; |
134 | 0 | DEBUGMSGTL(("snmp_alarm", "unregistered alarm %d\n", |
135 | 0 | sa_ptr->clientreg)); |
136 | | /* |
137 | | * Note: do not free the clientarg, it's the client's responsibility |
138 | | */ |
139 | 0 | free(sa_ptr); |
140 | 0 | } else { |
141 | 0 | DEBUGMSGTL(("snmp_alarm", "no alarm %d to unregister\n", clientreg)); |
142 | 0 | } |
143 | 0 | } |
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 |
155 | | snmp_alarm_unregister_all(void) |
156 | 0 | { |
157 | 0 | struct snmp_alarm *sa_ptr, *sa_tmp; |
158 | |
|
159 | 0 | for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_tmp) { |
160 | 0 | sa_tmp = sa_ptr->next; |
161 | 0 | free(sa_ptr); |
162 | 0 | } |
163 | 0 | DEBUGMSGTL(("snmp_alarm", "ALL alarms unregistered\n")); |
164 | 0 | thealarms = NULL; |
165 | 0 | } |
166 | | |
167 | | struct snmp_alarm * |
168 | | sa_find_next(void) |
169 | 0 | { |
170 | 0 | struct snmp_alarm *a, *lowest = NULL; |
171 | |
|
172 | 0 | for (a = thealarms; a != NULL; a = a->next) |
173 | 0 | if (!(a->flags & SA_FIRED) |
174 | 0 | && (lowest == NULL || timercmp(&a->t_nextM, &lowest->t_nextM, <))) |
175 | 0 | lowest = a; |
176 | |
|
177 | 0 | return lowest; |
178 | 0 | } |
179 | | |
180 | | NETSNMP_IMPORT struct snmp_alarm *sa_find_specific(unsigned int clientreg); |
181 | | struct snmp_alarm * |
182 | | sa_find_specific(unsigned int clientreg) |
183 | 0 | { |
184 | 0 | struct snmp_alarm *sa_ptr; |
185 | 0 | for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_ptr->next) { |
186 | 0 | if (sa_ptr->clientreg == clientreg) { |
187 | 0 | return sa_ptr; |
188 | 0 | } |
189 | 0 | } |
190 | 0 | return NULL; |
191 | 0 | } |
192 | | |
193 | | void |
194 | | run_alarms(void) |
195 | 0 | { |
196 | 0 | struct snmp_alarm *a; |
197 | 0 | unsigned int clientreg; |
198 | 0 | 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 | 0 | while ((a = sa_find_next()) != NULL) { |
206 | 0 | netsnmp_get_monotonic_clock(&t_now); |
207 | |
|
208 | 0 | if (timercmp(&a->t_nextM, &t_now, >)) |
209 | 0 | return; |
210 | | |
211 | 0 | clientreg = a->clientreg; |
212 | 0 | a->flags |= SA_FIRED; |
213 | 0 | DEBUGMSGTL(("snmp_alarm", "run alarm %d\n", clientreg)); |
214 | 0 | (*(a->thecallback)) (clientreg, a->clientarg); |
215 | 0 | DEBUGMSGTL(("snmp_alarm", "alarm %d completed\n", clientreg)); |
216 | |
|
217 | 0 | a = sa_find_specific(clientreg); |
218 | 0 | if (a) { |
219 | 0 | a->t_lastM = t_now; |
220 | 0 | timerclear(&a->t_nextM); |
221 | 0 | a->flags &= ~SA_FIRED; |
222 | 0 | sa_update_entry(a); |
223 | 0 | } else { |
224 | 0 | DEBUGMSGTL(("snmp_alarm", "alarm %d deleted itself\n", |
225 | 0 | clientreg)); |
226 | 0 | } |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | | |
231 | | |
232 | | RETSIGTYPE |
233 | | alarm_handler(int a) |
234 | 0 | { |
235 | 0 | run_alarms(); |
236 | 0 | set_an_alarm(); |
237 | 0 | } |
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 |
252 | | netsnmp_get_next_alarm_time(struct timeval *alarm_tm, const struct timeval *now) |
253 | 0 | { |
254 | 0 | struct snmp_alarm *sa_ptr; |
255 | |
|
256 | 0 | sa_ptr = sa_find_next(); |
257 | |
|
258 | 0 | if (sa_ptr) { |
259 | 0 | netsnmp_assert(alarm_tm); |
260 | 0 | netsnmp_assert(timerisset(&sa_ptr->t_nextM)); |
261 | 0 | if (timercmp(&sa_ptr->t_nextM, now, >)) |
262 | 0 | *alarm_tm = sa_ptr->t_nextM; |
263 | 0 | else |
264 | 0 | *alarm_tm = *now; |
265 | 0 | return sa_ptr->clientreg; |
266 | 0 | } else { |
267 | 0 | return 0; |
268 | 0 | } |
269 | 0 | } |
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 |
281 | | get_next_alarm_delay_time(struct timeval *delta) |
282 | 0 | { |
283 | 0 | struct timeval t_now, alarm_tm; |
284 | 0 | int res; |
285 | |
|
286 | 0 | netsnmp_get_monotonic_clock(&t_now); |
287 | 0 | res = netsnmp_get_next_alarm_time(&alarm_tm, &t_now); |
288 | 0 | if (res) |
289 | 0 | NETSNMP_TIMERSUB(&alarm_tm, &t_now, delta); |
290 | 0 | return res; |
291 | 0 | } |
292 | | |
293 | | |
294 | | void |
295 | | set_an_alarm(void) |
296 | 0 | { |
297 | 0 | struct timeval delta; |
298 | 0 | 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 | 0 | if (nextalarm && !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
307 | 0 | NETSNMP_DS_LIB_ALARM_DONT_USE_SIG)) { |
308 | 0 | #if defined(HAVE_SETITIMER) |
309 | 0 | struct itimerval it; |
310 | |
|
311 | 0 | it.it_value = delta; |
312 | 0 | timerclear(&it.it_interval); |
313 | |
|
314 | 0 | signal(SIGALRM, alarm_handler); |
315 | 0 | setitimer(ITIMER_REAL, &it, NULL); |
316 | 0 | DEBUGMSGTL(("snmp_alarm", "schedule alarm %d in %ld.%03ld seconds\n", |
317 | 0 | nextalarm, (long) delta.tv_sec, (long)(delta.tv_usec / 1000))); |
318 | | #elif defined(SIGALRM) |
319 | | signal(SIGALRM, alarm_handler); |
320 | | alarm(delta.tv_sec); |
321 | | DEBUGMSGTL(("snmp_alarm", |
322 | | "schedule alarm %d in roughly %ld seconds\n", nextalarm, |
323 | | delta.tv_sec)); |
324 | | #endif |
325 | 0 | } else { |
326 | 0 | DEBUGMSGTL(("snmp_alarm", "no alarms found to schedule\n")); |
327 | 0 | } |
328 | 0 | } |
329 | | |
330 | | |
331 | | /** |
332 | | * This function registers function callbacks to occur at a specific time |
333 | | * in the future. |
334 | | * |
335 | | * @param when is an unsigned integer specifying when the callback function |
336 | | * will be called in seconds. |
337 | | * |
338 | | * @param flags is an unsigned integer that specifies how frequent the callback |
339 | | * function is called in seconds. Should be SA_REPEAT or 0. If |
340 | | * flags is set with SA_REPEAT, then the registered callback function |
341 | | * will be called every SA_REPEAT seconds. If flags is 0 then the |
342 | | * function will only be called once and then removed from the |
343 | | * registered alarm list. |
344 | | * |
345 | | * @param thecallback is a pointer SNMPAlarmCallback which is the callback |
346 | | * function being stored and registered. |
347 | | * |
348 | | * @param clientarg is a void pointer used by the callback function. This |
349 | | * pointer is assigned to snmp_alarm->clientarg and passed into the |
350 | | * callback function for the client's specific needs. |
351 | | * |
352 | | * @return Returns a unique unsigned integer(which is also passed as the first |
353 | | * argument of each callback), which can then be used to remove the |
354 | | * callback from the list at a later point in the future using the |
355 | | * snmp_alarm_unregister() function. If memory could not be allocated |
356 | | * for the snmp_alarm struct 0 is returned. |
357 | | * |
358 | | * @see snmp_alarm_unregister |
359 | | * @see snmp_alarm_register_hr |
360 | | * @see snmp_alarm_unregister_all |
361 | | */ |
362 | | unsigned int |
363 | | snmp_alarm_register(unsigned int when, unsigned int flags, |
364 | | SNMPAlarmCallback * thecallback, void *clientarg) |
365 | 0 | { |
366 | 0 | struct timeval t; |
367 | |
|
368 | 0 | if (0 == when) { |
369 | 0 | t.tv_sec = 0; |
370 | 0 | t.tv_usec = 1; |
371 | 0 | } else { |
372 | 0 | t.tv_sec = when; |
373 | 0 | t.tv_usec = 0; |
374 | 0 | } |
375 | |
|
376 | 0 | return snmp_alarm_register_hr(t, flags, thecallback, clientarg); |
377 | 0 | } |
378 | | |
379 | | |
380 | | /** |
381 | | * This function offers finer granularity as to when the callback |
382 | | * function is called by making use of t->tv_usec value forming the |
383 | | * "when" aspect of snmp_alarm_register(). |
384 | | * |
385 | | * @param t is a timeval structure used to specify when the callback |
386 | | * function(alarm) will be called. Adds the ability to specify |
387 | | * microseconds. t.tv_sec and t.tv_usec are assigned |
388 | | * to snmp_alarm->tv_sec and snmp_alarm->tv_usec respectively internally. |
389 | | * The snmp_alarm_register function only assigns seconds(it's when |
390 | | * argument). |
391 | | * |
392 | | * @param flags is an unsigned integer that specifies how frequent the callback |
393 | | * function is called in seconds. Should be SA_REPEAT or NULL. If |
394 | | * flags is set with SA_REPEAT, then the registered callback function |
395 | | * will be called every SA_REPEAT seconds. If flags is NULL then the |
396 | | * function will only be called once and then removed from the |
397 | | * registered alarm list. |
398 | | * |
399 | | * @param cb is a pointer SNMPAlarmCallback which is the callback |
400 | | * function being stored and registered. |
401 | | * |
402 | | * @param cd is a void pointer used by the callback function. This |
403 | | * pointer is assigned to snmp_alarm->clientarg and passed into the |
404 | | * callback function for the client's specific needs. |
405 | | * |
406 | | * @return Returns a unique unsigned integer(which is also passed as the first |
407 | | * argument of each callback), which can then be used to remove the |
408 | | * callback from the list at a later point in the future using the |
409 | | * snmp_alarm_unregister() function. If memory could not be allocated |
410 | | * for the snmp_alarm struct 0 is returned. |
411 | | * |
412 | | * @see snmp_alarm_register |
413 | | * @see snmp_alarm_unregister |
414 | | * @see snmp_alarm_unregister_all |
415 | | */ |
416 | | unsigned int |
417 | | snmp_alarm_register_hr(struct timeval t, unsigned int flags, |
418 | | SNMPAlarmCallback * cb, void *cd) |
419 | 0 | { |
420 | 0 | struct snmp_alarm **s = NULL; |
421 | |
|
422 | 0 | for (s = &(thealarms); *s != NULL; s = &((*s)->next)); |
423 | |
|
424 | 0 | *s = SNMP_MALLOC_STRUCT(snmp_alarm); |
425 | 0 | if (*s == NULL) { |
426 | 0 | return 0; |
427 | 0 | } |
428 | | |
429 | 0 | (*s)->t = t; |
430 | 0 | (*s)->flags = flags; |
431 | 0 | (*s)->clientarg = cd; |
432 | 0 | (*s)->thecallback = cb; |
433 | 0 | (*s)->clientreg = regnum++; |
434 | 0 | (*s)->next = NULL; |
435 | |
|
436 | 0 | sa_update_entry(*s); |
437 | |
|
438 | 0 | DEBUGMSGTL(("snmp_alarm", |
439 | 0 | "registered alarm %d, t = %ld.%03ld, flags=0x%02x\n", |
440 | 0 | (*s)->clientreg, (long) (*s)->t.tv_sec, (long)((*s)->t.tv_usec / 1000), |
441 | 0 | (*s)->flags)); |
442 | |
|
443 | 0 | if (start_alarms) { |
444 | 0 | set_an_alarm(); |
445 | 0 | } |
446 | |
|
447 | 0 | return (*s)->clientreg; |
448 | 0 | } |
449 | | |
450 | | /** |
451 | | * This function resets an existing alarm. |
452 | | * |
453 | | * @param clientreg is a unique unsigned integer representing a registered |
454 | | * alarm which the client wants to unregister. |
455 | | * |
456 | | * @return 0 on success, -1 if the alarm was not found |
457 | | * |
458 | | * @see snmp_alarm_register |
459 | | * @see snmp_alarm_register_hr |
460 | | * @see snmp_alarm_unregister |
461 | | */ |
462 | | int |
463 | | snmp_alarm_reset(unsigned int clientreg) |
464 | 0 | { |
465 | 0 | struct snmp_alarm *a; |
466 | 0 | struct timeval t_now; |
467 | 0 | if ((a = sa_find_specific(clientreg)) != NULL) { |
468 | 0 | netsnmp_get_monotonic_clock(&t_now); |
469 | 0 | a->t_lastM.tv_sec = t_now.tv_sec; |
470 | 0 | a->t_lastM.tv_usec = t_now.tv_usec; |
471 | 0 | a->t_nextM.tv_sec = 0; |
472 | 0 | a->t_nextM.tv_usec = 0; |
473 | 0 | NETSNMP_TIMERADD(&t_now, &a->t, &a->t_nextM); |
474 | 0 | return 0; |
475 | 0 | } |
476 | 0 | DEBUGMSGTL(("snmp_alarm_reset", "alarm %d not found\n", |
477 | 0 | clientreg)); |
478 | 0 | return -1; |
479 | 0 | } |
480 | | /** @} */ |