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