net-snmp 5.7
object_monitor.c
00001 /*
00002  * object_monitor.c
00003  *
00004  * $Id$
00005  *
00006  * functions and data structures for cooperating code to monitor objects.
00007  *
00008  * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
00009  * WARNING!                                                       WARNING!
00010  * WARNING!                                                       WARNING!
00011  * WARNING!         This code is under active development         WARNING!
00012  * WARNING!         and is subject to change at any time.         WARNING!
00013  * WARNING!                                                       WARNING!
00014  * WARNING!                                                       WARNING!
00015  * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
00016  */
00017 
00018 #include <net-snmp/net-snmp-config.h>
00019 #include <net-snmp/net-snmp-includes.h>
00020 #include <net-snmp/agent/net-snmp-agent-includes.h>
00021 #include <net-snmp/library/container.h>
00022 #include <net-snmp/library/snmp_assert.h>
00023 
00024 #include "net-snmp/agent/object_monitor.h"
00025 
00026 #if ! defined TRUE
00027 #  define TRUE 1
00028 #elsif TRUE != 1
00029 error "TRUE != 1"
00030 #endif
00031 /**************************************************************************
00032  *
00033  * Private data structures
00034  *
00035  **************************************************************************/
00036     /*
00037      * individual callback info for an object
00038      */
00039     typedef struct monitor_info_s {
00040 
00042     int             priority;
00043 
00045     netsnmp_mib_handler *watcher;
00046 
00048     unsigned int    events;
00049 
00051     netsnmp_object_monitor_callback *cb;
00052 
00054     void           *watcher_data;
00055 
00056     struct monitor_info_s *next;
00057 
00058 } monitor_info;
00059 
00060 /*
00061  * list of watchers for a given object
00062  */
00063 typedef struct watcher_list_s {
00064 
00066     netsnmp_index  monitored_object;
00067 
00068     monitor_info   *head;
00069 
00070 } watcher_list;
00071 
00072 /*
00073  * temp holder for ordered list of callbacks
00074  */
00075 typedef struct callback_placeholder_s {
00076 
00077     monitor_info   *mi;
00078     netsnmp_monitor_callback_header *cbh;
00079 
00080     struct callback_placeholder_s *next;
00081 
00082 } callback_placeholder;
00083 
00084 
00085 /**************************************************************************
00086  *
00087  * 
00088  *
00089  **************************************************************************/
00090 
00091 /*
00092  * local statics
00093  */
00094 static char     need_init = 1;
00095 static netsnmp_container *monitored_objects = NULL;
00096 static netsnmp_monitor_callback_header *callback_pending_list;
00097 static callback_placeholder *callback_ready_list;
00098 
00099 /*
00100  * local prototypes
00101  */
00102 static watcher_list *find_watchers(oid * object, size_t oid_len);
00103 static int      insert_watcher(oid *, size_t, monitor_info *);
00104 static int      check_registered(unsigned int event, oid * o, int o_l,
00105                                  watcher_list ** pWl, monitor_info ** pMi);
00106 static void     move_pending_to_ready(void);
00107 
00108 
00109 /**************************************************************************
00110  *
00111  * Public functions
00112  *
00113  **************************************************************************/
00114 
00115 /*
00116  * 
00117  */
00118 void
00119 netsnmp_monitor_init(void)
00120 {
00121     if (!need_init)
00122         return;
00123 
00124     callback_pending_list = NULL;
00125     callback_ready_list = NULL;
00126 
00127     monitored_objects = netsnmp_container_get("object_monitor:binary_array");
00128     if (NULL != monitored_objects)
00129         need_init = 0;
00130     monitored_objects->compare = netsnmp_compare_netsnmp_index;
00131     monitored_objects->ncompare = netsnmp_ncompare_netsnmp_index;
00132     
00133     return;
00134 }
00135 
00136 
00137 /**************************************************************************
00138  *
00139  * Registration functions
00140  *
00141  **************************************************************************/
00142 
00168 int
00169 netsnmp_monitor_register(oid * object, size_t oid_len, int priority,
00170                          unsigned int events, void *watcher_data,
00171                          netsnmp_object_monitor_callback * cb)
00172 {
00173     monitor_info   *mi;
00174     int             rc;
00175 
00176     netsnmp_assert(need_init == 0);
00177 
00178     mi = calloc(1, sizeof(monitor_info));
00179     if (NULL == mi)
00180         return SNMPERR_MALLOC;
00181 
00182     mi->priority = priority;
00183     mi->events = events;
00184     mi->watcher_data = watcher_data;
00185     mi->cb = cb;
00186 
00187     rc = insert_watcher(object, oid_len, mi);
00188     if (rc != SNMPERR_SUCCESS)
00189         free(mi);
00190 
00191     return rc;
00192 }
00193 
00204 int
00205 netsnmp_monitor_unregister(oid * object, size_t oid_len, int priority,
00206                            void *wd, netsnmp_object_monitor_callback * cb)
00207 {
00208     monitor_info   *mi, *last;
00209 
00210     watcher_list   *wl = find_watchers(object, oid_len);
00211     if (NULL == wl)
00212         return SNMPERR_GENERR;
00213 
00214     last = NULL;
00215     mi = wl->head;
00216     while (mi) {
00217         if ((mi->cb == cb) && (mi->priority == priority) &&
00218             (mi->watcher_data == wd))
00219             break;
00220         last = mi;
00221         mi = mi->next;
00222     }
00223 
00224     if (NULL == mi)
00225         return SNMPERR_GENERR;
00226 
00227     if (NULL == last)
00228         wl->head = mi->next;
00229     else
00230         last->next = mi->next;
00231 
00232     if (NULL == wl->head) {
00233         CONTAINER_REMOVE(monitored_objects, wl);
00234         free(wl->monitored_object.oids);
00235         free(wl);
00236     }
00237 
00238     free(mi);
00239 
00240     return SNMPERR_SUCCESS;
00241 }
00242 
00243 /**************************************************************************
00244  *
00245  * object monitor functions
00246  *
00247  **************************************************************************/
00248 
00265 void
00266 netsnmp_notify_monitor(netsnmp_monitor_callback_header * cbh)
00267 {
00268 
00269     netsnmp_assert(need_init == 0);
00270 
00271     /*
00272      * put processing of until response has been sent
00273      */
00274     cbh->private = callback_pending_list;
00275     callback_pending_list = cbh;
00276 
00277     return;
00278 }
00279 
00290 int
00291 netsnmp_monitor_check_registered(int event, oid * o, int o_l)
00292 {
00293     return check_registered(event, o, o_l, NULL, NULL);
00294 }
00295 
00302 void
00303 netsnmp_monitor_process_callbacks(void)
00304 {
00305     netsnmp_assert(need_init == 0);
00306     netsnmp_assert(NULL == callback_ready_list);
00307 
00308     if (NULL == callback_pending_list) {
00309         DEBUGMSGT(("object_monitor", "No callbacks to process"));
00310         return;
00311     }
00312 
00313     DEBUGMSG(("object_monitor", "Checking for registered " "callbacks."));
00314 
00315     /*
00316      * move an pending notification which has a registered watcher to the
00317      * ready list. Free any other notifications.
00318      */
00319     move_pending_to_ready();
00320 
00321     /*
00322      * call callbacks
00323      */
00324     while (callback_ready_list) {
00325 
00326         /*
00327          * pop off the first item
00328          */
00329         callback_placeholder *current_cbr;
00330         current_cbr = callback_ready_list;
00331         callback_ready_list = current_cbr->next;
00332 
00333         /*
00334          * setup, then call callback
00335          */
00336         current_cbr->cbh->watcher_data = current_cbr->mi->watcher_data;
00337         current_cbr->cbh->priority = current_cbr->mi->priority;
00338         (*current_cbr->mi->cb) (current_cbr->cbh);
00339 
00340         /*
00341          * release memory (don't free current_cbr->mi)
00342          */
00343         if (--(current_cbr->cbh->refs) == 0) {
00344             free(current_cbr->cbh->monitored_object.oids);
00345             free(current_cbr->cbh);
00346         }
00347         free(current_cbr);
00348 
00349         /*
00350          * check for any new pending notifications
00351          */
00352         move_pending_to_ready();
00353 
00354     }
00355 
00356     netsnmp_assert(callback_ready_list == NULL);
00357     netsnmp_assert(callback_pending_list = NULL);
00358 
00359     return;
00360 }
00361 
00362 /**************************************************************************
00363  *
00364  * COOPERATIVE helpers
00365  *
00366  **************************************************************************/
00386 void
00387 netsnmp_notify_cooperative(int event, oid * o, size_t o_len, char o_steal,
00388                            void *object_info)
00389 {
00390     netsnmp_monitor_callback_cooperative *cbh;
00391 
00392     netsnmp_assert(need_init == 0);
00393 
00394     cbh = SNMP_MALLOC_TYPEDEF(netsnmp_monitor_callback_cooperative);
00395     if (NULL == cbh) {
00396         snmp_log(LOG_ERR, "could not allocate memory for "
00397                  "cooperative callback");
00398         return;
00399     }
00400 
00401     cbh->hdr.event = event;
00402     cbh->hdr.object_info = object_info;
00403     cbh->hdr.monitored_object.len = o_len;
00404 
00405     if (o_steal) {
00406         cbh->hdr.monitored_object.oids = o;
00407     } else {
00408         cbh->hdr.monitored_object.oids = snmp_duplicate_objid(o, o_len);
00409     }
00410 
00411     netsnmp_notify_monitor((netsnmp_monitor_callback_header *) cbh);
00412 }
00413 
00415 /*************************************************************************
00416  *************************************************************************
00417  *************************************************************************
00418  * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
00419  * WARNING!                                                       WARNING!
00420  * WARNING!                                                       WARNING!
00421  * WARNING!         This code is under active development         WARNING!
00422  * WARNING!         and is subject to change at any time.         WARNING!
00423  * WARNING!                                                       WARNING!
00424  * WARNING!                                                       WARNING!
00425  * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
00426  *************************************************************************
00427  *************************************************************************
00428  *************************************************************************
00429  */
00430 static watcher_list *
00431 find_watchers(oid * object, size_t oid_len)
00432 {
00433     netsnmp_index oah;
00434 
00435     oah.oids = object;
00436     oah.len = oid_len;
00437 
00438     return (watcher_list *)CONTAINER_FIND(monitored_objects, &oah);
00439 }
00440 
00441 static int
00442 insert_watcher(oid * object, size_t oid_len, monitor_info * mi)
00443 {
00444     watcher_list   *wl = find_watchers(object, oid_len);
00445     int             rc = SNMPERR_SUCCESS;
00446 
00447     if (NULL != wl) {
00448 
00449         monitor_info   *last, *current;
00450 
00451         netsnmp_assert(wl->head != NULL);
00452 
00453         last = NULL;
00454         current = wl->head;
00455         while (current) {
00456             if (mi->priority == current->priority) {
00457                 /*
00458                  * check for duplicate
00459                  */
00460                 if (mi->watcher_data == current->watcher_data)
00461                     return SNMPERR_VALUE; 
00462             } else if (mi->priority > current->priority) {
00463                 break;
00464             }
00465             last = current;
00466             current = current->next;
00467         }
00468         if (NULL == last) {
00469             mi->next = wl->head;
00470             wl->head = mi;
00471         } else {
00472             mi->next = last->next;
00473             last->next = mi;
00474         }
00475     } else {
00476 
00477         /*
00478          * first watcher for this oid; set up list
00479          */
00480         wl = SNMP_MALLOC_TYPEDEF(watcher_list);
00481         if (NULL == wl)
00482             return SNMPERR_MALLOC;
00483 
00484         /*
00485          * copy index oid
00486          */
00487         wl->monitored_object.len = oid_len;
00488         wl->monitored_object.oids = malloc(sizeof(oid) * oid_len);
00489         if (NULL == wl->monitored_object.oids) {
00490             free(wl);
00491             return SNMPERR_MALLOC;
00492         }
00493         memcpy(wl->monitored_object.oids, object, sizeof(oid) * oid_len);
00494 
00495         /*
00496          * add watcher, and insert into array
00497          */
00498         wl->head = mi;
00499         mi->next = NULL;
00500         rc = CONTAINER_INSERT(monitored_objects, wl);
00501         if (rc) {
00502             free(wl->monitored_object.oids);
00503             free(wl);
00504             return rc;
00505         }
00506     }
00507     return rc;
00508 }
00509 
00528 static int
00529 check_registered(unsigned int event, oid * o, int o_l,
00530                  watcher_list ** pWl, monitor_info ** pMi)
00531 {
00532     watcher_list   *wl;
00533     monitor_info   *mi;
00534 
00535     netsnmp_assert(need_init == 0);
00536 
00537     /*
00538      * check to see if anyone has registered for callbacks
00539      * for the object.
00540      */
00541     wl = find_watchers(o, o_l);
00542     if (pWl)
00543         *pWl = wl;
00544     if (NULL == wl)
00545         return 0;
00546 
00547     /*
00548      * check if any watchers are watching for this specific event
00549      */
00550     for (mi = wl->head; mi; mi = mi->next) {
00551 
00552         if (mi->events & event) {
00553             if (pMi)
00554                 *pMi = mi;
00555             return TRUE;
00556         }
00557     }
00558 
00559     return 0;
00560 }
00561 
00565 inline void
00566 insert_ready(callback_placeholder * new_cbr)
00567 {
00568     callback_placeholder *current_cbr, *last_cbr;
00569 
00570     /*
00571      * insert in callback ready list
00572      */
00573     last_cbr = NULL;
00574     current_cbr = callback_ready_list;
00575     while (current_cbr) {
00576 
00577         if (new_cbr->mi->priority > current_cbr->mi->priority)
00578             break;
00579 
00580         last_cbr = current_cbr;
00581         current_cbr = current_cbr->next;
00582     }
00583     if (NULL == last_cbr) {
00584         new_cbr->next = callback_ready_list;
00585         callback_ready_list = new_cbr;
00586     } else {
00587         new_cbr->next = last_cbr->next;
00588         last_cbr->next = new_cbr;
00589     }
00590 }
00591 
00598 static void
00599 move_pending_to_ready(void)
00600 {
00601     /*
00602      * check to see if anyone has registered for callbacks
00603      * for each object.
00604      */
00605     while (callback_pending_list) {
00606 
00607         watcher_list   *wl;
00608         monitor_info   *mi;
00609         netsnmp_monitor_callback_header *cbp;
00610 
00611         /*
00612          * pop off first item
00613          */
00614         cbp = callback_pending_list;
00615         callback_pending_list = cbp->private; 
00617         if (0 == check_registered(cbp->event, cbp->monitored_object.oids,
00618                                   cbp->monitored_object.len, &wl,
00619                                   &mi)) {
00620 
00621             /*
00622              * nobody watching, free memory
00623              */
00624             free(cbp);
00625             continue;
00626         }
00627 
00628         /*
00629          * Found at least one; check the rest of the list and
00630          * save callback for processing
00631          */
00632         for (; mi; mi = mi->next) {
00633 
00634             callback_placeholder *new_cbr;
00635 
00636             if (0 == (mi->events & cbp->event))
00637                 continue;
00638 
00639             /*
00640              * create temprory placeholder.
00641              *
00642              * I hate to allocate memory here, as I'd like this code to
00643              * be fast and lean. But I don't have time to think of another
00644              * solution os this will have to do for now.
00645              *
00646              * I need a list of monitor_info (mi) objects for each
00647              * callback which has registered for the event, and want
00648              * that list sorted by the priority required by the watcher.
00649              */
00650             new_cbr = SNMP_MALLOC_TYPEDEF(callback_placeholder);
00651             if (NULL == new_cbr) {
00652                 snmp_log(LOG_ERR, "malloc failed, callback dropped.");
00653                 continue;
00654             }
00655             new_cbr->cbh = cbp;
00656             new_cbr->mi = mi;
00657             ++cbp->refs;
00658 
00659             /*
00660              * insert in callback ready list
00661              */
00662             insert_ready(new_cbr);
00663 
00664         } 
00665     } 
00667     netsnmp_assert(callback_pending_list == NULL);
00668 }
00669 
00670 
00671 #if defined TESTING_OBJECT_MONITOR
00672 /**************************************************************************
00673  *
00674  * (untested) TEST CODE
00675  *
00676  */
00677 void
00678 dummy_callback(netsnmp_monitor_callback_header * cbh)
00679 {
00680     printf("Callback received.\n");
00681 }
00682 
00683 void
00684 dump_watchers(netsnmp_index *oah, void *)
00685 {
00686     watcher_list   *wl = (watcher_list *) oah;
00687     netsnmp_monitor_callback_header *cbh = wl->head;
00688 
00689     printf("Watcher List for OID ");
00690     print_objid(wl->hdr->oids, wl->hdr->len);
00691     printf("\n");
00692 
00693     while (cbh) {
00694 
00695         printf("Priority = %d;, Events = %d; Watcher Data = 0x%x\n",
00696                cbh->priority, cbh->events, cbh->watcher_data);
00697 
00698         cbh = cbh->private;
00699     }
00700 }
00701 
00702 void
00703 main(int argc, char **argv)
00704 {
00705 
00706     oid             object[3] = { 1, 3, 6 };
00707     int             object_len = 3;
00708     int             rc;
00709 
00710     /*
00711      * init
00712      */
00713     netsnmp_monitor_init();
00714 
00715     /*
00716      * insert an object
00717      */
00718     rc = netsnmp_monitor_register(object, object_len, 0,
00719                                   EVENT_ROW_ADD, (void *) 0xdeadbeef,
00720                                   dummy_callback);
00721     printf("insert an object: %d\n", rc);
00722 
00723     /*
00724      * insert same object, new priority
00725      */
00726     netsnmp_monitor_register(object, object_len, 10,
00727                              EVENT_ROW_ADD, (void *) 0xdeadbeef,
00728                              dummy_callback);
00729     printf("insert same object, new priority: %d\n", rc);
00730 
00731     /*
00732      * insert same object, same priority, new data
00733      */
00734     netsnmp_monitor_register(object, object_len, 10,
00735                              EVENT_ROW_ADD, (void *) 0xbeefdead,
00736                              dummy_callback);
00737     printf("insert same object, same priority, new data: %d\n", rc);
00738 
00739     /*
00740      * insert same object, same priority, same data
00741      */
00742     netsnmp_monitor_register(object, object_len, 10,
00743                              EVENT_ROW_ADD, (void *) 0xbeefdead,
00744                              dummy_callback);
00745     printf("insert same object, same priority, new data: %d\n", rc);
00746 
00747 
00748     /*
00749      * dump table
00750      */
00751     CONTAINER_FOR_EACH(monitored_objects, dump_watchers, NULL);
00752 }
00753 #endif