Coverage Report

Created: 2023-06-07 06:42

/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
/**  @} */