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);
1851 * \brief Helper function to move an ast_data tree to xml.
1852 * \param[in] parent_data The initial ast_data node to be passed to xml.
1853 * \param[out] parent_xml The root node to insert the xml.
1855 static void data_get_xml_add_child(struct ast_data *parent_data,
1856 struct ast_xml_node *parent_xml)
1858 struct ao2_iterator i;
1859 struct ast_data *node;
1860 struct ast_xml_node *child_xml;
1861 char node_content[256];
1863 i = ao2_iterator_init(parent_data->children, 0);
1864 while ((node = ao2_iterator_next(&i))) {
1865 child_xml = ast_xml_new_node(node->name);
1871 switch (node->type) {
1872 case AST_DATA_CONTAINER:
1873 data_get_xml_add_child(node, child_xml);
1875 case AST_DATA_STRING:
1876 ast_xml_set_text(child_xml, node->payload.str);
1878 case AST_DATA_INTEGER:
1879 snprintf(node_content, sizeof(node_content), "%d",
1880 node->payload.sint);
1881 ast_xml_set_text(child_xml, node_content);
1883 case AST_DATA_UNSIGNED_INTEGER:
1884 snprintf(node_content, sizeof(node_content), "%u",
1885 node->payload.uint);
1886 ast_xml_set_text(child_xml, node_content);
1888 case AST_DATA_DOUBLE:
1889 snprintf(node_content, sizeof(node_content), "%f",
1891 ast_xml_set_text(child_xml, node_content);
1893 case AST_DATA_BOOLEAN:
1894 if (node->payload.boolean) {
1895 ast_xml_set_text(child_xml, "true");
1897 ast_xml_set_text(child_xml, "false");
1900 case AST_DATA_POINTER:
1901 snprintf(node_content, sizeof(node_content), "%p",
1903 ast_xml_set_text(child_xml, node_content);
1905 case AST_DATA_IPADDR:
1906 snprintf(node_content, sizeof(node_content), "%s",
1907 ast_inet_ntoa(node->payload.ipaddr));
1908 ast_xml_set_text(child_xml, node_content);
1911 ast_xml_add_child(parent_xml, child_xml);
1915 ao2_iterator_destroy(&i);
1919 struct ast_xml_doc *ast_data_get_xml(const struct ast_data_query *query)
1921 struct ast_xml_doc *doc;
1922 struct ast_xml_node *root;
1923 struct ast_data *res;
1925 res = ast_data_get(query);
1930 doc = ast_xml_new();
1935 root = ast_xml_new_node(res->name);
1940 ast_xml_set_root(doc, root);
1942 data_get_xml_add_child(res, root);
1950 enum ast_data_type ast_data_retrieve_type(struct ast_data *node, const char *path)
1952 struct ast_data *internal;
1954 internal = data_result_get_node(node, path);
1959 return internal->type;
1962 char *ast_data_retrieve_name(struct ast_data *node)
1969 * \brief Insert a child node inside a passed parent node.
1970 * \param root Where we are going to insert the child node.
1971 * \param name The name of the child node to add.
1972 * \param type The type of content inside the child node.
1973 * \param ptr The actual content of the child node.
1974 * \retval NULL on error.
1975 * \retval non-NULL The added child node pointer.
1977 static struct ast_data *__ast_data_add(struct ast_data *root, const char *name,
1978 enum ast_data_type type, void *ptr)
1980 struct ast_data *node;
1981 struct data_filter *filter, *filter_child = NULL;
1983 if (!root || !root->children) {
1984 /* invalid data result node. */
1988 /* check if we need to add this node, based on the filter. */
1990 filter = data_filter_find(root->filter->children, name);
1994 ao2_ref(filter, -1);
1997 node = data_result_create(name);
2005 case AST_DATA_BOOLEAN:
2006 node->payload.boolean = *(unsigned int *) ptr;
2008 case AST_DATA_INTEGER:
2009 node->payload.sint = *(unsigned int *) ptr;
2011 case AST_DATA_UNSIGNED_INTEGER:
2012 node->payload.sint = *(unsigned int *) ptr;
2014 case AST_DATA_DOUBLE:
2015 node->payload.dbl = *(double *) ptr;
2017 case AST_DATA_STRING:
2018 case AST_DATA_POINTER:
2019 node->payload.ptr = ptr;
2021 case AST_DATA_IPADDR:
2022 node->payload.ipaddr = *(struct in_addr *) ptr;
2024 case AST_DATA_CONTAINER:
2026 filter_child = data_filter_find(root->filter->children, name);
2028 /* do not increment the refcount because it is not neccesary. */
2029 ao2_ref(filter_child, -1);
2032 node->filter = filter_child;
2038 data_result_add_child(root, node);
2045 struct ast_data *ast_data_add_node(struct ast_data *root, const char *name)
2047 return __ast_data_add(root, name, AST_DATA_CONTAINER, NULL);
2050 struct ast_data *ast_data_add_int(struct ast_data *root, const char *name, int value)
2052 return __ast_data_add(root, name, AST_DATA_INTEGER, &value);
2055 struct ast_data *ast_data_add_uint(struct ast_data *root, const char *name,
2058 return __ast_data_add(root, name, AST_DATA_UNSIGNED_INTEGER, &value);
2061 struct ast_data *ast_data_add_dbl(struct ast_data *root, const char *childname,
2064 return __ast_data_add(root, childname, AST_DATA_DOUBLE, &dbl);
2067 struct ast_data *ast_data_add_bool(struct ast_data *root, const char *childname,
2068 unsigned int boolean)
2070 return __ast_data_add(root, childname, AST_DATA_BOOLEAN, &boolean);
2073 struct ast_data *ast_data_add_ipaddr(struct ast_data *root, const char *childname,
2074 struct in_addr addr)
2076 return __ast_data_add(root, childname, AST_DATA_IPADDR, &addr);
2079 struct ast_data *ast_data_add_ptr(struct ast_data *root, const char *childname,
2082 return __ast_data_add(root, childname, AST_DATA_POINTER, ptr);
2085 struct ast_data *ast_data_add_str(struct ast_data *root, const char *childname,
2089 size_t namelen = 1 + (ast_strlen_zero(value) ? 0 : strlen(value));
2090 struct ast_data *res;
2092 if (!(name = ast_malloc(namelen))) {
2096 strcpy(name, (ast_strlen_zero(value) ? "" : value));
2098 res = __ast_data_add(root, childname, AST_DATA_STRING, name);
2106 int __ast_data_add_structure(struct ast_data *root,
2107 const struct ast_data_mapping_structure *mapping, size_t mapping_len,
2112 for (i = 0; i < mapping_len; i++) {
2113 switch (mapping[i].type) {
2114 case AST_DATA_INTEGER:
2115 ast_data_add_int(root, mapping[i].name,
2116 mapping[i].get.AST_DATA_INTEGER(structure));
2118 case AST_DATA_UNSIGNED_INTEGER:
2119 ast_data_add_uint(root, mapping[i].name,
2120 mapping[i].get.AST_DATA_UNSIGNED_INTEGER(structure));
2122 case AST_DATA_DOUBLE:
2123 ast_data_add_dbl(root, mapping[i].name,
2124 mapping[i].get.AST_DATA_DOUBLE(structure));
2126 case AST_DATA_BOOLEAN:
2127 ast_data_add_bool(root, mapping[i].name,
2128 mapping[i].get.AST_DATA_BOOLEAN(structure));
2130 case AST_DATA_STRING:
2131 ast_data_add_str(root, mapping[i].name,
2132 mapping[i].get.AST_DATA_STRING(structure));
2134 case AST_DATA_CONTAINER:
2136 case AST_DATA_IPADDR:
2137 ast_data_add_ipaddr(root, mapping[i].name,
2138 mapping[i].get.AST_DATA_IPADDR(structure));
2140 case AST_DATA_POINTER:
2141 ast_data_add_ptr(root, mapping[i].name,
2142 mapping[i].get.AST_DATA_POINTER(structure));
2150 void ast_data_remove_node(struct ast_data *root, struct ast_data *child)
2152 ao2_unlink(root->children, child);
2155 void ast_data_free(struct ast_data *root)
2157 /* destroy it, this will destroy all the internal nodes. */
2161 struct ast_data_iterator *ast_data_iterator_init(struct ast_data *tree,
2162 const char *elements)
2164 struct ast_data_iterator *iterator;
2165 struct ao2_iterator i;
2166 struct ast_data *internal = tree;
2167 char *path, *ptr = NULL;
2169 /* tree is the node we want to use to iterate? or we are going
2170 * to iterate thow an internal node? */
2172 path = ast_strdupa(elements);
2174 ptr = strrchr(path, '/');
2177 internal = data_result_get_node(tree, path);
2184 iterator = ast_calloc(1, sizeof(*iterator));
2189 i = ao2_iterator_init(internal->children, 0);
2191 iterator->pattern = (ptr ? strrchr(elements, '/') + 1 : elements);
2193 /* is the last node a regular expression?, compile it! */
2194 if (!regcomp(&(iterator->regex_pattern), iterator->pattern,
2195 REG_EXTENDED | REG_NOSUB | REG_ICASE)) {
2196 iterator->is_pattern = 1;
2199 iterator->internal_iterator = i;
2204 void ast_data_iterator_end(struct ast_data_iterator *iterator)
2206 /* decrement the reference counter. */
2207 if (iterator->last) {
2208 ao2_ref(iterator->last, -1);
2211 /* release the generated pattern. */
2212 if (iterator->is_pattern) {
2213 regfree(&(iterator->regex_pattern));
2216 ao2_iterator_destroy(&(iterator->internal_iterator));
2222 struct ast_data *ast_data_iterator_next(struct ast_data_iterator *iterator)
2224 struct ast_data *res;
2226 if (iterator->last) {
2227 /* release the last retrieved node reference. */
2228 ao2_ref(iterator->last, -1);
2231 while ((res = ao2_iterator_next(&iterator->internal_iterator))) {
2232 /* if there is no node name pattern specified, return
2234 if (!iterator->pattern) {
2238 /* if the pattern is a regular expression, check if this node
2240 if (iterator->is_pattern && !regexec(&(iterator->regex_pattern),
2241 res->name, 0, NULL, 0)) {
2245 /* if there is a pattern specified, check if this node matches
2246 * the wanted node names. */
2247 if (!iterator->is_pattern && (iterator->pattern &&
2248 !strcasecmp(res->name, iterator->pattern))) {
2255 iterator->last = res;
2260 int ast_data_retrieve(struct ast_data *tree, const char *path,
2261 struct ast_data_retrieve *content)
2263 struct ast_data *node;
2269 node = data_result_get_node(tree, path);
2271 ast_log(LOG_ERROR, "Invalid internal node %s\n", path);
2275 content->type = node->type;
2276 switch (node->type) {
2277 case AST_DATA_STRING:
2278 content->value.AST_DATA_STRING = node->payload.str;
2280 case AST_DATA_INTEGER:
2281 content->value.AST_DATA_INTEGER = node->payload.sint;
2283 case AST_DATA_UNSIGNED_INTEGER:
2284 content->value.AST_DATA_UNSIGNED_INTEGER = node->payload.uint;
2286 case AST_DATA_BOOLEAN:
2287 content->value.AST_DATA_BOOLEAN = node->payload.boolean;
2289 case AST_DATA_IPADDR:
2290 content->value.AST_DATA_IPADDR = node->payload.ipaddr;
2292 case AST_DATA_DOUBLE:
2293 content->value.AST_DATA_DOUBLE = node->payload.dbl;
2295 case AST_DATA_CONTAINER:
2297 case AST_DATA_POINTER:
2298 content->value.AST_DATA_POINTER = node->payload.ptr;
2307 * \brief One color for each node type.
2309 static const struct {
2310 enum ast_data_type type;
2312 } data_result_color[] = {
2313 { AST_DATA_STRING, COLOR_CYAN },
2314 { AST_DATA_INTEGER, COLOR_RED },
2315 { AST_DATA_UNSIGNED_INTEGER, COLOR_RED },
2316 { AST_DATA_DOUBLE, COLOR_RED },
2317 { AST_DATA_BOOLEAN, COLOR_BRRED },
2318 { AST_DATA_CONTAINER, COLOR_GREEN },
2319 { AST_DATA_IPADDR, COLOR_BROWN },
2320 { AST_DATA_POINTER, COLOR_YELLOW },
2325 * \brief Get the color configured for a specific node type.
2326 * \param[in] type The node type.
2327 * \returns The color specified for the passed type.
2329 static int data_result_get_color(enum ast_data_type type)
2332 for (i = 0; i < ARRAY_LEN(data_result_color); i++) {
2333 if (data_result_color[i].type == type) {
2334 return data_result_color[i].color;
2343 * \brief Print a node to the CLI.
2344 * \param[in] fd The CLI file descriptor.
2345 * \param[in] node The node to print.
2346 * \param[in] depth The actual node depth in the tree.
2348 static void data_result_print_cli_node(int fd, const struct ast_data *node, uint32_t depth)
2351 struct ast_str *tabs, *output;
2353 tabs = ast_str_create(depth * 10 + 1);
2357 ast_str_reset(tabs);
2358 for (i = 0; i < depth; i++) {
2359 ast_str_append(&tabs, 0, " ");
2362 output = ast_str_create(20);
2368 ast_str_reset(output);
2369 ast_term_color_code(&output, data_result_get_color(node->type), 0);
2371 switch (node->type) {
2372 case AST_DATA_POINTER:
2373 ast_str_append(&output, 0, "%s%s: %p\n", ast_str_buffer(tabs),
2374 node->name, node->payload.ptr);
2376 case AST_DATA_STRING:
2377 ast_str_append(&output, 0, "%s%s: \"%s\"\n",
2378 ast_str_buffer(tabs),
2382 case AST_DATA_CONTAINER:
2383 ast_str_append(&output, 0, "%s%s\n", ast_str_buffer(tabs),
2386 case AST_DATA_INTEGER:
2387 ast_str_append(&output, 0, "%s%s: %d\n", ast_str_buffer(tabs),
2389 node->payload.sint);
2391 case AST_DATA_UNSIGNED_INTEGER:
2392 ast_str_append(&output, 0, "%s%s: %u\n", ast_str_buffer(tabs),
2394 node->payload.uint);
2396 case AST_DATA_DOUBLE:
2397 ast_str_append(&output, 0, "%s%s: %lf\n", ast_str_buffer(tabs),
2401 case AST_DATA_BOOLEAN:
2402 ast_str_append(&output, 0, "%s%s: %s\n", ast_str_buffer(tabs),
2404 ((node->payload.boolean) ? "True" : "False"));
2406 case AST_DATA_IPADDR:
2407 ast_str_append(&output, 0, "%s%s: %s\n", ast_str_buffer(tabs),
2409 ast_inet_ntoa(node->payload.ipaddr));
2415 ast_term_color_code(&output, COLOR_WHITE, 0);
2417 ast_cli(fd, "%s", ast_str_buffer(output));
2421 if (node->type == AST_DATA_CONTAINER) {
2422 __data_result_print_cli(fd, node, depth + 1);
2428 * \brief Print out an ast_data tree to the CLI.
2429 * \param[in] fd The CLI file descriptor.
2430 * \param[in] root The root node of the tree.
2431 * \param[in] depth Actual depth.
2433 static void __data_result_print_cli(int fd, const struct ast_data *root, uint32_t depth)
2435 struct ao2_iterator iter;
2436 struct ast_data *node;
2438 if (root->type == AST_DATA_CONTAINER) {
2439 iter = ao2_iterator_init(root->children, 0);
2440 while ((node = ao2_iterator_next(&iter))) {
2441 data_result_print_cli_node(fd, node, depth + 1);
2444 ao2_iterator_destroy(&iter);
2446 data_result_print_cli_node(fd, root, depth);
2453 * \param[in] fd The CLI file descriptor.
2454 * \param[in] root The root node of the tree.
2456 static void data_result_print_cli(int fd, const struct ast_data *root)
2458 struct ast_str *output;
2460 /* print the initial node. */
2461 output = ast_str_create(30);
2466 ast_term_color_code(&output, data_result_get_color(root->type), 0);
2467 ast_str_append(&output, 0, "%s\n", root->name);
2468 ast_term_color_code(&output, COLOR_WHITE, 0);
2469 ast_cli(fd, "%s", ast_str_buffer(output));
2472 __data_result_print_cli(fd, root, 0);
2477 * \brief Handle the CLI command "data get".
2479 static char *handle_cli_data_get(struct ast_cli_entry *e, int cmd,
2480 struct ast_cli_args *a)
2482 struct ast_data_query query = {
2483 .version = AST_DATA_QUERY_VERSION
2485 struct ast_data *tree;
2489 e->command = "data get";
2491 "Usage: data get <path> [<search> [<filter>]]\n"
2492 " Get the tree based on a path.\n";
2498 if (a->argc < e->args + 1) {
2499 return CLI_SHOWUSAGE;
2502 query.path = (char *) a->argv[e->args];
2504 if (a->argc > e->args + 1) {
2505 query.search = (char *) a->argv[e->args + 1];
2508 if (a->argc > e->args + 2) {
2509 query.filter = (char *) a->argv[e->args + 2];
2512 tree = ast_data_get(&query);
2517 data_result_print_cli(a->fd, tree);
2519 ast_data_free(tree);
2526 * \brief Print the list of data providers.
2527 * \param[in] fd The CLI file descriptor.
2528 * \param[in] name The last node visited name.
2529 * \param[in] container The childrens of the last node.
2530 * \param[in] path The path to the current node.
2532 static void data_provider_print_cli(int fd, const char *name,
2533 struct ao2_container *container, struct ast_str *path)
2535 struct ao2_iterator i;
2536 struct ast_str *current_path;
2537 struct data_provider *provider;
2539 current_path = ast_str_create(60);
2540 if (!current_path) {
2544 ast_str_reset(current_path);
2546 ast_str_set(¤t_path, 0, "%s/%s", ast_str_buffer(path), name);
2548 ast_str_set(¤t_path, 0, "%s", name);
2551 i = ao2_iterator_init(container, 0);
2552 while ((provider = ao2_iterator_next(&i))) {
2553 if (provider->handler) {
2554 /* terminal node, print it. */
2555 ast_cli(fd, "%s/%s (", ast_str_buffer(current_path),
2557 if (provider->handler->get) {
2560 ast_cli(fd, ") [%s]\n", provider->registrar);
2562 data_provider_print_cli(fd, provider->name, provider->children,
2564 ao2_ref(provider, -1);
2566 ao2_iterator_destroy(&i);
2568 ast_free(current_path);
2573 * \brief Handle CLI command "data show providers"
2575 static char *handle_cli_data_show_providers(struct ast_cli_entry *e, int cmd,
2576 struct ast_cli_args *a)
2580 e->command = "data show providers";
2582 "Usage: data show providers\n"
2583 " Show the list of registered providers\n";
2590 data_provider_print_cli(a->fd, "", root_data.container, NULL);
2598 * \brief Data API CLI commands.
2600 static struct ast_cli_entry cli_data[] = {
2601 AST_CLI_DEFINE(handle_cli_data_get, "Data API get"),
2602 AST_CLI_DEFINE(handle_cli_data_show_providers, "Show data providers")
2607 * \brief Output a tree to the AMI.
2608 * \param[in] s AMI session.
2609 * \param[in] name The root node name.
2610 * \param[in] container The root container.
2611 * \param[in] path The current path.
2613 static void data_result_manager_output(struct mansession *s, const char *name,
2614 struct ao2_container *container, struct ast_str *path, int id)
2616 struct ao2_iterator i;
2617 struct ast_str *current_path;
2618 struct ast_data *node;
2619 int current_id = id;
2621 current_path = ast_str_create(60);
2622 if (!current_path) {
2626 ast_str_reset(current_path);
2628 ast_str_set(¤t_path, 0, "%s.%s", ast_str_buffer(path), name);
2630 ast_str_set(¤t_path, 0, "%s", name);
2633 i = ao2_iterator_init(container, 0);
2634 while ((node = ao2_iterator_next(&i))) {
2635 /* terminal node, print it. */
2636 if (node->type != AST_DATA_CONTAINER) {
2637 astman_append(s, "%d-%s.%s", id, ast_str_buffer(current_path),
2640 switch (node->type) {
2641 case AST_DATA_CONTAINER:
2642 data_result_manager_output(s, node->name, node->children, current_path, ++current_id);
2644 case AST_DATA_INTEGER:
2645 astman_append(s, ": %d\r\n", node->payload.sint);
2647 case AST_DATA_UNSIGNED_INTEGER:
2648 astman_append(s, ": %u\r\n", node->payload.uint);
2650 case AST_DATA_STRING:
2651 astman_append(s, ": %s\r\n", node->payload.str);
2653 case AST_DATA_IPADDR:
2654 astman_append(s, ": %s\r\n", ast_inet_ntoa(node->payload.ipaddr));
2656 case AST_DATA_POINTER:
2658 case AST_DATA_DOUBLE:
2659 astman_append(s, ": %f\r\n", node->payload.dbl);
2661 case AST_DATA_BOOLEAN:
2662 astman_append(s, ": %s\r\n",
2663 (node->payload.boolean ? "True" : "False"));
2669 ao2_iterator_destroy(&i);
2671 ast_free(current_path);
2676 * \brief Implements the manager action: "DataGet".
2678 static int manager_data_get(struct mansession *s, const struct message *m)
2680 const char *path = astman_get_header(m, "Path");
2681 const char *search = astman_get_header(m, "Search");
2682 const char *filter = astman_get_header(m, "Filter");
2683 const char *id = astman_get_header(m, "ActionID");
2684 struct ast_data *res;
2685 struct ast_data_query query = {
2686 .version = AST_DATA_QUERY_VERSION,
2687 .path = (char *) path,
2688 .search = (char *) search,
2689 .filter = (char *) filter,
2692 if (ast_strlen_zero(path)) {
2693 astman_send_error(s, m, "'Path' parameter not specified");
2697 res = ast_data_get(&query);
2699 astman_send_error(s, m, "No data returned");
2703 astman_append(s, "Event: DataGet Tree\r\n");
2704 if (!ast_strlen_zero(id)) {
2705 astman_append(s, "ActionID: %s\r\n", id);
2707 data_result_manager_output(s, res->name, res->children, NULL, 0);
2708 astman_append(s, "\r\n");
2712 return RESULT_SUCCESS;
2715 #ifdef TEST_FRAMEWORK
2719 * \brief Structure used to test how to add a complete structure,
2720 * and how to compare it.
2722 struct test_structure {
2724 unsigned int b_bool:1;
2726 unsigned int a_uint;
2731 * \brief test_structure mapping.
2733 #define DATA_EXPORT_TEST_STRUCTURE(MEMBER) \
2734 MEMBER(test_structure, a_int, AST_DATA_INTEGER) \
2735 MEMBER(test_structure, b_bool, AST_DATA_BOOLEAN) \
2736 MEMBER(test_structure, c_str, AST_DATA_STRING) \
2737 MEMBER(test_structure, a_uint, AST_DATA_UNSIGNED_INTEGER)
2739 AST_DATA_STRUCTURE(test_structure, DATA_EXPORT_TEST_STRUCTURE);
2743 * \brief Callback implementation.
2745 static int test_data_full_provider(const struct ast_data_search *search,
2746 struct ast_data *root)
2748 struct ast_data *test_structure;
2749 struct test_structure local_test_structure = {
2752 .c_str = "test string",
2756 if (ast_data_search_cmp_structure(search, test_structure, &local_test_structure, "test_structure")) {
2760 test_structure = ast_data_add_node(root, "test_structure");
2761 if (!test_structure) {
2762 ast_debug(1, "Internal data api error\n");
2766 /* add the complete structure. */
2767 ast_data_add_structure(test_structure, test_structure, &local_test_structure);
2774 * \brief Handler definition for the full provider.
2776 static const struct ast_data_handler full_provider = {
2777 .version = AST_DATA_HANDLER_VERSION,
2778 .get = test_data_full_provider
2783 * \brief Structure used to define multiple providers at once.
2785 static const struct ast_data_entry test_providers[] = {
2786 AST_DATA_ENTRY("test/node1/node11/node111", &full_provider)
2789 AST_TEST_DEFINE(test_data_get)
2791 struct ast_data *res, *node;
2792 struct ast_data_iterator *i;
2793 struct ast_data_query query = {
2794 .version = AST_DATA_QUERY_VERSION,
2795 .path = "test/node1/node11/node111",
2796 .search = "node111/test_structure/a_int=10",
2797 .filter = "node111/test_structure/a*int"
2802 info->name = "data_test";
2803 info->category = "main/data/";
2804 info->summary = "Data API unit test";
2806 "Tests whether data API get implementation works as expected.";
2807 return AST_TEST_NOT_RUN;
2812 ast_data_register_multiple_core(test_providers, ARRAY_LEN(test_providers));
2814 res = ast_data_get(&query);
2816 ast_test_status_update(test, "Unable to get tree.");
2817 ast_data_unregister("test/node1/node11/node111");
2818 return AST_TEST_FAIL;
2821 /* initiate the iterator and check for errors. */
2822 i = ast_data_iterator_init(res, "test_structure/");
2824 ast_test_status_update(test, "Unable to initiate the iterator.");
2826 ast_data_unregister("test/node1/node11/node111");
2827 return AST_TEST_FAIL;
2830 /* walk the returned nodes. */
2831 while ((node = ast_data_iterator_next(i))) {
2832 if (!strcmp(ast_data_retrieve_name(node), "a_int")) {
2833 if (ast_data_retrieve_int(node, "/") != 10) {
2834 ast_data_iterator_end(i);
2836 ast_data_unregister("test/node1/node11/node111");
2837 return AST_TEST_FAIL;
2839 } else if (!strcmp(ast_data_retrieve_name(node), "a_uint")) {
2840 if (ast_data_retrieve_uint(node, "/") != 20) {
2841 ast_data_iterator_end(i);
2843 ast_data_unregister("test/node1/node11/node111");
2844 return AST_TEST_FAIL;
2849 /* finish the iterator. */
2850 ast_data_iterator_end(i);
2854 ast_data_unregister("test/node1/node11/node111");
2856 return AST_TEST_PASS;
2861 int ast_data_init(void)
2865 ast_rwlock_init(&root_data.lock);
2867 if (!(root_data.container = ao2_container_alloc(NUM_DATA_NODE_BUCKETS,
2868 data_provider_hash, data_provider_cmp))) {
2872 res |= ast_cli_register_multiple(cli_data, ARRAY_LEN(cli_data));
2874 res |= ast_manager_register_xml("DataGet", 0, manager_data_get);
2876 #ifdef TEST_FRAMEWORK
2877 AST_TEST_REGISTER(test_data_get);