/* * The helper handler that takes care of passing a specific row of * data down to the lower handler(s). The table_container helper * has already taken care of identifying the appropriate row of the * table (and converting GETNEXT requests into an equivalent GET request) * So all we need to do here is make sure that the row is accessible * using tdata-style retrieval techniques as well. */ int _netsnmp_tdata_helper_handler (netsnmp_mib_handler * handler, netsnmp_handler_registration * reginfo, netsnmp_agent_request_info * reqinfo, netsnmp_request_info * requests) { netsnmp_tdata *table = (netsnmp_tdata *) handler->myvoid; netsnmp_request_info *request; netsnmp_table_request_info *table_info; netsnmp_tdata_row *row; int need_processing = 1; switch (reqinfo->mode) { case MODE_GET: need_processing = 0; /* only need processing if some vars found */ /** Fall through */ #ifndef NETSNMP_NO_WRITE_SUPPORT case MODE_SET_RESERVE1: #endif /* NETSNMP_NO_WRITE_SUPPORT */ for (request = requests; request; request = request->next) { if (request->processed) continue; table_info = netsnmp_extract_table_info (request); if (!table_info) { netsnmp_assert (table_info); /* yes, this will always hit */ netsnmp_set_request_error (reqinfo, request, SNMP_ERR_GENERR); continue; /* eek */ } row = (netsnmp_tdata_row *) netsnmp_container_table_row_extract (request); if (!row && (reqinfo->mode == MODE_GET)) { netsnmp_assert (row); /* yes, this will always hit */ netsnmp_set_request_error (reqinfo, request, SNMP_ERR_GENERR); continue; /* eek */ } ++need_processing; netsnmp_request_add_list_data (request, netsnmp_create_data_list (TABLE_TDATA_TABLE, table, NULL)); netsnmp_request_add_list_data (request, netsnmp_create_data_list (TABLE_TDATA_ROW, row, NULL)); } /** skip next handler if processing not needed */ if (!need_processing) handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; } /* next handler called automatically - 'AUTO_NEXT' */ return SNMP_ERR_NOERROR; }
/** * Initialize the table dot11UsrLinkTable * (Define its contents and how it's structured) */ void initialize_table_dot11UsrLinkTable(void) { dot11UsrLinkTable_registration_ptr user_context; u_long flags; DEBUGMSGTL(("verbose:dot11UsrLinkTable:initialize_table_dot11UsrLinkTable","called\n")); /* * TODO:301:o: Perform dot11UsrLinkTable one-time table initialization. */ /* * TODO:302:o: |->Initialize dot11UsrLinkTable user context * if you'd like to pass in a pointer to some data for this * table, allocate or set it up here. */ /* * a netsnmp_data_list is a simple way to store void pointers. A simple * string token is used to add, find or remove pointers. */ user_context = netsnmp_create_data_list("dot11UsrLinkTable", NULL, NULL); /* * No support for any flags yet, but in the future you would * set any flags here. */ flags = 0; mad_dev_oid(dot11UsrLinkTable_oid,USRLINKTABLE,&dot11UsrLinkTable_oid_size,enterprise_pvivate_oid); /* * call interface initialization code */ _dot11UsrLinkTable_initialize_interface(user_context, flags); } /* initialize_table_dot11UsrLinkTable */
/** adds data to a datalist * @param head a pointer to the head node of a data_list * @param name the name of the node to cache the data. * @param data the data to be stored under that name * @param beer A function that can free the data pointer (in the future) * @return a newly created data_list node which was inserted in the list */ NETSNMP_INLINE netsnmp_data_list * netsnmp_data_list_add_data(netsnmp_data_list **head, const char *name, void *data, Netsnmp_Free_List_Data * beer) { netsnmp_data_list *ptr; netsnmp_data_list *node = netsnmp_create_data_list(name, data, beer); if(NULL == node) { snmp_log(LOG_ERR,"could not allocte memory for node."); return NULL; } if (!*head) { *head = node; return node; } /* * xxx-rks: check for duplicate names? */ for (ptr = *head; ptr->next != NULL; ptr = ptr->next) { /* * noop */ } if (ptr) /* should always be true */ ptr->next = node; return node; }
/** updates a given cache depending on whether it needs to or not. */ int _netsnmp_stash_cache_load( netsnmp_cache *cache, void *magic ) { netsnmp_mib_handler *handler = cache->cache_hint->handler; netsnmp_handler_registration *reginfo = cache->cache_hint->reginfo; netsnmp_agent_request_info *reqinfo = cache->cache_hint->reqinfo; netsnmp_request_info *requests = cache->cache_hint->requests; netsnmp_stash_cache_info *cinfo = (netsnmp_stash_cache_info*) magic; int old_mode; int ret; if (!cinfo) { cinfo = netsnmp_get_new_stash_cache(); cache->magic = cinfo; } /* change modes to the GET_STASH mode */ old_mode = reqinfo->mode; reqinfo->mode = MODE_GET_STASH; netsnmp_agent_add_list_data(reqinfo, netsnmp_create_data_list(STASH_CACHE_NAME, &cinfo->cache, NULL)); /* have the next handler fill stuff in and switch modes back */ ret = netsnmp_call_next_handler(handler->next, reginfo, reqinfo, requests); reqinfo->mode = old_mode; return ret; }
/** * Initialize the table ipv4InterfaceTable * (Define its contents and how it's structured) */ void initialize_table_ipv4InterfaceTable(void) { u_long flags; DEBUGMSGTL(("verbose:ipv4InterfaceTable:initialize_table_ipv4InterfaceTable", "called\n")); /* * TODO:301:o: Perform ipv4InterfaceTable one-time table initialization. */ /* * TODO:302:o: |->Initialize ipv4InterfaceTable user context * if you'd like to pass in a pointer to some data for this * table, allocate or set it up here. */ /* * a netsnmp_data_list is a simple way to store void pointers. A simple * string token is used to add, find or remove pointers. */ ipv4InterfaceTable_user_context_p = netsnmp_create_data_list("ipv4InterfaceTable", NULL, NULL); /* * No support for any flags yet, but in the future you would * set any flags here. */ flags = 0; /* * call interface initialization code */ _ipv4InterfaceTable_initialize_interface (ipv4InterfaceTable_user_context_p, flags); } /* initialize_table_ipv4InterfaceTable */
/** registers a table_dataset so that the "add_row" snmpd.conf token * can be used to add data to this table. If registration_name is * NULL then the name used when the table was created will be used * instead. * * @todo create a properly free'ing registeration pointer for the * datalist, and get the datalist freed at shutdown. */ void netsnmp_register_auto_data_table(netsnmp_table_data_set *table_set, char *registration_name) { data_set_tables *tables; tables = SNMP_MALLOC_TYPEDEF(data_set_tables); if (!tables) return; tables->table_set = table_set; if (!registration_name) { registration_name = table_set->table->name; } netsnmp_add_list_data(&auto_tables, netsnmp_create_data_list(registration_name, tables, NULL)); /* XXX */ }
/* caches information (in the request) we'll need at a later point in time */ static ti_cache_info * netsnmp_iterator_remember(netsnmp_request_info *request, oid *oid_to_save, size_t oid_to_save_len, void *callback_data_context, void *callback_loop_context, netsnmp_iterator_info *iinfo) { ti_cache_info *ti_info; if (!request || !oid_to_save || oid_to_save_len > MAX_OID_LEN) return NULL; /* extract existing cached state */ ti_info = netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); /* no existing cached state. make a new one. */ if (!ti_info) { ti_info = SNMP_MALLOC_TYPEDEF(ti_cache_info); if (ti_info == NULL) return NULL; netsnmp_request_add_list_data(request, netsnmp_create_data_list (TI_REQUEST_CACHE, ti_info, netsnmp_free_ti_cache)); } /* free existing cache before replacing */ if (ti_info->data_context && ti_info->free_context) (ti_info->free_context)(ti_info->data_context, iinfo); /* maybe generate it from the loop context? */ if (iinfo->make_data_context && !callback_data_context) { callback_data_context = (iinfo->make_data_context)(callback_loop_context, iinfo); } /* save data as requested */ ti_info->data_context = callback_data_context; ti_info->free_context = iinfo->free_data_context; ti_info->best_match_len = oid_to_save_len; ti_info->iinfo = iinfo; if (oid_to_save_len) memcpy(ti_info->best_match, oid_to_save, oid_to_save_len * sizeof(oid)); return ti_info; }
/* * The helper handler that takes care of passing a specific row of * data down to the lower handler(s). The table_container helper * has already taken care of identifying the appropriate row of the * table (and converting GETNEXT requests into an equivalent GET request) * So all we need to do here is make sure that the row is accessible * using tdata-style retrieval techniques as well. */ int _netsnmp_tdata_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_tdata *table = (netsnmp_tdata *) handler->myvoid; netsnmp_request_info *request; netsnmp_table_request_info *table_info; netsnmp_tdata_row *row; switch ( reqinfo->mode ) { case MODE_GET: case MODE_SET_RESERVE1: for (request = requests; request; request = request->next) { if (request->processed) continue; table_info = netsnmp_extract_table_info(request); if (!table_info) continue; /* ack */ row = netsnmp_container_table_row_extract( request ); netsnmp_request_add_list_data(request, netsnmp_create_data_list( TABLE_TDATA_TABLE, table, NULL)); netsnmp_request_add_list_data(request, netsnmp_create_data_list( TABLE_TDATA_ROW, row, NULL)); } } /* next handler called automatically - 'AUTO_NEXT' */ return SNMP_ERR_NOERROR; }
/** Insert the cache information for a given request (PDU) */ void netsnmp_cache_reqinfo_insert(netsnmp_cache* cache, netsnmp_agent_request_info * reqinfo, const char *name) { char *cache_name = _build_cache_name(name); if (NULL == netsnmp_agent_get_list_data(reqinfo, cache_name)) { DEBUGMSGTL(("verbose:helper:cache_handler", " adding '%s' to %p\n", cache_name, reqinfo)); netsnmp_agent_add_list_data(reqinfo, netsnmp_create_data_list(cache_name, cache, NULL)); } SNMP_FREE(cache_name); }
/** registers to store a data_list set of data at persistent storage time * * @param datalist the data to be saved * @param type the name of the application to save the data as. If left NULL the default application name that was registered during the init_snmp call will be used (recommended). * @param token the unique token identifier string to use as the first word in the persistent file line. * @param data_list_save_ptr a function pointer which will be called to save the rest of the data to a buffer. * @param data_list_read_ptr a function pointer which can read the remainder of a saved line and return the application specific void * pointer. * @param data_list_free_ptr a function pointer which will be passed to the data node for freeing it in the future when/if the list/node is cleaned up or destroyed. */ void netsnmp_register_save_list(netsnmp_data_list **datalist, const char *type, const char *token, Netsnmp_Save_List_Data *data_list_save_ptr, Netsnmp_Read_List_Data *data_list_read_ptr, Netsnmp_Free_List_Data *data_list_free_ptr) { netsnmp_data_list_saveinfo *info; if (!data_list_save_ptr && !data_list_read_ptr) return; info = SNMP_MALLOC_TYPEDEF(netsnmp_data_list_saveinfo); if (!info) { snmp_log(LOG_ERR, "couldn't malloc a netsnmp_data_list_saveinfo typedef"); return; } info->datalist = datalist; info->token = token; info->type = type; if (!info->type) { info->type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE); } /* function which will save the data */ info->data_list_save_ptr = data_list_save_ptr; if (data_list_save_ptr) snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA, netsnmp_save_all_data_callback, info); /* function which will read the data back in */ info->data_list_read_ptr = data_list_read_ptr; if (data_list_read_ptr) { /** @todo netsnmp_register_save_list should handle the same token name being saved from different types? */ netsnmp_add_list_data(&saveHead, netsnmp_create_data_list(token, info, NULL)); register_config_handler(type, token, netsnmp_read_data_callback, NULL /* XXX */, NULL); } info->data_list_free_ptr = data_list_free_ptr; }
int process_set_requests(netsnmp_agent_request_info *agtreq_info, netsnmp_request_info *requests, table_container_data * tad, char *handler_name) { set_context context; netsnmp_container *request_group; /* * create and save structure for set info */ request_group = (netsnmp_container*) netsnmp_agent_get_list_data (agtreq_info, handler_name); if (request_group == NULL) { netsnmp_data_list *tmp; request_group = netsnmp_container_find("request_group:" "table_container"); request_group->compare = netsnmp_compare_netsnmp_index; request_group->ncompare = netsnmp_ncompare_netsnmp_index; DEBUGMSGTL(("table_array", "Grouping requests by oid\n")); tmp = netsnmp_create_data_list(handler_name, request_group, release_netsnmp_request_groups); netsnmp_agent_add_list_data(agtreq_info, tmp); /* * group requests. */ group_requests(agtreq_info, requests, request_group, tad); } /* * process each group one at a time */ context.agtreq_info = agtreq_info; context.tad = tad; context.status = SNMP_ERR_NOERROR; CONTAINER_FOR_EACH(request_group, (netsnmp_container_obj_func*)process_set_group, &context); return context.status; }
/** adds data to a datalist * @param head a pointer to the head node of a data_list * @param name the name of the node to cache the data. * @param data the data to be stored under that name * @param beer A function that can free the data pointer (in the future) * @return a newly created data_list node which was inserted in the list */ NETSNMP_INLINE netsnmp_data_list * netsnmp_data_list_add_data(netsnmp_data_list **head, const char *name, void *data, Netsnmp_Free_List_Data * beer) { netsnmp_data_list *node; if (!name) { snmp_log(LOG_ERR,"no name provided."); return NULL; } node = netsnmp_create_data_list(name, data, beer); if(NULL == node) { snmp_log(LOG_ERR,"could not allocate memory for node."); return NULL; } netsnmp_add_list_data(head, node); return node; }
/** intended to be registerd as a .conf parser * It should be registered using: * * register_app_config_handler("token", netsnmp_read_data_callback, XXX) * * where INFO_POINTER is a pointer to a netsnmp_data_list_saveinfo object * containing apporpriate registration information * @todo make netsnmp_read_data_callback deal with a free routine */ void netsnmp_read_data_callback(const char *token, char *line) { netsnmp_data_list_saveinfo *info; char *dataname = NULL; size_t dataname_len; void *data = NULL; /* find the stashed information about what we're parsing */ info = (netsnmp_data_list_saveinfo *) netsnmp_get_list_data(saveHead, token); if (!info) { snmp_log(LOG_WARNING, "netsnmp_read_data_callback called without previously registered subparser"); return; } /* read in the token */ line = read_config_read_data(ASN_OCTET_STR, line, &dataname, &dataname_len); if (!line || !dataname) return; /* call the sub-parser to read the rest */ data = (info->data_list_read_ptr)(line, strlen(line)); if (!data) { free(dataname); return; } /* add to the datalist */ netsnmp_add_list_data(info->datalist, netsnmp_create_data_list(dataname, data, info->data_list_free_ptr)); return; }
static int _table_row_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int rc = SNMP_ERR_NOERROR; netsnmp_request_info *req; void *row; /** sanity checks */ netsnmp_assert((NULL != handler) && (NULL != handler->myvoid)); netsnmp_assert((NULL != reginfo) && (NULL != reqinfo)); DEBUGMSGTL(("table_row", "Mode %s, Got request:\n", se_find_label_in_slist("agent_mode",reqinfo->mode))); /* * First off, get our pointer from the handler. * This contains the row that was actually registered. * Make this available for each of the requests passed in. */ row = handler->myvoid; for (req = requests; req; req=req->next) netsnmp_request_add_list_data(req, netsnmp_create_data_list(TABLE_ROW_DATA, row, NULL)); /* * Then call the next handler, to actually process the request */ rc = netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); if (rc != SNMP_ERR_NOERROR) { DEBUGMSGTL(("table_row", "next handler returned %d\n", rc)); } return rc; }
/** implements the table_iterator helper */ int netsnmp_table_iterator_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_table_registration_info *tbl_info; oid coloid[MAX_OID_LEN]; size_t coloid_len; int ret; static oid myname[MAX_OID_LEN]; size_t myname_len; int oldmode; netsnmp_iterator_info *iinfo; iinfo = (netsnmp_iterator_info *) handler->myvoid; if (!iinfo || !reginfo || !reqinfo) return SNMPERR_GENERR; tbl_info = iinfo->table_reginfo; /* * copy in the table registration oid for later use */ coloid_len = reginfo->rootoid_len + 2; memcpy(coloid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid)); coloid[reginfo->rootoid_len] = 1; /* table.entry node */ /* * illegally got here if these functions aren't defined */ if (iinfo->get_first_data_point == NULL || iinfo->get_next_data_point == NULL) { snmp_log(LOG_ERR, "table_iterator helper called without data accessor functions\n"); return SNMP_ERR_GENERR; } /* * XXXWWW: deal with SET caching */ #ifdef NOT_SERIALIZED while (requests) /* XXX: currently only serialized */ #endif { /* * XXXWWW: optimize by reversing loops (look through data only once) */ netsnmp_variable_list *results = NULL; netsnmp_variable_list *index_search = NULL; /* WWW: move up? */ netsnmp_variable_list *free_this_index_search = NULL; netsnmp_table_request_info *table_info = netsnmp_extract_table_info(requests); void *callback_loop_context = NULL; void *callback_data_context = NULL; void *callback_data_keep = NULL; if (requests->processed != 0) { #ifdef NOT_SERIALIZED continue; #else return SNMP_ERR_NOERROR; #endif } if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT && reqinfo->mode != MODE_GETBULK && /* XXX */ reqinfo->mode != MODE_SET_RESERVE1) { goto skip_processing; } if (table_info->colnum > tbl_info->max_column) { requests->processed = 1; #ifdef NOT_SERIALIZED break; #else return SNMP_ERR_NOERROR; #endif } /* * XXX: if loop through everything, these are never free'd * since iterator returns NULL and thus we forget about * these */ index_search = snmp_clone_varbind(table_info->indexes); if (!index_search) { /* * hmmm.... invalid table? */ snmp_log(LOG_WARNING, "invalid index list or failed malloc for table %s\n", reginfo->handlerName); return SNMP_ERR_NOERROR; } free_this_index_search = index_search; /* * below our minimum column? */ if (table_info->colnum < tbl_info->min_column) { results = (iinfo->get_first_data_point) (&callback_loop_context, &callback_data_context, index_search, iinfo); if (iinfo->free_loop_context) { (iinfo->free_loop_context) (callback_loop_context, iinfo); callback_loop_context = NULL; } goto got_results; } /* * XXX: do "only got some indexes" */ /* * find the next legal result to return */ /* * find the first node */ index_search = (iinfo->get_first_data_point) (&callback_loop_context, &callback_data_context, index_search, iinfo); /* * table.entry.column node */ coloid[reginfo->rootoid_len + 1] = table_info->colnum; switch (reqinfo->mode) { case MODE_GETNEXT: case MODE_GETBULK: /* XXXWWW */ /* * loop through all data and find next one */ while (index_search) { /* * compare the node with previous results */ if (netsnmp_check_getnext_reply (requests, coloid, coloid_len, index_search, &results)) { /* * result is our current choice, so keep a pointer to * the data that the lower handler wants us to * remember (possibly freeing the last known "good" * result data pointer) */ if (callback_data_keep && iinfo->free_data_context) { (iinfo->free_data_context) (callback_data_keep, iinfo); callback_data_keep = NULL; } if (iinfo->make_data_context && !callback_data_context) { callback_data_context = (iinfo-> make_data_context) (callback_loop_context, iinfo); } callback_data_keep = callback_data_context; callback_data_context = NULL; } else { if (callback_data_context && iinfo->free_data_context) (iinfo->free_data_context) (callback_data_context, iinfo); callback_data_context = NULL; } /* * get the next node in the data chain */ index_search = (iinfo->get_next_data_point) (&callback_loop_context, &callback_data_context, index_search, iinfo); if (!index_search && !results && tbl_info->max_column > table_info->colnum) { /* * restart loop. XXX: Should cache this better */ table_info->colnum++; coloid[reginfo->rootoid_len + 1] = table_info->colnum; if (free_this_index_search != NULL) snmp_free_varbind(free_this_index_search); index_search = snmp_clone_varbind(table_info->indexes); free_this_index_search = index_search; if (callback_loop_context && iinfo->free_loop_context_at_end) { (iinfo->free_loop_context_at_end)(callback_loop_context, iinfo); callback_loop_context = NULL; } if (iinfo->free_loop_context && callback_loop_context) { (iinfo->free_loop_context) (callback_loop_context, iinfo); callback_loop_context = NULL; } if (callback_data_context && iinfo->free_data_context) { (iinfo->free_data_context) (callback_data_context, iinfo); callback_data_context = NULL; } index_search = (iinfo-> get_first_data_point) (&callback_loop_context, &callback_data_context, index_search, iinfo); } } break; case MODE_GET: case MODE_SET_RESERVE1: /* * loop through all data till exact results are found */ while (index_search) { build_oid_noalloc(myname, MAX_OID_LEN, &myname_len, coloid, coloid_len, index_search); if (snmp_oid_compare(myname, myname_len, requests->requestvb->name, requests->requestvb->name_length) == 0) { /* * found the exact match, so we're done */ if (iinfo->make_data_context && !callback_data_context) { callback_data_context = (iinfo-> make_data_context) (callback_loop_context, iinfo); } callback_data_keep = callback_data_context; callback_data_context = NULL; results = snmp_clone_varbind(index_search); snmp_set_var_objid(results, myname, myname_len); goto got_results; } else { /* * free not-needed data context */ if (callback_data_context && iinfo->free_data_context) { (iinfo->free_data_context) (callback_data_context, iinfo); callback_data_context = NULL; } } /* * get the next node in the data chain */ index_search = (iinfo->get_next_data_point) (&callback_loop_context, &callback_data_context, index_search, iinfo); } break; default: /* * the rest of the set states have been dealt with already */ goto got_results; } /* * XXX: free index_search? */ if (callback_loop_context && iinfo->free_loop_context) { (iinfo->free_loop_context) (callback_loop_context, iinfo); callback_loop_context = NULL; } got_results: /* not milk */ /* * This free_data_context call is required in the event that your * get_next_data_point method allocates new memory, even during the * calls where it eventually returns a NULL */ if (callback_data_context && iinfo->free_data_context) { (iinfo->free_data_context) (callback_data_context, iinfo); callback_data_context = NULL; } if (!results && !MODE_IS_SET(reqinfo->mode)) { /* * no results found. */ /* * XXX: check for at least one entry at the very top */ #ifdef NOT_SERIALIZED break; #else if (callback_loop_context && iinfo->free_loop_context_at_end) { (iinfo->free_loop_context_at_end) (callback_loop_context, iinfo); callback_loop_context = NULL; } if (free_this_index_search != NULL) { snmp_free_varbind(free_this_index_search); } return SNMP_ERR_NOERROR; #endif } skip_processing: /* * OK, here results should be a pointer to the data that we * actually need to GET */ oldmode = reqinfo->mode; if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) { /* XXX */ snmp_set_var_objid(requests->requestvb, results->name, results->name_length); reqinfo->mode = MODE_GET; } if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK || /* XXX */ reqinfo->mode == MODE_SET_RESERVE1) { /* * first (or only) pass stuff */ /* * let set requsets use previously constructed data */ snmp_free_varbind(results); if (callback_data_keep) netsnmp_request_add_list_data(requests, netsnmp_create_data_list (TABLE_ITERATOR_NAME, callback_data_keep, NULL)); netsnmp_request_add_list_data(requests, netsnmp_create_data_list (TABLE_ITERATOR_LAST_CONTEXT, callback_loop_context, NULL)); } DEBUGMSGTL(("table_iterator", "doing mode: %s\n", se_find_label_in_slist("agent_mode", oldmode))); ret = netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); if (oldmode == MODE_GETNEXT || oldmode == MODE_GETBULK) { /* XXX */ if (requests->requestvb->type == ASN_NULL || requests->requestvb->type == SNMP_NOSUCHINSTANCE) { /* * get next skipped this value for this column, we * need to keep searching forward */ requests->requestvb->type = ASN_PRIV_RETRY; } reqinfo->mode = oldmode; } callback_data_keep = netsnmp_request_get_list_data(requests, TABLE_ITERATOR_NAME); callback_loop_context = netsnmp_request_get_list_data(requests, TABLE_ITERATOR_LAST_CONTEXT); /* * This has to be done to prevent a memory leak. Notice that on * SET_RESERVE1 we're assigning something to * 'free_this_index_search' at the beginning of this handler (right * above the line that says 'below our minimum column?'), * but we're not given a chance to free it below with the other * SET modes, hence our doing it here. */ if (reqinfo->mode == MODE_SET_RESERVE1) { if (free_this_index_search) { snmp_free_varbind(free_this_index_search); free_this_index_search = NULL; } } if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK || /* XXX */ reqinfo->mode == MODE_SET_FREE || reqinfo->mode == MODE_SET_UNDO || reqinfo->mode == MODE_SET_COMMIT) { if (callback_data_keep && iinfo->free_data_context) { (iinfo->free_data_context) (callback_data_keep, iinfo); callback_data_keep = NULL; } if (free_this_index_search) { snmp_free_varbind(free_this_index_search); free_this_index_search = NULL; } #ifndef NOT_SERIALIZED if (callback_loop_context && iinfo->free_loop_context_at_end) { (iinfo->free_loop_context_at_end) (callback_loop_context, iinfo); callback_loop_context = NULL; } #endif } #ifdef NOT_SERIALIZED return ret; #else requests = requests->next; #endif } #ifdef NOT_SERIALIZED if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK || /* XXX */ reqinfo->mode == MODE_SET_FREE || reqinfo->mode == MODE_SET_UNDO || reqinfo->mode == MODE_SET_COMMIT) { if (callback_loop_context && iinfo->free_loop_context_at_end) { (iinfo->free_loop_context_at_end) (callback_loop_context, iinfo); callback_loop_context = NULL; } } #endif return SNMP_ERR_NOERROR; }
/** * The helper handler that takes care of passing a specific row of * data down to the lower handler(s). It sets request->processed if * the request should not be handled. */ int netsnmp_table_data_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_table_data *table = (netsnmp_table_data *) handler->myvoid; netsnmp_request_info *request; int valid_request = 0; netsnmp_table_row *row; netsnmp_table_request_info *table_info; netsnmp_table_registration_info *table_reg_info = netsnmp_find_table_registration_info(reginfo); int result, regresult; int oldmode; for (request = requests; request; request = request->next) { if (request->processed) continue; table_info = netsnmp_extract_table_info(request); if (!table_info) continue; /* ack */ /* * find the row in question */ switch (reqinfo->mode) { case MODE_GETNEXT: case MODE_GETBULK: /* XXXWWW */ if (request->requestvb->type != ASN_NULL) continue; /* * loop through data till we find the next row */ result = snmp_oid_compare(request->requestvb->name, request->requestvb->name_length, reginfo->rootoid, reginfo->rootoid_len); regresult = snmp_oid_compare(request->requestvb->name, SNMP_MIN(request->requestvb-> name_length, reginfo->rootoid_len), reginfo->rootoid, reginfo->rootoid_len); if (regresult == 0 && request->requestvb->name_length < reginfo->rootoid_len) regresult = -1; if (result < 0 || 0 == result) { /* * before us entirely, return the first */ row = table->first_row; table_info->colnum = table_reg_info->min_column; } else if (regresult == 0 && request->requestvb->name_length == reginfo->rootoid_len + 1 && /* entry node must be 1, but any column is ok */ request->requestvb->name[reginfo->rootoid_len] == 1) { /* * exactly to the entry */ row = table->first_row; table_info->colnum = table_reg_info->min_column; } else if (regresult == 0 && request->requestvb->name_length == reginfo->rootoid_len + 2 && /* entry node must be 1, but any column is ok */ request->requestvb->name[reginfo->rootoid_len] == 1) { /* * exactly to the column */ row = table->first_row; } else { /* * loop through all rows looking for the first one * that is equal to the request or greater than it */ for (row = table->first_row; row; row = row->next) { /* * compare the index of the request to the row */ result = snmp_oid_compare(row->index_oid, row->index_oid_len, request->requestvb->name + 2 + reginfo->rootoid_len, request->requestvb->name_length - 2 - reginfo->rootoid_len); if (result == 0) { /* * equal match, return the next row */ if (row) { row = row->next; } break; } else if (result > 0) { /* * the current row is greater than the * request, use it */ break; } } } if (!row) { table_info->colnum++; if (table_info->colnum <= table_reg_info->max_column) { row = table->first_row; } } if (row) { valid_request = 1; netsnmp_request_add_list_data(request, netsnmp_create_data_list (TABLE_DATA_NAME, row, NULL)); /* * Set the name appropriately, so we can pass this * request on as a simple GET request */ netsnmp_table_data_build_result(reginfo, reqinfo, request, row, table_info->colnum, ASN_NULL, NULL, 0); } else { /* no decent result found. Give up. It's beyond us. */ request->processed = 1; } break; case MODE_GET: if (request->requestvb->type != ASN_NULL) continue; /* * find the row in question */ if (request->requestvb->name_length < (reginfo->rootoid_len + 3)) { /* table.entry.column... */ /* * request too short */ netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE); break; } else if (NULL == (row = netsnmp_table_data_get_from_oid(table, request-> requestvb->name + reginfo-> rootoid_len + 2, request-> requestvb-> name_length - reginfo-> rootoid_len - 2))) { /* * no such row */ netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE); break; } else { valid_request = 1; netsnmp_request_add_list_data(request, netsnmp_create_data_list (TABLE_DATA_NAME, row, NULL)); } break; case MODE_SET_RESERVE1: valid_request = 1; if (NULL != (row = netsnmp_table_data_get_from_oid(table, request->requestvb->name + reginfo->rootoid_len + 2, request->requestvb-> name_length - reginfo->rootoid_len - 2))) { netsnmp_request_add_list_data(request, netsnmp_create_data_list (TABLE_DATA_NAME, row, NULL)); } break; case MODE_SET_RESERVE2: case MODE_SET_ACTION: case MODE_SET_COMMIT: case MODE_SET_FREE: case MODE_SET_UNDO: valid_request = 1; } } if (valid_request) { /* * If this is a GetNext or GetBulk request, then we've identified * the row that ought to include the appropriate next instance. * Convert the request into a Get request, so that the lower-level * handlers don't need to worry about skipping on.... */ oldmode = reqinfo->mode; if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) { reqinfo->mode = MODE_GET; } result = netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); if (oldmode == MODE_GETNEXT || oldmode == MODE_GETBULK) { /* XXX */ for (request = requests; request; request = request->next) { /* * ... but if the lower-level handlers aren't dealing with * skipping on to the next instance, then we must handle * this situation here. * Mark 'holes' in the table as needing to be retried. * * This approach is less efficient than handling such * holes directly in the table_dataset handler, but allows * user-provided handlers to override the dataset handler * if this proves necessary. */ if (request->requestvb->type == ASN_NULL || request->requestvb->type == SNMP_NOSUCHINSTANCE) { request->requestvb->type = ASN_PRIV_RETRY; } /* XXX - Move on to the next object */ if (request->requestvb->type == SNMP_NOSUCHOBJECT) { request->requestvb->type = ASN_PRIV_RETRY; } } reqinfo->mode = oldmode; } return result; } else return SNMP_ERR_NOERROR; }
void return_delayed_response(unsigned int clientreg, void *clientarg) { /* * extract the cache from the passed argument */ netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) clientarg; netsnmp_request_info *requests; netsnmp_agent_request_info *reqinfo; u_long *delay_time_cache = NULL; /* * here we double check that the cache we created earlier is still * * valid. If not, the request timed out for some reason and we * * don't need to keep processing things. Should never happen, but * * this double checks. */ cache = netsnmp_handler_check_cache(cache); if (!cache) { snmp_log(LOG_ERR, "illegal call to return delayed response\n"); return; } /* * re-establish the previous pointers we are used to having */ reqinfo = cache->reqinfo; requests = cache->requests; DEBUGMSGTL(("delayed_instance", "continuing delayed request, mode = %d\n", cache->reqinfo->mode)); /* * mention that it's no longer delegated, and we've now answered * the query (which we'll do down below). */ requests->delegated = 0; switch (cache->reqinfo->mode) { /* * registering as an instance means we don't need to deal with * getnext processing, so we don't handle it here at all. * * However, since the instance handler already reset the mode * back to GETNEXT from the faked GET mode, we need to do the * same thing in both cases. This should be fixed in future * versions of net-snmp hopefully. */ case MODE_GET: case MODE_GETNEXT: /* * return the currend delay time */ snmp_set_var_typed_value(cache->requests->requestvb, ASN_INTEGER, (u_char *) & delay_time, sizeof(delay_time)); break; #ifndef NETSNMP_NO_WRITE_SUPPORT case MODE_SET_RESERVE1: /* * check type */ if (requests->requestvb->type != ASN_INTEGER) { /* * not an integer. Bad dog, no bone. */ netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); /* * we don't need the cache any longer */ netsnmp_free_delegated_cache(cache); return; } break; case MODE_SET_RESERVE2: /* * store old value for UNDO support in the future. */ delay_time_cache = netsnmp_memdup(&delay_time, sizeof(delay_time)); /* * malloc failed */ if (delay_time_cache == NULL) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); netsnmp_free_delegated_cache(cache); return; } /* * Add our temporary information to the request itself. * This is then retrivable later. The free function * passed auto-frees it when the request is later * deleted. */ netsnmp_request_add_list_data(requests, netsnmp_create_data_list (DELAYED_INSTANCE_SET_NAME, delay_time_cache, free)); break; case MODE_SET_ACTION: /* * update current value */ delay_time = *(requests->requestvb->val.integer); DEBUGMSGTL(("testhandler", "updated delay_time -> %ld\n", delay_time)); break; case MODE_SET_UNDO: /* * ack, something somewhere failed. We reset back to the * previously old value by extracting the previosuly * stored information back out of the request */ delay_time = *((u_long *) netsnmp_request_get_list_data(requests, DELAYED_INSTANCE_SET_NAME)); break; case MODE_SET_COMMIT: case MODE_SET_FREE: /* * the only thing to do here is free the old memdup'ed * value, but it's auto-freed by the datalist recovery, so * we don't have anything to actually do here */ break; #endif /* NETSNMP_NO_WRITE_SUPPORT */ } /* * free the information cache */ netsnmp_free_delegated_cache(cache); }
int handle_ipForwarding(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { int rc; u_long value; /* We are never called for a GETNEXT if it's registered as a "instance", as it's "magically" handled for us. */ /* a instance handler also only hands us one request at a time, so we don't need to loop over a list of requests; we'll only get one. */ switch(reqinfo->mode) { case MODE_GET: rc = netsnmp_arch_ip_scalars_ipForwarding_get(&value); if (rc != 0) { netsnmp_set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE); } else { value = value ? 1 : 2; snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *)&value, sizeof(value)); } break; /* * SET REQUEST * * multiple states in the transaction. See: * http://www.net-snmp.org/tutorial-5/toolkit/mib_module/set-actions.jpg */ case MODE_SET_RESERVE1: break; case MODE_SET_RESERVE2: /* * store old info for undo later */ rc = netsnmp_arch_ip_scalars_ipForwarding_get(&value); if (rc < 0) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_NOCREATION); } else { u_long *value_save = NULL; u_char * _value_save = (u_char *)value_save; memdup(& _value_save, (u_char *) &value, sizeof(value)); if ( NULL == value_save ) netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); else netsnmp_request_add_list_data(requests, netsnmp_create_data_list ("ipfw", value_save, free)); } break; case MODE_SET_FREE: /* XXX: free resources allocated in RESERVE1 and/or RESERVE2. Something failed somewhere, and the states below won't be called. */ break; case MODE_SET_ACTION: /* XXX: perform the value change here */ value = *(requests->requestvb->val.integer); rc = netsnmp_arch_ip_scalars_ipForwarding_set(value); if ( 0 != rc ) { netsnmp_set_request_error(reqinfo, requests, rc); } break; case MODE_SET_COMMIT: break; case MODE_SET_UNDO: value = *((u_long *) netsnmp_request_get_list_data(requests, "ipfw")); rc = netsnmp_arch_ip_scalars_ipForwarding_set(value); if ( 0 != rc ) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_UNDOFAILED); } break; default: /* we should never get here, so this is a really bad error */ snmp_log(LOG_ERR, "unknown mode (%d) in handle_ipForwarding\n", reqinfo->mode ); return SNMP_ERR_GENERR; } return SNMP_ERR_NOERROR; }
/** implements the old_api handler */ int netsnmp_old_api_helper(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { #if MIB_CLIENTS_ARE_EVIL oid save[MAX_OID_LEN]; size_t savelen = 0; #endif struct variable compat_var, *cvp = &compat_var; int exact = 1; int status; struct variable *vp; netsnmp_old_api_cache *cacheptr; netsnmp_agent_session *oldasp = NULL; u_char *access = NULL; WriteMethod *write_method = NULL; size_t len; size_t tmp_len; oid tmp_name[MAX_OID_LEN]; vp = (struct variable *) handler->myvoid; /* * create old variable structure with right information */ memcpy(cvp->name, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid)); cvp->namelen = reginfo->rootoid_len; cvp->type = vp->type; cvp->magic = vp->magic; cvp->acl = vp->acl; cvp->findVar = vp->findVar; switch (reqinfo->mode) { case MODE_GETNEXT: case MODE_GETBULK: exact = 0; } for (; requests; requests = requests->next) { #if MIB_CLIENTS_ARE_EVIL savelen = requests->requestvb->name_length; memcpy(save, requests->requestvb->name, savelen * sizeof(oid)); #endif switch (reqinfo->mode) { case MODE_GET: case MODE_GETNEXT: #ifndef NETSNMP_NO_WRITE_SUPPORT case MODE_SET_RESERVE1: #endif /* !NETSNMP_NO_WRITE_SUPPORT */ /* * Actually call the old mib-module function */ if (vp && vp->findVar) { memcpy(tmp_name, requests->requestvb->name, requests->requestvb->name_length*sizeof(oid)); tmp_len = requests->requestvb->name_length; access = (*(vp->findVar)) (cvp, tmp_name, &tmp_len, exact, &len, &write_method); snmp_set_var_objid( requests->requestvb, tmp_name, tmp_len ); } else access = NULL; #ifdef WWW_FIX if (IS_DELEGATED(cvp->type)) { add_method = (AddVarMethod *) statP; requests->delayed = 1; have_delegated = 1; continue; /* WWW: This may not get to the right place */ } #endif /* * WWW: end range checking */ if (access) { /* * result returned */ #ifndef NETSNMP_NO_WRITE_SUPPORT if (reqinfo->mode != MODE_SET_RESERVE1) #endif /* !NETSNMP_NO_WRITE_SUPPORT */ snmp_set_var_typed_value(requests->requestvb, cvp->type, access, len); } else { /* * no result returned */ #if MIB_CLIENTS_ARE_EVIL if (access == NULL) { if (netsnmp_oid_equals(requests->requestvb->name, requests->requestvb->name_length, save, savelen) != 0) { DEBUGMSGTL(("old_api", "evil_client: %s\n", reginfo->handlerName)); memcpy(requests->requestvb->name, save, savelen * sizeof(oid)); requests->requestvb->name_length = savelen; } } #endif } /* * AAA: fall through for everything that is a set (see BBB) */ #ifndef NETSNMP_NO_WRITE_SUPPORT if (reqinfo->mode != MODE_SET_RESERVE1) #endif /* !NETSNMP_NO_WRITE_SUPPORT */ break; cacheptr = SNMP_MALLOC_TYPEDEF(netsnmp_old_api_cache); if (!cacheptr) return netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); cacheptr->data = access; cacheptr->write_method = write_method; write_method = NULL; netsnmp_request_add_list_data(requests, netsnmp_create_data_list (OLD_API_NAME, cacheptr, &free_wrapper)); /* * BBB: fall through for everything that is a set (see AAA) */ default: /* * WWW: explicitly list the SET conditions */ /* * (the rest of the) SET contions */ cacheptr = (netsnmp_old_api_cache *) netsnmp_request_get_list_data(requests, OLD_API_NAME); if (cacheptr == NULL || cacheptr->write_method == NULL) { /* * WWW: try to set ourselves if possible? */ return netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_NOTWRITABLE); } oldasp = netsnmp_get_current_agent_session(); set_current_agent_session(reqinfo->asp); status = (*(cacheptr->write_method)) (reqinfo->mode, requests->requestvb->val. string, requests->requestvb->type, requests->requestvb->val_len, cacheptr->data, requests->requestvb->name, requests->requestvb-> name_length); set_current_agent_session(oldasp); if (status != SNMP_ERR_NOERROR) { netsnmp_set_request_error(reqinfo, requests, status); } /* * clean up is done by the automatic freeing of the * cache stored in the request. */ break; } } return SNMP_ERR_NOERROR; }
int netsnmp_instance_num_file_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_num_file_instance *nfi; u_long it, *it_save; int rc; netsnmp_assert(NULL != handler); nfi = (netsnmp_num_file_instance *)handler->myvoid; netsnmp_assert(NULL != nfi); netsnmp_assert(NULL != nfi->file_name); DEBUGMSGTL(("netsnmp_instance_int_handler", "Got request: %d\n", reqinfo->mode)); switch (reqinfo->mode) { /* * data requests */ case MODE_GET: /* * Use a long here, otherwise on 64 bit use of an int would fail */ netsnmp_assert(NULL == nfi->filep); nfi->filep = fopen(nfi->file_name, "r"); if (NULL == nfi->filep) { netsnmp_set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE); return SNMP_ERR_NOERROR; } rc = fscanf(nfi->filep, (nfi->type == ASN_INTEGER) ? "%ld" : "%lu", &it); fclose(nfi->filep); nfi->filep = NULL; if (rc != 1) { netsnmp_set_request_error(reqinfo, requests, SNMP_NOSUCHINSTANCE); return SNMP_ERR_NOERROR; } snmp_set_var_typed_value(requests->requestvb, nfi->type, (u_char *) &it, sizeof(it)); break; /* * SET requests. Should only get here if registered RWRITE */ case MODE_SET_RESERVE1: netsnmp_assert(NULL == nfi->filep); if (requests->requestvb->type != nfi->type) netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); break; case MODE_SET_RESERVE2: netsnmp_assert(NULL == nfi->filep); nfi->filep = fopen(nfi->file_name, "w+"); if (NULL == nfi->filep) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_NOTWRITABLE); return SNMP_ERR_NOERROR; } /* * store old info for undo later */ if (fscanf(nfi->filep, (nfi->type == ASN_INTEGER) ? "%ld" : "%lu", &it) != 1) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); return SNMP_ERR_NOERROR; } memdup((u_char **) & it_save, (u_char *)&it, sizeof(u_long)); if (it_save == NULL) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); return SNMP_ERR_NOERROR; } netsnmp_request_add_list_data(requests, netsnmp_create_data_list (INSTANCE_HANDLER_NAME, it_save, free)); break; case MODE_SET_ACTION: /* * update current */ DEBUGMSGTL(("helper:instance", "updated %s -> %ld\n", nfi->file_name, *(requests->requestvb->val.integer))); it = *(requests->requestvb->val.integer); rewind(nfi->filep); /* rewind to make sure we are at the beginning */ rc = fprintf(nfi->filep, (nfi->type == ASN_INTEGER) ? "%ld" : "%lu", it); if (rc < 0) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_GENERR); return SNMP_ERR_NOERROR; } break; case MODE_SET_UNDO: it = *((u_int *) netsnmp_request_get_list_data(requests, INSTANCE_HANDLER_NAME)); rc = fprintf(nfi->filep, (nfi->type == ASN_INTEGER) ? "%ld" : "%lu", it); if (rc < 0) netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_UNDOFAILED); /** fall through */ case MODE_SET_COMMIT: case MODE_SET_FREE: if (NULL != nfi->filep) { fclose(nfi->filep); nfi->filep = NULL; } break; } if (handler->next && handler->next->access_method) return netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); return SNMP_ERR_NOERROR; }
int my_test_instance_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { static u_long accesses = 0; u_long *accesses_cache = NULL; DEBUGMSGTL(("testhandler", "Got instance request:\n")); switch (reqinfo->mode) { case MODE_GET: snmp_set_var_typed_value(requests->requestvb, ASN_UNSIGNED, (u_char *) & accesses, sizeof(accesses)); break; #ifndef NETSNMP_NO_WRITE_SUPPORT case MODE_SET_RESERVE1: if (requests->requestvb->type != ASN_UNSIGNED) netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); break; case MODE_SET_RESERVE2: /* * store old info for undo later */ accesses_cache = netsnmp_memdup(&accesses, sizeof(accesses)); if (accesses_cache == NULL) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); return SNMP_ERR_NOERROR; } netsnmp_request_add_list_data(requests, netsnmp_create_data_list (TESTHANDLER_SET_NAME, accesses_cache, free)); break; case MODE_SET_ACTION: /* * update current */ accesses = *(requests->requestvb->val.integer); DEBUGMSGTL(("testhandler", "updated accesses -> %d\n", accesses)); break; case MODE_SET_UNDO: accesses = *((u_long *) netsnmp_request_get_list_data(requests, TESTHANDLER_SET_NAME)); break; case MODE_SET_COMMIT: case MODE_SET_FREE: /* * nothing to do */ break; #endif /* NETSNMP_NO_WRITE_SUPPORT */ } return SNMP_ERR_NOERROR; }
/* * The helper handler that takes care of passing a specific row of * data down to the lower handler(s). It sets request->processed if * the request should not be handled. */ int netsnmp_table_data_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_table_data *table = (netsnmp_table_data *) handler->myvoid; netsnmp_request_info *request; int valid_request = 0; netsnmp_table_row *row; netsnmp_table_request_info *table_info; netsnmp_table_registration_info *table_reg_info = netsnmp_find_table_registration_info(reginfo); int result, regresult; int oldmode; for (request = requests; request; request = request->next) { if (request->processed) continue; table_info = netsnmp_extract_table_info(request); if (!table_info) continue; /* ack */ switch (reqinfo->mode) { case MODE_GET: case MODE_GETNEXT: case MODE_SET_RESERVE1: netsnmp_request_add_list_data(request, netsnmp_create_data_list( TABLE_DATA_TABLE, table, NULL)); } /* * find the row in question */ switch (reqinfo->mode) { case MODE_GETNEXT: case MODE_GETBULK: /* XXXWWW */ if (request->requestvb->type != ASN_NULL) continue; /* * loop through data till we find the next row */ result = snmp_oid_compare(request->requestvb->name, request->requestvb->name_length, reginfo->rootoid, reginfo->rootoid_len); regresult = snmp_oid_compare(request->requestvb->name, SNMP_MIN(request->requestvb-> name_length, reginfo->rootoid_len), reginfo->rootoid, reginfo->rootoid_len); if (regresult == 0 && request->requestvb->name_length < reginfo->rootoid_len) regresult = -1; if (result < 0 || 0 == result) { /* * before us entirely, return the first */ row = table->first_row; table_info->colnum = table_reg_info->min_column; } else if (regresult == 0 && request->requestvb->name_length == reginfo->rootoid_len + 1 && /* entry node must be 1, but any column is ok */ request->requestvb->name[reginfo->rootoid_len] == 1) { /* * exactly to the entry */ row = table->first_row; table_info->colnum = table_reg_info->min_column; } else if (regresult == 0 && request->requestvb->name_length == reginfo->rootoid_len + 2 && /* entry node must be 1, but any column is ok */ request->requestvb->name[reginfo->rootoid_len] == 1) { /* * exactly to the column */ row = table->first_row; } else { /* * loop through all rows looking for the first one * that is equal to the request or greater than it */ for (row = table->first_row; row; row = row->next) { /* * compare the index of the request to the row */ result = snmp_oid_compare(row->index_oid, row->index_oid_len, request->requestvb->name + 2 + reginfo->rootoid_len, request->requestvb->name_length - 2 - reginfo->rootoid_len); if (result == 0) { /* * equal match, return the next row */ if (row) { row = row->next; } break; } else if (result > 0) { /* * the current row is greater than the * request, use it */ break; } } } if (!row) { table_info->colnum++; if (table_info->colnum <= table_reg_info->max_column) { row = table->first_row; } } if (row) { valid_request = 1; netsnmp_request_add_list_data(request, netsnmp_create_data_list (TABLE_DATA_ROW, row, NULL)); /* * Set the name appropriately, so we can pass this * request on as a simple GET request */ netsnmp_table_data_build_result(reginfo, reqinfo, request, row, table_info->colnum, ASN_NULL, NULL, 0); } else { /* no decent result found. Give up. It's beyond us. */ request->processed = 1; } break; case MODE_GET: if (request->requestvb->type != ASN_NULL) continue; /* * find the row in question */ if (request->requestvb->name_length < (reginfo->rootoid_len + 3)) { /* table.entry.column... */ /* * request too short */ netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE); break; } else if (NULL == (row = netsnmp_table_data_get_from_oid(table, request-> requestvb->name + reginfo-> rootoid_len + 2, request-> requestvb-> name_length - reginfo-> rootoid_len - 2))) { /* * no such row */ netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE); break; } else { valid_request = 1; netsnmp_request_add_list_data(request, netsnmp_create_data_list (TABLE_DATA_ROW, row, NULL)); } break; case MODE_SET_RESERVE1: valid_request = 1; if (NULL != (row = netsnmp_table_data_get_from_oid(table, request->requestvb->name + reginfo->rootoid_len + 2, request->requestvb-> name_length - reginfo->rootoid_len - 2))) { netsnmp_request_add_list_data(request, netsnmp_create_data_list (TABLE_DATA_ROW, row, NULL)); } break; case MODE_SET_RESERVE2: case MODE_SET_ACTION: case MODE_SET_COMMIT: case MODE_SET_FREE: case MODE_SET_UNDO: valid_request = 1; } } if (valid_request && (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK)) { /* * If this is a GetNext or GetBulk request, then we've identified * the row that ought to include the appropriate next instance. * Convert the request into a Get request, so that the lower-level * handlers don't need to worry about skipping on, and call these * handlers ourselves (so we can undo this again afterwards). */ oldmode = reqinfo->mode; reqinfo->mode = MODE_GET; result = netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); reqinfo->mode = oldmode; handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; return result; } else /* next handler called automatically - 'AUTO_NEXT' */ return SNMP_ERR_NOERROR; }
/** * \deprecated This function is unused and scheduled for removal in Net-SNMP 5.6 */ int netsnmp_instance_uint_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { unsigned int *it = (unsigned int *) handler->myvoid; unsigned int *it_save; unsigned long tmp_it; DEBUGMSGTL(("netsnmp_instance_uint_handler", "Got request: %d\n", reqinfo->mode)); switch (reqinfo->mode) { /* * data requests */ case MODE_GET: /* * Use a long here, otherwise on 64 bit use of an int would fail */ tmp_it = *it; snmp_set_var_typed_value(requests->requestvb, ASN_UNSIGNED, (u_char *) &tmp_it, sizeof(unsigned long)); break; /* * SET requests. Should only get here if registered RWRITE */ case MODE_SET_RESERVE1: if (requests->requestvb->type != ASN_UNSIGNED) netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); break; case MODE_SET_RESERVE2: /* * store old info for undo later */ memdup((u_char **) & it_save, (u_char *) it, sizeof(u_int)); if (it_save == NULL) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); return SNMP_ERR_NOERROR; } netsnmp_request_add_list_data(requests, netsnmp_create_data_list (INSTANCE_HANDLER_NAME, it_save, free)); break; case MODE_SET_ACTION: /* * update current */ DEBUGMSGTL(("testhandler", "updated uint %d -> %ld\n", *it, *(requests->requestvb->val.integer))); *it = (unsigned int) *(requests->requestvb->val.integer); break; case MODE_SET_UNDO: *it = *((u_int *) netsnmp_request_get_list_data(requests, INSTANCE_HANDLER_NAME)); break; case MODE_SET_COMMIT: case MODE_SET_FREE: /* * nothing to do */ break; } if (handler->next && handler->next->access_method) return netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); return SNMP_ERR_NOERROR; }
int netsnmp_watcher_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_watcher_info *winfo = (netsnmp_watcher_info *) handler->myvoid; netsnmp_watcher_cache *old_data; DEBUGMSGTL(("helper:watcher", "Got request: %d\n", reqinfo->mode)); DEBUGMSGTL(( "helper:watcher", " oid:")); DEBUGMSGOID(("helper:watcher", requests->requestvb->name, requests->requestvb->name_length)); DEBUGMSG(( "helper:watcher", "\n")); switch (reqinfo->mode) { /* * data requests */ case MODE_GET: snmp_set_var_typed_value(requests->requestvb, winfo->type, winfo->data, get_data_size(winfo)); break; /* * SET requests. Should only get here if registered RWRITE */ case MODE_SET_RESERVE1: if (requests->requestvb->type != winfo->type) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; } else if (((winfo->flags & WATCHER_MAX_SIZE) && requests->requestvb->val_len > winfo->max_size) || ((winfo->flags & WATCHER_FIXED_SIZE) && requests->requestvb->val_len != get_data_size(winfo))) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGLENGTH); handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; } else if ((winfo->flags & WATCHER_SIZE_STRLEN) && (memchr(requests->requestvb->val.string, '\0', requests->requestvb->val_len) != NULL)) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGVALUE); handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; } break; case MODE_SET_RESERVE2: /* * store old info for undo later */ old_data = netsnmp_watcher_cache_create(winfo->data, get_data_size(winfo)); if (old_data == NULL) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; } else netsnmp_request_add_list_data(requests, netsnmp_create_data_list ("watcher", old_data, &free_wrapper)); break; case MODE_SET_FREE: /* * nothing to do */ break; case MODE_SET_ACTION: /* * update current */ set_data(winfo, (void *)requests->requestvb->val.string, requests->requestvb->val_len); break; case MODE_SET_UNDO: old_data = (netsnmp_watcher_cache*)netsnmp_request_get_list_data(requests, "watcher"); set_data(winfo, old_data->data, old_data->size); break; case MODE_SET_COMMIT: break; } /* next handler called automatically - 'AUTO_NEXT' */ return SNMP_ERR_NOERROR; }
/* implements the table_iterator helper */ int netsnmp_table_iterator_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_table_registration_info *tbl_info; netsnmp_table_request_info *table_info = NULL; oid coloid[MAX_OID_LEN]; size_t coloid_len; int ret; static oid myname[MAX_OID_LEN]; size_t myname_len; int oldmode = 0; netsnmp_iterator_info *iinfo; int notdone; netsnmp_request_info *request, *reqtmp = NULL; netsnmp_variable_list *index_search = NULL; netsnmp_variable_list *free_this_index_search = NULL; void *callback_loop_context = NULL, *last_loop_context; void *callback_data_context = NULL; ti_cache_info *ti_info = NULL; int request_count = 0; netsnmp_oid_stash_node **cinfo = NULL; netsnmp_variable_list *old_indexes = NULL, *vb; netsnmp_table_registration_info *table_reg_info = NULL; int i; netsnmp_data_list *ldata = NULL; iinfo = (netsnmp_iterator_info *) handler->myvoid; if (!iinfo || !reginfo || !reqinfo) return SNMPERR_GENERR; tbl_info = iinfo->table_reginfo; /* * copy in the table registration oid for later use */ coloid_len = reginfo->rootoid_len + 2; memcpy(coloid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid)); coloid[reginfo->rootoid_len] = 1; /* table.entry node */ /* * illegally got here if these functions aren't defined */ if (iinfo->get_first_data_point == NULL || iinfo->get_next_data_point == NULL) { snmp_log(LOG_ERR, "table_iterator helper called without data accessor functions\n"); return SNMP_ERR_GENERR; } /* preliminary analysis */ switch (reqinfo->mode) { case MODE_GET_STASH: cinfo = netsnmp_extract_stash_cache(reqinfo); table_reg_info = netsnmp_find_table_registration_info(reginfo); /* XXX: move this malloc to stash_cache handler? */ reqtmp = SNMP_MALLOC_TYPEDEF(netsnmp_request_info); reqtmp->subtree = requests->subtree; table_info = netsnmp_extract_table_info(requests); netsnmp_request_add_list_data(reqtmp, netsnmp_create_data_list (TABLE_HANDLER_NAME, (void *) table_info, NULL)); /* remember the indexes that were originally parsed. */ old_indexes = table_info->indexes; break; case MODE_GETNEXT: for(request = requests ; request; request = request->next) { if (request->processed) continue; table_info = netsnmp_extract_table_info(request); if (table_info->colnum < tbl_info->min_column - 1) { /* XXX: optimize better than this */ /* for now, just increase to colnum-1 */ /* we need to jump to the lowest result of the min_column and take it, comparing to nothing from the request */ table_info->colnum = tbl_info->min_column - 1; } else if (table_info->colnum > tbl_info->max_column) { request->processed = TABLE_ITERATOR_NOTAGAIN; } ti_info = netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); if (!ti_info) { ti_info = SNMP_MALLOC_TYPEDEF(ti_cache_info); netsnmp_request_add_list_data(request, netsnmp_create_data_list (TI_REQUEST_CACHE, ti_info, netsnmp_free_ti_cache)); } /* XXX: if no valid requests, don't even loop below */ } break; } /* * collect all information for each needed row */ if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GET_STASH || reqinfo->mode == MODE_SET_RESERVE1) { /* * Count the number of request in the list, * so that we'll know when we're finished */ for(request = requests ; request; request = request->next) if (!request->processed) request_count++; notdone = 1; while(notdone) { notdone = 0; /* find first data point */ if (!index_search) { if (free_this_index_search) { /* previously done */ index_search = free_this_index_search; } else { for(request=requests ; request; request=request->next) { table_info = netsnmp_extract_table_info(request); if (table_info) break; } if (!table_info) { snmp_log(LOG_WARNING, "no valid requests for iterator table %s\n", reginfo->handlerName); netsnmp_free_request_data_sets(reqtmp); SNMP_FREE(reqtmp); return SNMP_ERR_NOERROR; } index_search = snmp_clone_varbind(table_info->indexes); free_this_index_search = index_search; /* setup, malloc search data: */ if (!index_search) { /* * hmmm.... invalid table? */ snmp_log(LOG_WARNING, "invalid index list or failed malloc for table %s\n", reginfo->handlerName); netsnmp_free_request_data_sets(reqtmp); SNMP_FREE(reqtmp); return SNMP_ERR_NOERROR; } } } /* if sorted, pass in a hint */ if (iinfo->flags & NETSNMP_ITERATOR_FLAG_SORTED) { callback_loop_context = table_info; } index_search = (iinfo->get_first_data_point) (&callback_loop_context, &callback_data_context, index_search, iinfo); /* loop over each data point */ while(index_search) { /* remember to free this later */ free_this_index_search = index_search; /* compare against each request*/ for(request = requests ; request; request = request->next) { if (request->processed) continue; /* XXX: store in an array for faster retrival */ table_info = netsnmp_extract_table_info(request); coloid[reginfo->rootoid_len + 1] = table_info->colnum; ti_info = netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); switch(reqinfo->mode) { case MODE_GET: case MODE_SET_RESERVE1: /* looking for exact matches */ build_oid_noalloc(myname, MAX_OID_LEN, &myname_len, coloid, coloid_len, index_search); if (snmp_oid_compare(myname, myname_len, request->requestvb->name, request->requestvb->name_length) == 0) { /* keep this */ netsnmp_iterator_remember(request, myname, myname_len, callback_data_context, callback_loop_context, iinfo); request_count--; /* One less to look for */ } else { if (iinfo->free_data_context && callback_data_context) { (iinfo->free_data_context)(callback_data_context, iinfo); } } break; case MODE_GET_STASH: /* collect data for each column for every row */ build_oid_noalloc(myname, MAX_OID_LEN, &myname_len, coloid, coloid_len, index_search); reqinfo->mode = MODE_GET; if (reqtmp) ldata = netsnmp_get_list_node(reqtmp->parent_data, TABLE_ITERATOR_NAME); if (!ldata) { netsnmp_request_add_list_data(reqtmp, netsnmp_create_data_list (TABLE_ITERATOR_NAME, callback_data_context, NULL)); } else { /* may have changed */ ldata->data = callback_data_context; } table_info->indexes = index_search; for(i = table_reg_info->min_column; i <= (int)table_reg_info->max_column; i++) { myname[reginfo->rootoid_len + 1] = i; table_info->colnum = i; vb = reqtmp->requestvb = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); vb->type = ASN_NULL; snmp_set_var_objid(vb, myname, myname_len); netsnmp_call_next_handler(handler, reginfo, reqinfo, reqtmp); reqtmp->requestvb = NULL; reqtmp->processed = 0; if (vb->type != ASN_NULL) { /* XXX, not all */ netsnmp_oid_stash_add_data(cinfo, myname, myname_len, vb); } else { snmp_free_var(vb); } } reqinfo->mode = MODE_GET_STASH; break; case MODE_GETNEXT: /* looking for "next" matches */ if (netsnmp_check_getnext_reply (request, coloid, coloid_len, index_search, &ti_info->results)) { netsnmp_iterator_remember(request, ti_info->results->name, ti_info->results->name_length, callback_data_context, callback_loop_context, iinfo); /* * If we've been told that the rows are sorted, * then the first valid one we find * must be the right one. */ if (iinfo->flags & NETSNMP_ITERATOR_FLAG_SORTED) request_count--; } else { if (iinfo->free_data_context && callback_data_context) { (iinfo->free_data_context)(callback_data_context, iinfo); } } break; case MODE_SET_RESERVE2: case MODE_SET_FREE: case MODE_SET_UNDO: case MODE_SET_COMMIT: /* needed processing already done in RESERVE1 */ break; default: snmp_log(LOG_ERR, "table_iterator called with unsupported mode\n"); break; /* XXX return */ } } /* Is there any point in carrying on? */ if (!request_count) break; /* get the next search possibility */ last_loop_context = callback_loop_context; index_search = (iinfo->get_next_data_point) (&callback_loop_context, &callback_data_context, index_search, iinfo); if (iinfo->free_loop_context && last_loop_context && callback_data_context != last_loop_context) { (iinfo->free_loop_context) (last_loop_context, iinfo); last_loop_context = NULL; } } /* free loop context before going on */ if (callback_loop_context && iinfo->free_loop_context_at_end) { (iinfo->free_loop_context_at_end) (callback_loop_context, iinfo); callback_loop_context = NULL; } /* decide which (GETNEXT) requests are not yet filled */ if (reqinfo->mode == MODE_GETNEXT) { for(request = requests ; request; request = request->next) { if (request->processed) continue; ti_info = netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); if (!ti_info->results) { int nc; table_info = netsnmp_extract_table_info(request); nc = netsnmp_table_next_column(table_info); if (0 == nc) { coloid[reginfo->rootoid_len+1] = table_info->colnum+1; snmp_set_var_objid(request->requestvb, coloid, reginfo->rootoid_len+2); request->processed = TABLE_ITERATOR_NOTAGAIN; break; } else { table_info->colnum = nc; notdone = 1; } } } } } } if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_SET_RESERVE1) { /* per request last minute processing */ for(request = requests ; request; request = request->next) { if (request->processed) continue; ti_info = netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); table_info = netsnmp_extract_table_info(request); if (!ti_info) continue; switch(reqinfo->mode) { case MODE_GETNEXT: if (ti_info->best_match_len) snmp_set_var_objid(request->requestvb, ti_info->best_match, ti_info->best_match_len); else { coloid[reginfo->rootoid_len+1] = netsnmp_table_next_column(table_info); if (0 == coloid[reginfo->rootoid_len+1]) { /* out of range. */ coloid[reginfo->rootoid_len+1] = tbl_info->max_column + 1; request->processed = TABLE_ITERATOR_NOTAGAIN; } snmp_set_var_objid(request->requestvb, coloid, reginfo->rootoid_len+2); request->processed = 1; } snmp_free_varbind(table_info->indexes); table_info->indexes = snmp_clone_varbind(ti_info->results); /* FALL THROUGH */ case MODE_GET: case MODE_SET_RESERVE1: if (ti_info->data_context) /* we don't add a free pointer, since it's in the TI_REQUEST_CACHE instead */ netsnmp_request_add_list_data(request, netsnmp_create_data_list (TABLE_ITERATOR_NAME, ti_info->data_context, NULL)); break; default: break; } } /* we change all GETNEXT operations into GET operations. why? because we're just so nice to the lower levels. maybe someday they'll pay us for it. doubtful though. */ oldmode = reqinfo->mode; if (reqinfo->mode == MODE_GETNEXT) { reqinfo->mode = MODE_GET; } } else if (reqinfo->mode == MODE_GET_STASH) { netsnmp_free_request_data_sets(reqtmp); SNMP_FREE(reqtmp); table_info->indexes = old_indexes; } /* Finally, we get to call the next handler below us. Boy, wasn't all that simple? They better be glad they don't have to do it! */ if (reqinfo->mode != MODE_GET_STASH) { DEBUGMSGTL(("table_iterator", "call subhandler for mode: %s\n", se_find_label_in_slist("agent_mode", oldmode))); ret = netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); } /* reverse the previously saved mode if we were a getnext */ if (oldmode == MODE_GETNEXT) { reqinfo->mode = oldmode; } /* cleanup */ if (free_this_index_search) snmp_free_varbind(free_this_index_search); return SNMP_ERR_NOERROR; }
/** inserts table_iterator specific data for a newly * created row into a request */ NETSNMP_INLINE void netsnmp_insert_iterator_context(netsnmp_request_info *request, void *data) { netsnmp_request_info *req; netsnmp_table_request_info *table_info = NULL; netsnmp_variable_list *this_index = NULL; netsnmp_variable_list *that_index = NULL; oid base_oid[] = {0, 0}; /* Make sure index OIDs are legal! */ oid this_oid[MAX_OID_LEN]; oid that_oid[MAX_OID_LEN]; size_t this_oid_len, that_oid_len; if (!request) return; /* * We'll add the new row information to any request * structure with the same index values as the request * passed in (which includes that one!). * * So construct an OID based on these index values. */ table_info = netsnmp_extract_table_info(request); this_index = table_info->indexes; build_oid_noalloc(this_oid, MAX_OID_LEN, &this_oid_len, base_oid, 2, this_index); /* * We need to look through the whole of the request list * (as received by the current handler), as there's no * guarantee that this routine will be called by the first * varbind that refers to this row. * In particular, a RowStatus controlled row creation * may easily occur later in the variable list. * * So first, we rewind to the head of the list.... */ for (req=request; req->prev; req=req->prev) ; /* * ... and then start looking for matching indexes * (by constructing OIDs from these index values) */ for (; req; req=req->next) { table_info = netsnmp_extract_table_info(req); that_index = table_info->indexes; build_oid_noalloc(that_oid, MAX_OID_LEN, &that_oid_len, base_oid, 2, that_index); /* * This request has the same index values, * so add the newly-created row information. */ if (snmp_oid_compare(this_oid, this_oid_len, that_oid, that_oid_len) == 0) { netsnmp_request_add_list_data(req, netsnmp_create_data_list(TABLE_ITERATOR_NAME, data, NULL)); } } }
/* neIeee8021QBridgeVlanCurrentTable table mapper */ int neIeee8021QBridgeVlanCurrentTable_mapper ( netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_request_info *request; netsnmp_table_request_info *table_info; neIeee8021QBridgeVlanCurrentEntry_t *table_entry; register ieee8021QBridgeVlanCurrentEntry_t *poEntry = NULL; void *pvOldDdata = NULL; int ret; switch (reqinfo->mode) { /* * Read-support (also covers GetNext requests) */ case MODE_GET: for (request = requests; request != NULL; request = request->next) { poEntry = (ieee8021QBridgeVlanCurrentEntry_t*) netsnmp_extract_iterator_context (request); table_info = netsnmp_extract_table_info (request); if (poEntry == NULL) { netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); continue; } table_entry = &poEntry->oNe; switch (table_info->colnum) { case NEIEEE8021QBRIDGEVLANCURRENTADMINFLAGS: snmp_set_var_typed_value (request->requestvb, ASN_OCTET_STR, (u_char*) table_entry->au8AdminFlags, table_entry->u16AdminFlags_len); break; case NEIEEE8021QBRIDGEVLANCURRENTOPERSTATE: snmp_set_var_typed_integer (request->requestvb, ASN_INTEGER, table_entry->i32OperState); break; case NEIEEE8021QBRIDGEVLANCURRENTLEARNT: snmp_set_var_typed_value (request->requestvb, ASN_OCTET_STR, (u_char*) table_entry->pu8Learnt, table_entry->u16Learnt_len); break; case NEIEEE8021QBRIDGEVLANCURRENTIFINDEX: snmp_set_var_typed_integer (request->requestvb, ASN_INTEGER, table_entry->u32IfIndex); break; default: netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHOBJECT); break; } } break; /* * Write-support */ case MODE_SET_RESERVE1: for (request = requests; request != NULL; request = request->next) { poEntry = (ieee8021QBridgeVlanCurrentEntry_t*) netsnmp_extract_iterator_context (request); table_info = netsnmp_extract_table_info (request); table_entry = &poEntry->oNe; switch (table_info->colnum) { case NEIEEE8021QBRIDGEVLANCURRENTADMINFLAGS: ret = netsnmp_check_vb_type_and_max_size (request->requestvb, ASN_OCTET_STR, sizeof (table_entry->au8AdminFlags)); if (ret != SNMP_ERR_NOERROR) { netsnmp_set_request_error (reqinfo, request, ret); return SNMP_ERR_NOERROR; } break; default: netsnmp_set_request_error (reqinfo, request, SNMP_ERR_NOTWRITABLE); return SNMP_ERR_NOERROR; } } break; case MODE_SET_RESERVE2: for (request = requests; request != NULL; request = request->next) { poEntry = (ieee8021QBridgeVlanCurrentEntry_t*) netsnmp_extract_iterator_context (request); table_info = netsnmp_extract_table_info (request); if (poEntry == NULL) { netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE); continue; } table_entry = &poEntry->oNe; switch (table_info->colnum) { case NEIEEE8021QBRIDGEVLANCURRENTADMINFLAGS: if (poEntry->u8RowStatus == xRowStatus_active_c || poEntry->u8RowStatus == xRowStatus_notReady_c) { netsnmp_set_request_error (reqinfo, request, SNMP_ERR_RESOURCEUNAVAILABLE); return SNMP_ERR_NOERROR; } break; } } break; case MODE_SET_FREE: break; case MODE_SET_ACTION: for (request = requests; request != NULL; request = request->next) { pvOldDdata = netsnmp_request_get_list_data (request, ROLLBACK_BUFFER); poEntry = (ieee8021QBridgeVlanCurrentEntry_t*) netsnmp_extract_iterator_context (request); table_info = netsnmp_extract_table_info (request); table_entry = &poEntry->oNe; switch (table_info->colnum) { case NEIEEE8021QBRIDGEVLANCURRENTADMINFLAGS: if (pvOldDdata == NULL && (pvOldDdata = xBuffer_cAlloc (sizeof (xOctetString_t) + sizeof (table_entry->au8AdminFlags))) == NULL) { netsnmp_set_request_error (reqinfo, request, SNMP_ERR_RESOURCEUNAVAILABLE); return SNMP_ERR_NOERROR; } else if (pvOldDdata != table_entry) { ((xOctetString_t*) pvOldDdata)->pData = pvOldDdata + sizeof (xOctetString_t); ((xOctetString_t*) pvOldDdata)->u16Len = table_entry->u16AdminFlags_len; memcpy (((xOctetString_t*) pvOldDdata)->pData, table_entry->au8AdminFlags, sizeof (table_entry->au8AdminFlags)); netsnmp_request_add_list_data (request, netsnmp_create_data_list (ROLLBACK_BUFFER, pvOldDdata, &xBuffer_free)); } memset (table_entry->au8AdminFlags, 0, sizeof (table_entry->au8AdminFlags)); memcpy (table_entry->au8AdminFlags, request->requestvb->val.string, request->requestvb->val_len); table_entry->u16AdminFlags_len = request->requestvb->val_len; break; } } break; case MODE_SET_UNDO: for (request = requests; request != NULL; request = request->next) { pvOldDdata = netsnmp_request_get_list_data (request, ROLLBACK_BUFFER); poEntry = (ieee8021QBridgeVlanCurrentEntry_t*) netsnmp_extract_iterator_context (request); table_info = netsnmp_extract_table_info (request); if (poEntry == NULL || pvOldDdata == NULL) { continue; } table_entry = &poEntry->oNe; switch (table_info->colnum) { case NEIEEE8021QBRIDGEVLANCURRENTADMINFLAGS: memcpy (table_entry->au8AdminFlags, ((xOctetString_t*) pvOldDdata)->pData, ((xOctetString_t*) pvOldDdata)->u16Len); table_entry->u16AdminFlags_len = ((xOctetString_t*) pvOldDdata)->u16Len; break; } } break; case MODE_SET_COMMIT: break; } return SNMP_ERR_NOERROR; }
/** implements the table data helper. This is the routine that takes * care of all SNMP requests coming into the table. */ int netsnmp_table_data_set_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_table_data_set_storage *data = NULL; newrow_stash *newrowstash = NULL; netsnmp_table_row *row, *newrow = NULL; netsnmp_table_request_info *table_info; netsnmp_request_info *request; oid *suffix; size_t suffix_len; netsnmp_oid_stash_node **stashp = NULL; if (!handler) return SNMPERR_GENERR; DEBUGMSGTL(("netsnmp_table_data_set", "handler starting\n")); for (request = requests; request; request = request->next) { netsnmp_table_data_set *datatable = (netsnmp_table_data_set *) handler->myvoid; if (request->processed) continue; /* * extract our stored data and table info */ row = netsnmp_extract_table_row(request); table_info = netsnmp_extract_table_info(request); suffix = requests->requestvb->name + reginfo->rootoid_len + 2; suffix_len = requests->requestvb->name_length - (reginfo->rootoid_len + 2); if (MODE_IS_SET(reqinfo->mode)) { char buf[256]; /* is this reasonable size?? */ int rc; size_t len; /* * use a cached copy of the row for modification */ /* * cache location: may have been created already by other * SET requests in the same master request. */ rc = snprintf(buf, sizeof(buf), "dataset_row_stash:%s:", datatable->table->name); if ((-1 == rc) || (rc >= sizeof(buf))) { snmp_log(LOG_ERR,"%s handler name too long\n", datatable->table->name); netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); continue; } len = sizeof(buf) - rc; rc = snprint_objid(&buf[rc], len, table_info->index_oid, table_info->index_oid_len); if (-1 == rc) { snmp_log(LOG_ERR,"%s oid or name too long\n", datatable->table->name); netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); continue; } stashp = (netsnmp_oid_stash_node **) netsnmp_table_get_or_create_row_stash(reqinfo, buf); newrowstash = netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len); if (!newrowstash) { if (!row) { if (datatable->allow_creation) { /* * entirely new row. Create the row from the template */ newrowstash = netsnmp_table_data_set_create_newrowstash( datatable, table_info); newrow = newrowstash->newrow; } else if (datatable->rowstatus_column == 0) { /* * A RowStatus object may be used to control the * creation of a new row. But if this object * isn't declared (and the table isn't marked as * 'auto-create'), then we can't create a new row. */ netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOCREATION); continue; } } else { /* * existing row that needs to be modified */ newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash); newrow = netsnmp_table_data_set_clone_row(row); newrowstash->newrow = newrow; } netsnmp_oid_stash_add_data(stashp, suffix, suffix_len, newrowstash); } else { newrow = newrowstash->newrow; } /* * all future SET data modification operations use this * temp pointer */ if (reqinfo->mode == MODE_SET_RESERVE1 || reqinfo->mode == MODE_SET_RESERVE2) row = newrow; } if (row) data = (netsnmp_table_data_set_storage *) row->data; if (!row || !table_info || !data) { if (!MODE_IS_SET(reqinfo->mode)) { netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE); continue; } } data = netsnmp_table_data_set_find_column(data, table_info->colnum); switch (reqinfo->mode) { case MODE_GET: case MODE_GETNEXT: case MODE_GETBULK: /* XXXWWW */ if (data && data->data.voidp) netsnmp_table_data_build_result(reginfo, reqinfo, request, row, table_info->colnum, data->type, data->data.voidp, data->data_len); break; case MODE_SET_RESERVE1: if (data) { /* * Can we modify the existing row? */ if (!data->writable) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOTWRITABLE); } else if (request->requestvb->type != data->type) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGTYPE); } } else if (datatable->rowstatus_column == table_info->colnum) { /* * Otherwise, this is where we create a new row using * the RowStatus object (essentially duplicating the * steps followed earlier in the 'allow_creation' case) */ switch (*(request->requestvb->val.integer)) { case RS_CREATEANDGO: case RS_CREATEANDWAIT: newrowstash = netsnmp_table_data_set_create_newrowstash( datatable, table_info); newrow = newrowstash->newrow; row = newrow; netsnmp_oid_stash_add_data(stashp, suffix, suffix_len, newrowstash); } } break; case MODE_SET_RESERVE2: /* * If the agent receives a SET request for an object in a non-existant * row, then the RESERVE1 pass will create the row automatically. * * But since the row doesn't exist at that point, the test for whether * the object is writable or not will be skipped. So we need to check * for this possibility again here. * * Similarly, if row creation is under the control of the RowStatus * object (i.e. allow_creation == 0), but this particular request * doesn't include such an object, then the row won't have been created, * and the writable check will also have been skipped. Again - check here. */ if (data && data->writable == 0) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOTWRITABLE); continue; } if (datatable->rowstatus_column == table_info->colnum) { switch (*(request->requestvb->val.integer)) { case RS_ACTIVE: case RS_NOTINSERVICE: /* * Can only operate on pre-existing rows. */ if (!newrowstash || newrowstash->created) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_INCONSISTENTVALUE); continue; } break; case RS_CREATEANDGO: case RS_CREATEANDWAIT: /* * Can only operate on newly created rows. */ if (!(newrowstash && newrowstash->created)) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_INCONSISTENTVALUE); continue; } break; case RS_DESTROY: /* * Can operate on new or pre-existing rows. */ break; case RS_NOTREADY: default: /* * Not a valid value to Set */ netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGVALUE); continue; } } if (!data ) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOCREATION); continue; } /* * modify row and set new value */ SNMP_FREE(data->data.string); data->data.string = netsnmp_strdup_and_null(request->requestvb->val.string, request->requestvb->val_len); if (!data->data.string) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); } data->data_len = request->requestvb->val_len; if (datatable->rowstatus_column == table_info->colnum) { switch (*(request->requestvb->val.integer)) { case RS_CREATEANDGO: /* * XXX: check legality */ *(data->data.integer) = RS_ACTIVE; break; case RS_CREATEANDWAIT: /* * XXX: check legality */ *(data->data.integer) = RS_NOTINSERVICE; break; case RS_DESTROY: newrowstash->deleted = 1; break; } } break; case MODE_SET_ACTION: /* * Install the new row into the stored table. * Do this only *once* per row .... */ if (newrowstash->state != STATE_ACTION) { newrowstash->state = STATE_ACTION; if (newrowstash->created) { netsnmp_table_dataset_add_row(datatable, newrow); } else { netsnmp_table_dataset_replace_row(datatable, row, newrow); } } /* * ... but every (relevant) varbind in the request will * need to know about this new row, so update the * per-request row information regardless */ if (newrowstash->created) { netsnmp_request_add_list_data(request, netsnmp_create_data_list(TABLE_DATA_NAME, newrow, NULL)); } break; case MODE_SET_UNDO: /* * extract the new row, replace with the old or delete */ if (newrowstash->state != STATE_UNDO) { newrowstash->state = STATE_UNDO; if (newrowstash->created) { netsnmp_table_dataset_remove_and_delete_row(datatable, newrow); } else { netsnmp_table_dataset_replace_row(datatable, newrow, row); netsnmp_table_dataset_delete_row(newrow); } } break; case MODE_SET_COMMIT: if (newrowstash->state != STATE_COMMIT) { newrowstash->state = STATE_COMMIT; if (!newrowstash->created) { netsnmp_table_dataset_delete_row(row); } if (newrowstash->deleted) { netsnmp_table_dataset_remove_and_delete_row(datatable, newrow); } } break; case MODE_SET_FREE: if (newrowstash && newrowstash->state != STATE_FREE) { newrowstash->state = STATE_FREE; netsnmp_table_dataset_delete_row(newrow); } break; } } /* next handler called automatically - 'AUTO_NEXT' */ return SNMP_ERR_NOERROR; }
void get_status(unsigned int clientreg, void *clientarg) { /* * This function returns the value of the status variable. * If the value of status is 0 (no collection is running), it * calles collect_data to start one. * */ /* * Extract the cache from the passed argument. */ netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) clientarg; netsnmp_request_info *requests; netsnmp_agent_request_info *reqinfo; u_long *delay_time_cache = NULL; /* * Make sure the cache created earlier is still * valid. If not, the request timed out for some reason and we * do not need to keep processing things. Should never happen, but * this double checks. */ cache = netsnmp_handler_check_cache(cache); if (!cache) { snmp_log(LOG_ERR, "illegal call to return delayed response\n"); return; } /* * Re-establish the previous pointers, */ reqinfo = cache->reqinfo; requests = cache->requests; DEBUGMSGTL(("demo_module_10", "continuing delayed request, mode = %d\n", cache->reqinfo->mode)); /* * Set delegated to zero to indicate that the request is no longer * delegated and answer the query. */ requests->delegated = 0; switch (cache->reqinfo->mode) { /* * Registering as an instance means we do not need to deal with * GETNEXT processing, so we do not handle it here at all. * * However, since the instance handler already reset the mode * back to GETNEXT from the GET mode, we need to do the * same thing in both cases. * */ case MODE_GET: // no collection running, start one. if (status == 0) collect_data(); snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER, (u_char *) & status, sizeof (status) /* length in bytes */); break; case MODE_SET_RESERVE1: /* * check type */ if (requests->requestvb->type != ASN_INTEGER) { /* * If not an integer, return SNMP error. */ netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); /* * Free cache. It is no longer needed. */ netsnmp_free_delegated_cache(cache); return; } break; case MODE_SET_RESERVE2: /* * Store old value for UNDO support in the future. */ memdup((u_char **) & delay_time_cache, (u_char *) & delay_time, sizeof(delay_time)); /* * malloc failed */ if (delay_time_cache == NULL) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); netsnmp_free_delegated_cache(cache); return; } /* * Add our temporary information to the request itself. * This is then retrivable later. The free function * passed auto-frees it when the request is later * deleted. */ netsnmp_request_add_list_data(requests, netsnmp_create_data_list (DELAYED_INSTANCE_SET_NAME, delay_time_cache, free)); break; case MODE_SET_ACTION: // get status integer from request // if status == 0, start data collection, else return error if (*(requests->requestvb->val.integer) == 0) { status = *(requests->requestvb->val.integer); collect_data(); } else netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_WRONGTYPE); break; case MODE_SET_UNDO: /* * A failure occurred. Reset to the * previously value by extracting the previosuly * stored information from the request. */ delay_time = *((u_long *) netsnmp_request_get_list_data(requests, DELAYED_INSTANCE_SET_NAME)); break; case MODE_SET_COMMIT: break; case MODE_SET_FREE: /* * The only thing to do here is free the old memdup'ed * value, but it's auto-freed by the datalist recovery, so * we don't have anything to actually do here */ break; } /* * free the information cache */ netsnmp_free_delegated_cache(cache); }