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