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