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

SNMP编程

开发平台:

Unix_Linux

  1. #include <net-snmp/net-snmp-config.h>
  2. #if HAVE_STRING_H
  3. #include <string.h>
  4. #else
  5. #include <strings.h>
  6. #endif
  7. #include <net-snmp/net-snmp-includes.h>
  8. #include <net-snmp/agent/net-snmp-agent-includes.h>
  9. #if HAVE_DMALLOC_H
  10. #include <dmalloc.h>
  11. #endif
  12. static netsnmp_data_list *auto_tables;
  13. typedef struct data_set_tables_s {
  14.     netsnmp_table_data_set *table_set;
  15. } data_set_tables;
  16. typedef struct data_set_cache_s {
  17.     void           *data;
  18.     size_t          data_len;
  19. } data_set_cache;
  20. #define STATE_ACTION   1
  21. #define STATE_COMMIT   2
  22. #define STATE_UNDO     3
  23. #define STATE_FREE     4
  24. typedef struct newrow_stash_s {
  25.     netsnmp_table_row *newrow;
  26.     int             state;
  27.     int             created;
  28.     int             deleted;
  29. } newrow_stash;
  30. /** @defgroup table_dataset table_dataset: Helps you implement a table with automatted storage.
  31.  *  @ingroup table_data
  32.  *
  33.  *  This handler helps you implement a table where all the data is
  34.  *  expected to be stored within the agent itself and not in some
  35.  *  external storage location.  It handles all MIB requests including
  36.  *  GETs, GETNEXTs and SETs.  It's possible to simply create a table
  37.  *  without actually ever defining a handler to be called when SNMP
  38.  *  requests come in.  To use the data, you can either attach a
  39.  *  sub-handler that merely uses/manipulates the data further when
  40.  *  requests come in, or you can loop through it externally when it's
  41.  *  actually needed.  This handler is most useful in cases where a
  42.  *  table is holding configuration data for something which gets
  43.  *  triggered via another event.
  44.  *
  45.  *  NOTE NOTE NOTE: This helper isn't complete and is likely to change
  46.  *  somewhat over time.  Specifically, the way it stores data
  47.  *  internally may change drastically.
  48.  *  
  49.  *  @{
  50.  */
  51. void
  52. netsnmp_init_table_dataset(void) {
  53. #ifndef DISABLE_MIB_LOADING
  54.     register_app_config_handler("table",
  55.                                 netsnmp_config_parse_table_set, NULL,
  56.                                 "tableoid");
  57. #endif /* DISABLE_MIB_LOADING */
  58.     register_app_config_handler("add_row", netsnmp_config_parse_add_row,
  59.                                 NULL, "table_name indexes... values...");
  60. }
  61. /** Create a netsnmp_table_data_set structure given a table_data definition */
  62. netsnmp_table_data_set *
  63. netsnmp_create_table_data_set(const char *table_name)
  64. {
  65.     netsnmp_table_data_set *table_set =
  66.         SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set);
  67.     if (!table_set)
  68.         return NULL;
  69.     table_set->table = netsnmp_create_table_data(table_name);
  70.     return table_set;
  71. }
  72. /** Given a netsnmp_table_data_set definition, create a handler for it */
  73. netsnmp_mib_handler *
  74. netsnmp_get_table_data_set_handler(netsnmp_table_data_set *data_set)
  75. {
  76.     netsnmp_mib_handler *ret = NULL;
  77.     if (!data_set) {
  78.         snmp_log(LOG_INFO,
  79.                  "netsnmp_get_table_data_set_handler(NULL) calledn");
  80.         return NULL;
  81.     }
  82.     ret =
  83.         netsnmp_create_handler(TABLE_DATA_SET_NAME,
  84.                                netsnmp_table_data_set_helper_handler);
  85.     if (ret) {
  86.         ret->flags |= MIB_HANDLER_AUTO_NEXT;
  87.         ret->myvoid = (void *) data_set;
  88.     }
  89.     return ret;
  90. }
  91. /** register a given data_set at a given oid (specified in the
  92.     netsnmp_handler_registration pointer).  The
  93.     reginfo->handler->access_method *may* be null if the call doesn't
  94.     ever want to be called for SNMP operations.
  95. */
  96. int
  97. netsnmp_register_table_data_set(netsnmp_handler_registration *reginfo,
  98.                                 netsnmp_table_data_set *data_set,
  99.                                 netsnmp_table_registration_info
  100.                                 *table_info)
  101. {
  102.     if (NULL == table_info) {
  103.         /*
  104.          * allocate the table if one wasn't allocated 
  105.          */
  106.         table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
  107.     }
  108.     if (NULL == table_info->indexes && data_set->table->indexes_template) {
  109.         /*
  110.          * copy the indexes in 
  111.          */
  112.         table_info->indexes =
  113.             snmp_clone_varbind(data_set->table->indexes_template);
  114.     }
  115.     if ((!table_info->min_column || !table_info->max_column) &&
  116.         (data_set->default_row)) {
  117.         /*
  118.          * determine min/max columns 
  119.          */
  120.         unsigned int    mincol = 0xffffffff, maxcol = 0;
  121.         netsnmp_table_data_set_storage *row;
  122.         for (row = data_set->default_row; row; row = row->next) {
  123.             mincol = SNMP_MIN(mincol, row->column);
  124.             maxcol = SNMP_MAX(maxcol, row->column);
  125.         }
  126.         if (!table_info->min_column)
  127.             table_info->min_column = mincol;
  128.         if (!table_info->max_column)
  129.             table_info->max_column = maxcol;
  130.     }
  131.     netsnmp_inject_handler(reginfo,
  132.                            netsnmp_get_table_data_set_handler(data_set));
  133.     return netsnmp_register_table_data(reginfo, data_set->table,
  134.                                        table_info);
  135. }
  136. /** Finds a column within a given storage set, given the pointer to
  137.    the start of the storage set list.
  138. */
  139. netsnmp_table_data_set_storage *
  140. netsnmp_table_data_set_find_column(netsnmp_table_data_set_storage *start,
  141.                                    unsigned int column)
  142. {
  143.     while (start && start->column != column)
  144.         start = start->next;
  145.     return start;
  146. }
  147. /**
  148.  * extracts a netsnmp_table_data_set pointer from a given request
  149.  */
  150. netsnmp_table_data_set_storage *
  151. netsnmp_extract_table_data_set_column(netsnmp_request_info *request,
  152.                                      unsigned int column)
  153. {
  154.     netsnmp_table_data_set_storage *data =
  155.         netsnmp_extract_table_row_data( request );
  156.     if (data) {
  157.         data = netsnmp_table_data_set_find_column(data, column);
  158.     }
  159.     return data;
  160. }
  161. /**
  162.  * extracts a netsnmp_table_data_set pointer from a given request
  163.  */
  164. NETSNMP_INLINE netsnmp_table_data_set *
  165. netsnmp_extract_table_data_set(netsnmp_request_info *request)
  166. {
  167.     return (netsnmp_table_data_set *)
  168.         netsnmp_request_get_list_data(request, TABLE_DATA_SET_NAME);
  169. }
  170. /**
  171.  * marks a given column in a row as writable or not.
  172.  */
  173. int
  174. netsnmp_mark_row_column_writable(netsnmp_table_row *row, int column,
  175.                                  int writable)
  176. {
  177.     netsnmp_table_data_set_storage *data;
  178.     if (!row)
  179.         return SNMPERR_GENERR;
  180.     data = (netsnmp_table_data_set_storage *) row->data;
  181.     data = netsnmp_table_data_set_find_column(data, column);
  182.     if (!data) {
  183.         /*
  184.          * create it 
  185.          */
  186.         data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
  187.         if (!data) {
  188.             snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
  189.             return SNMPERR_MALLOC;
  190.         }
  191.         data->column = column;
  192.         data->writable = writable;
  193.         data->next = row->data;
  194.         row->data = data;
  195.     } else {
  196.         data->writable = writable;
  197.     }
  198.     return SNMPERR_SUCCESS;
  199. }
  200. /**
  201.  * sets a given column in a row with data given a type, value, and
  202.  * length.  Data is memdup'ed by the function.
  203.  */
  204. int
  205. netsnmp_set_row_column(netsnmp_table_row *row, unsigned int column,
  206.                        int type, const char *value, size_t value_len)
  207. {
  208.     netsnmp_table_data_set_storage *data;
  209.     if (!row)
  210.         return SNMPERR_GENERR;
  211.     data = (netsnmp_table_data_set_storage *) row->data;
  212.     data = netsnmp_table_data_set_find_column(data, column);
  213.     if (!data) {
  214.         /*
  215.          * create it 
  216.          */
  217.         data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
  218.         if (!data) {
  219.             snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
  220.             return SNMPERR_MALLOC;
  221.         }
  222.         data->column = column;
  223.         data->type = type;
  224.         data->next = row->data;
  225.         row->data = data;
  226.     }
  227.     if (value) {
  228.         if (data->type != type)
  229.             return SNMPERR_GENERR;
  230.         SNMP_FREE(data->data.voidp);
  231.         if (value_len) {
  232.             if (memdup(&data->data.string, value, (value_len)) !=
  233.                 SNMPERR_SUCCESS) {
  234.                 snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
  235.                 return SNMPERR_MALLOC;
  236.             }
  237.         } else {
  238.             data->data.string = malloc(1);
  239.         }
  240.         data->data_len = value_len;
  241.     }
  242.     return SNMPERR_SUCCESS;
  243. }
  244. /** adds a new default row to a table_set.
  245.  * Arguments should be the table_set, column number, variable type and
  246.  * finally a 1 if it is allowed to be writable, or a 0 if not.  If the
  247.  * default_value field is not NULL, it will be used to populate new
  248.  * valuse in that column fro newly created rows. It is copied into the
  249.  * storage template (free your calling argument).
  250.  *
  251.  * returns SNMPERR_SUCCESS or SNMPERR_FAILURE
  252.  */
  253. int
  254. netsnmp_table_set_add_default_row(netsnmp_table_data_set *table_set,
  255.                                   unsigned int column,
  256.                                   int type, int writable,
  257.                                   void *default_value,
  258.                                   size_t default_value_len)
  259. {
  260.     netsnmp_table_data_set_storage *new_col, *ptr, *pptr;
  261.     if (!table_set)
  262.         return SNMPERR_GENERR;
  263.     /*
  264.      * double check 
  265.      */
  266.     new_col =
  267.         netsnmp_table_data_set_find_column(table_set->default_row, column);
  268.     if (new_col != NULL) {
  269.         if (new_col->type == type && new_col->writable == writable)
  270.             return SNMPERR_SUCCESS;
  271.         return SNMPERR_GENERR;
  272.     }
  273.     new_col = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
  274.     new_col->type = type;
  275.     new_col->writable = writable;
  276.     new_col->column = column;
  277.     if (default_value) {
  278.         memdup((u_char **) & (new_col->data.voidp),
  279.                (u_char *) default_value, default_value_len);
  280.         new_col->data_len = default_value_len;
  281.     }
  282.     if (table_set->default_row == NULL)
  283.         table_set->default_row = new_col;
  284.     else {
  285.         /* sort in order just because (needed for add_row support) */
  286.         for (ptr = table_set->default_row, pptr = NULL;
  287.              ptr;
  288.              pptr = ptr, ptr = ptr->next) {
  289.             if (ptr->column > column) {
  290.                 new_col->next = ptr;
  291.                 if (pptr)
  292.                     pptr->next = new_col;
  293.                 else
  294.                     table_set->default_row = new_col;
  295.                 return SNMPERR_SUCCESS;
  296.             }
  297.         }
  298.         if (pptr)
  299.             pptr->next = new_col;
  300.         else
  301.             snmp_log(LOG_ERR,"Shouldn't have gotten here: table_dataset/add_row");
  302.     }
  303.     return SNMPERR_SUCCESS;
  304. }
  305. /** clones a dataset row, including all data. */
  306. netsnmp_table_row *
  307. netsnmp_table_data_set_clone_row(netsnmp_table_row *row)
  308. {
  309.     netsnmp_table_data_set_storage *data, **newrowdata;
  310.     netsnmp_table_row *newrow;
  311.     if (!row)
  312.         return NULL;
  313.     newrow = netsnmp_table_data_clone_row(row);
  314.     if (!newrow)
  315.         return NULL;
  316.     data = (netsnmp_table_data_set_storage *) row->data;
  317.     if (data) {
  318.         for (newrowdata =
  319.              (netsnmp_table_data_set_storage **) &(newrow->data); data;
  320.              newrowdata = &((*newrowdata)->next), data = data->next) {
  321.             memdup((u_char **) newrowdata, (u_char *) data,
  322.                    sizeof(netsnmp_table_data_set_storage));
  323.             if (!*newrowdata) {
  324.                 netsnmp_table_dataset_delete_row(newrow);
  325.                 return NULL;
  326.             }
  327.             if (data->data.voidp) {
  328.                 memdup((u_char **) & ((*newrowdata)->data.voidp),
  329.                        (u_char *) data->data.voidp, data->data_len);
  330.                 if (!(*newrowdata)->data.voidp) {
  331.                     netsnmp_table_dataset_delete_row(newrow);
  332.                     return NULL;
  333.                 }
  334.             }
  335.         }
  336.     }
  337.     return newrow;
  338. }
  339. /** creates a new row from an existing defined default set */
  340. netsnmp_table_row *
  341. netsnmp_table_data_set_create_row_from_defaults
  342.     (netsnmp_table_data_set_storage *defrow)
  343. {
  344.     netsnmp_table_row *row;
  345.     row = netsnmp_create_table_data_row();
  346.     if (!row)
  347.         return NULL;
  348.     for (; defrow; defrow = defrow->next) {
  349.         netsnmp_set_row_column(row, defrow->column, defrow->type,
  350.                                defrow->data.voidp, defrow->data_len);
  351.         if (defrow->writable)
  352.             netsnmp_mark_row_column_writable(row, defrow->column, 1);
  353.     }
  354.     return row;
  355. }
  356. newrow_stash   *
  357. netsnmp_table_data_set_create_newrowstash
  358.     (netsnmp_table_data_set     *datatable,
  359.      netsnmp_table_request_info *table_info)
  360. {
  361.     newrow_stash   *newrowstash = NULL;
  362.     netsnmp_table_row *newrow   = NULL;
  363.     newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
  364.     newrowstash->created = 1;
  365.     newrow = netsnmp_table_data_set_create_row_from_defaults
  366.                         (datatable->default_row);
  367.     newrow->indexes = snmp_clone_varbind(table_info->indexes);
  368.     newrowstash->newrow = newrow;
  369.     return newrowstash;
  370. }
  371. /** implements the table data helper.  This is the routine that takes
  372.  *  care of all SNMP requests coming into the table. */
  373. int
  374. netsnmp_table_data_set_helper_handler(netsnmp_mib_handler *handler,
  375.                                       netsnmp_handler_registration
  376.                                       *reginfo,
  377.                                       netsnmp_agent_request_info *reqinfo,
  378.                                       netsnmp_request_info *requests)
  379. {
  380.     netsnmp_table_data_set_storage *data = NULL;
  381.     newrow_stash   *newrowstash = NULL;
  382.     netsnmp_table_row *row, *newrow = NULL;
  383.     netsnmp_table_request_info *table_info;
  384.     netsnmp_request_info *request;
  385.     oid            *suffix;
  386.     size_t          suffix_len;
  387.     netsnmp_oid_stash_node **stashp = NULL;
  388.     if (!handler)
  389.         return SNMPERR_GENERR;
  390.         
  391.     DEBUGMSGTL(("netsnmp_table_data_set", "handler startingn"));
  392.     for (request = requests; request; request = request->next) {
  393.         netsnmp_table_data_set *datatable =
  394.             (netsnmp_table_data_set *) handler->myvoid;
  395.         if (request->processed)
  396.             continue;
  397.         /*
  398.          * extract our stored data and table info 
  399.          */
  400.         row = netsnmp_extract_table_row(request);
  401.         table_info = netsnmp_extract_table_info(request);
  402.         suffix = requests->requestvb->name + reginfo->rootoid_len + 2;
  403.         suffix_len = requests->requestvb->name_length -
  404.             (reginfo->rootoid_len + 2);
  405.         if (MODE_IS_SET(reqinfo->mode)) {
  406.             char buf[256]; /* is this reasonable size?? */
  407.             int  rc;
  408.             size_t len;
  409.             /*
  410.              * use a cached copy of the row for modification 
  411.              */
  412.             /*
  413.              * cache location: may have been created already by other
  414.              * SET requests in the same master request. 
  415.              */
  416.             rc = snprintf(buf, sizeof(buf), "dataset_row_stash:%s:",
  417.                           datatable->table->name);
  418.             if ((-1 == rc) || (rc >= sizeof(buf))) {
  419.                 snmp_log(LOG_ERR,"%s handler name too longn",
  420.                          datatable->table->name);
  421.                 netsnmp_set_request_error(reqinfo, request,
  422.                                           SNMP_ERR_GENERR);
  423.                 continue;
  424.             }
  425.             len = sizeof(buf) - rc;
  426.             rc = snprint_objid(&buf[rc], len, table_info->index_oid,
  427.                                table_info->index_oid_len);
  428.             if (-1 == rc) {
  429.                 snmp_log(LOG_ERR,"%s oid or name too longn",
  430.                          datatable->table->name);
  431.                 netsnmp_set_request_error(reqinfo, request,
  432.                                           SNMP_ERR_GENERR);
  433.                 continue;
  434.             }
  435.             stashp = (netsnmp_oid_stash_node **)
  436.                 netsnmp_table_get_or_create_row_stash(reqinfo, buf);
  437.             newrowstash
  438.                 = netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len);
  439.             if (!newrowstash) {
  440.                 if (!row) {
  441.                     if (datatable->allow_creation) {
  442.                         /*
  443.                          * entirely new row.  Create the row from the template 
  444.                          */
  445.                         newrowstash =
  446.                              netsnmp_table_data_set_create_newrowstash(
  447.                                                  datatable, table_info);
  448.                         newrow = newrowstash->newrow;
  449.                     } else if (datatable->rowstatus_column == 0) {
  450.                         /*
  451.                          * A RowStatus object may be used to control the
  452.                          *  creation of a new row.  But if this object
  453.                          *  isn't declared (and the table isn't marked as
  454.                          *  'auto-create'), then we can't create a new row.
  455.                          */
  456.                         netsnmp_set_request_error(reqinfo, request,
  457.                                                   SNMP_ERR_NOCREATION);
  458.                         continue;
  459.                     }
  460.                 } else {
  461.                     /*
  462.                      * existing row that needs to be modified 
  463.                      */
  464.                     newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
  465.                     newrow = netsnmp_table_data_set_clone_row(row);
  466.                     newrowstash->newrow = newrow;
  467.                 }
  468.                 netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
  469.                                            newrowstash);
  470.             } else {
  471.                 newrow = newrowstash->newrow;
  472.             }
  473.             /*
  474.              * all future SET data modification operations use this
  475.              * temp pointer 
  476.              */
  477.             if (reqinfo->mode == MODE_SET_RESERVE1 ||
  478.                 reqinfo->mode == MODE_SET_RESERVE2)
  479.                 row = newrow;
  480.         }
  481.         if (row)
  482.             data = (netsnmp_table_data_set_storage *) row->data;
  483.         if (!row || !table_info || !data) {
  484.             if (!MODE_IS_SET(reqinfo->mode)) {
  485.                 netsnmp_set_request_error(reqinfo, request,
  486.                                           SNMP_NOSUCHINSTANCE);
  487.                 continue;
  488.             }
  489.         }
  490.         data =
  491.             netsnmp_table_data_set_find_column(data, table_info->colnum);
  492.         switch (reqinfo->mode) {
  493.         case MODE_GET:
  494.         case MODE_GETNEXT:
  495.         case MODE_GETBULK:     /* XXXWWW */
  496.             if (data && data->data.voidp)
  497.                 netsnmp_table_data_build_result(reginfo, reqinfo, request,
  498.                                                 row,
  499.                                                 table_info->colnum,
  500.                                                 data->type,
  501.                                                 data->data.voidp,
  502.                                                 data->data_len);
  503.             break;
  504.         case MODE_SET_RESERVE1:
  505.             if (data) {
  506.                 /*
  507.                  * Can we modify the existing row?
  508.                  */
  509.                 if (!data->writable) {
  510.                     netsnmp_set_request_error(reqinfo, request,
  511.                                               SNMP_ERR_NOTWRITABLE);
  512.                 } else if (request->requestvb->type != data->type) {
  513.                     netsnmp_set_request_error(reqinfo, request,
  514.                                               SNMP_ERR_WRONGTYPE);
  515.                 }
  516.             } else if (datatable->rowstatus_column == table_info->colnum) {
  517.                 /*
  518.                  * Otherwise, this is where we create a new row using
  519.                  * the RowStatus object (essentially duplicating the
  520.                  * steps followed earlier in the 'allow_creation' case)
  521.                  */
  522.                 switch (*(request->requestvb->val.integer)) {
  523.                 case RS_CREATEANDGO:
  524.                 case RS_CREATEANDWAIT:
  525.                     newrowstash =
  526.                              netsnmp_table_data_set_create_newrowstash(
  527.                                                  datatable, table_info);
  528.                     newrow = newrowstash->newrow;
  529.                     row    = newrow;
  530.                     netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
  531.                                                newrowstash);
  532.                 }
  533.             }
  534.             break;
  535.         case MODE_SET_RESERVE2:
  536.             /*
  537.              * If the agent receives a SET request for an object in a non-existant
  538.              *  row, then the RESERVE1 pass will create the row automatically.
  539.              *
  540.              * But since the row doesn't exist at that point, the test for whether
  541.              *  the object is writable or not will be skipped.  So we need to check
  542.              *  for this possibility again here.
  543.              *
  544.              * Similarly, if row creation is under the control of the RowStatus
  545.              *  object (i.e. allow_creation == 0), but this particular request
  546.              *  doesn't include such an object, then the row won't have been created,
  547.              *  and the writable check will also have been skipped.  Again - check here.
  548.              */
  549.             if (data && data->writable == 0) {
  550.                 netsnmp_set_request_error(reqinfo, request,
  551.                                           SNMP_ERR_NOTWRITABLE);
  552.                 continue;
  553.             }
  554.             if (datatable->rowstatus_column == table_info->colnum) {
  555.                 switch (*(request->requestvb->val.integer)) {
  556.                 case RS_ACTIVE:
  557.                 case RS_NOTINSERVICE:
  558.                     /*
  559.                      * Can only operate on pre-existing rows.
  560.                      */
  561.                     if (!newrowstash || newrowstash->created) {
  562.                         netsnmp_set_request_error(reqinfo, request,
  563.                                                   SNMP_ERR_INCONSISTENTVALUE);
  564.                         continue;
  565.                     }
  566.                     break;
  567.                 case RS_CREATEANDGO:
  568.                 case RS_CREATEANDWAIT:
  569.                     /*
  570.                      * Can only operate on newly created rows.
  571.                      */
  572.                     if (!(newrowstash && newrowstash->created)) {
  573.                         netsnmp_set_request_error(reqinfo, request,
  574.                                                   SNMP_ERR_INCONSISTENTVALUE);
  575.                         continue;
  576.                     }
  577.                     break;
  578.                 case RS_DESTROY:
  579.                     /*
  580.                      * Can operate on new or pre-existing rows.
  581.                      */
  582.                     break;
  583.                 case RS_NOTREADY:
  584.                 default:
  585.                     /*
  586.                      * Not a valid value to Set 
  587.                      */
  588.                     netsnmp_set_request_error(reqinfo, request,
  589.                                               SNMP_ERR_WRONGVALUE);
  590.                     continue;
  591.                 }
  592.             }
  593.             if (!data ) {
  594.                 netsnmp_set_request_error(reqinfo, request,
  595.                                           SNMP_ERR_NOCREATION);
  596.                 continue;
  597.             }
  598.             /*
  599.              * modify row and set new value 
  600.              */
  601.             SNMP_FREE(data->data.string);
  602.             data->data.string =
  603.                 netsnmp_strdup_and_null(request->requestvb->val.string,
  604.                                         request->requestvb->val_len);
  605.             if (!data->data.string) {
  606.                 netsnmp_set_request_error(reqinfo, requests,
  607.                                           SNMP_ERR_RESOURCEUNAVAILABLE);
  608.             }
  609.             data->data_len = request->requestvb->val_len;
  610.             if (datatable->rowstatus_column == table_info->colnum) {
  611.                 switch (*(request->requestvb->val.integer)) {
  612.                 case RS_CREATEANDGO:
  613.                     /*
  614.                      * XXX: check legality 
  615.                      */
  616.                     *(data->data.integer) = RS_ACTIVE;
  617.                     break;
  618.                 case RS_CREATEANDWAIT:
  619.                     /*
  620.                      * XXX: check legality 
  621.                      */
  622.                     *(data->data.integer) = RS_NOTINSERVICE;
  623.                     break;
  624.                 case RS_DESTROY:
  625.                     newrowstash->deleted = 1;
  626.                     break;
  627.                 }
  628.             }
  629.             break;
  630.         case MODE_SET_ACTION:
  631.             /*
  632.              * Install the new row into the stored table.
  633.      * Do this only *once* per row ....
  634.              */
  635.             if (newrowstash->state != STATE_ACTION) {
  636.                 newrowstash->state = STATE_ACTION;
  637. if (newrowstash->created) {
  638.                     netsnmp_table_dataset_add_row(datatable, newrow);
  639.                 } else {
  640.                     netsnmp_table_dataset_replace_row(datatable,
  641.                                                       row, newrow);
  642.                 }
  643.             }
  644.             /*
  645.              * ... but every (relevant) varbind in the request will
  646.      * need to know about this new row, so update the
  647.      * per-request row information regardless
  648.              */
  649.             if (newrowstash->created) {
  650. netsnmp_request_add_list_data(request,
  651. netsnmp_create_data_list(TABLE_DATA_NAME,
  652.  newrow, NULL));
  653.             }
  654.             break;
  655.         case MODE_SET_UNDO:
  656.             /*
  657.              * extract the new row, replace with the old or delete 
  658.              */
  659.             if (newrowstash->state != STATE_UNDO) {
  660.                 newrowstash->state = STATE_UNDO;
  661.                 if (newrowstash->created) {
  662.                     netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
  663.                 } else {
  664.                     netsnmp_table_dataset_replace_row(datatable,
  665.                                                       newrow, row);
  666.                     netsnmp_table_dataset_delete_row(newrow);
  667.                 }
  668.             }
  669.             break;
  670.         case MODE_SET_COMMIT:
  671.             if (newrowstash->state != STATE_COMMIT) {
  672.                 newrowstash->state = STATE_COMMIT;
  673.                 if (!newrowstash->created) {
  674.                     netsnmp_table_dataset_delete_row(row);
  675.                 }
  676.                 if (newrowstash->deleted) {
  677.                     netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
  678.                 }
  679.             }
  680.             break;
  681.         case MODE_SET_FREE:
  682.             if (newrowstash && newrowstash->state != STATE_FREE) {
  683.                 newrowstash->state = STATE_FREE;
  684.                 netsnmp_table_dataset_delete_row(newrow);
  685.             }
  686.             break;
  687.         }
  688.     }
  689.     /* next handler called automatically - 'AUTO_NEXT' */
  690.     return SNMP_ERR_NOERROR;
  691. }
  692. /** registers a table_dataset so that the "add_row" snmpd.conf token
  693.   * can be used to add data to this table.  If registration_name is
  694.   * NULL then the name used when the table was created will be used
  695.   * instead.
  696.   *
  697.   * @todo create a properly free'ing registeration pointer for the
  698.   * datalist, and get the datalist freed at shutdown.
  699.   */
  700. void
  701. netsnmp_register_auto_data_table(netsnmp_table_data_set *table_set,
  702.                                  char *registration_name)
  703. {
  704.     data_set_tables *tables;
  705.     tables = SNMP_MALLOC_TYPEDEF(data_set_tables);
  706.     if (!tables)
  707.         return;
  708.     tables->table_set = table_set;
  709.     if (!registration_name) {
  710.         registration_name = table_set->table->name;
  711.     }
  712.     netsnmp_add_list_data(&auto_tables, netsnmp_create_data_list(registration_name, tables, NULL));     /* XXX */
  713. }
  714. #ifndef DISABLE_MIB_LOADING
  715. static void
  716. _table_set_add_indexes(netsnmp_table_data_set *table_set, struct tree *tp)
  717. {
  718.     oid             name[MAX_OID_LEN];
  719.     size_t          name_length = MAX_OID_LEN;
  720.     struct index_list *index;
  721.     struct tree     *indexnode;
  722.     u_char          type;
  723.     
  724.     /*
  725.      * loop through indexes and add types 
  726.      */
  727.     for (index = tp->indexes; index; index = index->next) {
  728.         if (!snmp_parse_oid(index->ilabel, name, &name_length) ||
  729.             (NULL ==
  730.              (indexnode = get_tree(name, name_length, get_tree_head())))) {
  731.             config_pwarn("can't instatiate table since "
  732.                          "I don't know anything about one index");
  733.             snmp_log(LOG_WARNING, "  index %s not found in treen",
  734.                      index->ilabel);
  735.             return;             /* xxx mem leak */
  736.         }
  737.             
  738.         type = mib_to_asn_type(indexnode->type);
  739.         if (type == (u_char) - 1) {
  740.             config_pwarn("unknown index type");
  741.             return;             /* xxx mem leak */
  742.         }
  743.         if (index->isimplied)   /* if implied, mark it as such */
  744.             type |= ASN_PRIVATE;
  745.             
  746.         DEBUGMSGTL(("table_set_add_table",
  747.                     "adding default index of type %dn", type));
  748.         netsnmp_table_dataset_add_index(table_set, type);
  749.     }
  750. }
  751. /** @internal */
  752. void
  753. netsnmp_config_parse_table_set(const char *token, char *line)
  754. {
  755.     oid             table_name[MAX_OID_LEN];
  756.     size_t          table_name_length = MAX_OID_LEN;
  757.     struct tree    *tp;
  758.     netsnmp_table_data_set *table_set;
  759.     data_set_tables *tables;
  760.     unsigned int    mincol = 0xffffff, maxcol = 0;
  761.     char           *pos;
  762.     /*
  763.      * instatiate a fake table based on MIB information 
  764.      */
  765.     DEBUGMSGTL(("9:table_set_add_table", "processing '%s'n", line));
  766.     if (NULL != (pos = strchr(line,' '))) {
  767.         config_pwarn("ignoring extra tokens on line");
  768.         snmp_log(LOG_WARNING,"  ignoring '%s'n", pos);
  769.         *pos = '';
  770.     }
  771.     /*
  772.      * check for duplicate table
  773.      */
  774.     tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, line);
  775.     if (NULL != tables) {
  776.         config_pwarn("duplicate table definition");
  777.         return;
  778.     }
  779.     /*
  780.      * parse oid and find tree structure
  781.      */
  782.     if (!snmp_parse_oid(line, table_name, &table_name_length)) {
  783.         config_pwarn
  784.             ("can't instatiate table since I can't parse the table name");
  785.         return;
  786.     }
  787.     if(NULL == (tp = get_tree(table_name, table_name_length,
  788.                               get_tree_head()))) {
  789.         config_pwarn("can't instatiate table since "
  790.                      "I can't find mib information about it");
  791.         return;
  792.     }
  793.     if (NULL == (tp = tp->child_list) || NULL == tp->child_list) {
  794.         config_pwarn("can't instatiate table since it doesn't appear to be "
  795.                      "a proper table (no children)");
  796.         return;
  797.     }
  798.     table_set = netsnmp_create_table_data_set(line);
  799.     /*
  800.      * check for augments indexes
  801.      */
  802.     if (NULL != tp->augments) {
  803.         oid             name[MAX_OID_LEN];
  804.         size_t          name_length = MAX_OID_LEN;
  805.         struct tree    *tp2;
  806.     
  807.         if (!snmp_parse_oid(tp->augments, name, &name_length)) {
  808.             config_pwarn("I can't parse the augment tabel name");
  809.             snmp_log(LOG_WARNING, "  can't parse %sn", tp->augments);
  810.             return;
  811.         }
  812.         if(NULL == (tp2 = get_tree(name, name_length, get_tree_head()))) {
  813.             config_pwarn("can't instatiate table since "
  814.                          "I can't find mib information about augment table");
  815.             snmp_log(LOG_WARNING, "  table %s not found in treen",
  816.                      tp->augments);
  817.             return;
  818.         }
  819.         _table_set_add_indexes(table_set, tp2);
  820.     }
  821.     _table_set_add_indexes(table_set, tp);
  822.     
  823.     /*
  824.      * loop through children and add each column info 
  825.      */
  826.     for (tp = tp->child_list; tp; tp = tp->next_peer) {
  827.         int             canwrite = 0;
  828.         u_char          type;
  829.         type = mib_to_asn_type(tp->type);
  830.         if (type == (u_char) - 1) {
  831.             config_pwarn("unknown column type");
  832.             return;             /* xxx mem leak */
  833.         }
  834.         DEBUGMSGTL(("table_set_add_table",
  835.                     "adding column %s(%d) of type %d (access %d)n",
  836.                     tp->label, tp->subid, type, tp->access));
  837.         switch (tp->access) {
  838.         case MIB_ACCESS_CREATE:
  839.             table_set->allow_creation = 1;
  840.         case MIB_ACCESS_READWRITE:
  841.         case MIB_ACCESS_WRITEONLY:
  842.             canwrite = 1;
  843.         case MIB_ACCESS_READONLY:
  844.             DEBUGMSGTL(("table_set_add_table",
  845.                         "adding column %d of type %dn", tp->subid, type));
  846.             netsnmp_table_set_add_default_row(table_set, tp->subid, type,
  847.                                               canwrite, NULL, 0);
  848.             mincol = SNMP_MIN(mincol, tp->subid);
  849.             maxcol = SNMP_MAX(maxcol, tp->subid);
  850.             break;
  851.         case MIB_ACCESS_NOACCESS:
  852.         case MIB_ACCESS_NOTIFY:
  853.             break;
  854.         default:
  855.             config_pwarn("unknown column access type");
  856.             break;
  857.         }
  858.     }
  859.     /*
  860.      * register the table 
  861.      */
  862.     netsnmp_register_table_data_set(netsnmp_create_handler_registration
  863.                                     (line, NULL, table_name,
  864.                                      table_name_length,
  865.                                      HANDLER_CAN_RWRITE), table_set, NULL);
  866.     netsnmp_register_auto_data_table(table_set, NULL);
  867. }
  868. #endif /* DISABLE_MIB_LOADING */
  869. /** @internal */
  870. void
  871. netsnmp_config_parse_add_row(const char *token, char *line)
  872. {
  873.     char            buf[SNMP_MAXBUF_MEDIUM];
  874.     char            tname[SNMP_MAXBUF_MEDIUM];
  875.     size_t          buf_size;
  876.     int             rc;
  877.     data_set_tables *tables;
  878.     netsnmp_variable_list *vb;  /* containing only types */
  879.     netsnmp_table_row *row;
  880.     netsnmp_table_data_set_storage *dr;
  881.     line = copy_nword(line, tname, SNMP_MAXBUF_MEDIUM);
  882.     tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, tname);
  883.     if (!tables) {
  884.         config_pwarn("Unknown table trying to add a row");
  885.         return;
  886.     }
  887.     /*
  888.      * do the indexes first 
  889.      */
  890.     row = netsnmp_create_table_data_row();
  891.     for (vb = tables->table_set->table->indexes_template; vb;
  892.          vb = vb->next_variable) {
  893.         if (!line) {
  894.             config_pwarn("missing an index value");
  895.             return;
  896.         }
  897.         DEBUGMSGTL(("table_set_add_row", "adding index of type %dn",
  898.                     vb->type));
  899.         buf_size = SNMP_MAXBUF_MEDIUM;
  900.         line = read_config_read_memory(vb->type, line, buf, &buf_size);
  901.         netsnmp_table_row_add_index(row, vb->type, buf, buf_size);
  902.     }
  903.     /*
  904.      * then do the data 
  905.      */
  906.     for (dr = tables->table_set->default_row; dr; dr = dr->next) {
  907.         if (!line) {
  908.             config_pwarn("missing a data value. "
  909.                          "All columns must be specified.");
  910.             snmp_log(LOG_WARNING,"  can't find value for column %dn",
  911.                      dr->column - 1);
  912.             return;
  913.         }
  914.         buf_size = SNMP_MAXBUF_MEDIUM;
  915.         line = read_config_read_memory(dr->type, line, buf, &buf_size);
  916.         DEBUGMSGTL(("table_set_add_row",
  917.                     "adding data at column %d of type %dn", dr->column,
  918.                     dr->type));
  919.         netsnmp_set_row_column(row, dr->column, dr->type, buf, buf_size);
  920.         if (dr->writable)
  921.             netsnmp_mark_row_column_writable(row, dr->column, 1);       /* make writable */
  922.     }
  923.     rc = netsnmp_table_data_add_row(tables->table_set->table, row);
  924.     if (SNMPERR_SUCCESS != rc) {
  925.         config_pwarn("error adding table row");
  926.     }
  927.     if (NULL != line) {
  928.         config_pwarn("extra data value. Too many columns specified.");
  929.         snmp_log(LOG_WARNING,"  extra data '%s'n", line);
  930.     }
  931. }
  932. /** adds an index to the table.  Call this repeatly for each index. */
  933. NETSNMP_INLINE void
  934. netsnmp_table_dataset_add_index(netsnmp_table_data_set *table, u_char type)
  935. {
  936.     if (!table)
  937.         return;
  938.     netsnmp_table_data_add_index(table->table, type);
  939. }
  940. /** adds a new row to a dataset table */
  941. NETSNMP_INLINE void
  942. netsnmp_table_dataset_add_row(netsnmp_table_data_set *table,
  943.                               netsnmp_table_row *row)
  944. {
  945.     if (!table)
  946.         return;
  947.     netsnmp_table_data_add_row(table->table, row);
  948. }
  949. /** adds a new row to a dataset table */
  950. NETSNMP_INLINE void
  951. netsnmp_table_dataset_replace_row(netsnmp_table_data_set *table,
  952.                                   netsnmp_table_row *origrow,
  953.                                   netsnmp_table_row *newrow)
  954. {
  955.     if (!table)
  956.         return;
  957.     netsnmp_table_data_replace_row(table->table, origrow, newrow);
  958. }
  959. /** deletes a single dataset table data.
  960.  *  returns the (possibly still good) next pointer of the deleted data object.
  961.  */
  962. NETSNMP_INLINE netsnmp_table_data_set_storage *
  963. netsnmp_table_dataset_delete_data(netsnmp_table_data_set_storage *data)
  964. {
  965.     netsnmp_table_data_set_storage *nextPtr = NULL;
  966.     if (data) {
  967.         nextPtr = data->next;
  968.         SNMP_FREE(data->data.voidp);
  969.     }
  970.     SNMP_FREE(data);
  971.     return nextPtr;
  972. }
  973. /** deletes all the data from this node and beyond in the linked list */
  974. NETSNMP_INLINE void
  975. netsnmp_table_dataset_delete_all_data(netsnmp_table_data_set_storage *data)
  976. {
  977.     while (data) {
  978.         data = netsnmp_table_dataset_delete_data(data);
  979.     }
  980. }
  981. /** deletes all the data from this node and beyond in the linked list */
  982. NETSNMP_INLINE void
  983. netsnmp_table_dataset_delete_row(netsnmp_table_row *row)
  984. {
  985.     netsnmp_table_data_set_storage *data;
  986.     if (!row)
  987.         return;
  988.     data = netsnmp_table_data_delete_row(row);
  989.     netsnmp_table_dataset_delete_all_data(data);
  990. }
  991. /** removes a row from the table, but doesn't delete/free anything */
  992. NETSNMP_INLINE void
  993. netsnmp_table_dataset_remove_row(netsnmp_table_data_set *table,
  994.                                  netsnmp_table_row *row)
  995. {
  996.     if (!table)
  997.         return;
  998.     netsnmp_table_data_remove_and_delete_row(table->table, row);
  999. }
  1000. /** removes a row from the table and then deletes it (and all it's data) */
  1001. NETSNMP_INLINE void
  1002. netsnmp_table_dataset_remove_and_delete_row(netsnmp_table_data_set *table,
  1003.                                             netsnmp_table_row *row)
  1004. {
  1005.     netsnmp_table_data_set_storage *data;
  1006.     if (!table)
  1007.         return;
  1008.     data = (netsnmp_table_data_set_storage *)
  1009.         netsnmp_table_data_remove_and_delete_row(table->table, row);
  1010.     netsnmp_table_dataset_delete_all_data(data);
  1011. }
  1012. /** adds multiple data column definitions to each row.  Functionally,
  1013.  *  this is a wrapper around calling netsnmp_table_set_add_default_row
  1014.  *  repeatedly for you.
  1015.  */
  1016. void
  1017. #if HAVE_STDARG_H
  1018. netsnmp_table_set_multi_add_default_row(netsnmp_table_data_set *tset, ...)
  1019. #else
  1020. netsnmp_table_set_multi_add_default_row(va_dcl
  1021.     )
  1022.      va_dcl
  1023. #endif
  1024. {
  1025.     va_list         debugargs;
  1026.     unsigned int    column;
  1027.     int             type, writable;
  1028.     void           *data;
  1029.     size_t          data_len;
  1030. #if HAVE_STDARG_H
  1031.     va_start(debugargs, tset);
  1032. #else
  1033.     netsnmp_table_data_set *tset;
  1034.     va_start(debugargs);
  1035.     tset = va_arg(debugargs, netsnmp_table_data_set *);
  1036. #endif
  1037.     while ((column = va_arg(debugargs, unsigned int)) != 0) {
  1038.         type = va_arg(debugargs, int);
  1039.         writable = va_arg(debugargs, int);
  1040.         data = va_arg(debugargs, void *);
  1041.         data_len = va_arg(debugargs, size_t);
  1042.         netsnmp_table_set_add_default_row(tset, column, type, writable,
  1043.                                           data, data_len);
  1044.     }
  1045.     va_end(debugargs);
  1046. }
  1047. /** adds multiple indexes to a table_dataset helper object.
  1048.  *  To end the list, use a 0 after the list of ASN index types. */
  1049. void
  1050. #if HAVE_STDARG_H
  1051. netsnmp_table_set_add_indexes(netsnmp_table_data_set *tset,
  1052.                               ...)
  1053. #else
  1054. netsnmp_table_set_add_indexes(va_alist)
  1055.      va_dcl
  1056. #endif
  1057. {
  1058.     va_list         debugargs;
  1059.     int             type;
  1060. #if HAVE_STDARG_H
  1061.     va_start(debugargs, tset);
  1062. #else
  1063.     netsnmp_table_data_set *tset;
  1064.     va_start(debugargs);
  1065.     tset = va_arg(debugargs, netsnmp_table_data_set *);
  1066. #endif
  1067.     while ((type = va_arg(debugargs, int)) != 0) {
  1068.         netsnmp_table_dataset_add_index(tset, (u_char)type);
  1069.     }
  1070.     va_end(debugargs);
  1071. }
  1072. int
  1073. netsnmp_table_set_num_rows(netsnmp_table_data_set *table)
  1074. {
  1075.     if (!table)
  1076.         return 0;
  1077.     return netsnmp_table_data_num_rows(table->table);
  1078. }
  1079. /*
  1080.  * @} 
  1081.  */