4 * $Id: object_monitor.c,v 1.1.2.1 2004/06/20 21:54:09 nikki 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 */