1 #ifdef BRCM_SNMP_NOT_USED
4 * $Id: table_array.c,v 5.18.2.1 2003/03/14 17:37:21 rstory Exp $
7 #include <net-snmp/net-snmp-config.h>
15 #include <net-snmp/net-snmp-includes.h>
16 #include <net-snmp/agent/net-snmp-agent-includes.h>
18 #include <net-snmp/agent/table.h>
19 #include <net-snmp/agent/table_array.h>
20 #include <net-snmp/library/container.h>
21 #include <net-snmp/library/snmp_assert.h>
28 * snmp.h:#define SNMP_MSG_INTERNAL_SET_BEGIN -1
29 * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE1 0
30 * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE2 1
31 * snmp.h:#define SNMP_MSG_INTERNAL_SET_ACTION 2
32 * snmp.h:#define SNMP_MSG_INTERNAL_SET_COMMIT 3
33 * snmp.h:#define SNMP_MSG_INTERNAL_SET_FREE 4
34 * snmp.h:#define SNMP_MSG_INTERNAL_SET_UNDO 5
37 static const char *mode_name[] = {
47 * PRIVATE structure for holding important info for each table.
49 typedef struct table_container_data_s {
51 /** registration info for the table */
52 netsnmp_table_registration_info *tblreg_info;
54 /** container for the table rows */
55 netsnmp_container *table;
61 /** do we want to group rows with the same index
62 * together when calling callbacks? */
65 /** callbacks for this table */
66 netsnmp_table_array_callbacks *cb;
68 } table_container_data;
70 /** @defgroup table_array table_array: Helps you implement a table when data can be stored locally. The data is stored in a sorted array, using a binary search for lookups.
73 * The table_array handler is used (automatically) in conjuntion
74 * with the @link table table@endlink handler. It is primarily
75 * intended to be used with the mib2c configuration file
76 * mib2c.array-user.conf.
78 * The code generated by mib2c is useful when you have control of
79 * the data for each row. If you cannot control when rows are added
80 * and deleted (or at least be notified of changes to row data),
81 * then this handler is probably not for you.
83 * This handler makes use of callbacks (function pointers) to
84 * handle various tasks. Code is generated for each callback,
85 * but will need to be reviewed and flushed out by the user.
87 * NOTE NOTE NOTE: Once place where mib2c is somewhat lacking
88 * is with regards to tables with external indices. If your
89 * table makes use of one or more external indices, please
90 * review the generated code very carefully for comments
91 * regarding external indices.
93 * NOTE NOTE NOTE: This helper, the API and callbacks are still
94 * being tested and may change.
96 * The generated code will define a structure for storage of table
97 * related data. This structure must be used, as it contains the index
98 * OID for the row, which is used for keeping the array sorted. You can
99 * add addition fields or data to the structure for your own use.
101 * The generated code will also have code to handle SNMP-SET processing.
102 * If your table does not support any SET operations, simply comment
103 * out the #define <PREFIX>_SET_HANDLING (where <PREFIX> is your
104 * table name) in the header file.
106 * SET processing modifies the row in-place. The duplicate_row
107 * callback will be called to save a copy of the original row.
108 * In the event of a failure before the commite phase, the
109 * row_copy callback will be called to restore the original row
112 * Code will be generated to handle row creation. This code may be
113 * disabled by commenting out the #define <PREFIX>_ROW_CREATION
114 * in the header file.
116 * If your table contains a RowStatus object, by default the
117 * code will not allow object in an active row to be modified.
118 * To allow active rows to be modified, remove the comment block
119 * around the #define <PREFIX>_CAN_MODIFY_ACTIVE_ROW in the header
122 * Code will be generated to maintain a secondary index for all
123 * rows, stored in a binary tree. This is very useful for finding
124 * rows by a key other than the OID index. By default, the functions
125 * for maintaining this tree will be based on a character string.
126 * NOTE: this will likely be made into a more generic mechanism,
127 * using new callback methods, in the near future.
129 * The generated code contains many TODO comments. Make sure you
130 * check each one to see if it applies to your code. Examples include
131 * checking indices for syntax (ranges, etc), initializing default
132 * values in newly created rows, checking for row activation and
133 * deactivation requirements, etc.
138 /**********************************************************************
139 **********************************************************************
142 * PUBLIC Registration functions *
145 **********************************************************************
146 **********************************************************************/
147 /** register specified callbacks for the specified table/oid. If the
148 group_rows parameter is set, the row related callbacks will be
149 called once for each unique row index. Otherwise, each callback
150 will be called only once, for all objects.
153 netsnmp_table_container_register(netsnmp_handler_registration *reginfo,
154 netsnmp_table_registration_info *tabreg,
155 netsnmp_table_array_callbacks *cb,
156 netsnmp_container *container,
159 table_container_data *tad = SNMP_MALLOC_TYPEDEF(table_container_data);
161 return SNMPERR_GENERR;
162 tad->tblreg_info = tabreg; /* we need it too, but it really is not ours */
165 snmp_log(LOG_ERR, "table_array registration with no callbacks\n" );
166 return SNMPERR_GENERR;
169 * check for required callbacks
172 ((NULL==cb->duplicate_row) || (NULL==cb->delete_row) ||
173 (NULL==cb->row_copy)) )) {
174 snmp_log(LOG_ERR, "table_array registration with incomplete "
175 "callback structure.\n");
176 return SNMPERR_GENERR;
180 tad->table = netsnmp_container_find("table_array");
182 tad->table = container;
183 if (NULL==container->compare)
184 container->compare = netsnmp_compare_netsnmp_index;
185 if (NULL==container->ncompare)
186 container->ncompare = netsnmp_ncompare_netsnmp_index;
190 reginfo->handler->myvoid = tad;
192 return netsnmp_register_table(reginfo, tabreg);
195 /** find the handler for the table_array helper. */
196 netsnmp_mib_handler *
197 netsnmp_find_table_array_handler(netsnmp_handler_registration *reginfo)
199 netsnmp_mib_handler *mh;
202 mh = reginfo->handler;
204 if (mh->access_method == netsnmp_table_array_helper_handler)
212 /** find the context data used by the table_array helper */
214 netsnmp_extract_array_context(netsnmp_request_info *request)
216 return netsnmp_request_get_list_data(request, TABLE_ARRAY_NAME);
219 /** this function is called to validate RowStatus transitions. */
221 netsnmp_table_array_check_row_status(netsnmp_table_array_callbacks *cb,
222 netsnmp_request_group *ag,
223 long *rs_new, long *rs_old)
225 netsnmp_index *row_ctx;
226 netsnmp_index *undo_ctx;
228 return SNMPERR_GENERR;
229 row_ctx = ag->existing_row;
230 undo_ctx = ag->undo_info;
233 * xxx-rks: revisit row delete scenario
237 * either a new row, or change to old row
240 * is it set to active?
242 if (RS_IS_GOING_ACTIVE(*rs_new)) {
244 * is it ready to be active?
246 if ((NULL==cb->can_activate) ||
247 cb->can_activate(undo_ctx, row_ctx, ag))
250 return SNMP_ERR_INCONSISTENTVALUE;
259 if (RS_IS_ACTIVE(*rs_old)) {
261 * check pre-reqs for deactivation
263 if (cb->can_deactivate &&
264 !cb->can_deactivate(undo_ctx, row_ctx, ag)) {
265 return SNMP_ERR_INCONSISTENTVALUE;
274 if (*rs_new != RS_DESTROY) {
275 if ((NULL==cb->can_activate) ||
276 cb->can_activate(undo_ctx, row_ctx, ag))
277 *rs_new = RS_NOTINSERVICE;
279 *rs_new = RS_NOTREADY;
281 if (cb->can_delete && !cb->can_delete(undo_ctx, row_ctx, ag)) {
282 return SNMP_ERR_INCONSISTENTVALUE;
289 * check pre-reqs for delete row
291 if (cb->can_delete && !cb->can_delete(undo_ctx, row_ctx, ag)) {
292 return SNMP_ERR_INCONSISTENTVALUE;
296 return SNMP_ERR_NOERROR;
301 #ifndef DOXYGEN_SHOULD_SKIP_THIS
302 /**********************************************************************
303 **********************************************************************
304 **********************************************************************
305 **********************************************************************
310 * EVERYTHING BELOW THIS IS PRIVATE IMPLEMENTATION DETAILS. *
315 **********************************************************************
316 **********************************************************************
317 **********************************************************************
318 **********************************************************************/
320 /**********************************************************************
321 **********************************************************************
324 * Structures, Utility/convenience functions *
327 **********************************************************************
328 **********************************************************************/
330 * context info for SET requests
332 typedef struct set_context_s {
333 netsnmp_agent_request_info *agtreq_info;
334 table_container_data *tad;
339 release_netsnmp_request_group(netsnmp_index *g, void *v)
341 netsnmp_request_group_item *tmp;
342 netsnmp_request_group *group = (netsnmp_request_group *) g;
346 while (group->list) {
348 group->list = tmp->next;
356 release_netsnmp_request_groups(void *vp)
358 netsnmp_container *c = (netsnmp_container*)vp;
359 CONTAINER_FOR_EACH(c, (netsnmp_container_obj_func*)
360 release_netsnmp_request_group, NULL);
364 NETSNMP_INLINE netsnmp_index *
365 find_next_row(netsnmp_table_request_info *tblreq_info,
366 table_container_data * tad)
368 netsnmp_index *row = NULL;
371 if (!tblreq_info || !tad)
375 * below our minimum column?
377 if (tblreq_info->colnum < tad->tblreg_info->min_column) {
378 tblreq_info->colnum = tad->tblreg_info->min_column;
379 row = CONTAINER_FIRST(tad->table);
381 index.oids = tblreq_info->index_oid;
382 index.len = tblreq_info->index_oid_len;
384 row = CONTAINER_NEXT(tad->table, &index);
387 * we don't have a row, but we might be at the end of a
388 * column, so try the next one.
391 ++tblreq_info->colnum;
392 if (tad->tblreg_info->valid_columns) {
393 tblreq_info->colnum = netsnmp_closest_column
394 (tblreq_info->colnum, tad->tblreg_info->valid_columns);
395 } else if (tblreq_info->colnum > tad->tblreg_info->max_column)
396 tblreq_info->colnum = 0;
398 if (tblreq_info->colnum != 0)
399 row = CONTAINER_FIRST(tad->table);
407 build_new_oid(netsnmp_handler_registration *reginfo,
408 netsnmp_table_request_info *tblreq_info,
409 netsnmp_index *row, netsnmp_request_info *current)
411 oid coloid[MAX_OID_LEN];
414 if (!tblreq_info || !reginfo || !row || !current)
417 coloid_len = reginfo->rootoid_len + 2;
418 memcpy(coloid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid));
421 coloid[reginfo->rootoid_len] = 1;
423 /** table.entry.column */
424 coloid[reginfo->rootoid_len + 1] = tblreq_info->colnum;
426 /** table.entry.column.index */
427 memcpy(&coloid[reginfo->rootoid_len + 2], row->oids,
428 row->len * sizeof(oid));
430 snmp_set_var_objid(current->requestvb, coloid,
431 reginfo->rootoid_len + 2 + row->len);
434 /**********************************************************************
435 **********************************************************************
438 * GET procession functions *
441 **********************************************************************
442 **********************************************************************/
444 process_get_requests(netsnmp_handler_registration *reginfo,
445 netsnmp_agent_request_info *agtreq_info,
446 netsnmp_request_info *requests,
447 table_container_data * tad)
449 int rc = SNMP_ERR_NOERROR;
450 netsnmp_request_info *current;
451 netsnmp_index *row = NULL;
452 netsnmp_table_request_info *tblreq_info;
453 netsnmp_variable_list *var;
456 * Loop through each of the requests, and
457 * try to find the appropriate row from the container.
459 for (current = requests; current; current = current->next) {
461 var = current->requestvb;
462 DEBUGMSGTL(("table_array:get",
463 " process_get_request oid:"));
464 DEBUGMSGOID(("table_array:get", var->name,
466 DEBUGMSG(("table_array:get", "\n"));
469 * skip anything that doesn't need processing.
471 if (current->processed != 0) {
472 DEBUGMSGTL(("table_array:get", "already processed\n"));
477 * Get pointer to the table information for this request. This
478 * information was saved by table_helper_handler. When
479 * debugging, we double check a few assumptions. For example,
480 * the table_helper_handler should enforce column boundaries.
482 tblreq_info = netsnmp_extract_table_info(current);
483 netsnmp_assert(tblreq_info->colnum <= tad->tblreg_info->max_column);
485 if ((agtreq_info->mode == MODE_GETNEXT) ||
486 (agtreq_info->mode == MODE_GETBULK)) {
490 row = find_next_row(tblreq_info, tad);
495 * xxx-rks: how do we skip this entry for the next handler,
496 * but still allow it a chance to hit another handler?
498 DEBUGMSGTL(("table_array:get", "no row found\n"));
503 * * if data was found, make sure it has the column we want
505 /* xxx-rks: add suport for sparse tables */
510 build_new_oid(reginfo, tblreq_info, row, current);
512 } /** GETNEXT/GETBULK */
515 index.oids = tblreq_info->index_oid;
516 index.len = tblreq_info->index_oid_len;
518 row = CONTAINER_FIND(tad->table, &index);
520 DEBUGMSGTL(("table_array:get", "no row found\n"));
521 netsnmp_set_request_error(agtreq_info, current,
522 SNMP_NOSUCHINSTANCE);
530 rc = tad->cb->get_value(current, row, tblreq_info);
532 } /** for ( ... requests ... ) */
537 /**********************************************************************
538 **********************************************************************
541 * SET procession functions *
544 **********************************************************************
545 **********************************************************************/
548 group_requests(netsnmp_agent_request_info *agtreq_info,
549 netsnmp_request_info *requests,
550 netsnmp_container *request_group, table_container_data * tad)
552 netsnmp_table_request_info *tblreq_info;
553 netsnmp_variable_list *var;
554 netsnmp_index *row, *tmp, index;
555 netsnmp_request_info *current;
556 netsnmp_request_group *g;
557 netsnmp_request_group_item *i;
559 for (current = requests; current; current = current->next) {
561 var = current->requestvb;
564 * skip anything that doesn't need processing.
566 if (current->processed != 0) {
567 DEBUGMSGTL(("table_array:group",
568 "already processed\n"));
573 * 3.2.1 Setup and paranoia
575 * * Get pointer to the table information for this request. This
576 * * information was saved by table_helper_handler. When
577 * * debugging, we double check a few assumptions. For example,
578 * * the table_helper_handler should enforce column boundaries.
581 tblreq_info = netsnmp_extract_table_info(current);
582 netsnmp_assert(tblreq_info->colnum <= tad->tblreg_info->max_column);
587 index.oids = tblreq_info->index_oid;
588 index.len = tblreq_info->index_oid_len;
589 tmp = CONTAINER_FIND(request_group, &index);
591 DEBUGMSGTL(("table_array:group",
592 " existing group:"));
593 DEBUGMSGOID(("table_array:group", index.oids,
595 DEBUGMSG(("table_array:group", "\n"));
596 g = (netsnmp_request_group *) tmp;
597 i = SNMP_MALLOC_TYPEDEF(netsnmp_request_group_item);
599 i->tri = tblreq_info;
603 /** xxx-rks: store map of colnum to request */
607 DEBUGMSGTL(("table_array:group", " new group"));
608 DEBUGMSGOID(("table_array:group", index.oids,
610 DEBUGMSG(("table_array:group", "\n"));
611 g = SNMP_MALLOC_TYPEDEF(netsnmp_request_group);
612 i = SNMP_MALLOC_TYPEDEF(netsnmp_request_group_item);
614 g->table = tad->table;
616 i->tri = tblreq_info;
617 /** xxx-rks: store map of colnum to request */
620 * search for row. all changes are made to the original row,
621 * later, we'll make a copy in undo_info before we start processing.
623 row = g->existing_row = CONTAINER_FIND(tad->table, &index);
624 if (!g->existing_row) {
625 if (!tad->cb->create_row) {
626 if(MODE_IS_SET(agtreq_info->mode))
627 netsnmp_set_request_error(agtreq_info, current,
628 SNMP_ERR_NOTWRITABLE);
630 netsnmp_set_request_error(agtreq_info, current,
631 SNMP_NOSUCHINSTANCE);
636 /** use undo_info temporarily */
637 row = g->existing_row = tad->cb->create_row(&index);
639 /* xxx-rks : parameter to create_row to allow
640 * for better error reporting. */
641 netsnmp_set_request_error(agtreq_info, current,
650 g->index.oids = row->oids;
651 g->index.len = row->len;
653 CONTAINER_INSERT(request_group, g);
655 } /** for( current ... ) */
658 static NETSNMP_INLINE void
659 process_set_group(netsnmp_index *o, void *c)
661 /* xxx-rks: should we continue processing after an error?? */
662 set_context *context = (set_context *) c;
663 netsnmp_request_group *ag = (netsnmp_request_group *) o;
664 int rc = SNMP_ERR_NOERROR;
666 switch (context->agtreq_info->mode) {
668 case MODE_SET_RESERVE1:/** -> SET_RESERVE2 || SET_FREE */
671 * if not a new row, save undo info
673 if (ag->row_created == 0) {
674 ag->undo_info = context->tad->cb->duplicate_row(ag->existing_row);
675 if (NULL == ag->undo_info) {
676 rc = SNMP_ERR_RESOURCEUNAVAILABLE;
681 if (context->tad->cb->set_reserve1)
682 context->tad->cb->set_reserve1(ag);
685 case MODE_SET_RESERVE2:/** -> SET_ACTION || SET_FREE */
686 if (context->tad->cb->set_reserve2)
687 context->tad->cb->set_reserve2(ag);
690 case MODE_SET_ACTION:/** -> SET_COMMIT || SET_UNDO */
691 if (context->tad->cb->set_action)
692 context->tad->cb->set_action(ag);
695 case MODE_SET_COMMIT:/** FINAL CHANCE ON SUCCESS */
696 if (ag->row_created == 0) {
698 * this is an existing row, has it been deleted?
700 if (ag->row_deleted == 1) {
701 DEBUGMSGT((TABLE_ARRAY_NAME, "action: deleting row\n"));
702 if (CONTAINER_REMOVE(ag->table, ag->existing_row) != 0) {
703 rc = SNMP_ERR_COMMITFAILED;
707 } else if (ag->row_deleted == 0) {
709 * new row (that hasn't been deleted) should be inserted
711 DEBUGMSGT((TABLE_ARRAY_NAME, "action: inserting row\n"));
712 if (CONTAINER_INSERT(ag->table, ag->existing_row) != 0) {
713 rc = SNMP_ERR_COMMITFAILED;
718 if (context->tad->cb->set_commit)
719 context->tad->cb->set_commit(ag);
721 /** no more use for undo_info, so free it */
723 context->tad->cb->delete_row(ag->undo_info);
724 ag->undo_info = NULL;
728 /* XXX-rks: finish row cooperative notifications
729 * if the table has requested it, send cooperative notifications
730 * for row operations.
732 if (context->tad->notifications) {
734 if (!ag->existing_row)
735 netsnmp_monitor_notify(EVENT_ROW_DEL);
737 netsnmp_monitor_notify(EVENT_ROW_MOD);
740 netsnmp_monitor_notify(EVENT_ROW_ADD);
745 case MODE_SET_FREE:/** FINAL CHANCE ON FAILURE */
746 if (context->tad->cb->set_free)
747 context->tad->cb->set_free(ag);
749 /** no more use for undo_info, so free it */
751 context->tad->cb->delete_row(ag->undo_info);
752 ag->undo_info = NULL;
756 case MODE_SET_UNDO:/** FINAL CHANCE ON FAILURE */
757 if (ag->row_created == 0) {
759 * this row existed before.
761 if (ag->row_deleted == 1) {
763 * re-insert undo_info
765 DEBUGMSGT((TABLE_ARRAY_NAME, "undo: re-inserting row\n"));
766 if (CONTAINER_INSERT(ag->table, ag->existing_row) != 0) {
767 rc = SNMP_ERR_UNDOFAILED;
771 } else if (ag->row_deleted == 0) {
773 * new row that wasn't deleted should be removed
775 DEBUGMSGT((TABLE_ARRAY_NAME, "undo: removing new row\n"));
776 if (CONTAINER_REMOVE(ag->table, ag->existing_row) != 0) {
777 rc = SNMP_ERR_UNDOFAILED;
783 * status already set - don't change it now
785 if (context->tad->cb->set_undo)
786 context->tad->cb->set_undo(ag);
789 * no more use for undo_info, so free it
791 if (ag->row_created == 0) {
795 context->tad->cb->row_copy(ag->existing_row, ag->undo_info);
796 context->tad->cb->delete_row(ag->undo_info);
797 ag->undo_info = NULL;
800 context->tad->cb->delete_row(ag->existing_row);
801 ag->existing_row = NULL;
806 snmp_log(LOG_ERR, "unknown mode processing SET for "
807 "netsnmp_table_array_helper_handler\n");
808 rc = SNMP_ERR_GENERR;
813 netsnmp_set_request_error(context->agtreq_info,
819 process_set_requests(netsnmp_agent_request_info *agtreq_info,
820 netsnmp_request_info *requests,
821 table_container_data * tad, char *handler_name)
824 netsnmp_container *request_group;
827 * create and save structure for set info
829 request_group = (netsnmp_container*) netsnmp_agent_get_list_data
830 (agtreq_info, handler_name);
831 if (request_group == NULL) {
832 netsnmp_data_list *tmp;
833 request_group = netsnmp_container_find("request_group:"
835 request_group->compare = netsnmp_compare_netsnmp_index;
836 request_group->ncompare = netsnmp_ncompare_netsnmp_index;
838 DEBUGMSGTL(("table_array", "Grouping requests by oid\n"));
840 tmp = netsnmp_create_data_list(handler_name,
842 release_netsnmp_request_groups);
843 netsnmp_agent_add_list_data(agtreq_info, tmp);
847 group_requests(agtreq_info, requests, request_group, tad);
851 * process each group one at a time
853 context.agtreq_info = agtreq_info;
855 context.status = SNMP_ERR_NOERROR;
856 CONTAINER_FOR_EACH(request_group,
857 (netsnmp_container_obj_func*)process_set_group,
860 return context.status;
864 /**********************************************************************
865 **********************************************************************
868 * netsnmp_table_array_helper_handler() *
871 **********************************************************************
872 **********************************************************************/
874 netsnmp_table_array_helper_handler(netsnmp_mib_handler *handler,
875 netsnmp_handler_registration *reginfo,
876 netsnmp_agent_request_info *agtreq_info,
877 netsnmp_request_info *requests)
881 * First off, get our pointer from the handler. This
882 * lets us get to the table registration information we
883 * saved in get_table_array_handler(), as well as the
884 * container where the actual table data is stored.
886 int rc = SNMP_ERR_NOERROR;
887 table_container_data *tad = (table_container_data *)handler->myvoid;
889 if (agtreq_info->mode < 0 || agtreq_info->mode > 5) {
890 DEBUGMSGTL(("table_array", "Mode %d, Got request:\n",
893 DEBUGMSGTL(("table_array", "Mode %s, Got request:\n",
894 mode_name[agtreq_info->mode]));
897 if (MODE_IS_SET(agtreq_info->mode)) {
899 * netsnmp_mutex_lock(&tad->lock);
901 rc = process_set_requests(agtreq_info, requests,
902 tad, handler->handler_name);
904 * netsnmp_mutex_unlock(&tad->lock);
907 rc = process_get_requests(reginfo, agtreq_info, requests, tad);
909 if (rc != SNMP_ERR_NOERROR) {
910 DEBUGMSGTL(("table_array", "processing returned rc %d\n", rc));
914 * Now we've done out processing. If there is another handler below us,
918 rc = netsnmp_call_next_handler(handler, reginfo, agtreq_info, requests);
919 if (rc != SNMP_ERR_NOERROR) {
920 DEBUGMSGTL(("table_array", "next handler returned rc %d\n", rc));
926 #endif /** DOXYGEN_SHOULD_SKIP_THIS */
928 #endif /* #if BRCM_SNMP_NOT_USED */