2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Configuration File Parser
23 * \author Mark Spencer <markster@digium.com>
25 * Includes the Asterisk Realtime API - ARA
26 * See doc/realtime.txt and doc/extconfig.txt
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33 #include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
34 #include "asterisk/network.h" /* we do some sockaddr manipulation here */
38 #define AST_INCLUDE_GLOB 1
40 #ifdef AST_INCLUDE_GLOB
41 /* glob compat stuff - eventually this should go in compat.h or some
42 * header in include/asterisk/
44 #if defined(__Darwin__) || defined(__CYGWIN__)
45 #define GLOB_ABORTED GLOB_ABEND
51 #define MY_GLOB_FLAGS GLOB_NOCHECK
53 #define MY_GLOB_FLAGS (GLOB_NOMAGIC|GLOB_BRACE)
58 #include "asterisk/config.h"
59 #include "asterisk/cli.h"
60 #include "asterisk/lock.h"
61 #include "asterisk/utils.h"
62 #include "asterisk/channel.h"
63 #include "asterisk/app.h"
64 #include "asterisk/astobj2.h"
65 #include "asterisk/strings.h" /* for the ast_str_*() API */
67 #define MAX_NESTED_COMMENTS 128
68 #define COMMENT_START ";--"
69 #define COMMENT_END "--;"
70 #define COMMENT_META ';'
71 #define COMMENT_TAG '-'
73 static char *extconfig_conf = "extconfig.conf";
76 /*! \brief Structure to keep comments for rewriting configuration files */
78 struct ast_comment *next;
82 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
83 struct cache_file_include {
84 AST_LIST_ENTRY(cache_file_include) list;
88 struct cache_file_mtime {
89 AST_LIST_ENTRY(cache_file_mtime) list;
90 AST_LIST_HEAD(includes, cache_file_include) includes;
91 unsigned int has_exec:1;
96 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
99 /* comment buffers are better implemented using the ast_str_*() API */
100 #define CB_SIZE 250 /* initial size of comment buffers */
102 static void CB_ADD(struct ast_str **cb, const char *str)
104 ast_str_append(cb, 0, "%s", str);
107 static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
109 char *s = alloca(len + 1);
110 ast_copy_string(s, str, len);
111 ast_str_append(cb, 0, "%s", str);
114 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
122 static struct ast_comment *ALLOC_COMMENT(const struct ast_str *buffer)
124 struct ast_comment *x = NULL;
125 if (buffer && buffer->used)
126 x = ast_calloc(1, sizeof(*x) + buffer->used + 1);
128 strcpy(x->cmt, buffer->str);
132 /* I need to keep track of each config file, and all its inclusions,
133 so that we can track blank lines in each */
140 static int hash_string(const void *obj, const int flags)
142 char *str = ((struct inclfile*)obj)->fname;
145 for (total=0; *str; str++) {
146 unsigned int tmp = total;
147 total <<= 1; /* multiply by 2 */
148 total += tmp; /* multiply by 3 */
149 total <<= 2; /* multiply by 12 */
150 total += tmp; /* multiply by 13 */
152 total += ((unsigned int)(*str));
159 static int hashtab_compare_strings(void *a, void *b, int flags)
161 const struct inclfile *ae = a, *be = b;
162 return !strcmp(ae->fname, be->fname) ? CMP_MATCH : 0;
165 static struct ast_config_map {
166 struct ast_config_map *next;
172 } *config_maps = NULL;
174 AST_MUTEX_DEFINE_STATIC(config_lock);
175 static struct ast_config_engine *config_engine_list;
177 #define MAX_INCLUDE_LEVEL 10
179 struct ast_category_template_instance {
180 char name[80]; /* redundant? */
181 const struct ast_category *inst;
182 AST_LIST_ENTRY(ast_category_template_instance) next;
185 struct ast_category {
187 int ignored; /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
189 char *file; /*!< the file name from whence this declaration was read */
191 AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
192 struct ast_comment *precomments;
193 struct ast_comment *sameline;
194 struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
195 struct ast_variable *root;
196 struct ast_variable *last;
197 struct ast_category *next;
201 struct ast_category *root;
202 struct ast_category *last;
203 struct ast_category *current;
204 struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
206 int max_include_level;
207 struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
210 struct ast_config_include {
211 char *include_location_file; /*!< file name in which the include occurs */
212 int include_location_lineno; /*!< lineno where include occurred */
213 int exec; /*!< set to non-zero if itsa #exec statement */
214 char *exec_file; /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
215 char *included_file; /*!< file name included */
216 int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
217 we explode the instances and will include those-- so all entries will be unique */
218 int output; /*!< a flag to indicate if the inclusion has been output */
219 struct ast_config_include *next; /*!< ptr to next inclusion in the list */
222 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
224 struct ast_variable *variable;
225 int name_len = strlen(name) + 1;
226 int val_len = strlen(value) + 1;
227 int fn_len = strlen(filename) + 1;
229 if ((variable = ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable)))) {
230 char *dst = variable->stuff; /* writable space starts here */
231 variable->name = strcpy(dst, name);
233 variable->value = strcpy(dst, value);
235 variable->file = strcpy(dst, filename);
240 struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
242 /* a file should be included ONCE. Otherwise, if one of the instances is changed,
243 then all be changed. -- how do we know to include it? -- Handling modified
244 instances is possible, I'd have
245 to create a new master for each instance. */
246 struct ast_config_include *inc;
249 inc = ast_include_find(conf, included_file);
252 inc->inclusion_count++;
253 snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
254 } while (stat(real_included_file_name, &statbuf) == 0);
255 ast_log(LOG_WARNING,"'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
257 *real_included_file_name = 0;
259 inc = ast_calloc(1,sizeof(struct ast_config_include));
260 inc->include_location_file = ast_strdup(from_file);
261 inc->include_location_lineno = from_lineno;
262 if (!ast_strlen_zero(real_included_file_name))
263 inc->included_file = ast_strdup(real_included_file_name);
265 inc->included_file = ast_strdup(included_file);
269 inc->exec_file = ast_strdup(exec_file);
271 /* attach this new struct to the conf struct */
272 inc->next = conf->includes;
273 conf->includes = inc;
278 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
280 struct ast_config_include *incl;
281 struct ast_category *cat;
282 struct ast_variable *v;
284 int from_len = strlen(from_file);
285 int to_len = strlen(to_file);
287 if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
290 /* the manager code allows you to read in one config file, then
291 write it back out under a different name. But, the new arrangement
292 ties output lines to the file name. So, before you try to write
293 the config file to disk, better riffle thru the data and make sure
294 the file names are changed.
296 /* file names are on categories, includes (of course), and on variables. So,
297 traverse all this and swap names */
299 for (incl = conf->includes; incl; incl=incl->next) {
300 if (strcmp(incl->include_location_file,from_file) == 0) {
301 if (from_len >= to_len)
302 strcpy(incl->include_location_file, to_file);
304 free(incl->include_location_file);
305 incl->include_location_file = strdup(to_file);
309 for (cat = conf->root; cat; cat = cat->next) {
310 if (strcmp(cat->file,from_file) == 0) {
311 if (from_len >= to_len)
312 strcpy(cat->file, to_file);
315 cat->file = strdup(to_file);
318 for (v = cat->root; v; v = v->next) {
319 if (strcmp(v->file,from_file) == 0) {
320 if (from_len >= to_len)
321 strcpy(v->file, to_file);
324 v->file = strdup(to_file);
331 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
333 struct ast_config_include *x;
334 for (x=conf->includes;x;x=x->next) {
335 if (strcmp(x->included_file,included_file) == 0)
342 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
347 category->last->next = variable;
349 category->root = variable;
350 category->last = variable;
351 while (category->last->next)
352 category->last = category->last->next;
355 void ast_variables_destroy(struct ast_variable *v)
357 struct ast_variable *vn;
366 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
368 struct ast_category *cat = NULL;
370 if (category && config->last_browse && (config->last_browse->name == category))
371 cat = config->last_browse;
373 cat = ast_category_get(config, category);
375 return (cat) ? cat->root : NULL;
378 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
381 tmp = ast_variable_retrieve(cfg, cat, var);
383 tmp = ast_variable_retrieve(cfg, "general", var);
388 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
390 struct ast_variable *v;
393 for (v = ast_variable_browse(config, category); v; v = v->next) {
394 if (!strcasecmp(variable, v->name))
398 struct ast_category *cat;
400 for (cat = config->root; cat; cat = cat->next)
401 for (v = cat->root; v; v = v->next)
402 if (!strcasecmp(variable, v->name))
409 static struct ast_variable *variable_clone(const struct ast_variable *old)
411 struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
414 new->lineno = old->lineno;
415 new->object = old->object;
416 new->blanklines = old->blanklines;
417 /* TODO: clone comments? */
423 static void move_variables(struct ast_category *old, struct ast_category *new)
425 struct ast_variable *var = old->root;
428 /* we can just move the entire list in a single op */
429 ast_variable_append(new, var);
432 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
434 struct ast_category *category;
436 if ((category = ast_calloc(1, sizeof(*category))))
437 ast_copy_string(category->name, name, sizeof(category->name));
438 category->file = strdup(in_file);
439 category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
443 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
445 struct ast_category *cat;
447 /* try exact match first, then case-insensitive match */
448 for (cat = config->root; cat; cat = cat->next) {
449 if (cat->name == category_name && (ignored || !cat->ignored))
453 for (cat = config->root; cat; cat = cat->next) {
454 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
461 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
463 return category_get(config, category_name, 0);
466 int ast_category_exist(const struct ast_config *config, const char *category_name)
468 return !!ast_category_get(config, category_name);
471 void ast_category_append(struct ast_config *config, struct ast_category *category)
474 config->last->next = category;
476 config->root = category;
477 category->include_level = config->include_level;
478 config->last = category;
479 config->current = category;
482 static void ast_destroy_comments(struct ast_category *cat)
484 struct ast_comment *n, *p;
486 for (p=cat->precomments; p; p=n) {
490 for (p=cat->sameline; p; p=n) {
494 for (p=cat->trailing; p; p=n) {
498 cat->precomments = NULL;
499 cat->sameline = NULL;
500 cat->trailing = NULL;
503 static void ast_destroy_template_list(struct ast_category *cat)
505 struct ast_category_template_instance *x;
507 while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
511 void ast_category_destroy(struct ast_category *cat)
513 ast_variables_destroy(cat->root);
518 ast_destroy_comments(cat);
519 ast_destroy_template_list(cat);
523 static void ast_includes_destroy(struct ast_config_include *incls)
525 struct ast_config_include *incl,*inclnext;
527 for (incl=incls; incl; incl = inclnext) {
528 inclnext = incl->next;
529 if (incl->include_location_file)
530 free(incl->include_location_file);
532 free(incl->exec_file);
533 if (incl->included_file)
534 free(incl->included_file);
539 static struct ast_category *next_available_category(struct ast_category *cat)
541 for (; cat && cat->ignored; cat = cat->next);
546 /*! return the first var of a category */
547 struct ast_variable *ast_category_first(struct ast_category *cat)
549 return (cat) ? cat->root : NULL;
552 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
554 struct ast_category *category = ast_category_get(config, cat);
557 return category->root;
561 char *ast_category_browse(struct ast_config *config, const char *prev)
563 struct ast_category *cat = NULL;
565 if (prev && config->last_browse && (config->last_browse->name == prev))
566 cat = config->last_browse->next;
567 else if (!prev && config->root)
570 for (cat = config->root; cat; cat = cat->next) {
571 if (cat->name == prev) {
577 for (cat = config->root; cat; cat = cat->next) {
578 if (!strcasecmp(cat->name, prev)) {
587 cat = next_available_category(cat);
589 config->last_browse = cat;
590 return (cat) ? cat->name : NULL;
593 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
595 struct ast_variable *v;
604 void ast_category_rename(struct ast_category *cat, const char *name)
606 ast_copy_string(cat->name, name, sizeof(cat->name));
609 static void inherit_category(struct ast_category *new, const struct ast_category *base)
611 struct ast_variable *var;
612 struct ast_category_template_instance *x = ast_calloc(1,sizeof(struct ast_category_template_instance));
614 strcpy(x->name, base->name);
616 AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
617 for (var = base->root; var; var = var->next)
618 ast_variable_append(new, variable_clone(var));
621 struct ast_config *ast_config_new(void)
623 struct ast_config *config;
625 if ((config = ast_calloc(1, sizeof(*config))))
626 config->max_include_level = MAX_INCLUDE_LEVEL;
630 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match)
632 struct ast_variable *cur, *prev=NULL, *curn;
635 cur = category->root;
637 if (cur->name == variable) {
639 prev->next = cur->next;
640 if (cur == category->last)
641 category->last = prev;
643 category->root = cur->next;
644 if (cur == category->last)
645 category->last = NULL;
648 ast_variables_destroy(cur);
656 cur = category->root;
659 if (!strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match))) {
661 prev->next = cur->next;
662 if (cur == category->last)
663 category->last = prev;
665 category->root = cur->next;
666 if (cur == category->last)
667 category->last = NULL;
670 ast_variables_destroy(cur);
680 int ast_variable_update(struct ast_category *category, const char *variable,
681 const char *value, const char *match, unsigned int object)
683 struct ast_variable *cur, *prev=NULL, *newer=NULL;
685 for (cur = category->root; cur; prev = cur, cur = cur->next) {
686 if (strcasecmp(cur->name, variable) ||
687 (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
690 if (!(newer = ast_variable_new(variable, value, cur->file)))
693 newer->next = cur->next;
694 newer->object = cur->object || object;
698 category->root = newer;
699 if (category->last == cur)
700 category->last = newer;
703 ast_variables_destroy(cur);
711 category->root = newer;
716 int ast_category_delete(struct ast_config *cfg, const char *category)
718 struct ast_category *prev=NULL, *cat;
722 if (cat->name == category) {
724 prev->next = cat->next;
725 if (cat == cfg->last)
728 cfg->root = cat->next;
729 if (cat == cfg->last)
732 ast_category_destroy(cat);
742 if (!strcasecmp(cat->name, category)) {
744 prev->next = cat->next;
745 if (cat == cfg->last)
748 cfg->root = cat->next;
749 if (cat == cfg->last)
752 ast_category_destroy(cat);
761 void ast_config_destroy(struct ast_config *cfg)
763 struct ast_category *cat, *catn;
768 ast_includes_destroy(cfg->includes);
774 ast_category_destroy(catn);
779 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
784 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
786 /* cast below is just to silence compiler warning about dropping "const" */
787 cfg->current = (struct ast_category *) cat;
790 enum config_cache_attribute_enum {
791 ATTRIBUTE_INCLUDE = 0,
795 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename)
797 struct cache_file_mtime *cfmtime;
798 struct cache_file_include *cfinclude;
799 struct stat statbuf = { 0, };
801 /* Find our cached entry for this configuration file */
802 AST_LIST_LOCK(&cfmtime_head);
803 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
804 if (!strcmp(cfmtime->filename, configfile))
808 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1);
810 AST_LIST_UNLOCK(&cfmtime_head);
813 AST_LIST_HEAD_INIT(&cfmtime->includes);
814 strcpy(cfmtime->filename, configfile);
815 /* Note that the file mtime is initialized to 0, i.e. 1970 */
816 AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
819 if (!stat(configfile, &statbuf))
822 cfmtime->mtime = statbuf.st_mtime;
825 case ATTRIBUTE_INCLUDE:
826 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
827 if (!strcmp(cfinclude->include, filename)) {
828 AST_LIST_UNLOCK(&cfmtime_head);
832 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
834 AST_LIST_UNLOCK(&cfmtime_head);
837 strcpy(cfinclude->include, filename);
838 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
841 cfmtime->has_exec = 1;
844 AST_LIST_UNLOCK(&cfmtime_head);
847 /*! \brief parse one line in the configuration.
848 * We can have a category header [foo](...)
849 * a directive #include / #exec
850 * or a regular line name = value
852 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
853 char *buf, int lineno, const char *configfile, struct ast_flags flags,
854 struct ast_str *comment_buffer,
855 struct ast_str *lline_buffer,
856 const char *suggested_include_file,
857 struct ast_category **last_cat, struct ast_variable **last_var)
861 struct ast_variable *v;
862 char cmd[512], exec_file[512];
864 /* Actually parse the entry */
865 if (cur[0] == '[') { /* A category header */
866 /* format is one of the following:
867 * [foo] define a new category named 'foo'
868 * [foo](!) define a new template category named 'foo'
869 * [foo](+) append to category 'foo', error if foo does not exist.
870 * [foo](a) define a new category and inherit from template a.
871 * You can put a comma-separated list of templates and '!' and '+'
872 * between parentheses, with obvious meaning.
874 struct ast_category *newcat = NULL;
877 c = strchr(cur, ']');
879 ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
887 if (!(*cat = newcat = ast_category_new(catname, ast_strlen_zero(suggested_include_file)?configfile:suggested_include_file, lineno))) {
890 (*cat)->lineno = lineno;
895 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
896 newcat->precomments = ALLOC_COMMENT(comment_buffer);
897 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
898 newcat->sameline = ALLOC_COMMENT(lline_buffer);
899 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
900 CB_RESET(comment_buffer, lline_buffer);
902 /* If there are options or categories to inherit from, process them now */
904 if (!(cur = strchr(c, ')'))) {
905 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
909 while ((cur = strsep(&c, ","))) {
910 if (!strcasecmp(cur, "!")) {
912 } else if (!strcasecmp(cur, "+")) {
913 *cat = category_get(cfg, catname, 1);
916 ast_category_destroy(newcat);
917 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
921 move_variables(newcat, *cat);
922 ast_category_destroy(newcat);
926 struct ast_category *base;
928 base = category_get(cfg, cur, 1);
930 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
933 inherit_category(*cat, base);
938 ast_category_append(cfg, *cat);
939 } else if (cur[0] == '#') { /* A directive - #include or #exec */
941 char real_inclusion_name[256];
942 struct ast_config_include *inclu;
943 int do_include = 0; /* otherwise, it is exec */
947 while (*c && (*c > 32)) c++;
950 /* Find real argument */
951 c = ast_skip_blanks(c + 1);
956 if (!strcasecmp(cur, "include")) {
958 } else if (!strcasecmp(cur, "exec")) {
959 if (!ast_opt_exec_includes) {
960 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
961 return 0; /* XXX is this correct ? or we should return -1 ? */
964 ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
965 return 0; /* XXX is this correct ? or we should return -1 ? */
969 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
970 do_include ? "include" : "exec",
971 do_include ? "filename" : "/path/to/executable",
974 return 0; /* XXX is this correct ? or we should return -1 ? */
977 /* Strip off leading and trailing "'s and <>'s */
978 while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
979 /* Get rid of leading mess */
982 while (!ast_strlen_zero(cur)) {
983 c = cur + strlen(cur) - 1;
984 if ((*c == '>') || (*c == '<') || (*c == '\"'))
989 /* #exec </path/to/executable>
990 We create a tmp file, then we #include it, then we delete it. */
992 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
993 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL);
994 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self());
995 snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
996 ast_safe_system(cmd);
999 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1000 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur);
1001 exec_file[0] = '\0';
1004 /* record this inclusion */
1005 inclu = ast_include_new(cfg, configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1007 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name) ? 1 : 0;
1008 if (!ast_strlen_zero(exec_file))
1011 ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1014 /* XXX otherwise what ? the default return is 0 anyways */
1017 /* Just a line (variable = value) */
1019 ast_log(LOG_WARNING,
1020 "parse error: No category context for line %d of %s\n", lineno, configfile);
1023 c = strchr(cur, '=');
1028 /* Ignore > in => */
1034 if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), *suggested_include_file ? suggested_include_file : configfile))) {
1039 /* Put and reset comments */
1041 ast_variable_append(*cat, v);
1043 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1044 v->precomments = ALLOC_COMMENT(comment_buffer);
1045 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1046 v->sameline = ALLOC_COMMENT(lline_buffer);
1047 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1048 CB_RESET(comment_buffer, lline_buffer);
1054 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1060 static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
1064 char *new_buf, *comment_p, *process_buf;
1067 int comment = 0, nest[MAX_NESTED_COMMENTS];
1068 struct ast_category *cat = NULL;
1070 struct stat statbuf;
1071 struct cache_file_mtime *cfmtime = NULL;
1072 struct cache_file_include *cfinclude;
1073 struct ast_variable *last_var = 0;
1074 struct ast_category *last_cat = 0;
1075 /*! Growable string buffer */
1076 struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
1077 struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */
1080 cat = ast_config_get_current_category(cfg);
1082 if (filename[0] == '/') {
1083 ast_copy_string(fn, filename, sizeof(fn));
1085 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
1088 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1089 comment_buffer = ast_str_create(CB_SIZE);
1091 lline_buffer = ast_str_create(CB_SIZE);
1092 if (!lline_buffer) {
1094 ast_free(comment_buffer);
1095 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1099 #ifdef AST_INCLUDE_GLOB
1103 globbuf.gl_offs = 0; /* initialize it to silence gcc */
1104 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1105 if (glob_ret == GLOB_NOSPACE)
1106 ast_log(LOG_WARNING,
1107 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1108 else if (glob_ret == GLOB_ABORTED)
1109 ast_log(LOG_WARNING,
1110 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1112 /* loop over expanded files */
1114 for (i=0; i<globbuf.gl_pathc; i++) {
1115 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1118 * The following is not a loop, but just a convenient way to define a block
1119 * (using do { } while(0) ), and be able to exit from it with 'continue'
1120 * or 'break' in case of errors. Nice trick.
1123 if (stat(fn, &statbuf))
1126 if (!S_ISREG(statbuf.st_mode)) {
1127 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1131 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1132 /* Find our cached entry for this configuration file */
1133 AST_LIST_LOCK(&cfmtime_head);
1134 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1135 if (!strcmp(cfmtime->filename, fn))
1139 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1);
1142 AST_LIST_HEAD_INIT(&cfmtime->includes);
1143 strcpy(cfmtime->filename, fn);
1144 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1145 AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
1149 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1150 /* File is unchanged, what about the (cached) includes (if any)? */
1152 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1153 /* We must glob here, because if we did not, then adding a file to globbed directory would
1154 * incorrectly cause no reload to be necessary. */
1156 #ifdef AST_INCLUDE_GLOB
1158 glob_t globbuf = { .gl_offs = 0 };
1159 glob_ret = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &globbuf);
1160 /* On error, we reparse */
1161 if (glob_ret == GLOB_NOSPACE || glob_ret == GLOB_ABORTED)
1164 /* loop over expanded files */
1166 for (j = 0; j < globbuf.gl_pathc; j++) {
1167 ast_copy_string(fn2, globbuf.gl_pathv[j], sizeof(fn2));
1169 ast_copy_string(fn2, cfinclude->include);
1171 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "") == NULL) { /* that last field needs to be looked at in this case... TODO */
1173 /* One change is enough to short-circuit and reload the whole shebang */
1176 #ifdef AST_INCLUDE_GLOB
1183 AST_LIST_UNLOCK(&cfmtime_head);
1184 return CONFIG_STATUS_FILEUNCHANGED;
1187 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1188 AST_LIST_UNLOCK(&cfmtime_head);
1190 /* If cfg is NULL, then we just want an answer */
1195 cfmtime->mtime = statbuf.st_mtime;
1197 ast_verb(2, "Parsing '%s': ", fn);
1199 if (!(f = fopen(fn, "r"))) {
1200 ast_debug(1, "No file to parse: %s\n", fn);
1201 ast_verb(2, "Not found (%s)\n", strerror(errno));
1205 /* If we get to this point, then we're loading regardless */
1206 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1207 ast_debug(1, "Parsing %s\n", fn);
1208 ast_verb(2, "Found\n");
1211 if (fgets(buf, sizeof(buf), f)) {
1212 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && lline_buffer->used) {
1213 CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
1214 lline_buffer->used = 0; /* erase the lline buffer */
1223 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
1224 /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1225 CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
1226 continue; /* go get a new line, then */
1229 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1230 if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
1231 /* Escaped semicolons aren't comments. */
1232 new_buf = comment_p + 1;
1233 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1234 /* Meta-Comment start detected ";--" */
1235 if (comment < MAX_NESTED_COMMENTS) {
1237 new_buf = comment_p + 3;
1239 nest[comment-1] = lineno;
1241 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1243 } else if ((comment_p >= new_buf + 2) &&
1244 (*(comment_p - 1) == COMMENT_TAG) &&
1245 (*(comment_p - 2) == COMMENT_TAG)) {
1246 /* Meta-Comment end detected */
1248 new_buf = comment_p + 1;
1250 /* Back to non-comment now */
1252 /* Actually have to move what's left over the top, then continue */
1254 oldptr = process_buf + strlen(process_buf);
1255 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1256 CB_ADD(&comment_buffer, ";");
1257 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1260 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1263 process_buf = new_buf;
1267 /* If ; is found, and we are not nested in a comment,
1268 we immediately stop all comment processing */
1269 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1270 CB_ADD(&lline_buffer, comment_p);
1273 new_buf = comment_p;
1275 new_buf = comment_p + 1;
1278 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1279 CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
1283 char *buf = ast_strip(process_buf);
1284 if (!ast_strlen_zero(buf)) {
1285 if (process_text_line(cfg, &cat, buf, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var)) {
1293 /* end of file-- anything in a comment buffer? */
1295 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
1296 if (lline_buffer && lline_buffer->used) {
1297 CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
1298 lline_buffer->used = 0; /* erase the lline buffer */
1300 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1302 } else if (last_var) {
1303 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
1304 if (lline_buffer && lline_buffer->used) {
1305 CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
1306 lline_buffer->used = 0; /* erase the lline buffer */
1308 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1311 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used) {
1312 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", comment_buffer->str);
1315 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1316 CB_RESET(comment_buffer, lline_buffer);
1321 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1323 #ifdef AST_INCLUDE_GLOB
1324 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1332 if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1334 ast_free(comment_buffer);
1336 ast_free(lline_buffer);
1337 comment_buffer = NULL;
1338 lline_buffer = NULL;
1348 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1349 which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1350 recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1351 be shocked and mystified as to why things are not showing up in the files!
1353 Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1354 and line number are stored for each include, plus the name of the file included, so that these statements may be
1355 included in the output files on a file_save operation.
1357 The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1358 are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1359 the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1360 and a header gets added.
1362 vars and category heads are output in the order they are stored in the config file. So, if the software
1363 shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1364 file/lineno data probably won't get changed.
1368 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1374 ast_copy_string(date, ctime(&t), sizeof(date));
1376 fprintf(f1, ";!\n");
1377 fprintf(f1, ";! Automatically generated configuration file\n");
1378 if (strcmp(configfile, fn))
1379 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1381 fprintf(f1, ";! Filename: %s\n", configfile);
1382 fprintf(f1, ";! Generator: %s\n", generator);
1383 fprintf(f1, ";! Creation Date: %s", date);
1384 fprintf(f1, ";!\n");
1387 static void inclfile_destroy(void *obj)
1389 const struct inclfile *o = obj;
1396 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
1398 struct inclfile lookup;
1400 if (!file || file[0] == 0) {
1401 if (configfile[0] == '/')
1402 ast_copy_string(fn, configfile, fn_size);
1404 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1405 } else if (file[0] == '/')
1406 ast_copy_string(fn, file, fn_size);
1408 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1410 *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1412 /* set up a file scratch pad */
1413 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1414 fx->fname = ast_strdup(fn);
1417 ao2_link(fileset, fx);
1421 static int count_linefeeds(char *str)
1433 static int count_linefeeds_in_comments(struct ast_comment *x)
1438 count += count_linefeeds(x->cmt);
1444 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1446 int precomment_lines = count_linefeeds_in_comments(precomments);
1449 /* I don't have to worry about those ;! comments, they are
1450 stored in the precomments, but not printed back out.
1451 I did have to make sure that comments following
1452 the ;! header comments were not also deleted in the process */
1453 for (i=fi->lineno;i<lineno - precomment_lines; i++) {
1456 fi->lineno = lineno+1; /* Advance the file lineno */
1459 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1463 struct ast_variable *var;
1464 struct ast_category *cat;
1465 struct ast_comment *cmt;
1466 struct ast_config_include *incl;
1468 struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
1469 struct inclfile *fi = 0;
1471 /* reset all the output flags, in case this isn't our first time saving this data */
1473 for (incl=cfg->includes; incl; incl = incl->next)
1476 /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1477 are all truncated to zero bytes and have that nice header*/
1479 for (incl=cfg->includes; incl; incl = incl->next)
1481 if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
1484 set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
1487 gen_header(f1, configfile, fn, generator);
1488 fclose(f1); /* this should zero out the file */
1490 ast_debug(1, "Unable to open for writing: %s\n", fn);
1491 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1493 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1498 set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
1500 if ((f = fopen(fn, "w+"))) {
1502 if ((f = fopen(fn, "w"))) {
1504 ast_verb(2, "Saving '%s': ", fn);
1505 gen_header(f, configfile, fn, generator);
1508 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1510 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1511 /* since each var, cat, and associated comments can come from any file, we have to be
1512 mobile, and open each file, print, and close it on an entry-by-entry basis */
1515 set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
1519 ast_debug(1, "Unable to open for writing: %s\n", fn);
1520 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1521 ao2_ref(fileset, -1);
1525 /* dump any includes that happen before this category header */
1526 for (incl=cfg->includes; incl; incl = incl->next) {
1527 if (strcmp(incl->include_location_file, cat->file) == 0){
1528 if (cat->lineno > incl->include_location_lineno && !incl->output) {
1530 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1532 fprintf(f,"#include \"%s\"\n", incl->included_file);
1538 insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1539 /* Dump section with any appropriate comment */
1540 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1541 char *cmtp = cmt->cmt;
1542 while (*cmtp == ';' && *(cmtp+1) == '!') {
1543 char *cmtp2 = strchr(cmtp+1, '\n');
1549 fprintf(f,"%s", cmtp);
1551 if (!cat->precomments)
1553 fprintf(f, "[%s]", cat->name);
1556 if (!AST_LIST_EMPTY(&cat->template_instances)) {
1557 struct ast_category_template_instance *x;
1559 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1560 fprintf(f,"%s",x->name);
1561 if (x != AST_LIST_LAST(&cat->template_instances))
1566 for(cmt = cat->sameline; cmt; cmt=cmt->next)
1568 fprintf(f,"%s", cmt->cmt);
1572 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1573 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1574 fprintf(f,"%s", cmt->cmt);
1577 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1582 struct ast_category_template_instance *x;
1584 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1585 struct ast_variable *v;
1586 for (v = x->inst->root; v; v = v->next) {
1587 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
1599 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
1603 ast_debug(1, "Unable to open for writing: %s\n", fn);
1604 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1605 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1607 ao2_ref(fileset, -1);
1611 /* dump any includes that happen before this category header */
1612 for (incl=cfg->includes; incl; incl = incl->next) {
1613 if (strcmp(incl->include_location_file, var->file) == 0){
1614 if (var->lineno > incl->include_location_lineno && !incl->output) {
1616 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1618 fprintf(f,"#include \"%s\"\n", incl->included_file);
1624 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1625 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1626 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1627 fprintf(f,"%s", cmt->cmt);
1630 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1632 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1633 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1634 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1635 fprintf(f,"%s", cmt->cmt);
1637 if (var->blanklines) {
1638 blanklines = var->blanklines;
1639 while (blanklines--)
1644 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1652 ast_verb(2, "Saved\n");
1654 ast_debug(1, "Unable to open for writing: %s\n", fn);
1655 ast_verb(2, "Unable to write (%s)", strerror(errno));
1656 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1657 ao2_ref(fileset, -1);
1661 /* Now, for files with trailing #include/#exec statements,
1662 we have to make sure every entry is output */
1664 for (incl=cfg->includes; incl; incl = incl->next) {
1665 if (!incl->output) {
1666 /* open the respective file */
1667 set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
1671 ast_debug(1, "Unable to open for writing: %s\n", fn);
1672 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1673 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1675 ao2_ref(fileset, -1);
1679 /* output the respective include */
1681 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1683 fprintf(f,"#include \"%s\"\n", incl->included_file);
1686 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1690 ao2_ref(fileset, -1); /* this should destroy the hash container */
1695 static void clear_config_maps(void)
1697 struct ast_config_map *map;
1699 ast_mutex_lock(&config_lock);
1701 while (config_maps) {
1703 config_maps = config_maps->next;
1707 ast_mutex_unlock(&config_lock);
1710 static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
1712 struct ast_config_map *map;
1715 length = sizeof(*map);
1716 length += strlen(name) + 1;
1717 length += strlen(driver) + 1;
1718 length += strlen(database) + 1;
1720 length += strlen(table) + 1;
1722 if (!(map = ast_calloc(1, length)))
1725 map->name = map->stuff;
1726 strcpy(map->name, name);
1727 map->driver = map->name + strlen(map->name) + 1;
1728 strcpy(map->driver, driver);
1729 map->database = map->driver + strlen(map->driver) + 1;
1730 strcpy(map->database, database);
1732 map->table = map->database + strlen(map->database) + 1;
1733 strcpy(map->table, table);
1735 map->next = config_maps;
1737 ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
1743 int read_config_maps(void)
1745 struct ast_config *config, *configtmp;
1746 struct ast_variable *v;
1747 char *driver, *table, *database, *stringp, *tmp;
1748 struct ast_flags flags = { 0 };
1750 clear_config_maps();
1752 configtmp = ast_config_new();
1753 configtmp->max_include_level = 1;
1754 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "");
1756 ast_config_destroy(configtmp);
1760 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
1762 ast_copy_string(buf, v->value, sizeof(buf));
1764 driver = strsep(&stringp, ",");
1766 if ((tmp = strchr(stringp, '\"')))
1769 /* check if the database text starts with a double quote */
1770 if (*stringp == '"') {
1772 database = strsep(&stringp, "\"");
1773 strsep(&stringp, ",");
1775 /* apparently this text has no quotes */
1776 database = strsep(&stringp, ",");
1779 table = strsep(&stringp, ",");
1781 if (!strcmp(v->name, extconfig_conf)) {
1782 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
1786 if (!strcmp(v->name, "asterisk.conf")) {
1787 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
1791 if (!strcmp(v->name, "logger.conf")) {
1792 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
1796 if (!driver || !database)
1798 if (!strcasecmp(v->name, "sipfriends")) {
1799 ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
1800 append_mapping("sipusers", driver, database, table ? table : "sipfriends");
1801 append_mapping("sippeers", driver, database, table ? table : "sipfriends");
1802 } else if (!strcasecmp(v->name, "iaxfriends")) {
1803 ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
1804 append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
1805 append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
1807 append_mapping(v->name, driver, database, table);
1810 ast_config_destroy(config);
1814 int ast_config_engine_register(struct ast_config_engine *new)
1816 struct ast_config_engine *ptr;
1818 ast_mutex_lock(&config_lock);
1820 if (!config_engine_list) {
1821 config_engine_list = new;
1823 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
1827 ast_mutex_unlock(&config_lock);
1828 ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
1833 int ast_config_engine_deregister(struct ast_config_engine *del)
1835 struct ast_config_engine *ptr, *last=NULL;
1837 ast_mutex_lock(&config_lock);
1839 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
1842 last->next = ptr->next;
1844 config_engine_list = ptr->next;
1850 ast_mutex_unlock(&config_lock);
1855 /*! \brief Find realtime engine for realtime family */
1856 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
1858 struct ast_config_engine *eng, *ret = NULL;
1859 struct ast_config_map *map;
1861 ast_mutex_lock(&config_lock);
1863 for (map = config_maps; map; map = map->next) {
1864 if (!strcasecmp(family, map->name)) {
1866 ast_copy_string(database, map->database, dbsiz);
1868 ast_copy_string(table, map->table ? map->table : family, tabsiz);
1873 /* Check if the required driver (engine) exist */
1875 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
1876 if (!strcasecmp(eng->name, map->driver))
1881 ast_mutex_unlock(&config_lock);
1883 /* if we found a mapping, but the engine is not available, then issue a warning */
1885 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
1890 static struct ast_config_engine text_file_engine = {
1892 .load_func = config_text_file_load,
1895 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
1899 struct ast_config_engine *loader = &text_file_engine;
1900 struct ast_config *result;
1902 if (cfg->include_level == cfg->max_include_level) {
1903 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
1907 cfg->include_level++;
1909 if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
1910 struct ast_config_engine *eng;
1912 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
1915 if (eng && eng->load_func) {
1918 eng = find_engine("global", db, sizeof(db), table, sizeof(table));
1919 if (eng && eng->load_func)
1924 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file);
1926 if (result && result != CONFIG_STATUS_FILEUNCHANGED)
1927 result->include_level--;
1929 cfg->include_level--;
1934 struct ast_config *ast_config_load(const char *filename, struct ast_flags flags)
1936 struct ast_config *cfg;
1937 struct ast_config *result;
1939 cfg = ast_config_new();
1943 result = ast_config_internal_load(filename, cfg, flags, "");
1944 if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
1945 ast_config_destroy(cfg);
1950 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
1952 struct ast_config_engine *eng;
1955 struct ast_variable *res=NULL;
1957 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1958 if (eng && eng->realtime_func)
1959 res = eng->realtime_func(db, table, ap);
1964 struct ast_variable *ast_load_realtime_all(const char *family, ...)
1966 struct ast_variable *res;
1969 va_start(ap, family);
1970 res = ast_load_realtime_helper(family, ap);
1976 struct ast_variable *ast_load_realtime(const char *family, ...)
1978 struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
1981 va_start(ap, family);
1982 res = ast_load_realtime_helper(family, ap);
1985 /* Eliminate blank entries */
1986 for (cur = res; cur; cur = cur->next) {
1992 if (ast_strlen_zero(cur->value)) {
1994 prev->next = cur->next;
2005 /*! \brief Check if realtime engine is configured for family */
2006 int ast_check_realtime(const char *family)
2008 struct ast_config_engine *eng;
2010 eng = find_engine(family, NULL, 0, NULL, 0);
2016 /*! \brief Check if there's any realtime engines loaded */
2017 int ast_realtime_enabled()
2019 return config_maps ? 1 : 0;
2022 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2024 struct ast_config_engine *eng;
2027 struct ast_config *res=NULL;
2030 va_start(ap, family);
2031 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2032 if (eng && eng->realtime_multi_func)
2033 res = eng->realtime_multi_func(db, table, ap);
2039 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2041 struct ast_config_engine *eng;
2047 va_start(ap, lookup);
2048 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2049 if (eng && eng->update_func)
2050 res = eng->update_func(db, table, keyfield, lookup, ap);
2056 int ast_store_realtime(const char *family, ...) {
2057 struct ast_config_engine *eng;
2063 va_start(ap, family);
2064 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2065 if (eng && eng->store_func)
2066 res = eng->store_func(db, table, ap);
2072 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) {
2073 struct ast_config_engine *eng;
2079 va_start(ap, lookup);
2080 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2081 if (eng && eng->destroy_func)
2082 res = eng->destroy_func(db, table, keyfield, lookup, ap);
2088 /*! \brief Helper function to parse arguments
2089 * See documentation in config.h
2091 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2092 void *p_result, ...)
2097 va_start(ap, p_result);
2098 switch (flags & PARSE_TYPE) {
2101 int32_t *result = p_result;
2102 int32_t x, def = result ? *result : 0,
2103 high = (int32_t)0x7fffffff,
2104 low = (int32_t)0x80000000;
2105 /* optional argument: first default value, then range */
2106 if (flags & PARSE_DEFAULT)
2107 def = va_arg(ap, int32_t);
2108 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2109 /* range requested, update bounds */
2110 low = va_arg(ap, int32_t);
2111 high = va_arg(ap, int32_t);
2113 x = strtol(arg, NULL, 0);
2114 error = (x < low) || (x > high);
2115 if (flags & PARSE_OUT_RANGE)
2118 *result = error ? def : x;
2120 "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2122 result ? *result : x, error);
2128 uint32_t *result = p_result;
2129 uint32_t x, def = result ? *result : 0,
2130 low = 0, high = (uint32_t)~0;
2131 /* optional argument: first default value, then range */
2132 if (flags & PARSE_DEFAULT)
2133 def = va_arg(ap, uint32_t);
2134 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2135 /* range requested, update bounds */
2136 low = va_arg(ap, uint32_t);
2137 high = va_arg(ap, uint32_t);
2139 x = strtoul(arg, NULL, 0);
2140 error = (x < low) || (x > high);
2141 if (flags & PARSE_OUT_RANGE)
2144 *result = error ? def : x;
2146 "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2148 result ? *result : x, error);
2155 struct sockaddr_in _sa_buf; /* buffer for the result */
2156 struct sockaddr_in *sa = p_result ?
2157 (struct sockaddr_in *)p_result : &_sa_buf;
2158 /* default is either the supplied value or the result itself */
2159 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2160 va_arg(ap, struct sockaddr_in *) : sa;
2162 struct ast_hostent ahp;
2164 bzero(&_sa_buf, sizeof(_sa_buf)); /* clear buffer */
2165 /* duplicate the string to strip away the :port */
2166 port = ast_strdupa(arg);
2167 buf = strsep(&port, ":");
2168 sa->sin_family = AF_INET; /* assign family */
2170 * honor the ports flag setting, assign default value
2171 * in case of errors or field unset.
2173 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2175 if (flags == PARSE_PORT_FORBID) {
2176 error = 1; /* port was forbidden */
2177 sa->sin_port = def->sin_port;
2178 } else if (flags == PARSE_PORT_IGNORE)
2179 sa->sin_port = def->sin_port;
2180 else /* accept or require */
2181 sa->sin_port = htons(strtol(port, NULL, 0));
2183 sa->sin_port = def->sin_port;
2184 if (flags == PARSE_PORT_REQUIRE)
2187 /* Now deal with host part, even if we have errors before. */
2188 hp = ast_gethostbyname(buf, &ahp);
2189 if (hp) /* resolved successfully */
2190 memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2193 sa->sin_addr = def->sin_addr;
2196 "extract inaddr from [%s] gives [%s:%d](%d)\n",
2197 arg, ast_inet_ntoa(sa->sin_addr),
2198 ntohs(sa->sin_port), error);
2206 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2208 struct ast_config_engine *eng;
2209 struct ast_config_map *map;
2213 e->command = "core show config mappings";
2215 "Usage: core show config mappings\n"
2216 " Shows the filenames to config engines.\n";
2222 ast_mutex_lock(&config_lock);
2224 ast_cli(a->fd, "\n\n");
2225 for (eng = config_engine_list; eng; eng = eng->next) {
2226 ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
2227 for (map = config_maps; map; map = map->next)
2228 if (!strcasecmp(map->driver, eng->name)) {
2229 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2230 map->table ? map->table : map->name);
2233 ast_cli(a->fd,"\n\n");
2235 ast_mutex_unlock(&config_lock);
2240 static struct ast_cli_entry cli_config[] = {
2241 AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
2244 int register_config_cli()
2246 ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));