From 46ed6af9c29261232ced72196fd1cad942ce5a0a Mon Sep 17 00:00:00 2001
From: "Joshua C. Colp" <jcolp@sangoma.com>
Date: Wed, 10 Mar 2021 11:03:11 -0400
Subject: [PATCH] loader: Output warnings for deprecated modules.

Using the information from the MODULEINFO XML we can
now output useful information at the end of module
loading for deprecated modules. This includes the
version it was deprecated in, the version it will be
removed in, and the replacement if available.

ASTERISK-29339

Change-Id: I2080dab97d2186be94c421b41dabf6d79a11611a
---
 main/loader.c | 178 +++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 147 insertions(+), 31 deletions(-)

diff --git a/main/loader.c b/main/loader.c
index 033693e8b0..f06016c9fa 100644
--- a/main/loader.c
+++ b/main/loader.c
@@ -153,37 +153,7 @@ static unsigned int loader_ready;
 static struct ast_vector_string startup_errors;
 static struct ast_str *startup_error_builder;
 
-#if defined(HAVE_PERMANENT_DLOPEN)
-#define FIRST_DLOPEN 999
-
-struct ao2_container *info_list = NULL;
-
-struct info_list_obj {
-	const struct ast_module_info *info;
-	int dlopened;
-	char name[0];
-};
-
-static struct info_list_obj *info_list_obj_alloc(const char *name,
-	const struct ast_module_info *info)
-{
-	struct info_list_obj *new_entry;
-
-	new_entry = ao2_alloc(sizeof(*new_entry) + strlen(name) + 1, NULL);
-
-	if (!new_entry) {
-		return NULL;
-	}
-
-	strcpy(new_entry->name, name); /* SAFE */
-	new_entry->info = info;
-	new_entry->dlopened = FIRST_DLOPEN;
-
-	return new_entry;
-}
-
-AO2_STRING_FIELD_CMP_FN(info_list_obj, name)
-
+#if defined(HAVE_PERMANENT_DLOPEN) || defined(AST_XML_DOCS)
 static char *get_name_from_resource(const char *resource)
 {
 	int len;
@@ -219,6 +189,38 @@ static char *get_name_from_resource(const char *resource)
 	/* Unable to allocate memory. */
 	return NULL;
 }
+#endif
+
+#if defined(HAVE_PERMANENT_DLOPEN)
+#define FIRST_DLOPEN 999
+
+struct ao2_container *info_list = NULL;
+
+struct info_list_obj {
+	const struct ast_module_info *info;
+	int dlopened;
+	char name[0];
+};
+
+static struct info_list_obj *info_list_obj_alloc(const char *name,
+	const struct ast_module_info *info)
+{
+	struct info_list_obj *new_entry;
+
+	new_entry = ao2_alloc(sizeof(*new_entry) + strlen(name) + 1, NULL);
+
+	if (!new_entry) {
+		return NULL;
+	}
+
+	strcpy(new_entry->name, name); /* SAFE */
+	new_entry->info = info;
+	new_entry->dlopened = FIRST_DLOPEN;
+
+	return new_entry;
+}
+
+AO2_STRING_FIELD_CMP_FN(info_list_obj, name)
 
 static void manual_mod_reg(const void *lib, const char *resource)
 {
@@ -2341,6 +2343,16 @@ int load_modules(void)
 	int res = 0;
 	int modulecount = 0;
 	int i;
+	struct ast_module *cur;
+#ifdef AST_XML_DOCS
+	struct ast_str *warning_msg;
+	char deprecated_in[33];
+	char removed_in[33];
+	char replacement[129];
+#endif
+	struct timeval start_time = ast_tvnow();
+	struct timeval end_time;
+	int64_t usElapsed;
 
 	ast_verb(1, "Asterisk Dynamic Loader Starting:\n");
 
@@ -2388,8 +2400,103 @@ done:
 		ast_free(order);
 	}
 
+#ifdef AST_XML_DOCS
+	warning_msg = ast_str_create(512);
+#endif
+
+	AST_DLLIST_TRAVERSE(&module_list, cur, entry) {
+#ifdef AST_XML_DOCS
+		char *mod_name = NULL;
+		struct ast_xml_xpath_results *results;
+#endif
+
+		if (!cur->flags.running || cur->flags.declined) {
+			continue;
+		}
+
+#ifdef AST_XML_DOCS
+		mod_name = get_name_from_resource(cur->resource);
+		if (!warning_msg || !mod_name) {
+			/* If we can't allocate memory, we have bigger issues */
+			ast_free(mod_name);
+			continue;
+		}
+
+		results = ast_xmldoc_query("/docs/module[@name='%s']/deprecated_in", mod_name);
+		deprecated_in[0] = '\0';
+		if (results) {
+			const char *result_tmp = ast_xml_get_text(ast_xml_xpath_get_first_result(results));
+			if (!ast_strlen_zero(result_tmp)) {
+				ast_copy_string(deprecated_in, result_tmp, sizeof(deprecated_in));
+			}
+			ast_xml_xpath_results_free(results);
+		}
+
+		results = ast_xmldoc_query("/docs/module[@name='%s']/removed_in", mod_name);
+		removed_in[0] = '\0';
+		if (results) {
+			const char *result_tmp = ast_xml_get_text(ast_xml_xpath_get_first_result(results));
+			if (!ast_strlen_zero(result_tmp)) {
+				ast_copy_string(removed_in, result_tmp, sizeof(removed_in));
+			}
+			ast_xml_xpath_results_free(results);
+		}
+
+		results = ast_xmldoc_query("/docs/module[@name='%s']/replacement", mod_name);
+		replacement[0] = '\0';
+		if (results) {
+			const char *result_tmp = ast_xml_get_text(ast_xml_xpath_get_first_result(results));
+			if (!ast_strlen_zero(result_tmp)) {
+				ast_copy_string(replacement, result_tmp, sizeof(replacement));
+			}
+			ast_xml_xpath_results_free(results);
+		}
+
+		ast_str_reset(warning_msg);
+
+		if (cur->info->support_level == AST_MODULE_SUPPORT_DEPRECATED || !ast_strlen_zero(deprecated_in)
+			|| !ast_strlen_zero(removed_in) || !ast_strlen_zero(replacement)) {
+			int already_butted = 0;
+
+			ast_str_append(&warning_msg, -1, "Module '%s' has been loaded", mod_name);
+			if (!ast_strlen_zero(deprecated_in)) {
+				ast_str_append(&warning_msg, -1, " but %s deprecated in Asterisk version %s",
+					 cur->info->support_level == AST_MODULE_SUPPORT_DEPRECATED ? "was" : "will be", deprecated_in);
+				already_butted = 1;
+			}
+
+			if (!ast_strlen_zero(removed_in)) {
+				ast_str_append(&warning_msg, -1, " %s will be removed in Asterisk version %s", already_butted ? "and" : "but", removed_in);
+			} else {
+				ast_str_append(&warning_msg, -1, " %s may be removed in a future release", already_butted ? "and" : "but");
+			}
+
+			ast_str_append(&warning_msg, -1, ".");
+
+			if (!ast_strlen_zero(replacement)) {
+				ast_str_append(&warning_msg, -1, " Its replacement is '%s'.", replacement);
+			}
+		}
+
+		if (ast_str_strlen(warning_msg)) {
+			ast_log(LOG_WARNING, "%s\n", ast_str_buffer(warning_msg));
+		}
+
+		ast_free(mod_name);
+#else
+		if (cur->info->support_level == AST_MODULE_SUPPORT_DEPRECATED) {
+			ast_log(LOG_WARNING, "The deprecated module '%s' has been loaded and is running, it may be removed in a future version\n", cur->resource);
+		}
+#endif
+	}
+
+#ifdef AST_XML_DOCS
+	ast_free(warning_msg);
+#endif
+
 	AST_DLLIST_UNLOCK(&module_list);
 
+
 	for (i = 0; i < AST_VECTOR_SIZE(&startup_errors); i++) {
 		char *str = AST_VECTOR_GET(&startup_errors, i);
 
@@ -2401,6 +2508,15 @@ done:
 	ast_free(startup_error_builder);
 	startup_error_builder = NULL;
 
+	end_time = ast_tvnow();
+	usElapsed = ast_tvdiff_us(end_time, start_time);
+
+#ifdef AST_XML_DOCS
+	ast_debug(1, "Loader time with AST_XML_DOCS: %ld.%06ld\n", usElapsed / 1000000, usElapsed % 1000000);
+#else
+	ast_debug(1, "Loader time without AST_XML_DOCS: %ld.%06ld\n", usElapsed / 1000000, usElapsed % 1000000);
+#endif
+
 	return res;
 }
 
-- 
GitLab