]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_sorcery_config: Allow configuration section to be used based on name.
authorJoshua Colp <jcolp@digium.com>
Wed, 18 Jul 2018 16:12:59 +0000 (16:12 +0000)
committerJoshua Colp <jcolp@digium.com>
Wed, 18 Jul 2018 18:20:41 +0000 (13:20 -0500)
A problem I've seen countless times is a global or system section
for PJSIP not getting applied. This is inevitably the result of
the "type=" line missing. This change alleviates that problem.

The ability to specify an explicit section name has been
added to res_sorcery_config. If the configured section
name matches this and there are no unknown things configured
the section is taken as being for the given type.

Both the PJSIP "global" and "system" types now support this
so you can just name your section "global" or "system" and it
will be matched and used, even without a "type=" line.

ASTERISK-27972

Change-Id: Ie22723663c1ddd24f869af8c9b4c1b59e2476893

res/res_pjsip.c
res/res_pjsip/config_global.c
res/res_pjsip/config_system.c
res/res_sorcery_config.c

index e98d765d7fed43737d79d0977036dbca398972c3..0d9c77c516551684c6e3d2392d0bbe215c82873d 100644 (file)
                                        </description>
                                </configOption>
                                <configOption name="type">
-                                       <synopsis>Must be of type 'system'.</synopsis>
+                                       <synopsis>Must be of type 'system' UNLESS the object name is 'system'.</synopsis>
                                </configOption>
                        </configObject>
                        <configObject name="global">
                                        twice the unidentified_request_period are pruned.</synopsis>
                                </configOption>
                                <configOption name="type">
-                                       <synopsis>Must be of type 'global'.</synopsis>
+                                       <synopsis>Must be of type 'global' UNLESS the object name is 'global'.</synopsis>
                                </configOption>
                                <configOption name="user_agent" default="Asterisk &lt;Asterisk Version&gt;">
                                        <synopsis>Value used in User-Agent header for SIP requests and Server header for SIP responses.</synopsis>
index fc1227d2523305581584b874d3d84c2b5528aa53..2a803128d90bae514ac26b7a3b66be2eaee4ddad 100644 (file)
@@ -491,7 +491,7 @@ int ast_sip_initialize_sorcery_global(void)
        snprintf(default_useragent, sizeof(default_useragent), "%s %s",
                DEFAULT_USERAGENT_PREFIX, ast_get_version());
 
-       ast_sorcery_apply_default(sorcery, "global", "config", "pjsip.conf,criteria=type=global");
+       ast_sorcery_apply_default(sorcery, "global", "config", "pjsip.conf,criteria=type=global,single_object=yes,explicit_name=global");
 
        if (ast_sorcery_object_register(sorcery, "global", global_alloc, NULL, global_apply)) {
                return -1;
index 65e4e2c05ec7f7101aaddc87071a8e828bcaf4c8..2ff0a65efe680477a5f9e1ef9a2842b7b2f2bbcb 100644 (file)
@@ -175,7 +175,7 @@ int ast_sip_initialize_system(void)
                return -1;
        }
 
-       ast_sorcery_apply_default(system_sorcery, "system", "config", "pjsip.conf,criteria=type=system");
+       ast_sorcery_apply_default(system_sorcery, "system", "config", "pjsip.conf,criteria=type=system,single_object=yes,explicit_name=system");
 
        if (ast_sorcery_object_register_no_reload(system_sorcery, "system", system_alloc, NULL, system_apply)) {
                ast_log(LOG_ERROR, "Failed to register with sorcery (is res_sorcery_config loaded?)\n");
index 20178883b0ba7de29426b17506e1026f136be280..8e5ee205a8de2c0d30da24b3b4623ee6f24d2e16 100644 (file)
@@ -50,12 +50,18 @@ struct sorcery_config {
        /*! \brief Any specific variable criteria for considering a defined category for this object */
        struct ast_variable *criteria;
 
+       /*! \brief An explicit name for the configuration section, with it there can be only one */
+       char *explicit_name;
+
        /*! \brief Number of buckets to use for objects */
        unsigned int buckets;
 
        /*! \brief Enable file level integrity instead of object level */
        unsigned int file_integrity:1;
 
+       /*! \brief Enable enforcement of a single configuration object of this type */
+       unsigned int single_object:1;
+
        /*! \brief Filename of the configuration file */
        char filename[];
 };
@@ -113,6 +119,7 @@ static void sorcery_config_destructor(void *obj)
        ao2_global_obj_release(config->objects);
        ast_rwlock_destroy(&config->objects.lock);
        ast_variables_destroy(config->criteria);
+       ast_free(config->explicit_name);
 }
 
 static int sorcery_config_fields_cmp(void *obj, void *arg, int flags)
@@ -237,12 +244,66 @@ static void sorcery_config_retrieve_prefix(const struct ast_sorcery *sorcery, vo
        ao2_callback(config_objects, OBJ_NODATA | OBJ_MULTIPLE, sorcery_config_fields_cmp, &params);
 }
 
-/*! \brief Internal function which determines if criteria has been met for considering an object set applicable */
-static int sorcery_is_criteria_met(struct ast_variable *objset, struct ast_variable *criteria)
+/*! \brief Internal function which determines if a category matches based on explicit name */
+static int sorcery_is_explicit_name_met(const struct ast_sorcery *sorcery, const char *type,
+       struct ast_category *category, struct sorcery_config *config)
+{
+       struct ast_sorcery_object_type *object_type;
+       struct ast_variable *field;
+       int met = 1;
+
+       if (ast_strlen_zero(config->explicit_name) || strcmp(ast_category_get_name(category), config->explicit_name)) {
+               return 0;
+       }
+
+       object_type = ast_sorcery_get_object_type(sorcery, type);
+       if (!object_type) {
+               return 0;
+       }
+
+       /* We iterate the configured fields to see if we don't know any, if we don't then
+        * this is likely not for the given type and we skip it. If it actually is then criteria
+        * may pick it up in which case it would just get rejected as an invalid configuration later.
+        */
+       for (field = ast_category_first(category); field; field = field->next) {
+               if (!ast_sorcery_is_object_field_registered(object_type, field->name)) {
+                       met = 0;
+                       break;
+               }
+       }
+
+       ao2_ref(object_type, -1);
+
+       return met;
+}
+
+/*! \brief Internal function which determines if a category matches based on criteria */
+static int sorcery_is_criteria_met(struct ast_category *category, struct sorcery_config *config)
 {
        RAII_VAR(struct ast_variable *, diff, NULL, ast_variables_destroy);
 
-       return (!criteria || (!ast_sorcery_changeset_create(objset, criteria, &diff) && !diff)) ? 1 : 0;
+       if (!config->criteria) {
+               return 0;
+       }
+
+       return (!ast_sorcery_changeset_create(ast_category_first(category), config->criteria, &diff) && !diff) ? 1 : 0;
+}
+
+/*! \brief Internal function which determines if criteria has been met for considering an object set applicable */
+static int sorcery_is_configuration_met(const struct ast_sorcery *sorcery, const char *type,
+       struct ast_category *category, struct sorcery_config *config)
+{
+       if (!config->criteria && ast_strlen_zero(config->explicit_name)) {
+               /* Nothing is configured to allow specific matching, so accept it! */
+               return 1;
+       } else if (sorcery_is_explicit_name_met(sorcery, type, category, config)) {
+               return 1;
+       } else if (sorcery_is_criteria_met(category, config)) {
+               return 1;
+       } else {
+               /* Nothing explicitly matched so reject */
+               return 0;
+       }
 }
 
 static void sorcery_config_internal_load(void *data, const struct ast_sorcery *sorcery, const char *type, unsigned int reload)
@@ -269,8 +330,8 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
        if (!config->buckets) {
                while ((category = ast_category_browse_filtered(cfg, NULL, category, NULL))) {
 
-                       /* If given criteria has not been met skip the category, it is not applicable */
-                       if (!sorcery_is_criteria_met(ast_category_first(category), config->criteria)) {
+                       /* If given configuration has not been met skip the category, it is not applicable */
+                       if (!sorcery_is_configuration_met(sorcery, type, category, config)) {
                                continue;
                        }
 
@@ -292,6 +353,16 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
                buckets = config->buckets;
        }
 
+       /* For single object configurations there can only ever be one bucket, if there's more than the single
+        * object requirement has been violated.
+        */
+       if (config->single_object && buckets > 1) {
+               ast_log(LOG_ERROR, "Config file '%s' could not be loaded; configuration contains more than one object of type '%s'\n",
+                       config->filename, type);
+               ast_config_destroy(cfg);
+               return;
+       }
+
        ast_debug(2, "Using bucket size of '%d' for objects of type '%s' from '%s'\n",
                buckets, type, config->filename);
 
@@ -308,8 +379,8 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
                RAII_VAR(void *, obj, NULL, ao2_cleanup);
                id = ast_category_get_name(category);
 
-               /* If given criteria has not been met skip the category, it is not applicable */
-               if (!sorcery_is_criteria_met(ast_category_first(category), config->criteria)) {
+               /* If given configurationhas not been met skip the category, it is not applicable */
+               if (!sorcery_is_configuration_met(sorcery, type, category, config)) {
                        continue;
                }
 
@@ -418,6 +489,24 @@ static void *sorcery_config_open(const char *data)
                                ao2_ref(config, -1);
                                return NULL;
                        }
+               } else if (!strcasecmp(name, "explicit_name")) {
+                       ast_free(config->explicit_name);
+                       config->explicit_name = ast_strdup(value);
+                       if (ast_strlen_zero(config->explicit_name)) {
+                               /* This is fatal since it could stop a configuration section from getting applied */
+                               ast_log(LOG_ERROR, "Could not create explicit name entry of '%s' for configuration file '%s'\n",
+                                       value, filename);
+                               ao2_ref(config, -1);
+                               return NULL;
+                       }
+               } else if (!strcasecmp(name, "single_object")) {
+                       if (ast_strlen_zero(value)) {
+                               ast_log(LOG_ERROR, "Could not set single object value for configuration file '%s' as the value is empty\n",
+                                       filename);
+                               ao2_ref(config, -1);
+                               return NULL;
+                       }
+                       config->single_object = ast_true(value);
                } else {
                        ast_log(LOG_ERROR, "Unsupported option '%s' used for configuration file '%s'\n", name, filename);
                }