and added files
[bcm963xx.git] / userapps / opensource / net-snmp / agent / object_monitor.c
diff --git a/userapps/opensource/net-snmp/agent/object_monitor.c b/userapps/opensource/net-snmp/agent/object_monitor.c
new file mode 100644 (file)
index 0000000..c4c0318
--- /dev/null
@@ -0,0 +1,755 @@
+/*
+ * object_monitor.c
+ *
+ * $Id: object_monitor.c,v 1.6.2.1 2003/03/14 17:37:07 rstory Exp $
+ *
+ * functions and data structures for cooperating code to monitor objects.
+ *
+ * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
+ * WARNING!                                                       WARNING!
+ * WARNING!                                                       WARNING!
+ * WARNING!         This code is under active development         WARNING!
+ * WARNING!         and is subject to change at any time.         WARNING!
+ * WARNING!                                                       WARNING!
+ * WARNING!                                                       WARNING!
+ * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
+ */
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/library/container.h>
+#include <net-snmp/library/snmp_assert.h>
+
+#include "net-snmp/agent/object_monitor.h"
+
+#if ! defined TRUE
+#  define TRUE 1
+#elsif TRUE != 1
+error "TRUE != 1"
+#endif
+/**************************************************************************
+ *
+ * Private data structures
+ *
+ **************************************************************************/
+    /*
+     * individual callback info for an object
+     */
+    typedef struct monitor_info_s {
+
+   /** priority for this callback */
+    int             priority;
+
+   /** handler that registred to watch this object */
+    netsnmp_mib_handler *watcher;
+
+   /** events that the watcher cares about */
+    unsigned int    events;
+
+   /** callback function */
+    netsnmp_object_monitor_callback *cb;
+
+   /** pointer to data from the watcher */
+    void           *watcher_data;
+
+    struct monitor_info_s *next;
+
+} monitor_info;
+
+/*
+ * list of watchers for a given object
+ */
+typedef struct watcher_list_s {
+
+   /** netsnmp_index must be first! */
+    netsnmp_index  monitored_object;
+
+    monitor_info   *head;
+
+} watcher_list;
+
+/*
+ * temp holder for ordered list of callbacks
+ */
+typedef struct callback_placeholder_s {
+
+    monitor_info   *mi;
+    netsnmp_monitor_callback_header *cbh;
+
+    struct callback_placeholder_s *next;
+
+} callback_placeholder;
+
+
+/**************************************************************************
+ *
+ * 
+ *
+ **************************************************************************/
+
+/*
+ * local statics
+ */
+static char     need_init = 1;
+static netsnmp_container *monitored_objects = NULL;
+static netsnmp_monitor_callback_header *callback_pending_list;
+static callback_placeholder *callback_ready_list;
+
+/*
+ * local prototypes
+ */
+static watcher_list *find_watchers(oid * object, size_t oid_len);
+static int      insert_watcher(oid *, size_t, monitor_info *);
+static int      check_registered(unsigned int event, oid * o, int o_l,
+                                 watcher_list ** pWl, monitor_info ** pMi);
+static void     move_pending_to_ready(void);
+
+
+/**************************************************************************
+ *
+ * Public functions
+ *
+ **************************************************************************/
+
+/*
+ * 
+ */
+void
+netsnmp_monitor_init(void)
+{
+    if (!need_init)
+        return;
+
+    callback_pending_list = NULL;
+    callback_ready_list = NULL;
+
+    monitored_objects = netsnmp_container_get("object_monitor:binary_array");
+    if (NULL != monitored_objects)
+        need_init = 0;
+    monitored_objects->compare = netsnmp_compare_netsnmp_index;
+    monitored_objects->ncompare = netsnmp_ncompare_netsnmp_index;
+    
+    return;
+}
+
+
+/**************************************************************************
+ *
+ * Registration functions
+ *
+ **************************************************************************/
+
+/**
+ * Register a callback for the specified object.
+ *
+ * @param object  pointer to the OID of the object to monitor.
+ * @param oid_len length of the OID pointed to by object.
+ * @param priority the priority to associate with this callback. A
+ *                 higher number indicates higher priority. This
+ *                 allows multiple callbacks for the same object to
+ *                 coordinate the order in which they are called. If
+ *                 two callbacks register with the same priority, the
+ *                 order is undefined.
+ * @param events  the events which the callback is interested in.
+ * @param watcher_data pointer to data that will be supplied to the
+ *                     callback method when an event occurs.
+ * @param cb pointer to the function to be called when an event occurs.
+ *
+ * NOTE: the combination of the object, priority and watcher_data must
+ *       be unique, as they are the parameters for unregistering a
+ *       callback.
+ *
+ * @return SNMPERR_NOERR registration was successful
+ * @return SNMPERR_MALLOC memory allocation failed
+ * @return SNMPERR_VALUE the combination of the object, priority and
+ *                        watcher_data is not unique.
+ */
+int
+netsnmp_monitor_register(oid * object, size_t oid_len, int priority,
+                         unsigned int events, void *watcher_data,
+                         netsnmp_object_monitor_callback * cb)
+{
+    monitor_info   *mi;
+    int             rc;
+
+    netsnmp_assert(need_init == 0);
+
+    mi = calloc(1, sizeof(monitor_info));
+    if (NULL == mi)
+        return SNMPERR_MALLOC;
+
+    mi->priority = priority;
+    mi->events = events;
+    mi->watcher_data = watcher_data;
+    mi->cb = cb;
+
+    rc = insert_watcher(object, oid_len, mi);
+    if (rc != SNMPERR_SUCCESS)
+        free(mi);
+
+    return rc;
+}
+
+/**
+ * Unregister a callback for the specified object.
+ *
+ * @param object  pointer to the OID of the object to monitor.
+ * @param oid_len length of the OID pointed to by object.
+ * @param priority the priority to associate with this callback.
+ * @param watcher_data pointer to data that was supplied when the
+ *                     callback was registered.
+ * @param cb pointer to the function to be called when an event occurs.
+ */
+int
+netsnmp_monitor_unregister(oid * object, size_t oid_len, int priority,
+                           void *wd, netsnmp_object_monitor_callback * cb)
+{
+    monitor_info   *mi, *last;
+
+    watcher_list   *wl = find_watchers(object, oid_len);
+    if (NULL == wl)
+        return SNMPERR_GENERR;
+
+    last = NULL;
+    mi = wl->head;
+    while (mi) {
+        if ((mi->cb == cb) && (mi->priority == priority) &&
+            (mi->watcher_data == wd))
+            break;
+        last = mi;
+        mi = mi->next;
+    }
+
+    if (NULL == mi)
+        return SNMPERR_GENERR;
+
+    if (NULL == last)
+        wl->head = mi->next;
+    else
+        last->next = mi->next;
+
+    if (NULL == wl->head) {
+        CONTAINER_REMOVE(monitored_objects, wl);
+        free(wl->monitored_object.oids);
+        free(wl);
+    }
+
+    free(mi);
+
+    return SNMPERR_SUCCESS;
+}
+
+/**************************************************************************
+ *
+ * object monitor functions
+ *
+ **************************************************************************/
+
+/**
+ * Notifies the object monitor of an event.
+ *
+ * The object monitor funtions will save the callback information
+ * until all varbinds in the current PDU have been processed and
+ * a response has been sent. At that time, the object monitor will
+ * determine if there are any watchers monitoring for the event.
+ *
+ * NOTE: the actual type of the callback structure may vary. The
+ *       object monitor functions require only that the start of
+ *       the structure match the netsnmp_monitor_callback_header
+ *       structure. It is up to the watcher and monitored objects
+ *       to agree on the format of other data.
+ *
+ * @param cbh pointer to a callback header.
+ */
+void
+netsnmp_notify_monitor(netsnmp_monitor_callback_header * cbh)
+{
+
+    netsnmp_assert(need_init == 0);
+
+    /*
+     * put processing of until response has been sent
+     */
+    cbh->private = callback_pending_list;
+    callback_pending_list = cbh;
+
+    return;
+}
+
+/**
+ * check to see if a registration exists for an object/event combination
+ *
+ * @param event the event type to check for
+ * @param o     the oid to check for
+ * @param o_l   the length of the oid
+ *
+ * @returns TRUE(1) if a callback is registerd
+ * @returns FALSE(0) if no callback is registered
+ */
+int
+netsnmp_monitor_check_registered(int event, oid * o, int o_l)
+{
+    return check_registered(event, o, o_l, NULL, NULL);
+}
+
+/**
+ * Process all pending callbacks
+ *
+ * NOTE: this method is not in the public header, as it should only be
+ *       called in one place, in the agent.
+ */
+void
+netsnmp_monitor_process_callbacks(void)
+{
+    netsnmp_assert(need_init == 0);
+    netsnmp_assert(NULL == callback_ready_list);
+
+    if (NULL == callback_pending_list) {
+        DEBUGMSGT(("object_monitor", "No callbacks to process"));
+        return;
+    }
+
+    DEBUGMSG(("object_monitor", "Checking for registered " "callbacks."));
+
+    /*
+     * move an pending notification which has a registered watcher to the
+     * ready list. Free any other notifications.
+     */
+    move_pending_to_ready();
+
+    /*
+     * call callbacks
+     */
+    while (callback_ready_list) {
+
+        /*
+         * pop off the first item
+         */
+        callback_placeholder *current_cbr;
+        current_cbr = callback_ready_list;
+        callback_ready_list = current_cbr->next;
+
+        /*
+         * setup, then call callback
+         */
+        current_cbr->cbh->watcher_data = current_cbr->mi->watcher_data;
+        current_cbr->cbh->priority = current_cbr->mi->priority;
+        (*current_cbr->mi->cb) (current_cbr->cbh);
+
+        /*
+         * release memory (don't free current_cbr->mi)
+         */
+        if (--(current_cbr->cbh->refs) == 0) {
+            free(current_cbr->cbh->monitored_object.oids);
+            free(current_cbr->cbh);
+        }
+        free(current_cbr);
+
+        /*
+         * check for any new pending notifications
+         */
+        move_pending_to_ready();
+
+    }
+
+    netsnmp_assert(callback_ready_list == NULL);
+    netsnmp_assert(callback_pending_list = NULL);
+
+    return;
+}
+
+/**************************************************************************
+ *
+ * COOPERATIVE helpers
+ *
+ **************************************************************************/
+/**
+ * Notifies the object monitor of a cooperative event.
+ *
+ * This convenience function will build a
+ * @link netsnmp_monitor_callback_header@endlink and call
+ * @link netsnmp_notify_monitor@endlink.
+ *
+ * @param event the event type
+ * @param object pointer to the oid of the object sending the event
+ * @param len    the lenght of the oid
+ * @param oid_steal set to true if the function may keep the pointer
+ *                  to the memory (and free it later). set to false
+ *                  to make the function allocate and copy the oid.
+ * @param object_info pointer to data supplied by the object for
+ *                    the callback. This pointer must remain valid,
+ *                    will be provided to each callback registered
+ *                    for the object (i.e. it will not be copied or
+ *                    freed).
+ */
+void
+netsnmp_notify_cooperative(int event, oid * o, size_t o_len, char o_steal,
+                           void *object_info)
+{
+    netsnmp_monitor_callback_cooperative *cbh;
+
+    netsnmp_assert(need_init == 0);
+
+    cbh = SNMP_MALLOC_TYPEDEF(netsnmp_monitor_callback_cooperative);
+    if (NULL == cbh) {
+        snmp_log(LOG_ERR, "could not allocate memory for "
+                 "cooperative callback");
+        return;
+    }
+
+    cbh->hdr.event = event;
+    cbh->hdr.object_info = object_info;
+    cbh->hdr.monitored_object.len = o_len;
+
+    if (o_steal) {
+        cbh->hdr.monitored_object.oids = o;
+    } else {
+        cbh->hdr.monitored_object.oids = snmp_duplicate_objid(o, o_len);
+    }
+
+    netsnmp_notify_monitor((netsnmp_monitor_callback_header *) cbh);
+}
+
+#ifndef DOXYGEN_SHOULD_IGNORE_THIS
+/*************************************************************************
+ *************************************************************************
+ *************************************************************************
+ * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
+ * WARNING!                                                       WARNING!
+ * WARNING!                                                       WARNING!
+ * WARNING!         This code is under active development         WARNING!
+ * WARNING!         and is subject to change at any time.         WARNING!
+ * WARNING!                                                       WARNING!
+ * WARNING!                                                       WARNING!
+ * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
+ *************************************************************************
+ *************************************************************************
+ *************************************************************************
+ */
+static watcher_list *
+find_watchers(oid * object, size_t oid_len)
+{
+    netsnmp_index oah;
+
+    oah.oids = object;
+    oah.len = oid_len;
+
+    return (watcher_list *)CONTAINER_FIND(monitored_objects, &oah);
+}
+
+static int
+insert_watcher(oid * object, size_t oid_len, monitor_info * mi)
+{
+    watcher_list   *wl = find_watchers(object, oid_len);
+    int             rc = SNMPERR_SUCCESS;
+
+    if (NULL != wl) {
+
+        monitor_info   *last, *current;
+
+        netsnmp_assert(wl->head != NULL);
+
+        last = NULL;
+        current = wl->head;
+        while (current) {
+            if (mi->priority == current->priority) {
+                /*
+                 * check for duplicate
+                 */
+                if (mi->watcher_data == current->watcher_data)
+                    return SNMPERR_VALUE; /** duplicate! */
+            } else if (mi->priority > current->priority) {
+                break;
+            }
+            last = current;
+            current = current->next;
+        }
+        if (NULL == last) {
+            mi->next = wl->head;
+            wl->head = mi;
+        } else {
+            mi->next = last->next;
+            last->next = mi;
+        }
+    } else {
+
+        /*
+         * first watcher for this oid; set up list
+         */
+        wl = SNMP_MALLOC_TYPEDEF(watcher_list);
+        if (NULL == wl)
+            return SNMPERR_MALLOC;
+
+        /*
+         * copy index oid
+         */
+        wl->monitored_object.len = oid_len;
+        wl->monitored_object.oids = malloc(sizeof(oid) * oid_len);
+        if (NULL == wl->monitored_object.oids) {
+            free(wl);
+            return SNMPERR_MALLOC;
+        }
+        memcpy(wl->monitored_object.oids, object, sizeof(oid) * oid_len);
+
+        /*
+         * add watcher, and insert into array
+         */
+        wl->head = mi;
+        mi->next = NULL;
+        rc = CONTAINER_INSERT(monitored_objects, wl);
+        if (rc) {
+            free(wl->monitored_object.oids);
+            free(wl);
+            return rc;
+        }
+    }
+    return rc;
+}
+
+/**
+ * @internal
+ * check to see if a registration exists for an object/event combination
+ *
+ * @param event the event type to check for
+ * @param o     the oid to check for
+ * @param o_l   the length of the oid
+ * @param pWl   if a pointer to a watcher_list pointer is supplied,
+ *              upon return the watcher list pointer will be set to
+ *              the watcher list for the object, or NULL if there are
+ *              no watchers for the object.
+ * @param pMi   if a pointer to a monitor_info pointer is supplied,
+ *              upon return the pointer will be set to the first
+ *              monitor_info object for the specified event.
+ *
+ * @returns TRUE(1) if a callback is registerd
+ * @returns FALSE(0) if no callback is registered
+ */
+static int
+check_registered(unsigned int event, oid * o, int o_l,
+                 watcher_list ** pWl, monitor_info ** pMi)
+{
+    watcher_list   *wl;
+    monitor_info   *mi;
+
+    netsnmp_assert(need_init == 0);
+
+    /*
+     * check to see if anyone has registered for callbacks
+     * for the object.
+     */
+    wl = find_watchers(o, o_l);
+    if (pWl)
+        *pWl = wl;
+    if (NULL == wl)
+        return 0;
+
+    /*
+     * check if any watchers are watching for this specific event
+     */
+    for (mi = wl->head; mi; mi = mi->next) {
+
+        if (mi->events & event) {
+            if (pMi)
+                *pMi = mi;
+            return TRUE;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ *@internal
+ */
+NETSNMP_INLINE void
+insert_ready(callback_placeholder * new_cbr)
+{
+    callback_placeholder *current_cbr, *last_cbr;
+
+    /*
+     * insert in callback ready list
+     */
+    last_cbr = NULL;
+    current_cbr = callback_ready_list;
+    while (current_cbr) {
+
+        if (new_cbr->mi->priority > current_cbr->mi->priority)
+            break;
+
+        last_cbr = current_cbr;
+        current_cbr = current_cbr->next;
+    }
+    if (NULL == last_cbr) {
+        new_cbr->next = callback_ready_list;
+        callback_ready_list = new_cbr;
+    } else {
+        new_cbr->next = last_cbr->next;
+        last_cbr->next = new_cbr;
+    }
+}
+
+/**
+ *@internal
+ *
+ * move an pending notification which has a registered watcher to the
+ * ready list. Free any other notifications.
+ */
+static void
+move_pending_to_ready(void)
+{
+    /*
+     * check to see if anyone has registered for callbacks
+     * for each object.
+     */
+    while (callback_pending_list) {
+
+        watcher_list   *wl;
+        monitor_info   *mi;
+        netsnmp_monitor_callback_header *cbp;
+
+        /*
+         * pop off first item
+         */
+        cbp = callback_pending_list;
+        callback_pending_list = cbp->private; /** next */
+
+        if (0 == check_registered(cbp->event, cbp->monitored_object.oids,
+                                  cbp->monitored_object.len, &wl,
+                                  &mi)) {
+
+            /*
+             * nobody watching, free memory
+             */
+            free(cbp);
+            continue;
+        }
+
+        /*
+         * Found at least one; check the rest of the list and
+         * save callback for processing
+         */
+        for (; mi; mi = mi->next) {
+
+            callback_placeholder *new_cbr;
+
+            if (0 == (mi->events & cbp->event))
+                continue;
+
+            /*
+             * create temprory placeholder.
+             *
+             * I hate to allocate memory here, as I'd like this code to
+             * be fast and lean. But I don't have time to think of another
+             * solution os this will have to do for now.
+             *
+             * I need a list of monitor_info (mi) objects for each
+             * callback which has registered for the event, and want
+             * that list sorted by the priority required by the watcher.
+             */
+            new_cbr = SNMP_MALLOC_TYPEDEF(callback_placeholder);
+            if (NULL == new_cbr) {
+                snmp_log(LOG_ERR, "malloc failed, callback dropped.");
+                continue;
+            }
+            new_cbr->cbh = cbp;
+            new_cbr->mi = mi;
+            ++cbp->refs;
+
+            /*
+             * insert in callback ready list
+             */
+            insert_ready(new_cbr);
+
+        } /** end mi loop */
+    } /** end cbp loop */
+
+    netsnmp_assert(callback_pending_list == NULL);
+}
+
+
+#if defined TESTING_OBJECT_MONITOR
+/**************************************************************************
+ *
+ * (untested) TEST CODE
+ *
+ */
+void
+dummy_callback(netsnmp_monitor_callback_header * cbh)
+{
+    printf("Callback received.\n");
+}
+
+void
+dump_watchers(netsnmp_index *oah, void *)
+{
+    watcher_list   *wl = (watcher_list *) oah;
+    netsnmp_monitor_callback_header *cbh = wl->head;
+
+    printf("Watcher List for OID ");
+    print_objid(wl->hdr->oids, wl->hdr->len);
+    printf("\n");
+
+    while (cbh) {
+
+        printf("Priority = %d;, Events = %d; Watcher Data = 0x%x\n",
+               cbh->priority, cbh->events, cbh->watcher_data);
+
+        cbh = cbh->private;
+    }
+}
+
+void
+main(int argc, char **argv)
+{
+
+    oid             object[3] = { 1, 3, 6 };
+    int             object_len = 3;
+    int             rc;
+
+    /*
+     * init
+     */
+    netsnmp_monitor_init();
+
+    /*
+     * insert an object
+     */
+    rc = netsnmp_monitor_register(object, object_len, 0,
+                                  EVENT_ROW_ADD, (void *) 0xdeadbeef,
+                                  dummy_callback);
+    printf("insert an object: %d\n", rc);
+
+    /*
+     * insert same object, new priority
+     */
+    netsnmp_monitor_register(object, object_len, 10,
+                             EVENT_ROW_ADD, (void *) 0xdeadbeef,
+                             dummy_callback);
+    printf("insert same object, new priority: %d\n", rc);
+
+    /*
+     * insert same object, same priority, new data
+     */
+    netsnmp_monitor_register(object, object_len, 10,
+                             EVENT_ROW_ADD, (void *) 0xbeefdead,
+                             dummy_callback);
+    printf("insert same object, same priority, new data: %d\n", rc);
+
+    /*
+     * insert same object, same priority, same data
+     */
+    netsnmp_monitor_register(object, object_len, 10,
+                             EVENT_ROW_ADD, (void *) 0xbeefdead,
+                             dummy_callback);
+    printf("insert same object, same priority, new data: %d\n", rc);
+
+
+    /*
+     * dump table
+     */
+    CONTAINER_FOR_EACH(monitored_objects, dump_watchers, NULL);
+}
+#endif /** defined TESTING_OBJECT_MONITOR */
+
+#endif /** DOXYGEN_SHOULD_IGNORE_THIS */