4 * $Id: object_monitor.c,v 1.6.2.1 2003/03/14 17:37:07 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 */