net-snmp 5.7
|
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