Coverage Report

Created: 2025-10-10 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/agent/helpers/table_dataset.c
Line
Count
Source
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-features.h>
16
#include <net-snmp/net-snmp-includes.h>
17
#include <net-snmp/agent/net-snmp-agent-includes.h>
18
19
#include <net-snmp/agent/table_dataset.h>
20
21
#ifdef HAVE_STRING_H
22
#include <string.h>
23
#else
24
#include <strings.h>
25
#endif
26
27
netsnmp_feature_child_of(table_dataset_all, mib_helpers);
28
netsnmp_feature_child_of(table_dataset, table_dataset_all);
29
netsnmp_feature_child_of(table_dataset_remove_row, table_dataset_all);
30
netsnmp_feature_child_of(table_data_set_column, table_dataset_all);
31
netsnmp_feature_child_of(table_dataset_get_newrow, table_dataset_all);
32
netsnmp_feature_child_of(table_set_add_indexes, table_dataset_all);
33
netsnmp_feature_child_of(delete_table_data_set, table_dataset_all);
34
netsnmp_feature_child_of(table_set_multi_add_default_row, table_dataset_all);
35
netsnmp_feature_child_of(table_dataset_unregister_auto_data_table, table_dataset_all);
36
37
#ifdef NETSNMP_FEATURE_REQUIRE_TABLE_DATASET
38
netsnmp_feature_require(table_get_or_create_row_stash);
39
netsnmp_feature_require(table_data_delete_table);
40
netsnmp_feature_require(table_data);
41
netsnmp_feature_require(oid_stash_get_data);
42
netsnmp_feature_require(oid_stash_add_data);
43
#endif /* NETSNMP_FEATURE_REQUIRE_TABLE_DATASET */
44
45
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET
46
47
#ifndef NETSNMP_NO_WRITE_SUPPORT
48
netsnmp_feature_require(oid_stash);
49
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
50
51
#ifndef NETSNMP_DISABLE_MIB_LOADING
52
netsnmp_feature_require(mib_to_asn_type);
53
#endif /* NETSNMP_DISABLE_MIB_LOADING */
54
55
static netsnmp_data_list *auto_tables;
56
57
typedef struct data_set_tables_s {
58
    netsnmp_table_data_set *table_set;
59
} data_set_tables;
60
61
typedef struct data_set_cache_s {
62
    void           *data;
63
    size_t          data_len;
64
} data_set_cache;
65
66
0
#define STATE_ACTION   1
67
0
#define STATE_COMMIT   2
68
0
#define STATE_UNDO     3
69
0
#define STATE_FREE     4
70
71
typedef struct newrow_stash_s {
72
    netsnmp_table_row *newrow;
73
    int             state;
74
    int             created;
75
    int             deleted;
76
} newrow_stash;
77
78
/** @defgroup table_dataset table_dataset
79
 *  Helps you implement a table with automatted storage.
80
 *  @ingroup table_data
81
 *
82
 *  This handler helps you implement a table where all the data is
83
 *  expected to be stored within the agent itself and not in some
84
 *  external storage location.  It handles all MIB requests including
85
 *  GETs, GETNEXTs and SETs.  It's possible to simply create a table
86
 *  without actually ever defining a handler to be called when SNMP
87
 *  requests come in.  To use the data, you can either attach a
88
 *  sub-handler that merely uses/manipulates the data further when
89
 *  requests come in, or you can loop through it externally when it's
90
 *  actually needed.  This handler is most useful in cases where a
91
 *  table is holding configuration data for something which gets
92
 *  triggered via another event.
93
 *
94
 *  NOTE NOTE NOTE: This helper isn't complete and is likely to change
95
 *  somewhat over time.  Specifically, the way it stores data
96
 *  internally may change drastically.
97
 *  
98
 *  @{
99
 */
100
101
void
102
3.24k
netsnmp_init_table_dataset(void) {
103
3.24k
#ifndef NETSNMP_DISABLE_MIB_LOADING
104
3.24k
    register_app_config_handler("table",
105
3.24k
                                netsnmp_config_parse_table_set, NULL,
106
3.24k
                                "tableoid");
107
3.24k
#endif /* NETSNMP_DISABLE_MIB_LOADING */
108
3.24k
    register_app_config_handler("add_row", netsnmp_config_parse_add_row,
109
3.24k
                                NULL, "table_name indexes... values...");
110
3.24k
}
111
112
/* ==================================
113
 *
114
 * Data Set API: Table maintenance
115
 *
116
 * ================================== */
117
118
/** deletes a single dataset table data.
119
 *  returns the (possibly still good) next pointer of the deleted data object.
120
 */
121
NETSNMP_STATIC_INLINE netsnmp_table_data_set_storage *
122
netsnmp_table_dataset_delete_data(netsnmp_table_data_set_storage *data)
123
0
{
124
0
    netsnmp_table_data_set_storage *nextPtr = NULL;
125
0
    if (data) {
126
0
        nextPtr = data->next;
127
0
        SNMP_FREE(data->data.voidp);
128
0
    }
129
0
    SNMP_FREE(data);
130
0
    return nextPtr;
131
0
}
132
133
/** deletes all the data from this node and beyond in the linked list */
134
NETSNMP_INLINE void
135
netsnmp_table_dataset_delete_all_data(netsnmp_table_data_set_storage *data)
136
0
{
137
138
0
    while (data) {
139
0
        data = netsnmp_table_dataset_delete_data(data);
140
0
    }
141
0
}
142
143
/** deletes all the data from this node and beyond in the linked list */
144
NETSNMP_INLINE void
145
netsnmp_table_dataset_delete_row(netsnmp_table_row *row)
146
0
{
147
0
    netsnmp_table_data_set_storage *data;
148
149
0
    if (!row)
150
0
        return;
151
152
0
    data = (netsnmp_table_data_set_storage*)netsnmp_table_data_delete_row(row);
153
0
    netsnmp_table_dataset_delete_all_data(data);
154
0
}
155
156
/** adds a new row to a dataset table */
157
NETSNMP_INLINE void
158
netsnmp_table_dataset_add_row(netsnmp_table_data_set *table,
159
                              netsnmp_table_row *row)
160
0
{
161
0
    if (!table)
162
0
        return;
163
0
    netsnmp_table_data_add_row(table->table, row);
164
0
}
165
166
/** adds a new row to a dataset table */
167
NETSNMP_INLINE void
168
netsnmp_table_dataset_replace_row(netsnmp_table_data_set *table,
169
                                  netsnmp_table_row *origrow,
170
                                  netsnmp_table_row *newrow)
171
0
{
172
0
    if (!table)
173
0
        return;
174
0
    netsnmp_table_data_replace_row(table->table, origrow, newrow);
175
0
}
176
177
/** removes a row from the table, but doesn't delete/free the column values */
178
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_REMOVE_ROW
179
NETSNMP_INLINE void
180
netsnmp_table_dataset_remove_row(netsnmp_table_data_set *table,
181
                                 netsnmp_table_row *row)
182
0
{
183
0
    if (!table)
184
0
        return;
185
186
0
    netsnmp_table_data_remove_and_delete_row(table->table, row);
187
0
}
188
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_REMOVE_ROW */
189
190
/** removes a row from the table and then deletes it (and all its data) */
191
NETSNMP_INLINE void
192
netsnmp_table_dataset_remove_and_delete_row(netsnmp_table_data_set *table,
193
                                            netsnmp_table_row *row)
194
0
{
195
0
    netsnmp_table_data_set_storage *data;
196
197
0
    if (!table)
198
0
        return;
199
200
0
    data = (netsnmp_table_data_set_storage *)
201
0
        netsnmp_table_data_remove_and_delete_row(table->table, row);
202
203
0
    netsnmp_table_dataset_delete_all_data(data);
204
0
}
205
206
/** Create a netsnmp_table_data_set structure given a table_data definition */
207
netsnmp_table_data_set *
208
netsnmp_create_table_data_set(const char *table_name)
209
0
{
210
0
    netsnmp_table_data_set *table_set =
211
0
        SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set);
212
0
    if (!table_set)
213
0
        return NULL;
214
0
    table_set->table = netsnmp_create_table_data(table_name);
215
0
    return table_set;
216
0
}
217
218
#ifndef NETSNMP_FEATURE_REMOVE_DELETE_TABLE_DATA_SET
219
void netsnmp_delete_table_data_set(netsnmp_table_data_set *table_set)
220
0
{
221
0
    netsnmp_table_data_set_storage *ptr, *next;
222
0
    netsnmp_table_row *prow, *pnextrow;
223
224
0
    for (ptr = table_set->default_row; ptr; ptr = next) {
225
0
        next = ptr->next;
226
0
        free(ptr);
227
0
    }
228
0
    table_set->default_row = NULL;
229
0
    for (prow = table_set->table->first_row; prow; prow = pnextrow) {
230
0
        pnextrow = prow->next;
231
0
        netsnmp_table_dataset_remove_and_delete_row(table_set, prow);
232
0
    }
233
0
    table_set->table->first_row = NULL;
234
0
    netsnmp_table_data_delete_table(table_set->table);
235
0
    free(table_set);
236
0
}
237
#endif /* NETSNMP_FEATURE_REMOVE_DELETE_TABLE_DATA_SET */
238
239
/** clones a dataset row, including all data. */
240
netsnmp_table_row *
241
netsnmp_table_data_set_clone_row(netsnmp_table_row *row)
242
0
{
243
0
    netsnmp_table_data_set_storage *data, **newrowdata;
244
0
    netsnmp_table_row *newrow;
245
246
0
    if (!row)
247
0
        return NULL;
248
249
0
    newrow = netsnmp_table_data_clone_row(row);
250
0
    if (!newrow)
251
0
        return NULL;
252
253
0
    data = (netsnmp_table_data_set_storage *) row->data;
254
255
0
    if (data) {
256
0
        for (newrowdata =
257
0
             (netsnmp_table_data_set_storage **) &(newrow->data); data;
258
0
             newrowdata = &((*newrowdata)->next), data = data->next) {
259
260
0
            *newrowdata = netsnmp_memdup(data,
261
0
                sizeof(netsnmp_table_data_set_storage));
262
0
            if (!*newrowdata) {
263
0
                netsnmp_table_dataset_delete_row(newrow);
264
0
                return NULL;
265
0
            }
266
267
0
            if (data->data.voidp) {
268
0
                (*newrowdata)->data.voidp =
269
0
                    netsnmp_memdup(data->data.voidp, data->data_len);
270
0
                if (!(*newrowdata)->data.voidp) {
271
0
                    netsnmp_table_dataset_delete_row(newrow);
272
0
                    return NULL;
273
0
                }
274
0
            }
275
0
        }
276
0
    }
277
0
    return newrow;
278
0
}
279
280
/* ==================================
281
 *
282
 * Data Set API: Default row operations
283
 *
284
 * ================================== */
285
286
/** creates a new row from an existing defined default set */
287
netsnmp_table_row *
288
netsnmp_table_data_set_create_row_from_defaults
289
    (netsnmp_table_data_set_storage *defrow)
290
0
{
291
0
    netsnmp_table_row *row;
292
0
    row = netsnmp_create_table_data_row();
293
0
    if (!row)
294
0
        return NULL;
295
0
    for (; defrow; defrow = defrow->next) {
296
0
        netsnmp_set_row_column(row, defrow->column, defrow->type,
297
0
                               defrow->data.voidp, defrow->data_len);
298
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
299
0
        if (defrow->writable)
300
0
            netsnmp_mark_row_column_writable(row, defrow->column, 1);
301
0
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
302
0
    }
303
0
    return row;
304
0
}
305
306
/** adds a new default row to a table_set.
307
 * Arguments should be the table_set, column number, variable type and
308
 * finally a 1 if it is allowed to be writable, or a 0 if not.  If the
309
 * default_value field is not NULL, it will be used to populate new
310
 * values in that column fro newly created rows. It is copied into the
311
 * storage template (free your calling argument).
312
 *
313
 * returns SNMPERR_SUCCESS or SNMPERR_FAILURE
314
 */
315
int
316
netsnmp_table_set_add_default_row(netsnmp_table_data_set *table_set,
317
                                  unsigned int column,
318
                                  int type, int writable,
319
                                  void *default_value,
320
                                  size_t default_value_len)
321
0
{
322
0
    netsnmp_table_data_set_storage *new_col, *ptr, *pptr;
323
324
0
    if (!table_set)
325
0
        return SNMPERR_GENERR;
326
327
    /*
328
     * double check 
329
     */
330
0
    new_col =
331
0
        netsnmp_table_data_set_find_column(table_set->default_row, column);
332
0
    if (new_col != NULL) {
333
0
        if (new_col->type == type && new_col->writable == writable)
334
0
            return SNMPERR_SUCCESS;
335
0
        return SNMPERR_GENERR;
336
0
    }
337
338
0
    new_col = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
339
0
    if (new_col == NULL)
340
0
        return SNMPERR_GENERR;
341
0
    new_col->type = type;
342
0
    new_col->writable = writable;
343
0
    new_col->column = column;
344
0
    if (default_value) {
345
0
        new_col->data.voidp = netsnmp_memdup(default_value, default_value_len);
346
0
        new_col->data_len = default_value_len;
347
0
    }
348
0
    if (table_set->default_row == NULL)
349
0
        table_set->default_row = new_col;
350
0
    else {
351
        /* sort in order just because (needed for add_row support) */
352
0
        for (ptr = table_set->default_row, pptr = NULL;
353
0
             ptr;
354
0
             pptr = ptr, ptr = ptr->next) {
355
0
            if (ptr->column > column) {
356
0
                new_col->next = ptr;
357
0
                if (pptr)
358
0
                    pptr->next = new_col;
359
0
                else
360
0
                    table_set->default_row = new_col;
361
0
                return SNMPERR_SUCCESS;
362
0
            }
363
0
        }
364
0
        if (pptr)
365
0
            pptr->next = new_col;
366
0
        else
367
0
            snmp_log(LOG_ERR,"Shouldn't have gotten here: table_dataset/add_row");
368
0
    }
369
0
    return SNMPERR_SUCCESS;
370
0
}
371
372
/** adds multiple data column definitions to each row.  Functionally,
373
 *  this is a wrapper around calling netsnmp_table_set_add_default_row
374
 *  repeatedly for you.
375
 */
376
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_SET_MULTI_ADD_DEFAULT_ROW
377
void
378
netsnmp_table_set_multi_add_default_row(netsnmp_table_data_set *tset, ...)
379
0
{
380
0
    va_list         debugargs;
381
0
    unsigned int    column;
382
0
    int             type, writable;
383
0
    void           *data;
384
0
    size_t          data_len;
385
386
0
    va_start(debugargs, tset);
387
388
0
    while ((column = va_arg(debugargs, unsigned int)) != 0) {
389
0
        type = va_arg(debugargs, int);
390
0
        writable = va_arg(debugargs, int);
391
0
        data = va_arg(debugargs, void *);
392
0
        data_len = va_arg(debugargs, size_t);
393
0
        netsnmp_table_set_add_default_row(tset, column, type, writable,
394
0
                                          data, data_len);
395
0
    }
396
397
0
    va_end(debugargs);
398
0
}
399
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_SET_MULTI_ADD_DEFAULT_ROW */
400
401
/* ==================================
402
 *
403
 * Data Set API: MIB maintenance
404
 *
405
 * ================================== */
406
407
/** Given a netsnmp_table_data_set definition, create a handler for it */
408
netsnmp_mib_handler *
409
netsnmp_get_table_data_set_handler(netsnmp_table_data_set *data_set)
410
0
{
411
0
    netsnmp_mib_handler *ret = NULL;
412
413
0
    if (!data_set) {
414
0
        snmp_log(LOG_INFO,
415
0
                 "netsnmp_get_table_data_set_handler(NULL) called\n");
416
0
        return NULL;
417
0
    }
418
419
0
    ret =
420
0
        netsnmp_create_handler(TABLE_DATA_SET_NAME,
421
0
                               netsnmp_table_data_set_helper_handler);
422
0
    if (ret) {
423
0
        ret->flags |= MIB_HANDLER_AUTO_NEXT;
424
0
        ret->myvoid = (void *) data_set;
425
0
    }
426
0
    return ret;
427
0
}
428
429
/** register a given data_set at a given oid (specified in the
430
    netsnmp_handler_registration pointer).  The
431
    reginfo->handler->access_method *may* be null if the call doesn't
432
    ever want to be called for SNMP operations.
433
*/
434
int
435
netsnmp_register_table_data_set(netsnmp_handler_registration *reginfo,
436
                                netsnmp_table_data_set *data_set,
437
                                netsnmp_table_registration_info *table_info)
438
0
{
439
0
    netsnmp_mib_handler *handler;
440
0
    int ret;
441
442
0
    if (NULL == table_info) {
443
        /*
444
         * allocate the table if one wasn't allocated 
445
         */
446
0
        table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
447
0
        if (table_info == NULL)
448
0
            return SNMP_ERR_GENERR;
449
0
    }
450
451
0
    if (NULL == table_info->indexes && data_set->table->indexes_template) {
452
        /*
453
         * copy the indexes in 
454
         */
455
0
        table_info->indexes =
456
0
            snmp_clone_varbind(data_set->table->indexes_template);
457
0
    }
458
459
0
    if ((!table_info->min_column || !table_info->max_column) &&
460
0
        (data_set->default_row)) {
461
        /*
462
         * determine min/max columns 
463
         */
464
0
        unsigned int    mincol = 0xffffffff, maxcol = 0;
465
0
        netsnmp_table_data_set_storage *row;
466
467
0
        for (row = data_set->default_row; row; row = row->next) {
468
0
            mincol = SNMP_MIN(mincol, row->column);
469
0
            maxcol = SNMP_MAX(maxcol, row->column);
470
0
        }
471
0
        if (!table_info->min_column)
472
0
            table_info->min_column = mincol;
473
0
        if (!table_info->max_column)
474
0
            table_info->max_column = maxcol;
475
0
    }
476
477
0
    handler = netsnmp_get_table_data_set_handler(data_set);
478
0
    if (!handler ||
479
0
        (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
480
0
        snmp_log(LOG_ERR, "could not create table data set handler\n");
481
0
        netsnmp_handler_free(handler);
482
0
        netsnmp_handler_registration_free(reginfo);
483
0
        free(table_info);
484
0
        return MIB_REGISTRATION_FAILED;
485
0
    }
486
487
0
    ret = netsnmp_register_table_data(reginfo, data_set->table,
488
0
                                       table_info);
489
0
    if (ret == SNMPERR_SUCCESS && reginfo->handler)
490
0
        netsnmp_handler_owns_table_info(reginfo->handler->next);
491
0
    return ret;
492
0
}
493
494
newrow_stash   *
495
netsnmp_table_data_set_create_newrowstash
496
    (netsnmp_table_data_set     *datatable,
497
     netsnmp_table_request_info *table_info)
498
0
{
499
0
    newrow_stash   *newrowstash = NULL;
500
0
    netsnmp_table_row *newrow   = NULL;
501
502
0
    newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
503
504
0
    if (newrowstash != NULL) {
505
0
        newrowstash->created = 1;
506
0
        newrow = netsnmp_table_data_set_create_row_from_defaults
507
0
            (datatable->default_row);
508
0
        newrow->indexes = snmp_clone_varbind(table_info->indexes);
509
0
        newrowstash->newrow = newrow;
510
0
    }
511
512
0
    return newrowstash;
513
0
}
514
515
/* implements the table data helper.  This is the routine that takes
516
 *  care of all SNMP requests coming into the table. */
517
int
518
netsnmp_table_data_set_helper_handler(netsnmp_mib_handler *handler,
519
                                      netsnmp_handler_registration
520
                                      *reginfo,
521
                                      netsnmp_agent_request_info *reqinfo,
522
                                      netsnmp_request_info *requests)
523
0
{
524
0
    netsnmp_table_data_set_storage *data = NULL;
525
0
    netsnmp_table_request_info *table_info;
526
0
    netsnmp_request_info *request;
527
0
    netsnmp_table_row *row = NULL;
528
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
529
0
    netsnmp_oid_stash_node **stashp = NULL;
530
0
    netsnmp_table_row *newrow = NULL;
531
0
    newrow_stash   *newrowstash = NULL;
532
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
533
534
0
    if (!handler)
535
0
        return SNMPERR_GENERR;
536
        
537
0
    DEBUGMSGTL(("netsnmp_table_data_set", "handler starting\n"));
538
0
    for (request = requests; request; request = request->next) {
539
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
540
0
        netsnmp_table_data_set *datatable =
541
0
            (netsnmp_table_data_set *) handler->myvoid;
542
0
        const oid * const suffix =
543
0
            requests->requestvb->name + reginfo->rootoid_len + 2;
544
0
        const size_t suffix_len =
545
0
            requests->requestvb->name_length - (reginfo->rootoid_len + 2);
546
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
547
548
0
        if (request->processed)
549
0
            continue;
550
551
        /*
552
         * extract our stored data and table info 
553
         */
554
0
        row = netsnmp_extract_table_row(request);
555
0
        table_info = netsnmp_extract_table_info(request);
556
557
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
558
0
        if (MODE_IS_SET(reqinfo->mode)) {
559
560
            /*
561
             * use a cached copy of the row for modification 
562
             */
563
564
            /*
565
             * cache location: may have been created already by other
566
             * SET requests in the same master request. 
567
             */
568
0
            stashp = netsnmp_table_dataset_get_or_create_stash(reqinfo,
569
0
                                                               datatable,
570
0
                                                               table_info);
571
0
            if (NULL == stashp) {
572
0
                netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
573
0
                continue;
574
0
            }
575
576
0
            newrowstash
577
0
                = (newrow_stash*)netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len);
578
579
0
            if (!newrowstash) {
580
0
                if (!row) {
581
0
                    if (datatable->allow_creation) {
582
                        /*
583
                         * entirely new row.  Create the row from the template 
584
                         */
585
0
                        newrowstash =
586
0
                             netsnmp_table_data_set_create_newrowstash(
587
0
                                                 datatable, table_info);
588
0
                        newrow = newrowstash->newrow;
589
0
                    } else if (datatable->rowstatus_column == 0) {
590
                        /*
591
                         * A RowStatus object may be used to control the
592
                         *  creation of a new row.  But if this object
593
                         *  isn't declared (and the table isn't marked as
594
                         *  'auto-create'), then we can't create a new row.
595
                         */
596
0
                        netsnmp_set_request_error(reqinfo, request,
597
0
                                                  SNMP_ERR_NOCREATION);
598
0
                        continue;
599
0
                    }
600
0
                } else {
601
                    /*
602
                     * existing row that needs to be modified 
603
                     */
604
0
                    newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
605
0
                    if (newrowstash == NULL) {
606
0
                        netsnmp_set_request_error(reqinfo, request,
607
0
                                                  SNMP_ERR_GENERR);
608
0
                        continue;
609
0
                    }
610
0
                    newrow = netsnmp_table_data_set_clone_row(row);
611
0
                    newrowstash->newrow = newrow;
612
0
                }
613
0
                netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
614
0
                                           newrowstash);
615
0
            } else {
616
0
                newrow = newrowstash->newrow;
617
0
            }
618
            /*
619
             * all future SET data modification operations use this
620
             * temp pointer 
621
             */
622
0
            if (reqinfo->mode == MODE_SET_RESERVE1 ||
623
0
                reqinfo->mode == MODE_SET_RESERVE2)
624
0
                row = newrow;
625
0
        }
626
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
627
628
0
        if (row)
629
0
            data = (netsnmp_table_data_set_storage *) row->data;
630
631
0
        if (!row || !table_info || !data) {
632
0
            if (!table_info
633
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
634
0
                || !MODE_IS_SET(reqinfo->mode)
635
0
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
636
0
                ) {
637
0
                netsnmp_set_request_error(reqinfo, request,
638
0
                                          SNMP_NOSUCHINSTANCE);
639
0
                continue;
640
0
            }
641
0
        }
642
643
0
        data =
644
0
            netsnmp_table_data_set_find_column(data, table_info->colnum);
645
646
0
        switch (reqinfo->mode) {
647
0
        case MODE_GET:
648
0
        case MODE_GETNEXT:
649
0
        case MODE_GETBULK:     /* XXXWWW */
650
0
            if (!data || data->type == SNMP_NOSUCHINSTANCE) {
651
0
                netsnmp_set_request_error(reqinfo, request,
652
0
                                          SNMP_NOSUCHINSTANCE);
653
0
            } else {
654
                /*
655
                 * Note: data->data.voidp can be NULL, e.g. when a zero-length
656
                 * octet string has been stored in the table cache.
657
                 */
658
0
                netsnmp_table_data_build_result(reginfo, reqinfo, request,
659
0
                                                row,
660
0
                                                table_info->colnum,
661
0
                                                data->type,
662
0
                                       (u_char*)data->data.voidp,
663
0
                                                data->data_len);
664
0
            }
665
0
            break;
666
667
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
668
0
        case MODE_SET_RESERVE1:
669
0
            if (data) {
670
                /*
671
                 * Can we modify the existing row?
672
                 */
673
0
                if (!data->writable) {
674
0
                    netsnmp_set_request_error(reqinfo, request,
675
0
                                              SNMP_ERR_NOTWRITABLE);
676
0
                } else if (request->requestvb->type != data->type) {
677
0
                    netsnmp_set_request_error(reqinfo, request,
678
0
                                              SNMP_ERR_WRONGTYPE);
679
0
                }
680
0
            } else if (datatable->rowstatus_column == table_info->colnum) {
681
                /*
682
                 * Otherwise, this is where we create a new row using
683
                 * the RowStatus object (essentially duplicating the
684
                 * steps followed earlier in the 'allow_creation' case)
685
                 */
686
0
                switch (*(request->requestvb->val.integer)) {
687
0
                case RS_CREATEANDGO:
688
0
                case RS_CREATEANDWAIT:
689
0
                    newrowstash =
690
0
                             netsnmp_table_data_set_create_newrowstash(
691
0
                                                 datatable, table_info);
692
0
                    newrow = newrowstash->newrow;
693
0
                    row    = newrow;
694
0
                    netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
695
0
                                               newrowstash);
696
0
                }
697
0
            }
698
0
            break;
699
700
0
        case MODE_SET_RESERVE2:
701
            /*
702
             * If the agent receives a SET request for an object in a non-existent
703
             *  row, then the RESERVE1 pass will create the row automatically.
704
             *
705
             * But since the row doesn't exist at that point, the test for whether
706
             *  the object is writable or not will be skipped.  So we need to check
707
             *  for this possibility again here.
708
             *
709
             * Similarly, if row creation is under the control of the RowStatus
710
             *  object (i.e. allow_creation == 0), but this particular request
711
             *  doesn't include such an object, then the row won't have been created,
712
             *  and the writable check will also have been skipped.  Again - check here.
713
             */
714
0
            if (data && data->writable == 0) {
715
0
                netsnmp_set_request_error(reqinfo, request,
716
0
                                          SNMP_ERR_NOTWRITABLE);
717
0
                continue;
718
0
            }
719
0
            if (datatable->rowstatus_column == table_info->colnum) {
720
0
                switch (*(request->requestvb->val.integer)) {
721
0
                case RS_ACTIVE:
722
0
                case RS_NOTINSERVICE:
723
                    /*
724
                     * Can only operate on pre-existing rows.
725
                     */
726
0
                    if (!newrowstash || newrowstash->created) {
727
0
                        netsnmp_set_request_error(reqinfo, request,
728
0
                                                  SNMP_ERR_INCONSISTENTVALUE);
729
0
                        continue;
730
0
                    }
731
0
                    break;
732
733
0
                case RS_CREATEANDGO:
734
0
                case RS_CREATEANDWAIT:
735
                    /*
736
                     * Can only operate on newly created rows.
737
                     */
738
0
                    if (!(newrowstash && newrowstash->created)) {
739
0
                        netsnmp_set_request_error(reqinfo, request,
740
0
                                                  SNMP_ERR_INCONSISTENTVALUE);
741
0
                        continue;
742
0
                    }
743
0
                    break;
744
745
0
                case RS_DESTROY:
746
                    /*
747
                     * Can operate on new or pre-existing rows.
748
                     */
749
0
                    break;
750
751
0
                case RS_NOTREADY:
752
0
                default:
753
                    /*
754
                     * Not a valid value to Set 
755
                     */
756
0
                    netsnmp_set_request_error(reqinfo, request,
757
0
                                              SNMP_ERR_WRONGVALUE);
758
0
                    continue;
759
0
                }
760
0
            }
761
0
            if (!data ) {
762
0
                netsnmp_set_request_error(reqinfo, request,
763
0
                                          SNMP_ERR_NOCREATION);
764
0
                continue;
765
0
            }
766
767
            /*
768
             * modify row and set new value 
769
             */
770
0
            SNMP_FREE(data->data.string);
771
0
            data->data.string = (u_char *)
772
0
                netsnmp_strdup_and_null(request->requestvb->val.string,
773
0
                                        request->requestvb->val_len);
774
0
            if (!data->data.string) {
775
0
                netsnmp_set_request_error(reqinfo, requests,
776
0
                                          SNMP_ERR_RESOURCEUNAVAILABLE);
777
0
            }
778
0
            data->data_len = request->requestvb->val_len;
779
780
0
            if (datatable->rowstatus_column == table_info->colnum) {
781
0
                switch (*(request->requestvb->val.integer)) {
782
0
                case RS_CREATEANDGO:
783
                    /*
784
                     * XXX: check legality 
785
                     */
786
0
                    *(data->data.integer) = RS_ACTIVE;
787
0
                    break;
788
789
0
                case RS_CREATEANDWAIT:
790
                    /*
791
                     * XXX: check legality 
792
                     */
793
0
                    *(data->data.integer) = RS_NOTINSERVICE;
794
0
                    break;
795
796
0
                case RS_DESTROY:
797
0
                    newrowstash->deleted = 1;
798
0
                    break;
799
0
                }
800
0
            }
801
0
            break;
802
803
0
        case MODE_SET_ACTION:
804
805
            /*
806
             * Install the new row into the stored table.
807
       * Do this only *once* per row ....
808
             */
809
0
            if (newrowstash->state != STATE_ACTION) {
810
0
                newrowstash->state = STATE_ACTION;
811
0
    if (newrowstash->created) {
812
0
                    netsnmp_table_dataset_add_row(datatable, newrow);
813
0
                } else {
814
0
                    netsnmp_table_dataset_replace_row(datatable,
815
0
                                                      row, newrow);
816
0
                }
817
0
            }
818
            /*
819
             * ... but every (relevant) varbind in the request will
820
       * need to know about this new row, so update the
821
       * per-request row information regardless
822
             */
823
0
            if (newrowstash->created) {
824
0
    netsnmp_request_add_list_data(request,
825
0
      netsnmp_create_data_list(TABLE_DATA_NAME,
826
0
             newrow, NULL));
827
0
            }
828
0
            break;
829
830
0
        case MODE_SET_UNDO:
831
            /*
832
             * extract the new row, replace with the old or delete 
833
             */
834
0
            if (newrowstash->state != STATE_UNDO) {
835
0
                newrowstash->state = STATE_UNDO;
836
0
                if (newrowstash->created) {
837
0
                    netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
838
0
                } else {
839
0
                    netsnmp_table_dataset_replace_row(datatable,
840
0
                                                      newrow, row);
841
0
                    netsnmp_table_dataset_delete_row(newrow);
842
0
                }
843
0
                newrow = NULL;
844
0
            }
845
0
            break;
846
847
0
        case MODE_SET_COMMIT:
848
0
            if (newrowstash->state != STATE_COMMIT) {
849
0
                newrowstash->state = STATE_COMMIT;
850
0
                if (!newrowstash->created) {
851
0
        netsnmp_request_info       *req;
852
0
                    netsnmp_table_dataset_delete_row(row);
853
854
        /* Walk the request list to update the reference to the old row w/ th new one */
855
0
            for (req = requests; req; req=req->next) {
856
        
857
          /*
858
                         * For requests that have the old row values,
859
                         * so add the newly-created row information.
860
                         */
861
0
                if ((netsnmp_table_row *) netsnmp_extract_table_row(req) == row) {
862
0
            netsnmp_request_remove_list_data(req, TABLE_DATA_ROW);
863
0
                  netsnmp_request_add_list_data(req,
864
0
                        netsnmp_create_data_list(TABLE_DATA_ROW, newrow, NULL));
865
0
                }
866
0
            }
867
0
                }
868
0
                if (newrowstash->deleted) {
869
0
                    netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
870
0
                    newrow = NULL;
871
0
                }
872
0
            }
873
0
            break;
874
875
0
        case MODE_SET_FREE:
876
0
            if (newrowstash && newrowstash->state != STATE_FREE) {
877
0
                newrowstash->state = STATE_FREE;
878
0
                netsnmp_table_dataset_delete_row(newrow);
879
0
    newrow = NULL;
880
0
            }
881
0
            break;
882
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
883
884
0
        default:
885
0
            snmp_log(LOG_ERR,
886
0
                     "table_dataset: unknown mode passed into the handler\n");
887
0
            return SNMP_ERR_GENERR;
888
0
        }
889
0
    }
890
891
    /* next handler called automatically - 'AUTO_NEXT' */
892
0
    return SNMP_ERR_NOERROR;
893
0
}
894
895
/**
896
 * extracts a netsnmp_table_data_set pointer from a given request
897
 */
898
NETSNMP_INLINE netsnmp_table_data_set *
899
netsnmp_extract_table_data_set(netsnmp_request_info *request)
900
0
{
901
0
    return (netsnmp_table_data_set *)
902
0
        netsnmp_request_get_list_data(request, TABLE_DATA_SET_NAME);
903
0
}
904
905
/**
906
 * extracts a netsnmp_table_data_set pointer from a given request
907
 */
908
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_SET_COLUMN
909
netsnmp_table_data_set_storage *
910
netsnmp_extract_table_data_set_column(netsnmp_request_info *request,
911
                                     unsigned int column)
912
0
{
913
0
    netsnmp_table_data_set_storage *data =
914
0
        (netsnmp_table_data_set_storage*)netsnmp_extract_table_row_data( request );
915
0
    if (data) {
916
0
        data = netsnmp_table_data_set_find_column(data, column);
917
0
    }
918
0
    return data;
919
0
}
920
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_SET_COLUMN */
921
922
/* ==================================
923
 *
924
 * Data Set API: Config-based operation
925
 *
926
 * ================================== */
927
928
/** registers a table_dataset so that the "add_row" snmpd.conf token
929
  * can be used to add data to this table.  If registration_name is
930
  * NULL then the name used when the table was created will be used
931
  * instead.
932
  *
933
  * @todo create a properly free'ing registration pointer for the
934
  * datalist, and get the datalist freed at shutdown.
935
  */
936
void
937
netsnmp_register_auto_data_table(netsnmp_table_data_set *table_set,
938
                                 char *registration_name)
939
0
{
940
0
    data_set_tables *tables;
941
0
    tables = SNMP_MALLOC_TYPEDEF(data_set_tables);
942
0
    if (!tables)
943
0
        return;
944
0
    tables->table_set = table_set;
945
0
    if (!registration_name) {
946
0
        registration_name = table_set->table->name;
947
0
    }
948
0
    netsnmp_add_list_data(&auto_tables,
949
0
                          netsnmp_create_data_list(registration_name,
950
0
                                                   tables, free));     /* XXX */
951
0
}
952
953
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_UNREGISTER_AUTO_DATA_TABLE
954
/** Undo the effect of netsnmp_register_auto_data_table().
955
 */
956
void
957
netsnmp_unregister_auto_data_table(netsnmp_table_data_set *table_set,
958
           char *registration_name)
959
0
{
960
0
    netsnmp_remove_list_node(&auto_tables, registration_name
961
0
           ? registration_name : table_set->table->name);
962
0
}
963
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_UNREGISTER_AUTO_DATA_TABLE */
964
965
#ifndef NETSNMP_DISABLE_MIB_LOADING
966
static void
967
_table_set_add_indexes(netsnmp_table_data_set *table_set, struct tree *tp)
968
0
{
969
0
    oid             name[MAX_OID_LEN];
970
0
    size_t          name_length = MAX_OID_LEN;
971
0
    struct index_list *index;
972
0
    struct tree     *indexnode;
973
0
    u_char          type;
974
0
    int             fixed_len = 0;
975
    
976
    /*
977
     * loop through indexes and add types 
978
     */
979
0
    for (index = tp->indexes; index; index = index->next) {
980
0
        if (!snmp_parse_oid(index->ilabel, name, &name_length) ||
981
0
            (NULL ==
982
0
             (indexnode = get_tree(name, name_length, get_tree_head())))) {
983
0
            config_pwarn("can't instantiate table since "
984
0
                         "I don't know anything about one index");
985
0
            snmp_log(LOG_WARNING, "  index %s not found in tree\n",
986
0
                     index->ilabel);
987
0
            return;             /* xxx mem leak */
988
0
        }
989
            
990
0
        type = mib_to_asn_type(indexnode->type);
991
0
        if (type == (u_char) - 1) {
992
0
            config_pwarn("unknown index type");
993
0
            return;             /* xxx mem leak */
994
0
        }
995
        /*
996
         * if implied, mark it as such. also mark fixed length
997
         * octet strings as implied (ie no length prefix) as well.
998
         * */
999
0
        if ((TYPE_OCTETSTR == indexnode->type) &&  /* octet str */
1000
0
            (NULL != indexnode->ranges) &&         /* & has range */
1001
0
            (NULL == indexnode->ranges->next) &&   /*   but only one */
1002
0
            (indexnode->ranges->high ==            /*   & high==low */
1003
0
             indexnode->ranges->low)) {
1004
0
            type |= ASN_PRIVATE;
1005
0
            fixed_len = indexnode->ranges->high;
1006
0
        }
1007
0
        else if (index->isimplied)
1008
0
            type |= ASN_PRIVATE;
1009
        
1010
0
        DEBUGMSGTL(("table_set_add_table",
1011
0
                    "adding default index of type %d\n", type));
1012
0
        netsnmp_table_dataset_add_index(table_set, type);
1013
1014
        /*
1015
         * hack alert: for fixed length strings, save the
1016
         * length for use during oid parsing.
1017
         */
1018
0
        if (fixed_len) {
1019
            /*
1020
             * find last (just added) index
1021
             */
1022
0
            netsnmp_variable_list *var =  table_set->table->indexes_template;
1023
0
            while (NULL != var->next_variable)
1024
0
                var = var->next_variable;
1025
0
            var->val_len = fixed_len;
1026
0
        }
1027
0
    }
1028
0
}
1029
/** @internal */
1030
void
1031
netsnmp_config_parse_table_set(const char *token, char *line)
1032
0
{
1033
0
    oid             table_name[MAX_OID_LEN];
1034
0
    size_t          table_name_length = MAX_OID_LEN;
1035
0
    struct tree    *tp;
1036
0
    netsnmp_table_data_set *table_set;
1037
0
    data_set_tables *tables;
1038
0
    unsigned int    mincol = 0xffffff, maxcol = 0;
1039
0
    char           *pos;
1040
1041
    /*
1042
     * instantiate a fake table based on MIB information 
1043
     */
1044
0
    DEBUGMSGTL(("9:table_set_add_table", "processing '%s'\n", line));
1045
0
    if (NULL != (pos = strchr(line,' '))) {
1046
0
        config_pwarn("ignoring extra tokens on line");
1047
0
        snmp_log(LOG_WARNING,"  ignoring '%s'\n", pos);
1048
0
        *pos = '\0';
1049
0
    }
1050
1051
    /*
1052
     * check for duplicate table
1053
     */
1054
0
    tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, line);
1055
0
    if (NULL != tables) {
1056
0
        config_pwarn("duplicate table definition");
1057
0
        return;
1058
0
    }
1059
1060
    /*
1061
     * parse oid and find tree structure
1062
     */
1063
0
    if (!snmp_parse_oid(line, table_name, &table_name_length)) {
1064
0
        config_pwarn
1065
0
            ("can't instantiate table since I can't parse the table name");
1066
0
        return;
1067
0
    }
1068
0
    if(NULL == (tp = get_tree(table_name, table_name_length,
1069
0
                              get_tree_head()))) {
1070
0
        config_pwarn("can't instantiate table since "
1071
0
                     "I can't find mib information about it");
1072
0
        return;
1073
0
    }
1074
1075
0
    if (NULL == (tp = tp->child_list) || NULL == tp->child_list) {
1076
0
        config_pwarn("can't instantiate table since it doesn't appear to be "
1077
0
                     "a proper table (no children)");
1078
0
        return;
1079
0
    }
1080
1081
0
    table_set = netsnmp_create_table_data_set(line);
1082
1083
    /*
1084
     * check for augments indexes
1085
     */
1086
0
    if (NULL != tp->augments) {
1087
0
        oid             name[MAX_OID_LEN];
1088
0
        size_t          name_length = MAX_OID_LEN;
1089
0
        struct tree    *tp2;
1090
    
1091
0
        if (!snmp_parse_oid(tp->augments, name, &name_length)) {
1092
0
            config_pwarn("I can't parse the augment table name");
1093
0
            snmp_log(LOG_WARNING, "  can't parse %s\n", tp->augments);
1094
0
            SNMP_FREE (table_set);
1095
0
            return;
1096
0
        }
1097
0
        if(NULL == (tp2 = get_tree(name, name_length, get_tree_head()))) {
1098
0
            config_pwarn("can't instantiate table since "
1099
0
                         "I can't find mib information about augment table");
1100
0
            snmp_log(LOG_WARNING, "  table %s not found in tree\n",
1101
0
                     tp->augments);
1102
0
            SNMP_FREE (table_set);
1103
0
            return;
1104
0
        }
1105
0
        _table_set_add_indexes(table_set, tp2);
1106
0
    }
1107
1108
0
    _table_set_add_indexes(table_set, tp);
1109
    
1110
    /*
1111
     * loop through children and add each column info 
1112
     */
1113
0
    for (tp = tp->child_list; tp; tp = tp->next_peer) {
1114
0
        int             canwrite = 0;
1115
0
        u_char          type;
1116
0
        type = mib_to_asn_type(tp->type);
1117
0
        if (type == (u_char) - 1) {
1118
0
            config_pwarn("unknown column type");
1119
0
      SNMP_FREE (table_set);
1120
0
            return;             /* xxx mem leak */
1121
0
        }
1122
1123
0
        DEBUGMSGTL(("table_set_add_table",
1124
0
                    "adding column %s(%ld) of type %d (access %d)\n",
1125
0
                    tp->label, tp->subid, type, tp->access));
1126
1127
0
        switch (tp->access) {
1128
0
        case MIB_ACCESS_CREATE:
1129
0
            table_set->allow_creation = 1;
1130
0
            NETSNMP_FALLTHROUGH;
1131
0
        case MIB_ACCESS_READWRITE:
1132
0
        case MIB_ACCESS_WRITEONLY:
1133
0
            canwrite = 1;
1134
0
            NETSNMP_FALLTHROUGH;
1135
0
        case MIB_ACCESS_READONLY:
1136
0
            DEBUGMSGTL(("table_set_add_table",
1137
0
                        "adding column %ld of type %d\n", tp->subid, type));
1138
0
            netsnmp_table_set_add_default_row(table_set, tp->subid, type,
1139
0
                                              canwrite, NULL, 0);
1140
0
            mincol = SNMP_MIN(mincol, tp->subid);
1141
0
            maxcol = SNMP_MAX(maxcol, tp->subid);
1142
0
            break;
1143
1144
0
        case MIB_ACCESS_NOACCESS:
1145
0
        case MIB_ACCESS_NOTIFY:
1146
0
            break;
1147
1148
0
        default:
1149
0
            config_pwarn("unknown column access type");
1150
0
            break;
1151
0
        }
1152
0
    }
1153
1154
    /*
1155
     * register the table 
1156
     */
1157
0
    netsnmp_register_table_data_set(netsnmp_create_handler_registration
1158
0
                                    (line, NULL, table_name,
1159
0
                                     table_name_length,
1160
0
                                     HANDLER_CAN_RWRITE), table_set, NULL);
1161
1162
0
    netsnmp_register_auto_data_table(table_set, NULL);
1163
0
}
1164
#endif /* NETSNMP_DISABLE_MIB_LOADING */
1165
1166
/** @internal */
1167
void
1168
netsnmp_config_parse_add_row(const char *token, char *line)
1169
0
{
1170
0
    char            buf[SNMP_MAXBUF_MEDIUM];
1171
0
    char            tname[SNMP_MAXBUF_MEDIUM];
1172
0
    size_t          buf_size;
1173
0
    int             rc;
1174
1175
0
    data_set_tables *tables;
1176
0
    netsnmp_variable_list *vb;  /* containing only types */
1177
0
    netsnmp_table_row *row;
1178
0
    netsnmp_table_data_set_storage *dr;
1179
1180
0
    line = copy_nword(line, tname, sizeof(tname));
1181
1182
0
    tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, tname);
1183
0
    if (!tables) {
1184
0
        config_pwarn("Unknown table trying to add a row");
1185
0
        return;
1186
0
    }
1187
1188
    /*
1189
     * do the indexes first 
1190
     */
1191
0
    row = netsnmp_create_table_data_row();
1192
1193
0
    for (vb = tables->table_set->table->indexes_template; vb;
1194
0
         vb = vb->next_variable) {
1195
0
        if (!line) {
1196
0
            config_pwarn("missing an index value");
1197
0
            SNMP_FREE (row);
1198
0
            return;
1199
0
        }
1200
1201
0
        DEBUGMSGTL(("table_set_add_row", "adding index of type %d\n",
1202
0
                    vb->type));
1203
0
        buf_size = sizeof(buf);
1204
0
        line = read_config_read_memory(vb->type, line, buf, &buf_size);
1205
0
        netsnmp_table_row_add_index(row, vb->type, buf, buf_size);
1206
0
    }
1207
1208
    /*
1209
     * then do the data 
1210
     */
1211
0
    for (dr = tables->table_set->default_row; dr; dr = dr->next) {
1212
0
        if (!line) {
1213
0
            config_pwarn("missing a data value. "
1214
0
                         "All columns must be specified.");
1215
0
            snmp_log(LOG_WARNING,"  can't find value for column %d\n",
1216
0
                     dr->column - 1);
1217
0
            SNMP_FREE (row);
1218
0
            return;
1219
0
        }
1220
1221
0
        buf_size = sizeof(buf);
1222
0
        line = read_config_read_memory(dr->type, line, buf, &buf_size);
1223
0
        DEBUGMSGTL(("table_set_add_row",
1224
0
                    "adding data at column %d of type %d\n", dr->column,
1225
0
                    dr->type));
1226
0
        netsnmp_set_row_column(row, dr->column, dr->type, buf, buf_size);
1227
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
1228
0
        if (dr->writable)
1229
0
            netsnmp_mark_row_column_writable(row, dr->column, 1);       /* make writable */
1230
0
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
1231
0
    }
1232
0
    rc = netsnmp_table_data_add_row(tables->table_set->table, row);
1233
0
    if (SNMPERR_SUCCESS != rc) {
1234
0
        config_pwarn("error adding table row");
1235
0
    }
1236
0
    if (NULL != line) {
1237
0
        config_pwarn("extra data value. Too many columns specified.");
1238
0
        snmp_log(LOG_WARNING,"  extra data '%s'\n", line);
1239
0
    }
1240
0
}
1241
1242
1243
#ifndef NETSNMP_NO_WRITE_SUPPORT
1244
netsnmp_oid_stash_node **
1245
netsnmp_table_dataset_get_or_create_stash(netsnmp_agent_request_info *reqinfo,
1246
                                          netsnmp_table_data_set *datatable,
1247
                                          netsnmp_table_request_info *table_info)
1248
0
{
1249
0
    netsnmp_oid_stash_node **stashp = NULL;
1250
0
    char                     buf[256]; /* is this reasonable size?? */
1251
0
    size_t                   len;
1252
0
    int                      rc;
1253
1254
0
    rc = snprintf(buf, sizeof(buf), "dataset_row_stash:%s:",
1255
0
                  datatable->table->name);
1256
0
    if ((-1 == rc) || ((size_t)rc >= sizeof(buf))) {
1257
0
        snmp_log(LOG_ERR,"%s handler name too long\n", datatable->table->name);
1258
0
        return NULL;
1259
0
    }
1260
1261
0
    len = sizeof(buf) - rc;
1262
0
    rc = snprint_objid(&buf[rc], len, table_info->index_oid,
1263
0
                       table_info->index_oid_len);
1264
0
    if (-1 == rc) {
1265
0
        snmp_log(LOG_ERR,"%s oid or name too long\n", datatable->table->name);
1266
0
        return NULL;
1267
0
    }
1268
1269
0
    stashp = (netsnmp_oid_stash_node **)
1270
0
        netsnmp_table_get_or_create_row_stash(reqinfo, (u_char *) buf);
1271
0
    return stashp;
1272
0
}
1273
1274
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_GET_NEWROW
1275
netsnmp_table_row *
1276
netsnmp_table_dataset_get_newrow(netsnmp_request_info *request,
1277
                                 netsnmp_agent_request_info *reqinfo,
1278
                                 int rootoid_len,
1279
                                 netsnmp_table_data_set *datatable,
1280
                                 netsnmp_table_request_info *table_info)
1281
0
{
1282
0
    oid * const suffix = request->requestvb->name + rootoid_len + 2;
1283
0
    size_t suffix_len = request->requestvb->name_length - (rootoid_len + 2);
1284
0
    netsnmp_oid_stash_node **stashp;
1285
0
    newrow_stash   *newrowstash;
1286
1287
0
    stashp = netsnmp_table_dataset_get_or_create_stash(reqinfo, datatable,
1288
0
                                                       table_info);
1289
0
    if (NULL == stashp)
1290
0
        return NULL;
1291
1292
0
    newrowstash = (newrow_stash*)netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len);
1293
0
    if (NULL == newrowstash)
1294
0
        return NULL;
1295
1296
0
    return newrowstash->newrow;
1297
0
}
1298
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_GET_NEWROW */
1299
#endif /* NETSNMP_NO_WRITE_SUPPORT */
1300
1301
/* ==================================
1302
 *
1303
 * Data Set API: Row operations
1304
 *
1305
 * ================================== */
1306
1307
/** returns the first row in the table */
1308
netsnmp_table_row *
1309
netsnmp_table_data_set_get_first_row(netsnmp_table_data_set *table)
1310
0
{
1311
0
    return netsnmp_table_data_get_first_row(table->table);
1312
0
}
1313
1314
/** returns the next row in the table */
1315
netsnmp_table_row *
1316
netsnmp_table_data_set_get_next_row(netsnmp_table_data_set *table,
1317
                                    netsnmp_table_row      *row)
1318
0
{
1319
0
    return netsnmp_table_data_get_next_row(table->table, row);
1320
0
}
1321
1322
int
1323
netsnmp_table_set_num_rows(netsnmp_table_data_set *table)
1324
0
{
1325
0
    if (!table)
1326
0
        return 0;
1327
0
    return netsnmp_table_data_num_rows(table->table);
1328
0
}
1329
1330
/* ==================================
1331
 *
1332
 * Data Set API: Column operations
1333
 *
1334
 * ================================== */
1335
1336
/** Finds a column within a given storage set, given the pointer to
1337
   the start of the storage set list.
1338
*/
1339
netsnmp_table_data_set_storage *
1340
netsnmp_table_data_set_find_column(netsnmp_table_data_set_storage *start,
1341
                                   unsigned int column)
1342
0
{
1343
0
    while (start && start->column != column)
1344
0
        start = start->next;
1345
0
    return start;
1346
0
}
1347
1348
#ifndef NETSNMP_NO_WRITE_SUPPORT
1349
/**
1350
 * marks a given column in a row as writable or not.
1351
 */
1352
int
1353
netsnmp_mark_row_column_writable(netsnmp_table_row *row, int column,
1354
                                 int writable)
1355
0
{
1356
0
    netsnmp_table_data_set_storage *data;
1357
1358
0
    if (!row)
1359
0
        return SNMPERR_GENERR;
1360
1361
0
    data = (netsnmp_table_data_set_storage *) row->data;
1362
0
    data = netsnmp_table_data_set_find_column(data, column);
1363
1364
0
    if (!data) {
1365
        /*
1366
         * create it 
1367
         */
1368
0
        data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
1369
0
        if (!data) {
1370
0
            snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
1371
0
            return SNMPERR_MALLOC;
1372
0
        }
1373
0
        data->column = column;
1374
0
        data->writable = writable;
1375
0
        data->next = (struct netsnmp_table_data_set_storage_s*)row->data;
1376
0
        row->data = data;
1377
0
    } else {
1378
0
        data->writable = writable;
1379
0
    }
1380
0
    return SNMPERR_SUCCESS;
1381
0
}
1382
#endif /* NETSNMP_NO_WRITE_SUPPORT */
1383
1384
/**
1385
 * Sets a given column in a row with data given a type, value,
1386
 * and length. Data is memdup'ed by the function, at least if
1387
 * type != SNMP_NOSUCHINSTANCE and if value_len > 0.
1388
 *
1389
 * @param[in] row       Pointer to the row to be modified.
1390
 * @param[in] column    Index of the column to be modified.
1391
 * @param[in] type      Either the ASN type of the value to be set or
1392
 *   SNMP_NOSUCHINSTANCE.
1393
 * @param[in] value     If type != SNMP_NOSUCHINSTANCE, pointer to the
1394
 *   new value. May be NULL if value_len == 0, e.g. when storing a
1395
 *   zero-length octet string. Ignored when type == SNMP_NOSUCHINSTANCE.
1396
 * @param[in] value_len If type != SNMP_NOSUCHINSTANCE, number of bytes
1397
 *   occupied by *value. Ignored when type == SNMP_NOSUCHINSTANCE.
1398
 *
1399
 * @return SNMPERR_SUCCESS upon success; SNMPERR_MALLOC when out of memory;
1400
 *   or SNMPERR_GENERR when row == 0 or when type does not match the datatype
1401
 *   of the data stored in *row. 
1402
 *
1403
 */
1404
int
1405
netsnmp_set_row_column(netsnmp_table_row *row, unsigned int column,
1406
                       int type, const void *value, size_t value_len)
1407
0
{
1408
0
    netsnmp_table_data_set_storage *data;
1409
1410
0
    if (!row)
1411
0
        return SNMPERR_GENERR;
1412
1413
0
    data = (netsnmp_table_data_set_storage *) row->data;
1414
0
    data = netsnmp_table_data_set_find_column(data, column);
1415
1416
0
    if (!data) {
1417
        /*
1418
         * create it 
1419
         */
1420
0
        data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
1421
0
        if (!data) {
1422
0
            snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
1423
0
            return SNMPERR_MALLOC;
1424
0
        }
1425
1426
0
        data->column = column;
1427
0
        data->type = type;
1428
0
        data->next = (struct netsnmp_table_data_set_storage_s*)row->data;
1429
0
        row->data = data;
1430
0
    }
1431
1432
    /* Transitions from / to SNMP_NOSUCHINSTANCE are allowed, but no other transitions. */
1433
0
    if (data->type != type && data->type != SNMP_NOSUCHINSTANCE
1434
0
        && type != SNMP_NOSUCHINSTANCE)
1435
0
        return SNMPERR_GENERR;
1436
1437
    /* Return now if neither the type nor the data itself has been modified. */
1438
0
    if (data->type == type && data->data_len == value_len
1439
0
        && (value == NULL || memcmp(&data->data.string, value, value_len) == 0))
1440
0
            return SNMPERR_SUCCESS;
1441
1442
    /* Reallocate memory and store the new value. */
1443
0
    data->data.voidp = realloc(data->data.voidp, value ? value_len : 0);
1444
0
    if (value && value_len && !data->data.voidp) {
1445
0
        data->data_len = 0;
1446
0
        data->type = SNMP_NOSUCHINSTANCE;
1447
0
        snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
1448
0
        return SNMPERR_MALLOC;
1449
0
    }
1450
0
    if (value && value_len)
1451
0
        memcpy(data->data.string, value, value_len);
1452
0
    data->type = type;
1453
0
    data->data_len = value_len;
1454
0
    return SNMPERR_SUCCESS;
1455
0
}
1456
1457
1458
/* ==================================
1459
 *
1460
 * Data Set API: Index operations
1461
 *
1462
 * ================================== */
1463
1464
/** adds an index to the table.  Call this repeatedly for each index. */
1465
void
1466
netsnmp_table_dataset_add_index(netsnmp_table_data_set *table, u_char type)
1467
0
{
1468
0
    if (!table)
1469
0
        return;
1470
0
    netsnmp_table_data_add_index(table->table, type);
1471
0
}
1472
1473
/** adds multiple indexes to a table_dataset helper object.
1474
 *  To end the list, use a 0 after the list of ASN index types. */
1475
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_SET_ADD_INDEXES
1476
void
1477
netsnmp_table_set_add_indexes(netsnmp_table_data_set *tset,
1478
                              ...)
1479
0
{
1480
0
    va_list         debugargs;
1481
0
    int             type;
1482
1483
0
    va_start(debugargs, tset);
1484
1485
0
    if (tset)
1486
0
        while ((type = va_arg(debugargs, int)) != 0)
1487
0
            netsnmp_table_data_add_index(tset->table, (u_char)type);
1488
1489
    va_end(debugargs);
1490
0
}
1491
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_SET_ADD_INDEXES */
1492
1493
#else /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET */
1494
netsnmp_feature_unused(table_dataset);
1495
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET */
1496
/** @} 
1497
 */