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