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))
1012 /* XXX otherwise what ? the default return is 0 anyways */
1015 /* Just a line (variable = value) */
1017 ast_log(LOG_WARNING,
1018 "parse error: No category context for line %d of %s\n", lineno, configfile);
1021 c = strchr(cur, '=');
1026 /* Ignore > in => */
1032 if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), *suggested_include_file ? suggested_include_file : configfile))) {
1037 /* Put and reset comments */
1039 ast_variable_append(*cat, v);
1041 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1042 v->precomments = ALLOC_COMMENT(comment_buffer);
1043 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1044 v->sameline = ALLOC_COMMENT(lline_buffer);
1045 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1046 CB_RESET(comment_buffer, lline_buffer);
1052 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1058 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)
1062 char *new_buf, *comment_p, *process_buf;
1065 int comment = 0, nest[MAX_NESTED_COMMENTS];
1066 struct ast_category *cat = NULL;
1068 struct stat statbuf;
1069 struct cache_file_mtime *cfmtime = NULL;
1070 struct cache_file_include *cfinclude;
1071 struct ast_variable *last_var = 0;
1072 struct ast_category *last_cat = 0;
1073 /*! Growable string buffer */
1074 struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
1075 struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */
1078 cat = ast_config_get_current_category(cfg);
1080 if (filename[0] == '/') {
1081 ast_copy_string(fn, filename, sizeof(fn));
1083 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
1086 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1087 comment_buffer = ast_str_create(CB_SIZE);
1089 lline_buffer = ast_str_create(CB_SIZE);
1090 if (!lline_buffer) {
1092 ast_free(comment_buffer);
1093 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1097 #ifdef AST_INCLUDE_GLOB
1101 globbuf.gl_offs = 0; /* initialize it to silence gcc */
1102 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1103 if (glob_ret == GLOB_NOSPACE)
1104 ast_log(LOG_WARNING,
1105 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1106 else if (glob_ret == GLOB_ABORTED)
1107 ast_log(LOG_WARNING,
1108 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1110 /* loop over expanded files */
1112 for (i=0; i<globbuf.gl_pathc; i++) {
1113 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1116 * The following is not a loop, but just a convenient way to define a block
1117 * (using do { } while(0) ), and be able to exit from it with 'continue'
1118 * or 'break' in case of errors. Nice trick.
1121 if (stat(fn, &statbuf))
1124 if (!S_ISREG(statbuf.st_mode)) {
1125 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1129 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1130 /* Find our cached entry for this configuration file */
1131 AST_LIST_LOCK(&cfmtime_head);
1132 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1133 if (!strcmp(cfmtime->filename, fn))
1137 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1);
1140 AST_LIST_HEAD_INIT(&cfmtime->includes);
1141 strcpy(cfmtime->filename, fn);
1142 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1143 AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
1147 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1148 /* File is unchanged, what about the (cached) includes (if any)? */
1150 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1151 /* We must glob here, because if we did not, then adding a file to globbed directory would
1152 * incorrectly cause no reload to be necessary. */
1154 #ifdef AST_INCLUDE_GLOB
1156 glob_t globbuf = { .gl_offs = 0 };
1157 glob_ret = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &globbuf);
1158 /* On error, we reparse */
1159 if (glob_ret == GLOB_NOSPACE || glob_ret == GLOB_ABORTED)
1162 /* loop over expanded files */
1164 for (j = 0; j < globbuf.gl_pathc; j++) {
1165 ast_copy_string(fn2, globbuf.gl_pathv[j], sizeof(fn2));
1167 ast_copy_string(fn2, cfinclude->include);
1169 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "") == NULL) { /* that last field needs to be looked at in this case... TODO */
1171 /* One change is enough to short-circuit and reload the whole shebang */
1174 #ifdef AST_INCLUDE_GLOB
1181 AST_LIST_UNLOCK(&cfmtime_head);
1182 return CONFIG_STATUS_FILEUNCHANGED;
1185 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1186 AST_LIST_UNLOCK(&cfmtime_head);
1188 /* If cfg is NULL, then we just want an answer */
1193 cfmtime->mtime = statbuf.st_mtime;
1195 ast_verb(2, "Parsing '%s': ", fn);
1197 if (!(f = fopen(fn, "r"))) {
1198 ast_debug(1, "No file to parse: %s\n", fn);
1199 ast_verb(2, "Not found (%s)\n", strerror(errno));
1203 /* If we get to this point, then we're loading regardless */
1204 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1205 ast_debug(1, "Parsing %s\n", fn);
1206 ast_verb(2, "Found\n");
1209 if (fgets(buf, sizeof(buf), f)) {
1210 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && lline_buffer->used) {
1211 CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
1212 lline_buffer->used = 0; /* erase the lline buffer */
1221 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
1222 /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1223 CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
1224 continue; /* go get a new line, then */
1227 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1228 if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
1229 /* Escaped semicolons aren't comments. */
1230 new_buf = comment_p + 1;
1231 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1232 /* Meta-Comment start detected ";--" */
1233 if (comment < MAX_NESTED_COMMENTS) {
1235 new_buf = comment_p + 3;
1237 nest[comment-1] = lineno;
1239 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1241 } else if ((comment_p >= new_buf + 2) &&
1242 (*(comment_p - 1) == COMMENT_TAG) &&
1243 (*(comment_p - 2) == COMMENT_TAG)) {
1244 /* Meta-Comment end detected */
1246 new_buf = comment_p + 1;
1248 /* Back to non-comment now */
1250 /* Actually have to move what's left over the top, then continue */
1252 oldptr = process_buf + strlen(process_buf);
1253 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1254 CB_ADD(&comment_buffer, ";");
1255 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1258 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1261 process_buf = new_buf;
1265 /* If ; is found, and we are not nested in a comment,
1266 we immediately stop all comment processing */
1267 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1268 CB_ADD(&lline_buffer, comment_p);
1271 new_buf = comment_p;
1273 new_buf = comment_p + 1;
1276 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1277 CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
1281 char *buf = ast_strip(process_buf);
1282 if (!ast_strlen_zero(buf)) {
1283 if (process_text_line(cfg, &cat, buf, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var)) {
1291 /* end of file-- anything in a comment buffer? */
1293 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
1294 if (lline_buffer && lline_buffer->used) {
1295 CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
1296 lline_buffer->used = 0; /* erase the lline buffer */
1298 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1300 } else if (last_var) {
1301 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
1302 if (lline_buffer && lline_buffer->used) {
1303 CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
1304 lline_buffer->used = 0; /* erase the lline buffer */
1306 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1309 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used) {
1310 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", comment_buffer->str);
1313 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1314 CB_RESET(comment_buffer, lline_buffer);
1319 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1321 #ifdef AST_INCLUDE_GLOB
1322 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1330 if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1332 ast_free(comment_buffer);
1334 ast_free(lline_buffer);
1335 comment_buffer = NULL;
1336 lline_buffer = NULL;
1346 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1347 which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1348 recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1349 be shocked and mystified as to why things are not showing up in the files!
1351 Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1352 and line number are stored for each include, plus the name of the file included, so that these statements may be
1353 included in the output files on a file_save operation.
1355 The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1356 are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1357 the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1358 and a header gets added.
1360 vars and category heads are output in the order they are stored in the config file. So, if the software
1361 shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1362 file/lineno data probably won't get changed.
1366 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1372 ast_copy_string(date, ctime(&t), sizeof(date));
1374 fprintf(f1, ";!\n");
1375 fprintf(f1, ";! Automatically generated configuration file\n");
1376 if (strcmp(configfile, fn))
1377 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1379 fprintf(f1, ";! Filename: %s\n", configfile);
1380 fprintf(f1, ";! Generator: %s\n", generator);
1381 fprintf(f1, ";! Creation Date: %s", date);
1382 fprintf(f1, ";!\n");
1385 static void inclfile_destroy(void *obj)
1387 const struct inclfile *o = obj;
1394 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
1396 struct inclfile lookup;
1398 if (!file || file[0] == 0) {
1399 if (configfile[0] == '/')
1400 ast_copy_string(fn, configfile, fn_size);
1402 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1403 } else if (file[0] == '/')
1404 ast_copy_string(fn, file, fn_size);
1406 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1408 *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1410 /* set up a file scratch pad */
1411 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1412 fx->fname = ast_strdup(fn);
1415 ao2_link(fileset, fx);
1419 static int count_linefeeds(char *str)
1431 static int count_linefeeds_in_comments(struct ast_comment *x)
1436 count += count_linefeeds(x->cmt);
1442 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1444 int precomment_lines = count_linefeeds_in_comments(precomments);
1447 /* I don't have to worry about those ;! comments, they are
1448 stored in the precomments, but not printed back out.
1449 I did have to make sure that comments following
1450 the ;! header comments were not also deleted in the process */
1451 for (i=fi->lineno;i<lineno - precomment_lines; i++) {
1454 fi->lineno = lineno+1; /* Advance the file lineno */
1457 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1461 struct ast_variable *var;
1462 struct ast_category *cat;
1463 struct ast_comment *cmt;
1464 struct ast_config_include *incl;
1466 struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
1467 struct inclfile *fi = 0;
1469 /* reset all the output flags, in case this isn't our first time saving this data */
1471 for (incl=cfg->includes; incl; incl = incl->next)
1474 /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1475 are all truncated to zero bytes and have that nice header*/
1477 for (incl=cfg->includes; incl; incl = incl->next)
1479 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*/
1482 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 */
1485 gen_header(f1, configfile, fn, generator);
1486 fclose(f1); /* this should zero out the file */
1488 ast_debug(1, "Unable to open for writing: %s\n", fn);
1489 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1491 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1496 set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
1498 if ((f = fopen(fn, "w+"))) {
1500 if ((f = fopen(fn, "w"))) {
1502 ast_verb(2, "Saving '%s': ", fn);
1503 gen_header(f, configfile, fn, generator);
1506 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1508 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1509 /* since each var, cat, and associated comments can come from any file, we have to be
1510 mobile, and open each file, print, and close it on an entry-by-entry basis */
1513 set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
1517 ast_debug(1, "Unable to open for writing: %s\n", fn);
1518 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1519 ao2_ref(fileset, -1);
1523 /* dump any includes that happen before this category header */
1524 for (incl=cfg->includes; incl; incl = incl->next) {
1525 if (strcmp(incl->include_location_file, cat->file) == 0){
1526 if (cat->lineno > incl->include_location_lineno && !incl->output) {
1528 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1530 fprintf(f,"#include \"%s\"\n", incl->included_file);
1536 insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1537 /* Dump section with any appropriate comment */
1538 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1539 char *cmtp = cmt->cmt;
1540 while (*cmtp == ';' && *(cmtp+1) == '!') {
1541 char *cmtp2 = strchr(cmtp+1, '\n');
1547 fprintf(f,"%s", cmtp);
1549 if (!cat->precomments)
1551 fprintf(f, "[%s]", cat->name);
1554 if (!AST_LIST_EMPTY(&cat->template_instances)) {
1555 struct ast_category_template_instance *x;
1557 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1558 fprintf(f,"%s",x->name);
1559 if (x != AST_LIST_LAST(&cat->template_instances))
1564 for(cmt = cat->sameline; cmt; cmt=cmt->next)
1566 fprintf(f,"%s", cmt->cmt);
1570 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1571 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1572 fprintf(f,"%s", cmt->cmt);
1575 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1580 struct ast_category_template_instance *x;
1582 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1583 struct ast_variable *v;
1584 for (v = x->inst->root; v; v = v->next) {
1585 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
1597 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
1601 ast_debug(1, "Unable to open for writing: %s\n", fn);
1602 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1603 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1605 ao2_ref(fileset, -1);
1609 /* dump any includes that happen before this category header */
1610 for (incl=cfg->includes; incl; incl = incl->next) {
1611 if (strcmp(incl->include_location_file, var->file) == 0){
1612 if (var->lineno > incl->include_location_lineno && !incl->output) {
1614 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1616 fprintf(f,"#include \"%s\"\n", incl->included_file);
1622 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1623 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1624 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1625 fprintf(f,"%s", cmt->cmt);
1628 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1630 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1631 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1632 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1633 fprintf(f,"%s", cmt->cmt);
1635 if (var->blanklines) {
1636 blanklines = var->blanklines;
1637 while (blanklines--)
1642 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1650 ast_verb(2, "Saved\n");
1652 ast_debug(1, "Unable to open for writing: %s\n", fn);
1653 ast_verb(2, "Unable to write (%s)", strerror(errno));
1654 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1655 ao2_ref(fileset, -1);
1659 /* Now, for files with trailing #include/#exec statements,
1660 we have to make sure every entry is output */
1662 for (incl=cfg->includes; incl; incl = incl->next) {
1663 if (!incl->output) {
1664 /* open the respective file */
1665 set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
1669 ast_debug(1, "Unable to open for writing: %s\n", fn);
1670 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1671 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1673 ao2_ref(fileset, -1);
1677 /* output the respective include */
1679 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1681 fprintf(f,"#include \"%s\"\n", incl->included_file);
1684 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1688 ao2_ref(fileset, -1); /* this should destroy the hash container */
1693 static void clear_config_maps(void)
1695 struct ast_config_map *map;
1697 ast_mutex_lock(&config_lock);
1699 while (config_maps) {
1701 config_maps = config_maps->next;
1705 ast_mutex_unlock(&config_lock);
1708 static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
1710 struct ast_config_map *map;
1713 length = sizeof(*map);
1714 length += strlen(name) + 1;
1715 length += strlen(driver) + 1;
1716 length += strlen(database) + 1;
1718 length += strlen(table) + 1;
1720 if (!(map = ast_calloc(1, length)))
1723 map->name = map->stuff;
1724 strcpy(map->name, name);
1725 map->driver = map->name + strlen(map->name) + 1;
1726 strcpy(map->driver, driver);
1727 map->database = map->driver + strlen(map->driver) + 1;
1728 strcpy(map->database, database);
1730 map->table = map->database + strlen(map->database) + 1;
1731 strcpy(map->table, table);
1733 map->next = config_maps;
1735 ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
1741 int read_config_maps(void)
1743 struct ast_config *config, *configtmp;
1744 struct ast_variable *v;
1745 char *driver, *table, *database, *stringp, *tmp;
1746 struct ast_flags flags = { 0 };
1748 clear_config_maps();
1750 configtmp = ast_config_new();
1751 configtmp->max_include_level = 1;
1752 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "");
1754 ast_config_destroy(configtmp);
1758 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
1760 ast_copy_string(buf, v->value, sizeof(buf));
1762 driver = strsep(&stringp, ",");
1764 if ((tmp = strchr(stringp, '\"')))
1767 /* check if the database text starts with a double quote */
1768 if (*stringp == '"') {
1770 database = strsep(&stringp, "\"");
1771 strsep(&stringp, ",");
1773 /* apparently this text has no quotes */
1774 database = strsep(&stringp, ",");
1777 table = strsep(&stringp, ",");
1779 if (!strcmp(v->name, extconfig_conf)) {
1780 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
1784 if (!strcmp(v->name, "asterisk.conf")) {
1785 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
1789 if (!strcmp(v->name, "logger.conf")) {
1790 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
1794 if (!driver || !database)
1796 if (!strcasecmp(v->name, "sipfriends")) {
1797 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");
1798 append_mapping("sipusers", driver, database, table ? table : "sipfriends");
1799 append_mapping("sippeers", driver, database, table ? table : "sipfriends");
1800 } else if (!strcasecmp(v->name, "iaxfriends")) {
1801 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");
1802 append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
1803 append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
1805 append_mapping(v->name, driver, database, table);
1808 ast_config_destroy(config);
1812 int ast_config_engine_register(struct ast_config_engine *new)
1814 struct ast_config_engine *ptr;
1816 ast_mutex_lock(&config_lock);
1818 if (!config_engine_list) {
1819 config_engine_list = new;
1821 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
1825 ast_mutex_unlock(&config_lock);
1826 ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
1831 int ast_config_engine_deregister(struct ast_config_engine *del)
1833 struct ast_config_engine *ptr, *last=NULL;
1835 ast_mutex_lock(&config_lock);
1837 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
1840 last->next = ptr->next;
1842 config_engine_list = ptr->next;
1848 ast_mutex_unlock(&config_lock);
1853 /*! \brief Find realtime engine for realtime family */
1854 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
1856 struct ast_config_engine *eng, *ret = NULL;
1857 struct ast_config_map *map;
1859 ast_mutex_lock(&config_lock);
1861 for (map = config_maps; map; map = map->next) {
1862 if (!strcasecmp(family, map->name)) {
1864 ast_copy_string(database, map->database, dbsiz);
1866 ast_copy_string(table, map->table ? map->table : family, tabsiz);
1871 /* Check if the required driver (engine) exist */
1873 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
1874 if (!strcasecmp(eng->name, map->driver))
1879 ast_mutex_unlock(&config_lock);
1881 /* if we found a mapping, but the engine is not available, then issue a warning */
1883 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
1888 static struct ast_config_engine text_file_engine = {
1890 .load_func = config_text_file_load,
1893 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
1897 struct ast_config_engine *loader = &text_file_engine;
1898 struct ast_config *result;
1900 if (cfg->include_level == cfg->max_include_level) {
1901 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
1905 cfg->include_level++;
1907 if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
1908 struct ast_config_engine *eng;
1910 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
1913 if (eng && eng->load_func) {
1916 eng = find_engine("global", db, sizeof(db), table, sizeof(table));
1917 if (eng && eng->load_func)
1922 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file);
1924 if (result && result != CONFIG_STATUS_FILEUNCHANGED)
1925 result->include_level--;
1927 cfg->include_level--;
1932 struct ast_config *ast_config_load(const char *filename, struct ast_flags flags)
1934 struct ast_config *cfg;
1935 struct ast_config *result;
1937 cfg = ast_config_new();
1941 result = ast_config_internal_load(filename, cfg, flags, "");
1942 if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
1943 ast_config_destroy(cfg);
1948 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
1950 struct ast_config_engine *eng;
1953 struct ast_variable *res=NULL;
1955 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1956 if (eng && eng->realtime_func)
1957 res = eng->realtime_func(db, table, ap);
1962 struct ast_variable *ast_load_realtime_all(const char *family, ...)
1964 struct ast_variable *res;
1967 va_start(ap, family);
1968 res = ast_load_realtime_helper(family, ap);
1974 struct ast_variable *ast_load_realtime(const char *family, ...)
1976 struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
1979 va_start(ap, family);
1980 res = ast_load_realtime_helper(family, ap);
1983 /* Eliminate blank entries */
1984 for (cur = res; cur; cur = cur->next) {
1990 if (ast_strlen_zero(cur->value)) {
1992 prev->next = cur->next;
2003 /*! \brief Check if realtime engine is configured for family */
2004 int ast_check_realtime(const char *family)
2006 struct ast_config_engine *eng;
2008 eng = find_engine(family, NULL, 0, NULL, 0);
2014 /*! \brief Check if there's any realtime engines loaded */
2015 int ast_realtime_enabled()
2017 return config_maps ? 1 : 0;
2020 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2022 struct ast_config_engine *eng;
2025 struct ast_config *res=NULL;
2028 va_start(ap, family);
2029 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2030 if (eng && eng->realtime_multi_func)
2031 res = eng->realtime_multi_func(db, table, ap);
2037 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2039 struct ast_config_engine *eng;
2045 va_start(ap, lookup);
2046 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2047 if (eng && eng->update_func)
2048 res = eng->update_func(db, table, keyfield, lookup, ap);
2054 int ast_store_realtime(const char *family, ...) {
2055 struct ast_config_engine *eng;
2061 va_start(ap, family);
2062 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2063 if (eng && eng->store_func)
2064 res = eng->store_func(db, table, ap);
2070 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) {
2071 struct ast_config_engine *eng;
2077 va_start(ap, lookup);
2078 eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2079 if (eng && eng->destroy_func)
2080 res = eng->destroy_func(db, table, keyfield, lookup, ap);
2086 /*! \brief Helper function to parse arguments
2087 * See documentation in config.h
2089 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2090 void *p_result, ...)
2095 va_start(ap, p_result);
2096 switch (flags & PARSE_TYPE) {
2099 int32_t *result = p_result;
2100 int32_t x, def = result ? *result : 0,
2101 high = (int32_t)0x7fffffff,
2102 low = (int32_t)0x80000000;
2103 /* optional argument: first default value, then range */
2104 if (flags & PARSE_DEFAULT)
2105 def = va_arg(ap, int32_t);
2106 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2107 /* range requested, update bounds */
2108 low = va_arg(ap, int32_t);
2109 high = va_arg(ap, int32_t);
2111 x = strtol(arg, NULL, 0);
2112 error = (x < low) || (x > high);
2113 if (flags & PARSE_OUT_RANGE)
2116 *result = error ? def : x;
2118 "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2120 result ? *result : x, error);
2126 uint32_t *result = p_result;
2127 uint32_t x, def = result ? *result : 0,
2128 low = 0, high = (uint32_t)~0;
2129 /* optional argument: first default value, then range */
2130 if (flags & PARSE_DEFAULT)
2131 def = va_arg(ap, uint32_t);
2132 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2133 /* range requested, update bounds */
2134 low = va_arg(ap, uint32_t);
2135 high = va_arg(ap, uint32_t);
2137 x = strtoul(arg, NULL, 0);
2138 error = (x < low) || (x > high);
2139 if (flags & PARSE_OUT_RANGE)
2142 *result = error ? def : x;
2144 "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2146 result ? *result : x, error);
2153 struct sockaddr_in _sa_buf; /* buffer for the result */
2154 struct sockaddr_in *sa = p_result ?
2155 (struct sockaddr_in *)p_result : &_sa_buf;
2156 /* default is either the supplied value or the result itself */
2157 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2158 va_arg(ap, struct sockaddr_in *) : sa;
2160 struct ast_hostent ahp;
2162 bzero(&_sa_buf, sizeof(_sa_buf)); /* clear buffer */
2163 /* duplicate the string to strip away the :port */
2164 port = ast_strdupa(arg);
2165 buf = strsep(&port, ":");
2166 sa->sin_family = AF_INET; /* assign family */
2168 * honor the ports flag setting, assign default value
2169 * in case of errors or field unset.
2171 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2173 if (flags == PARSE_PORT_FORBID) {
2174 error = 1; /* port was forbidden */
2175 sa->sin_port = def->sin_port;
2176 } else if (flags == PARSE_PORT_IGNORE)
2177 sa->sin_port = def->sin_port;
2178 else /* accept or require */
2179 sa->sin_port = htons(strtol(port, NULL, 0));
2181 sa->sin_port = def->sin_port;
2182 if (flags == PARSE_PORT_REQUIRE)
2185 /* Now deal with host part, even if we have errors before. */
2186 hp = ast_gethostbyname(buf, &ahp);
2187 if (hp) /* resolved successfully */
2188 memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2191 sa->sin_addr = def->sin_addr;
2194 "extract inaddr from [%s] gives [%s:%d](%d)\n",
2195 arg, ast_inet_ntoa(sa->sin_addr),
2196 ntohs(sa->sin_port), error);
2204 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2206 struct ast_config_engine *eng;
2207 struct ast_config_map *map;
2211 e->command = "core show config mappings";
2213 "Usage: core show config mappings\n"
2214 " Shows the filenames to config engines.\n";
2220 ast_mutex_lock(&config_lock);
2222 ast_cli(a->fd, "\n\n");
2223 for (eng = config_engine_list; eng; eng = eng->next) {
2224 ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
2225 for (map = config_maps; map; map = map->next)
2226 if (!strcasecmp(map->driver, eng->name)) {
2227 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2228 map->table ? map->table : map->name);
2231 ast_cli(a->fd,"\n\n");
2233 ast_mutex_unlock(&config_lock);
2238 static struct ast_cli_entry cli_config[] = {
2239 AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
2242 int register_config_cli()
2244 ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));