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