[asterisk-commits] eliel: branch group/data_api_gsoc2009 r196867 - in /team/group/data_api_gsoc2...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue May 26 13:26:08 CDT 2009
Author: eliel
Date: Tue May 26 13:26:05 2009
New Revision: 196867
URL: http://svn.asterisk.org/svn-view/asterisk?view=rev&rev=196867
Log:
Initial generic data api implementation.
Implements the ast_data_register() and ast_data_unregister() functions.
Added:
team/group/data_api_gsoc2009/include/asterisk/data.h (with props)
team/group/data_api_gsoc2009/main/data.c (with props)
Modified:
team/group/data_api_gsoc2009/include/asterisk/_private.h
team/group/data_api_gsoc2009/main/Makefile
team/group/data_api_gsoc2009/main/asterisk.c
Modified: team/group/data_api_gsoc2009/include/asterisk/_private.h
URL: http://svn.asterisk.org/svn-view/asterisk/team/group/data_api_gsoc2009/include/asterisk/_private.h?view=diff&rev=196867&r1=196866&r2=196867
==============================================================================
--- team/group/data_api_gsoc2009/include/asterisk/_private.h (original)
+++ team/group/data_api_gsoc2009/include/asterisk/_private.h Tue May 26 13:26:05 2009
@@ -35,6 +35,7 @@
int ast_file_init(void); /*!< Provided by file.c */
int ast_features_init(void); /*!< Provided by features.c */
void ast_autoservice_init(void); /*!< Provided by autoservice.c */
+int ast_data_init(void); /*!< Provided by data.c */
int ast_http_init(void); /*!< Provided by http.c */
int ast_http_reload(void); /*!< Provided by http.c */
int ast_tps_init(void); /*!< Provided by taskprocessor.c */
Added: team/group/data_api_gsoc2009/include/asterisk/data.h
URL: http://svn.asterisk.org/svn-view/asterisk/team/group/data_api_gsoc2009/include/asterisk/data.h?view=auto&rev=196867
==============================================================================
--- team/group/data_api_gsoc2009/include/asterisk/data.h (added)
+++ team/group/data_api_gsoc2009/include/asterisk/data.h Tue May 26 13:26:05 2009
@@ -1,0 +1,139 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Eliel C. Sardanons (LU1ALY) <eliels at gmail.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Data retrieval API.
+ * \author Eliel C. Sardanons (LU1ALY) <eliels at gmail.com>
+ * \arg \ref AstDataRetrieval
+ */
+
+#ifndef ASTERISK_DATA_H
+#define ASTERISK_DATA_H
+
+/*!
+ * \page AstDataRetrieval The Asterisk DATA retrieval API.
+ *
+ * This module implements an abstraction for retrieving asterisk data and
+ * export it.
+ *
+ * \section USAGE
+ *
+ * To register a callback use:
+ *
+ * \code
+ * ast_data_register("/node/path", callback_fn);
+ * \endcode
+ *
+ * If you instead want to register multiple nodes at once use:
+ * \code
+ * static const struct ast_data_handler handler_struct1 = {
+ * .read = handler_callback_read1
+ * };
+ * ... other handlers ...
+ *
+ * static const struct ast_data_entry list_providers[] = {
+ * AST_DATA_ENTRY("/path1/node1", &handler_struct1),
+ * AST_DATA_ENTRY("/path2/node2", &handler_struct2),
+ * AST_DATA_ENTRY("/path3/node3", &handler_struct3),
+ * };
+ *
+ * ...
+ *
+ * ast_data_register_multiple(list_providers, ARRAY_LEN(list_providers));
+ * \endcode
+ *
+ * To unregister a callback function already registered you can just call:
+ *
+ * \code
+ * ast_data_unregister(NULL);
+ * \endcode
+ * And every node registered by the current module (file) will be unregistered.
+ * If you want to unregister an specific node use:
+ *
+ * \code
+ * ast_data_unregister("/node/path");
+ * \endcode
+ *
+ */
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! \brief opaque definition of an ast_data handler. */
+struct ast_data;
+/*! \brief opaque definition of an ast_data_node result. */
+struct ast_data_node;
+
+typedef struct ast_data_node *(*ast_data_read_cb)(void);
+
+/*! \brief The structure of the node handler. */
+struct ast_data_handler {
+ ast_data_read_cb read;
+};
+
+/*! \brief This entries are for multiple registers. */
+struct ast_data_entry {
+ /*! \brief Path of the node to register. */
+ const char *path;
+ /*! \brief Data handler structure. */
+ const struct ast_data_handler *handler;
+};
+
+#define AST_DATA_ENTRY(__path, __handler) { .path = __path, .handler = __handler }
+
+/*!
+ * \brief Register a data provider.
+ * \param[in] path The path of the node to register.
+ * \param[in] handler The structure defining this node handler.
+ * \param[in] registrar Who is registering this node.
+ * \see ast_data_unregister
+ * \retval < 0 on error.
+ * \retval 0 on success.
+ */
+int __ast_data_register(const char *path, const struct ast_data_handler *handler, const char *registrar);
+#define ast_data_register(path, handler) __ast_data_register(path, handler, __FILE__)
+
+/*!
+ * \brief Register multiple data providers at once.
+ * \param[in] data_entries An array of data_entries structures.
+ * \param[in] entries The number of entries in the data_entries array.
+ * \param[in] registrar Who is registering this nodes.
+ * \retval < 0 on error (none of the nodes are being registered on error).
+ * \retval 0 on success.
+ */
+int __ast_data_register_multiple(const struct ast_data_entry *data_entries, size_t entries, const char *registrar);
+#define ast_data_register_multiple(data_entries, entries) __ast_data_register_multiple(data_entries, entries, __FILE__);
+
+/*!
+ * \brief Unregister a data provider.
+ * \param[in] path Which node to unregister, if path is NULL unregister every node registered by
+ * the passed 'registrar'.
+ * \param[in] registrar Who is trying to unregister this node, only the owner (the one who registered
+ * the node) will be able to unregister it.
+ * \see ast_data_register
+ * \retval < 0 on error.
+ * \retval 0 on success.
+ */
+int __ast_data_unregister(const char *path, const char *registrar);
+#define ast_data_unregister(path) __ast_data_unregister(path, __FILE__)
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* ASTERISK_DATA_H */
Propchange: team/group/data_api_gsoc2009/include/asterisk/data.h
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/group/data_api_gsoc2009/include/asterisk/data.h
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/group/data_api_gsoc2009/include/asterisk/data.h
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: team/group/data_api_gsoc2009/main/Makefile
URL: http://svn.asterisk.org/svn-view/asterisk/team/group/data_api_gsoc2009/main/Makefile?view=diff&rev=196867&r1=196866&r2=196867
==============================================================================
--- team/group/data_api_gsoc2009/main/Makefile (original)
+++ team/group/data_api_gsoc2009/main/Makefile Tue May 26 13:26:05 2009
@@ -29,7 +29,7 @@
strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \
astobj2.o hashtab.o global_datastores.o version.o \
features.o taskprocessor.o timing.o datastore.o xml.o xmldoc.o \
- strings.o bridging.o poll.o rtp_engine.o stun.o autochan.o
+ strings.o bridging.o poll.o rtp_engine.o stun.o autochan.o data.o
# we need to link in the objects statically, not as a library, because
# otherwise modules will not have them available if none of the static
Modified: team/group/data_api_gsoc2009/main/asterisk.c
URL: http://svn.asterisk.org/svn-view/asterisk/team/group/data_api_gsoc2009/main/asterisk.c?view=diff&rev=196867&r1=196866&r2=196867
==============================================================================
--- team/group/data_api_gsoc2009/main/asterisk.c (original)
+++ team/group/data_api_gsoc2009/main/asterisk.c Tue May 26 13:26:05 2009
@@ -3576,6 +3576,12 @@
ast_xmldoc_load_documentation();
#endif
+ /* initialize the data retrieval API */
+ if (ast_data_init()) {
+ printf ("%s", term_quit());
+ exit(1);
+ }
+
if (load_modules(1)) { /* Load modules, pre-load only */
printf("%s", term_quit());
exit(1);
Added: team/group/data_api_gsoc2009/main/data.c
URL: http://svn.asterisk.org/svn-view/asterisk/team/group/data_api_gsoc2009/main/data.c?view=auto&rev=196867
==============================================================================
--- team/group/data_api_gsoc2009/main/data.c (added)
+++ team/group/data_api_gsoc2009/main/data.c Tue May 26 13:26:05 2009
@@ -1,0 +1,455 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Eliel C. Sardanons (LU1ALY) <eliels at gmail.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Data retrieval API.
+ *
+ * \author Eliel C. Sardanons (LU1ALY) <eliels at gmail.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/data.h"
+#include "asterisk/astobj2.h"
+
+#define NUM_DATA_NODE_BUCKETS 60
+
+/*! \brief A data container node pointing to the registered handler. */
+struct data_node_provider {
+ /*! \brief node content handler. */
+ const struct ast_data_handler *handler;
+ /*! \brief children nodes. */
+ struct ao2_container *children;
+ /*! \brief Who registered this node. */
+ const char *registrar;
+ /*! \brief Node name. */
+ char name[0];
+};
+
+/*! \brief The asterisk data main content structure. */
+static struct ao2_container *root_data;
+
+/*!
+ * \internal
+ * \brief asterisk data locking mechanism.
+ * \see root_data
+ */
+static ast_rwlock_t root_data_lock;
+
+/*!
+ * \internal
+ * \brief Common string hash function.
+ * \see ast_data_init
+ */
+static int data_node_provider_hash(const void *obj, const int flags)
+{
+ const struct data_node_provider *node = obj;
+
+ return ast_str_hash(node->name);
+}
+
+/*!
+ * \internal
+ * \brief Compare two data_node_provider's.
+ * \see ast_data_init
+ */
+static int data_node_provider_cmp(void *obj, void *arg, int flags)
+{
+ struct data_node_provider *node1 = obj, *node2 = arg;
+
+ return strcasecmp(node1->name, node2->name) ? 0 : CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Lock the data registered handlers structure for writing.
+ * \see data_unlock
+ */
+static void data_write_lock(void)
+{
+ ast_rwlock_wrlock(&root_data_lock);
+}
+
+/*!
+ * \internal
+ * \brief Lock the data registered handlers structure for reading.
+ * \see data_unlock
+ */
+/* XXX: avoid defining functions that at the moment are not being used.
+static void data_read_lock(void)
+{
+ ast_rwlock_rdlock(&root_data_lock);
+}
+*/
+
+/*!
+ * \internal
+ * \brief Unlock the data registered handlers structure.
+ */
+static void data_unlock(void)
+{
+ ast_rwlock_unlock(&root_data_lock);
+}
+
+/*!
+ * \internal
+ * \brief Get the next node name in a path (/node1/node2)
+ * Avoid null nodes like //node1//node2/node3.
+ * \param[in] path The path where we are going to search for the next node name.
+ * \retval The next node name we found inside the given path.
+ * \retval NULL if there are no more node names.
+ */
+static char *next_node_name(char **path)
+{
+ char *res;
+
+ do {
+ res = strsep(path, "/");
+ } while (res && !strlen(res));
+
+ return res;
+}
+
+/*!
+ * \internal
+ * \brief Create a new data node.
+ * \param[in] name The name of the node we are going to create.
+ * \param[in] handler The handler registered for this node.
+ * \param[in] registrar The name of the registrar.
+ * \retval NULL on error.
+ * \retval The allocated data node structure.
+ */
+static struct data_node_provider *data_node_provider_new(const char *name, const struct ast_data_handler *handler, const char *registrar)
+{
+ struct data_node_provider *node;
+ size_t namelen;
+
+ namelen = strlen(name) + 1;
+
+ node = ao2_alloc(sizeof(*node) + namelen, NULL);
+ if (!node) {
+ return NULL;
+ }
+
+ node->handler = handler;
+ node->registrar = registrar;
+ strcpy(node->name, name);
+
+ /* initialize the childrens container. */
+ if (!(node->children = ao2_container_alloc(NUM_DATA_NODE_BUCKETS,
+ data_node_provider_hash, data_node_provider_cmp))) {
+ ao2_ref(node, -1);
+ return NULL;
+ }
+
+ return node;
+}
+
+/*!
+ * \internal
+ * \brief Add a child node named 'name' to the 'parent' node.
+ * \param[in] parent Where to add the child node.
+ * \param[in] name The name of the child node.
+ * \param[in] handler The handler structure.
+ * \param[in] registrar Who registered this node.
+ * \retval NULL on error.
+ * \retval A newly allocated child in parent.
+ */
+static struct data_node_provider *data_node_provider_add_child(struct ao2_container *parent,
+ const char *name, const struct ast_data_handler *handler, const char *registrar)
+{
+ struct data_node_provider *child;
+
+ child = data_node_provider_new(name, handler, registrar);
+ if (!child) {
+ return NULL;
+ }
+
+ ao2_link(parent, child);
+
+ return child;
+}
+
+/*!
+ * \internal
+ * \brief Find a child node, based on his name.
+ * \param[in] parent Where to find the node.
+ * \param[in] name The node name to find.
+ * \param[in] registrar Also check if the node was being used by this registrar.
+ * \retval NULL if a node wasn't found.
+ * \retval The node found.
+ * \note Remember to decrement the ref count of the returned node after using it.
+ */
+static struct data_node_provider *data_find_child(struct ao2_container *parent, const char *name, const char *registrar)
+{
+ struct data_node_provider *find_node, *found;
+
+ /* XXX avoid allocating a new data node for searching... */
+ find_node = data_node_provider_new(name, NULL, NULL);
+ if (!find_node) {
+ return NULL;
+ }
+
+ found = ao2_find(parent, find_node, OBJ_POINTER);
+
+ /* free the created node used for searching. */
+ ao2_ref(find_node, -1);
+
+ if (found && found->registrar && registrar) {
+ if (strcmp(found->registrar, registrar)) {
+ /* if the name doesn't match, do not return this node. */
+ ast_log(LOG_DEBUG, "Registrar doesn't match, node was registered by '%s' "
+ "and we are searching for '%s'\n", found->registrar, registrar);
+ ao2_ref(found, -1);
+ return NULL;
+ }
+ }
+
+ return found;
+}
+
+/*!
+ * \internal
+ * \brief Release a group of nodes.
+ * \param[in] parent The parent node.
+ * \param[in] path The path of nodes to release.
+ * \param[in] registrar Who registered this node.
+ * \retval < 0 on error.
+ * \retval 0 on success.
+ * \see data_node_provider_create
+ */
+static int data_node_provider_release(struct ao2_container *parent, const char *path, const char *registrar)
+{
+ char *node_name, *rpath;
+ struct data_node_provider *child;
+ int ret = 0;
+
+ rpath = strdupa(path);
+ if (!rpath) {
+ return -1;
+ }
+
+ node_name = next_node_name(&rpath);
+ if (!node_name) {
+ return -1;
+ }
+
+ child = data_find_child(parent, node_name, registrar);
+ if (!child) {
+ return -1;
+ }
+
+ /* if this is not a terminal node. */
+ if (!child->handler && rpath) {
+ ret = data_node_provider_release(child->children, rpath, registrar);
+ }
+
+ /* if this node is empty, unlink it. */
+ if (!ret && !ao2_container_count(child->children)) {
+ ao2_unlink(parent, child);
+ }
+
+ ao2_ref(child, -1);
+
+ return ret;
+}
+
+/*!
+ * \internal
+ * \brief Release every node registered by 'registrar'.
+ * \param[in] parent The parent node.
+ * \param[in] registrar
+ * \see __ast_data_unregister
+ */
+static void data_node_provider_release_all(struct ao2_container *parent, const char *registrar)
+{
+ struct ao2_iterator i;
+ struct data_node_provider *node;
+
+ i = ao2_iterator_init(parent, 0);
+ while ((node = ao2_iterator_next(&i))) {
+ if (!node->handler) {
+ /* this is a non-terminal node, go inside it. */
+ data_node_provider_release_all(node->children, registrar);
+ if (!ao2_container_count(node->children)) {
+ /* if this node was left empty, unlink it. */
+ ao2_unlink(parent, node);
+ }
+ } else {
+ if (!strcmp(node->registrar, registrar)) {
+ /* if the registrars match, release it! */
+ ao2_unlink(parent, node);
+ }
+ }
+ ao2_ref(node, -1);
+ }
+
+}
+
+/*!
+ * \internal
+ * \brief Create the middle nodes for the specified path (asterisk/testnode1/childnode)
+ * \param[in] parent Where to add the middle nodes structure.
+ * \param[in] path The path of nodes to add.
+ * \param[in] registrar Who is trying to create this node provider.
+ * \retval NULL on error.
+ * \retval The created node.
+ * \see data_node_provider_release
+ */
+static struct data_node_provider *data_node_provider_create(struct ao2_container *parent, const char *path, const char *registrar)
+{
+ char *rpath, *node_name;
+ struct data_node_provider *child, *ret = NULL;
+
+ rpath = strdupa(path);
+ if (!rpath) {
+ return NULL;
+ }
+
+ node_name = next_node_name(&rpath);
+ if (!node_name) {
+ /* no more nodes to create. */
+ return NULL;
+ }
+
+ child = data_find_child(parent, node_name, registrar);
+
+ if (!child) {
+ /* nodes without handler are non-terminal nodes. */
+ child = data_node_provider_add_child(parent, node_name, NULL, registrar);
+ }
+
+ if (rpath) {
+ ret = data_node_provider_create(child->children, rpath, registrar);
+ if (ret) {
+ ao2_ref(child, -1);
+ }
+ }
+
+ return ret ? ret : child;
+}
+
+int __ast_data_register(const char *path, const struct ast_data_handler *handler, const char *registrar)
+{
+ struct data_node_provider *node;
+
+ if (!path) {
+ return -1;
+ }
+
+ /* create the node structure for the registered handler. */
+ data_write_lock();
+
+ node = data_node_provider_create(root_data, path, registrar);
+ if (!node) {
+ ast_log(LOG_ERROR, "Unable to create the specified path (%s) for '%s'.\n", path, registrar);
+ data_unlock();
+ return -1;
+ }
+
+ if (ao2_container_count(node->children) || node->handler) {
+ ast_log(LOG_ERROR, "The node '%s' was already registered. "
+ "We were unable to register '%s' for registrar '%s'.\n", node->name, path, registrar);
+ ao2_ref(node, -1);
+ data_unlock();
+ return -1;
+ }
+
+ /* add handler to that node. */
+ node->handler = handler;
+
+ ao2_ref(node, -1);
+
+ data_unlock();
+
+ return 0;
+}
+
+int __ast_data_register_multiple(const struct ast_data_entry *data_entries, size_t entries, const char *registrar)
+{
+ int i, res;
+
+ for (i = 0; i < entries; i++) {
+ res = __ast_data_register(data_entries[i].path, data_entries[i].handler, registrar);
+ if (res) {
+ /* unregister all the already registered nodes, and make this action atomic. */
+ while ((--i) >= 0) {
+ __ast_data_unregister(data_entries[i].path, registrar);
+ }
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int __ast_data_unregister(const char *path, const char *registrar)
+{
+ int ret = 0;
+
+ data_write_lock();
+ if (path) {
+ ret = data_node_provider_release(root_data, path, registrar);
+ } else {
+ data_node_provider_release_all(root_data, registrar);
+ }
+ data_unlock();
+
+ if (path && ret) {
+ ast_log(LOG_ERROR, "Unable to unregister '%s' for '%s'\n", path, registrar);
+ }
+
+ return ret;
+}
+
+static struct ast_data_node *test_data_provider(void)
+{
+ struct ast_data_node *res = NULL;
+
+ return res;
+}
+
+static const struct ast_data_handler test_provider = {
+ .read = test_data_provider
+};
+
+static const struct ast_data_entry test_providers[] = {
+ AST_DATA_ENTRY("asterisk/node1/node11/node111", &test_provider),
+ AST_DATA_ENTRY("asterisk/node1/node11/node112", &test_provider),
+ AST_DATA_ENTRY("asterisk/node1/node11/node113", &test_provider),
+};
+
+int ast_data_init(void)
+{
+ ast_rwlock_init(&root_data_lock);
+
+ if (!(root_data = ao2_container_alloc(NUM_DATA_NODE_BUCKETS,
+ data_node_provider_hash, data_node_provider_cmp))) {
+ return -1;
+ }
+
+ /* some tests */
+ ast_data_register_multiple(test_providers, ARRAY_LEN(test_providers));
+ ast_data_unregister(NULL);
+
+ return 0;
+}
Propchange: team/group/data_api_gsoc2009/main/data.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/group/data_api_gsoc2009/main/data.c
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/group/data_api_gsoc2009/main/data.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
More information about the asterisk-commits
mailing list