loader: Fix comments in struct ast_module.
[asterisk/asterisk.git] / main / config.c
index 98fa9a2..3d8dcfb 100644 (file)
 
 #include "asterisk.h"
 
-ASTERISK_REGISTER_FILE()
-
 #include "asterisk/paths.h"    /* use ast_config_AST_CONFIG_DIR */
 #include "asterisk/network.h"  /* we do some sockaddr manipulation here */
+
+#include <string.h>
+#include <libgen.h>
 #include <time.h>
 #include <sys/stat.h>
 
@@ -72,7 +73,8 @@ 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);
 static 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);
+static int does_category_match(struct ast_category *cat, const char *category_name,
+       const char *match, char sep);
 
 /*! \brief Structure to keep comments for rewriting configuration files */
 struct ast_comment {
@@ -279,7 +281,7 @@ struct ast_config_include {
 static void ast_variable_destroy(struct ast_variable *doomed);
 static void ast_includes_destroy(struct ast_config_include *incls);
 
-#ifdef MALLOC_DEBUG
+#ifdef __AST_DEBUG_MALLOC
 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
 #else
 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
@@ -296,7 +298,7 @@ struct ast_variable *ast_variable_new(const char *name, const char *value, const
        }
 
        if (
-#ifdef MALLOC_DEBUG
+#ifdef __AST_DEBUG_MALLOC
                (variable = __ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable), file, lineno, func))
 #else
                (variable = ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable)))
@@ -723,6 +725,96 @@ const char *ast_variable_find(const struct ast_category *category, const char *v
        return ast_variable_find_in_list(category->root, variable);
 }
 
+const struct ast_variable *ast_variable_find_variable_in_list(const struct ast_variable *list, const char *variable_name)
+{
+       const struct ast_variable *v;
+
+       for (v = list; v; v = v->next) {
+               if (!strcasecmp(variable_name, v->name)) {
+                       return v;
+               }
+       }
+       return NULL;
+}
+
+int ast_variables_match(const struct ast_variable *left, const struct ast_variable *right)
+{
+       char *op;
+
+       if (left == right) {
+               return 1;
+       }
+
+       if (!(left && right)) {
+               return 0;
+       }
+
+       op = strrchr(right->name, ' ');
+       if (op) {
+               op++;
+       }
+
+       return ast_strings_match(left->value, op ? ast_strdupa(op) : NULL, right->value);
+}
+
+int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
+{
+       const struct ast_variable *field;
+       int right_count = 0;
+       int left_count = 0;
+
+       if (left == right) {
+               return 1;
+       }
+
+       if (!(left && right)) {
+               return 0;
+       }
+
+       for (field = right; field; field = field->next) {
+               char *space = strrchr(field->name, ' ');
+               const struct ast_variable *old;
+               char * name = (char *)field->name;
+
+               if (space) {
+                       name = ast_strdup(field->name);
+                       if (!name) {
+                               return 0;
+                       }
+                       name[space - field->name] = '\0';
+               }
+
+               old = ast_variable_find_variable_in_list(left, name);
+               if (name != field->name) {
+                       ast_free(name);
+               }
+
+               if (exact_match) {
+                       if (!old || strcmp(old->value, field->value)) {
+                               return 0;
+                       }
+               } else {
+                       if (!ast_variables_match(old, field)) {
+                               return 0;
+                       }
+               }
+
+               right_count++;
+       }
+
+       if (exact_match) {
+               for (field = left; field; field = field->next) {
+                       left_count++;
+               }
+
+               if (right_count != left_count) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
 const char *ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
 {
        const struct ast_variable *v;
@@ -774,7 +866,8 @@ static void move_variables(struct ast_category *old, struct ast_category *new)
 /*! \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)
+static int does_category_match(struct ast_category *cat, const char *category_name,
+       const char *match, char sep)
 {
        char *dupmatch;
        char *nvp = NULL;
@@ -793,7 +886,7 @@ static int does_category_match(struct ast_category *cat, const char *category_na
 
        dupmatch = ast_strdupa(match);
 
-       while ((nvp = ast_strsep(&dupmatch, ',', AST_STRSEP_STRIP))) {
+       while ((nvp = ast_strsep(&dupmatch, sep, AST_STRSEP_STRIP))) {
                struct ast_variable *v;
                char *match_name;
                char *match_value = NULL;
@@ -892,19 +985,19 @@ struct ast_category *ast_category_new_template(const char *name, const char *in_
        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)
+static struct ast_category *category_get_sep(const struct ast_config *config,
+       const char *category_name, const char *filter, char sep)
 {
        struct ast_category *cat;
 
        for (cat = config->root; cat; cat = cat->next) {
-               if (cat->name == category_name && does_category_match(cat, category_name, filter)) {
+               if (cat->name == category_name && does_category_match(cat, category_name, filter, sep)) {
                        return cat;
                }
        }
 
        for (cat = config->root; cat; cat = cat->next) {
-               if (does_category_match(cat, category_name, filter)) {
+               if (does_category_match(cat, category_name, filter, sep)) {
                        return cat;
                }
        }
@@ -912,6 +1005,12 @@ struct ast_category *ast_category_get(const struct ast_config *config,
        return NULL;
 }
 
+struct ast_category *ast_category_get(const struct ast_config *config,
+       const char *category_name, const char *filter)
+{
+       return category_get_sep(config, category_name, filter, ',');
+}
+
 const char *ast_category_get_name(const struct ast_category *category)
 {
        return category->name;
@@ -1035,7 +1134,7 @@ static void ast_includes_destroy(struct ast_config_include *incls)
 static struct ast_category *next_available_category(struct ast_category *cat,
        const char *name, const char *filter)
 {
-       for (; cat && !does_category_match(cat, name, filter); cat = cat->next);
+       for (; cat && !does_category_match(cat, name, filter, ','); cat = cat->next);
 
        return cat;
 }
@@ -1687,8 +1786,13 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                        while ((cur = strsep(&c, ","))) {
                                if (!strcasecmp(cur, "!")) {
                                        (*cat)->ignored = 1;
-                               } else if (!strcasecmp(cur, "+")) {
-                                       *cat = ast_category_get(cfg, catname, NULL);
+                               } else if (cur[0] == '+') {
+                                       char *filter = NULL;
+
+                                       if (cur[1] != ',') {
+                                               filter = &cur[1];
+                                       }
+                                       *cat = category_get_sep(cfg, catname, filter, '&');
                                        if (!(*cat)) {
                                                if (newcat) {
                                                        ast_category_destroy(newcat);
@@ -2409,6 +2513,25 @@ int ast_config_text_file_save(const char *configfile, const struct ast_config *c
        return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
 }
 
+static int is_writable(const char *fn)
+{
+       if (access(fn, F_OK)) {
+               char *dn = dirname(ast_strdupa(fn));
+
+               if (access(dn, R_OK | W_OK)) {
+                       ast_log(LOG_ERROR, "Unable to write to directory %s (%s)\n", dn, strerror(errno));
+                       return 0;
+               }
+       } else {
+               if (access(fn, R_OK | W_OK)) {
+                       ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
 int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags)
 {
        FILE *f;
@@ -2431,20 +2554,20 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config *
        for (incl = cfg->includes; incl; incl = incl->next) {
                /* reset all the output flags in case this isn't our first time saving this data */
                incl->output = 0;
-               /* now make sure we have write access */
+
                if (!incl->exec) {
+                       /* now make sure we have write access to the include file or its parent directory */
                        make_fn(fn, sizeof(fn), incl->included_file, configfile);
-                       if (access(fn, R_OK | W_OK)) {
-                               ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
+                       /* If the file itself doesn't exist, make sure we have write access to the directory */
+                       if (!is_writable(fn)) {
                                return -1;
                        }
                }
        }
 
-       /* now make sure we have write access to the main config file */
+       /* now make sure we have write access to the main config file or its parent directory */
        make_fn(fn, sizeof(fn), 0, configfile);
-       if (access(fn, R_OK | W_OK)) {
-               ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
+       if (!is_writable(fn)) {
                return -1;
        }
 
@@ -3618,6 +3741,55 @@ uint32_done:
                break;
        }
 
+       case PARSE_TIMELEN:
+       {
+               int x = 0;
+               int *result = p_result;
+               int def = result ? *result : 0;
+               int high = INT_MAX;
+               int low = INT_MIN;
+               enum ast_timelen defunit;
+
+               defunit = va_arg(ap, enum ast_timelen);
+               /* optional arguments: default value and/or (low, high) */
+               if (flags & PARSE_DEFAULT) {
+                       def = va_arg(ap, int);
+               }
+               if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
+                       low = va_arg(ap, int);
+                       high = va_arg(ap, int);
+               }
+               if (ast_strlen_zero(arg)) {
+                       error = 1;
+                       goto timelen_done;
+               }
+               error = ast_app_parse_timelen(arg, &x, defunit);
+               if (error || x < INT_MIN || x > INT_MAX) {
+                       /* Parse error, or type out of int bounds */
+                       error = 1;
+                       goto timelen_done;
+               }
+               error = (x < low) || (x > high);
+               if (flags & PARSE_RANGE_DEFAULTS) {
+                       if (x < low) {
+                               def = low;
+                       } else if (x > high) {
+                               def = high;
+                       }
+               }
+               if (flags & PARSE_OUT_RANGE) {
+                       error = !error;
+               }
+timelen_done:
+               if (result) {
+                       *result  = error ? def : x;
+               }
+
+               ast_debug(3, "extract timelen from [%s] in [%d, %d] gives [%d](%d)\n",
+                               arg, low, high, result ? *result : x, error);
+               break;
+       }
+
        case PARSE_DOUBLE:
        {
                double *result = p_result;
@@ -3863,6 +4035,8 @@ static void config_shutdown(void)
 
        ast_cli_unregister_multiple(cli_config, ARRAY_LEN(cli_config));
 
+       clear_config_maps();
+
        ao2_cleanup(cfg_hooks);
        cfg_hooks = NULL;
 }