Coverage Report

Created: 2024-02-25 06:16

/src/net-snmp/agent/helpers/table_data.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Portions of this file are subject to the following copyright(s).  See
3
 * the Net-SNMP's COPYING file for more details and other copyrights
4
 * that may apply:
5
 *
6
 * Portions of this file are copyrighted by:
7
 * Copyright (c) 2016 VMware, Inc. All rights reserved.
8
 * Use is subject to license terms specified in the COPYING file
9
 * distributed with the Net-SNMP package.
10
 */
11
12
#include <net-snmp/net-snmp-config.h>
13
#include <net-snmp/net-snmp-features.h>
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_data.h>
19
20
#ifdef HAVE_STRING_H
21
#include <string.h>
22
#else
23
#include <strings.h>
24
#endif
25
26
#include <net-snmp/agent/table.h>
27
#include <net-snmp/agent/read_only.h>
28
29
netsnmp_feature_child_of(table_data_all, mib_helpers);
30
31
netsnmp_feature_child_of(table_data, table_data_all);
32
netsnmp_feature_child_of(register_read_only_table_data, table_data_all);
33
netsnmp_feature_child_of(extract_table_row_data, table_data_all);
34
netsnmp_feature_child_of(insert_table_row, table_data_all);
35
netsnmp_feature_child_of(table_data_delete_table, table_data_all);
36
37
netsnmp_feature_child_of(table_data_extras, table_data_all);
38
39
netsnmp_feature_child_of(table_data_create_table, table_data_extras);
40
netsnmp_feature_child_of(table_data_create_row, table_data_extras);
41
netsnmp_feature_child_of(table_data_copy_row, table_data_extras);
42
netsnmp_feature_child_of(table_data_remove_delete_row, table_data_extras);
43
netsnmp_feature_child_of(table_data_unregister, table_data_extras);
44
netsnmp_feature_child_of(table_data_row_count, table_data_extras);
45
netsnmp_feature_child_of(table_data_row_operations, table_data_extras);
46
netsnmp_feature_child_of(table_data_row_first, table_data_extras);
47
48
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA
49
50
/** @defgroup table_data table_data
51
 *  Helps you implement a table with datamatted storage.
52
 *  @ingroup table
53
 *
54
 *  This helper is obsolete.  If you are writing a new module, please
55
 *  consider using the table_tdata helper instead.
56
 *
57
 *  This helper helps you implement a table where all the indexes are
58
 *  expected to be stored within the agent itself and not in some
59
 *  external storage location.  It can be used to store a list of
60
 *  rows, where a row consists of the indexes to the table and a
61
 *  generic data pointer.  You can then implement a subhandler which
62
 *  is passed the exact row definition and data it must return data
63
 *  for or accept data for.  Complex GETNEXT handling is greatly
64
 *  simplified in this case.
65
 *
66
 *  @{
67
 */
68
69
/* ==================================
70
 *
71
 * Table Data API: Table maintenance
72
 *
73
 * ================================== */
74
75
/*
76
 * generates the index portion of an table oid from a varlist.
77
 */
78
void
79
netsnmp_table_data_generate_index_oid(netsnmp_table_row *row)
80
0
{
81
0
    build_oid(&row->index_oid, &row->index_oid_len, NULL, 0, row->indexes);
82
0
}
83
84
/** creates and returns a pointer to table data set */
85
netsnmp_table_data *
86
netsnmp_create_table_data(const char *name)
87
0
{
88
0
    netsnmp_table_data *table = SNMP_MALLOC_TYPEDEF(netsnmp_table_data);
89
0
    if (name && table)
90
0
        table->name = strdup(name);
91
0
    return table;
92
0
}
93
94
/** creates and returns a pointer to table data set */
95
netsnmp_table_row *
96
netsnmp_create_table_data_row(void)
97
0
{
98
0
    netsnmp_table_row *row = SNMP_MALLOC_TYPEDEF(netsnmp_table_row);
99
0
    return row;
100
0
}
101
102
/** clones a data row. DOES NOT CLONE THE CONTAINED DATA. */
103
netsnmp_table_row *
104
netsnmp_table_data_clone_row(netsnmp_table_row *row)
105
0
{
106
0
    netsnmp_table_row *newrow = NULL;
107
0
    if (!row)
108
0
        return NULL;
109
110
0
    newrow = netsnmp_memdup(row, sizeof(netsnmp_table_row));
111
0
    if (!newrow)
112
0
        return NULL;
113
114
0
    if (row->indexes) {
115
0
        newrow->indexes = snmp_clone_varbind(newrow->indexes);
116
0
        if (!newrow->indexes) {
117
0
            free(newrow);
118
0
            return NULL;
119
0
        }
120
0
    }
121
122
0
    if (row->index_oid) {
123
0
        newrow->index_oid =
124
0
            snmp_duplicate_objid(row->index_oid, row->index_oid_len);
125
0
        if (!newrow->index_oid) {
126
0
            free(newrow->indexes);
127
0
            free(newrow);
128
0
            return NULL;
129
0
        }
130
0
    }
131
132
0
    return newrow;
133
0
}
134
135
/** deletes a row's memory.
136
 *  returns the void data that it doesn't know how to delete. */
137
void           *
138
netsnmp_table_data_delete_row(netsnmp_table_row *row)
139
0
{
140
0
    void           *data;
141
142
0
    if (!row)
143
0
        return NULL;
144
145
    /*
146
     * free the memory we can 
147
     */
148
0
    if (row->indexes)
149
0
        snmp_free_varbind(row->indexes);
150
0
    SNMP_FREE(row->index_oid);
151
0
    data = row->data;
152
0
    free(row);
153
154
    /*
155
     * return the void * pointer 
156
     */
157
0
    return data;
158
0
}
159
160
/**
161
 * Adds a row of data to a given table (stored in proper lexographical order).
162
 *
163
 * returns SNMPERR_SUCCESS on successful addition.
164
 *      or SNMPERR_GENERR  on failure (E.G., indexes already existed)
165
 */
166
int
167
netsnmp_table_data_add_row(netsnmp_table_data *table,
168
                           netsnmp_table_row *row)
169
0
{
170
0
    int rc, dup = 0;
171
0
    netsnmp_table_row *nextrow = NULL, *prevrow;
172
173
0
    if (!row || !table)
174
0
        return SNMPERR_GENERR;
175
176
0
    if (row->indexes)
177
0
        netsnmp_table_data_generate_index_oid(row);
178
179
    /*
180
     * we don't store the index info as it
181
     * takes up memory. 
182
     */
183
0
    if (!table->store_indexes) {
184
0
        snmp_free_varbind(row->indexes);
185
0
        row->indexes = NULL;
186
0
    }
187
188
0
    if (!row->index_oid) {
189
0
        snmp_log(LOG_ERR,
190
0
                 "illegal data attempted to be added to table %s (no index)\n",
191
0
                 table->name);
192
0
        return SNMPERR_GENERR;
193
0
    }
194
195
    /*
196
     * check for simple append
197
     */
198
0
    if ((prevrow = table->last_row) != NULL) {
199
0
        rc = snmp_oid_compare(prevrow->index_oid, prevrow->index_oid_len,
200
0
                              row->index_oid, row->index_oid_len);
201
0
        if (0 == rc)
202
0
            dup = 1;
203
0
    }
204
0
    else
205
0
        rc = 1;
206
    
207
    /*
208
     * if no last row, or newrow < last row, search the table and
209
     * insert it into the table in the proper oid-lexographical order 
210
     */
211
0
    if (rc > 0) {
212
0
        for (nextrow = table->first_row, prevrow = NULL;
213
0
             nextrow != NULL; prevrow = nextrow, nextrow = nextrow->next) {
214
0
            if (NULL == nextrow->index_oid) {
215
0
                DEBUGMSGT(("table_data_add_data", "row doesn't have index!\n"));
216
                /** xxx-rks: remove invalid row? */
217
0
                continue;
218
0
            }
219
0
            rc = snmp_oid_compare(nextrow->index_oid, nextrow->index_oid_len,
220
0
                                  row->index_oid, row->index_oid_len);
221
0
            if(rc > 0)
222
0
                break;
223
0
            if (0 == rc) {
224
0
                dup = 1;
225
0
                break;
226
0
            }
227
0
        }
228
0
    }
229
230
0
    if (dup) {
231
        /*
232
         * exact match.  Duplicate entries illegal 
233
         */
234
0
        snmp_log(LOG_WARNING,
235
0
                 "duplicate table data attempted to be entered. row exists\n");
236
0
        return SNMPERR_GENERR;
237
0
    }
238
239
    /*
240
     * ok, we have the location of where it should go 
241
     */
242
    /*
243
     * (after prevrow, and before nextrow) 
244
     */
245
0
    row->next = nextrow;
246
0
    row->prev = prevrow;
247
248
0
    if (row->next)
249
0
        row->next->prev = row;
250
251
0
    if (row->prev)
252
0
        row->prev->next = row;
253
254
0
    if (NULL == row->prev)      /* it's the (new) first row */
255
0
        table->first_row = row;
256
0
    if (NULL == row->next)      /* it's the last row */
257
0
        table->last_row = row;
258
259
0
    DEBUGMSGTL(("table_data_add_data", "added something...\n"));
260
261
0
    return SNMPERR_SUCCESS;
262
0
}
263
264
/** swaps out origrow with newrow.  This does *not* delete/free anything! */
265
void
266
netsnmp_table_data_replace_row(netsnmp_table_data *table,
267
                               netsnmp_table_row *origrow,
268
                               netsnmp_table_row *newrow)
269
0
{
270
0
    netsnmp_table_data_remove_row(table, origrow);
271
0
    netsnmp_table_data_add_row(table, newrow);
272
0
}
273
274
/**
275
 * removes a row of data to a given table and returns it (no free's called)
276
 *
277
 * returns the row pointer itself on successful removing.
278
 *      or NULL on failure (bad arguments)
279
 */
280
netsnmp_table_row *
281
netsnmp_table_data_remove_row(netsnmp_table_data *table,
282
                              netsnmp_table_row *row)
283
0
{
284
0
    if (!row || !table)
285
0
        return NULL;
286
287
0
    if (row->prev)
288
0
        row->prev->next = row->next;
289
0
    else
290
0
        table->first_row = row->next;
291
292
0
    if (row->next)
293
0
        row->next->prev = row->prev;
294
0
    else
295
0
        table->last_row = row->prev;
296
297
0
    return row;
298
0
}
299
300
/**
301
 * removes and frees a row of data to a given table and returns the void *
302
 *
303
 * returns the void * data on successful deletion.
304
 *      or NULL on failure (bad arguments)
305
 */
306
void           *
307
netsnmp_table_data_remove_and_delete_row(netsnmp_table_data *table,
308
                                         netsnmp_table_row *row)
309
0
{
310
0
    if (!row || !table)
311
0
        return NULL;
312
313
    /*
314
     * remove it from the list 
315
     */
316
0
    netsnmp_table_data_remove_row(table, row);
317
0
    return netsnmp_table_data_delete_row(row);
318
0
}
319
320
    /* =====================================
321
     * Generic API - mostly renamed wrappers
322
     * ===================================== */
323
324
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_TABLE
325
netsnmp_table_data *
326
netsnmp_table_data_create_table(const char *name, long flags)
327
0
{
328
0
    return netsnmp_create_table_data( name );
329
0
}
330
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_TABLE */
331
332
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_DELETE_TABLE
333
void
334
netsnmp_table_data_delete_table( netsnmp_table_data *table )
335
0
{
336
0
    netsnmp_table_row *row, *nextrow;
337
338
0
    if (!table)
339
0
        return;
340
341
0
    snmp_free_varbind(table->indexes_template);
342
0
    table->indexes_template = NULL;
343
344
0
    for (row = table->first_row; row; row=nextrow) {
345
0
        nextrow   = row->next;
346
0
        row->next = NULL;
347
0
        netsnmp_table_data_delete_row(row);
348
        /* Can't delete table-specific entry memory */
349
0
    }
350
0
    table->first_row = NULL;
351
352
0
    SNMP_FREE(table->name);
353
0
    SNMP_FREE(table);
354
0
    return;
355
0
}
356
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_DELETE_TABLE */
357
358
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_ROW
359
netsnmp_table_row *
360
netsnmp_table_data_create_row( void* entry )
361
0
{
362
0
    netsnmp_table_row *row = SNMP_MALLOC_TYPEDEF(netsnmp_table_row);
363
0
    if (row)
364
0
        row->data = entry;
365
0
    return row;
366
0
}
367
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_ROW */
368
369
    /* netsnmp_table_data_clone_row() defined above */
370
371
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_COPY_ROW
372
int
373
netsnmp_table_data_copy_row( netsnmp_table_row  *old_row,
374
                             netsnmp_table_row  *new_row )
375
0
{
376
0
    if (!old_row || !new_row)
377
0
        return -1;
378
379
0
    memcpy(new_row, old_row, sizeof(netsnmp_table_row));
380
381
0
    if (old_row->indexes)
382
0
        new_row->indexes = snmp_clone_varbind(old_row->indexes);
383
0
    if (old_row->index_oid)
384
0
        new_row->index_oid =
385
0
            snmp_duplicate_objid(old_row->index_oid, old_row->index_oid_len);
386
    /* XXX - Doesn't copy table-specific row structure */
387
0
    return 0;
388
0
}
389
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_COPY_ROW */
390
391
    /*
392
     * netsnmp_table_data_delete_row()
393
     * netsnmp_table_data_add_row()
394
     * netsnmp_table_data_replace_row()
395
     * netsnmp_table_data_remove_row()
396
     *     all defined above
397
     */
398
399
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_REMOVE_DELETE_ROW
400
void *
401
netsnmp_table_data_remove_delete_row(netsnmp_table_data *table,
402
                                     netsnmp_table_row *row)
403
0
{
404
0
    return netsnmp_table_data_remove_and_delete_row(table, row);
405
0
}
406
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_REMOVE_DELETE_ROW */
407
408
409
/* ==================================
410
 *
411
 * Table Data API: MIB maintenance
412
 *
413
 * ================================== */
414
415
/** Creates a table_data handler and returns it */
416
netsnmp_mib_handler *
417
netsnmp_get_table_data_handler(netsnmp_table_data *table)
418
0
{
419
0
    netsnmp_mib_handler *ret = NULL;
420
421
0
    if (!table) {
422
0
        snmp_log(LOG_INFO,
423
0
                 "netsnmp_get_table_data_handler(NULL) called\n");
424
0
        return NULL;
425
0
    }
426
427
0
    ret =
428
0
        netsnmp_create_handler(TABLE_DATA_NAME,
429
0
                               netsnmp_table_data_helper_handler);
430
0
    if (ret) {
431
0
        ret->flags |= MIB_HANDLER_AUTO_NEXT;
432
0
        ret->myvoid = (void *) table;
433
0
    }
434
0
    return ret;
435
0
}
436
437
/** registers a handler as a data table.
438
 *  If table_info != NULL, it registers it as a normal table too. */
439
int
440
netsnmp_register_table_data(netsnmp_handler_registration *reginfo,
441
                            netsnmp_table_data *table,
442
                            netsnmp_table_registration_info *table_info)
443
0
{
444
0
    netsnmp_mib_handler *handler = netsnmp_get_table_data_handler(table);
445
0
    if (!table || !handler ||
446
0
        (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
447
0
        snmp_log(LOG_ERR, "could not create table data handler\n");
448
0
        netsnmp_handler_free(handler);
449
0
        netsnmp_handler_registration_free(reginfo);
450
0
        return MIB_REGISTRATION_FAILED;
451
0
    }
452
453
0
    return netsnmp_register_table(reginfo, table_info);
454
0
}
455
456
457
#ifndef NETSNMP_FEATURE_REMOVE_REGISTER_READ_ONLY_TABLE_DATA
458
/** registers a handler as a read-only data table
459
 *  If table_info != NULL, it registers it as a normal table too. */
460
int
461
netsnmp_register_read_only_table_data(netsnmp_handler_registration *reginfo,
462
                                      netsnmp_table_data *table,
463
                                      netsnmp_table_registration_info *table_info)
464
0
{
465
0
    netsnmp_mib_handler *handler = netsnmp_get_read_only_handler();
466
0
    if (!handler ||
467
0
        (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
468
0
        snmp_log(LOG_ERR, "could not create read only table data handler\n");
469
0
        netsnmp_handler_free(handler);
470
0
        netsnmp_handler_registration_free(reginfo);
471
0
        return MIB_REGISTRATION_FAILED;
472
0
    }
473
474
0
    return netsnmp_register_table_data(reginfo, table, table_info);
475
0
}
476
#endif /* NETSNMP_FEATURE_REMOVE_REGISTER_READ_ONLY_TABLE_DATA */
477
478
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_UNREGISTER
479
int
480
netsnmp_unregister_table_data(netsnmp_handler_registration *reginfo)
481
0
{
482
    /* free table; */
483
0
    return netsnmp_unregister_table(reginfo);
484
0
}
485
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_UNREGISTER */
486
487
/*
488
 * The helper handler that takes care of passing a specific row of
489
 * data down to the lower handler(s).  It sets request->processed if
490
 * the request should not be handled.
491
 */
492
int
493
netsnmp_table_data_helper_handler(netsnmp_mib_handler *handler,
494
                                  netsnmp_handler_registration *reginfo,
495
                                  netsnmp_agent_request_info *reqinfo,
496
                                  netsnmp_request_info *requests)
497
0
{
498
0
    netsnmp_table_data *table = (netsnmp_table_data *) handler->myvoid;
499
0
    netsnmp_request_info *request;
500
0
    int             valid_request = 0;
501
0
    netsnmp_table_row *row;
502
0
    netsnmp_table_request_info *table_info;
503
0
    netsnmp_table_registration_info *table_reg_info =
504
0
        netsnmp_find_table_registration_info(reginfo);
505
0
    int             result, regresult;
506
0
    int             oldmode;
507
508
0
    for (request = requests; request; request = request->next) {
509
0
        if (request->processed)
510
0
            continue;
511
512
0
        table_info = netsnmp_extract_table_info(request);
513
0
        if (!table_info)
514
0
            continue;           /* ack */
515
0
        switch (reqinfo->mode) {
516
0
        case MODE_GET:
517
0
        case MODE_GETNEXT:
518
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
519
0
        case MODE_SET_RESERVE1:
520
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
521
0
            netsnmp_request_add_list_data(request,
522
0
                                      netsnmp_create_data_list(
523
0
                                          TABLE_DATA_TABLE, table, NULL));
524
0
        }
525
526
        /*
527
         * find the row in question 
528
         */
529
0
        switch (reqinfo->mode) {
530
0
        case MODE_GETNEXT:
531
0
        case MODE_GETBULK:     /* XXXWWW */
532
0
            if (request->requestvb->type != ASN_NULL)
533
0
                continue;
534
            /*
535
             * loop through data till we find the next row 
536
             */
537
0
            result = snmp_oid_compare(request->requestvb->name,
538
0
                                      request->requestvb->name_length,
539
0
                                      reginfo->rootoid,
540
0
                                      reginfo->rootoid_len);
541
0
            regresult = snmp_oid_compare(request->requestvb->name,
542
0
                                         SNMP_MIN(request->requestvb->
543
0
                                                  name_length,
544
0
                                                  reginfo->rootoid_len),
545
0
                                         reginfo->rootoid,
546
0
                                         reginfo->rootoid_len);
547
0
            if (regresult == 0
548
0
                && request->requestvb->name_length < reginfo->rootoid_len)
549
0
                regresult = -1;
550
551
0
            if (result < 0 || 0 == result) {
552
                /*
553
                 * before us entirely, return the first 
554
                 */
555
0
                row = table->first_row;
556
0
                table_info->colnum = table_reg_info->min_column;
557
0
            } else if (regresult == 0 && request->requestvb->name_length ==
558
0
                       reginfo->rootoid_len + 1 &&
559
                       /* entry node must be 1, but any column is ok */
560
0
                       request->requestvb->name[reginfo->rootoid_len] == 1) {
561
                /*
562
                 * exactly to the entry 
563
                 */
564
0
                row = table->first_row;
565
0
                table_info->colnum = table_reg_info->min_column;
566
0
            } else if (regresult == 0 && request->requestvb->name_length ==
567
0
                       reginfo->rootoid_len + 2 &&
568
                       /* entry node must be 1, but any column is ok */
569
0
                       request->requestvb->name[reginfo->rootoid_len] == 1) {
570
                /*
571
                 * exactly to the column 
572
                 */
573
0
                row = table->first_row;
574
0
            } else {
575
                /*
576
                 * loop through all rows looking for the first one
577
                 * that is equal to the request or greater than it 
578
                 */
579
0
                for (row = table->first_row; row; row = row->next) {
580
                    /*
581
                     * compare the index of the request to the row 
582
                     */
583
0
                    result =
584
0
                        snmp_oid_compare(row->index_oid,
585
0
                                         row->index_oid_len,
586
0
                                         request->requestvb->name + 2 +
587
0
                                         reginfo->rootoid_len,
588
0
                                         request->requestvb->name_length -
589
0
                                         2 - reginfo->rootoid_len);
590
0
                    if (result == 0) {
591
                        /*
592
                         * equal match, return the next row 
593
                         */
594
0
                        row = row->next;
595
0
                        break;
596
0
                    } else if (result > 0) {
597
                        /*
598
                         * the current row is greater than the
599
                         * request, use it 
600
                         */
601
0
                        break;
602
0
                    }
603
0
                }
604
0
            }
605
0
            if (!row) {
606
0
                table_info->colnum++;
607
0
                if (table_info->colnum <= table_reg_info->max_column) {
608
0
                    row = table->first_row;
609
0
                }
610
0
            }
611
0
            if (row) {
612
0
                valid_request = 1;
613
0
                netsnmp_request_add_list_data(request,
614
0
                                              netsnmp_create_data_list
615
0
                                              (TABLE_DATA_ROW, row,
616
0
                                               NULL));
617
                /*
618
                 * Set the name appropriately, so we can pass this
619
                 *  request on as a simple GET request
620
                 */
621
0
                netsnmp_table_data_build_result(reginfo, reqinfo, request,
622
0
                                                row,
623
0
                                                table_info->colnum,
624
0
                                                ASN_NULL, NULL, 0);
625
0
            } else {            /* no decent result found.  Give up. It's beyond us. */
626
0
                request->processed = 1;
627
0
            }
628
0
            break;
629
630
0
        case MODE_GET:
631
0
            if (request->requestvb->type != ASN_NULL)
632
0
                continue;
633
            /*
634
             * find the row in question 
635
             */
636
0
            if (request->requestvb->name_length < (reginfo->rootoid_len + 3)) { /* table.entry.column... */
637
                /*
638
                 * request too short 
639
                 */
640
0
                netsnmp_set_request_error(reqinfo, request,
641
0
                                          SNMP_NOSUCHINSTANCE);
642
0
                break;
643
0
            } else if (NULL ==
644
0
                       (row =
645
0
                        netsnmp_table_data_get_from_oid(table,
646
0
                                                        request->
647
0
                                                        requestvb->name +
648
0
                                                        reginfo->
649
0
                                                        rootoid_len + 2,
650
0
                                                        request->
651
0
                                                        requestvb->
652
0
                                                        name_length -
653
0
                                                        reginfo->
654
0
                                                        rootoid_len -
655
0
                                                        2))) {
656
                /*
657
                 * no such row 
658
                 */
659
0
                netsnmp_set_request_error(reqinfo, request,
660
0
                                          SNMP_NOSUCHINSTANCE);
661
0
                break;
662
0
            } else {
663
0
                valid_request = 1;
664
0
                netsnmp_request_add_list_data(request,
665
0
                                              netsnmp_create_data_list
666
0
                                              (TABLE_DATA_ROW, row,
667
0
                                               NULL));
668
0
            }
669
0
            break;
670
671
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
672
0
        case MODE_SET_RESERVE1:
673
0
            valid_request = 1;
674
0
            if (NULL !=
675
0
                (row =
676
0
                 netsnmp_table_data_get_from_oid(table,
677
0
                                                 request->requestvb->name +
678
0
                                                 reginfo->rootoid_len + 2,
679
0
                                                 request->requestvb->
680
0
                                                 name_length -
681
0
                                                 reginfo->rootoid_len -
682
0
                                                 2))) {
683
0
                netsnmp_request_add_list_data(request,
684
0
                                              netsnmp_create_data_list
685
0
                                              (TABLE_DATA_ROW, row,
686
0
                                               NULL));
687
0
            }
688
0
            break;
689
690
0
        case MODE_SET_RESERVE2:
691
0
        case MODE_SET_ACTION:
692
0
        case MODE_SET_COMMIT:
693
0
        case MODE_SET_FREE:
694
0
        case MODE_SET_UNDO:
695
0
            valid_request = 1;
696
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
697
698
0
        }
699
0
    }
700
701
0
    if (valid_request &&
702
0
       (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK)) {
703
        /*
704
         * If this is a GetNext or GetBulk request, then we've identified
705
         *  the row that ought to include the appropriate next instance.
706
         *  Convert the request into a Get request, so that the lower-level
707
         *  handlers don't need to worry about skipping on, and call these
708
         *  handlers ourselves (so we can undo this again afterwards).
709
         */
710
0
        oldmode = reqinfo->mode;
711
0
        reqinfo->mode = MODE_GET;
712
0
        result = netsnmp_call_next_handler(handler, reginfo, reqinfo,
713
0
                                         requests);
714
0
        reqinfo->mode = oldmode;
715
0
        handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE;
716
0
        return result;
717
0
    }
718
0
    else
719
        /* next handler called automatically - 'AUTO_NEXT' */
720
0
        return SNMP_ERR_NOERROR;
721
0
}
722
723
/** extracts the table being accessed passed from the table_data helper */
724
netsnmp_table_data *
725
netsnmp_extract_table(netsnmp_request_info *request)
726
0
{
727
0
    return (netsnmp_table_data *)
728
0
                netsnmp_request_get_list_data(request, TABLE_DATA_TABLE);
729
0
}
730
731
/** extracts the row being accessed passed from the table_data helper */
732
netsnmp_table_row *
733
netsnmp_extract_table_row(netsnmp_request_info *request)
734
0
{
735
0
    return (netsnmp_table_row *) netsnmp_request_get_list_data(request,
736
0
                                                               TABLE_DATA_ROW);
737
0
}
738
739
#ifndef NETSNMP_FEATURE_REMOVE_EXTRACT_TABLE_ROW_DATA
740
/** extracts the data from the row being accessed passed from the
741
 * table_data helper */
742
void           *
743
netsnmp_extract_table_row_data(netsnmp_request_info *request)
744
0
{
745
0
    netsnmp_table_row *row;
746
0
    row = (netsnmp_table_row *) netsnmp_extract_table_row(request);
747
0
    if (row)
748
0
        return row->data;
749
0
    else
750
0
        return NULL;
751
0
}
752
#endif /* NETSNMP_FEATURE_REMOVE_EXTRACT_TABLE_ROW_DATA */
753
754
#ifndef NETSNMP_FEATURE_REMOVE_INSERT_TABLE_ROW
755
/** inserts a newly created table_data row into a request */
756
void
757
netsnmp_insert_table_row(netsnmp_request_info *request,
758
                         netsnmp_table_row *row)
759
0
{
760
0
    netsnmp_request_info       *req;
761
0
    netsnmp_table_request_info *table_info = NULL;
762
0
    netsnmp_variable_list      *this_index = NULL;
763
0
    netsnmp_variable_list      *that_index = NULL;
764
0
    oid      base_oid[] = {0, 0}; /* Make sure index OIDs are legal! */
765
0
    oid      this_oid[MAX_OID_LEN];
766
0
    oid      that_oid[MAX_OID_LEN];
767
0
    size_t   this_oid_len, that_oid_len;
768
769
0
    if (!request)
770
0
        return;
771
772
    /*
773
     * We'll add the new row information to any request
774
     * structure with the same index values as the request
775
     * passed in (which includes that one!).
776
     *
777
     * So construct an OID based on these index values.
778
     */
779
780
0
    table_info = netsnmp_extract_table_info(request);
781
0
    this_index = table_info->indexes;
782
0
    build_oid_noalloc(this_oid, MAX_OID_LEN, &this_oid_len,
783
0
                      base_oid, 2, this_index);
784
785
    /*
786
     * We need to look through the whole of the request list
787
     * (as received by the current handler), as there's no
788
     * guarantee that this routine will be called by the first
789
     * varbind that refers to this row.
790
     *   In particular, a RowStatus controlled row creation
791
     * may easily occur later in the variable list.
792
     *
793
     * So first, we rewind to the head of the list....
794
     */
795
0
    for (req=request; req->prev; req=req->prev)
796
0
        ;
797
798
    /*
799
     * ... and then start looking for matching indexes
800
     * (by constructing OIDs from these index values)
801
     */
802
0
    for (; req; req=req->next) {
803
0
        table_info = netsnmp_extract_table_info(req);
804
0
        that_index = table_info->indexes;
805
0
        build_oid_noalloc(that_oid, MAX_OID_LEN, &that_oid_len,
806
0
                          base_oid, 2, that_index);
807
      
808
        /*
809
         * This request has the same index values,
810
         * so add the newly-created row information.
811
         */
812
0
        if (snmp_oid_compare(this_oid, this_oid_len,
813
0
                             that_oid, that_oid_len) == 0) {
814
0
            netsnmp_request_add_list_data(req,
815
0
                netsnmp_create_data_list(TABLE_DATA_ROW, row, NULL));
816
0
        }
817
0
    }
818
0
}
819
#endif /* NETSNMP_FEATURE_REMOVE_INSERT_TABLE_ROW */
820
821
/* builds a result given a row, a varbind to set and the data */
822
int
823
netsnmp_table_data_build_result(netsnmp_handler_registration *reginfo,
824
                                netsnmp_agent_request_info *reqinfo,
825
                                netsnmp_request_info *request,
826
                                netsnmp_table_row *row,
827
                                int column,
828
                                u_char type,
829
                                u_char * result_data,
830
                                size_t result_data_len)
831
0
{
832
0
    oid             build_space[MAX_OID_LEN];
833
834
0
    if (!reginfo || !reqinfo || !request)
835
0
        return SNMPERR_GENERR;
836
837
0
    if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) {
838
        /*
839
         * only need to do this for getnext type cases where oid is changing 
840
         */
841
0
        memcpy(build_space, reginfo->rootoid,   /* registered oid */
842
0
               reginfo->rootoid_len * sizeof(oid));
843
0
        build_space[reginfo->rootoid_len] = 1;  /* entry */
844
0
        build_space[reginfo->rootoid_len + 1] = column; /* column */
845
0
        memcpy(build_space + reginfo->rootoid_len + 2,  /* index data */
846
0
               row->index_oid, row->index_oid_len * sizeof(oid));
847
0
        snmp_set_var_objid(request->requestvb, build_space,
848
0
                           reginfo->rootoid_len + 2 + row->index_oid_len);
849
0
    }
850
0
    snmp_set_var_typed_value(request->requestvb, type,
851
0
                             result_data, result_data_len);
852
0
    return SNMPERR_SUCCESS;     /* WWWXXX: check for bounds */
853
0
}
854
855
856
/* ==================================
857
 *
858
 * Table Data API: Row operations
859
 *     (table-independent rows)
860
 *
861
 * ================================== */
862
863
/** returns the first row in the table */
864
netsnmp_table_row *
865
netsnmp_table_data_get_first_row(netsnmp_table_data *table)
866
0
{
867
0
    if (!table)
868
0
        return NULL;
869
0
    return table->first_row;
870
0
}
871
872
/** returns the next row in the table */
873
netsnmp_table_row *
874
netsnmp_table_data_get_next_row(netsnmp_table_data *table,
875
                                netsnmp_table_row  *row)
876
0
{
877
0
    if (!row)
878
0
        return NULL;
879
0
    return row->next;
880
0
}
881
882
/** finds the data in "datalist" stored at "indexes" */
883
netsnmp_table_row *
884
netsnmp_table_data_get(netsnmp_table_data *table,
885
                       netsnmp_variable_list * indexes)
886
0
{
887
0
    oid             searchfor[MAX_OID_LEN];
888
0
    size_t          searchfor_len = MAX_OID_LEN;
889
890
0
    build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0,
891
0
                      indexes);
892
0
    return netsnmp_table_data_get_from_oid(table, searchfor,
893
0
                                           searchfor_len);
894
0
}
895
896
/** finds the data in "datalist" stored at the searchfor oid */
897
netsnmp_table_row *
898
netsnmp_table_data_get_from_oid(netsnmp_table_data *table,
899
                                oid * searchfor, size_t searchfor_len)
900
0
{
901
0
    netsnmp_table_row *row;
902
0
    if (!table)
903
0
        return NULL;
904
905
0
    for (row = table->first_row; row != NULL; row = row->next) {
906
0
        if (row->index_oid &&
907
0
            snmp_oid_compare(searchfor, searchfor_len,
908
0
                             row->index_oid, row->index_oid_len) == 0)
909
0
            return row;
910
0
    }
911
0
    return NULL;
912
0
}
913
914
int
915
netsnmp_table_data_num_rows(netsnmp_table_data *table)
916
0
{
917
0
    int i=0;
918
0
    netsnmp_table_row *row;
919
0
    if (!table)
920
0
        return 0;
921
0
    for (row = table->first_row; row; row = row->next) {
922
0
        i++;
923
0
    }
924
0
    return i;
925
0
}
926
927
    /* =====================================
928
     * Generic API - mostly renamed wrappers
929
     * ===================================== */
930
931
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_FIRST
932
netsnmp_table_row *
933
netsnmp_table_data_row_first(netsnmp_table_data *table)
934
0
{
935
0
    return netsnmp_table_data_get_first_row(table);
936
0
}
937
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_FIRST */
938
939
netsnmp_table_row *
940
netsnmp_table_data_row_get(  netsnmp_table_data *table,
941
                             netsnmp_table_row  *row)
942
0
{
943
0
    if (!table || !row)
944
0
        return NULL;
945
0
    return netsnmp_table_data_get_from_oid(table, row->index_oid,
946
0
                                                  row->index_oid_len);
947
0
}
948
949
netsnmp_table_row *
950
netsnmp_table_data_row_next( netsnmp_table_data *table,
951
                             netsnmp_table_row  *row)
952
0
{
953
0
    return netsnmp_table_data_get_next_row(table, row);
954
0
}
955
956
netsnmp_table_row *
957
netsnmp_table_data_row_get_byoid( netsnmp_table_data *table,
958
                                  oid *instance, size_t len)
959
0
{
960
0
    return netsnmp_table_data_get_from_oid(table, instance, len);
961
0
}
962
963
netsnmp_table_row *
964
netsnmp_table_data_row_next_byoid(netsnmp_table_data *table,
965
                                  oid *instance, size_t len)
966
0
{
967
0
    netsnmp_table_row *row;
968
969
0
    if (!table || !instance)
970
0
        return NULL;
971
    
972
0
    for (row = table->first_row; row; row = row->next) {
973
0
        if (snmp_oid_compare(row->index_oid,
974
0
                             row->index_oid_len,
975
0
                             instance, len) > 0)
976
0
            return row;
977
0
    }
978
0
    return NULL;
979
0
}
980
981
netsnmp_table_row *
982
netsnmp_table_data_row_get_byidx( netsnmp_table_data    *table,
983
                                  netsnmp_variable_list *indexes)
984
0
{
985
0
    return netsnmp_table_data_get(table, indexes);
986
0
}
987
988
netsnmp_table_row *
989
netsnmp_table_data_row_next_byidx(netsnmp_table_data    *table,
990
                                  netsnmp_variable_list *indexes)
991
0
{
992
0
    oid    instance[MAX_OID_LEN];
993
0
    size_t len    = MAX_OID_LEN;
994
995
0
    if (!table || !indexes)
996
0
        return NULL;
997
998
0
    build_oid_noalloc(instance, MAX_OID_LEN, &len, NULL, 0, indexes);
999
0
    return netsnmp_table_data_row_next_byoid(table, instance, len);
1000
0
}
1001
1002
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_COUNT
1003
int
1004
netsnmp_table_data_row_count(netsnmp_table_data *table)
1005
0
{
1006
0
    return netsnmp_table_data_num_rows(table);
1007
0
}
1008
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_COUNT */
1009
1010
1011
/* ==================================
1012
 *
1013
 * Table Data API: Row operations
1014
 *     (table-specific rows)
1015
 *
1016
 * ================================== */
1017
1018
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_OPERATIONS
1019
void *
1020
netsnmp_table_data_entry_first(netsnmp_table_data *table)
1021
0
{
1022
0
    netsnmp_table_row *row =
1023
0
        netsnmp_table_data_get_first_row(table);
1024
0
    return (row ? row->data : NULL );
1025
0
}
1026
1027
void *
1028
netsnmp_table_data_entry_get(  netsnmp_table_data *table,
1029
                               netsnmp_table_row  *row)
1030
0
{
1031
0
    return (row ? row->data : NULL );
1032
0
}
1033
1034
void *
1035
netsnmp_table_data_entry_next( netsnmp_table_data *table,
1036
                               netsnmp_table_row  *row)
1037
0
{
1038
0
    row =
1039
0
        netsnmp_table_data_row_next(table, row);
1040
0
    return (row ? row->data : NULL );
1041
0
}
1042
1043
void *
1044
netsnmp_table_data_entry_get_byidx( netsnmp_table_data    *table,
1045
                                    netsnmp_variable_list *indexes)
1046
0
{
1047
0
    netsnmp_table_row *row =
1048
0
        netsnmp_table_data_row_get_byidx(table, indexes);
1049
0
    return (row ? row->data : NULL );
1050
0
}
1051
1052
void *
1053
netsnmp_table_data_entry_next_byidx(netsnmp_table_data    *table,
1054
                                    netsnmp_variable_list *indexes)
1055
0
{
1056
0
    netsnmp_table_row *row =
1057
0
        netsnmp_table_data_row_next_byidx(table, indexes);
1058
0
    return (row ? row->data : NULL );
1059
0
}
1060
1061
void *
1062
netsnmp_table_data_entry_get_byoid( netsnmp_table_data *table,
1063
                                    oid *instance, size_t len)
1064
0
{
1065
0
    netsnmp_table_row *row =
1066
0
        netsnmp_table_data_row_get_byoid(table, instance, len);
1067
0
    return (row ? row->data : NULL );
1068
0
}
1069
1070
void *
1071
netsnmp_table_data_entry_next_byoid(netsnmp_table_data *table,
1072
                                    oid *instance, size_t len)
1073
0
{
1074
0
    netsnmp_table_row *row =
1075
0
        netsnmp_table_data_row_next_byoid(table, instance, len);
1076
0
    return (row ? row->data : NULL );
1077
0
}
1078
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_OPERATIONS */
1079
1080
    /* =====================================
1081
     * Generic API - mostly renamed wrappers
1082
     * ===================================== */
1083
1084
/* ==================================
1085
 *
1086
 * Table Data API: Index operations
1087
 *
1088
 * ================================== */
1089
1090
#else /* NETSNMP_FEATURE_REMOVE_TABLE_DATA */
1091
netsnmp_feature_unused(table_data);
1092
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA */
1093
1094
/** @} 
1095
 */