2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2009, Eliel C. Sardanons (LU1ALY) <eliels@gmail.com>
6 * See http://www.asterisk.org for more information about
7 * the Asterisk project. Please do not directly contact
8 * any of the maintainers of this project for assistance;
9 * the project provides a web site, mailing lists and IRC
10 * channels for your use.
12 * This program is free software, distributed under the terms of
13 * the GNU General Public License Version 2. See the LICENSE file
14 * at the top of the source tree.
19 * \brief Data retrieval API.
21 * \author Brett Bryant <brettbryant@gmail.com>
22 * \author Eliel C. Sardanons (LU1ALY) <eliels@gmail.com>
27 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29 #include "asterisk/_private.h"
33 #include "asterisk/module.h"
34 #include "asterisk/utils.h"
35 #include "asterisk/lock.h"
36 #include "asterisk/data.h"
37 #include "asterisk/astobj2.h"
38 #include "asterisk/xml.h"
39 #include "asterisk/cli.h"
40 #include "asterisk/term.h"
41 #include "asterisk/manager.h"
42 #include "asterisk/test.h"
45 <manager name="DataGet" language="en_US">
47 Retrieve the data api tree.
50 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
51 <parameter name="Path" required="true" />
52 <parameter name="Search" />
53 <parameter name="Filter" />
56 <para>Retrieve the data api tree.</para>
61 #define NUM_DATA_NODE_BUCKETS 59
62 #define NUM_DATA_RESULT_BUCKETS 59
63 #define NUM_DATA_SEARCH_BUCKETS 59
64 #define NUM_DATA_FILTER_BUCKETS 59
66 /*! \brief The last compatible version. */
67 static const uint32_t latest_handler_compatible_version = 0;
69 /*! \brief The last compatible version. */
70 static const uint32_t latest_query_compatible_version = 0;
72 /*! \brief Current handler structure version. */
73 static const uint32_t current_handler_version = AST_DATA_HANDLER_VERSION;
75 /*! \brief Current query structure version. */
76 static const uint32_t current_query_version = AST_DATA_QUERY_VERSION;
78 /*! \brief The data tree to be returned by the callbacks and
79 managed by functions local to this file. */
81 enum ast_data_type type;
83 /*! \brief The node content. */
88 unsigned int boolean:1;
90 struct in_addr ipaddr;
94 /*! \brief The filter node that depends on the current node,
95 * this is used only when creating the result tree. */
96 const struct data_filter *filter;
98 /*! \brief The list of nodes inside this node. */
99 struct ao2_container *children;
100 /*! \brief The name of the node. */
104 /*! \brief Type of comparisons allow in the search string. */
105 enum data_search_comparison {
108 DATA_CMP_NEQ, /* != */
110 DATA_CMP_GE, /* >= */
115 /*! \brief The list of nodes with their search requirement. */
116 struct ast_data_search {
117 /*! \brief The value of the comparison. */
119 /*! \brief The type of comparison. */
120 enum data_search_comparison cmp_type;
121 /*! \brief reference another node. */
122 struct ao2_container *children;
123 /*! \brief The name of the node we are trying to compare. */
129 /*! \brief The filter node. */
131 /*! \brief node childrens. */
132 struct ao2_container *children;
133 /*! \brief glob list */
134 AST_LIST_HEAD_NOLOCK(glob_list_t, data_filter) glob_list;
135 /*! \brief glob list entry */
136 AST_LIST_ENTRY(data_filter) list;
137 /*! \brief node name. */
141 /*! \brief A data container node pointing to the registered handler. */
142 struct data_provider {
143 /*! \brief node content handler. */
144 const struct ast_data_handler *handler;
145 /*! \brief Module providing this handler. */
146 struct ast_module *module;
147 /*! \brief children nodes. */
148 struct ao2_container *children;
149 /*! \brief Who registered this node. */
150 const char *registrar;
151 /*! \brief Node name. */
155 /*! \brief This structure is used by the iterator. */
156 struct ast_data_iterator {
157 /*! \brief The internal iterator. */
158 struct ao2_iterator internal_iterator;
159 /*! \brief The last returned node. */
160 struct ast_data *last;
161 /*! \brief The iterator pattern. */
163 /*! \brief The compiled patter. */
164 regex_t regex_pattern;
165 /*! \brief is a regular expression. */
166 unsigned int is_pattern:1;
170 /*! \brief The asterisk data main content structure. */
171 struct ao2_container *container;
172 /*! \brief asterisk data locking mechanism. */
176 static void __data_result_print_cli(int fd, const struct ast_data *root, uint32_t depth);
180 * \brief Common string hash function.
183 static int data_provider_hash(const void *obj, const int flags)
185 const struct data_provider *node = obj;
186 return ast_str_case_hash(node->name);
191 * \brief Compare two data_provider's.
194 static int data_provider_cmp(void *obj1, void *obj2, int flags)
196 struct data_provider *node1 = obj1, *node2 = obj2;
197 return strcasecmp(node1->name, node2->name) ? 0 : CMP_MATCH;
202 * \brief Common string hash function for data nodes
204 static int data_result_hash(const void *obj, const int flags)
206 const struct ast_data *node = obj;
207 return ast_str_hash(node->name);
212 * \brief Common string comparison function
214 static int data_result_cmp(void *obj, void *arg, int flags)
216 struct ast_data *node1 = obj, *node2 = arg;
217 return strcasecmp(node1->name, node2->name) ? 0 : CMP_MATCH;
222 * \brief Lock the data registered handlers structure for writing.
225 #define data_write_lock() ast_rwlock_wrlock(&root_data.lock)
229 * \brief Lock the data registered handlers structure for reading.
232 #define data_read_lock() ast_rwlock_rdlock(&root_data.lock)
236 * \brief Unlock the data registered handlers structure.
238 #define data_unlock() ast_rwlock_unlock(&root_data.lock)
242 * \brief Check if a version is compatible with the current core.
243 * \param[in] structure_version The current structure version.
244 * \param[in] latest_compatible The latest compatible version.
245 * \param[in] current The current Data API version.
246 * \retval 1 If the module is compatible.
247 * \retval 0 If the module is NOT compatible.
249 static int data_structure_compatible(int structure_version, uint32_t latest_compatible,
252 if (structure_version >= latest_compatible && structure_version <= current) {
256 ast_log(LOG_ERROR, "A module is not compatible with the"
257 "current data api version\n");
264 * \brief Get the next node name in a path (/node1/node2)
265 * Avoid null nodes like //node1//node2/node3.
266 * \param[in] path The path where we are going to search for the next node name.
267 * \retval The next node name we found inside the given path.
268 * \retval NULL if there are no more node names.
270 static char *next_node_name(char **path)
275 res = strsep(path, "/");
276 } while (res && ast_strlen_zero(res));
283 * \brief Release the memory allocated by a call to ao2_alloc.
285 static void data_provider_destructor(void *obj)
287 struct data_provider *provider = obj;
289 ao2_ref(provider->children, -1);
294 * \brief Create a new data node.
295 * \param[in] name The name of the node we are going to create.
296 * \param[in] handler The handler registered for this node.
297 * \param[in] registrar The name of the registrar.
298 * \retval NULL on error.
299 * \retval The allocated data node structure.
301 static struct data_provider *data_provider_new(const char *name,
302 const struct ast_data_handler *handler, const char *registrar)
304 struct data_provider *node;
307 namelen = strlen(name) + 1;
309 node = ao2_alloc(sizeof(*node) + namelen, data_provider_destructor);
314 node->handler = handler;
315 node->registrar = registrar;
316 strcpy(node->name, name);
318 /* initialize the childrens container. */
319 if (!(node->children = ao2_container_alloc(NUM_DATA_NODE_BUCKETS,
320 data_provider_hash, data_provider_cmp))) {
330 * \brief Add a child node named 'name' to the 'parent' node.
331 * \param[in] parent Where to add the child node.
332 * \param[in] name The name of the child node.
333 * \param[in] handler The handler structure.
334 * \param[in] registrar Who registered this node.
335 * \retval NULL on error.
336 * \retval A newly allocated child in parent.
338 static struct data_provider *data_provider_add_child(struct ao2_container *parent,
339 const char *name, const struct ast_data_handler *handler, const char *registrar)
341 struct data_provider *child;
343 child = data_provider_new(name, handler, registrar);
348 ao2_link(parent, child);
355 * \brief Find a child node, based on his name.
356 * \param[in] parent Where to find the node.
357 * \param[in] name The node name to find.
358 * \param[in] registrar Also check if the node was being used by this registrar.
359 * \retval NULL if a node wasn't found.
360 * \retval The node found.
361 * \note Remember to decrement the ref count of the returned node after using it.
363 static struct data_provider *data_provider_find(struct ao2_container *parent,
364 const char *name, const char *registrar)
366 struct data_provider *find_node, *found;
368 /* XXX avoid allocating a new data node for searching... */
369 find_node = data_provider_new(name, NULL, NULL);
374 found = ao2_find(parent, find_node, OBJ_POINTER);
376 /* free the created node used for searching. */
377 ao2_ref(find_node, -1);
379 if (found && found->registrar && registrar) {
380 if (strcmp(found->registrar, registrar)) {
381 /* if the name doesn't match, do not return this node. */
382 ast_debug(1, "Registrar doesn't match, node was registered"
383 " by '%s' and we are searching for '%s'\n",
384 found->registrar, registrar);
395 * \brief Release a group of nodes.
396 * \param[in] parent The parent node.
397 * \param[in] path The path of nodes to release.
398 * \param[in] registrar Who registered this node.
399 * \retval <0 on error.
400 * \retval 0 on success.
401 * \see data_provider_create
403 static int data_provider_release(struct ao2_container *parent, const char *path,
404 const char *registrar)
406 char *node_name, *rpath;
407 struct data_provider *child;
410 rpath = ast_strdupa(path);
412 node_name = next_node_name(&rpath);
417 child = data_provider_find(parent, node_name, registrar);
422 /* if this is not a terminal node. */
423 if (!child->handler && rpath) {
424 ret = data_provider_release(child->children, rpath, registrar);
427 /* if this node is empty, unlink it. */
428 if (!ret && !ao2_container_count(child->children)) {
429 ao2_unlink(parent, child);
439 * \brief Release every node registered by 'registrar'.
440 * \param[in] parent The parent node.
441 * \param[in] registrar
442 * \see __ast_data_unregister
444 static void data_provider_release_all(struct ao2_container *parent,
445 const char *registrar)
447 struct ao2_iterator i;
448 struct data_provider *node;
450 i = ao2_iterator_init(parent, 0);
451 while ((node = ao2_iterator_next(&i))) {
452 if (!node->handler) {
453 /* this is a non-terminal node, go inside it. */
454 data_provider_release_all(node->children, registrar);
455 if (!ao2_container_count(node->children)) {
456 /* if this node was left empty, unlink it. */
457 ao2_unlink(parent, node);
460 if (!strcmp(node->registrar, registrar)) {
461 /* if the registrars match, release it! */
462 ao2_unlink(parent, node);
467 ao2_iterator_destroy(&i);
473 * \brief Create the middle nodes for the specified path (asterisk/testnode1/childnode)
474 * \param[in] parent Where to add the middle nodes structure.
475 * \param[in] path The path of nodes to add.
476 * \param[in] registrar Who is trying to create this node provider.
477 * \retval NULL on error.
478 * \retval The created node.
479 * \see data_provider_release
481 static struct data_provider *data_provider_create(struct ao2_container *parent,
482 const char *path, const char *registrar)
484 char *rpath, *node_name;
485 struct data_provider *child, *ret = NULL;
487 rpath = ast_strdupa(path);
489 node_name = next_node_name(&rpath);
491 /* no more nodes to create. */
495 child = data_provider_find(parent, node_name, NULL);
498 /* nodes without handler are non-terminal nodes. */
499 child = data_provider_add_child(parent, node_name, NULL, registrar);
503 ret = data_provider_create(child->children, rpath, registrar);
509 return ret ? ret : child;
512 int __ast_data_register(const char *path, const struct ast_data_handler *handler,
513 const char *registrar, struct ast_module *mod)
515 struct data_provider *node;
521 /* check if the handler structure is compatible. */
522 if (!data_structure_compatible(handler->version,
523 latest_handler_compatible_version,
524 current_handler_version)) {
528 /* create the node structure for the registered handler. */
531 node = data_provider_create(root_data.container, path, registrar);
533 ast_log(LOG_ERROR, "Unable to create the specified path (%s) "
534 "for '%s'.\n", path, registrar);
539 if (ao2_container_count(node->children) || node->handler) {
540 ast_log(LOG_ERROR, "The node '%s' was already registered. "
541 "We were unable to register '%s' for registrar '%s'.\n",
542 node->name, path, registrar);
548 /* add handler to that node. */
549 node->handler = handler;
559 int __ast_data_register_multiple(const struct ast_data_entry *data_entries,
560 size_t entries, const char *registrar, struct ast_module *mod)
564 for (i = 0; i < entries; i++) {
565 res = __ast_data_register(data_entries[i].path, data_entries[i].handler,
568 /* unregister all the already registered nodes, and make
569 * this an atomic action. */
571 __ast_data_unregister(data_entries[i].path, registrar);
580 int __ast_data_unregister(const char *path, const char *registrar)
586 ret = data_provider_release(root_data.container, path, registrar);
588 data_provider_release_all(root_data.container, registrar);
593 ast_log(LOG_ERROR, "Unable to unregister '%s' for '%s'\n",
602 * \brief Is a char used to specify a comparison?
603 * \param[in] a Character to evaluate.
604 * \retval 1 It is a char used to specify a comparison.
605 * \retval 0 It is NOT a char used to specify a comparison.
607 static int data_search_comparison_char(char a)
622 * \brief Get the type of comparison.
624 static enum data_search_comparison data_search_comparison_type(const char *comparison)
626 if (!strcmp(comparison, "=")) {
628 } else if (!strcmp(comparison, "!=")) {
630 } else if (!strcmp(comparison, "<")) {
632 } else if (!strcmp(comparison, ">")) {
634 } else if (!strcmp(comparison, "<=")) {
636 } else if (!strcmp(comparison, ">=")) {
640 return DATA_CMP_UNKNOWN;
645 * \brief Common string hash function for data nodes
647 static int data_search_hash(const void *obj, const int flags)
649 const struct ast_data_search *node = obj;
650 return ast_str_hash(node->name);
655 * \brief Common string comparison function
657 static int data_search_cmp(void *obj, void *arg, int flags)
659 struct ast_data_search *node1 = obj, *node2 = arg;
660 return strcasecmp(node1->name, node2->name) ? 0 : CMP_MATCH;
665 * \brief Destroy the ao2 search node.
667 static void data_search_destructor(void *obj)
669 struct ast_data_search *node = obj;
672 ast_free(node->value);
675 ao2_ref(node->children, -1);
680 * \brief Allocate a search node.
681 * \retval NULL on error.
682 * \retval non-NULL The allocated search node structure.
684 static struct ast_data_search *data_search_alloc(const char *name)
686 struct ast_data_search *res;
687 size_t name_len = strlen(name) + 1;
689 res = ao2_alloc(sizeof(*res) + name_len, data_search_destructor);
694 res->children = ao2_container_alloc(NUM_DATA_SEARCH_BUCKETS, data_search_hash,
702 strcpy(res->name, name);
709 * \brief Find a child node, based on his name.
710 * \param[in] parent Where to find the node.
711 * \param[in] name The node name to find.
712 * \retval NULL if a node wasn't found.
713 * \retval The node found.
714 * \note Remember to decrement the ref count of the returned node after using it.
716 static struct ast_data_search *data_search_find(struct ao2_container *parent,
719 struct ast_data_search *find_node, *found;
721 find_node = data_search_alloc(name);
726 found = ao2_find(parent, find_node, OBJ_POINTER);
728 /* free the created node used for searching. */
729 ao2_ref(find_node, -1);
736 * \brief Add a child node named 'name' to the 'parent' node.
737 * \param[in] parent Where to add the child node.
738 * \param[in] name The name of the child node.
739 * \retval NULL on error.
740 * \retval A newly allocated child in parent.
742 static struct ast_data_search *data_search_add_child(struct ao2_container *parent,
745 struct ast_data_search *child;
747 child = data_search_alloc(name);
752 ao2_link(parent, child);
759 * \brief Create the middle nodes for the specified path (asterisk/testnode1/childnode)
760 * \param[in] parent Where to add the middle nodes structure.
761 * \param[in] path The path of nodes to add.
762 * \retval NULL on error.
763 * \retval The created node.
765 static struct ast_data_search *data_search_create(struct ao2_container *parent,
768 char *rpath, *node_name;
769 struct ast_data_search *child = NULL;
770 struct ao2_container *current = parent;
772 rpath = ast_strdupa(path);
774 node_name = next_node_name(&rpath);
776 child = data_search_find(current, node_name);
778 child = data_search_add_child(current, node_name);
781 current = child->children;
782 node_name = next_node_name(&rpath);
790 * \brief Allocate a tree with the search string parsed.
791 * \param[in] search_string The search string.
792 * \retval NULL on error.
793 * \retval non-NULL A dynamically allocated search tree.
795 static struct ast_data_search *data_search_generate(const char *search_string)
797 struct ast_str *name, *value, *comparison;
798 char *elements, *search_string_dup, *saveptr;
800 struct ast_data_search *root, *child;
801 enum data_search_comparison cmp_type;
802 size_t search_string_len;
804 if (!search_string) {
805 ast_log(LOG_ERROR, "You must pass a valid search string.\n");
809 search_string_len = strlen(search_string);
811 name = ast_str_create(search_string_len);
815 value = ast_str_create(search_string_len);
820 comparison = ast_str_create(search_string_len);
827 search_string_dup = ast_strdupa(search_string);
829 /* Create the root node (just used as a container) */
830 root = data_search_alloc("/");
834 ast_free(comparison);
838 for (elements = strtok_r(search_string_dup, ",", &saveptr); elements;
839 elements = strtok_r(NULL, ",", &saveptr)) {
842 for (i = 0; !data_search_comparison_char(elements[i]) &&
844 ast_str_append(&name, 0, "%c", elements[i]);
847 /* check if the syntax is ok. */
848 if (!data_search_comparison_char(elements[i])) {
849 /* if this is the end of the string, then this is
851 ast_log(LOG_ERROR, "Invalid search string!\n");
855 /* parse the comparison string. */
856 ast_str_reset(comparison);
857 for (; data_search_comparison_char(elements[i]) && elements[i]; i++) {
858 ast_str_append(&comparison, 0, "%c", elements[i]);
861 /* parse the value string. */
862 ast_str_reset(value);
863 for (; elements[i]; i++) {
864 ast_str_append(&value, 0, "%c", elements[i]);
867 cmp_type = data_search_comparison_type(ast_str_buffer(comparison));
868 if (cmp_type == DATA_CMP_UNKNOWN) {
869 ast_log(LOG_ERROR, "Invalid comparison '%s'\n",
870 ast_str_buffer(comparison));
874 /* add this node to the tree. */
875 child = data_search_create(root->children, ast_str_buffer(name));
877 child->cmp_type = cmp_type;
878 child->value = ast_strdup(ast_str_buffer(value));
884 ast_free(comparison);
891 * \brief Release the allocated memory for the search tree.
892 * \param[in] search The search tree root node.
894 static void data_search_release(struct ast_data_search *search)
901 * \brief Based on the kind of comparison and the result in cmpval, return
903 * \param[in] cmpval A result returned by a strcmp() for example.
904 * \param[in] comparison_type The kind of comparison (<,>,=,!=,...)
905 * \retval 1 If the comparison doesn't match.
906 * \retval 0 If the comparison matches.
908 static inline int data_search_comparison_result(int cmpval,
909 enum data_search_comparison comparison_type)
911 switch (comparison_type) {
942 case DATA_CMP_UNKNOWN:
950 * \brief Get an internal node, from the search tree.
951 * \param[in] node A node container.
952 * \param[in] path The path to the needed internal node.
953 * \retval NULL if the internal node is not found.
954 * \retval non-NULL the internal node with path 'path'.
956 static struct ast_data_search *data_search_get_node(const struct ast_data_search *node,
959 char *savepath, *node_name;
960 struct ast_data_search *child, *current = (struct ast_data_search *) node;
966 savepath = ast_strdupa(path);
967 node_name = next_node_name(&savepath);
970 child = data_search_find(current->children, node_name);
971 if (current != node) {
972 ao2_ref(current, -1);
978 node_name = next_node_name(&savepath);
984 int ast_data_search_cmp_string(const struct ast_data_search *root, const char *name,
987 struct ast_data_search *child;
988 enum data_search_comparison cmp_type;
991 child = data_search_get_node(root, name);
996 ret = strcmp(value, child->value);
997 cmp_type = child->cmp_type;
1001 return data_search_comparison_result(ret, cmp_type);
1004 int ast_data_search_cmp_ptr(const struct ast_data_search *root, const char *name,
1007 struct ast_data_search *child;
1008 enum data_search_comparison cmp_type;
1011 child = data_search_get_node(root, name);
1016 cmp_type = child->cmp_type;
1018 if (sscanf(child->value, "%p", &node_ptr) <= 0) {
1024 return data_search_comparison_result((node_ptr - ptr), cmp_type);
1027 int ast_data_search_cmp_ipaddr(const struct ast_data_search *root, const char *name,
1028 struct in_addr addr)
1030 struct ast_data_search *child;
1031 enum data_search_comparison cmp_type;
1032 struct in_addr node_addr;
1034 child = data_search_get_node(root, name);
1038 cmp_type = child->cmp_type;
1040 inet_aton(child->value, &node_addr);
1044 return data_search_comparison_result((node_addr.s_addr - addr.s_addr), cmp_type);
1047 int ast_data_search_cmp_bool(const struct ast_data_search *root, const char *name,
1050 struct ast_data_search *child;
1051 unsigned int node_value;
1052 enum data_search_comparison cmp_type;
1054 child = data_search_get_node(root, name);
1059 node_value = abs(ast_true(child->value));
1060 cmp_type = child->cmp_type;
1064 return data_search_comparison_result(value - node_value, cmp_type);
1067 int ast_data_search_cmp_dbl(const struct ast_data_search *root, const char *name,
1070 struct ast_data_search *child;
1072 enum data_search_comparison cmp_type;
1074 child = data_search_get_node(root, name);
1079 node_value = strtod(child->value, NULL);
1080 cmp_type = child->cmp_type;
1084 return data_search_comparison_result(value - node_value, cmp_type);
1087 int ast_data_search_cmp_uint(const struct ast_data_search *root, const char *name,
1090 struct ast_data_search *child;
1091 unsigned int node_value;
1092 enum data_search_comparison cmp_type;
1094 child = data_search_get_node(root, name);
1099 node_value = atoi(child->value);
1100 cmp_type = child->cmp_type;
1104 return data_search_comparison_result(value - node_value, cmp_type);
1107 int ast_data_search_cmp_int(const struct ast_data_search *root, const char *name,
1110 struct ast_data_search *child;
1112 enum data_search_comparison cmp_type;
1114 child = data_search_get_node(root, name);
1119 node_value = atoi(child->value);
1120 cmp_type = child->cmp_type;
1124 return data_search_comparison_result(value - node_value, cmp_type);
1129 * \brief Get the member pointer, from a mapping structure, based on its name.
1130 * \XXX We will need to improve performance here!!.
1131 * \retval <0 if the member was not found.
1132 * \retval >=0 The member position in the mapping structure.
1134 static inline int data_search_mapping_find(const struct ast_data_mapping_structure *map,
1136 const char *member_name)
1140 for (i = 0; i < mapping_len; i++) {
1141 if (!strcmp(map[i].name, member_name)) {
1149 int ast_data_search_has_condition(const struct ast_data_search *search,
1150 const char *compare_condition)
1152 struct ast_data_search *child;
1154 child = data_search_get_node(search, compare_condition);
1164 int __ast_data_search_cmp_structure(const struct ast_data_search *search,
1165 const struct ast_data_mapping_structure *mapping, size_t mapping_len,
1166 void *structure, const char *structure_name)
1168 struct ao2_iterator i;
1169 struct ast_data_search *node, *struct_children;
1170 int member, notmatch = 0;
1176 struct_children = data_search_get_node(search, structure_name);
1177 if (!struct_children) {
1181 i = ao2_iterator_init(struct_children->children, 0);
1182 while ((node = ao2_iterator_next(&i))) {
1183 member = data_search_mapping_find(mapping, mapping_len, node->name);
1185 /* the structure member name doesn't match! */
1187 ao2_ref(struct_children, -1);
1188 ao2_iterator_destroy(&i);
1193 switch (mapping[member].type) {
1194 case AST_DATA_STRING:
1195 notmatch = ast_data_search_cmp_string(struct_children,
1197 mapping[member].get.AST_DATA_STRING(structure));
1199 case AST_DATA_INTEGER:
1200 notmatch = ast_data_search_cmp_int(struct_children,
1202 mapping[member].get.AST_DATA_INTEGER(structure));
1204 case AST_DATA_BOOLEAN:
1205 notmatch = ast_data_search_cmp_bool(struct_children,
1207 mapping[member].get.AST_DATA_BOOLEAN(structure));
1209 case AST_DATA_UNSIGNED_INTEGER:
1210 notmatch = ast_data_search_cmp_uint(struct_children,
1212 mapping[member].get.AST_DATA_UNSIGNED_INTEGER(structure));
1214 case AST_DATA_DOUBLE:
1215 notmatch = ast_data_search_cmp_dbl(struct_children,
1217 mapping[member].get.AST_DATA_DOUBLE(structure));
1219 case AST_DATA_IPADDR:
1220 notmatch = ast_data_search_cmp_ipaddr(struct_children,
1222 mapping[member].get.AST_DATA_IPADDR(structure));
1224 case AST_DATA_POINTER:
1225 notmatch = ast_data_search_cmp_ptr(struct_children,
1227 mapping[member].get.AST_DATA_POINTER(structure));
1229 case AST_DATA_CONTAINER:
1235 ao2_iterator_destroy(&i);
1237 ao2_ref(struct_children, -1);
1244 * \brief Release the memory allocated by a call to ao2_alloc.
1246 static void data_result_destructor(void *obj)
1248 struct ast_data *root = obj;
1250 switch (root->type) {
1251 case AST_DATA_POINTER:
1252 case AST_DATA_STRING:
1253 ast_free(root->payload.ptr);
1254 case AST_DATA_CONTAINER:
1255 case AST_DATA_INTEGER:
1256 case AST_DATA_UNSIGNED_INTEGER:
1257 case AST_DATA_DOUBLE:
1258 case AST_DATA_BOOLEAN:
1259 case AST_DATA_IPADDR:
1260 ao2_ref(root->children, -1);
1265 static struct ast_data *data_result_create(const char *name)
1267 struct ast_data *res;
1270 namelen = ast_strlen_zero(name) ? 1 : strlen(name) + 1;
1272 res = ao2_alloc(sizeof(*res) + namelen, data_result_destructor);
1277 strcpy(res->name, namelen ? name : "");
1279 /* initialize the children container */
1280 res->children = ao2_container_alloc(NUM_DATA_RESULT_BUCKETS, data_result_hash,
1282 if (!res->children) {
1287 /* set this node as a container. */
1288 res->type = AST_DATA_CONTAINER;
1295 * \brief Find a child node, based on its name.
1296 * \param[in] root The starting point.
1297 * \param[in] name The child name.
1298 * \retval NULL if the node wasn't found.
1299 * \retval non-NULL the node we were looking for.
1301 static struct ast_data *data_result_find_child(struct ast_data *root, const char *name)
1303 struct ast_data *found, *find_node;
1305 find_node = data_result_create(name);
1310 found = ao2_find(root->children, find_node, OBJ_POINTER);
1312 /* release the temporary created node used for searching. */
1313 ao2_ref(find_node, -1);
1320 * \brief Get an internal node, from the result set.
1321 * \param[in] node A node container.
1322 * \param[in] path The path to the needed internal node.
1323 * \retval NULL if the internal node is not found.
1324 * \retval non-NULL the internal node with path 'path'.
1326 static struct ast_data *data_result_get_node(struct ast_data *node,
1329 char *savepath, *node_name;
1330 struct ast_data *child, *current = node;
1332 savepath = ast_strdupa(path);
1333 node_name = next_node_name(&savepath);
1336 child = data_result_find_child(current, node_name);
1337 if (current != node) {
1338 ao2_ref(current, -1);
1344 node_name = next_node_name(&savepath);
1347 /* do not increment the refcount of the returned object. */
1348 if (current != node) {
1349 ao2_ref(current, -1);
1357 * \brief Add a child to the specified root node.
1358 * \param[in] root The root node pointer.
1359 * \param[in] child The child to add to the root node.
1361 static void data_result_add_child(struct ast_data *root, struct ast_data *child)
1363 ao2_link(root->children, child);
1368 * \brief Common string hash function for data nodes
1370 static int data_filter_hash(const void *obj, const int flags)
1372 const struct data_filter *node = obj;
1373 return ast_str_hash(node->name);
1378 * \brief Common string comparison function
1380 static int data_filter_cmp(void *obj, void *arg, int flags)
1382 struct data_filter *node1 = obj, *node2 = arg;
1383 return strcasecmp(node1->name, node2->name) ? 0 : CMP_MATCH;
1388 * \brief Destroy a data filter tree.
1389 * \param[in] obj Data filter list to be destroyed.
1391 static void data_filter_destructor(void *obj)
1393 struct data_filter *filter = obj, *globres;
1395 AST_LIST_TRAVERSE(&(filter->glob_list), globres, list) {
1396 ao2_ref(globres, -1);
1399 ao2_ref(filter->children, -1);
1404 * \brief Allocate a filter node.
1405 * \retval NULL on error.
1406 * \retval non-NULL The allocated search node structure.
1408 static struct data_filter *data_filter_alloc(const char *name)
1410 char *globname, *token;
1411 struct data_filter *res, *globfilter;
1412 size_t name_len = strlen(name) + 1;
1414 res = ao2_alloc(sizeof(*res) + name_len, data_filter_destructor);
1419 res->children = ao2_container_alloc(NUM_DATA_FILTER_BUCKETS, data_filter_hash,
1427 strcpy(res->name, name);
1429 if (strchr(res->name, '*')) {
1430 globname = ast_strdupa(res->name);
1432 while ((token = strsep(&globname, "*"))) {
1433 globfilter = data_filter_alloc(token);
1434 AST_LIST_INSERT_TAIL(&(res->glob_list), globfilter, list);
1443 * \brief Release a filter tree.
1444 * \param[in] filter The filter tree root node.
1446 static void data_filter_release(struct data_filter *filter)
1448 ao2_ref(filter, -1);
1453 * \brief Find a child node, based on his name.
1454 * \param[in] parent Where to find the node.
1455 * \param[in] name The node name to find.
1456 * \retval NULL if a node wasn't found.
1457 * \retval The node found.
1458 * \note Remember to decrement the ref count of the returned node after using it.
1460 static struct data_filter *data_filter_find(struct ao2_container *parent,
1463 int i, olend, orend, globfound;
1464 size_t name_len = strlen(name), glob_len;
1465 struct ao2_iterator iter;
1466 struct data_filter *find_node, *found, *globres;
1468 find_node = data_filter_alloc(name);
1473 found = ao2_find(parent, find_node, OBJ_POINTER);
1475 /* free the created node used for searching. */
1476 ao2_ref(find_node, -1);
1482 iter = ao2_iterator_init(parent, 0);
1483 while ((found = ao2_iterator_next(&iter))) {
1484 if (!AST_LIST_EMPTY(&(found->glob_list))) {
1488 olend = ast_strlen_zero(AST_LIST_FIRST(&(found->glob_list))->name);
1489 orend = ast_strlen_zero(AST_LIST_LAST(&(found->glob_list))->name);
1491 AST_LIST_TRAVERSE(&(found->glob_list), globres, list) {
1492 if (!*globres->name) {
1496 glob_len = strlen(globres->name);
1499 if (strncasecmp(name, globres->name, glob_len)) {
1508 for (globfound = 0; name_len - i >= glob_len; ++i) {
1509 if (!strncasecmp(name + i, globres->name, glob_len)) {
1521 if (globfound && (i == name_len || orend)) {
1522 ao2_iterator_destroy(&iter);
1529 ao2_iterator_destroy(&iter);
1536 * \brief Add a child to the specified node.
1537 * \param[in] root The root node where to add the child.
1538 * \param[in] name The name of the node to add.
1539 * \note Remember to decrement the ref count after using the returned node.
1541 static struct data_filter *data_filter_add_child(struct ao2_container *root,
1544 struct data_filter *node;
1546 node = data_filter_find(root, name);
1551 node = data_filter_alloc(name);
1556 ao2_link(root, node);
1563 * \brief Add a node to a filter list from a path
1564 * \param[in] Filter list to add the path onto.
1565 * \param[in] The path to add into the filter list.
1566 * \retval NULL on error.
1567 * \retval non-NULL A tree with the wanted nodes.
1569 static int data_filter_add_nodes(struct ao2_container *root, char *path)
1571 struct data_filter *node;
1572 char *savepath, *saveptr, *token, *node_name;
1579 savepath = ast_strdupa(path);
1581 node_name = next_node_name(&savepath);
1587 for (token = strtok_r(node_name, "|", &saveptr);
1588 token; token = strtok_r(NULL, "|", &saveptr)) {
1589 node = data_filter_add_child(root, token);
1593 data_filter_add_nodes(node->children, savepath);
1603 * \brief Generate a filter list based on a filter string provided by the API user.
1604 * \param[in] A filter string to create a filter from.
1606 static struct data_filter *data_filter_generate(const char *constfilter)
1608 struct data_filter *filter = NULL;
1609 char *strfilter, *token, *saveptr;
1616 strfilter = ast_strdupa(constfilter);
1618 filter = data_filter_alloc("/");
1623 for (token = strtok_r(strfilter, ",", &saveptr); token;
1624 token = strtok_r(NULL, ",", &saveptr)) {
1625 node_added = data_filter_add_nodes(filter->children, token);
1629 ao2_ref(filter, -1);
1638 * \brief Generate all the tree from a specified provider.
1639 * \param[in] query The query executed.
1640 * \param[in] root_provider The provider specified in the path of the query.
1641 * \param[in] parent_node_name The root node name.
1642 * \retval NULL on error.
1643 * \retval non-NULL The generated result tree.
1645 static struct ast_data *data_result_generate_node(const struct ast_data_query *query,
1646 const struct data_provider *root_provider,
1647 const char *parent_node_name,
1648 const struct ast_data_search *search,
1649 const struct data_filter *filter)
1651 struct ast_data *generated, *node;
1652 struct ao2_iterator i;
1653 struct data_provider *provider;
1654 struct ast_data_search *search_child = NULL;
1655 struct data_filter *filter_child;
1657 node = data_result_create(parent_node_name);
1659 ast_log(LOG_ERROR, "Unable to allocate '%s' node\n", parent_node_name);
1663 if (root_provider->module) {
1664 ast_module_ref(root_provider->module);
1667 /* if this is a terminal node, just run the callback function. */
1668 if (root_provider->handler && root_provider->handler->get) {
1669 node->filter = filter;
1670 root_provider->handler->get(search, node);
1671 if (root_provider->module) {
1672 ast_module_unref(root_provider->module);
1677 if (root_provider->module) {
1678 ast_module_unref(root_provider->module);
1681 /* if this is not a terminal node, generate every child node. */
1682 i = ao2_iterator_init(root_provider->children, 0);
1683 while ((provider = ao2_iterator_next(&i))) {
1684 filter_child = NULL;
1687 /* get the internal search node. */
1689 search_child = data_search_find(search->children, provider->name);
1691 /* get the internal filter node. */
1693 filter_child = data_filter_find(filter->children, provider->name);
1696 if (!filter || filter_child) {
1697 /* only generate the internal node, if we have something to
1698 * generate based on the filtering string. */
1699 generated = data_result_generate_node(query, provider,
1701 search_child, filter_child);
1704 /* decrement the refcount of the internal search node. */
1706 ao2_ref(search_child, -1);
1709 /* decrement the refcount of the internal filter node. */
1711 ao2_ref(filter_child, -1);
1715 data_result_add_child(node, generated);
1716 ao2_ref(generated, -1);
1719 ao2_ref(provider, -1);
1721 ao2_iterator_destroy(&i);
1728 * \brief Generate a result tree based on a query.
1729 * \param[in] query The complete query structure.
1730 * \param[in] search_path The path to retrieve.
1731 * \retval NULL on error.
1732 * \retval non-NULL The generated data result.
1734 static struct ast_data *data_result_generate(const struct ast_data_query *query,
1735 const char *search_path)
1737 char *node_name, *tmp_path;
1738 struct data_provider *provider_child, *tmp_provider_child;
1739 struct ast_data *result, *result_filtered;
1740 struct ast_data_search *search = NULL, *search_child = NULL;
1741 struct data_filter *filter = NULL, *filter_child = NULL;
1744 /* generate all the trees?. */
1748 tmp_path = ast_strdupa(search_path);
1750 /* start searching the root node name */
1751 node_name = next_node_name(&tmp_path);
1755 provider_child = data_provider_find(root_data.container, node_name, NULL);
1757 /* continue with the rest of the path. */
1758 while (provider_child) {
1759 node_name = next_node_name(&tmp_path);
1764 tmp_provider_child = data_provider_find(provider_child->children,
1767 /* release the reference from this child */
1768 ao2_ref(provider_child, -1);
1770 provider_child = tmp_provider_child;
1773 if (!provider_child) {
1774 ast_log(LOG_ERROR, "Invalid path '%s', '%s' not found.\n",
1775 tmp_path, node_name);
1779 /* generate the search tree. */
1780 if (query->search) {
1781 search = data_search_generate(query->search);
1783 search_child = data_search_find(search->children,
1784 provider_child->name);
1788 /* generate the filter tree. */
1789 if (query->filter) {
1790 filter = data_filter_generate(query->filter);
1792 filter_child = data_filter_find(filter->children,
1793 provider_child->name);
1797 result = data_result_generate_node(query, provider_child, provider_child->name,
1798 search_child, filter_child);
1800 /* release the requested provider. */
1801 ao2_ref(provider_child, -1);
1803 /* release the generated search tree. */
1805 ao2_ref(search_child, -1);
1809 ao2_ref(filter_child, -1);
1813 data_search_release(search);
1816 result_filtered = result;
1818 /* release the generated filter tree. */
1820 data_filter_release(filter);
1823 return result_filtered;
1826 struct ast_data *ast_data_get(const struct ast_data_query *query)
1828 struct ast_data *res;
1830 /* check compatibility */
1831 if (!data_structure_compatible(query->version, latest_query_compatible_version,
1832 current_query_version)) {
1837 res = data_result_generate(query, query->path);
1841 ast_log(LOG_ERROR, "Unable to get data from %s\n", query->path);
1850 * \brief Helper function to move an ast_data tree to xml.
1851 * \param[in] parent_data The initial ast_data node to be passed to xml.
1852 * \param[out] parent_xml The root node to insert the xml.
1854 static void data_get_xml_add_child(struct ast_data *parent_data,
1855 struct ast_xml_node *parent_xml)
1857 struct ao2_iterator i;
1858 struct ast_data *node;
1859 struct ast_xml_node *child_xml;
1860 char node_content[256];
1862 i = ao2_iterator_init(parent_data->children, 0);
1863 while ((node = ao2_iterator_next(&i))) {
1864 child_xml = ast_xml_new_node(node->name);
1870 switch (node->type) {
1871 case AST_DATA_CONTAINER:
1872 data_get_xml_add_child(node, child_xml);
1874 case AST_DATA_STRING:
1875 ast_xml_set_text(child_xml, node->payload.str);
1877 case AST_DATA_INTEGER:
1878 snprintf(node_content, sizeof(node_content), "%d",
1879 node->payload.sint);
1880 ast_xml_set_text(child_xml, node_content);
1882 case AST_DATA_UNSIGNED_INTEGER:
1883 snprintf(node_content, sizeof(node_content), "%u",
1884 node->payload.uint);
1885 ast_xml_set_text(child_xml, node_content);
1887 case AST_DATA_DOUBLE:
1888 snprintf(node_content, sizeof(node_content), "%f",
1890 ast_xml_set_text(child_xml, node_content);
1892 case AST_DATA_BOOLEAN:
1893 if (node->payload.boolean) {
1894 ast_xml_set_text(child_xml, "true");
1896 ast_xml_set_text(child_xml, "false");
1899 case AST_DATA_POINTER:
1900 snprintf(node_content, sizeof(node_content), "%p",
1902 ast_xml_set_text(child_xml, node_content);
1904 case AST_DATA_IPADDR:
1905 snprintf(node_content, sizeof(node_content), "%s",
1906 ast_inet_ntoa(node->payload.ipaddr));
1907 ast_xml_set_text(child_xml, node_content);
1910 ast_xml_add_child(parent_xml, child_xml);
1914 ao2_iterator_destroy(&i);
1918 struct ast_xml_doc *ast_data_get_xml(const struct ast_data_query *query)
1920 struct ast_xml_doc *doc;
1921 struct ast_xml_node *root;
1922 struct ast_data *res;
1924 res = ast_data_get(query);
1929 doc = ast_xml_new();
1934 root = ast_xml_new_node(res->name);
1939 ast_xml_set_root(doc, root);
1941 data_get_xml_add_child(res, root);
1948 enum ast_data_type ast_data_retrieve_type(struct ast_data *node, const char *path)
1950 struct ast_data *internal;
1952 internal = data_result_get_node(node, path);
1957 return internal->type;
1960 char *ast_data_retrieve_name(struct ast_data *node)
1967 * \brief Insert a child node inside a passed parent node.
1968 * \param root Where we are going to insert the child node.
1969 * \param name The name of the child node to add.
1970 * \param type The type of content inside the child node.
1971 * \param ptr The actual content of the child node.
1972 * \retval NULL on error.
1973 * \retval non-NULL The added child node pointer.
1975 static struct ast_data *__ast_data_add(struct ast_data *root, const char *name,
1976 enum ast_data_type type, void *ptr)
1978 struct ast_data *node;
1979 struct data_filter *filter, *filter_child = NULL;
1981 if (!root || !root->children) {
1982 /* invalid data result node. */
1986 /* check if we need to add this node, based on the filter. */
1988 filter = data_filter_find(root->filter->children, name);
1992 ao2_ref(filter, -1);
1995 node = data_result_create(name);
2003 case AST_DATA_BOOLEAN:
2004 node->payload.boolean = *(unsigned int *) ptr;
2006 case AST_DATA_INTEGER:
2007 node->payload.sint = *(unsigned int *) ptr;
2009 case AST_DATA_UNSIGNED_INTEGER:
2010 node->payload.sint = *(unsigned int *) ptr;
2012 case AST_DATA_DOUBLE:
2013 node->payload.dbl = *(double *) ptr;
2015 case AST_DATA_STRING:
2016 case AST_DATA_POINTER:
2017 node->payload.ptr = ptr;
2019 case AST_DATA_IPADDR:
2020 node->payload.ipaddr = *(struct in_addr *) ptr;
2022 case AST_DATA_CONTAINER:
2024 filter_child = data_filter_find(root->filter->children, name);
2026 /* do not increment the refcount because it is not neccesary. */
2027 ao2_ref(filter_child, -1);
2030 node->filter = filter_child;
2036 data_result_add_child(root, node);
2043 struct ast_data *ast_data_add_node(struct ast_data *root, const char *name)
2045 return __ast_data_add(root, name, AST_DATA_CONTAINER, NULL);
2048 struct ast_data *ast_data_add_int(struct ast_data *root, const char *name, int value)
2050 return __ast_data_add(root, name, AST_DATA_INTEGER, &value);
2053 struct ast_data *ast_data_add_uint(struct ast_data *root, const char *name,
2056 return __ast_data_add(root, name, AST_DATA_UNSIGNED_INTEGER, &value);
2059 struct ast_data *ast_data_add_dbl(struct ast_data *root, const char *childname,
2062 return __ast_data_add(root, childname, AST_DATA_DOUBLE, &dbl);
2065 struct ast_data *ast_data_add_bool(struct ast_data *root, const char *childname,
2066 unsigned int boolean)
2068 return __ast_data_add(root, childname, AST_DATA_BOOLEAN, &boolean);
2071 struct ast_data *ast_data_add_ipaddr(struct ast_data *root, const char *childname,
2072 struct in_addr addr)
2074 return __ast_data_add(root, childname, AST_DATA_IPADDR, &addr);
2077 struct ast_data *ast_data_add_ptr(struct ast_data *root, const char *childname,
2080 return __ast_data_add(root, childname, AST_DATA_POINTER, ptr);
2083 struct ast_data *ast_data_add_str(struct ast_data *root, const char *childname,
2087 size_t namelen = 1 + (ast_strlen_zero(value) ? 0 : strlen(value));
2088 struct ast_data *res;
2090 if (!(name = ast_malloc(namelen))) {
2094 strcpy(name, (ast_strlen_zero(value) ? "" : value));
2096 res = __ast_data_add(root, childname, AST_DATA_STRING, name);
2104 int __ast_data_add_structure(struct ast_data *root,
2105 const struct ast_data_mapping_structure *mapping, size_t mapping_len,
2110 for (i = 0; i < mapping_len; i++) {
2111 switch (mapping[i].type) {
2112 case AST_DATA_INTEGER:
2113 ast_data_add_int(root, mapping[i].name,
2114 mapping[i].get.AST_DATA_INTEGER(structure));
2116 case AST_DATA_UNSIGNED_INTEGER:
2117 ast_data_add_uint(root, mapping[i].name,
2118 mapping[i].get.AST_DATA_UNSIGNED_INTEGER(structure));
2120 case AST_DATA_DOUBLE:
2121 ast_data_add_dbl(root, mapping[i].name,
2122 mapping[i].get.AST_DATA_DOUBLE(structure));
2124 case AST_DATA_BOOLEAN:
2125 ast_data_add_bool(root, mapping[i].name,
2126 mapping[i].get.AST_DATA_BOOLEAN(structure));
2128 case AST_DATA_STRING:
2129 ast_data_add_str(root, mapping[i].name,
2130 mapping[i].get.AST_DATA_STRING(structure));
2132 case AST_DATA_CONTAINER:
2134 case AST_DATA_IPADDR:
2135 ast_data_add_ipaddr(root, mapping[i].name,
2136 mapping[i].get.AST_DATA_IPADDR(structure));
2138 case AST_DATA_POINTER:
2139 ast_data_add_ptr(root, mapping[i].name,
2140 mapping[i].get.AST_DATA_POINTER(structure));
2148 void ast_data_remove_node(struct ast_data *root, struct ast_data *child)
2150 ao2_unlink(root->children, child);
2153 void ast_data_free(struct ast_data *root)
2155 /* destroy it, this will destroy all the internal nodes. */
2159 struct ast_data_iterator *ast_data_iterator_init(struct ast_data *tree,
2160 const char *elements)
2162 struct ast_data_iterator *iterator;
2163 struct ao2_iterator i;
2164 struct ast_data *internal = tree;
2165 char *path, *ptr = NULL;
2167 /* tree is the node we want to use to iterate? or we are going
2168 * to iterate thow an internal node? */
2170 path = ast_strdupa(elements);
2172 ptr = strrchr(path, '/');
2175 internal = data_result_get_node(tree, path);
2182 iterator = ast_calloc(1, sizeof(*iterator));
2187 i = ao2_iterator_init(internal->children, 0);
2189 iterator->pattern = (ptr ? strrchr(elements, '/') + 1 : elements);
2191 /* is the last node a regular expression?, compile it! */
2192 if (!regcomp(&(iterator->regex_pattern), iterator->pattern,
2193 REG_EXTENDED | REG_NOSUB | REG_ICASE)) {
2194 iterator->is_pattern = 1;
2197 iterator->internal_iterator = i;
2202 void ast_data_iterator_end(struct ast_data_iterator *iterator)
2204 /* decrement the reference counter. */
2205 if (iterator->last) {
2206 ao2_ref(iterator->last, -1);
2209 /* release the generated pattern. */
2210 if (iterator->is_pattern) {
2211 regfree(&(iterator->regex_pattern));
2214 ao2_iterator_destroy(&(iterator->internal_iterator));
2220 struct ast_data *ast_data_iterator_next(struct ast_data_iterator *iterator)
2222 struct ast_data *res;
2224 if (iterator->last) {
2225 /* release the last retrieved node reference. */
2226 ao2_ref(iterator->last, -1);
2229 while ((res = ao2_iterator_next(&iterator->internal_iterator))) {
2230 /* if there is no node name pattern specified, return
2232 if (!iterator->pattern) {
2236 /* if the pattern is a regular expression, check if this node
2238 if (iterator->is_pattern && !regexec(&(iterator->regex_pattern),
2239 res->name, 0, NULL, 0)) {
2243 /* if there is a pattern specified, check if this node matches
2244 * the wanted node names. */
2245 if (!iterator->is_pattern && (iterator->pattern &&
2246 !strcasecmp(res->name, iterator->pattern))) {
2253 iterator->last = res;
2258 int ast_data_retrieve(struct ast_data *tree, const char *path,
2259 struct ast_data_retrieve *content)
2261 struct ast_data *node;
2267 node = data_result_get_node(tree, path);
2269 ast_log(LOG_ERROR, "Invalid internal node %s\n", path);
2273 content->type = node->type;
2274 switch (node->type) {
2275 case AST_DATA_STRING:
2276 content->value.AST_DATA_STRING = node->payload.str;
2278 case AST_DATA_INTEGER:
2279 content->value.AST_DATA_INTEGER = node->payload.sint;
2281 case AST_DATA_UNSIGNED_INTEGER:
2282 content->value.AST_DATA_UNSIGNED_INTEGER = node->payload.uint;
2284 case AST_DATA_BOOLEAN:
2285 content->value.AST_DATA_BOOLEAN = node->payload.boolean;
2287 case AST_DATA_IPADDR:
2288 content->value.AST_DATA_IPADDR = node->payload.ipaddr;
2290 case AST_DATA_DOUBLE:
2291 content->value.AST_DATA_DOUBLE = node->payload.dbl;
2293 case AST_DATA_CONTAINER:
2295 case AST_DATA_POINTER:
2296 content->value.AST_DATA_POINTER = node->payload.ptr;
2305 * \brief One color for each node type.
2307 static const struct {
2308 enum ast_data_type type;
2310 } data_result_color[] = {
2311 { AST_DATA_STRING, COLOR_CYAN },
2312 { AST_DATA_INTEGER, COLOR_RED },
2313 { AST_DATA_UNSIGNED_INTEGER, COLOR_RED },
2314 { AST_DATA_DOUBLE, COLOR_RED },
2315 { AST_DATA_BOOLEAN, COLOR_BRRED },
2316 { AST_DATA_CONTAINER, COLOR_GREEN },
2317 { AST_DATA_IPADDR, COLOR_BROWN },
2318 { AST_DATA_POINTER, COLOR_YELLOW },
2323 * \brief Get the color configured for a specific node type.
2324 * \param[in] type The node type.
2325 * \returns The color specified for the passed type.
2327 static int data_result_get_color(enum ast_data_type type)
2330 for (i = 0; i < ARRAY_LEN(data_result_color); i++) {
2331 if (data_result_color[i].type == type) {
2332 return data_result_color[i].color;
2341 * \brief Print a node to the CLI.
2342 * \param[in] fd The CLI file descriptor.
2343 * \param[in] node The node to print.
2344 * \param[in] depth The actual node depth in the tree.
2346 static void data_result_print_cli_node(int fd, const struct ast_data *node, uint32_t depth)
2349 struct ast_str *tabs, *output;
2351 tabs = ast_str_create(depth * 10 + 1);
2355 ast_str_reset(tabs);
2356 for (i = 0; i < depth; i++) {
2357 ast_str_append(&tabs, 0, " ");
2360 output = ast_str_create(20);
2366 ast_str_reset(output);
2367 ast_term_color_code(&output, data_result_get_color(node->type), 0);
2369 switch (node->type) {
2370 case AST_DATA_POINTER:
2371 ast_str_append(&output, 0, "%s%s: %p\n", ast_str_buffer(tabs),
2372 node->name, node->payload.ptr);
2374 case AST_DATA_STRING:
2375 ast_str_append(&output, 0, "%s%s: \"%s\"\n",
2376 ast_str_buffer(tabs),
2380 case AST_DATA_CONTAINER:
2381 ast_str_append(&output, 0, "%s%s\n", ast_str_buffer(tabs),
2384 case AST_DATA_INTEGER:
2385 ast_str_append(&output, 0, "%s%s: %d\n", ast_str_buffer(tabs),
2387 node->payload.sint);
2389 case AST_DATA_UNSIGNED_INTEGER:
2390 ast_str_append(&output, 0, "%s%s: %u\n", ast_str_buffer(tabs),
2392 node->payload.uint);
2394 case AST_DATA_DOUBLE:
2395 ast_str_append(&output, 0, "%s%s: %lf\n", ast_str_buffer(tabs),
2399 case AST_DATA_BOOLEAN:
2400 ast_str_append(&output, 0, "%s%s: %s\n", ast_str_buffer(tabs),
2402 ((node->payload.boolean) ? "True" : "False"));
2404 case AST_DATA_IPADDR:
2405 ast_str_append(&output, 0, "%s%s: %s\n", ast_str_buffer(tabs),
2407 ast_inet_ntoa(node->payload.ipaddr));
2413 ast_term_color_code(&output, COLOR_WHITE, 0);
2415 ast_cli(fd, "%s", ast_str_buffer(output));
2419 if (node->type == AST_DATA_CONTAINER) {
2420 __data_result_print_cli(fd, node, depth + 1);
2426 * \brief Print out an ast_data tree to the CLI.
2427 * \param[in] fd The CLI file descriptor.
2428 * \param[in] root The root node of the tree.
2429 * \param[in] depth Actual depth.
2431 static void __data_result_print_cli(int fd, const struct ast_data *root, uint32_t depth)
2433 struct ao2_iterator iter;
2434 struct ast_data *node;
2436 if (root->type == AST_DATA_CONTAINER) {
2437 iter = ao2_iterator_init(root->children, 0);
2438 while ((node = ao2_iterator_next(&iter))) {
2439 data_result_print_cli_node(fd, node, depth + 1);
2442 ao2_iterator_destroy(&iter);
2444 data_result_print_cli_node(fd, root, depth);
2451 * \param[in] fd The CLI file descriptor.
2452 * \param[in] root The root node of the tree.
2454 static void data_result_print_cli(int fd, const struct ast_data *root)
2456 struct ast_str *output;
2458 /* print the initial node. */
2459 output = ast_str_create(30);
2464 ast_term_color_code(&output, data_result_get_color(root->type), 0);
2465 ast_str_append(&output, 0, "%s\n", root->name);
2466 ast_term_color_code(&output, COLOR_WHITE, 0);
2467 ast_cli(fd, "%s", ast_str_buffer(output));
2470 __data_result_print_cli(fd, root, 0);
2475 * \brief Handle the CLI command "data get".
2477 static char *handle_cli_data_get(struct ast_cli_entry *e, int cmd,
2478 struct ast_cli_args *a)
2480 struct ast_data_query query = {
2481 .version = AST_DATA_QUERY_VERSION
2483 struct ast_data *tree;
2487 e->command = "data get";
2489 "Usage: data get <path> [<search> [<filter>]]\n"
2490 " Get the tree based on a path.\n";
2496 if (a->argc < e->args + 1) {
2497 return CLI_SHOWUSAGE;
2500 query.path = (char *) a->argv[e->args];
2502 if (a->argc > e->args + 1) {
2503 query.search = (char *) a->argv[e->args + 1];
2506 if (a->argc > e->args + 2) {
2507 query.filter = (char *) a->argv[e->args + 2];
2510 tree = ast_data_get(&query);
2515 data_result_print_cli(a->fd, tree);
2517 ast_data_free(tree);
2524 * \brief Print the list of data providers.
2525 * \param[in] fd The CLI file descriptor.
2526 * \param[in] name The last node visited name.
2527 * \param[in] container The childrens of the last node.
2528 * \param[in] path The path to the current node.
2530 static void data_provider_print_cli(int fd, const char *name,
2531 struct ao2_container *container, struct ast_str *path)
2533 struct ao2_iterator i;
2534 struct ast_str *current_path;
2535 struct data_provider *provider;
2537 current_path = ast_str_create(60);
2538 if (!current_path) {
2542 ast_str_reset(current_path);
2544 ast_str_set(¤t_path, 0, "%s/%s", ast_str_buffer(path), name);
2546 ast_str_set(¤t_path, 0, "%s", name);
2549 i = ao2_iterator_init(container, 0);
2550 while ((provider = ao2_iterator_next(&i))) {
2551 if (provider->handler) {
2552 /* terminal node, print it. */
2553 ast_cli(fd, "%s/%s (", ast_str_buffer(current_path),
2555 if (provider->handler->get) {
2558 ast_cli(fd, ") [%s]\n", provider->registrar);
2560 data_provider_print_cli(fd, provider->name, provider->children,
2562 ao2_ref(provider, -1);
2564 ao2_iterator_destroy(&i);
2566 ast_free(current_path);
2571 * \brief Handle CLI command "data show providers"
2573 static char *handle_cli_data_show_providers(struct ast_cli_entry *e, int cmd,
2574 struct ast_cli_args *a)
2578 e->command = "data show providers";
2580 "Usage: data show providers\n"
2581 " Show the list of registered providers\n";
2588 data_provider_print_cli(a->fd, "", root_data.container, NULL);
2596 * \brief Data API CLI commands.
2598 static struct ast_cli_entry cli_data[] = {
2599 AST_CLI_DEFINE(handle_cli_data_get, "Data API get"),
2600 AST_CLI_DEFINE(handle_cli_data_show_providers, "Show data providers")
2605 * \brief Output a tree to the AMI.
2606 * \param[in] s AMI session.
2607 * \param[in] name The root node name.
2608 * \param[in] container The root container.
2609 * \param[in] path The current path.
2611 static void data_result_manager_output(struct mansession *s, const char *name,
2612 struct ao2_container *container, struct ast_str *path, int id)
2614 struct ao2_iterator i;
2615 struct ast_str *current_path;
2616 struct ast_data *node;
2617 int current_id = id;
2619 current_path = ast_str_create(60);
2620 if (!current_path) {
2624 ast_str_reset(current_path);
2626 ast_str_set(¤t_path, 0, "%s.%s", ast_str_buffer(path), name);
2628 ast_str_set(¤t_path, 0, "%s", name);
2631 i = ao2_iterator_init(container, 0);
2632 while ((node = ao2_iterator_next(&i))) {
2633 /* terminal node, print it. */
2634 if (node->type != AST_DATA_CONTAINER) {
2635 astman_append(s, "%d-%s.%s", id, ast_str_buffer(current_path),
2638 switch (node->type) {
2639 case AST_DATA_CONTAINER:
2640 data_result_manager_output(s, node->name, node->children, current_path, ++current_id);
2642 case AST_DATA_INTEGER:
2643 astman_append(s, ": %d\r\n", node->payload.sint);
2645 case AST_DATA_UNSIGNED_INTEGER:
2646 astman_append(s, ": %u\r\n", node->payload.uint);
2648 case AST_DATA_STRING:
2649 astman_append(s, ": %s\r\n", node->payload.str);
2651 case AST_DATA_IPADDR:
2652 astman_append(s, ": %s\r\n", ast_inet_ntoa(node->payload.ipaddr));
2654 case AST_DATA_POINTER:
2656 case AST_DATA_DOUBLE:
2657 astman_append(s, ": %f\r\n", node->payload.dbl);
2659 case AST_DATA_BOOLEAN:
2660 astman_append(s, ": %s\r\n",
2661 (node->payload.boolean ? "True" : "False"));
2667 ao2_iterator_destroy(&i);
2669 ast_free(current_path);
2674 * \brief Implements the manager action: "DataGet".
2676 static int manager_data_get(struct mansession *s, const struct message *m)
2678 const char *path = astman_get_header(m, "Path");
2679 const char *search = astman_get_header(m, "Search");
2680 const char *filter = astman_get_header(m, "Filter");
2681 const char *id = astman_get_header(m, "ActionID");
2682 struct ast_data *res;
2683 struct ast_data_query query = {
2684 .version = AST_DATA_QUERY_VERSION,
2685 .path = (char *) path,
2686 .search = (char *) search,
2687 .filter = (char *) filter,
2690 if (ast_strlen_zero(path)) {
2691 astman_send_error(s, m, "'Path' parameter not specified");
2695 res = ast_data_get(&query);
2697 astman_send_error(s, m, "No data returned");
2701 astman_append(s, "Event: DataGet Tree\r\n");
2702 if (!ast_strlen_zero(id)) {
2703 astman_append(s, "ActionID: %s\r\n", id);
2705 data_result_manager_output(s, res->name, res->children, NULL, 0);
2706 astman_append(s, "\r\n");
2710 return RESULT_SUCCESS;
2713 #ifdef TEST_FRAMEWORK
2717 * \brief Structure used to test how to add a complete structure,
2718 * and how to compare it.
2720 struct test_structure {
2722 unsigned int b_bool:1;
2724 unsigned int a_uint;
2729 * \brief test_structure mapping.
2731 #define DATA_EXPORT_TEST_STRUCTURE(MEMBER) \
2732 MEMBER(test_structure, a_int, AST_DATA_INTEGER) \
2733 MEMBER(test_structure, b_bool, AST_DATA_BOOLEAN) \
2734 MEMBER(test_structure, c_str, AST_DATA_STRING) \
2735 MEMBER(test_structure, a_uint, AST_DATA_UNSIGNED_INTEGER)
2737 AST_DATA_STRUCTURE(test_structure, DATA_EXPORT_TEST_STRUCTURE);
2741 * \brief Callback implementation.
2743 static int test_data_full_provider(const struct ast_data_search *search,
2744 struct ast_data *root)
2746 struct ast_data *test_structure;
2747 struct test_structure local_test_structure = {
2750 .c_str = "test string",
2754 if (ast_data_search_cmp_structure(search, test_structure, &local_test_structure, "test_structure")) {
2758 test_structure = ast_data_add_node(root, "test_structure");
2759 if (!test_structure) {
2760 ast_debug(1, "Internal data api error\n");
2764 /* add the complete structure. */
2765 ast_data_add_structure(test_structure, test_structure, &local_test_structure);
2772 * \brief Handler definition for the full provider.
2774 static const struct ast_data_handler full_provider = {
2775 .version = AST_DATA_HANDLER_VERSION,
2776 .get = test_data_full_provider
2781 * \brief Structure used to define multiple providers at once.
2783 static const struct ast_data_entry test_providers[] = {
2784 AST_DATA_ENTRY("test/node1/node11/node111", &full_provider)
2787 AST_TEST_DEFINE(test_data_get)
2789 struct ast_data *res, *node;
2790 struct ast_data_iterator *i;
2791 struct ast_data_query query = {
2792 .version = AST_DATA_QUERY_VERSION,
2793 .path = "test/node1/node11/node111",
2794 .search = "node111/test_structure/a_int=10",
2795 .filter = "node111/test_structure/a*int"
2800 info->name = "data_test";
2801 info->category = "main/data/";
2802 info->summary = "Data API unit test";
2804 "Tests whether data API get implementation works as expected.";
2805 return AST_TEST_NOT_RUN;
2810 ast_data_register_multiple_core(test_providers, ARRAY_LEN(test_providers));
2812 res = ast_data_get(&query);
2814 ast_test_status_update(test, "Unable to get tree.");
2815 ast_data_unregister("test/node1/node11/node111");
2816 return AST_TEST_FAIL;
2819 /* initiate the iterator and check for errors. */
2820 i = ast_data_iterator_init(res, "test_structure/");
2822 ast_test_status_update(test, "Unable to initiate the iterator.");
2824 ast_data_unregister("test/node1/node11/node111");
2825 return AST_TEST_FAIL;
2828 /* walk the returned nodes. */
2829 while ((node = ast_data_iterator_next(i))) {
2830 if (!strcmp(ast_data_retrieve_name(node), "a_int")) {
2831 if (ast_data_retrieve_int(node, "/") != 10) {
2832 ast_data_iterator_end(i);
2834 ast_data_unregister("test/node1/node11/node111");
2835 return AST_TEST_FAIL;
2837 } else if (!strcmp(ast_data_retrieve_name(node), "a_uint")) {
2838 if (ast_data_retrieve_uint(node, "/") != 20) {
2839 ast_data_iterator_end(i);
2841 ast_data_unregister("test/node1/node11/node111");
2842 return AST_TEST_FAIL;
2847 /* finish the iterator. */
2848 ast_data_iterator_end(i);
2852 ast_data_unregister("test/node1/node11/node111");
2854 return AST_TEST_PASS;
2859 int ast_data_init(void)
2863 ast_rwlock_init(&root_data.lock);
2865 if (!(root_data.container = ao2_container_alloc(NUM_DATA_NODE_BUCKETS,
2866 data_provider_hash, data_provider_cmp))) {
2870 res |= ast_cli_register_multiple(cli_data, ARRAY_LEN(cli_data));
2872 res |= ast_manager_register_xml("DataGet", 0, manager_data_get);
2874 #ifdef TEST_FRAMEWORK
2875 AST_TEST_REGISTER(test_data_get);