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 ast_verb(2, "Parsing '%s': ", fn);
1508 if (!(f = fopen(fn, "r"))) {
1509 ast_debug(1, "No file to parse: %s\n", fn);
1510 ast_verb(2, "Not found (%s)\n", strerror(errno));
1514 /* If we get to this point, then we're loading regardless */
1515 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1516 ast_debug(1, "Parsing %s\n", fn);
1517 ast_verb(2, "Found\n");
1520 if (fgets(buf, sizeof(buf), f)) {
1521 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
1522 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1523 ast_str_reset(lline_buffer); /* erase the lline buffer */
1532 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"))) {
1533 /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1534 CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
1535 continue; /* go get a new line, then */
1538 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1539 if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
1540 /* Escaped semicolons aren't comments. */
1541 new_buf = comment_p + 1;
1542 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1543 /* Meta-Comment start detected ";--" */
1544 if (comment < MAX_NESTED_COMMENTS) {
1546 new_buf = comment_p + 3;
1548 nest[comment-1] = lineno;
1550 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1552 } else if ((comment_p >= new_buf + 2) &&
1553 (*(comment_p - 1) == COMMENT_TAG) &&
1554 (*(comment_p - 2) == COMMENT_TAG)) {
1555 /* Meta-Comment end detected */
1557 new_buf = comment_p + 1;
1559 /* Back to non-comment now */
1561 /* Actually have to move what's left over the top, then continue */
1563 oldptr = process_buf + strlen(process_buf);
1564 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1565 CB_ADD(&comment_buffer, ";");
1566 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1569 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1572 process_buf = new_buf;
1576 /* If ; is found, and we are not nested in a comment,
1577 we immediately stop all comment processing */
1578 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1579 CB_ADD(&lline_buffer, comment_p);
1582 new_buf = comment_p;
1584 new_buf = comment_p + 1;
1587 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1588 CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
1592 char *buffer = ast_strip(process_buf);
1593 if (!ast_strlen_zero(buffer)) {
1594 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
1595 cfg = CONFIG_STATUS_FILEINVALID;
1602 /* end of file-- anything in a comment buffer? */
1604 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1605 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1606 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1607 ast_str_reset(lline_buffer); /* erase the lline buffer */
1609 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1611 } else if (last_var) {
1612 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1613 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1614 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1615 ast_str_reset(lline_buffer); /* erase the lline buffer */
1617 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1620 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1621 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
1624 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1625 CB_RESET(comment_buffer, lline_buffer);
1630 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1632 #ifdef AST_INCLUDE_GLOB
1633 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1642 if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg != CONFIG_STATUS_FILEINVALID && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1643 ast_free(comment_buffer);
1644 ast_free(lline_buffer);
1645 comment_buffer = NULL;
1646 lline_buffer = NULL;
1656 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1657 which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1658 recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1659 be shocked and mystified as to why things are not showing up in the files!
1661 Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1662 and line number are stored for each include, plus the name of the file included, so that these statements may be
1663 included in the output files on a file_save operation.
1665 The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1666 are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1667 the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1668 and a header gets added.
1670 vars and category heads are output in the order they are stored in the config file. So, if the software
1671 shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1672 file/lineno data probably won't get changed.
1676 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1682 ast_copy_string(date, ctime(&t), sizeof(date));
1684 fprintf(f1, ";!\n");
1685 fprintf(f1, ";! Automatically generated configuration file\n");
1686 if (strcmp(configfile, fn))
1687 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1689 fprintf(f1, ";! Filename: %s\n", configfile);
1690 fprintf(f1, ";! Generator: %s\n", generator);
1691 fprintf(f1, ";! Creation Date: %s", date);
1692 fprintf(f1, ";!\n");
1695 static void inclfile_destroy(void *obj)
1697 const struct inclfile *o = obj;
1703 static struct inclfile *set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
1705 struct inclfile lookup;
1706 struct inclfile *fi;
1708 if (ast_strlen_zero(file)) {
1709 if (configfile[0] == '/')
1710 ast_copy_string(fn, configfile, fn_size);
1712 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1713 } else if (file[0] == '/')
1714 ast_copy_string(fn, file, fn_size);
1716 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1718 fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1720 /* Found existing include file scratch pad. */
1724 /* set up a file scratch pad */
1725 fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1727 /* Scratch pad creation failed. */
1730 fi->fname = ast_strdup(fn);
1732 /* Scratch pad creation failed. */
1738 ao2_link(fileset, fi);
1743 static int count_linefeeds(char *str)
1755 static int count_linefeeds_in_comments(struct ast_comment *x)
1760 count += count_linefeeds(x->cmt);
1766 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1768 int precomment_lines;
1772 /* No file scratch pad object so insert no blank lines. */
1776 precomment_lines = count_linefeeds_in_comments(precomments);
1778 /* I don't have to worry about those ;! comments, they are
1779 stored in the precomments, but not printed back out.
1780 I did have to make sure that comments following
1781 the ;! header comments were not also deleted in the process */
1782 if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
1784 } else if (lineno == 0) {
1785 /* Line replacements also mess things up */
1787 } else if (lineno - precomment_lines - fi->lineno < 5) {
1788 /* Only insert less than 5 blank lines; if anything more occurs,
1789 * it's probably due to context deletion. */
1790 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
1794 /* Deletion occurred - insert a single blank line, for separation of
1799 fi->lineno = lineno + 1; /* Advance the file lineno */
1802 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1804 return ast_config_text_file_save(configfile, cfg, generator);
1807 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1811 struct ast_variable *var;
1812 struct ast_category *cat;
1813 struct ast_comment *cmt;
1814 struct ast_config_include *incl;
1816 struct ao2_container *fileset;
1817 struct inclfile *fi;
1819 fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
1821 /* Container creation failed. */
1825 /* reset all the output flags, in case this isn't our first time saving this data */
1826 for (incl = cfg->includes; incl; incl = incl->next) {
1830 /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1831 are all truncated to zero bytes and have that nice header*/
1832 for (incl = cfg->includes; incl; incl = incl->next) {
1833 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*/
1834 /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
1835 fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
1838 gen_header(f, configfile, fn, generator);
1839 fclose(f); /* this should zero out the file */
1841 ast_debug(1, "Unable to open for writing: %s\n", fn);
1842 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1850 /* just set fn to absolute ver of configfile */
1851 fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
1854 (f = fopen(fn, "w+"))
1856 (f = fopen(fn, "w"))
1859 ast_verb(2, "Saving '%s': ", fn);
1860 gen_header(f, configfile, fn, generator);
1867 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1868 /* since each var, cat, and associated comments can come from any file, we have to be
1869 mobile, and open each file, print, and close it on an entry-by-entry basis */
1872 fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
1875 ast_debug(1, "Unable to open for writing: %s\n", fn);
1876 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1880 ao2_ref(fileset, -1);
1884 /* dump any includes that happen before this category header */
1885 for (incl=cfg->includes; incl; incl = incl->next) {
1886 if (strcmp(incl->include_location_file, cat->file) == 0){
1887 if (cat->lineno > incl->include_location_lineno && !incl->output) {
1889 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1891 fprintf(f,"#include \"%s\"\n", incl->included_file);
1897 insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1898 /* Dump section with any appropriate comment */
1899 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1900 char *cmtp = cmt->cmt;
1901 while (*cmtp == ';' && *(cmtp+1) == '!') {
1902 char *cmtp2 = strchr(cmtp+1, '\n');
1908 fprintf(f,"%s", cmtp);
1910 fprintf(f, "[%s]", cat->name);
1911 if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
1916 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
1919 if (!AST_LIST_EMPTY(&cat->template_instances)) {
1920 struct ast_category_template_instance *x;
1921 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1922 fprintf(f,"%s",x->name);
1923 if (x != AST_LIST_LAST(&cat->template_instances))
1929 for(cmt = cat->sameline; cmt; cmt=cmt->next)
1931 fprintf(f,"%s", cmt->cmt);
1935 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1936 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1937 fprintf(f,"%s", cmt->cmt);
1946 struct ast_category_template_instance *x;
1948 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1949 struct ast_variable *v;
1950 for (v = x->inst->root; v; v = v->next) {
1951 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
1963 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
1966 ast_debug(1, "Unable to open for writing: %s\n", fn);
1967 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1971 ao2_ref(fileset, -1);
1975 /* dump any includes that happen before this category header */
1976 for (incl=cfg->includes; incl; incl = incl->next) {
1977 if (strcmp(incl->include_location_file, var->file) == 0){
1978 if (var->lineno > incl->include_location_lineno && !incl->output) {
1980 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1982 fprintf(f,"#include \"%s\"\n", incl->included_file);
1988 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1989 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1990 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1991 fprintf(f,"%s", cmt->cmt);
1994 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1996 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1997 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1998 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1999 fprintf(f,"%s", cmt->cmt);
2001 if (var->blanklines) {
2002 blanklines = var->blanklines;
2003 while (blanklines--)
2017 ast_verb(2, "Saved\n");
2019 ast_debug(1, "Unable to open for writing: %s\n", fn);
2020 ast_verb(2, "Unable to write (%s)", strerror(errno));
2024 ao2_ref(fileset, -1);
2028 /* Now, for files with trailing #include/#exec statements,
2029 we have to make sure every entry is output */
2030 for (incl=cfg->includes; incl; incl = incl->next) {
2031 if (!incl->output) {
2032 /* open the respective file */
2033 fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
2036 ast_debug(1, "Unable to open for writing: %s\n", fn);
2037 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
2041 ao2_ref(fileset, -1);
2045 /* output the respective include */
2047 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2049 fprintf(f,"#include \"%s\"\n", incl->included_file);
2057 ao2_ref(fileset, -1); /* this should destroy the hash container */
2062 static void clear_config_maps(void)
2064 struct ast_config_map *map;
2066 ast_mutex_lock(&config_lock);
2068 while (config_maps) {
2070 config_maps = config_maps->next;
2074 ast_mutex_unlock(&config_lock);
2077 static int append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2079 struct ast_config_map *map;
2083 length = sizeof(*map);
2084 length += strlen(name) + 1;
2085 length += strlen(driver) + 1;
2086 length += strlen(database) + 1;
2088 length += strlen(table) + 1;
2090 if (!(map = ast_calloc(1, length)))
2093 dst = map->stuff; /* writable space starts here */
2094 map->name = strcpy(dst, name);
2095 dst += strlen(dst) + 1;
2096 map->driver = strcpy(dst, driver);
2097 dst += strlen(dst) + 1;
2098 map->database = strcpy(dst, database);
2100 dst += strlen(dst) + 1;
2101 map->table = strcpy(dst, table);
2103 map->priority = priority;
2104 map->next = config_maps;
2107 ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
2112 int read_config_maps(void)
2114 struct ast_config *config, *configtmp;
2115 struct ast_variable *v;
2116 char *driver, *table, *database, *textpri, *stringp, *tmp;
2117 struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
2120 clear_config_maps();
2122 configtmp = ast_config_new();
2123 configtmp->max_include_level = 1;
2124 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
2125 if (config == CONFIG_STATUS_FILEINVALID) {
2127 } else if (!config) {
2128 ast_config_destroy(configtmp);
2132 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
2134 ast_copy_string(buf, v->value, sizeof(buf));
2136 driver = strsep(&stringp, ",");
2138 if ((tmp = strchr(stringp, '\"')))
2141 /* check if the database text starts with a double quote */
2142 if (*stringp == '"') {
2144 database = strsep(&stringp, "\"");
2145 strsep(&stringp, ",");
2147 /* apparently this text has no quotes */
2148 database = strsep(&stringp, ",");
2151 table = strsep(&stringp, ",");
2152 textpri = strsep(&stringp, ",");
2153 if (!textpri || !(pri = atoi(textpri))) {
2157 if (!strcmp(v->name, extconfig_conf)) {
2158 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
2162 if (!strcmp(v->name, "asterisk.conf")) {
2163 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
2167 if (!strcmp(v->name, "logger.conf")) {
2168 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
2172 if (!driver || !database)
2174 if (!strcasecmp(v->name, "sipfriends")) {
2175 ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
2176 append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
2177 } else if (!strcasecmp(v->name, "iaxfriends")) {
2178 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");
2179 append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
2180 append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
2182 append_mapping(v->name, driver, database, table, pri);
2185 ast_config_destroy(config);
2189 int ast_config_engine_register(struct ast_config_engine *new)
2191 struct ast_config_engine *ptr;
2193 ast_mutex_lock(&config_lock);
2195 if (!config_engine_list) {
2196 config_engine_list = new;
2198 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
2202 ast_mutex_unlock(&config_lock);
2203 ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
2208 int ast_config_engine_deregister(struct ast_config_engine *del)
2210 struct ast_config_engine *ptr, *last=NULL;
2212 ast_mutex_lock(&config_lock);
2214 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
2217 last->next = ptr->next;
2219 config_engine_list = ptr->next;
2225 ast_mutex_unlock(&config_lock);
2230 /*! \brief Find realtime engine for realtime family */
2231 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
2233 struct ast_config_engine *eng, *ret = NULL;
2234 struct ast_config_map *map;
2236 ast_mutex_lock(&config_lock);
2238 for (map = config_maps; map; map = map->next) {
2239 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
2241 ast_copy_string(database, map->database, dbsiz);
2243 ast_copy_string(table, map->table ? map->table : family, tabsiz);
2248 /* Check if the required driver (engine) exist */
2250 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
2251 if (!strcasecmp(eng->name, map->driver))
2256 ast_mutex_unlock(&config_lock);
2258 /* if we found a mapping, but the engine is not available, then issue a warning */
2260 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
2265 static struct ast_config_engine text_file_engine = {
2267 .load_func = config_text_file_load,
2270 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)
2274 struct ast_config_engine *loader = &text_file_engine;
2275 struct ast_config *result;
2277 /* The config file itself bumps include_level by 1 */
2278 if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2279 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2283 cfg->include_level++;
2285 if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
2286 struct ast_config_engine *eng;
2288 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
2291 if (eng && eng->load_func) {
2294 eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
2295 if (eng && eng->load_func)
2300 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2302 if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED)
2303 result->include_level--;
2304 else if (result != CONFIG_STATUS_FILEINVALID)
2305 cfg->include_level--;
2310 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
2312 struct ast_config *cfg;
2313 struct ast_config *result;
2315 cfg = ast_config_new();
2319 result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
2320 if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
2321 ast_config_destroy(cfg);
2326 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
2328 struct ast_config_engine *eng;
2331 struct ast_variable *res=NULL;
2334 for (i = 1; ; i++) {
2335 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2336 if (eng->realtime_func && (res = eng->realtime_func(db, table, ap))) {
2347 struct ast_variable *ast_load_realtime_all(const char *family, ...)
2349 struct ast_variable *res;
2352 va_start(ap, family);
2353 res = ast_load_realtime_helper(family, ap);
2359 struct ast_variable *ast_load_realtime(const char *family, ...)
2361 struct ast_variable *res;
2362 struct ast_variable *cur;
2363 struct ast_variable **prev;
2366 va_start(ap, family);
2367 res = ast_load_realtime_helper(family, ap);
2370 /* Filter the list. */
2374 if (ast_strlen_zero(cur->value)) {
2375 /* Eliminate empty entries */
2376 struct ast_variable *next;
2380 ast_variable_destroy(cur);
2383 /* Make blank entries empty and keep them. */
2384 if (cur->value[0] == ' ' && cur->value[1] == '\0') {
2385 char *vptr = (char *) cur->value;
2397 /*! \brief Check if realtime engine is configured for family */
2398 int ast_check_realtime(const char *family)
2400 struct ast_config_engine *eng;
2401 if (!ast_realtime_enabled()) {
2402 return 0; /* There are no engines at all so fail early */
2405 eng = find_engine(family, 1, NULL, 0, NULL, 0);
2411 /*! \brief Check if there's any realtime engines loaded */
2412 int ast_realtime_enabled(void)
2414 return config_maps ? 1 : 0;
2417 int ast_realtime_require_field(const char *family, ...)
2419 struct ast_config_engine *eng;
2425 va_start(ap, family);
2426 for (i = 1; ; i++) {
2427 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2428 /* If the require succeeds, it returns 0. */
2429 if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
2441 int ast_unload_realtime(const char *family)
2443 struct ast_config_engine *eng;
2448 for (i = 1; ; i++) {
2449 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2450 if (eng->unload_func) {
2451 /* Do this for ALL engines */
2452 res = eng->unload_func(db, table);
2461 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2463 struct ast_config_engine *eng;
2466 struct ast_config *res = NULL;
2470 va_start(ap, family);
2471 for (i = 1; ; i++) {
2472 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2473 if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, ap))) {
2474 /* If we were returned an empty cfg, destroy it and return NULL */
2476 ast_config_destroy(res);
2490 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2492 struct ast_config_engine *eng;
2498 va_start(ap, lookup);
2499 for (i = 1; ; i++) {
2500 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2501 /* If the update succeeds, it returns 0. */
2502 if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, ap))) {
2514 int ast_update2_realtime(const char *family, ...)
2516 struct ast_config_engine *eng;
2522 va_start(ap, family);
2523 for (i = 1; ; i++) {
2524 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2525 if (eng->update2_func && !(res = eng->update2_func(db, table, ap))) {
2537 int ast_store_realtime(const char *family, ...)
2539 struct ast_config_engine *eng;
2545 va_start(ap, family);
2546 for (i = 1; ; i++) {
2547 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2548 /* If the store succeeds, it returns 0. */
2549 if (eng->store_func && !(res = eng->store_func(db, table, ap))) {
2561 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2563 struct ast_config_engine *eng;
2569 va_start(ap, lookup);
2570 for (i = 1; ; i++) {
2571 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2572 if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, ap))) {
2584 char *ast_realtime_decode_chunk(char *chunk)
2587 for (; *chunk; chunk++) {
2588 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
2589 sscanf(chunk + 1, "%02hhX", chunk);
2590 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
2596 char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
2598 if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
2599 ast_str_set(dest, maxlen, "%s", chunk);
2601 ast_str_reset(*dest);
2602 for (; *chunk; chunk++) {
2603 if (strchr(";^", *chunk)) {
2604 ast_str_append(dest, maxlen, "^%02hhX", *chunk);
2606 ast_str_append(dest, maxlen, "%c", *chunk);
2610 return ast_str_buffer(*dest);
2613 /*! \brief Helper function to parse arguments
2614 * See documentation in config.h
2616 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2617 void *p_result, ...)
2622 va_start(ap, p_result);
2623 switch (flags & PARSE_TYPE) {
2626 int32_t *result = p_result;
2627 int32_t x, def = result ? *result : 0,
2628 high = (int32_t)0x7fffffff,
2629 low = (int32_t)0x80000000;
2630 /* optional argument: first default value, then range */
2631 if (flags & PARSE_DEFAULT)
2632 def = va_arg(ap, int32_t);
2633 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2634 /* range requested, update bounds */
2635 low = va_arg(ap, int32_t);
2636 high = va_arg(ap, int32_t);
2638 x = strtol(arg, NULL, 0);
2639 error = (x < low) || (x > high);
2640 if (flags & PARSE_OUT_RANGE)
2643 *result = error ? def : x;
2645 "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2647 result ? *result : x, error);
2653 uint32_t *result = p_result;
2654 uint32_t x, def = result ? *result : 0,
2655 low = 0, high = (uint32_t)~0;
2656 /* optional argument: first default value, then range */
2657 if (flags & PARSE_DEFAULT)
2658 def = va_arg(ap, uint32_t);
2659 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2660 /* range requested, update bounds */
2661 low = va_arg(ap, uint32_t);
2662 high = va_arg(ap, uint32_t);
2664 x = strtoul(arg, NULL, 0);
2665 error = (x < low) || (x > high);
2666 if (flags & PARSE_OUT_RANGE)
2669 *result = error ? def : x;
2671 "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2673 result ? *result : x, error);
2679 double *result = p_result;
2680 double x, def = result ? *result : 0,
2681 low = -HUGE_VAL, high = HUGE_VAL;
2683 /* optional argument: first default value, then range */
2684 if (flags & PARSE_DEFAULT)
2685 def = va_arg(ap, double);
2686 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2687 /* range requested, update bounds */
2688 low = va_arg(ap, double);
2689 high = va_arg(ap, double);
2691 x = strtod(arg, NULL);
2692 error = (x < low) || (x > high);
2693 if (flags & PARSE_OUT_RANGE)
2696 *result = error ? def : x;
2698 "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
2700 result ? *result : x, error);
2705 struct ast_sockaddr *addr = (struct ast_sockaddr *)p_result;
2707 if (!ast_sockaddr_parse(addr, arg, flags & PARSE_PORT_MASK)) {
2711 ast_debug(3, "extract addr from %s gives %s(%d)\n",
2712 arg, ast_sockaddr_stringify(addr), error);
2716 case PARSE_INADDR: /* TODO Remove this (use PARSE_ADDR instead). */
2719 struct sockaddr_in _sa_buf; /* buffer for the result */
2720 struct sockaddr_in *sa = p_result ?
2721 (struct sockaddr_in *)p_result : &_sa_buf;
2722 /* default is either the supplied value or the result itself */
2723 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2724 va_arg(ap, struct sockaddr_in *) : sa;
2726 struct ast_hostent ahp;
2728 memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
2729 /* duplicate the string to strip away the :port */
2730 port = ast_strdupa(arg);
2731 buf = strsep(&port, ":");
2732 sa->sin_family = AF_INET; /* assign family */
2734 * honor the ports flag setting, assign default value
2735 * in case of errors or field unset.
2737 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2739 if (flags == PARSE_PORT_FORBID) {
2740 error = 1; /* port was forbidden */
2741 sa->sin_port = def->sin_port;
2742 } else if (flags == PARSE_PORT_IGNORE)
2743 sa->sin_port = def->sin_port;
2744 else /* accept or require */
2745 sa->sin_port = htons(strtol(port, NULL, 0));
2747 sa->sin_port = def->sin_port;
2748 if (flags == PARSE_PORT_REQUIRE)
2751 /* Now deal with host part, even if we have errors before. */
2752 hp = ast_gethostbyname(buf, &ahp);
2753 if (hp) /* resolved successfully */
2754 memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2757 sa->sin_addr = def->sin_addr;
2760 "extract inaddr from [%s] gives [%s:%d](%d)\n",
2761 arg, ast_inet_ntoa(sa->sin_addr),
2762 ntohs(sa->sin_port), error);
2770 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2772 struct ast_config_engine *eng;
2773 struct ast_config_map *map;
2777 e->command = "core show config mappings";
2779 "Usage: core show config mappings\n"
2780 " Shows the filenames to config engines.\n";
2786 ast_mutex_lock(&config_lock);
2788 if (!config_engine_list) {
2789 ast_cli(a->fd, "No config mappings found.\n");
2791 for (eng = config_engine_list; eng; eng = eng->next) {
2792 ast_cli(a->fd, "Config Engine: %s\n", eng->name);
2793 for (map = config_maps; map; map = map->next) {
2794 if (!strcasecmp(map->driver, eng->name)) {
2795 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2796 map->table ? map->table : map->name);
2802 ast_mutex_unlock(&config_lock);
2807 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2809 struct cache_file_mtime *cfmtime;
2810 char *prev = "", *completion_value = NULL;
2811 int wordlen, which = 0;
2815 e->command = "config reload";
2817 "Usage: config reload <filename.conf>\n"
2818 " Reloads all modules that reference <filename.conf>\n";
2825 wordlen = strlen(a->word);
2827 AST_LIST_LOCK(&cfmtime_head);
2828 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2829 /* Skip duplicates - this only works because the list is sorted by filename */
2830 if (strcmp(cfmtime->filename, prev) == 0) {
2834 /* Core configs cannot be reloaded */
2835 if (ast_strlen_zero(cfmtime->who_asked)) {
2839 if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
2840 completion_value = ast_strdup(cfmtime->filename);
2844 /* Otherwise save that we've seen this filename */
2845 prev = cfmtime->filename;
2847 AST_LIST_UNLOCK(&cfmtime_head);
2849 return completion_value;
2853 return CLI_SHOWUSAGE;
2856 AST_LIST_LOCK(&cfmtime_head);
2857 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2858 if (!strcmp(cfmtime->filename, a->argv[2])) {
2859 char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
2860 sprintf(buf, "module reload %s", cfmtime->who_asked);
2861 ast_cli_command(a->fd, buf);
2864 AST_LIST_UNLOCK(&cfmtime_head);
2869 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2871 struct cache_file_mtime *cfmtime;
2875 e->command = "config list";
2877 "Usage: config list\n"
2878 " Show all modules that have loaded a configuration file\n";
2884 AST_LIST_LOCK(&cfmtime_head);
2885 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2886 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
2888 AST_LIST_UNLOCK(&cfmtime_head);
2893 static struct ast_cli_entry cli_config[] = {
2894 AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
2895 AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
2896 AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
2899 int register_config_cli(void)
2901 ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));