/src/net-snmp/snmplib/data_list.c
Line | Count | Source |
1 | | /* |
2 | | * netsnmp_data_list.c |
3 | | * |
4 | | * Portions of this file are subject to the following copyright(s). See |
5 | | * the Net-SNMP's COPYING file for more details and other copyrights |
6 | | * that may apply: |
7 | | * |
8 | | * Portions of this file are copyrighted by: |
9 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
10 | | * Use is subject to license terms specified in the COPYING file |
11 | | * distributed with the Net-SNMP package. |
12 | | */ |
13 | | #include <net-snmp/net-snmp-config.h> |
14 | | #include <net-snmp/net-snmp-features.h> |
15 | | #include <net-snmp/net-snmp-includes.h> |
16 | | |
17 | | netsnmp_feature_child_of(data_list_all, libnetsnmp); |
18 | | |
19 | | netsnmp_feature_child_of(data_list_add_data, data_list_all); |
20 | | netsnmp_feature_child_of(data_list_get_list_node, data_list_all); |
21 | | |
22 | | /** @defgroup data_list generic linked-list data handling with a string as a key. |
23 | | * @ingroup library |
24 | | * @{ |
25 | | */ |
26 | | |
27 | | /** frees the data and a name at a given data_list node. |
28 | | * Note that this doesn't free the node itself. |
29 | | * @param node the node for which the data should be freed |
30 | | */ |
31 | | NETSNMP_INLINE void |
32 | | netsnmp_free_list_data(netsnmp_data_list *node) |
33 | 19.6k | { |
34 | 19.6k | Netsnmp_Free_List_Data *beer; |
35 | 19.6k | if (!node) |
36 | 0 | return; |
37 | | |
38 | 19.6k | beer = node->free_func; |
39 | 19.6k | if (beer) |
40 | 19.6k | (beer) (node->data); |
41 | 19.6k | SNMP_FREE(node->name); |
42 | 19.6k | } |
43 | | |
44 | | /** frees all data and nodes in a list. |
45 | | * @param head the top node of the list to be freed. |
46 | | */ |
47 | | NETSNMP_INLINE void |
48 | | netsnmp_free_all_list_data(netsnmp_data_list *head) |
49 | 13.1k | { |
50 | 13.1k | netsnmp_data_list *tmpptr; |
51 | 32.7k | for (; head;) { |
52 | 19.6k | netsnmp_free_list_data(head); |
53 | 19.6k | tmpptr = head; |
54 | 19.6k | head = head->next; |
55 | 19.6k | SNMP_FREE(tmpptr); |
56 | 19.6k | } |
57 | 13.1k | } |
58 | | |
59 | | /** adds creates a data_list node given a name, data and a free function ptr. |
60 | | * @param name the name of the node to cache the data. |
61 | | * @param data the data to be stored under that name |
62 | | * @param beer A function that can free the data pointer (in the future) |
63 | | * @return a newly created data_list node which can be given to the netsnmp_add_list_data function. |
64 | | */ |
65 | | NETSNMP_INLINE netsnmp_data_list * |
66 | | netsnmp_create_data_list(const char *name, void *data, |
67 | | Netsnmp_Free_List_Data * beer) |
68 | 19.9k | { |
69 | 19.9k | netsnmp_data_list *node; |
70 | | |
71 | 19.9k | if (!name) |
72 | 0 | return NULL; |
73 | 19.9k | node = SNMP_MALLOC_TYPEDEF(netsnmp_data_list); |
74 | 19.9k | if (!node) |
75 | 0 | return NULL; |
76 | 19.9k | node->name = strdup(name); |
77 | 19.9k | if (!node->name) { |
78 | 0 | free(node); |
79 | 0 | return NULL; |
80 | 0 | } |
81 | 19.9k | node->data = data; |
82 | 19.9k | node->free_func = beer; |
83 | 19.9k | return node; |
84 | 19.9k | } |
85 | | |
86 | | /** adds data to a datalist |
87 | | * @param head a pointer to the head node of a data_list |
88 | | * @param node a node to stash in the data_list |
89 | | */ |
90 | | NETSNMP_INLINE void |
91 | | netsnmp_data_list_add_node(netsnmp_data_list **head, netsnmp_data_list *node) |
92 | 19.9k | { |
93 | 19.9k | netsnmp_data_list *ptr; |
94 | | |
95 | 19.9k | netsnmp_assert(NULL != head); |
96 | 19.9k | netsnmp_assert(NULL != node); |
97 | 19.9k | if (!head || !node) |
98 | 0 | return; |
99 | | |
100 | 19.9k | netsnmp_assert(NULL != node->name); |
101 | | |
102 | 19.9k | DEBUGMSGTL(("data_list","adding key '%s'\n", node->name)); |
103 | | |
104 | 19.9k | if (!*head) { |
105 | 3.27k | *head = node; |
106 | 3.27k | return; |
107 | 3.27k | } |
108 | | |
109 | 16.6k | if (0 == strcmp(node->name, (*head)->name)) { |
110 | 0 | netsnmp_assert(!"list key == is unique"); /* always fail */ |
111 | 0 | snmp_log(LOG_WARNING, |
112 | 0 | "WARNING: adding duplicate key '%s' to data list\n", |
113 | 0 | node->name); |
114 | 0 | } |
115 | | |
116 | 83.8k | for (ptr = *head; ptr->next != NULL; ptr = ptr->next) { |
117 | 67.2k | netsnmp_assert(NULL != ptr->name); |
118 | 67.2k | if (0 == strcmp(node->name, ptr->name)) { |
119 | 541 | netsnmp_assert(!"list key == is unique"); /* always fail */ |
120 | 541 | snmp_log(LOG_WARNING, |
121 | 541 | "WARNING: adding duplicate key '%s' to data list\n", |
122 | 541 | node->name); |
123 | 541 | } |
124 | 67.2k | } |
125 | | |
126 | 16.6k | netsnmp_assert(NULL != ptr); |
127 | 16.6k | if (ptr) /* should always be true */ |
128 | 16.6k | ptr->next = node; |
129 | 16.6k | } |
130 | | |
131 | | /** adds data to a datalist |
132 | | * @note netsnmp_data_list_add_node is preferred |
133 | | * @param head a pointer to the head node of a data_list |
134 | | * @param node a node to stash in the data_list |
135 | | */ |
136 | | /** */ |
137 | | NETSNMP_INLINE void |
138 | | netsnmp_add_list_data(netsnmp_data_list **head, netsnmp_data_list *node) |
139 | 19.6k | { |
140 | 19.6k | netsnmp_data_list_add_node(head, node); |
141 | 19.6k | } |
142 | | |
143 | | /** adds data to a datalist |
144 | | * @param head a pointer to the head node of a data_list |
145 | | * @param name the name of the node to cache the data. |
146 | | * @param data the data to be stored under that name |
147 | | * @param beer A function that can free the data pointer (in the future) |
148 | | * @return a newly created data_list node which was inserted in the list |
149 | | */ |
150 | | #ifndef NETSNMP_FEATURE_REMOVE_DATA_LIST_ADD_DATA |
151 | | NETSNMP_INLINE netsnmp_data_list * |
152 | | netsnmp_data_list_add_data(netsnmp_data_list **head, const char *name, |
153 | | void *data, Netsnmp_Free_List_Data * beer) |
154 | 0 | { |
155 | 0 | netsnmp_data_list *node; |
156 | 0 | if (!name) { |
157 | 0 | snmp_log(LOG_ERR,"no name provided."); |
158 | 0 | return NULL; |
159 | 0 | } |
160 | 0 | node = netsnmp_create_data_list(name, data, beer); |
161 | 0 | if(NULL == node) { |
162 | 0 | snmp_log(LOG_ERR,"could not allocate memory for node."); |
163 | 0 | return NULL; |
164 | 0 | } |
165 | | |
166 | 0 | netsnmp_add_list_data(head, node); |
167 | |
|
168 | 0 | return node; |
169 | 0 | } |
170 | | #endif /* NETSNMP_FEATURE_REMOVE_DATA_LIST_ADD_DATA */ |
171 | | |
172 | | /** returns a data_list node's data for a given name within a data_list |
173 | | * @param head the head node of a data_list |
174 | | * @param name the name to find |
175 | | * @return a pointer to the data cached at that node |
176 | | */ |
177 | | NETSNMP_INLINE void * |
178 | | netsnmp_get_list_data(netsnmp_data_list *head, const char *name) |
179 | 0 | { |
180 | 0 | if (!name) |
181 | 0 | return NULL; |
182 | 0 | for (; head; head = head->next) |
183 | 0 | if (head->name && strcmp(head->name, name) == 0) |
184 | 0 | break; |
185 | 0 | if (head) |
186 | 0 | return head->data; |
187 | 0 | return NULL; |
188 | 0 | } |
189 | | |
190 | | /** returns a data_list node for a given name within a data_list |
191 | | * @param head the head node of a data_list |
192 | | * @param name the name to find |
193 | | * @return a pointer to the data_list node |
194 | | */ |
195 | | #ifndef NETSNMP_FEATURE_REMOVE_DATA_LIST_GET_LIST_NODE |
196 | | NETSNMP_INLINE netsnmp_data_list * |
197 | | netsnmp_get_list_node(netsnmp_data_list *head, const char *name) |
198 | 0 | { |
199 | 0 | if (!name) |
200 | 0 | return NULL; |
201 | 0 | for (; head; head = head->next) |
202 | 0 | if (head->name && strcmp(head->name, name) == 0) |
203 | 0 | break; |
204 | 0 | if (head) |
205 | 0 | return head; |
206 | 0 | return NULL; |
207 | 0 | } |
208 | | #endif /* NETSNMP_FEATURE_REMOVE_DATA_LIST_GET_LIST_NODE */ |
209 | | |
210 | | /** Removes a named node from a data_list (and frees it) |
211 | | * @param realhead a pointer to the head node of a data_list |
212 | | * @param name the name to find and remove |
213 | | * @return 0 on successful find-and-delete, 1 otherwise. |
214 | | */ |
215 | | int |
216 | | netsnmp_remove_list_node(netsnmp_data_list **realhead, const char *name) |
217 | 0 | { |
218 | 0 | netsnmp_data_list *head, *prev; |
219 | 0 | if (!name) |
220 | 0 | return 1; |
221 | 0 | for (head = *realhead, prev = NULL; head; |
222 | 0 | prev = head, head = head->next) { |
223 | 0 | if (head->name && strcmp(head->name, name) == 0) { |
224 | 0 | if (prev) |
225 | 0 | prev->next = head->next; |
226 | 0 | else |
227 | 0 | *realhead = head->next; |
228 | 0 | netsnmp_free_list_data(head); |
229 | 0 | free(head); |
230 | 0 | return 0; |
231 | 0 | } |
232 | 0 | } |
233 | 0 | return 1; |
234 | 0 | } |
235 | | |
236 | | /** used to store registered save/parse handlers (specifically, parsing info) */ |
237 | | static netsnmp_data_list *saveHead; |
238 | | |
239 | | /** registers to store a data_list set of data at persistent storage time |
240 | | * |
241 | | * @param datalist the data to be saved |
242 | | * @param type the name of the application to save the data as. If left NULL the default application name that was registered during the init_snmp call will be used (recommended). |
243 | | * @param token the unique token identifier string to use as the first word in the persistent file line. |
244 | | * @param data_list_save_ptr a function pointer which will be called to save the rest of the data to a buffer. |
245 | | * @param data_list_read_ptr a function pointer which can read the remainder of a saved line and return the application specific void * pointer. |
246 | | * @param data_list_free_ptr a function pointer which will be passed to the data node for freeing it in the future when/if the list/node is cleaned up or destroyed. |
247 | | */ |
248 | | void |
249 | | netsnmp_register_save_list(netsnmp_data_list **datalist, |
250 | | const char *type, const char *token, |
251 | | Netsnmp_Save_List_Data *data_list_save_ptr, |
252 | | Netsnmp_Read_List_Data *data_list_read_ptr, |
253 | | Netsnmp_Free_List_Data *data_list_free_ptr) |
254 | 0 | { |
255 | 0 | netsnmp_data_list_saveinfo *info; |
256 | |
|
257 | 0 | if (!data_list_save_ptr && !data_list_read_ptr) |
258 | 0 | return; |
259 | | |
260 | 0 | info = SNMP_MALLOC_TYPEDEF(netsnmp_data_list_saveinfo); |
261 | |
|
262 | 0 | if (!info) { |
263 | 0 | snmp_log(LOG_ERR, "couldn't malloc a netsnmp_data_list_saveinfo typedef"); |
264 | 0 | return; |
265 | 0 | } |
266 | | |
267 | 0 | info->datalist = datalist; |
268 | 0 | info->token = token; |
269 | 0 | info->type = type; |
270 | 0 | if (!info->type) { |
271 | 0 | info->type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
272 | 0 | NETSNMP_DS_LIB_APPTYPE); |
273 | 0 | } |
274 | | |
275 | | /* function which will save the data */ |
276 | 0 | info->data_list_save_ptr = data_list_save_ptr; |
277 | 0 | if (data_list_save_ptr) |
278 | 0 | snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA, |
279 | 0 | netsnmp_save_all_data_callback, info); |
280 | | |
281 | | /* function which will read the data back in */ |
282 | 0 | info->data_list_read_ptr = data_list_read_ptr; |
283 | 0 | if (data_list_read_ptr) { |
284 | | /** @todo netsnmp_register_save_list should handle the same token name being saved from different types? */ |
285 | 0 | netsnmp_add_list_data(&saveHead, |
286 | 0 | netsnmp_create_data_list(token, info, NULL)); |
287 | 0 | register_config_handler(type, token, netsnmp_read_data_callback, |
288 | 0 | NULL /* XXX */, NULL); |
289 | 0 | } |
290 | |
|
291 | 0 | info->data_list_free_ptr = data_list_free_ptr; |
292 | 0 | } |
293 | | |
294 | | |
295 | | /** intended to be registered as a callback operation. |
296 | | * It should be registered using: |
297 | | * |
298 | | * snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA, netsnmp_save_all_data_callback, INFO_POINTER); |
299 | | * |
300 | | * where INFO_POINTER is a pointer to a netsnmp_data_list_saveinfo object containing appropriate registration information |
301 | | */ |
302 | | int |
303 | | netsnmp_save_all_data_callback(int major, int minor, |
304 | 0 | void *serverarg, void *clientarg) { |
305 | 0 | netsnmp_data_list_saveinfo *info = (netsnmp_data_list_saveinfo *)clientarg; |
306 | |
|
307 | 0 | if (!clientarg) { |
308 | 0 | snmp_log(LOG_WARNING, "netsnmp_save_all_data_callback called with no passed data"); |
309 | 0 | return SNMP_ERR_NOERROR; |
310 | 0 | } |
311 | | |
312 | 0 | netsnmp_save_all_data(*(info->datalist), info->type, info->token, |
313 | 0 | info->data_list_save_ptr); |
314 | 0 | return SNMP_ERR_NOERROR; |
315 | 0 | } |
316 | | |
317 | | /** intended to be called as a callback during persistent save operations. |
318 | | * See the netsnmp_save_all_data_callback for where this is typically used. */ |
319 | | int |
320 | | netsnmp_save_all_data(netsnmp_data_list *head, |
321 | | const char *type, const char *token, |
322 | | Netsnmp_Save_List_Data * data_list_save_ptr) |
323 | 0 | { |
324 | 0 | char buf[SNMP_MAXBUF], *cp; |
325 | |
|
326 | 0 | for (; head; head = head->next) { |
327 | 0 | if (head->name) { |
328 | | /* save begining of line */ |
329 | 0 | snprintf(buf, sizeof(buf), "%s ", token); |
330 | 0 | cp = buf + strlen(buf); |
331 | 0 | cp = read_config_save_octet_string(cp, (u_char*)head->name, |
332 | 0 | strlen(head->name)); |
333 | 0 | *cp++ = ' '; |
334 | | |
335 | | /* call registered function to save the rest */ |
336 | 0 | if (!(data_list_save_ptr)(cp, |
337 | 0 | sizeof(buf) - strlen(buf), |
338 | 0 | head->data)) { |
339 | 0 | read_config_store(type, buf); |
340 | 0 | } |
341 | 0 | } |
342 | 0 | } |
343 | 0 | return SNMP_ERR_NOERROR; |
344 | 0 | } |
345 | | |
346 | | /** intended to be registered as a .conf parser |
347 | | * It should be registered using: |
348 | | * |
349 | | * register_app_config_handler("token", netsnmp_read_data_callback, XXX) |
350 | | * |
351 | | * where INFO_POINTER is a pointer to a netsnmp_data_list_saveinfo object |
352 | | * containing appropriate registration information |
353 | | * @todo make netsnmp_read_data_callback deal with a free routine |
354 | | */ |
355 | | void |
356 | 0 | netsnmp_read_data_callback(const char *token, char *line) { |
357 | 0 | netsnmp_data_list_saveinfo *info; |
358 | 0 | char *dataname = NULL; |
359 | 0 | size_t dataname_len; |
360 | 0 | void *data = NULL; |
361 | | |
362 | | /* find the stashed information about what we're parsing */ |
363 | 0 | info = (netsnmp_data_list_saveinfo *) netsnmp_get_list_data(saveHead, token); |
364 | 0 | if (!info) { |
365 | 0 | snmp_log(LOG_WARNING, "netsnmp_read_data_callback called without previously registered subparser"); |
366 | 0 | return; |
367 | 0 | } |
368 | | |
369 | | /* read in the token */ |
370 | 0 | line = |
371 | 0 | read_config_read_data(ASN_OCTET_STR, line, |
372 | 0 | &dataname, &dataname_len); |
373 | |
|
374 | 0 | if (!line || !dataname) |
375 | 0 | return; |
376 | | |
377 | | /* call the sub-parser to read the rest */ |
378 | 0 | data = (info->data_list_read_ptr)(line, strlen(line)); |
379 | |
|
380 | 0 | if (!data) { |
381 | 0 | free(dataname); |
382 | 0 | return; |
383 | 0 | } |
384 | | |
385 | | /* add to the datalist */ |
386 | 0 | netsnmp_add_list_data(info->datalist, |
387 | 0 | netsnmp_create_data_list(dataname, data, |
388 | 0 | info->data_list_free_ptr)); |
389 | |
|
390 | 0 | return; |
391 | 0 | } |
392 | | |
393 | | void |
394 | | shutdown_data_list(void) |
395 | 3.27k | { |
396 | 3.27k | netsnmp_free_all_list_data(saveHead); |
397 | 3.27k | } |
398 | | |
399 | | /** @} */ |
400 | | |