~ubuntu-branches/ubuntu/trusty/net-snmp/trusty

« back to all changes in this revision

Viewing changes to agent/helpers/table_array.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2004-09-13 12:06:21 UTC
  • Revision ID: james.westby@ubuntu.com-20040913120621-g952ntonlleihcvm
Tags: upstream-5.1.1
ImportĀ upstreamĀ versionĀ 5.1.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * table_array.c
 
3
 * $Id: table_array.c,v 5.19.2.2 2004/03/14 06:05:39 rstory Exp $
 
4
 */
 
5
 
 
6
#include <net-snmp/net-snmp-config.h>
 
7
 
 
8
#if HAVE_STRING_H
 
9
#include <string.h>
 
10
#else
 
11
#include <strings.h>
 
12
#endif
 
13
 
 
14
#include <net-snmp/net-snmp-includes.h>
 
15
#include <net-snmp/agent/net-snmp-agent-includes.h>
 
16
 
 
17
#include <net-snmp/agent/table.h>
 
18
#include <net-snmp/agent/table_array.h>
 
19
#include <net-snmp/library/container.h>
 
20
#include <net-snmp/library/snmp_assert.h>
 
21
 
 
22
#if HAVE_DMALLOC_H
 
23
#include <dmalloc.h>
 
24
#endif
 
25
 
 
26
/*
 
27
 * snmp.h:#define SNMP_MSG_INTERNAL_SET_BEGIN        -1 
 
28
 * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE1     0 
 
29
 * snmp.h:#define SNMP_MSG_INTERNAL_SET_RESERVE2     1 
 
30
 * snmp.h:#define SNMP_MSG_INTERNAL_SET_ACTION       2 
 
31
 * snmp.h:#define SNMP_MSG_INTERNAL_SET_COMMIT       3 
 
32
 * snmp.h:#define SNMP_MSG_INTERNAL_SET_FREE         4 
 
33
 * snmp.h:#define SNMP_MSG_INTERNAL_SET_UNDO         5 
 
34
 */
 
35
 
 
36
static const char *mode_name[] = {
 
37
    "Reserve 1",
 
38
    "Reserve 2",
 
39
    "Action",
 
40
    "Commit",
 
41
    "Free",
 
42
    "Undo"
 
43
};
 
44
 
 
45
/*
 
46
 * PRIVATE structure for holding important info for each table.
 
47
 */
 
48
typedef struct table_container_data_s {
 
49
 
 
50
   /** registration info for the table */
 
51
    netsnmp_table_registration_info *tblreg_info;
 
52
 
 
53
   /** container for the table rows */
 
54
   netsnmp_container          *table;
 
55
 
 
56
    /*
 
57
     * mutex_type                lock;
 
58
     */
 
59
 
 
60
   /** do we want to group rows with the same index
 
61
    * together when calling callbacks? */
 
62
    int             group_rows;
 
63
 
 
64
   /** callbacks for this table */
 
65
    netsnmp_table_array_callbacks *cb;
 
66
 
 
67
} table_container_data;
 
68
 
 
69
/** @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.
 
70
 *  @ingroup table
 
71
 *
 
72
 *  The table_array handler is used (automatically) in conjuntion
 
73
 *  with the @link table table@endlink handler. It is primarily
 
74
 *  intended to be used with the mib2c configuration file
 
75
 *  mib2c.array-user.conf.
 
76
 *
 
77
 *  The code generated by mib2c is useful when you have control of
 
78
 *  the data for each row. If you cannot control when rows are added
 
79
 *  and deleted (or at least be notified of changes to row data),
 
80
 *  then this handler is probably not for you.
 
81
 *
 
82
 *  This handler makes use of callbacks (function pointers) to
 
83
 *  handle various tasks. Code is generated for each callback,
 
84
 *  but will need to be reviewed and flushed out by the user.
 
85
 *
 
86
 *  NOTE NOTE NOTE: Once place where mib2c is somewhat lacking
 
87
 *  is with regards to tables with external indices. If your
 
88
 *  table makes use of one or more external indices, please
 
89
 *  review the generated code very carefully for comments
 
90
 *  regarding external indices.
 
91
 *
 
92
 *  NOTE NOTE NOTE: This helper, the API and callbacks are still
 
93
 *  being tested and may change.
 
94
 *
 
95
 *  The generated code will define a structure for storage of table
 
96
 *  related data. This structure must be used, as it contains the index
 
97
 *  OID for the row, which is used for keeping the array sorted. You can
 
98
 *  add addition fields or data to the structure for your own use.
 
99
 *
 
100
 *  The generated code will also have code to handle SNMP-SET processing.
 
101
 *  If your table does not support any SET operations, simply comment
 
102
 *  out the #define <PREFIX>_SET_HANDLING (where <PREFIX> is your
 
103
 *  table name) in the header file.
 
104
 *
 
105
 *  SET processing modifies the row in-place. The duplicate_row
 
106
 *  callback will be called to save a copy of the original row.
 
107
 *  In the event of a failure before the commite phase, the
 
108
 *  row_copy callback will be called to restore the original row
 
109
 *  from the copy.
 
110
 *
 
111
 *  Code will be generated to handle row creation. This code may be
 
112
 *  disabled by commenting out the #define <PREFIX>_ROW_CREATION
 
113
 *  in the header file.
 
114
 *
 
115
 *  If your table contains a RowStatus object, by default the
 
116
 *  code will not allow object in an active row to be modified.
 
117
 *  To allow active rows to be modified, remove the comment block
 
118
 *  around the #define <PREFIX>_CAN_MODIFY_ACTIVE_ROW in the header
 
119
 *  file.
 
120
 *
 
121
 *  Code will be generated to maintain a secondary index for all
 
122
 *  rows, stored in a binary tree. This is very useful for finding
 
123
 *  rows by a key other than the OID index. By default, the functions
 
124
 *  for maintaining this tree will be based on a character string.
 
125
 *  NOTE: this will likely be made into a more generic mechanism,
 
126
 *  using new callback methods, in the near future.
 
127
 *
 
128
 *  The generated code contains many TODO comments. Make sure you
 
129
 *  check each one to see if it applies to your code. Examples include
 
130
 *  checking indices for syntax (ranges, etc), initializing default
 
131
 *  values in newly created rows, checking for row activation and
 
132
 *  deactivation requirements, etc.
 
133
 *
 
134
 * @{
 
135
 */
 
136
 
 
137
/**********************************************************************
 
138
 **********************************************************************
 
139
 *                                                                    *
 
140
 *                                                                    *
 
141
 * PUBLIC Registration functions                                      *
 
142
 *                                                                    *
 
143
 *                                                                    *
 
144
 **********************************************************************
 
145
 **********************************************************************/
 
146
/** register specified callbacks for the specified table/oid. If the
 
147
    group_rows parameter is set, the row related callbacks will be
 
148
    called once for each unique row index. Otherwise, each callback
 
149
    will be called only once, for all objects.
 
150
*/
 
151
int
 
152
netsnmp_table_container_register(netsnmp_handler_registration *reginfo,
 
153
                             netsnmp_table_registration_info *tabreg,
 
154
                             netsnmp_table_array_callbacks *cb,
 
155
                             netsnmp_container *container,
 
156
                             int group_rows)
 
157
{
 
158
    table_container_data *tad = SNMP_MALLOC_TYPEDEF(table_container_data);
 
159
    if (!tad)
 
160
        return SNMPERR_GENERR;
 
161
    tad->tblreg_info = tabreg;  /* we need it too, but it really is not ours */
 
162
 
 
163
    if (!cb) {
 
164
        snmp_log(LOG_ERR, "table_array registration with no callbacks\n" );
 
165
        return SNMPERR_GENERR;
 
166
    }
 
167
    /*
 
168
     * check for required callbacks
 
169
     */
 
170
    if ((cb->can_set &&
 
171
         ((NULL==cb->duplicate_row) || (NULL==cb->delete_row) ||
 
172
          (NULL==cb->row_copy)) )) {
 
173
        snmp_log(LOG_ERR, "table_array registration with incomplete "
 
174
                 "callback structure.\n");
 
175
        return SNMPERR_GENERR;
 
176
    }
 
177
 
 
178
    if (NULL==container)
 
179
        tad->table = netsnmp_container_find("table_array");
 
180
    else
 
181
        tad->table = container;
 
182
    if (NULL==container->compare)
 
183
        container->compare = netsnmp_compare_netsnmp_index;
 
184
    if (NULL==container->ncompare)
 
185
        container->ncompare = netsnmp_ncompare_netsnmp_index;
 
186
    
 
187
    tad->cb = cb;
 
188
 
 
189
    reginfo->handler->myvoid = tad;
 
190
 
 
191
    return netsnmp_register_table(reginfo, tabreg);
 
192
}
 
193
 
 
194
/** find the handler for the table_array helper. */
 
195
netsnmp_mib_handler *
 
196
netsnmp_find_table_array_handler(netsnmp_handler_registration *reginfo)
 
197
{
 
198
    netsnmp_mib_handler *mh;
 
199
    if (!reginfo)
 
200
        return NULL;
 
201
    mh = reginfo->handler;
 
202
    while (mh) {
 
203
        if (mh->access_method == netsnmp_table_array_helper_handler)
 
204
            break;
 
205
        mh = mh->next;
 
206
    }
 
207
 
 
208
    return mh;
 
209
}
 
210
 
 
211
/** find the context data used by the table_array helper */
 
212
netsnmp_container      *
 
213
netsnmp_extract_array_context(netsnmp_request_info *request)
 
214
{
 
215
    return netsnmp_request_get_list_data(request, TABLE_ARRAY_NAME);
 
216
}
 
217
 
 
218
/** this function is called to validate RowStatus transitions. */
 
219
int
 
220
netsnmp_table_array_check_row_status(netsnmp_table_array_callbacks *cb,
 
221
                                     netsnmp_request_group *ag,
 
222
                                     long *rs_new, long *rs_old)
 
223
{
 
224
    netsnmp_index *row_ctx;
 
225
    netsnmp_index *undo_ctx;
 
226
    if (!ag || !cb)
 
227
        return SNMPERR_GENERR;
 
228
    row_ctx  = ag->existing_row;
 
229
    undo_ctx = ag->undo_info;
 
230
    
 
231
    /*
 
232
     * xxx-rks: revisit row delete scenario
 
233
     */
 
234
    if (row_ctx) {
 
235
        /*
 
236
         * either a new row, or change to old row
 
237
         */
 
238
        /*
 
239
         * is it set to active?
 
240
         */
 
241
        if (RS_IS_GOING_ACTIVE(*rs_new)) {
 
242
            /*
 
243
             * is it ready to be active?
 
244
             */
 
245
            if ((NULL==cb->can_activate) ||
 
246
                cb->can_activate(undo_ctx, row_ctx, ag))
 
247
                *rs_new = RS_ACTIVE;
 
248
            else
 
249
                return SNMP_ERR_INCONSISTENTVALUE;
 
250
        } else {
 
251
            /*
 
252
             * not going active
 
253
             */
 
254
            if (undo_ctx) {
 
255
                /*
 
256
                 * change
 
257
                 */
 
258
                if (RS_IS_ACTIVE(*rs_old)) {
 
259
                    /*
 
260
                     * check pre-reqs for deactivation
 
261
                     */
 
262
                    if (cb->can_deactivate &&
 
263
                        !cb->can_deactivate(undo_ctx, row_ctx, ag)) {
 
264
                        return SNMP_ERR_INCONSISTENTVALUE;
 
265
                    }
 
266
                }
 
267
            } else {
 
268
                /*
 
269
                 * new row
 
270
                 */
 
271
            }
 
272
 
 
273
            if (*rs_new != RS_DESTROY) {
 
274
                if ((NULL==cb->can_activate) ||
 
275
                    cb->can_activate(undo_ctx, row_ctx, ag))
 
276
                    *rs_new = RS_NOTINSERVICE;
 
277
                else
 
278
                    *rs_new = RS_NOTREADY;
 
279
            } else {
 
280
                if (cb->can_delete && !cb->can_delete(undo_ctx, row_ctx, ag)) {
 
281
                    return SNMP_ERR_INCONSISTENTVALUE;
 
282
                }
 
283
                ag->row_deleted = 1;
 
284
            }
 
285
        }
 
286
    } else {
 
287
        /*
 
288
         * check pre-reqs for delete row
 
289
         */
 
290
        if (cb->can_delete && !cb->can_delete(undo_ctx, row_ctx, ag)) {
 
291
            return SNMP_ERR_INCONSISTENTVALUE;
 
292
        }
 
293
    }
 
294
 
 
295
    return SNMP_ERR_NOERROR;
 
296
}
 
297
 
 
298
/** @} */
 
299
 
 
300
#ifndef DOXYGEN_SHOULD_SKIP_THIS
 
301
/**********************************************************************
 
302
 **********************************************************************
 
303
 **********************************************************************
 
304
 **********************************************************************
 
305
 *                                                                    *
 
306
 *                                                                    *
 
307
 *                                                                    *
 
308
 *                                                                    *
 
309
 * EVERYTHING BELOW THIS IS PRIVATE IMPLEMENTATION DETAILS.           *
 
310
 *                                                                    *
 
311
 *                                                                    *
 
312
 *                                                                    *
 
313
 *                                                                    *
 
314
 **********************************************************************
 
315
 **********************************************************************
 
316
 **********************************************************************
 
317
 **********************************************************************/
 
318
 
 
319
/**********************************************************************
 
320
 **********************************************************************
 
321
 *                                                                    *
 
322
 *                                                                    *
 
323
 * Structures, Utility/convenience functions                          *
 
324
 *                                                                    *
 
325
 *                                                                    *
 
326
 **********************************************************************
 
327
 **********************************************************************/
 
328
/*
 
329
 * context info for SET requests
 
330
 */
 
331
typedef struct set_context_s {
 
332
    netsnmp_agent_request_info *agtreq_info;
 
333
    table_container_data *tad;
 
334
    int             status;
 
335
} set_context;
 
336
 
 
337
static void
 
338
release_netsnmp_request_group(netsnmp_index *g, void *v)
 
339
{
 
340
    netsnmp_request_group_item *tmp;
 
341
    netsnmp_request_group *group = (netsnmp_request_group *) g;
 
342
 
 
343
    if (!g)
 
344
        return;
 
345
    while (group->list) {
 
346
        tmp = group->list;
 
347
        group->list = tmp->next;
 
348
        free(tmp);
 
349
    }
 
350
 
 
351
    free(group);
 
352
}
 
353
 
 
354
static void
 
355
release_netsnmp_request_groups(void *vp)
 
356
{
 
357
    netsnmp_container *c = (netsnmp_container*)vp;
 
358
    CONTAINER_FOR_EACH(c, (netsnmp_container_obj_func*)
 
359
                       release_netsnmp_request_group, NULL);
 
360
    CONTAINER_FREE(c);
 
361
}
 
362
 
 
363
void
 
364
build_new_oid(netsnmp_handler_registration *reginfo,
 
365
              netsnmp_table_request_info *tblreq_info,
 
366
              netsnmp_index *row, netsnmp_request_info *current)
 
367
{
 
368
    oid             coloid[MAX_OID_LEN];
 
369
    int             coloid_len;
 
370
 
 
371
    if (!tblreq_info || !reginfo || !row || !current)
 
372
        return;
 
373
 
 
374
    coloid_len = reginfo->rootoid_len + 2;
 
375
    memcpy(coloid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid));
 
376
 
 
377
    /** table.entry */
 
378
    coloid[reginfo->rootoid_len] = 1;
 
379
 
 
380
    /** table.entry.column */
 
381
    coloid[reginfo->rootoid_len + 1] = tblreq_info->colnum;
 
382
 
 
383
    /** table.entry.column.index */
 
384
    memcpy(&coloid[reginfo->rootoid_len + 2], row->oids,
 
385
           row->len * sizeof(oid));
 
386
 
 
387
    snmp_set_var_objid(current->requestvb, coloid,
 
388
                       reginfo->rootoid_len + 2 + row->len);
 
389
}
 
390
 
 
391
/**********************************************************************
 
392
 **********************************************************************
 
393
 *                                                                    *
 
394
 *                                                                    *
 
395
 * GET procession functions                                           *
 
396
 *                                                                    *
 
397
 *                                                                    *
 
398
 **********************************************************************
 
399
 **********************************************************************/
 
400
int
 
401
process_get_requests(netsnmp_handler_registration *reginfo,
 
402
                     netsnmp_agent_request_info *agtreq_info,
 
403
                     netsnmp_request_info *requests,
 
404
                     table_container_data * tad)
 
405
{
 
406
    int             rc = SNMP_ERR_NOERROR;
 
407
    netsnmp_request_info *current;
 
408
    netsnmp_index *row = NULL;
 
409
    netsnmp_table_request_info *tblreq_info;
 
410
    netsnmp_variable_list *var;
 
411
 
 
412
    /*
 
413
     * Loop through each of the requests, and
 
414
     * try to find the appropriate row from the container.
 
415
     */
 
416
    for (current = requests; current; current = current->next) {
 
417
 
 
418
        var = current->requestvb;
 
419
        DEBUGMSGTL(("table_array:get",
 
420
                    "  process_get_request oid:"));
 
421
        DEBUGMSGOID(("table_array:get", var->name,
 
422
                     var->name_length));
 
423
        DEBUGMSG(("table_array:get", "\n"));
 
424
 
 
425
        /*
 
426
         * skip anything that doesn't need processing.
 
427
         */
 
428
        if (current->processed != 0) {
 
429
            DEBUGMSGTL(("table_array:get", "already processed\n"));
 
430
            continue;
 
431
        }
 
432
 
 
433
        /*
 
434
         * Get pointer to the table information for this request. This
 
435
         * information was saved by table_helper_handler. When
 
436
         * debugging, we double check a few assumptions. For example,
 
437
         * the table_helper_handler should enforce column boundaries.
 
438
         */
 
439
        tblreq_info = netsnmp_extract_table_info(current);
 
440
        netsnmp_assert(tblreq_info->colnum <= tad->tblreg_info->max_column);
 
441
 
 
442
        if ((agtreq_info->mode == MODE_GETNEXT) ||
 
443
            (agtreq_info->mode == MODE_GETBULK)) {
 
444
            /*
 
445
             * find the row
 
446
             */
 
447
            row = netsnmp_table_index_find_next_row(tad->table, tblreq_info);
 
448
            if (!row) {
 
449
                /*
 
450
                 * no results found.
 
451
                 *
 
452
                 * xxx-rks: how do we skip this entry for the next handler,
 
453
                 * but still allow it a chance to hit another handler?
 
454
                 */
 
455
                DEBUGMSGTL(("table_array:get", "no row found\n"));
 
456
                continue;
 
457
            }
 
458
 
 
459
            /*
 
460
             * * if data was found, make sure it has the column we want
 
461
             */
 
462
/* xxx-rks: add suport for sparse tables */
 
463
 
 
464
            /*
 
465
             * build new oid
 
466
             */
 
467
            build_new_oid(reginfo, tblreq_info, row, current);
 
468
 
 
469
        } /** GETNEXT/GETBULK */
 
470
        else {
 
471
            netsnmp_index index;
 
472
            index.oids = tblreq_info->index_oid;
 
473
            index.len = tblreq_info->index_oid_len;
 
474
 
 
475
            row = CONTAINER_FIND(tad->table, &index);
 
476
            if (!row) {
 
477
                DEBUGMSGTL(("table_array:get", "no row found\n"));
 
478
                netsnmp_set_request_error(agtreq_info, current,
 
479
                                          SNMP_NOSUCHINSTANCE);
 
480
                continue;
 
481
            }
 
482
        } /** GET */
 
483
 
 
484
        /*
 
485
         * get the data
 
486
         */
 
487
        rc = tad->cb->get_value(current, row, tblreq_info);
 
488
 
 
489
    } /** for ( ... requests ... ) */
 
490
 
 
491
    return rc;
 
492
}
 
493
 
 
494
/**********************************************************************
 
495
 **********************************************************************
 
496
 *                                                                    *
 
497
 *                                                                    *
 
498
 * SET procession functions                                           *
 
499
 *                                                                    *
 
500
 *                                                                    *
 
501
 **********************************************************************
 
502
 **********************************************************************/
 
503
 
 
504
void
 
505
group_requests(netsnmp_agent_request_info *agtreq_info,
 
506
               netsnmp_request_info *requests,
 
507
               netsnmp_container *request_group, table_container_data * tad)
 
508
{
 
509
    netsnmp_table_request_info *tblreq_info;
 
510
    netsnmp_variable_list *var;
 
511
    netsnmp_index *row, *tmp, index;
 
512
    netsnmp_request_info *current;
 
513
    netsnmp_request_group *g;
 
514
    netsnmp_request_group_item *i;
 
515
 
 
516
    for (current = requests; current; current = current->next) {
 
517
 
 
518
        var = current->requestvb;
 
519
 
 
520
        /*
 
521
         * skip anything that doesn't need processing.
 
522
         */
 
523
        if (current->processed != 0) {
 
524
            DEBUGMSGTL(("table_array:group",
 
525
                        "already processed\n"));
 
526
            continue;
 
527
        }
 
528
 
 
529
        /*
 
530
         * 3.2.1 Setup and paranoia
 
531
         * *
 
532
         * * Get pointer to the table information for this request. This
 
533
         * * information was saved by table_helper_handler. When
 
534
         * * debugging, we double check a few assumptions. For example,
 
535
         * * the table_helper_handler should enforce column boundaries.
 
536
         */
 
537
        row = NULL;
 
538
        tblreq_info = netsnmp_extract_table_info(current);
 
539
        netsnmp_assert(tblreq_info->colnum <= tad->tblreg_info->max_column);
 
540
 
 
541
        /*
 
542
         * search for index
 
543
         */
 
544
        index.oids = tblreq_info->index_oid;
 
545
        index.len = tblreq_info->index_oid_len;
 
546
        tmp = CONTAINER_FIND(request_group, &index);
 
547
        if (tmp) {
 
548
            DEBUGMSGTL(("table_array:group",
 
549
                        "    existing group:"));
 
550
            DEBUGMSGOID(("table_array:group", index.oids,
 
551
                         index.len));
 
552
            DEBUGMSG(("table_array:group", "\n"));
 
553
            g = (netsnmp_request_group *) tmp;
 
554
            i = SNMP_MALLOC_TYPEDEF(netsnmp_request_group_item);
 
555
            i->ri = current;
 
556
            i->tri = tblreq_info;
 
557
            i->next = g->list;
 
558
            g->list = i;
 
559
 
 
560
            /** xxx-rks: store map of colnum to request */
 
561
            continue;
 
562
        }
 
563
 
 
564
        DEBUGMSGTL(("table_array:group", "    new group"));
 
565
        DEBUGMSGOID(("table_array:group", index.oids,
 
566
                     index.len));
 
567
        DEBUGMSG(("table_array:group", "\n"));
 
568
        g = SNMP_MALLOC_TYPEDEF(netsnmp_request_group);
 
569
        i = SNMP_MALLOC_TYPEDEF(netsnmp_request_group_item);
 
570
        g->list = i;
 
571
        g->table = tad->table;
 
572
        i->ri = current;
 
573
        i->tri = tblreq_info;
 
574
        /** xxx-rks: store map of colnum to request */
 
575
 
 
576
        /*
 
577
         * search for row. all changes are made to the original row,
 
578
         * later, we'll make a copy in undo_info before we start processing.
 
579
         */
 
580
        row = g->existing_row = CONTAINER_FIND(tad->table, &index);
 
581
        if (!g->existing_row) {
 
582
            if (!tad->cb->create_row) {
 
583
                if(MODE_IS_SET(agtreq_info->mode))
 
584
                    netsnmp_set_request_error(agtreq_info, current,
 
585
                                              SNMP_ERR_NOTWRITABLE);
 
586
                else
 
587
                    netsnmp_set_request_error(agtreq_info, current,
 
588
                                              SNMP_NOSUCHINSTANCE);
 
589
                free(g);
 
590
                free(i);
 
591
                continue;
 
592
            }
 
593
            /** use undo_info temporarily */
 
594
            row = g->existing_row = tad->cb->create_row(&index);
 
595
            if (!row) {
 
596
                /* xxx-rks : parameter to create_row to allow
 
597
                 * for better error reporting. */
 
598
                netsnmp_set_request_error(agtreq_info, current,
 
599
                                          SNMP_ERR_GENERR);
 
600
                free(g);
 
601
                free(i);
 
602
                continue;
 
603
            }
 
604
            g->row_created = 1;
 
605
        }
 
606
 
 
607
        g->index.oids = row->oids;
 
608
        g->index.len = row->len;
 
609
 
 
610
        CONTAINER_INSERT(request_group, g);
 
611
 
 
612
    } /** for( current ... ) */
 
613
}
 
614
 
 
615
static void
 
616
process_set_group(netsnmp_index *o, void *c)
 
617
{
 
618
    /* xxx-rks: should we continue processing after an error?? */
 
619
    set_context           *context = (set_context *) c;
 
620
    netsnmp_request_group *ag = (netsnmp_request_group *) o;
 
621
    int                    rc = SNMP_ERR_NOERROR;
 
622
 
 
623
    switch (context->agtreq_info->mode) {
 
624
 
 
625
    case MODE_SET_RESERVE1:/** -> SET_RESERVE2 || SET_FREE */
 
626
 
 
627
        /*
 
628
         * if not a new row, save undo info
 
629
         */
 
630
        if (ag->row_created == 0) {
 
631
            ag->undo_info = context->tad->cb->duplicate_row(ag->existing_row);
 
632
            if (NULL == ag->undo_info) {
 
633
                rc = SNMP_ERR_RESOURCEUNAVAILABLE;
 
634
                break;
 
635
            }
 
636
        }
 
637
        
 
638
        if (context->tad->cb->set_reserve1)
 
639
            context->tad->cb->set_reserve1(ag);
 
640
        break;
 
641
 
 
642
    case MODE_SET_RESERVE2:/** -> SET_ACTION || SET_FREE */
 
643
        if (context->tad->cb->set_reserve2)
 
644
            context->tad->cb->set_reserve2(ag);
 
645
        break;
 
646
 
 
647
    case MODE_SET_ACTION:/** -> SET_COMMIT || SET_UNDO */
 
648
        if (context->tad->cb->set_action)
 
649
            context->tad->cb->set_action(ag);
 
650
        break;
 
651
 
 
652
    case MODE_SET_COMMIT:/** FINAL CHANCE ON SUCCESS */
 
653
        if (ag->row_created == 0) {
 
654
            /*
 
655
             * this is an existing row, has it been deleted?
 
656
             */
 
657
            if (ag->row_deleted == 1) {
 
658
                DEBUGMSGT((TABLE_ARRAY_NAME, "action: deleting row\n"));
 
659
                if (CONTAINER_REMOVE(ag->table, ag->existing_row) != 0) {
 
660
                    rc = SNMP_ERR_COMMITFAILED;
 
661
                    break;
 
662
                }
 
663
            }
 
664
        } else if (ag->row_deleted == 0) {
 
665
            /*
 
666
             * new row (that hasn't been deleted) should be inserted
 
667
             */
 
668
            DEBUGMSGT((TABLE_ARRAY_NAME, "action: inserting row\n"));
 
669
            if (CONTAINER_INSERT(ag->table, ag->existing_row) != 0) {
 
670
                rc = SNMP_ERR_COMMITFAILED;
 
671
                break;
 
672
            }
 
673
        }
 
674
 
 
675
        if (context->tad->cb->set_commit)
 
676
            context->tad->cb->set_commit(ag);
 
677
 
 
678
        /** no more use for undo_info, so free it */
 
679
        if (ag->undo_info) {
 
680
            context->tad->cb->delete_row(ag->undo_info);
 
681
            ag->undo_info = NULL;
 
682
        }
 
683
 
 
684
#if 0
 
685
        /* XXX-rks: finish row cooperative notifications
 
686
         * if the table has requested it, send cooperative notifications
 
687
         * for row operations.
 
688
         */
 
689
        if (context->tad->notifications) {
 
690
            if (ag->undo_info) {
 
691
                if (!ag->existing_row)
 
692
                    netsnmp_monitor_notify(EVENT_ROW_DEL);
 
693
                else
 
694
                    netsnmp_monitor_notify(EVENT_ROW_MOD);
 
695
            }
 
696
            else
 
697
                netsnmp_monitor_notify(EVENT_ROW_ADD);
 
698
        }
 
699
#endif
 
700
        break;
 
701
 
 
702
    case MODE_SET_FREE:/** FINAL CHANCE ON FAILURE */
 
703
        if (context->tad->cb->set_free)
 
704
            context->tad->cb->set_free(ag);
 
705
 
 
706
        /** no more use for undo_info, so free it */
 
707
        if (ag->row_created == 1) {
 
708
            context->tad->cb->delete_row(ag->existing_row);
 
709
            ag->existing_row = NULL;
 
710
        }
 
711
        else {
 
712
            context->tad->cb->delete_row(ag->undo_info);
 
713
            ag->undo_info = NULL;
 
714
        }
 
715
        break;
 
716
 
 
717
    case MODE_SET_UNDO:/** FINAL CHANCE ON FAILURE */
 
718
        if (ag->row_created == 0) {
 
719
            /*
 
720
             * this row existed before.
 
721
             */
 
722
            if (ag->row_deleted == 1) {
 
723
                /*
 
724
                 * re-insert undo_info
 
725
                 */
 
726
                DEBUGMSGT((TABLE_ARRAY_NAME, "undo: re-inserting row\n"));
 
727
                if (CONTAINER_INSERT(ag->table, ag->existing_row) != 0) {
 
728
                    rc = SNMP_ERR_UNDOFAILED;
 
729
                    break;
 
730
                }
 
731
            }
 
732
        } else if (ag->row_deleted == 0) {
 
733
            /*
 
734
             * new row that wasn't deleted should be removed
 
735
             */
 
736
            DEBUGMSGT((TABLE_ARRAY_NAME, "undo: removing new row\n"));
 
737
            if (CONTAINER_REMOVE(ag->table, ag->existing_row) != 0) {
 
738
                rc = SNMP_ERR_UNDOFAILED;
 
739
                break;
 
740
            }
 
741
        }
 
742
 
 
743
        /*
 
744
         * status already set - don't change it now
 
745
         */
 
746
        if (context->tad->cb->set_undo)
 
747
            context->tad->cb->set_undo(ag);
 
748
 
 
749
        /*
 
750
         * no more use for undo_info, so free it
 
751
         */
 
752
        if (ag->row_created == 0) {
 
753
            /*
 
754
             * restore old values
 
755
             */
 
756
            context->tad->cb->row_copy(ag->existing_row, ag->undo_info);
 
757
            context->tad->cb->delete_row(ag->undo_info);
 
758
            ag->undo_info = NULL;
 
759
        }
 
760
        else {
 
761
            context->tad->cb->delete_row(ag->existing_row);
 
762
            ag->existing_row = NULL;
 
763
        }
 
764
        break;
 
765
 
 
766
    default:
 
767
        snmp_log(LOG_ERR, "unknown mode processing SET for "
 
768
                 "netsnmp_table_array_helper_handler\n");
 
769
        rc = SNMP_ERR_GENERR;
 
770
        break;
 
771
    }
 
772
    
 
773
    if (rc)
 
774
        netsnmp_set_request_error(context->agtreq_info,
 
775
                                  ag->list->ri, rc);
 
776
                                               
 
777
}
 
778
 
 
779
int
 
780
process_set_requests(netsnmp_agent_request_info *agtreq_info,
 
781
                     netsnmp_request_info *requests,
 
782
                     table_container_data * tad, char *handler_name)
 
783
{
 
784
    set_context         context;
 
785
    netsnmp_container  *request_group;
 
786
 
 
787
    /*
 
788
     * create and save structure for set info
 
789
     */
 
790
    request_group = (netsnmp_container*) netsnmp_agent_get_list_data
 
791
        (agtreq_info, handler_name);
 
792
    if (request_group == NULL) {
 
793
        netsnmp_data_list *tmp;
 
794
        request_group = netsnmp_container_find("request_group:"
 
795
                                               "table_container");
 
796
        request_group->compare = netsnmp_compare_netsnmp_index;
 
797
        request_group->ncompare = netsnmp_ncompare_netsnmp_index;
 
798
 
 
799
        DEBUGMSGTL(("table_array", "Grouping requests by oid\n"));
 
800
 
 
801
        tmp = netsnmp_create_data_list(handler_name,
 
802
                                       request_group,
 
803
                                       release_netsnmp_request_groups);
 
804
        netsnmp_agent_add_list_data(agtreq_info, tmp);
 
805
        /*
 
806
         * group requests.
 
807
         */
 
808
        group_requests(agtreq_info, requests, request_group, tad);
 
809
    }
 
810
 
 
811
    /*
 
812
     * process each group one at a time
 
813
     */
 
814
    context.agtreq_info = agtreq_info;
 
815
    context.tad = tad;
 
816
    context.status = SNMP_ERR_NOERROR;
 
817
    CONTAINER_FOR_EACH(request_group,
 
818
                       (netsnmp_container_obj_func*)process_set_group,
 
819
                       &context);
 
820
 
 
821
    return context.status;
 
822
}
 
823
 
 
824
 
 
825
/**********************************************************************
 
826
 **********************************************************************
 
827
 *                                                                    *
 
828
 *                                                                    *
 
829
 * netsnmp_table_array_helper_handler()                               *
 
830
 *                                                                    *
 
831
 *                                                                    *
 
832
 **********************************************************************
 
833
 **********************************************************************/
 
834
int
 
835
netsnmp_table_array_helper_handler(netsnmp_mib_handler *handler,
 
836
                                   netsnmp_handler_registration *reginfo,
 
837
                                   netsnmp_agent_request_info *agtreq_info,
 
838
                                   netsnmp_request_info *requests)
 
839
{
 
840
 
 
841
    /*
 
842
     * First off, get our pointer from the handler. This
 
843
     * lets us get to the table registration information we
 
844
     * saved in get_table_array_handler(), as well as the
 
845
     * container where the actual table data is stored.
 
846
     */
 
847
    int             rc = SNMP_ERR_NOERROR;
 
848
    table_container_data *tad = (table_container_data *)handler->myvoid;
 
849
 
 
850
    if (agtreq_info->mode < 0 || agtreq_info->mode > 5) {
 
851
        DEBUGMSGTL(("table_array", "Mode %d, Got request:\n",
 
852
                    agtreq_info->mode));
 
853
    } else {
 
854
        DEBUGMSGTL(("table_array", "Mode %s, Got request:\n",
 
855
                    mode_name[agtreq_info->mode]));
 
856
    }
 
857
 
 
858
    if (MODE_IS_SET(agtreq_info->mode)) {
 
859
        /*
 
860
         * netsnmp_mutex_lock(&tad->lock);
 
861
         */
 
862
        rc = process_set_requests(agtreq_info, requests,
 
863
                                  tad, handler->handler_name);
 
864
        /*
 
865
         * netsnmp_mutex_unlock(&tad->lock);
 
866
         */
 
867
    } else
 
868
        rc = process_get_requests(reginfo, agtreq_info, requests, tad);
 
869
 
 
870
    if (rc != SNMP_ERR_NOERROR) {
 
871
        DEBUGMSGTL(("table_array", "processing returned rc %d\n", rc));
 
872
    }
 
873
    
 
874
    /*
 
875
     * Now we've done our processing. If there is another handler below us,
 
876
     * call them.
 
877
     */
 
878
    if (handler->next) {
 
879
        rc = netsnmp_call_next_handler(handler, reginfo, agtreq_info, requests);
 
880
        if (rc != SNMP_ERR_NOERROR) {
 
881
            DEBUGMSGTL(("table_array", "next handler returned rc %d\n", rc));
 
882
        }
 
883
    }
 
884
    
 
885
    return rc;
 
886
}
 
887
#endif /** DOXYGEN_SHOULD_SKIP_THIS */