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