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