net-snmp 5.7
table_array.c
00001 /*
00002  * table_array.c
00003  * $Id$
00004  */
00005 
00006 #include <net-snmp/net-snmp-config.h>
00007 #include <net-snmp/net-snmp-features.h>
00008 
00009 #include <net-snmp/net-snmp-includes.h>
00010 #include <net-snmp/agent/net-snmp-agent-includes.h>
00011 
00012 #include <net-snmp/agent/table_array.h>
00013 
00014 #if HAVE_STRING_H
00015 #include <string.h>
00016 #else
00017 #include <strings.h>
00018 #endif
00019 
00020 #include <net-snmp/agent/table.h>
00021 #include <net-snmp/library/container.h>
00022 #include <net-snmp/library/snmp_assert.h>
00023 
00024 netsnmp_feature_child_of(table_array_all, mib_helpers)
00025 
00026 netsnmp_feature_child_of(table_array_register,table_array_all)
00027 netsnmp_feature_child_of(table_array_find_table_array_handler,table_array_all)
00028 netsnmp_feature_child_of(table_array_extract_array_context,table_array_all)
00029 netsnmp_feature_child_of(table_array_check_row_status,table_array_all)
00030 
00031 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_CONTAINER
00032 
00033 /*
00034  * snmp.h:#define SNMP_MSG_INTERNAL_SET_BEGIN        -1 
00035  * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE1     0 
00036  * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE2     1 
00037  * snmp.h:#define SNMP_MSG_INTERNAL_SET_ACTION       2 
00038  * snmp.h:#define SNMP_MSG_INTERNAL_SET_COMMIT       3 
00039  * snmp.h:#define SNMP_MSG_INTERNAL_SET_FREE         4 
00040  * snmp.h:#define SNMP_MSG_INTERNAL_SET_UNDO         5 
00041  */
00042 
00043 static const char *mode_name[] = {
00044     "Reserve 1",
00045     "Reserve 2",
00046     "Action",
00047     "Commit",
00048     "Free",
00049     "Undo"
00050 };
00051 
00052 /*
00053  * PRIVATE structure for holding important info for each table.
00054  */
00055 typedef struct table_container_data_s {
00056 
00058     netsnmp_table_registration_info *tblreg_info;
00059 
00061    netsnmp_container          *table;
00062 
00063     /*
00064      * mutex_type                lock;
00065      */
00066 
00069     int             group_rows;
00070 
00072     netsnmp_table_array_callbacks *cb;
00073 
00074 } table_container_data;
00075 
00145 /**********************************************************************
00146  **********************************************************************
00147  *                                                                    *
00148  *                                                                    *
00149  * PUBLIC Registration functions                                      *
00150  *                                                                    *
00151  *                                                                    *
00152  **********************************************************************
00153  **********************************************************************/
00159 int
00160 netsnmp_table_container_register(netsnmp_handler_registration *reginfo,
00161                              netsnmp_table_registration_info *tabreg,
00162                              netsnmp_table_array_callbacks *cb,
00163                              netsnmp_container *container,
00164                              int group_rows)
00165 {
00166     table_container_data *tad = SNMP_MALLOC_TYPEDEF(table_container_data);
00167     if (!tad)
00168         return SNMPERR_GENERR;
00169     tad->tblreg_info = tabreg;  /* we need it too, but it really is not ours */
00170 
00171     if (!cb) {
00172         snmp_log(LOG_ERR, "table_array registration with no callbacks\n" );
00173         free(tad); /* SNMP_FREE is overkill for local var */
00174         return SNMPERR_GENERR;
00175     }
00176     /*
00177      * check for required callbacks
00178      */
00179     if ((cb->can_set &&
00180          ((NULL==cb->duplicate_row) || (NULL==cb->delete_row) ||
00181           (NULL==cb->row_copy)) )) {
00182         snmp_log(LOG_ERR, "table_array registration with incomplete "
00183                  "callback structure.\n");
00184         free(tad); /* SNMP_FREE is overkill for local var */
00185         return SNMPERR_GENERR;
00186     }
00187 
00188     if (NULL==container) {
00189         tad->table = netsnmp_container_find("table_array");
00190         snmp_log(LOG_ERR, "table_array couldn't allocate container\n" );
00191         free(tad); /* SNMP_FREE is overkill for local var */
00192         return SNMPERR_GENERR;
00193     } else
00194         tad->table = container;
00195     if (NULL==tad->table->compare)
00196         tad->table->compare = netsnmp_compare_netsnmp_index;
00197     if (NULL==tad->table->ncompare)
00198         tad->table->ncompare = netsnmp_ncompare_netsnmp_index;
00199     
00200     tad->cb = cb;
00201 
00202     reginfo->handler->myvoid = tad;
00203 
00204     return netsnmp_register_table(reginfo, tabreg);
00205 }
00206 
00207 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_REGISTER
00208 int
00209 netsnmp_table_array_register(netsnmp_handler_registration *reginfo,
00210                              netsnmp_table_registration_info *tabreg,
00211                              netsnmp_table_array_callbacks *cb,
00212                              netsnmp_container *container,
00213                              int group_rows)
00214 {
00215     netsnmp_inject_handler(reginfo,
00216                            netsnmp_create_handler(reginfo->handlerName,
00217                                netsnmp_table_array_helper_handler));
00218     return netsnmp_table_container_register(reginfo, tabreg, cb,
00219                                             container, group_rows);
00220 }
00221 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_REGISTER */
00222 
00224 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_FIND_TABLE_ARRAY_HANDLER
00225 netsnmp_mib_handler *
00226 netsnmp_find_table_array_handler(netsnmp_handler_registration *reginfo)
00227 {
00228     netsnmp_mib_handler *mh;
00229     if (!reginfo)
00230         return NULL;
00231     mh = reginfo->handler;
00232     while (mh) {
00233         if (mh->access_method == netsnmp_table_array_helper_handler)
00234             break;
00235         mh = mh->next;
00236     }
00237 
00238     return mh;
00239 }
00240 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_FIND_TABLE_ARRAY_HANDLER */
00241 
00243 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_EXTRACT_ARRAY_CONTEXT
00244 netsnmp_container      *
00245 netsnmp_extract_array_context(netsnmp_request_info *request)
00246 {
00247     return (netsnmp_container*)netsnmp_request_get_list_data(request, TABLE_ARRAY_NAME);
00248 }
00249 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_EXTRACT_ARRAY_CONTEXT */
00250 
00252 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_CHECK_ROW_STATUS
00253 int
00254 netsnmp_table_array_check_row_status(netsnmp_table_array_callbacks *cb,
00255                                      netsnmp_request_group *ag,
00256                                      long *rs_new, long *rs_old)
00257 {
00258     netsnmp_index *row_ctx;
00259     netsnmp_index *undo_ctx;
00260     if (!ag || !cb)
00261         return SNMPERR_GENERR;
00262     row_ctx  = ag->existing_row;
00263     undo_ctx = ag->undo_info;
00264     
00265     /*
00266      * xxx-rks: revisit row delete scenario
00267      */
00268     if (row_ctx) {
00269         /*
00270          * either a new row, or change to old row
00271          */
00272         /*
00273          * is it set to active?
00274          */
00275         if (RS_IS_GOING_ACTIVE(*rs_new)) {
00276             /*
00277              * is it ready to be active?
00278              */
00279             if ((NULL==cb->can_activate) ||
00280                 cb->can_activate(undo_ctx, row_ctx, ag))
00281                 *rs_new = RS_ACTIVE;
00282             else
00283                 return SNMP_ERR_INCONSISTENTVALUE;
00284         } else {
00285             /*
00286              * not going active
00287              */
00288             if (undo_ctx) {
00289                 /*
00290                  * change
00291                  */
00292                 if (RS_IS_ACTIVE(*rs_old)) {
00293                     /*
00294                      * check pre-reqs for deactivation
00295                      */
00296                     if (cb->can_deactivate &&
00297                         !cb->can_deactivate(undo_ctx, row_ctx, ag)) {
00298                         return SNMP_ERR_INCONSISTENTVALUE;
00299                     }
00300                 }
00301             } else {
00302                 /*
00303                  * new row
00304                  */
00305             }
00306 
00307             if (*rs_new != RS_DESTROY) {
00308                 if ((NULL==cb->can_activate) ||
00309                     cb->can_activate(undo_ctx, row_ctx, ag))
00310                     *rs_new = RS_NOTINSERVICE;
00311                 else
00312                     *rs_new = RS_NOTREADY;
00313             } else {
00314                 if (cb->can_delete && !cb->can_delete(undo_ctx, row_ctx, ag)) {
00315                     return SNMP_ERR_INCONSISTENTVALUE;
00316                 }
00317                 ag->row_deleted = 1;
00318             }
00319         }
00320     } else {
00321         /*
00322          * check pre-reqs for delete row
00323          */
00324         if (cb->can_delete && !cb->can_delete(undo_ctx, row_ctx, ag)) {
00325             return SNMP_ERR_INCONSISTENTVALUE;
00326         }
00327     }
00328 
00329     return SNMP_ERR_NOERROR;
00330 }
00331 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_ARRAY_CHECK_ROW_STATUS */
00332 
00336 /**********************************************************************
00337  **********************************************************************
00338  **********************************************************************
00339  **********************************************************************
00340  *                                                                    *
00341  *                                                                    *
00342  *                                                                    *
00343  *                                                                    *
00344  * EVERYTHING BELOW THIS IS PRIVATE IMPLEMENTATION DETAILS.           *
00345  *                                                                    *
00346  *                                                                    *
00347  *                                                                    *
00348  *                                                                    *
00349  **********************************************************************
00350  **********************************************************************
00351  **********************************************************************
00352  **********************************************************************/
00353 
00354 /**********************************************************************
00355  **********************************************************************
00356  *                                                                    *
00357  *                                                                    *
00358  * Structures, Utility/convenience functions                          *
00359  *                                                                    *
00360  *                                                                    *
00361  **********************************************************************
00362  **********************************************************************/
00363 /*
00364  * context info for SET requests
00365  */
00366 #ifndef NETSNMP_NO_WRITE_SUPPORT
00367 typedef struct set_context_s {
00368     netsnmp_agent_request_info *agtreq_info;
00369     table_container_data *tad;
00370     int             status;
00371 } set_context;
00372 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00373 
00374 void
00375 build_new_oid(netsnmp_handler_registration *reginfo,
00376               netsnmp_table_request_info *tblreq_info,
00377               netsnmp_index *row, netsnmp_request_info *current)
00378 {
00379     oid             coloid[MAX_OID_LEN];
00380     int             coloid_len;
00381 
00382     if (!tblreq_info || !reginfo || !row || !current)
00383         return;
00384 
00385     coloid_len = reginfo->rootoid_len + 2;
00386     memcpy(coloid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid));
00387 
00389     coloid[reginfo->rootoid_len] = 1;
00390 
00392     coloid[reginfo->rootoid_len + 1] = tblreq_info->colnum;
00393 
00395     memcpy(&coloid[reginfo->rootoid_len + 2], row->oids,
00396            row->len * sizeof(oid));
00397 
00398     snmp_set_var_objid(current->requestvb, coloid,
00399                        reginfo->rootoid_len + 2 + row->len);
00400 }
00401 
00402 /**********************************************************************
00403  **********************************************************************
00404  *                                                                    *
00405  *                                                                    *
00406  * GET procession functions                                           *
00407  *                                                                    *
00408  *                                                                    *
00409  **********************************************************************
00410  **********************************************************************/
00411 int
00412 process_get_requests(netsnmp_handler_registration *reginfo,
00413                      netsnmp_agent_request_info *agtreq_info,
00414                      netsnmp_request_info *requests,
00415                      table_container_data * tad)
00416 {
00417     int             rc = SNMP_ERR_NOERROR;
00418     netsnmp_request_info *current;
00419     netsnmp_index *row = NULL;
00420     netsnmp_table_request_info *tblreq_info;
00421     netsnmp_variable_list *var;
00422 
00423     /*
00424      * Loop through each of the requests, and
00425      * try to find the appropriate row from the container.
00426      */
00427     for (current = requests; current; current = current->next) {
00428 
00429         var = current->requestvb;
00430         DEBUGMSGTL(("table_array:get",
00431                     "  process_get_request oid:"));
00432         DEBUGMSGOID(("table_array:get", var->name,
00433                      var->name_length));
00434         DEBUGMSG(("table_array:get", "\n"));
00435 
00436         /*
00437          * skip anything that doesn't need processing.
00438          */
00439         if (current->processed != 0) {
00440             DEBUGMSGTL(("table_array:get", "already processed\n"));
00441             continue;
00442         }
00443 
00444         /*
00445          * Get pointer to the table information for this request. This
00446          * information was saved by table_helper_handler. When
00447          * debugging, we double check a few assumptions. For example,
00448          * the table_helper_handler should enforce column boundaries.
00449          */
00450         tblreq_info = netsnmp_extract_table_info(current);
00451         netsnmp_assert(tblreq_info->colnum <= tad->tblreg_info->max_column);
00452 
00453         if ((agtreq_info->mode == MODE_GETNEXT) ||
00454             (agtreq_info->mode == MODE_GETBULK)) {
00455             /*
00456              * find the row
00457              */
00458             row = netsnmp_table_index_find_next_row(tad->table, tblreq_info);
00459             if (!row) {
00460                 /*
00461                  * no results found.
00462                  *
00463                  * xxx-rks: how do we skip this entry for the next handler,
00464                  * but still allow it a chance to hit another handler?
00465                  */
00466                 DEBUGMSGTL(("table_array:get", "no row found\n"));
00467                 netsnmp_set_request_error(agtreq_info, current,
00468                                           SNMP_ENDOFMIBVIEW);
00469                 continue;
00470             }
00471 
00472             /*
00473              * * if data was found, make sure it has the column we want
00474              */
00475 /* xxx-rks: add suport for sparse tables */
00476 
00477             /*
00478              * build new oid
00479              */
00480             build_new_oid(reginfo, tblreq_info, row, current);
00481 
00482         } 
00483         else {
00484             netsnmp_index index;
00485             index.oids = tblreq_info->index_oid;
00486             index.len = tblreq_info->index_oid_len;
00487 
00488             row = (netsnmp_index*)CONTAINER_FIND(tad->table, &index);
00489             if (!row) {
00490                 DEBUGMSGTL(("table_array:get", "no row found\n"));
00491                 netsnmp_set_request_error(agtreq_info, current,
00492                                           SNMP_NOSUCHINSTANCE);
00493                 continue;
00494             }
00495         } 
00497         /*
00498          * get the data
00499          */
00500         rc = tad->cb->get_value(current, row, tblreq_info);
00501 
00502     } 
00504     return rc;
00505 }
00506 
00507 /**********************************************************************
00508  **********************************************************************
00509  *                                                                    *
00510  *                                                                    *
00511  * SET procession functions                                           *
00512  *                                                                    *
00513  *                                                                    *
00514  **********************************************************************
00515  **********************************************************************/
00516 
00517 void
00518 group_requests(netsnmp_agent_request_info *agtreq_info,
00519                netsnmp_request_info *requests,
00520                netsnmp_container *request_group, table_container_data * tad)
00521 {
00522     netsnmp_table_request_info *tblreq_info;
00523     netsnmp_variable_list *var;
00524     netsnmp_index *row, *tmp, index;
00525     netsnmp_request_info *current;
00526     netsnmp_request_group *g;
00527     netsnmp_request_group_item *i;
00528 
00529     for (current = requests; current; current = current->next) {
00530 
00531         var = current->requestvb;
00532 
00533         /*
00534          * skip anything that doesn't need processing.
00535          */
00536         if (current->processed != 0) {
00537             DEBUGMSGTL(("table_array:group",
00538                         "already processed\n"));
00539             continue;
00540         }
00541 
00542         /*
00543          * 3.2.1 Setup and paranoia
00544          * *
00545          * * Get pointer to the table information for this request. This
00546          * * information was saved by table_helper_handler. When
00547          * * debugging, we double check a few assumptions. For example,
00548          * * the table_helper_handler should enforce column boundaries.
00549          */
00550         row = NULL;
00551         tblreq_info = netsnmp_extract_table_info(current);
00552         netsnmp_assert(tblreq_info->colnum <= tad->tblreg_info->max_column);
00553 
00554         /*
00555          * search for index
00556          */
00557         index.oids = tblreq_info->index_oid;
00558         index.len = tblreq_info->index_oid_len;
00559         tmp = (netsnmp_index*)CONTAINER_FIND(request_group, &index);
00560         if (tmp) {
00561             DEBUGMSGTL(("table_array:group",
00562                         "    existing group:"));
00563             DEBUGMSGOID(("table_array:group", index.oids,
00564                          index.len));
00565             DEBUGMSG(("table_array:group", "\n"));
00566             g = (netsnmp_request_group *) tmp;
00567             i = SNMP_MALLOC_TYPEDEF(netsnmp_request_group_item);
00568             if (i == NULL)
00569                 return;
00570             i->ri = current;
00571             i->tri = tblreq_info;
00572             i->next = g->list;
00573             g->list = i;
00574 
00576             continue;
00577         }
00578 
00579         DEBUGMSGTL(("table_array:group", "    new group"));
00580         DEBUGMSGOID(("table_array:group", index.oids,
00581                      index.len));
00582         DEBUGMSG(("table_array:group", "\n"));
00583         g = SNMP_MALLOC_TYPEDEF(netsnmp_request_group);
00584         i = SNMP_MALLOC_TYPEDEF(netsnmp_request_group_item);
00585         if (i == NULL || g == NULL)
00586             return;
00587         g->list = i;
00588         g->table = tad->table;
00589         i->ri = current;
00590         i->tri = tblreq_info;
00593         /*
00594          * search for row. all changes are made to the original row,
00595          * later, we'll make a copy in undo_info before we start processing.
00596          */
00597         row = g->existing_row = (netsnmp_index*)CONTAINER_FIND(tad->table, &index);
00598         if (!g->existing_row) {
00599             if (!tad->cb->create_row) {
00600 #ifndef NETSNMP_NO_WRITE_SUPPORT
00601                 if(MODE_IS_SET(agtreq_info->mode))
00602                     netsnmp_set_request_error(agtreq_info, current,
00603                                               SNMP_ERR_NOTWRITABLE);
00604                 else
00605 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00606                     netsnmp_set_request_error(agtreq_info, current,
00607                                               SNMP_NOSUCHINSTANCE);
00608                 free(g);
00609                 free(i);
00610                 continue;
00611             }
00613             row = g->existing_row = tad->cb->create_row(&index);
00614             if (!row) {
00615                 /* xxx-rks : parameter to create_row to allow
00616                  * for better error reporting. */
00617                 netsnmp_set_request_error(agtreq_info, current,
00618                                           SNMP_ERR_GENERR);
00619                 free(g);
00620                 free(i);
00621                 continue;
00622             }
00623             g->row_created = 1;
00624         }
00625 
00626         g->index.oids = row->oids;
00627         g->index.len = row->len;
00628 
00629         CONTAINER_INSERT(request_group, g);
00630 
00631     } 
00632 }
00633 
00634 #ifndef NETSNMP_NO_WRITE_SUPPORT
00635 static void
00636 release_netsnmp_request_group(netsnmp_index *g, void *v)
00637 {
00638     netsnmp_request_group_item *tmp;
00639     netsnmp_request_group *group = (netsnmp_request_group *) g;
00640 
00641     if (!g)
00642         return;
00643     while (group->list) {
00644         tmp = group->list;
00645         group->list = tmp->next;
00646         free(tmp);
00647     }
00648 
00649     free(group);
00650 }
00651 
00652 static void
00653 release_netsnmp_request_groups(void *vp)
00654 {
00655     netsnmp_container *c = (netsnmp_container*)vp;
00656     CONTAINER_FOR_EACH(c, (netsnmp_container_obj_func*)
00657                        release_netsnmp_request_group, NULL);
00658     CONTAINER_FREE(c);
00659 }
00660 
00661 static void
00662 process_set_group(netsnmp_index *o, void *c)
00663 {
00664     /* xxx-rks: should we continue processing after an error?? */
00665     set_context           *context = (set_context *) c;
00666     netsnmp_request_group *ag = (netsnmp_request_group *) o;
00667     int                    rc = SNMP_ERR_NOERROR;
00668 
00669     switch (context->agtreq_info->mode) {
00670 
00671     case MODE_SET_RESERVE1:
00673         /*
00674          * if not a new row, save undo info
00675          */
00676         if (ag->row_created == 0) {
00677             if (context->tad->cb->duplicate_row)
00678                 ag->undo_info = context->tad->cb->duplicate_row(ag->existing_row);
00679             else
00680                 ag->undo_info = NULL;
00681             if (NULL == ag->undo_info) {
00682                 rc = SNMP_ERR_RESOURCEUNAVAILABLE;
00683                 break;
00684             }
00685         }
00686         
00687         if (context->tad->cb->set_reserve1)
00688             context->tad->cb->set_reserve1(ag);
00689         break;
00690 
00691     case MODE_SET_RESERVE2:
00692         if (context->tad->cb->set_reserve2)
00693             context->tad->cb->set_reserve2(ag);
00694         break;
00695 
00696     case MODE_SET_ACTION:
00697         if (context->tad->cb->set_action)
00698             context->tad->cb->set_action(ag);
00699         break;
00700 
00701     case MODE_SET_COMMIT:
00702         if (ag->row_created == 0) {
00703             /*
00704              * this is an existing row, has it been deleted?
00705              */
00706             if (ag->row_deleted == 1) {
00707                 DEBUGMSGT((TABLE_ARRAY_NAME, "action: deleting row\n"));
00708                 if (CONTAINER_REMOVE(ag->table, ag->existing_row) != 0) {
00709                     rc = SNMP_ERR_COMMITFAILED;
00710                     break;
00711                 }
00712             }
00713         } else if (ag->row_deleted == 0) {
00714             /*
00715              * new row (that hasn't been deleted) should be inserted
00716              */
00717             DEBUGMSGT((TABLE_ARRAY_NAME, "action: inserting row\n"));
00718             if (CONTAINER_INSERT(ag->table, ag->existing_row) != 0) {
00719                 rc = SNMP_ERR_COMMITFAILED;
00720                 break;
00721             }
00722         }
00723 
00724         if (context->tad->cb->set_commit)
00725             context->tad->cb->set_commit(ag);
00726 
00728         if (ag->undo_info) {
00729             context->tad->cb->delete_row(ag->undo_info);
00730             ag->undo_info = NULL;
00731         }
00732 
00733 #if 0
00734         /* XXX-rks: finish row cooperative notifications
00735          * if the table has requested it, send cooperative notifications
00736          * for row operations.
00737          */
00738         if (context->tad->notifications) {
00739             if (ag->undo_info) {
00740                 if (!ag->existing_row)
00741                     netsnmp_monitor_notify(EVENT_ROW_DEL);
00742                 else
00743                     netsnmp_monitor_notify(EVENT_ROW_MOD);
00744             }
00745             else
00746                 netsnmp_monitor_notify(EVENT_ROW_ADD);
00747         }
00748 #endif
00749 
00750         if ((ag->row_created == 0) && (ag->row_deleted == 1)) {
00751             context->tad->cb->delete_row(ag->existing_row);
00752             ag->existing_row = NULL;
00753         }
00754         break;
00755 
00756     case MODE_SET_FREE:
00757         if (context->tad->cb->set_free)
00758             context->tad->cb->set_free(ag);
00759 
00761         if (ag->row_created == 1) {
00762             if (context->tad->cb->delete_row)
00763                 context->tad->cb->delete_row(ag->existing_row);
00764             ag->existing_row = NULL;
00765         }
00766         else {
00767             if (context->tad->cb->delete_row)
00768                 context->tad->cb->delete_row(ag->undo_info);
00769             ag->undo_info = NULL;
00770         }
00771         break;
00772 
00773     case MODE_SET_UNDO:
00774         /*
00775          * status already set - don't change it now
00776          */
00777         if (context->tad->cb->set_undo)
00778             context->tad->cb->set_undo(ag);
00779 
00780         /*
00781          * no more use for undo_info, so free it
00782          */
00783         if (ag->row_created == 0) {
00784             /*
00785              * restore old values
00786              */
00787             context->tad->cb->row_copy(ag->existing_row, ag->undo_info);
00788             context->tad->cb->delete_row(ag->undo_info);
00789             ag->undo_info = NULL;
00790         }
00791         else {
00792             context->tad->cb->delete_row(ag->existing_row);
00793             ag->existing_row = NULL;
00794         }
00795         break;
00796 
00797     default:
00798         snmp_log(LOG_ERR, "unknown mode processing SET for "
00799                  "netsnmp_table_array_helper_handler\n");
00800         rc = SNMP_ERR_GENERR;
00801         break;
00802     }
00803     
00804     if (rc)
00805         netsnmp_set_request_error(context->agtreq_info,
00806                                   ag->list->ri, rc);
00807                                                
00808 }
00809 
00810 int
00811 process_set_requests(netsnmp_agent_request_info *agtreq_info,
00812                      netsnmp_request_info *requests,
00813                      table_container_data * tad, char *handler_name)
00814 {
00815     set_context         context;
00816     netsnmp_container  *request_group;
00817 
00818     /*
00819      * create and save structure for set info
00820      */
00821     request_group = (netsnmp_container*) netsnmp_agent_get_list_data
00822         (agtreq_info, handler_name);
00823     if (request_group == NULL) {
00824         netsnmp_data_list *tmp;
00825         request_group = netsnmp_container_find("request_group:"
00826                                                "table_container");
00827         request_group->compare = netsnmp_compare_netsnmp_index;
00828         request_group->ncompare = netsnmp_ncompare_netsnmp_index;
00829 
00830         DEBUGMSGTL(("table_array", "Grouping requests by oid\n"));
00831 
00832         tmp = netsnmp_create_data_list(handler_name,
00833                                        request_group,
00834                                        release_netsnmp_request_groups);
00835         netsnmp_agent_add_list_data(agtreq_info, tmp);
00836         /*
00837          * group requests.
00838          */
00839         group_requests(agtreq_info, requests, request_group, tad);
00840     }
00841 
00842     /*
00843      * process each group one at a time
00844      */
00845     context.agtreq_info = agtreq_info;
00846     context.tad = tad;
00847     context.status = SNMP_ERR_NOERROR;
00848     CONTAINER_FOR_EACH(request_group,
00849                        (netsnmp_container_obj_func*)process_set_group,
00850                        &context);
00851 
00852     return context.status;
00853 }
00854 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00855 
00856 
00857 /**********************************************************************
00858  **********************************************************************
00859  *                                                                    *
00860  *                                                                    *
00861  * netsnmp_table_array_helper_handler()                               *
00862  *                                                                    *
00863  *                                                                    *
00864  **********************************************************************
00865  **********************************************************************/
00866 int
00867 netsnmp_table_array_helper_handler(netsnmp_mib_handler *handler,
00868                                    netsnmp_handler_registration *reginfo,
00869                                    netsnmp_agent_request_info *agtreq_info,
00870                                    netsnmp_request_info *requests)
00871 {
00872 
00873     /*
00874      * First off, get our pointer from the handler. This
00875      * lets us get to the table registration information we
00876      * saved in get_table_array_handler(), as well as the
00877      * container where the actual table data is stored.
00878      */
00879     int             rc = SNMP_ERR_NOERROR;
00880     table_container_data *tad = (table_container_data *)handler->myvoid;
00881 
00882     if (agtreq_info->mode < 0 || agtreq_info->mode > 5) {
00883         DEBUGMSGTL(("table_array", "Mode %d, Got request:\n",
00884                     agtreq_info->mode));
00885     } else {
00886         DEBUGMSGTL(("table_array", "Mode %s, Got request:\n",
00887                     mode_name[agtreq_info->mode]));
00888     }
00889 
00890 #ifndef NETSNMP_NO_WRITE_SUPPORT
00891     if (MODE_IS_SET(agtreq_info->mode)) {
00892         /*
00893          * netsnmp_mutex_lock(&tad->lock);
00894          */
00895         rc = process_set_requests(agtreq_info, requests,
00896                                   tad, handler->handler_name);
00897         /*
00898          * netsnmp_mutex_unlock(&tad->lock);
00899          */
00900     } else
00901 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00902         rc = process_get_requests(reginfo, agtreq_info, requests, tad);
00903 
00904     if (rc != SNMP_ERR_NOERROR) {
00905         DEBUGMSGTL(("table_array", "processing returned rc %d\n", rc));
00906     }
00907     
00908     /*
00909      * Now we've done our processing. If there is another handler below us,
00910      * call them.
00911      */
00912     if (handler->next) {
00913         rc = netsnmp_call_next_handler(handler, reginfo, agtreq_info, requests);
00914         if (rc != SNMP_ERR_NOERROR) {
00915             DEBUGMSGTL(("table_array", "next handler returned rc %d\n", rc));
00916         }
00917     }
00918     
00919     return rc;
00920 }
00921 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_CONTAINER */
00922