Merge "CLI: Fix 'core set debug channel' completion bug."
authorJenkins2 <jenkins2@gerrit.asterisk.org>
Mon, 18 Dec 2017 22:01:11 +0000 (16:01 -0600)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Mon, 18 Dec 2017 22:01:11 +0000 (16:01 -0600)
28 files changed:
CHANGES
apps/app_agent_pool.c
apps/app_skel.c
apps/app_transfer.c
apps/confbridge/conf_config_parser.c
channels/chan_motif.c
channels/chan_pjsip.c
channels/chan_sip.c
configs/samples/confbridge.conf.sample
include/asterisk/config_options.h
include/asterisk/module.h
main/asterisk.c
main/cdr.c
main/cel.c
main/config_options.c
main/features_config.c
main/loader.c
main/named_acl.c
main/stasis.c
main/udptl.c
res/ari/config.c
res/res_hep.c
res/res_parking.c
res/res_pjsip_notify.c
res/res_resolver_unbound.c
res/res_statsd.c
res/res_xmpp.c
tests/test_config.c

diff --git a/CHANGES b/CHANGES
index f367f46..bd1ca67 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -50,6 +50,11 @@ Core
    Asterisk is compiled with the LOW_MEMORY compile time option enabled because
    the cache code does not exist.
 
+chan_sip
+------------------
+ * Calls to invalid extensions are now reported as an ACL failure security event
+   "no_extension_match".
+
 res_rtp_asterisk
 ------------------
  * The X.509 certificate used for DTLS negotation can now be automatically
index 6a8c395..3c2ea38 100644 (file)
@@ -455,11 +455,17 @@ struct agents_cfg {
        struct ao2_container *agents;
 };
 
+static const char *agent_type_blacklist[] = {
+       "general",
+       "agents",
+       NULL,
+};
+
 static struct aco_type agent_type = {
        .type = ACO_ITEM,
        .name = "agent-id",
-       .category_match = ACO_BLACKLIST,
-       .category = "^(general|agents)$",
+       .category_match = ACO_BLACKLIST_ARRAY,
+       .category = (const char *)agent_type_blacklist,
        .item_alloc = agent_cfg_alloc,
        .item_find = agent_cfg_find,
        .item_offset = offsetof(struct agents_cfg, agents),
@@ -471,8 +477,8 @@ static struct aco_type *agent_types[] = ACO_TYPES(&agent_type);
 static struct aco_type general_type = {
        .type = ACO_GLOBAL,
        .name = "global",
-       .category_match = ACO_WHITELIST,
-       .category = "^general$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "general",
 };
 
 static struct aco_file agents_conf = {
index b3c8876..68f313f 100644 (file)
@@ -242,8 +242,8 @@ static struct aco_type global_option = {
        .type = ACO_GLOBAL,
        .name = "globals",
        .item_offset = offsetof(struct skel_config, global),
-       .category_match = ACO_WHITELIST,
-       .category = "^general$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "general",
 };
 
 struct aco_type *global_options[] = ACO_TYPES(&global_option);
@@ -253,18 +253,24 @@ static struct aco_type sound_option = {
        .type = ACO_GLOBAL,
        .name = "sounds",
        .item_offset = offsetof(struct skel_config, global),
-       .category_match = ACO_WHITELIST,
-       .category = "^sounds$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "sounds",
 };
 
 struct aco_type *sound_options[] = ACO_TYPES(&sound_option);
 
+static const char *level_categories[] = {
+       "general",
+       "sounds",
+       NULL,
+};
+
 /*! \brief An aco_type structure to link the everything but the "general" and "sounds" categories to the skel_level type */
 static struct aco_type level_option = {
        .type = ACO_ITEM,
        .name = "level",
-       .category_match = ACO_BLACKLIST,
-       .category = "^(general|sounds)$",
+       .category_match = ACO_BLACKLIST_ARRAY,
+       .category = (const char *)level_categories,
        .item_alloc = skel_level_alloc,
        .item_find = skel_level_find,
        .item_offset = offsetof(struct skel_config, levels),
index 7bc67e1..1d7c879 100644 (file)
@@ -51,7 +51,7 @@
                </syntax>
                <description>
                        <para>Requests the remote caller be transferred
-                       to a given destination. If TECH (SIP, IAX2, LOCAL etc) is used, only
+                       to a given destination. If TECH (SIP, IAX2, etc) is used, only
                        an incoming call with the same channel technology will be transferred.
                        Note that for SIP, if you transfer before call is setup, a 302 redirect
                        SIP message will be returned to the caller.</para>
index 6659305..99eea0a 100644 (file)
                                                regardless if this limit is reached or not.
                                        </para></description>
                                </configOption>
-                               <configOption name="^sound_">
+                               <configOption name="sound_">
                                        <synopsis>Override the various conference bridge sound files</synopsis>
                                        <description><para>
                                                All sounds in the conference are customizable using the bridge profile options below.
@@ -639,8 +639,8 @@ static void *bridge_profile_find(struct ao2_container *container, const char *ca
 static struct aco_type bridge_type = {
        .type = ACO_ITEM,
        .name = "bridge_profile",
-       .category_match = ACO_BLACKLIST,
-       .category = "^general$",
+       .category_match = ACO_BLACKLIST_EXACT,
+       .category = "general",
        .matchfield = "type",
        .matchvalue = "bridge",
        .item_alloc = bridge_profile_alloc,
@@ -676,8 +676,8 @@ static void *user_profile_find(struct ao2_container *container, const char *cate
 static struct aco_type user_type = {
        .type = ACO_ITEM,
        .name  = "user_profile",
-       .category_match = ACO_BLACKLIST,
-       .category = "^general$",
+       .category_match = ACO_BLACKLIST_EXACT,
+       .category = "general",
        .matchfield = "type",
        .matchvalue = "user",
        .item_alloc = user_profile_alloc,
@@ -707,8 +707,8 @@ static void *menu_find(struct ao2_container *container, const char *category)
 static struct aco_type menu_type = {
        .type = ACO_ITEM,
        .name = "menu",
-       .category_match = ACO_BLACKLIST,
-       .category = "^general$",
+       .category_match = ACO_BLACKLIST_EXACT,
+       .category = "general",
        .matchfield = "type",
        .matchvalue = "menu",
        .item_alloc = menu_alloc,
@@ -725,8 +725,8 @@ static struct aco_type *user_types[] = ACO_TYPES(&user_type);
 static struct aco_type general_type = {
        .type = ACO_GLOBAL,
        .name = "global",
-       .category_match = ACO_WHITELIST,
-       .category = "^general$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "general",
 };
 
 static struct aco_file confbridge_conf = {
@@ -2235,7 +2235,7 @@ int conf_load_config(void)
        aco_option_register(&cfg_info, "record_command", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, rec_command));
        aco_option_register(&cfg_info, "regcontext", ACO_EXACT, bridge_types, NULL, OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, regcontext));
        aco_option_register(&cfg_info, "language", ACO_EXACT, bridge_types, "en", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, language));
-       aco_option_register_custom(&cfg_info, "^sound_", ACO_REGEX, bridge_types, NULL, sound_option_handler, 0);
+       aco_option_register_custom(&cfg_info, "sound_", ACO_PREFIX, bridge_types, NULL, sound_option_handler, 0);
        aco_option_register(&cfg_info, "video_update_discard", ACO_EXACT, bridge_types, "2000", OPT_UINT_T, 0, FLDSET(struct bridge_profile, video_update_discard));
        /* This option should only be used with the CONFBRIDGE dialplan function */
        aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);
index 210cf36..6e8ce35 100644 (file)
@@ -541,8 +541,8 @@ static int jingle_endpoint_cmp(void *obj, void *arg, int flags)
 static struct aco_type endpoint_option = {
        .type = ACO_ITEM,
        .name = "endpoint",
-       .category_match = ACO_BLACKLIST,
-       .category = "^general$",
+       .category_match = ACO_BLACKLIST_EXACT,
+       .category = "general",
        .item_alloc = jingle_endpoint_alloc,
        .item_find = jingle_endpoint_find,
        .item_offset = offsetof(struct jingle_config, endpoints),
index 69bcc18..10f45a4 100644 (file)
@@ -2381,7 +2381,7 @@ static int request(void *obj)
        struct request_data *req_data = obj;
        struct ast_sip_session *session = NULL;
        char *tmp = ast_strdupa(req_data->dest), *endpoint_name = NULL, *request_user = NULL;
-       RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
+       struct ast_sip_endpoint *endpoint;
 
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(endpoint);
@@ -2406,10 +2406,18 @@ static int request(void *obj)
                }
 
                if (ast_strlen_zero(endpoint_name)) {
-                       ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name\n");
+                       if (request_user) {
+                               ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name: %s@<endpoint-name>\n",
+                                       request_user);
+                       } else {
+                               ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name\n");
+                       }
                        req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
                        return -1;
-               } else if (!(endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
+               }
+               endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
+                       endpoint_name);
+               if (!endpoint) {
                        ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n", endpoint_name);
                        req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;
                        return -1;
@@ -2421,23 +2429,38 @@ static int request(void *obj)
                        ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name\n");
                        req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
                        return -1;
-               } else if (!(endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
+               }
+               endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
+                       endpoint_name);
+               if (!endpoint) {
                        /* It seems it's not a multi-domain endpoint or single endpoint exact match,
                         * it's possible that it's a SIP trunk with a specified user (user@trunkname),
                         * so extract the user before @ sign.
                         */
-                       if ((endpoint_name = strchr(args.endpoint, '@'))) {
-                               request_user = args.endpoint;
-                               *endpoint_name++ = '\0';
+                       endpoint_name = strchr(args.endpoint, '@');
+                       if (!endpoint_name) {
+                               /*
+                                * Couldn't find an '@' so it had to be an endpoint
+                                * name that doesn't exist.
+                                */
+                               ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n",
+                                       args.endpoint);
+                               req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;
+                               return -1;
                        }
+                       request_user = args.endpoint;
+                       *endpoint_name++ = '\0';
 
                        if (ast_strlen_zero(endpoint_name)) {
-                               ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name\n");
+                               ast_log(LOG_ERROR, "Unable to create PJSIP channel with empty endpoint name: %s@<endpoint-name>\n",
+                                       request_user);
                                req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
                                return -1;
                        }
 
-                       if (!(endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
+                       endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
+                               endpoint_name);
+                       if (!endpoint) {
                                ast_log(LOG_ERROR, "Unable to create PJSIP channel - endpoint '%s' was not found\n", endpoint_name);
                                req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;
                                return -1;
@@ -2445,7 +2468,10 @@ static int request(void *obj)
                }
        }
 
-       if (!(session = ast_sip_session_create_outgoing(endpoint, NULL, args.aor, request_user, req_data->topology))) {
+       session = ast_sip_session_create_outgoing(endpoint, NULL, args.aor, request_user,
+               req_data->topology);
+       ao2_ref(endpoint, -1);
+       if (!session) {
                ast_log(LOG_ERROR, "Failed to create outgoing session to endpoint '%s'\n", endpoint_name);
                req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;
                return -1;
index 2d20442..e54997b 100644 (file)
@@ -26654,6 +26654,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
                                        ast_log(LOG_NOTICE, "Call from '%s' (%s) to extension"
                                                " '%s' rejected because extension not found in context '%s'.\n",
                                                S_OR(p->username, p->peername), ast_sockaddr_stringify(&p->recv), decoded_exten, p->context);
+                                       sip_report_failed_acl(p, "no_extension_match");
                                }
                                break;
                        case SIP_GET_DEST_REFUSED:
index 9cf86aa..c8f30bc 100644 (file)
@@ -243,8 +243,8 @@ type=bridge
                 ; left the conference. This is used for user intros.
                 ; Example "_____ has left the conference"
 ;sound_kicked ; The sound played to a user who has been kicked from the conference.
-;sound_muted  ; The sound played when the mute option it toggled on.
-;sound_unmuted  ; The sound played when the mute option it toggled off.
+;sound_muted  ; The sound played when the mute option is toggled on using DTMF menu.
+;sound_unmuted  ; The sound played when the mute option is toggled off using DTMF menu.
 ;sound_only_person ; The sound played when the user is the only person in the conference.
 ;sound_only_one ; The sound played to a user when there is only one other
                 ; person is in the conference.
index f4c3db1..3227f94 100644 (file)
@@ -40,18 +40,30 @@ struct aco_type_internal;
 enum aco_type_t {
        ACO_GLOBAL,
        ACO_ITEM,
+       ACO_IGNORE,
 };
 
-/*! \brief Whether a category regex is a blackist or a whitelist */
+/*! Type of category matching to perform */
 enum aco_category_op {
+       /*! Regex based blacklist. */
        ACO_BLACKLIST = 0,
+       /*! Regex based whitelist. */
        ACO_WHITELIST,
+       /*! Blacklist with a single string matched with strcasecmp. */
+       ACO_BLACKLIST_EXACT,
+       /*! Whitelist with a single string matched with strcasecmp. */
+       ACO_WHITELIST_EXACT,
+       /*! Blacklist with a NULL terminated array of strings matched with strcasecmp. */
+       ACO_BLACKLIST_ARRAY,
+       /*! Whitelist with a NULL terminated array of strings matched with strcasecmp. */
+       ACO_WHITELIST_ARRAY,
 };
 
 /*! \brief What kind of matching should be done on an option name */
 enum aco_matchtype {
        ACO_EXACT = 1,
        ACO_REGEX,
+       ACO_PREFIX,
 };
 
 /*! Callback functions for option parsing via aco_process_config() */
index 69ebb26..59509d9 100644 (file)
@@ -69,7 +69,7 @@ enum ast_module_load_result {
        AST_MODULE_LOAD_SUCCESS = 0,    /*!< Module loaded and configured */
        AST_MODULE_LOAD_DECLINE = 1,    /*!< Module is not configured */
        AST_MODULE_LOAD_SKIP = 2,       /*!< Module was skipped for some reason (For loader.c use only. Should never be returned by modules)*/
-       AST_MODULE_LOAD_PRIORITY = 3,   /*!< Module is not loaded yet, but is added to prioity heap */
+       AST_MODULE_LOAD_PRIORITY = 3,   /*!< Module is not loaded yet, but is added to priority list */
        AST_MODULE_LOAD_FAILURE = -1,   /*!< Module could not be loaded properly */
 };
 
index 86f252c..75d611f 100644 (file)
@@ -2391,52 +2391,6 @@ static int remoteconsolehandler(const char *s)
            (s[4] == '\0' || isspace(s[4]))) {
                quit_handler(0, SHUTDOWN_FAST, 0);
                ret = 1;
-       } else if (s[0]) {
-               char *shrunk = ast_strdupa(s);
-               char *cur;
-               char *prev;
-
-               /*
-                * Remove duplicate spaces from shrunk for matching purposes.
-                *
-                * shrunk has at least one character in it to start with or we
-                * couldn't get here.
-                */
-               for (prev = shrunk, cur = shrunk + 1; *cur; ++cur) {
-                       if (*prev == ' ' && *cur == ' ') {
-                               /* Skip repeated space delimiter. */
-                               continue;
-                       }
-                       *++prev = *cur;
-               }
-               *++prev = '\0';
-
-               if (strncasecmp(shrunk, "core set verbose ", 17) == 0) {
-                       /*
-                        * We need to still set the rasterisk option_verbose in case we are
-                        * talking to an earlier version which doesn't prefilter verbose
-                        * levels.  This is really a compromise as we should always take
-                        * whatever the server sends.
-                        */
-
-                       if (!strncasecmp(shrunk + 17, "off", 3)) {
-                               ast_verb_console_set(0);
-                       } else {
-                               int verbose_new;
-                               int atleast;
-
-                               atleast = 8;
-                               if (strncasecmp(shrunk + 17, "atleast ", atleast)) {
-                                       atleast = 0;
-                               }
-
-                               if (sscanf(shrunk + 17 + atleast, "%30d", &verbose_new) == 1) {
-                                       if (!atleast || ast_verb_console_get() < verbose_new) {
-                                               ast_verb_console_set(verbose_new);
-                                       }
-                               }
-                       }
-               }
        }
 
        return ret;
@@ -2760,6 +2714,9 @@ static void send_rasterisk_connect_commands(void)
                fdsend(ast_consock, buf);
        }
 
+       /* Leave verbose filtering to the server. */
+       option_verbose = INT_MAX;
+
        if (!ast_opt_mute) {
                fdsend(ast_consock, "logger mute silent");
        } else {
index 3681cdc..0f6a665 100644 (file)
@@ -238,8 +238,29 @@ static struct aco_type general_option = {
        .type = ACO_GLOBAL,
        .name = "general",
        .item_offset = offsetof(struct module_config, general),
-       .category = "^general$",
-       .category_match = ACO_WHITELIST,
+       .category = "general",
+       .category_match = ACO_WHITELIST_EXACT,
+};
+
+/*! Config sections used by existing modules. Do not add to this list. */
+static const char *ignore_categories[] = {
+       "csv",
+       "custom",
+       "manager",
+       "odbc",
+       "pgsql",
+       "radius",
+       "sqlite",
+       "tds",
+       "mysql",
+       NULL,
+};
+
+static struct aco_type ignore_option = {
+       .type = ACO_IGNORE,
+       .name = "modules",
+       .category = (const char*)ignore_categories,
+       .category_match = ACO_WHITELIST_ARRAY,
 };
 
 static void *module_config_alloc(void);
@@ -249,8 +270,7 @@ static void module_config_post_apply(void);
 /*! \brief The file definition */
 static struct aco_file module_file_conf = {
        .filename = "cdr.conf",
-       .skip_category = "(^csv$|^custom$|^manager$|^odbc$|^pgsql$|^radius$|^sqlite$|^tds$|^mysql$)",
-       .types = ACO_TYPES(&general_option),
+       .types = ACO_TYPES(&general_option, &ignore_option),
 };
 
 CONFIG_INFO_CORE("cdr", cfg_info, module_configs, module_config_alloc,
@@ -2457,12 +2477,12 @@ static void bridge_candidate_add_to_cdr(struct cdr_object *cdr,
  * \param cand_cdr The \ref cdr_object that is a candidate
  *
  */
-static int bridge_candidate_process(struct cdr_object *cdr, struct cdr_object *base_cand_cdr)
+static void bridge_candidate_process(struct cdr_object *cdr, struct cdr_object *base_cand_cdr)
 {
        struct cdr_object_snapshot *party_a;
        struct cdr_object *cand_cdr;
 
-       SCOPED_AO2LOCK(lock, base_cand_cdr);
+       ao2_lock(base_cand_cdr);
 
        for (cand_cdr = base_cand_cdr; cand_cdr; cand_cdr = cand_cdr->next) {
                /* Skip any records that are not in this bridge */
@@ -2474,7 +2494,7 @@ static int bridge_candidate_process(struct cdr_object *cdr, struct cdr_object *b
                if (!strcasecmp(cdr->party_a.snapshot->name, cand_cdr->party_a.snapshot->name)
                        || (cdr->party_b.snapshot
                                && !strcasecmp(cdr->party_b.snapshot->name, cand_cdr->party_a.snapshot->name))) {
-                       return 0;
+                       break;
                }
 
                party_a = cdr_object_pick_party_a(&cdr->party_a, &cand_cdr->party_a);
@@ -2482,7 +2502,7 @@ static int bridge_candidate_process(struct cdr_object *cdr, struct cdr_object *b
                 * Party B */
                if (!strcasecmp(party_a->snapshot->name, cdr->party_a.snapshot->name)) {
                        bridge_candidate_add_to_cdr(cdr, &cand_cdr->party_a);
-                       return 0;
+                       break;
                }
 
                /* We're Party B. Check if we can add ourselves immediately or if we need
@@ -2502,9 +2522,11 @@ static int bridge_candidate_process(struct cdr_object *cdr, struct cdr_object *b
                         */
                        memset(&cand_cdr->end, 0, sizeof(cand_cdr->end));
                }
-               return 0;
+
+               break;
        }
-       return 0;
+
+       ao2_unlock(base_cand_cdr);
 }
 
 /*!
@@ -2587,6 +2609,7 @@ static void handle_standard_bridge_enter_message(struct cdr_object *cdr,
 
        ao2_lock(cdr);
 
+try_again:
        for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
                if (it_cdr->fn_table->process_party_a) {
                        CDR_DEBUG("%p - Updating Party A %s snapshot\n", it_cdr,
@@ -2639,7 +2662,7 @@ static void handle_standard_bridge_enter_message(struct cdr_object *cdr,
                        /* This is guaranteed to succeed: the new CDR is created in the single state
                         * and will be able to handle the bridge enter message
                         */
-                       handle_standard_bridge_enter_message(cdr, bridge, channel);
+                       goto try_again;
                }
        }
        ao2_unlock(cdr);
index faf4fde..cec0a85 100644 (file)
@@ -242,15 +242,28 @@ static struct aco_type general_option = {
        .type = ACO_GLOBAL,
        .name = "general",
        .item_offset = offsetof(struct cel_config, general),
-       .category_match = ACO_WHITELIST,
-       .category = "^general$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "general",
+};
+
+/*! Config sections used by existing modules. Do not add to this list. */
+static const char *ignore_categories[] = {
+       "manager",
+       "radius",
+       NULL,
+};
+
+static struct aco_type ignore_option = {
+       .type = ACO_IGNORE,
+       .name = "modules",
+       .category = (const char*)ignore_categories,
+       .category_match = ACO_WHITELIST_ARRAY,
 };
 
 /*! \brief The config file to be processed for the module. */
 static struct aco_file cel_conf = {
        .filename = "cel.conf",                  /*!< The name of the config file */
-       .types = ACO_TYPES(&general_option),     /*!< The mapping object types to be processed */
-       .skip_category = "(^manager$|^radius$)", /*!< Config sections used by existing modules. Do not add to this list. */
+       .types = ACO_TYPES(&general_option, &ignore_option),     /*!< The mapping object types to be processed */
 };
 
 static int cel_pre_apply_config(void);
index a9a145b..1f4f99b 100644 (file)
@@ -131,7 +131,7 @@ static int noop_handler_fn(const struct aco_option *opt, struct ast_variable *va
 static int chararray_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
 
 #ifdef AST_XML_DOCS
-static int xmldoc_update_config_type(const char *module, const char *name, const char *category, const char *matchfield, const char *matchvalue, unsigned int matches);
+static int xmldoc_update_config_type(const char *module, const char *name, const char *category, const char *matchfield, const char *matchvalue, enum aco_category_op category_match);
 static int xmldoc_update_config_option(struct aco_type **types, const char *module, const char *name, const char *object_name, const char *default_value, unsigned int regex, enum aco_option_type type);
 #endif
 
@@ -373,6 +373,8 @@ static int find_option_cb(void *obj, void *arg, int flags)
        switch (match->match_type) {
        case ACO_EXACT:
                return strcasecmp(name, match->name) ? 0 : CMP_MATCH | CMP_STOP;
+       case ACO_PREFIX:
+               return strncasecmp(name, match->name, strlen(match->name)) ? 0 : CMP_MATCH | CMP_STOP;
        case ACO_REGEX:
                return regexec(match->name_regex, name, 0, NULL, 0) ? 0 : CMP_MATCH | CMP_STOP;
        }
@@ -402,6 +404,45 @@ struct ao2_container *aco_option_container_alloc(void)
        return ao2_container_alloc(CONFIG_OPT_BUCKETS, config_opt_hash, config_opt_cmp);
 }
 
+static int internal_aco_type_category_check(struct aco_type *match, const char *category)
+{
+       const char **categories = (const char **)match->category;
+
+       switch (match->category_match) {
+       case ACO_WHITELIST:
+               return regexec(match->internal->regex, category, 0, NULL, 0);
+
+       case ACO_BLACKLIST:
+               return !regexec(match->internal->regex, category, 0, NULL, 0);
+
+       case ACO_WHITELIST_EXACT:
+               return strcasecmp(match->category, category);
+
+       case ACO_BLACKLIST_EXACT:
+               return !strcasecmp(match->category, category);
+
+       case ACO_WHITELIST_ARRAY:
+               while (*categories) {
+                       if (!strcasecmp(*categories, category)) {
+                               return 0;
+                       }
+                       categories++;
+               }
+               return -1;
+
+       case ACO_BLACKLIST_ARRAY:
+               while (*categories) {
+                       if (!strcasecmp(*categories, category)) {
+                               return -1;
+                       }
+                       categories++;
+               }
+               return 0;
+       }
+
+       return -1;
+}
+
 static struct aco_type *internal_aco_type_find(struct aco_file *file, struct ast_config *cfg, const char *category)
 {
        size_t x;
@@ -410,7 +451,7 @@ static struct aco_type *internal_aco_type_find(struct aco_file *file, struct ast
 
        for (x = 0, match = file->types[x]; match; match = file->types[++x]) {
                /* First make sure we are an object that can service this category */
-               if (!regexec(match->internal->regex, category, 0, NULL, 0) == !match->category_match) {
+               if (internal_aco_type_category_check(match, category)) {
                        continue;
                }
 
@@ -483,6 +524,10 @@ static int process_category(struct ast_config *cfg, struct aco_info *info, struc
                return -1;
        }
 
+       if (type->type == ACO_IGNORE) {
+               return 0;
+       }
+
        field = info->internal->pending + type->item_offset;
        if (!*field) {
                ast_log(LOG_ERROR, "In %s: %s - No object to update!\n", file->filename, cat);
@@ -631,6 +676,10 @@ enum aco_process_status aco_process_config(struct aco_info *info, int reload)
                for (i = 0, match = file->types[i]; match; match = file->types[++i]) {
                        void **field = info->internal->pending + match->item_offset;
 
+                       if (match->type == ACO_IGNORE) {
+                               continue;
+                       }
+
                        if (match->type != ACO_GLOBAL || !*field) {
                                continue;
                        }
@@ -797,9 +846,19 @@ static int internal_type_init(struct aco_type *type)
                return -1;
        }
 
-       if (!(type->internal->regex = build_regex(type->category))) {
-               internal_type_destroy(type);
-               return -1;
+       switch (type->category_match) {
+       case ACO_BLACKLIST:
+       case ACO_WHITELIST:
+               if (!(type->internal->regex = build_regex(type->category))) {
+                       internal_type_destroy(type);
+                       return -1;
+               }
+               break;
+       case ACO_BLACKLIST_EXACT:
+       case ACO_WHITELIST_EXACT:
+       case ACO_BLACKLIST_ARRAY:
+       case ACO_WHITELIST_ARRAY:
+               break;
        }
 
        if (!(type->internal->opts = aco_option_container_alloc())) {
@@ -828,7 +887,8 @@ int aco_info_init(struct aco_info *info)
 #ifdef AST_XML_DOCS
                        if (!info->hidden &&
                                !type->hidden &&
-                               xmldoc_update_config_type(info->module, type->name, type->category, type->matchfield, type->matchvalue, type->category_match == ACO_WHITELIST)) {
+                               type->type != ACO_IGNORE &&
+                               xmldoc_update_config_type(info->module, type->name, type->category, type->matchfield, type->matchvalue, type->category_match)) {
                                goto error;
                        }
 #endif /* AST_XML_DOCS */
@@ -989,7 +1049,7 @@ static char *complete_config_option(const char *module, const char *option, cons
 /*! \internal
  * \brief Update the XML documentation for a config type based on its registration
  */
-static int xmldoc_update_config_type(const char *module, const char *name, const char *category, const char *matchfield, const char *matchvalue, unsigned int matches)
+static int xmldoc_update_config_type(const char *module, const char *name, const char *category, const char *matchfield, const char *matchvalue, enum aco_category_op category_match)
 {
        RAII_VAR(struct ast_xml_xpath_results *, results, NULL, ast_xml_xpath_results_free);
        RAII_VAR(struct ast_xml_doc_item *, config_info, ao2_find(xmldocs, module, OBJ_KEY), ao2_cleanup);
@@ -1028,7 +1088,18 @@ static int xmldoc_update_config_type(const char *module, const char *name, const
        }
 
        ast_xml_set_text(tmp, category);
-       ast_xml_set_attribute(tmp, "match", matches ? "true" : "false");
+       switch (category_match) {
+       case ACO_WHITELIST:
+       case ACO_WHITELIST_EXACT:
+       case ACO_WHITELIST_ARRAY:
+               ast_xml_set_attribute(tmp, "match", "true");
+               break;
+       case ACO_BLACKLIST:
+       case ACO_BLACKLIST_EXACT:
+       case ACO_BLACKLIST_ARRAY:
+               ast_xml_set_attribute(tmp, "match", "false");
+               break;
+       }
 
        if (!ast_strlen_zero(matchfield) && !(tmp = ast_xml_new_child(matchinfo, "field"))) {
                ast_log(LOG_WARNING, "Could not add %s attribute for type '%s' in module '%s'\n", matchfield, name, module);
index 72cd040..a773f49 100644 (file)
                                        The <replaceable>DYNAMIC_FEATURES</replaceable> is a <literal>#</literal> separated list of
                                        either applicationmap item names or featuregroup names.</para>
                                </description>
-                               <configOption name="^.*$" regex="true">
+                               <configOption name="">
                                        <synopsis>A custom feature to invoke during a bridged call</synopsis>
                                        <description>
                                                <para>Each item listed here is a comma-separated list of parameters that determine
                                        DTMF sequence used to invoke an applicationmap item to be overridden with
                                        a different sequence.</para>
                                </description>
-                               <configOption name="^.*$" regex="true">
+                               <configOption name="">
                                        <synopsis>Applicationmap item to place in the feature group</synopsis>
                                        <description>
                                                <para>Each item here must be a name of an item in the applicationmap. The
@@ -578,24 +578,24 @@ struct features_config {
 static struct aco_type global_option = {
        .type = ACO_GLOBAL,
        .name = "globals",
-       .category_match = ACO_WHITELIST,
-       .category = "^general$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "general",
        .item_offset = offsetof(struct features_config, global),
 };
 
 static struct aco_type featuremap_option = {
        .type = ACO_GLOBAL,
        .name = "featuremap",
-       .category_match = ACO_WHITELIST,
-       .category = "^featuremap$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "featuremap",
        .item_offset = offsetof(struct features_config, featuremap),
 };
 
 static struct aco_type applicationmap_option = {
        .type = ACO_GLOBAL,
        .name = "applicationmap",
-       .category_match = ACO_WHITELIST,
-       .category = "^applicationmap$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "applicationmap",
        .item_offset = offsetof(struct features_config, applicationmap),
 };
 
@@ -1851,13 +1851,13 @@ static int load_config(void)
        aco_option_register_custom(&cfg_info, "automixmon", ACO_EXACT, featuremap_options,
                        DEFAULT_FEATUREMAP_AUTOMIXMON, featuremap_handler, 0);
 
-       aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, applicationmap_options,
+       aco_option_register_custom(&cfg_info, "", ACO_PREFIX, applicationmap_options,
                        "", applicationmap_handler, 0);
 
-       aco_option_register_custom(&cfg_info, "^.*$", ACO_REGEX, featuregroup_options,
+       aco_option_register_custom(&cfg_info, "", ACO_PREFIX, featuregroup_options,
                        "", featuregroup_handler, 0);
 
-       aco_option_register_custom_nodoc(&cfg_info, "^.*$", ACO_REGEX, parkinglot_options,
+       aco_option_register_custom_nodoc(&cfg_info, "", ACO_PREFIX, parkinglot_options,
                        "", unsupported_handler, 0);
 
        if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
index e073365..8f65fb3 100644 (file)
@@ -52,7 +52,7 @@
 #include "asterisk/features_config.h"
 #include "asterisk/dsp.h"
 #include "asterisk/udptl.h"
-#include "asterisk/heap.h"
+#include "asterisk/vector.h"
 #include "asterisk/app.h"
 #include "asterisk/test.h"
 #include "asterisk/sounds_index.h"
@@ -110,6 +110,8 @@ static const unsigned char expected_key[] =
 
 static char buildopt_sum[33] = AST_BUILDOPT_SUM;
 
+AST_VECTOR(module_vector, struct ast_module *);
+
 /*!
  * \brief Internal flag to indicate all modules have been initially loaded.
  */
@@ -133,13 +135,34 @@ struct ast_module {
                /*! This module is being held open until it's time to shutdown. */
                unsigned int keepuntilshutdown:1;
        } flags;
-       AST_LIST_ENTRY(ast_module) list_entry;
        AST_DLLIST_ENTRY(ast_module) entry;
        char resource[0];
 };
 
 static AST_DLLIST_HEAD_STATIC(module_list, ast_module);
 
+static int module_vector_strcasecmp(struct ast_module *a, struct ast_module *b)
+{
+       return strcasecmp(a->resource, b->resource);
+}
+
+static int module_vector_cmp(struct ast_module *a, struct ast_module *b)
+{
+       /* if load_pri is not set, default is 128.  Lower is better */
+       int a_pri = ast_test_flag(a->info, AST_MODFLAG_LOAD_ORDER)
+               ? a->info->load_pri : AST_MODPRI_DEFAULT;
+       int b_pri = ast_test_flag(b->info, AST_MODFLAG_LOAD_ORDER)
+               ? b->info->load_pri : AST_MODPRI_DEFAULT;
+
+       /*
+        * Returns comparison values for a vector sorted by priority.
+        * <0 a_pri < b_pri
+        * =0 a_pri == b_pri
+        * >0 a_pri > b_pri
+        */
+       return a_pri - b_pri;
+}
+
 const char *ast_module_name(const struct ast_module *mod)
 {
        if (!mod || !mod->info) {
@@ -516,87 +539,93 @@ static void unload_dynamic_module(struct ast_module *mod)
 #endif
 }
 
-static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, unsigned int suppress_logging, struct ast_heap *resource_heap, int required);
-
 #define MODULE_LOCAL_ONLY (void *)-1
 
-static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only, unsigned int suppress_logging, struct ast_heap *resource_heap)
+/*!
+ * \internal
+ * \brief Attempt to dlopen a module.
+ *
+ * \param resource_in The module name to load.
+ * \param so_ext ".so" or blank if ".so" is already part of resource_in.
+ * \param filename Passed directly to dlopen.
+ * \param flags Passed directly to dlopen.
+ * \param suppress_logging Do not log any error from dlopen.
+ *
+ * \return Pointer to opened module, NULL on error.
+ *
+ * \warning module_list must be locked before calling this function.
+ */
+static struct ast_module *load_dlopen(const char *resource_in, const char *so_ext,
+       const char *filename, int flags, unsigned int suppress_logging)
 {
-       char fn[PATH_MAX] = "";
-       void *lib = NULL;
        struct ast_module *mod;
-       unsigned int wants_global;
-       int space;      /* room needed for the descriptor */
-       int missing_so = 0;
 
        ast_assert(!resource_being_loaded);
 
-       space = sizeof(*resource_being_loaded) + strlen(resource_in) + 1;
-       if (strcasecmp(resource_in + strlen(resource_in) - 3, ".so")) {
-               missing_so = 1;
-               space += 3;     /* room for the extra ".so" */
+       mod = ast_calloc(1, sizeof(*mod) + strlen(resource_in) + strlen(so_ext) + 1);
+       if (!mod) {
+               return NULL;
        }
 
-       snprintf(fn, sizeof(fn), "%s/%s%s", ast_config_AST_MODULE_DIR, resource_in, missing_so ? ".so" : "");
-
-       /* make a first load of the module in 'quiet' mode... don't try to resolve
-          any symbols, and don't export any symbols. this will allow us to peek into
-          the module's info block (if available) to see what flags it has set */
-
-       mod = resource_being_loaded = ast_calloc(1, space);
-       if (!resource_being_loaded)
-               return NULL;
-       strcpy(resource_being_loaded->resource, resource_in);
-       if (missing_so)
-               strcat(resource_being_loaded->resource, ".so");
+       sprintf(mod->resource, "%s%s", resource_in, so_ext); /* safe */
 
-       lib = dlopen(fn, RTLD_LAZY | RTLD_GLOBAL);
+       resource_being_loaded = mod;
+       mod->lib = dlopen(filename, flags);
        if (resource_being_loaded) {
                resource_being_loaded = NULL;
-               if (lib) {
+               if (mod->lib) {
                        ast_log(LOG_ERROR, "Module '%s' did not register itself during load\n", resource_in);
-                       logged_dlclose(resource_in, lib);
+                       logged_dlclose(resource_in, mod->lib);
                } else if (!suppress_logging) {
                        ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
                }
                ast_free(mod);
+
                return NULL;
        }
 
-       wants_global = ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS);
+       return mod;
+}
 
-       /* if we are being asked only to load modules that provide global symbols,
-          and this one does not, then close it and return */
-       if (global_symbols_only && !wants_global) {
-               logged_dlclose(resource_in, lib);
-               return MODULE_LOCAL_ONLY;
+static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only, unsigned int suppress_logging)
+{
+       char fn[PATH_MAX];
+       struct ast_module *mod;
+       size_t resource_in_len = strlen(resource_in);
+       int exports_globals;
+       const char *so_ext = "";
+
+       if (resource_in_len < 4 || strcasecmp(resource_in + resource_in_len - 3, ".so")) {
+               so_ext = ".so";
        }
 
-       logged_dlclose(resource_in, lib);
+       snprintf(fn, sizeof(fn), "%s/%s%s", ast_config_AST_MODULE_DIR, resource_in, so_ext);
 
-       /* start the load process again */
-       mod = resource_being_loaded = ast_calloc(1, space);
-       if (!resource_being_loaded)
-               return NULL;
-       strcpy(resource_being_loaded->resource, resource_in);
-       if (missing_so)
-               strcat(resource_being_loaded->resource, ".so");
+       /* Try loading in quiet mode first with flags to export global symbols.
+        * If the module does not want to export globals we will close and reopen. */
+       mod = load_dlopen(resource_in, so_ext, fn,
+               global_symbols_only ? RTLD_NOW | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL,
+               suppress_logging);
 
-       lib = dlopen(fn, wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL);
-       resource_being_loaded = NULL;
-       if (!lib) {
-               ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
-               ast_free(mod);
+       if (!mod) {
                return NULL;
        }
 
-       /* since the module was successfully opened, and it registered itself
-          the previous time we did that, we're going to assume it worked this
-          time too :) */
+       exports_globals = ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS);
+       if ((global_symbols_only && exports_globals) || (!global_symbols_only && !exports_globals)) {
+               /* The first dlopen had the correct flags. */
+               return mod;
+       }
 
-       AST_DLLIST_LAST(&module_list)->lib = lib;
+       /* Close the module so we can reopen with correct flags. */
+       logged_dlclose(resource_in, mod->lib);
+       if (global_symbols_only) {
+               return MODULE_LOCAL_ONLY;
+       }
 
-       return AST_DLLIST_LAST(&module_list);
+       return load_dlopen(resource_in, so_ext, fn,
+               exports_globals ? RTLD_NOW | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL,
+               0);
 }
 
 int modules_shutdown(void)
@@ -1126,13 +1155,13 @@ static enum ast_module_load_result start_resource(struct ast_module *mod)
 /*! loads a resource based upon resource_name. If global_symbols_only is set
  *  only modules with global symbols will be loaded.
  *
- *  If the ast_heap is provided (not NULL) the module is found and added to the
- *  heap without running the module's load() function.  By doing this, modules
- *  added to the resource_heap can be initialized later in order by priority.
+ *  If the module_vector is provided (not NULL) the module is found and added to the
+ *  vector without running the module's load() function.  By doing this, modules
+ *  can be initialized later in order by priority and dependencies.
  *
- *  If the ast_heap is not provided, the module's load function will be executed
+ *  If the module_vector is not provided, the module's load function will be executed
  *  immediately */
-static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, unsigned int suppress_logging, struct ast_heap *resource_heap, int required)
+static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, unsigned int suppress_logging, struct module_vector *resource_heap, int required)
 {
        struct ast_module *mod;
        enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS;
@@ -1145,7 +1174,7 @@ static enum ast_module_load_result load_resource(const char *resource_name, unsi
                if (global_symbols_only && !ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS))
                        return AST_MODULE_LOAD_SKIP;
        } else {
-               mod = load_dynamic_module(resource_name, global_symbols_only, suppress_logging, resource_heap);
+               mod = load_dynamic_module(resource_name, global_symbols_only, suppress_logging);
                if (mod == MODULE_LOCAL_ONLY) {
                                return AST_MODULE_LOAD_SKIP;
                }
@@ -1158,21 +1187,26 @@ static enum ast_module_load_result load_resource(const char *resource_name, unsi
        }
 
        if (inspect_module(mod)) {
-               ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
-               unload_dynamic_module(mod);
-               return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE;
+               goto prestart_error;
        }
 
        mod->flags.declined = 0;
 
        if (resource_heap) {
-               ast_heap_push(resource_heap, mod);
+               if (AST_VECTOR_ADD_SORTED(resource_heap, mod, module_vector_cmp)) {
+                       goto prestart_error;
+               }
                res = AST_MODULE_LOAD_PRIORITY;
        } else {
                res = start_resource(mod);
        }
 
        return res;
+
+prestart_error:
+       ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
+       unload_dynamic_module(mod);
+       return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE;
 }
 
 int ast_load_resource(const char *resource_name)
@@ -1220,23 +1254,6 @@ static struct load_order_entry *add_to_load_order(const char *resource, struct l
        return order;
 }
 
-static int mod_load_cmp(void *a, void *b)
-{
-       struct ast_module *a_mod = (struct ast_module *) a;
-       struct ast_module *b_mod = (struct ast_module *) b;
-       /* if load_pri is not set, default is 128.  Lower is better */
-       int a_pri = ast_test_flag(a_mod->info, AST_MODFLAG_LOAD_ORDER) ? a_mod->info->load_pri : 128;
-       int b_pri = ast_test_flag(b_mod->info, AST_MODFLAG_LOAD_ORDER) ? b_mod->info->load_pri : 128;
-
-       /*
-        * Returns comparison values for a min-heap
-        * <0 a_pri > b_pri
-        * =0 a_pri == b_pri
-        * >0 a_pri < b_pri
-        */
-       return b_pri - a_pri;
-}
-
 AST_LIST_HEAD_NOLOCK(load_retries, load_order_entry);
 
 /*! loads modules in order by load_pri, updates mod_count
@@ -1244,9 +1261,8 @@ AST_LIST_HEAD_NOLOCK(load_retries, load_order_entry);
 */
 static int load_resource_list(struct load_order *load_order, unsigned int global_symbols, int *mod_count)
 {
-       struct ast_heap *resource_heap;
+       struct module_vector resource_heap;
        struct load_order_entry *order;
-       struct ast_module *mod;
        struct load_retries load_retries;
        int count = 0;
        int res = 0;
@@ -1255,7 +1271,9 @@ static int load_resource_list(struct load_order *load_order, unsigned int global
 
        AST_LIST_HEAD_INIT_NOLOCK(&load_retries);
 
-       if(!(resource_heap = ast_heap_create(8, mod_load_cmp, -1))) {
+       if (AST_VECTOR_INIT(&resource_heap, 500)) {
+               ast_log(LOG_ERROR, "Failed to initialize module loader.\n");
+
                return -1;
        }
 
@@ -1264,7 +1282,7 @@ static int load_resource_list(struct load_order *load_order, unsigned int global
                enum ast_module_load_result lres;
 
                /* Suppress log messages unless this is the last pass */
-               lres = load_resource(order->resource, global_symbols, 1, resource_heap, order->required);
+               lres = load_resource(order->resource, global_symbols, 1, &resource_heap, order->required);
                ast_debug(3, "PASS 0: %-46s %d %d\n", order->resource, lres, global_symbols);
                switch (lres) {
                case AST_MODULE_LOAD_SUCCESS:
@@ -1288,7 +1306,7 @@ static int load_resource_list(struct load_order *load_order, unsigned int global
                         */
                        break;
                case AST_MODULE_LOAD_PRIORITY:
-                       /* load_resource worked and the module was added to the priority heap */
+                       /* load_resource worked and the module was added to the priority vector */
                        AST_LIST_REMOVE_CURRENT(entry);
                        ast_free(order->resource);
                        ast_free(order);
@@ -1303,7 +1321,7 @@ static int load_resource_list(struct load_order *load_order, unsigned int global
                        enum ast_module_load_result lres;
 
                        /* Suppress log messages unless this is the last pass */
-                       lres = load_resource(order->resource, global_symbols, (i < LOAD_RETRIES - 1), resource_heap, order->required);
+                       lres = load_resource(order->resource, global_symbols, (i < LOAD_RETRIES - 1), &resource_heap, order->required);
                        ast_debug(3, "PASS %d %-46s %d %d\n", i + 1, order->resource, lres, global_symbols);
                        switch (lres) {
                        /* These are all retryable. */
@@ -1341,7 +1359,8 @@ static int load_resource_list(struct load_order *load_order, unsigned int global
        }
 
        /* second remove modules from heap sorted by priority */
-       while ((mod = ast_heap_pop(resource_heap))) {
+       for (i = 0; i < AST_VECTOR_SIZE(&resource_heap); i++) {
+               struct ast_module *mod = AST_VECTOR_GET(&resource_heap, i);
                enum ast_module_load_result lres;
 
                lres = start_resource(mod);
@@ -1371,7 +1390,7 @@ done:
        if (mod_count) {
                *mod_count += count;
        }
-       ast_heap_destroy(resource_heap);
+       AST_VECTOR_FREE(&resource_heap);
 
        return res;
 }
@@ -1509,33 +1528,57 @@ void ast_update_use_count(void)
        AST_LIST_UNLOCK(&updaters);
 }
 
+/*!
+ * \internal
+ * \brief Build an alpha sorted list of modules.
+ *
+ * \param alpha_module_list Pointer to uninitialized module_vector.
+ *
+ * This function always initializes alpha_module_list.
+ *
+ * \pre module_list must be locked.
+ */
+static int alpha_module_list_create(struct module_vector *alpha_module_list)
+{
+       struct ast_module *cur;
+
+       if (AST_VECTOR_INIT(alpha_module_list, 32)) {
+               return -1;
+       }
+
+       AST_DLLIST_TRAVERSE(&module_list, cur, entry) {
+               if (AST_VECTOR_ADD_SORTED(alpha_module_list, cur, module_vector_strcasecmp)) {
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
 int ast_update_module_list(int (*modentry)(const char *module, const char *description,
                                            int usecnt, const char *status, const char *like,
                                                                                   enum ast_module_support_level support_level),
                            const char *like)
 {
-       struct ast_module *cur;
-       int unlock = -1;
        int total_mod_loaded = 0;
-       AST_LIST_HEAD_NOLOCK(, ast_module) alpha_module_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+       struct module_vector alpha_module_list;
 
-       if (AST_DLLIST_TRYLOCK(&module_list)) {
-               unlock = 0;
-       }
+       AST_DLLIST_LOCK(&module_list);
 
-       AST_DLLIST_TRAVERSE(&module_list, cur, entry) {
-               AST_LIST_INSERT_SORTALPHA(&alpha_module_list, cur, list_entry, resource);
-       }
+       if (!alpha_module_list_create(&alpha_module_list)) {
+               int idx;
 
-       while ((cur = AST_LIST_REMOVE_HEAD(&alpha_module_list, list_entry))) {
-               total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount,
-                                               cur->flags.running ? "Running" : "Not Running", like, cur->info->support_level);
-       }
+               for (idx = 0; idx < AST_VECTOR_SIZE(&alpha_module_list); idx++) {
+                       struct ast_module *cur = AST_VECTOR_GET(&alpha_module_list, idx);
 
-       if (unlock) {
-               AST_DLLIST_UNLOCK(&module_list);
+                       total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount,
+                               cur->flags.running ? "Running" : "Not Running", like, cur->info->support_level);
+               }
        }
 
+       AST_DLLIST_UNLOCK(&module_list);
+       AST_VECTOR_FREE(&alpha_module_list);
+
        return total_mod_loaded;
 }
 
@@ -1545,22 +1588,24 @@ int ast_update_module_list_data(int (*modentry)(const char *module, const char *
                                                 void *data),
                                 const char *like, void *data)
 {
-       struct ast_module *cur;
        int total_mod_loaded = 0;
-       AST_LIST_HEAD_NOLOCK(, ast_module) alpha_module_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+       struct module_vector alpha_module_list;
 
        AST_DLLIST_LOCK(&module_list);
 
-       AST_DLLIST_TRAVERSE(&module_list, cur, entry) {
-               AST_LIST_INSERT_SORTALPHA(&alpha_module_list, cur, list_entry, resource);
-       }
+       if (!alpha_module_list_create(&alpha_module_list)) {
+               int idx;
 
-       while ((cur = AST_LIST_REMOVE_HEAD(&alpha_module_list, list_entry))) {
-               total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount,
-                       cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data);
+               for (idx = 0; idx < AST_VECTOR_SIZE(&alpha_module_list); idx++) {
+                       struct ast_module *cur = AST_VECTOR_GET(&alpha_module_list, idx);
+
+                       total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount,
+                               cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data);
+               }
        }
 
        AST_DLLIST_UNLOCK(&module_list);
+       AST_VECTOR_FREE(&alpha_module_list);
 
        return total_mod_loaded;
 }
@@ -1572,23 +1617,25 @@ int ast_update_module_list_condition(int (*modentry)(const char *module, const c
                                                      void *data, const char *condition),
                                      const char *like, void *data, const char *condition)
 {
-       struct ast_module *cur;
        int conditions_met = 0;
-       AST_LIST_HEAD_NOLOCK(, ast_module) alpha_module_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+       struct module_vector alpha_module_list;
 
        AST_DLLIST_LOCK(&module_list);
 
-       AST_DLLIST_TRAVERSE(&module_list, cur, entry) {
-               AST_LIST_INSERT_SORTALPHA(&alpha_module_list, cur, list_entry, resource);
-       }
+       if (!alpha_module_list_create(&alpha_module_list)) {
+               int idx;
 
-       while ((cur = AST_LIST_REMOVE_HEAD(&alpha_module_list, list_entry))) {
-               conditions_met += modentry(cur->resource, cur->info->description, cur->usecount,
-                       cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data,
-                       condition);
+               for (idx = 0; idx < AST_VECTOR_SIZE(&alpha_module_list); idx++) {
+                       struct ast_module *cur = AST_VECTOR_GET(&alpha_module_list, idx);
+
+                       conditions_met += modentry(cur->resource, cur->info->description, cur->usecount,
+                               cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data,
+                               condition);
+               }
        }
 
        AST_DLLIST_UNLOCK(&module_list);
+       AST_VECTOR_FREE(&alpha_module_list);
 
        return conditions_met;
 }
index 8b5aeda..393532a 100644 (file)
@@ -80,8 +80,8 @@ static void *named_acl_find(struct ao2_container *container, const char *cat);
 static struct aco_type named_acl_type = {
        .type = ACO_ITEM,                  /*!< named_acls are items stored in containers, not individual global objects */
        .name = "named_acl",
-       .category_match = ACO_BLACKLIST,
-       .category = "^general$",           /*!< Match everything but "general" */
+       .category_match = ACO_BLACKLIST_EXACT,
+       .category = "general",           /*!< Match everything but "general" */
        .item_alloc = named_acl_alloc,     /*!< A callback to allocate a new named_acl based on category */
        .item_find = named_acl_find,       /*!< A callback to find a named_acl in some container of named_acls */
        .item_offset = offsetof(struct named_acl_config, named_acl_list), /*!< Could leave this out since 0 */
index 186d88f..77bf340 100644 (file)
@@ -1434,8 +1434,8 @@ static struct aco_type threadpool_option = {
        .type = ACO_GLOBAL,
        .name = "threadpool",
        .item_offset = offsetof(struct stasis_config, threadpool_options),
-       .category = "^threadpool$",
-       .category_match = ACO_WHITELIST,
+       .category = "threadpool",
+       .category_match = ACO_WHITELIST_EXACT,
 };
 
 static struct aco_type *threadpool_options[] = ACO_TYPES(&threadpool_option);
@@ -1445,8 +1445,8 @@ static struct aco_type declined_option = {
        .type = ACO_GLOBAL,
        .name = "declined_message_types",
        .item_offset = offsetof(struct stasis_config, declined_message_types),
-       .category_match = ACO_WHITELIST,
-       .category = "^declined_message_types$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "declined_message_types",
 };
 
 struct aco_type *declined_options[] = ACO_TYPES(&declined_option);
index d982f6b..5a491e6 100644 (file)
@@ -237,9 +237,9 @@ static int udptl_pre_apply_config(void);
 static struct aco_type general_option = {
        .type = ACO_GLOBAL,
        .name = "global",
-       .category_match = ACO_WHITELIST,
+       .category_match = ACO_WHITELIST_EXACT,
        .item_offset = offsetof(struct udptl_config, general),
-       .category = "^general$",
+       .category = "general",
 };
 
 static struct aco_type *general_options[] = ACO_TYPES(&general_option);
index a080bb7..46d23c6 100644 (file)
@@ -39,8 +39,8 @@ static struct aco_type general_option = {
        .type = ACO_GLOBAL,
        .name = "general",
        .item_offset = offsetof(struct ast_ari_conf, general),
-       .category = "^general$",
-       .category_match = ACO_WHITELIST,
+       .category = "general",
+       .category_match = ACO_WHITELIST_EXACT,
 };
 
 static struct aco_type *general_options[] = ACO_TYPES(&general_option);
@@ -156,8 +156,8 @@ static void *user_find(struct ao2_container *tmp_container, const char *cat)
 static struct aco_type user_option = {
        .type = ACO_ITEM,
        .name = "user",
-       .category_match = ACO_BLACKLIST,
-       .category = "^general$",
+       .category_match = ACO_BLACKLIST_EXACT,
+       .category = "general",
        .matchfield = "type",
        .matchvalue = "user",
        .item_alloc = user_alloc,
index ba036d7..4e548e2 100644 (file)
@@ -258,8 +258,8 @@ static struct aco_type global_option = {
        .type = ACO_GLOBAL,
        .name = "general",
        .item_offset = offsetof(struct module_config, general),
-       .category_match = ACO_WHITELIST,
-       .category = "^general$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "general",
 };
 
 struct aco_type *global_options[] = ACO_TYPES(&global_option);
index 2dbe6f7..5d1b30c 100644 (file)
@@ -289,8 +289,8 @@ static struct aco_type global_option = {
        .type = ACO_GLOBAL,
        .name = "globals",
        .item_offset = offsetof(struct parking_config, global),
-       .category_match = ACO_WHITELIST,
-       .category = "^general$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "general",
 };
 
 struct aco_type *global_options[] = ACO_TYPES(&global_option);
@@ -298,8 +298,8 @@ struct aco_type *global_options[] = ACO_TYPES(&global_option);
 static struct aco_type parking_lot_type = {
        .type = ACO_ITEM,
        .name = "parking_lot",
-       .category_match = ACO_BLACKLIST,
-       .category = "^(general)$",
+       .category_match = ACO_BLACKLIST_EXACT,
+       .category = "general",
        .item_alloc = parking_lot_cfg_alloc,
        .item_find = named_item_find,
        .item_offset = offsetof(struct parking_config, parking_lots),
index 8de88c7..59b7c6e 100644 (file)
@@ -82,7 +82,7 @@
                                        order; any other header is treated as part of the SIP
                                        request.</para>
                                </description>
-                               <configOption name="^.*$">
+                               <configOption name="">
                                        <synopsis>A key/value pair to add to a NOTIFY request.</synopsis>
                                        <description>
                                                <para>If the key is <literal>Content</literal>,
@@ -234,8 +234,8 @@ static void *notify_cfg_alloc(void)
 static struct aco_type notify_option = {
        .type = ACO_ITEM,
        .name = "notify",
-       .category_match = ACO_BLACKLIST,
-       .category = "^general$",
+       .category_match = ACO_BLACKLIST_EXACT,
+       .category = "general",
        .item_offset = offsetof(struct notify_cfg, notify_options),
        .item_alloc = notify_option_alloc,
        .item_find = notify_option_find
@@ -993,7 +993,7 @@ static int load_module(void)
                return AST_MODULE_LOAD_DECLINE;
        }
 
-       aco_option_register_custom(&notify_cfg, "^.*$", ACO_REGEX, notify_options,
+       aco_option_register_custom(&notify_cfg, "", ACO_PREFIX, notify_options,
                                   "", notify_option_handler, 0);
 
        if (aco_process_config(&notify_cfg, 0)) {
index 3c78050..25f6150 100644 (file)
@@ -142,8 +142,8 @@ static struct aco_type global_option = {
        .type = ACO_GLOBAL,
        .name = "general",
        .item_offset = offsetof(struct unbound_config, global),
-       .category_match = ACO_WHITELIST,
-       .category = "^general$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "general",
 };
 
 static struct aco_type *global_options[] = ACO_TYPES(&global_option);
index aee0bcd..221b359 100644 (file)
@@ -231,8 +231,8 @@ static struct aco_type global_option = {
        .type = ACO_GLOBAL,
        .name = "global",
        .item_offset = offsetof(struct conf, global),
-       .category = "^general$",
-       .category_match = ACO_WHITELIST
+       .category = "general",
+       .category_match = ACO_WHITELIST_EXACT,
 };
 
 static struct aco_type *global_options[] = ACO_TYPES(&global_option);
index f683557..b72581f 100644 (file)
@@ -820,8 +820,8 @@ static struct aco_type global_option = {
        .type = ACO_GLOBAL,
        .name = "global",
        .item_offset = offsetof(struct xmpp_config, global),
-       .category_match = ACO_WHITELIST,
-       .category = "^general$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "general",
 };
 
 struct aco_type *global_options[] = ACO_TYPES(&global_option);
@@ -829,8 +829,8 @@ struct aco_type *global_options[] = ACO_TYPES(&global_option);
 static struct aco_type client_option = {
        .type = ACO_ITEM,
        .name = "client",
-       .category_match = ACO_BLACKLIST,
-       .category = "^(general)$",
+       .category_match = ACO_BLACKLIST_EXACT,
+       .category = "general",
        .item_alloc = ast_xmpp_client_config_alloc,
        .item_find = xmpp_config_find,
        .item_prelink = xmpp_config_prelink,
index d74726a..de85f23 100644 (file)
@@ -1456,13 +1456,19 @@ static struct aco_type global = {
 static struct aco_type global_defaults = {
        .type = ACO_GLOBAL,
        .item_offset = offsetof(struct test_config, global_defaults),
-       .category_match = ACO_WHITELIST,
-       .category = "^global_defaults$",
+       .category_match = ACO_WHITELIST_EXACT,
+       .category = "global_defaults",
+};
+static const char *item_blacklist[] = {
+       "global",
+       "global_defaults",
+       NULL,
 };
+
 static struct aco_type item = {
        .type = ACO_ITEM,
-       .category_match = ACO_BLACKLIST,
-       .category = "^(global|global_defaults)$",
+       .category_match = ACO_BLACKLIST_ARRAY,
+       .category = (const char *)item_blacklist,
        .item_alloc = test_item_alloc,
        .item_find = test_item_find,
        .item_offset = offsetof(struct test_config, items),