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
netsnmp_free_list_data(netsnmp_data_list * node)32 netsnmp_free_list_data(netsnmp_data_list *node)
33 {
34 Netsnmp_Free_List_Data *beer;
35 if (!node)
36 return;
37
38 beer = node->free_func;
39 if (beer)
40 (beer) (node->data);
41 SNMP_FREE(node->name);
42 }
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
netsnmp_free_all_list_data(netsnmp_data_list * head)48 netsnmp_free_all_list_data(netsnmp_data_list *head)
49 {
50 netsnmp_data_list *tmpptr;
51 for (; head;) {
52 netsnmp_free_list_data(head);
53 tmpptr = head;
54 head = head->next;
55 SNMP_FREE(tmpptr);
56 }
57 }
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 *
netsnmp_create_data_list(const char * name,void * data,Netsnmp_Free_List_Data * beer)66 netsnmp_create_data_list(const char *name, void *data,
67 Netsnmp_Free_List_Data * beer)
68 {
69 netsnmp_data_list *node;
70
71 if (!name)
72 return NULL;
73 node = SNMP_MALLOC_TYPEDEF(netsnmp_data_list);
74 if (!node)
75 return NULL;
76 node->name = strdup(name);
77 if (!node->name) {
78 free(node);
79 return NULL;
80 }
81 node->data = data;
82 node->free_func = beer;
83 return node;
84 }
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
netsnmp_data_list_add_node(netsnmp_data_list ** head,netsnmp_data_list * node)91 netsnmp_data_list_add_node(netsnmp_data_list **head, netsnmp_data_list *node)
92 {
93 netsnmp_data_list *ptr;
94
95 netsnmp_assert(NULL != head);
96 netsnmp_assert(NULL != node);
97 if (!head || !node)
98 return;
99
100 netsnmp_assert(NULL != node->name);
101
102 DEBUGMSGTL(("data_list","adding key '%s'\n", node->name));
103
104 if (!*head) {
105 *head = node;
106 return;
107 }
108
109 if (0 == strcmp(node->name, (*head)->name)) {
110 netsnmp_assert(!"list key == is unique"); /* always fail */
111 snmp_log(LOG_WARNING,
112 "WARNING: adding duplicate key '%s' to data list\n",
113 node->name);
114 }
115
116 for (ptr = *head; ptr->next != NULL; ptr = ptr->next) {
117 netsnmp_assert(NULL != ptr->name);
118 if (0 == strcmp(node->name, ptr->name)) {
119 netsnmp_assert(!"list key == is unique"); /* always fail */
120 snmp_log(LOG_WARNING,
121 "WARNING: adding duplicate key '%s' to data list\n",
122 node->name);
123 }
124 }
125
126 netsnmp_assert(NULL != ptr);
127 if (ptr) /* should always be true */
128 ptr->next = node;
129 }
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
netsnmp_add_list_data(netsnmp_data_list ** head,netsnmp_data_list * node)138 netsnmp_add_list_data(netsnmp_data_list **head, netsnmp_data_list *node)
139 {
140 netsnmp_data_list_add_node(head, node);
141 }
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 *
netsnmp_data_list_add_data(netsnmp_data_list ** head,const char * name,void * data,Netsnmp_Free_List_Data * beer)152 netsnmp_data_list_add_data(netsnmp_data_list **head, const char *name,
153 void *data, Netsnmp_Free_List_Data * beer)
154 {
155 netsnmp_data_list *node;
156 if (!name) {
157 snmp_log(LOG_ERR,"no name provided.");
158 return NULL;
159 }
160 node = netsnmp_create_data_list(name, data, beer);
161 if(NULL == node) {
162 snmp_log(LOG_ERR,"could not allocate memory for node.");
163 return NULL;
164 }
165
166 netsnmp_add_list_data(head, node);
167
168 return node;
169 }
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 *
netsnmp_get_list_data(netsnmp_data_list * head,const char * name)178 netsnmp_get_list_data(netsnmp_data_list *head, const char *name)
179 {
180 if (!name)
181 return NULL;
182 for (; head; head = head->next)
183 if (head->name && strcmp(head->name, name) == 0)
184 break;
185 if (head)
186 return head->data;
187 return NULL;
188 }
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 *
netsnmp_get_list_node(netsnmp_data_list * head,const char * name)197 netsnmp_get_list_node(netsnmp_data_list *head, const char *name)
198 {
199 if (!name)
200 return NULL;
201 for (; head; head = head->next)
202 if (head->name && strcmp(head->name, name) == 0)
203 break;
204 if (head)
205 return head;
206 return NULL;
207 }
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
netsnmp_remove_list_node(netsnmp_data_list ** realhead,const char * name)216 netsnmp_remove_list_node(netsnmp_data_list **realhead, const char *name)
217 {
218 netsnmp_data_list *head, *prev;
219 if (!name)
220 return 1;
221 for (head = *realhead, prev = NULL; head;
222 prev = head, head = head->next) {
223 if (head->name && strcmp(head->name, name) == 0) {
224 if (prev)
225 prev->next = head->next;
226 else
227 *realhead = head->next;
228 netsnmp_free_list_data(head);
229 free(head);
230 return 0;
231 }
232 }
233 return 1;
234 }
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
netsnmp_register_save_list(netsnmp_data_list ** datalist,const char * type,const char * token,Netsnmp_Save_List_Data * data_list_save_ptr,Netsnmp_Read_List_Data * data_list_read_ptr,Netsnmp_Free_List_Data * data_list_free_ptr)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 {
255 netsnmp_data_list_saveinfo *info;
256
257 if (!data_list_save_ptr && !data_list_read_ptr)
258 return;
259
260 info = SNMP_MALLOC_TYPEDEF(netsnmp_data_list_saveinfo);
261
262 if (!info) {
263 snmp_log(LOG_ERR, "couldn't malloc a netsnmp_data_list_saveinfo typedef");
264 return;
265 }
266
267 info->datalist = datalist;
268 info->token = token;
269 info->type = type;
270 if (!info->type) {
271 info->type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
272 NETSNMP_DS_LIB_APPTYPE);
273 }
274
275 /* function which will save the data */
276 info->data_list_save_ptr = data_list_save_ptr;
277 if (data_list_save_ptr)
278 snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA,
279 netsnmp_save_all_data_callback, info);
280
281 /* function which will read the data back in */
282 info->data_list_read_ptr = data_list_read_ptr;
283 if (data_list_read_ptr) {
284 /** @todo netsnmp_register_save_list should handle the same token name being saved from different types? */
285 netsnmp_add_list_data(&saveHead,
286 netsnmp_create_data_list(token, info, NULL));
287 register_config_handler(type, token, netsnmp_read_data_callback,
288 NULL /* XXX */, NULL);
289 }
290
291 info->data_list_free_ptr = data_list_free_ptr;
292 }
293
294
295 /** intended to be registerd 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 apporpriate registration information
301 */
302 int
netsnmp_save_all_data_callback(int major,int minor,void * serverarg,void * clientarg)303 netsnmp_save_all_data_callback(int major, int minor,
304 void *serverarg, void *clientarg) {
305 netsnmp_data_list_saveinfo *info = (netsnmp_data_list_saveinfo *)clientarg;
306
307 if (!clientarg) {
308 snmp_log(LOG_WARNING, "netsnmp_save_all_data_callback called with no passed data");
309 return SNMP_ERR_NOERROR;
310 }
311
312 netsnmp_save_all_data(*(info->datalist), info->type, info->token,
313 info->data_list_save_ptr);
314 return SNMP_ERR_NOERROR;
315 }
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
netsnmp_save_all_data(netsnmp_data_list * head,const char * type,const char * token,Netsnmp_Save_List_Data * data_list_save_ptr)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 {
324 char buf[SNMP_MAXBUF], *cp;
325
326 for (; head; head = head->next) {
327 if (head->name) {
328 /* save begining of line */
329 snprintf(buf, sizeof(buf), "%s ", token);
330 cp = buf + strlen(buf);
331 cp = read_config_save_octet_string(cp, (u_char*)head->name,
332 strlen(head->name));
333 *cp++ = ' ';
334
335 /* call registered function to save the rest */
336 if (!(data_list_save_ptr)(cp,
337 sizeof(buf) - strlen(buf),
338 head->data)) {
339 read_config_store(type, buf);
340 }
341 }
342 }
343 return SNMP_ERR_NOERROR;
344 }
345
346 /** intended to be registerd 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 apporpriate registration information
353 * @todo make netsnmp_read_data_callback deal with a free routine
354 */
355 void
netsnmp_read_data_callback(const char * token,char * line)356 netsnmp_read_data_callback(const char *token, char *line) {
357 netsnmp_data_list_saveinfo *info;
358 char *dataname = NULL;
359 size_t dataname_len;
360 void *data = NULL;
361
362 /* find the stashed information about what we're parsing */
363 info = (netsnmp_data_list_saveinfo *) netsnmp_get_list_data(saveHead, token);
364 if (!info) {
365 snmp_log(LOG_WARNING, "netsnmp_read_data_callback called without previously registered subparser");
366 return;
367 }
368
369 /* read in the token */
370 line =
371 read_config_read_data(ASN_OCTET_STR, line,
372 &dataname, &dataname_len);
373
374 if (!line || !dataname)
375 return;
376
377 /* call the sub-parser to read the rest */
378 data = (info->data_list_read_ptr)(line, strlen(line));
379
380 if (!data) {
381 free(dataname);
382 return;
383 }
384
385 /* add to the datalist */
386 netsnmp_add_list_data(info->datalist,
387 netsnmp_create_data_list(dataname, data,
388 info->data_list_free_ptr));
389
390 return;
391 }
392
393 void
shutdown_data_list(void)394 shutdown_data_list(void)
395 {
396 netsnmp_free_all_list_data(saveHead);
397 }
398
399 /** @} */
400
401