[Asterisk-code-review] xmldoc: Allow XML docs to be reloaded. (asterisk[20])

Friendly Automation asteriskteam at digium.com
Thu Dec 8 09:17:27 CST 2022


Friendly Automation has submitted this change. ( https://gerrit.asterisk.org/c/asterisk/+/19667 )

Change subject: xmldoc: Allow XML docs to be reloaded.
......................................................................

xmldoc: Allow XML docs to be reloaded.

The XML docs are currently only loaded on
startup with no way to update them during runtime.
This makes it impossible to load modules that
use ACO/Sorcery (which require documentation)
if they are added to the source tree and built while
Asterisk is running (e.g. external modules).

This adds a CLI command to reload the XML docs
during runtime so that documentation can be updated
without a full restart of Asterisk.

ASTERISK-30289 #close

Change-Id: I4f265b0e5517e757c5453a0f241201a5788d3a07
---
A doc/CHANGES-staging/xmldoc.txt
M main/config_options.c
M main/xmldoc.c
3 files changed, 131 insertions(+), 24 deletions(-)

Approvals:
  Joshua Colp: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved
  Friendly Automation: Approved for Submit




diff --git a/doc/CHANGES-staging/xmldoc.txt b/doc/CHANGES-staging/xmldoc.txt
new file mode 100644
index 0000000..50324e4
--- /dev/null
+++ b/doc/CHANGES-staging/xmldoc.txt
@@ -0,0 +1,5 @@
+Subject: xmldocs
+
+The XML documentation can now be reloaded without restarting
+Asterisk, which makes it possible to load new modules that
+enforce documentation without restarting Asterisk.
diff --git a/main/config_options.c b/main/config_options.c
index c59a8b5..d258f60 100644
--- a/main/config_options.c
+++ b/main/config_options.c
@@ -1099,7 +1099,9 @@
 	}
 
 	if (!(results = ast_xmldoc_query("/docs/configInfo[@name='%s']/configFile/configObject[@name='%s']", module, name))) {
-		ast_log(LOG_WARNING, "Cannot update type '%s' in module '%s' because it has no existing documentation!\n", name, module);
+		ast_log(LOG_WARNING, "Cannot update type '%s' in module '%s' because it has no existing documentation!"
+			" If this module was recently built, run 'xmldoc reload' to refresh documentation, then load the module again\n",
+			name, module);
 		return XMLDOC_STRICT ? -1 : 0;
 	}
 
diff --git a/main/xmldoc.c b/main/xmldoc.c
index f924717..2b75523 100644
--- a/main/xmldoc.c
+++ b/main/xmldoc.c
@@ -68,9 +68,8 @@
 /*!
  * \brief Container of documentation trees
  *
- * \note A RWLIST is a sufficient container type to use here for now.
- *       However, some changes will need to be made to implement ref counting
- *       if reload support is added in the future.
+ * \note A RWLIST is a sufficient container type to use, provided
+ *       the list lock is always held while there are references to the list.
  */
 static AST_RWLIST_HEAD_STATIC(xmldoc_tree, documentation_tree);
 
@@ -430,6 +429,8 @@
  *
  * \retval NULL on error.
  * \retval A node of type ast_xml_node.
+ *
+ * \note Must be called with a RDLOCK held on xmldoc_tree
  */
 static struct ast_xml_node *xmldoc_get_node(const char *type, const char *name, const char *module, const char *language)
 {
@@ -438,7 +439,6 @@
 	struct ast_xml_node *lang_match = NULL;
 	struct documentation_tree *doctree;
 
-	AST_RWLIST_RDLOCK(&xmldoc_tree);
 	AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
 		/* the core xml documents have priority over thirdparty document. */
 		node = ast_xml_get_root(doctree->doc);
@@ -497,7 +497,6 @@
 			break;
 		}
 	}
-	AST_RWLIST_UNLOCK(&xmldoc_tree);
 
 	return node;
 }
@@ -1253,13 +1252,18 @@
 char *ast_xmldoc_build_syntax(const char *type, const char *name, const char *module)
 {
 	struct ast_xml_node *node;
+	char *syntax;
 
+	AST_RWLIST_RDLOCK(&xmldoc_tree);
 	node = xmldoc_get_node(type, name, module, documentation_language);
 	if (!node) {
+		AST_RWLIST_UNLOCK(&xmldoc_tree);
 		return NULL;
 	}
 
-	return _ast_xmldoc_build_syntax(node, type, name);
+	syntax = _ast_xmldoc_build_syntax(node, type, name);
+	AST_RWLIST_UNLOCK(&xmldoc_tree);
+	return syntax;
 }
 
 /*!
@@ -1705,12 +1709,15 @@
 	}
 
 	/* get the application/function root node. */
+	AST_RWLIST_RDLOCK(&xmldoc_tree);
 	node = xmldoc_get_node(type, name, module, documentation_language);
 	if (!node || !ast_xml_node_get_children(node)) {
+		AST_RWLIST_UNLOCK(&xmldoc_tree);
 		return NULL;
 	}
 
 	output = _ast_xmldoc_build_seealso(node);
+	AST_RWLIST_UNLOCK(&xmldoc_tree);
 
 	return output;
 }
@@ -2077,18 +2084,23 @@
 char *ast_xmldoc_build_arguments(const char *type, const char *name, const char *module)
 {
 	struct ast_xml_node *node;
+	char *arguments;
 
 	if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
 		return NULL;
 	}
 
+	AST_RWLIST_RDLOCK(&xmldoc_tree);
 	node = xmldoc_get_node(type, name, module, documentation_language);
 
 	if (!node || !ast_xml_node_get_children(node)) {
+		AST_RWLIST_UNLOCK(&xmldoc_tree);
 		return NULL;
 	}
 
-	return _ast_xmldoc_build_arguments(node);
+	arguments = _ast_xmldoc_build_arguments(node);
+	AST_RWLIST_UNLOCK(&xmldoc_tree);
+	return arguments;
 }
 
 /*!
@@ -2192,20 +2204,27 @@
 static char *xmldoc_build_field(const char *type, const char *name, const char *module, const char *var, int raw)
 {
 	struct ast_xml_node *node;
+	char *field;
 
 	if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
 		ast_log(LOG_ERROR, "Tried to look in XML tree with faulty values.\n");
 		return NULL;
 	}
 
+	AST_RWLIST_RDLOCK(&xmldoc_tree);
 	node = xmldoc_get_node(type, name, module, documentation_language);
 
 	if (!node) {
-		ast_log(LOG_WARNING, "Couldn't find %s %s in XML documentation\n", type, name);
+		AST_RWLIST_UNLOCK(&xmldoc_tree);
+		ast_log(LOG_WARNING, "Couldn't find %s %s in XML documentation"
+			" If this module was recently built, run 'xmldoc reload' to refresh documentation\n",
+			type, name);
 		return NULL;
 	}
 
-	return _xmldoc_build_field(node, var, raw);
+	field = _xmldoc_build_field(node, var, raw);
+	AST_RWLIST_UNLOCK(&xmldoc_tree);
+	return field;
 }
 
 /*!
@@ -2465,18 +2484,23 @@
 struct ast_xml_doc_item *ast_xmldoc_build_list_responses(const char *type, const char *name, const char *module)
 {
 	struct ast_xml_node *node;
+	struct ast_xml_doc_item *responses;
 
 	if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
 		return NULL;
 	}
 
+	AST_RWLIST_RDLOCK(&xmldoc_tree);
 	node = xmldoc_get_node(type, name, module, documentation_language);
 
 	if (!node || !ast_xml_node_get_children(node)) {
+		AST_RWLIST_UNLOCK(&xmldoc_tree);
 		return NULL;
 	}
 
-	return xmldoc_build_list_responses(node);
+	responses = xmldoc_build_list_responses(node);
+	AST_RWLIST_UNLOCK(&xmldoc_tree);
+	return responses;
 }
 
 /*!
@@ -2530,18 +2554,23 @@
 struct ast_xml_doc_item *ast_xmldoc_build_final_response(const char *type, const char *name, const char *module)
 {
 	struct ast_xml_node *node;
+	static struct ast_xml_doc_item *response;
 
 	if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
 		return NULL;
 	}
 
+	AST_RWLIST_RDLOCK(&xmldoc_tree);
 	node = xmldoc_get_node(type, name, module, documentation_language);
 
 	if (!node || !ast_xml_node_get_children(node)) {
+		AST_RWLIST_UNLOCK(&xmldoc_tree);
 		return NULL;
 	}
 
-	return xmldoc_build_final_response(node);
+	response = xmldoc_build_final_response(node);
+	AST_RWLIST_UNLOCK(&xmldoc_tree);
+	return response;
 }
 
 struct ast_xml_xpath_results *__attribute__((format(printf, 1, 2))) ast_xmldoc_query(const char *fmt, ...)
@@ -2860,25 +2889,35 @@
 
 static struct ast_cli_entry cli_dump_xmldocs = AST_CLI_DEFINE(handle_dump_docs, "Dump the XML docs to the specified file");
 
-/*! \brief Close and unload XML documentation. */
-static void xmldoc_unload_documentation(void)
+static char *handle_reload_docs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static struct ast_cli_entry cli_reload_xmldocs = AST_CLI_DEFINE(handle_reload_docs, "Reload the XML docs");
+
+/*! \note Must be called with xmldoc_tree locked */
+static void xmldoc_purge_documentation(void)
 {
 	struct documentation_tree *doctree;
 
-	ast_cli_unregister(&cli_dump_xmldocs);
-
-	AST_RWLIST_WRLOCK(&xmldoc_tree);
 	while ((doctree = AST_RWLIST_REMOVE_HEAD(&xmldoc_tree, entry))) {
 		ast_free(doctree->filename);
 		ast_xml_close(doctree->doc);
 		ast_free(doctree);
 	}
+}
+
+/*! \brief Close and unload XML documentation. */
+static void xmldoc_unload_documentation(void)
+{
+	ast_cli_unregister(&cli_reload_xmldocs);
+	ast_cli_unregister(&cli_dump_xmldocs);
+
+	AST_RWLIST_WRLOCK(&xmldoc_tree);
+	xmldoc_purge_documentation();
 	AST_RWLIST_UNLOCK(&xmldoc_tree);
 
 	ast_xml_finish();
 }
 
-int ast_xmldoc_load_documentation(void)
+static int xmldoc_load_documentation(int first_time)
 {
 	struct ast_xml_node *root_node;
 	struct ast_xml_doc *tmpdoc;
@@ -2907,12 +2946,14 @@
 		ast_config_destroy(cfg);
 	}
 
-	/* initialize the XML library. */
-	ast_xml_init();
-
-	ast_cli_register(&cli_dump_xmldocs);
-	/* register function to be run when asterisk finish. */
-	ast_register_cleanup(xmldoc_unload_documentation);
+	if (first_time) {
+		/* initialize the XML library. */
+		ast_xml_init();
+		ast_cli_register(&cli_dump_xmldocs);
+		ast_cli_register(&cli_reload_xmldocs);
+		/* register function to be run when asterisk finish. */
+		ast_register_cleanup(xmldoc_unload_documentation);
+	}
 
 	globbuf.gl_offs = 0;    /* slots to reserve in gl_pathv */
 
@@ -2942,6 +2983,16 @@
 	ast_free(xmlpattern);
 
 	AST_RWLIST_WRLOCK(&xmldoc_tree);
+
+	if (!first_time) {
+		/* If we're reloading, purge the existing documentation.
+		 * We do this with the lock held so that if somebody
+		 * else tries to get documentation, there's no chance
+		 * of retrieiving it after we purged the old docs
+		 * but before we loaded the new ones. */
+		xmldoc_purge_documentation();
+	}
+
 	/* loop over expanded files */
 	for (i = 0; i < globbuf.gl_pathc; i++) {
 		/* check for duplicates (if we already [try to] open the same file. */
@@ -2993,4 +3044,31 @@
 	return 0;
 }
 
+int ast_xmldoc_load_documentation(void)
+{
+	return xmldoc_load_documentation(1);
+}
+
+static int xmldoc_reload_documentation(void)
+{
+	return xmldoc_load_documentation(0);
+}
+
+static char *handle_reload_docs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "xmldoc reload";
+		e->usage =
+			"Usage: xmldoc reload\n"
+			"  Reload XML documentation\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	xmldoc_reload_documentation();
+	return CLI_SUCCESS;
+}
+
 #endif /* AST_XML_DOCS */

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/19667
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: 20
Gerrit-Change-Id: I4f265b0e5517e757c5453a0f241201a5788d3a07
Gerrit-Change-Number: 19667
Gerrit-PatchSet: 2
Gerrit-Owner: N A <asterisk at phreaknet.org>
Gerrit-Reviewer: Friendly Automation
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at sangoma.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20221208/5c1490d5/attachment-0001.html>


More information about the asterisk-code-review mailing list