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