net-snmp 5.7
table_dataset.c
00001 #include <net-snmp/net-snmp-config.h>
00002 #include <net-snmp/net-snmp-features.h>
00003 
00004 #include <net-snmp/net-snmp-features.h>
00005 #include <net-snmp/net-snmp-includes.h>
00006 #include <net-snmp/agent/net-snmp-agent-includes.h>
00007 
00008 #include <net-snmp/agent/table_dataset.h>
00009 
00010 #if HAVE_STRING_H
00011 #include <string.h>
00012 #else
00013 #include <strings.h>
00014 #endif
00015 
00016 netsnmp_feature_child_of(table_dataset_all, mib_helpers)
00017 netsnmp_feature_child_of(table_dataset, table_dataset_all)
00018 netsnmp_feature_child_of(table_dataset_remove_row, table_dataset_all)
00019 netsnmp_feature_child_of(table_data_set_column, table_dataset_all)
00020 netsnmp_feature_child_of(table_dataset_get_newrow, table_dataset_all)
00021 netsnmp_feature_child_of(table_set_add_indexes, table_dataset_all)
00022 netsnmp_feature_child_of(delete_table_data_set, table_dataset_all)
00023 netsnmp_feature_child_of(table_set_multi_add_default_row, table_dataset_all)
00024 netsnmp_feature_child_of(table_dataset_unregister_auto_data_table, table_dataset_all)
00025 
00026 #ifdef NETSNMP_FEATURE_REQUIRE_TABLE_DATASET
00027 netsnmp_feature_require(table_get_or_create_row_stash)
00028 netsnmp_feature_require(table_data_delete_table)
00029 netsnmp_feature_require(table_data)
00030 netsnmp_feature_require(oid_stash_get_data)
00031 netsnmp_feature_require(oid_stash_add_data)
00032 #endif /* NETSNMP_FEATURE_REQUIRE_TABLE_DATASET */
00033 
00034 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET
00035 
00036 #ifndef NETSNMP_NO_WRITE_SUPPORT
00037 netsnmp_feature_require(oid_stash)
00038 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
00039 
00040 #ifndef NETSNMP_DISABLE_MIB_LOADING
00041 netsnmp_feature_require(mib_to_asn_type)
00042 #endif /* NETSNMP_DISABLE_MIB_LOADING */
00043 
00044 static netsnmp_data_list *auto_tables;
00045 
00046 typedef struct data_set_tables_s {
00047     netsnmp_table_data_set *table_set;
00048 } data_set_tables;
00049 
00050 typedef struct data_set_cache_s {
00051     void           *data;
00052     size_t          data_len;
00053 } data_set_cache;
00054 
00055 #define STATE_ACTION   1
00056 #define STATE_COMMIT   2
00057 #define STATE_UNDO     3
00058 #define STATE_FREE     4
00059 
00060 typedef struct newrow_stash_s {
00061     netsnmp_table_row *newrow;
00062     int             state;
00063     int             created;
00064     int             deleted;
00065 } newrow_stash;
00066 
00090 void
00091 netsnmp_init_table_dataset(void) {
00092 #ifndef NETSNMP_DISABLE_MIB_LOADING
00093     register_app_config_handler("table",
00094                                 netsnmp_config_parse_table_set, NULL,
00095                                 "tableoid");
00096 #endif /* NETSNMP_DISABLE_MIB_LOADING */
00097     register_app_config_handler("add_row", netsnmp_config_parse_add_row,
00098                                 NULL, "table_name indexes... values...");
00099 }
00100 
00101 /* ==================================
00102  *
00103  * Data Set API: Table maintenance
00104  *
00105  * ================================== */
00106 
00110 NETSNMP_INLINE netsnmp_table_data_set_storage *
00111 netsnmp_table_dataset_delete_data(netsnmp_table_data_set_storage *data)
00112 {
00113     netsnmp_table_data_set_storage *nextPtr = NULL;
00114     if (data) {
00115         nextPtr = data->next;
00116         SNMP_FREE(data->data.voidp);
00117     }
00118     SNMP_FREE(data);
00119     return nextPtr;
00120 }
00121 
00123 NETSNMP_INLINE void
00124 netsnmp_table_dataset_delete_all_data(netsnmp_table_data_set_storage *data)
00125 {
00126 
00127     while (data) {
00128         data = netsnmp_table_dataset_delete_data(data);
00129     }
00130 }
00131 
00133 NETSNMP_INLINE void
00134 netsnmp_table_dataset_delete_row(netsnmp_table_row *row)
00135 {
00136     netsnmp_table_data_set_storage *data;
00137 
00138     if (!row)
00139         return;
00140 
00141     data = (netsnmp_table_data_set_storage*)netsnmp_table_data_delete_row(row);
00142     netsnmp_table_dataset_delete_all_data(data);
00143 }
00144 
00146 NETSNMP_INLINE void
00147 netsnmp_table_dataset_add_row(netsnmp_table_data_set *table,
00148                               netsnmp_table_row *row)
00149 {
00150     if (!table)
00151         return;
00152     netsnmp_table_data_add_row(table->table, row);
00153 }
00154 
00156 NETSNMP_INLINE void
00157 netsnmp_table_dataset_replace_row(netsnmp_table_data_set *table,
00158                                   netsnmp_table_row *origrow,
00159                                   netsnmp_table_row *newrow)
00160 {
00161     if (!table)
00162         return;
00163     netsnmp_table_data_replace_row(table->table, origrow, newrow);
00164 }
00165 
00167 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_REMOVE_ROW
00168 NETSNMP_INLINE void
00169 netsnmp_table_dataset_remove_row(netsnmp_table_data_set *table,
00170                                  netsnmp_table_row *row)
00171 {
00172     if (!table)
00173         return;
00174 
00175     netsnmp_table_data_remove_and_delete_row(table->table, row);
00176 }
00177 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_REMOVE_ROW */
00178 
00180 NETSNMP_INLINE void
00181 netsnmp_table_dataset_remove_and_delete_row(netsnmp_table_data_set *table,
00182                                             netsnmp_table_row *row)
00183 {
00184     netsnmp_table_data_set_storage *data;
00185 
00186     if (!table)
00187         return;
00188 
00189     data = (netsnmp_table_data_set_storage *)
00190         netsnmp_table_data_remove_and_delete_row(table->table, row);
00191 
00192     netsnmp_table_dataset_delete_all_data(data);
00193 }
00194 
00196 netsnmp_table_data_set *
00197 netsnmp_create_table_data_set(const char *table_name)
00198 {
00199     netsnmp_table_data_set *table_set =
00200         SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set);
00201     if (!table_set)
00202         return NULL;
00203     table_set->table = netsnmp_create_table_data(table_name);
00204     return table_set;
00205 }
00206 
00207 #ifndef NETSNMP_FEATURE_REMOVE_DELETE_TABLE_DATA_SET
00208 void netsnmp_delete_table_data_set(netsnmp_table_data_set *table_set)
00209 {
00210     netsnmp_table_data_set_storage *ptr, *next;
00211     netsnmp_table_row *prow, *pnextrow;
00212 
00213     for (ptr = table_set->default_row; ptr; ptr = next) {
00214         next = ptr->next;
00215         free(ptr);
00216     }
00217     table_set->default_row = NULL;
00218     for (prow = table_set->table->first_row; prow; prow = pnextrow) {
00219         pnextrow = prow->next;
00220         netsnmp_table_dataset_remove_and_delete_row(table_set, prow);
00221     }
00222     table_set->table->first_row = NULL;
00223     netsnmp_table_data_delete_table(table_set->table);
00224     free(table_set);
00225 }
00226 #endif /* NETSNMP_FEATURE_REMOVE_DELETE_TABLE_DATA_SET */
00227 
00229 netsnmp_table_row *
00230 netsnmp_table_data_set_clone_row(netsnmp_table_row *row)
00231 {
00232     netsnmp_table_data_set_storage *data, **newrowdata;
00233     netsnmp_table_row *newrow;
00234 
00235     if (!row)
00236         return NULL;
00237 
00238     newrow = netsnmp_table_data_clone_row(row);
00239     if (!newrow)
00240         return NULL;
00241 
00242     data = (netsnmp_table_data_set_storage *) row->data;
00243 
00244     if (data) {
00245         for (newrowdata =
00246              (netsnmp_table_data_set_storage **) &(newrow->data); data;
00247              newrowdata = &((*newrowdata)->next), data = data->next) {
00248 
00249             memdup((u_char **) newrowdata, (u_char *) data,
00250                    sizeof(netsnmp_table_data_set_storage));
00251             if (!*newrowdata) {
00252                 netsnmp_table_dataset_delete_row(newrow);
00253                 return NULL;
00254             }
00255 
00256             if (data->data.voidp) {
00257                 memdup((u_char **) & ((*newrowdata)->data.voidp),
00258                        (u_char *) data->data.voidp, data->data_len);
00259                 if (!(*newrowdata)->data.voidp) {
00260                     netsnmp_table_dataset_delete_row(newrow);
00261                     return NULL;
00262                 }
00263             }
00264         }
00265     }
00266     return newrow;
00267 }
00268 
00269 /* ==================================
00270  *
00271  * Data Set API: Default row operations
00272  *
00273  * ================================== */
00274 
00276 netsnmp_table_row *
00277 netsnmp_table_data_set_create_row_from_defaults
00278     (netsnmp_table_data_set_storage *defrow)
00279 {
00280     netsnmp_table_row *row;
00281     row = netsnmp_create_table_data_row();
00282     if (!row)
00283         return NULL;
00284     for (; defrow; defrow = defrow->next) {
00285         netsnmp_set_row_column(row, defrow->column, defrow->type,
00286                                defrow->data.voidp, defrow->data_len);
00287 #ifndef NETSNMP_NO_WRITE_SUPPORT
00288         if (defrow->writable)
00289             netsnmp_mark_row_column_writable(row, defrow->column, 1);
00290 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
00291     }
00292     return row;
00293 }
00294 
00304 int
00305 netsnmp_table_set_add_default_row(netsnmp_table_data_set *table_set,
00306                                   unsigned int column,
00307                                   int type, int writable,
00308                                   void *default_value,
00309                                   size_t default_value_len)
00310 {
00311     netsnmp_table_data_set_storage *new_col, *ptr, *pptr;
00312 
00313     if (!table_set)
00314         return SNMPERR_GENERR;
00315 
00316     /*
00317      * double check 
00318      */
00319     new_col =
00320         netsnmp_table_data_set_find_column(table_set->default_row, column);
00321     if (new_col != NULL) {
00322         if (new_col->type == type && new_col->writable == writable)
00323             return SNMPERR_SUCCESS;
00324         return SNMPERR_GENERR;
00325     }
00326 
00327     new_col = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
00328     if (new_col == NULL)
00329         return SNMPERR_GENERR;
00330     new_col->type = type;
00331     new_col->writable = writable;
00332     new_col->column = column;
00333     if (default_value) {
00334         memdup((u_char **) & (new_col->data.voidp),
00335                (u_char *) default_value, default_value_len);
00336         new_col->data_len = default_value_len;
00337     }
00338     if (table_set->default_row == NULL)
00339         table_set->default_row = new_col;
00340     else {
00341         /* sort in order just because (needed for add_row support) */
00342         for (ptr = table_set->default_row, pptr = NULL;
00343              ptr;
00344              pptr = ptr, ptr = ptr->next) {
00345             if (ptr->column > column) {
00346                 new_col->next = ptr;
00347                 if (pptr)
00348                     pptr->next = new_col;
00349                 else
00350                     table_set->default_row = new_col;
00351                 return SNMPERR_SUCCESS;
00352             }
00353         }
00354         if (pptr)
00355             pptr->next = new_col;
00356         else
00357             snmp_log(LOG_ERR,"Shouldn't have gotten here: table_dataset/add_row");
00358     }
00359     return SNMPERR_SUCCESS;
00360 }
00361 
00366 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_SET_MULTI_ADD_DEFAULT_ROW
00367 void
00368 netsnmp_table_set_multi_add_default_row(netsnmp_table_data_set *tset, ...)
00369 {
00370     va_list         debugargs;
00371     unsigned int    column;
00372     int             type, writable;
00373     void           *data;
00374     size_t          data_len;
00375 
00376     va_start(debugargs, tset);
00377 
00378     while ((column = va_arg(debugargs, unsigned int)) != 0) {
00379         type = va_arg(debugargs, int);
00380         writable = va_arg(debugargs, int);
00381         data = va_arg(debugargs, void *);
00382         data_len = va_arg(debugargs, size_t);
00383         netsnmp_table_set_add_default_row(tset, column, type, writable,
00384                                           data, data_len);
00385     }
00386 
00387     va_end(debugargs);
00388 }
00389 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_SET_MULTI_ADD_DEFAULT_ROW */
00390 
00391 /* ==================================
00392  *
00393  * Data Set API: MIB maintenance
00394  *
00395  * ================================== */
00396 
00398 netsnmp_mib_handler *
00399 netsnmp_get_table_data_set_handler(netsnmp_table_data_set *data_set)
00400 {
00401     netsnmp_mib_handler *ret = NULL;
00402 
00403     if (!data_set) {
00404         snmp_log(LOG_INFO,
00405                  "netsnmp_get_table_data_set_handler(NULL) called\n");
00406         return NULL;
00407     }
00408 
00409     ret =
00410         netsnmp_create_handler(TABLE_DATA_SET_NAME,
00411                                netsnmp_table_data_set_helper_handler);
00412     if (ret) {
00413         ret->flags |= MIB_HANDLER_AUTO_NEXT;
00414         ret->myvoid = (void *) data_set;
00415     }
00416     return ret;
00417 }
00418 
00424 int
00425 netsnmp_register_table_data_set(netsnmp_handler_registration *reginfo,
00426                                 netsnmp_table_data_set *data_set,
00427                                 netsnmp_table_registration_info *table_info)
00428 {
00429     int ret;
00430 
00431     if (NULL == table_info) {
00432         /*
00433          * allocate the table if one wasn't allocated 
00434          */
00435         table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
00436         if (table_info == NULL)
00437             return SNMP_ERR_GENERR;
00438     }
00439 
00440     if (NULL == table_info->indexes && data_set->table->indexes_template) {
00441         /*
00442          * copy the indexes in 
00443          */
00444         table_info->indexes =
00445             snmp_clone_varbind(data_set->table->indexes_template);
00446     }
00447 
00448     if ((!table_info->min_column || !table_info->max_column) &&
00449         (data_set->default_row)) {
00450         /*
00451          * determine min/max columns 
00452          */
00453         unsigned int    mincol = 0xffffffff, maxcol = 0;
00454         netsnmp_table_data_set_storage *row;
00455 
00456         for (row = data_set->default_row; row; row = row->next) {
00457             mincol = SNMP_MIN(mincol, row->column);
00458             maxcol = SNMP_MAX(maxcol, row->column);
00459         }
00460         if (!table_info->min_column)
00461             table_info->min_column = mincol;
00462         if (!table_info->max_column)
00463             table_info->max_column = maxcol;
00464     }
00465 
00466     netsnmp_inject_handler(reginfo,
00467                            netsnmp_get_table_data_set_handler(data_set));
00468     ret = netsnmp_register_table_data(reginfo, data_set->table,
00469                                        table_info);
00470     if (reginfo->handler)
00471         netsnmp_handler_owns_table_info(reginfo->handler->next);
00472     return ret;
00473 }
00474 
00475 newrow_stash   *
00476 netsnmp_table_data_set_create_newrowstash
00477     (netsnmp_table_data_set     *datatable,
00478      netsnmp_table_request_info *table_info)
00479 {
00480     newrow_stash   *newrowstash = NULL;
00481     netsnmp_table_row *newrow   = NULL;
00482 
00483     newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
00484 
00485     if (newrowstash != NULL) {
00486         newrowstash->created = 1;
00487         newrow = netsnmp_table_data_set_create_row_from_defaults
00488             (datatable->default_row);
00489         newrow->indexes = snmp_clone_varbind(table_info->indexes);
00490         newrowstash->newrow = newrow;
00491     }
00492 
00493     return newrowstash;
00494 }
00495 
00496 /* implements the table data helper.  This is the routine that takes
00497  *  care of all SNMP requests coming into the table. */
00498 int
00499 netsnmp_table_data_set_helper_handler(netsnmp_mib_handler *handler,
00500                                       netsnmp_handler_registration
00501                                       *reginfo,
00502                                       netsnmp_agent_request_info *reqinfo,
00503                                       netsnmp_request_info *requests)
00504 {
00505     netsnmp_table_data_set_storage *data = NULL;
00506     newrow_stash   *newrowstash = NULL;
00507     netsnmp_table_row *row, *newrow = NULL;
00508     netsnmp_table_request_info *table_info;
00509     netsnmp_request_info *request;
00510     netsnmp_oid_stash_node **stashp = NULL;
00511 
00512     if (!handler)
00513         return SNMPERR_GENERR;
00514         
00515     DEBUGMSGTL(("netsnmp_table_data_set", "handler starting\n"));
00516     for (request = requests; request; request = request->next) {
00517         netsnmp_table_data_set *datatable =
00518             (netsnmp_table_data_set *) handler->myvoid;
00519         const oid * const suffix =
00520             requests->requestvb->name + reginfo->rootoid_len + 2;
00521         const size_t suffix_len =
00522             requests->requestvb->name_length - (reginfo->rootoid_len + 2);
00523 
00524         if (request->processed)
00525             continue;
00526 
00527         /*
00528          * extract our stored data and table info 
00529          */
00530         row = netsnmp_extract_table_row(request);
00531         table_info = netsnmp_extract_table_info(request);
00532 
00533 #ifndef NETSNMP_NO_WRITE_SUPPORT
00534         if (MODE_IS_SET(reqinfo->mode)) {
00535 
00536             /*
00537              * use a cached copy of the row for modification 
00538              */
00539 
00540             /*
00541              * cache location: may have been created already by other
00542              * SET requests in the same master request. 
00543              */
00544             stashp = netsnmp_table_dataset_get_or_create_stash(reqinfo,
00545                                                                datatable,
00546                                                                table_info);
00547             if (NULL == stashp) {
00548                 netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
00549                 continue;
00550             }
00551 
00552             newrowstash
00553                 = (newrow_stash*)netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len);
00554 
00555             if (!newrowstash) {
00556                 if (!row) {
00557                     if (datatable->allow_creation) {
00558                         /*
00559                          * entirely new row.  Create the row from the template 
00560                          */
00561                         newrowstash =
00562                              netsnmp_table_data_set_create_newrowstash(
00563                                                  datatable, table_info);
00564                         newrow = newrowstash->newrow;
00565                     } else if (datatable->rowstatus_column == 0) {
00566                         /*
00567                          * A RowStatus object may be used to control the
00568                          *  creation of a new row.  But if this object
00569                          *  isn't declared (and the table isn't marked as
00570                          *  'auto-create'), then we can't create a new row.
00571                          */
00572                         netsnmp_set_request_error(reqinfo, request,
00573                                                   SNMP_ERR_NOCREATION);
00574                         continue;
00575                     }
00576                 } else {
00577                     /*
00578                      * existing row that needs to be modified 
00579                      */
00580                     newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
00581                     if (newrowstash == NULL) {
00582                         netsnmp_set_request_error(reqinfo, request,
00583                                                   SNMP_ERR_GENERR);
00584                         continue;
00585                     }
00586                     newrow = netsnmp_table_data_set_clone_row(row);
00587                     newrowstash->newrow = newrow;
00588                 }
00589                 netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
00590                                            newrowstash);
00591             } else {
00592                 newrow = newrowstash->newrow;
00593             }
00594             /*
00595              * all future SET data modification operations use this
00596              * temp pointer 
00597              */
00598             if (reqinfo->mode == MODE_SET_RESERVE1 ||
00599                 reqinfo->mode == MODE_SET_RESERVE2)
00600                 row = newrow;
00601         }
00602 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00603 
00604         if (row)
00605             data = (netsnmp_table_data_set_storage *) row->data;
00606 
00607         if (!row || !table_info || !data) {
00608             if (!table_info
00609 #ifndef NETSNMP_NO_WRITE_SUPPORT
00610                 || !MODE_IS_SET(reqinfo->mode)
00611 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
00612                 ) {
00613                 netsnmp_set_request_error(reqinfo, request,
00614                                           SNMP_NOSUCHINSTANCE);
00615                 continue;
00616             }
00617         }
00618 
00619         data =
00620             netsnmp_table_data_set_find_column(data, table_info->colnum);
00621 
00622         switch (reqinfo->mode) {
00623         case MODE_GET:
00624         case MODE_GETNEXT:
00625         case MODE_GETBULK:     /* XXXWWW */
00626             if (!data || data->type == SNMP_NOSUCHINSTANCE) {
00627                 netsnmp_set_request_error(reqinfo, request,
00628                                           SNMP_NOSUCHINSTANCE);
00629             } else {
00630                 /*
00631                  * Note: data->data.voidp can be NULL, e.g. when a zero-length
00632                  * octet string has been stored in the table cache.
00633                  */
00634                 netsnmp_table_data_build_result(reginfo, reqinfo, request,
00635                                                 row,
00636                                                 table_info->colnum,
00637                                                 data->type,
00638                                        (u_char*)data->data.voidp,
00639                                                 data->data_len);
00640             }
00641             break;
00642 
00643 #ifndef NETSNMP_NO_WRITE_SUPPORT
00644         case MODE_SET_RESERVE1:
00645             if (data) {
00646                 /*
00647                  * Can we modify the existing row?
00648                  */
00649                 if (!data->writable) {
00650                     netsnmp_set_request_error(reqinfo, request,
00651                                               SNMP_ERR_NOTWRITABLE);
00652                 } else if (request->requestvb->type != data->type) {
00653                     netsnmp_set_request_error(reqinfo, request,
00654                                               SNMP_ERR_WRONGTYPE);
00655                 }
00656             } else if (datatable->rowstatus_column == table_info->colnum) {
00657                 /*
00658                  * Otherwise, this is where we create a new row using
00659                  * the RowStatus object (essentially duplicating the
00660                  * steps followed earlier in the 'allow_creation' case)
00661                  */
00662                 switch (*(request->requestvb->val.integer)) {
00663                 case RS_CREATEANDGO:
00664                 case RS_CREATEANDWAIT:
00665                     newrowstash =
00666                              netsnmp_table_data_set_create_newrowstash(
00667                                                  datatable, table_info);
00668                     newrow = newrowstash->newrow;
00669                     row    = newrow;
00670                     netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
00671                                                newrowstash);
00672                 }
00673             }
00674             break;
00675 
00676         case MODE_SET_RESERVE2:
00677             /*
00678              * If the agent receives a SET request for an object in a non-existant
00679              *  row, then the RESERVE1 pass will create the row automatically.
00680              *
00681              * But since the row doesn't exist at that point, the test for whether
00682              *  the object is writable or not will be skipped.  So we need to check
00683              *  for this possibility again here.
00684              *
00685              * Similarly, if row creation is under the control of the RowStatus
00686              *  object (i.e. allow_creation == 0), but this particular request
00687              *  doesn't include such an object, then the row won't have been created,
00688              *  and the writable check will also have been skipped.  Again - check here.
00689              */
00690             if (data && data->writable == 0) {
00691                 netsnmp_set_request_error(reqinfo, request,
00692                                           SNMP_ERR_NOTWRITABLE);
00693                 continue;
00694             }
00695             if (datatable->rowstatus_column == table_info->colnum) {
00696                 switch (*(request->requestvb->val.integer)) {
00697                 case RS_ACTIVE:
00698                 case RS_NOTINSERVICE:
00699                     /*
00700                      * Can only operate on pre-existing rows.
00701                      */
00702                     if (!newrowstash || newrowstash->created) {
00703                         netsnmp_set_request_error(reqinfo, request,
00704                                                   SNMP_ERR_INCONSISTENTVALUE);
00705                         continue;
00706                     }
00707                     break;
00708 
00709                 case RS_CREATEANDGO:
00710                 case RS_CREATEANDWAIT:
00711                     /*
00712                      * Can only operate on newly created rows.
00713                      */
00714                     if (!(newrowstash && newrowstash->created)) {
00715                         netsnmp_set_request_error(reqinfo, request,
00716                                                   SNMP_ERR_INCONSISTENTVALUE);
00717                         continue;
00718                     }
00719                     break;
00720 
00721                 case RS_DESTROY:
00722                     /*
00723                      * Can operate on new or pre-existing rows.
00724                      */
00725                     break;
00726 
00727                 case RS_NOTREADY:
00728                 default:
00729                     /*
00730                      * Not a valid value to Set 
00731                      */
00732                     netsnmp_set_request_error(reqinfo, request,
00733                                               SNMP_ERR_WRONGVALUE);
00734                     continue;
00735                 }
00736             }
00737             if (!data ) {
00738                 netsnmp_set_request_error(reqinfo, request,
00739                                           SNMP_ERR_NOCREATION);
00740                 continue;
00741             }
00742 
00743             /*
00744              * modify row and set new value 
00745              */
00746             SNMP_FREE(data->data.string);
00747             data->data.string = (u_char *)
00748                 netsnmp_strdup_and_null(request->requestvb->val.string,
00749                                         request->requestvb->val_len);
00750             if (!data->data.string) {
00751                 netsnmp_set_request_error(reqinfo, requests,
00752                                           SNMP_ERR_RESOURCEUNAVAILABLE);
00753             }
00754             data->data_len = request->requestvb->val_len;
00755 
00756             if (datatable->rowstatus_column == table_info->colnum) {
00757                 switch (*(request->requestvb->val.integer)) {
00758                 case RS_CREATEANDGO:
00759                     /*
00760                      * XXX: check legality 
00761                      */
00762                     *(data->data.integer) = RS_ACTIVE;
00763                     break;
00764 
00765                 case RS_CREATEANDWAIT:
00766                     /*
00767                      * XXX: check legality 
00768                      */
00769                     *(data->data.integer) = RS_NOTINSERVICE;
00770                     break;
00771 
00772                 case RS_DESTROY:
00773                     newrowstash->deleted = 1;
00774                     break;
00775                 }
00776             }
00777             break;
00778 
00779         case MODE_SET_ACTION:
00780 
00781             /*
00782              * Install the new row into the stored table.
00783              * Do this only *once* per row ....
00784              */
00785             if (newrowstash->state != STATE_ACTION) {
00786                 newrowstash->state = STATE_ACTION;
00787                 if (newrowstash->created) {
00788                     netsnmp_table_dataset_add_row(datatable, newrow);
00789                 } else {
00790                     netsnmp_table_dataset_replace_row(datatable,
00791                                                       row, newrow);
00792                 }
00793             }
00794             /*
00795              * ... but every (relevant) varbind in the request will
00796              * need to know about this new row, so update the
00797              * per-request row information regardless
00798              */
00799             if (newrowstash->created) {
00800                 netsnmp_request_add_list_data(request,
00801                         netsnmp_create_data_list(TABLE_DATA_NAME,
00802                                                  newrow, NULL));
00803             }
00804             break;
00805 
00806         case MODE_SET_UNDO:
00807             /*
00808              * extract the new row, replace with the old or delete 
00809              */
00810             if (newrowstash->state != STATE_UNDO) {
00811                 newrowstash->state = STATE_UNDO;
00812                 if (newrowstash->created) {
00813                     netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
00814                 } else {
00815                     netsnmp_table_dataset_replace_row(datatable,
00816                                                       newrow, row);
00817                     netsnmp_table_dataset_delete_row(newrow);
00818                 }
00819                 newrow = NULL;
00820             }
00821             break;
00822 
00823         case MODE_SET_COMMIT:
00824             if (newrowstash->state != STATE_COMMIT) {
00825                 newrowstash->state = STATE_COMMIT;
00826                 if (!newrowstash->created) {
00827                     netsnmp_request_info       *req;
00828                     netsnmp_table_dataset_delete_row(row);
00829 
00830                     /* Walk the request list to update the reference to the old row w/ th new one */
00831                     for (req = requests; req; req=req->next) {
00832         
00833                         /*
00834                          * For requests that have the old row values,
00835                          * so add the newly-created row information.
00836                          */
00837                         if ((netsnmp_table_row *) netsnmp_extract_table_row(req) == row) {
00838                                 netsnmp_request_remove_list_data(req, TABLE_DATA_ROW);
00839                                 netsnmp_request_add_list_data(req,
00840                                     netsnmp_create_data_list(TABLE_DATA_ROW, newrow, NULL));
00841                         }
00842                     }
00843 
00844                     row = NULL;
00845                 }
00846                 if (newrowstash->deleted) {
00847                     netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
00848                     newrow = NULL;
00849                 }
00850             }
00851             break;
00852 
00853         case MODE_SET_FREE:
00854             if (newrowstash && newrowstash->state != STATE_FREE) {
00855                 newrowstash->state = STATE_FREE;
00856                 netsnmp_table_dataset_delete_row(newrow);
00857                 newrow = NULL;
00858             }
00859             break;
00860 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00861 
00862         default:
00863             snmp_log(LOG_ERR,
00864                      "table_dataset: unknown mode passed into the handler\n");
00865             return SNMP_ERR_GENERR;
00866         }
00867     }
00868 
00869     /* next handler called automatically - 'AUTO_NEXT' */
00870     return SNMP_ERR_NOERROR;
00871 }
00872 
00876 NETSNMP_INLINE netsnmp_table_data_set *
00877 netsnmp_extract_table_data_set(netsnmp_request_info *request)
00878 {
00879     return (netsnmp_table_data_set *)
00880         netsnmp_request_get_list_data(request, TABLE_DATA_SET_NAME);
00881 }
00882 
00886 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_SET_COLUMN
00887 netsnmp_table_data_set_storage *
00888 netsnmp_extract_table_data_set_column(netsnmp_request_info *request,
00889                                      unsigned int column)
00890 {
00891     netsnmp_table_data_set_storage *data =
00892         (netsnmp_table_data_set_storage*)netsnmp_extract_table_row_data( request );
00893     if (data) {
00894         data = netsnmp_table_data_set_find_column(data, column);
00895     }
00896     return data;
00897 }
00898 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_SET_COLUMN */
00899 
00900 /* ==================================
00901  *
00902  * Data Set API: Config-based operation
00903  *
00904  * ================================== */
00905 
00914 void
00915 netsnmp_register_auto_data_table(netsnmp_table_data_set *table_set,
00916                                  char *registration_name)
00917 {
00918     data_set_tables *tables;
00919     tables = SNMP_MALLOC_TYPEDEF(data_set_tables);
00920     if (!tables)
00921         return;
00922     tables->table_set = table_set;
00923     if (!registration_name) {
00924         registration_name = table_set->table->name;
00925     }
00926     netsnmp_add_list_data(&auto_tables,
00927                           netsnmp_create_data_list(registration_name,
00928                                                    tables, free));     /* XXX */
00929 }
00930 
00931 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_UNREGISTER_AUTO_DATA_TABLE
00932 
00934 void
00935 netsnmp_unregister_auto_data_table(netsnmp_table_data_set *table_set,
00936                                    char *registration_name)
00937 {
00938     netsnmp_remove_list_node(&auto_tables, registration_name
00939                              ? registration_name : table_set->table->name);
00940 }
00941 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_UNREGISTER_AUTO_DATA_TABLE */
00942 
00943 #ifndef NETSNMP_DISABLE_MIB_LOADING
00944 static void
00945 _table_set_add_indexes(netsnmp_table_data_set *table_set, struct tree *tp)
00946 {
00947     oid             name[MAX_OID_LEN];
00948     size_t          name_length = MAX_OID_LEN;
00949     struct index_list *index;
00950     struct tree     *indexnode;
00951     u_char          type;
00952     int             fixed_len = 0;
00953     
00954     /*
00955      * loop through indexes and add types 
00956      */
00957     for (index = tp->indexes; index; index = index->next) {
00958         if (!snmp_parse_oid(index->ilabel, name, &name_length) ||
00959             (NULL ==
00960              (indexnode = get_tree(name, name_length, get_tree_head())))) {
00961             config_pwarn("can't instatiate table since "
00962                          "I don't know anything about one index");
00963             snmp_log(LOG_WARNING, "  index %s not found in tree\n",
00964                      index->ilabel);
00965             return;             /* xxx mem leak */
00966         }
00967             
00968         type = mib_to_asn_type(indexnode->type);
00969         if (type == (u_char) - 1) {
00970             config_pwarn("unknown index type");
00971             return;             /* xxx mem leak */
00972         }
00973         /*
00974          * if implied, mark it as such. also mark fixed length
00975          * octet strings as implied (ie no length prefix) as well.
00976          * */
00977         if ((TYPE_OCTETSTR == indexnode->type) &&  /* octet str */
00978             (NULL != indexnode->ranges) &&         /* & has range */
00979             (NULL == indexnode->ranges->next) &&   /*   but only one */
00980             (indexnode->ranges->high ==            /*   & high==low */
00981              indexnode->ranges->low)) {
00982             type |= ASN_PRIVATE;
00983             fixed_len = indexnode->ranges->high;
00984         }
00985         else if (index->isimplied)
00986             type |= ASN_PRIVATE;
00987         
00988         DEBUGMSGTL(("table_set_add_table",
00989                     "adding default index of type %d\n", type));
00990         netsnmp_table_dataset_add_index(table_set, type);
00991 
00992         /*
00993          * hack alert: for fixed lenght strings, save the
00994          * lenght for use during oid parsing.
00995          */
00996         if (fixed_len) {
00997             /*
00998              * find last (just added) index
00999              */
01000             netsnmp_variable_list *var =  table_set->table->indexes_template;
01001             while (NULL != var->next_variable)
01002                 var = var->next_variable;
01003             var->val_len = fixed_len;
01004         }
01005     }
01006 }
01008 void
01009 netsnmp_config_parse_table_set(const char *token, char *line)
01010 {
01011     oid             table_name[MAX_OID_LEN];
01012     size_t          table_name_length = MAX_OID_LEN;
01013     struct tree    *tp;
01014     netsnmp_table_data_set *table_set;
01015     data_set_tables *tables;
01016     unsigned int    mincol = 0xffffff, maxcol = 0;
01017     char           *pos;
01018 
01019     /*
01020      * instatiate a fake table based on MIB information 
01021      */
01022     DEBUGMSGTL(("9:table_set_add_table", "processing '%s'\n", line));
01023     if (NULL != (pos = strchr(line,' '))) {
01024         config_pwarn("ignoring extra tokens on line");
01025         snmp_log(LOG_WARNING,"  ignoring '%s'\n", pos);
01026         *pos = '\0';
01027     }
01028 
01029     /*
01030      * check for duplicate table
01031      */
01032     tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, line);
01033     if (NULL != tables) {
01034         config_pwarn("duplicate table definition");
01035         return;
01036     }
01037 
01038     /*
01039      * parse oid and find tree structure
01040      */
01041     if (!snmp_parse_oid(line, table_name, &table_name_length)) {
01042         config_pwarn
01043             ("can't instatiate table since I can't parse the table name");
01044         return;
01045     }
01046     if(NULL == (tp = get_tree(table_name, table_name_length,
01047                               get_tree_head()))) {
01048         config_pwarn("can't instatiate table since "
01049                      "I can't find mib information about it");
01050         return;
01051     }
01052 
01053     if (NULL == (tp = tp->child_list) || NULL == tp->child_list) {
01054         config_pwarn("can't instatiate table since it doesn't appear to be "
01055                      "a proper table (no children)");
01056         return;
01057     }
01058 
01059     table_set = netsnmp_create_table_data_set(line);
01060 
01061     /*
01062      * check for augments indexes
01063      */
01064     if (NULL != tp->augments) {
01065         oid             name[MAX_OID_LEN];
01066         size_t          name_length = MAX_OID_LEN;
01067         struct tree    *tp2;
01068     
01069         if (!snmp_parse_oid(tp->augments, name, &name_length)) {
01070             config_pwarn("I can't parse the augment table name");
01071             snmp_log(LOG_WARNING, "  can't parse %s\n", tp->augments);
01072             SNMP_FREE (table_set);
01073             return;
01074         }
01075         if(NULL == (tp2 = get_tree(name, name_length, get_tree_head()))) {
01076             config_pwarn("can't instatiate table since "
01077                          "I can't find mib information about augment table");
01078             snmp_log(LOG_WARNING, "  table %s not found in tree\n",
01079                      tp->augments);
01080             SNMP_FREE (table_set);
01081             return;
01082         }
01083         _table_set_add_indexes(table_set, tp2);
01084     }
01085 
01086     _table_set_add_indexes(table_set, tp);
01087     
01088     /*
01089      * loop through children and add each column info 
01090      */
01091     for (tp = tp->child_list; tp; tp = tp->next_peer) {
01092         int             canwrite = 0;
01093         u_char          type;
01094         type = mib_to_asn_type(tp->type);
01095         if (type == (u_char) - 1) {
01096             config_pwarn("unknown column type");
01097             SNMP_FREE (table_set);
01098             return;             /* xxx mem leak */
01099         }
01100 
01101         DEBUGMSGTL(("table_set_add_table",
01102                     "adding column %s(%ld) of type %d (access %d)\n",
01103                     tp->label, tp->subid, type, tp->access));
01104 
01105         switch (tp->access) {
01106         case MIB_ACCESS_CREATE:
01107             table_set->allow_creation = 1;
01108         case MIB_ACCESS_READWRITE:
01109         case MIB_ACCESS_WRITEONLY:
01110             canwrite = 1;
01111         case MIB_ACCESS_READONLY:
01112             DEBUGMSGTL(("table_set_add_table",
01113                         "adding column %ld of type %d\n", tp->subid, type));
01114             netsnmp_table_set_add_default_row(table_set, tp->subid, type,
01115                                               canwrite, NULL, 0);
01116             mincol = SNMP_MIN(mincol, tp->subid);
01117             maxcol = SNMP_MAX(maxcol, tp->subid);
01118             break;
01119 
01120         case MIB_ACCESS_NOACCESS:
01121         case MIB_ACCESS_NOTIFY:
01122             break;
01123 
01124         default:
01125             config_pwarn("unknown column access type");
01126             break;
01127         }
01128     }
01129 
01130     /*
01131      * register the table 
01132      */
01133     netsnmp_register_table_data_set(netsnmp_create_handler_registration
01134                                     (line, NULL, table_name,
01135                                      table_name_length,
01136                                      HANDLER_CAN_RWRITE), table_set, NULL);
01137 
01138     netsnmp_register_auto_data_table(table_set, NULL);
01139 }
01140 #endif /* NETSNMP_DISABLE_MIB_LOADING */
01141 
01143 void
01144 netsnmp_config_parse_add_row(const char *token, char *line)
01145 {
01146     char            buf[SNMP_MAXBUF_MEDIUM];
01147     char            tname[SNMP_MAXBUF_MEDIUM];
01148     size_t          buf_size;
01149     int             rc;
01150 
01151     data_set_tables *tables;
01152     netsnmp_variable_list *vb;  /* containing only types */
01153     netsnmp_table_row *row;
01154     netsnmp_table_data_set_storage *dr;
01155 
01156     line = copy_nword(line, tname, sizeof(tname));
01157 
01158     tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, tname);
01159     if (!tables) {
01160         config_pwarn("Unknown table trying to add a row");
01161         return;
01162     }
01163 
01164     /*
01165      * do the indexes first 
01166      */
01167     row = netsnmp_create_table_data_row();
01168 
01169     for (vb = tables->table_set->table->indexes_template; vb;
01170          vb = vb->next_variable) {
01171         if (!line) {
01172             config_pwarn("missing an index value");
01173             SNMP_FREE (row);
01174             return;
01175         }
01176 
01177         DEBUGMSGTL(("table_set_add_row", "adding index of type %d\n",
01178                     vb->type));
01179         buf_size = sizeof(buf);
01180         line = read_config_read_memory(vb->type, line, buf, &buf_size);
01181         netsnmp_table_row_add_index(row, vb->type, buf, buf_size);
01182     }
01183 
01184     /*
01185      * then do the data 
01186      */
01187     for (dr = tables->table_set->default_row; dr; dr = dr->next) {
01188         if (!line) {
01189             config_pwarn("missing a data value. "
01190                          "All columns must be specified.");
01191             snmp_log(LOG_WARNING,"  can't find value for column %d\n",
01192                      dr->column - 1);
01193             SNMP_FREE (row);
01194             return;
01195         }
01196 
01197         buf_size = sizeof(buf);
01198         line = read_config_read_memory(dr->type, line, buf, &buf_size);
01199         DEBUGMSGTL(("table_set_add_row",
01200                     "adding data at column %d of type %d\n", dr->column,
01201                     dr->type));
01202         netsnmp_set_row_column(row, dr->column, dr->type, buf, buf_size);
01203 #ifndef NETSNMP_NO_WRITE_SUPPORT
01204         if (dr->writable)
01205             netsnmp_mark_row_column_writable(row, dr->column, 1);       /* make writable */
01206 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
01207     }
01208     rc = netsnmp_table_data_add_row(tables->table_set->table, row);
01209     if (SNMPERR_SUCCESS != rc) {
01210         config_pwarn("error adding table row");
01211     }
01212     if (NULL != line) {
01213         config_pwarn("extra data value. Too many columns specified.");
01214         snmp_log(LOG_WARNING,"  extra data '%s'\n", line);
01215     }
01216 }
01217 
01218 
01219 #ifndef NETSNMP_NO_WRITE_SUPPORT
01220 netsnmp_oid_stash_node **
01221 netsnmp_table_dataset_get_or_create_stash(netsnmp_agent_request_info *reqinfo,
01222                                           netsnmp_table_data_set *datatable,
01223                                           netsnmp_table_request_info *table_info)
01224 {
01225     netsnmp_oid_stash_node **stashp = NULL;
01226     char                     buf[256]; /* is this reasonable size?? */
01227     size_t                   len;
01228     int                      rc;
01229 
01230     rc = snprintf(buf, sizeof(buf), "dataset_row_stash:%s:",
01231                   datatable->table->name);
01232     if ((-1 == rc) || ((size_t)rc >= sizeof(buf))) {
01233         snmp_log(LOG_ERR,"%s handler name too long\n", datatable->table->name);
01234         return NULL;
01235     }
01236 
01237     len = sizeof(buf) - rc;
01238     rc = snprint_objid(&buf[rc], len, table_info->index_oid,
01239                        table_info->index_oid_len);
01240     if (-1 == rc) {
01241         snmp_log(LOG_ERR,"%s oid or name too long\n", datatable->table->name);
01242         return NULL;
01243     }
01244 
01245     stashp = (netsnmp_oid_stash_node **)
01246         netsnmp_table_get_or_create_row_stash(reqinfo, (u_char *) buf);
01247     return stashp;
01248 }
01249 
01250 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_GET_NEWROW
01251 netsnmp_table_row *
01252 netsnmp_table_dataset_get_newrow(netsnmp_request_info *request,
01253                                  netsnmp_agent_request_info *reqinfo,
01254                                  int rootoid_len,
01255                                  netsnmp_table_data_set *datatable,
01256                                  netsnmp_table_request_info *table_info)
01257 {
01258     oid * const suffix = request->requestvb->name + rootoid_len + 2;
01259     size_t suffix_len = request->requestvb->name_length - (rootoid_len + 2);
01260     netsnmp_oid_stash_node **stashp;
01261     newrow_stash   *newrowstash;
01262 
01263     stashp = netsnmp_table_dataset_get_or_create_stash(reqinfo, datatable,
01264                                                        table_info);
01265     if (NULL == stashp)
01266         return NULL;
01267 
01268     newrowstash = (newrow_stash*)netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len);
01269     if (NULL == newrowstash)
01270         return NULL;
01271 
01272     return newrowstash->newrow;
01273 }
01274 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_GET_NEWROW */
01275 #endif /* NETSNMP_NO_WRITE_SUPPORT */
01276 
01277 /* ==================================
01278  *
01279  * Data Set API: Row operations
01280  *
01281  * ================================== */
01282 
01284 netsnmp_table_row *
01285 netsnmp_table_data_set_get_first_row(netsnmp_table_data_set *table)
01286 {
01287     return netsnmp_table_data_get_first_row(table->table);
01288 }
01289 
01291 netsnmp_table_row *
01292 netsnmp_table_data_set_get_next_row(netsnmp_table_data_set *table,
01293                                     netsnmp_table_row      *row)
01294 {
01295     return netsnmp_table_data_get_next_row(table->table, row);
01296 }
01297 
01298 int
01299 netsnmp_table_set_num_rows(netsnmp_table_data_set *table)
01300 {
01301     if (!table)
01302         return 0;
01303     return netsnmp_table_data_num_rows(table->table);
01304 }
01305 
01306 /* ==================================
01307  *
01308  * Data Set API: Column operations
01309  *
01310  * ================================== */
01311 
01315 netsnmp_table_data_set_storage *
01316 netsnmp_table_data_set_find_column(netsnmp_table_data_set_storage *start,
01317                                    unsigned int column)
01318 {
01319     while (start && start->column != column)
01320         start = start->next;
01321     return start;
01322 }
01323 
01324 #ifndef NETSNMP_NO_WRITE_SUPPORT
01325 
01328 int
01329 netsnmp_mark_row_column_writable(netsnmp_table_row *row, int column,
01330                                  int writable)
01331 {
01332     netsnmp_table_data_set_storage *data;
01333 
01334     if (!row)
01335         return SNMPERR_GENERR;
01336 
01337     data = (netsnmp_table_data_set_storage *) row->data;
01338     data = netsnmp_table_data_set_find_column(data, column);
01339 
01340     if (!data) {
01341         /*
01342          * create it 
01343          */
01344         data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
01345         if (!data) {
01346             snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
01347             return SNMPERR_MALLOC;
01348         }
01349         data->column = column;
01350         data->writable = writable;
01351         data->next = (struct netsnmp_table_data_set_storage_s*)row->data;
01352         row->data = data;
01353     } else {
01354         data->writable = writable;
01355     }
01356     return SNMPERR_SUCCESS;
01357 }
01358 #endif /* NETSNMP_NO_WRITE_SUPPORT */
01359 
01380 int
01381 netsnmp_set_row_column(netsnmp_table_row *row, unsigned int column,
01382                        int type, const void *value, size_t value_len)
01383 {
01384     netsnmp_table_data_set_storage *data;
01385 
01386     if (!row)
01387         return SNMPERR_GENERR;
01388 
01389     data = (netsnmp_table_data_set_storage *) row->data;
01390     data = netsnmp_table_data_set_find_column(data, column);
01391 
01392     if (!data) {
01393         /*
01394          * create it 
01395          */
01396         data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
01397         if (!data) {
01398             snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
01399             return SNMPERR_MALLOC;
01400         }
01401 
01402         data->column = column;
01403         data->type = type;
01404         data->next = (struct netsnmp_table_data_set_storage_s*)row->data;
01405         row->data = data;
01406     }
01407 
01408     /* Transitions from / to SNMP_NOSUCHINSTANCE are allowed, but no other transitions. */
01409     if (data->type != type && data->type != SNMP_NOSUCHINSTANCE
01410         && type != SNMP_NOSUCHINSTANCE)
01411         return SNMPERR_GENERR;
01412 
01413     /* Return now if neither the type nor the data itself has been modified. */
01414     if (data->type == type && data->data_len == value_len
01415         && (value == NULL || memcmp(&data->data.string, value, value_len) == 0))
01416             return SNMPERR_SUCCESS;
01417 
01418     /* Reallocate memory and store the new value. */
01419     data->data.voidp = realloc(data->data.voidp, value ? value_len : 0);
01420     if (value && value_len && !data->data.voidp) {
01421         data->data_len = 0;
01422         data->type = SNMP_NOSUCHINSTANCE;
01423         snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
01424         return SNMPERR_MALLOC;
01425     }
01426     if (value && value_len)
01427         memcpy(data->data.string, value, value_len);
01428     data->type = type;
01429     data->data_len = value_len;
01430     return SNMPERR_SUCCESS;
01431 }
01432 
01433 
01434 /* ==================================
01435  *
01436  * Data Set API: Index operations
01437  *
01438  * ================================== */
01439 
01441 void
01442 netsnmp_table_dataset_add_index(netsnmp_table_data_set *table, u_char type)
01443 {
01444     if (!table)
01445         return;
01446     netsnmp_table_data_add_index(table->table, type);
01447 }
01448 
01451 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_SET_ADD_INDEXES
01452 void
01453 netsnmp_table_set_add_indexes(netsnmp_table_data_set *tset,
01454                               ...)
01455 {
01456     va_list         debugargs;
01457     int             type;
01458 
01459     va_start(debugargs, tset);
01460 
01461     if (tset)
01462         while ((type = va_arg(debugargs, int)) != 0)
01463             netsnmp_table_data_add_index(tset->table, (u_char)type);
01464 
01465     va_end(debugargs);
01466 }
01467 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_SET_ADD_INDEXES */
01468 
01469 #else /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET */
01470 netsnmp_feature_unused(table_dataset);
01471 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET */
01472