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;
92 /*! stat() file size */
93 unsigned long stat_size;
94 /*! stat() file modtime nanoseconds */
95 unsigned long stat_mtime_nsec;
96 /*! stat() file modtime seconds since epoc */
99 /*! String stuffed in filename[] after the filename string. */
100 const char *who_asked;
101 /*! Filename and who_asked stuffed after it. */
105 /*! Cached file mtime list. */
106 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
108 static int init_appendbuf(void *data)
110 struct ast_str **str = data;
111 *str = ast_str_create(16);
112 return *str ? 0 : -1;
115 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
117 /* comment buffers are better implemented using the ast_str_*() API */
118 #define CB_SIZE 250 /* initial size of comment buffers */
120 static void CB_ADD(struct ast_str **cb, const char *str)
122 ast_str_append(cb, 0, "%s", str);
125 static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
127 char *s = ast_alloca(len + 1);
128 ast_copy_string(s, str, len);
129 ast_str_append(cb, 0, "%s", str);
132 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
142 static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
144 struct ast_comment *x = NULL;
145 if (!buffer || !ast_str_strlen(buffer)) {
148 if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
149 strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
154 /* I need to keep track of each config file, and all its inclusions,
155 so that we can track blank lines in each */
162 static int hash_string(const void *obj, const int flags)
164 char *str = ((struct inclfile *) obj)->fname;
167 for (total = 0; *str; str++) {
168 unsigned int tmp = total;
169 total <<= 1; /* multiply by 2 */
170 total += tmp; /* multiply by 3 */
171 total <<= 2; /* multiply by 12 */
172 total += tmp; /* multiply by 13 */
174 total += ((unsigned int) (*str));
182 static int hashtab_compare_strings(void *a, void *b, int flags)
184 const struct inclfile *ae = a, *be = b;
185 return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
188 static struct ast_config_map {
189 struct ast_config_map *next;
191 /*! Stored in stuff[] at struct end. */
193 /*! Stored in stuff[] at struct end. */
195 /*! Stored in stuff[] at struct end. */
196 const char *database;
197 /*! Stored in stuff[] at struct end. */
199 /*! Contents of name, driver, database, and table in that order stuffed here. */
201 } *config_maps = NULL;
203 AST_MUTEX_DEFINE_STATIC(config_lock);
204 static struct ast_config_engine *config_engine_list;
206 #define MAX_INCLUDE_LEVEL 10
208 struct ast_category_template_instance {
209 char name[80]; /* redundant? */
210 const struct ast_category *inst;
211 AST_LIST_ENTRY(ast_category_template_instance) next;
214 struct ast_category {
216 int ignored; /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
219 * \brief The file name from whence this declaration was read
220 * \note Will never be NULL
224 AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
225 struct ast_comment *precomments;
226 struct ast_comment *sameline;
227 struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
228 /*! First category variable in the list. */
229 struct ast_variable *root;
230 /*! Last category variable in the list. */
231 struct ast_variable *last;
232 /*! Next node in the list. */
233 struct ast_category *next;
237 /*! First config category in the list. */
238 struct ast_category *root;
239 /*! Last config category in the list. */
240 struct ast_category *last;
241 struct ast_category *current;
242 struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
244 int max_include_level;
245 struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
248 struct ast_config_include {
250 * \brief file name in which the include occurs
251 * \note Will never be NULL
253 char *include_location_file;
254 int include_location_lineno; /*!< lineno where include occurred */
255 int exec; /*!< set to non-zero if its a #exec statement */
257 * \brief if it's an exec, you'll have both the /var/tmp to read, and the original script
258 * \note Will never be NULL if exec is non-zero
262 * \brief file name included
263 * \note Will never be NULL
266 int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
267 we explode the instances and will include those-- so all entries will be unique */
268 int output; /*!< a flag to indicate if the inclusion has been output */
269 struct ast_config_include *next; /*!< ptr to next inclusion in the list */
272 static void ast_variable_destroy(struct ast_variable *doomed);
273 static void ast_includes_destroy(struct ast_config_include *incls);
276 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
278 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
281 struct ast_variable *variable;
282 int name_len = strlen(name) + 1;
283 int val_len = strlen(value) + 1;
284 int fn_len = strlen(filename) + 1;
286 /* Ensure a minimum length in case the filename is changed later. */
287 if (fn_len < MIN_VARIABLE_FNAME_SPACE) {
288 fn_len = MIN_VARIABLE_FNAME_SPACE;
293 (variable = __ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable), file, lineno, func))
295 (variable = ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable)))
298 char *dst = variable->stuff; /* writable space starts here */
300 /* Put file first so ast_include_rename() can calculate space available. */
301 variable->file = strcpy(dst, filename);
303 variable->name = strcpy(dst, name);
305 variable->value = strcpy(dst, value);
312 * \brief Move the contents from the source to the destination variable.
314 * \param dst_var Destination variable node
315 * \param src_var Source variable node
319 static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable *src_var)
321 dst_var->lineno = src_var->lineno;
322 dst_var->object = src_var->object;
323 dst_var->blanklines = src_var->blanklines;
324 dst_var->precomments = src_var->precomments;
325 src_var->precomments = NULL;
326 dst_var->sameline = src_var->sameline;
327 src_var->sameline = NULL;
328 dst_var->trailing = src_var->trailing;
329 src_var->trailing = NULL;
332 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)
334 /* a file should be included ONCE. Otherwise, if one of the instances is changed,
335 * then all be changed. -- how do we know to include it? -- Handling modified
336 * instances is possible, I'd have
337 * to create a new master for each instance. */
338 struct ast_config_include *inc;
341 inc = ast_include_find(conf, included_file);
344 inc->inclusion_count++;
345 snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
346 } while (stat(real_included_file_name, &statbuf) == 0);
347 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);
349 *real_included_file_name = 0;
351 inc = ast_calloc(1,sizeof(struct ast_config_include));
355 inc->include_location_file = ast_strdup(from_file);
356 inc->include_location_lineno = from_lineno;
357 if (!ast_strlen_zero(real_included_file_name))
358 inc->included_file = ast_strdup(real_included_file_name);
360 inc->included_file = ast_strdup(included_file);
364 inc->exec_file = ast_strdup(exec_file);
366 if (!inc->include_location_file
367 || !inc->included_file
368 || (is_exec && !inc->exec_file)) {
369 ast_includes_destroy(inc);
373 /* attach this new struct to the conf struct */
374 inc->next = conf->includes;
375 conf->includes = inc;
380 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
382 struct ast_config_include *incl;
383 struct ast_category *cat;
386 int from_len = strlen(from_file);
387 int to_len = strlen(to_file);
389 if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
392 /* the manager code allows you to read in one config file, then
393 * write it back out under a different name. But, the new arrangement
394 * ties output lines to the file name. So, before you try to write
395 * the config file to disk, better riffle thru the data and make sure
396 * the file names are changed.
398 /* file names are on categories, includes (of course), and on variables. So,
399 * traverse all this and swap names */
401 for (incl = conf->includes; incl; incl=incl->next) {
402 if (strcmp(incl->include_location_file,from_file) == 0) {
403 if (from_len >= to_len)
404 strcpy(incl->include_location_file, to_file);
406 /* Keep the old filename if the allocation fails. */
407 str = ast_strdup(to_file);
409 ast_free(incl->include_location_file);
410 incl->include_location_file = str;
415 for (cat = conf->root; cat; cat = cat->next) {
416 struct ast_variable **prev;
417 struct ast_variable *v;
418 struct ast_variable *new_var;
420 if (strcmp(cat->file,from_file) == 0) {
421 if (from_len >= to_len)
422 strcpy(cat->file, to_file);
424 /* Keep the old filename if the allocation fails. */
425 str = ast_strdup(to_file);
432 for (prev = &cat->root, v = cat->root; v; prev = &v->next, v = v->next) {
433 if (strcmp(v->file, from_file)) {
438 * Calculate actual space available. The file string is
439 * intentionally stuffed before the name string just so we can
442 if (to_len < v->name - v->file) {
443 /* The new name will fit in the available space. */
444 str = (char *) v->file;/* Stupid compiler complains about discarding qualifiers even though I used a cast. */
445 strcpy(str, to_file);/* SAFE */
449 /* Keep the old filename if the allocation fails. */
450 new_var = ast_variable_new(v->name, v->value, to_file);
455 /* Move items from the old list node to the replacement node. */
456 ast_variable_move(new_var, v);
458 /* Replace the old node in the list with the new node. */
459 new_var->next = v->next;
460 if (cat->last == v) {
465 ast_variable_destroy(v);
472 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
474 struct ast_config_include *x;
475 for (x=conf->includes;x;x=x->next) {
476 if (strcmp(x->included_file,included_file) == 0)
483 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
488 category->last->next = variable;
490 category->root = variable;
491 category->last = variable;
492 while (category->last->next)
493 category->last = category->last->next;
496 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
498 struct ast_variable *cur = category->root;
502 if (!variable || sscanf(line, "%30d", &insertline) != 1) {
506 variable->next = category->root;
507 category->root = variable;
509 for (lineno = 1; lineno < insertline; lineno++) {
515 variable->next = cur->next;
516 cur->next = variable;
520 static void ast_comment_destroy(struct ast_comment **comment)
522 struct ast_comment *n, *p;
524 for (p = *comment; p; p = n) {
532 static void ast_variable_destroy(struct ast_variable *doomed)
534 ast_comment_destroy(&doomed->precomments);
535 ast_comment_destroy(&doomed->sameline);
536 ast_comment_destroy(&doomed->trailing);
540 struct ast_variable *ast_variables_dup(struct ast_variable *var)
542 struct ast_variable *cloned;
543 struct ast_variable *tmp;
545 if (!(cloned = ast_variable_new(var->name, var->value, var->file))) {
551 while ((var = var->next)) {
552 if (!(tmp->next = ast_variable_new(var->name, var->value, var->file))) {
553 ast_variables_destroy(cloned);
562 void ast_variables_destroy(struct ast_variable *v)
564 struct ast_variable *vn;
569 ast_variable_destroy(vn);
573 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
575 struct ast_category *cat = NULL;
581 if (config->last_browse && (config->last_browse->name == category)) {
582 cat = config->last_browse;
584 cat = ast_category_get(config, category);
587 return (cat) ? cat->root : NULL;
590 inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
597 struct ast_variable *ast_variable_list_sort(struct ast_variable *start)
599 struct ast_variable *p, *q;
600 struct ast_variable top;
602 memset(&top, 0, sizeof(top));
604 if (start != NULL && start->next != NULL) {
609 while (p->next != NULL) {
610 if (p->next != NULL && strcmp(p->name, p->next->name) > 0) {
611 q->next = variable_list_switch(p, p->next);
623 struct ast_variable *ast_variable_list_append_hint(struct ast_variable **head, struct ast_variable *search_hint, struct ast_variable *newvar)
625 struct ast_variable *curr;
626 ast_assert(head != NULL);
631 if (search_hint == NULL) {
634 for (curr = search_hint; curr->next; curr = curr->next);
638 for (curr = newvar; curr->next; curr = curr->next);
643 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
646 tmp = ast_variable_retrieve(cfg, cat, var);
648 tmp = ast_variable_retrieve(cfg, "general", var);
654 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
656 struct ast_variable *v;
659 for (v = ast_variable_browse(config, category); v; v = v->next) {
660 if (!strcasecmp(variable, v->name)) {
665 struct ast_category *cat;
667 for (cat = config->root; cat; cat = cat->next) {
668 for (v = cat->root; v; v = v->next) {
669 if (!strcasecmp(variable, v->name)) {
679 static struct ast_variable *variable_clone(const struct ast_variable *old)
681 struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
684 new->lineno = old->lineno;
685 new->object = old->object;
686 new->blanklines = old->blanklines;
687 /* TODO: clone comments? */
693 static void move_variables(struct ast_category *old, struct ast_category *new)
695 struct ast_variable *var = old->root;
698 /* we can just move the entire list in a single op */
699 ast_variable_append(new, var);
702 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
704 struct ast_category *category;
706 category = ast_calloc(1, sizeof(*category));
710 category->file = ast_strdup(in_file);
711 if (!category->file) {
712 ast_category_destroy(category);
715 ast_copy_string(category->name, name, sizeof(category->name));
716 category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
720 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
722 struct ast_category *cat;
724 /* try exact match first, then case-insensitive match */
725 for (cat = config->root; cat; cat = cat->next) {
726 if (cat->name == category_name && (ignored || !cat->ignored))
730 for (cat = config->root; cat; cat = cat->next) {
731 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
738 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
740 return category_get(config, category_name, 0);
743 int ast_category_exist(const struct ast_config *config, const char *category_name)
745 return !!ast_category_get(config, category_name);
748 void ast_category_append(struct ast_config *config, struct ast_category *category)
751 config->last->next = category;
753 config->root = category;
754 category->include_level = config->include_level;
755 config->last = category;
756 config->current = category;
759 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
761 struct ast_category *cur_category;
765 if (!strcasecmp(config->root->name, match)) {
766 cat->next = config->root;
770 for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
771 if (!strcasecmp(cur_category->next->name, match)) {
772 cat->next = cur_category->next;
773 cur_category->next = cat;
779 static void ast_destroy_template_list(struct ast_category *cat)
781 struct ast_category_template_instance *x;
783 while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
787 void ast_category_destroy(struct ast_category *cat)
789 ast_variables_destroy(cat->root);
792 ast_comment_destroy(&cat->precomments);
793 ast_comment_destroy(&cat->sameline);
794 ast_comment_destroy(&cat->trailing);
795 ast_destroy_template_list(cat);
800 static void ast_includes_destroy(struct ast_config_include *incls)
802 struct ast_config_include *incl,*inclnext;
804 for (incl=incls; incl; incl = inclnext) {
805 inclnext = incl->next;
806 ast_free(incl->include_location_file);
807 ast_free(incl->exec_file);
808 ast_free(incl->included_file);
813 static struct ast_category *next_available_category(struct ast_category *cat)
815 for (; cat && cat->ignored; cat = cat->next);
820 /*! return the first var of a category */
821 struct ast_variable *ast_category_first(struct ast_category *cat)
823 return (cat) ? cat->root : NULL;
826 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
828 struct ast_category *category = ast_category_get(config, cat);
831 return category->root;
835 void ast_config_sort_categories(struct ast_config *config, int descending,
836 int (*comparator)(struct ast_category *p, struct ast_category *q))
839 * The contents of this function are adapted from
840 * an example of linked list merge sorting
841 * copyright 2001 Simon Tatham.
843 * Permission is hereby granted, free of charge, to any person
844 * obtaining a copy of this software and associated documentation
845 * files (the "Software"), to deal in the Software without
846 * restriction, including without limitation the rights to use,
847 * copy, modify, merge, publish, distribute, sublicense, and/or
848 * sell copies of the Software, and to permit persons to whom the
849 * Software is furnished to do so, subject to the following
852 * The above copyright notice and this permission notice shall be
853 * included in all copies or substantial portions of the Software.
855 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
856 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
857 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
858 * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
859 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
860 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
861 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
866 struct ast_category *p, *q, *e, *tail;
867 int nmerges, psize, qsize, i;
869 /* If the descending flag was sent, we'll apply inversion to the comparison function's return. */
885 nmerges = 0; /* count number of merges we do in this pass */
888 nmerges++; /* there exists a merge to be done */
890 /* step `insize' places along from p */
893 for (i = 0; i < insize; i++) {
901 /* if q hasn't fallen off end, we have two lists to merge */
904 /* now we have two lists; merge them */
905 while (psize > 0 || (qsize > 0 && q)) {
906 /* decide whether next element of merge comes from p or q */
908 /* p is empty; e must come from q. */
912 } else if (qsize == 0 || !q) {
913 /* q is empty; e must come from p. */
914 e = p; p = p->next; psize--;
915 } else if ((comparator(p,q) * descending) <= 0) {
916 /* First element of p is lower (or same) e must come from p. */
921 /* First element of q is lower; e must come from q. */
927 /* add the next element to the merged list */
936 /* now p has stepped `insize' places along, and q has too */
942 /* If we have done only one merge, we're finished. */
943 if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
947 /* Otherwise repeat, merging lists twice the size */
953 char *ast_category_browse(struct ast_config *config, const char *prev)
955 struct ast_category *cat;
958 /* First time browse. */
960 } else if (config->last_browse && (config->last_browse->name == prev)) {
961 /* Simple last browse found. */
962 cat = config->last_browse->next;
965 * Config changed since last browse.
967 * First try cheap last browse search. (Rebrowsing a different
968 * previous category?)
970 for (cat = config->root; cat; cat = cat->next) {
971 if (cat->name == prev) {
979 * Have to do it the hard way. (Last category was deleted and
982 for (cat = config->root; cat; cat = cat->next) {
983 if (!strcasecmp(cat->name, prev)) {
993 cat = next_available_category(cat);
995 config->last_browse = cat;
996 return (cat) ? cat->name : NULL;
999 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
1001 struct ast_variable *v;
1010 void ast_category_rename(struct ast_category *cat, const char *name)
1012 ast_copy_string(cat->name, name, sizeof(cat->name));
1015 static void inherit_category(struct ast_category *new, const struct ast_category *base)
1017 struct ast_variable *var;
1018 struct ast_category_template_instance *x;
1020 x = ast_calloc(1, sizeof(*x));
1024 strcpy(x->name, base->name);
1026 AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
1027 for (var = base->root; var; var = var->next)
1028 ast_variable_append(new, variable_clone(var));
1031 struct ast_config *ast_config_new(void)
1033 struct ast_config *config;
1035 if ((config = ast_calloc(1, sizeof(*config))))
1036 config->max_include_level = MAX_INCLUDE_LEVEL;
1040 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
1042 struct ast_variable *cur, *prev=NULL, *curn;
1048 if (!ast_strlen_zero(line)) {
1049 /* Requesting to delete by item number. */
1050 if (sscanf(line, "%30d", &req_item) != 1
1052 /* Invalid item number to delete. */
1058 cur = category->root;
1061 /* Delete by item number or by variable name with optional value. */
1062 if ((0 <= req_item && num_item == req_item)
1063 || (req_item < 0 && !strcasecmp(cur->name, variable)
1064 && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
1066 prev->next = cur->next;
1067 if (cur == category->last)
1068 category->last = prev;
1070 category->root = cur->next;
1071 if (cur == category->last)
1072 category->last = NULL;
1074 ast_variable_destroy(cur);
1085 int ast_variable_update(struct ast_category *category, const char *variable,
1086 const char *value, const char *match, unsigned int object)
1088 struct ast_variable *cur, *prev=NULL, *newer=NULL;
1090 for (cur = category->root; cur; prev = cur, cur = cur->next) {
1091 if (strcasecmp(cur->name, variable) ||
1092 (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
1095 if (!(newer = ast_variable_new(variable, value, cur->file)))
1098 ast_variable_move(newer, cur);
1099 newer->object = newer->object || object;
1101 /* Replace the old node in the list with the new node. */
1102 newer->next = cur->next;
1106 category->root = newer;
1107 if (category->last == cur)
1108 category->last = newer;
1110 ast_variable_destroy(cur);
1115 /* Could not find variable to update */
1119 int ast_category_delete(struct ast_config *cfg, const char *category)
1121 struct ast_category *prev=NULL, *cat;
1125 if (cat->name == category) {
1127 prev->next = cat->next;
1128 if (cat == cfg->last)
1131 cfg->root = cat->next;
1132 if (cat == cfg->last)
1135 ast_category_destroy(cat);
1145 if (!strcasecmp(cat->name, category)) {
1147 prev->next = cat->next;
1148 if (cat == cfg->last)
1151 cfg->root = cat->next;
1152 if (cat == cfg->last)
1155 ast_category_destroy(cat);
1164 int ast_category_empty(struct ast_config *cfg, const char *category)
1166 struct ast_category *cat;
1168 for (cat = cfg->root; cat; cat = cat->next) {
1169 if (!strcasecmp(cat->name, category))
1171 ast_variables_destroy(cat->root);
1180 void ast_config_destroy(struct ast_config *cfg)
1182 struct ast_category *cat, *catn;
1187 ast_includes_destroy(cfg->includes);
1193 ast_category_destroy(catn);
1198 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
1200 return cfg->current;
1203 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
1205 /* cast below is just to silence compiler warning about dropping "const" */
1206 cfg->current = (struct ast_category *) cat;
1211 * \brief Create a new cfmtime list node.
1213 * \param filename Config filename caching.
1214 * \param who_asked Who wanted to know.
1216 * \retval cfmtime New node on success.
1217 * \retval NULL on error.
1219 static struct cache_file_mtime *cfmtime_new(const char *filename, const char *who_asked)
1221 struct cache_file_mtime *cfmtime;
1224 cfmtime = ast_calloc(1,
1225 sizeof(*cfmtime) + strlen(filename) + 1 + strlen(who_asked) + 1);
1229 dst = cfmtime->filename; /* writable space starts here */
1230 strcpy(dst, filename);
1231 dst += strlen(dst) + 1;
1232 cfmtime->who_asked = strcpy(dst, who_asked);
1237 enum config_cache_attribute_enum {
1238 ATTRIBUTE_INCLUDE = 0,
1244 * \brief Clear the stat() data in the cached file modtime struct.
1246 * \param cfmtime Cached file modtime.
1250 static void cfmstat_clear(struct cache_file_mtime *cfmtime)
1252 cfmtime->stat_size = 0;
1253 cfmtime->stat_mtime_nsec = 0;
1254 cfmtime->stat_mtime = 0;
1259 * \brief Save the stat() data to the cached file modtime struct.
1261 * \param cfmtime Cached file modtime.
1262 * \param statbuf Buffer filled in by stat().
1266 static void cfmstat_save(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1268 cfmtime->stat_size = statbuf->st_size;
1269 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
1270 cfmtime->stat_mtime_nsec = statbuf->st_mtim.tv_nsec;
1271 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
1272 cfmtime->stat_mtime_nsec = statbuf->st_mtimensec;
1273 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1274 cfmtime->stat_mtime_nsec = statbuf->st_mtimespec.tv_nsec;
1276 cfmtime->stat_mtime_nsec = 0;
1278 cfmtime->stat_mtime = statbuf->st_mtime;
1283 * \brief Compare the stat() data with the cached file modtime struct.
1285 * \param cfmtime Cached file modtime.
1286 * \param statbuf Buffer filled in by stat().
1288 * \retval non-zero if different.
1290 static int cfmstat_cmp(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1292 struct cache_file_mtime cfm_buf;
1294 cfmstat_save(&cfm_buf, statbuf);
1296 return cfmtime->stat_size != cfm_buf.stat_size
1297 || cfmtime->stat_mtime != cfm_buf.stat_mtime
1298 || cfmtime->stat_mtime_nsec != cfm_buf.stat_mtime_nsec;
1301 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
1303 struct cache_file_mtime *cfmtime;
1304 struct cache_file_include *cfinclude;
1305 struct stat statbuf = { 0, };
1307 /* Find our cached entry for this configuration file */
1308 AST_LIST_LOCK(&cfmtime_head);
1309 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1310 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
1314 cfmtime = cfmtime_new(configfile, who_asked);
1316 AST_LIST_UNLOCK(&cfmtime_head);
1319 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1320 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1323 if (stat(configfile, &statbuf)) {
1324 cfmstat_clear(cfmtime);
1326 cfmstat_save(cfmtime, &statbuf);
1330 case ATTRIBUTE_INCLUDE:
1331 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1332 if (!strcmp(cfinclude->include, filename)) {
1333 AST_LIST_UNLOCK(&cfmtime_head);
1337 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
1339 AST_LIST_UNLOCK(&cfmtime_head);
1342 strcpy(cfinclude->include, filename);
1343 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
1345 case ATTRIBUTE_EXEC:
1346 cfmtime->has_exec = 1;
1349 AST_LIST_UNLOCK(&cfmtime_head);
1352 /*! \brief parse one line in the configuration.
1354 * We can have a category header [foo](...)
1355 * a directive #include / #exec
1356 * or a regular line name = value
1359 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
1360 char *buf, int lineno, const char *configfile, struct ast_flags flags,
1361 struct ast_str *comment_buffer,
1362 struct ast_str *lline_buffer,
1363 const char *suggested_include_file,
1364 struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
1368 struct ast_variable *v;
1369 char cmd[512], exec_file[512];
1371 /* Actually parse the entry */
1372 if (cur[0] == '[') { /* A category header */
1373 /* format is one of the following:
1374 * [foo] define a new category named 'foo'
1375 * [foo](!) define a new template category named 'foo'
1376 * [foo](+) append to category 'foo', error if foo does not exist.
1377 * [foo](a) define a new category and inherit from category or template a.
1378 * You can put a comma-separated list of categories and templates
1379 * and '!' and '+' between parentheses, with obvious meaning.
1381 struct ast_category *newcat = NULL;
1384 c = strchr(cur, ']');
1386 ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
1394 if (!(*cat = newcat = ast_category_new(catname,
1395 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
1399 (*cat)->lineno = lineno;
1404 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1405 newcat->precomments = ALLOC_COMMENT(comment_buffer);
1406 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1407 newcat->sameline = ALLOC_COMMENT(lline_buffer);
1408 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1409 CB_RESET(comment_buffer, lline_buffer);
1411 /* If there are options or categories to inherit from, process them now */
1413 if (!(cur = strchr(c, ')'))) {
1414 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
1418 while ((cur = strsep(&c, ","))) {
1419 if (!strcasecmp(cur, "!")) {
1420 (*cat)->ignored = 1;
1421 } else if (!strcasecmp(cur, "+")) {
1422 *cat = category_get(cfg, catname, 1);
1425 ast_category_destroy(newcat);
1426 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
1430 move_variables(newcat, *cat);
1431 ast_category_destroy(newcat);
1435 struct ast_category *base;
1437 base = category_get(cfg, cur, 1);
1439 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1442 inherit_category(*cat, base);
1447 ast_category_append(cfg, *cat);
1448 } else if (cur[0] == '#') { /* A directive - #include or #exec */
1450 char real_inclusion_name[256];
1451 int do_include = 0; /* otherwise, it is exec */
1452 int try_include = 0;
1456 while (*c && (*c > 32)) {
1462 /* Find real argument */
1463 c = ast_strip(c + 1);
1470 if (!strcasecmp(cur, "include")) {
1472 } else if (!strcasecmp(cur, "tryinclude")) {
1475 } else if (!strcasecmp(cur, "exec")) {
1476 if (!ast_opt_exec_includes) {
1477 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1478 return 0; /* XXX is this correct ? or we should return -1 ? */
1481 ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1482 return 0; /* XXX is this correct ? or we should return -1 ? */
1486 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
1487 do_include ? "include / tryinclude" : "exec",
1488 do_include ? "filename" : "/path/to/executable",
1491 return 0; /* XXX is this correct ? or we should return -1 ? */
1495 /* Strip off leading and trailing "'s and <>'s */
1497 if ((*c == '"') || (*c == '<')) {
1498 char quote_char = *c;
1499 if (quote_char == '<') {
1503 if (*(c + strlen(c) - 1) == quote_char) {
1505 *(c + strlen(c) - 1) = '\0';
1510 /* #exec </path/to/executable>
1511 We create a tmp file, then we #include it, then we delete it. */
1513 struct timeval now = ast_tvnow();
1514 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1515 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1516 snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1517 snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1518 ast_safe_system(cmd);
1521 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1522 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1523 exec_file[0] = '\0';
1526 /* record this inclusion */
1527 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1529 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1530 if (!ast_strlen_zero(exec_file))
1532 if (!do_include && !try_include) {
1533 ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1536 /* XXX otherwise what ? the default return is 0 anyways */
1539 /* Just a line (variable = value) */
1544 ast_log(LOG_WARNING,
1545 "parse error: No category context for line %d of %s\n", lineno, configfile);
1549 is_escaped = cur[0] == '\\';
1551 /* First character is escaped. */
1554 ast_log(LOG_ERROR, "Invalid escape in line %d of %s\n", lineno, configfile);
1558 c = strchr(cur + is_escaped, '=');
1560 if (c && c > cur + is_escaped && (*(c - 1) == '+')) {
1561 struct ast_variable *var, *replace = NULL;
1562 struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1564 if (!str || !*str) {
1570 cur = ast_strip(cur);
1572 /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1573 for (var = ast_category_first(*cat); var; var = var->next) {
1574 if (!strcmp(var->name, cur)) {
1580 /* Nothing to replace; just set a variable normally. */
1581 goto set_new_variable;
1584 ast_str_set(str, 0, "%s", replace->value);
1585 ast_str_append(str, 0, "%s", c);
1586 ast_str_trim_blanks(*str);
1587 ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1591 /* Ignore > in => */
1596 cur = ast_strip(cur);
1598 if (ast_strlen_zero(cur)) {
1599 ast_log(LOG_WARNING, "No variable name in line %d of %s\n", lineno, configfile);
1600 } else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1605 /* Put and reset comments */
1607 ast_variable_append(*cat, v);
1609 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1610 v->precomments = ALLOC_COMMENT(comment_buffer);
1611 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1612 v->sameline = ALLOC_COMMENT(lline_buffer);
1613 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1614 CB_RESET(comment_buffer, lline_buffer);
1620 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1626 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)
1629 #if defined(LOW_MEMORY)
1634 char *new_buf, *comment_p, *process_buf;
1637 int comment = 0, nest[MAX_NESTED_COMMENTS];
1638 struct ast_category *cat = NULL;
1640 struct stat statbuf;
1641 struct cache_file_mtime *cfmtime = NULL;
1642 struct cache_file_include *cfinclude;
1643 struct ast_variable *last_var = 0;
1644 struct ast_category *last_cat = 0;
1645 /*! Growable string buffer */
1646 struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
1647 struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */
1650 cat = ast_config_get_current_category(cfg);
1652 if (filename[0] == '/') {
1653 ast_copy_string(fn, filename, sizeof(fn));
1655 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1658 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1659 comment_buffer = ast_str_create(CB_SIZE);
1661 lline_buffer = ast_str_create(CB_SIZE);
1662 if (!lline_buffer) {
1663 ast_free(comment_buffer);
1664 ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1668 #ifdef AST_INCLUDE_GLOB
1672 globbuf.gl_offs = 0; /* initialize it to silence gcc */
1673 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1674 if (glob_ret == GLOB_NOSPACE)
1675 ast_log(LOG_WARNING,
1676 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1677 else if (glob_ret == GLOB_ABORTED)
1678 ast_log(LOG_WARNING,
1679 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1681 /* loop over expanded files */
1683 for (i=0; i<globbuf.gl_pathc; i++) {
1684 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1687 * The following is not a loop, but just a convenient way to define a block
1688 * (using do { } while(0) ), and be able to exit from it with 'continue'
1689 * or 'break' in case of errors. Nice trick.
1692 if (stat(fn, &statbuf))
1695 if (!S_ISREG(statbuf.st_mode)) {
1696 ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1700 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1701 /* Find our cached entry for this configuration file */
1702 AST_LIST_LOCK(&cfmtime_head);
1703 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1704 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
1708 cfmtime = cfmtime_new(fn, who_asked);
1710 AST_LIST_UNLOCK(&cfmtime_head);
1713 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1714 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1719 && !cfmtime->has_exec
1720 && !cfmstat_cmp(cfmtime, &statbuf)
1721 && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1722 /* File is unchanged, what about the (cached) includes (if any)? */
1724 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1725 /* We must glob here, because if we did not, then adding a file to globbed directory would
1726 * incorrectly cause no reload to be necessary. */
1728 #ifdef AST_INCLUDE_GLOB
1730 glob_t glob_buf = { .gl_offs = 0 };
1731 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
1732 /* On error, we reparse */
1733 if (glob_return == GLOB_NOSPACE || glob_return == GLOB_ABORTED)
1736 /* loop over expanded files */
1738 for (j = 0; j < glob_buf.gl_pathc; j++) {
1739 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
1741 ast_copy_string(fn2, cfinclude->include);
1743 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
1744 /* that second-to-last field needs to be looked at in this case... TODO */
1746 /* One change is enough to short-circuit and reload the whole shebang */
1749 #ifdef AST_INCLUDE_GLOB
1756 AST_LIST_UNLOCK(&cfmtime_head);
1757 ast_free(comment_buffer);
1758 ast_free(lline_buffer);
1759 #ifdef AST_INCLUDE_GLOB
1762 return CONFIG_STATUS_FILEUNCHANGED;
1765 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1766 AST_LIST_UNLOCK(&cfmtime_head);
1768 /* If cfg is NULL, then we just want an answer */
1770 ast_free(comment_buffer);
1771 ast_free(lline_buffer);
1772 #ifdef AST_INCLUDE_GLOB
1779 cfmstat_save(cfmtime, &statbuf);
1782 if (!(f = fopen(fn, "r"))) {
1783 ast_debug(1, "No file to parse: %s\n", fn);
1784 ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
1788 /* If we get to this point, then we're loading regardless */
1789 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1790 ast_debug(1, "Parsing %s\n", fn);
1791 ast_verb(2, "Parsing '%s': Found\n", fn);
1794 if (fgets(buf, sizeof(buf), f)) {
1795 /* Skip lines that are too long */
1796 if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 1] != '\n') {
1797 ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
1798 while (fgets(buf, sizeof(buf), f)) {
1799 if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 1] == '\n') {
1806 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
1807 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1808 ast_str_reset(lline_buffer); /* erase the lline buffer */
1817 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"))) {
1818 /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1819 CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
1820 continue; /* go get a new line, then */
1823 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1824 if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
1825 /* Escaped semicolons aren't comments. */
1826 new_buf = comment_p;
1827 /* write over the \ and bring the null terminator with us */
1828 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
1829 } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1830 /* Meta-Comment start detected ";--" */
1831 if (comment < MAX_NESTED_COMMENTS) {
1833 new_buf = comment_p + 3;
1835 nest[comment-1] = lineno;
1837 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1839 } else if ((comment_p >= new_buf + 2) &&
1840 (*(comment_p - 1) == COMMENT_TAG) &&
1841 (*(comment_p - 2) == COMMENT_TAG)) {
1842 /* Meta-Comment end detected */
1844 new_buf = comment_p + 1;
1846 /* Back to non-comment now */
1848 /* Actually have to move what's left over the top, then continue */
1850 oldptr = process_buf + strlen(process_buf);
1851 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1852 CB_ADD(&comment_buffer, ";");
1853 CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1856 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1859 process_buf = new_buf;
1863 /* If ; is found, and we are not nested in a comment,
1864 we immediately stop all comment processing */
1865 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1866 CB_ADD(&lline_buffer, comment_p);
1869 new_buf = comment_p;
1871 new_buf = comment_p + 1;
1874 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1875 CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
1879 char *buffer = ast_strip(process_buf);
1880 if (!ast_strlen_zero(buffer)) {
1881 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
1882 cfg = CONFIG_STATUS_FILEINVALID;
1889 /* end of file-- anything in a comment buffer? */
1891 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1892 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1893 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1894 ast_str_reset(lline_buffer); /* erase the lline buffer */
1896 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1898 } else if (last_var) {
1899 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1900 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1901 CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
1902 ast_str_reset(lline_buffer); /* erase the lline buffer */
1904 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1907 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1908 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
1911 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1912 CB_RESET(comment_buffer, lline_buffer);
1917 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1919 #ifdef AST_INCLUDE_GLOB
1920 if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1929 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1930 ast_free(comment_buffer);
1931 ast_free(lline_buffer);
1932 comment_buffer = NULL;
1933 lline_buffer = NULL;
1943 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1944 which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1945 recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1946 be shocked and mystified as to why things are not showing up in the files!
1948 Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1949 and line number are stored for each include, plus the name of the file included, so that these statements may be
1950 included in the output files on a file_save operation.
1952 The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1953 are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1954 the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1955 and a header gets added.
1957 vars and category heads are output in the order they are stored in the config file. So, if the software
1958 shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1959 file/lineno data probably won't get changed.
1963 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1969 ast_copy_string(date, ctime(&t), sizeof(date));
1971 fprintf(f1, ";!\n");
1972 fprintf(f1, ";! Automatically generated configuration file\n");
1973 if (strcmp(configfile, fn))
1974 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1976 fprintf(f1, ";! Filename: %s\n", configfile);
1977 fprintf(f1, ";! Generator: %s\n", generator);
1978 fprintf(f1, ";! Creation Date: %s", date);
1979 fprintf(f1, ";!\n");
1982 static void inclfile_destroy(void *obj)
1984 const struct inclfile *o = obj;
1990 static struct inclfile *set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
1992 struct inclfile lookup;
1993 struct inclfile *fi;
1995 if (ast_strlen_zero(file)) {
1996 if (configfile[0] == '/')
1997 ast_copy_string(fn, configfile, fn_size);
1999 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
2000 } else if (file[0] == '/')
2001 ast_copy_string(fn, file, fn_size);
2003 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
2005 fi = ao2_find(fileset, &lookup, OBJ_POINTER);
2007 /* Found existing include file scratch pad. */
2011 /* set up a file scratch pad */
2012 fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
2014 /* Scratch pad creation failed. */
2017 fi->fname = ast_strdup(fn);
2019 /* Scratch pad creation failed. */
2025 ao2_link(fileset, fi);
2030 static int count_linefeeds(char *str)
2042 static int count_linefeeds_in_comments(struct ast_comment *x)
2047 count += count_linefeeds(x->cmt);
2053 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
2055 int precomment_lines;
2059 /* No file scratch pad object so insert no blank lines. */
2063 precomment_lines = count_linefeeds_in_comments(precomments);
2065 /* I don't have to worry about those ;! comments, they are
2066 stored in the precomments, but not printed back out.
2067 I did have to make sure that comments following
2068 the ;! header comments were not also deleted in the process */
2069 if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
2071 } else if (lineno == 0) {
2072 /* Line replacements also mess things up */
2074 } else if (lineno - precomment_lines - fi->lineno < 5) {
2075 /* Only insert less than 5 blank lines; if anything more occurs,
2076 * it's probably due to context deletion. */
2077 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
2081 /* Deletion occurred - insert a single blank line, for separation of
2086 fi->lineno = lineno + 1; /* Advance the file lineno */
2089 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2091 return ast_config_text_file_save(configfile, cfg, generator);
2094 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2098 struct ast_variable *var;
2099 struct ast_category *cat;
2100 struct ast_comment *cmt;
2101 struct ast_config_include *incl;
2103 struct ao2_container *fileset;
2104 struct inclfile *fi;
2106 fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
2108 /* Container creation failed. */
2112 /* reset all the output flags, in case this isn't our first time saving this data */
2113 for (incl = cfg->includes; incl; incl = incl->next) {
2117 /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
2118 are all truncated to zero bytes and have that nice header*/
2119 for (incl = cfg->includes; incl; incl = incl->next) {
2120 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*/
2121 /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
2122 fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
2125 gen_header(f, configfile, fn, generator);
2126 fclose(f); /* this should zero out the file */
2128 ast_debug(1, "Unable to open for writing: %s\n", fn);
2129 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2137 /* just set fn to absolute ver of configfile */
2138 fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
2141 (f = fopen(fn, "w+"))
2143 (f = fopen(fn, "w"))
2146 ast_verb(2, "Saving '%s'\n", fn);
2147 gen_header(f, configfile, fn, generator);
2154 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
2155 /* since each var, cat, and associated comments can come from any file, we have to be
2156 mobile, and open each file, print, and close it on an entry-by-entry basis */
2159 fi = set_fn(fn, sizeof(fn), cat->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, cat->file) == 0){
2174 if (cat->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, cat->precomments, cat->lineno);
2185 /* Dump section with any appropriate comment */
2186 for (cmt = cat->precomments; cmt; cmt=cmt->next) {
2187 char *cmtp = cmt->cmt;
2188 while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
2189 char *cmtp2 = strchr(cmtp+1, '\n');
2195 fprintf(f,"%s", cmtp);
2197 fprintf(f, "[%s]", cat->name);
2198 if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
2203 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
2206 if (!AST_LIST_EMPTY(&cat->template_instances)) {
2207 struct ast_category_template_instance *x;
2208 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2209 fprintf(f,"%s",x->name);
2210 if (x != AST_LIST_LAST(&cat->template_instances))
2216 for(cmt = cat->sameline; cmt; cmt=cmt->next)
2218 fprintf(f,"%s", cmt->cmt);
2222 for (cmt = cat->trailing; cmt; cmt=cmt->next) {
2223 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2224 fprintf(f,"%s", cmt->cmt);
2233 struct ast_category_template_instance *x;
2235 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2236 struct ast_variable *v;
2237 for (v = x->inst->root; v; v = v->next) {
2238 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2250 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
2253 ast_debug(1, "Unable to open for writing: %s\n", fn);
2254 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2258 ao2_ref(fileset, -1);
2262 /* dump any includes that happen before this category header */
2263 for (incl=cfg->includes; incl; incl = incl->next) {
2264 if (strcmp(incl->include_location_file, var->file) == 0){
2265 if (var->lineno > incl->include_location_lineno && !incl->output) {
2267 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2269 fprintf(f,"#include \"%s\"\n", incl->included_file);
2275 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
2276 for (cmt = var->precomments; cmt; cmt=cmt->next) {
2277 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2278 fprintf(f,"%s", cmt->cmt);
2281 fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
2283 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
2284 for (cmt = var->trailing; cmt; cmt=cmt->next) {
2285 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2286 fprintf(f,"%s", cmt->cmt);
2288 if (var->blanklines) {
2289 blanklines = var->blanklines;
2290 while (blanklines--)
2303 if (!option_debug) {
2304 ast_verb(2, "Saving '%s': saved\n", fn);
2307 ast_debug(1, "Unable to open for writing: %s\n", fn);
2308 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
2312 ao2_ref(fileset, -1);
2316 /* Now, for files with trailing #include/#exec statements,
2317 we have to make sure every entry is output */
2318 for (incl=cfg->includes; incl; incl = incl->next) {
2319 if (!incl->output) {
2320 /* open the respective file */
2321 fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
2324 ast_debug(1, "Unable to open for writing: %s\n", fn);
2325 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2329 ao2_ref(fileset, -1);
2333 /* output the respective include */
2335 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2337 fprintf(f,"#include \"%s\"\n", incl->included_file);
2345 ao2_ref(fileset, -1); /* this should destroy the hash container */
2350 static void clear_config_maps(void)
2352 struct ast_config_map *map;
2354 SCOPED_MUTEX(lock, &config_lock);
2356 while (config_maps) {
2358 config_maps = config_maps->next;
2363 #ifdef TEST_FRAMEWORK
2364 int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2366 static int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2369 struct ast_config_map *map;
2373 length = sizeof(*map);
2374 length += strlen(name) + 1;
2375 length += strlen(driver) + 1;
2376 length += strlen(database) + 1;
2378 length += strlen(table) + 1;
2380 if (!(map = ast_calloc(1, length)))
2383 dst = map->stuff; /* writable space starts here */
2384 map->name = strcpy(dst, name);
2385 dst += strlen(dst) + 1;
2386 map->driver = strcpy(dst, driver);
2387 dst += strlen(dst) + 1;
2388 map->database = strcpy(dst, database);
2390 dst += strlen(dst) + 1;
2391 map->table = strcpy(dst, table);
2393 map->priority = priority;
2394 map->next = config_maps;
2397 ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
2402 int read_config_maps(void)
2404 struct ast_config *config, *configtmp;
2405 struct ast_variable *v;
2406 char *driver, *table, *database, *textpri, *stringp, *tmp;
2407 struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
2410 clear_config_maps();
2412 configtmp = ast_config_new();
2414 ast_log(LOG_ERROR, "Unable to allocate memory for new config\n");
2417 config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
2418 if (config == CONFIG_STATUS_FILEINVALID) {
2420 } else if (!config) {
2421 ast_config_destroy(configtmp);
2425 for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
2427 ast_copy_string(buf, v->value, sizeof(buf));
2429 driver = strsep(&stringp, ",");
2431 if ((tmp = strchr(stringp, '\"')))
2434 /* check if the database text starts with a double quote */
2435 if (*stringp == '"') {
2437 database = strsep(&stringp, "\"");
2438 strsep(&stringp, ",");
2440 /* apparently this text has no quotes */
2441 database = strsep(&stringp, ",");
2444 table = strsep(&stringp, ",");
2445 textpri = strsep(&stringp, ",");
2446 if (!textpri || !(pri = atoi(textpri))) {
2450 if (!strcmp(v->name, extconfig_conf)) {
2451 ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
2455 if (!strcmp(v->name, "asterisk.conf")) {
2456 ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
2460 if (!strcmp(v->name, "logger.conf")) {
2461 ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
2465 if (!driver || !database)
2467 if (!strcasecmp(v->name, "sipfriends")) {
2468 ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
2469 ast_realtime_append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
2470 } else if (!strcasecmp(v->name, "iaxfriends")) {
2471 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");
2472 ast_realtime_append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
2473 ast_realtime_append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
2475 ast_realtime_append_mapping(v->name, driver, database, table, pri);
2478 ast_config_destroy(config);
2482 int ast_config_engine_register(struct ast_config_engine *new)
2484 struct ast_config_engine *ptr;
2486 SCOPED_MUTEX(lock, &config_lock);
2488 if (!config_engine_list) {
2489 config_engine_list = new;
2491 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
2498 int ast_config_engine_deregister(struct ast_config_engine *del)
2500 struct ast_config_engine *ptr, *last=NULL;
2502 SCOPED_MUTEX(lock, &config_lock);
2504 for (ptr = config_engine_list; ptr; ptr=ptr->next) {
2507 last->next = ptr->next;
2509 config_engine_list = ptr->next;
2518 int ast_realtime_is_mapping_defined(const char *family)
2520 struct ast_config_map *map;
2521 SCOPED_MUTEX(lock, &config_lock);
2523 for (map = config_maps; map; map = map->next) {
2524 if (!strcasecmp(family, map->name)) {
2532 /*! \brief Find realtime engine for realtime family */
2533 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
2535 struct ast_config_engine *eng, *ret = NULL;
2536 struct ast_config_map *map;
2538 SCOPED_MUTEX(lock, &config_lock);
2540 for (map = config_maps; map; map = map->next) {
2541 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
2543 ast_copy_string(database, map->database, dbsiz);
2545 ast_copy_string(table, map->table ? map->table : family, tabsiz);
2550 /* Check if the required driver (engine) exist */
2552 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
2553 if (!strcasecmp(eng->name, map->driver))
2558 /* if we found a mapping, but the engine is not available, then issue a warning */
2560 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
2565 static struct ast_config_engine text_file_engine = {
2567 .load_func = config_text_file_load,
2570 struct ast_config *ast_config_copy(const struct ast_config *old)
2572 struct ast_config *new_config = ast_config_new();
2573 struct ast_category *cat_iter;
2579 for (cat_iter = old->root; cat_iter; cat_iter = cat_iter->next) {
2580 struct ast_category *new_cat =
2581 ast_category_new(cat_iter->name, cat_iter->file, cat_iter->lineno);
2585 ast_category_append(new_config, new_cat);
2586 if (cat_iter->root) {
2587 new_cat->root = ast_variables_dup(cat_iter->root);
2588 if (!new_cat->root) {
2591 new_cat->last = cat_iter->last;
2598 ast_config_destroy(new_config);
2603 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)
2607 struct ast_config_engine *loader = &text_file_engine;
2608 struct ast_config *result;
2610 /* The config file itself bumps include_level by 1 */
2611 if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2612 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2616 cfg->include_level++;
2618 if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
2619 struct ast_config_engine *eng;
2621 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
2624 if (eng && eng->load_func) {
2627 eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
2628 if (eng && eng->load_func)
2633 result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2635 if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED) {
2636 result->include_level--;
2637 config_hook_exec(filename, who_asked, result);
2638 } else if (result != CONFIG_STATUS_FILEINVALID) {
2639 cfg->include_level--;
2645 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
2647 struct ast_config *cfg;
2648 struct ast_config *result;
2650 cfg = ast_config_new();
2654 result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
2655 if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
2656 ast_config_destroy(cfg);
2661 #define realtime_arguments_to_fields(ap) realtime_arguments_to_fields2(ap, 0)
2663 static struct ast_variable *realtime_arguments_to_fields2(va_list ap, int skip)
2665 struct ast_variable *first, *fields = NULL;
2666 const char *newparam;
2670 * Previously we would do:
2672 * va_start(ap, last);
2673 * x = realtime_arguments_to_fields(ap);
2674 * y = realtime_arguments_to_fields(ap);
2677 * While this works on generic amd64 machines (2014), it doesn't on the
2678 * raspberry PI. The va_arg() manpage says:
2680 * If ap is passed to a function that uses va_arg(ap,type) then
2681 * the value of ap is undefined after the return of that function.
2683 * On the raspberry, ap seems to get reset after the call: the contents
2684 * of y would be equal to the contents of x.
2686 * So, instead we allow the caller to skip past earlier argument sets
2687 * using the skip parameter:
2689 * va_start(ap, last);
2690 * x = realtime_arguments_to_fields(ap);
2692 * va_start(ap, last);
2693 * y = realtime_arguments_to_fields2(ap, 1);
2697 /* There must be at least one argument. */
2698 newparam = va_arg(ap, const char *);
2699 newval = va_arg(ap, const char *);
2700 while ((newparam = va_arg(ap, const char *))) {
2701 newval = va_arg(ap, const char *);
2705 /* Load up the first vars. */
2706 newparam = va_arg(ap, const char *);
2707 newval = va_arg(ap, const char *);
2709 if (!(first = ast_variable_new(newparam, newval, ""))) {
2713 while ((newparam = va_arg(ap, const char *))) {
2714 struct ast_variable *field;
2716 newval = va_arg(ap, const char *);
2717 if (!(field = ast_variable_new(newparam, newval, ""))) {
2718 ast_variables_destroy(fields);
2719 ast_variables_destroy(first);
2723 field->next = fields;
2727 first->next = fields;
2733 struct ast_variable *ast_load_realtime_all_fields(const char *family, const struct ast_variable *fields)
2735 struct ast_config_engine *eng;
2738 struct ast_variable *res=NULL;
2741 for (i = 1; ; i++) {
2742 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2743 if (eng->realtime_func && (res = eng->realtime_func(db, table, fields))) {
2754 struct ast_variable *ast_load_realtime_all(const char *family, ...)
2756 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
2757 struct ast_variable *res = NULL;
2760 va_start(ap, family);
2761 fields = realtime_arguments_to_fields(ap);
2765 res = ast_load_realtime_all_fields(family, fields);
2771 struct ast_variable *ast_load_realtime_fields(const char *family, const struct ast_variable *fields)
2773 struct ast_variable *res;
2774 struct ast_variable *cur;
2775 struct ast_variable **prev;
2777 res = ast_load_realtime_all_fields(family, fields);
2779 /* Filter the list. */
2783 if (ast_strlen_zero(cur->value)) {
2784 /* Eliminate empty entries */
2785 struct ast_variable *next;
2789 ast_variable_destroy(cur);
2792 /* Make blank entries empty and keep them. */
2793 if (cur->value[0] == ' ' && cur->value[1] == '\0') {
2794 char *vptr = (char *) cur->value;
2806 struct ast_variable *ast_load_realtime(const char *family, ...)
2808 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
2811 va_start(ap, family);
2812 fields = realtime_arguments_to_fields(ap);
2819 return ast_load_realtime_fields(family, fields);
2822 /*! \brief Check if realtime engine is configured for family */
2823 int ast_check_realtime(const char *family)
2825 struct ast_config_engine *eng;
2826 if (!ast_realtime_enabled()) {
2827 return 0; /* There are no engines at all so fail early */
2830 eng = find_engine(family, 1, NULL, 0, NULL, 0);
2836 /*! \brief Check if there's any realtime engines loaded */
2837 int ast_realtime_enabled(void)
2839 return config_maps ? 1 : 0;
2842 int ast_realtime_require_field(const char *family, ...)
2844 struct ast_config_engine *eng;
2850 va_start(ap, family);
2851 for (i = 1; ; i++) {
2852 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2853 /* If the require succeeds, it returns 0. */
2854 if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
2866 int ast_unload_realtime(const char *family)
2868 struct ast_config_engine *eng;
2873 for (i = 1; ; i++) {
2874 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2875 if (eng->unload_func) {
2876 /* Do this for ALL engines */
2877 res = eng->unload_func(db, table);
2886 struct ast_config *ast_load_realtime_multientry_fields(const char *family, const struct ast_variable *fields)
2888 struct ast_config_engine *eng;
2891 struct ast_config *res = NULL;
2894 for (i = 1; ; i++) {
2895 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2896 if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, fields))) {
2897 /* If we were returned an empty cfg, destroy it and return NULL */
2899 ast_config_destroy(res);
2912 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2914 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
2917 va_start(ap, family);
2918 fields = realtime_arguments_to_fields(ap);
2925 return ast_load_realtime_multientry_fields(family, fields);
2928 int ast_update_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
2930 struct ast_config_engine *eng;
2935 for (i = 1; ; i++) {
2936 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2937 /* If the update succeeds, it returns >= 0. */
2938 if (eng->update_func && ((res = eng->update_func(db, table, keyfield, lookup, fields)) >= 0)) {
2949 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2951 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
2954 va_start(ap, lookup);
2955 fields = realtime_arguments_to_fields(ap);
2962 return ast_update_realtime_fields(family, keyfield, lookup, fields);
2965 int ast_update2_realtime_fields(const char *family, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
2967 struct ast_config_engine *eng;
2972 for (i = 1; ; i++) {
2973 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2974 if (eng->update2_func && !(res = eng->update2_func(db, table, lookup_fields, update_fields))) {
2985 int ast_update2_realtime(const char *family, ...)
2987 RAII_VAR(struct ast_variable *, lookup_fields, NULL, ast_variables_destroy);
2988 RAII_VAR(struct ast_variable *, update_fields, NULL, ast_variables_destroy);
2991 va_start(ap, family);
2992 /* XXX: If we wanted to pass no lookup fields (select all), we'd be
2993 * out of luck. realtime_arguments_to_fields expects at least one key
2995 lookup_fields = realtime_arguments_to_fields(ap);
2998 va_start(ap, family);
2999 update_fields = realtime_arguments_to_fields2(ap, 1);
3002 if (!lookup_fields || !update_fields) {
3006 return ast_update2_realtime_fields(family, lookup_fields, update_fields);
3009 int ast_store_realtime_fields(const char *family, const struct ast_variable *fields)
3011 struct ast_config_engine *eng;
3016 for (i = 1; ; i++) {
3017 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3018 /* If the store succeeds, it returns >= 0*/
3019 if (eng->store_func && ((res = eng->store_func(db, table, fields)) >= 0)) {
3030 int ast_store_realtime(const char *family, ...)
3032 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3035 va_start(ap, family);
3036 fields = realtime_arguments_to_fields(ap);
3043 return ast_store_realtime_fields(family, fields);
3046 int ast_destroy_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
3048 struct ast_config_engine *eng;
3053 for (i = 1; ; i++) {
3054 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3055 if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, fields))) {
3066 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
3068 RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3071 va_start(ap, lookup);
3072 fields = realtime_arguments_to_fields(ap);
3079 return ast_destroy_realtime_fields(family, keyfield, lookup, fields);
3082 char *ast_realtime_decode_chunk(char *chunk)
3085 for (; *chunk; chunk++) {
3086 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
3087 sscanf(chunk + 1, "%02hhX", chunk);
3088 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);