1 #ifdef BRCM_SNMP_SUPPORT
2 #include <net-snmp/net-snmp-config.h>
10 #include <net-snmp/net-snmp-includes.h>
11 #include <net-snmp/agent/net-snmp-agent-includes.h>
13 #include <net-snmp/agent/table.h>
14 #include <net-snmp/agent/table_data.h>
15 #include <net-snmp/agent/read_only.h>
21 /** @defgroup table_data table_data: Helps you implement a table with datamatted storage.
24 * This helper helps you implement a table where all the indexes are
25 * expected to be stored within the agent itself and not in some
26 * external storage location. It can be used to store a list of
27 * rows, where a row consists of the indexes to the table and a
28 * generic data pointer. You can then implement a subhandler which
29 * is passed the exact row definition and data it must return data
30 * for or accept data for. Complex GETNEXT handling is greatly
31 * simplified in this case.
37 * generates the index portion of an table oid from a varlist.
40 netsnmp_table_data_generate_index_oid(netsnmp_table_row *row)
42 build_oid(&row->index_oid, &row->index_oid_len, NULL, 0, row->indexes);
46 * Adds a row of data to a given table (stored in proper lexographical order).
48 * returns SNMPERR_SUCCESS on successful addition.
49 * or SNMPERR_GENERR on failure (E.G., indexes already existed)
52 netsnmp_table_data_add_row(netsnmp_table_data *table,
53 netsnmp_table_row *row)
55 netsnmp_table_row *nextrow, *prevrow;
58 return SNMPERR_GENERR;
61 netsnmp_table_data_generate_index_oid(row);
64 * we don't store the index info as it
67 if (!table->store_indexes) {
68 snmp_free_varbind(row->indexes);
72 if (!row->index_oid) {
74 "illegal data attempted to be added to table %s\n",
76 return SNMPERR_GENERR;
80 * insert it into the table in the proper oid-lexographical order
82 for (nextrow = table->first_row, prevrow = NULL;
83 nextrow != NULL; prevrow = nextrow, nextrow = nextrow->next) {
84 if (nextrow->index_oid &&
85 snmp_oid_compare(nextrow->index_oid, nextrow->index_oid_len,
86 row->index_oid, row->index_oid_len) > 0)
88 if (nextrow->index_oid &&
89 snmp_oid_compare(nextrow->index_oid, nextrow->index_oid_len,
90 row->index_oid, row->index_oid_len) == 0) {
92 * exact match. Duplicate entries illegal
95 "duplicate table data attempted to be entered\n");
96 return SNMPERR_GENERR;
102 * ok, we have the location of where it should go
105 * (after prevrow, and before nextrow)
111 row->next->prev = row;
114 row->prev->next = row;
116 if (NULL == row->prev) /* it's the (new) first row */
117 table->first_row = row;
119 DEBUGMSGTL(("table_data_add_data", "added something...\n"));
121 return SNMPERR_SUCCESS;
125 * removes a row of data to a given table and returns it (no free's called)
127 * returns the row pointer itself on successful removing.
128 * or NULL on failure (bad arguments)
131 netsnmp_table_data_remove_row(netsnmp_table_data *table,
132 netsnmp_table_row *row)
138 row->prev->next = row->next;
140 table->first_row = row->next;
143 row->next->prev = row->prev;
148 /** deletes a row's memory.
149 * returns the void data that it doesn't know how to delete. */
151 netsnmp_table_data_delete_row(netsnmp_table_row *row)
159 * free the memory we can
162 snmp_free_varbind(row->indexes);
163 SNMP_FREE(row->index_oid);
168 * return the void * pointer
174 * removes and frees a row of data to a given table and returns the void *
176 * returns the void * data on successful deletion.
177 * or NULL on failure (bad arguments)
180 netsnmp_table_data_remove_and_delete_row(netsnmp_table_data *table,
181 netsnmp_table_row *row)
187 * remove it from the list
189 netsnmp_table_data_remove_row(table, row);
190 return netsnmp_table_data_delete_row(row);
193 /** swaps out origrow with newrow. This does *not* delete/free anything! */
195 netsnmp_table_data_replace_row(netsnmp_table_data *table,
196 netsnmp_table_row *origrow,
197 netsnmp_table_row *newrow)
199 netsnmp_table_data_remove_row(table, origrow);
200 netsnmp_table_data_add_row(table, newrow);
203 /** finds the data in "datalist" stored at "indexes" */
205 netsnmp_table_data_get(netsnmp_table_data *table,
206 netsnmp_variable_list * indexes)
208 oid searchfor[MAX_OID_LEN];
209 size_t searchfor_len = MAX_OID_LEN;
211 build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0,
213 return netsnmp_table_data_get_from_oid(table, searchfor,
217 /** finds the data in "datalist" stored at the searchfor oid */
219 netsnmp_table_data_get_from_oid(netsnmp_table_data *table,
220 oid * searchfor, size_t searchfor_len)
222 netsnmp_table_row *row;
226 for (row = table->first_row; row != NULL; row = row->next) {
227 if (row->index_oid &&
228 snmp_oid_compare(searchfor, searchfor_len,
229 row->index_oid, row->index_oid_len) == 0)
235 /** Creates a table_data handler and returns it */
236 netsnmp_mib_handler *
237 netsnmp_get_table_data_handler(netsnmp_table_data *table)
239 netsnmp_mib_handler *ret = NULL;
243 "netsnmp_get_table_data_handler(NULL) called\n");
248 netsnmp_create_handler(TABLE_DATA_NAME,
249 netsnmp_table_data_helper_handler);
251 ret->myvoid = (void *) table;
256 /** registers a handler as a data table.
257 * If table_info != NULL, it registers it as a normal table too. */
259 netsnmp_register_table_data(netsnmp_handler_registration *reginfo,
260 netsnmp_table_data *table,
261 netsnmp_table_registration_info *table_info)
263 netsnmp_inject_handler(reginfo, netsnmp_get_table_data_handler(table));
264 return netsnmp_register_table(reginfo, table_info);
267 /** registers a handler as a read-only data table
268 * If table_info != NULL, it registers it as a normal table too. */
270 netsnmp_register_read_only_table_data(netsnmp_handler_registration
271 *reginfo, netsnmp_table_data *table,
272 netsnmp_table_registration_info
275 netsnmp_inject_handler(reginfo, netsnmp_get_read_only_handler());
276 return netsnmp_register_table_data(reginfo, table, table_info);
281 * The helper handler that takes care of passing a specific row of
282 * data down to the lower handler(s). It sets request->processed if
283 * the request should not be handled.
286 netsnmp_table_data_helper_handler(netsnmp_mib_handler *handler,
287 netsnmp_handler_registration *reginfo,
288 netsnmp_agent_request_info *reqinfo,
289 netsnmp_request_info *requests)
292 netsnmp_table_data *table = (netsnmp_table_data *) handler->myvoid;
293 netsnmp_request_info *request;
294 int valid_request = 0;
295 netsnmp_table_row *row;
296 netsnmp_table_request_info *table_info;
297 netsnmp_table_registration_info *table_reg_info =
298 netsnmp_find_table_registration_info(reginfo);
299 int result, regresult;
302 for (request = requests; request; request = request->next) {
303 if (request->processed)
306 table_info = netsnmp_extract_table_info(request);
311 * find the row in question
313 switch (reqinfo->mode) {
315 case MODE_GETBULK: /* XXXWWW */
316 if (request->requestvb->type != ASN_NULL)
319 * loop through data till we find the next row
321 result = snmp_oid_compare(request->requestvb->name,
322 request->requestvb->name_length,
324 reginfo->rootoid_len);
325 regresult = snmp_oid_compare(request->requestvb->name,
326 SNMP_MIN(request->requestvb->
328 reginfo->rootoid_len),
330 reginfo->rootoid_len);
332 && request->requestvb->name_length < reginfo->rootoid_len)
335 if (result < 0 || 0 == result) {
337 * before us entirely, return the first
339 row = table->first_row;
340 table_info->colnum = table_reg_info->min_column;
341 } else if (regresult == 0 && request->requestvb->name_length ==
342 reginfo->rootoid_len + 1 &&
343 /* entry node must be 1, but any column is ok */
344 request->requestvb->name[reginfo->rootoid_len] == 1) {
346 * exactly to the entry
348 row = table->first_row;
349 table_info->colnum = table_reg_info->min_column;
350 } else if (regresult == 0 && request->requestvb->name_length ==
351 reginfo->rootoid_len + 2 &&
352 /* entry node must be 1, but any column is ok */
353 request->requestvb->name[reginfo->rootoid_len] == 1) {
355 * exactly to the column
357 row = table->first_row;
360 * loop through all rows looking for the first one
361 * that is equal to the request or greater than it
363 for (row = table->first_row; row; row = row->next) {
365 * compare the index of the request to the row
368 snmp_oid_compare(row->index_oid,
370 request->requestvb->name + 2 +
371 reginfo->rootoid_len,
372 request->requestvb->name_length -
373 2 - reginfo->rootoid_len);
376 * equal match, return the next row
382 } else if (result > 0) {
384 * the current row is greater than the
392 table_info->colnum++;
393 if (table_info->colnum <= table_reg_info->max_column) {
394 row = table->first_row;
399 netsnmp_request_add_list_data(request,
400 netsnmp_create_data_list
401 (TABLE_DATA_NAME, row,
404 * Set the name appropriately, so we can pass this
405 * request on as a simple GET request
407 netsnmp_table_data_build_result(reginfo, reqinfo, request,
411 } else { /* no decent result found. Give up. It's beyond us. */
412 request->processed = 1;
417 if (request->requestvb->type != ASN_NULL)
420 * find the row in question
422 if (request->requestvb->name_length < (reginfo->rootoid_len + 3)) { /* table.entry.column... */
426 netsnmp_set_request_error(reqinfo, request,
427 SNMP_NOSUCHINSTANCE);
431 netsnmp_table_data_get_from_oid(table,
445 netsnmp_set_request_error(reqinfo, request,
446 SNMP_NOSUCHINSTANCE);
450 netsnmp_request_add_list_data(request,
451 netsnmp_create_data_list
452 (TABLE_DATA_NAME, row,
457 case MODE_SET_RESERVE1:
461 netsnmp_table_data_get_from_oid(table,
462 request->requestvb->name +
463 reginfo->rootoid_len + 2,
466 reginfo->rootoid_len -
468 netsnmp_request_add_list_data(request,
469 netsnmp_create_data_list
470 (TABLE_DATA_NAME, row,
475 case MODE_SET_RESERVE2:
476 case MODE_SET_ACTION:
477 case MODE_SET_COMMIT:
487 * If this is a GetNext or GetBulk request, then we've identified
488 * the row that ought to include the appropriate next instance.
489 * Convert the request into a Get request, so that the lower-level
490 * handlers don't need to worry about skipping on....
492 oldmode = reqinfo->mode;
493 if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) {
494 reqinfo->mode = MODE_GET;
496 result = netsnmp_call_next_handler(handler, reginfo, reqinfo,
498 if (oldmode == MODE_GETNEXT || oldmode == MODE_GETBULK) { /* XXX */
499 for (request = requests; request; request = request->next) {
501 * ... but if the lower-level handlers aren't dealing with
502 * skipping on to the next instance, then we must handle
503 * this situation here.
504 * Mark 'holes' in the table as needing to be retried.
506 * This approach is less efficient than handling such
507 * holes directly in the table_dataset handler, but allows
508 * user-provided handlers to override the dataset handler
509 * if this proves necessary.
511 if (requests->requestvb->type == ASN_NULL ||
512 requests->requestvb->type == SNMP_NOSUCHINSTANCE) {
513 requests->requestvb->type = ASN_PRIV_RETRY;
516 reqinfo->mode = oldmode;
521 return SNMP_ERR_NOERROR;
524 /** creates and returns a pointer to table data set */
526 netsnmp_create_table_data(const char *name)
528 netsnmp_table_data *table = SNMP_MALLOC_TYPEDEF(netsnmp_table_data);
530 table->name = strdup(name);
534 /** creates and returns a pointer to table data set */
536 netsnmp_create_table_data_row(void)
538 netsnmp_table_row *row = SNMP_MALLOC_TYPEDEF(netsnmp_table_row);
542 /** extracts the row being accessed passed from the table_data helper */
544 netsnmp_extract_table_row(netsnmp_request_info *request)
546 return (netsnmp_table_row *) netsnmp_request_get_list_data(request,
550 /** extracts the data from the row being accessed passed from the
551 * table_data helper */
553 netsnmp_extract_table_row_data(netsnmp_request_info *request)
555 netsnmp_table_row *row;
556 row = (netsnmp_table_row *) netsnmp_extract_table_row(request);
563 /** builds a result given a row, a varbind to set and the data */
565 netsnmp_table_data_build_result(netsnmp_handler_registration *reginfo,
566 netsnmp_agent_request_info *reqinfo,
567 netsnmp_request_info *request,
568 netsnmp_table_row *row,
571 u_char * result_data,
572 size_t result_data_len)
574 oid build_space[MAX_OID_LEN];
576 if (!reginfo || !reqinfo || !request)
577 return SNMPERR_GENERR;
579 if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) {
581 * only need to do this for getnext type cases where oid is changing
583 memcpy(build_space, reginfo->rootoid, /* registered oid */
584 reginfo->rootoid_len * sizeof(oid));
585 build_space[reginfo->rootoid_len] = 1; /* entry */
586 build_space[reginfo->rootoid_len + 1] = column; /* column */
587 memcpy(build_space + reginfo->rootoid_len + 2, /* index data */
588 row->index_oid, row->index_oid_len * sizeof(oid));
589 snmp_set_var_objid(request->requestvb, build_space,
590 reginfo->rootoid_len + 2 + row->index_oid_len);
592 snmp_set_var_typed_value(request->requestvb, type,
593 result_data, result_data_len);
594 return SNMPERR_SUCCESS; /* WWWXXX: check for bounds */
597 /** clones a data row. DOES NOT CLONE THE CONTAINED DATA. */
599 netsnmp_table_data_clone_row(netsnmp_table_row *row)
601 netsnmp_table_row *newrow = NULL;
605 memdup((u_char **) & newrow, (u_char *) row,
606 sizeof(netsnmp_table_row));
611 newrow->indexes = snmp_clone_varbind(newrow->indexes);
612 if (!newrow->indexes)
616 if (row->index_oid) {
617 memdup((u_char **) & newrow->index_oid,
618 (u_char *) row->index_oid,
619 row->index_oid_len * sizeof(oid));
620 if (!newrow->index_oid)
631 #endif /* BRCM_SNMP_SUPPORT */