table_container.c
上传用户:wxp200602
上传日期:2007-10-30
资源大小:4028k
文件大小:22k
源码类别:

SNMP编程

开发平台:

Unix_Linux

  1. /*
  2.  * table_container.c
  3.  * $Id: table_container.c,v 1.13.2.1 2005/10/07 20:42:29 rstory Exp $
  4.  */
  5. #include <net-snmp/net-snmp-config.h>
  6. #if HAVE_STRING_H
  7. #include <string.h>
  8. #else
  9. #include <strings.h>
  10. #endif
  11. #include <net-snmp/net-snmp-includes.h>
  12. #include <net-snmp/agent/net-snmp-agent-includes.h>
  13. #include <net-snmp/agent/table.h>
  14. #include <net-snmp/agent/table_container.h>
  15. #include <net-snmp/library/container.h>
  16. #include <net-snmp/library/snmp_assert.h>
  17. #if HAVE_DMALLOC_H
  18. #include <dmalloc.h>
  19. #endif
  20. /*
  21.  * snmp.h:#define SNMP_MSG_INTERNAL_SET_BEGIN        -1 
  22.  * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE1     0 
  23.  * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE2     1 
  24.  * snmp.h:#define SNMP_MSG_INTERNAL_SET_ACTION       2 
  25.  * snmp.h:#define SNMP_MSG_INTERNAL_SET_COMMIT       3 
  26.  * snmp.h:#define SNMP_MSG_INTERNAL_SET_FREE         4 
  27.  * snmp.h:#define SNMP_MSG_INTERNAL_SET_UNDO         5 
  28.  */
  29. /*
  30.  * PRIVATE structure for holding important info for each table.
  31.  */
  32. typedef struct container_table_data_s {
  33.    /** registration info for the table */
  34.     netsnmp_table_registration_info *tblreg_info;
  35.    /** container for the table rows */
  36.    netsnmp_container          *table;
  37.     /*
  38.      * mutex_type                lock;
  39.      */
  40.    /* what type of key do we want? */
  41.    char            key_type;
  42. } container_table_data;
  43. /** @defgroup table_container table_container: Helps you implement a table when data can be found via a netsnmp_container.
  44.  *  @ingroup table
  45.  *
  46.  *  The table_container handler is used (automatically) in conjuntion
  47.  *  with the @link table table@endlink handler.
  48.  *
  49.  *  This handler will use the index information provided by
  50.  *  the @link table @endlink handler to find the row needed to process
  51.  *  the request.
  52.  *
  53.  *  The container must use one of 3 key types. It is the sub-handler's
  54.  *  responsibility to ensure that the container and key type match (unless
  55.  *  neither is specified, in which case a default will be used.)
  56.  *
  57.  *  The current key types are:
  58.  *
  59.  *    TABLE_CONTAINER_KEY_NETSNMP_INDEX
  60.  *        The container should do comparisons based on a key that may be cast
  61.  *        to a netsnmp index (netsnmp_index *). This index contains only the
  62.  *        index portion of the OID, not the entire OID.
  63.  *
  64.  *    TABLE_CONTAINER_KEY_VARBIND_INDEX
  65.  *        The container should do comparisons based on a key that may be cast
  66.  *        to a netsnmp variable list (netsnmp_variable_list *). This variable
  67.  *        list will contain one varbind for each index component.
  68.  *
  69.  *    TABLE_CONTAINER_KEY_VARBIND_RAW    (NOTE: unimplemented)
  70.  *        While not yet implemented, future plans include passing the request
  71.  *        varbind with the full OID to a container.
  72.  *
  73.  *  If a key type is not specified at registration time, the default key type
  74.  *  of TABLE_CONTAINER_KEY_NETSNMP_INDEX will be used. If a container is
  75.  *  provided, or the handler name is aliased to a container type, the container
  76.  *  must use a netsnmp index.
  77.  *
  78.  *  If no container is provided, a lookup will be made based on the
  79.  *  sub-handler's name, or if that isn't found, "table_container". The 
  80.  *  table_container key type will be netsnmp_index.
  81.  *
  82.  *  The container must, at a minimum, implement find and find_next. If a NULL
  83.  *  key is passed to the container, it must return the first item, if any.
  84.  *  All containers provided by net-snmp fulfil this requirement.
  85.  *
  86.  *  This handler will only register to process 'data lookup' modes. In
  87.  *  traditional net-snmp modes, that is any GET-like mode (GET, GET-NEXT,
  88.  *  GET-BULK) or the first phase of a SET (RESERVE1). In the new baby-steps
  89.  *  mode, DATA_LOOKUP is it's own mode, and is a pre-cursor to other modes.
  90.  *
  91.  *  When called, the handler will call the appropriate container method
  92.  *  with the appropriate key type. If a row was not found, the result depends
  93.  *  on the mode.
  94.  *
  95.  *  GET Processing
  96.  *    An exact match must be found. If one is not, the error NOSUCHINSTANCE
  97.  *    is set.
  98.  *
  99.  *  GET-NEXT / GET-BULK
  100.  *    If no row is found, the column number will be increased (using any
  101.  *    valid_columns structure that may have been provided), and the first row
  102.  *    will be retrieved. If no first row is found, the processed flag will be
  103.  *    set, so that the sub-handler can skip any processing related to the
  104.  *    request. The agent will notice this unsatisfied request, and attempt to
  105.  *    pass it to the next appropriate handler.
  106.  *
  107.  *  SET
  108.  *    If the hander did not register with the HANDLER_CAN_NOT_CREATE flag
  109.  *    set in the registration modes, it is assumed that this is a row
  110.  *    creation request and a NULL row is added to the request's data list.
  111.  *    The sub-handler is responsbile for dealing with any row creation
  112.  *    contraints and inserting any newly created rows into the container
  113.  *    and the request's data list.
  114.  *
  115.  *  If a row is found, it will be inserted into
  116.  *  the request's data list. The sub-handler may retrieve it by calling
  117.  *      netsnmp_container_table_extract_context(request); *
  118.  *  NOTE NOTE NOTE:
  119.  *
  120.  *  This helper and it's API are still being tested and are subject to change.
  121.  *
  122.  * @{
  123.  */
  124. static int
  125. _container_table_handler(netsnmp_mib_handler *handler,
  126.                          netsnmp_handler_registration *reginfo,
  127.                          netsnmp_agent_request_info *agtreq_info,
  128.                          netsnmp_request_info *requests);
  129. /**********************************************************************
  130.  **********************************************************************
  131.  *                                                                    *
  132.  *                                                                    *
  133.  * PUBLIC Registration functions                                      *
  134.  *                                                                    *
  135.  *                                                                    *
  136.  **********************************************************************
  137.  **********************************************************************/
  138. /** register specified callbacks for the specified table/oid.
  139. */
  140. netsnmp_mib_handler *
  141. netsnmp_container_table_handler_get(netsnmp_table_registration_info *tabreg,
  142.                                     netsnmp_container *container, char key_type)
  143. {
  144.     container_table_data *tad;
  145.     netsnmp_mib_handler *handler;
  146.     if (NULL == tabreg) {
  147.         snmp_log(LOG_ERR, "bad param in netsnmp_container_table_registern");
  148.         return NULL;
  149.     }
  150.     tad = SNMP_MALLOC_TYPEDEF(container_table_data);
  151.     handler = netsnmp_create_handler("table_container",
  152.                                      _container_table_handler);
  153.     if((NULL == tad) || (NULL == handler)) {
  154.         if(tad) free(tad); /* SNMP_FREE wasted on locals */
  155.         if(handler) free(handler); /* SNMP_FREE wasted on locals */
  156.         snmp_log(LOG_ERR,
  157.                  "malloc failure in netsnmp_container_table_registern");
  158.         return NULL;
  159.     }
  160.     tad->tblreg_info = tabreg;  /* we need it too, but it really is not ours */
  161.     if(key_type)
  162.         tad->key_type = key_type;
  163.     else
  164.         tad->key_type = TABLE_CONTAINER_KEY_NETSNMP_INDEX;
  165.     if(NULL == container)
  166.         container = netsnmp_container_find("table_container");
  167.     tad->table = container;
  168.     if (NULL==container->compare)
  169.         container->compare = netsnmp_compare_netsnmp_index;
  170.     if (NULL==container->ncompare)
  171.         container->ncompare = netsnmp_ncompare_netsnmp_index;
  172.     
  173.     handler->myvoid = (void*)tad;
  174.     handler->flags |= MIB_HANDLER_AUTO_NEXT;
  175.     
  176.     return handler;
  177. }
  178. int
  179. netsnmp_container_table_register(netsnmp_handler_registration *reginfo,
  180.                                  netsnmp_table_registration_info *tabreg,
  181.                                  netsnmp_container *container, char key_type )
  182. {
  183.     netsnmp_mib_handler *handler;
  184.     if ((NULL == reginfo) || (NULL == reginfo->handler) || (NULL == tabreg)) {
  185.         snmp_log(LOG_ERR, "bad param in netsnmp_container_table_registern");
  186.         return SNMPERR_GENERR;
  187.     }
  188.     if (NULL==container)
  189.         container = netsnmp_container_find(reginfo->handlerName);
  190.     handler = netsnmp_container_table_handler_get(tabreg, container, key_type);
  191.     netsnmp_inject_handler(reginfo, handler );
  192.     return netsnmp_register_table(reginfo, tabreg);
  193. }
  194. /** @} */
  195. #ifndef DOXYGEN_SHOULD_SKIP_THIS
  196. /**********************************************************************
  197.  **********************************************************************
  198.  *                                                                    *
  199.  *                                                                    *
  200.  * DATA LOOKUP functions                                              *
  201.  *                                                                    *
  202.  *                                                                    *
  203.  **********************************************************************
  204.  **********************************************************************/
  205. NETSNMP_STATIC_INLINE void
  206. _set_key( container_table_data * tad, netsnmp_request_info *request,
  207.           netsnmp_table_request_info *tblreq_info,
  208.           void **key, netsnmp_index *index )
  209. {
  210.     if (TABLE_CONTAINER_KEY_NETSNMP_INDEX == tad->key_type) {
  211.         index->oids = tblreq_info->index_oid;
  212.         index->len = tblreq_info->index_oid_len;
  213.         *key = index;
  214.     }
  215.     else if (TABLE_CONTAINER_KEY_VARBIND_INDEX == tad->key_type) {
  216.         *key = tblreq_info->indexes;
  217.     }
  218. #if 0
  219.     else if (TABLE_CONTAINER_KEY_VARBIND_RAW == tad->key_type) {
  220.         *key = request->requestvb;
  221.     }
  222. #endif
  223.     else
  224.         *key = NULL;
  225. }
  226. static void *
  227. _find_next_row(netsnmp_container *c,
  228.                netsnmp_table_request_info *tblreq,
  229.                void * key)
  230. {
  231.     void *row = NULL;
  232.     if (!c || !tblreq || !tblreq->reg_info ) {
  233.         snmp_log(LOG_ERR,"_find_next_row param errorn");
  234.         return NULL;
  235.     }
  236.     /*
  237.      * table helper should have made sure we aren't below our minimum column
  238.      */
  239.     netsnmp_assert(tblreq->colnum >= tblreq->reg_info->min_column);
  240.     /*
  241.      * if no indexes then use first row.
  242.      */
  243.     if(tblreq->number_indexes == 0) {
  244.         row = CONTAINER_FIRST(c);
  245.     } else {
  246.         if(NULL == key) {
  247.             netsnmp_index index;
  248.             index.oids = tblreq->index_oid;
  249.             index.len = tblreq->index_oid_len;
  250.             row = CONTAINER_NEXT(c, &index);
  251.         }
  252.         else
  253.             row = CONTAINER_NEXT(c, key);
  254.         /*
  255.          * we don't have a row, but we might be at the end of a
  256.          * column, so try the next column.
  257.          */
  258.         if (NULL == row) {
  259.             /*
  260.              * don't set tblreq next_col unless we know there is one,
  261.              * so we don't mess up table handler sparse table processing.
  262.              */
  263.             oid next_col = netsnmp_table_next_column(tblreq);
  264.             if (0 != next_col) {
  265.                 tblreq->colnum = next_col;
  266.                 row = CONTAINER_FIRST(c);
  267.             }
  268.         }
  269.     }
  270.     
  271.     return row;
  272. }
  273. /**
  274.  * deprecated, backwards compatability only
  275.  *
  276.  * expected impact to remove: none
  277.  *  - used between helpers, shouldn't have been used by end users
  278.  *
  279.  * replacement: none
  280.  *  - never should have been a public method in the first place
  281.  */
  282. netsnmp_index *
  283. netsnmp_table_index_find_next_row(netsnmp_container *c,
  284.                                   netsnmp_table_request_info *tblreq)
  285. {
  286.     return _find_next_row(c, tblreq, NULL );
  287. }
  288. NETSNMP_STATIC_INLINE void
  289. _data_lookup(netsnmp_handler_registration *reginfo,
  290.             netsnmp_agent_request_info *agtreq_info,
  291.             netsnmp_request_info *request, container_table_data * tad)
  292. {
  293.     netsnmp_index *row = NULL;
  294.     netsnmp_table_request_info *tblreq_info;
  295.     netsnmp_variable_list *var;
  296.     netsnmp_index index;
  297.     void *key;
  298.     var = request->requestvb;
  299.     DEBUGIF("table_container") {
  300.         DEBUGMSGTL(("table_container", "  data_lookup oid:"));
  301.         DEBUGMSGOID(("table_container", var->name, var->name_length));
  302.         DEBUGMSG(("table_container", "n"));
  303.     }
  304.     /*
  305.      * Get pointer to the table information for this request. This
  306.      * information was saved by table_helper_handler.
  307.      */
  308.     tblreq_info = netsnmp_extract_table_info(request);
  309.     /** the table_helper_handler should enforce column boundaries. */
  310.     netsnmp_assert((NULL != tblreq_info) &&
  311.                    (tblreq_info->colnum <= tad->tblreg_info->max_column));
  312.     
  313.     if ((agtreq_info->mode == MODE_GETNEXT) ||
  314.         (agtreq_info->mode == MODE_GETBULK)) {
  315.         /*
  316.          * find the row. This will automatically move to the next
  317.          * column, if necessary.
  318.          */
  319.         _set_key( tad, request, tblreq_info, &key, &index );
  320.         row = _find_next_row(tad->table, tblreq_info, key);
  321.         if (row) {
  322.             /*
  323.              * update indexes in tblreq_info (index & varbind),
  324.              * then update request varbind oid
  325.              */
  326.             if(TABLE_CONTAINER_KEY_NETSNMP_INDEX == tad->key_type) {
  327.                 tblreq_info->index_oid_len = row->len;
  328.                 memcpy(tblreq_info->index_oid, row->oids,
  329.                        row->len * sizeof(oid));
  330.                 netsnmp_update_variable_list_from_index(tblreq_info);
  331.             }
  332.             else if (TABLE_CONTAINER_KEY_VARBIND_INDEX == tad->key_type) {
  333.                 netsnmp_update_indexes_from_variable_list(tblreq_info);
  334.             }
  335.             if (TABLE_CONTAINER_KEY_VARBIND_RAW != tad->key_type) {
  336.                 netsnmp_table_build_oid_from_index(reginfo, request,
  337.                                                    tblreq_info);
  338.             }
  339.         }
  340.         else {
  341.             /*
  342.              * no results found. Flag the request so lower handlers will
  343.              * ignore it, but it is not an error - getnext will move
  344.              * on to another handler to process this request.
  345.              */
  346.             netsnmp_set_request_error(agtreq_info, request, SNMP_ENDOFMIBVIEW);
  347.             DEBUGMSGTL(("table_container", "no row foundn"));
  348.         }
  349.     } /** GETNEXT/GETBULK */
  350.     else {
  351.         _set_key( tad, request, tblreq_info, &key, &index );
  352.         row = CONTAINER_FIND(tad->table, key);
  353.         if (NULL == row) {
  354.             /*
  355.              * not results found. For a get, that is an error
  356.              */
  357.             DEBUGMSGTL(("table_container", "no row foundn"));
  358.             if((agtreq_info->mode != MODE_SET_RESERVE1) || /* get */
  359.                (reginfo->modes & HANDLER_CAN_NOT_CREATE)) { /* no create */
  360.                 netsnmp_set_request_error(agtreq_info, request,
  361.                                           SNMP_NOSUCHINSTANCE);
  362.             }
  363.         }
  364.     } /** GET/SET */
  365.     
  366.     /*
  367.      * save the data and table in the request.
  368.      */
  369.     if (SNMP_ENDOFMIBVIEW != request->status) {
  370.         if (NULL != row)
  371.             netsnmp_request_add_list_data(request,
  372.                                           netsnmp_create_data_list
  373.                                           (TABLE_CONTAINER_ROW,
  374.                                            row, NULL));
  375.         netsnmp_request_add_list_data(request,
  376.                                       netsnmp_create_data_list
  377.                                       (TABLE_CONTAINER_CONTAINER,
  378.                                        tad->table, NULL));
  379.     }
  380. }
  381. /**********************************************************************
  382.  **********************************************************************
  383.  *                                                                    *
  384.  *                                                                    *
  385.  * netsnmp_table_container_helper_handler()                           *
  386.  *                                                                    *
  387.  *                                                                    *
  388.  **********************************************************************
  389.  **********************************************************************/
  390. static int
  391. _container_table_handler(netsnmp_mib_handler *handler,
  392.                          netsnmp_handler_registration *reginfo,
  393.                          netsnmp_agent_request_info *agtreq_info,
  394.                          netsnmp_request_info *requests)
  395. {
  396.     int             rc = SNMP_ERR_NOERROR;
  397.     int             oldmode, need_processing = 0;
  398.     container_table_data *tad;
  399.     /** sanity checks */
  400.     netsnmp_assert((NULL != handler) && (NULL != handler->myvoid));
  401.     netsnmp_assert((NULL != reginfo) && (NULL != agtreq_info));
  402.     DEBUGMSGTL(("table_container", "Mode %s, Got request:n",
  403.                 se_find_label_in_slist("agent_mode",agtreq_info->mode)));
  404.     /*
  405.      * First off, get our pointer from the handler. This
  406.      * lets us get to the table registration information we
  407.      * saved in get_table_container_handler(), as well as the
  408.      * container where the actual table data is stored.
  409.      */
  410.     tad = (container_table_data *)handler->myvoid;
  411.     /*
  412.      * only do data lookup for first pass
  413.      *
  414.      * xxx-rks: this should really be handled up one level. we should
  415.      * be able to say what modes we want to be called for during table
  416.      * registration.
  417.      */
  418.     oldmode = agtreq_info->mode;
  419.     if(MODE_IS_GET(oldmode) || (MODE_SET_RESERVE1 == oldmode)) {
  420.         netsnmp_request_info *curr_request;
  421.         /*
  422.          * Loop through each of the requests, and
  423.          * try to find the appropriate row from the container.
  424.          */
  425.         for (curr_request = requests; curr_request; curr_request = curr_request->next) {
  426.             /*
  427.              * skip anything that doesn't need processing.
  428.              */
  429.             if (curr_request->processed != 0) {
  430.                 DEBUGMSGTL(("table_container", "already processedn"));
  431.                 continue;
  432.             }
  433.             
  434.             /*
  435.              * find data for this request
  436.              */
  437.             _data_lookup(reginfo, agtreq_info, curr_request, tad);
  438.             if(curr_request->processed)
  439.                 continue;
  440.             ++need_processing;
  441.         } /** for ( ... requests ... ) */
  442.     }
  443.     
  444.     /*
  445.      * send GET instead of GETNEXT to sub-handlers
  446.      * xxx-rks: again, this should be handled further up.
  447.      */
  448.     if ((oldmode == MODE_GETNEXT) && (handler->next)) {
  449.         /*
  450.          * tell agent handlder not to auto call next handler
  451.          */
  452.         handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE;
  453.         /*
  454.          * if we found rows to process, pretend to be a get request
  455.          * and call handler below us.
  456.          */
  457.         if(need_processing > 0) {
  458.             agtreq_info->mode = MODE_GET;
  459.             rc = netsnmp_call_next_handler(handler, reginfo, agtreq_info,
  460.                                            requests);
  461.             if (rc != SNMP_ERR_NOERROR) {
  462.                 DEBUGMSGTL(("table_container",
  463.                             "next handler returned %dn", rc));
  464.             }
  465.             agtreq_info->mode = oldmode; /* restore saved mode */
  466.         }
  467.     }
  468.     return rc;
  469. }
  470. /** retrieve the container used by the table_container helper */
  471. netsnmp_container*
  472. netsnmp_container_table_container_extract(netsnmp_request_info *request)
  473. {
  474.     return (netsnmp_container *)
  475.          netsnmp_request_get_list_data(request, TABLE_CONTAINER_CONTAINER);
  476. }
  477. /** inserts a newly created table_container entry into a request list */
  478. void
  479. netsnmp_container_table_row_insert(netsnmp_request_info *request,
  480.                                    netsnmp_index        *row)
  481. {
  482.     netsnmp_request_info       *req;
  483.     netsnmp_table_request_info *table_info = NULL;
  484.     netsnmp_variable_list      *this_index = NULL;
  485.     netsnmp_variable_list      *that_index = NULL;
  486.     oid      base_oid[] = {0, 0}; /* Make sure index OIDs are legal! */
  487.     oid      this_oid[MAX_OID_LEN];
  488.     oid      that_oid[MAX_OID_LEN];
  489.     size_t   this_oid_len, that_oid_len;
  490.     if (!request)
  491.         return;
  492.     /*
  493.      * We'll add the new row information to any request
  494.      * structure with the same index values as the request
  495.      * passed in (which includes that one!).
  496.      *
  497.      * So construct an OID based on these index values.
  498.      */
  499.     table_info = netsnmp_extract_table_info(request);
  500.     this_index = table_info->indexes;
  501.     build_oid_noalloc(this_oid, MAX_OID_LEN, &this_oid_len,
  502.                       base_oid, 2, this_index);
  503.     /*
  504.      * We need to look through the whole of the request list
  505.      * (as received by the current handler), as there's no
  506.      * guarantee that this routine will be called by the first
  507.      * varbind that refers to this row.
  508.      *   In particular, a RowStatus controlled row creation
  509.      * may easily occur later in the variable list.
  510.      *
  511.      * So first, we rewind to the head of the list....
  512.      */
  513.     for (req=request; req->prev; req=req->prev)
  514.         ;
  515.     /*
  516.      * ... and then start looking for matching indexes
  517.      * (by constructing OIDs from these index values)
  518.      */
  519.     for (; req; req=req->next) {
  520.         table_info = netsnmp_extract_table_info(req);
  521.         that_index = table_info->indexes;
  522.         build_oid_noalloc(that_oid, MAX_OID_LEN, &that_oid_len,
  523.                           base_oid, 2, that_index);
  524.       
  525.         /*
  526.          * This request has the same index values,
  527.          * so add the newly-created row information.
  528.          */
  529.         if (snmp_oid_compare(this_oid, this_oid_len,
  530.                              that_oid, that_oid_len) == 0) {
  531.             netsnmp_request_add_list_data(req,
  532.                 netsnmp_create_data_list(TABLE_CONTAINER_ROW, row, NULL));
  533.         }
  534.     }
  535. }
  536. #ifndef NETSNMP_USE_INLINE
  537. /** find the context data used by the table_container helper */
  538. void *
  539. netsnmp_container_table_row_extract(netsnmp_request_info *request)
  540. {
  541.     /*
  542.      * NOTE: this function must match in table_container.c and table_container.h.
  543.      *       if you change one, change them both!
  544.      */
  545.     return netsnmp_request_get_list_data(request, TABLE_CONTAINER_ROW);
  546. }
  547. #endif /* inline */
  548. #endif /** DOXYGEN_SHOULD_SKIP_THIS */