Coverage Report

Created: 2025-11-09 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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