# BRCM_VERSION=3
[bcm963xx.git] / userapps / opensource / net-snmp / snmplib / snmp_alarm.c
1 /*
2  * snmp_alarm.c: generic library based alarm timers for various parts
3  * of an application 
4  */
5
6 #include <net-snmp/net-snmp-config.h>
7
8 #if HAVE_UNISTD_H
9 #include <unistd.h>
10 #endif
11 #include <signal.h>
12 #if HAVE_STDLIB_H
13 #include <stdlib.h>
14 #endif
15 #include <sys/types.h>
16 #if HAVE_NETINET_IN_H
17 #include <netinet/in.h>
18 #endif
19
20 #if TIME_WITH_SYS_TIME
21 # ifdef WIN32
22 #  include <sys/timeb.h>
23 # else
24 #  include <sys/time.h>
25 # endif
26 # include <time.h>
27 #else
28 # if HAVE_SYS_TIME_H
29 #  include <sys/time.h>
30 # else
31 #  include <time.h>
32 # endif
33 #endif
34 #if HAVE_WINSOCK_H
35 #include <winsock.h>
36 #endif
37
38 #if HAVE_DMALLOC_H
39 #include <dmalloc.h>
40 #endif
41
42 #include <net-snmp/types.h>
43 #include <net-snmp/output_api.h>
44 #include <net-snmp/config_api.h>
45 #include <net-snmp/utilities.h>
46
47 #include <net-snmp/library/snmp_api.h>
48 #include <net-snmp/library/callback.h>
49 #include <net-snmp/library/snmp_alarm.h>
50
51 static struct snmp_alarm *thealarms = NULL;
52 static int      start_alarms = 0;
53 static unsigned int regnum = 1;
54
55 int
56 init_alarm_post_config(int majorid, int minorid, void *serverarg,
57                        void *clientarg)
58 {
59     start_alarms = 1;
60     set_an_alarm();
61     return SNMPERR_SUCCESS;
62 }
63
64 void
65 init_snmp_alarm(void)
66 {
67     start_alarms = 0;
68     snmp_register_callback(SNMP_CALLBACK_LIBRARY,
69                            SNMP_CALLBACK_POST_READ_CONFIG,
70                            init_alarm_post_config, NULL);
71 }
72
73 void
74 sa_update_entry(struct snmp_alarm *a)
75 {
76     if (a->t.tv_sec == 0 && a->t.tv_usec == 0) {
77         DEBUGMSGTL(("snmp_alarm",
78                     "update_entry: illegal interval specified\n"));
79         return;
80     }
81
82     if (a->t_last.tv_sec == 0 && a->t_last.tv_usec == 0) {
83         struct timeval  t_now;
84         /*
85          * Never been called yet, call time `t' from now.  
86          */
87         gettimeofday(&t_now, NULL);
88
89         a->t_last.tv_sec = t_now.tv_sec;
90         a->t_last.tv_usec = t_now.tv_usec;
91
92         a->t_next.tv_sec = t_now.tv_sec + a->t.tv_sec;
93         a->t_next.tv_usec = t_now.tv_usec + a->t.tv_usec;
94
95         while (a->t_next.tv_usec >= 1000000) {
96             a->t_next.tv_usec -= 1000000;
97             a->t_next.tv_sec += 1;
98         }
99     } else if (a->t_next.tv_sec == 0 && a->t_next.tv_usec == 0) {
100         /*
101          * We've been called but not reset for the next call.  
102          */
103         if (a->flags & SA_REPEAT) {
104             a->t_next.tv_sec = a->t_last.tv_sec + a->t.tv_sec;
105             a->t_next.tv_usec = a->t_last.tv_usec + a->t.tv_usec;
106
107             while (a->t_next.tv_usec >= 1000000) {
108                 a->t_next.tv_usec -= 1000000;
109                 a->t_next.tv_sec += 1;
110             }
111         } else {
112             /*
113              * Single time call, remove it.  
114              */
115             snmp_alarm_unregister(a->clientreg);
116         }
117     }
118 }
119
120 void
121 snmp_alarm_unregister(unsigned int clientreg)
122 {
123     struct snmp_alarm *sa_ptr, **prevNext = &thealarms;
124
125     for (sa_ptr = thealarms;
126          sa_ptr != NULL && sa_ptr->clientreg != clientreg;
127          sa_ptr = sa_ptr->next) {
128         prevNext = &(sa_ptr->next);
129     }
130
131     if (sa_ptr != NULL) {
132         *prevNext = sa_ptr->next;
133         DEBUGMSGTL(("snmp_alarm", "unregistered alarm %d\n", 
134                     sa_ptr->clientreg));
135         /*
136          * Note:  do not free the clientarg, its the clients responsibility 
137          */
138         free(sa_ptr);
139     } else {
140         DEBUGMSGTL(("snmp_alarm", "no alarm %d to unregister\n", clientreg));
141     }
142 }
143
144 void
145 snmp_alarm_unregister_all(void)
146 {
147   struct snmp_alarm *sa_ptr, *sa_tmp;
148
149   for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_tmp) {
150     sa_tmp = sa_ptr->next;
151     free(sa_ptr);
152   }
153   DEBUGMSGTL(("snmp_alarm", "ALL alarms unregistered\n"));
154   thealarms = NULL;
155 }  
156
157 struct snmp_alarm *
158 sa_find_next(void)
159 {
160     struct snmp_alarm *a, *lowest = NULL;
161
162     for (a = thealarms; a != NULL; a = a->next) {
163         if (lowest == NULL) {
164             lowest = a;
165         } else if (a->t_next.tv_sec == lowest->t_next.tv_sec) {
166             if (a->t_next.tv_usec < lowest->t_next.tv_usec) {
167                 lowest = a;
168             }
169         } else if (a->t_next.tv_sec < lowest->t_next.tv_sec) {
170             lowest = a;
171         }
172     }
173     return lowest;
174 }
175
176 struct snmp_alarm *
177 sa_find_specific(unsigned int clientreg)
178 {
179     struct snmp_alarm *sa_ptr;
180     for (sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_ptr->next) {
181         if (sa_ptr->clientreg == clientreg) {
182             return sa_ptr;
183         }
184     }
185     return NULL;
186 }
187
188 void
189 run_alarms(void)
190 {
191     int             done = 0;
192     struct snmp_alarm *a = NULL;
193     unsigned int    clientreg;
194     struct timeval  t_now;
195
196     /*
197      * Loop through everything we have repeatedly looking for the next thing to
198      * call until all events are finally in the future again.  
199      */
200
201     while (!done) {
202         if ((a = sa_find_next()) == NULL) {
203             return;
204         }
205
206         gettimeofday(&t_now, NULL);
207
208         if ((a->t_next.tv_sec < t_now.tv_sec) ||
209             ((a->t_next.tv_sec == t_now.tv_sec) &&
210              (a->t_next.tv_usec < t_now.tv_usec))) {
211             clientreg = a->clientreg;
212             DEBUGMSGTL(("snmp_alarm", "run alarm %d\n", clientreg));
213             (*(a->thecallback)) (clientreg, a->clientarg);
214             DEBUGMSGTL(("snmp_alarm", "alarm %d completed\n", clientreg));
215
216             if ((a = sa_find_specific(clientreg)) != NULL) {
217                 a->t_last.tv_sec = t_now.tv_sec;
218                 a->t_last.tv_usec = t_now.tv_usec;
219                 a->t_next.tv_sec = 0;
220                 a->t_next.tv_usec = 0;
221                 sa_update_entry(a);
222             } else {
223                 DEBUGMSGTL(("snmp_alarm", "alarm %d deleted itself\n",
224                             clientreg));
225             }
226         } else {
227             done = 1;
228         }
229     }
230 }
231
232
233
234 RETSIGTYPE
235 alarm_handler(int a)
236 {
237     run_alarms();
238     set_an_alarm();
239 }
240
241
242
243 int
244 get_next_alarm_delay_time(struct timeval *delta)
245 {
246     struct snmp_alarm *sa_ptr;
247     struct timeval  t_diff, t_now;
248
249     sa_ptr = sa_find_next();
250
251     if (sa_ptr) {
252         gettimeofday(&t_now, 0);
253
254         if ((t_now.tv_sec > sa_ptr->t_next.tv_sec) ||
255             ((t_now.tv_sec == sa_ptr->t_next.tv_sec) &&
256              (t_now.tv_usec > sa_ptr->t_next.tv_usec))) {
257             /*
258              * Time has already passed.  Return the smallest possible amount of
259              * time.  
260              */
261             delta->tv_sec = 0;
262             delta->tv_usec = 1;
263             return sa_ptr->clientreg;
264         } else {
265             /*
266              * Time is still in the future.  
267              */
268             t_diff.tv_sec = sa_ptr->t_next.tv_sec - t_now.tv_sec;
269             t_diff.tv_usec = sa_ptr->t_next.tv_usec - t_now.tv_usec;
270
271             while (t_diff.tv_usec < 0) {
272                 t_diff.tv_sec -= 1;
273                 t_diff.tv_usec += 1000000;
274             }
275
276             delta->tv_sec = t_diff.tv_sec;
277             delta->tv_usec = t_diff.tv_usec;
278             return sa_ptr->clientreg;
279         }
280     }
281
282     /*
283      * Nothing Left.  
284      */
285     return 0;
286 }
287
288
289 void
290 set_an_alarm(void)
291 {
292     struct timeval  delta;
293     int             nextalarm = get_next_alarm_delay_time(&delta);
294
295     /*
296      * We don't use signals if they asked us nicely not to.  It's expected
297      * they'll check the next alarm time and do their own calling of
298      * run_alarms().  
299      */
300
301     if (nextalarm && !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
302                                         NETSNMP_DS_LIB_ALARM_DONT_USE_SIG)) {
303 #ifndef WIN32
304 # ifdef HAVE_SETITIMER
305         struct itimerval it;
306
307         it.it_value.tv_sec = delta.tv_sec;
308         it.it_value.tv_usec = delta.tv_usec;
309         it.it_interval.tv_sec = 0;
310         it.it_interval.tv_usec = 0;
311
312         signal(SIGALRM, alarm_handler);
313         setitimer(ITIMER_REAL, &it, NULL);
314         DEBUGMSGTL(("snmp_alarm", "schedule alarm %d in %d.%03d seconds\n",
315                     nextalarm, delta.tv_sec, (delta.tv_usec / 1000)));
316 # else  /* HAVE_SETITIMER */
317 #  ifdef SIGALRM
318         signal(SIGALRM, alarm_handler);
319         alarm(delta.tv_sec);
320         DEBUGMSGTL(("snmp_alarm",
321                     "schedule alarm %d in roughly %d seconds\n", nextalarm,
322                     delta.tv_sec));
323 #  endif  /* SIGALRM */
324 # endif  /* HAVE_SETITIMER */
325 #endif  /* WIN32 */
326
327     } else {
328         DEBUGMSGTL(("snmp_alarm", "no alarms found to schedule\n"));
329     }
330 }
331
332
333
334 unsigned int
335 snmp_alarm_register(unsigned int when, unsigned int flags,
336                     SNMPAlarmCallback * thecallback, void *clientarg)
337 {
338     struct snmp_alarm **sa_pptr;
339     if (thealarms != NULL) {
340         for (sa_pptr = &thealarms; (*sa_pptr) != NULL;
341              sa_pptr = &((*sa_pptr)->next));
342     } else {
343         sa_pptr = &thealarms;
344     }
345
346     *sa_pptr = SNMP_MALLOC_STRUCT(snmp_alarm);
347     if (*sa_pptr == NULL)
348         return 0;
349
350     (*sa_pptr)->t.tv_sec = when;
351     (*sa_pptr)->t.tv_usec = 0;
352     (*sa_pptr)->flags = flags;
353     (*sa_pptr)->clientarg = clientarg;
354     (*sa_pptr)->thecallback = thecallback;
355     (*sa_pptr)->clientreg = regnum++;
356     (*sa_pptr)->next = NULL;
357     sa_update_entry(*sa_pptr);
358
359     DEBUGMSGTL(("snmp_alarm",
360                 "registered alarm %d, t = %d.%03d, flags=0x%02x\n",
361                 (*sa_pptr)->clientreg, (*sa_pptr)->t.tv_sec,
362                 ((*sa_pptr)->t.tv_usec / 1000), (*sa_pptr)->flags));
363
364     if (start_alarms)
365         set_an_alarm();
366     return (*sa_pptr)->clientreg;
367 }
368
369
370
371 unsigned int
372 snmp_alarm_register_hr(struct timeval t, unsigned int flags,
373                        SNMPAlarmCallback * cb, void *cd)
374 {
375     struct snmp_alarm **s = NULL;
376
377     for (s = &(thealarms); *s != NULL; s = &((*s)->next));
378
379     *s = SNMP_MALLOC_STRUCT(snmp_alarm);
380     if (*s == NULL) {
381         return 0;
382     }
383
384     (*s)->t.tv_sec = t.tv_sec;
385     (*s)->t.tv_usec = t.tv_usec;
386     (*s)->flags = flags;
387     (*s)->clientarg = cd;
388     (*s)->thecallback = cb;
389     (*s)->clientreg = regnum++;
390     (*s)->next = NULL;
391
392     sa_update_entry(*s);
393
394     DEBUGMSGTL(("snmp_alarm",
395                 "registered alarm %d, t = %d.%03d, flags=0x%02x\n",
396                 (*s)->clientreg, (*s)->t.tv_sec, ((*s)->t.tv_usec / 1000),
397                 (*s)->flags));
398
399     if (start_alarms) {
400         set_an_alarm();
401     }
402
403     return (*s)->clientreg;
404 }