and added files
[bcm963xx.git] / userapps / opensource / net-snmp / agent / helpers / table_iterator.c
diff --git a/userapps/opensource/net-snmp/agent/helpers/table_iterator.c b/userapps/opensource/net-snmp/agent/helpers/table_iterator.c
new file mode 100644 (file)
index 0000000..f2c73aa
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * table_iterator.c 
+ */
+/** @defgroup table_iterator table_iterator: The table iterator helper is designed to simplify the task of writing a table handler for the net-snmp agent when the data being accessed is not in an oid sorted form and must be accessed externally.
+ *  @ingroup table
+ *  Functionally, it is a specialized version of the more
+ *  generic table helper but easies the burden of GETNEXT processing by
+ *  manually looping through all the data indexes retrieved through
+ *  function calls which should be supplied by the module that wishes
+ *  help.  The module the table_iterator helps should, afterwards,
+ *  never be called for the case of "MODE_GETNEXT" and only for the GET
+ *  and SET related modes instead.
+ */
+
+#include <net-snmp/net-snmp-config.h>
+
+#if HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+#include <net-snmp/agent/table.h>
+#include <net-snmp/agent/serialize.h>
+#include <net-snmp/agent/table_iterator.h>
+
+#if HAVE_DMALLOC_H
+#include <dmalloc.h>
+#endif
+
+/*
+ * doesn't work yet, but shouldn't be serialized (for efficiency) 
+ */
+#undef NOT_SERIALIZED
+#define TABLE_ITERATOR_LAST_CONTEXT "ti_last_c"
+
+/** returns a netsnmp_mib_handler object for the table_iterator helper */
+netsnmp_mib_handler *
+netsnmp_get_table_iterator_handler(netsnmp_iterator_info *iinfo)
+{
+    netsnmp_mib_handler *me =
+        netsnmp_create_handler(TABLE_ITERATOR_NAME,
+                               netsnmp_table_iterator_helper_handler);
+
+    if (!me || !iinfo)
+        return NULL;
+
+    me->myvoid = iinfo;
+    return me;
+}
+
+
+/** registers a table after attaching it to a table_iterator helper */
+int
+netsnmp_register_table_iterator(netsnmp_handler_registration *reginfo,
+                                netsnmp_iterator_info *iinfo)
+{
+    netsnmp_inject_handler(reginfo,
+                           netsnmp_get_table_iterator_handler(iinfo));
+    if (!iinfo)
+        return SNMPERR_GENERR;
+
+#ifndef NOT_SERIALIZED
+    netsnmp_inject_handler(reginfo, netsnmp_get_serialize_handler());
+#endif
+    return netsnmp_register_table(reginfo, iinfo->table_reginfo);
+}
+
+/** extracts the table_iterator specific data from a request */
+NETSNMP_INLINE void    *
+netsnmp_extract_iterator_context(netsnmp_request_info *request)
+{
+    return netsnmp_request_get_list_data(request, TABLE_ITERATOR_NAME);
+}
+
+/** implements the table_iterator helper */
+int
+netsnmp_table_iterator_helper_handler(netsnmp_mib_handler *handler,
+                                      netsnmp_handler_registration
+                                      *reginfo,
+                                      netsnmp_agent_request_info *reqinfo,
+                                      netsnmp_request_info *requests)
+{
+
+    netsnmp_table_registration_info *tbl_info;
+    oid             coloid[MAX_OID_LEN];
+    size_t          coloid_len;
+    int             ret;
+    static oid      myname[MAX_OID_LEN];
+    static int      myname_len;
+    int             oldmode;
+    netsnmp_iterator_info *iinfo;
+
+    iinfo = (netsnmp_iterator_info *) handler->myvoid;
+    if (!iinfo || !reginfo || !reqinfo)
+        return SNMPERR_GENERR;
+
+    tbl_info = iinfo->table_reginfo;
+
+    /*
+     * copy in the table registration oid for later use 
+     */
+    coloid_len = reginfo->rootoid_len + 2;
+    memcpy(coloid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid));
+    coloid[reginfo->rootoid_len] = 1;   /* table.entry node */
+
+    /*
+     * illegally got here if these functions aren't defined 
+     */
+    if (iinfo->get_first_data_point == NULL ||
+        iinfo->get_next_data_point == NULL) {
+        snmp_log(LOG_ERR,
+                 "table_iterator helper called without data accessor functions\n");
+        return SNMP_ERR_GENERR;
+    }
+
+    /*
+     * XXXWWW: deal with SET caching 
+     */
+
+#ifdef NOT_SERIALIZED
+    while (requests)            /* XXX: currently only serialized */
+#endif
+    {
+        /*
+         * XXXWWW: optimize by reversing loops (look through data only once) 
+         */
+        netsnmp_variable_list *results = NULL;
+        netsnmp_variable_list *index_search = NULL;     /* WWW: move up? */
+        netsnmp_variable_list *free_this_index_search = NULL;
+        netsnmp_table_request_info *table_info =
+            netsnmp_extract_table_info(requests);
+        void           *callback_loop_context = NULL;
+        void           *callback_data_context = NULL;
+        void           *callback_data_keep = NULL;
+
+        if (requests->processed != 0) {
+#ifdef NOT_SERIALIZED
+            continue;
+#else
+            return SNMP_ERR_NOERROR;
+#endif
+        }
+
+        if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT && reqinfo->mode != MODE_GETBULK &&      /* XXX */
+            reqinfo->mode != MODE_SET_RESERVE1) {
+            goto skip_processing;
+        }
+
+
+        if (table_info->colnum > tbl_info->max_column) {
+            requests->processed = 1;
+#ifdef NOT_SERIALIZED
+            break;
+#else
+            return SNMP_ERR_NOERROR;
+#endif
+        }
+
+        /*
+         * XXX: if loop through everything, these are never free'd
+         * since iterator returns NULL and thus we forget about
+         * these 
+         */
+
+        index_search = snmp_clone_varbind(table_info->indexes);
+        if (!index_search) {
+            /*
+             * hmmm....  invalid table? 
+             */
+            snmp_log(LOG_WARNING,
+                     "invalid index list or failed malloc for table %s\n",
+                     reginfo->handlerName);
+            return SNMP_ERR_NOERROR;
+        }
+
+        free_this_index_search = index_search;
+
+        /*
+         * below our minimum column? 
+         */
+        if (table_info->colnum < tbl_info->min_column) {
+            results =
+                (iinfo->get_first_data_point) (&callback_loop_context,
+                                               &callback_data_context,
+                                               index_search, iinfo);
+            if (iinfo->free_loop_context) {
+                (iinfo->free_loop_context) (callback_loop_context, iinfo);
+               callback_loop_context = NULL;
+           }
+            goto got_results;
+        }
+
+        /*
+         * XXX: do "only got some indexes" 
+         */
+
+        /*
+         * find the next legal result to return 
+         */
+        /*
+         * find the first node 
+         */
+        index_search =
+            (iinfo->get_first_data_point) (&callback_loop_context,
+                                           &callback_data_context,
+                                           index_search, iinfo);
+        /*
+         * table.entry.column node 
+         */
+        coloid[reginfo->rootoid_len + 1] = table_info->colnum;
+
+        switch (reqinfo->mode) {
+        case MODE_GETNEXT:
+        case MODE_GETBULK:     /* XXXWWW */
+            /*
+             * loop through all data and find next one 
+             */
+            while (index_search) {
+                /*
+                 * compare the node with previous results 
+                 */
+                if (netsnmp_check_getnext_reply
+                    (requests, coloid, coloid_len, index_search,
+                     &results)) {
+
+                    /*
+                     * result is our current choice, so keep a pointer to
+                     * the data that the lower handler wants us to
+                     * remember (possibly freeing the last known "good"
+                     * result data pointer) 
+                     */
+                    if (callback_data_keep && iinfo->free_data_context) {
+                        (iinfo->free_data_context) (callback_data_keep,
+                                                    iinfo);
+                        callback_data_keep = NULL;
+                    }
+                    if (iinfo->make_data_context && !callback_data_context) {
+                        callback_data_context =
+                            (iinfo->
+                             make_data_context) (callback_loop_context,
+                                                 iinfo);
+
+                    }
+                    callback_data_keep = callback_data_context;
+                    callback_data_context = NULL;
+                } else {
+                    if (callback_data_context && iinfo->free_data_context)
+                        (iinfo->free_data_context) (callback_data_context,
+                                                    iinfo);
+                    callback_data_context = NULL;
+                }
+
+                /*
+                 * get the next node in the data chain 
+                 */
+                index_search =
+                    (iinfo->get_next_data_point) (&callback_loop_context,
+                                                  &callback_data_context,
+                                                  index_search, iinfo);
+
+                if (!index_search && !results &&
+                    tbl_info->max_column > table_info->colnum) {
+                    /*
+                     * restart loop.  XXX: Should cache this better 
+                     */
+                    table_info->colnum++;
+                    coloid[reginfo->rootoid_len + 1] = table_info->colnum;
+                    if (free_this_index_search != NULL)
+                        snmp_free_varbind(free_this_index_search);
+                    index_search = snmp_clone_varbind(table_info->indexes);
+                   free_this_index_search = index_search;
+
+                    if (callback_loop_context &&
+                        iinfo->free_loop_context_at_end) {
+                        (iinfo->free_loop_context_at_end)(callback_loop_context,
+                                                          iinfo);
+                        callback_loop_context = NULL;
+                    }
+                    if (iinfo->free_loop_context && callback_loop_context) {
+                        (iinfo->free_loop_context) (callback_loop_context,
+                                                    iinfo);
+                        callback_loop_context = NULL;
+                    }
+                    if (callback_data_context && iinfo->free_data_context) {
+                        (iinfo->free_data_context) (callback_data_context,
+                                                    iinfo);
+                        callback_data_context = NULL;
+                    }
+                    
+                    index_search =
+                        (iinfo->
+                         get_first_data_point) (&callback_loop_context,
+                                                &callback_data_context,
+                                                index_search, iinfo);
+                }
+            }
+
+            break;
+
+        case MODE_GET:
+        case MODE_SET_RESERVE1:
+            /*
+             * loop through all data till exact results are found 
+             */
+
+            while (index_search) {
+                build_oid_noalloc(myname, MAX_OID_LEN, &myname_len,
+                                  coloid, coloid_len, index_search);
+                if (snmp_oid_compare(myname, myname_len,
+                                     requests->requestvb->name,
+                                     requests->requestvb->name_length) ==
+                    0) {
+                    /*
+                     * found the exact match, so we're done 
+                     */
+                    if (iinfo->make_data_context && !callback_data_context) {
+                        callback_data_context =
+                            (iinfo->
+                             make_data_context) (callback_loop_context,
+                                                 iinfo);
+
+                    }
+                    callback_data_keep = callback_data_context;
+                    callback_data_context = NULL;
+                    results = snmp_clone_varbind(index_search);
+                    snmp_set_var_objid(results, myname, myname_len);
+                    goto got_results;
+                } else {
+                    /*
+                     * free not-needed data context 
+                     */
+                    if (callback_data_context && iinfo->free_data_context) {
+                        (iinfo->free_data_context) (callback_data_context,
+                                                    iinfo);
+                        callback_data_context = NULL;
+                    }
+
+                }
+
+                /*
+                 * get the next node in the data chain 
+                 */
+                index_search =
+                    (iinfo->get_next_data_point) (&callback_loop_context,
+                                                  &callback_data_context,
+                                                  index_search, iinfo);
+            }
+            break;
+
+        default:
+            /*
+             * the rest of the set states have been dealt with already 
+             */
+            goto got_results;
+        }
+
+        /*
+         * XXX: free index_search? 
+         */
+        if (callback_loop_context && iinfo->free_loop_context) {
+            (iinfo->free_loop_context) (callback_loop_context, iinfo);
+            callback_loop_context = NULL;
+        }
+
+      got_results:             /* not milk */
+   
+       /*
+        * This free_data_context call is required in the event that your
+        * get_next_data_point method allocates new memory, even during the
+        * calls where it eventually returns a NULL
+        */
+        if (callback_data_context && iinfo->free_data_context) {
+               (iinfo->free_data_context) (callback_data_context,
+                                           iinfo);
+               callback_data_context = NULL;
+        }
+
+        if (!results && !MODE_IS_SET(reqinfo->mode)) {
+            /*
+             * no results found. 
+             */
+            /*
+             * XXX: check for at least one entry at the very top 
+             */
+#ifdef NOT_SERIALIZED
+            break;
+#else
+            if (callback_loop_context && iinfo->free_loop_context_at_end) {
+                (iinfo->free_loop_context_at_end) (callback_loop_context,
+                                                   iinfo);
+               callback_loop_context = NULL;
+           }
+            if (free_this_index_search != NULL) {
+                snmp_free_varbind(free_this_index_search);
+            }
+            return SNMP_ERR_NOERROR;
+#endif
+        }
+
+      skip_processing:
+        /*
+         * OK, here results should be a pointer to the data that we
+         * actually need to GET 
+         */
+        oldmode = reqinfo->mode;
+        if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) {   /* XXX */
+            snmp_set_var_objid(requests->requestvb, results->name,
+                               results->name_length);
+            reqinfo->mode = MODE_GET;
+        }
+        if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK ||      /* XXX */
+            reqinfo->mode == MODE_SET_RESERVE1) {
+            /*
+             * first (or only) pass stuff 
+             */
+            /*
+             * let set requsets use previously constructed data 
+             */
+            snmp_free_varbind(results);
+            if (callback_data_keep)
+                netsnmp_request_add_list_data(requests,
+                                              netsnmp_create_data_list
+                                              (TABLE_ITERATOR_NAME,
+                                               callback_data_keep, NULL));
+            netsnmp_request_add_list_data(requests,
+                                          netsnmp_create_data_list
+                                          (TABLE_ITERATOR_LAST_CONTEXT,
+                                           callback_loop_context, NULL));
+        }
+
+        DEBUGMSGTL(("table_iterator", "doing mode: %s\n",
+                    se_find_label_in_slist("agent_mode", oldmode)));
+        ret =
+            netsnmp_call_next_handler(handler, reginfo, reqinfo, requests);
+        if (oldmode == MODE_GETNEXT || oldmode == MODE_GETBULK) {       /* XXX */
+            if (requests->requestvb->type == ASN_NULL ||
+                requests->requestvb->type == SNMP_NOSUCHINSTANCE) {
+                /*
+                 * get next skipped this value for this column, we
+                 * need to keep searching forward 
+                 */
+                requests->requestvb->type = ASN_PRIV_RETRY;
+            }
+            reqinfo->mode = oldmode;
+        }
+
+        callback_data_keep =
+            netsnmp_request_get_list_data(requests, TABLE_ITERATOR_NAME);
+        callback_loop_context =
+            netsnmp_request_get_list_data(requests,
+                                          TABLE_ITERATOR_LAST_CONTEXT);
+
+        /* 
+         * This has to be done to prevent a memory leak. Notice that on
+         * SET_RESERVE1 we're assigning something to
+         * 'free_this_index_search' at the beginning of this handler (right
+         * above the line that says 'below our minimum column?'), 
+         * but we're not given a chance to free it below with the other 
+         * SET modes, hence our doing it here. 
+         */
+        if (reqinfo->mode == MODE_SET_RESERVE1) {
+            if (free_this_index_search) {
+                snmp_free_varbind(free_this_index_search);
+                free_this_index_search = NULL;
+            }
+        }
+        if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT ||
+            reqinfo->mode == MODE_GETBULK ||      /* XXX */
+            reqinfo->mode == MODE_SET_FREE ||
+            reqinfo->mode == MODE_SET_UNDO ||
+            reqinfo->mode == MODE_SET_COMMIT) {
+            if (callback_data_keep && iinfo->free_data_context) {
+                (iinfo->free_data_context) (callback_data_keep, iinfo);
+                callback_data_keep = NULL;
+            }
+
+            if (free_this_index_search) {
+                snmp_free_varbind(free_this_index_search);
+                free_this_index_search = NULL;
+            }
+#ifndef NOT_SERIALIZED
+            if (callback_loop_context && iinfo->free_loop_context_at_end) {
+                (iinfo->free_loop_context_at_end) (callback_loop_context,
+                                                   iinfo);
+               callback_loop_context = NULL;
+            }
+#endif
+        }
+#ifdef NOT_SERIALIZED
+        return ret;
+#else
+        requests = requests->next;
+#endif
+    }
+#ifdef NOT_SERIALIZED
+    if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK ||  /* XXX */
+        reqinfo->mode == MODE_SET_FREE ||
+        reqinfo->mode == MODE_SET_UNDO ||
+        reqinfo->mode == MODE_SET_COMMIT) {
+        if (callback_loop_context && iinfo->free_loop_context_at_end) {
+            (iinfo->free_loop_context_at_end) (callback_loop_context,
+                                               iinfo);
+           callback_loop_context = NULL;
+       }
+    }
+#endif
+    return SNMP_ERR_NOERROR;
+}