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