loader: Fix comments in struct ast_module.
[asterisk/asterisk.git] / main / config.c
index fe3ad3f..3d8dcfb 100644 (file)
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #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>
 
@@ -71,8 +72,9 @@ 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);
+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, 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)))
@@ -614,7 +616,7 @@ struct ast_variable *ast_variable_browse(const struct ast_config *config, const
        return (cat) ? cat->root : NULL;
 }
 
-inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
+static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
 {
     l1->next = l2->next;
     l2->next = l1;
@@ -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;
@@ -735,6 +827,19 @@ const char *ast_variable_find_in_list(const struct ast_variable *list, const cha
        return NULL;
 }
 
+const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
+{
+       const struct ast_variable *v;
+       const char *found = NULL;
+
+       for (v = list; v; v = v->next) {
+               if (!strcasecmp(variable, v->name)) {
+                       found = v->value;
+               }
+       }
+       return found;
+}
+
 static struct ast_variable *variable_clone(const struct ast_variable *old)
 {
        struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
@@ -761,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;
@@ -780,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;
@@ -879,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;
                }
        }
@@ -899,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;
@@ -1022,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;
 }
@@ -1238,20 +1350,27 @@ void ast_category_rename(struct ast_category *cat, const char *name)
        ast_copy_string(cat->name, name, sizeof(cat->name));
 }
 
-void ast_category_inherit(struct ast_category *new, const struct ast_category *base)
+int ast_category_inherit(struct ast_category *new, const struct ast_category *base)
 {
        struct ast_variable *var;
        struct ast_category_template_instance *x;
 
        x = ast_calloc(1, sizeof(*x));
        if (!x) {
-               return;
+               return -1;
        }
        strcpy(x->name, base->name);
        x->inst = base;
        AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
-       for (var = base->root; var; var = var->next)
-               ast_variable_append(new, variable_clone(var));
+       for (var = base->root; var; var = var->next) {
+               struct ast_variable *cloned = variable_clone(var);
+               if (!cloned) {
+                       return -1;
+               }
+               cloned->inherited = 1;
+               ast_variable_append(new, cloned);
+       }
+       return 0;
 }
 
 struct ast_config *ast_config_new(void)
@@ -1627,7 +1746,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                 *              You can put a comma-separated list of categories and templates
                 *              and '!' and '+' between parentheses, with obvious meaning.
                 */
-               struct ast_category *newcat = NULL;
+               struct ast_category *newcat;
                char *catname;
 
                c = strchr(cur, ']');
@@ -1640,14 +1759,13 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                if (*c++ != '(')
                        c = NULL;
                catname = cur;
-               if (!(*cat = newcat = ast_category_new(catname,
-                               S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
-                               lineno))) {
+               *cat = newcat = ast_category_new(catname,
+                       S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
+                       lineno);
+               if (!newcat) {
                        return -1;
                }
                (*cat)->lineno = lineno;
-               *last_var = 0;
-               *last_cat = newcat;
 
                /* add comments */
                if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
@@ -1660,6 +1778,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                /* If there are options or categories to inherit from, process them now */
                if (c) {
                        if (!(cur = strchr(c, ')'))) {
+                               ast_category_destroy(newcat);
                                ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
                                return -1;
                        }
@@ -1667,15 +1786,23 @@ 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)
+                                               if (newcat) {
                                                        ast_category_destroy(newcat);
+                                               }
                                                ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
                                                return -1;
                                        }
                                        if (newcat) {
+                                               ast_config_set_current_category(cfg, *cat);
+                                               (*cat)->ignored |= newcat->ignored;
                                                move_variables(newcat, *cat);
                                                ast_category_destroy(newcat);
                                                newcat = NULL;
@@ -1685,15 +1812,35 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
 
                                        base = ast_category_get(cfg, cur, "TEMPLATES=include");
                                        if (!base) {
+                                               if (newcat) {
+                                                       ast_category_destroy(newcat);
+                                               }
                                                ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
                                                return -1;
                                        }
-                                       ast_category_inherit(*cat, base);
+                                       if (ast_category_inherit(*cat, base)) {
+                                               if (newcat) {
+                                                       ast_category_destroy(newcat);
+                                               }
+                                               ast_log(LOG_ERROR, "Inheritence requested, but allocation failed\n");
+                                               return -1;
+                                       }
                                }
                        }
                }
-               if (newcat)
-                       ast_category_append(cfg, *cat);
+
+               /*
+                * We need to set *last_cat to newcat here regardless.  If the
+                * category is being appended to we have no place for trailing
+                * comments on the appended category.  The appended category
+                * may be in another file or it already has trailing comments
+                * that we would then leak.
+                */
+               *last_var = NULL;
+               *last_cat = newcat;
+               if (newcat) {
+                       ast_category_append(cfg, newcat);
+               }
        } else if (cur[0] == '#') { /* A directive - #include or #exec */
                char *cur2;
                char real_inclusion_name[256];
@@ -1849,7 +1996,7 @@ set_new_variable:
                        } else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
                                v->lineno = lineno;
                                v->object = object;
-                               *last_cat = 0;
+                               *last_cat = NULL;
                                *last_var = v;
                                /* Put and reset comments */
                                v->blanklines = 0;
@@ -1889,8 +2036,8 @@ static struct ast_config *config_text_file_load(const char *database, const char
        struct stat statbuf;
        struct cache_file_mtime *cfmtime = NULL;
        struct cache_file_include *cfinclude;
-       struct ast_variable *last_var = 0;
-       struct ast_category *last_cat = 0;
+       struct ast_variable *last_var = NULL;
+       struct ast_category *last_cat = NULL;
        /*! Growable string buffer */
        struct ast_str *comment_buffer = NULL;  /*!< this will be a comment collector.*/
        struct ast_str *lline_buffer = NULL;    /*!< A buffer for stuff behind the ; */
@@ -2358,11 +2505,35 @@ static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast
 
 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
 {
-       return ast_config_text_file_save(configfile, cfg, generator);
+       return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
 }
 
 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
 {
+       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;
        char fn[PATH_MAX];
        struct ast_variable *var;
@@ -2383,20 +2554,20 @@ int ast_config_text_file_save(const char *configfile, const struct ast_config *c
        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;
        }
 
@@ -2522,13 +2693,27 @@ int ast_config_text_file_save(const char *configfile, const struct ast_config *c
                                AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
                                        struct ast_variable *v;
                                        for (v = x->inst->root; v; v = v->next) {
-                                               if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
-                                                       found = 1;
-                                                       break;
+
+                                               if (flags & CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT) {
+                                                       if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
+                                                               found = 1;
+                                                               break;
+                                                       }
+                                               } else {
+                                                       if (var->inherited) {
+                                                               found = 1;
+                                                               break;
+                                                       } else {
+                                                               if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
+                                                                       found = 1;
+                                                                       break;
+                                                               }
+                                                       }
                                                }
                                        }
-                                       if (found)
+                                       if (found) {
                                                break;
+                                       }
                                }
                                if (found) {
                                        var = var->next;
@@ -2827,6 +3012,7 @@ int ast_realtime_is_mapping_defined(const char *family)
                        return 1;
                }
        }
+       ast_debug(5, "Failed to find a realtime mapping for %s\n", family);
 
        return 0;
 }
@@ -3329,7 +3515,7 @@ int ast_update2_realtime(const char *family, ...)
        va_end(ap);
 
        va_start(ap, family);
-       realtime_arguments_to_fields2(ap, 1, &lookup_fields);
+       realtime_arguments_to_fields2(ap, 1, &update_fields);
        va_end(ap);
 
        if (!lookup_fields || !update_fields) {
@@ -3555,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;
@@ -3799,12 +4034,17 @@ static void config_shutdown(void)
        AST_LIST_UNLOCK(&cfmtime_head);
 
        ast_cli_unregister_multiple(cli_config, ARRAY_LEN(cli_config));
+
+       clear_config_maps();
+
+       ao2_cleanup(cfg_hooks);
+       cfg_hooks = NULL;
 }
 
 int register_config_cli(void)
 {
        ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
-       ast_register_atexit(config_shutdown);
+       ast_register_cleanup(config_shutdown);
        return 0;
 }
 
@@ -3887,5 +4127,6 @@ int ast_config_hook_register(const char *name,
        hook->module = ast_strdup(module);
 
        ao2_link(cfg_hooks, hook);
+       ao2_ref(hook, -1);
        return 0;
 }