ef1b1c8543072bb0ac9b13fc27bb9a26bd597195
[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 = NULL;
623
624         if (prev && config->last_browse && (config->last_browse->name == prev))
625                 cat = config->last_browse->next;
626         else if (!prev && config->root)
627                 cat = config->root;
628         else if (prev) {
629                 for (cat = config->root; cat; cat = cat->next) {
630                         if (cat->name == prev) {
631                                 cat = cat->next;
632                                 break;
633                         }
634                 }
635                 if (!cat) {
636                         for (cat = config->root; cat; cat = cat->next) {
637                                 if (!strcasecmp(cat->name, prev)) {
638                                         cat = cat->next;
639                                         break;
640                                 }
641                         }
642                 }
643         }
644         
645         if (cat)
646                 cat = next_available_category(cat);
647
648         config->last_browse = cat;
649         return (cat) ? cat->name : NULL;
650 }
651
652 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
653 {
654         struct ast_variable *v;
655
656         v = cat->root;
657         cat->root = NULL;
658         cat->last = NULL;
659
660         return v;
661 }
662
663 void ast_category_rename(struct ast_category *cat, const char *name)
664 {
665         ast_copy_string(cat->name, name, sizeof(cat->name));
666 }
667
668 static void inherit_category(struct ast_category *new, const struct ast_category *base)
669 {
670         struct ast_variable *var;
671         struct ast_category_template_instance *x = ast_calloc(1,sizeof(struct ast_category_template_instance));
672
673         strcpy(x->name, base->name);
674         x->inst = base;
675         AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
676         for (var = base->root; var; var = var->next)
677                 ast_variable_append(new, variable_clone(var));
678 }
679
680 struct ast_config *ast_config_new(void) 
681 {
682         struct ast_config *config;
683
684         if ((config = ast_calloc(1, sizeof(*config))))
685                 config->max_include_level = MAX_INCLUDE_LEVEL;
686         return config;
687 }
688
689 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
690 {
691         struct ast_variable *cur, *prev=NULL, *curn;
692         int res = -1;
693         int lineno = 0;
694
695         cur = category->root;
696         while (cur) {
697                 if (cur->name == variable) {
698                         if (prev) {
699                                 prev->next = cur->next;
700                                 if (cur == category->last)
701                                         category->last = prev;
702                         } else {
703                                 category->root = cur->next;
704                                 if (cur == category->last)
705                                         category->last = NULL;
706                         }
707                         cur->next = NULL;
708                         ast_variables_destroy(cur);
709                         return 0;
710                 }
711                 prev = cur;
712                 cur = cur->next;
713         }
714
715         prev = NULL;
716         cur = category->root;
717         while (cur) {
718                 curn = cur->next;
719                 if ((!ast_strlen_zero(line) && lineno == atoi(line)) || (ast_strlen_zero(line) && !strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
720                         if (prev) {
721                                 prev->next = cur->next;
722                                 if (cur == category->last)
723                                         category->last = prev;
724                         } else {
725                                 category->root = cur->next;
726                                 if (cur == category->last)
727                                         category->last = NULL;
728                         }
729                         cur->next = NULL;
730                         ast_variables_destroy(cur);
731                         res = 0;
732                 } else
733                         prev = cur;
734
735                 cur = curn;
736                 lineno++;
737         }
738         return res;
739 }
740
741 int ast_variable_update(struct ast_category *category, const char *variable, 
742                                                 const char *value, const char *match, unsigned int object)
743 {
744         struct ast_variable *cur, *prev=NULL, *newer=NULL;
745
746         for (cur = category->root; cur; prev = cur, cur = cur->next) {
747                 if (strcasecmp(cur->name, variable) ||
748                         (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
749                         continue;
750
751                 if (!(newer = ast_variable_new(variable, value, cur->file)))
752                         return -1;
753         
754                 newer->next = cur->next;
755                 newer->object = cur->object || object;
756
757                 /* Preserve everything */
758                 newer->lineno = cur->lineno;
759                 newer->blanklines = cur->blanklines;
760                 newer->precomments = cur->precomments; cur->precomments = NULL;
761                 newer->sameline = cur->sameline; cur->sameline = NULL;
762                 newer->trailing = cur->trailing; cur->trailing = NULL;
763
764                 if (prev)
765                         prev->next = newer;
766                 else
767                         category->root = newer;
768                 if (category->last == cur)
769                         category->last = newer;
770
771                 cur->next = NULL;
772                 ast_variables_destroy(cur);
773
774                 return 0;
775         }
776
777         /* Could not find variable to update */
778         return -1;
779 }
780
781 int ast_category_delete(struct ast_config *cfg, const char *category)
782 {
783         struct ast_category *prev=NULL, *cat;
784
785         cat = cfg->root;
786         while (cat) {
787                 if (cat->name == category) {
788                         if (prev) {
789                                 prev->next = cat->next;
790                                 if (cat == cfg->last)
791                                         cfg->last = prev;
792                         } else {
793                                 cfg->root = cat->next;
794                                 if (cat == cfg->last)
795                                         cfg->last = NULL;
796                         }
797                         ast_category_destroy(cat);
798                         return 0;
799                 }
800                 prev = cat;
801                 cat = cat->next;
802         }
803
804         prev = NULL;
805         cat = cfg->root;
806         while (cat) {
807                 if (!strcasecmp(cat->name, category)) {
808                         if (prev) {
809                                 prev->next = cat->next;
810                                 if (cat == cfg->last)
811                                         cfg->last = prev;
812                         } else {
813                                 cfg->root = cat->next;
814                                 if (cat == cfg->last)
815                                         cfg->last = NULL;
816                         }
817                         ast_category_destroy(cat);
818                         return 0;
819                 }
820                 prev = cat;
821                 cat = cat->next;
822         }
823         return -1;
824 }
825
826 int ast_category_empty(struct ast_config *cfg, const char *category)
827 {
828         struct ast_category *cat;
829
830         for (cat = cfg->root; cat; cat = cat->next) {
831                 if (!strcasecmp(cat->name, category))
832                         continue;
833                 ast_variables_destroy(cat->root);
834                 cat->root = NULL;
835                 cat->last = NULL;
836                 return 0;
837         }
838
839         return -1;
840 }
841
842 void ast_config_destroy(struct ast_config *cfg)
843 {
844         struct ast_category *cat, *catn;
845
846         if (!cfg)
847                 return;
848
849         ast_includes_destroy(cfg->includes);
850
851         cat = cfg->root;
852         while (cat) {
853                 catn = cat;
854                 cat = cat->next;
855                 ast_category_destroy(catn);
856         }
857         ast_free(cfg);
858 }
859
860 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
861 {
862         return cfg->current;
863 }
864
865 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
866 {
867         /* cast below is just to silence compiler warning about dropping "const" */
868         cfg->current = (struct ast_category *) cat;
869 }
870
871 enum config_cache_attribute_enum {
872         ATTRIBUTE_INCLUDE = 0,
873         ATTRIBUTE_EXEC = 1,
874 };
875
876 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
877 {
878         struct cache_file_mtime *cfmtime;
879         struct cache_file_include *cfinclude;
880         struct stat statbuf = { 0, };
881
882         /* Find our cached entry for this configuration file */
883         AST_LIST_LOCK(&cfmtime_head);
884         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
885                 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
886                         break;
887         }
888         if (!cfmtime) {
889                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1 + strlen(who_asked) + 1);
890                 if (!cfmtime) {
891                         AST_LIST_UNLOCK(&cfmtime_head);
892                         return;
893                 }
894                 AST_LIST_HEAD_INIT(&cfmtime->includes);
895                 strcpy(cfmtime->filename, configfile);
896                 cfmtime->who_asked = cfmtime->filename + strlen(configfile) + 1;
897                 strcpy(cfmtime->who_asked, who_asked);
898                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
899                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
900         }
901
902         if (!stat(configfile, &statbuf))
903                 cfmtime->mtime = 0;
904         else
905                 cfmtime->mtime = statbuf.st_mtime;
906
907         switch (attrtype) {
908         case ATTRIBUTE_INCLUDE:
909                 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
910                         if (!strcmp(cfinclude->include, filename)) {
911                                 AST_LIST_UNLOCK(&cfmtime_head);
912                                 return;
913                         }
914                 }
915                 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
916                 if (!cfinclude) {
917                         AST_LIST_UNLOCK(&cfmtime_head);
918                         return;
919                 }
920                 strcpy(cfinclude->include, filename);
921                 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
922                 break;
923         case ATTRIBUTE_EXEC:
924                 cfmtime->has_exec = 1;
925                 break;
926         }
927         AST_LIST_UNLOCK(&cfmtime_head);
928 }
929
930 /*! \brief parse one line in the configuration.
931  * \verbatim
932  * We can have a category header        [foo](...)
933  * a directive                          #include / #exec
934  * or a regular line                    name = value
935  * \endverbatim
936  */
937 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
938         char *buf, int lineno, const char *configfile, struct ast_flags flags,
939         struct ast_str *comment_buffer,
940         struct ast_str *lline_buffer,
941         const char *suggested_include_file,
942         struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
943 {
944         char *c;
945         char *cur = buf;
946         struct ast_variable *v;
947         char cmd[512], exec_file[512];
948
949         /* Actually parse the entry */
950         if (cur[0] == '[') { /* A category header */
951                 /* format is one of the following:
952                  * [foo]        define a new category named 'foo'
953                  * [foo](!)     define a new template category named 'foo'
954                  * [foo](+)     append to category 'foo', error if foo does not exist.
955                  * [foo](a)     define a new category and inherit from category or template a.
956                  *              You can put a comma-separated list of categories and templates
957                  *              and '!' and '+' between parentheses, with obvious meaning.
958                  */
959                 struct ast_category *newcat = NULL;
960                 char *catname;
961
962                 c = strchr(cur, ']');
963                 if (!c) {
964                         ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
965                         return -1;
966                 }
967                 *c++ = '\0';
968                 cur++;
969                 if (*c++ != '(')
970                         c = NULL;
971                 catname = cur;
972                 if (!(*cat = newcat = ast_category_new(catname,
973                                 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
974                                 lineno))) {
975                         return -1;
976                 }
977                 (*cat)->lineno = lineno;
978                 *last_var = 0;
979                 *last_cat = newcat;
980                 
981                 /* add comments */
982                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
983                         newcat->precomments = ALLOC_COMMENT(comment_buffer);
984                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
985                         newcat->sameline = ALLOC_COMMENT(lline_buffer);
986                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
987                         CB_RESET(comment_buffer, lline_buffer);
988                 
989                 /* If there are options or categories to inherit from, process them now */
990                 if (c) {
991                         if (!(cur = strchr(c, ')'))) {
992                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
993                                 return -1;
994                         }
995                         *cur = '\0';
996                         while ((cur = strsep(&c, ","))) {
997                                 if (!strcasecmp(cur, "!")) {
998                                         (*cat)->ignored = 1;
999                                 } else if (!strcasecmp(cur, "+")) {
1000                                         *cat = category_get(cfg, catname, 1);
1001                                         if (!(*cat)) {
1002                                                 if (newcat)
1003                                                         ast_category_destroy(newcat);
1004                                                 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
1005                                                 return -1;
1006                                         }
1007                                         if (newcat) {
1008                                                 move_variables(newcat, *cat);
1009                                                 ast_category_destroy(newcat);
1010                                                 newcat = NULL;
1011                                         }
1012                                 } else {
1013                                         struct ast_category *base;
1014                                 
1015                                         base = category_get(cfg, cur, 1);
1016                                         if (!base) {
1017                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1018                                                 return -1;
1019                                         }
1020                                         inherit_category(*cat, base);
1021                                 }
1022                         }
1023                 }
1024                 if (newcat)
1025                         ast_category_append(cfg, *cat);
1026         } else if (cur[0] == '#') { /* A directive - #include or #exec */
1027                 char *cur2;
1028                 char real_inclusion_name[256];
1029                 int do_include = 0;     /* otherwise, it is exec */
1030
1031                 cur++;
1032                 c = cur;
1033                 while (*c && (*c > 32)) {
1034                         c++;
1035                 }
1036
1037                 if (*c) {
1038                         *c = '\0';
1039                         /* Find real argument */
1040                         c = ast_strip(c + 1);
1041                         if (!(*c)) {
1042                                 c = NULL;
1043                         }
1044                 } else {
1045                         c = NULL;
1046                 }
1047                 if (!strcasecmp(cur, "include")) {
1048                         do_include = 1;
1049                 } else if (!strcasecmp(cur, "exec")) {
1050                         if (!ast_opt_exec_includes) {
1051                                 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1052                                 return 0;       /* XXX is this correct ? or we should return -1 ? */
1053                         }
1054                 } else {
1055                         ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1056                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1057                 }
1058
1059                 if (c == NULL) {
1060                         ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 
1061                                         do_include ? "include" : "exec",
1062                                         do_include ? "filename" : "/path/to/executable",
1063                                         lineno,
1064                                         configfile);
1065                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1066                 }
1067
1068                 cur = c;
1069                 /* Strip off leading and trailing "'s and <>'s */
1070                 /* Dequote */
1071                 if ((*c == '"') || (*c == '<')) {
1072                         char quote_char = *c;
1073                         if (quote_char == '<') {
1074                                 quote_char = '>';
1075                         }
1076
1077                         if (*(c + strlen(c) - 1) == quote_char) {
1078                                 cur++;
1079                                 *(c + strlen(c) - 1) = '\0';
1080                         }
1081                 }
1082                 cur2 = cur;
1083
1084                 /* #exec </path/to/executable>
1085                    We create a tmp file, then we #include it, then we delete it. */
1086                 if (!do_include) {
1087                         struct timeval now = ast_tvnow();
1088                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1089                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1090                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1091                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1092                         ast_safe_system(cmd);
1093                         cur = exec_file;
1094                 } else {
1095                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1096                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1097                         exec_file[0] = '\0';
1098                 }
1099                 /* A #include */
1100                 /* record this inclusion */
1101                 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1102
1103                 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1104                 if (!ast_strlen_zero(exec_file))
1105                         unlink(exec_file);
1106                 if (!do_include) {
1107                         ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1108                         return -1;
1109                 }
1110                 /* XXX otherwise what ? the default return is 0 anyways */
1111
1112         } else {
1113                 /* Just a line (variable = value) */
1114                 int object = 0;
1115                 if (!(*cat)) {
1116                         ast_log(LOG_WARNING,
1117                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
1118                         return -1;
1119                 }
1120                 c = strchr(cur, '=');
1121
1122                 if (c && c > cur && (*(c - 1) == '+')) {
1123                         struct ast_variable *var, *replace = NULL;
1124                         struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1125
1126                         if (!str || !*str) {
1127                                 return -1;
1128                         }
1129
1130                         *(c - 1) = '\0';
1131                         c++;
1132                         cur = ast_strip(cur);
1133
1134                         /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1135                         for (var = ast_category_first(*cat); var; var = var->next) {
1136                                 if (!strcmp(var->name, cur)) {
1137                                         replace = var;
1138                                 }
1139                         }
1140
1141                         if (!replace) {
1142                                 /* Nothing to replace; just set a variable normally. */
1143                                 goto set_new_variable;
1144                         }
1145
1146                         ast_str_set(str, 0, "%s", replace->value);
1147                         ast_str_append(str, 0, "%s", c);
1148                         ast_str_trim_blanks(*str);
1149                         ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1150                 } else if (c) {
1151                         *c = 0;
1152                         c++;
1153                         /* Ignore > in => */
1154                         if (*c== '>') {
1155                                 object = 1;
1156                                 c++;
1157                         }
1158 set_new_variable:
1159                         if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1160                                 v->lineno = lineno;
1161                                 v->object = object;
1162                                 *last_cat = 0;
1163                                 *last_var = v;
1164                                 /* Put and reset comments */
1165                                 v->blanklines = 0;
1166                                 ast_variable_append(*cat, v);
1167                                 /* add comments */
1168                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1169                                         v->precomments = ALLOC_COMMENT(comment_buffer);
1170                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1171                                         v->sameline = ALLOC_COMMENT(lline_buffer);
1172                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1173                                         CB_RESET(comment_buffer, lline_buffer);
1174                                 
1175                         } else {
1176                                 return -1;
1177                         }
1178                 } else {
1179                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1180                 }
1181         }
1182         return 0;
1183 }
1184
1185 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)
1186 {
1187         char fn[256];
1188 #if defined(LOW_MEMORY)
1189         char buf[512];
1190 #else
1191         char buf[8192];
1192 #endif
1193         char *new_buf, *comment_p, *process_buf;
1194         FILE *f;
1195         int lineno=0;
1196         int comment = 0, nest[MAX_NESTED_COMMENTS];
1197         struct ast_category *cat = NULL;
1198         int count = 0;
1199         struct stat statbuf;
1200         struct cache_file_mtime *cfmtime = NULL;
1201         struct cache_file_include *cfinclude;
1202         struct ast_variable *last_var = 0;
1203         struct ast_category *last_cat = 0;
1204         /*! Growable string buffer */
1205         struct ast_str *comment_buffer = NULL;  /*!< this will be a comment collector.*/
1206         struct ast_str *lline_buffer = NULL;    /*!< A buffer for stuff behind the ; */
1207
1208         if (cfg)
1209                 cat = ast_config_get_current_category(cfg);
1210
1211         if (filename[0] == '/') {
1212                 ast_copy_string(fn, filename, sizeof(fn));
1213         } else {
1214                 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1215         }
1216
1217         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1218                 comment_buffer = ast_str_create(CB_SIZE);
1219                 if (comment_buffer)
1220                         lline_buffer = ast_str_create(CB_SIZE);
1221                 if (!lline_buffer) {
1222                         if (comment_buffer)
1223                                 ast_free(comment_buffer);
1224                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1225                         return NULL;
1226                 }
1227         }
1228 #ifdef AST_INCLUDE_GLOB
1229         {
1230                 int glob_ret;
1231                 glob_t globbuf;
1232                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
1233                 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1234                 if (glob_ret == GLOB_NOSPACE)
1235                         ast_log(LOG_WARNING,
1236                                 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1237                 else if (glob_ret  == GLOB_ABORTED)
1238                         ast_log(LOG_WARNING,
1239                                 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1240                 else  {
1241                         /* loop over expanded files */
1242                         int i;
1243                         for (i=0; i<globbuf.gl_pathc; i++) {
1244                                 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1245 #endif
1246         /*
1247          * The following is not a loop, but just a convenient way to define a block
1248          * (using do { } while(0) ), and be able to exit from it with 'continue'
1249          * or 'break' in case of errors. Nice trick.
1250          */
1251         do {
1252                 if (stat(fn, &statbuf))
1253                         continue;
1254
1255                 if (!S_ISREG(statbuf.st_mode)) {
1256                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1257                         continue;
1258                 }
1259
1260                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1261                         /* Find our cached entry for this configuration file */
1262                         AST_LIST_LOCK(&cfmtime_head);
1263                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1264                                 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
1265                                         break;
1266                         }
1267                         if (!cfmtime) {
1268                                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1 + strlen(who_asked) + 1);
1269                                 if (!cfmtime)
1270                                         continue;
1271                                 AST_LIST_HEAD_INIT(&cfmtime->includes);
1272                                 strcpy(cfmtime->filename, fn);
1273                                 cfmtime->who_asked = cfmtime->filename + strlen(fn) + 1;
1274                                 strcpy(cfmtime->who_asked, who_asked);
1275                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1276                                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1277                         }
1278                 }
1279
1280                 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1281                         /* File is unchanged, what about the (cached) includes (if any)? */
1282                         int unchanged = 1;
1283                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1284                                 /* We must glob here, because if we did not, then adding a file to globbed directory would
1285                                  * incorrectly cause no reload to be necessary. */
1286                                 char fn2[256];
1287 #ifdef AST_INCLUDE_GLOB
1288                                 int glob_return;
1289                                 glob_t glob_buf = { .gl_offs = 0 };
1290                                 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
1291                                 /* On error, we reparse */
1292                                 if (glob_return == GLOB_NOSPACE || glob_return  == GLOB_ABORTED)
1293                                         unchanged = 0;
1294                                 else  {
1295                                         /* loop over expanded files */
1296                                         int j;
1297                                         for (j = 0; j < glob_buf.gl_pathc; j++) {
1298                                                 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
1299 #else
1300                                                 ast_copy_string(fn2, cfinclude->include);
1301 #endif
1302                                                 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
1303                                                         /* that second-to-last field needs to be looked at in this case... TODO */
1304                                                         unchanged = 0;
1305                                                         /* One change is enough to short-circuit and reload the whole shebang */
1306                                                         break;
1307                                                 }
1308 #ifdef AST_INCLUDE_GLOB
1309                                         }
1310                                 }
1311 #endif
1312                         }
1313
1314                         if (unchanged) {
1315                                 AST_LIST_UNLOCK(&cfmtime_head);
1316                                 return CONFIG_STATUS_FILEUNCHANGED;
1317                         }
1318                 }
1319                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1320                         AST_LIST_UNLOCK(&cfmtime_head);
1321
1322                 /* If cfg is NULL, then we just want an answer */
1323                 if (cfg == NULL)
1324                         return NULL;
1325
1326                 if (cfmtime)
1327                         cfmtime->mtime = statbuf.st_mtime;
1328
1329                 ast_verb(2, "Parsing '%s': ", fn);
1330                         fflush(stdout);
1331                 if (!(f = fopen(fn, "r"))) {
1332                         ast_debug(1, "No file to parse: %s\n", fn);
1333                         ast_verb(2, "Not found (%s)\n", strerror(errno));
1334                         continue;
1335                 }
1336                 count++;
1337                 /* If we get to this point, then we're loading regardless */
1338                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1339                 ast_debug(1, "Parsing %s\n", fn);
1340                 ast_verb(2, "Found\n");
1341                 while (!feof(f)) {
1342                         lineno++;
1343                         if (fgets(buf, sizeof(buf), f)) {
1344                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
1345                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1346                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1347                                 }
1348                                 
1349                                 new_buf = buf;
1350                                 if (comment) 
1351                                         process_buf = NULL;
1352                                 else
1353                                         process_buf = buf;
1354                                 
1355                                 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"))) {
1356                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1357                                         CB_ADD(&comment_buffer, "\n");       /* add a newline to the comment buffer */
1358                                         continue; /* go get a new line, then */
1359                                 }
1360                                 
1361                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1362                                         if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
1363                                                 /* Escaped semicolons aren't comments. */
1364                                                 new_buf = comment_p + 1;
1365                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1366                                                 /* Meta-Comment start detected ";--" */
1367                                                 if (comment < MAX_NESTED_COMMENTS) {
1368                                                         *comment_p = '\0';
1369                                                         new_buf = comment_p + 3;
1370                                                         comment++;
1371                                                         nest[comment-1] = lineno;
1372                                                 } else {
1373                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1374                                                 }
1375                                         } else if ((comment_p >= new_buf + 2) &&
1376                                                    (*(comment_p - 1) == COMMENT_TAG) &&
1377                                                    (*(comment_p - 2) == COMMENT_TAG)) {
1378                                                 /* Meta-Comment end detected */
1379                                                 comment--;
1380                                                 new_buf = comment_p + 1;
1381                                                 if (!comment) {
1382                                                         /* Back to non-comment now */
1383                                                         if (process_buf) {
1384                                                                 /* Actually have to move what's left over the top, then continue */
1385                                                                 char *oldptr;
1386                                                                 oldptr = process_buf + strlen(process_buf);
1387                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1388                                                                         CB_ADD(&comment_buffer, ";");
1389                                                                         CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1390                                                                 }
1391                                                                 
1392                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1393                                                                 new_buf = oldptr;
1394                                                         } else
1395                                                                 process_buf = new_buf;
1396                                                 }
1397                                         } else {
1398                                                 if (!comment) {
1399                                                         /* If ; is found, and we are not nested in a comment, 
1400                                                            we immediately stop all comment processing */
1401                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1402                                                                 CB_ADD(&lline_buffer, comment_p);
1403                                                         }
1404                                                         *comment_p = '\0'; 
1405                                                         new_buf = comment_p;
1406                                                 } else
1407                                                         new_buf = comment_p + 1;
1408                                         }
1409                                 }
1410                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1411                                         CB_ADD(&comment_buffer, buf);  /* the whole line is a comment, store it */
1412                                 }
1413                                 
1414                                 if (process_buf) {
1415                                         char *buffer = ast_strip(process_buf);
1416                                         if (!ast_strlen_zero(buffer)) {
1417                                                 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
1418                                                         cfg = CONFIG_STATUS_FILEINVALID;
1419                                                         break;
1420                                                 }
1421                                         }
1422                                 }
1423                         }
1424                 }
1425                 /* end of file-- anything in a comment buffer? */
1426                 if (last_cat) {
1427                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1428                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1429                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1430                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1431                                 }
1432                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1433                         }
1434                 } else if (last_var) {
1435                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1436                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1437                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1438                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1439                                 }
1440                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1441                         }
1442                 } else {
1443                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1444                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
1445                         }
1446                 }
1447                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1448                         CB_RESET(comment_buffer, lline_buffer);
1449
1450                 fclose(f);
1451         } while (0);
1452         if (comment) {
1453                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1454         }
1455 #ifdef AST_INCLUDE_GLOB
1456                                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1457                                                 break;
1458                                         }
1459                                 }
1460                                 globfree(&globbuf);
1461                         }
1462                 }
1463 #endif
1464
1465         if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg != CONFIG_STATUS_FILEINVALID && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1466                 if (comment_buffer)
1467                         ast_free(comment_buffer);
1468                 if (lline_buffer)
1469                         ast_free(lline_buffer);
1470                 comment_buffer = NULL;
1471                 lline_buffer = NULL;
1472         }
1473         
1474         if (count == 0)
1475                 return NULL;
1476
1477         return cfg;
1478 }
1479
1480
1481 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1482    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1483    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1484    be shocked and mystified as to why things are not showing up in the files! 
1485
1486    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1487    and line number are stored for each include, plus the name of the file included, so that these statements may be
1488    included in the output files on a file_save operation. 
1489
1490    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1491    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1492    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1493    and a header gets added.
1494
1495    vars and category heads are output in the order they are stored in the config file. So, if the software
1496    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1497    file/lineno data probably won't get changed.
1498
1499 */
1500
1501 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1502 {
1503         char date[256]="";
1504         time_t t;
1505
1506         time(&t);
1507         ast_copy_string(date, ctime(&t), sizeof(date));
1508
1509         fprintf(f1, ";!\n");
1510         fprintf(f1, ";! Automatically generated configuration file\n");
1511         if (strcmp(configfile, fn))
1512                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1513         else
1514                 fprintf(f1, ";! Filename: %s\n", configfile);
1515         fprintf(f1, ";! Generator: %s\n", generator);
1516         fprintf(f1, ";! Creation Date: %s", date);
1517         fprintf(f1, ";!\n");
1518 }
1519
1520 static void   inclfile_destroy(void *obj)
1521 {
1522         const struct inclfile *o = obj;
1523
1524         if (o->fname)
1525                 free(o->fname);
1526 }
1527
1528
1529 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
1530 {
1531         struct inclfile lookup;
1532         
1533         if (!file || file[0] == 0) {
1534                 if (configfile[0] == '/')
1535                         ast_copy_string(fn, configfile, fn_size);
1536                 else
1537                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1538         } else if (file[0] == '/') 
1539                 ast_copy_string(fn, file, fn_size);
1540         else
1541                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1542         lookup.fname = fn;
1543         *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1544         if (!(*fi)) {
1545                 /* set up a file scratch pad */
1546                 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1547                 fx->fname = ast_strdup(fn);
1548                 fx->lineno = 1;
1549                 *fi = fx;
1550                 ao2_link(fileset, fx);
1551         }
1552 }
1553
1554 static int count_linefeeds(char *str)
1555 {
1556         int count = 0;
1557
1558         while (*str) {
1559                 if (*str =='\n')
1560                         count++;
1561                 str++;
1562         }
1563         return count;
1564 }
1565
1566 static int count_linefeeds_in_comments(struct ast_comment *x)
1567 {
1568         int count = 0;
1569
1570         while (x) {
1571                 count += count_linefeeds(x->cmt);
1572                 x = x->next;
1573         }
1574         return count;
1575 }
1576
1577 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1578 {
1579         int precomment_lines = count_linefeeds_in_comments(precomments);
1580         int i;
1581
1582         /* I don't have to worry about those ;! comments, they are
1583            stored in the precomments, but not printed back out.
1584            I did have to make sure that comments following
1585            the ;! header comments were not also deleted in the process */
1586         if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
1587                 return;
1588         } else if (lineno == 0) {
1589                 /* Line replacements also mess things up */
1590                 return;
1591         } else if (lineno - precomment_lines - fi->lineno < 5) {
1592                 /* Only insert less than 5 blank lines; if anything more occurs,
1593                  * it's probably due to context deletion. */
1594                 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
1595                         fprintf(fp, "\n");
1596                 }
1597         } else {
1598                 /* Deletion occurred - insert a single blank line, for separation of
1599                  * contexts. */
1600                 fprintf(fp, "\n");
1601         }
1602  
1603         fi->lineno = lineno + 1; /* Advance the file lineno */
1604 }
1605
1606 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1607 {
1608         return ast_config_text_file_save(configfile, cfg, generator);
1609 }
1610
1611 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1612 {
1613         FILE *f;
1614         char fn[256];
1615         struct ast_variable *var;
1616         struct ast_category *cat;
1617         struct ast_comment *cmt;
1618         struct ast_config_include *incl;
1619         int blanklines = 0;
1620         struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
1621         struct inclfile *fi = 0;
1622
1623         /* reset all the output flags, in case this isn't our first time saving this data */
1624
1625         for (incl=cfg->includes; incl; incl = incl->next)
1626                 incl->output = 0;
1627
1628         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1629            are all truncated to zero bytes and have that nice header*/
1630
1631         for (incl=cfg->includes; incl; incl = incl->next)
1632         {
1633                 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*/
1634                         FILE *f1;
1635
1636                         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 */
1637                         f1 = fopen(fn,"w");
1638                         if (f1) {
1639                                 gen_header(f1, configfile, fn, generator);
1640                                 fclose(f1); /* this should zero out the file */
1641                         } else {
1642                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1643                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1644                         }
1645                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1646                         fi = 0;
1647                 }
1648         }
1649
1650         set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
1651 #ifdef __CYGWIN__       
1652         if ((f = fopen(fn, "w+"))) {
1653 #else
1654         if ((f = fopen(fn, "w"))) {
1655 #endif      
1656                 ast_verb(2, "Saving '%s': ", fn);
1657                 gen_header(f, configfile, fn, generator);
1658                 cat = cfg->root;
1659                 fclose(f);
1660                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1661                 
1662                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1663                 /* since each var, cat, and associated comments can come from any file, we have to be 
1664                    mobile, and open each file, print, and close it on an entry-by-entry basis */
1665
1666                 while (cat) {
1667                         set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
1668                         f = fopen(fn, "a");
1669                         if (!f)
1670                         {
1671                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1672                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1673                                 ao2_ref(fileset, -1);
1674                                 return -1;
1675                         }
1676
1677                         /* dump any includes that happen before this category header */
1678                         for (incl=cfg->includes; incl; incl = incl->next) {
1679                                 if (strcmp(incl->include_location_file, cat->file) == 0){
1680                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
1681                                                 if (incl->exec)
1682                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1683                                                 else
1684                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
1685                                                 incl->output = 1;
1686                                         }
1687                                 }
1688                         }
1689
1690                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1691                         /* Dump section with any appropriate comment */
1692                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1693                                 char *cmtp = cmt->cmt;
1694                                 while (*cmtp == ';' && *(cmtp+1) == '!') {
1695                                         char *cmtp2 = strchr(cmtp+1, '\n');
1696                                         if (cmtp2)
1697                                                 cmtp = cmtp2+1;
1698                                         else cmtp = 0;
1699                                 }
1700                                 if (cmtp)
1701                                         fprintf(f,"%s", cmtp);
1702                         }
1703                         fprintf(f, "[%s]", cat->name);
1704                         if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
1705                                 fprintf(f, "(");
1706                                 if (cat->ignored) {
1707                                         fprintf(f, "!");
1708                                 }
1709                                 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
1710                                         fprintf(f, ",");
1711                                 }
1712                                 if (!AST_LIST_EMPTY(&cat->template_instances)) {
1713                                         struct ast_category_template_instance *x;
1714                                         AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1715                                                 fprintf(f,"%s",x->name);
1716                                                 if (x != AST_LIST_LAST(&cat->template_instances))
1717                                                         fprintf(f,",");
1718                                         }
1719                                 }
1720                                 fprintf(f, ")");
1721                         }
1722                         for(cmt = cat->sameline; cmt; cmt=cmt->next)
1723                         {
1724                                 fprintf(f,"%s", cmt->cmt);
1725                         }
1726                         if (!cat->sameline)
1727                                 fprintf(f,"\n");
1728                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1729                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1730                                         fprintf(f,"%s", cmt->cmt);
1731                         }
1732                         fclose(f);
1733                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1734                         fi = 0;
1735                         
1736                         var = cat->root;
1737                         while (var) {
1738                                 struct ast_category_template_instance *x;
1739                                 int found = 0;
1740                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1741                                         struct ast_variable *v;
1742                                         for (v = x->inst->root; v; v = v->next) {
1743                                                 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
1744                                                         found = 1;
1745                                                         break;
1746                                                 }
1747                                         }
1748                                         if (found)
1749                                                 break;
1750                                 }
1751                                 if (found) {
1752                                         var = var->next;
1753                                         continue;
1754                                 }
1755                                 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
1756                                 f = fopen(fn, "a");
1757                                 if (!f)
1758                                 {
1759                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
1760                                         ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1761                                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1762                                         fi = 0;
1763                                         ao2_ref(fileset, -1);
1764                                         return -1;
1765                                 }
1766                                 
1767                                 /* dump any includes that happen before this category header */
1768                                 for (incl=cfg->includes; incl; incl = incl->next) {
1769                                         if (strcmp(incl->include_location_file, var->file) == 0){
1770                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
1771                                                         if (incl->exec)
1772                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1773                                                         else
1774                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1775                                                         incl->output = 1;
1776                                                 }
1777                                         }
1778                                 }
1779                                 
1780                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1781                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1782                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1783                                                 fprintf(f,"%s", cmt->cmt);
1784                                 }
1785                                 if (var->sameline) 
1786                                         fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1787                                 else    
1788                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1789                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1790                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1791                                                 fprintf(f,"%s", cmt->cmt);
1792                                 }
1793                                 if (var->blanklines) {
1794                                         blanklines = var->blanklines;
1795                                         while (blanklines--)
1796                                                 fprintf(f, "\n");
1797                                 }
1798                                 
1799                                 fclose(f);
1800                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1801                                 fi = 0;
1802                                 
1803                                 var = var->next;
1804                         }
1805                         cat = cat->next;
1806                 }
1807                 if (!option_debug)
1808                         ast_verb(2, "Saved\n");
1809         } else {
1810                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1811                 ast_verb(2, "Unable to write (%s)", strerror(errno));
1812                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1813                 ao2_ref(fileset, -1);
1814                 return -1;
1815         }
1816
1817         /* Now, for files with trailing #include/#exec statements,
1818            we have to make sure every entry is output */
1819
1820         for (incl=cfg->includes; incl; incl = incl->next) {
1821                 if (!incl->output) {
1822                         /* open the respective file */
1823                         set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
1824                         f = fopen(fn, "a");
1825                         if (!f)
1826                         {
1827                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1828                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1829                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1830                                 fi = 0;
1831                                 ao2_ref(fileset, -1);
1832                                 return -1;
1833                         }
1834                         
1835                         /* output the respective include */
1836                         if (incl->exec)
1837                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1838                         else
1839                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1840                         fclose(f);
1841                         incl->output = 1;
1842                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1843                         fi = 0;
1844                 }
1845         }
1846         ao2_ref(fileset, -1); /* this should destroy the hash container */
1847                                 
1848         return 0;
1849 }
1850
1851 static void clear_config_maps(void) 
1852 {
1853         struct ast_config_map *map;
1854
1855         ast_mutex_lock(&config_lock);
1856
1857         while (config_maps) {
1858                 map = config_maps;
1859                 config_maps = config_maps->next;
1860                 ast_free(map);
1861         }
1862                 
1863         ast_mutex_unlock(&config_lock);
1864 }
1865
1866 static int append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
1867 {
1868         struct ast_config_map *map;
1869         int length;
1870
1871         length = sizeof(*map);
1872         length += strlen(name) + 1;
1873         length += strlen(driver) + 1;
1874         length += strlen(database) + 1;
1875         if (table)
1876                 length += strlen(table) + 1;
1877
1878         if (!(map = ast_calloc(1, length)))
1879                 return -1;
1880
1881         map->name = map->stuff;
1882         strcpy(map->name, name);
1883         map->driver = map->name + strlen(map->name) + 1;
1884         strcpy(map->driver, driver);
1885         map->database = map->driver + strlen(map->driver) + 1;
1886         strcpy(map->database, database);
1887         if (table) {
1888                 map->table = map->database + strlen(map->database) + 1;
1889                 strcpy(map->table, table);
1890         }
1891         map->priority = priority;
1892         map->next = config_maps;
1893
1894         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
1895
1896         config_maps = map;
1897         return 0;
1898 }
1899
1900 int read_config_maps(void) 
1901 {
1902         struct ast_config *config, *configtmp;
1903         struct ast_variable *v;
1904         char *driver, *table, *database, *textpri, *stringp, *tmp;
1905         struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
1906         int pri;
1907
1908         clear_config_maps();
1909
1910         configtmp = ast_config_new();
1911         configtmp->max_include_level = 1;
1912         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
1913         if (config == CONFIG_STATUS_FILEINVALID) {
1914                 return -1;
1915         } else if (!config) {
1916                 ast_config_destroy(configtmp);
1917                 return 0;
1918         }
1919
1920         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
1921                 char buf[512];
1922                 ast_copy_string(buf, v->value, sizeof(buf));
1923                 stringp = buf;
1924                 driver = strsep(&stringp, ",");
1925
1926                 if ((tmp = strchr(stringp, '\"')))
1927                         stringp = tmp;
1928
1929                 /* check if the database text starts with a double quote */
1930                 if (*stringp == '"') {
1931                         stringp++;
1932                         database = strsep(&stringp, "\"");
1933                         strsep(&stringp, ",");
1934                 } else {
1935                         /* apparently this text has no quotes */
1936                         database = strsep(&stringp, ",");
1937                 }
1938
1939                 table = strsep(&stringp, ",");
1940                 textpri = strsep(&stringp, ",");
1941                 if (!textpri || !(pri = atoi(textpri))) {
1942                         pri = 1;
1943                 }
1944
1945                 if (!strcmp(v->name, extconfig_conf)) {
1946                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
1947                         continue;
1948                 }
1949
1950                 if (!strcmp(v->name, "asterisk.conf")) {
1951                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
1952                         continue;
1953                 }
1954
1955                 if (!strcmp(v->name, "logger.conf")) {
1956                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
1957                         continue;
1958                 }
1959
1960                 if (!driver || !database)
1961                         continue;
1962                 if (!strcasecmp(v->name, "sipfriends")) {
1963                         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");
1964                         append_mapping("sipusers", driver, database, table ? table : "sipfriends", pri);
1965                         append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
1966                 } else if (!strcasecmp(v->name, "iaxfriends")) {
1967                         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");
1968                         append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
1969                         append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
1970                 } else 
1971                         append_mapping(v->name, driver, database, table, pri);
1972         }
1973                 
1974         ast_config_destroy(config);
1975         return 0;
1976 }
1977
1978 int ast_config_engine_register(struct ast_config_engine *new) 
1979 {
1980         struct ast_config_engine *ptr;
1981
1982         ast_mutex_lock(&config_lock);
1983
1984         if (!config_engine_list) {
1985                 config_engine_list = new;
1986         } else {
1987                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
1988                 ptr->next = new;
1989         }
1990
1991         ast_mutex_unlock(&config_lock);
1992         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
1993
1994         return 1;
1995 }
1996
1997 int ast_config_engine_deregister(struct ast_config_engine *del) 
1998 {
1999         struct ast_config_engine *ptr, *last=NULL;
2000
2001         ast_mutex_lock(&config_lock);
2002
2003         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
2004                 if (ptr == del) {
2005                         if (last)
2006                                 last->next = ptr->next;
2007                         else
2008                                 config_engine_list = ptr->next;
2009                         break;
2010                 }
2011                 last = ptr;
2012         }
2013
2014         ast_mutex_unlock(&config_lock);
2015
2016         return 0;
2017 }
2018
2019 /*! \brief Find realtime engine for realtime family */
2020 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz) 
2021 {
2022         struct ast_config_engine *eng, *ret = NULL;
2023         struct ast_config_map *map;
2024
2025         ast_mutex_lock(&config_lock);
2026
2027         for (map = config_maps; map; map = map->next) {
2028                 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
2029                         if (database)
2030                                 ast_copy_string(database, map->database, dbsiz);
2031                         if (table)
2032                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
2033                         break;
2034                 }
2035         }
2036
2037         /* Check if the required driver (engine) exist */
2038         if (map) {
2039                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
2040                         if (!strcasecmp(eng->name, map->driver))
2041                                 ret = eng;
2042                 }
2043         }
2044
2045         ast_mutex_unlock(&config_lock);
2046         
2047         /* if we found a mapping, but the engine is not available, then issue a warning */
2048         if (map && !ret)
2049                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
2050
2051         return ret;
2052 }
2053
2054 static struct ast_config_engine text_file_engine = {
2055         .name = "text",
2056         .load_func = config_text_file_load,
2057 };
2058
2059 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)
2060 {
2061         char db[256];
2062         char table[256];
2063         struct ast_config_engine *loader = &text_file_engine;
2064         struct ast_config *result; 
2065
2066         /* The config file itself bumps include_level by 1 */
2067         if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2068                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2069                 return NULL;
2070         }
2071
2072         cfg->include_level++;
2073
2074         if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
2075                 struct ast_config_engine *eng;
2076
2077                 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
2078
2079
2080                 if (eng && eng->load_func) {
2081                         loader = eng;
2082                 } else {
2083                         eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
2084                         if (eng && eng->load_func)
2085                                 loader = eng;
2086                 }
2087         }
2088
2089         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2090
2091         if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED)
2092                 result->include_level--;
2093         else if (result != CONFIG_STATUS_FILEINVALID)
2094                 cfg->include_level--;
2095
2096         return result;
2097 }
2098
2099 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
2100 {
2101         struct ast_config *cfg;
2102         struct ast_config *result;
2103
2104         cfg = ast_config_new();
2105         if (!cfg)
2106                 return NULL;
2107
2108         result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
2109         if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
2110                 ast_config_destroy(cfg);
2111
2112         return result;
2113 }
2114
2115 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
2116 {
2117         struct ast_config_engine *eng;
2118         char db[256];
2119         char table[256];
2120         struct ast_variable *res=NULL;
2121         int i;
2122
2123         for (i = 1; ; i++) {
2124                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2125                         if (eng->realtime_func && (res = eng->realtime_func(db, table, ap))) {
2126                                 return res;
2127                         }
2128                 } else {
2129                         return NULL;
2130                 }
2131         }
2132
2133         return res;
2134 }
2135
2136 struct ast_variable *ast_load_realtime_all(const char *family, ...)
2137 {
2138         struct ast_variable *res;
2139         va_list ap;
2140
2141         va_start(ap, family);
2142         res = ast_load_realtime_helper(family, ap);
2143         va_end(ap);
2144
2145         return res;
2146 }
2147
2148 struct ast_variable *ast_load_realtime(const char *family, ...)
2149 {
2150         struct ast_variable *res;
2151         struct ast_variable *cur;
2152         struct ast_variable **prev;
2153         va_list ap;
2154
2155         va_start(ap, family);
2156         res = ast_load_realtime_helper(family, ap);
2157         va_end(ap);
2158
2159         /* Filter the list. */
2160         prev = &res;
2161         cur = res;
2162         while (cur) {
2163                 if (ast_strlen_zero(cur->value)) {
2164                         /* Eliminate empty entries */
2165                         struct ast_variable *next;
2166
2167                         next = cur->next;
2168                         *prev = next;
2169                         ast_variable_destroy(cur);
2170                         cur = next;
2171                 } else {
2172                         /* Make blank entries empty and keep them. */
2173                         if (cur->value[0] == ' ' && cur->value[1] == '\0') {
2174                                 char *vptr = (char *) cur->value;
2175
2176                                 vptr[0] = '\0';
2177                         }
2178
2179                         prev = &cur->next;
2180                         cur = cur->next;
2181                 }
2182         }
2183         return res;
2184 }
2185
2186 /*! \brief Check if realtime engine is configured for family */
2187 int ast_check_realtime(const char *family)
2188 {
2189         struct ast_config_engine *eng;
2190         if (!ast_realtime_enabled()) {
2191                 return 0;       /* There are no engines at all so fail early */
2192         }
2193
2194         eng = find_engine(family, 1, NULL, 0, NULL, 0);
2195         if (eng)
2196                 return 1;
2197         return 0;
2198 }
2199
2200 /*! \brief Check if there's any realtime engines loaded */
2201 int ast_realtime_enabled()
2202 {
2203         return config_maps ? 1 : 0;
2204 }
2205
2206 int ast_realtime_require_field(const char *family, ...)
2207 {
2208         struct ast_config_engine *eng;
2209         char db[256];
2210         char table[256];
2211         va_list ap;
2212         int res = -1, i;
2213
2214         va_start(ap, family);
2215         for (i = 1; ; i++) {
2216                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2217                         /* If the require succeeds, it returns 0. */
2218                         if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
2219                                 break;
2220                         }
2221                 } else {
2222                         break;
2223                 }
2224         }
2225         va_end(ap);
2226
2227         return res;
2228 }
2229
2230 int ast_unload_realtime(const char *family)
2231 {
2232         struct ast_config_engine *eng;
2233         char db[256];
2234         char table[256];
2235         int res = -1, i;
2236
2237         for (i = 1; ; i++) {
2238                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2239                         if (eng->unload_func) {
2240                                 /* Do this for ALL engines */
2241                                 res = eng->unload_func(db, table);
2242                         }
2243                 } else {
2244                         break;
2245                 }
2246         }
2247         return res;
2248 }
2249
2250 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2251 {
2252         struct ast_config_engine *eng;
2253         char db[256];
2254         char table[256];
2255         struct ast_config *res = NULL;
2256         va_list ap;
2257         int i;
2258
2259         va_start(ap, family);
2260         for (i = 1; ; i++) {
2261                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2262                         if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, ap))) {
2263                                 break;
2264                         }
2265                 } else {
2266                         break;
2267                 }
2268         }
2269         va_end(ap);
2270
2271         return res;
2272 }
2273
2274 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2275 {
2276         struct ast_config_engine *eng;
2277         int res = -1, i;
2278         char db[256];
2279         char table[256];
2280         va_list ap;
2281
2282         va_start(ap, lookup);
2283         for (i = 1; ; i++) {
2284                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2285                         /* If the update succeeds, it returns 0. */
2286                         if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, ap))) {
2287                                 break;
2288                         }
2289                 } else {
2290                         break;
2291                 }
2292         }
2293         va_end(ap);
2294
2295         return res;
2296 }
2297
2298 int ast_update2_realtime(const char *family, ...)
2299 {
2300         struct ast_config_engine *eng;
2301         int res = -1, i;
2302         char db[256];
2303         char table[256];
2304         va_list ap;
2305
2306         va_start(ap, family);
2307         for (i = 1; ; i++) {
2308                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2309                         if (eng->update2_func && !(res = eng->update2_func(db, table, ap))) {
2310                                 break;
2311                         }
2312                 } else {
2313                         break;
2314                 }
2315         }
2316         va_end(ap);
2317
2318         return res;
2319 }
2320
2321 int ast_store_realtime(const char *family, ...)
2322 {
2323         struct ast_config_engine *eng;
2324         int res = -1, i;
2325         char db[256];
2326         char table[256];
2327         va_list ap;
2328
2329         va_start(ap, family);
2330         for (i = 1; ; i++) {
2331                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2332                         /* If the store succeeds, it returns 0. */
2333                         if (eng->store_func && !(res = eng->store_func(db, table, ap))) {
2334                                 break;
2335                         }
2336                 } else {
2337                         break;
2338                 }
2339         }
2340         va_end(ap);
2341
2342         return res;
2343 }
2344
2345 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2346 {
2347         struct ast_config_engine *eng;
2348         int res = -1, i;
2349         char db[256];
2350         char table[256];
2351         va_list ap;
2352
2353         va_start(ap, lookup);
2354         for (i = 1; ; i++) {
2355                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2356                         if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, ap))) {
2357                                 break;
2358                         }
2359                 } else {
2360                         break;
2361                 }
2362         }
2363         va_end(ap);
2364
2365         return res;
2366 }
2367
2368 char *ast_realtime_decode_chunk(char *chunk)
2369 {
2370         char *orig = chunk;
2371         for (; *chunk; chunk++) {
2372                 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
2373                         sscanf(chunk + 1, "%02hhX", chunk);
2374                         memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
2375                 }
2376         }
2377         return orig;
2378 }
2379
2380 char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
2381 {
2382         if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
2383                 ast_str_set(dest, maxlen, "%s", chunk);
2384         } else {
2385                 ast_str_reset(*dest);
2386                 for (; *chunk; chunk++) {
2387                         if (strchr(";^", *chunk)) {
2388                                 ast_str_append(dest, maxlen, "^%02hhX", *chunk);
2389                         } else {
2390                                 ast_str_append(dest, maxlen, "%c", *chunk);
2391                         }
2392                 }
2393         }
2394         return ast_str_buffer(*dest);
2395 }
2396
2397 /*! \brief Helper function to parse arguments
2398  * See documentation in config.h
2399  */
2400 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2401         void *p_result, ...)
2402 {
2403         va_list ap;
2404         int error = 0;
2405
2406         va_start(ap, p_result);
2407         switch (flags & PARSE_TYPE) {
2408         case PARSE_INT32:
2409             {
2410                 int32_t *result = p_result;
2411                 int32_t x, def = result ? *result : 0,
2412                         high = (int32_t)0x7fffffff,
2413                         low  = (int32_t)0x80000000;
2414                 /* optional argument: first default value, then range */
2415                 if (flags & PARSE_DEFAULT)
2416                         def = va_arg(ap, int32_t);
2417                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2418                         /* range requested, update bounds */
2419                         low = va_arg(ap, int32_t);
2420                         high = va_arg(ap, int32_t);
2421                 }
2422                 x = strtol(arg, NULL, 0);
2423                 error = (x < low) || (x > high);
2424                 if (flags & PARSE_OUT_RANGE)
2425                         error = !error;
2426                 if (result)
2427                         *result  = error ? def : x;
2428                 ast_debug(3,
2429                         "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2430                         arg, low, high,
2431                         result ? *result : x, error);
2432                 break;
2433             }
2434
2435         case PARSE_UINT32:
2436             {
2437                 uint32_t *result = p_result;
2438                 uint32_t x, def = result ? *result : 0,
2439                         low = 0, high = (uint32_t)~0;
2440                 /* optional argument: first default value, then range */
2441                 if (flags & PARSE_DEFAULT)
2442                         def = va_arg(ap, uint32_t);
2443                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2444                         /* range requested, update bounds */
2445                         low = va_arg(ap, uint32_t);
2446                         high = va_arg(ap, uint32_t);
2447                 }
2448                 x = strtoul(arg, NULL, 0);
2449                 error = (x < low) || (x > high);
2450                 if (flags & PARSE_OUT_RANGE)
2451                         error = !error;
2452                 if (result)
2453                         *result  = error ? def : x;
2454                 ast_debug(3,
2455                         "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2456                         arg, low, high,
2457                         result ? *result : x, error);
2458                 break;
2459             }
2460
2461         case PARSE_DOUBLE:
2462             {
2463                 double *result = p_result;
2464                 double x, def = result ? *result : 0,
2465                         low = -HUGE_VAL, high = HUGE_VAL;
2466
2467                 /* optional argument: first default value, then range */
2468                 if (flags & PARSE_DEFAULT)
2469                         def = va_arg(ap, double);
2470                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2471                         /* range requested, update bounds */
2472                         low = va_arg(ap, double);
2473                         high = va_arg(ap, double);
2474                 }
2475                 x = strtod(arg, NULL);
2476                 error = (x < low) || (x > high);
2477                 if (flags & PARSE_OUT_RANGE)
2478                         error = !error;
2479                 if (result)
2480                         *result  = error ? def : x;
2481                 ast_debug(3,
2482                         "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
2483                         arg, low, high,
2484                         result ? *result : x, error);
2485                 break;
2486             }
2487         case PARSE_ADDR:
2488             {
2489                 struct ast_sockaddr *addr = (struct ast_sockaddr *)p_result;
2490
2491                 if (!ast_sockaddr_parse(addr, arg, flags & PARSE_PORT_MASK)) {
2492                         error = 1;
2493                 }
2494
2495                 ast_debug(3, "extract addr from %s gives %s(%d)\n",
2496                           arg, ast_sockaddr_stringify(addr), error);
2497
2498                 break;
2499             }
2500         case PARSE_INADDR:      /* TODO Remove this (use PARSE_ADDR instead). */
2501             {
2502                 char *port, *buf;
2503                 struct sockaddr_in _sa_buf;     /* buffer for the result */
2504                 struct sockaddr_in *sa = p_result ?
2505                         (struct sockaddr_in *)p_result : &_sa_buf;
2506                 /* default is either the supplied value or the result itself */
2507                 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2508                         va_arg(ap, struct sockaddr_in *) : sa;
2509                 struct hostent *hp;
2510                 struct ast_hostent ahp;
2511
2512                 memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
2513                 /* duplicate the string to strip away the :port */
2514                 port = ast_strdupa(arg);
2515                 buf = strsep(&port, ":");
2516                 sa->sin_family = AF_INET;       /* assign family */
2517                 /*
2518                  * honor the ports flag setting, assign default value
2519                  * in case of errors or field unset.
2520                  */
2521                 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2522                 if (port) {
2523                         if (flags == PARSE_PORT_FORBID) {
2524                                 error = 1;      /* port was forbidden */
2525                                 sa->sin_port = def->sin_port;
2526                         } else if (flags == PARSE_PORT_IGNORE)
2527                                 sa->sin_port = def->sin_port;
2528                         else /* accept or require */
2529                                 sa->sin_port = htons(strtol(port, NULL, 0));
2530                 } else {
2531                         sa->sin_port = def->sin_port;
2532                         if (flags == PARSE_PORT_REQUIRE)
2533                                 error = 1;
2534                 }
2535                 /* Now deal with host part, even if we have errors before. */
2536                 hp = ast_gethostbyname(buf, &ahp);
2537                 if (hp) /* resolved successfully */
2538                         memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2539                 else {
2540                         error = 1;
2541                         sa->sin_addr = def->sin_addr;
2542                 }
2543                 ast_debug(3,
2544                         "extract inaddr from [%s] gives [%s:%d](%d)\n",
2545                         arg, ast_inet_ntoa(sa->sin_addr),
2546                         ntohs(sa->sin_port), error);
2547                 break;
2548             }
2549         }
2550         va_end(ap);
2551         return error;
2552 }
2553
2554 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2555 {
2556         struct ast_config_engine *eng;
2557         struct ast_config_map *map;
2558
2559         switch (cmd) {
2560         case CLI_INIT:
2561                 e->command = "core show config mappings";
2562                 e->usage =
2563                         "Usage: core show config mappings\n"
2564                         "       Shows the filenames to config engines.\n";
2565                 return NULL;
2566         case CLI_GENERATE:
2567                 return NULL;
2568         }
2569         
2570         ast_mutex_lock(&config_lock);
2571
2572         if (!config_engine_list) {
2573                 ast_cli(a->fd, "No config mappings found.\n");
2574         } else {
2575                 for (eng = config_engine_list; eng; eng = eng->next) {
2576                         ast_cli(a->fd, "Config Engine: %s\n", eng->name);
2577                         for (map = config_maps; map; map = map->next) {
2578                                 if (!strcasecmp(map->driver, eng->name)) {
2579                                         ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2580                                                         map->table ? map->table : map->name);
2581                                 }
2582                         }
2583                 }
2584         }
2585         
2586         ast_mutex_unlock(&config_lock);
2587
2588         return CLI_SUCCESS;
2589 }
2590
2591 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2592 {
2593         struct cache_file_mtime *cfmtime;
2594         char *prev = "", *completion_value = NULL;
2595         int wordlen, which = 0;
2596
2597         switch (cmd) {
2598         case CLI_INIT:
2599                 e->command = "config reload";
2600                 e->usage =
2601                         "Usage: config reload <filename.conf>\n"
2602                         "   Reloads all modules that reference <filename.conf>\n";
2603                 return NULL;
2604         case CLI_GENERATE:
2605                 if (a->pos > 2) {
2606                         return NULL;
2607                 }
2608
2609                 wordlen = strlen(a->word);
2610
2611                 AST_LIST_LOCK(&cfmtime_head);
2612                 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2613                         /* Skip duplicates - this only works because the list is sorted by filename */
2614                         if (strcmp(cfmtime->filename, prev) == 0) {
2615                                 continue;
2616                         }
2617
2618                         /* Core configs cannot be reloaded */
2619                         if (ast_strlen_zero(cfmtime->who_asked)) {
2620                                 continue;
2621                         }
2622
2623                         if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
2624                                 completion_value = ast_strdup(cfmtime->filename);
2625                                 break;
2626                         }
2627
2628                         /* Otherwise save that we've seen this filename */
2629                         prev = cfmtime->filename;
2630                 }
2631                 AST_LIST_UNLOCK(&cfmtime_head);
2632
2633                 return completion_value;
2634         }
2635
2636         if (a->argc != 3) {
2637                 return CLI_SHOWUSAGE;
2638         }
2639
2640         AST_LIST_LOCK(&cfmtime_head);
2641         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2642                 if (!strcmp(cfmtime->filename, a->argv[2])) {
2643                         char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
2644                         sprintf(buf, "module reload %s", cfmtime->who_asked);
2645                         ast_cli_command(a->fd, buf);
2646                 }
2647         }
2648         AST_LIST_UNLOCK(&cfmtime_head);
2649
2650         return CLI_SUCCESS;
2651 }
2652
2653 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2654 {
2655         struct cache_file_mtime *cfmtime;
2656
2657         switch (cmd) {
2658         case CLI_INIT:
2659                 e->command = "config list";
2660                 e->usage =
2661                         "Usage: config list\n"
2662                         "   Show all modules that have loaded a configuration file\n";
2663                 return NULL;
2664         case CLI_GENERATE:
2665                 return NULL;
2666         }
2667
2668         AST_LIST_LOCK(&cfmtime_head);
2669         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2670                 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
2671         }
2672         AST_LIST_UNLOCK(&cfmtime_head);
2673
2674         return CLI_SUCCESS;
2675 }
2676
2677 static struct ast_cli_entry cli_config[] = {
2678         AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
2679         AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
2680         AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
2681 };
2682
2683 int register_config_cli() 
2684 {
2685         ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
2686         return 0;
2687 }