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