[svn-commits] eliel: branch group/data_api_gsoc2009 r196867 - in /team/group/data_api_gsoc2...

SVN commits to the Digium repositories svn-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 svn-commits mailing list