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);
73 inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2);
75 /*! \brief Structure to keep comments for rewriting configuration files */
77 struct ast_comment *next;
78 /*! Comment body allocated after struct. */
82 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
83 struct cache_file_include {
84 AST_LIST_ENTRY(cache_file_include) list;
88 struct cache_file_mtime {
89 AST_LIST_ENTRY(cache_file_mtime) list;
90 AST_LIST_HEAD_NOLOCK(includes, cache_file_include) includes;
91 unsigned int has_exec:1;
94 /*! String stuffed in filename[] after the filename string. */
95 const char *who_asked;
96 /*! Filename and who_asked stuffed after it. */
100 /*! Cached file mtime list. */
101 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
103 static int init_appendbuf(void *data)
105 struct ast_str **str = data;
106 *str = ast_str_create(16);
107 return *str ? 0 : -1;
110 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
112 /* comment buffers are better implemented using the ast_str_*() API */
113 #define CB_SIZE 250 /* initial size of comment buffers */
115 static void CB_ADD(struct ast_str **cb, const char *str)
117 ast_str_append(cb, 0, "%s", str);
120 static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
122 char *s = ast_alloca(len + 1);
123 ast_copy_string(s, str, len);
124 ast_str_append(cb, 0, "%s", str);
127 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
137 static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
139 struct ast_comment *x = NULL;
140 if (!buffer || !ast_str_strlen(buffer)) {
143 if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
144 strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
149 /* I need to keep track of each config file, and all its inclusions,
150 so that we can track blank lines in each */
157 static int hash_string(const void *obj, const int flags)
159 char *str = ((struct inclfile *) obj)->fname;
162 for (total = 0; *str; str++) {
163 unsigned int tmp = total;
164 total <<= 1; /* multiply by 2 */
165 total += tmp; /* multiply by 3 */
166 total <<= 2; /* multiply by 12 */
167 total += tmp; /* multiply by 13 */
169 total += ((unsigned int) (*str));
177 static int hashtab_compare_strings(void *a, void *b, int flags)
179 const struct inclfile *ae = a, *be = b;
180 return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
183 static struct ast_config_map {
184 struct ast_config_map *next;
186 /*! Stored in stuff[] at struct end. */
188 /*! Stored in stuff[] at struct end. */
190 /*! Stored in stuff[] at struct end. */
191 const char *database;
192 /*! Stored in stuff[] at struct end. */
194 /*! Contents of name, driver, database, and table in that order stuffed here. */
196 } *config_maps = NULL;
198 AST_MUTEX_DEFINE_STATIC(config_lock);
199 static struct ast_config_engine *config_engine_list;
201 #define MAX_INCLUDE_LEVEL 10
203 struct ast_category_template_instance {
204 char name[80]; /* redundant? */
205 const struct ast_category *inst;
206 AST_LIST_ENTRY(ast_category_template_instance) next;
209 struct ast_category {
211 int ignored; /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
214 * \brief The file name from whence this declaration was read
215 * \note Will never be NULL
219 AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
220 struct ast_comment *precomments;
221 struct ast_comment *sameline;
222 struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
223 /*! First category variable in the list. */
224 struct ast_variable *root;
225 /*! Last category variable in the list. */
226 struct ast_variable *last;
227 /*! Next node in the list. */
228 struct ast_category *next;
232 /*! First config category in the list. */
233 struct ast_category *root;
234 /*! Last config category in the list. */
235 struct ast_category *last;
236 struct ast_category *current;
237 struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
239 int max_include_level;
240 struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
243 struct ast_config_include {
245 * \brief file name in which the include occurs
246 * \note Will never be NULL
248 char *include_location_file;
249 int include_location_lineno; /*!< lineno where include occurred */
250 int exec; /*!< set to non-zero if its a #exec statement */
252 * \brief if it's an exec, you'll have both the /var/tmp to read, and the original script
253 * \note Will never be NULL if exec is non-zero
257 * \brief file name included
258 * \note Will never be NULL
261 int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
262 we explode the instances and will include those-- so all entries will be unique */
263 int output; /*!< a flag to indicate if the inclusion has been output */
264 struct ast_config_include *next; /*!< ptr to next inclusion in the list */
267 static void ast_variable_destroy(struct ast_variable *doomed);
268 static void ast_includes_destroy(struct ast_config_include *incls);
271 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
273 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
276 struct ast_variable *variable;
277 int name_len = strlen(name) + 1;
278 int val_len = strlen(value) + 1;
279 int fn_len = strlen(filename) + 1;
281 /* Ensure a minimum length in case the filename is changed later. */
282 if (fn_len < MIN_VARIABLE_FNAME_SPACE) {
283 fn_len = MIN_VARIABLE_FNAME_SPACE;
288 (variable = __ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable), file, lineno, func))
290 (variable = ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable)))
293 char *dst = variable->stuff; /* writable space starts here */
295 /* Put file first so ast_include_rename() can calculate space available. */
296 variable->file = strcpy(dst, filename);
298 variable->name = strcpy(dst, name);
300 variable->value = strcpy(dst, value);
307 * \brief Move the contents from the source to the destination variable.
309 * \param dst_var Destination variable node
310 * \param src_var Source variable node
314 static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable *src_var)
316 dst_var->lineno = src_var->lineno;
317 dst_var->object = src_var->object;
318 dst_var->blanklines = src_var->blanklines;
319 dst_var->precomments = src_var->precomments;
320 src_var->precomments = NULL;
321 dst_var->sameline = src_var->sameline;
322 src_var->sameline = NULL;
323 dst_var->trailing = src_var->trailing;
324 src_var->trailing = NULL;
327 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)
329 /* a file should be included ONCE. Otherwise, if one of the instances is changed,
330 * then all be changed. -- how do we know to include it? -- Handling modified
331 * instances is possible, I'd have
332 * to create a new master for each instance. */
333 struct ast_config_include *inc;
336 inc = ast_include_find(conf, included_file);
339 inc->inclusion_count++;
340 snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
341 } while (stat(real_included_file_name, &statbuf) == 0);
342 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);
344 *real_included_file_name = 0;
346 inc = ast_calloc(1,sizeof(struct ast_config_include));
350 inc->include_location_file = ast_strdup(from_file);
351 inc->include_location_lineno = from_lineno;
352 if (!ast_strlen_zero(real_included_file_name))
353 inc->included_file = ast_strdup(real_included_file_name);
355 inc->included_file = ast_strdup(included_file);
359 inc->exec_file = ast_strdup(exec_file);
361 if (!inc->include_location_file
362 || !inc->included_file
363 || (is_exec && !inc->exec_file)) {
364 ast_includes_destroy(inc);
368 /* attach this new struct to the conf struct */
369 inc->next = conf->includes;
370 conf->includes = inc;
375 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
377 struct ast_config_include *incl;
378 struct ast_category *cat;
381 int from_len = strlen(from_file);
382 int to_len = strlen(to_file);
384 if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
387 /* the manager code allows you to read in one config file, then
388 * write it back out under a different name. But, the new arrangement
389 * ties output lines to the file name. So, before you try to write
390 * the config file to disk, better riffle thru the data and make sure
391 * the file names are changed.
393 /* file names are on categories, includes (of course), and on variables. So,
394 * traverse all this and swap names */
396 for (incl = conf->includes; incl; incl=incl->next) {
397 if (strcmp(incl->include_location_file,from_file) == 0) {
398 if (from_len >= to_len)
399 strcpy(incl->include_location_file, to_file);
401 /* Keep the old filename if the allocation fails. */
402 str = ast_strdup(to_file);
404 ast_free(incl->include_location_file);
405 incl->include_location_file = str;
410 for (cat = conf->root; cat; cat = cat->next) {
411 struct ast_variable **prev;
412 struct ast_variable *v;
413 struct ast_variable *new_var;
415 if (strcmp(cat->file,from_file) == 0) {
416 if (from_len >= to_len)
417 strcpy(cat->file, to_file);
419 /* Keep the old filename if the allocation fails. */
420 str = ast_strdup(to_file);
427 for (prev = &cat->root, v = cat->root; v; prev = &v->next, v = v->next) {
428 if (strcmp(v->file, from_file)) {
433 * Calculate actual space available. The file string is
434 * intentionally stuffed before the name string just so we can
437 if (to_len < v->name - v->file) {
438 /* The new name will fit in the available space. */
439 str = (char *) v->file;/* Stupid compiler complains about discarding qualifiers even though I used a cast. */
440 strcpy(str, to_file);/* SAFE */
444 /* Keep the old filename if the allocation fails. */
445 new_var = ast_variable_new(v->name, v->value, to_file);
450 /* Move items from the old list node to the replacement node. */
451 ast_variable_move(new_var, v);
453 /* Replace the old node in the list with the new node. */
454 new_var->next = v->next;
455 if (cat->last == v) {
460 ast_variable_destroy(v);
467 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
469 struct ast_config_include *x;
470 for (x=conf->includes;x;x=x->next) {
471 if (strcmp(x->included_file,included_file) == 0)
478 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
483 category->last->next = variable;
485 category->root = variable;
486 category->last = variable;
487 while (category->last->next)
488 category->last = category->last->next;
491 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
493 struct ast_variable *cur = category->root;
497 if (!variable || sscanf(line, "%30d", &insertline) != 1) {
501 variable->next = category->root;
502 category->root = variable;
504 for (lineno = 1; lineno < insertline; lineno++) {
510 variable->next = cur->next;
511 cur->next = variable;
515 static void ast_comment_destroy(struct ast_comment **comment)
517 struct ast_comment *n, *p;
519 for (p = *comment; p; p = n) {
527 static void ast_variable_destroy(struct ast_variable *doomed)
529 ast_comment_destroy(&doomed->precomments);
530 ast_comment_destroy(&doomed->sameline);
531 ast_comment_destroy(&doomed->trailing);
535 struct ast_variable *ast_variables_dup(struct ast_variable *var)
537 struct ast_variable *cloned;
538 struct ast_variable *tmp;
540 if (!(cloned = ast_variable_new(var->name, var->value, var->file))) {
546 while ((var = var->next)) {
547 if (!(tmp->next = ast_variable_new(var->name, var->value, var->file))) {
548 ast_variables_destroy(cloned);
557 void ast_variables_destroy(struct ast_variable *v)
559 struct ast_variable *vn;
564 ast_variable_destroy(vn);
568 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
570 struct ast_category *cat = NULL;
576 if (config->last_browse && (config->last_browse->name == category)) {
577 cat = config->last_browse;
579 cat = ast_category_get(config, category);
582 return (cat) ? cat->root : NULL;
585 inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
592 struct ast_variable *ast_variable_list_sort(struct ast_variable *start)
594 struct ast_variable *p, *q, *top;
596 top = ast_calloc(1, sizeof(struct ast_variable));
598 if (start != NULL && start->next != NULL) {
603 while (p->next != NULL) {
604 if (p->next != NULL && strcmp(p->name, p->next->name) > 0) {
605 q->next = variable_list_switch(p, p->next);
618 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
621 tmp = ast_variable_retrieve(cfg, cat, var);
623 tmp = ast_variable_retrieve(cfg, "general", var);
629 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
631 struct ast_variable *v;
634 for (v = ast_variable_browse(config, category); v; v = v->next) {
635 if (!strcasecmp(variable, v->name)) {
640 struct ast_category *cat;
642 for (cat = config->root; cat; cat = cat->next) {
643 for (v = cat->root; v; v = v->next) {
644 if (!strcasecmp(variable, v->name)) {
654 static struct ast_variable *variable_clone(const struct ast_variable *old)
656 struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
659 new->lineno = old->lineno;
660 new->object = old->object;
661 new->blanklines = old->blanklines;
662 /* TODO: clone comments? */
668 static void move_variables(struct ast_category *old, struct ast_category *new)
670 struct ast_variable *var = old->root;
673 /* we can just move the entire list in a single op */
674 ast_variable_append(new, var);
677 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
679 struct ast_category *category;
681 category = ast_calloc(1, sizeof(*category));
685 category->file = ast_strdup(in_file);
686 if (!category->file) {
687 ast_category_destroy(category);
690 ast_copy_string(category->name, name, sizeof(category->name));
691 category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
695 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
697 struct ast_category *cat;
699 /* try exact match first, then case-insensitive match */
700 for (cat = config->root; cat; cat = cat->next) {
701 if (cat->name == category_name && (ignored || !cat->ignored))
705 for (cat = config->root; cat; cat = cat->next) {
706 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
713 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
715 return category_get(config, category_name, 0);
718 int ast_category_exist(const struct ast_config *config, const char *category_name)
720 return !!ast_category_get(config, category_name);
723 void ast_category_append(struct ast_config *config, struct ast_category *category)
726 config->last->next = category;
728 config->root = category;
729 category->include_level = config->include_level;
730 config->last = category;
731 config->current = category;
734 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
736 struct ast_category *cur_category;
740 if (!strcasecmp(config->root->name, match)) {
741 cat->next = config->root;
745 for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
746 if (!strcasecmp(cur_category->next->name, match)) {
747 cat->next = cur_category->next;
748 cur_category->next = cat;
754 static void ast_destroy_template_list(struct ast_category *cat)
756 struct ast_category_template_instance *x;
758 while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
762 void ast_category_destroy(struct ast_category *cat)
764 ast_variables_destroy(cat->root);
767 ast_comment_destroy(&cat->precomments);
768 ast_comment_destroy(&cat->sameline);
769 ast_comment_destroy(&cat->trailing);
770 ast_destroy_template_list(cat);
775 static void ast_includes_destroy(struct ast_config_include *incls)
777 struct ast_config_include *incl,*inclnext;
779 for (incl=incls; incl; incl = inclnext) {
780 inclnext = incl->next;
781 ast_free(incl->include_location_file);
782 ast_free(incl->exec_file);
783 ast_free(incl->included_file);
788 static struct ast_category *next_available_category(struct ast_category *cat)
790 for (; cat && cat->ignored; cat = cat->next);
795 /*! return the first var of a category */
796 struct ast_variable *ast_category_first(struct ast_category *cat)
798 return (cat) ? cat->root : NULL;
801 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
803 struct ast_category *category = ast_category_get(config, cat);
806 return category->root;
810 void ast_config_sort_categories(struct ast_config *config, int descending,
811 int (*comparator)(struct ast_category *p, struct ast_category *q))
814 * The contents of this function are adapted from
815 * an example of linked list merge sorting
816 * copyright 2001 Simon Tatham.
818 * Permission is hereby granted, free of charge, to any person
819 * obtaining a copy of this software and associated documentation
820 * files (the "Software"), to deal in the Software without
821 * restriction, including without limitation the rights to use,
822 * copy, modify, merge, publish, distribute, sublicense, and/or
823 * sell copies of the Software, and to permit persons to whom the
824 * Software is furnished to do so, subject to the following
827 * The above copyright notice and this permission notice shall be
828 * included in all copies or substantial portions of the Software.
830 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
831 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
832 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
833 * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
834 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
835 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
836 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
841 struct ast_category *p, *q, *e, *tail;
842 int nmerges, psize, qsize, i;
844 /* If the descending flag was sent, we'll apply inversion to the comparison function's return. */
860 nmerges = 0; /* count number of merges we do in this pass */
863 nmerges++; /* there exists a merge to be done */
865 /* step `insize' places along from p */
868 for (i = 0; i < insize; i++) {
876 /* if q hasn't fallen off end, we have two lists to merge */
879 /* now we have two lists; merge them */
880 while (psize > 0 || (qsize > 0 && q)) {
881 /* decide whether next element of merge comes from p or q */
883 /* p is empty; e must come from q. */
887 } else if (qsize == 0 || !q) {
888 /* q is empty; e must come from p. */
889 e = p; p = p->next; psize--;
890 } else if ((comparator(p,q) * descending) <= 0) {
891 /* First element of p is lower (or same) e must come from p. */
896 /* First element of q is lower; e must come from q. */
902 /* add the next element to the merged list */
911 /* now p has stepped `insize' places along, and q has too */
917 /* If we have done only one merge, we're finished. */
918 if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
922 /* Otherwise repeat, merging lists twice the size */
928 char *ast_category_browse(struct ast_config *config, const char *prev)
930 struct ast_category *cat;
933 /* First time browse. */
935 } else if (config->last_browse && (config->last_browse->name == prev)) {
936 /* Simple last browse found. */
937 cat = config->last_browse->next;
940 * Config changed since last browse.
942 * First try cheap last browse search. (Rebrowsing a different
943 * previous category?)
945 for (cat = config->root; cat; cat = cat->next) {
946 if (cat->name == prev) {
954 * Have to do it the hard way. (Last category was deleted and
957 for (cat = config->root; cat; cat = cat->next) {
958 if (!strcasecmp(cat->name, prev)) {
968 cat = next_available_category(cat);
970 config->last_browse = cat;
971 return (cat) ? cat->name : NULL;
974 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
976 struct ast_variable *v;
985 void ast_category_rename(struct ast_category *cat, const char *name)
987 ast_copy_string(cat->name, name, sizeof(cat->name));
990 static void inherit_category(struct ast_category *new, const struct ast_category *base)
992 struct ast_variable *var;
993 struct ast_category_template_instance *x;
995 x = ast_calloc(1, sizeof(*x));
999 strcpy(x->name, base->name);
1001 AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
1002 for (var = base->root; var; var = var->next)
1003 ast_variable_append(new, variable_clone(var));
1006 struct ast_config *ast_config_new(void)
1008 struct ast_config *config;
1010 if ((config = ast_calloc(1, sizeof(*config))))
1011 config->max_include_level = MAX_INCLUDE_LEVEL;
1015 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
1017 struct ast_variable *cur, *prev=NULL, *curn;
1023 if (!ast_strlen_zero(line)) {
1024 /* Requesting to delete by item number. */
1025 if (sscanf(line, "%30d", &req_item) != 1
1027 /* Invalid item number to delete. */
1033 cur = category->root;
1036 /* Delete by item number or by variable name with optional value. */
1037 if ((0 <= req_item && num_item == req_item)
1038 || (req_item < 0 && !strcasecmp(cur->name, variable)
1039 && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
1041 prev->next = cur->next;
1042 if (cur == category->last)
1043 category->last = prev;
1045 category->root = cur->next;
1046 if (cur == category->last)
1047 category->last = NULL;
1049 ast_variable_destroy(cur);
1060 int ast_variable_update(struct ast_category *category, const char *variable,
1061 const char *value, const char *match, unsigned int object)
1063 struct ast_variable *cur, *prev=NULL, *newer=NULL;
1065 for (cur = category->root; cur; prev = cur, cur = cur->next) {
1066 if (strcasecmp(cur->name, variable) ||
1067 (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
1070 if (!(newer = ast_variable_new(variable, value, cur->file)))
1073 ast_variable_move(newer, cur);
1074 newer->object = newer->object || object;
1076 /* Replace the old node in the list with the new node. */
1077 newer->next = cur->next;
1081 category->root = newer;
1082 if (category->last == cur)
1083 category->last = newer;
1085 ast_variable_destroy(cur);
1090 /* Could not find variable to update */
1094 int ast_category_delete(struct ast_config *cfg, const char *category)
1096 struct ast_category *prev=NULL, *cat;
1100 if (cat->name == category) {
1102 prev->next = cat->next;
1103 if (cat == cfg->last)
1106 cfg->root = cat->next;
1107 if (cat == cfg->last)
1110 ast_category_destroy(cat);
1120 if (!strcasecmp(cat->name, category)) {
1122 prev->next = cat->next;
1123 if (cat == cfg->last)
1126 cfg->root = cat->next;
1127 if (cat == cfg->last)
1130 ast_category_destroy(cat);
1139 int ast_category_empty(struct ast_config *cfg, const char *category)
1141 struct ast_category *cat;
1143 for (cat = cfg->root; cat; cat = cat->next) {
1144 if (!strcasecmp(cat->name, category))
1146 ast_variables_destroy(cat->root);
1155 void ast_config_destroy(struct ast_config *cfg)
1157 struct ast_category *cat, *catn;
1162 ast_includes_destroy(cfg->includes);
1168 ast_category_destroy(catn);
1173 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
1175 return cfg->current;
1178 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
1180 /* cast below is just to silence compiler warning about dropping "const" */
1181 cfg->current = (struct ast_category *) cat;
1186 * \brief Create a new cfmtime list node.
1188 * \param filename Config filename caching.
1189 * \param who_asked Who wanted to know.
1191 * \retval cfmtime New node on success.
1192 * \retval NULL on error.
1194 static struct cache_file_mtime *cfmtime_new(const char *filename, const char *who_asked)
1196 struct cache_file_mtime *cfmtime;
1199 cfmtime = ast_calloc(1,
1200 sizeof(*cfmtime) + strlen(filename) + 1 + strlen(who_asked) + 1);
1204 dst = cfmtime->filename; /* writable space starts here */
1205 strcpy(dst, filename);
1206 dst += strlen(dst) + 1;
1207 cfmtime->who_asked = strcpy(dst, who_asked);
1212 enum config_cache_attribute_enum {
1213 ATTRIBUTE_INCLUDE = 0,
1217 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
1219 struct cache_file_mtime *cfmtime;
1220 struct cache_file_include *cfinclude;
1221 struct stat statbuf = { 0, };
1223 /* Find our cached entry for this configuration file */
1224 AST_LIST_LOCK(&cfmtime_head);
1225 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1226 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
1230 cfmtime = cfmtime_new(configfile, who_asked);
1232 AST_LIST_UNLOCK(&cfmtime_head);
1235 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1236 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1239 if (!stat(configfile, &statbuf))
1242 cfmtime->mtime = statbuf.st_mtime;
1245 case ATTRIBUTE_INCLUDE:
1246 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1247 if (!strcmp(cfinclude->include, filename)) {
1248 AST_LIST_UNLOCK(&cfmtime_head);
1252 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
1254 AST_LIST_UNLOCK(&cfmtime_head);
1257 strcpy(cfinclude->include, filename);
1258 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
1260 case ATTRIBUTE_EXEC:
1261 cfmtime->has_exec = 1;
1264 AST_LIST_UNLOCK(&cfmtime_head);
1267 /*! \brief parse one line in the configuration.
1269 * We can have a category header [foo](...)
1270 * a directive #include / #exec
1271 * or a regular line name = value
1274 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
1275 char *buf, int lineno, const char *configfile, struct ast_flags flags,
1276 struct ast_str *comment_buffer,
1277 struct ast_str *lline_buffer,
1278 const char *suggested_include_file,
1279 struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
1283 struct ast_variable *v;
1284 char cmd[512], exec_file[512];
1286 /* Actually parse the entry */
1287 if (cur[0] == '[') { /* A category header */
1288 /* format is one of the following:
1289 * [foo] define a new category named 'foo'
1290 * [foo](!) define a new template category named 'foo'
1291 * [foo](+) append to category 'foo', error if foo does not exist.
1292 * [foo](a) define a new category and inherit from category or template a.
1293 * You can put a comma-separated list of categories and templates
1294 * and '!' and '+' between parentheses, with obvious meaning.
1296 struct ast_category *newcat = NULL;
1299 c = strchr(cur, ']');
1301 ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
1309 if (!(*cat = newcat = ast_category_new(catname,
1310 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
1314 (*cat)->lineno = lineno;
1319 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1320 newcat->precomments = ALLOC_COMMENT(comment_buffer);
1321 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1322 newcat->sameline = ALLOC_COMMENT(lline_buffer);
1323 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1324 CB_RESET(comment_buffer, lline_buffer);
1326 /* If there are options or categories to inherit from, process them now */
1328 if (!(cur = strchr(c, ')'))) {
1329 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
1333 while ((cur = strsep(&c, ","))) {
1334 if (!strcasecmp(cur, "!")) {
1335 (*cat)->ignored = 1;
1336 } else if (!strcasecmp(cur, "+")) {
1337 *cat = category_get(cfg, catname, 1);
1340 ast_category_destroy(newcat);
1341 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
1345 move_variables(newcat, *cat);
1346 ast_category_destroy(newcat);
1350 struct ast_category *base;
1352 base = category_get(cfg, cur, 1);
1354 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1357 inherit_category(*cat, base);
1362 ast_category_append(cfg, *cat);
1363 } else if (cur[0] == '#') { /* A directive - #include or #exec */
1365 char real_inclusion_name[256];
1366 int do_include = 0; /* otherwise, it is exec */
1367 int try_include = 0;
1371 while (*c && (*c > 32)) {
1377 /* Find real argument */
1378 c = ast_strip(c + 1);
1385 if (!strcasecmp(cur, "include")) {
1387 } else if (!strcasecmp(cur, "tryinclude")) {
1390 } else if (!strcasecmp(cur, "exec")) {
1391 if (!ast_opt_exec_includes) {
1392 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1393 return 0; /* XXX is this correct ? or we should return -1 ? */
1396 ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1397 return 0; /* XXX is this correct ? or we should return -1 ? */
1401 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
1402 do_include ? "include / tryinclude" : "exec",
1403 do_include ? "filename" : "/path/to/executable",
1406 return 0; /* XXX is this correct ? or we should return -1 ? */
1410 /* Strip off leading and trailing "'s and <>'s */
1412 if ((*c == '"') || (*c == '<')) {
1413 char quote_char = *c;
1414 if (quote_char == '<') {
1418 if (*(c + strlen(c) - 1) == quote_char) {
1420 *(c + strlen(c) - 1) = '\0';
1425 /* #exec </path/to/executable>
1426 We create a tmp file, then we #include it, then we delete it. */
1428 struct timeval now = ast_tvnow();
1429 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1430 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1431 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1432 snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1433 ast_safe_system(cmd);
1436 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1437 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1438 exec_file[0] = '\0';
1441 /* record this inclusion */
1442 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1444 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1445 if (!ast_strlen_zero(exec_file))
1447 if (!do_include && !try_include) {
1448 ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1451 /* XXX otherwise what ? the default return is 0 anyways */
1454 /* Just a line (variable = value) */
1459 ast_log(LOG_WARNING,
1460 "parse error: No category context for line %d of %s\n", lineno, configfile);
1464 is_escaped = cur[0] == '\\';
1466 /* First character is escaped. */
1469 ast_log(LOG_ERROR, "Invalid escape in line %d of %s\n", lineno, configfile);
1473 c = strchr(cur + is_escaped, '=');
1475 if (c && c > cur + is_escaped && (*(c - 1) == '+')) {
1476 struct ast_variable *var, *replace = NULL;
1477 struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1479 if (!str || !*str) {
1485 cur = ast_strip(cur);
1487 /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1488 for (var = ast_category_first(*cat); var; var = var->next) {
1489 if (!strcmp(var->name, cur)) {
1495 /* Nothing to replace; just set a variable normally. */
1496 goto set_new_variable;
1499 ast_str_set(str, 0, "%s", replace->value);
1500 ast_str_append(str, 0, "%s", c);
1501 ast_str_trim_blanks(*str);
1502 ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1506 /* Ignore > in => */
1511 cur = ast_strip(cur);
1513 if (ast_strlen_zero(cur)) {
1514 ast_log(LOG_WARNING, "No variable name in line %d of %s\n", lineno, configfile);
1515 } else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1520 /* Put and reset comments */
1522 ast_variable_append(*cat, v);
1524 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1525 v->precomments = ALLOC_COMMENT(comment_buffer);
1526 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1527 v->sameline = ALLOC_COMMENT(lline_buffer);
1528 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1529 CB_RESET(comment_buffer, lline_buffer);
1535 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1541 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)
1544 #if defined(LOW_MEMORY)
1549 char *new_buf, *comment_p, *process_buf;
1552 int comment = 0, nest[MAX_NESTED_COMMENTS];
1553 struct ast_category *cat = NULL;
1555 struct stat statbuf;
1556 struct cache_file_mtime *cfmtime = NULL;
1557 struct cache_file_include *cfinclude;
1558 struct ast_variable *last_var = 0;
1559 struct ast_category *last_cat = 0;
1560 /*! Growable string buffer */
1561 struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
1562 struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */
1565 cat = ast_config_get_current_category(cfg);
1567 if (filename[0] == '/') {
1568 ast_copy_string(fn, filename, sizeof(fn));
1570 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1573 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1574 comment_buffer = ast_str_create(CB_SIZE);
1576 lline_buffer = ast_str_create(CB_SIZE);
1577 if (!lline_buffer) {
1578 ast_free(comment_buffer);
1579 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1583 #ifdef AST_INCLUDE_GLOB
1587 globbuf.gl_offs = 0; /* initialize it to silence gcc */
1588 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1589 if (glob_ret == GLOB_NOSPACE)
1590 ast_log(LOG_WARNING,
1591 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1592 else if (glob_ret == GLOB_ABORTED)
1593 ast_log(LOG_WARNING,
1594 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1596 /* loop over expanded files */
1598 for (i=0; i<globbuf.gl_pathc; i++) {
1599 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1602 * The following is not a loop, but just a convenient way to define a block
1603 * (using do { } while(0) ), and be able to exit from it with 'continue'
1604 * or 'break' in case of errors. Nice trick.
1607 if (stat(fn, &statbuf))
1610 if (!S_ISREG(statbuf.st_mode)) {
1611 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1615 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1616 /* Find our cached entry for this configuration file */
1617 AST_LIST_LOCK(&cfmtime_head);
1618 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1619 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
1623 cfmtime = cfmtime_new(fn, who_asked);
1626 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1627 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1631 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1632 /* File is unchanged, what about the (cached) includes (if any)? */
1634 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1635 /* We must glob here, because if we did not, then adding a file to globbed directory would
1636 * incorrectly cause no reload to be necessary. */
1638 #ifdef AST_INCLUDE_GLOB
1640 glob_t glob_buf = { .gl_offs = 0 };
1641 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
1642 /* On error, we reparse */
1643 if (glob_return == GLOB_NOSPACE || glob_return == GLOB_ABORTED)
1646 /* loop over expanded files */
1648 for (j = 0; j < glob_buf.gl_pathc; j++) {
1649 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
1651 ast_copy_string(fn2, cfinclude->include);
1653 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
1654 /* that second-to-last field needs to be looked at in this case... TODO */
1656 /* One change is enough to short-circuit and reload the whole shebang */
1659 #ifdef AST_INCLUDE_GLOB
1666 AST_LIST_UNLOCK(&cfmtime_head);
1667 ast_free(comment_buffer);
1668 ast_free(lline_buffer);
1669 #ifdef AST_INCLUDE_GLOB
1672 return CONFIG_STATUS_FILEUNCHANGED;
1675 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1676 AST_LIST_UNLOCK(&cfmtime_head);
1678 /* If cfg is NULL, then we just want an answer */
1680 ast_free(comment_buffer);
1681 ast_free(lline_buffer);
1682 #ifdef AST_INCLUDE_GLOB
1689 cfmtime->mtime = statbuf.st_mtime;
1691 if (!(f = fopen(fn, "r"))) {
1692 ast_debug(1, "No file to parse: %s\n", fn);
1693 ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
1697 /* If we get to this point, then we're loading regardless */
1698 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1699 ast_debug(1, "Parsing %s\n", fn);
1700 ast_verb(2, "Parsing '%s': Found\n", fn);
1703 if (fgets(buf, sizeof(buf), f)) {
1704 /* Skip lines that are too long */
1705 if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 1] != '\n') {
1706 ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
1707 while (fgets(buf, sizeof(buf), f)) {
1708 if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 1] == '\n') {
1715 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
1716 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1717 ast_str_reset(lline_buffer); /* erase the lline buffer */
1726 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"))) {
1727 /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1728 CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
1729 continue; /* go get a new line, then */
1732 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1733 if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
1734 /* Escaped semicolons aren't comments. */
1735 new_buf = comment_p;
1736 /* write over the \ and bring the null terminator with us */
1737 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
1738 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1739 /* Meta-Comment start detected ";--" */
1740 if (comment < MAX_NESTED_COMMENTS) {
1742 new_buf = comment_p + 3;
1744 nest[comment-1] = lineno;
1746 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1748 } else if ((comment_p >= new_buf + 2) &&
1749 (*(comment_p - 1) == COMMENT_TAG) &&
1750 (*(comment_p - 2) == COMMENT_TAG)) {
1751 /* Meta-Comment end detected */
1753 new_buf = comment_p + 1;
1755 /* Back to non-comment now */
1757 /* Actually have to move what's left over the top, then continue */
1759 oldptr = process_buf + strlen(process_buf);
1760 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1761 CB_ADD(&comment_buffer, ";");
1762 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1765 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1768 process_buf = new_buf;
1772 /* If ; is found, and we are not nested in a comment,
1773 we immediately stop all comment processing */
1774 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1775 CB_ADD(&lline_buffer, comment_p);
1778 new_buf = comment_p;
1780 new_buf = comment_p + 1;
1783 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1784 CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
1788 char *buffer = ast_strip(process_buf);
1789 if (!ast_strlen_zero(buffer)) {
1790 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
1791 cfg = CONFIG_STATUS_FILEINVALID;
1798 /* end of file-- anything in a comment buffer? */
1800 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1801 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1802 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1803 ast_str_reset(lline_buffer); /* erase the lline buffer */
1805 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1807 } else if (last_var) {
1808 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1809 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1810 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1811 ast_str_reset(lline_buffer); /* erase the lline buffer */
1813 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1816 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1817 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
1820 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1821 CB_RESET(comment_buffer, lline_buffer);
1826 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1828 #ifdef AST_INCLUDE_GLOB
1829 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1838 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1839 ast_free(comment_buffer);
1840 ast_free(lline_buffer);
1841 comment_buffer = NULL;
1842 lline_buffer = NULL;
1852 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1853 which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1854 recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1855 be shocked and mystified as to why things are not showing up in the files!
1857 Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1858 and line number are stored for each include, plus the name of the file included, so that these statements may be
1859 included in the output files on a file_save operation.
1861 The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1862 are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1863 the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1864 and a header gets added.
1866 vars and category heads are output in the order they are stored in the config file. So, if the software
1867 shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1868 file/lineno data probably won't get changed.
1872 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1878 ast_copy_string(date, ctime(&t), sizeof(date));
1880 fprintf(f1, ";!\n");
1881 fprintf(f1, ";! Automatically generated configuration file\n");
1882 if (strcmp(configfile, fn))
1883 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1885 fprintf(f1, ";! Filename: %s\n", configfile);
1886 fprintf(f1, ";! Generator: %s\n", generator);
1887 fprintf(f1, ";! Creation Date: %s", date);
1888 fprintf(f1, ";!\n");
1891 static void inclfile_destroy(void *obj)
1893 const struct inclfile *o = obj;
1899 static struct inclfile *set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
1901 struct inclfile lookup;
1902 struct inclfile *fi;
1904 if (ast_strlen_zero(file)) {
1905 if (configfile[0] == '/')
1906 ast_copy_string(fn, configfile, fn_size);
1908 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1909 } else if (file[0] == '/')
1910 ast_copy_string(fn, file, fn_size);
1912 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1914 fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1916 /* Found existing include file scratch pad. */
1920 /* set up a file scratch pad */
1921 fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1923 /* Scratch pad creation failed. */
1926 fi->fname = ast_strdup(fn);
1928 /* Scratch pad creation failed. */
1934 ao2_link(fileset, fi);
1939 static int count_linefeeds(char *str)
1951 static int count_linefeeds_in_comments(struct ast_comment *x)
1956 count += count_linefeeds(x->cmt);
1962 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1964 int precomment_lines;
1968 /* No file scratch pad object so insert no blank lines. */
1972 precomment_lines = count_linefeeds_in_comments(precomments);
1974 /* I don't have to worry about those ;! comments, they are
1975 stored in the precomments, but not printed back out.
1976 I did have to make sure that comments following
1977 the ;! header comments were not also deleted in the process */
1978 if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
1980 } else if (lineno == 0) {
1981 /* Line replacements also mess things up */
1983 } else if (lineno - precomment_lines - fi->lineno < 5) {
1984 /* Only insert less than 5 blank lines; if anything more occurs,
1985 * it's probably due to context deletion. */
1986 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
1990 /* Deletion occurred - insert a single blank line, for separation of
1995 fi->lineno = lineno + 1; /* Advance the file lineno */
1998 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2000 return ast_config_text_file_save(configfile, cfg, generator);
2003 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2007 struct ast_variable *var;
2008 struct ast_category *cat;
2009 struct ast_comment *cmt;
2010 struct ast_config_include *incl;
2012 struct ao2_container *fileset;
2013 struct inclfile *fi;
2015 fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
2017 /* Container creation failed. */
2021 /* reset all the output flags, in case this isn't our first time saving this data */
2022 for (incl = cfg->includes; incl; incl = incl->next) {
2026 /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
2027 are all truncated to zero bytes and have that nice header*/
2028 for (incl = cfg->includes; incl; incl = incl->next) {
2029 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*/
2030 /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
2031 fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
2034 gen_header(f, configfile, fn, generator);
2035 fclose(f); /* this should zero out the file */
2037 ast_debug(1, "Unable to open for writing: %s\n", fn);
2038 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2046 /* just set fn to absolute ver of configfile */
2047 fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
2050 (f = fopen(fn, "w+"))
2052 (f = fopen(fn, "w"))
2055 ast_verb(2, "Saving '%s'\n", fn);
2056 gen_header(f, configfile, fn, generator);
2063 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
2064 /* since each var, cat, and associated comments can come from any file, we have to be
2065 mobile, and open each file, print, and close it on an entry-by-entry basis */
2068 fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
2071 ast_debug(1, "Unable to open for writing: %s\n", fn);
2072 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2076 ao2_ref(fileset, -1);
2080 /* dump any includes that happen before this category header */
2081 for (incl=cfg->includes; incl; incl = incl->next) {
2082 if (strcmp(incl->include_location_file, cat->file) == 0){
2083 if (cat->lineno > incl->include_location_lineno && !incl->output) {
2085 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2087 fprintf(f,"#include \"%s\"\n", incl->included_file);
2093 insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
2094 /* Dump section with any appropriate comment */
2095 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
2096 char *cmtp = cmt->cmt;
2097 while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
2098 char *cmtp2 = strchr(cmtp+1, '\n');
2104 fprintf(f,"%s", cmtp);
2106 fprintf(f, "[%s]", cat->name);
2107 if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
2112 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
2115 if (!AST_LIST_EMPTY(&cat->template_instances)) {
2116 struct ast_category_template_instance *x;
2117 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2118 fprintf(f,"%s",x->name);
2119 if (x != AST_LIST_LAST(&cat->template_instances))
2125 for(cmt = cat->sameline; cmt; cmt=cmt->next)
2127 fprintf(f,"%s", cmt->cmt);
2131 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
2132 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2133 fprintf(f,"%s", cmt->cmt);
2142 struct ast_category_template_instance *x;
2144 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2145 struct ast_variable *v;
2146 for (v = x->inst->root; v; v = v->next) {
2147 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2159 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
2162 ast_debug(1, "Unable to open for writing: %s\n", fn);
2163 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2167 ao2_ref(fileset, -1);
2171 /* dump any includes that happen before this category header */
2172 for (incl=cfg->includes; incl; incl = incl->next) {
2173 if (strcmp(incl->include_location_file, var->file) == 0){
2174 if (var->lineno > incl->include_location_lineno && !incl->output) {
2176 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2178 fprintf(f,"#include \"%s\"\n", incl->included_file);
2184 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
2185 for (cmt = var->precomments; cmt; cmt=cmt->next) {
2186 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2187 fprintf(f,"%s", cmt->cmt);
2190 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
2192 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
2193 for (cmt = var->trailing; cmt; cmt=cmt->next) {
2194 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2195 fprintf(f,"%s", cmt->cmt);
2197 if (var->blanklines) {
2198 blanklines = var->blanklines;
2199 while (blanklines--)
2212 if (!option_debug) {
2213 ast_verb(2, "Saving '%s': saved\n", fn);
2216 ast_debug(1, "Unable to open for writing: %s\n", fn);
2217 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
2221 ao2_ref(fileset, -1);
2225 /* Now, for files with trailing #include/#exec statements,
2226 we have to make sure every entry is output */
2227 for (incl=cfg->includes; incl; incl = incl->next) {
2228 if (!incl->output) {
2229 /* open the respective file */
2230 fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
2233 ast_debug(1, "Unable to open for writing: %s\n", fn);
2234 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2238 ao2_ref(fileset, -1);
2242 /* output the respective include */
2244 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2246 fprintf(f,"#include \"%s\"\n", incl->included_file);
2254 ao2_ref(fileset, -1); /* this should destroy the hash container */
2259 static void clear_config_maps(void)
2261 struct ast_config_map *map;
2263 SCOPED_MUTEX(lock, &config_lock);
2265 while (config_maps) {
2267 config_maps = config_maps->next;
2272 #ifdef TEST_FRAMEWORK
2273 int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2275 static int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2278 struct ast_config_map *map;
2282 length = sizeof(*map);
2283 length += strlen(name) + 1;
2284 length += strlen(driver) + 1;
2285 length += strlen(database) + 1;
2287 length += strlen(table) + 1;
2289 if (!(map = ast_calloc(1, length)))
2292 dst = map->stuff; /* writable space starts here */
2293 map->name = strcpy(dst, name);
2294 dst += strlen(dst) + 1;
2295 map->driver = strcpy(dst, driver);
2296 dst += strlen(dst) + 1;
2297 map->database = strcpy(dst, database);
2299 dst += strlen(dst) + 1;
2300 map->table = strcpy(dst, table);
2302 map->priority = priority;
2303 map->next = config_maps;
2306 ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
2311 int read_config_maps(void)
2313 struct ast_config *config, *configtmp;
2314 struct ast_variable *v;
2315 char *driver, *table, *database, *textpri, *stringp, *tmp;
2316 struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
2319 clear_config_maps();
2321 configtmp = ast_config_new();
2323 ast_log(LOG_ERROR, "Unable to allocate memory for new config\n");
2326 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
2327 if (config == CONFIG_STATUS_FILEINVALID) {
2329 } else if (!config) {
2330 ast_config_destroy(configtmp);
2334 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
2336 ast_copy_string(buf, v->value, sizeof(buf));
2338 driver = strsep(&stringp, ",");
2340 if ((tmp = strchr(stringp, '\"')))
2343 /* check if the database text starts with a double quote */
2344 if (*stringp == '"') {
2346 database = strsep(&stringp, "\"");
2347 strsep(&stringp, ",");
2349 /* apparently this text has no quotes */
2350 database = strsep(&stringp, ",");
2353 table = strsep(&stringp, ",");
2354 textpri = strsep(&stringp, ",");
2355 if (!textpri || !(pri = atoi(textpri))) {
2359 if (!strcmp(v->name, extconfig_conf)) {
2360 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
2364 if (!strcmp(v->name, "asterisk.conf")) {
2365 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
2369 if (!strcmp(v->name, "logger.conf")) {
2370 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
2374 if (!driver || !database)
2376 if (!strcasecmp(v->name, "sipfriends")) {
2377 ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
2378 ast_realtime_append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
2379 } else if (!strcasecmp(v->name, "iaxfriends")) {
2380 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");
2381 ast_realtime_append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
2382 ast_realtime_append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
2384 ast_realtime_append_mapping(v->name, driver, database, table, pri);
2387 ast_config_destroy(config);
2391 int ast_config_engine_register(struct ast_config_engine *new)
2393 struct ast_config_engine *ptr;
2395 SCOPED_MUTEX(lock, &config_lock);
2397 if (!config_engine_list) {
2398 config_engine_list = new;
2400 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
2404 ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
2409 int ast_config_engine_deregister(struct ast_config_engine *del)
2411 struct ast_config_engine *ptr, *last=NULL;
2413 SCOPED_MUTEX(lock, &config_lock);
2415 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
2418 last->next = ptr->next;
2420 config_engine_list = ptr->next;
2429 int ast_realtime_is_mapping_defined(const char *family)
2431 struct ast_config_map *map;
2432 SCOPED_MUTEX(lock, &config_lock);
2434 for (map = config_maps; map; map = map->next) {
2435 if (!strcasecmp(family, map->name)) {
2443 /*! \brief Find realtime engine for realtime family */
2444 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
2446 struct ast_config_engine *eng, *ret = NULL;
2447 struct ast_config_map *map;
2449 SCOPED_MUTEX(lock, &config_lock);
2451 for (map = config_maps; map; map = map->next) {
2452 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
2454 ast_copy_string(database, map->database, dbsiz);
2456 ast_copy_string(table, map->table ? map->table : family, tabsiz);
2461 /* Check if the required driver (engine) exist */
2463 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
2464 if (!strcasecmp(eng->name, map->driver))
2469 /* if we found a mapping, but the engine is not available, then issue a warning */
2471 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
2476 static struct ast_config_engine text_file_engine = {
2478 .load_func = config_text_file_load,
2481 struct ast_config *ast_config_copy(const struct ast_config *old)
2483 struct ast_config *new_config = ast_config_new();
2484 struct ast_category *cat_iter;
2490 for (cat_iter = old->root; cat_iter; cat_iter = cat_iter->next) {
2491 struct ast_category *new_cat =
2492 ast_category_new(cat_iter->name, cat_iter->file, cat_iter->lineno);
2496 ast_category_append(new_config, new_cat);
2497 if (cat_iter->root) {
2498 new_cat->root = ast_variables_dup(cat_iter->root);
2499 if (!new_cat->root) {
2502 new_cat->last = cat_iter->last;
2509 ast_config_destroy(new_config);
2514 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)
2518 struct ast_config_engine *loader = &text_file_engine;
2519 struct ast_config *result;
2521 /* The config file itself bumps include_level by 1 */
2522 if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2523 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2527 cfg->include_level++;
2529 if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
2530 struct ast_config_engine *eng;
2532 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
2535 if (eng && eng->load_func) {
2538 eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
2539 if (eng && eng->load_func)
2544 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2546 if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED) {
2547 result->include_level--;
2548 config_hook_exec(filename, who_asked, result);
2549 } else if (result != CONFIG_STATUS_FILEINVALID) {
2550 cfg->include_level--;
2556 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
2558 struct ast_config *cfg;
2559 struct ast_config *result;
2561 cfg = ast_config_new();
2565 result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
2566 if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
2567 ast_config_destroy(cfg);
2572 static struct ast_variable *realtime_arguments_to_fields(va_list ap)
2574 struct ast_variable *first, *fields = NULL;
2575 const char *newparam = va_arg(ap, const char *), *newval = va_arg(ap, const char *);
2577 if (!(first = ast_variable_new(newparam, newval, ""))) {
2581 while ((newparam = va_arg(ap, const char *))) {
2582 struct ast_variable *field;
2584 newval = va_arg(ap, const char *);
2585 if (!(field = ast_variable_new(newparam, newval, ""))) {
2586 ast_variables_destroy(fields);
2587 ast_variables_destroy(first);
2591 field->next = fields;
2595 first->next = fields;
2601 struct ast_variable *ast_load_realtime_all_fields(const char *family, const struct ast_variable *fields)
2603 struct ast_config_engine *eng;
2606 struct ast_variable *res=NULL;
2609 for (i = 1; ; i++) {
2610 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2611 if (eng->realtime_func && (res = eng->realtime_func(db, table, fields))) {
2622 struct ast_variable *ast_load_realtime_all(const char *family, ...)
2624 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
2625 struct ast_variable *res = NULL;
2628 va_start(ap, family);
2629 fields = realtime_arguments_to_fields(ap);
2633 res = ast_load_realtime_all_fields(family, fields);
2639 struct ast_variable *ast_load_realtime_fields(const char *family, const struct ast_variable *fields)
2641 struct ast_variable *res;
2642 struct ast_variable *cur;
2643 struct ast_variable **prev;
2645 res = ast_load_realtime_all_fields(family, fields);
2647 /* Filter the list. */
2651 if (ast_strlen_zero(cur->value)) {
2652 /* Eliminate empty entries */
2653 struct ast_variable *next;
2657 ast_variable_destroy(cur);
2660 /* Make blank entries empty and keep them. */
2661 if (cur->value[0] == ' ' && cur->value[1] == '\0') {
2662 char *vptr = (char *) cur->value;
2674 struct ast_variable *ast_load_realtime(const char *family, ...)
2676 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
2679 va_start(ap, family);
2680 fields = realtime_arguments_to_fields(ap);
2683 return ast_load_realtime_fields(family, fields);
2686 /*! \brief Check if realtime engine is configured for family */
2687 int ast_check_realtime(const char *family)
2689 struct ast_config_engine *eng;
2690 if (!ast_realtime_enabled()) {
2691 return 0; /* There are no engines at all so fail early */
2694 eng = find_engine(family, 1, NULL, 0, NULL, 0);
2700 /*! \brief Check if there's any realtime engines loaded */
2701 int ast_realtime_enabled(void)
2703 return config_maps ? 1 : 0;
2706 int ast_realtime_require_field(const char *family, ...)
2708 struct ast_config_engine *eng;
2714 va_start(ap, family);
2715 for (i = 1; ; i++) {
2716 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2717 /* If the require succeeds, it returns 0. */
2718 if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
2730 int ast_unload_realtime(const char *family)
2732 struct ast_config_engine *eng;
2737 for (i = 1; ; i++) {
2738 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2739 if (eng->unload_func) {
2740 /* Do this for ALL engines */
2741 res = eng->unload_func(db, table);
2750 struct ast_config *ast_load_realtime_multientry_fields(const char *family, const struct ast_variable *fields)
2752 struct ast_config_engine *eng;
2755 struct ast_config *res = NULL;
2758 for (i = 1; ; i++) {
2759 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2760 if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, fields))) {
2761 /* If we were returned an empty cfg, destroy it and return NULL */
2763 ast_config_destroy(res);
2776 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2778 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
2781 va_start(ap, family);
2782 fields = realtime_arguments_to_fields(ap);
2785 return ast_load_realtime_multientry_fields(family, fields);
2788 int ast_update_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
2790 struct ast_config_engine *eng;
2795 for (i = 1; ; i++) {
2796 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2797 /* If the update succeeds, it returns 0. */
2798 if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, fields))) {
2809 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2811 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
2814 va_start(ap, lookup);
2815 fields = realtime_arguments_to_fields(ap);
2818 return ast_update_realtime_fields(family, keyfield, lookup, fields);
2821 int ast_update2_realtime_fields(const char *family, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
2823 struct ast_config_engine *eng;
2828 for (i = 1; ; i++) {
2829 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2830 if (eng->update2_func && !(res = eng->update2_func(db, table, lookup_fields, update_fields))) {
2841 int ast_update2_realtime(const char *family, ...)
2843 RAII_VAR(struct ast_variable *, lookup_fields, NULL, ast_variables_destroy);
2844 RAII_VAR(struct ast_variable *, update_fields, NULL, ast_variables_destroy);
2847 va_start(ap, family);
2848 lookup_fields = realtime_arguments_to_fields(ap);
2849 update_fields = realtime_arguments_to_fields(ap);
2852 return ast_update2_realtime_fields(family, lookup_fields, update_fields);
2855 int ast_store_realtime_fields(const char *family, const struct ast_variable *fields)
2857 struct ast_config_engine *eng;
2862 for (i = 1; ; i++) {
2863 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2864 /* If the store succeeds, it returns 0. */
2865 if (eng->store_func && !(res = eng->store_func(db, table, fields))) {
2876 int ast_store_realtime(const char *family, ...)
2878 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
2881 va_start(ap, family);
2882 fields = realtime_arguments_to_fields(ap);
2885 return ast_store_realtime_fields(family, fields);
2888 int ast_destroy_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
2890 struct ast_config_engine *eng;
2895 for (i = 1; ; i++) {
2896 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2897 if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, fields))) {
2908 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2910 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
2913 va_start(ap, lookup);
2914 fields = realtime_arguments_to_fields(ap);
2917 return ast_destroy_realtime_fields(family, keyfield, lookup, fields);
2920 char *ast_realtime_decode_chunk(char *chunk)
2923 for (; *chunk; chunk++) {
2924 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
2925 sscanf(chunk + 1, "%02hhX", chunk);
2926 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
2932 char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
2934 if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
2935 ast_str_set(dest, maxlen, "%s", chunk);
2937 ast_str_reset(*dest);
2938 for (; *chunk; chunk++) {
2939 if (strchr(";^", *chunk)) {
2940 ast_str_append(dest, maxlen, "^%02hhX", *chunk);
2942 ast_str_append(dest, maxlen, "%c", *chunk);
2946 return ast_str_buffer(*dest);
2949 /*! \brief Helper function to parse arguments
2950 * See documentation in config.h
2952 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2953 void *p_result, ...)
2958 va_start(ap, p_result);
2959 switch (flags & PARSE_TYPE) {
2963 int32_t *result = p_result;
2964 int32_t def = result ? *result : 0, high = INT32_MAX, low = INT32_MIN;
2965 char *endptr = NULL;
2967 /* optional arguments: default value and/or (low, high) */
2968 if (flags & PARSE_DEFAULT) {
2969 def = va_arg(ap, int32_t);
2971 if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
2972 low = va_arg(ap, int32_t);
2973 high = va_arg(ap, int32_t);
2975 if (ast_strlen_zero(arg)) {
2980 x = strtol(arg, &endptr, 0);
2981 if (*endptr || errno || x < INT32_MIN || x > INT32_MAX) {
2982 /* Parse error, or type out of int32_t bounds */
2986 error = (x < low) || (x > high);
2987 if (flags & PARSE_RANGE_DEFAULTS) {
2990 } else if (x > high) {
2994 if (flags & PARSE_OUT_RANGE) {
2999 *result = error ? def : x;
3002 ast_debug(3, "extract int from [%s] in [%d, %d] gives [%ld](%d)\n",
3003 arg, low, high, result ? *result : x, error);
3009 unsigned long int x = 0;
3010 uint32_t *result = p_result;
3011 uint32_t def = result ? *result : 0, low = 0, high = UINT32_MAX;
3012 char *endptr = NULL;
3014 /* optional argument: first default value, then range */
3015 if (flags & PARSE_DEFAULT) {
3016 def = va_arg(ap, uint32_t);
3018 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
3019 /* range requested, update bounds */
3020 low = va_arg(ap, uint32_t);
3021 high = va_arg(ap, uint32_t);
3024 if (ast_strlen_zero(arg)) {
3028 /* strtoul will happilly and silently negate negative numbers */
3029 arg = ast_skip_blanks(arg);
3035 x = strtoul(arg, &endptr, 0);
3036 if (*endptr || errno || x > UINT32_MAX) {
3040 error = (x < low) || (x > high);
3041 if (flags & PARSE_RANGE_DEFAULTS) {
3044 } else if (x > high) {
3048 if (flags & PARSE_OUT_RANGE) {
3053 *result = error ? def : x;
3055 ast_debug(3, "extract uint from [%s] in [%u, %u] gives [%lu](%d)\n",
3056 arg, low, high, result ? *result : x, error);
3062 double *result = p_result;
3063 double x = 0, def = result ? *result : 0, low = -HUGE_VAL, high = HUGE_VAL;
3064 char *endptr = NULL;
3066 /* optional argument: first default value, then range */
3067 if (flags & PARSE_DEFAULT) {