sorcery/res_pjsip: Refactor for realtime performance
[asterisk/asterisk.git] / main / config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2010, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief Configuration File Parser
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * Includes the Asterisk Realtime API - ARA
26  * See http://wiki.asterisk.org
27  */
28
29 /*** MODULEINFO
30         <support_level>core</support_level>
31  ***/
32
33 #include "asterisk.h"
34
35 ASTERISK_REGISTER_FILE()
36
37 #include "asterisk/paths.h"     /* use ast_config_AST_CONFIG_DIR */
38 #include "asterisk/network.h"   /* we do some sockaddr manipulation here */
39 #include <time.h>
40 #include <sys/stat.h>
41
42 #include <math.h>       /* HUGE_VAL */
43 #include <regex.h>
44
45 #define AST_INCLUDE_GLOB 1
46
47 #include "asterisk/config.h"
48 #include "asterisk/cli.h"
49 #include "asterisk/lock.h"
50 #include "asterisk/utils.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/app.h"
53 #include "asterisk/astobj2.h"
54 #include "asterisk/strings.h"   /* for the ast_str_*() API */
55 #include "asterisk/netsock2.h"
56
57 #define MAX_NESTED_COMMENTS 128
58 #define COMMENT_START ";--"
59 #define COMMENT_END "--;"
60 #define COMMENT_META ';'
61 #define COMMENT_TAG '-'
62
63 /*!
64  * Define the minimum filename space to reserve for each
65  * ast_variable in case the filename is renamed later by
66  * ast_include_rename().
67  */
68 #define MIN_VARIABLE_FNAME_SPACE        40
69
70 static char *extconfig_conf = "extconfig.conf";
71
72 static struct ao2_container *cfg_hooks;
73 static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg);
74 static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2);
75 static int does_category_match(struct ast_category *cat, const char *category_name, const char *match);
76
77 /*! \brief Structure to keep comments for rewriting configuration files */
78 struct ast_comment {
79         struct ast_comment *next;
80         /*! Comment body allocated after struct. */
81         char cmt[0];
82 };
83
84 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
85 struct cache_file_include {
86         AST_LIST_ENTRY(cache_file_include) list;
87         /*! Filename or wildcard pattern as specified by the including file. */
88         char include[0];
89 };
90
91 struct cache_file_mtime {
92         AST_LIST_ENTRY(cache_file_mtime) list;
93         AST_LIST_HEAD_NOLOCK(includes, cache_file_include) includes;
94         unsigned int has_exec:1;
95         /*! stat() file size */
96         unsigned long stat_size;
97         /*! stat() file modtime nanoseconds */
98         unsigned long stat_mtime_nsec;
99         /*! stat() file modtime seconds since epoc */
100         time_t stat_mtime;
101
102         /*! String stuffed in filename[] after the filename string. */
103         const char *who_asked;
104         /*! Filename and who_asked stuffed after it. */
105         char filename[0];
106 };
107
108 /*! Cached file mtime list. */
109 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
110
111 static int init_appendbuf(void *data)
112 {
113         struct ast_str **str = data;
114         *str = ast_str_create(16);
115         return *str ? 0 : -1;
116 }
117
118 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
119
120 /* comment buffers are better implemented using the ast_str_*() API */
121 #define CB_SIZE 250     /* initial size of comment buffers */
122
123 static void  CB_ADD(struct ast_str **cb, const char *str)
124 {
125         ast_str_append(cb, 0, "%s", str);
126 }
127
128 static void  CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
129 {
130         char *s = ast_alloca(len + 1);
131
132         memcpy(s, str, len);
133         s[len] = '\0';
134         ast_str_append(cb, 0, "%s", s);
135 }
136
137 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
138 {
139         if (cb) {
140                 ast_str_reset(cb);
141         }
142         if (llb) {
143                 ast_str_reset(llb);
144         }
145 }
146
147 static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
148 {
149         struct ast_comment *x = NULL;
150         if (!buffer || !ast_str_strlen(buffer)) {
151                 return NULL;
152         }
153         if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
154                 strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
155         }
156         return x;
157 }
158
159 /* I need to keep track of each config file, and all its inclusions,
160    so that we can track blank lines in each */
161
162 struct inclfile {
163         char *fname;
164         int lineno;
165 };
166
167 static int hash_string(const void *obj, const int flags)
168 {
169         char *str = ((struct inclfile *) obj)->fname;
170         int total;
171
172         for (total = 0; *str; str++) {
173                 unsigned int tmp = total;
174                 total <<= 1; /* multiply by 2 */
175                 total += tmp; /* multiply by 3 */
176                 total <<= 2; /* multiply by 12 */
177                 total += tmp; /* multiply by 13 */
178
179                 total += ((unsigned int) (*str));
180         }
181         if (total < 0) {
182                 total = -total;
183         }
184         return total;
185 }
186
187 static int hashtab_compare_strings(void *a, void *b, int flags)
188 {
189         const struct inclfile *ae = a, *be = b;
190         return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
191 }
192
193 static struct ast_config_map {
194         struct ast_config_map *next;
195         int priority;
196         /*! Stored in stuff[] at struct end. */
197         const char *name;
198         /*! Stored in stuff[] at struct end. */
199         const char *driver;
200         /*! Stored in stuff[] at struct end. */
201         const char *database;
202         /*! Stored in stuff[] at struct end. */
203         const char *table;
204         /*! Contents of name, driver, database, and table in that order stuffed here. */
205         char stuff[0];
206 } *config_maps = NULL;
207
208 AST_MUTEX_DEFINE_STATIC(config_lock);
209 static struct ast_config_engine *config_engine_list;
210
211 #define MAX_INCLUDE_LEVEL 10
212
213 struct ast_category_template_instance {
214         char name[80]; /* redundant? */
215         const struct ast_category *inst;
216         AST_LIST_ENTRY(ast_category_template_instance) next;
217 };
218
219 struct ast_category {
220         char name[80];
221         int ignored;                    /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
222         int include_level;
223         /*!
224          * \brief The file name from whence this declaration was read
225          * \note Will never be NULL
226          */
227         char *file;
228         int lineno;
229         AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
230         struct ast_comment *precomments;
231         struct ast_comment *sameline;
232         struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
233         /*! First category variable in the list. */
234         struct ast_variable *root;
235         /*! Last category variable in the list. */
236         struct ast_variable *last;
237         /*! Previous node in the list. */
238         struct ast_category *prev;
239         /*! Next node in the list. */
240         struct ast_category *next;
241 };
242
243 struct ast_config {
244         /*! First config category in the list. */
245         struct ast_category *root;
246         /*! Last config category in the list. */
247         struct ast_category *last;
248         struct ast_category *current;
249         struct ast_category *last_browse;     /*!< used to cache the last category supplied via category_browse */
250         int include_level;
251         int max_include_level;
252         struct ast_config_include *includes;  /*!< a list of inclusions, which should describe the entire tree */
253 };
254
255 struct ast_config_include {
256         /*!
257          * \brief file name in which the include occurs
258          * \note Will never be NULL
259          */
260         char *include_location_file;
261         int  include_location_lineno;    /*!< lineno where include occurred */
262         int  exec;                       /*!< set to non-zero if its a #exec statement */
263         /*!
264          * \brief if it's an exec, you'll have both the /var/tmp to read, and the original script
265          * \note Will never be NULL if exec is non-zero
266          */
267         char *exec_file;
268         /*!
269          * \brief file name included
270          * \note Will never be NULL
271          */
272         char *included_file;
273         int inclusion_count;             /*!< if the file is included more than once, a running count thereof -- but, worry not,
274                                               we explode the instances and will include those-- so all entries will be unique */
275         int output;                      /*!< a flag to indicate if the inclusion has been output */
276         struct ast_config_include *next; /*!< ptr to next inclusion in the list */
277 };
278
279 static void ast_variable_destroy(struct ast_variable *doomed);
280 static void ast_includes_destroy(struct ast_config_include *incls);
281
282 #ifdef MALLOC_DEBUG
283 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
284 #else
285 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
286 #endif
287 {
288         struct ast_variable *variable;
289         int name_len = strlen(name) + 1;
290         int val_len = strlen(value) + 1;
291         int fn_len = strlen(filename) + 1;
292
293         /* Ensure a minimum length in case the filename is changed later. */
294         if (fn_len < MIN_VARIABLE_FNAME_SPACE) {
295                 fn_len = MIN_VARIABLE_FNAME_SPACE;
296         }
297
298         if (
299 #ifdef MALLOC_DEBUG
300                 (variable = __ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable), file, lineno, func))
301 #else
302                 (variable = ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable)))
303 #endif
304                 ) {
305                 char *dst = variable->stuff;    /* writable space starts here */
306
307                 /* Put file first so ast_include_rename() can calculate space available. */
308                 variable->file = strcpy(dst, filename);
309                 dst += fn_len;
310                 variable->name = strcpy(dst, name);
311                 dst += name_len;
312                 variable->value = strcpy(dst, value);
313         }
314         return variable;
315 }
316
317 /*!
318  * \internal
319  * \brief Move the contents from the source to the destination variable.
320  *
321  * \param dst_var Destination variable node
322  * \param src_var Source variable node
323  *
324  * \return Nothing
325  */
326 static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable *src_var)
327 {
328         dst_var->lineno = src_var->lineno;
329         dst_var->object = src_var->object;
330         dst_var->blanklines = src_var->blanklines;
331         dst_var->precomments = src_var->precomments;
332         src_var->precomments = NULL;
333         dst_var->sameline = src_var->sameline;
334         src_var->sameline = NULL;
335         dst_var->trailing = src_var->trailing;
336         src_var->trailing = NULL;
337 }
338
339 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)
340 {
341         /* a file should be included ONCE. Otherwise, if one of the instances is changed,
342          * then all be changed. -- how do we know to include it? -- Handling modified
343          * instances is possible, I'd have
344          * to create a new master for each instance. */
345         struct ast_config_include *inc;
346         struct stat statbuf;
347
348         inc = ast_include_find(conf, included_file);
349         if (inc) {
350                 do {
351                         inc->inclusion_count++;
352                         snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
353                 } while (stat(real_included_file_name, &statbuf) == 0);
354                 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);
355         } else
356                 *real_included_file_name = 0;
357
358         inc = ast_calloc(1,sizeof(struct ast_config_include));
359         if (!inc) {
360                 return NULL;
361         }
362         inc->include_location_file = ast_strdup(from_file);
363         inc->include_location_lineno = from_lineno;
364         if (!ast_strlen_zero(real_included_file_name))
365                 inc->included_file = ast_strdup(real_included_file_name);
366         else
367                 inc->included_file = ast_strdup(included_file);
368
369         inc->exec = is_exec;
370         if (is_exec)
371                 inc->exec_file = ast_strdup(exec_file);
372
373         if (!inc->include_location_file
374                 || !inc->included_file
375                 || (is_exec && !inc->exec_file)) {
376                 ast_includes_destroy(inc);
377                 return NULL;
378         }
379
380         /* attach this new struct to the conf struct */
381         inc->next = conf->includes;
382         conf->includes = inc;
383
384         return inc;
385 }
386
387 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
388 {
389         struct ast_config_include *incl;
390         struct ast_category *cat;
391         char *str;
392
393         int from_len = strlen(from_file);
394         int to_len = strlen(to_file);
395
396         if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
397                 return;
398
399         /* the manager code allows you to read in one config file, then
400          * write it back out under a different name. But, the new arrangement
401          * ties output lines to the file name. So, before you try to write
402          * the config file to disk, better riffle thru the data and make sure
403          * the file names are changed.
404          */
405         /* file names are on categories, includes (of course), and on variables. So,
406          * traverse all this and swap names */
407
408         for (incl = conf->includes; incl; incl=incl->next) {
409                 if (strcmp(incl->include_location_file,from_file) == 0) {
410                         if (from_len >= to_len)
411                                 strcpy(incl->include_location_file, to_file);
412                         else {
413                                 /* Keep the old filename if the allocation fails. */
414                                 str = ast_strdup(to_file);
415                                 if (str) {
416                                         ast_free(incl->include_location_file);
417                                         incl->include_location_file = str;
418                                 }
419                         }
420                 }
421         }
422         for (cat = conf->root; cat; cat = cat->next) {
423                 struct ast_variable **prev;
424                 struct ast_variable *v;
425                 struct ast_variable *new_var;
426
427                 if (strcmp(cat->file,from_file) == 0) {
428                         if (from_len >= to_len)
429                                 strcpy(cat->file, to_file);
430                         else {
431                                 /* Keep the old filename if the allocation fails. */
432                                 str = ast_strdup(to_file);
433                                 if (str) {
434                                         ast_free(cat->file);
435                                         cat->file = str;
436                                 }
437                         }
438                 }
439                 for (prev = &cat->root, v = cat->root; v; prev = &v->next, v = v->next) {
440                         if (strcmp(v->file, from_file)) {
441                                 continue;
442                         }
443
444                         /*
445                          * Calculate actual space available.  The file string is
446                          * intentionally stuffed before the name string just so we can
447                          * do this.
448                          */
449                         if (to_len < v->name - v->file) {
450                                 /* The new name will fit in the available space. */
451                                 str = (char *) v->file;/* Stupid compiler complains about discarding qualifiers even though I used a cast. */
452                                 strcpy(str, to_file);/* SAFE */
453                                 continue;
454                         }
455
456                         /* Keep the old filename if the allocation fails. */
457                         new_var = ast_variable_new(v->name, v->value, to_file);
458                         if (!new_var) {
459                                 continue;
460                         }
461
462                         /* Move items from the old list node to the replacement node. */
463                         ast_variable_move(new_var, v);
464
465                         /* Replace the old node in the list with the new node. */
466                         new_var->next = v->next;
467                         if (cat->last == v) {
468                                 cat->last = new_var;
469                         }
470                         *prev = new_var;
471
472                         ast_variable_destroy(v);
473
474                         v = new_var;
475                 }
476         }
477 }
478
479 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
480 {
481         struct ast_config_include *x;
482         for (x=conf->includes;x;x=x->next) {
483                 if (strcmp(x->included_file,included_file) == 0)
484                         return x;
485         }
486         return 0;
487 }
488
489
490 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
491 {
492         if (!variable)
493                 return;
494         if (category->last)
495                 category->last->next = variable;
496         else
497                 category->root = variable;
498         category->last = variable;
499         while (category->last->next)
500                 category->last = category->last->next;
501 }
502
503 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
504 {
505         struct ast_variable *cur = category->root;
506         int lineno;
507         int insertline;
508
509         if (!variable || sscanf(line, "%30d", &insertline) != 1) {
510                 return;
511         }
512         if (!insertline) {
513                 variable->next = category->root;
514                 category->root = variable;
515         } else {
516                 for (lineno = 1; lineno < insertline; lineno++) {
517                         cur = cur->next;
518                         if (!cur->next) {
519                                 break;
520                         }
521                 }
522                 variable->next = cur->next;
523                 cur->next = variable;
524         }
525 }
526
527 static void ast_comment_destroy(struct ast_comment **comment)
528 {
529         struct ast_comment *n, *p;
530
531         for (p = *comment; p; p = n) {
532                 n = p->next;
533                 ast_free(p);
534         }
535
536         *comment = NULL;
537 }
538
539 static void ast_variable_destroy(struct ast_variable *doomed)
540 {
541         ast_comment_destroy(&doomed->precomments);
542         ast_comment_destroy(&doomed->sameline);
543         ast_comment_destroy(&doomed->trailing);
544         ast_free(doomed);
545 }
546
547 struct ast_variable *ast_variables_dup(struct ast_variable *var)
548 {
549         struct ast_variable *cloned;
550         struct ast_variable *tmp;
551
552         if (!(cloned = ast_variable_new(var->name, var->value, var->file))) {
553                 return NULL;
554         }
555
556         tmp = cloned;
557
558         while ((var = var->next)) {
559                 if (!(tmp->next = ast_variable_new(var->name, var->value, var->file))) {
560                         ast_variables_destroy(cloned);
561                         return NULL;
562                 }
563                 tmp = tmp->next;
564         }
565
566         return cloned;
567 }
568
569 struct ast_variable *ast_variables_reverse(struct ast_variable *var)
570 {
571         struct ast_variable *var1, *var2;
572
573         var1 = var;
574
575         if (!var1 || !var1->next) {
576                 return var1;
577         }
578
579         var2 = var1->next;
580         var1->next = NULL;
581
582         while (var2) {
583                 struct ast_variable *next = var2->next;
584
585                 var2->next = var1;
586                 var1 = var2;
587                 var2 = next;
588         }
589
590         return var1;
591 }
592
593 void ast_variables_destroy(struct ast_variable *v)
594 {
595         struct ast_variable *vn;
596
597         while (v) {
598                 vn = v;
599                 v = v->next;
600                 ast_variable_destroy(vn);
601         }
602 }
603
604 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
605 {
606         struct ast_category *cat;
607
608         if (config->last_browse && (config->last_browse->name == category)) {
609                 cat = config->last_browse;
610         } else {
611                 cat = ast_category_get(config, category, NULL);
612         }
613
614         return (cat) ? cat->root : NULL;
615 }
616
617 static inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
618 {
619     l1->next = l2->next;
620     l2->next = l1;
621     return l2;
622 }
623
624 struct ast_variable *ast_variable_list_sort(struct ast_variable *start)
625 {
626         struct ast_variable *p, *q;
627         struct ast_variable top;
628         int changed = 1;
629         memset(&top, 0, sizeof(top));
630         top.next = start;
631         if (start != NULL && start->next != NULL) {
632                 while (changed) {
633                         changed = 0;
634                         q = &top;
635                         p = top.next;
636                         while (p->next != NULL) {
637                                 if (p->next != NULL && strcmp(p->name, p->next->name) > 0) {
638                                         q->next = variable_list_switch(p, p->next);
639                                         changed = 1;
640                                 }
641                                 q = p;
642                                 if (p->next != NULL)
643                                         p = p->next;
644                         }
645                 }
646         }
647         return top.next;
648 }
649
650 struct ast_variable *ast_variable_list_append_hint(struct ast_variable **head, struct ast_variable *search_hint, struct ast_variable *newvar)
651 {
652         struct ast_variable *curr;
653         ast_assert(head != NULL);
654
655         if (!*head) {
656                 *head = newvar;
657         } else {
658                 if (search_hint == NULL) {
659                         search_hint = *head;
660                 }
661                 for (curr = search_hint; curr->next; curr = curr->next);
662                 curr->next = newvar;
663         }
664
665         for (curr = newvar; curr->next; curr = curr->next);
666
667         return curr;
668 }
669
670 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
671 {
672         const char *tmp;
673         tmp = ast_variable_retrieve(cfg, cat, var);
674         if (!tmp) {
675                 tmp = ast_variable_retrieve(cfg, "general", var);
676         }
677         return tmp;
678 }
679
680 const char *ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
681 {
682         struct ast_variable *v;
683
684         if (category) {
685                 for (v = ast_variable_browse(config, category); v; v = v->next) {
686                         if (!strcasecmp(variable, v->name)) {
687                                 return v->value;
688                         }
689                 }
690         } else {
691                 struct ast_category *cat;
692
693                 for (cat = config->root; cat; cat = cat->next) {
694                         for (v = cat->root; v; v = v->next) {
695                                 if (!strcasecmp(variable, v->name)) {
696                                         return v->value;
697                                 }
698                         }
699                 }
700         }
701
702         return NULL;
703 }
704
705 const char *ast_variable_retrieve_filtered(struct ast_config *config,
706         const char *category, const char *variable, const char *filter)
707 {
708         struct ast_category *cat = NULL;
709         const char *value;
710
711         while ((cat = ast_category_browse_filtered(config, category, cat, filter))) {
712                 value = ast_variable_find(cat, variable);
713                 if (value) {
714                         return value;
715                 }
716         }
717
718         return NULL;
719 }
720
721 const char *ast_variable_find(const struct ast_category *category, const char *variable)
722 {
723         return ast_variable_find_in_list(category->root, variable);
724 }
725
726 const struct ast_variable *ast_variable_find_variable_in_list(const struct ast_variable *list, const char *variable_name)
727 {
728         const struct ast_variable *v;
729
730         for (v = list; v; v = v->next) {
731                 if (!strcasecmp(variable_name, v->name)) {
732                         return v;
733                 }
734         }
735         return NULL;
736 }
737
738 int ast_variables_match(const struct ast_variable *left, const struct ast_variable *right)
739 {
740         char *op;
741
742         if (left == right) {
743                 return 1;
744         }
745
746         if (!(left && right)) {
747                 return 0;
748         }
749
750         op = strrchr(right->name, ' ');
751         if (op) {
752                 op++;
753         }
754
755         return ast_strings_match(left->value, op ? ast_strdupa(op) : NULL, right->value);
756 }
757
758 int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
759 {
760         const struct ast_variable *field;
761         int right_count = 0;
762         int left_count = 0;
763
764         if (left == right) {
765                 return 1;
766         }
767
768         if (!(left && right)) {
769                 return 0;
770         }
771
772         for (field = right; field; field = field->next) {
773                 char *space = strrchr(field->name, ' ');
774                 const struct ast_variable *old;
775                 char * name = (char *)field->name;
776
777                 if (space) {
778                         name = ast_strdup(field->name);
779                         if (!name) {
780                                 return 0;
781                         }
782                         name[space - field->name] = '\0';
783                 }
784
785                 old = ast_variable_find_variable_in_list(left, name);
786                 if (name != field->name) {
787                         ast_free(name);
788                 }
789
790                 if (exact_match) {
791                         if (!old || strcmp(old->value, field->value)) {
792                                 return 0;
793                         }
794                 } else {
795                         if (!ast_variables_match(old, field)) {
796                                 return 0;
797                         }
798                 }
799
800                 right_count++;
801         }
802
803         if (exact_match) {
804                 for (field = left; field; field = field->next) {
805                         left_count++;
806                 }
807
808                 if (right_count != left_count) {
809                         return 0;
810                 }
811         }
812
813         return 1;
814 }
815
816 const char *ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
817 {
818         const struct ast_variable *v;
819
820         for (v = list; v; v = v->next) {
821                 if (!strcasecmp(variable, v->name)) {
822                         return v->value;
823                 }
824         }
825         return NULL;
826 }
827
828 const char *ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
829 {
830         const struct ast_variable *v;
831         const char *found = NULL;
832
833         for (v = list; v; v = v->next) {
834                 if (!strcasecmp(variable, v->name)) {
835                         found = v->value;
836                 }
837         }
838         return found;
839 }
840
841 static struct ast_variable *variable_clone(const struct ast_variable *old)
842 {
843         struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
844
845         if (new) {
846                 new->lineno = old->lineno;
847                 new->object = old->object;
848                 new->blanklines = old->blanklines;
849                 /* TODO: clone comments? */
850         }
851
852         return new;
853 }
854
855 static void move_variables(struct ast_category *old, struct ast_category *new)
856 {
857         struct ast_variable *var = old->root;
858
859         old->root = NULL;
860         /* we can just move the entire list in a single op */
861         ast_variable_append(new, var);
862 }
863
864 /*! \brief Returns true if ALL of the regex expressions and category name match.
865  * Both can be NULL (I.E. no predicate) which results in a true return;
866  */
867 static int does_category_match(struct ast_category *cat, const char *category_name, const char *match)
868 {
869         char *dupmatch;
870         char *nvp = NULL;
871         int match_found = 0, match_expressions = 0;
872         int template_ok = 0;
873
874         /* Only match on category name if it's not a NULL or empty string */
875         if (!ast_strlen_zero(category_name) && strcasecmp(cat->name, category_name)) {
876                 return 0;
877         }
878
879         /* If match is NULL or empty, automatically match if not a template */
880         if (ast_strlen_zero(match)) {
881                 return !cat->ignored;
882         }
883
884         dupmatch = ast_strdupa(match);
885
886         while ((nvp = ast_strsep(&dupmatch, ',', AST_STRSEP_STRIP))) {
887                 struct ast_variable *v;
888                 char *match_name;
889                 char *match_value = NULL;
890                 char *regerr;
891                 int rc;
892                 regex_t r_name, r_value;
893
894                 match_expressions++;
895
896                 match_name = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
897                 match_value = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
898
899                 /* an empty match value is OK.  A NULL match value (no =) is NOT. */
900                 if (match_value == NULL) {
901                         break;
902                 }
903
904                 if (!strcmp("TEMPLATES", match_name)) {
905                         if (!strcasecmp("include", match_value)) {
906                                 if (cat->ignored) {
907                                         template_ok = 1;
908                                 }
909                                 match_found++;
910                         } else if (!strcasecmp("restrict", match_value)) {
911                                 if (cat->ignored) {
912                                         match_found++;
913                                         template_ok = 1;
914                                 } else {
915                                         break;
916                                 }
917                         }
918                         continue;
919                 }
920
921                 if ((rc = regcomp(&r_name, match_name, REG_EXTENDED | REG_NOSUB))) {
922                         regerr = ast_alloca(128);
923                         regerror(rc, &r_name, regerr, 128);
924                         ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
925                                 match_name, regerr);
926                         regfree(&r_name);
927                         return 0;
928                 }
929                 if ((rc = regcomp(&r_value, match_value, REG_EXTENDED | REG_NOSUB))) {
930                         regerr = ast_alloca(128);
931                         regerror(rc, &r_value, regerr, 128);
932                         ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
933                                 match_value, regerr);
934                         regfree(&r_name);
935                         regfree(&r_value);
936                         return 0;
937                 }
938
939                 for (v = cat->root; v; v = v->next) {
940                         if (!regexec(&r_name, v->name, 0, NULL, 0)
941                                 && !regexec(&r_value, v->value, 0, NULL, 0)) {
942                                 match_found++;
943                                 break;
944                         }
945                 }
946                 regfree(&r_name);
947                 regfree(&r_value);
948         }
949         if (match_found == match_expressions && (!cat->ignored || template_ok)) {
950                 return 1;
951         }
952         return 0;
953 }
954
955
956 static struct ast_category *new_category(const char *name, const char *in_file, int lineno, int template)
957 {
958         struct ast_category *category;
959
960         category = ast_calloc(1, sizeof(*category));
961         if (!category) {
962                 return NULL;
963         }
964         category->file = ast_strdup(in_file);
965         if (!category->file) {
966                 ast_category_destroy(category);
967                 return NULL;
968         }
969         ast_copy_string(category->name, name, sizeof(category->name));
970         category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
971         category->ignored = template;
972         return category;
973 }
974
975 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
976 {
977         return new_category(name, in_file, lineno, 0);
978 }
979
980 struct ast_category *ast_category_new_template(const char *name, const char *in_file, int lineno)
981 {
982         return new_category(name, in_file, lineno, 1);
983 }
984
985 struct ast_category *ast_category_get(const struct ast_config *config,
986         const char *category_name, const char *filter)
987 {
988         struct ast_category *cat;
989
990         for (cat = config->root; cat; cat = cat->next) {
991                 if (cat->name == category_name && does_category_match(cat, category_name, filter)) {
992                         return cat;
993                 }
994         }
995
996         for (cat = config->root; cat; cat = cat->next) {
997                 if (does_category_match(cat, category_name, filter)) {
998                         return cat;
999                 }
1000         }
1001
1002         return NULL;
1003 }
1004
1005 const char *ast_category_get_name(const struct ast_category *category)
1006 {
1007         return category->name;
1008 }
1009
1010 int ast_category_is_template(const struct ast_category *category)
1011 {
1012         return category->ignored;
1013 }
1014
1015 struct ast_str *ast_category_get_templates(const struct ast_category *category)
1016 {
1017         struct ast_category_template_instance *template;
1018         struct ast_str *str;
1019         int first = 1;
1020
1021         if (AST_LIST_EMPTY(&category->template_instances)) {
1022                 return NULL;
1023         }
1024
1025         str = ast_str_create(128);
1026         if (!str) {
1027                 return NULL;
1028         }
1029
1030         AST_LIST_TRAVERSE(&category->template_instances, template, next) {
1031                 ast_str_append(&str, 0, "%s%s", first ? "" : ",", template->name);
1032                 first = 0;
1033         }
1034
1035         return str;
1036 }
1037
1038 int ast_category_exist(const struct ast_config *config, const char *category_name,
1039         const char *filter)
1040 {
1041         return !!ast_category_get(config, category_name, filter);
1042 }
1043
1044 void ast_category_append(struct ast_config *config, struct ast_category *category)
1045 {
1046         if (config->last) {
1047                 config->last->next = category;
1048                 category->prev = config->last;
1049         } else {
1050                 config->root = category;
1051                 category->prev = NULL;
1052         }
1053         category->next = NULL;
1054         category->include_level = config->include_level;
1055
1056         config->last = category;
1057         config->current = category;
1058 }
1059
1060 int ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
1061 {
1062         struct ast_category *cur_category;
1063
1064         if (!config || !config->root || !cat || !match) {
1065                 return -1;
1066         }
1067
1068         if (!strcasecmp(config->root->name, match)) {
1069                 cat->next = config->root;
1070                 cat->prev = NULL;
1071                 config->root->prev = cat;
1072                 config->root = cat;
1073                 return 0;
1074         }
1075
1076         for (cur_category = config->root->next; cur_category; cur_category = cur_category->next) {
1077                 if (!strcasecmp(cur_category->name, match)) {
1078                         cat->prev = cur_category->prev;
1079                         cat->prev->next = cat;
1080
1081                         cat->next = cur_category;
1082                         cur_category->prev = cat;
1083
1084                         return 0;
1085                 }
1086         }
1087
1088         return -1;
1089 }
1090
1091 static void ast_destroy_template_list(struct ast_category *cat)
1092 {
1093         struct ast_category_template_instance *x;
1094
1095         while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
1096                 ast_free(x);
1097 }
1098
1099 void ast_category_destroy(struct ast_category *cat)
1100 {
1101         ast_variables_destroy(cat->root);
1102         cat->root = NULL;
1103         cat->last = NULL;
1104         ast_comment_destroy(&cat->precomments);
1105         ast_comment_destroy(&cat->sameline);
1106         ast_comment_destroy(&cat->trailing);
1107         ast_destroy_template_list(cat);
1108         ast_free(cat->file);
1109         ast_free(cat);
1110 }
1111
1112 static void ast_includes_destroy(struct ast_config_include *incls)
1113 {
1114         struct ast_config_include *incl,*inclnext;
1115
1116         for (incl=incls; incl; incl = inclnext) {
1117                 inclnext = incl->next;
1118                 ast_free(incl->include_location_file);
1119                 ast_free(incl->exec_file);
1120                 ast_free(incl->included_file);
1121                 ast_free(incl);
1122         }
1123 }
1124
1125 static struct ast_category *next_available_category(struct ast_category *cat,
1126         const char *name, const char *filter)
1127 {
1128         for (; cat && !does_category_match(cat, name, filter); cat = cat->next);
1129
1130         return cat;
1131 }
1132
1133 /*! return the first var of a category */
1134 struct ast_variable *ast_category_first(struct ast_category *cat)
1135 {
1136         return (cat) ? cat->root : NULL;
1137 }
1138
1139 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
1140 {
1141         struct ast_category *category = ast_category_get(config, cat, NULL);
1142
1143         if (category)
1144                 return category->root;
1145         return NULL;
1146 }
1147
1148 void ast_config_sort_categories(struct ast_config *config, int descending,
1149                                                                 int (*comparator)(struct ast_category *p, struct ast_category *q))
1150 {
1151         /*
1152          * The contents of this function are adapted from
1153          * an example of linked list merge sorting
1154          * copyright 2001 Simon Tatham.
1155          *
1156          * Permission is hereby granted, free of charge, to any person
1157          * obtaining a copy of this software and associated documentation
1158          * files (the "Software"), to deal in the Software without
1159          * restriction, including without limitation the rights to use,
1160          * copy, modify, merge, publish, distribute, sublicense, and/or
1161          * sell copies of the Software, and to permit persons to whom the
1162          * Software is furnished to do so, subject to the following
1163          * conditions:
1164          *
1165          * The above copyright notice and this permission notice shall be
1166          * included in all copies or substantial portions of the Software.
1167          *
1168          * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1169          * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
1170          * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1171          * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
1172          * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
1173          * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1174          * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1175          * SOFTWARE.
1176          */
1177
1178         int insize = 1;
1179         struct ast_category *p, *q, *e, *tail;
1180         int nmerges, psize, qsize, i;
1181
1182         /* If the descending flag was sent, we'll apply inversion to the comparison function's return. */
1183         if (descending) {
1184                 descending = -1;
1185         } else {
1186                 descending = 1;
1187         }
1188
1189         if (!config->root) {
1190                 return;
1191         }
1192
1193         while (1) {
1194                 p = config->root;
1195                 config->root = NULL;
1196                 tail = NULL;
1197
1198                 nmerges = 0; /* count number of merges we do in this pass */
1199
1200                 while (p) {
1201                         nmerges++; /* there exists a merge to be done */
1202
1203                         /* step `insize' places along from p */
1204                         q = p;
1205                         psize = 0;
1206                         for (i = 0; i < insize; i++) {
1207                                 psize++;
1208                                 q = q->next;
1209                                 if (!q) {
1210                                         break;
1211                                 }
1212                         }
1213
1214                         /* if q hasn't fallen off end, we have two lists to merge */
1215                         qsize = insize;
1216
1217                         /* now we have two lists; merge them */
1218                         while (psize > 0 || (qsize > 0 && q)) {
1219                                 /* decide whether next element of merge comes from p or q */
1220                                 if (psize == 0) {
1221                                         /* p is empty; e must come from q. */
1222                                         e = q;
1223                                         q = q->next;
1224                                         qsize--;
1225                                 } else if (qsize == 0 || !q) {
1226                                         /* q is empty; e must come from p. */
1227                                         e = p; p = p->next; psize--;
1228                                 } else if ((comparator(p,q) * descending) <= 0) {
1229                                         /* First element of p is lower (or same) e must come from p. */
1230                                         e = p;
1231                                         p = p->next;
1232                                         psize--;
1233                                 } else {
1234                                         /* First element of q is lower; e must come from q. */
1235                                         e = q;
1236                                         q = q->next;
1237                                         qsize--;
1238                                 }
1239
1240                                 /* add the next element to the merged list */
1241                                 if (tail) {
1242                                         tail->next = e;
1243                                 } else {
1244                                         config->root = e;
1245                                 }
1246                                 tail = e;
1247                         }
1248
1249                         /* now p has stepped `insize' places along, and q has too */
1250                         p = q;
1251                 }
1252
1253                 tail->next = NULL;
1254
1255                 /* If we have done only one merge, we're finished. */
1256                 if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
1257                         return;
1258                 }
1259
1260                 /* Otherwise repeat, merging lists twice the size */
1261                 insize *= 2;
1262         }
1263
1264 }
1265
1266 char *ast_category_browse(struct ast_config *config, const char *prev)
1267 {
1268         struct ast_category *cat;
1269
1270         if (!prev) {
1271                 /* First time browse. */
1272                 cat = config->root;
1273         } else if (config->last_browse && (config->last_browse->name == prev)) {
1274                 /* Simple last browse found. */
1275                 cat = config->last_browse->next;
1276         } else {
1277                 /*
1278                  * Config changed since last browse.
1279                  *
1280                  * First try cheap last browse search. (Rebrowsing a different
1281                  * previous category?)
1282                  */
1283                 for (cat = config->root; cat; cat = cat->next) {
1284                         if (cat->name == prev) {
1285                                 /* Found it. */
1286                                 cat = cat->next;
1287                                 break;
1288                         }
1289                 }
1290                 if (!cat) {
1291                         /*
1292                          * Have to do it the hard way. (Last category was deleted and
1293                          * re-added?)
1294                          */
1295                         for (cat = config->root; cat; cat = cat->next) {
1296                                 if (!strcasecmp(cat->name, prev)) {
1297                                         /* Found it. */
1298                                         cat = cat->next;
1299                                         break;
1300                                 }
1301                         }
1302                 }
1303         }
1304
1305         if (cat)
1306                 cat = next_available_category(cat, NULL, NULL);
1307
1308         config->last_browse = cat;
1309         return (cat) ? cat->name : NULL;
1310 }
1311
1312 struct ast_category *ast_category_browse_filtered(struct ast_config *config,
1313         const char *category_name, struct ast_category *prev, const char *filter)
1314 {
1315         struct ast_category *cat;
1316
1317         if (!prev) {
1318                 prev = config->root;
1319         } else {
1320                 prev = prev->next;
1321         }
1322
1323         cat = next_available_category(prev, category_name, filter);
1324
1325         return cat;
1326 }
1327
1328 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
1329 {
1330         struct ast_variable *v;
1331
1332         v = cat->root;
1333         cat->root = NULL;
1334         cat->last = NULL;
1335
1336         return v;
1337 }
1338
1339 void ast_category_rename(struct ast_category *cat, const char *name)
1340 {
1341         ast_copy_string(cat->name, name, sizeof(cat->name));
1342 }
1343
1344 int ast_category_inherit(struct ast_category *new, const struct ast_category *base)
1345 {
1346         struct ast_variable *var;
1347         struct ast_category_template_instance *x;
1348
1349         x = ast_calloc(1, sizeof(*x));
1350         if (!x) {
1351                 return -1;
1352         }
1353         strcpy(x->name, base->name);
1354         x->inst = base;
1355         AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
1356         for (var = base->root; var; var = var->next) {
1357                 struct ast_variable *cloned = variable_clone(var);
1358                 if (!cloned) {
1359                         return -1;
1360                 }
1361                 cloned->inherited = 1;
1362                 ast_variable_append(new, cloned);
1363         }
1364         return 0;
1365 }
1366
1367 struct ast_config *ast_config_new(void)
1368 {
1369         struct ast_config *config;
1370
1371         if ((config = ast_calloc(1, sizeof(*config))))
1372                 config->max_include_level = MAX_INCLUDE_LEVEL;
1373         return config;
1374 }
1375
1376 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
1377 {
1378         struct ast_variable *cur, *prev=NULL, *curn;
1379         int res = -1;
1380         int num_item = 0;
1381         int req_item;
1382
1383         req_item = -1;
1384         if (!ast_strlen_zero(line)) {
1385                 /* Requesting to delete by item number. */
1386                 if (sscanf(line, "%30d", &req_item) != 1
1387                         || req_item < 0) {
1388                         /* Invalid item number to delete. */
1389                         return -1;
1390                 }
1391         }
1392
1393         prev = NULL;
1394         cur = category->root;
1395         while (cur) {
1396                 curn = cur->next;
1397                 /* Delete by item number or by variable name with optional value. */
1398                 if ((0 <= req_item && num_item == req_item)
1399                         || (req_item < 0 && !strcasecmp(cur->name, variable)
1400                                 && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
1401                         if (prev) {
1402                                 prev->next = cur->next;
1403                                 if (cur == category->last)
1404                                         category->last = prev;
1405                         } else {
1406                                 category->root = cur->next;
1407                                 if (cur == category->last)
1408                                         category->last = NULL;
1409                         }
1410                         ast_variable_destroy(cur);
1411                         res = 0;
1412                 } else
1413                         prev = cur;
1414
1415                 cur = curn;
1416                 ++num_item;
1417         }
1418         return res;
1419 }
1420
1421 int ast_variable_update(struct ast_category *category, const char *variable,
1422                                                 const char *value, const char *match, unsigned int object)
1423 {
1424         struct ast_variable *cur, *prev=NULL, *newer=NULL;
1425
1426         for (cur = category->root; cur; prev = cur, cur = cur->next) {
1427                 if (strcasecmp(cur->name, variable) ||
1428                         (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
1429                         continue;
1430
1431                 if (!(newer = ast_variable_new(variable, value, cur->file)))
1432                         return -1;
1433
1434                 ast_variable_move(newer, cur);
1435                 newer->object = newer->object || object;
1436
1437                 /* Replace the old node in the list with the new node. */
1438                 newer->next = cur->next;
1439                 if (prev)
1440                         prev->next = newer;
1441                 else
1442                         category->root = newer;
1443                 if (category->last == cur)
1444                         category->last = newer;
1445
1446                 ast_variable_destroy(cur);
1447
1448                 return 0;
1449         }
1450
1451         /* Could not find variable to update */
1452         return -1;
1453 }
1454
1455 struct ast_category *ast_category_delete(struct ast_config *config,
1456         struct ast_category *category)
1457 {
1458         struct ast_category *prev;
1459
1460         if (!config || !category) {
1461                 return NULL;
1462         }
1463
1464         if (category->prev) {
1465                 category->prev->next = category->next;
1466         } else {
1467                 config->root = category->next;
1468         }
1469
1470         if (category->next) {
1471                 category->next->prev = category->prev;
1472         } else {
1473                 config->last = category->prev;
1474         }
1475
1476         prev = category->prev;
1477
1478         if (config->last_browse == category) {
1479                 config->last_browse = prev;
1480         }
1481
1482         ast_category_destroy(category);
1483
1484         return prev;
1485 }
1486
1487 int ast_category_empty(struct ast_category *category)
1488 {
1489         if (!category) {
1490                 return -1;
1491         }
1492
1493         ast_variables_destroy(category->root);
1494         category->root = NULL;
1495         category->last = NULL;
1496
1497         return 0;
1498 }
1499
1500 void ast_config_destroy(struct ast_config *cfg)
1501 {
1502         struct ast_category *cat, *catn;
1503
1504         if (!cfg)
1505                 return;
1506
1507         ast_includes_destroy(cfg->includes);
1508
1509         cat = cfg->root;
1510         while (cat) {
1511                 catn = cat;
1512                 cat = cat->next;
1513                 ast_category_destroy(catn);
1514         }
1515         ast_free(cfg);
1516 }
1517
1518 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
1519 {
1520         return cfg->current;
1521 }
1522
1523 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
1524 {
1525         /* cast below is just to silence compiler warning about dropping "const" */
1526         cfg->current = (struct ast_category *) cat;
1527 }
1528
1529 /*!
1530  * \internal
1531  * \brief Create a new cfmtime list node.
1532  *
1533  * \param filename Config filename caching.
1534  * \param who_asked Who wanted to know.
1535  *
1536  * \retval cfmtime New node on success.
1537  * \retval NULL on error.
1538  */
1539 static struct cache_file_mtime *cfmtime_new(const char *filename, const char *who_asked)
1540 {
1541         struct cache_file_mtime *cfmtime;
1542         char *dst;
1543
1544         cfmtime = ast_calloc(1,
1545                 sizeof(*cfmtime) + strlen(filename) + 1 + strlen(who_asked) + 1);
1546         if (!cfmtime) {
1547                 return NULL;
1548         }
1549         dst = cfmtime->filename;        /* writable space starts here */
1550         strcpy(dst, filename); /* Safe */
1551         dst += strlen(dst) + 1;
1552         cfmtime->who_asked = strcpy(dst, who_asked); /* Safe */
1553
1554         return cfmtime;
1555 }
1556
1557 enum config_cache_attribute_enum {
1558         ATTRIBUTE_INCLUDE = 0,
1559         ATTRIBUTE_EXEC = 1,
1560 };
1561
1562 /*!
1563  * \internal
1564  * \brief Save the stat() data to the cached file modtime struct.
1565  *
1566  * \param cfmtime Cached file modtime.
1567  * \param statbuf Buffer filled in by stat().
1568  *
1569  * \return Nothing
1570  */
1571 static void cfmstat_save(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1572 {
1573         cfmtime->stat_size = statbuf->st_size;
1574 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
1575         cfmtime->stat_mtime_nsec = statbuf->st_mtim.tv_nsec;
1576 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
1577         cfmtime->stat_mtime_nsec = statbuf->st_mtimensec;
1578 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1579         cfmtime->stat_mtime_nsec = statbuf->st_mtimespec.tv_nsec;
1580 #else
1581         cfmtime->stat_mtime_nsec = 0;
1582 #endif
1583         cfmtime->stat_mtime = statbuf->st_mtime;
1584 }
1585
1586 /*!
1587  * \internal
1588  * \brief Compare the stat() data with the cached file modtime struct.
1589  *
1590  * \param cfmtime Cached file modtime.
1591  * \param statbuf Buffer filled in by stat().
1592  *
1593  * \retval non-zero if different.
1594  */
1595 static int cfmstat_cmp(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1596 {
1597         struct cache_file_mtime cfm_buf;
1598
1599         cfmstat_save(&cfm_buf, statbuf);
1600
1601         return cfmtime->stat_size != cfm_buf.stat_size
1602                 || cfmtime->stat_mtime != cfm_buf.stat_mtime
1603                 || cfmtime->stat_mtime_nsec != cfm_buf.stat_mtime_nsec;
1604 }
1605
1606 /*!
1607  * \internal
1608  * \brief Clear the cached file modtime include list.
1609  *
1610  * \param cfmtime Cached file modtime.
1611  *
1612  * \note cfmtime_head is assumed already locked.
1613  *
1614  * \return Nothing
1615  */
1616 static void config_cache_flush_includes(struct cache_file_mtime *cfmtime)
1617 {
1618         struct cache_file_include *cfinclude;
1619
1620         while ((cfinclude = AST_LIST_REMOVE_HEAD(&cfmtime->includes, list))) {
1621                 ast_free(cfinclude);
1622         }
1623 }
1624
1625 /*!
1626  * \internal
1627  * \brief Destroy the given cached file modtime entry.
1628  *
1629  * \param cfmtime Cached file modtime.
1630  *
1631  * \note cfmtime_head is assumed already locked.
1632  *
1633  * \return Nothing
1634  */
1635 static void config_cache_destroy_entry(struct cache_file_mtime *cfmtime)
1636 {
1637         config_cache_flush_includes(cfmtime);
1638         ast_free(cfmtime);
1639 }
1640
1641 /*!
1642  * \internal
1643  * \brief Remove and destroy the config cache entry for the filename and who_asked.
1644  *
1645  * \param filename Config filename.
1646  * \param who_asked Which module asked.
1647  *
1648  * \return Nothing
1649  */
1650 static void config_cache_remove(const char *filename, const char *who_asked)
1651 {
1652         struct cache_file_mtime *cfmtime;
1653
1654         AST_LIST_LOCK(&cfmtime_head);
1655         AST_LIST_TRAVERSE_SAFE_BEGIN(&cfmtime_head, cfmtime, list) {
1656                 if (!strcmp(cfmtime->filename, filename)
1657                         && !strcmp(cfmtime->who_asked, who_asked)) {
1658                         AST_LIST_REMOVE_CURRENT(list);
1659                         config_cache_destroy_entry(cfmtime);
1660                         break;
1661                 }
1662         }
1663         AST_LIST_TRAVERSE_SAFE_END;
1664         AST_LIST_UNLOCK(&cfmtime_head);
1665 }
1666
1667 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
1668 {
1669         struct cache_file_mtime *cfmtime;
1670         struct cache_file_include *cfinclude;
1671
1672         /* Find our cached entry for this configuration file */
1673         AST_LIST_LOCK(&cfmtime_head);
1674         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1675                 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
1676                         break;
1677         }
1678         if (!cfmtime) {
1679                 cfmtime = cfmtime_new(configfile, who_asked);
1680                 if (!cfmtime) {
1681                         AST_LIST_UNLOCK(&cfmtime_head);
1682                         return;
1683                 }
1684                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1685                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1686         }
1687
1688         switch (attrtype) {
1689         case ATTRIBUTE_INCLUDE:
1690                 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1691                         if (!strcmp(cfinclude->include, filename)) {
1692                                 AST_LIST_UNLOCK(&cfmtime_head);
1693                                 return;
1694                         }
1695                 }
1696                 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
1697                 if (!cfinclude) {
1698                         AST_LIST_UNLOCK(&cfmtime_head);
1699                         return;
1700                 }
1701                 strcpy(cfinclude->include, filename); /* Safe */
1702                 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
1703                 break;
1704         case ATTRIBUTE_EXEC:
1705                 cfmtime->has_exec = 1;
1706                 break;
1707         }
1708         AST_LIST_UNLOCK(&cfmtime_head);
1709 }
1710
1711 /*! \brief parse one line in the configuration.
1712  * \verbatim
1713  * We can have a category header        [foo](...)
1714  * a directive                          #include / #exec
1715  * or a regular line                    name = value
1716  * \endverbatim
1717  */
1718 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
1719         char *buf, int lineno, const char *configfile, struct ast_flags flags,
1720         struct ast_str *comment_buffer,
1721         struct ast_str *lline_buffer,
1722         const char *suggested_include_file,
1723         struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
1724 {
1725         char *c;
1726         char *cur = buf;
1727         struct ast_variable *v;
1728         char cmd[512], exec_file[512];
1729
1730         /* Actually parse the entry */
1731         if (cur[0] == '[') { /* A category header */
1732                 /* format is one of the following:
1733                  * [foo]        define a new category named 'foo'
1734                  * [foo](!)     define a new template category named 'foo'
1735                  * [foo](+)     append to category 'foo', error if foo does not exist.
1736                  * [foo](a)     define a new category and inherit from category or template a.
1737                  *              You can put a comma-separated list of categories and templates
1738                  *              and '!' and '+' between parentheses, with obvious meaning.
1739                  */
1740                 struct ast_category *newcat;
1741                 char *catname;
1742
1743                 c = strchr(cur, ']');
1744                 if (!c) {
1745                         ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
1746                         return -1;
1747                 }
1748                 *c++ = '\0';
1749                 cur++;
1750                 if (*c++ != '(')
1751                         c = NULL;
1752                 catname = cur;
1753                 *cat = newcat = ast_category_new(catname,
1754                         S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
1755                         lineno);
1756                 if (!newcat) {
1757                         return -1;
1758                 }
1759                 (*cat)->lineno = lineno;
1760
1761                 /* add comments */
1762                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1763                         newcat->precomments = ALLOC_COMMENT(comment_buffer);
1764                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1765                         newcat->sameline = ALLOC_COMMENT(lline_buffer);
1766                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1767                         CB_RESET(comment_buffer, lline_buffer);
1768
1769                 /* If there are options or categories to inherit from, process them now */
1770                 if (c) {
1771                         if (!(cur = strchr(c, ')'))) {
1772                                 ast_category_destroy(newcat);
1773                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
1774                                 return -1;
1775                         }
1776                         *cur = '\0';
1777                         while ((cur = strsep(&c, ","))) {
1778                                 if (!strcasecmp(cur, "!")) {
1779                                         (*cat)->ignored = 1;
1780                                 } else if (!strcasecmp(cur, "+")) {
1781                                         *cat = ast_category_get(cfg, catname, NULL);
1782                                         if (!(*cat)) {
1783                                                 if (newcat) {
1784                                                         ast_category_destroy(newcat);
1785                                                 }
1786                                                 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
1787                                                 return -1;
1788                                         }
1789                                         if (newcat) {
1790                                                 ast_config_set_current_category(cfg, *cat);
1791                                                 (*cat)->ignored |= newcat->ignored;
1792                                                 move_variables(newcat, *cat);
1793                                                 ast_category_destroy(newcat);
1794                                                 newcat = NULL;
1795                                         }
1796                                 } else {
1797                                         struct ast_category *base;
1798
1799                                         base = ast_category_get(cfg, cur, "TEMPLATES=include");
1800                                         if (!base) {
1801                                                 if (newcat) {
1802                                                         ast_category_destroy(newcat);
1803                                                 }
1804                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1805                                                 return -1;
1806                                         }
1807                                         if (ast_category_inherit(*cat, base)) {
1808                                                 if (newcat) {
1809                                                         ast_category_destroy(newcat);
1810                                                 }
1811                                                 ast_log(LOG_ERROR, "Inheritence requested, but allocation failed\n");
1812                                                 return -1;
1813                                         }
1814                                 }
1815                         }
1816                 }
1817
1818                 /*
1819                  * We need to set *last_cat to newcat here regardless.  If the
1820                  * category is being appended to we have no place for trailing
1821                  * comments on the appended category.  The appended category
1822                  * may be in another file or it already has trailing comments
1823                  * that we would then leak.
1824                  */
1825                 *last_var = NULL;
1826                 *last_cat = newcat;
1827                 if (newcat) {
1828                         ast_category_append(cfg, newcat);
1829                 }
1830         } else if (cur[0] == '#') { /* A directive - #include or #exec */
1831                 char *cur2;
1832                 char real_inclusion_name[256];
1833                 int do_include = 0;     /* otherwise, it is exec */
1834                 int try_include = 0;
1835
1836                 cur++;
1837                 c = cur;
1838                 while (*c && (*c > 32)) {
1839                         c++;
1840                 }
1841
1842                 if (*c) {
1843                         *c = '\0';
1844                         /* Find real argument */
1845                         c = ast_strip(c + 1);
1846                         if (!(*c)) {
1847                                 c = NULL;
1848                         }
1849                 } else {
1850                         c = NULL;
1851                 }
1852                 if (!strcasecmp(cur, "include")) {
1853                         do_include = 1;
1854                 } else if (!strcasecmp(cur, "tryinclude")) {
1855                         do_include = 1;
1856                         try_include = 1;
1857                 } else if (!strcasecmp(cur, "exec")) {
1858                         if (!ast_opt_exec_includes) {
1859                                 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1860                                 return 0;       /* XXX is this correct ? or we should return -1 ? */
1861                         }
1862                 } else {
1863                         ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1864                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1865                 }
1866
1867                 if (c == NULL) {
1868                         ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
1869                                         do_include ? "include / tryinclude" : "exec",
1870                                         do_include ? "filename" : "/path/to/executable",
1871                                         lineno,
1872                                         configfile);
1873                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1874                 }
1875
1876                 cur = c;
1877                 /* Strip off leading and trailing "'s and <>'s */
1878                 /* Dequote */
1879                 if ((*c == '"') || (*c == '<')) {
1880                         char quote_char = *c;
1881                         if (quote_char == '<') {
1882                                 quote_char = '>';
1883                         }
1884
1885                         if (*(c + strlen(c) - 1) == quote_char) {
1886                                 cur++;
1887                                 *(c + strlen(c) - 1) = '\0';
1888                         }
1889                 }
1890                 cur2 = cur;
1891
1892                 /* #exec </path/to/executable>
1893                    We create a tmp file, then we #include it, then we delete it. */
1894                 if (!do_include) {
1895                         struct timeval now = ast_tvnow();
1896                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1897                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1898                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1899                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1900                         ast_safe_system(cmd);
1901                         cur = exec_file;
1902                 } else {
1903                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1904                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1905                         exec_file[0] = '\0';
1906                 }
1907                 /* A #include */
1908                 /* record this inclusion */
1909                 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1910
1911                 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1912                 if (!ast_strlen_zero(exec_file))
1913                         unlink(exec_file);
1914                 if (!do_include && !try_include) {
1915                         ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1916                         return -1;
1917                 }
1918                 /* XXX otherwise what ? the default return is 0 anyways */
1919
1920         } else {
1921                 /* Just a line (variable = value) */
1922                 int object = 0;
1923                 int is_escaped;
1924
1925                 if (!(*cat)) {
1926                         ast_log(LOG_WARNING,
1927                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
1928                         return -1;
1929                 }
1930
1931                 is_escaped = cur[0] == '\\';
1932                 if (is_escaped) {
1933                         /* First character is escaped. */
1934                         ++cur;
1935                         if (cur[0] < 33) {
1936                                 ast_log(LOG_ERROR, "Invalid escape in line %d of %s\n", lineno, configfile);
1937                                 return -1;
1938                         }
1939                 }
1940                 c = strchr(cur + is_escaped, '=');
1941
1942                 if (c && c > cur + is_escaped && (*(c - 1) == '+')) {
1943                         struct ast_variable *var, *replace = NULL;
1944                         struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1945
1946                         if (!str || !*str) {
1947                                 return -1;
1948                         }
1949
1950                         *(c - 1) = '\0';
1951                         c++;
1952                         cur = ast_strip(cur);
1953
1954                         /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1955                         for (var = ast_category_first(*cat); var; var = var->next) {
1956                                 if (!strcmp(var->name, cur)) {
1957                                         replace = var;
1958                                 }
1959                         }
1960
1961                         if (!replace) {
1962                                 /* Nothing to replace; just set a variable normally. */
1963                                 goto set_new_variable;
1964                         }
1965
1966                         ast_str_set(str, 0, "%s", replace->value);
1967                         ast_str_append(str, 0, "%s", c);
1968                         ast_str_trim_blanks(*str);
1969                         ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1970                 } else if (c) {
1971                         *c = 0;
1972                         c++;
1973                         /* Ignore > in => */
1974                         if (*c== '>') {
1975                                 object = 1;
1976                                 c++;
1977                         }
1978                         cur = ast_strip(cur);
1979 set_new_variable:
1980                         if (ast_strlen_zero(cur)) {
1981                                 ast_log(LOG_WARNING, "No variable name in line %d of %s\n", lineno, configfile);
1982                         } else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1983                                 v->lineno = lineno;
1984                                 v->object = object;
1985                                 *last_cat = NULL;
1986                                 *last_var = v;
1987                                 /* Put and reset comments */
1988                                 v->blanklines = 0;
1989                                 ast_variable_append(*cat, v);
1990                                 /* add comments */
1991                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1992                                         v->precomments = ALLOC_COMMENT(comment_buffer);
1993                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1994                                         v->sameline = ALLOC_COMMENT(lline_buffer);
1995                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1996                                         CB_RESET(comment_buffer, lline_buffer);
1997
1998                         } else {
1999                                 return -1;
2000                         }
2001                 } else {
2002                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
2003                 }
2004         }
2005         return 0;
2006 }
2007
2008 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)
2009 {
2010         char fn[256];
2011 #if defined(LOW_MEMORY)
2012         char buf[512];
2013 #else
2014         char buf[8192];
2015 #endif
2016         char *new_buf, *comment_p, *process_buf;
2017         FILE *f;
2018         int lineno=0;
2019         int comment = 0, nest[MAX_NESTED_COMMENTS];
2020         struct ast_category *cat = NULL;
2021         int count = 0;
2022         struct stat statbuf;
2023         struct cache_file_mtime *cfmtime = NULL;
2024         struct cache_file_include *cfinclude;
2025         struct ast_variable *last_var = NULL;
2026         struct ast_category *last_cat = NULL;
2027         /*! Growable string buffer */
2028         struct ast_str *comment_buffer = NULL;  /*!< this will be a comment collector.*/
2029         struct ast_str *lline_buffer = NULL;    /*!< A buffer for stuff behind the ; */
2030 #ifdef AST_INCLUDE_GLOB
2031         int glob_ret;
2032         glob_t globbuf;
2033 #endif
2034
2035         if (cfg) {
2036                 cat = ast_config_get_current_category(cfg);
2037         }
2038
2039         if (filename[0] == '/') {
2040                 ast_copy_string(fn, filename, sizeof(fn));
2041         } else {
2042                 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
2043         }
2044
2045         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2046                 comment_buffer = ast_str_create(CB_SIZE);
2047                 if (comment_buffer) {
2048                         lline_buffer = ast_str_create(CB_SIZE);
2049                 }
2050                 if (!lline_buffer) {
2051                         ast_free(comment_buffer);
2052                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
2053                         return NULL;
2054                 }
2055         }
2056 #ifdef AST_INCLUDE_GLOB
2057         globbuf.gl_offs = 0;    /* initialize it to silence gcc */
2058         glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
2059         if (glob_ret == GLOB_NOSPACE) {
2060                 ast_log(LOG_WARNING,
2061                         "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
2062         } else if (glob_ret  == GLOB_ABORTED) {
2063                 ast_log(LOG_WARNING,
2064                         "Glob Expansion of pattern '%s' failed: Read error\n", fn);
2065         } else {
2066                 /* loop over expanded files */
2067                 int i;
2068
2069                 if (!cfg && (globbuf.gl_pathc != 1 || strcmp(fn, globbuf.gl_pathv[0]))) {
2070                         /*
2071                          * We just want a file changed answer and since we cannot
2072                          * tell if a file was deleted with wildcard matching we will
2073                          * assume that something has always changed.  Also without
2074                          * a lot of refactoring we couldn't check more than one file
2075                          * for changes in the glob loop anyway.
2076                          */
2077                         globfree(&globbuf);
2078                         ast_free(comment_buffer);
2079                         ast_free(lline_buffer);
2080                         return NULL;
2081                 }
2082                 for (i=0; i<globbuf.gl_pathc; i++) {
2083                         ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
2084 #endif
2085                         /*
2086                          * The following is not a loop, but just a convenient way to define a block
2087                          * (using do { } while(0) ), and be able to exit from it with 'continue'
2088                          * or 'break' in case of errors. Nice trick.
2089                          */
2090                         do {
2091                                 if (stat(fn, &statbuf)) {
2092                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2093                                                 config_cache_remove(fn, who_asked);
2094                                         }
2095                                         continue;
2096                                 }
2097
2098                                 if (!S_ISREG(statbuf.st_mode)) {
2099                                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
2100                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2101                                                 config_cache_remove(fn, who_asked);
2102                                         }
2103                                         continue;
2104                                 }
2105
2106                                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2107                                         /* Find our cached entry for this configuration file */
2108                                         AST_LIST_LOCK(&cfmtime_head);
2109                                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2110                                                 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked)) {
2111                                                         break;
2112                                                 }
2113                                         }
2114                                         if (!cfmtime) {
2115                                                 cfmtime = cfmtime_new(fn, who_asked);
2116                                                 if (!cfmtime) {
2117                                                         AST_LIST_UNLOCK(&cfmtime_head);
2118                                                         continue;
2119                                                 }
2120                                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
2121                                                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
2122                                         }
2123                                 }
2124
2125                                 if (cfmtime
2126                                         && !cfmtime->has_exec
2127                                         && !cfmstat_cmp(cfmtime, &statbuf)
2128                                         && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
2129                                         int unchanged = 1;
2130
2131                                         /* File is unchanged, what about the (cached) includes (if any)? */
2132                                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
2133                                                 if (!config_text_file_load(NULL, NULL, cfinclude->include,
2134                                                         NULL, flags, "", who_asked)) {
2135                                                         /* One change is enough to short-circuit and reload the whole shebang */
2136                                                         unchanged = 0;
2137                                                         break;
2138                                                 }
2139                                         }
2140
2141                                         if (unchanged) {
2142                                                 AST_LIST_UNLOCK(&cfmtime_head);
2143 #ifdef AST_INCLUDE_GLOB
2144                                                 globfree(&globbuf);
2145 #endif
2146                                                 ast_free(comment_buffer);
2147                                                 ast_free(lline_buffer);
2148                                                 return CONFIG_STATUS_FILEUNCHANGED;
2149                                         }
2150                                 }
2151
2152                                 /* If cfg is NULL, then we just want a file changed answer. */
2153                                 if (cfg == NULL) {
2154                                         if (cfmtime) {
2155                                                 AST_LIST_UNLOCK(&cfmtime_head);
2156                                         }
2157                                         continue;
2158                                 }
2159
2160                                 if (cfmtime) {
2161                                         /* Forget about what we thought we knew about this file's includes. */
2162                                         cfmtime->has_exec = 0;
2163                                         config_cache_flush_includes(cfmtime);
2164
2165                                         cfmstat_save(cfmtime, &statbuf);
2166                                         AST_LIST_UNLOCK(&cfmtime_head);
2167                                 }
2168
2169                                 if (!(f = fopen(fn, "r"))) {
2170                                         ast_debug(1, "No file to parse: %s\n", fn);
2171                                         ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
2172                                         continue;
2173                                 }
2174                                 count++;
2175                                 /* If we get to this point, then we're loading regardless */
2176                                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
2177                                 ast_debug(1, "Parsing %s\n", fn);
2178                                 ast_verb(2, "Parsing '%s': Found\n", fn);
2179                                 while (!feof(f)) {
2180                                         lineno++;
2181                                         if (fgets(buf, sizeof(buf), f)) {
2182                                                 /* Skip lines that are too long */
2183                                                 if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 1] != '\n') {
2184                                                         ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
2185                                                         while (fgets(buf, sizeof(buf), f)) {
2186                                                                 if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 1] == '\n') {
2187                                                                         break;
2188                                                                 }
2189                                                         }
2190                                                         continue;
2191                                                 }
2192
2193                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)
2194                                                         && lline_buffer
2195                                                         && ast_str_strlen(lline_buffer)) {
2196                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2197                                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
2198                                                 }
2199
2200                                                 new_buf = buf;
2201                                                 if (comment) {
2202                                                         process_buf = NULL;
2203                                                 } else {
2204                                                         process_buf = buf;
2205                                                 }
2206
2207                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)
2208                                                         && comment_buffer
2209                                                         && ast_str_strlen(comment_buffer)
2210                                                         && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
2211                                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
2212                                                         CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
2213                                                         continue; /* go get a new line, then */
2214                                                 }
2215
2216                                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
2217                                                         if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
2218                                                                 /* Escaped semicolons aren't comments. */
2219                                                                 new_buf = comment_p;
2220                                                                 /* write over the \ and bring the null terminator with us */
2221                                                                 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
2222                                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
2223                                                                 /* Meta-Comment start detected ";--" */
2224                                                                 if (comment < MAX_NESTED_COMMENTS) {
2225                                                                         *comment_p = '\0';
2226                                                                         new_buf = comment_p + 3;
2227                                                                         comment++;
2228                                                                         nest[comment-1] = lineno;
2229                                                                 } else {
2230                                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
2231                                                                 }
2232                                                         } else if ((comment_p >= new_buf + 2) &&
2233                                                                    (*(comment_p - 1) == COMMENT_TAG) &&
2234                                                                    (*(comment_p - 2) == COMMENT_TAG)) {
2235                                                                 /* Meta-Comment end detected "--;" */
2236                                                                 comment--;
2237                                                                 new_buf = comment_p + 1;
2238                                                                 if (!comment) {
2239                                                                         /* Back to non-comment now */
2240                                                                         if (process_buf) {
2241                                                                                 /* Actually have to move what's left over the top, then continue */
2242                                                                                 char *oldptr;
2243
2244                                                                                 oldptr = process_buf + strlen(process_buf);
2245                                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2246                                                                                         CB_ADD(&comment_buffer, ";");
2247                                                                                         CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
2248                                                                                 }
2249
2250                                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
2251                                                                                 new_buf = oldptr;
2252                                                                         } else {
2253                                                                                 process_buf = new_buf;
2254                                                                         }
2255                                                                 }
2256                                                         } else {
2257                                                                 if (!comment) {
2258                                                                         /* If ; is found, and we are not nested in a comment,
2259                                                                            we immediately stop all comment processing */
2260                                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2261                                                                                 CB_ADD(&lline_buffer, comment_p);
2262                                                                         }
2263                                                                         *comment_p = '\0';
2264                                                                         new_buf = comment_p;
2265                                                                 } else {
2266                                                                         new_buf = comment_p + 1;
2267                                                                 }
2268                                                         }
2269                                                 }
2270                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
2271                                                         CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
2272                                                 }
2273
2274                                                 if (process_buf) {
2275                                                         char *buffer = ast_strip(process_buf);
2276
2277                                                         if (!ast_strlen_zero(buffer)) {
2278                                                                 if (process_text_line(cfg, &cat, buffer, lineno, fn,
2279                                                                         flags, comment_buffer, lline_buffer,
2280                                                                         suggested_include_file, &last_cat, &last_var,
2281                                                                         who_asked)) {
2282                                                                         cfg = CONFIG_STATUS_FILEINVALID;
2283                                                                         break;
2284                                                                 }
2285                                                         }
2286                                                 }
2287                                         }
2288                                 }
2289                                 /* end of file-- anything in a comment buffer? */
2290                                 if (last_cat) {
2291                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2292                                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
2293                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2294                                                         ast_str_reset(lline_buffer); /* erase the lline buffer */
2295                                                 }
2296                                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
2297                                         }
2298                                 } else if (last_var) {
2299                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2300                                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
2301                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2302                                                         ast_str_reset(lline_buffer); /* erase the lline buffer */
2303                                                 }
2304                                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
2305                                         }
2306                                 } else {
2307                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2308                                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
2309                                         }
2310                                 }
2311                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2312                                         CB_RESET(comment_buffer, lline_buffer);
2313                                 }
2314
2315                                 fclose(f);
2316                         } while (0);
2317                         if (comment) {
2318                                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
2319                         }
2320 #ifdef AST_INCLUDE_GLOB
2321                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
2322                                 break;
2323                         }
2324                 }
2325                 globfree(&globbuf);
2326         }
2327 #endif
2328
2329         ast_free(comment_buffer);
2330         ast_free(lline_buffer);
2331
2332         if (count == 0) {
2333                 return NULL;
2334         }
2335
2336         return cfg;
2337 }
2338
2339
2340 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
2341    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
2342    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
2343    be shocked and mystified as to why things are not showing up in the files!
2344
2345    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
2346    and line number are stored for each include, plus the name of the file included, so that these statements may be
2347    included in the output files on a file_save operation.
2348
2349    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
2350    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
2351    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
2352    and a header gets added.
2353
2354    vars and category heads are output in the order they are stored in the config file. So, if the software
2355    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
2356    file/lineno data probably won't get changed.
2357
2358 */
2359
2360 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
2361 {
2362         char date[256]="";
2363         time_t t;
2364
2365         time(&t);
2366         ast_copy_string(date, ctime(&t), sizeof(date));
2367
2368         fprintf(f1, ";!\n");
2369         fprintf(f1, ";! Automatically generated configuration file\n");
2370         if (strcmp(configfile, fn))
2371                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
2372         else
2373                 fprintf(f1, ";! Filename: %s\n", configfile);
2374         fprintf(f1, ";! Generator: %s\n", generator);
2375         fprintf(f1, ";! Creation Date: %s", date);
2376         fprintf(f1, ";!\n");
2377 }
2378
2379 static void inclfile_destroy(void *obj)
2380 {
2381         const struct inclfile *o = obj;
2382
2383         ast_free(o->fname);
2384 }
2385
2386 static void make_fn(char *fn, size_t fn_size, const char *file, const char *configfile)
2387 {
2388         if (ast_strlen_zero(file)) {
2389                 if (configfile[0] == '/') {
2390                         ast_copy_string(fn, configfile, fn_size);
2391                 } else {
2392                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
2393                 }
2394         } else if (file[0] == '/') {
2395                 ast_copy_string(fn, file, fn_size);
2396         } else {
2397                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
2398         }
2399 }
2400
2401 static struct inclfile *set_fn(char *fn, size_t fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
2402 {
2403         struct inclfile lookup;
2404         struct inclfile *fi;
2405
2406         make_fn(fn, fn_size, file, configfile);
2407         lookup.fname = fn;
2408         fi = ao2_find(fileset, &lookup, OBJ_POINTER);
2409         if (fi) {
2410                 /* Found existing include file scratch pad. */
2411                 return fi;
2412         }
2413
2414         /* set up a file scratch pad */
2415         fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
2416         if (!fi) {
2417                 /* Scratch pad creation failed. */
2418                 return NULL;
2419         }
2420         fi->fname = ast_strdup(fn);
2421         if (!fi->fname) {
2422                 /* Scratch pad creation failed. */
2423                 ao2_ref(fi, -1);
2424                 return NULL;
2425         }
2426         fi->lineno = 1;
2427
2428         ao2_link(fileset, fi);
2429
2430         return fi;
2431 }
2432
2433 static int count_linefeeds(char *str)
2434 {
2435         int count = 0;
2436
2437         while (*str) {
2438                 if (*str =='\n')
2439                         count++;
2440                 str++;
2441         }
2442         return count;
2443 }
2444
2445 static int count_linefeeds_in_comments(struct ast_comment *x)
2446 {
2447         int count = 0;
2448
2449         while (x) {
2450                 count += count_linefeeds(x->cmt);
2451                 x = x->next;
2452         }
2453         return count;
2454 }
2455
2456 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
2457 {
2458         int precomment_lines;
2459         int i;
2460
2461         if (!fi) {
2462                 /* No file scratch pad object so insert no blank lines. */
2463                 return;
2464         }
2465
2466         precomment_lines = count_linefeeds_in_comments(precomments);
2467
2468         /* I don't have to worry about those ;! comments, they are
2469            stored in the precomments, but not printed back out.
2470            I did have to make sure that comments following
2471            the ;! header comments were not also deleted in the process */
2472         if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
2473                 return;
2474         } else if (lineno == 0) {
2475                 /* Line replacements also mess things up */
2476                 return;
2477         } else if (lineno - precomment_lines - fi->lineno < 5) {
2478                 /* Only insert less than 5 blank lines; if anything more occurs,
2479                  * it's probably due to context deletion. */
2480                 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
2481                         fprintf(fp, "\n");
2482                 }
2483         } else {
2484                 /* Deletion occurred - insert a single blank line, for separation of
2485                  * contexts. */
2486                 fprintf(fp, "\n");
2487         }
2488
2489         fi->lineno = lineno + 1; /* Advance the file lineno */
2490 }
2491
2492 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2493 {
2494         return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
2495 }
2496
2497 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2498 {
2499         return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
2500 }
2501
2502 int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags)
2503 {
2504         FILE *f;
2505         char fn[PATH_MAX];
2506         struct ast_variable *var;
2507         struct ast_category *cat;
2508         struct ast_comment *cmt;
2509         struct ast_config_include *incl;
2510         int blanklines = 0;
2511         struct ao2_container *fileset;
2512         struct inclfile *fi;
2513
2514         fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
2515         if (!fileset) {
2516                 /* Container creation failed. */
2517                 return -1;
2518         }
2519
2520         /* Check all the files for write access before attempting to modify any of them */
2521         for (incl = cfg->includes; incl; incl = incl->next) {
2522                 /* reset all the output flags in case this isn't our first time saving this data */
2523                 incl->output = 0;
2524                 /* now make sure we have write access */
2525                 if (!incl->exec) {
2526                         make_fn(fn, sizeof(fn), incl->included_file, configfile);
2527                         if (access(fn, R_OK | W_OK)) {
2528                                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2529                                 return -1;
2530                         }
2531                 }
2532         }
2533
2534         /* now make sure we have write access to the main config file */
2535         make_fn(fn, sizeof(fn), 0, configfile);
2536         if (access(fn, R_OK | W_OK)) {
2537                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2538                 return -1;
2539         }
2540
2541         /* Now that we know we have write access to all files, it's safe to start truncating them */
2542
2543         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
2544            are all truncated to zero bytes and have that nice header*/
2545         for (incl = cfg->includes; incl; incl = incl->next) {
2546                 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*/
2547                         /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
2548                         fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
2549                         f = fopen(fn, "w");
2550                         if (f) {
2551                                 gen_header(f, configfile, fn, generator);
2552                                 fclose(f); /* this should zero out the file */
2553                         } else {
2554                                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2555                         }
2556                         if (fi) {
2557                                 ao2_ref(fi, -1);
2558                         }
2559                 }
2560         }
2561
2562         /* just set fn to absolute ver of configfile */
2563         fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
2564         if (
2565 #ifdef __CYGWIN__
2566                 (f = fopen(fn, "w+"))
2567 #else
2568                 (f = fopen(fn, "w"))
2569 #endif
2570                 ) {
2571                 ast_verb(2, "Saving '%s'\n", fn);
2572                 gen_header(f, configfile, fn, generator);
2573                 cat = cfg->root;
2574                 fclose(f);
2575                 if (fi) {
2576                         ao2_ref(fi, -1);
2577                 }
2578
2579                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
2580                 /* since each var, cat, and associated comments can come from any file, we have to be
2581                    mobile, and open each file, print, and close it on an entry-by-entry basis */
2582
2583                 while (cat) {
2584                         fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
2585                         f = fopen(fn, "a");
2586                         if (!f) {
2587                                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2588                                 if (fi) {
2589                                         ao2_ref(fi, -1);
2590                                 }
2591                                 ao2_ref(fileset, -1);
2592                                 return -1;
2593                         }
2594
2595                         /* dump any includes that happen before this category header */
2596                         for (incl=cfg->includes; incl; incl = incl->next) {
2597                                 if (strcmp(incl->include_location_file, cat->file) == 0){
2598                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
2599                                                 if (incl->exec)
2600                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2601                                                 else
2602                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
2603                                                 incl->output = 1;
2604                                         }
2605                                 }
2606                         }
2607
2608                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
2609                         /* Dump section with any appropriate comment */
2610                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
2611                                 char *cmtp = cmt->cmt;
2612                                 while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
2613                                         char *cmtp2 = strchr(cmtp+1, '\n');
2614                                         if (cmtp2)
2615                                                 cmtp = cmtp2+1;
2616                                         else cmtp = 0;
2617                                 }
2618                                 if (cmtp)
2619                                         fprintf(f,"%s", cmtp);
2620                         }
2621                         fprintf(f, "[%s]", cat->name);
2622                         if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
2623                                 fprintf(f, "(");
2624                                 if (cat->ignored) {
2625                                         fprintf(f, "!");
2626                                 }
2627                                 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
2628                                         fprintf(f, ",");
2629                                 }
2630                                 if (!AST_LIST_EMPTY(&cat->template_instances)) {
2631                                         struct ast_category_template_instance *x;
2632                                         AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2633                                                 fprintf(f,"%s",x->name);
2634                                                 if (x != AST_LIST_LAST(&cat->template_instances))
2635                                                         fprintf(f,",");
2636                                         }
2637                                 }
2638                                 fprintf(f, ")");
2639                         }
2640                         for(cmt = cat->sameline; cmt; cmt=cmt->next)
2641                         {
2642                                 fprintf(f,"%s", cmt->cmt);
2643                         }
2644                         if (!cat->sameline)
2645                                 fprintf(f,"\n");
2646                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
2647                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2648                                         fprintf(f,"%s", cmt->cmt);
2649                         }
2650                         fclose(f);
2651                         if (fi) {
2652                                 ao2_ref(fi, -1);
2653                         }
2654
2655                         var = cat->root;
2656                         while (var) {
2657                                 struct ast_category_template_instance *x;
2658                                 int found = 0;
2659
2660                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2661                                         struct ast_variable *v;
2662                                         for (v = x->inst->root; v; v = v->next) {
2663
2664                                                 if (flags & CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT) {
2665                                                         if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2666                                                                 found = 1;
2667                                                                 break;
2668                                                         }
2669                                                 } else {
2670                                                         if (var->inherited) {
2671                                                                 found = 1;
2672                                                                 break;
2673                                                         } else {
2674                                                                 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2675                                                                         found = 1;
2676                                                                         break;
2677                                                                 }
2678                                                         }
2679                                                 }
2680                                         }
2681                                         if (found) {
2682                                                 break;
2683                                         }
2684                                 }
2685                                 if (found) {
2686                                         var = var->next;
2687                                         continue;
2688                                 }
2689                                 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
2690                                 f = fopen(fn, "a");
2691                                 if (!f) {
2692                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
2693                                         ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2694                                         if (fi) {
2695                                                 ao2_ref(fi, -1);
2696                                         }
2697                                         ao2_ref(fileset, -1);
2698                                         return -1;
2699                                 }
2700
2701                                 /* dump any includes that happen before this category header */
2702                                 for (incl=cfg->includes; incl; incl = incl->next) {
2703                                         if (strcmp(incl->include_location_file, var->file) == 0){
2704                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
2705                                                         if (incl->exec)
2706                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2707                                                         else
2708                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
2709                                                         incl->output = 1;
2710                                                 }
2711                                         }
2712                                 }
2713
2714                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
2715                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
2716                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2717                                                 fprintf(f,"%s", cmt->cmt);
2718                                 }
2719
2720                                 { /* Block for 'escaped' scope */
2721                                         int escaped_len = 2 * strlen(var->value) + 1;
2722                                         char escaped[escaped_len];
2723
2724                                         ast_escape_semicolons(var->value, escaped, escaped_len);
2725
2726                                         if (var->sameline) {
2727                                                 fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="),
2728                                                         escaped, var->sameline->cmt);
2729                                         } else {
2730                                                 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="),
2731                                                         escaped);
2732                                         }
2733                                 }
2734
2735                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
2736                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2737                                                 fprintf(f,"%s", cmt->cmt);
2738                                 }
2739                                 if (var->blanklines) {
2740                                         blanklines = var->blanklines;
2741                                         while (blanklines--)
2742                                                 fprintf(f, "\n");
2743                                 }
2744
2745                                 fclose(f);
2746                                 if (fi) {
2747                                         ao2_ref(fi, -1);
2748                                 }
2749
2750                                 var = var->next;
2751                         }
2752                         cat = cat->next;
2753                 }
2754                 if (!option_debug) {
2755                         ast_verb(2, "Saving '%s': saved\n", fn);
2756                 }
2757         } else {
2758                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2759                 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
2760                 if (fi) {
2761                         ao2_ref(fi, -1);
2762                 }
2763                 ao2_ref(fileset, -1);
2764                 return -1;
2765         }
2766
2767         /* Now, for files with trailing #include/#exec statements,
2768            we have to make sure every entry is output */
2769         for (incl=cfg->includes; incl; incl = incl->next) {
2770                 if (!incl->output) {
2771                         /* open the respective file */
2772                         fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
2773                         f = fopen(fn, "a");
2774                         if (!f) {
2775                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2776                                 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2777                                 if (fi) {
2778                                         ao2_ref(fi, -1);
2779                                 }
2780                                 ao2_ref(fileset, -1);
2781                                 return -1;
2782                         }
2783
2784                         /* output the respective include */
2785                         if (incl->exec)
2786                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2787                         else
2788                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
2789                         fclose(f);
2790                         incl->output = 1;
2791                         if (fi) {
2792                                 ao2_ref(fi, -1);
2793                         }
2794                 }
2795         }
2796         ao2_ref(fileset, -1); /* this should destroy the hash container */
2797
2798         /* pass new configuration to any config hooks */
2799         config_hook_exec(configfile, generator, cfg);
2800
2801         return 0;
2802 }
2803
2804 static void clear_config_maps(void)
2805 {
2806         struct ast_config_map *map;
2807
2808         SCOPED_MUTEX(lock, &config_lock);
2809
2810         while (config_maps) {
2811                 map = config_maps;
2812                 config_maps = config_maps->next;
2813                 ast_free(map);
2814         }
2815 }
2816
2817 #ifdef TEST_FRAMEWORK
2818 int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2819 #else
2820 static int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2821 #endif
2822 {
2823         struct ast_config_map *map;
2824         char *dst;
2825         int length;
2826
2827         length = sizeof(*map);
2828         length += strlen(name) + 1;
2829         length += strlen(driver) + 1;
2830         length += strlen(database) + 1;
2831         if (table)
2832                 length += strlen(table) + 1;
2833
2834         if (!(map = ast_calloc(1, length)))
2835                 return -1;
2836
2837         dst = map->stuff;       /* writable space starts here */
2838         map->name = strcpy(dst, name);
2839         dst += strlen(dst) + 1;
2840         map->driver = strcpy(dst, driver);
2841         dst += strlen(dst) + 1;
2842         map->database = strcpy(dst, database);
2843         if (table) {
2844                 dst += strlen(dst) + 1;
2845                 map->table = strcpy(dst, table);
2846         }
2847         map->priority = priority;
2848         map->next = config_maps;
2849         config_maps = map;
2850
2851         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
2852
2853         return 0;
2854 }
2855
2856 int read_config_maps(void)
2857 {
2858         struct ast_config *config, *configtmp;
2859         struct ast_variable *v;
2860         char *driver, *table, *database, *textpri, *stringp, *tmp;
2861         struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
2862         int pri;
2863
2864         clear_config_maps();
2865
2866         configtmp = ast_config_new();
2867         if (!configtmp) {
2868                 ast_log(LOG_ERROR, "Unable to allocate memory for new config\n");
2869                 return -1;
2870         }
2871         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
2872         if (config == CONFIG_STATUS_FILEINVALID) {
2873                 return -1;
2874         } else if (!config) {
2875                 ast_config_destroy(configtmp);
2876                 return 0;
2877         }
2878
2879         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
2880                 char buf[512];
2881                 ast_copy_string(buf, v->value, sizeof(buf));
2882                 stringp = buf;
2883                 driver = strsep(&stringp, ",");
2884
2885                 if ((tmp = strchr(stringp, '\"')))
2886                         stringp = tmp;
2887
2888                 /* check if the database text starts with a double quote */
2889                 if (*stringp == '"') {
2890                         stringp++;
2891                         database = strsep(&stringp, "\"");
2892                         strsep(&stringp, ",");
2893                 } else {
2894                         /* apparently this text has no quotes */
2895                         database = strsep(&stringp, ",");
2896                 }
2897
2898                 table = strsep(&stringp, ",");
2899                 textpri = strsep(&stringp, ",");
2900                 if (!textpri || !(pri = atoi(textpri))) {
2901                         pri = 1;
2902                 }
2903
2904                 if (!strcmp(v->name, extconfig_conf)) {
2905                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
2906                         continue;
2907                 }
2908
2909                 if (!strcmp(v->name, "asterisk.conf")) {
2910                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
2911                         continue;
2912                 }
2913
2914                 if (!strcmp(v->name, "logger.conf")) {
2915                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
2916                         continue;
2917                 }
2918
2919                 if (!driver || !database)
2920                         continue;
2921                 if (!strcasecmp(v->name, "sipfriends")) {
2922                         ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
2923                         ast_realtime_append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
2924                 } else if (!strcasecmp(v->name, "iaxfriends")) {
2925                         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");
2926                         ast_realtime_append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
2927                         ast_realtime_append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
2928                 } else
2929                         ast_realtime_append_mapping(v->name, driver, database, table, pri);
2930         }
2931
2932         ast_config_destroy(config);
2933         return 0;
2934 }
2935
2936 int ast_config_engine_register(struct ast_config_engine *new)
2937 {
2938         struct ast_config_engine *ptr;
2939
2940         SCOPED_MUTEX(lock, &config_lock);
2941
2942         if (!config_engine_list) {
2943                 config_engine_list = new;
2944         } else {
2945                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
2946                 ptr->next = new;
2947         }
2948
2949         return 1;
2950 }
2951
2952 int ast_config_engine_deregister(struct ast_config_engine *del)
2953 {
2954         struct ast_config_engine *ptr, *last=NULL;
2955
2956         SCOPED_MUTEX(lock, &config_lock);
2957
2958         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
2959                 if (ptr == del) {
2960                         if (last)
2961                                 last->next = ptr->next;
2962                         else
2963                                 config_engine_list = ptr->next;
2964                         break;
2965                 }
2966                 last = ptr;
2967         }
2968
2969         return 0;
2970 }
2971
2972 int ast_realtime_is_mapping_defined(const char *family)
2973 {
2974         struct ast_config_map *map;
2975         SCOPED_MUTEX(lock, &config_lock);
2976
2977         for (map = config_maps; map; map = map->next) {
2978                 if (!strcasecmp(family, map->name)) {
2979                         return 1;
2980                 }
2981         }
2982         ast_debug(5, "Failed to find a realtime mapping for %s\n", family);
2983
2984         return 0;
2985 }
2986
2987 /*! \brief Find realtime engine for realtime family */
2988 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
2989 {
2990         struct ast_config_engine *eng, *ret = NULL;
2991         struct ast_config_map *map;
2992
2993         SCOPED_MUTEX(lock, &config_lock);
2994
2995         for (map = config_maps; map; map = map->next) {
2996                 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
2997                         if (database)
2998                                 ast_copy_string(database, map->database, dbsiz);
2999                         if (table)
3000                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
3001                         break;
3002                 }
3003         }
3004
3005         /* Check if the required driver (engine) exist */
3006         if (map) {
3007                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
3008                         if (!strcasecmp(eng->name, map->driver))
3009                                 ret = eng;
3010                 }
3011         }
3012
3013         /* if we found a mapping, but the engine is not available, then issue a warning */
3014         if (map && !ret)
3015                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
3016
3017         return ret;
3018 }
3019
3020 static struct ast_config_engine text_file_engine = {
3021         .name = "text",
3022         .load_func = config_text_file_load,
3023 };
3024
3025 struct ast_config *ast_config_copy(const struct ast_config *old)
3026 {
3027         struct ast_config *new_config = ast_config_new();
3028         struct ast_category *cat_iter;
3029
3030         if (!new_config) {
3031                 return NULL;
3032         }
3033
3034         for (cat_iter = old->root; cat_iter; cat_iter = cat_iter->next) {
3035                 struct ast_category *new_cat =
3036                         ast_category_new(cat_iter->name, cat_iter->file, cat_iter->lineno);
3037                 if (!new_cat) {
3038                         goto fail;
3039                 }
3040                 ast_category_append(new_config, new_cat);
3041                 if (cat_iter->root) {
3042                         new_cat->root = ast_variables_dup(cat_iter->root);
3043                         if (!new_cat->root) {
3044                                 goto fail;
3045                         }
3046                         new_cat->last = cat_iter->last;
3047                 }
3048         }
3049
3050         return new_config;
3051
3052 fail:
3053         ast_config_destroy(new_config);
3054         return NULL;
3055 }
3056
3057
3058 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)
3059 {
3060         char db[256];
3061         char table[256];
3062         struct ast_config_engine *loader = &text_file_engine;
3063         struct ast_config *result;
3064
3065         /* The config file itself bumps include_level by 1 */
3066         if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
3067                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
3068                 return NULL;
3069         }
3070
3071         cfg->include_level++;
3072
3073         if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
3074                 struct ast_config_engine *eng;
3075
3076                 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
3077
3078
3079                 if (eng && eng->load_func) {
3080                         loader = eng;
3081                 } else {
3082                         eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
3083                         if (eng && eng->load_func)
3084                                 loader = eng;
3085                 }
3086         }
3087
3088         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
3089
3090         if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED) {
3091                 result->include_level--;
3092                 config_hook_exec(filename, who_asked, result);
3093         } else if (result != CONFIG_STATUS_FILEINVALID) {
3094                 cfg->include_level--;
3095         }
3096
3097         return result;
3098 }
3099
3100 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
3101 {
3102         struct ast_config *cfg;
3103         struct ast_config *result;
3104
3105         cfg = ast_config_new();
3106         if (!cfg)
3107                 return NULL;
3108
3109         result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
3110         if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
3111                 ast_config_destroy(cfg);
3112
3113         return result;
3114 }
3115
3116 #define realtime_arguments_to_fields(ap, result) realtime_arguments_to_fields2(ap, 0, result)
3117
3118 /*!
3119  * \internal
3120  * \brief
3121  *
3122  * \param ap list of variable arguments
3123  * \param skip Skip argument pairs for this number of variables
3124  * \param result Address of a variables pointer to store the results
3125  *               May be NULL if no arguments are parsed
3126  *               Will be NULL on failure.
3127  *
3128  * \retval 0 on success or empty ap list
3129  * \retval -1 on failure
3130  */
3131 static int realtime_arguments_to_fields2(va_list ap, int skip, struct ast_variable **result)
3132 {
3133         struct ast_variable *first, *fields = NULL;
3134         const char *newparam;
3135         const char *newval;
3136
3137         /*
3138          * Previously we would do:
3139          *
3140          *     va_start(ap, last);
3141          *     x = realtime_arguments_to_fields(ap);
3142          *     y = realtime_arguments_to_fields(ap);
3143          *     va_end(ap);
3144          *
3145          * While this works on generic amd64 machines (2014), it doesn't on the
3146          * raspberry PI. The va_arg() manpage says:
3147          *
3148          *     If ap is passed to a function that uses va_arg(ap,type) then
3149          *     the value of ap is undefined after the return of that function.
3150          *
3151          * On the raspberry, ap seems to get reset after the call: the contents
3152          * of y would be equal to the contents of x.
3153          *
3154          * So, instead we allow the caller to skip past earlier argument sets
3155          * using the skip parameter:
3156          *
3157          *     va_start(ap, last);
3158          *     if (realtime_arguments_to_fields(ap, &x)) {
3159          *         // FAILURE CONDITIONS
3160          *     }
3161          *     va_end(ap);
3162          *     va_start(ap, last);
3163          *     if (realtime_arguments_to_fields2(ap, 1, &y)) {
3164          *         // FAILURE CONDITIONS
3165          *     }
3166          *     va_end(ap);
3167          */
3168         while (skip--) {
3169                 /* There must be at least one argument. */
3170                 newparam = va_arg(ap, const char *);
3171                 newval = va_arg(ap, const char *);
3172                 while ((newparam = va_arg(ap, const char *))) {
3173                         newval = va_arg(ap, const char *);
3174                 }
3175         }
3176
3177         /* Load up the first vars. */
3178         newparam = va_arg(ap, const char *);
3179         if (!newparam) {
3180                 *result = NULL;
3181                 return 0;
3182         }
3183         newval = va_arg(ap, const char *);
3184
3185         if (!(first = ast_variable_new(newparam, newval, ""))) {
3186                 *result = NULL;
3187                 return -1;
3188         }
3189
3190         while ((newparam = va_arg(ap, const char *))) {
3191                 struct ast_variable *field;
3192
3193                 newval = va_arg(ap, const char *);
3194                 if (!(field = ast_variable_new(newparam, newval, ""))) {
3195                         ast_variables_destroy(fields);
3196                         ast_variables_destroy(first);
3197                         *result = NULL;
3198                         return -1;
3199                 }
3200
3201                 field->next = fields;
3202                 fields = field;
3203         }
3204
3205         first->next = fields;
3206         fields = first;
3207
3208         *result = fields;
3209         return 0;
3210 }
3211
3212 struct ast_variable *ast_load_realtime_all_fields(const char *family, const struct ast_variable *fields)
3213 {
3214         struct ast_config_engine *eng;
3215         char db[256];
3216         char table[256];
3217         struct ast_variable *res=NULL;
3218         int i;
3219
3220         for (i = 1; ; i++) {
3221                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3222                         if (eng->realtime_func && (res = eng->realtime_func(db, table, fields))) {
3223                                 return res;
3224                         }
3225                 } else {
3226                         return NULL;
3227                 }
3228         }
3229
3230         return res;
3231 }
3232
3233 struct ast_variable *ast_load_realtime_all(const char *family, ...)
3234 {
3235         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3236         struct ast_variable *res = NULL;
3237         va_list ap;
3238
3239         va_start(ap, family);
3240         realtime_arguments_to_fields(ap, &fields);
3241         va_end(ap);
3242
3243         if (fields) {
3244                 res = ast_load_realtime_all_fields(family, fields);
3245         }
3246
3247         return res;
3248 }
3249
3250 struct ast_variable *ast_load_realtime_fields(const char *family, const struct ast_variable *fields)
3251 {
3252         struct ast_variable *res;
3253         struct ast_variable *cur;
3254         struct ast_variable **prev;
3255
3256         res = ast_load_realtime_all_fields(family, fields);
3257
3258         /* Filter the list. */
3259         prev = &res;
3260         cur = res;
3261         while (cur) {
3262                 if (ast_strlen_zero(cur->value)) {
3263                         /* Eliminate empty entries */
3264                         struct ast_variable *next;
3265
3266                         next = cur->next;
3267                         *prev = next;
3268                         ast_variable_destroy(cur);
3269                         cur = next;
3270                 } else {
3271                         /* Make blank entries empty and keep them. */
3272                         if (cur->value[0] == ' ' && cur->value[1] == '\0') {
3273                                 char *vptr = (char *) cur->value;
3274
3275                                 vptr[0] = '\0';
3276                         }
3277
3278                         prev = &cur->next;
3279                         cur = cur->next;
3280                 }
3281         }
3282         return res;
3283 }
3284
3285 struct ast_variable *ast_load_realtime(const char *family, ...)
3286 {
3287         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3288         int field_res = 0;
3289         va_list ap;
3290
3291         va_start(ap, family);
3292         if (realtime_arguments_to_fields(ap, &fields)) {
3293                 field_res = -1;
3294         }
3295         va_end(ap);
3296
3297         if (field_res) {
3298                 return NULL;
3299         }
3300
3301         if (!fields) {
3302                 return NULL;
3303         }
3304
3305         return ast_load_realtime_fields(family, fields);
3306 }
3307
3308 /*! \brief Check if realtime engine is configured for family */
3309 int ast_check_realtime(const char *family)
3310 {
3311         struct ast_config_engine *eng;
3312         if (!ast_realtime_enabled()) {
3313                 return 0;       /* There are no engines at all so fail early */
3314         }
3315
3316         eng = find_engine(family, 1, NULL, 0, NULL, 0);
3317         if (eng)
3318                 return 1;
3319         return 0;
3320 }
3321
3322 /*! \brief Check if there's any realtime engines loaded */
3323 int ast_realtime_enabled(void)
3324 {
3325         return config_maps ? 1 : 0;
3326 }
3327
3328 int ast_realtime_require_field(const char *family, ...)
3329 {
3330         struct ast_config_engine *eng;
3331         char db[256];
3332         char table[256];
3333         va_list ap;
3334         int res = -1, i;
3335
3336         va_start(ap, family);
3337         for (i = 1; ; i++) {
3338                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3339                         /* If the require succeeds, it returns 0. */
3340                         if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
3341                                 break;
3342                         }
3343                 } else {
3344                         break;
3345                 }
3346         }
3347         va_end(ap);
3348
3349         return res;
3350 }
3351
3352 int ast_unload_realtime(const char *family)
3353 {
3354         struct ast_config_engine *eng;
3355         char db[256];
3356         char table[256];
3357         int res = -1, i;
3358
3359         for (i = 1; ; i++) {
3360                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3361                         if (eng->unload_func) {
3362                                 /* Do this for ALL engines */
3363                                 res = eng->unload_func(db, table);
3364                         }
3365                 } else {
3366                         break;
3367                 }
3368         }
3369         return res;
3370 }
3371
3372 struct ast_config *ast_load_realtime_multientry_fields(const char *family, const struct ast_variable *fields)
3373 {
3374         struct ast_config_engine *eng;
3375         char db[256];
3376         char table[256];
3377         struct ast_config *res = NULL;
3378         int i;
3379
3380         for (i = 1; ; i++) {
3381                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3382                         if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, fields))) {
3383                                 /* If we were returned an empty cfg, destroy it and return NULL */
3384                                 if (!res->root) {
3385                                         ast_config_destroy(res);
3386                                         res = NULL;
3387                                 }
3388                                 break;
3389                         }
3390                 } else {
3391                         break;
3392                 }
3393         }
3394
3395         return res;
3396 }
3397
3398 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
3399 {
3400         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3401         va_list ap;
3402
3403         va_start(ap, family);
3404         realtime_arguments_to_fields(ap, &fields);
3405         va_end(ap);
3406
3407         if (!fields) {
3408                 return NULL;
3409         }
3410
3411         return ast_load_realtime_multientry_fields(family, fields);
3412 }
3413
3414 int ast_update_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
3415 {
3416         struct ast_config_engine *eng;
3417         int res = -1, i;
3418         char db[256];
3419         char table[256];
3420
3421         for (i = 1; ; i++) {
3422                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3423                         /* If the update succeeds, it returns >= 0. */
3424                         if (eng->update_func && ((res = eng->update_func(db, table, keyfield, lookup, fields)) >= 0)) {
3425                                 break;
3426                         }
3427                 } else {
3428                         break;
3429                 }
3430         }
3431
3432         return res;
3433 }
3434
3435 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
3436 {
3437         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3438         va_list ap;
3439
3440         va_start(ap, lookup);
3441         realtime_arguments_to_fields(ap, &fields);
3442         va_end(ap);
3443
3444         if (!fields) {
3445                 return -1;
3446         }
3447
3448         return ast_update_realtime_fields(family, keyfield, lookup, fields);
3449 }
3450
3451 int ast_update2_realtime_fields(const char *family, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
3452 {
3453         struct ast_config_engine *eng;
3454         int res = -1, i;
3455         char db[256];
3456         char table[256];
3457
3458         for (i = 1; ; i++) {
3459                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3460                         if (eng->update2_func && !(res = eng->update2_func(db, table, lookup_fields, update_fields))) {
3461                                 break;
3462                         }
3463                 } else {
3464                         break;
3465                 }
3466         }
3467
3468         return res;
3469 }
3470
3471 int ast_update2_realtime(const char *family, ...)
3472 {
3473         RAII_VAR(struct ast_variable *, lookup_fields, NULL, ast_variables_destroy);
3474         RAII_VAR(struct ast_variable *, update_fields, NULL, ast_variables_destroy);
3475         va_list ap;
3476
3477         va_start(ap, family);
3478         /* XXX: If we wanted to pass no lookup fields (select all), we'd be
3479          * out of luck. realtime_arguments_to_fields expects at least one key
3480          * value pair. */
3481         realtime_arguments_to_fields(ap, &lookup_fields);
3482         va_end(ap);
3483
3484         va_start(ap, family);
3485         realtime_arguments_to_fields2(ap, 1, &update_fields);
3486         va_end(ap);
3487
3488         if (!lookup_fields || !update_fields) {
3489                 return -1;
3490         }
3491
3492         return ast_update2_realtime_fields(family, lookup_fields, update_fields);
3493 }
3494
3495 int ast_store_realtime_fields(const char *family, const struct ast_variable *fields)
3496 {
3497         struct ast_config_engine *eng;
3498         int res = -1, i;
3499         char db[256];
3500      &n