and added files
[bcm963xx.git] / userapps / opensource / net-snmp / agent / helpers / table_array.c
1 #ifdef BRCM_SNMP_NOT_USED
2 /*
3  * table_array.c
4  * $Id: table_array.c,v 5.18.2.1 2003/03/14 17:37:21 rstory Exp $
5  */
6
7 #include <net-snmp/net-snmp-config.h>
8
9 #if HAVE_STRING_H
10 #include <string.h>
11 #else
12 #include <strings.h>
13 #endif
14
15 #include <net-snmp/net-snmp-includes.h>
16 #include <net-snmp/agent/net-snmp-agent-includes.h>
17
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>
22
23 #if HAVE_DMALLOC_H
24 #include <dmalloc.h>
25 #endif
26
27 /*
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 
35  */
36
37 static const char *mode_name[] = {
38     "Reserve 1",
39     "Reserve 2",
40     "Action",
41     "Commit",
42     "Free",
43     "Undo"
44 };
45
46 /*
47  * PRIVATE structure for holding important info for each table.
48  */
49 typedef struct table_container_data_s {
50
51    /** registration info for the table */
52     netsnmp_table_registration_info *tblreg_info;
53
54    /** container for the table rows */
55    netsnmp_container          *table;
56
57     /*
58      * mutex_type                lock;
59      */
60
61    /** do we want to group rows with the same index
62     * together when calling callbacks? */
63     int             group_rows;
64
65    /** callbacks for this table */
66     netsnmp_table_array_callbacks *cb;
67
68 } table_container_data;
69
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.
71  *  @ingroup table
72  *
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.
77  *
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.
82  *
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.
86  *
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.
92  *
93  *  NOTE NOTE NOTE: This helper, the API and callbacks are still
94  *  being tested and may change.
95  *
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.
100  *
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.
105  *
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
110  *  from the copy.
111  *
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.
115  *
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
120  *  file.
121  *
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.
128  *
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.
134  *
135  * @{
136  */
137
138 /**********************************************************************
139  **********************************************************************
140  *                                                                    *
141  *                                                                    *
142  * PUBLIC Registration functions                                      *
143  *                                                                    *
144  *                                                                    *
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.
151 */
152 int
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,
157                              int group_rows)
158 {
159     table_container_data *tad = SNMP_MALLOC_TYPEDEF(table_container_data);
160     if (!tad)
161         return SNMPERR_GENERR;
162     tad->tblreg_info = tabreg;  /* we need it too, but it really is not ours */
163
164     if (!cb) {
165         snmp_log(LOG_ERR, "table_array registration with no callbacks\n" );
166         return SNMPERR_GENERR;
167     }
168     /*
169      * check for required callbacks
170      */
171     if ((cb->can_set &&
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;
177     }
178
179     if (NULL==container)
180         tad->table = netsnmp_container_find("table_array");
181     else
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;
187     
188     tad->cb = cb;
189
190     reginfo->handler->myvoid = tad;
191
192     return netsnmp_register_table(reginfo, tabreg);
193 }
194
195 /** find the handler for the table_array helper. */
196 netsnmp_mib_handler *
197 netsnmp_find_table_array_handler(netsnmp_handler_registration *reginfo)
198 {
199     netsnmp_mib_handler *mh;
200     if (!reginfo)
201         return NULL;
202     mh = reginfo->handler;
203     while (mh) {
204         if (mh->access_method == netsnmp_table_array_helper_handler)
205             break;
206         mh = mh->next;
207     }
208
209     return mh;
210 }
211
212 /** find the context data used by the table_array helper */
213 netsnmp_container      *
214 netsnmp_extract_array_context(netsnmp_request_info *request)
215 {
216     return netsnmp_request_get_list_data(request, TABLE_ARRAY_NAME);
217 }
218
219 /** this function is called to validate RowStatus transitions. */
220 int
221 netsnmp_table_array_check_row_status(netsnmp_table_array_callbacks *cb,
222                                      netsnmp_request_group *ag,
223                                      long *rs_new, long *rs_old)
224 {
225     netsnmp_index *row_ctx;
226     netsnmp_index *undo_ctx;
227     if (!ag || !cb)
228         return SNMPERR_GENERR;
229     row_ctx  = ag->existing_row;
230     undo_ctx = ag->undo_info;
231     
232     /*
233      * xxx-rks: revisit row delete scenario
234      */
235     if (row_ctx) {
236         /*
237          * either a new row, or change to old row
238          */
239         /*
240          * is it set to active?
241          */
242         if (RS_IS_GOING_ACTIVE(*rs_new)) {
243             /*
244              * is it ready to be active?
245              */
246             if ((NULL==cb->can_activate) ||
247                 cb->can_activate(undo_ctx, row_ctx, ag))
248                 *rs_new = RS_ACTIVE;
249             else
250                 return SNMP_ERR_INCONSISTENTVALUE;
251         } else {
252             /*
253              * not going active
254              */
255             if (undo_ctx) {
256                 /*
257                  * change
258                  */
259                 if (RS_IS_ACTIVE(*rs_old)) {
260                     /*
261                      * check pre-reqs for deactivation
262                      */
263                     if (cb->can_deactivate &&
264                         !cb->can_deactivate(undo_ctx, row_ctx, ag)) {
265                         return SNMP_ERR_INCONSISTENTVALUE;
266                     }
267                 }
268             } else {
269                 /*
270                  * new row
271                  */
272             }
273
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;
278                 else
279                     *rs_new = RS_NOTREADY;
280             } else {
281                 if (cb->can_delete && !cb->can_delete(undo_ctx, row_ctx, ag)) {
282                     return SNMP_ERR_INCONSISTENTVALUE;
283                 }
284                 ag->row_deleted = 1;
285             }
286         }
287     } else {
288         /*
289          * check pre-reqs for delete row
290          */
291         if (cb->can_delete && !cb->can_delete(undo_ctx, row_ctx, ag)) {
292             return SNMP_ERR_INCONSISTENTVALUE;
293         }
294     }
295
296     return SNMP_ERR_NOERROR;
297 }
298
299 /** @} */
300
301 #ifndef DOXYGEN_SHOULD_SKIP_THIS
302 /**********************************************************************
303  **********************************************************************
304  **********************************************************************
305  **********************************************************************
306  *                                                                    *
307  *                                                                    *
308  *                                                                    *
309  *                                                                    *
310  * EVERYTHING BELOW THIS IS PRIVATE IMPLEMENTATION DETAILS.           *
311  *                                                                    *
312  *                                                                    *
313  *                                                                    *
314  *                                                                    *
315  **********************************************************************
316  **********************************************************************
317  **********************************************************************
318  **********************************************************************/
319
320 /**********************************************************************
321  **********************************************************************
322  *                                                                    *
323  *                                                                    *
324  * Structures, Utility/convenience functions                          *
325  *                                                                    *
326  *                                                                    *
327  **********************************************************************
328  **********************************************************************/
329 /*
330  * context info for SET requests
331  */
332 typedef struct set_context_s {
333     netsnmp_agent_request_info *agtreq_info;
334     table_container_data *tad;
335     int             status;
336 } set_context;
337
338 static void
339 release_netsnmp_request_group(netsnmp_index *g, void *v)
340 {
341     netsnmp_request_group_item *tmp;
342     netsnmp_request_group *group = (netsnmp_request_group *) g;
343
344     if (!g)
345         return;
346     while (group->list) {
347         tmp = group->list;
348         group->list = tmp->next;
349         free(tmp);
350     }
351
352     free(group);
353 }
354
355 static void
356 release_netsnmp_request_groups(void *vp)
357 {
358     netsnmp_container *c = (netsnmp_container*)vp;
359     CONTAINER_FOR_EACH(c, (netsnmp_container_obj_func*)
360                        release_netsnmp_request_group, NULL);
361     CONTAINER_FREE(c);
362 }
363
364 NETSNMP_INLINE netsnmp_index *
365 find_next_row(netsnmp_table_request_info *tblreq_info,
366               table_container_data * tad)
367 {
368     netsnmp_index *row = NULL;
369     netsnmp_index index;
370
371     if (!tblreq_info || !tad)
372         return NULL;
373
374     /*
375      * below our minimum column?
376      */
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);
380     } else {
381         index.oids = tblreq_info->index_oid;
382         index.len = tblreq_info->index_oid_len;
383
384         row = CONTAINER_NEXT(tad->table, &index);
385
386         /*
387          * we don't have a row, but we might be at the end of a
388          * column, so try the next one.
389          */
390         if (!row) {
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;
397
398             if (tblreq_info->colnum != 0)
399                 row = CONTAINER_FIRST(tad->table);
400         }
401     }
402
403     return row;
404 }
405
406 NETSNMP_INLINE void
407 build_new_oid(netsnmp_handler_registration *reginfo,
408               netsnmp_table_request_info *tblreq_info,
409               netsnmp_index *row, netsnmp_request_info *current)
410 {
411     oid             coloid[MAX_OID_LEN];
412     int             coloid_len;
413
414     if (!tblreq_info || !reginfo || !row || !current)
415         return;
416
417     coloid_len = reginfo->rootoid_len + 2;
418     memcpy(coloid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid));
419
420     /** table.entry */
421     coloid[reginfo->rootoid_len] = 1;
422
423     /** table.entry.column */
424     coloid[reginfo->rootoid_len + 1] = tblreq_info->colnum;
425
426     /** table.entry.column.index */
427     memcpy(&coloid[reginfo->rootoid_len + 2], row->oids,
428            row->len * sizeof(oid));
429
430     snmp_set_var_objid(current->requestvb, coloid,
431                        reginfo->rootoid_len + 2 + row->len);
432 }
433
434 /**********************************************************************
435  **********************************************************************
436  *                                                                    *
437  *                                                                    *
438  * GET procession functions                                           *
439  *                                                                    *
440  *                                                                    *
441  **********************************************************************
442  **********************************************************************/
443 NETSNMP_INLINE int
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)
448 {
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;
454
455     /*
456      * Loop through each of the requests, and
457      * try to find the appropriate row from the container.
458      */
459     for (current = requests; current; current = current->next) {
460
461         var = current->requestvb;
462         DEBUGMSGTL(("table_array:get",
463                     "  process_get_request oid:"));
464         DEBUGMSGOID(("table_array:get", var->name,
465                      var->name_length));
466         DEBUGMSG(("table_array:get", "\n"));
467
468         /*
469          * skip anything that doesn't need processing.
470          */
471         if (current->processed != 0) {
472             DEBUGMSGTL(("table_array:get", "already processed\n"));
473             continue;
474         }
475
476         /*
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.
481          */
482         tblreq_info = netsnmp_extract_table_info(current);
483         netsnmp_assert(tblreq_info->colnum <= tad->tblreg_info->max_column);
484
485         if ((agtreq_info->mode == MODE_GETNEXT) ||
486             (agtreq_info->mode == MODE_GETBULK)) {
487             /*
488              * find the row
489              */
490             row = find_next_row(tblreq_info, tad);
491             if (!row) {
492                 /*
493                  * no results found.
494                  *
495                  * xxx-rks: how do we skip this entry for the next handler,
496                  * but still allow it a chance to hit another handler?
497                  */
498                 DEBUGMSGTL(("table_array:get", "no row found\n"));
499                 continue;
500             }
501
502             /*
503              * * if data was found, make sure it has the column we want
504              */
505 /* xxx-rks: add suport for sparse tables */
506
507             /*
508              * build new oid
509              */
510             build_new_oid(reginfo, tblreq_info, row, current);
511
512         } /** GETNEXT/GETBULK */
513         else {
514             netsnmp_index index;
515             index.oids = tblreq_info->index_oid;
516             index.len = tblreq_info->index_oid_len;
517
518             row = CONTAINER_FIND(tad->table, &index);
519             if (!row) {
520                 DEBUGMSGTL(("table_array:get", "no row found\n"));
521                 netsnmp_set_request_error(agtreq_info, current,
522                                           SNMP_NOSUCHINSTANCE);
523                 continue;
524             }
525         } /** GET */
526
527         /*
528          * get the data
529          */
530         rc = tad->cb->get_value(current, row, tblreq_info);
531
532     } /** for ( ... requests ... ) */
533
534     return rc;
535 }
536
537 /**********************************************************************
538  **********************************************************************
539  *                                                                    *
540  *                                                                    *
541  * SET procession functions                                           *
542  *                                                                    *
543  *                                                                    *
544  **********************************************************************
545  **********************************************************************/
546
547 NETSNMP_INLINE void
548 group_requests(netsnmp_agent_request_info *agtreq_info,
549                netsnmp_request_info *requests,
550                netsnmp_container *request_group, table_container_data * tad)
551 {
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;
558
559     for (current = requests; current; current = current->next) {
560
561         var = current->requestvb;
562
563         /*
564          * skip anything that doesn't need processing.
565          */
566         if (current->processed != 0) {
567             DEBUGMSGTL(("table_array:group",
568                         "already processed\n"));
569             continue;
570         }
571
572         /*
573          * 3.2.1 Setup and paranoia
574          * *
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.
579          */
580         row = NULL;
581         tblreq_info = netsnmp_extract_table_info(current);
582         netsnmp_assert(tblreq_info->colnum <= tad->tblreg_info->max_column);
583
584         /*
585          * search for index
586          */
587         index.oids = tblreq_info->index_oid;
588         index.len = tblreq_info->index_oid_len;
589         tmp = CONTAINER_FIND(request_group, &index);
590         if (tmp) {
591             DEBUGMSGTL(("table_array:group",
592                         "    existing group:"));
593             DEBUGMSGOID(("table_array:group", index.oids,
594                          index.len));
595             DEBUGMSG(("table_array:group", "\n"));
596             g = (netsnmp_request_group *) tmp;
597             i = SNMP_MALLOC_TYPEDEF(netsnmp_request_group_item);
598             i->ri = current;
599             i->tri = tblreq_info;
600             i->next = g->list;
601             g->list = i;
602
603             /** xxx-rks: store map of colnum to request */
604             continue;
605         }
606
607         DEBUGMSGTL(("table_array:group", "    new group"));
608         DEBUGMSGOID(("table_array:group", index.oids,
609                      index.len));
610         DEBUGMSG(("table_array:group", "\n"));
611         g = SNMP_MALLOC_TYPEDEF(netsnmp_request_group);
612         i = SNMP_MALLOC_TYPEDEF(netsnmp_request_group_item);
613         g->list = i;
614         g->table = tad->table;
615         i->ri = current;
616         i->tri = tblreq_info;
617         /** xxx-rks: store map of colnum to request */
618
619         /*
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.
622          */
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);
629                 else
630                     netsnmp_set_request_error(agtreq_info, current,
631                                               SNMP_NOSUCHINSTANCE);
632                 free(g);
633                 free(i);
634                 continue;
635             }
636             /** use undo_info temporarily */
637             row = g->existing_row = tad->cb->create_row(&index);
638             if (!row) {
639                 /* xxx-rks : parameter to create_row to allow
640                  * for better error reporting. */
641                 netsnmp_set_request_error(agtreq_info, current,
642                                           SNMP_ERR_GENERR);
643                 free(g);
644                 free(i);
645                 continue;
646             }
647             g->row_created = 1;
648         }
649
650         g->index.oids = row->oids;
651         g->index.len = row->len;
652
653         CONTAINER_INSERT(request_group, g);
654
655     } /** for( current ... ) */
656 }
657
658 static NETSNMP_INLINE void
659 process_set_group(netsnmp_index *o, void *c)
660 {
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;
665
666     switch (context->agtreq_info->mode) {
667
668     case MODE_SET_RESERVE1:/** -> SET_RESERVE2 || SET_FREE */
669
670         /*
671          * if not a new row, save undo info
672          */
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;
677                 break;
678             }
679         }
680         
681         if (context->tad->cb->set_reserve1)
682             context->tad->cb->set_reserve1(ag);
683         break;
684
685     case MODE_SET_RESERVE2:/** -> SET_ACTION || SET_FREE */
686         if (context->tad->cb->set_reserve2)
687             context->tad->cb->set_reserve2(ag);
688         break;
689
690     case MODE_SET_ACTION:/** -> SET_COMMIT || SET_UNDO */
691         if (context->tad->cb->set_action)
692             context->tad->cb->set_action(ag);
693         break;
694
695     case MODE_SET_COMMIT:/** FINAL CHANCE ON SUCCESS */
696         if (ag->row_created == 0) {
697             /*
698              * this is an existing row, has it been deleted?
699              */
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;
704                     break;
705                 }
706             }
707         } else if (ag->row_deleted == 0) {
708             /*
709              * new row (that hasn't been deleted) should be inserted
710              */
711             DEBUGMSGT((TABLE_ARRAY_NAME, "action: inserting row\n"));
712             if (CONTAINER_INSERT(ag->table, ag->existing_row) != 0) {
713                 rc = SNMP_ERR_COMMITFAILED;
714                 break;
715             }
716         }
717
718         if (context->tad->cb->set_commit)
719             context->tad->cb->set_commit(ag);
720
721         /** no more use for undo_info, so free it */
722         if (ag->undo_info) {
723             context->tad->cb->delete_row(ag->undo_info);
724             ag->undo_info = NULL;
725         }
726
727 #if 0
728         /* XXX-rks: finish row cooperative notifications
729          * if the table has requested it, send cooperative notifications
730          * for row operations.
731          */
732         if (context->tad->notifications) {
733             if (ag->undo_info) {
734                 if (!ag->existing_row)
735                     netsnmp_monitor_notify(EVENT_ROW_DEL);
736                 else
737                     netsnmp_monitor_notify(EVENT_ROW_MOD);
738             }
739             else
740                 netsnmp_monitor_notify(EVENT_ROW_ADD);
741         }
742 #endif
743         break;
744
745     case MODE_SET_FREE:/** FINAL CHANCE ON FAILURE */
746         if (context->tad->cb->set_free)
747             context->tad->cb->set_free(ag);
748
749         /** no more use for undo_info, so free it */
750         if (ag->undo_info) {
751             context->tad->cb->delete_row(ag->undo_info);
752             ag->undo_info = NULL;
753         }
754         break;
755
756     case MODE_SET_UNDO:/** FINAL CHANCE ON FAILURE */
757         if (ag->row_created == 0) {
758             /*
759              * this row existed before.
760              */
761             if (ag->row_deleted == 1) {
762                 /*
763                  * re-insert undo_info
764                  */
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;
768                     break;
769                 }
770             }
771         } else if (ag->row_deleted == 0) {
772             /*
773              * new row that wasn't deleted should be removed
774              */
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;
778                 break;
779             }
780         }
781
782         /*
783          * status already set - don't change it now
784          */
785         if (context->tad->cb->set_undo)
786             context->tad->cb->set_undo(ag);
787
788         /*
789          * no more use for undo_info, so free it
790          */
791         if (ag->row_created == 0) {
792             /*
793              * restore old values
794              */
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;
798         }
799         else {
800             context->tad->cb->delete_row(ag->existing_row);
801             ag->existing_row = NULL;
802         }
803         break;
804
805     default:
806         snmp_log(LOG_ERR, "unknown mode processing SET for "
807                  "netsnmp_table_array_helper_handler\n");
808         rc = SNMP_ERR_GENERR;
809         break;
810     }
811     
812     if (rc)
813         netsnmp_set_request_error(context->agtreq_info,
814                                   ag->list->ri, rc);
815                                                
816 }
817
818 int
819 process_set_requests(netsnmp_agent_request_info *agtreq_info,
820                      netsnmp_request_info *requests,
821                      table_container_data * tad, char *handler_name)
822 {
823     set_context         context;
824     netsnmp_container  *request_group;
825
826     /*
827      * create and save structure for set info
828      */
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:"
834                                                "table_container");
835         request_group->compare = netsnmp_compare_netsnmp_index;
836         request_group->ncompare = netsnmp_ncompare_netsnmp_index;
837
838         DEBUGMSGTL(("table_array", "Grouping requests by oid\n"));
839
840         tmp = netsnmp_create_data_list(handler_name,
841                                        request_group,
842                                        release_netsnmp_request_groups);
843         netsnmp_agent_add_list_data(agtreq_info, tmp);
844         /*
845          * group requests.
846          */
847         group_requests(agtreq_info, requests, request_group, tad);
848     }
849
850     /*
851      * process each group one at a time
852      */
853     context.agtreq_info = agtreq_info;
854     context.tad = tad;
855     context.status = SNMP_ERR_NOERROR;
856     CONTAINER_FOR_EACH(request_group,
857                        (netsnmp_container_obj_func*)process_set_group,
858                        &context);
859
860     return context.status;
861 }
862
863
864 /**********************************************************************
865  **********************************************************************
866  *                                                                    *
867  *                                                                    *
868  * netsnmp_table_array_helper_handler()                               *
869  *                                                                    *
870  *                                                                    *
871  **********************************************************************
872  **********************************************************************/
873 int
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)
878 {
879
880     /*
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.
885      */
886     int             rc = SNMP_ERR_NOERROR;
887     table_container_data *tad = (table_container_data *)handler->myvoid;
888
889     if (agtreq_info->mode < 0 || agtreq_info->mode > 5) {
890         DEBUGMSGTL(("table_array", "Mode %d, Got request:\n",
891                     agtreq_info->mode));
892     } else {
893         DEBUGMSGTL(("table_array", "Mode %s, Got request:\n",
894                     mode_name[agtreq_info->mode]));
895     }
896
897     if (MODE_IS_SET(agtreq_info->mode)) {
898         /*
899          * netsnmp_mutex_lock(&tad->lock);
900          */
901         rc = process_set_requests(agtreq_info, requests,
902                                   tad, handler->handler_name);
903         /*
904          * netsnmp_mutex_unlock(&tad->lock);
905          */
906     } else
907         rc = process_get_requests(reginfo, agtreq_info, requests, tad);
908
909     if (rc != SNMP_ERR_NOERROR) {
910         DEBUGMSGTL(("table_array", "processing returned rc %d\n", rc));
911     }
912     
913     /*
914      * Now we've done out processing. If there is another handler below us,
915      * call them.
916      */
917     if (handler->next) {
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));
921         }
922     }
923     
924     return rc;
925 }
926 #endif /** DOXYGEN_SHOULD_SKIP_THIS */
927
928 #endif /* #if BRCM_SNMP_NOT_USED */