Merge "rtp_engine/res_rtp_asterisk: Fix RTP struct reentrancy crashes."
[asterisk/asterisk.git] / res / res_clialiases.c
index a20947b..1a2fc69 100644 (file)
  * CLI commands.
  */
 
+/*! \li \ref res_clialiases.c uses the configuration file \ref cli_aliases.conf
+ * \addtogroup configuration_file Configuration Files
+ */
 
-#include "asterisk.h"
+/*! 
+ * \page cli_aliases.conf cli_aliases.conf
+ * \verbinclude cli_aliases.conf.sample
+ */
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
 
 #include "asterisk/module.h"
 #include "asterisk/config.h"
@@ -46,7 +56,6 @@ struct cli_alias {
        struct ast_cli_entry cli_entry; /*!< Actual CLI structure used for this alias */
        char *alias;                    /*!< CLI Alias */
        char *real_cmd;                 /*!< Actual CLI command it is aliased to */
-       unsigned int marked:1;          /*!< Bit to indicate whether this CLI alias is marked for destruction or not */
 };
 
 static struct ao2_container *cli_aliases;
@@ -59,22 +68,32 @@ static int alias_hash_cb(const void *obj, const int flags)
 }
 
 /*! \brief Comparison function used for aliases */
-static int alias_cmp_cb(void *obj, void *arg, void *data, int flags)
+static int alias_cmp_cb(void *obj, void *arg, int flags)
 {
        const struct cli_alias *alias0 = obj, *alias1 = arg;
 
        return (alias0->cli_entry.command == alias1->cli_entry.command ? CMP_MATCH | CMP_STOP : 0);
 }
 
-/*! \brief Destruction function used for aliases */
-static void alias_destroy(void *obj)
+/*! \brief Callback for unregistering an alias */
+static int alias_unregister_cb(void *obj, void *arg, int flags)
 {
        struct cli_alias *alias = obj;
 
        /* Unregister the CLI entry from the core */
        ast_cli_unregister(&alias->cli_entry);
 
-       return;
+       /* We can determine if this worked or not by looking at the cli_entry itself */
+       return !alias->cli_entry.command ? CMP_MATCH : 0;
+}
+
+/*! \brief Callback for finding an alias based on name */
+static int alias_name_cb(void *obj, void *arg, int flags)
+{
+       struct cli_alias *alias = obj;
+       char *name = arg;
+
+       return !strcmp(alias->alias, name) ? CMP_MATCH | CMP_STOP : 0;
 }
 
 /*! \brief Function which passes through an aliased CLI command to the real one */
@@ -88,7 +107,7 @@ static char *cli_alias_passthrough(struct ast_cli_entry *e, int cmd, struct ast_
        const char *line;
 
        /* Try to find the alias based on the CLI entry */
-       if (!(alias = ao2_find(cli_aliases, &tmp, NULL, OBJ_POINTER))) {
+       if (!(alias = ao2_find(cli_aliases, &tmp, OBJ_POINTER))) {
                return 0;
        }
 
@@ -99,10 +118,12 @@ static char *cli_alias_passthrough(struct ast_cli_entry *e, int cmd, struct ast_
        case CLI_GENERATE:
                line = a->line;
                line += (strlen(alias->alias));
-               if (!ast_strlen_zero(a->word)) {
+               if (!strncasecmp(alias->alias, alias->real_cmd, strlen(alias->alias))) {
+                       generator = NULL;
+               } else if (!ast_strlen_zero(a->word)) {
                        struct ast_str *real_cmd = ast_str_alloca(strlen(alias->real_cmd) + strlen(line) + 1);
                        ast_str_append(&real_cmd, 0, "%s%s", alias->real_cmd, line);
-                       generator = ast_cli_generator(real_cmd->str, a->word, a->n);
+                       generator = ast_cli_generator(ast_str_buffer(real_cmd), a->word, a->n);
                } else {
                        generator = ast_cli_generator(alias->real_cmd, a->word, a->n);
                }
@@ -122,7 +143,7 @@ static char *cli_alias_passthrough(struct ast_cli_entry *e, int cmd, struct ast_
                        ast_str_append(&real_cmd, 0, " %s", a->argv[i - 1]);
                }
 
-               ast_cli_command(a->fd, real_cmd->str);
+               ast_cli_command(a->fd, ast_str_buffer(real_cmd));
        } else {
                ast_cli_command(a->fd, alias->real_cmd);
        }
@@ -153,10 +174,10 @@ static char *alias_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
        ast_cli(a->fd, FORMAT, "Alias Command", "Real Command");
 
        i = ao2_iterator_init(cli_aliases, 0);
-
        for (; (alias = ao2_iterator_next(&i)); ao2_ref(alias, -1)) {
                ast_cli(a->fd, FORMAT, alias->alias, alias->real_cmd);
        }
+       ao2_iterator_destroy(&i);
 
        return CLI_SUCCESS;
 #undef FORMAT
@@ -167,21 +188,6 @@ static struct ast_cli_entry cli_alias[] = {
        AST_CLI_DEFINE(alias_show, "Show CLI command aliases"),
 };
 
-/*! \brief Function called to mark an alias for destruction */
-static int alias_mark(void *obj, void *arg, void *data, int flags)
-{
-       struct cli_alias *alias = obj;
-       alias->marked = 1;
-       return 0;
-}
-
-/*! \brief Function called to see if an alias is marked for destruction */
-static int alias_marked(void *obj, void *arg, void *data, int flags)
-{
-       struct cli_alias *alias = obj;
-       return alias->marked ? CMP_MATCH : 0;
-}
-
 /*! \brief Function called to load or reload the configuration file */
 static void load_config(int reload)
 {
@@ -190,16 +196,16 @@ static void load_config(int reload)
        struct cli_alias *alias;
        struct ast_variable *v, *v1;
 
-       if (!(cfg = ast_config_load(config_file, config_flags))) {
+       if (!(cfg = ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
                ast_log(LOG_ERROR, "res_clialiases configuration file '%s' not found\n", config_file);
                return;
        } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
                return;
        }
 
-       /* Mark CLI aliases for pruning */
+       /* Destroy any existing CLI aliases */
        if (reload) {
-               ao2_callback(cli_aliases, OBJ_NODATA, alias_mark, NULL, NULL);
+               ao2_callback(cli_aliases, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, alias_unregister_cb, NULL);
        }
 
        for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
@@ -209,7 +215,16 @@ static void load_config(int reload)
                }
                /* Read in those there CLI aliases */
                for (v1 = ast_variable_browse(cfg, v->value); v1; v1 = v1->next) {
-                       if (!(alias = ao2_alloc((sizeof(*alias) + strlen(v1->name) + strlen(v1->value) + 2), alias_destroy))) {
+                       struct cli_alias *existing = ao2_callback(cli_aliases, 0, alias_name_cb, (char*)v1->name);
+
+                       if (existing) {
+                               ast_log(LOG_WARNING, "Alias '%s' could not be unregistered and has been retained\n",
+                                       existing->alias);
+                               ao2_ref(existing, -1);
+                               continue;
+                       }
+
+                       if (!(alias = ao2_alloc((sizeof(*alias) + strlen(v1->name) + strlen(v1->value) + 2), NULL))) {
                                continue;
                        }
                        alias->alias = ((char *) alias) + sizeof(*alias);
@@ -218,20 +233,18 @@ static void load_config(int reload)
                        strcpy(alias->real_cmd, v1->value);
                        alias->cli_entry.handler = cli_alias_passthrough;
                        alias->cli_entry.command = alias->alias;
-                       alias->cli_entry.usage = "Aliased CLI Command";
+                       alias->cli_entry.usage = "Aliased CLI Command\n";
 
-                       ast_cli_register(&alias->cli_entry);
+                       if (ast_cli_register(&alias->cli_entry)) {
+                               ao2_ref(alias, -1);
+                               continue;
+                       }
                        ao2_link(cli_aliases, alias);
-                       ast_verbose(VERBOSE_PREFIX_2 "Aliased CLI command '%s' to '%s'\n", v1->name, v1->value);
+                       ast_verb(2, "Aliased CLI command '%s' to '%s'\n", v1->name, v1->value);
                        ao2_ref(alias, -1);
                }
        }
 
-       /* Drop any CLI aliases that should no longer exist */
-       if (reload) {
-               ao2_callback(cli_aliases, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE , alias_marked, NULL, NULL);
-       }
-
        ast_config_destroy(cfg);
 
        return;
@@ -247,6 +260,13 @@ static int reload_module(void)
 /*! \brief Function called to unload the module */
 static int unload_module(void)
 {
+       ao2_callback(cli_aliases, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, alias_unregister_cb, NULL);
+
+       if (ao2_container_count(cli_aliases)) {
+               ast_log(LOG_ERROR, "Could not unregister all CLI aliases\n");
+               return -1;
+       }
+
        ao2_ref(cli_aliases, -1);
 
        ast_cli_unregister_multiple(cli_alias, ARRAY_LEN(cli_alias));
@@ -254,7 +274,16 @@ static int unload_module(void)
        return 0;
 }
 
-/*! \brief Function called to load the module */
+/*!
+ * \brief Load the module
+ *
+ * Module loading including tests for configuration or dependencies.
+ * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
+ * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
+ * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
+ * configuration file or other non-critical problem return 
+ * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
+ */
 static int load_module(void)
 {
        if (!(cli_aliases = ao2_container_alloc(MAX_ALIAS_BUCKETS, alias_hash_cb, alias_cmp_cb))) {
@@ -269,7 +298,8 @@ static int load_module(void)
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "CLI Aliases",
-               .load = load_module,
-               .unload = unload_module,
-               .reload = reload_module,
-               );
+       .support_level = AST_MODULE_SUPPORT_CORE,
+       .load = load_module,
+       .unload = unload_module,
+       .reload = reload_module,
+);