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