2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2010, 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 http://wiki.asterisk.org
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 #include <math.h> /* HUGE_VAL */
40 #define AST_INCLUDE_GLOB 1
42 #include "asterisk/config.h"
43 #include "asterisk/cli.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/app.h"
48 #include "asterisk/astobj2.h"
49 #include "asterisk/strings.h" /* for the ast_str_*() API */
50 #include "asterisk/netsock2.h"
52 #define MAX_NESTED_COMMENTS 128
53 #define COMMENT_START ";--"
54 #define COMMENT_END "--;"
55 #define COMMENT_META ';'
56 #define COMMENT_TAG '-'
59 * Define the minimum filename space to reserve for each
60 * ast_variable in case the filename is renamed later by
61 * ast_include_rename().
63 #define MIN_VARIABLE_FNAME_SPACE 40
65 static char *extconfig_conf = "extconfig.conf";
68 /*! \brief Structure to keep comments for rewriting configuration files */
70 struct ast_comment *next;
71 /*! Comment body allocated after struct. */
75 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
76 struct cache_file_include {
77 AST_LIST_ENTRY(cache_file_include) list;
81 struct cache_file_mtime {
82 AST_LIST_ENTRY(cache_file_mtime) list;
83 AST_LIST_HEAD_NOLOCK(includes, cache_file_include) includes;
84 unsigned int has_exec:1;
87 /*! String stuffed in filename[] after the filename string. */
88 const char *who_asked;
89 /*! Filename and who_asked stuffed after it. */
93 /*! Cached file mtime list. */
94 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
96 static int init_appendbuf(void *data)
98 struct ast_str **str = data;
99 *str = ast_str_create(16);
100 return *str ? 0 : -1;
103 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
105 /* comment buffers are better implemented using the ast_str_*() API */
106 #define CB_SIZE 250 /* initial size of comment buffers */
108 static void CB_ADD(struct ast_str **cb, const char *str)
110 ast_str_append(cb, 0, "%s", str);
113 static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
115 char *s = alloca(len + 1);
116 ast_copy_string(s, str, len);
117 ast_str_append(cb, 0, "%s", str);
120 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
130 static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
132 struct ast_comment *x = NULL;
133 if (!buffer || !ast_str_strlen(buffer)) {
136 if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
137 strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
142 /* I need to keep track of each config file, and all its inclusions,
143 so that we can track blank lines in each */
150 static int hash_string(const void *obj, const int flags)
152 char *str = ((struct inclfile *) obj)->fname;
155 for (total = 0; *str; str++) {
156 unsigned int tmp = total;
157 total <<= 1; /* multiply by 2 */
158 total += tmp; /* multiply by 3 */
159 total <<= 2; /* multiply by 12 */
160 total += tmp; /* multiply by 13 */
162 total += ((unsigned int) (*str));
170 static int hashtab_compare_strings(void *a, void *b, int flags)
172 const struct inclfile *ae = a, *be = b;
173 return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
176 static struct ast_config_map {
177 struct ast_config_map *next;
179 /*! Stored in stuff[] at struct end. */
181 /*! Stored in stuff[] at struct end. */
183 /*! Stored in stuff[] at struct end. */
184 const char *database;
185 /*! Stored in stuff[] at struct end. */
187 /*! Contents of name, driver, database, and table in that order stuffed here. */
189 } *config_maps = NULL;
191 AST_MUTEX_DEFINE_STATIC(config_lock);
192 static struct ast_config_engine *config_engine_list;
194 #define MAX_INCLUDE_LEVEL 10
196 struct ast_category_template_instance {
197 char name[80]; /* redundant? */
198 const struct ast_category *inst;
199 AST_LIST_ENTRY(ast_category_template_instance) next;
202 struct ast_category {
204 int ignored; /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
207 * \brief The file name from whence this declaration was read
208 * \note Will never be NULL
212 AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
213 struct ast_comment *precomments;
214 struct ast_comment *sameline;
215 struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
216 /*! First category variable in the list. */
217 struct ast_variable *root;
218 /*! Last category variable in the list. */
219 struct ast_variable *last;
220 /*! Next node in the list. */
221 struct ast_category *next;
225 /*! First config category in the list. */
226 struct ast_category *root;
227 /*! Last config category in the list. */
228 struct ast_category *last;
229 struct ast_category *current;
230 struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
232 int max_include_level;
233 struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
236 struct ast_config_include {
238 * \brief file name in which the include occurs
239 * \note Will never be NULL
241 char *include_location_file;
242 int include_location_lineno; /*!< lineno where include occurred */
243 int exec; /*!< set to non-zero if its a #exec statement */
245 * \brief if it's an exec, you'll have both the /var/tmp to read, and the original script
246 * \note Will never be NULL if exec is non-zero
250 * \brief file name included
251 * \note Will never be NULL
254 int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
255 we explode the instances and will include those-- so all entries will be unique */
256 int output; /*!< a flag to indicate if the inclusion has been output */
257 struct ast_config_include *next; /*!< ptr to next inclusion in the list */
260 static void ast_variable_destroy(struct ast_variable *doomed);
261 static void ast_includes_destroy(struct ast_config_include *incls);
264 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
266 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
269 struct ast_variable *variable;
270 int name_len = strlen(name) + 1;
271 int val_len = strlen(value) + 1;
272 int fn_len = strlen(filename) + 1;
274 /* Ensure a minimum length in case the filename is changed later. */
275 if (fn_len < MIN_VARIABLE_FNAME_SPACE) {
276 fn_len = MIN_VARIABLE_FNAME_SPACE;
281 (variable = __ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable), file, lineno, func))
283 (variable = ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable)))
286 char *dst = variable->stuff; /* writable space starts here */
288 /* Put file first so ast_include_rename() can calculate space available. */
289 variable->file = strcpy(dst, filename);
291 variable->name = strcpy(dst, name);
293 variable->value = strcpy(dst, value);
300 * \brief Move the contents from the source to the destination variable.
302 * \param dst_var Destination variable node
303 * \param src_var Source variable node
307 static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable *src_var)
309 dst_var->lineno = src_var->lineno;
310 dst_var->object = src_var->object;
311 dst_var->blanklines = src_var->blanklines;
312 dst_var->precomments = src_var->precomments;
313 src_var->precomments = NULL;
314 dst_var->sameline = src_var->sameline;
315 src_var->sameline = NULL;
316 dst_var->trailing = src_var->trailing;
317 src_var->trailing = NULL;
320 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)
322 /* a file should be included ONCE. Otherwise, if one of the instances is changed,
323 * then all be changed. -- how do we know to include it? -- Handling modified
324 * instances is possible, I'd have
325 * to create a new master for each instance. */
326 struct ast_config_include *inc;
329 inc = ast_include_find(conf, included_file);
332 inc->inclusion_count++;
333 snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
334 } while (stat(real_included_file_name, &statbuf) == 0);
335 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);
337 *real_included_file_name = 0;
339 inc = ast_calloc(1,sizeof(struct ast_config_include));
343 inc->include_location_file = ast_strdup(from_file);
344 inc->include_location_lineno = from_lineno;
345 if (!ast_strlen_zero(real_included_file_name))
346 inc->included_file = ast_strdup(real_included_file_name);
348 inc->included_file = ast_strdup(included_file);
352 inc->exec_file = ast_strdup(exec_file);
354 if (!inc->include_location_file
355 || !inc->included_file
356 || (is_exec && !inc->exec_file)) {
357 ast_includes_destroy(inc);
361 /* attach this new struct to the conf struct */
362 inc->next = conf->includes;
363 conf->includes = inc;
368 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
370 struct ast_config_include *incl;
371 struct ast_category *cat;
374 int from_len = strlen(from_file);
375 int to_len = strlen(to_file);
377 if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
380 /* the manager code allows you to read in one config file, then
381 * write it back out under a different name. But, the new arrangement
382 * ties output lines to the file name. So, before you try to write
383 * the config file to disk, better riffle thru the data and make sure
384 * the file names are changed.
386 /* file names are on categories, includes (of course), and on variables. So,
387 * traverse all this and swap names */
389 for (incl = conf->includes; incl; incl=incl->next) {
390 if (strcmp(incl->include_location_file,from_file) == 0) {
391 if (from_len >= to_len)
392 strcpy(incl->include_location_file, to_file);
394 /* Keep the old filename if the allocation fails. */
395 str = ast_strdup(to_file);
397 ast_free(incl->include_location_file);
398 incl->include_location_file = str;
403 for (cat = conf->root; cat; cat = cat->next) {
404 struct ast_variable **prev;
405 struct ast_variable *v;
406 struct ast_variable *new_var;
408 if (strcmp(cat->file,from_file) == 0) {
409 if (from_len >= to_len)
410 strcpy(cat->file, to_file);
412 /* Keep the old filename if the allocation fails. */
413 str = ast_strdup(to_file);
420 for (prev = &cat->root, v = cat->root; v; prev = &v->next, v = v->next) {
421 if (strcmp(v->file, from_file)) {
426 * Calculate actual space available. The file string is
427 * intentionally stuffed before the name string just so we can
430 if (to_len < v->name - v->file) {
431 /* The new name will fit in the available space. */
432 str = (char *) v->file;/* Stupid compiler complains about discarding qualifiers even though I used a cast. */
433 strcpy(str, to_file);/* SAFE */
437 /* Keep the old filename if the allocation fails. */
438 new_var = ast_variable_new(v->name, v->value, to_file);
443 /* Move items from the old list node to the replacement node. */
444 ast_variable_move(new_var, v);
446 /* Replace the old node in the list with the new node. */
447 new_var->next = v->next;
448 if (cat->last == v) {
453 ast_variable_destroy(v);
460 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
462 struct ast_config_include *x;
463 for (x=conf->includes;x;x=x->next) {
464 if (strcmp(x->included_file,included_file) == 0)
471 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
476 category->last->next = variable;
478 category->root = variable;
479 category->last = variable;
480 while (category->last->next)
481 category->last = category->last->next;
484 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
486 struct ast_variable *cur = category->root;
490 if (!variable || sscanf(line, "%30d", &insertline) != 1) {
494 variable->next = category->root;
495 category->root = variable;
497 for (lineno = 1; lineno < insertline; lineno++) {
503 variable->next = cur->next;
504 cur->next = variable;
508 static void ast_comment_destroy(struct ast_comment **comment)
510 struct ast_comment *n, *p;
512 for (p = *comment; p; p = n) {
520 static void ast_variable_destroy(struct ast_variable *doomed)
522 ast_comment_destroy(&doomed->precomments);
523 ast_comment_destroy(&doomed->sameline);
524 ast_comment_destroy(&doomed->trailing);
528 struct ast_variable *ast_variables_dup(struct ast_variable *var)
530 struct ast_variable *cloned;
531 struct ast_variable *tmp;
533 if (!(cloned = ast_variable_new(var->name, var->value, var->file))) {
539 while ((var = var->next)) {
540 if (!(tmp->next = ast_variable_new(var->name, var->value, var->file))) {
541 ast_variables_destroy(cloned);
550 void ast_variables_destroy(struct ast_variable *v)
552 struct ast_variable *vn;
557 ast_variable_destroy(vn);
561 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
563 struct ast_category *cat = NULL;
565 if (category && config->last_browse && (config->last_browse->name == category)) {
566 cat = config->last_browse;
568 cat = ast_category_get(config, category);
571 return (cat) ? cat->root : NULL;
574 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
577 tmp = ast_variable_retrieve(cfg, cat, var);
579 tmp = ast_variable_retrieve(cfg, "general", var);
585 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
587 struct ast_variable *v;
590 for (v = ast_variable_browse(config, category); v; v = v->next) {
591 if (!strcasecmp(variable, v->name)) {
596 struct ast_category *cat;
598 for (cat = config->root; cat; cat = cat->next) {
599 for (v = cat->root; v; v = v->next) {
600 if (!strcasecmp(variable, v->name)) {
610 static struct ast_variable *variable_clone(const struct ast_variable *old)
612 struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
615 new->lineno = old->lineno;
616 new->object = old->object;
617 new->blanklines = old->blanklines;
618 /* TODO: clone comments? */
624 static void move_variables(struct ast_category *old, struct ast_category *new)
626 struct ast_variable *var = old->root;
629 /* we can just move the entire list in a single op */
630 ast_variable_append(new, var);
633 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
635 struct ast_category *category;
637 category = ast_calloc(1, sizeof(*category));
641 category->file = ast_strdup(in_file);
642 if (!category->file) {
643 ast_category_destroy(category);
646 ast_copy_string(category->name, name, sizeof(category->name));
647 category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
651 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
653 struct ast_category *cat;
655 /* try exact match first, then case-insensitive match */
656 for (cat = config->root; cat; cat = cat->next) {
657 if (cat->name == category_name && (ignored || !cat->ignored))
661 for (cat = config->root; cat; cat = cat->next) {
662 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
669 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
671 return category_get(config, category_name, 0);
674 int ast_category_exist(const struct ast_config *config, const char *category_name)
676 return !!ast_category_get(config, category_name);
679 void ast_category_append(struct ast_config *config, struct ast_category *category)
682 config->last->next = category;
684 config->root = category;
685 category->include_level = config->include_level;
686 config->last = category;
687 config->current = category;
690 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
692 struct ast_category *cur_category;
696 if (!strcasecmp(config->root->name, match)) {
697 cat->next = config->root;
701 for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
702 if (!strcasecmp(cur_category->next->name, match)) {
703 cat->next = cur_category->next;
704 cur_category->next = cat;
710 static void ast_destroy_template_list(struct ast_category *cat)
712 struct ast_category_template_instance *x;
714 while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
718 void ast_category_destroy(struct ast_category *cat)
720 ast_variables_destroy(cat->root);
723 ast_comment_destroy(&cat->precomments);
724 ast_comment_destroy(&cat->sameline);
725 ast_comment_destroy(&cat->trailing);
726 ast_destroy_template_list(cat);
731 static void ast_includes_destroy(struct ast_config_include *incls)
733 struct ast_config_include *incl,*inclnext;
735 for (incl=incls; incl; incl = inclnext) {
736 inclnext = incl->next;
737 ast_free(incl->include_location_file);
738 ast_free(incl->exec_file);
739 ast_free(incl->included_file);
744 static struct ast_category *next_available_category(struct ast_category *cat)
746 for (; cat && cat->ignored; cat = cat->next);
751 /*! return the first var of a category */
752 struct ast_variable *ast_category_first(struct ast_category *cat)
754 return (cat) ? cat->root : NULL;
757 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
759 struct ast_category *category = ast_category_get(config, cat);
762 return category->root;
766 char *ast_category_browse(struct ast_config *config, const char *prev)
768 struct ast_category *cat;
771 /* First time browse. */
773 } else if (config->last_browse && (config->last_browse->name == prev)) {
774 /* Simple last browse found. */
775 cat = config->last_browse->next;
778 * Config changed since last browse.
780 * First try cheap last browse search. (Rebrowsing a different
781 * previous category?)
783 for (cat = config->root; cat; cat = cat->next) {
784 if (cat->name == prev) {
792 * Have to do it the hard way. (Last category was deleted and
795 for (cat = config->root; cat; cat = cat->next) {
796 if (!strcasecmp(cat->name, prev)) {
806 cat = next_available_category(cat);
808 config->last_browse = cat;
809 return (cat) ? cat->name : NULL;
812 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
814 struct ast_variable *v;
823 void ast_category_rename(struct ast_category *cat, const char *name)
825 ast_copy_string(cat->name, name, sizeof(cat->name));
828 static void inherit_category(struct ast_category *new, const struct ast_category *base)
830 struct ast_variable *var;
831 struct ast_category_template_instance *x;
833 x = ast_calloc(1, sizeof(*x));
837 strcpy(x->name, base->name);
839 AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
840 for (var = base->root; var; var = var->next)
841 ast_variable_append(new, variable_clone(var));
844 struct ast_config *ast_config_new(void)
846 struct ast_config *config;
848 if ((config = ast_calloc(1, sizeof(*config))))
849 config->max_include_level = MAX_INCLUDE_LEVEL;
853 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
855 struct ast_variable *cur, *prev=NULL, *curn;
861 if (!ast_strlen_zero(line)) {
862 /* Requesting to delete by item number. */
863 if (sscanf(line, "%30d", &req_item) != 1
865 /* Invalid item number to delete. */
871 cur = category->root;
874 /* Delete by item number or by variable name with optional value. */
875 if ((0 <= req_item && num_item == req_item)
876 || (req_item < 0 && !strcasecmp(cur->name, variable)
877 && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
879 prev->next = cur->next;
880 if (cur == category->last)
881 category->last = prev;
883 category->root = cur->next;
884 if (cur == category->last)
885 category->last = NULL;
887 ast_variable_destroy(cur);
898 int ast_variable_update(struct ast_category *category, const char *variable,
899 const char *value, const char *match, unsigned int object)
901 struct ast_variable *cur, *prev=NULL, *newer=NULL;
903 for (cur = category->root; cur; prev = cur, cur = cur->next) {
904 if (strcasecmp(cur->name, variable) ||
905 (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
908 if (!(newer = ast_variable_new(variable, value, cur->file)))
911 ast_variable_move(newer, cur);
912 newer->object = newer->object || object;
914 /* Replace the old node in the list with the new node. */
915 newer->next = cur->next;
919 category->root = newer;
920 if (category->last == cur)
921 category->last = newer;
923 ast_variable_destroy(cur);
928 /* Could not find variable to update */
932 int ast_category_delete(struct ast_config *cfg, const char *category)
934 struct ast_category *prev=NULL, *cat;
938 if (cat->name == category) {
940 prev->next = cat->next;
941 if (cat == cfg->last)
944 cfg->root = cat->next;
945 if (cat == cfg->last)
948 ast_category_destroy(cat);
958 if (!strcasecmp(cat->name, category)) {
960 prev->next = cat->next;
961 if (cat == cfg->last)
964 cfg->root = cat->next;
965 if (cat == cfg->last)
968 ast_category_destroy(cat);
977 int ast_category_empty(struct ast_config *cfg, const char *category)
979 struct ast_category *cat;
981 for (cat = cfg->root; cat; cat = cat->next) {
982 if (!strcasecmp(cat->name, category))
984 ast_variables_destroy(cat->root);
993 void ast_config_destroy(struct ast_config *cfg)
995 struct ast_category *cat, *catn;
1000 ast_includes_destroy(cfg->includes);
1006 ast_category_destroy(catn);
1011 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
1013 return cfg->current;
1016 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
1018 /* cast below is just to silence compiler warning about dropping "const" */
1019 cfg->current = (struct ast_category *) cat;
1024 * \brief Create a new cfmtime list node.
1026 * \param filename Config filename caching.
1027 * \param who_asked Who wanted to know.
1029 * \retval cfmtime New node on success.
1030 * \retval NULL on error.
1032 static struct cache_file_mtime *cfmtime_new(const char *filename, const char *who_asked)
1034 struct cache_file_mtime *cfmtime;
1037 cfmtime = ast_calloc(1,
1038 sizeof(*cfmtime) + strlen(filename) + 1 + strlen(who_asked) + 1);
1042 dst = cfmtime->filename; /* writable space starts here */
1043 strcpy(dst, filename);
1044 dst += strlen(dst) + 1;
1045 cfmtime->who_asked = strcpy(dst, who_asked);
1050 enum config_cache_attribute_enum {
1051 ATTRIBUTE_INCLUDE = 0,
1055 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
1057 struct cache_file_mtime *cfmtime;
1058 struct cache_file_include *cfinclude;
1059 struct stat statbuf = { 0, };
1061 /* Find our cached entry for this configuration file */
1062 AST_LIST_LOCK(&cfmtime_head);
1063 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1064 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
1068 cfmtime = cfmtime_new(configfile, who_asked);
1070 AST_LIST_UNLOCK(&cfmtime_head);
1073 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1074 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1077 if (!stat(configfile, &statbuf))
1080 cfmtime->mtime = statbuf.st_mtime;
1083 case ATTRIBUTE_INCLUDE:
1084 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1085 if (!strcmp(cfinclude->include, filename)) {
1086 AST_LIST_UNLOCK(&cfmtime_head);
1090 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
1092 AST_LIST_UNLOCK(&cfmtime_head);
1095 strcpy(cfinclude->include, filename);
1096 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
1098 case ATTRIBUTE_EXEC:
1099 cfmtime->has_exec = 1;
1102 AST_LIST_UNLOCK(&cfmtime_head);
1105 /*! \brief parse one line in the configuration.
1107 * We can have a category header [foo](...)
1108 * a directive #include / #exec
1109 * or a regular line name = value
1112 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
1113 char *buf, int lineno, const char *configfile, struct ast_flags flags,
1114 struct ast_str *comment_buffer,
1115 struct ast_str *lline_buffer,
1116 const char *suggested_include_file,
1117 struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
1121 struct ast_variable *v;
1122 char cmd[512], exec_file[512];
1124 /* Actually parse the entry */
1125 if (cur[0] == '[') { /* A category header */
1126 /* format is one of the following:
1127 * [foo] define a new category named 'foo'
1128 * [foo](!) define a new template category named 'foo'
1129 * [foo](+) append to category 'foo', error if foo does not exist.
1130 * [foo](a) define a new category and inherit from category or template a.
1131 * You can put a comma-separated list of categories and templates
1132 * and '!' and '+' between parentheses, with obvious meaning.
1134 struct ast_category *newcat = NULL;
1137 c = strchr(cur, ']');
1139 ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
1147 if (!(*cat = newcat = ast_category_new(catname,
1148 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
1152 (*cat)->lineno = lineno;
1157 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1158 newcat->precomments = ALLOC_COMMENT(comment_buffer);
1159 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1160 newcat->sameline = ALLOC_COMMENT(lline_buffer);
1161 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1162 CB_RESET(comment_buffer, lline_buffer);
1164 /* If there are options or categories to inherit from, process them now */
1166 if (!(cur = strchr(c, ')'))) {
1167 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
1171 while ((cur = strsep(&c, ","))) {
1172 if (!strcasecmp(cur, "!")) {
1173 (*cat)->ignored = 1;
1174 } else if (!strcasecmp(cur, "+")) {
1175 *cat = category_get(cfg, catname, 1);
1178 ast_category_destroy(newcat);
1179 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
1183 move_variables(newcat, *cat);
1184 ast_category_destroy(newcat);
1188 struct ast_category *base;
1190 base = category_get(cfg, cur, 1);
1192 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1195 inherit_category(*cat, base);
1200 ast_category_append(cfg, *cat);
1201 } else if (cur[0] == '#') { /* A directive - #include or #exec */
1203 char real_inclusion_name[256];
1204 int do_include = 0; /* otherwise, it is exec */
1205 int try_include = 0;
1209 while (*c && (*c > 32)) {
1215 /* Find real argument */
1216 c = ast_strip(c + 1);
1223 if (!strcasecmp(cur, "include")) {
1225 } else if (!strcasecmp(cur, "tryinclude")) {
1228 } else if (!strcasecmp(cur, "exec")) {
1229 if (!ast_opt_exec_includes) {
1230 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1231 return 0; /* XXX is this correct ? or we should return -1 ? */
1234 ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1235 return 0; /* XXX is this correct ? or we should return -1 ? */
1239 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
1240 do_include ? "include / tryinclude" : "exec",
1241 do_include ? "filename" : "/path/to/executable",
1244 return 0; /* XXX is this correct ? or we should return -1 ? */
1248 /* Strip off leading and trailing "'s and <>'s */
1250 if ((*c == '"') || (*c == '<')) {
1251 char quote_char = *c;
1252 if (quote_char == '<') {
1256 if (*(c + strlen(c) - 1) == quote_char) {
1258 *(c + strlen(c) - 1) = '\0';
1263 /* #exec </path/to/executable>
1264 We create a tmp file, then we #include it, then we delete it. */
1266 struct timeval now = ast_tvnow();
1267 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1268 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1269 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1270 snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1271 ast_safe_system(cmd);
1274 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1275 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1276 exec_file[0] = '\0';
1279 /* record this inclusion */
1280 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1282 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1283 if (!ast_strlen_zero(exec_file))
1285 if (!do_include && !try_include) {
1286 ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1289 /* XXX otherwise what ? the default return is 0 anyways */
1292 /* Just a line (variable = value) */
1295 ast_log(LOG_WARNING,
1296 "parse error: No category context for line %d of %s\n", lineno, configfile);
1299 c = strchr(cur, '=');
1301 if (c && c > cur && (*(c - 1) == '+')) {
1302 struct ast_variable *var, *replace = NULL;
1303 struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1305 if (!str || !*str) {
1311 cur = ast_strip(cur);
1313 /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1314 for (var = ast_category_first(*cat); var; var = var->next) {
1315 if (!strcmp(var->name, cur)) {
1321 /* Nothing to replace; just set a variable normally. */
1322 goto set_new_variable;
1325 ast_str_set(str, 0, "%s", replace->value);
1326 ast_str_append(str, 0, "%s", c);
1327 ast_str_trim_blanks(*str);
1328 ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1332 /* Ignore > in => */
1338 if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1343 /* Put and reset comments */
1345 ast_variable_append(*cat, v);
1347 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1348 v->precomments = ALLOC_COMMENT(comment_buffer);
1349 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1350 v->sameline = ALLOC_COMMENT(lline_buffer);
1351 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1352 CB_RESET(comment_buffer, lline_buffer);
1358 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1364 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, const char *who_asked)
1367 #if defined(LOW_MEMORY)
1372 char *new_buf, *comment_p, *process_buf;
1375 int comment = 0, nest[MAX_NESTED_COMMENTS];
1376 struct ast_category *cat = NULL;
1378 struct stat statbuf;
1379 struct cache_file_mtime *cfmtime = NULL;
1380 struct cache_file_include *cfinclude;
1381 struct ast_variable *last_var = 0;
1382 struct ast_category *last_cat = 0;
1383 /*! Growable string buffer */
1384 struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
1385 struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */
1388 cat = ast_config_get_current_category(cfg);
1390 if (filename[0] == '/') {
1391 ast_copy_string(fn, filename, sizeof(fn));
1393 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1396 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1397 comment_buffer = ast_str_create(CB_SIZE);
1399 lline_buffer = ast_str_create(CB_SIZE);
1400 if (!lline_buffer) {
1401 ast_free(comment_buffer);
1402 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1406 #ifdef AST_INCLUDE_GLOB
1410 globbuf.gl_offs = 0; /* initialize it to silence gcc */
1411 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1412 if (glob_ret == GLOB_NOSPACE)
1413 ast_log(LOG_WARNING,
1414 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1415 else if (glob_ret == GLOB_ABORTED)
1416 ast_log(LOG_WARNING,
1417 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1419 /* loop over expanded files */
1421 for (i=0; i<globbuf.gl_pathc; i++) {
1422 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1425 * The following is not a loop, but just a convenient way to define a block
1426 * (using do { } while(0) ), and be able to exit from it with 'continue'
1427 * or 'break' in case of errors. Nice trick.
1430 if (stat(fn, &statbuf))
1433 if (!S_ISREG(statbuf.st_mode)) {
1434 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1438 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1439 /* Find our cached entry for this configuration file */
1440 AST_LIST_LOCK(&cfmtime_head);
1441 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1442 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
1446 cfmtime = cfmtime_new(fn, who_asked);
1449 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1450 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1454 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1455 /* File is unchanged, what about the (cached) includes (if any)? */
1457 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1458 /* We must glob here, because if we did not, then adding a file to globbed directory would
1459 * incorrectly cause no reload to be necessary. */
1461 #ifdef AST_INCLUDE_GLOB
1463 glob_t glob_buf = { .gl_offs = 0 };
1464 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
1465 /* On error, we reparse */
1466 if (glob_return == GLOB_NOSPACE || glob_return == GLOB_ABORTED)
1469 /* loop over expanded files */
1471 for (j = 0; j < glob_buf.gl_pathc; j++) {
1472 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
1474 ast_copy_string(fn2, cfinclude->include);
1476 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
1477 /* that second-to-last field needs to be looked at in this case... TODO */
1479 /* One change is enough to short-circuit and reload the whole shebang */
1482 #ifdef AST_INCLUDE_GLOB
1489 AST_LIST_UNLOCK(&cfmtime_head);
1490 return CONFIG_STATUS_FILEUNCHANGED;
1493 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1494 AST_LIST_UNLOCK(&cfmtime_head);
1496 /* If cfg is NULL, then we just want an answer */
1498 ast_free(comment_buffer);
1499 ast_free(lline_buffer);
1504 cfmtime->mtime = statbuf.st_mtime;
1506 if (!(f = fopen(fn, "r"))) {
1507 ast_debug(1, "No file to parse: %s\n", fn);
1508 ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
1512 /* If we get to this point, then we're loading regardless */
1513 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1514 ast_debug(1, "Parsing %s\n", fn);
1515 ast_verb(2, "Parsing '%s': Found\n", fn);
1518 if (fgets(buf, sizeof(buf), f)) {
1519 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
1520 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1521 ast_str_reset(lline_buffer); /* erase the lline buffer */
1530 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer) && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
1531 /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1532 CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
1533 continue; /* go get a new line, then */
1536 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1537 if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
1538 /* Escaped semicolons aren't comments. */
1539 new_buf = comment_p + 1;
1540 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1541 /* Meta-Comment start detected ";--" */
1542 if (comment < MAX_NESTED_COMMENTS) {
1544 new_buf = comment_p + 3;
1546 nest[comment-1] = lineno;
1548 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1550 } else if ((comment_p >= new_buf + 2) &&
1551 (*(comment_p - 1) == COMMENT_TAG) &&
1552 (*(comment_p - 2) == COMMENT_TAG)) {
1553 /* Meta-Comment end detected */
1555 new_buf = comment_p + 1;
1557 /* Back to non-comment now */
1559 /* Actually have to move what's left over the top, then continue */
1561 oldptr = process_buf + strlen(process_buf);
1562 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1563 CB_ADD(&comment_buffer, ";");
1564 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1567 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1570 process_buf = new_buf;
1574 /* If ; is found, and we are not nested in a comment,
1575 we immediately stop all comment processing */
1576 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1577 CB_ADD(&lline_buffer, comment_p);
1580 new_buf = comment_p;
1582 new_buf = comment_p + 1;
1585 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1586 CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
1590 char *buffer = ast_strip(process_buf);
1591 if (!ast_strlen_zero(buffer)) {
1592 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
1593 cfg = CONFIG_STATUS_FILEINVALID;
1600 /* end of file-- anything in a comment buffer? */
1602 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1603 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1604 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1605 ast_str_reset(lline_buffer); /* erase the lline buffer */
1607 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1609 } else if (last_var) {
1610 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1611 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1612 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1613 ast_str_reset(lline_buffer); /* erase the lline buffer */
1615 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1618 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1619 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
1622 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1623 CB_RESET(comment_buffer, lline_buffer);
1628 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1630 #ifdef AST_INCLUDE_GLOB
1631 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1640 if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg != CONFIG_STATUS_FILEINVALID && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1641 ast_free(comment_buffer);
1642 ast_free(lline_buffer);
1643 comment_buffer = NULL;
1644 lline_buffer = NULL;
1654 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1655 which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1656 recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1657 be shocked and mystified as to why things are not showing up in the files!
1659 Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1660 and line number are stored for each include, plus the name of the file included, so that these statements may be
1661 included in the output files on a file_save operation.
1663 The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1664 are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1665 the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1666 and a header gets added.
1668 vars and category heads are output in the order they are stored in the config file. So, if the software
1669 shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1670 file/lineno data probably won't get changed.
1674 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1680 ast_copy_string(date, ctime(&t), sizeof(date));
1682 fprintf(f1, ";!\n");
1683 fprintf(f1, ";! Automatically generated configuration file\n");
1684 if (strcmp(configfile, fn))
1685 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1687 fprintf(f1, ";! Filename: %s\n", configfile);
1688 fprintf(f1, ";! Generator: %s\n", generator);
1689 fprintf(f1, ";! Creation Date: %s", date);
1690 fprintf(f1, ";!\n");
1693 static void inclfile_destroy(void *obj)
1695 const struct inclfile *o = obj;
1701 static struct inclfile *set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
1703 struct inclfile lookup;
1704 struct inclfile *fi;
1706 if (ast_strlen_zero(file)) {
1707 if (configfile[0] == '/')
1708 ast_copy_string(fn, configfile, fn_size);
1710 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1711 } else if (file[0] == '/')
1712 ast_copy_string(fn, file, fn_size);
1714 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1716 fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1718 /* Found existing include file scratch pad. */
1722 /* set up a file scratch pad */
1723 fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1725 /* Scratch pad creation failed. */
1728 fi->fname = ast_strdup(fn);
1730 /* Scratch pad creation failed. */
1736 ao2_link(fileset, fi);
1741 static int count_linefeeds(char *str)
1753 static int count_linefeeds_in_comments(struct ast_comment *x)
1758 count += count_linefeeds(x->cmt);
1764 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1766 int precomment_lines;
1770 /* No file scratch pad object so insert no blank lines. */
1774 precomment_lines = count_linefeeds_in_comments(precomments);
1776 /* I don't have to worry about those ;! comments, they are
1777 stored in the precomments, but not printed back out.
1778 I did have to make sure that comments following
1779 the ;! header comments were not also deleted in the process */
1780 if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
1782 } else if (lineno == 0) {
1783 /* Line replacements also mess things up */
1785 } else if (lineno - precomment_lines - fi->lineno < 5) {
1786 /* Only insert less than 5 blank lines; if anything more occurs,
1787 * it's probably due to context deletion. */
1788 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
1792 /* Deletion occurred - insert a single blank line, for separation of
1797 fi->lineno = lineno + 1; /* Advance the file lineno */
1800 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1802 return ast_config_text_file_save(configfile, cfg, generator);
1805 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1809 struct ast_variable *var;
1810 struct ast_category *cat;
1811 struct ast_comment *cmt;
1812 struct ast_config_include *incl;
1814 struct ao2_container *fileset;
1815 struct inclfile *fi;
1817 fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
1819 /* Container creation failed. */
1823 /* reset all the output flags, in case this isn't our first time saving this data */
1824 for (incl = cfg->includes; incl; incl = incl->next) {
1828 /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1829 are all truncated to zero bytes and have that nice header*/
1830 for (incl = cfg->includes; incl; incl = incl->next) {
1831 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*/
1832 /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
1833 fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
1836 gen_header(f, configfile, fn, generator);
1837 fclose(f); /* this should zero out the file */
1839 ast_debug(1, "Unable to open for writing: %s\n", fn);
1840 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
1848 /* just set fn to absolute ver of configfile */
1849 fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
1852 (f = fopen(fn, "w+"))
1854 (f = fopen(fn, "w"))
1857 ast_verb(2, "Saving '%s'\n", fn);
1858 gen_header(f, configfile, fn, generator);
1865 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1866 /* since each var, cat, and associated comments can come from any file, we have to be
1867 mobile, and open each file, print, and close it on an entry-by-entry basis */
1870 fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
1873 ast_debug(1, "Unable to open for writing: %s\n", fn);
1874 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
1878 ao2_ref(fileset, -1);
1882 /* dump any includes that happen before this category header */
1883 for (incl=cfg->includes; incl; incl = incl->next) {
1884 if (strcmp(incl->include_location_file, cat->file) == 0){
1885 if (cat->lineno > incl->include_location_lineno && !incl->output) {
1887 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1889 fprintf(f,"#include \"%s\"\n", incl->included_file);
1895 insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1896 /* Dump section with any appropriate comment */
1897 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1898 char *cmtp = cmt->cmt;
1899 while (*cmtp == ';' && *(cmtp+1) == '!') {
1900 char *cmtp2 = strchr(cmtp+1, '\n');
1906 fprintf(f,"%s", cmtp);
1908 fprintf(f, "[%s]", cat->name);
1909 if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
1914 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
1917 if (!AST_LIST_EMPTY(&cat->template_instances)) {
1918 struct ast_category_template_instance *x;
1919 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1920 fprintf(f,"%s",x->name);
1921 if (x != AST_LIST_LAST(&cat->template_instances))
1927 for(cmt = cat->sameline; cmt; cmt=cmt->next)
1929 fprintf(f,"%s", cmt->cmt);
1933 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1934 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1935 fprintf(f,"%s", cmt->cmt);
1944 struct ast_category_template_instance *x;
1946 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1947 struct ast_variable *v;
1948 for (v = x->inst->root; v; v = v->next) {
1949 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
1961 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
1964 ast_debug(1, "Unable to open for writing: %s\n", fn);
1965 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
1969 ao2_ref(fileset, -1);
1973 /* dump any includes that happen before this category header */
1974 for (incl=cfg->includes; incl; incl = incl->next) {
1975 if (strcmp(incl->include_location_file, var->file) == 0){
1976 if (var->lineno > incl->include_location_lineno && !incl->output) {
1978 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1980 fprintf(f,"#include \"%s\"\n", incl->included_file);
1986 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1987 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1988 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1989 fprintf(f,"%s", cmt->cmt);
1992 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1994 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1995 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1996 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1997 fprintf(f,"%s", cmt->cmt);
1999 if (var->blanklines) {
2000 blanklines = var->blanklines;
2001 while (blanklines--)
2014 if (!option_debug) {
2015 ast_verb(2, "Saving '%s': saved\n", fn);
2018 ast_debug(1, "Unable to open for writing: %s\n", fn);
2019 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
2023 ao2_ref(fileset, -1);
2027 /* Now, for files with trailing #include/#exec statements,
2028 we have to make sure every entry is output */
2029 for (incl=cfg->includes; incl; incl = incl->next) {
2030 if (!incl->output) {
2031 /* open the respective file */
2032 fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
2035 ast_debug(1, "Unable to open for writing: %s\n", fn);
2036 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2040 ao2_ref(fileset, -1);
2044 /* output the respective include */
2046 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2048 fprintf(f,"#include \"%s\"\n", incl->included_file);
2056 ao2_ref(fileset, -1); /* this should destroy the hash container */
2061 static void clear_config_maps(void)
2063 struct ast_config_map *map;
2065 ast_mutex_lock(&config_lock);
2067 while (config_maps) {
2069 config_maps = config_maps->next;
2073 ast_mutex_unlock(&config_lock);
2076 static int append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2078 struct ast_config_map *map;
2082 length = sizeof(*map);
2083 length += strlen(name) + 1;
2084 length += strlen(driver) + 1;
2085 length += strlen(database) + 1;
2087 length += strlen(table) + 1;
2089 if (!(map = ast_calloc(1, length)))
2092 dst = map->stuff; /* writable space starts here */
2093 map->name = strcpy(dst, name);
2094 dst += strlen(dst) + 1;
2095 map->driver = strcpy(dst, driver);
2096 dst += strlen(dst) + 1;
2097 map->database = strcpy(dst, database);
2099 dst += strlen(dst) + 1;
2100 map->table = strcpy(dst, table);
2102 map->priority = priority;
2103 map->next = config_maps;
2106 ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
2111 int read_config_maps(void)
2113 struct ast_config *config, *configtmp;
2114 struct ast_variable *v;
2115 char *driver, *table, *database, *textpri, *stringp, *tmp;
2116 struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
2119 clear_config_maps();
2121 configtmp = ast_config_new();
2122 configtmp->max_include_level = 1;
2123 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
2124 if (config == CONFIG_STATUS_FILEINVALID) {
2126 } else if (!config) {
2127 ast_config_destroy(configtmp);
2131 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
2133 ast_copy_string(buf, v->value, sizeof(buf));
2135 driver = strsep(&stringp, ",");
2137 if ((tmp = strchr(stringp, '\"')))
2140 /* check if the database text starts with a double quote */
2141 if (*stringp == '"') {
2143 database = strsep(&stringp, "\"");
2144 strsep(&stringp, ",");
2146 /* apparently this text has no quotes */
2147 database = strsep(&stringp, ",");
2150 table = strsep(&stringp, ",");
2151 textpri = strsep(&stringp, ",");
2152 if (!textpri || !(pri = atoi(textpri))) {
2156 if (!strcmp(v->name, extconfig_conf)) {
2157 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
2161 if (!strcmp(v->name, "asterisk.conf")) {
2162 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
2166 if (!strcmp(v->name, "logger.conf")) {
2167 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
2171 if (!driver || !database)
2173 if (!strcasecmp(v->name, "sipfriends")) {
2174 ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
2175 append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
2176 } else if (!strcasecmp(v->name, "iaxfriends")) {
2177 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");
2178 append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
2179 append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
2181 append_mapping(v->name, driver, database, table, pri);
2184 ast_config_destroy(config);
2188 int ast_config_engine_register(struct ast_config_engine *new)
2190 struct ast_config_engine *ptr;
2192 ast_mutex_lock(&config_lock);
2194 if (!config_engine_list) {
2195 config_engine_list = new;
2197 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
2201 ast_mutex_unlock(&config_lock);
2202 ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
2207 int ast_config_engine_deregister(struct ast_config_engine *del)
2209 struct ast_config_engine *ptr, *last=NULL;
2211 ast_mutex_lock(&config_lock);
2213 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
2216 last->next = ptr->next;
2218 config_engine_list = ptr->next;
2224 ast_mutex_unlock(&config_lock);
2229 /*! \brief Find realtime engine for realtime family */
2230 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
2232 struct ast_config_engine *eng, *ret = NULL;
2233 struct ast_config_map *map;
2235 ast_mutex_lock(&config_lock);
2237 for (map = config_maps; map; map = map->next) {
2238 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
2240 ast_copy_string(database, map->database, dbsiz);
2242 ast_copy_string(table, map->table ? map->table : family, tabsiz);
2247 /* Check if the required driver (engine) exist */
2249 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
2250 if (!strcasecmp(eng->name, map->driver))
2255 ast_mutex_unlock(&config_lock);
2257 /* if we found a mapping, but the engine is not available, then issue a warning */
2259 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
2264 static struct ast_config_engine text_file_engine = {
2266 .load_func = config_text_file_load,
2269 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
2273 struct ast_config_engine *loader = &text_file_engine;
2274 struct ast_config *result;
2276 /* The config file itself bumps include_level by 1 */
2277 if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2278 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2282 cfg->include_level++;
2284 if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
2285 struct ast_config_engine *eng;
2287 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
2290 if (eng && eng->load_func) {
2293 eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
2294 if (eng && eng->load_func)
2299 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2301 if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED)
2302 result->include_level--;
2303 else if (result != CONFIG_STATUS_FILEINVALID)
2304 cfg->include_level--;
2309 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
2311 struct ast_config *cfg;
2312 struct ast_config *result;
2314 cfg = ast_config_new();
2318 result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
2319 if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
2320 ast_config_destroy(cfg);
2325 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
2327 struct ast_config_engine *eng;
2330 struct ast_variable *res=NULL;
2333 for (i = 1; ; i++) {
2334 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2335 if (eng->realtime_func && (res = eng->realtime_func(db, table, ap))) {
2346 struct ast_variable *ast_load_realtime_all(const char *family, ...)
2348 struct ast_variable *res;
2351 va_start(ap, family);
2352 res = ast_load_realtime_helper(family, ap);
2358 struct ast_variable *ast_load_realtime(const char *family, ...)
2360 struct ast_variable *res;
2361 struct ast_variable *cur;
2362 struct ast_variable **prev;
2365 va_start(ap, family);
2366 res = ast_load_realtime_helper(family, ap);
2369 /* Filter the list. */
2373 if (ast_strlen_zero(cur->value)) {
2374 /* Eliminate empty entries */
2375 struct ast_variable *next;
2379 ast_variable_destroy(cur);
2382 /* Make blank entries empty and keep them. */
2383 if (cur->value[0] == ' ' && cur->value[1] == '\0') {
2384 char *vptr = (char *) cur->value;
2396 /*! \brief Check if realtime engine is configured for family */
2397 int ast_check_realtime(const char *family)
2399 struct ast_config_engine *eng;
2400 if (!ast_realtime_enabled()) {
2401 return 0; /* There are no engines at all so fail early */
2404 eng = find_engine(family, 1, NULL, 0, NULL, 0);
2410 /*! \brief Check if there's any realtime engines loaded */
2411 int ast_realtime_enabled(void)
2413 return config_maps ? 1 : 0;
2416 int ast_realtime_require_field(const char *family, ...)
2418 struct ast_config_engine *eng;
2424 va_start(ap, family);
2425 for (i = 1; ; i++) {
2426 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2427 /* If the require succeeds, it returns 0. */
2428 if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
2440 int ast_unload_realtime(const char *family)
2442 struct ast_config_engine *eng;
2447 for (i = 1; ; i++) {
2448 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2449 if (eng->unload_func) {
2450 /* Do this for ALL engines */
2451 res = eng->unload_func(db, table);
2460 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2462 struct ast_config_engine *eng;
2465 struct ast_config *res = NULL;
2469 va_start(ap, family);
2470 for (i = 1; ; i++) {
2471 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2472 if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, ap))) {
2473 /* If we were returned an empty cfg, destroy it and return NULL */
2475 ast_config_destroy(res);
2489 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2491 struct ast_config_engine *eng;
2497 va_start(ap, lookup);
2498 for (i = 1; ; i++) {
2499 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2500 /* If the update succeeds, it returns 0. */
2501 if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, ap))) {
2513 int ast_update2_realtime(const char *family, ...)
2515 struct ast_config_engine *eng;
2521 va_start(ap, family);
2522 for (i = 1; ; i++) {
2523 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2524 if (eng->update2_func && !(res = eng->update2_func(db, table, ap))) {
2536 int ast_store_realtime(const char *family, ...)
2538 struct ast_config_engine *eng;
2544 va_start(ap, family);
2545 for (i = 1; ; i++) {
2546 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2547 /* If the store succeeds, it returns 0. */
2548 if (eng->store_func && !(res = eng->store_func(db, table, ap))) {
2560 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2562 struct ast_config_engine *eng;
2568 va_start(ap, lookup);
2569 for (i = 1; ; i++) {
2570 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2571 if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, ap))) {
2583 char *ast_realtime_decode_chunk(char *chunk)
2586 for (; *chunk; chunk++) {
2587 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
2588 sscanf(chunk + 1, "%02hhX", chunk);
2589 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
2595 char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
2597 if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
2598 ast_str_set(dest, maxlen, "%s", chunk);
2600 ast_str_reset(*dest);
2601 for (; *chunk; chunk++) {
2602 if (strchr(";^", *chunk)) {
2603 ast_str_append(dest, maxlen, "^%02hhX", *chunk);
2605 ast_str_append(dest, maxlen, "%c", *chunk);
2609 return ast_str_buffer(*dest);
2612 /*! \brief Helper function to parse arguments
2613 * See documentation in config.h
2615 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2616 void *p_result, ...)
2621 va_start(ap, p_result);
2622 switch (flags & PARSE_TYPE) {
2625 int32_t *result = p_result;
2626 int32_t x, def = result ? *result : 0,
2627 high = (int32_t)0x7fffffff,
2628 low = (int32_t)0x80000000;
2629 /* optional argument: first default value, then range */
2630 if (flags & PARSE_DEFAULT)
2631 def = va_arg(ap, int32_t);
2632 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2633 /* range requested, update bounds */
2634 low = va_arg(ap, int32_t);
2635 high = va_arg(ap, int32_t);
2637 x = strtol(arg, NULL, 0);
2638 error = (x < low) || (x > high);
2639 if (flags & PARSE_OUT_RANGE)
2642 *result = error ? def : x;
2644 "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2646 result ? *result : x, error);
2652 uint32_t *result = p_result;
2653 uint32_t x, def = result ? *result : 0,
2654 low = 0, high = (uint32_t)~0;
2655 /* optional argument: first default value, then range */
2656 if (flags & PARSE_DEFAULT)
2657 def = va_arg(ap, uint32_t);
2658 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2659 /* range requested, update bounds */
2660 low = va_arg(ap, uint32_t);
2661 high = va_arg(ap, uint32_t);
2663 x = strtoul(arg, NULL, 0);
2664 error = (x < low) || (x > high);
2665 if (flags & PARSE_OUT_RANGE)
2668 *result = error ? def : x;
2670 "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2672 result ? *result : x, error);
2678 double *result = p_result;
2679 double x, def = result ? *result : 0,
2680 low = -HUGE_VAL, high = HUGE_VAL;
2682 /* optional argument: first default value, then range */
2683 if (flags & PARSE_DEFAULT)
2684 def = va_arg(ap, double);
2685 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2686 /* range requested, update bounds */
2687 low = va_arg(ap, double);
2688 high = va_arg(ap, double);
2690 x = strtod(arg, NULL);
2691 error = (x < low) || (x > high);
2692 if (flags & PARSE_OUT_RANGE)
2695 *result = error ? def : x;
2697 "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
2699 result ? *result : x, error);
2704 struct ast_sockaddr *addr = (struct ast_sockaddr *)p_result;
2706 if (!ast_sockaddr_parse(addr, arg, flags & PARSE_PORT_MASK)) {
2710 ast_debug(3, "extract addr from %s gives %s(%d)\n",
2711 arg, ast_sockaddr_stringify(addr), error);
2715 case PARSE_INADDR: /* TODO Remove this (use PARSE_ADDR instead). */
2718 struct sockaddr_in _sa_buf; /* buffer for the result */
2719 struct sockaddr_in *sa = p_result ?
2720 (struct sockaddr_in *)p_result : &_sa_buf;
2721 /* default is either the supplied value or the result itself */
2722 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2723 va_arg(ap, struct sockaddr_in *) : sa;
2725 struct ast_hostent ahp;
2727 memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
2728 /* duplicate the string to strip away the :port */
2729 port = ast_strdupa(arg);
2730 buf = strsep(&port, ":");
2731 sa->sin_family = AF_INET; /* assign family */
2733 * honor the ports flag setting, assign default value
2734 * in case of errors or field unset.
2736 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2738 if (flags == PARSE_PORT_FORBID) {
2739 error = 1; /* port was forbidden */
2740 sa->sin_port = def->sin_port;
2741 } else if (flags == PARSE_PORT_IGNORE)
2742 sa->sin_port = def->sin_port;
2743 else /* accept or require */
2744 sa->sin_port = htons(strtol(port, NULL, 0));
2746 sa->sin_port = def->sin_port;
2747 if (flags == PARSE_PORT_REQUIRE)
2750 /* Now deal with host part, even if we have errors before. */
2751 hp = ast_gethostbyname(buf, &ahp);
2752 if (hp) /* resolved successfully */
2753 memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2756 sa->sin_addr = def->sin_addr;
2759 "extract inaddr from [%s] gives [%s:%d](%d)\n",
2760 arg, ast_inet_ntoa(sa->sin_addr),
2761 ntohs(sa->sin_port), error);
2769 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2771 struct ast_config_engine *eng;
2772 struct ast_config_map *map;
2776 e->command = "core show config mappings";
2778 "Usage: core show config mappings\n"
2779 " Shows the filenames to config engines.\n";
2785 ast_mutex_lock(&config_lock);
2787 if (!config_engine_list) {
2788 ast_cli(a->fd, "No config mappings found.\n");
2790 for (eng = config_engine_list; eng; eng = eng->next) {
2791 ast_cli(a->fd, "Config Engine: %s\n", eng->name);
2792 for (map = config_maps; map; map = map->next) {
2793 if (!strcasecmp(map->driver, eng->name)) {
2794 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2795 map->table ? map->table : map->name);
2801 ast_mutex_unlock(&config_lock);
2806 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2808 struct cache_file_mtime *cfmtime;
2809 char *prev = "", *completion_value = NULL;
2810 int wordlen, which = 0;
2814 e->command = "config reload";
2816 "Usage: config reload <filename.conf>\n"
2817 " Reloads all modules that reference <filename.conf>\n";
2824 wordlen = strlen(a->word);
2826 AST_LIST_LOCK(&cfmtime_head);
2827 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2828 /* Skip duplicates - this only works because the list is sorted by filename */
2829 if (strcmp(cfmtime->filename, prev) == 0) {
2833 /* Core configs cannot be reloaded */
2834 if (ast_strlen_zero(cfmtime->who_asked)) {
2838 if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
2839 completion_value = ast_strdup(cfmtime->filename);
2843 /* Otherwise save that we've seen this filename */
2844 prev = cfmtime->filename;
2846 AST_LIST_UNLOCK(&cfmtime_head);
2848 return completion_value;
2852 return CLI_SHOWUSAGE;
2855 AST_LIST_LOCK(&cfmtime_head);
2856 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2857 if (!strcmp(cfmtime->filename, a->argv[2])) {
2858 char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
2859 sprintf(buf, "module reload %s", cfmtime->who_asked);
2860 ast_cli_command(a->fd, buf);
2863 AST_LIST_UNLOCK(&cfmtime_head);
2868 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2870 struct cache_file_mtime *cfmtime;
2874 e->command = "config list";
2876 "Usage: config list\n"
2877 " Show all modules that have loaded a configuration file\n";
2883 AST_LIST_LOCK(&cfmtime_head);
2884 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2885 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
2887 AST_LIST_UNLOCK(&cfmtime_head);
2892 static struct ast_cli_entry cli_config[] = {
2893 AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
2894 AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
2895 AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
2898 int register_config_cli(void)
2900 ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));