summaryrefslogtreecommitdiff
path: root/main/config.c
diff options
context:
space:
mode:
authorGeorge Joseph <george.joseph@fairview5.com>2014-10-13 16:12:17 +0000
committerGeorge Joseph <george.joseph@fairview5.com>2014-10-13 16:12:17 +0000
commitc7e6b6ba3d68d3666965286acf035d09c2fc3c9f (patch)
tree0adacef4f1d976c9e37434fd2ace78fafae578eb /main/config.c
parent8d6f1d763c69b0ebf04784e89d1cde8e1ac0acda (diff)
manager/config: Support templates and non-unique category names via AMI
This patch provides the capability to manipulate templates and categories with non-unique names via AMI. Summary of changes: GetConfig and GetConfigJSON: Added "Filter" parameter: A comma separated list of name_regex=value_regex expressions which will cause only categories whose variables match all expressions to be considered. The special variable name TEMPLATES can be used to control whether templates are included. Passing 'include' as the value will include templates along with normal categories. Passing 'restrict' as the value will restrict the operation to ONLY templates. Not specifying a TEMPLATES expression results in the current default behavior which is to not include templates. UpdateConfig: NewCat now includes options for allowing duplicate category names, indicating if the category should be created as a template, and specifying templates the category should inherit from. The rest of the actions now accept a filter string as defined above. If there are non-unique category names, you can now update specific ones based on variable values. To facilitate the new capabilities in manager, corresponding changes had to be made to config, most notably the addition of filter criteria to many of the APIs. In some cases it was easy to change the references to use the new prototype but others would have required touching too many files for this patch so a wrapper with the original prototype was created. Macros couldn't be used in this case because it would break binary compatibility with modules such as res_digium_phone that are linked to real symbols. Tested-by: George Joseph Review: https://reviewboard.asterisk.org/r/4033/ ........ Merged revisions 425383 from http://svn.asterisk.org/svn/asterisk/branches/12 ........ Merged revisions 425384 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@425385 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/config.c')
-rw-r--r--main/config.c346
1 files changed, 246 insertions, 100 deletions
diff --git a/main/config.c b/main/config.c
index 55c40a955..7009e7d08 100644
--- a/main/config.c
+++ b/main/config.c
@@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/stat.h>
#include <math.h> /* HUGE_VAL */
+#include <regex.h>
#define AST_INCLUDE_GLOB 1
@@ -71,6 +72,7 @@ static char *extconfig_conf = "extconfig.conf";
static struct ao2_container *cfg_hooks;
static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg);
inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2);
+static int does_category_match(struct ast_category *cat, const char *category_name, const char *match);
/*! \brief Structure to keep comments for rewriting configuration files */
struct ast_comment {
@@ -232,6 +234,8 @@ struct ast_category {
struct ast_variable *root;
/*! Last category variable in the list. */
struct ast_variable *last;
+ /*! Previous node in the list. */
+ struct ast_category *prev;
/*! Next node in the list. */
struct ast_category *next;
};
@@ -599,16 +603,12 @@ void ast_variables_destroy(struct ast_variable *v)
struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
{
- struct ast_category *cat = NULL;
-
- if (!category) {
- return NULL;
- }
+ struct ast_category *cat;
if (config->last_browse && (config->last_browse->name == category)) {
cat = config->last_browse;
} else {
- cat = ast_category_get(config, category);
+ cat = ast_category_get(config, category, NULL);
}
return (cat) ? cat->root : NULL;
@@ -677,29 +677,37 @@ const char *ast_config_option(struct ast_config *cfg, const char *cat, const cha
return tmp;
}
+const char *ast_variable_retrieve(struct ast_config *config,
+ const char *category, const char *variable)
+{
+ return ast_variable_retrieve_filtered(config, category, variable, NULL);
+}
-const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
+const char *ast_variable_retrieve_filtered(struct ast_config *config,
+ const char *category, const char *variable, const char *filter)
{
- struct ast_variable *v;
+ struct ast_category *cat = NULL;
+ const char *value;
- if (category) {
- for (v = ast_variable_browse(config, category); v; v = v->next) {
- if (!strcasecmp(variable, v->name)) {
- return v->value;
- }
+ while ((cat = ast_category_browse_filtered(config, category, cat, filter))) {
+ value = ast_variable_find(cat, variable);
+ if (value) {
+ return value;
}
- } else {
- struct ast_category *cat;
+ }
- for (cat = config->root; cat; cat = cat->next) {
- for (v = cat->root; v; v = v->next) {
- if (!strcasecmp(variable, v->name)) {
- return v->value;
- }
- }
+ return NULL;
+}
+
+const char *ast_variable_find(const struct ast_category *category, const char *variable)
+{
+ struct ast_variable *v;
+
+ for (v = category->root; v; v = v->next) {
+ if (!strcasecmp(variable, v->name)) {
+ return v->value;
}
}
-
return NULL;
}
@@ -726,7 +734,99 @@ static void move_variables(struct ast_category *old, struct ast_category *new)
ast_variable_append(new, var);
}
-struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
+/*! \brief Returns true if ALL of the regex expressions and category name match.
+ * Both can be NULL (I.E. no predicate) which results in a true return;
+ */
+static int does_category_match(struct ast_category *cat, const char *category_name, const char *match)
+{
+ char *dupmatch;
+ char *nvp = NULL;
+ int match_found = 0, match_expressions = 0;
+ int template_ok = 0;
+
+ /* Only match on category name if it's not a NULL or empty string */
+ if (!ast_strlen_zero(category_name) && strcasecmp(cat->name, category_name)) {
+ return 0;
+ }
+
+ /* If match is NULL or empty, automatically match if not a template */
+ if (ast_strlen_zero(match)) {
+ return !cat->ignored;
+ }
+
+ dupmatch = ast_strdupa(match);
+
+ while ((nvp = ast_strsep(&dupmatch, ',', AST_STRSEP_STRIP))) {
+ struct ast_variable *v;
+ char *match_name;
+ char *match_value = NULL;
+ char *regerr;
+ int rc;
+ regex_t r_name, r_value;
+
+ match_expressions++;
+
+ match_name = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
+ match_value = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
+
+ /* an empty match value is OK. A NULL match value (no =) is NOT. */
+ if (match_value == NULL) {
+ break;
+ }
+
+ if (!strcmp("TEMPLATES", match_name)) {
+ if (!strcasecmp("include", match_value)) {
+ if (cat->ignored) {
+ template_ok = 1;
+ }
+ match_found++;
+ } else if (!strcasecmp("restrict", match_value)) {
+ if (cat->ignored) {
+ match_found++;
+ template_ok = 1;
+ } else {
+ break;
+ }
+ }
+ continue;
+ }
+
+ if ((rc = regcomp(&r_name, match_name, REG_EXTENDED | REG_NOSUB))) {
+ regerr = ast_alloca(128);
+ regerror(rc, &r_name, regerr, 128);
+ ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
+ match_name, regerr);
+ regfree(&r_name);
+ return 0;
+ }
+ if ((rc = regcomp(&r_value, match_value, REG_EXTENDED | REG_NOSUB))) {
+ regerr = ast_alloca(128);
+ regerror(rc, &r_value, regerr, 128);
+ ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
+ match_value, regerr);
+ regfree(&r_name);
+ regfree(&r_value);
+ return 0;
+ }
+
+ for (v = cat->root; v; v = v->next) {
+ if (!regexec(&r_name, v->name, 0, NULL, 0)
+ && !regexec(&r_value, v->value, 0, NULL, 0)) {
+ match_found++;
+ break;
+ }
+ }
+ regfree(&r_name);
+ regfree(&r_value);
+ }
+ if (match_found == match_expressions && (!cat->ignored || template_ok)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+static struct ast_category *new_category(const char *name, const char *in_file, int lineno, int template)
{
struct ast_category *category;
@@ -741,44 +841,85 @@ struct ast_category *ast_category_new(const char *name, const char *in_file, int
}
ast_copy_string(category->name, name, sizeof(category->name));
category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
+ category->ignored = template;
return category;
}
-static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
+struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
{
- struct ast_category *cat;
+ return new_category(name, in_file, lineno, 0);
+}
- /* try exact match first, then case-insensitive match */
- for (cat = config->root; cat; cat = cat->next) {
- if (cat->name == category_name && (ignored || !cat->ignored))
- return cat;
- }
+struct ast_category *ast_category_new_template(const char *name, const char *in_file, int lineno)
+{
+ return new_category(name, in_file, lineno, 1);
+}
+
+struct ast_category *ast_category_get(const struct ast_config *config,
+ const char *category_name, const char *filter)
+{
+ struct ast_category *cat;
for (cat = config->root; cat; cat = cat->next) {
- if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
+ if (does_category_match(cat, category_name, filter)) {
return cat;
+ }
}
return NULL;
}
-struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
+const char *ast_category_get_name(const struct ast_category *category)
{
- return category_get(config, category_name, 0);
+ return category->name;
}
-int ast_category_exist(const struct ast_config *config, const char *category_name)
+int ast_category_is_template(const struct ast_category *category)
{
- return !!ast_category_get(config, category_name);
+ return category->ignored;
+}
+
+struct ast_str *ast_category_get_templates(const struct ast_category *category)
+{
+ struct ast_category_template_instance *template;
+ struct ast_str *str;
+ int first = 1;
+
+ if (AST_LIST_EMPTY(&category->template_instances)) {
+ return NULL;
+ }
+
+ str = ast_str_create(128);
+ if (!str) {
+ return NULL;
+ }
+
+ AST_LIST_TRAVERSE(&category->template_instances, template, next) {
+ ast_str_append(&str, 0, "%s%s", first ? "" : ",", template->name);
+ first = 0;
+ }
+
+ return str;
+}
+
+int ast_category_exist(const struct ast_config *config, const char *category_name,
+ const char *filter)
+{
+ return !!ast_category_get(config, category_name, filter);
}
void ast_category_append(struct ast_config *config, struct ast_category *category)
{
- if (config->last)
+ if (config->last) {
config->last->next = category;
- else
+ category->prev = config->last;
+ } else {
config->root = category;
+ category->prev = NULL;
+ }
+ category->next = NULL;
category->include_level = config->include_level;
+
config->last = category;
config->current = category;
}
@@ -787,19 +928,26 @@ int ast_category_insert(struct ast_config *config, struct ast_category *cat, con
{
struct ast_category *cur_category;
- if (!config || !cat || !match) {
+ if (!config || !config->root || !cat || !match) {
return -1;
}
+
if (!strcasecmp(config->root->name, match)) {
cat->next = config->root;
+ cat->prev = NULL;
+ config->root->prev = cat;
config->root = cat;
return 0;
}
- for (cur_category = config->root; cur_category && cur_category->next;
- cur_category = cur_category->next) {
- if (!strcasecmp(cur_category->next->name, match)) {
- cat->next = cur_category->next;
- cur_category->next = cat;
+
+ for (cur_category = config->root->next; cur_category; cur_category = cur_category->next) {
+ if (!strcasecmp(cur_category->name, match)) {
+ cat->prev = cur_category->prev;
+ cat->prev->next = cat;
+
+ cat->next = cur_category;
+ cur_category->prev = cat;
+
return 0;
}
}
@@ -841,9 +989,10 @@ static void ast_includes_destroy(struct ast_config_include *incls)
}
}
-static struct ast_category *next_available_category(struct ast_category *cat)
+static struct ast_category *next_available_category(struct ast_category *cat,
+ const char *name, const char *filter)
{
- for (; cat && cat->ignored; cat = cat->next);
+ for (; cat && !does_category_match(cat, name, filter); cat = cat->next);
return cat;
}
@@ -856,7 +1005,7 @@ struct ast_variable *ast_category_first(struct ast_category *cat)
struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
{
- struct ast_category *category = ast_category_get(config, cat);
+ struct ast_category *category = ast_category_get(config, cat, NULL);
if (category)
return category->root;
@@ -1021,12 +1170,29 @@ char *ast_category_browse(struct ast_config *config, const char *prev)
}
if (cat)
- cat = next_available_category(cat);
+ cat = next_available_category(cat, NULL, NULL);
config->last_browse = cat;
return (cat) ? cat->name : NULL;
}
+struct ast_category *ast_category_browse_filtered(struct ast_config *config,
+ const char *category_name, struct ast_category *prev, const char *filter)
+{
+ struct ast_category *cat;
+
+ if (!prev) {
+ prev = config->root;
+ } else {
+ prev = prev->next;
+ }
+
+ cat = next_available_category(prev, category_name, filter);
+ config->last_browse = cat;
+
+ return cat;
+}
+
struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
{
struct ast_variable *v;
@@ -1043,7 +1209,7 @@ void ast_category_rename(struct ast_category *cat, const char *name)
ast_copy_string(cat->name, name, sizeof(cat->name));
}
-static void inherit_category(struct ast_category *new, const struct ast_category *base)
+void ast_category_inherit(struct ast_category *new, const struct ast_category *base)
{
struct ast_variable *var;
struct ast_category_template_instance *x;
@@ -1147,65 +1313,45 @@ int ast_variable_update(struct ast_category *category, const char *variable,
return -1;
}
-int ast_category_delete(struct ast_config *cfg, const char *category)
+struct ast_category *ast_category_delete(struct ast_config *config,
+ struct ast_category *category)
{
- struct ast_category *prev=NULL, *cat;
+ struct ast_category *prev;
- cat = cfg->root;
- while (cat) {
- if (cat->name == category) {
- if (prev) {
- prev->next = cat->next;
- if (cat == cfg->last)
- cfg->last = prev;
- } else {
- cfg->root = cat->next;
- if (cat == cfg->last)
- cfg->last = NULL;
- }
- ast_category_destroy(cat);
- return 0;
- }
- prev = cat;
- cat = cat->next;
+ if (!config || !category) {
+ return NULL;
}
- prev = NULL;
- cat = cfg->root;
- while (cat) {
- if (!strcasecmp(cat->name, category)) {
- if (prev) {
- prev->next = cat->next;
- if (cat == cfg->last)
- cfg->last = prev;
- } else {
- cfg->root = cat->next;
- if (cat == cfg->last)
- cfg->last = NULL;
- }
- ast_category_destroy(cat);
- return 0;
- }
- prev = cat;
- cat = cat->next;
+ if (category->prev) {
+ category->prev->next = category->next;
+ } else {
+ config->root = category->next;
}
- return -1;
+
+ if (category->next) {
+ category->next->prev = category->prev;
+ } else {
+ config->last = category->prev;
+ }
+
+ prev = category->prev;
+
+ ast_category_destroy(category);
+
+ return prev;
}
-int ast_category_empty(struct ast_config *cfg, const char *category)
+int ast_category_empty(struct ast_category *category)
{
- struct ast_category *cat;
-
- for (cat = cfg->root; cat; cat = cat->next) {
- if (strcasecmp(cat->name, category))
- continue;
- ast_variables_destroy(cat->root);
- cat->root = NULL;
- cat->last = NULL;
- return 0;
+ if (!category) {
+ return -1;
}
- return -1;
+ ast_variables_destroy(category->root);
+ category->root = NULL;
+ category->last = NULL;
+
+ return 0;
}
void ast_config_destroy(struct ast_config *cfg)
@@ -1489,7 +1635,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
if (!strcasecmp(cur, "!")) {
(*cat)->ignored = 1;
} else if (!strcasecmp(cur, "+")) {
- *cat = category_get(cfg, catname, 1);
+ *cat = ast_category_get(cfg, catname, NULL);
if (!(*cat)) {
if (newcat)
ast_category_destroy(newcat);
@@ -1504,12 +1650,12 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
} else {
struct ast_category *base;
- base = category_get(cfg, cur, 1);
+ base = ast_category_get(cfg, cur, "TEMPLATES=restrict");
if (!base) {
ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
return -1;
}
- inherit_category(*cat, base);
+ ast_category_inherit(*cat, base);
}
}
}