/* * netsnmp_data_list.c * * Portions of this file are subject to the following copyright(s). See * the Net-SNMP's COPYING file for more details and other copyrights * that may apply: * * Portions of this file are copyrighted by: * Copyright (c) 2016 VMware, Inc. All rights reserved. * Use is subject to license terms specified in the COPYING file * distributed with the Net-SNMP package. */ #include #include #include netsnmp_feature_child_of(data_list_all, libnetsnmp); netsnmp_feature_child_of(data_list_add_data, data_list_all); netsnmp_feature_child_of(data_list_get_list_node, data_list_all); /** @defgroup data_list generic linked-list data handling with a string as a key. * @ingroup library * @{ */ /** frees the data and a name at a given data_list node. * Note that this doesn't free the node itself. * @param node the node for which the data should be freed */ NETSNMP_INLINE void netsnmp_free_list_data(netsnmp_data_list *node) { Netsnmp_Free_List_Data *beer; if (!node) return; beer = node->free_func; if (beer) (beer) (node->data); SNMP_FREE(node->name); } /** frees all data and nodes in a list. * @param head the top node of the list to be freed. */ NETSNMP_INLINE void netsnmp_free_all_list_data(netsnmp_data_list *head) { netsnmp_data_list *tmpptr; for (; head;) { netsnmp_free_list_data(head); tmpptr = head; head = head->next; SNMP_FREE(tmpptr); } } /** adds creates a data_list node given a name, data and a free function ptr. * @param name the name of the node to cache the data. * @param data the data to be stored under that name * @param beer A function that can free the data pointer (in the future) * @return a newly created data_list node which can be given to the netsnmp_add_list_data function. */ NETSNMP_INLINE netsnmp_data_list * netsnmp_create_data_list(const char *name, void *data, Netsnmp_Free_List_Data * beer) { netsnmp_data_list *node; if (!name) return NULL; node = SNMP_MALLOC_TYPEDEF(netsnmp_data_list); if (!node) return NULL; node->name = strdup(name); if (!node->name) { free(node); return NULL; } node->data = data; node->free_func = beer; return node; } /** adds data to a datalist * @param head a pointer to the head node of a data_list * @param node a node to stash in the data_list */ NETSNMP_INLINE void netsnmp_data_list_add_node(netsnmp_data_list **head, netsnmp_data_list *node) { netsnmp_data_list *ptr; netsnmp_assert(NULL != head); netsnmp_assert(NULL != node); if (!head || !node) return; netsnmp_assert(NULL != node->name); DEBUGMSGTL(("data_list","adding key '%s'\n", node->name)); if (!*head) { *head = node; return; } if (0 == strcmp(node->name, (*head)->name)) { netsnmp_assert(!"list key == is unique"); /* always fail */ snmp_log(LOG_WARNING, "WARNING: adding duplicate key '%s' to data list\n", node->name); } for (ptr = *head; ptr->next != NULL; ptr = ptr->next) { netsnmp_assert(NULL != ptr->name); if (0 == strcmp(node->name, ptr->name)) { netsnmp_assert(!"list key == is unique"); /* always fail */ snmp_log(LOG_WARNING, "WARNING: adding duplicate key '%s' to data list\n", node->name); } } netsnmp_assert(NULL != ptr); if (ptr) /* should always be true */ ptr->next = node; } /** adds data to a datalist * @note netsnmp_data_list_add_node is preferred * @param head a pointer to the head node of a data_list * @param node a node to stash in the data_list */ /** */ NETSNMP_INLINE void netsnmp_add_list_data(netsnmp_data_list **head, netsnmp_data_list *node) { netsnmp_data_list_add_node(head, node); } /** adds data to a datalist * @param head a pointer to the head node of a data_list * @param name the name of the node to cache the data. * @param data the data to be stored under that name * @param beer A function that can free the data pointer (in the future) * @return a newly created data_list node which was inserted in the list */ #ifndef NETSNMP_FEATURE_REMOVE_DATA_LIST_ADD_DATA NETSNMP_INLINE netsnmp_data_list * netsnmp_data_list_add_data(netsnmp_data_list **head, const char *name, void *data, Netsnmp_Free_List_Data * beer) { netsnmp_data_list *node; if (!name) { snmp_log(LOG_ERR,"no name provided."); return NULL; } node = netsnmp_create_data_list(name, data, beer); if(NULL == node) { snmp_log(LOG_ERR,"could not allocate memory for node."); return NULL; } netsnmp_add_list_data(head, node); return node; } #endif /* NETSNMP_FEATURE_REMOVE_DATA_LIST_ADD_DATA */ /** returns a data_list node's data for a given name within a data_list * @param head the head node of a data_list * @param name the name to find * @return a pointer to the data cached at that node */ NETSNMP_INLINE void * netsnmp_get_list_data(netsnmp_data_list *head, const char *name) { if (!name) return NULL; for (; head; head = head->next) if (head->name && strcmp(head->name, name) == 0) break; if (head) return head->data; return NULL; } /** returns a data_list node for a given name within a data_list * @param head the head node of a data_list * @param name the name to find * @return a pointer to the data_list node */ #ifndef NETSNMP_FEATURE_REMOVE_DATA_LIST_GET_LIST_NODE NETSNMP_INLINE netsnmp_data_list * netsnmp_get_list_node(netsnmp_data_list *head, const char *name) { if (!name) return NULL; for (; head; head = head->next) if (head->name && strcmp(head->name, name) == 0) break; if (head) return head; return NULL; } #endif /* NETSNMP_FEATURE_REMOVE_DATA_LIST_GET_LIST_NODE */ /** Removes a named node from a data_list (and frees it) * @param realhead a pointer to the head node of a data_list * @param name the name to find and remove * @return 0 on successful find-and-delete, 1 otherwise. */ int netsnmp_remove_list_node(netsnmp_data_list **realhead, const char *name) { netsnmp_data_list *head, *prev; if (!name) return 1; for (head = *realhead, prev = NULL; head; prev = head, head = head->next) { if (head->name && strcmp(head->name, name) == 0) { if (prev) prev->next = head->next; else *realhead = head->next; netsnmp_free_list_data(head); free(head); return 0; } } return 1; } /** used to store registered save/parse handlers (specifically, parsing info) */ static netsnmp_data_list *saveHead; /** registers to store a data_list set of data at persistent storage time * * @param datalist the data to be saved * @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). * @param token the unique token identifier string to use as the first word in the persistent file line. * @param data_list_save_ptr a function pointer which will be called to save the rest of the data to a buffer. * @param data_list_read_ptr a function pointer which can read the remainder of a saved line and return the application specific void * pointer. * @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. */ 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) { netsnmp_data_list_saveinfo *info; if (!data_list_save_ptr && !data_list_read_ptr) return; info = SNMP_MALLOC_TYPEDEF(netsnmp_data_list_saveinfo); if (!info) { snmp_log(LOG_ERR, "couldn't malloc a netsnmp_data_list_saveinfo typedef"); return; } info->datalist = datalist; info->token = token; info->type = type; if (!info->type) { info->type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE); } /* function which will save the data */ info->data_list_save_ptr = data_list_save_ptr; if (data_list_save_ptr) snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA, netsnmp_save_all_data_callback, info); /* function which will read the data back in */ info->data_list_read_ptr = data_list_read_ptr; if (data_list_read_ptr) { /** @todo netsnmp_register_save_list should handle the same token name being saved from different types? */ netsnmp_add_list_data(&saveHead, netsnmp_create_data_list(token, info, NULL)); register_config_handler(type, token, netsnmp_read_data_callback, NULL /* XXX */, NULL); } info->data_list_free_ptr = data_list_free_ptr; } /** intended to be registerd as a callback operation. * It should be registered using: * * snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA, netsnmp_save_all_data_callback, INFO_POINTER); * * where INFO_POINTER is a pointer to a netsnmp_data_list_saveinfo object containing apporpriate registration information */ int netsnmp_save_all_data_callback(int major, int minor, void *serverarg, void *clientarg) { netsnmp_data_list_saveinfo *info = (netsnmp_data_list_saveinfo *)clientarg; if (!clientarg) { snmp_log(LOG_WARNING, "netsnmp_save_all_data_callback called with no passed data"); return SNMP_ERR_NOERROR; } netsnmp_save_all_data(*(info->datalist), info->type, info->token, info->data_list_save_ptr); return SNMP_ERR_NOERROR; } /** intended to be called as a callback during persistent save operations. * See the netsnmp_save_all_data_callback for where this is typically used. */ int netsnmp_save_all_data(netsnmp_data_list *head, const char *type, const char *token, Netsnmp_Save_List_Data * data_list_save_ptr) { char buf[SNMP_MAXBUF], *cp; for (; head; head = head->next) { if (head->name) { /* save begining of line */ snprintf(buf, sizeof(buf), "%s ", token); cp = buf + strlen(buf); cp = read_config_save_octet_string(cp, (u_char*)head->name, strlen(head->name)); *cp++ = ' '; /* call registered function to save the rest */ if (!(data_list_save_ptr)(cp, sizeof(buf) - strlen(buf), head->data)) { read_config_store(type, buf); } } } return SNMP_ERR_NOERROR; } /** intended to be registerd as a .conf parser * It should be registered using: * * register_app_config_handler("token", netsnmp_read_data_callback, XXX) * * where INFO_POINTER is a pointer to a netsnmp_data_list_saveinfo object * containing apporpriate registration information * @todo make netsnmp_read_data_callback deal with a free routine */ void netsnmp_read_data_callback(const char *token, char *line) { netsnmp_data_list_saveinfo *info; char *dataname = NULL; size_t dataname_len; void *data = NULL; /* find the stashed information about what we're parsing */ info = (netsnmp_data_list_saveinfo *) netsnmp_get_list_data(saveHead, token); if (!info) { snmp_log(LOG_WARNING, "netsnmp_read_data_callback called without previously registered subparser"); return; } /* read in the token */ line = read_config_read_data(ASN_OCTET_STR, line, &dataname, &dataname_len); if (!line || !dataname) return; /* call the sub-parser to read the rest */ data = (info->data_list_read_ptr)(line, strlen(line)); if (!data) { free(dataname); return; } /* add to the datalist */ netsnmp_add_list_data(info->datalist, netsnmp_create_data_list(dataname, data, info->data_list_free_ptr)); return; } void shutdown_data_list(void) { netsnmp_free_all_list_data(saveHead); } /** @} */