db9182a4698244626fe98c4fd185f5fe862ee892
[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         ast_copy_string(s, str, len);
129         ast_str_append(cb, 0, "%s", str);
130 }
131
132 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
133 {
134         if (cb) {
135                 ast_str_reset(cb);
136         }
137         if (llb) {
138                 ast_str_reset(llb);
139         }
140 }
141
142 static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
143 {
144         struct ast_comment *x = NULL;
145         if (!buffer || !ast_str_strlen(buffer)) {
146                 return NULL;
147         }
148         if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
149                 strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
150         }
151         return x;
152 }
153
154 /* I need to keep track of each config file, and all its inclusions,
155    so that we can track blank lines in each */
156
157 struct inclfile {
158         char *fname;
159         int lineno;
160 };
161
162 static int hash_string(const void *obj, const int flags)
163 {
164         char *str = ((struct inclfile *) obj)->fname;
165         int total;
166
167         for (total = 0; *str; str++) {
168                 unsigned int tmp = total;
169                 total <<= 1; /* multiply by 2 */
170                 total += tmp; /* multiply by 3 */
171                 total <<= 2; /* multiply by 12 */
172                 total += tmp; /* multiply by 13 */
173
174                 total += ((unsigned int) (*str));
175         }
176         if (total < 0) {
177                 total = -total;
178         }
179         return total;
180 }
181
182 static int hashtab_compare_strings(void *a, void *b, int flags)
183 {
184         const struct inclfile *ae = a, *be = b;
185         return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
186 }
187
188 static struct ast_config_map {
189         struct ast_config_map *next;
190         int priority;
191         /*! Stored in stuff[] at struct end. */
192         const char *name;
193         /*! Stored in stuff[] at struct end. */
194         const char *driver;
195         /*! Stored in stuff[] at struct end. */
196         const char *database;
197         /*! Stored in stuff[] at struct end. */
198         const char *table;
199         /*! Contents of name, driver, database, and table in that order stuffed here. */
200         char stuff[0];
201 } *config_maps = NULL;
202
203 AST_MUTEX_DEFINE_STATIC(config_lock);
204 static struct ast_config_engine *config_engine_list;
205
206 #define MAX_INCLUDE_LEVEL 10
207
208 struct ast_category_template_instance {
209         char name[80]; /* redundant? */
210         const struct ast_category *inst;
211         AST_LIST_ENTRY(ast_category_template_instance) next;
212 };
213
214 struct ast_category {
215         char name[80];
216         int ignored;                    /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
217         int include_level;
218         /*!
219          * \brief The file name from whence this declaration was read
220          * \note Will never be NULL
221          */
222         char *file;
223         int lineno;
224         AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
225         struct ast_comment *precomments;
226         struct ast_comment *sameline;
227         struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
228         /*! First category variable in the list. */
229         struct ast_variable *root;
230         /*! Last category variable in the list. */
231         struct ast_variable *last;
232         /*! Next node in the list. */
233         struct ast_category *next;
234 };
235
236 struct ast_config {
237         /*! First config category in the list. */
238         struct ast_category *root;
239         /*! Last config category in the list. */
240         struct ast_category *last;
241         struct ast_category *current;
242         struct ast_category *last_browse;     /*!< used to cache the last category supplied via category_browse */
243         int include_level;
244         int max_include_level;
245         struct ast_config_include *includes;  /*!< a list of inclusions, which should describe the entire tree */
246 };
247
248 struct ast_config_include {
249         /*!
250          * \brief file name in which the include occurs
251          * \note Will never be NULL
252          */
253         char *include_location_file;
254         int  include_location_lineno;    /*!< lineno where include occurred */
255         int  exec;                       /*!< set to non-zero if its a #exec statement */
256         /*!
257          * \brief if it's an exec, you'll have both the /var/tmp to read, and the original script
258          * \note Will never be NULL if exec is non-zero
259          */
260         char *exec_file;
261         /*!
262          * \brief file name included
263          * \note Will never be NULL
264          */
265         char *included_file;
266         int inclusion_count;             /*!< if the file is included more than once, a running count thereof -- but, worry not,
267                                               we explode the instances and will include those-- so all entries will be unique */
268         int output;                      /*!< a flag to indicate if the inclusion has been output */
269         struct ast_config_include *next; /*!< ptr to next inclusion in the list */
270 };
271
272 static void ast_variable_destroy(struct ast_variable *doomed);
273 static void ast_includes_destroy(struct ast_config_include *incls);
274
275 #ifdef MALLOC_DEBUG
276 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
277 #else
278 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
279 #endif
280 {
281         struct ast_variable *variable;
282         int name_len = strlen(name) + 1;
283         int val_len = strlen(value) + 1;
284         int fn_len = strlen(filename) + 1;
285
286         /* Ensure a minimum length in case the filename is changed later. */
287         if (fn_len < MIN_VARIABLE_FNAME_SPACE) {
288                 fn_len = MIN_VARIABLE_FNAME_SPACE;
289         }
290
291         if (
292 #ifdef MALLOC_DEBUG
293                 (variable = __ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable), file, lineno, func))
294 #else
295                 (variable = ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable)))
296 #endif
297                 ) {
298                 char *dst = variable->stuff;    /* writable space starts here */
299
300                 /* Put file first so ast_include_rename() can calculate space available. */
301                 variable->file = strcpy(dst, filename);
302                 dst += fn_len;
303                 variable->name = strcpy(dst, name);
304                 dst += name_len;
305                 variable->value = strcpy(dst, value);
306         }
307         return variable;
308 }
309
310 /*!
311  * \internal
312  * \brief Move the contents from the source to the destination variable.
313  *
314  * \param dst_var Destination variable node
315  * \param src_var Source variable node
316  *
317  * \return Nothing
318  */
319 static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable *src_var)
320 {
321         dst_var->lineno = src_var->lineno;
322         dst_var->object = src_var->object;
323         dst_var->blanklines = src_var->blanklines;
324         dst_var->precomments = src_var->precomments;
325         src_var->precomments = NULL;
326         dst_var->sameline = src_var->sameline;
327         src_var->sameline = NULL;
328         dst_var->trailing = src_var->trailing;
329         src_var->trailing = NULL;
330 }
331
332 struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
333 {
334         /* a file should be included ONCE. Otherwise, if one of the instances is changed,
335          * then all be changed. -- how do we know to include it? -- Handling modified
336          * instances is possible, I'd have
337          * to create a new master for each instance. */
338         struct ast_config_include *inc;
339         struct stat statbuf;
340
341         inc = ast_include_find(conf, included_file);
342         if (inc) {
343                 do {
344                         inc->inclusion_count++;
345                         snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
346                 } while (stat(real_included_file_name, &statbuf) == 0);
347                 ast_log(LOG_WARNING,"'%s', line %d:  Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
348         } else
349                 *real_included_file_name = 0;
350
351         inc = ast_calloc(1,sizeof(struct ast_config_include));
352         if (!inc) {
353                 return NULL;
354         }
355         inc->include_location_file = ast_strdup(from_file);
356         inc->include_location_lineno = from_lineno;
357         if (!ast_strlen_zero(real_included_file_name))
358                 inc->included_file = ast_strdup(real_included_file_name);
359         else
360                 inc->included_file = ast_strdup(included_file);
361
362         inc->exec = is_exec;
363         if (is_exec)
364                 inc->exec_file = ast_strdup(exec_file);
365
366         if (!inc->include_location_file
367                 || !inc->included_file
368                 || (is_exec && !inc->exec_file)) {
369                 ast_includes_destroy(inc);
370                 return NULL;
371         }
372
373         /* attach this new struct to the conf struct */
374         inc->next = conf->includes;
375         conf->includes = inc;
376
377         return inc;
378 }
379
380 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
381 {
382         struct ast_config_include *incl;
383         struct ast_category *cat;
384         char *str;
385
386         int from_len = strlen(from_file);
387         int to_len = strlen(to_file);
388
389         if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
390                 return;
391
392         /* the manager code allows you to read in one config file, then
393          * write it back out under a different name. But, the new arrangement
394          * ties output lines to the file name. So, before you try to write
395          * the config file to disk, better riffle thru the data and make sure
396          * the file names are changed.
397          */
398         /* file names are on categories, includes (of course), and on variables. So,
399          * traverse all this and swap names */
400
401         for (incl = conf->includes; incl; incl=incl->next) {
402                 if (strcmp(incl->include_location_file,from_file) == 0) {
403                         if (from_len >= to_len)
404                                 strcpy(incl->include_location_file, to_file);
405                         else {
406                                 /* Keep the old filename if the allocation fails. */
407                                 str = ast_strdup(to_file);
408                                 if (str) {
409                                         ast_free(incl->include_location_file);
410                                         incl->include_location_file = str;
411                                 }
412                         }
413                 }
414         }
415         for (cat = conf->root; cat; cat = cat->next) {
416                 struct ast_variable **prev;
417                 struct ast_variable *v;
418                 struct ast_variable *new_var;
419
420                 if (strcmp(cat->file,from_file) == 0) {
421                         if (from_len >= to_len)
422                                 strcpy(cat->file, to_file);
423                         else {
424                                 /* Keep the old filename if the allocation fails. */
425                                 str = ast_strdup(to_file);
426                                 if (str) {
427                                         ast_free(cat->file);
428                                         cat->file = str;
429                                 }
430                         }
431                 }
432                 for (prev = &cat->root, v = cat->root; v; prev = &v->next, v = v->next) {
433                         if (strcmp(v->file, from_file)) {
434                                 continue;
435                         }
436
437                         /*
438                          * Calculate actual space available.  The file string is
439                          * intentionally stuffed before the name string just so we can
440                          * do this.
441                          */
442                         if (to_len < v->name - v->file) {
443                                 /* The new name will fit in the available space. */
444                                 str = (char *) v->file;/* Stupid compiler complains about discarding qualifiers even though I used a cast. */
445                                 strcpy(str, to_file);/* SAFE */
446                                 continue;
447                         }
448
449                         /* Keep the old filename if the allocation fails. */
450                         new_var = ast_variable_new(v->name, v->value, to_file);
451                         if (!new_var) {
452                                 continue;
453                         }
454
455                         /* Move items from the old list node to the replacement node. */
456                         ast_variable_move(new_var, v);
457
458                         /* Replace the old node in the list with the new node. */
459                         new_var->next = v->next;
460                         if (cat->last == v) {
461                                 cat->last = new_var;
462                         }
463                         *prev = new_var;
464
465                         ast_variable_destroy(v);
466
467                         v = new_var;
468                 }
469         }
470 }
471
472 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
473 {
474         struct ast_config_include *x;
475         for (x=conf->includes;x;x=x->next) {
476                 if (strcmp(x->included_file,included_file) == 0)
477                         return x;
478         }
479         return 0;
480 }
481
482
483 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
484 {
485         if (!variable)
486                 return;
487         if (category->last)
488                 category->last->next = variable;
489         else
490                 category->root = variable;
491         category->last = variable;
492         while (category->last->next)
493                 category->last = category->last->next;
494 }
495
496 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
497 {
498         struct ast_variable *cur = category->root;
499         int lineno;
500         int insertline;
501
502         if (!variable || sscanf(line, "%30d", &insertline) != 1) {
503                 return;
504         }
505         if (!insertline) {
506                 variable->next = category->root;
507                 category->root = variable;
508         } else {
509                 for (lineno = 1; lineno < insertline; lineno++) {
510                         cur = cur->next;
511                         if (!cur->next) {
512                                 break;
513                         }
514                 }
515                 variable->next = cur->next;
516                 cur->next = variable;
517         }
518 }
519
520 static void ast_comment_destroy(struct ast_comment **comment)
521 {
522         struct ast_comment *n, *p;
523
524         for (p = *comment; p; p = n) {
525                 n = p->next;
526                 ast_free(p);
527         }
528
529         *comment = NULL;
530 }
531
532 static void ast_variable_destroy(struct ast_variable *doomed)
533 {
534         ast_comment_destroy(&doomed->precomments);
535         ast_comment_destroy(&doomed->sameline);
536         ast_comment_destroy(&doomed->trailing);
537         ast_free(doomed);
538 }
539
540 struct ast_variable *ast_variables_dup(struct ast_variable *var)
541 {
542         struct ast_variable *cloned;
543         struct ast_variable *tmp;
544
545         if (!(cloned = ast_variable_new(var->name, var->value, var->file))) {
546                 return NULL;
547         }
548
549         tmp = cloned;
550
551         while ((var = var->next)) {
552                 if (!(tmp->next = ast_variable_new(var->name, var->value, var->file))) {
553                         ast_variables_destroy(cloned);
554                         return NULL;
555                 }
556                 tmp = tmp->next;
557         }
558
559         return cloned;
560 }
561
562 void ast_variables_destroy(struct ast_variable *v)
563 {
564         struct ast_variable *vn;
565
566         while (v) {
567                 vn = v;
568                 v = v->next;
569                 ast_variable_destroy(vn);
570         }
571 }
572
573 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
574 {
575         struct ast_category *cat = NULL;
576
577         if (!category) {
578                 return NULL;
579         }
580
581         if (config->last_browse && (config->last_browse->name == category)) {
582                 cat = config->last_browse;
583         } else {
584                 cat = ast_category_get(config, category);
585         }
586
587         return (cat) ? cat->root : NULL;
588 }
589
590 inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2)
591 {
592     l1->next = l2->next;
593     l2->next = l1;
594     return l2;
595 }
596
597 struct ast_variable *ast_variable_list_sort(struct ast_variable *start)
598 {
599         struct ast_variable *p, *q;
600         struct ast_variable top;
601         int changed = 1;
602         memset(&top, 0, sizeof(top));
603         top.next = start;
604         if (start != NULL && start->next != NULL) {
605                 while (changed) {
606                         changed = 0;
607                         q = &top;
608                         p = top.next;
609                         while (p->next != NULL) {
610                                 if (p->next != NULL && strcmp(p->name, p->next->name) > 0) {
611                                         q->next = variable_list_switch(p, p->next);
612                                         changed = 1;
613                                 }
614                                 q = p;
615                                 if (p->next != NULL)
616                                         p = p->next;
617                         }
618                 }
619         }
620         return top.next;
621 }
622
623 struct ast_variable *ast_variable_list_append_hint(struct ast_variable **head, struct ast_variable *search_hint, struct ast_variable *newvar)
624 {
625         struct ast_variable *curr;
626         ast_assert(head != NULL);
627
628         if (!*head) {
629                 *head = newvar;
630         } else {
631                 if (search_hint == NULL) {
632                         search_hint = *head;
633                 }
634                 for (curr = search_hint; curr->next; curr = curr->next);
635                 curr->next = newvar;
636         }
637
638         for (curr = newvar; curr->next; curr = curr->next);
639
640         return curr;
641 }
642
643 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
644 {
645         const char *tmp;
646         tmp = ast_variable_retrieve(cfg, cat, var);
647         if (!tmp) {
648                 tmp = ast_variable_retrieve(cfg, "general", var);
649         }
650         return tmp;
651 }
652
653
654 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
655 {
656         struct ast_variable *v;
657
658         if (category) {
659                 for (v = ast_variable_browse(config, category); v; v = v->next) {
660                         if (!strcasecmp(variable, v->name)) {
661                                 return v->value;
662                         }
663                 }
664         } else {
665                 struct ast_category *cat;
666
667                 for (cat = config->root; cat; cat = cat->next) {
668                         for (v = cat->root; v; v = v->next) {
669                                 if (!strcasecmp(variable, v->name)) {
670                                         return v->value;
671                                 }
672                         }
673                 }
674         }
675
676         return NULL;
677 }
678
679 static struct ast_variable *variable_clone(const struct ast_variable *old)
680 {
681         struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
682
683         if (new) {
684                 new->lineno = old->lineno;
685                 new->object = old->object;
686                 new->blanklines = old->blanklines;
687                 /* TODO: clone comments? */
688         }
689
690         return new;
691 }
692
693 static void move_variables(struct ast_category *old, struct ast_category *new)
694 {
695         struct ast_variable *var = old->root;
696
697         old->root = NULL;
698         /* we can just move the entire list in a single op */
699         ast_variable_append(new, var);
700 }
701
702 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
703 {
704         struct ast_category *category;
705
706         category = ast_calloc(1, sizeof(*category));
707         if (!category) {
708                 return NULL;
709         }
710         category->file = ast_strdup(in_file);
711         if (!category->file) {
712                 ast_category_destroy(category);
713                 return NULL;
714         }
715         ast_copy_string(category->name, name, sizeof(category->name));
716         category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
717         return category;
718 }
719
720 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
721 {
722         struct ast_category *cat;
723
724         /* try exact match first, then case-insensitive match */
725         for (cat = config->root; cat; cat = cat->next) {
726                 if (cat->name == category_name && (ignored || !cat->ignored))
727                         return cat;
728         }
729
730         for (cat = config->root; cat; cat = cat->next) {
731                 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
732                         return cat;
733         }
734
735         return NULL;
736 }
737
738 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
739 {
740         return category_get(config, category_name, 0);
741 }
742
743 int ast_category_exist(const struct ast_config *config, const char *category_name)
744 {
745         return !!ast_category_get(config, category_name);
746 }
747
748 void ast_category_append(struct ast_config *config, struct ast_category *category)
749 {
750         if (config->last)
751                 config->last->next = category;
752         else
753                 config->root = category;
754         category->include_level = config->include_level;
755         config->last = category;
756         config->current = category;
757 }
758
759 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
760 {
761         struct ast_category *cur_category;
762
763         if (!cat || !match)
764                 return;
765         if (!strcasecmp(config->root->name, match)) {
766                 cat->next = config->root;
767                 config->root = cat;
768                 return;
769         }
770         for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
771                 if (!strcasecmp(cur_category->next->name, match)) {
772                         cat->next = cur_category->next;
773                         cur_category->next = cat;
774                         break;
775                 }
776         }
777 }
778
779 static void ast_destroy_template_list(struct ast_category *cat)
780 {
781         struct ast_category_template_instance *x;
782
783         while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
784                 ast_free(x);
785 }
786
787 void ast_category_destroy(struct ast_category *cat)
788 {
789         ast_variables_destroy(cat->root);
790         cat->root = NULL;
791         cat->last = NULL;
792         ast_comment_destroy(&cat->precomments);
793         ast_comment_destroy(&cat->sameline);
794         ast_comment_destroy(&cat->trailing);
795         ast_destroy_template_list(cat);
796         ast_free(cat->file);
797         ast_free(cat);
798 }
799
800 static void ast_includes_destroy(struct ast_config_include *incls)
801 {
802         struct ast_config_include *incl,*inclnext;
803
804         for (incl=incls; incl; incl = inclnext) {
805                 inclnext = incl->next;
806                 ast_free(incl->include_location_file);
807                 ast_free(incl->exec_file);
808                 ast_free(incl->included_file);
809                 ast_free(incl);
810         }
811 }
812
813 static struct ast_category *next_available_category(struct ast_category *cat)
814 {
815         for (; cat && cat->ignored; cat = cat->next);
816
817         return cat;
818 }
819
820 /*! return the first var of a category */
821 struct ast_variable *ast_category_first(struct ast_category *cat)
822 {
823         return (cat) ? cat->root : NULL;
824 }
825
826 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
827 {
828         struct ast_category *category = ast_category_get(config, cat);
829
830         if (category)
831                 return category->root;
832         return NULL;
833 }
834
835 void ast_config_sort_categories(struct ast_config *config, int descending,
836                                                                 int (*comparator)(struct ast_category *p, struct ast_category *q))
837 {
838         /*
839          * The contents of this function are adapted from
840          * an example of linked list merge sorting
841          * copyright 2001 Simon Tatham.
842          *
843          * Permission is hereby granted, free of charge, to any person
844          * obtaining a copy of this software and associated documentation
845          * files (the "Software"), to deal in the Software without
846          * restriction, including without limitation the rights to use,
847          * copy, modify, merge, publish, distribute, sublicense, and/or
848          * sell copies of the Software, and to permit persons to whom the
849          * Software is furnished to do so, subject to the following
850          * conditions:
851          *
852          * The above copyright notice and this permission notice shall be
853          * included in all copies or substantial portions of the Software.
854          *
855          * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
856          * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
857          * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
858          * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
859          * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
860          * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
861          * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
862          * SOFTWARE.
863          */
864
865         int insize = 1;
866         struct ast_category *p, *q, *e, *tail;
867         int nmerges, psize, qsize, i;
868
869         /* If the descending flag was sent, we'll apply inversion to the comparison function's return. */
870         if (descending) {
871                 descending = -1;
872         } else {
873                 descending = 1;
874         }
875
876         if (!config->root) {
877                 return;
878         }
879
880         while (1) {
881                 p = config->root;
882                 config->root = NULL;
883                 tail = NULL;
884
885                 nmerges = 0; /* count number of merges we do in this pass */
886
887                 while (p) {
888                         nmerges++; /* there exists a merge to be done */
889
890                         /* step `insize' places along from p */
891                         q = p;
892                         psize = 0;
893                         for (i = 0; i < insize; i++) {
894                                 psize++;
895                                 q = q->next;
896                                 if (!q) {
897                                         break;
898                                 }
899                         }
900
901                         /* if q hasn't fallen off end, we have two lists to merge */
902                         qsize = insize;
903
904                         /* now we have two lists; merge them */
905                         while (psize > 0 || (qsize > 0 && q)) {
906                                 /* decide whether next element of merge comes from p or q */
907                                 if (psize == 0) {
908                                         /* p is empty; e must come from q. */
909                                         e = q;
910                                         q = q->next;
911                                         qsize--;
912                                 } else if (qsize == 0 || !q) {
913                                         /* q is empty; e must come from p. */
914                                         e = p; p = p->next; psize--;
915                                 } else if ((comparator(p,q) * descending) <= 0) {
916                                         /* First element of p is lower (or same) e must come from p. */
917                                         e = p;
918                                         p = p->next;
919                                         psize--;
920                                 } else {
921                                         /* First element of q is lower; e must come from q. */
922                                         e = q;
923                                         q = q->next;
924                                         qsize--;
925                                 }
926
927                                 /* add the next element to the merged list */
928                                 if (tail) {
929                                         tail->next = e;
930                                 } else {
931                                         config->root = e;
932                                 }
933                                 tail = e;
934                         }
935
936                         /* now p has stepped `insize' places along, and q has too */
937                         p = q;
938                 }
939
940                 tail->next = NULL;
941
942                 /* If we have done only one merge, we're finished. */
943                 if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
944                         return;
945                 }
946
947                 /* Otherwise repeat, merging lists twice the size */
948                 insize *= 2;
949         }
950
951 }
952
953 char *ast_category_browse(struct ast_config *config, const char *prev)
954 {
955         struct ast_category *cat;
956
957         if (!prev) {
958                 /* First time browse. */
959                 cat = config->root;
960         } else if (config->last_browse && (config->last_browse->name == prev)) {
961                 /* Simple last browse found. */
962                 cat = config->last_browse->next;
963         } else {
964                 /*
965                  * Config changed since last browse.
966                  *
967                  * First try cheap last browse search. (Rebrowsing a different
968                  * previous category?)
969                  */
970                 for (cat = config->root; cat; cat = cat->next) {
971                         if (cat->name == prev) {
972                                 /* Found it. */
973                                 cat = cat->next;
974                                 break;
975                         }
976                 }
977                 if (!cat) {
978                         /*
979                          * Have to do it the hard way. (Last category was deleted and
980                          * re-added?)
981                          */
982                         for (cat = config->root; cat; cat = cat->next) {
983                                 if (!strcasecmp(cat->name, prev)) {
984                                         /* Found it. */
985                                         cat = cat->next;
986                                         break;
987                                 }
988                         }
989                 }
990         }
991
992         if (cat)
993                 cat = next_available_category(cat);
994
995         config->last_browse = cat;
996         return (cat) ? cat->name : NULL;
997 }
998
999 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
1000 {
1001         struct ast_variable *v;
1002
1003         v = cat->root;
1004         cat->root = NULL;
1005         cat->last = NULL;
1006
1007         return v;
1008 }
1009
1010 void ast_category_rename(struct ast_category *cat, const char *name)
1011 {
1012         ast_copy_string(cat->name, name, sizeof(cat->name));
1013 }
1014
1015 static void inherit_category(struct ast_category *new, const struct ast_category *base)
1016 {
1017         struct ast_variable *var;
1018         struct ast_category_template_instance *x;
1019
1020         x = ast_calloc(1, sizeof(*x));
1021         if (!x) {
1022                 return;
1023         }
1024         strcpy(x->name, base->name);
1025         x->inst = base;
1026         AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
1027         for (var = base->root; var; var = var->next)
1028                 ast_variable_append(new, variable_clone(var));
1029 }
1030
1031 struct ast_config *ast_config_new(void)
1032 {
1033         struct ast_config *config;
1034
1035         if ((config = ast_calloc(1, sizeof(*config))))
1036                 config->max_include_level = MAX_INCLUDE_LEVEL;
1037         return config;
1038 }
1039
1040 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
1041 {
1042         struct ast_variable *cur, *prev=NULL, *curn;
1043         int res = -1;
1044         int num_item = 0;
1045         int req_item;
1046
1047         req_item = -1;
1048         if (!ast_strlen_zero(line)) {
1049                 /* Requesting to delete by item number. */
1050                 if (sscanf(line, "%30d", &req_item) != 1
1051                         || req_item < 0) {
1052                         /* Invalid item number to delete. */
1053                         return -1;
1054                 }
1055         }
1056
1057         prev = NULL;
1058         cur = category->root;
1059         while (cur) {
1060                 curn = cur->next;
1061                 /* Delete by item number or by variable name with optional value. */
1062                 if ((0 <= req_item && num_item == req_item)
1063                         || (req_item < 0 && !strcasecmp(cur->name, variable)
1064                                 && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
1065                         if (prev) {
1066                                 prev->next = cur->next;
1067                                 if (cur == category->last)
1068                                         category->last = prev;
1069                         } else {
1070                                 category->root = cur->next;
1071                                 if (cur == category->last)
1072                                         category->last = NULL;
1073                         }
1074                         ast_variable_destroy(cur);
1075                         res = 0;
1076                 } else
1077                         prev = cur;
1078
1079                 cur = curn;
1080                 ++num_item;
1081         }
1082         return res;
1083 }
1084
1085 int ast_variable_update(struct ast_category *category, const char *variable,
1086                                                 const char *value, const char *match, unsigned int object)
1087 {
1088         struct ast_variable *cur, *prev=NULL, *newer=NULL;
1089
1090         for (cur = category->root; cur; prev = cur, cur = cur->next) {
1091                 if (strcasecmp(cur->name, variable) ||
1092                         (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
1093                         continue;
1094
1095                 if (!(newer = ast_variable_new(variable, value, cur->file)))
1096                         return -1;
1097
1098                 ast_variable_move(newer, cur);
1099                 newer->object = newer->object || object;
1100
1101                 /* Replace the old node in the list with the new node. */
1102                 newer->next = cur->next;
1103                 if (prev)
1104                         prev->next = newer;
1105                 else
1106                         category->root = newer;
1107                 if (category->last == cur)
1108                         category->last = newer;
1109
1110                 ast_variable_destroy(cur);
1111
1112                 return 0;
1113         }
1114
1115         /* Could not find variable to update */
1116         return -1;
1117 }
1118
1119 int ast_category_delete(struct ast_config *cfg, const char *category)
1120 {
1121         struct ast_category *prev=NULL, *cat;
1122
1123         cat = cfg->root;
1124         while (cat) {
1125                 if (cat->name == category) {
1126                         if (prev) {
1127                                 prev->next = cat->next;
1128                                 if (cat == cfg->last)
1129                                         cfg->last = prev;
1130                         } else {
1131                                 cfg->root = cat->next;
1132                                 if (cat == cfg->last)
1133                                         cfg->last = NULL;
1134                         }
1135                         ast_category_destroy(cat);
1136                         return 0;
1137                 }
1138                 prev = cat;
1139                 cat = cat->next;
1140         }
1141
1142         prev = NULL;
1143         cat = cfg->root;
1144         while (cat) {
1145                 if (!strcasecmp(cat->name, category)) {
1146                         if (prev) {
1147                                 prev->next = cat->next;
1148                                 if (cat == cfg->last)
1149                                         cfg->last = prev;
1150                         } else {
1151                                 cfg->root = cat->next;
1152                                 if (cat == cfg->last)
1153                                         cfg->last = NULL;
1154                         }
1155                         ast_category_destroy(cat);
1156                         return 0;
1157                 }
1158                 prev = cat;
1159                 cat = cat->next;
1160         }
1161         return -1;
1162 }
1163
1164 int ast_category_empty(struct ast_config *cfg, const char *category)
1165 {
1166         struct ast_category *cat;
1167
1168         for (cat = cfg->root; cat; cat = cat->next) {
1169                 if (!strcasecmp(cat->name, category))
1170                         continue;
1171                 ast_variables_destroy(cat->root);
1172                 cat->root = NULL;
1173                 cat->last = NULL;
1174                 return 0;
1175         }
1176
1177         return -1;
1178 }
1179
1180 void ast_config_destroy(struct ast_config *cfg)
1181 {
1182         struct ast_category *cat, *catn;
1183
1184         if (!cfg)
1185                 return;
1186
1187         ast_includes_destroy(cfg->includes);
1188
1189         cat = cfg->root;
1190         while (cat) {
1191                 catn = cat;
1192                 cat = cat->next;
1193                 ast_category_destroy(catn);
1194         }
1195         ast_free(cfg);
1196 }
1197
1198 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
1199 {
1200         return cfg->current;
1201 }
1202
1203 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
1204 {
1205         /* cast below is just to silence compiler warning about dropping "const" */
1206         cfg->current = (struct ast_category *) cat;
1207 }
1208
1209 /*!
1210  * \internal
1211  * \brief Create a new cfmtime list node.
1212  *
1213  * \param filename Config filename caching.
1214  * \param who_asked Who wanted to know.
1215  *
1216  * \retval cfmtime New node on success.
1217  * \retval NULL on error.
1218  */
1219 static struct cache_file_mtime *cfmtime_new(const char *filename, const char *who_asked)
1220 {
1221         struct cache_file_mtime *cfmtime;
1222         char *dst;
1223
1224         cfmtime = ast_calloc(1,
1225                 sizeof(*cfmtime) + strlen(filename) + 1 + strlen(who_asked) + 1);
1226         if (!cfmtime) {
1227                 return NULL;
1228         }
1229         dst = cfmtime->filename;        /* writable space starts here */
1230         strcpy(dst, filename);
1231         dst += strlen(dst) + 1;
1232         cfmtime->who_asked = strcpy(dst, who_asked);
1233
1234         return cfmtime;
1235 }
1236
1237 enum config_cache_attribute_enum {
1238         ATTRIBUTE_INCLUDE = 0,
1239         ATTRIBUTE_EXEC = 1,
1240 };
1241
1242 /*!
1243  * \internal
1244  * \brief Clear the stat() data in the cached file modtime struct.
1245  *
1246  * \param cfmtime Cached file modtime.
1247  *
1248  * \return Nothing
1249  */
1250 static void cfmstat_clear(struct cache_file_mtime *cfmtime)
1251 {
1252         cfmtime->stat_size = 0;
1253         cfmtime->stat_mtime_nsec = 0;
1254         cfmtime->stat_mtime = 0;
1255 }
1256
1257 /*!
1258  * \internal
1259  * \brief Save the stat() data to the cached file modtime struct.
1260  *
1261  * \param cfmtime Cached file modtime.
1262  * \param statbuf Buffer filled in by stat().
1263  *
1264  * \return Nothing
1265  */
1266 static void cfmstat_save(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1267 {
1268         cfmtime->stat_size = statbuf->st_size;
1269 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
1270         cfmtime->stat_mtime_nsec = statbuf->st_mtim.tv_nsec;
1271 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
1272         cfmtime->stat_mtime_nsec = statbuf->st_mtimensec;
1273 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1274         cfmtime->stat_mtime_nsec = statbuf->st_mtimespec.tv_nsec;
1275 #else
1276         cfmtime->stat_mtime_nsec = 0;
1277 #endif
1278         cfmtime->stat_mtime = statbuf->st_mtime;
1279 }
1280
1281 /*!
1282  * \internal
1283  * \brief Compare the stat() data with the cached file modtime struct.
1284  *
1285  * \param cfmtime Cached file modtime.
1286  * \param statbuf Buffer filled in by stat().
1287  *
1288  * \retval non-zero if different.
1289  */
1290 static int cfmstat_cmp(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1291 {
1292         struct cache_file_mtime cfm_buf;
1293
1294         cfmstat_save(&cfm_buf, statbuf);
1295
1296         return cfmtime->stat_size != cfm_buf.stat_size
1297                 || cfmtime->stat_mtime != cfm_buf.stat_mtime
1298                 || cfmtime->stat_mtime_nsec != cfm_buf.stat_mtime_nsec;
1299 }
1300
1301 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
1302 {
1303         struct cache_file_mtime *cfmtime;
1304         struct cache_file_include *cfinclude;
1305         struct stat statbuf = { 0, };
1306
1307         /* Find our cached entry for this configuration file */
1308         AST_LIST_LOCK(&cfmtime_head);
1309         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1310                 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
1311                         break;
1312         }
1313         if (!cfmtime) {
1314                 cfmtime = cfmtime_new(configfile, who_asked);
1315                 if (!cfmtime) {
1316                         AST_LIST_UNLOCK(&cfmtime_head);
1317                         return;
1318                 }
1319                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1320                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1321         }
1322
1323         if (stat(configfile, &statbuf)) {
1324                 cfmstat_clear(cfmtime);
1325         } else {
1326                 cfmstat_save(cfmtime, &statbuf);
1327         }
1328
1329         switch (attrtype) {
1330         case ATTRIBUTE_INCLUDE:
1331                 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1332                         if (!strcmp(cfinclude->include, filename)) {
1333                                 AST_LIST_UNLOCK(&cfmtime_head);
1334                                 return;
1335                         }
1336                 }
1337                 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
1338                 if (!cfinclude) {
1339                         AST_LIST_UNLOCK(&cfmtime_head);
1340                         return;
1341                 }
1342                 strcpy(cfinclude->include, filename);
1343                 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
1344                 break;
1345         case ATTRIBUTE_EXEC:
1346                 cfmtime->has_exec = 1;
1347                 break;
1348         }
1349         AST_LIST_UNLOCK(&cfmtime_head);
1350 }
1351
1352 /*! \brief parse one line in the configuration.
1353  * \verbatim
1354  * We can have a category header        [foo](...)
1355  * a directive                          #include / #exec
1356  * or a regular line                    name = value
1357  * \endverbatim
1358  */
1359 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
1360         char *buf, int lineno, const char *configfile, struct ast_flags flags,
1361         struct ast_str *comment_buffer,
1362         struct ast_str *lline_buffer,
1363         const char *suggested_include_file,
1364         struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
1365 {
1366         char *c;
1367         char *cur = buf;
1368         struct ast_variable *v;
1369         char cmd[512], exec_file[512];
1370
1371         /* Actually parse the entry */
1372         if (cur[0] == '[') { /* A category header */
1373                 /* format is one of the following:
1374                  * [foo]        define a new category named 'foo'
1375                  * [foo](!)     define a new template category named 'foo'
1376                  * [foo](+)     append to category 'foo', error if foo does not exist.
1377                  * [foo](a)     define a new category and inherit from category or template a.
1378                  *              You can put a comma-separated list of categories and templates
1379                  *              and '!' and '+' between parentheses, with obvious meaning.
1380                  */
1381                 struct ast_category *newcat = NULL;
1382                 char *catname;
1383
1384                 c = strchr(cur, ']');
1385                 if (!c) {
1386                         ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
1387                         return -1;
1388                 }
1389                 *c++ = '\0';
1390                 cur++;
1391                 if (*c++ != '(')
1392                         c = NULL;
1393                 catname = cur;
1394                 if (!(*cat = newcat = ast_category_new(catname,
1395                                 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
1396                                 lineno))) {
1397                         return -1;
1398                 }
1399                 (*cat)->lineno = lineno;
1400                 *last_var = 0;
1401                 *last_cat = newcat;
1402
1403                 /* add comments */
1404                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1405                         newcat->precomments = ALLOC_COMMENT(comment_buffer);
1406                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1407                         newcat->sameline = ALLOC_COMMENT(lline_buffer);
1408                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1409                         CB_RESET(comment_buffer, lline_buffer);
1410
1411                 /* If there are options or categories to inherit from, process them now */
1412                 if (c) {
1413                         if (!(cur = strchr(c, ')'))) {
1414                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
1415                                 return -1;
1416                         }
1417                         *cur = '\0';
1418                         while ((cur = strsep(&c, ","))) {
1419                                 if (!strcasecmp(cur, "!")) {
1420                                         (*cat)->ignored = 1;
1421                                 } else if (!strcasecmp(cur, "+")) {
1422                                         *cat = category_get(cfg, catname, 1);
1423                                         if (!(*cat)) {
1424                                                 if (newcat)
1425                                                         ast_category_destroy(newcat);
1426                                                 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
1427                                                 return -1;
1428                                         }
1429                                         if (newcat) {
1430                                                 move_variables(newcat, *cat);
1431                                                 ast_category_destroy(newcat);
1432                                                 newcat = NULL;
1433                                         }
1434                                 } else {
1435                                         struct ast_category *base;
1436
1437                                         base = category_get(cfg, cur, 1);
1438                                         if (!base) {
1439                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1440                                                 return -1;
1441                                         }
1442                                         inherit_category(*cat, base);
1443                                 }
1444                         }
1445                 }
1446                 if (newcat)
1447                         ast_category_append(cfg, *cat);
1448         } else if (cur[0] == '#') { /* A directive - #include or #exec */
1449                 char *cur2;
1450                 char real_inclusion_name[256];
1451                 int do_include = 0;     /* otherwise, it is exec */
1452                 int try_include = 0;
1453
1454                 cur++;
1455                 c = cur;
1456                 while (*c && (*c > 32)) {
1457                         c++;
1458                 }
1459
1460                 if (*c) {
1461                         *c = '\0';
1462                         /* Find real argument */
1463                         c = ast_strip(c + 1);
1464                         if (!(*c)) {
1465                                 c = NULL;
1466                         }
1467                 } else {
1468                         c = NULL;
1469                 }
1470                 if (!strcasecmp(cur, "include")) {
1471                         do_include = 1;
1472                 } else if (!strcasecmp(cur, "tryinclude")) {
1473                         do_include = 1;
1474                         try_include = 1;
1475                 } else if (!strcasecmp(cur, "exec")) {
1476                         if (!ast_opt_exec_includes) {
1477                                 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1478                                 return 0;       /* XXX is this correct ? or we should return -1 ? */
1479                         }
1480                 } else {
1481                         ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1482                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1483                 }
1484
1485                 if (c == NULL) {
1486                         ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
1487                                         do_include ? "include / tryinclude" : "exec",
1488                                         do_include ? "filename" : "/path/to/executable",
1489                                         lineno,
1490                                         configfile);
1491                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1492                 }
1493
1494                 cur = c;
1495                 /* Strip off leading and trailing "'s and <>'s */
1496                 /* Dequote */
1497                 if ((*c == '"') || (*c == '<')) {
1498                         char quote_char = *c;
1499                         if (quote_char == '<') {
1500                                 quote_char = '>';
1501                         }
1502
1503                         if (*(c + strlen(c) - 1) == quote_char) {
1504                                 cur++;
1505                                 *(c + strlen(c) - 1) = '\0';
1506                         }
1507                 }
1508                 cur2 = cur;
1509
1510                 /* #exec </path/to/executable>
1511                    We create a tmp file, then we #include it, then we delete it. */
1512                 if (!do_include) {
1513                         struct timeval now = ast_tvnow();
1514                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1515                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1516                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1517                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1518                         ast_safe_system(cmd);
1519                         cur = exec_file;
1520                 } else {
1521                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1522                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1523                         exec_file[0] = '\0';
1524                 }
1525                 /* A #include */
1526                 /* record this inclusion */
1527                 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1528
1529                 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1530                 if (!ast_strlen_zero(exec_file))
1531                         unlink(exec_file);
1532                 if (!do_include && !try_include) {
1533                         ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1534                         return -1;
1535                 }
1536                 /* XXX otherwise what ? the default return is 0 anyways */
1537
1538         } else {
1539                 /* Just a line (variable = value) */
1540                 int object = 0;
1541                 int is_escaped;
1542
1543                 if (!(*cat)) {
1544                         ast_log(LOG_WARNING,
1545                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
1546                         return -1;
1547                 }
1548
1549                 is_escaped = cur[0] == '\\';
1550                 if (is_escaped) {
1551                         /* First character is escaped. */
1552                         ++cur;
1553                         if (cur[0] < 33) {
1554                                 ast_log(LOG_ERROR, "Invalid escape in line %d of %s\n", lineno, configfile);
1555                                 return -1;
1556                         }
1557                 }
1558                 c = strchr(cur + is_escaped, '=');
1559
1560                 if (c && c > cur + is_escaped && (*(c - 1) == '+')) {
1561                         struct ast_variable *var, *replace = NULL;
1562                         struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1563
1564                         if (!str || !*str) {
1565                                 return -1;
1566                         }
1567
1568                         *(c - 1) = '\0';
1569                         c++;
1570                         cur = ast_strip(cur);
1571
1572                         /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1573                         for (var = ast_category_first(*cat); var; var = var->next) {
1574                                 if (!strcmp(var->name, cur)) {
1575                                         replace = var;
1576                                 }
1577                         }
1578
1579                         if (!replace) {
1580                                 /* Nothing to replace; just set a variable normally. */
1581                                 goto set_new_variable;
1582                         }
1583
1584                         ast_str_set(str, 0, "%s", replace->value);
1585                         ast_str_append(str, 0, "%s", c);
1586                         ast_str_trim_blanks(*str);
1587                         ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1588                 } else if (c) {
1589                         *c = 0;
1590                         c++;
1591                         /* Ignore > in => */
1592                         if (*c== '>') {
1593                                 object = 1;
1594                                 c++;
1595                         }
1596                         cur = ast_strip(cur);
1597 set_new_variable:
1598                         if (ast_strlen_zero(cur)) {
1599                                 ast_log(LOG_WARNING, "No variable name in line %d of %s\n", lineno, configfile);
1600                         } else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1601                                 v->lineno = lineno;
1602                                 v->object = object;
1603                                 *last_cat = 0;
1604                                 *last_var = v;
1605                                 /* Put and reset comments */
1606                                 v->blanklines = 0;
1607                                 ast_variable_append(*cat, v);
1608                                 /* add comments */
1609                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1610                                         v->precomments = ALLOC_COMMENT(comment_buffer);
1611                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1612                                         v->sameline = ALLOC_COMMENT(lline_buffer);
1613                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1614                                         CB_RESET(comment_buffer, lline_buffer);
1615
1616                         } else {
1617                                 return -1;
1618                         }
1619                 } else {
1620                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1621                 }
1622         }
1623         return 0;
1624 }
1625
1626 static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
1627 {
1628         char fn[256];
1629 #if defined(LOW_MEMORY)
1630         char buf[512];
1631 #else
1632         char buf[8192];
1633 #endif
1634         char *new_buf, *comment_p, *process_buf;
1635         FILE *f;
1636         int lineno=0;
1637         int comment = 0, nest[MAX_NESTED_COMMENTS];
1638         struct ast_category *cat = NULL;
1639         int count = 0;
1640         struct stat statbuf;
1641         struct cache_file_mtime *cfmtime = NULL;
1642         struct cache_file_include *cfinclude;
1643         struct ast_variable *last_var = 0;
1644         struct ast_category *last_cat = 0;
1645         /*! Growable string buffer */
1646         struct ast_str *comment_buffer = NULL;  /*!< this will be a comment collector.*/
1647         struct ast_str *lline_buffer = NULL;    /*!< A buffer for stuff behind the ; */
1648
1649         if (cfg)
1650                 cat = ast_config_get_current_category(cfg);
1651
1652         if (filename[0] == '/') {
1653                 ast_copy_string(fn, filename, sizeof(fn));
1654         } else {
1655                 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1656         }
1657
1658         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1659                 comment_buffer = ast_str_create(CB_SIZE);
1660                 if (comment_buffer)
1661                         lline_buffer = ast_str_create(CB_SIZE);
1662                 if (!lline_buffer) {
1663                         ast_free(comment_buffer);
1664                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1665                         return NULL;
1666                 }
1667         }
1668 #ifdef AST_INCLUDE_GLOB
1669         {
1670                 int glob_ret;
1671                 glob_t globbuf;
1672                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
1673                 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1674                 if (glob_ret == GLOB_NOSPACE)
1675                         ast_log(LOG_WARNING,
1676                                 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1677                 else if (glob_ret  == GLOB_ABORTED)
1678                         ast_log(LOG_WARNING,
1679                                 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1680                 else  {
1681                         /* loop over expanded files */
1682                         int i;
1683                         for (i=0; i<globbuf.gl_pathc; i++) {
1684                                 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1685 #endif
1686         /*
1687          * The following is not a loop, but just a convenient way to define a block
1688          * (using do { } while(0) ), and be able to exit from it with 'continue'
1689          * or 'break' in case of errors. Nice trick.
1690          */
1691         do {
1692                 if (stat(fn, &statbuf))
1693                         continue;
1694
1695                 if (!S_ISREG(statbuf.st_mode)) {
1696                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1697                         continue;
1698                 }
1699
1700                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1701                         /* Find our cached entry for this configuration file */
1702                         AST_LIST_LOCK(&cfmtime_head);
1703                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1704                                 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
1705                                         break;
1706                         }
1707                         if (!cfmtime) {
1708                                 cfmtime = cfmtime_new(fn, who_asked);
1709                                 if (!cfmtime) {
1710                                         AST_LIST_UNLOCK(&cfmtime_head);
1711                                         continue;
1712                                 }
1713                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1714                                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1715                         }
1716                 }
1717
1718                 if (cfmtime
1719                         && !cfmtime->has_exec
1720                         && !cfmstat_cmp(cfmtime, &statbuf)
1721                         && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1722                         /* File is unchanged, what about the (cached) includes (if any)? */
1723                         int unchanged = 1;
1724                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1725                                 /* We must glob here, because if we did not, then adding a file to globbed directory would
1726                                  * incorrectly cause no reload to be necessary. */
1727                                 char fn2[256];
1728 #ifdef AST_INCLUDE_GLOB
1729                                 int glob_return;
1730                                 glob_t glob_buf = { .gl_offs = 0 };
1731                                 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
1732                                 /* On error, we reparse */
1733                                 if (glob_return == GLOB_NOSPACE || glob_return  == GLOB_ABORTED)
1734                                         unchanged = 0;
1735                                 else  {
1736                                         /* loop over expanded files */
1737                                         int j;
1738                                         for (j = 0; j < glob_buf.gl_pathc; j++) {
1739                                                 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
1740 #else
1741                                                 ast_copy_string(fn2, cfinclude->include);
1742 #endif
1743                                                 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
1744                                                         /* that second-to-last field needs to be looked at in this case... TODO */
1745                                                         unchanged = 0;
1746                                                         /* One change is enough to short-circuit and reload the whole shebang */
1747                                                         break;
1748                                                 }
1749 #ifdef AST_INCLUDE_GLOB
1750                                         }
1751                                 }
1752 #endif
1753                         }
1754
1755                         if (unchanged) {
1756                                 AST_LIST_UNLOCK(&cfmtime_head);
1757                                 ast_free(comment_buffer);
1758                                 ast_free(lline_buffer);
1759 #ifdef AST_INCLUDE_GLOB
1760                                 globfree(&globbuf);
1761 #endif
1762                                 return CONFIG_STATUS_FILEUNCHANGED;
1763                         }
1764                 }
1765                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1766                         AST_LIST_UNLOCK(&cfmtime_head);
1767
1768                 /* If cfg is NULL, then we just want an answer */
1769                 if (cfg == NULL) {
1770                         ast_free(comment_buffer);
1771                         ast_free(lline_buffer);
1772 #ifdef AST_INCLUDE_GLOB
1773                                 globfree(&globbuf);
1774 #endif
1775                         return NULL;
1776                 }
1777
1778                 if (cfmtime) {
1779                         cfmstat_save(cfmtime, &statbuf);
1780                 }
1781
1782                 if (!(f = fopen(fn, "r"))) {
1783                         ast_debug(1, "No file to parse: %s\n", fn);
1784                         ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
1785                         continue;
1786                 }
1787                 count++;
1788                 /* If we get to this point, then we're loading regardless */
1789                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1790                 ast_debug(1, "Parsing %s\n", fn);
1791                 ast_verb(2, "Parsing '%s': Found\n", fn);
1792                 while (!feof(f)) {
1793                         lineno++;
1794                         if (fgets(buf, sizeof(buf), f)) {
1795                                 /* Skip lines that are too long */
1796                                 if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 1] != '\n') {
1797                                         ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
1798                                         while (fgets(buf, sizeof(buf), f)) {
1799                                                 if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 1] == '\n') {
1800                                                         break;
1801                                                 }
1802                                         }
1803                                         continue;
1804                                 }
1805
1806                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
1807                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1808                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1809                                 }
1810
1811                                 new_buf = buf;
1812                                 if (comment)
1813                                         process_buf = NULL;
1814                                 else
1815                                         process_buf = buf;
1816
1817                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer) && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
1818                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1819                                         CB_ADD(&comment_buffer, "\n");       /* add a newline to the comment buffer */
1820                                         continue; /* go get a new line, then */
1821                                 }
1822
1823                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1824                                         if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
1825                                                 /* Escaped semicolons aren't comments. */
1826                                                 new_buf = comment_p;
1827                                                 /* write over the \ and bring the null terminator with us */
1828                                                 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
1829                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1830                                                 /* Meta-Comment start detected ";--" */
1831                                                 if (comment < MAX_NESTED_COMMENTS) {
1832                                                         *comment_p = '\0';
1833                                                         new_buf = comment_p + 3;
1834                                                         comment++;
1835                                                         nest[comment-1] = lineno;
1836                                                 } else {
1837                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1838                                                 }
1839                                         } else if ((comment_p >= new_buf + 2) &&
1840                                                    (*(comment_p - 1) == COMMENT_TAG) &&
1841                                                    (*(comment_p - 2) == COMMENT_TAG)) {
1842                                                 /* Meta-Comment end detected */
1843                                                 comment--;
1844                                                 new_buf = comment_p + 1;
1845                                                 if (!comment) {
1846                                                         /* Back to non-comment now */
1847                                                         if (process_buf) {
1848                                                                 /* Actually have to move what's left over the top, then continue */
1849                                                                 char *oldptr;
1850                                                                 oldptr = process_buf + strlen(process_buf);
1851                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1852                                                                         CB_ADD(&comment_buffer, ";");
1853                                                                         CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1854                                                                 }
1855
1856                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1857                                                                 new_buf = oldptr;
1858                                                         } else
1859                                                                 process_buf = new_buf;
1860                                                 }
1861                                         } else {
1862                                                 if (!comment) {
1863                                                         /* If ; is found, and we are not nested in a comment,
1864                                                            we immediately stop all comment processing */
1865                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1866                                                                 CB_ADD(&lline_buffer, comment_p);
1867                                                         }
1868                                                         *comment_p = '\0';
1869                                                         new_buf = comment_p;
1870                                                 } else
1871                                                         new_buf = comment_p + 1;
1872                                         }
1873                                 }
1874                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1875                                         CB_ADD(&comment_buffer, buf);  /* the whole line is a comment, store it */
1876                                 }
1877
1878                                 if (process_buf) {
1879                                         char *buffer = ast_strip(process_buf);
1880                                         if (!ast_strlen_zero(buffer)) {
1881                                                 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
1882                                                         cfg = CONFIG_STATUS_FILEINVALID;
1883                                                         break;
1884                                                 }
1885                                         }
1886                                 }
1887                         }
1888                 }
1889                 /* end of file-- anything in a comment buffer? */
1890                 if (last_cat) {
1891                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1892                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1893                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1894                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1895                                 }
1896                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1897                         }
1898                 } else if (last_var) {
1899                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1900                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1901                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1902                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1903                                 }
1904                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1905                         }
1906                 } else {
1907                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1908                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
1909                         }
1910                 }
1911                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1912                         CB_RESET(comment_buffer, lline_buffer);
1913
1914                 fclose(f);
1915         } while (0);
1916         if (comment) {
1917                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1918         }
1919 #ifdef AST_INCLUDE_GLOB
1920                                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1921                                                 break;
1922                                         }
1923                                 }
1924                                 globfree(&globbuf);
1925                         }
1926                 }
1927 #endif
1928
1929         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1930                 ast_free(comment_buffer);
1931                 ast_free(lline_buffer);
1932                 comment_buffer = NULL;
1933                 lline_buffer = NULL;
1934         }
1935
1936         if (count == 0)
1937                 return NULL;
1938
1939         return cfg;
1940 }
1941
1942
1943 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1944    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1945    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1946    be shocked and mystified as to why things are not showing up in the files!
1947
1948    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1949    and line number are stored for each include, plus the name of the file included, so that these statements may be
1950    included in the output files on a file_save operation.
1951
1952    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1953    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1954    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1955    and a header gets added.
1956
1957    vars and category heads are output in the order they are stored in the config file. So, if the software
1958    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1959    file/lineno data probably won't get changed.
1960
1961 */
1962
1963 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1964 {
1965         char date[256]="";
1966         time_t t;
1967
1968         time(&t);
1969         ast_copy_string(date, ctime(&t), sizeof(date));
1970
1971         fprintf(f1, ";!\n");
1972         fprintf(f1, ";! Automatically generated configuration file\n");
1973         if (strcmp(configfile, fn))
1974                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1975         else
1976                 fprintf(f1, ";! Filename: %s\n", configfile);
1977         fprintf(f1, ";! Generator: %s\n", generator);
1978         fprintf(f1, ";! Creation Date: %s", date);
1979         fprintf(f1, ";!\n");
1980 }
1981
1982 static void inclfile_destroy(void *obj)
1983 {
1984         const struct inclfile *o = obj;
1985
1986         ast_free(o->fname);
1987 }
1988
1989
1990 static struct inclfile *set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
1991 {
1992         struct inclfile lookup;
1993         struct inclfile *fi;
1994
1995         if (ast_strlen_zero(file)) {
1996                 if (configfile[0] == '/')
1997                         ast_copy_string(fn, configfile, fn_size);
1998                 else
1999                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
2000         } else if (file[0] == '/')
2001                 ast_copy_string(fn, file, fn_size);
2002         else
2003                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
2004         lookup.fname = fn;
2005         fi = ao2_find(fileset, &lookup, OBJ_POINTER);
2006         if (fi) {
2007                 /* Found existing include file scratch pad. */
2008                 return fi;
2009         }
2010
2011         /* set up a file scratch pad */
2012         fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
2013         if (!fi) {
2014                 /* Scratch pad creation failed. */
2015                 return NULL;
2016         }
2017         fi->fname = ast_strdup(fn);
2018         if (!fi->fname) {
2019                 /* Scratch pad creation failed. */
2020                 ao2_ref(fi, -1);
2021                 return NULL;
2022         }
2023         fi->lineno = 1;
2024
2025         ao2_link(fileset, fi);
2026
2027         return fi;
2028 }
2029
2030 static int count_linefeeds(char *str)
2031 {
2032         int count = 0;
2033
2034         while (*str) {
2035                 if (*str =='\n')
2036                         count++;
2037                 str++;
2038         }
2039         return count;
2040 }
2041
2042 static int count_linefeeds_in_comments(struct ast_comment *x)
2043 {
2044         int count = 0;
2045
2046         while (x) {
2047                 count += count_linefeeds(x->cmt);
2048                 x = x->next;
2049         }
2050         return count;
2051 }
2052
2053 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
2054 {
2055         int precomment_lines;
2056         int i;
2057
2058         if (!fi) {
2059                 /* No file scratch pad object so insert no blank lines. */
2060                 return;
2061         }
2062
2063         precomment_lines = count_linefeeds_in_comments(precomments);
2064
2065         /* I don't have to worry about those ;! comments, they are
2066            stored in the precomments, but not printed back out.
2067            I did have to make sure that comments following
2068            the ;! header comments were not also deleted in the process */
2069         if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
2070                 return;
2071         } else if (lineno == 0) {
2072                 /* Line replacements also mess things up */
2073                 return;
2074         } else if (lineno - precomment_lines - fi->lineno < 5) {
2075                 /* Only insert less than 5 blank lines; if anything more occurs,
2076                  * it's probably due to context deletion. */
2077                 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
2078                         fprintf(fp, "\n");
2079                 }
2080         } else {
2081                 /* Deletion occurred - insert a single blank line, for separation of
2082                  * contexts. */
2083                 fprintf(fp, "\n");
2084         }
2085
2086         fi->lineno = lineno + 1; /* Advance the file lineno */
2087 }
2088
2089 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2090 {
2091         return ast_config_text_file_save(configfile, cfg, generator);
2092 }
2093
2094 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2095 {
2096         FILE *f;
2097         char fn[PATH_MAX];
2098         struct ast_variable *var;
2099         struct ast_category *cat;
2100         struct ast_comment *cmt;
2101         struct ast_config_include *incl;
2102         int blanklines = 0;
2103         struct ao2_container *fileset;
2104         struct inclfile *fi;
2105
2106         fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
2107         if (!fileset) {
2108                 /* Container creation failed. */
2109                 return -1;
2110         }
2111
2112         /* reset all the output flags, in case this isn't our first time saving this data */
2113         for (incl = cfg->includes; incl; incl = incl->next) {
2114                 incl->output = 0;
2115         }
2116
2117         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
2118            are all truncated to zero bytes and have that nice header*/
2119         for (incl = cfg->includes; incl; incl = incl->next) {
2120                 if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
2121                         /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
2122                         fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
2123                         f = fopen(fn, "w");
2124                         if (f) {
2125                                 gen_header(f, configfile, fn, generator);
2126                                 fclose(f); /* this should zero out the file */
2127                         } else {
2128                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2129                                 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2130                         }
2131                         if (fi) {
2132                                 ao2_ref(fi, -1);
2133                         }
2134                 }
2135         }
2136
2137         /* just set fn to absolute ver of configfile */
2138         fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
2139         if (
2140 #ifdef __CYGWIN__
2141                 (f = fopen(fn, "w+"))
2142 #else
2143                 (f = fopen(fn, "w"))
2144 #endif
2145                 ) {
2146                 ast_verb(2, "Saving '%s'\n", fn);
2147                 gen_header(f, configfile, fn, generator);
2148                 cat = cfg->root;
2149                 fclose(f);
2150                 if (fi) {
2151                         ao2_ref(fi, -1);
2152                 }
2153
2154                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
2155                 /* since each var, cat, and associated comments can come from any file, we have to be
2156                    mobile, and open each file, print, and close it on an entry-by-entry basis */
2157
2158                 while (cat) {
2159                         fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
2160                         f = fopen(fn, "a");
2161                         if (!f) {
2162                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2163                                 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2164                                 if (fi) {
2165                                         ao2_ref(fi, -1);
2166                                 }
2167                                 ao2_ref(fileset, -1);
2168                                 return -1;
2169                         }
2170
2171                         /* dump any includes that happen before this category header */
2172                         for (incl=cfg->includes; incl; incl = incl->next) {
2173                                 if (strcmp(incl->include_location_file, cat->file) == 0){
2174                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
2175                                                 if (incl->exec)
2176                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2177                                                 else
2178                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
2179                                                 incl->output = 1;
2180                                         }
2181                                 }
2182                         }
2183
2184                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
2185                         /* Dump section with any appropriate comment */
2186                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
2187                                 char *cmtp = cmt->cmt;
2188                                 while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
2189                                         char *cmtp2 = strchr(cmtp+1, '\n');
2190                                         if (cmtp2)
2191                                                 cmtp = cmtp2+1;
2192                                         else cmtp = 0;
2193                                 }
2194                                 if (cmtp)
2195                                         fprintf(f,"%s", cmtp);
2196                         }
2197                         fprintf(f, "[%s]", cat->name);
2198                         if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
2199                                 fprintf(f, "(");
2200                                 if (cat->ignored) {
2201                                         fprintf(f, "!");
2202                                 }
2203                                 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
2204                                         fprintf(f, ",");
2205                                 }
2206                                 if (!AST_LIST_EMPTY(&cat->template_instances)) {
2207                                         struct ast_category_template_instance *x;
2208                                         AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2209                                                 fprintf(f,"%s",x->name);
2210                                                 if (x != AST_LIST_LAST(&cat->template_instances))
2211                                                         fprintf(f,",");
2212                                         }
2213                                 }
2214                                 fprintf(f, ")");
2215                         }
2216                         for(cmt = cat->sameline; cmt; cmt=cmt->next)
2217                         {
2218                                 fprintf(f,"%s", cmt->cmt);
2219                         }
2220                         if (!cat->sameline)
2221                                 fprintf(f,"\n");
2222                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
2223                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2224                                         fprintf(f,"%s", cmt->cmt);
2225                         }
2226                         fclose(f);
2227                         if (fi) {
2228                                 ao2_ref(fi, -1);
2229                         }
2230
2231                         var = cat->root;
2232                         while (var) {
2233                                 struct ast_category_template_instance *x;
2234                                 int found = 0;
2235                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2236                                         struct ast_variable *v;
2237                                         for (v = x->inst->root; v; v = v->next) {
2238                                                 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2239                                                         found = 1;
2240                                                         break;
2241                                                 }
2242                                         }
2243                                         if (found)
2244                                                 break;
2245                                 }
2246                                 if (found) {
2247                                         var = var->next;
2248                                         continue;
2249                                 }
2250                                 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
2251                                 f = fopen(fn, "a");
2252                                 if (!f) {
2253                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
2254                                         ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2255                                         if (fi) {
2256                                                 ao2_ref(fi, -1);
2257                                         }
2258                                         ao2_ref(fileset, -1);
2259                                         return -1;
2260                                 }
2261
2262                                 /* dump any includes that happen before this category header */
2263                                 for (incl=cfg->includes; incl; incl = incl->next) {
2264                                         if (strcmp(incl->include_location_file, var->file) == 0){
2265                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
2266                                                         if (incl->exec)
2267                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2268                                                         else
2269                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
2270                                                         incl->output = 1;
2271                                                 }
2272                                         }
2273                                 }
2274
2275                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
2276                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
2277                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2278                                                 fprintf(f,"%s", cmt->cmt);
2279                                 }
2280                                 if (var->sameline)
2281                                         fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
2282                                 else
2283                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
2284                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
2285                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2286                                                 fprintf(f,"%s", cmt->cmt);
2287                                 }
2288                                 if (var->blanklines) {
2289                                         blanklines = var->blanklines;
2290                                         while (blanklines--)
2291                                                 fprintf(f, "\n");
2292                                 }
2293
2294                                 fclose(f);
2295                                 if (fi) {
2296                                         ao2_ref(fi, -1);
2297                                 }
2298
2299                                 var = var->next;
2300                         }
2301                         cat = cat->next;
2302                 }
2303                 if (!option_debug) {
2304                         ast_verb(2, "Saving '%s': saved\n", fn);
2305                 }
2306         } else {
2307                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2308                 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
2309                 if (fi) {
2310                         ao2_ref(fi, -1);
2311                 }
2312                 ao2_ref(fileset, -1);
2313                 return -1;
2314         }
2315
2316         /* Now, for files with trailing #include/#exec statements,
2317            we have to make sure every entry is output */
2318         for (incl=cfg->includes; incl; incl = incl->next) {
2319                 if (!incl->output) {
2320                         /* open the respective file */
2321                         fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
2322                         f = fopen(fn, "a");
2323                         if (!f) {
2324                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2325                                 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2326                                 if (fi) {
2327                                         ao2_ref(fi, -1);
2328                                 }
2329                                 ao2_ref(fileset, -1);
2330                                 return -1;
2331                         }
2332
2333                         /* output the respective include */
2334                         if (incl->exec)
2335                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2336                         else
2337                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
2338                         fclose(f);
2339                         incl->output = 1;
2340                         if (fi) {
2341                                 ao2_ref(fi, -1);
2342                         }
2343                 }
2344         }
2345         ao2_ref(fileset, -1); /* this should destroy the hash container */
2346
2347         return 0;
2348 }
2349
2350 static void clear_config_maps(void)
2351 {
2352         struct ast_config_map *map;
2353
2354         SCOPED_MUTEX(lock, &config_lock);
2355
2356         while (config_maps) {
2357                 map = config_maps;
2358                 config_maps = config_maps->next;
2359                 ast_free(map);
2360         }
2361 }
2362
2363 #ifdef TEST_FRAMEWORK
2364 int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2365 #else
2366 static int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2367 #endif
2368 {
2369         struct ast_config_map *map;
2370         char *dst;
2371         int length;
2372
2373         length = sizeof(*map);
2374         length += strlen(name) + 1;
2375         length += strlen(driver) + 1;
2376         length += strlen(database) + 1;
2377         if (table)
2378                 length += strlen(table) + 1;
2379
2380         if (!(map = ast_calloc(1, length)))
2381                 return -1;
2382
2383         dst = map->stuff;       /* writable space starts here */
2384         map->name = strcpy(dst, name);
2385         dst += strlen(dst) + 1;
2386         map->driver = strcpy(dst, driver);
2387         dst += strlen(dst) + 1;
2388         map->database = strcpy(dst, database);
2389         if (table) {
2390                 dst += strlen(dst) + 1;
2391                 map->table = strcpy(dst, table);
2392         }
2393         map->priority = priority;
2394         map->next = config_maps;
2395         config_maps = map;
2396
2397         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
2398
2399         return 0;
2400 }
2401
2402 int read_config_maps(void)
2403 {
2404         struct ast_config *config, *configtmp;
2405         struct ast_variable *v;
2406         char *driver, *table, *database, *textpri, *stringp, *tmp;
2407         struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
2408         int pri;
2409
2410         clear_config_maps();
2411
2412         configtmp = ast_config_new();
2413         if (!configtmp) {
2414                 ast_log(LOG_ERROR, "Unable to allocate memory for new config\n");
2415                 return -1;
2416         }
2417         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
2418         if (config == CONFIG_STATUS_FILEINVALID) {
2419                 return -1;
2420         } else if (!config) {
2421                 ast_config_destroy(configtmp);
2422                 return 0;
2423         }
2424
2425         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
2426                 char buf[512];
2427                 ast_copy_string(buf, v->value, sizeof(buf));
2428                 stringp = buf;
2429                 driver = strsep(&stringp, ",");
2430
2431                 if ((tmp = strchr(stringp, '\"')))
2432                         stringp = tmp;
2433
2434                 /* check if the database text starts with a double quote */
2435                 if (*stringp == '"') {
2436                         stringp++;
2437                         database = strsep(&stringp, "\"");
2438                         strsep(&stringp, ",");
2439                 } else {
2440                         /* apparently this text has no quotes */
2441                         database = strsep(&stringp, ",");
2442                 }
2443
2444                 table = strsep(&stringp, ",");
2445                 textpri = strsep(&stringp, ",");
2446                 if (!textpri || !(pri = atoi(textpri))) {
2447                         pri = 1;
2448                 }
2449
2450                 if (!strcmp(v->name, extconfig_conf)) {
2451                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
2452                         continue;
2453                 }
2454
2455                 if (!strcmp(v->name, "asterisk.conf")) {
2456                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
2457                         continue;
2458                 }
2459
2460                 if (!strcmp(v->name, "logger.conf")) {
2461                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
2462                         continue;
2463                 }
2464
2465                 if (!driver || !database)
2466                         continue;
2467                 if (!strcasecmp(v->name, "sipfriends")) {
2468                         ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
2469                         ast_realtime_append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
2470                 } else if (!strcasecmp(v->name, "iaxfriends")) {
2471                         ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
2472                         ast_realtime_append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
2473                         ast_realtime_append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
2474                 } else
2475                         ast_realtime_append_mapping(v->name, driver, database, table, pri);
2476         }
2477
2478         ast_config_destroy(config);
2479         return 0;
2480 }
2481
2482 int ast_config_engine_register(struct ast_config_engine *new)
2483 {
2484         struct ast_config_engine *ptr;
2485
2486         SCOPED_MUTEX(lock, &config_lock);
2487
2488         if (!config_engine_list) {
2489                 config_engine_list = new;
2490         } else {
2491                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
2492                 ptr->next = new;
2493         }
2494
2495         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
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))) {
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))) {
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         }
3549
3550         if (!(hook = ao2_alloc(sizeof(*hook), hook_destroy))) {
3551                 return -1;
3552         }
3553
3554         hook->hook_cb = hook_cb;
3555         hook->filename = ast_strdup(filename);
3556         hook->name = ast_strdup(name);
3557         hook->module = ast_strdup(module);
3558
3559         ao2_link(cfg_hooks, hook);
3560         return 0;
3561 }