4
* $Id: object_monitor.c,v 1.6 2002/07/23 19:02:56 rstory Exp $
6
* functions and data structures for cooperating code to monitor objects.
8
* WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
11
* WARNING! This code is under active development WARNING!
12
* WARNING! and is subject to change at any time. WARNING!
15
* WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
18
#include <net-snmp/net-snmp-config.h>
19
#include <net-snmp/net-snmp-includes.h>
20
#include <net-snmp/agent/net-snmp-agent-includes.h>
21
#include <net-snmp/library/container.h>
22
#include <net-snmp/library/snmp_assert.h>
24
#include "net-snmp/agent/object_monitor.h"
31
/**************************************************************************
33
* Private data structures
35
**************************************************************************/
37
* individual callback info for an object
39
typedef struct monitor_info_s {
41
/** priority for this callback */
44
/** handler that registred to watch this object */
45
netsnmp_mib_handler *watcher;
47
/** events that the watcher cares about */
50
/** callback function */
51
netsnmp_object_monitor_callback *cb;
53
/** pointer to data from the watcher */
56
struct monitor_info_s *next;
61
* list of watchers for a given object
63
typedef struct watcher_list_s {
65
/** netsnmp_index must be first! */
66
netsnmp_index monitored_object;
73
* temp holder for ordered list of callbacks
75
typedef struct callback_placeholder_s {
78
netsnmp_monitor_callback_header *cbh;
80
struct callback_placeholder_s *next;
82
} callback_placeholder;
85
/**************************************************************************
89
**************************************************************************/
94
static char need_init = 1;
95
static netsnmp_container *monitored_objects = NULL;
96
static netsnmp_monitor_callback_header *callback_pending_list;
97
static callback_placeholder *callback_ready_list;
102
static watcher_list *find_watchers(oid * object, size_t oid_len);
103
static int insert_watcher(oid *, size_t, monitor_info *);
104
static int check_registered(unsigned int event, oid * o, int o_l,
105
watcher_list ** pWl, monitor_info ** pMi);
106
static void move_pending_to_ready(void);
109
/**************************************************************************
113
**************************************************************************/
119
netsnmp_monitor_init(void)
124
callback_pending_list = NULL;
125
callback_ready_list = NULL;
127
monitored_objects = netsnmp_container_get("object_monitor:binary_array");
128
if (NULL != monitored_objects)
130
monitored_objects->compare = netsnmp_compare_netsnmp_index;
131
monitored_objects->ncompare = netsnmp_ncompare_netsnmp_index;
137
/**************************************************************************
139
* Registration functions
141
**************************************************************************/
144
* Register a callback for the specified object.
146
* @param object pointer to the OID of the object to monitor.
147
* @param oid_len length of the OID pointed to by object.
148
* @param priority the priority to associate with this callback. A
149
* higher number indicates higher priority. This
150
* allows multiple callbacks for the same object to
151
* coordinate the order in which they are called. If
152
* two callbacks register with the same priority, the
153
* order is undefined.
154
* @param events the events which the callback is interested in.
155
* @param watcher_data pointer to data that will be supplied to the
156
* callback method when an event occurs.
157
* @param cb pointer to the function to be called when an event occurs.
159
* NOTE: the combination of the object, priority and watcher_data must
160
* be unique, as they are the parameters for unregistering a
163
* @return SNMPERR_NOERR registration was successful
164
* @return SNMPERR_MALLOC memory allocation failed
165
* @return SNMPERR_VALUE the combination of the object, priority and
166
* watcher_data is not unique.
169
netsnmp_monitor_register(oid * object, size_t oid_len, int priority,
170
unsigned int events, void *watcher_data,
171
netsnmp_object_monitor_callback * cb)
176
netsnmp_assert(need_init == 0);
178
mi = calloc(1, sizeof(monitor_info));
180
return SNMPERR_MALLOC;
182
mi->priority = priority;
184
mi->watcher_data = watcher_data;
187
rc = insert_watcher(object, oid_len, mi);
188
if (rc != SNMPERR_SUCCESS)
195
* Unregister a callback for the specified object.
197
* @param object pointer to the OID of the object to monitor.
198
* @param oid_len length of the OID pointed to by object.
199
* @param priority the priority to associate with this callback.
200
* @param watcher_data pointer to data that was supplied when the
201
* callback was registered.
202
* @param cb pointer to the function to be called when an event occurs.
205
netsnmp_monitor_unregister(oid * object, size_t oid_len, int priority,
206
void *wd, netsnmp_object_monitor_callback * cb)
208
monitor_info *mi, *last;
210
watcher_list *wl = find_watchers(object, oid_len);
212
return SNMPERR_GENERR;
217
if ((mi->cb == cb) && (mi->priority == priority) &&
218
(mi->watcher_data == wd))
225
return SNMPERR_GENERR;
230
last->next = mi->next;
232
if (NULL == wl->head) {
233
CONTAINER_REMOVE(monitored_objects, wl);
234
free(wl->monitored_object.oids);
240
return SNMPERR_SUCCESS;
243
/**************************************************************************
245
* object monitor functions
247
**************************************************************************/
250
* Notifies the object monitor of an event.
252
* The object monitor funtions will save the callback information
253
* until all varbinds in the current PDU have been processed and
254
* a response has been sent. At that time, the object monitor will
255
* determine if there are any watchers monitoring for the event.
257
* NOTE: the actual type of the callback structure may vary. The
258
* object monitor functions require only that the start of
259
* the structure match the netsnmp_monitor_callback_header
260
* structure. It is up to the watcher and monitored objects
261
* to agree on the format of other data.
263
* @param cbh pointer to a callback header.
266
netsnmp_notify_monitor(netsnmp_monitor_callback_header * cbh)
269
netsnmp_assert(need_init == 0);
272
* put processing of until response has been sent
274
cbh->private = callback_pending_list;
275
callback_pending_list = cbh;
281
* check to see if a registration exists for an object/event combination
283
* @param event the event type to check for
284
* @param o the oid to check for
285
* @param o_l the length of the oid
287
* @returns TRUE(1) if a callback is registerd
288
* @returns FALSE(0) if no callback is registered
291
netsnmp_monitor_check_registered(int event, oid * o, int o_l)
293
return check_registered(event, o, o_l, NULL, NULL);
297
* Process all pending callbacks
299
* NOTE: this method is not in the public header, as it should only be
300
* called in one place, in the agent.
303
netsnmp_monitor_process_callbacks(void)
305
netsnmp_assert(need_init == 0);
306
netsnmp_assert(NULL == callback_ready_list);
308
if (NULL == callback_pending_list) {
309
DEBUGMSGT(("object_monitor", "No callbacks to process"));
313
DEBUGMSG(("object_monitor", "Checking for registered " "callbacks."));
316
* move an pending notification which has a registered watcher to the
317
* ready list. Free any other notifications.
319
move_pending_to_ready();
324
while (callback_ready_list) {
327
* pop off the first item
329
callback_placeholder *current_cbr;
330
current_cbr = callback_ready_list;
331
callback_ready_list = current_cbr->next;
334
* setup, then call callback
336
current_cbr->cbh->watcher_data = current_cbr->mi->watcher_data;
337
current_cbr->cbh->priority = current_cbr->mi->priority;
338
(*current_cbr->mi->cb) (current_cbr->cbh);
341
* release memory (don't free current_cbr->mi)
343
if (--(current_cbr->cbh->refs) == 0) {
344
free(current_cbr->cbh->monitored_object.oids);
345
free(current_cbr->cbh);
350
* check for any new pending notifications
352
move_pending_to_ready();
356
netsnmp_assert(callback_ready_list == NULL);
357
netsnmp_assert(callback_pending_list = NULL);
362
/**************************************************************************
364
* COOPERATIVE helpers
366
**************************************************************************/
368
* Notifies the object monitor of a cooperative event.
370
* This convenience function will build a
371
* @link netsnmp_monitor_callback_header@endlink and call
372
* @link netsnmp_notify_monitor@endlink.
374
* @param event the event type
375
* @param object pointer to the oid of the object sending the event
376
* @param len the lenght of the oid
377
* @param oid_steal set to true if the function may keep the pointer
378
* to the memory (and free it later). set to false
379
* to make the function allocate and copy the oid.
380
* @param object_info pointer to data supplied by the object for
381
* the callback. This pointer must remain valid,
382
* will be provided to each callback registered
383
* for the object (i.e. it will not be copied or
387
netsnmp_notify_cooperative(int event, oid * o, size_t o_len, char o_steal,
390
netsnmp_monitor_callback_cooperative *cbh;
392
netsnmp_assert(need_init == 0);
394
cbh = SNMP_MALLOC_TYPEDEF(netsnmp_monitor_callback_cooperative);
396
snmp_log(LOG_ERR, "could not allocate memory for "
397
"cooperative callback");
401
cbh->hdr.event = event;
402
cbh->hdr.object_info = object_info;
403
cbh->hdr.monitored_object.len = o_len;
406
cbh->hdr.monitored_object.oids = o;
408
cbh->hdr.monitored_object.oids = snmp_duplicate_objid(o, o_len);
411
netsnmp_notify_monitor((netsnmp_monitor_callback_header *) cbh);
414
#ifndef DOXYGEN_SHOULD_IGNORE_THIS
415
/*************************************************************************
416
*************************************************************************
417
*************************************************************************
418
* WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
421
* WARNING! This code is under active development WARNING!
422
* WARNING! and is subject to change at any time. WARNING!
425
* WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
426
*************************************************************************
427
*************************************************************************
428
*************************************************************************
430
static watcher_list *
431
find_watchers(oid * object, size_t oid_len)
438
return (watcher_list *)CONTAINER_FIND(monitored_objects, &oah);
442
insert_watcher(oid * object, size_t oid_len, monitor_info * mi)
444
watcher_list *wl = find_watchers(object, oid_len);
445
int rc = SNMPERR_SUCCESS;
449
monitor_info *last, *current;
451
netsnmp_assert(wl->head != NULL);
456
if (mi->priority == current->priority) {
458
* check for duplicate
460
if (mi->watcher_data == current->watcher_data)
461
return SNMPERR_VALUE; /** duplicate! */
462
} else if (mi->priority > current->priority) {
466
current = current->next;
472
mi->next = last->next;
478
* first watcher for this oid; set up list
480
wl = SNMP_MALLOC_TYPEDEF(watcher_list);
482
return SNMPERR_MALLOC;
487
wl->monitored_object.len = oid_len;
488
wl->monitored_object.oids = malloc(sizeof(oid) * oid_len);
489
if (NULL == wl->monitored_object.oids) {
491
return SNMPERR_MALLOC;
493
memcpy(wl->monitored_object.oids, object, sizeof(oid) * oid_len);
496
* add watcher, and insert into array
500
rc = CONTAINER_INSERT(monitored_objects, wl);
502
free(wl->monitored_object.oids);
512
* check to see if a registration exists for an object/event combination
514
* @param event the event type to check for
515
* @param o the oid to check for
516
* @param o_l the length of the oid
517
* @param pWl if a pointer to a watcher_list pointer is supplied,
518
* upon return the watcher list pointer will be set to
519
* the watcher list for the object, or NULL if there are
520
* no watchers for the object.
521
* @param pMi if a pointer to a monitor_info pointer is supplied,
522
* upon return the pointer will be set to the first
523
* monitor_info object for the specified event.
525
* @returns TRUE(1) if a callback is registerd
526
* @returns FALSE(0) if no callback is registered
529
check_registered(unsigned int event, oid * o, int o_l,
530
watcher_list ** pWl, monitor_info ** pMi)
535
netsnmp_assert(need_init == 0);
538
* check to see if anyone has registered for callbacks
541
wl = find_watchers(o, o_l);
548
* check if any watchers are watching for this specific event
550
for (mi = wl->head; mi; mi = mi->next) {
552
if (mi->events & event) {
566
insert_ready(callback_placeholder * new_cbr)
568
callback_placeholder *current_cbr, *last_cbr;
571
* insert in callback ready list
574
current_cbr = callback_ready_list;
575
while (current_cbr) {
577
if (new_cbr->mi->priority > current_cbr->mi->priority)
580
last_cbr = current_cbr;
581
current_cbr = current_cbr->next;
583
if (NULL == last_cbr) {
584
new_cbr->next = callback_ready_list;
585
callback_ready_list = new_cbr;
587
new_cbr->next = last_cbr->next;
588
last_cbr->next = new_cbr;
595
* move an pending notification which has a registered watcher to the
596
* ready list. Free any other notifications.
599
move_pending_to_ready(void)
602
* check to see if anyone has registered for callbacks
605
while (callback_pending_list) {
609
netsnmp_monitor_callback_header *cbp;
614
cbp = callback_pending_list;
615
callback_pending_list = cbp->private; /** next */
617
if (0 == check_registered(cbp->event, cbp->monitored_object.oids,
618
cbp->monitored_object.len, &wl,
622
* nobody watching, free memory
629
* Found at least one; check the rest of the list and
630
* save callback for processing
632
for (; mi; mi = mi->next) {
634
callback_placeholder *new_cbr;
636
if (0 == (mi->events & cbp->event))
640
* create temprory placeholder.
642
* I hate to allocate memory here, as I'd like this code to
643
* be fast and lean. But I don't have time to think of another
644
* solution os this will have to do for now.
646
* I need a list of monitor_info (mi) objects for each
647
* callback which has registered for the event, and want
648
* that list sorted by the priority required by the watcher.
650
new_cbr = SNMP_MALLOC_TYPEDEF(callback_placeholder);
651
if (NULL == new_cbr) {
652
snmp_log(LOG_ERR, "malloc failed, callback dropped.");
660
* insert in callback ready list
662
insert_ready(new_cbr);
665
} /** end cbp loop */
667
netsnmp_assert(callback_pending_list == NULL);
671
#if defined TESTING_OBJECT_MONITOR
672
/**************************************************************************
674
* (untested) TEST CODE
678
dummy_callback(netsnmp_monitor_callback_header * cbh)
680
printf("Callback received.\n");
684
dump_watchers(netsnmp_index *oah, void *)
686
watcher_list *wl = (watcher_list *) oah;
687
netsnmp_monitor_callback_header *cbh = wl->head;
689
printf("Watcher List for OID ");
690
print_objid(wl->hdr->oids, wl->hdr->len);
695
printf("Priority = %d;, Events = %d; Watcher Data = 0x%x\n",
696
cbh->priority, cbh->events, cbh->watcher_data);
703
main(int argc, char **argv)
706
oid object[3] = { 1, 3, 6 };
713
netsnmp_monitor_init();
718
rc = netsnmp_monitor_register(object, object_len, 0,
719
EVENT_ROW_ADD, (void *) 0xdeadbeef,
721
printf("insert an object: %d\n", rc);
724
* insert same object, new priority
726
netsnmp_monitor_register(object, object_len, 10,
727
EVENT_ROW_ADD, (void *) 0xdeadbeef,
729
printf("insert same object, new priority: %d\n", rc);
732
* insert same object, same priority, new data
734
netsnmp_monitor_register(object, object_len, 10,
735
EVENT_ROW_ADD, (void *) 0xbeefdead,
737
printf("insert same object, same priority, new data: %d\n", rc);
740
* insert same object, same priority, same data
742
netsnmp_monitor_register(object, object_len, 10,
743
EVENT_ROW_ADD, (void *) 0xbeefdead,
745
printf("insert same object, same priority, new data: %d\n", rc);
751
CONTAINER_FOR_EACH(monitored_objects, dump_watchers, NULL);
753
#endif /** defined TESTING_OBJECT_MONITOR */
755
#endif /** DOXYGEN_SHOULD_IGNORE_THIS */