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
30 <support_level>core</support_level>
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37 #include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
38 #include "asterisk/network.h" /* we do some sockaddr manipulation here */
42 #include <math.h> /* HUGE_VAL */
44 #define AST_INCLUDE_GLOB 1
46 #include "asterisk/config.h"
47 #include "asterisk/cli.h"
48 #include "asterisk/lock.h"
49 #include "asterisk/utils.h"
50 #include "asterisk/channel.h"
51 #include "asterisk/app.h"
52 #include "asterisk/astobj2.h"
53 #include "asterisk/strings.h" /* for the ast_str_*() API */
54 #include "asterisk/netsock2.h"
56 #define MAX_NESTED_COMMENTS 128
57 #define COMMENT_START ";--"
58 #define COMMENT_END "--;"
59 #define COMMENT_META ';'
60 #define COMMENT_TAG '-'
63 * Define the minimum filename space to reserve for each
64 * ast_variable in case the filename is renamed later by
65 * ast_include_rename().
67 #define MIN_VARIABLE_FNAME_SPACE 40
69 static char *extconfig_conf = "extconfig.conf";
71 static struct ao2_container *cfg_hooks;
72 static void config_hook_exec(const char *filename, const char *module, struct ast_config *cfg);
74 /*! \brief Structure to keep comments for rewriting configuration files */
76 struct ast_comment *next;
77 /*! Comment body allocated after struct. */
81 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
82 struct cache_file_include {
83 AST_LIST_ENTRY(cache_file_include) list;
87 struct cache_file_mtime {
88 AST_LIST_ENTRY(cache_file_mtime) list;
89 AST_LIST_HEAD_NOLOCK(includes, cache_file_include) includes;
90 unsigned int has_exec:1;
93 /*! String stuffed in filename[] after the filename string. */
94 const char *who_asked;
95 /*! Filename and who_asked stuffed after it. */
99 /*! Cached file mtime list. */
100 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
102 static int init_appendbuf(void *data)
104 struct ast_str **str = data;
105 *str = ast_str_create(16);
106 return *str ? 0 : -1;
109 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
111 /* comment buffers are better implemented using the ast_str_*() API */
112 #define CB_SIZE 250 /* initial size of comment buffers */
114 static void CB_ADD(struct ast_str **cb, const char *str)
116 ast_str_append(cb, 0, "%s", str);
119 static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
121 char *s = ast_alloca(len + 1);
122 ast_copy_string(s, str, len);
123 ast_str_append(cb, 0, "%s", str);
126 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
136 static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
138 struct ast_comment *x = NULL;
139 if (!buffer || !ast_str_strlen(buffer)) {
142 if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
143 strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
148 /* I need to keep track of each config file, and all its inclusions,
149 so that we can track blank lines in each */
156 static int hash_string(const void *obj, const int flags)
158 char *str = ((struct inclfile *) obj)->fname;
161 for (total = 0; *str; str++) {
162 unsigned int tmp = total;
163 total <<= 1; /* multiply by 2 */
164 total += tmp; /* multiply by 3 */
165 total <<= 2; /* multiply by 12 */
166 total += tmp; /* multiply by 13 */
168 total += ((unsigned int) (*str));
176 static int hashtab_compare_strings(void *a, void *b, int flags)
178 const struct inclfile *ae = a, *be = b;
179 return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
182 static struct ast_config_map {
183 struct ast_config_map *next;
185 /*! Stored in stuff[] at struct end. */
187 /*! Stored in stuff[] at struct end. */
189 /*! Stored in stuff[] at struct end. */
190 const char *database;
191 /*! Stored in stuff[] at struct end. */
193 /*! Contents of name, driver, database, and table in that order stuffed here. */
195 } *config_maps = NULL;
197 AST_MUTEX_DEFINE_STATIC(config_lock);
198 static struct ast_config_engine *config_engine_list;
200 #define MAX_INCLUDE_LEVEL 10
202 struct ast_category_template_instance {
203 char name[80]; /* redundant? */
204 const struct ast_category *inst;
205 AST_LIST_ENTRY(ast_category_template_instance) next;
208 struct ast_category {
210 int ignored; /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
213 * \brief The file name from whence this declaration was read
214 * \note Will never be NULL
218 AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
219 struct ast_comment *precomments;
220 struct ast_comment *sameline;
221 struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
222 /*! First category variable in the list. */
223 struct ast_variable *root;
224 /*! Last category variable in the list. */
225 struct ast_variable *last;
226 /*! Next node in the list. */
227 struct ast_category *next;
231 /*! First config category in the list. */
232 struct ast_category *root;
233 /*! Last config category in the list. */
234 struct ast_category *last;
235 struct ast_category *current;
236 struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
238 int max_include_level;
239 struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
242 struct ast_config_include {
244 * \brief file name in which the include occurs
245 * \note Will never be NULL
247 char *include_location_file;
248 int include_location_lineno; /*!< lineno where include occurred */
249 int exec; /*!< set to non-zero if its a #exec statement */
251 * \brief if it's an exec, you'll have both the /var/tmp to read, and the original script
252 * \note Will never be NULL if exec is non-zero
256 * \brief file name included
257 * \note Will never be NULL
260 int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
261 we explode the instances and will include those-- so all entries will be unique */
262 int output; /*!< a flag to indicate if the inclusion has been output */
263 struct ast_config_include *next; /*!< ptr to next inclusion in the list */
266 static void ast_variable_destroy(struct ast_variable *doomed);
267 static void ast_includes_destroy(struct ast_config_include *incls);
270 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
272 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
275 struct ast_variable *variable;
276 int name_len = strlen(name) + 1;
277 int val_len = strlen(value) + 1;
278 int fn_len = strlen(filename) + 1;
280 /* Ensure a minimum length in case the filename is changed later. */
281 if (fn_len < MIN_VARIABLE_FNAME_SPACE) {
282 fn_len = MIN_VARIABLE_FNAME_SPACE;
287 (variable = __ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable), file, lineno, func))
289 (variable = ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable)))
292 char *dst = variable->stuff; /* writable space starts here */
294 /* Put file first so ast_include_rename() can calculate space available. */
295 variable->file = strcpy(dst, filename);
297 variable->name = strcpy(dst, name);
299 variable->value = strcpy(dst, value);
306 * \brief Move the contents from the source to the destination variable.
308 * \param dst_var Destination variable node
309 * \param src_var Source variable node
313 static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable *src_var)
315 dst_var->lineno = src_var->lineno;
316 dst_var->object = src_var->object;
317 dst_var->blanklines = src_var->blanklines;
318 dst_var->precomments = src_var->precomments;
319 src_var->precomments = NULL;
320 dst_var->sameline = src_var->sameline;
321 src_var->sameline = NULL;
322 dst_var->trailing = src_var->trailing;
323 src_var->trailing = NULL;
326 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)
328 /* a file should be included ONCE. Otherwise, if one of the instances is changed,
329 * then all be changed. -- how do we know to include it? -- Handling modified
330 * instances is possible, I'd have
331 * to create a new master for each instance. */
332 struct ast_config_include *inc;
335 inc = ast_include_find(conf, included_file);
338 inc->inclusion_count++;
339 snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
340 } while (stat(real_included_file_name, &statbuf) == 0);
341 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);
343 *real_included_file_name = 0;
345 inc = ast_calloc(1,sizeof(struct ast_config_include));
349 inc->include_location_file = ast_strdup(from_file);
350 inc->include_location_lineno = from_lineno;
351 if (!ast_strlen_zero(real_included_file_name))
352 inc->included_file = ast_strdup(real_included_file_name);
354 inc->included_file = ast_strdup(included_file);
358 inc->exec_file = ast_strdup(exec_file);
360 if (!inc->include_location_file
361 || !inc->included_file
362 || (is_exec && !inc->exec_file)) {
363 ast_includes_destroy(inc);
367 /* attach this new struct to the conf struct */
368 inc->next = conf->includes;
369 conf->includes = inc;
374 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
376 struct ast_config_include *incl;
377 struct ast_category *cat;
380 int from_len = strlen(from_file);
381 int to_len = strlen(to_file);
383 if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
386 /* the manager code allows you to read in one config file, then
387 * write it back out under a different name. But, the new arrangement
388 * ties output lines to the file name. So, before you try to write
389 * the config file to disk, better riffle thru the data and make sure
390 * the file names are changed.
392 /* file names are on categories, includes (of course), and on variables. So,
393 * traverse all this and swap names */
395 for (incl = conf->includes; incl; incl=incl->next) {
396 if (strcmp(incl->include_location_file,from_file) == 0) {
397 if (from_len >= to_len)
398 strcpy(incl->include_location_file, to_file);
400 /* Keep the old filename if the allocation fails. */
401 str = ast_strdup(to_file);
403 ast_free(incl->include_location_file);
404 incl->include_location_file = str;
409 for (cat = conf->root; cat; cat = cat->next) {
410 struct ast_variable **prev;
411 struct ast_variable *v;
412 struct ast_variable *new_var;
414 if (strcmp(cat->file,from_file) == 0) {
415 if (from_len >= to_len)
416 strcpy(cat->file, to_file);
418 /* Keep the old filename if the allocation fails. */
419 str = ast_strdup(to_file);
426 for (prev = &cat->root, v = cat->root; v; prev = &v->next, v = v->next) {
427 if (strcmp(v->file, from_file)) {
432 * Calculate actual space available. The file string is
433 * intentionally stuffed before the name string just so we can
436 if (to_len < v->name - v->file) {
437 /* The new name will fit in the available space. */
438 str = (char *) v->file;/* Stupid compiler complains about discarding qualifiers even though I used a cast. */
439 strcpy(str, to_file);/* SAFE */
443 /* Keep the old filename if the allocation fails. */
444 new_var = ast_variable_new(v->name, v->value, to_file);
449 /* Move items from the old list node to the replacement node. */
450 ast_variable_move(new_var, v);
452 /* Replace the old node in the list with the new node. */
453 new_var->next = v->next;
454 if (cat->last == v) {
459 ast_variable_destroy(v);
466 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
468 struct ast_config_include *x;
469 for (x=conf->includes;x;x=x->next) {
470 if (strcmp(x->included_file,included_file) == 0)
477 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
482 category->last->next = variable;
484 category->root = variable;
485 category->last = variable;
486 while (category->last->next)
487 category->last = category->last->next;
490 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
492 struct ast_variable *cur = category->root;
496 if (!variable || sscanf(line, "%30d", &insertline) != 1) {
500 variable->next = category->root;
501 category->root = variable;
503 for (lineno = 1; lineno < insertline; lineno++) {
509 variable->next = cur->next;
510 cur->next = variable;
514 static void ast_comment_destroy(struct ast_comment **comment)
516 struct ast_comment *n, *p;
518 for (p = *comment; p; p = n) {
526 static void ast_variable_destroy(struct ast_variable *doomed)
528 ast_comment_destroy(&doomed->precomments);
529 ast_comment_destroy(&doomed->sameline);
530 ast_comment_destroy(&doomed->trailing);
534 struct ast_variable *ast_variables_dup(struct ast_variable *var)
536 struct ast_variable *cloned;
537 struct ast_variable *tmp;
539 if (!(cloned = ast_variable_new(var->name, var->value, var->file))) {
545 while ((var = var->next)) {
546 if (!(tmp->next = ast_variable_new(var->name, var->value, var->file))) {
547 ast_variables_destroy(cloned);
556 void ast_variables_destroy(struct ast_variable *v)
558 struct ast_variable *vn;
563 ast_variable_destroy(vn);
567 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
569 struct ast_category *cat = NULL;
575 if (config->last_browse && (config->last_browse->name == category)) {
576 cat = config->last_browse;
578 cat = ast_category_get(config, category);
581 return (cat) ? cat->root : NULL;
584 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
587 tmp = ast_variable_retrieve(cfg, cat, var);
589 tmp = ast_variable_retrieve(cfg, "general", var);
595 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
597 struct ast_variable *v;
600 for (v = ast_variable_browse(config, category); v; v = v->next) {
601 if (!strcasecmp(variable, v->name)) {
606 struct ast_category *cat;
608 for (cat = config->root; cat; cat = cat->next) {
609 for (v = cat->root; v; v = v->next) {
610 if (!strcasecmp(variable, v->name)) {
620 static struct ast_variable *variable_clone(const struct ast_variable *old)
622 struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
625 new->lineno = old->lineno;
626 new->object = old->object;
627 new->blanklines = old->blanklines;
628 /* TODO: clone comments? */
634 static void move_variables(struct ast_category *old, struct ast_category *new)
636 struct ast_variable *var = old->root;
639 /* we can just move the entire list in a single op */
640 ast_variable_append(new, var);
643 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
645 struct ast_category *category;
647 category = ast_calloc(1, sizeof(*category));
651 category->file = ast_strdup(in_file);
652 if (!category->file) {
653 ast_category_destroy(category);
656 ast_copy_string(category->name, name, sizeof(category->name));
657 category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
661 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
663 struct ast_category *cat;
665 /* try exact match first, then case-insensitive match */
666 for (cat = config->root; cat; cat = cat->next) {
667 if (cat->name == category_name && (ignored || !cat->ignored))
671 for (cat = config->root; cat; cat = cat->next) {
672 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
679 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
681 return category_get(config, category_name, 0);
684 int ast_category_exist(const struct ast_config *config, const char *category_name)
686 return !!ast_category_get(config, category_name);
689 void ast_category_append(struct ast_config *config, struct ast_category *category)
692 config->last->next = category;
694 config->root = category;
695 category->include_level = config->include_level;
696 config->last = category;
697 config->current = category;
700 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
702 struct ast_category *cur_category;
706 if (!strcasecmp(config->root->name, match)) {
707 cat->next = config->root;
711 for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
712 if (!strcasecmp(cur_category->next->name, match)) {
713 cat->next = cur_category->next;
714 cur_category->next = cat;
720 static void ast_destroy_template_list(struct ast_category *cat)
722 struct ast_category_template_instance *x;
724 while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
728 void ast_category_destroy(struct ast_category *cat)
730 ast_variables_destroy(cat->root);
733 ast_comment_destroy(&cat->precomments);
734 ast_comment_destroy(&cat->sameline);
735 ast_comment_destroy(&cat->trailing);
736 ast_destroy_template_list(cat);
741 static void ast_includes_destroy(struct ast_config_include *incls)
743 struct ast_config_include *incl,*inclnext;
745 for (incl=incls; incl; incl = inclnext) {
746 inclnext = incl->next;
747 ast_free(incl->include_location_file);
748 ast_free(incl->exec_file);
749 ast_free(incl->included_file);
754 static struct ast_category *next_available_category(struct ast_category *cat)
756 for (; cat && cat->ignored; cat = cat->next);
761 /*! return the first var of a category */
762 struct ast_variable *ast_category_first(struct ast_category *cat)
764 return (cat) ? cat->root : NULL;
767 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
769 struct ast_category *category = ast_category_get(config, cat);
772 return category->root;
776 void ast_config_sort_categories(struct ast_config *config, int descending,
777 int (*comparator)(struct ast_category *p, struct ast_category *q))
780 * The contents of this function are adapted from
781 * an example of linked list merge sorting
782 * copyright 2001 Simon Tatham.
784 * Permission is hereby granted, free of charge, to any person
785 * obtaining a copy of this software and associated documentation
786 * files (the "Software"), to deal in the Software without
787 * restriction, including without limitation the rights to use,
788 * copy, modify, merge, publish, distribute, sublicense, and/or
789 * sell copies of the Software, and to permit persons to whom the
790 * Software is furnished to do so, subject to the following
793 * The above copyright notice and this permission notice shall be
794 * included in all copies or substantial portions of the Software.
796 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
797 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
798 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
799 * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
800 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
801 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
802 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
807 struct ast_category *p, *q, *e, *tail;
808 int nmerges, psize, qsize, i;
810 /* If the descending flag was sent, we'll apply inversion to the comparison function's return. */
826 nmerges = 0; /* count number of merges we do in this pass */
829 nmerges++; /* there exists a merge to be done */
831 /* step `insize' places along from p */
834 for (i = 0; i < insize; i++) {
842 /* if q hasn't fallen off end, we have two lists to merge */
845 /* now we have two lists; merge them */
846 while (psize > 0 || (qsize > 0 && q)) {
847 /* decide whether next element of merge comes from p or q */
849 /* p is empty; e must come from q. */
853 } else if (qsize == 0 || !q) {
854 /* q is empty; e must come from p. */
855 e = p; p = p->next; psize--;
856 } else if ((comparator(p,q) * descending) <= 0) {
857 /* First element of p is lower (or same) e must come from p. */
862 /* First element of q is lower; e must come from q. */
868 /* add the next element to the merged list */
877 /* now p has stepped `insize' places along, and q has too */
883 /* If we have done only one merge, we're finished. */
884 if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
888 /* Otherwise repeat, merging lists twice the size */
894 char *ast_category_browse(struct ast_config *config, const char *prev)
896 struct ast_category *cat;
899 /* First time browse. */
901 } else if (config->last_browse && (config->last_browse->name == prev)) {
902 /* Simple last browse found. */
903 cat = config->last_browse->next;
906 * Config changed since last browse.
908 * First try cheap last browse search. (Rebrowsing a different
909 * previous category?)
911 for (cat = config->root; cat; cat = cat->next) {
912 if (cat->name == prev) {
920 * Have to do it the hard way. (Last category was deleted and
923 for (cat = config->root; cat; cat = cat->next) {
924 if (!strcasecmp(cat->name, prev)) {
934 cat = next_available_category(cat);
936 config->last_browse = cat;
937 return (cat) ? cat->name : NULL;
940 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
942 struct ast_variable *v;
951 void ast_category_rename(struct ast_category *cat, const char *name)
953 ast_copy_string(cat->name, name, sizeof(cat->name));
956 static void inherit_category(struct ast_category *new, const struct ast_category *base)
958 struct ast_variable *var;
959 struct ast_category_template_instance *x;
961 x = ast_calloc(1, sizeof(*x));
965 strcpy(x->name, base->name);
967 AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
968 for (var = base->root; var; var = var->next)
969 ast_variable_append(new, variable_clone(var));
972 struct ast_config *ast_config_new(void)
974 struct ast_config *config;
976 if ((config = ast_calloc(1, sizeof(*config))))
977 config->max_include_level = MAX_INCLUDE_LEVEL;
981 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
983 struct ast_variable *cur, *prev=NULL, *curn;
989 if (!ast_strlen_zero(line)) {
990 /* Requesting to delete by item number. */
991 if (sscanf(line, "%30d", &req_item) != 1
993 /* Invalid item number to delete. */
999 cur = category->root;
1002 /* Delete by item number or by variable name with optional value. */
1003 if ((0 <= req_item && num_item == req_item)
1004 || (req_item < 0 && !strcasecmp(cur->name, variable)
1005 && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
1007 prev->next = cur->next;
1008 if (cur == category->last)
1009 category->last = prev;
1011 category->root = cur->next;
1012 if (cur == category->last)
1013 category->last = NULL;
1015 ast_variable_destroy(cur);
1026 int ast_variable_update(struct ast_category *category, const char *variable,
1027 const char *value, const char *match, unsigned int object)
1029 struct ast_variable *cur, *prev=NULL, *newer=NULL;
1031 for (cur = category->root; cur; prev = cur, cur = cur->next) {
1032 if (strcasecmp(cur->name, variable) ||
1033 (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
1036 if (!(newer = ast_variable_new(variable, value, cur->file)))
1039 ast_variable_move(newer, cur);
1040 newer->object = newer->object || object;
1042 /* Replace the old node in the list with the new node. */
1043 newer->next = cur->next;
1047 category->root = newer;
1048 if (category->last == cur)
1049 category->last = newer;
1051 ast_variable_destroy(cur);
1056 /* Could not find variable to update */
1060 int ast_category_delete(struct ast_config *cfg, const char *category)
1062 struct ast_category *prev=NULL, *cat;
1066 if (cat->name == category) {
1068 prev->next = cat->next;
1069 if (cat == cfg->last)
1072 cfg->root = cat->next;
1073 if (cat == cfg->last)
1076 ast_category_destroy(cat);
1086 if (!strcasecmp(cat->name, category)) {
1088 prev->next = cat->next;
1089 if (cat == cfg->last)
1092 cfg->root = cat->next;
1093 if (cat == cfg->last)
1096 ast_category_destroy(cat);
1105 int ast_category_empty(struct ast_config *cfg, const char *category)
1107 struct ast_category *cat;
1109 for (cat = cfg->root; cat; cat = cat->next) {
1110 if (!strcasecmp(cat->name, category))
1112 ast_variables_destroy(cat->root);
1121 void ast_config_destroy(struct ast_config *cfg)
1123 struct ast_category *cat, *catn;
1128 ast_includes_destroy(cfg->includes);
1134 ast_category_destroy(catn);
1139 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
1141 return cfg->current;
1144 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
1146 /* cast below is just to silence compiler warning about dropping "const" */
1147 cfg->current = (struct ast_category *) cat;
1152 * \brief Create a new cfmtime list node.
1154 * \param filename Config filename caching.
1155 * \param who_asked Who wanted to know.
1157 * \retval cfmtime New node on success.
1158 * \retval NULL on error.
1160 static struct cache_file_mtime *cfmtime_new(const char *filename, const char *who_asked)
1162 struct cache_file_mtime *cfmtime;
1165 cfmtime = ast_calloc(1,
1166 sizeof(*cfmtime) + strlen(filename) + 1 + strlen(who_asked) + 1);
1170 dst = cfmtime->filename; /* writable space starts here */
1171 strcpy(dst, filename);
1172 dst += strlen(dst) + 1;
1173 cfmtime->who_asked = strcpy(dst, who_asked);
1178 enum config_cache_attribute_enum {
1179 ATTRIBUTE_INCLUDE = 0,
1183 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
1185 struct cache_file_mtime *cfmtime;
1186 struct cache_file_include *cfinclude;
1187 struct stat statbuf = { 0, };
1189 /* Find our cached entry for this configuration file */
1190 AST_LIST_LOCK(&cfmtime_head);
1191 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1192 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
1196 cfmtime = cfmtime_new(configfile, who_asked);
1198 AST_LIST_UNLOCK(&cfmtime_head);
1201 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1202 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1205 if (!stat(configfile, &statbuf))
1208 cfmtime->mtime = statbuf.st_mtime;
1211 case ATTRIBUTE_INCLUDE:
1212 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1213 if (!strcmp(cfinclude->include, filename)) {
1214 AST_LIST_UNLOCK(&cfmtime_head);
1218 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
1220 AST_LIST_UNLOCK(&cfmtime_head);
1223 strcpy(cfinclude->include, filename);
1224 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
1226 case ATTRIBUTE_EXEC:
1227 cfmtime->has_exec = 1;
1230 AST_LIST_UNLOCK(&cfmtime_head);
1233 /*! \brief parse one line in the configuration.
1235 * We can have a category header [foo](...)
1236 * a directive #include / #exec
1237 * or a regular line name = value
1240 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
1241 char *buf, int lineno, const char *configfile, struct ast_flags flags,
1242 struct ast_str *comment_buffer,
1243 struct ast_str *lline_buffer,
1244 const char *suggested_include_file,
1245 struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
1249 struct ast_variable *v;
1250 char cmd[512], exec_file[512];
1252 /* Actually parse the entry */
1253 if (cur[0] == '[') { /* A category header */
1254 /* format is one of the following:
1255 * [foo] define a new category named 'foo'
1256 * [foo](!) define a new template category named 'foo'
1257 * [foo](+) append to category 'foo', error if foo does not exist.
1258 * [foo](a) define a new category and inherit from category or template a.
1259 * You can put a comma-separated list of categories and templates
1260 * and '!' and '+' between parentheses, with obvious meaning.
1262 struct ast_category *newcat = NULL;
1265 c = strchr(cur, ']');
1267 ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
1275 if (!(*cat = newcat = ast_category_new(catname,
1276 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
1280 (*cat)->lineno = lineno;
1285 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1286 newcat->precomments = ALLOC_COMMENT(comment_buffer);
1287 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1288 newcat->sameline = ALLOC_COMMENT(lline_buffer);
1289 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1290 CB_RESET(comment_buffer, lline_buffer);
1292 /* If there are options or categories to inherit from, process them now */
1294 if (!(cur = strchr(c, ')'))) {
1295 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
1299 while ((cur = strsep(&c, ","))) {
1300 if (!strcasecmp(cur, "!")) {
1301 (*cat)->ignored = 1;
1302 } else if (!strcasecmp(cur, "+")) {
1303 *cat = category_get(cfg, catname, 1);
1306 ast_category_destroy(newcat);
1307 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
1311 move_variables(newcat, *cat);
1312 ast_category_destroy(newcat);
1316 struct ast_category *base;
1318 base = category_get(cfg, cur, 1);
1320 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1323 inherit_category(*cat, base);
1328 ast_category_append(cfg, *cat);
1329 } else if (cur[0] == '#') { /* A directive - #include or #exec */
1331 char real_inclusion_name[256];
1332 int do_include = 0; /* otherwise, it is exec */
1333 int try_include = 0;
1337 while (*c && (*c > 32)) {
1343 /* Find real argument */
1344 c = ast_strip(c + 1);
1351 if (!strcasecmp(cur, "include")) {
1353 } else if (!strcasecmp(cur, "tryinclude")) {
1356 } else if (!strcasecmp(cur, "exec")) {
1357 if (!ast_opt_exec_includes) {
1358 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1359 return 0; /* XXX is this correct ? or we should return -1 ? */
1362 ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1363 return 0; /* XXX is this correct ? or we should return -1 ? */
1367 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
1368 do_include ? "include / tryinclude" : "exec",
1369 do_include ? "filename" : "/path/to/executable",
1372 return 0; /* XXX is this correct ? or we should return -1 ? */
1376 /* Strip off leading and trailing "'s and <>'s */
1378 if ((*c == '"') || (*c == '<')) {
1379 char quote_char = *c;
1380 if (quote_char == '<') {
1384 if (*(c + strlen(c) - 1) == quote_char) {
1386 *(c + strlen(c) - 1) = '\0';
1391 /* #exec </path/to/executable>
1392 We create a tmp file, then we #include it, then we delete it. */
1394 struct timeval now = ast_tvnow();
1395 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1396 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1397 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1398 snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1399 ast_safe_system(cmd);
1402 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1403 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1404 exec_file[0] = '\0';
1407 /* record this inclusion */
1408 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1410 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1411 if (!ast_strlen_zero(exec_file))
1413 if (!do_include && !try_include) {
1414 ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1417 /* XXX otherwise what ? the default return is 0 anyways */
1420 /* Just a line (variable = value) */
1423 ast_log(LOG_WARNING,
1424 "parse error: No category context for line %d of %s\n", lineno, configfile);
1427 c = strchr(cur, '=');
1429 if (c && c > cur && (*(c - 1) == '+')) {
1430 struct ast_variable *var, *replace = NULL;
1431 struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1433 if (!str || !*str) {
1439 cur = ast_strip(cur);
1441 /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1442 for (var = ast_category_first(*cat); var; var = var->next) {
1443 if (!strcmp(var->name, cur)) {
1449 /* Nothing to replace; just set a variable normally. */
1450 goto set_new_variable;
1453 ast_str_set(str, 0, "%s", replace->value);
1454 ast_str_append(str, 0, "%s", c);
1455 ast_str_trim_blanks(*str);
1456 ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1460 /* Ignore > in => */
1466 if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1471 /* Put and reset comments */
1473 ast_variable_append(*cat, v);
1475 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1476 v->precomments = ALLOC_COMMENT(comment_buffer);
1477 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1478 v->sameline = ALLOC_COMMENT(lline_buffer);
1479 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1480 CB_RESET(comment_buffer, lline_buffer);
1486 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1492 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)
1495 #if defined(LOW_MEMORY)
1500 char *new_buf, *comment_p, *process_buf;
1503 int comment = 0, nest[MAX_NESTED_COMMENTS];
1504 struct ast_category *cat = NULL;
1506 struct stat statbuf;
1507 struct cache_file_mtime *cfmtime = NULL;
1508 struct cache_file_include *cfinclude;
1509 struct ast_variable *last_var = 0;
1510 struct ast_category *last_cat = 0;
1511 /*! Growable string buffer */
1512 struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
1513 struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */
1516 cat = ast_config_get_current_category(cfg);
1518 if (filename[0] == '/') {
1519 ast_copy_string(fn, filename, sizeof(fn));
1521 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1524 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1525 comment_buffer = ast_str_create(CB_SIZE);
1527 lline_buffer = ast_str_create(CB_SIZE);
1528 if (!lline_buffer) {
1529 ast_free(comment_buffer);
1530 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1534 #ifdef AST_INCLUDE_GLOB
1538 globbuf.gl_offs = 0; /* initialize it to silence gcc */
1539 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1540 if (glob_ret == GLOB_NOSPACE)
1541 ast_log(LOG_WARNING,
1542 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1543 else if (glob_ret == GLOB_ABORTED)
1544 ast_log(LOG_WARNING,
1545 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1547 /* loop over expanded files */
1549 for (i=0; i<globbuf.gl_pathc; i++) {
1550 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1553 * The following is not a loop, but just a convenient way to define a block
1554 * (using do { } while(0) ), and be able to exit from it with 'continue'
1555 * or 'break' in case of errors. Nice trick.
1558 if (stat(fn, &statbuf))
1561 if (!S_ISREG(statbuf.st_mode)) {
1562 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1566 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1567 /* Find our cached entry for this configuration file */
1568 AST_LIST_LOCK(&cfmtime_head);
1569 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1570 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
1574 cfmtime = cfmtime_new(fn, who_asked);
1577 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1578 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1582 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1583 /* File is unchanged, what about the (cached) includes (if any)? */
1585 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1586 /* We must glob here, because if we did not, then adding a file to globbed directory would
1587 * incorrectly cause no reload to be necessary. */
1589 #ifdef AST_INCLUDE_GLOB
1591 glob_t glob_buf = { .gl_offs = 0 };
1592 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
1593 /* On error, we reparse */
1594 if (glob_return == GLOB_NOSPACE || glob_return == GLOB_ABORTED)
1597 /* loop over expanded files */
1599 for (j = 0; j < glob_buf.gl_pathc; j++) {
1600 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
1602 ast_copy_string(fn2, cfinclude->include);
1604 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
1605 /* that second-to-last field needs to be looked at in this case... TODO */
1607 /* One change is enough to short-circuit and reload the whole shebang */
1610 #ifdef AST_INCLUDE_GLOB
1617 AST_LIST_UNLOCK(&cfmtime_head);
1618 ast_free(comment_buffer);
1619 ast_free(lline_buffer);
1620 return CONFIG_STATUS_FILEUNCHANGED;
1623 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1624 AST_LIST_UNLOCK(&cfmtime_head);
1626 /* If cfg is NULL, then we just want an answer */
1628 ast_free(comment_buffer);
1629 ast_free(lline_buffer);
1634 cfmtime->mtime = statbuf.st_mtime;
1636 if (!(f = fopen(fn, "r"))) {
1637 ast_debug(1, "No file to parse: %s\n", fn);
1638 ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
1642 /* If we get to this point, then we're loading regardless */
1643 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1644 ast_debug(1, "Parsing %s\n", fn);
1645 ast_verb(2, "Parsing '%s': Found\n", fn);
1648 if (fgets(buf, sizeof(buf), f)) {
1649 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
1650 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1651 ast_str_reset(lline_buffer); /* erase the lline buffer */
1660 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"))) {
1661 /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1662 CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
1663 continue; /* go get a new line, then */
1666 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1667 if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
1668 /* Escaped semicolons aren't comments. */
1669 new_buf = comment_p;
1670 /* write over the \ and bring the null terminator with us */
1671 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
1672 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1673 /* Meta-Comment start detected ";--" */
1674 if (comment < MAX_NESTED_COMMENTS) {
1676 new_buf = comment_p + 3;
1678 nest[comment-1] = lineno;
1680 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1682 } else if ((comment_p >= new_buf + 2) &&
1683 (*(comment_p - 1) == COMMENT_TAG) &&
1684 (*(comment_p - 2) == COMMENT_TAG)) {
1685 /* Meta-Comment end detected */
1687 new_buf = comment_p + 1;
1689 /* Back to non-comment now */
1691 /* Actually have to move what's left over the top, then continue */
1693 oldptr = process_buf + strlen(process_buf);
1694 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1695 CB_ADD(&comment_buffer, ";");
1696 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1699 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1702 process_buf = new_buf;
1706 /* If ; is found, and we are not nested in a comment,
1707 we immediately stop all comment processing */
1708 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1709 CB_ADD(&lline_buffer, comment_p);
1712 new_buf = comment_p;
1714 new_buf = comment_p + 1;
1717 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1718 CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
1722 char *buffer = ast_strip(process_buf);
1723 if (!ast_strlen_zero(buffer)) {
1724 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
1725 cfg = CONFIG_STATUS_FILEINVALID;
1732 /* end of file-- anything in a comment buffer? */
1734 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1735 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1736 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1737 ast_str_reset(lline_buffer); /* erase the lline buffer */
1739 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1741 } else if (last_var) {
1742 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1743 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1744 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1745 ast_str_reset(lline_buffer); /* erase the lline buffer */
1747 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1750 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1751 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
1754 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1755 CB_RESET(comment_buffer, lline_buffer);
1760 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1762 #ifdef AST_INCLUDE_GLOB
1763 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1772 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1773 ast_free(comment_buffer);
1774 ast_free(lline_buffer);
1775 comment_buffer = NULL;
1776 lline_buffer = NULL;
1786 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1787 which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1788 recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1789 be shocked and mystified as to why things are not showing up in the files!
1791 Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1792 and line number are stored for each include, plus the name of the file included, so that these statements may be
1793 included in the output files on a file_save operation.
1795 The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1796 are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1797 the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1798 and a header gets added.
1800 vars and category heads are output in the order they are stored in the config file. So, if the software
1801 shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1802 file/lineno data probably won't get changed.
1806 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1812 ast_copy_string(date, ctime(&t), sizeof(date));
1814 fprintf(f1, ";!\n");
1815 fprintf(f1, ";! Automatically generated configuration file\n");
1816 if (strcmp(configfile, fn))
1817 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1819 fprintf(f1, ";! Filename: %s\n", configfile);
1820 fprintf(f1, ";! Generator: %s\n", generator);
1821 fprintf(f1, ";! Creation Date: %s", date);
1822 fprintf(f1, ";!\n");
1825 static void inclfile_destroy(void *obj)
1827 const struct inclfile *o = obj;
1833 static struct inclfile *set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
1835 struct inclfile lookup;
1836 struct inclfile *fi;
1838 if (ast_strlen_zero(file)) {
1839 if (configfile[0] == '/')
1840 ast_copy_string(fn, configfile, fn_size);
1842 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1843 } else if (file[0] == '/')
1844 ast_copy_string(fn, file, fn_size);
1846 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1848 fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1850 /* Found existing include file scratch pad. */
1854 /* set up a file scratch pad */
1855 fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1857 /* Scratch pad creation failed. */
1860 fi->fname = ast_strdup(fn);
1862 /* Scratch pad creation failed. */
1868 ao2_link(fileset, fi);
1873 static int count_linefeeds(char *str)
1885 static int count_linefeeds_in_comments(struct ast_comment *x)
1890 count += count_linefeeds(x->cmt);
1896 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1898 int precomment_lines;
1902 /* No file scratch pad object so insert no blank lines. */
1906 precomment_lines = count_linefeeds_in_comments(precomments);
1908 /* I don't have to worry about those ;! comments, they are
1909 stored in the precomments, but not printed back out.
1910 I did have to make sure that comments following
1911 the ;! header comments were not also deleted in the process */
1912 if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
1914 } else if (lineno == 0) {
1915 /* Line replacements also mess things up */
1917 } else if (lineno - precomment_lines - fi->lineno < 5) {
1918 /* Only insert less than 5 blank lines; if anything more occurs,
1919 * it's probably due to context deletion. */
1920 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
1924 /* Deletion occurred - insert a single blank line, for separation of
1929 fi->lineno = lineno + 1; /* Advance the file lineno */
1932 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1934 return ast_config_text_file_save(configfile, cfg, generator);
1937 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1941 struct ast_variable *var;
1942 struct ast_category *cat;
1943 struct ast_comment *cmt;
1944 struct ast_config_include *incl;
1946 struct ao2_container *fileset;
1947 struct inclfile *fi;
1949 fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
1951 /* Container creation failed. */
1955 /* reset all the output flags, in case this isn't our first time saving this data */
1956 for (incl = cfg->includes; incl; incl = incl->next) {
1960 /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1961 are all truncated to zero bytes and have that nice header*/
1962 for (incl = cfg->includes; incl; incl = incl->next) {
1963 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*/
1964 /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
1965 fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
1968 gen_header(f, configfile, fn, generator);
1969 fclose(f); /* this should zero out the file */
1971 ast_debug(1, "Unable to open for writing: %s\n", fn);
1972 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
1980 /* just set fn to absolute ver of configfile */
1981 fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
1984 (f = fopen(fn, "w+"))
1986 (f = fopen(fn, "w"))
1989 ast_verb(2, "Saving '%s'\n", fn);
1990 gen_header(f, configfile, fn, generator);
1997 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1998 /* since each var, cat, and associated comments can come from any file, we have to be
1999 mobile, and open each file, print, and close it on an entry-by-entry basis */
2002 fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
2005 ast_debug(1, "Unable to open for writing: %s\n", fn);
2006 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2010 ao2_ref(fileset, -1);
2014 /* dump any includes that happen before this category header */
2015 for (incl=cfg->includes; incl; incl = incl->next) {
2016 if (strcmp(incl->include_location_file, cat->file) == 0){
2017 if (cat->lineno > incl->include_location_lineno && !incl->output) {
2019 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2021 fprintf(f,"#include \"%s\"\n", incl->included_file);
2027 insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
2028 /* Dump section with any appropriate comment */
2029 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
2030 char *cmtp = cmt->cmt;
2031 while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
2032 char *cmtp2 = strchr(cmtp+1, '\n');
2038 fprintf(f,"%s", cmtp);
2040 fprintf(f, "[%s]", cat->name);
2041 if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
2046 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
2049 if (!AST_LIST_EMPTY(&cat->template_instances)) {
2050 struct ast_category_template_instance *x;
2051 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2052 fprintf(f,"%s",x->name);
2053 if (x != AST_LIST_LAST(&cat->template_instances))
2059 for(cmt = cat->sameline; cmt; cmt=cmt->next)
2061 fprintf(f,"%s", cmt->cmt);
2065 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
2066 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2067 fprintf(f,"%s", cmt->cmt);
2076 struct ast_category_template_instance *x;
2078 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2079 struct ast_variable *v;
2080 for (v = x->inst->root; v; v = v->next) {
2081 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2093 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
2096 ast_debug(1, "Unable to open for writing: %s\n", fn);
2097 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2101 ao2_ref(fileset, -1);
2105 /* dump any includes that happen before this category header */
2106 for (incl=cfg->includes; incl; incl = incl->next) {
2107 if (strcmp(incl->include_location_file, var->file) == 0){
2108 if (var->lineno > incl->include_location_lineno && !incl->output) {
2110 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2112 fprintf(f,"#include \"%s\"\n", incl->included_file);
2118 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
2119 for (cmt = var->precomments; cmt; cmt=cmt->next) {
2120 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2121 fprintf(f,"%s", cmt->cmt);
2124 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
2126 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
2127 for (cmt = var->trailing; cmt; cmt=cmt->next) {
2128 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2129 fprintf(f,"%s", cmt->cmt);
2131 if (var->blanklines) {
2132 blanklines = var->blanklines;
2133 while (blanklines--)
2146 if (!option_debug) {
2147 ast_verb(2, "Saving '%s': saved\n", fn);
2150 ast_debug(1, "Unable to open for writing: %s\n", fn);
2151 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
2155 ao2_ref(fileset, -1);
2159 /* Now, for files with trailing #include/#exec statements,
2160 we have to make sure every entry is output */
2161 for (incl=cfg->includes; incl; incl = incl->next) {
2162 if (!incl->output) {
2163 /* open the respective file */
2164 fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
2167 ast_debug(1, "Unable to open for writing: %s\n", fn);
2168 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2172 ao2_ref(fileset, -1);
2176 /* output the respective include */
2178 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2180 fprintf(f,"#include \"%s\"\n", incl->included_file);
2188 ao2_ref(fileset, -1); /* this should destroy the hash container */
2193 static void clear_config_maps(void)
2195 struct ast_config_map *map;
2197 SCOPED_MUTEX(lock, &config_lock);
2199 while (config_maps) {
2201 config_maps = config_maps->next;
2206 static int append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2208 struct ast_config_map *map;
2212 length = sizeof(*map);
2213 length += strlen(name) + 1;
2214 length += strlen(driver) + 1;
2215 length += strlen(database) + 1;
2217 length += strlen(table) + 1;
2219 if (!(map = ast_calloc(1, length)))
2222 dst = map->stuff; /* writable space starts here */
2223 map->name = strcpy(dst, name);
2224 dst += strlen(dst) + 1;
2225 map->driver = strcpy(dst, driver);
2226 dst += strlen(dst) + 1;
2227 map->database = strcpy(dst, database);
2229 dst += strlen(dst) + 1;
2230 map->table = strcpy(dst, table);
2232 map->priority = priority;
2233 map->next = config_maps;
2236 ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
2241 int read_config_maps(void)
2243 struct ast_config *config, *configtmp;
2244 struct ast_variable *v;
2245 char *driver, *table, *database, *textpri, *stringp, *tmp;
2246 struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
2249 clear_config_maps();
2251 configtmp = ast_config_new();
2253 ast_log(LOG_ERROR, "Unable to allocate memory for new config\n");
2256 configtmp->max_include_level = 1;
2257 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
2258 if (config == CONFIG_STATUS_FILEINVALID) {
2260 } else if (!config) {
2261 ast_config_destroy(configtmp);
2265 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
2267 ast_copy_string(buf, v->value, sizeof(buf));
2269 driver = strsep(&stringp, ",");
2271 if ((tmp = strchr(stringp, '\"')))
2274 /* check if the database text starts with a double quote */
2275 if (*stringp == '"') {
2277 database = strsep(&stringp, "\"");
2278 strsep(&stringp, ",");
2280 /* apparently this text has no quotes */
2281 database = strsep(&stringp, ",");
2284 table = strsep(&stringp, ",");
2285 textpri = strsep(&stringp, ",");
2286 if (!textpri || !(pri = atoi(textpri))) {
2290 if (!strcmp(v->name, extconfig_conf)) {
2291 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
2295 if (!strcmp(v->name, "asterisk.conf")) {
2296 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
2300 if (!strcmp(v->name, "logger.conf")) {
2301 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
2305 if (!driver || !database)
2307 if (!strcasecmp(v->name, "sipfriends")) {
2308 ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
2309 append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
2310 } else if (!strcasecmp(v->name, "iaxfriends")) {
2311 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");
2312 append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
2313 append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
2315 append_mapping(v->name, driver, database, table, pri);
2318 ast_config_destroy(config);
2322 int ast_config_engine_register(struct ast_config_engine *new)
2324 struct ast_config_engine *ptr;
2326 SCOPED_MUTEX(lock, &config_lock);
2328 if (!config_engine_list) {
2329 config_engine_list = new;
2331 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
2335 ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
2340 int ast_config_engine_deregister(struct ast_config_engine *del)
2342 struct ast_config_engine *ptr, *last=NULL;
2344 SCOPED_MUTEX(lock, &config_lock);
2346 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
2349 last->next = ptr->next;
2351 config_engine_list = ptr->next;
2360 int ast_realtime_is_mapping_defined(const char *family)
2362 struct ast_config_map *map;
2363 SCOPED_MUTEX(lock, &config_lock);
2365 for (map = config_maps; map; map = map->next) {
2366 if (!strcasecmp(family, map->name)) {
2374 /*! \brief Find realtime engine for realtime family */
2375 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
2377 struct ast_config_engine *eng, *ret = NULL;
2378 struct ast_config_map *map;
2380 SCOPED_MUTEX(lock, &config_lock);
2382 for (map = config_maps; map; map = map->next) {
2383 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
2385 ast_copy_string(database, map->database, dbsiz);
2387 ast_copy_string(table, map->table ? map->table : family, tabsiz);
2392 /* Check if the required driver (engine) exist */
2394 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
2395 if (!strcasecmp(eng->name, map->driver))
2400 /* if we found a mapping, but the engine is not available, then issue a warning */
2402 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
2407 static struct ast_config_engine text_file_engine = {
2409 .load_func = config_text_file_load,
2412 struct ast_config *ast_config_copy(const struct ast_config *old)
2414 struct ast_config *new_config = ast_config_new();
2415 struct ast_category *cat_iter;
2421 for (cat_iter = old->root; cat_iter; cat_iter = cat_iter->next) {
2422 struct ast_category *new_cat =
2423 ast_category_new(cat_iter->name, cat_iter->file, cat_iter->lineno);
2427 ast_category_append(new_config, new_cat);
2428 if (cat_iter->root) {
2429 new_cat->root = ast_variables_dup(cat_iter->root);
2430 if (!new_cat->root) {
2433 new_cat->last = cat_iter->last;
2440 ast_config_destroy(new_config);
2445 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)
2449 struct ast_config_engine *loader = &text_file_engine;
2450 struct ast_config *result;
2452 /* The config file itself bumps include_level by 1 */
2453 if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2454 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2458 cfg->include_level++;
2460 if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
2461 struct ast_config_engine *eng;
2463 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
2466 if (eng && eng->load_func) {
2469 eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
2470 if (eng && eng->load_func)
2475 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2477 if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED) {
2478 result->include_level--;
2479 config_hook_exec(filename, who_asked, result);
2480 } else if (result != CONFIG_STATUS_FILEINVALID) {
2481 cfg->include_level--;
2487 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
2489 struct ast_config *cfg;
2490 struct ast_config *result;
2492 cfg = ast_config_new();
2496 result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
2497 if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
2498 ast_config_destroy(cfg);
2503 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
2505 struct ast_config_engine *eng;
2508 struct ast_variable *res=NULL;
2511 for (i = 1; ; i++) {
2512 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2513 if (eng->realtime_func && (res = eng->realtime_func(db, table, ap))) {
2524 struct ast_variable *ast_load_realtime_all(const char *family, ...)
2526 struct ast_variable *res;
2529 va_start(ap, family);
2530 res = ast_load_realtime_helper(family, ap);
2536 struct ast_variable *ast_load_realtime(const char *family, ...)
2538 struct ast_variable *res;
2539 struct ast_variable *cur;
2540 struct ast_variable **prev;
2543 va_start(ap, family);
2544 res = ast_load_realtime_helper(family, ap);
2547 /* Filter the list. */
2551 if (ast_strlen_zero(cur->value)) {
2552 /* Eliminate empty entries */
2553 struct ast_variable *next;
2557 ast_variable_destroy(cur);
2560 /* Make blank entries empty and keep them. */
2561 if (cur->value[0] == ' ' && cur->value[1] == '\0') {
2562 char *vptr = (char *) cur->value;
2574 /*! \brief Check if realtime engine is configured for family */
2575 int ast_check_realtime(const char *family)
2577 struct ast_config_engine *eng;
2578 if (!ast_realtime_enabled()) {
2579 return 0; /* There are no engines at all so fail early */
2582 eng = find_engine(family, 1, NULL, 0, NULL, 0);
2588 /*! \brief Check if there's any realtime engines loaded */
2589 int ast_realtime_enabled(void)
2591 return config_maps ? 1 : 0;
2594 int ast_realtime_require_field(const char *family, ...)
2596 struct ast_config_engine *eng;
2602 va_start(ap, family);
2603 for (i = 1; ; i++) {
2604 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2605 /* If the require succeeds, it returns 0. */
2606 if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
2618 int ast_unload_realtime(const char *family)
2620 struct ast_config_engine *eng;
2625 for (i = 1; ; i++) {
2626 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2627 if (eng->unload_func) {
2628 /* Do this for ALL engines */
2629 res = eng->unload_func(db, table);
2638 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2640 struct ast_config_engine *eng;
2643 struct ast_config *res = NULL;
2647 va_start(ap, family);
2648 for (i = 1; ; i++) {
2649 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2650 if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, ap))) {
2651 /* If we were returned an empty cfg, destroy it and return NULL */
2653 ast_config_destroy(res);
2667 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2669 struct ast_config_engine *eng;
2675 va_start(ap, lookup);
2676 for (i = 1; ; i++) {
2677 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2678 /* If the update succeeds, it returns 0. */
2679 if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, ap))) {
2691 int ast_update2_realtime(const char *family, ...)
2693 struct ast_config_engine *eng;
2699 va_start(ap, family);
2700 for (i = 1; ; i++) {
2701 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2702 if (eng->update2_func && !(res = eng->update2_func(db, table, ap))) {
2714 int ast_store_realtime(const char *family, ...)
2716 struct ast_config_engine *eng;
2722 va_start(ap, family);
2723 for (i = 1; ; i++) {
2724 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2725 /* If the store succeeds, it returns 0. */
2726 if (eng->store_func && !(res = eng->store_func(db, table, ap))) {
2738 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2740 struct ast_config_engine *eng;
2746 va_start(ap, lookup);
2747 for (i = 1; ; i++) {
2748 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2749 if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, ap))) {
2761 char *ast_realtime_decode_chunk(char *chunk)
2764 for (; *chunk; chunk++) {
2765 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
2766 sscanf(chunk + 1, "%02hhX", chunk);
2767 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
2773 char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
2775 if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
2776 ast_str_set(dest, maxlen, "%s", chunk);
2778 ast_str_reset(*dest);
2779 for (; *chunk; chunk++) {
2780 if (strchr(";^", *chunk)) {
2781 ast_str_append(dest, maxlen, "^%02hhX", *chunk);
2783 ast_str_append(dest, maxlen, "%c", *chunk);
2787 return ast_str_buffer(*dest);
2790 /*! \brief Helper function to parse arguments
2791 * See documentation in config.h
2793 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2794 void *p_result, ...)
2799 va_start(ap, p_result);
2800 switch (flags & PARSE_TYPE) {
2804 int32_t *result = p_result;
2805 int32_t def = result ? *result : 0, high = INT32_MAX, low = INT32_MIN;
2806 char *endptr = NULL;
2808 /* optional arguments: default value and/or (low, high) */
2809 if (flags & PARSE_DEFAULT) {
2810 def = va_arg(ap, int32_t);
2812 if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
2813 low = va_arg(ap, int32_t);
2814 high = va_arg(ap, int32_t);
2816 if (ast_strlen_zero(arg)) {
2821 x = strtol(arg, &endptr, 0);
2822 if (*endptr || errno || x < INT32_MIN || x > INT32_MAX) {
2823 /* Parse error, or type out of int32_t bounds */
2827 error = (x < low) || (x > high);
2828 if (flags & PARSE_RANGE_DEFAULTS) {
2831 } else if (x > high) {
2835 if (flags & PARSE_OUT_RANGE) {
2840 *result = error ? def : x;
2843 ast_debug(3, "extract int from [%s] in [%d, %d] gives [%ld](%d)\n",
2844 arg, low, high, result ? *result : x, error);
2850 unsigned long int x = 0;
2851 uint32_t *result = p_result;
2852 uint32_t def = result ? *result : 0, low = 0, high = UINT32_MAX;
2853 char *endptr = NULL;
2855 /* optional argument: first default value, then range */
2856 if (flags & PARSE_DEFAULT) {
2857 def = va_arg(ap, uint32_t);
2859 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2860 /* range requested, update bounds */
2861 low = va_arg(ap, uint32_t);
2862 high = va_arg(ap, uint32_t);
2865 if (ast_strlen_zero(arg)) {
2869 /* strtoul will happilly and silently negate negative numbers */
2870 arg = ast_skip_blanks(arg);
2876 x = strtoul(arg, &endptr, 0);
2877 if (*endptr || errno || x > UINT32_MAX) {
2881 error = (x < low) || (x > high);
2882 if (flags & PARSE_RANGE_DEFAULTS) {
2885 } else if (x > high) {
2889 if (flags & PARSE_OUT_RANGE) {
2894 *result = error ? def : x;
2896 ast_debug(3, "extract uint from [%s] in [%u, %u] gives [%lu](%d)\n",
2897 arg, low, high, result ? *result : x, error);
2903 double *result = p_result;
2904 double x = 0, def = result ? *result : 0, low = -HUGE_VAL, high = HUGE_VAL;
2905 char *endptr = NULL;
2907 /* optional argument: first default value, then range */
2908 if (flags & PARSE_DEFAULT) {
2909 def = va_arg(ap, double);
2911 if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
2912 /* range requested, update bounds */
2913 low = va_arg(ap, double);
2914 high = va_arg(ap, double);
2916 if (ast_strlen_zero(arg)) {
2921 x = strtod(arg, &endptr);
2922 if (*endptr || errno == ERANGE) {
2926 error = (x < low) || (x > high);
2927 if (flags & PARSE_OUT_RANGE) {
2932 *result = error ? def : x;
2934 ast_debug(3, "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
2935 arg, low, high, result ? *result : x, error);
2940 struct ast_sockaddr *addr = (struct ast_sockaddr *)p_result;
2942 if (!ast_sockaddr_parse(addr, arg, flags & PARSE_PORT_MASK)) {
2946 ast_debug(3, "extract addr from %s gives %s(%d)\n",
2947 arg, ast_sockaddr_stringify(addr), error);
2951 case PARSE_INADDR: /* TODO Remove this (use PARSE_ADDR instead). */
2954 struct sockaddr_in _sa_buf; /* buffer for the result */
2955 struct sockaddr_in *sa = p_result ?
2956 (struct sockaddr_in *)p_result : &_sa_buf;
2957 /* default is either the supplied value or the result itself */
2958 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2959 va_arg(ap, struct sockaddr_in *) : sa;
2961 struct ast_hostent ahp;
2963 memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
2964 /* duplicate the string to strip away the :port */
2965 port = ast_strdupa(arg);
2966 buf = strsep(&port, ":");
2967 sa->sin_family = AF_INET; /* assign family */
2969 * honor the ports flag setting, assign default value
2970 * in case of errors or field unset.
2972 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2974 if (flags == PARSE_PORT_FORBID) {
2975 error = 1; /* port was forbidden */
2976 sa->sin_port = def->sin_port;
2977 } else if (flags == PARSE_PORT_IGNORE)
2978 sa->sin_port = def->sin_port;
2979 else /* accept or require */
2980 sa->sin_port = htons(strtol(port, NULL, 0));
2982 sa->sin_port = def->sin_port;
2983 if (flags == PARSE_PORT_REQUIRE)
2986 /* Now deal with host part, even if we have errors before. */
2987 hp = ast_gethostbyname(buf, &ahp);
2988 if (hp) /* resolved successfully */
2989 memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2992 sa->sin_addr = def->sin_addr;
2995 "extract inaddr from [%s] gives [%s:%d](%d)\n",
2996 arg, ast_inet_ntoa(sa->sin_addr),
2997 ntohs(sa->sin_port), error);
3005 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3007 struct ast_config_engine *eng;
3008 struct ast_config_map *map;
3012 e->command = "core show config mappings";
3014 "Usage: core show config mappings\n"
3015 " Shows the filenames to config engines.\n";
3022 SCOPED_MUTEX(lock, &config_lock);
3024 if (!config_engine_list) {
3025 ast_cli(a->fd, "No config mappings found.\n");
3027 for (eng = config_engine_list; eng; eng = eng->next) {
3028 ast_cli(a->fd, "Config Engine: %s\n", eng->name);
3029 for (map = config_maps; map; map = map->next) {
3030 if (!strcasecmp(map->driver, eng->name)) {
3031 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
3032 map->table ? map->table : map->name);
3042 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3044 struct cache_file_mtime *cfmtime;
3045 char *prev = "", *completion_value = NULL;
3046 int wordlen, which = 0;
3050 e->command = "config reload";
3052 "Usage: config reload <filename.conf>\n"
3053 " Reloads all modules that reference <filename.conf>\n";
3060 wordlen = strlen(a->word);
3062 AST_LIST_LOCK(&cfmtime_head);
3063 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
3064 /* Skip duplicates - this only works because the list is sorted by filename */
3065 if (strcmp(cfmtime->filename, prev) == 0) {
3069 /* Core configs cannot be reloaded */
3070 if (ast_strlen_zero(cfmtime->who_asked)) {
3074 if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
3075 completion_value = ast_strdup(cfmtime->filename);
3079 /* Otherwise save that we've seen this filename */
3080 prev = cfmtime->filename;
3082 AST_LIST_UNLOCK(&cfmtime_head);
3084 return completion_value;
3088 return CLI_SHOWUSAGE;
3091 AST_LIST_LOCK(&cfmtime_head);
3092 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
3093 if (!strcmp(cfmtime->filename, a->argv[2])) {
3094 char *buf = ast_alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
3095 sprintf(buf, "module reload %s", cfmtime->who_asked);
3096 ast_cli_command(a->fd, buf);
3099 AST_LIST_UNLOCK(&cfmtime_head);
3104 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3106 struct cache_file_mtime *cfmtime;
3110 e->command = "config list";
3112 "Usage: config list\n"
3113 " Show all modules that have loaded a configuration file\n";
3119 AST_LIST_LOCK(&cfmtime_head);
3120 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
3121 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
3123 AST_LIST_UNLOCK(&cfmtime_head);
3128 static struct ast_cli_entry cli_config[] = {
3129 AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
3130 AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
3131 AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
3134 static void config_shutdown(void)
3136 struct cache_file_mtime *cfmtime;
3138 AST_LIST_LOCK(&cfmtime_head);
3139 while ((cfmtime = AST_LIST_REMOVE_HEAD(&cfmtime_head, list))) {
3142 AST_LIST_UNLOCK(&cfmtime_head);
3144 ast_cli_unregister_multiple(cli_config, ARRAY_LEN(cli_config));
3147 int register_config_cli(void)
3149 ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
3150 ast_register_atexit(config_shutdown);
3156 const char *filename;
3158 config_hook_cb hook_cb;
3161 static void hook_destroy(void *obj)
3163 struct cfg_hook *hook = obj;
3164 ast_free((void *) hook->name);
3165 ast_free((void *) hook->filename);
3166 ast_free((void *) hook->module);
3169 static int hook_cmp(void *obj, void *arg, int flags)
3171 struct cfg_hook *hook1 = obj;
3172 struct cfg_hook *hook2 = arg;
3174 return !(strcasecmp(hook1->name, hook2->name)) ? CMP_MATCH | CMP_STOP : 0;
3177 static int hook_hash(const void *obj, const int flags)
3179 const struct cfg_hook *hook = obj;
3181 return ast_str_hash(hook->name);
3184 void ast_config_hook_unregister(const char *name)
3186 struct cfg_hook tmp;
3188 tmp.name = ast_strdupa(name);
3190 ao2_find(cfg_hooks, &tmp, OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
3193 static void config_hook_exec(const char *filename, const char *module, struct ast_config *cfg)
3195 struct ao2_iterator it;
3196 struct cfg_hook *hook;
3200 it = ao2_iterator_init(cfg_hooks, 0);
3201 while ((hook = ao2_iterator_next(&it))) {
3202 if (!strcasecmp(hook->filename, filename) &&
3203 !strcasecmp(hook->module, module)) {
3204 struct ast_config *copy = ast_config_copy(cfg);
3205 hook->hook_cb(copy);
3209 ao2_iterator_destroy(&it);
3212 int ast_config_hook_register(const char *name,
3213 const char *filename,
3215 enum config_hook_flags flags,
3216 config_hook_cb hook_cb)
3218 struct cfg_hook *hook;
3219 if (!cfg_hooks && !(cfg_hooks = ao2_container_alloc(17, hook_hash, hook_cmp))) {
3223 if (!(hook = ao2_alloc(sizeof(*hook), hook_destroy))) {
3227 hook->hook_cb = hook_cb;
3228 hook->filename = ast_strdup(filename);
3229 hook->name = ast_strdup(name);
3230 hook->module = ast_strdup(module);
3232 ao2_link(cfg_hooks, hook);