c0e1669e6c8ca610a4cf7a6f3b891d6285625a2b
[asterisk/asterisk.git] / main / config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, 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 doc/realtime.txt and doc/extconfig.txt
27  */
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <time.h>
39 #include <sys/stat.h>
40 #include <sys/socket.h>         /* for AF_INET */
41 #define AST_INCLUDE_GLOB 1
42 #ifdef AST_INCLUDE_GLOB
43 #if defined(__Darwin__) || defined(__CYGWIN__)
44 #define GLOB_ABORTED GLOB_ABEND
45 #endif
46 # include <glob.h>
47 #endif
48
49 #include "asterisk/config.h"
50 #include "asterisk/cli.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/options.h"
53 #include "asterisk/logger.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/channel.h"
56 #include "asterisk/app.h"
57
58 #define MAX_NESTED_COMMENTS 128
59 #define COMMENT_START ";--"
60 #define COMMENT_END "--;"
61 #define COMMENT_META ';'
62 #define COMMENT_TAG '-'
63
64 static char *extconfig_conf = "extconfig.conf";
65
66
67 /*! \brief Structure to keep comments for rewriting configuration files */
68 struct ast_comment {
69         struct ast_comment *next;
70         char cmt[0];
71 };
72
73 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
74 struct cache_file_include {
75         AST_LIST_ENTRY(cache_file_include) list;
76         char include[0];
77 };
78
79 struct cache_file_mtime {
80         AST_LIST_ENTRY(cache_file_mtime) list;
81         AST_LIST_HEAD(includes, cache_file_include) includes;
82         unsigned int has_exec:1;
83         time_t mtime;
84         char filename[0];
85 };
86
87 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
88
89 #define CB_INCR 250
90
91 static void CB_INIT(char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
92 {
93         if (!(*comment_buffer)) {
94                 *comment_buffer = ast_malloc(CB_INCR);
95                 if (!(*comment_buffer))
96                         return;
97                 (*comment_buffer)[0] = 0;
98                 *comment_buffer_size = CB_INCR;
99                 *lline_buffer = ast_malloc(CB_INCR);
100                 if (!(*lline_buffer))
101                         return;
102                 (*lline_buffer)[0] = 0;
103                 *lline_buffer_size = CB_INCR;
104         } else {
105                 (*comment_buffer)[0] = 0;
106                 (*lline_buffer)[0] = 0;
107         }
108 }
109
110 static void  CB_ADD(char **comment_buffer, int *comment_buffer_size, char *str)
111 {
112         int rem = *comment_buffer_size - strlen(*comment_buffer) - 1;
113         int siz = strlen(str);
114         if (rem < siz+1) {
115                 *comment_buffer = ast_realloc(*comment_buffer, *comment_buffer_size + CB_INCR + siz + 1);
116                 if (!(*comment_buffer))
117                         return;
118                 *comment_buffer_size += CB_INCR+siz+1;
119         }
120         strcat(*comment_buffer,str);
121 }
122
123 static void  CB_ADD_LEN(char **comment_buffer, int *comment_buffer_size, char *str, int len)
124 {
125         int cbl = strlen(*comment_buffer) + 1;
126         int rem = *comment_buffer_size - cbl;
127         if (rem < len+1) {
128                 *comment_buffer = ast_realloc(*comment_buffer, *comment_buffer_size + CB_INCR + len + 1);
129                 if (!(*comment_buffer))
130                         return;
131                 *comment_buffer_size += CB_INCR+len+1;
132         }
133         strncat(*comment_buffer,str,len);
134         (*comment_buffer)[cbl+len-1] = 0;
135 }
136
137 static void  LLB_ADD(char **lline_buffer, int *lline_buffer_size, char *str)
138 {
139         int rem = *lline_buffer_size - strlen(*lline_buffer) - 1;
140         int siz = strlen(str);
141         if (rem < siz+1) {
142                 *lline_buffer = ast_realloc(*lline_buffer, *lline_buffer_size + CB_INCR + siz + 1);
143                 if (!(*lline_buffer)) 
144                         return;
145                 *lline_buffer_size += CB_INCR + siz + 1;
146         }
147         strcat(*lline_buffer,str);
148 }
149
150 static void CB_RESET(char **comment_buffer, char **lline_buffer)  
151
152         (*comment_buffer)[0] = 0; 
153         (*lline_buffer)[0] = 0;
154 }
155
156
157 static struct ast_comment *ALLOC_COMMENT(const char *buffer)
158
159         struct ast_comment *x;
160         x = ast_calloc(1, sizeof(*x)+strlen(buffer)+1);
161         strcpy(x->cmt, buffer);
162         return x;
163 }
164
165
166 static struct ast_config_map {
167         struct ast_config_map *next;
168         char *name;
169         char *driver;
170         char *database;
171         char *table;
172         char stuff[0];
173 } *config_maps = NULL;
174
175 AST_MUTEX_DEFINE_STATIC(config_lock);
176 static struct ast_config_engine *config_engine_list;
177
178 #define MAX_INCLUDE_LEVEL 10
179
180 struct ast_category {
181         char name[80];
182         int ignored;                    /*!< do not let user of the config see this category */
183         int include_level;      
184         struct ast_comment *precomments;
185         struct ast_comment *sameline;
186         struct ast_variable *root;
187         struct ast_variable *last;
188         struct ast_category *next;
189 };
190
191 struct ast_config {
192         struct ast_category *root;
193         struct ast_category *last;
194         struct ast_category *current;
195         struct ast_category *last_browse;               /*!< used to cache the last category supplied via category_browse */
196         int include_level;
197         int max_include_level;
198 };
199
200 struct ast_variable *ast_variable_new(const char *name, const char *value) 
201 {
202         struct ast_variable *variable;
203         int name_len = strlen(name) + 1;        
204
205         if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + sizeof(*variable)))) {
206                 variable->name = variable->stuff;
207                 variable->value = variable->stuff + name_len;           
208                 strcpy(variable->name,name);
209                 strcpy(variable->value,value);
210         }
211
212         return variable;
213 }
214
215 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
216 {
217         if (!variable)
218                 return;
219         if (category->last)
220                 category->last->next = variable;
221         else
222                 category->root = variable;
223         category->last = variable;
224         while (category->last->next)
225                 category->last = category->last->next;
226 }
227
228 void ast_variables_destroy(struct ast_variable *v)
229 {
230         struct ast_variable *vn;
231
232         while (v) {
233                 vn = v;
234                 v = v->next;
235                 ast_free(vn);
236         }
237 }
238
239 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
240 {
241         struct ast_category *cat = NULL;
242
243         if (category && config->last_browse && (config->last_browse->name == category))
244                 cat = config->last_browse;
245         else
246                 cat = ast_category_get(config, category);
247
248         return (cat) ? cat->root : NULL;
249 }
250
251 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
252 {
253         const char *tmp;
254         tmp = ast_variable_retrieve(cfg, cat, var);
255         if (!tmp)
256                 tmp = ast_variable_retrieve(cfg, "general", var);
257         return tmp;
258 }
259
260
261 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
262 {
263         struct ast_variable *v;
264
265         if (category) {
266                 for (v = ast_variable_browse(config, category); v; v = v->next) {
267                         if (!strcasecmp(variable, v->name))
268                                 return v->value;
269                 }
270         } else {
271                 struct ast_category *cat;
272
273                 for (cat = config->root; cat; cat = cat->next)
274                         for (v = cat->root; v; v = v->next)
275                                 if (!strcasecmp(variable, v->name))
276                                         return v->value;
277         }
278
279         return NULL;
280 }
281
282 static struct ast_variable *variable_clone(const struct ast_variable *old)
283 {
284         struct ast_variable *new = ast_variable_new(old->name, old->value);
285
286         if (new) {
287                 new->lineno = old->lineno;
288                 new->object = old->object;
289                 new->blanklines = old->blanklines;
290                 /* TODO: clone comments? */
291         }
292
293         return new;
294 }
295  
296 static void move_variables(struct ast_category *old, struct ast_category *new)
297 {
298         struct ast_variable *var = old->root;
299         old->root = NULL;
300 #if 1
301         /* we can just move the entire list in a single op */
302         ast_variable_append(new, var);
303 #else
304         while (var) {
305                 struct ast_variable *next = var->next;
306                 var->next = NULL;
307                 ast_variable_append(new, var);
308                 var = next;
309         }
310 #endif
311 }
312
313 struct ast_category *ast_category_new(const char *name) 
314 {
315         struct ast_category *category;
316
317         if ((category = ast_calloc(1, sizeof(*category))))
318                 ast_copy_string(category->name, name, sizeof(category->name));
319         return category;
320 }
321
322 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
323 {
324         struct ast_category *cat;
325
326         /* try exact match first, then case-insensitive match */
327         for (cat = config->root; cat; cat = cat->next) {
328                 if (cat->name == category_name && (ignored || !cat->ignored))
329                         return cat;
330         }
331
332         for (cat = config->root; cat; cat = cat->next) {
333                 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
334                         return cat;
335         }
336
337         return NULL;
338 }
339
340 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
341 {
342         return category_get(config, category_name, 0);
343 }
344
345 int ast_category_exist(const struct ast_config *config, const char *category_name)
346 {
347         return !!ast_category_get(config, category_name);
348 }
349
350 void ast_category_append(struct ast_config *config, struct ast_category *category)
351 {
352         if (config->last)
353                 config->last->next = category;
354         else
355                 config->root = category;
356         category->include_level = config->include_level;
357         config->last = category;
358         config->current = category;
359 }
360
361 void ast_category_destroy(struct ast_category *cat)
362 {
363         ast_variables_destroy(cat->root);
364         ast_free(cat);
365 }
366
367 static struct ast_category *next_available_category(struct ast_category *cat)
368 {
369         for (; cat && cat->ignored; cat = cat->next);
370
371         return cat;
372 }
373
374 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
375 {
376         struct ast_category *category = ast_category_get(config, cat);
377         if (category)
378                 return category->root;
379         return NULL;
380 }
381
382 char *ast_category_browse(struct ast_config *config, const char *prev)
383 {       
384         struct ast_category *cat = NULL;
385
386         if (prev && config->last_browse && (config->last_browse->name == prev))
387                 cat = config->last_browse->next;
388         else if (!prev && config->root)
389                 cat = config->root;
390         else if (prev) {
391                 for (cat = config->root; cat; cat = cat->next) {
392                         if (cat->name == prev) {
393                                 cat = cat->next;
394                                 break;
395                         }
396                 }
397                 if (!cat) {
398                         for (cat = config->root; cat; cat = cat->next) {
399                                 if (!strcasecmp(cat->name, prev)) {
400                                         cat = cat->next;
401                                         break;
402                                 }
403                         }
404                 }
405         }
406         
407         if (cat)
408                 cat = next_available_category(cat);
409
410         config->last_browse = cat;
411         return (cat) ? cat->name : NULL;
412 }
413
414 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
415 {
416         struct ast_variable *v;
417
418         v = cat->root;
419         cat->root = NULL;
420         cat->last = NULL;
421
422         return v;
423 }
424
425 void ast_category_rename(struct ast_category *cat, const char *name)
426 {
427         ast_copy_string(cat->name, name, sizeof(cat->name));
428 }
429
430 static void inherit_category(struct ast_category *new, const struct ast_category *base)
431 {
432         struct ast_variable *var;
433
434         for (var = base->root; var; var = var->next)
435                 ast_variable_append(new, variable_clone(var));
436 }
437
438 struct ast_config *ast_config_new(void) 
439 {
440         struct ast_config *config;
441
442         if ((config = ast_calloc(1, sizeof(*config))))
443                 config->max_include_level = MAX_INCLUDE_LEVEL;
444         return config;
445 }
446
447 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match)
448 {
449         struct ast_variable *cur, *prev=NULL, *curn;
450         int res = -1;
451         cur = category->root;
452         while (cur) {
453                 if (cur->name == variable) {
454                         if (prev) {
455                                 prev->next = cur->next;
456                                 if (cur == category->last)
457                                         category->last = prev;
458                         } else {
459                                 category->root = cur->next;
460                                 if (cur == category->last)
461                                         category->last = NULL;
462                         }
463                         cur->next = NULL;
464                         ast_variables_destroy(cur);
465                         return 0;
466                 }
467                 prev = cur;
468                 cur = cur->next;
469         }
470
471         prev = NULL;
472         cur = category->root;
473         while (cur) {
474                 curn = cur->next;
475                 if (!strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match))) {
476                         if (prev) {
477                                 prev->next = cur->next;
478                                 if (cur == category->last)
479                                         category->last = prev;
480                         } else {
481                                 category->root = cur->next;
482                                 if (cur == category->last)
483                                         category->last = NULL;
484                         }
485                         cur->next = NULL;
486                         ast_variables_destroy(cur);
487                         res = 0;
488                 } else
489                         prev = cur;
490
491                 cur = curn;
492         }
493         return res;
494 }
495
496 int ast_variable_update(struct ast_category *category, const char *variable, 
497         const char *value, const char *match, unsigned int object)
498 {
499         struct ast_variable *cur, *prev=NULL, *newer;
500
501         if (!(newer = ast_variable_new(variable, value)))
502                 return -1;
503         
504         newer->object = object;
505
506         for (cur = category->root; cur; prev = cur, cur = cur->next) {
507                 if (strcasecmp(cur->name, variable) ||
508                         (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
509                         continue;
510
511                 newer->next = cur->next;
512                 newer->object = cur->object || object;
513                 if (prev)
514                         prev->next = newer;
515                 else
516                         category->root = newer;
517                 if (category->last == cur)
518                         category->last = newer;
519
520                 cur->next = NULL;
521                 ast_variables_destroy(cur);
522
523                 return 0;
524         }
525
526         if (prev)
527                 prev->next = newer;
528         else
529                 category->root = newer;
530
531         return 0;
532 }
533
534 int ast_category_delete(struct ast_config *cfg, const char *category)
535 {
536         struct ast_category *prev=NULL, *cat;
537         cat = cfg->root;
538         while (cat) {
539                 if (cat->name == category) {
540                         ast_variables_destroy(cat->root);
541                         if (prev) {
542                                 prev->next = cat->next;
543                                 if (cat == cfg->last)
544                                         cfg->last = prev;
545                         } else {
546                                 cfg->root = cat->next;
547                                 if (cat == cfg->last)
548                                         cfg->last = NULL;
549                         }
550                         ast_free(cat);
551                         return 0;
552                 }
553                 prev = cat;
554                 cat = cat->next;
555         }
556
557         prev = NULL;
558         cat = cfg->root;
559         while (cat) {
560                 if (!strcasecmp(cat->name, category)) {
561                         ast_variables_destroy(cat->root);
562                         if (prev) {
563                                 prev->next = cat->next;
564                                 if (cat == cfg->last)
565                                         cfg->last = prev;
566                         } else {
567                                 cfg->root = cat->next;
568                                 if (cat == cfg->last)
569                                         cfg->last = NULL;
570                         }
571                         ast_free(cat);
572                         return 0;
573                 }
574                 prev = cat;
575                 cat = cat->next;
576         }
577         return -1;
578 }
579
580 void ast_config_destroy(struct ast_config *cfg)
581 {
582         struct ast_category *cat, *catn;
583
584         if (!cfg)
585                 return;
586
587         cat = cfg->root;
588         while (cat) {
589                 ast_variables_destroy(cat->root);
590                 catn = cat;
591                 cat = cat->next;
592                 ast_free(catn);
593         }
594         ast_free(cfg);
595 }
596
597 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
598 {
599         return cfg->current;
600 }
601
602 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
603 {
604         /* cast below is just to silence compiler warning about dropping "const" */
605         cfg->current = (struct ast_category *) cat;
606 }
607
608 enum config_cache_attribute_enum {
609         ATTRIBUTE_INCLUDE = 0,
610         ATTRIBUTE_EXEC = 1,
611 };
612
613 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename)
614 {
615         struct cache_file_mtime *cfmtime;
616         struct cache_file_include *cfinclude;
617         struct stat statbuf = { 0, };
618
619         /* Find our cached entry for this configuration file */
620         AST_LIST_LOCK(&cfmtime_head);
621         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
622                 if (!strcmp(cfmtime->filename, configfile))
623                         break;
624         }
625         if (!cfmtime) {
626                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1);
627                 if (!cfmtime) {
628                         AST_LIST_UNLOCK(&cfmtime_head);
629                         return;
630                 }
631                 AST_LIST_HEAD_INIT(&cfmtime->includes);
632                 strcpy(cfmtime->filename, configfile);
633                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
634                 AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
635         }
636
637         if (!stat(configfile, &statbuf))
638                 cfmtime->mtime = 0;
639         else
640                 cfmtime->mtime = statbuf.st_mtime;
641
642         switch (attrtype) {
643         case ATTRIBUTE_INCLUDE:
644                 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
645                 if (!cfinclude) {
646                         AST_LIST_UNLOCK(&cfmtime_head);
647                         return;
648                 }
649                 strcpy(cfinclude->include, filename);
650                 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
651                 break;
652         case ATTRIBUTE_EXEC:
653                 cfmtime->has_exec = 1;
654                 break;
655         }
656         AST_LIST_UNLOCK(&cfmtime_head);
657 }
658
659 static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, struct ast_flags flags,
660                                 char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
661 {
662         char *c;
663         char *cur = buf;
664         struct ast_variable *v;
665         char cmd[512], exec_file[512];
666         int object, do_exec, do_include;
667
668         /* Actually parse the entry */
669         if (cur[0] == '[') {
670                 struct ast_category *newcat = NULL;
671                 char *catname;
672
673                 /* A category header */
674                 c = strchr(cur, ']');
675                 if (!c) {
676                         ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
677                         return -1;
678                 }
679                 *c++ = '\0';
680                 cur++;
681                 if (*c++ != '(')
682                         c = NULL;
683                 catname = cur;
684                 if (!(*cat = newcat = ast_category_new(catname))) {
685                         return -1;
686                 }
687                 /* add comments */
688                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *comment_buffer && (*comment_buffer)[0] ) {
689                         newcat->precomments = ALLOC_COMMENT(*comment_buffer);
690                 }
691                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *lline_buffer && (*lline_buffer)[0] ) {
692                         newcat->sameline = ALLOC_COMMENT(*lline_buffer);
693                 }
694                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
695                         CB_RESET(comment_buffer, lline_buffer);
696                 
697                 /* If there are options or categories to inherit from, process them now */
698                 if (c) {
699                         if (!(cur = strchr(c, ')'))) {
700                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
701                                 return -1;
702                         }
703                         *cur = '\0';
704                         while ((cur = strsep(&c, ","))) {
705                                 if (!strcasecmp(cur, "!")) {
706                                         (*cat)->ignored = 1;
707                                 } else if (!strcasecmp(cur, "+")) {
708                                         *cat = category_get(cfg, catname, 1);
709                                         if (!(*cat)) {
710                                                 if (newcat)
711                                                         ast_category_destroy(newcat);
712                                                 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
713                                                 return -1;
714                                         }
715                                         if (newcat) {
716                                                 move_variables(newcat, *cat);
717                                                 ast_category_destroy(newcat);
718                                                 newcat = NULL;
719                                         }
720                                 } else {
721                                         struct ast_category *base;
722                                 
723                                         base = category_get(cfg, cur, 1);
724                                         if (!base) {
725                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
726                                                 return -1;
727                                         }
728                                         inherit_category(*cat, base);
729                                 }
730                         }
731                 }
732                 if (newcat)
733                         ast_category_append(cfg, *cat);
734         } else if (cur[0] == '#') {
735                 /* A directive */
736                 cur++;
737                 c = cur;
738                 while (*c && (*c > 32)) c++;
739                 if (*c) {
740                         *c = '\0';
741                         /* Find real argument */
742                         c = ast_skip_blanks(c + 1);
743                         if (!(*c))
744                                 c = NULL;
745                 } else 
746                         c = NULL;
747                 do_include = !strcasecmp(cur, "include");
748                 if (!do_include)
749                         do_exec = !strcasecmp(cur, "exec");
750                 else
751                         do_exec = 0;
752                 if (do_exec && !ast_opt_exec_includes) {
753                         ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
754                         do_exec = 0;
755                 }
756                 if (do_include || do_exec) {
757                         if (c) {
758                                 /* Strip off leading and trailing "'s and <>'s */
759                                 while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
760                                 /* Get rid of leading mess */
761                                 cur = c;
762                                 while (!ast_strlen_zero(cur)) {
763                                         c = cur + strlen(cur) - 1;
764                                         if ((*c == '>') || (*c == '<') || (*c == '\"'))
765                                                 *c = '\0';
766                                         else
767                                                 break;
768                                 }
769                                 /* #exec </path/to/executable>
770                                    We create a tmp file, then we #include it, then we delete it. */
771                                 if (do_exec) {
772                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
773                                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL);
774                                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self());
775                                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
776                                         ast_safe_system(cmd);
777                                         cur = exec_file;
778                                 } else {
779                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
780                                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur);
781                                         exec_file[0] = '\0';
782                                 }
783                                 /* A #include */
784                                 do_include = ast_config_internal_load(cur, cfg, flags) ? 1 : 0;
785                                 if (!ast_strlen_zero(exec_file))
786                                         unlink(exec_file);
787                                 if (!do_include)
788                                         return 0;
789
790                         } else {
791                                 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 
792                                                 do_exec ? "exec" : "include",
793                                                 do_exec ? "/path/to/executable" : "filename",
794                                                 lineno,
795                                                 configfile);
796                         }
797                 }
798                 else 
799                         ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
800         } else {
801                 /* Just a line (variable = value) */
802                 if (!(*cat)) {
803                         ast_log(LOG_WARNING,
804                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
805                         return -1;
806                 }
807                 c = strchr(cur, '=');
808                 if (c) {
809                         *c = 0;
810                         c++;
811                         /* Ignore > in => */
812                         if (*c== '>') {
813                                 object = 1;
814                                 c++;
815                         } else
816                                 object = 0;
817                         if ((v = ast_variable_new(ast_strip(cur), ast_strip(c)))) {
818                                 v->lineno = lineno;
819                                 v->object = object;
820                                 /* Put and reset comments */
821                                 v->blanklines = 0;
822                                 ast_variable_append(*cat, v);
823                                 /* add comments */
824                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *comment_buffer && (*comment_buffer)[0] ) {
825                                         v->precomments = ALLOC_COMMENT(*comment_buffer);
826                                 }
827                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *lline_buffer && (*lline_buffer)[0] ) {
828                                         v->sameline = ALLOC_COMMENT(*lline_buffer);
829                                 }
830                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
831                                         CB_RESET(comment_buffer, lline_buffer);
832                                 
833                         } else {
834                                 return -1;
835                         }
836                 } else {
837                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
838                 }
839         }
840         return 0;
841 }
842
843 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)
844 {
845         char fn[256];
846         char buf[8192];
847         char *new_buf, *comment_p, *process_buf;
848         FILE *f;
849         int lineno=0;
850         int comment = 0, nest[MAX_NESTED_COMMENTS];
851         struct ast_category *cat = NULL;
852         int count = 0;
853         struct stat statbuf;
854         struct cache_file_mtime *cfmtime = NULL;
855         struct cache_file_include *cfinclude;
856         /*! Growable string buffer */
857         char *comment_buffer=0;   /*!< this will be a comment collector.*/
858         int   comment_buffer_size=0;  /*!< the amount of storage so far alloc'd for the comment_buffer */
859
860         char *lline_buffer=0;    /*!< A buffer for stuff behind the ; */
861         int  lline_buffer_size=0;
862
863         if (cfg)
864                 cat = ast_config_get_current_category(cfg);
865
866         if (filename[0] == '/') {
867                 ast_copy_string(fn, filename, sizeof(fn));
868         } else {
869                 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
870         }
871
872         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
873                 CB_INIT(&comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size);
874                 if (!lline_buffer || !comment_buffer) {
875                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
876                         return NULL;
877                 }
878         }
879 #ifdef AST_INCLUDE_GLOB
880         {
881                 int glob_ret;
882                 glob_t globbuf;
883                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
884 #ifdef SOLARIS
885                 glob_ret = glob(fn, GLOB_NOCHECK, NULL, &globbuf);
886 #else
887                 glob_ret = glob(fn, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
888 #endif
889                 if (glob_ret == GLOB_NOSPACE)
890                         ast_log(LOG_WARNING,
891                                 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
892                 else if (glob_ret  == GLOB_ABORTED)
893                         ast_log(LOG_WARNING,
894                                 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
895                 else  {
896                         /* loop over expanded files */
897                         int i;
898                         for (i=0; i<globbuf.gl_pathc; i++) {
899                                 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
900 #endif
901         do {
902                 if (stat(fn, &statbuf))
903                         continue;
904
905                 if (!S_ISREG(statbuf.st_mode)) {
906                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
907                         continue;
908                 }
909
910                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
911                         /* Find our cached entry for this configuration file */
912                         AST_LIST_LOCK(&cfmtime_head);
913                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
914                                 if (!strcmp(cfmtime->filename, fn))
915                                         break;
916                         }
917                         if (!cfmtime) {
918                                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1);
919                                 if (!cfmtime)
920                                         continue;
921                                 AST_LIST_HEAD_INIT(&cfmtime->includes);
922                                 strcpy(cfmtime->filename, fn);
923                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
924                                 AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
925                         }
926                 }
927
928                 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
929                         /* File is unchanged, what about the (cached) includes (if any)? */
930                         int unchanged = 1;
931                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
932                                 /* We must glob here, because if we did not, then adding a file to globbed directory would
933                                  * incorrectly cause no reload to be necessary. */
934                                 char fn2[256];
935 #ifdef AST_INCLUDE_GLOB
936                                 int glob_ret;
937                                 glob_t globbuf = { .gl_offs = 0 };
938 #ifdef SOLARIS
939                                 glob_ret = glob(cfinclude->include, GLOB_NOCHECK, NULL, &globbuf);
940 #else
941                                 glob_ret = glob(cfinclude->include, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
942 #endif
943                                 /* On error, we reparse */
944                                 if (glob_ret == GLOB_NOSPACE || glob_ret  == GLOB_ABORTED)
945                                         unchanged = 0;
946                                 else  {
947                                         /* loop over expanded files */
948                                         int j;
949                                         for (j = 0; j < globbuf.gl_pathc; j++) {
950                                                 ast_copy_string(fn2, globbuf.gl_pathv[j], sizeof(fn2));
951 #else
952                                                 ast_copy_string(fn2, cfinclude->include);
953 #endif
954                                                 if (config_text_file_load(NULL, NULL, fn2, NULL, flags) == NULL) {
955                                                         unchanged = 0;
956                                                         /* One change is enough to short-circuit and reload the whole shebang */
957                                                         break;
958                                                 }
959 #ifdef AST_INCLUDE_GLOB
960                                         }
961                                 }
962 #endif
963                         }
964
965                         if (unchanged) {
966                                 AST_LIST_UNLOCK(&cfmtime_head);
967                                 return CONFIG_STATUS_FILEUNCHANGED;
968                         }
969                 }
970                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
971                         AST_LIST_UNLOCK(&cfmtime_head);
972
973                 /* If cfg is NULL, then we just want an answer */
974                 if (cfg == NULL)
975                         return NULL;
976
977                 if (cfmtime)
978                         cfmtime->mtime = statbuf.st_mtime;
979
980                 ast_verb(2, "Parsing '%s': ", fn);
981                         fflush(stdout);
982                 if (!(f = fopen(fn, "r"))) {
983                         ast_debug(1, "No file to parse: %s\n", fn);
984                         ast_verb(2, "Not found (%s)\n", strerror(errno));
985                         continue;
986                 }
987                 count++;
988                 ast_debug(1, "Parsing %s\n", fn);
989                 ast_verb(2, "Found\n");
990                 while (!feof(f)) {
991                         lineno++;
992                         if (fgets(buf, sizeof(buf), f)) {
993                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
994                                         CB_ADD(&comment_buffer, &comment_buffer_size, lline_buffer);       /* add the current lline buffer to the comment buffer */
995                                         lline_buffer[0] = 0;        /* erase the lline buffer */
996                                 }
997                                 
998                                 new_buf = buf;
999                                 if (comment) 
1000                                         process_buf = NULL;
1001                                 else
1002                                         process_buf = buf;
1003                                 
1004                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1005                                         if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
1006                                                 /* Escaped semicolons aren't comments. */
1007                                                 new_buf = comment_p + 1;
1008                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1009                                                 /* Meta-Comment start detected ";--" */
1010                                                 if (comment < MAX_NESTED_COMMENTS) {
1011                                                         *comment_p = '\0';
1012                                                         new_buf = comment_p + 3;
1013                                                         comment++;
1014                                                         nest[comment-1] = lineno;
1015                                                 } else {
1016                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1017                                                 }
1018                                         } else if ((comment_p >= new_buf + 2) &&
1019                                                    (*(comment_p - 1) == COMMENT_TAG) &&
1020                                                    (*(comment_p - 2) == COMMENT_TAG)) {
1021                                                 /* Meta-Comment end detected */
1022                                                 comment--;
1023                                                 new_buf = comment_p + 1;
1024                                                 if (!comment) {
1025                                                         /* Back to non-comment now */
1026                                                         if (process_buf) {
1027                                                                 /* Actually have to move what's left over the top, then continue */
1028                                                                 char *oldptr;
1029                                                                 oldptr = process_buf + strlen(process_buf);
1030                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1031                                                                         CB_ADD(&comment_buffer, &comment_buffer_size, ";");
1032                                                                         CB_ADD_LEN(&comment_buffer, &comment_buffer_size, oldptr+1, new_buf-oldptr-1);
1033                                                                 }
1034                                                                 
1035                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1036                                                                 new_buf = oldptr;
1037                                                         } else
1038                                                                 process_buf = new_buf;
1039                                                 }
1040                                         } else {
1041                                                 if (!comment) {
1042                                                         /* If ; is found, and we are not nested in a comment, 
1043                                                            we immediately stop all comment processing */
1044                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1045                                                                 LLB_ADD(&lline_buffer, &lline_buffer_size, comment_p);
1046                                                         }
1047                                                         *comment_p = '\0'; 
1048                                                         new_buf = comment_p;
1049                                                 } else
1050                                                         new_buf = comment_p + 1;
1051                                         }
1052                                 }
1053                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf )
1054                                 {
1055                                         CB_ADD(&comment_buffer, &comment_buffer_size, buf);  /* the whole line is a comment, store it */
1056                                 }
1057                                 
1058                                 if (process_buf) {
1059                                         char *buf = ast_strip(process_buf);
1060                                         if (!ast_strlen_zero(buf)) {
1061                                                 if (process_text_line(cfg, &cat, buf, lineno, fn, flags, &comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size)) {
1062                                                         cfg = NULL;
1063                                                         break;
1064                                                 }
1065                                         }
1066                                 }
1067                         }
1068                 }
1069                 fclose(f);              
1070         } while (0);
1071         if (comment) {
1072                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1073         }
1074 #ifdef AST_INCLUDE_GLOB
1075                                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1076                                                 break;
1077                                 }
1078                                 globfree(&globbuf);
1079                         }
1080                 }
1081 #endif
1082
1083         if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer) {
1084                 ast_free(comment_buffer);
1085                 ast_free(lline_buffer);
1086                 comment_buffer = NULL;
1087                 lline_buffer = NULL;
1088                 comment_buffer_size = 0;
1089                 lline_buffer_size = 0;
1090         }
1091         
1092         if (count == 0)
1093                 return NULL;
1094
1095         return cfg;
1096 }
1097
1098 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1099 {
1100         FILE *f;
1101         char fn[256];
1102         char date[256]="";
1103         time_t t;
1104         struct ast_variable *var;
1105         struct ast_category *cat;
1106         struct ast_comment *cmt;
1107         int blanklines = 0;
1108
1109         if (configfile[0] == '/') {
1110                 ast_copy_string(fn, configfile, sizeof(fn));
1111         } else {
1112                 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1113         }
1114         time(&t);
1115         ast_copy_string(date, ctime(&t), sizeof(date));
1116 #ifdef __CYGWIN__       
1117         if ((f = fopen(fn, "w+"))) {
1118 #else
1119         if ((f = fopen(fn, "w"))) {
1120 #endif      
1121                 ast_verb(2, "Saving '%s': ", fn);
1122                 fprintf(f, ";!\n");
1123                 fprintf(f, ";! Automatically generated configuration file\n");
1124                 if (strcmp(configfile, fn))
1125                         fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
1126                 else
1127                         fprintf(f, ";! Filename: %s\n", configfile);
1128                 fprintf(f, ";! Generator: %s\n", generator);
1129                 fprintf(f, ";! Creation Date: %s", date);
1130                 fprintf(f, ";!\n");
1131                 cat = cfg->root;
1132                 while (cat) {
1133                         /* Dump section with any appropriate comment */
1134                         for (cmt = cat->precomments; cmt; cmt=cmt->next)
1135                         {
1136                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1137                                         fprintf(f,"%s", cmt->cmt);
1138                         }
1139                         if (!cat->precomments)
1140                                 fprintf(f,"\n");
1141                         fprintf(f, "[%s]", cat->name);
1142                         for (cmt = cat->sameline; cmt; cmt=cmt->next)
1143                         {
1144                                 fprintf(f,"%s", cmt->cmt);
1145                         }
1146                         if (!cat->sameline)
1147                                 fprintf(f,"\n");
1148                         var = cat->root;
1149                         while (var) {
1150                                 for (cmt = var->precomments; cmt; cmt=cmt->next)
1151                                 {
1152                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1153                                                 fprintf(f,"%s", cmt->cmt);
1154                                 }
1155                                 if (var->sameline) 
1156                                         fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1157                                 else    
1158                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1159                                 if (var->blanklines) {
1160                                         blanklines = var->blanklines;
1161                                         while (blanklines--)
1162                                                 fprintf(f, "\n");
1163                                 }
1164                                         
1165                                 var = var->next;
1166                         }
1167 #if 0
1168                         /* Put an empty line */
1169                         fprintf(f, "\n");
1170 #endif
1171                         cat = cat->next;
1172                 }
1173                 if (!option_debug)
1174                         ast_verb(2, "Saved\n");
1175         } else {
1176                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1177                 ast_verb(2, "Unable to write (%s)", strerror(errno));
1178                 return -1;
1179         }
1180         fclose(f);
1181         return 0;
1182 }
1183
1184 static void clear_config_maps(void) 
1185 {
1186         struct ast_config_map *map;
1187
1188         ast_mutex_lock(&config_lock);
1189
1190         while (config_maps) {
1191                 map = config_maps;
1192                 config_maps = config_maps->next;
1193                 ast_free(map);
1194         }
1195                 
1196         ast_mutex_unlock(&config_lock);
1197 }
1198
1199 static int append_mapping(char *name, char *driver, char *database, char *table)
1200 {
1201         struct ast_config_map *map;
1202         int length;
1203
1204         length = sizeof(*map);
1205         length += strlen(name) + 1;
1206         length += strlen(driver) + 1;
1207         length += strlen(database) + 1;
1208         if (table)
1209                 length += strlen(table) + 1;
1210
1211         if (!(map = ast_calloc(1, length)))
1212                 return -1;
1213
1214         map->name = map->stuff;
1215         strcpy(map->name, name);
1216         map->driver = map->name + strlen(map->name) + 1;
1217         strcpy(map->driver, driver);
1218         map->database = map->driver + strlen(map->driver) + 1;
1219         strcpy(map->database, database);
1220         if (table) {
1221                 map->table = map->database + strlen(map->database) + 1;
1222                 strcpy(map->table, table);
1223         }
1224         map->next = config_maps;
1225
1226         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
1227
1228         config_maps = map;
1229         return 0;
1230 }
1231
1232 int read_config_maps(void) 
1233 {
1234         struct ast_config *config, *configtmp;
1235         struct ast_variable *v;
1236         char *driver, *table, *database, *stringp, *tmp;
1237         struct ast_flags flags = { 0 };
1238
1239         clear_config_maps();
1240
1241         configtmp = ast_config_new();
1242         configtmp->max_include_level = 1;
1243         config = ast_config_internal_load(extconfig_conf, configtmp, flags);
1244         if (!config) {
1245                 ast_config_destroy(configtmp);
1246                 return 0;
1247         }
1248
1249         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
1250                 stringp = v->value;
1251                 driver = strsep(&stringp, ",");
1252
1253                 if ((tmp = strchr(stringp, '\"')))
1254                         stringp = tmp;
1255
1256                 /* check if the database text starts with a double quote */
1257                 if (*stringp == '"') {
1258                         stringp++;
1259                         database = strsep(&stringp, "\"");
1260                         strsep(&stringp, ",");
1261                 } else {
1262                         /* apparently this text has no quotes */
1263                         database = strsep(&stringp, ",");
1264                 }
1265
1266                 table = strsep(&stringp, ",");
1267
1268                 if (!strcmp(v->name, extconfig_conf)) {
1269                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
1270                         continue;
1271                 }
1272
1273                 if (!strcmp(v->name, "asterisk.conf")) {
1274                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
1275                         continue;
1276                 }
1277
1278                 if (!strcmp(v->name, "logger.conf")) {
1279                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
1280                         continue;
1281                 }
1282
1283                 if (!driver || !database)
1284                         continue;
1285                 if (!strcasecmp(v->name, "sipfriends")) {
1286                         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");
1287                         append_mapping("sipusers", driver, database, table ? table : "sipfriends");
1288                         append_mapping("sippeers", driver, database, table ? table : "sipfriends");
1289                 } else if (!strcasecmp(v->name, "iaxfriends")) {
1290                         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");
1291                         append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
1292                         append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
1293                 } else 
1294                         append_mapping(v->name, driver, database, table);
1295         }
1296                 
1297         ast_config_destroy(config);
1298         return 0;
1299 }
1300
1301 int ast_config_engine_register(struct ast_config_engine *new) 
1302 {
1303         struct ast_config_engine *ptr;
1304
1305         ast_mutex_lock(&config_lock);
1306
1307         if (!config_engine_list) {
1308                 config_engine_list = new;
1309         } else {
1310                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
1311                 ptr->next = new;
1312         }
1313
1314         ast_mutex_unlock(&config_lock);
1315         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
1316
1317         return 1;
1318 }
1319
1320 int ast_config_engine_deregister(struct ast_config_engine *del) 
1321 {
1322         struct ast_config_engine *ptr, *last=NULL;
1323
1324         ast_mutex_lock(&config_lock);
1325
1326         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
1327                 if (ptr == del) {
1328                         if (last)
1329                                 last->next = ptr->next;
1330                         else
1331                                 config_engine_list = ptr->next;
1332                         break;
1333                 }
1334                 last = ptr;
1335         }
1336
1337         ast_mutex_unlock(&config_lock);
1338
1339         return 0;
1340 }
1341
1342 /*! \brief Find realtime engine for realtime family */
1343 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) 
1344 {
1345         struct ast_config_engine *eng, *ret = NULL;
1346         struct ast_config_map *map;
1347
1348         ast_mutex_lock(&config_lock);
1349
1350         for (map = config_maps; map; map = map->next) {
1351                 if (!strcasecmp(family, map->name)) {
1352                         if (database)
1353                                 ast_copy_string(database, map->database, dbsiz);
1354                         if (table)
1355                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
1356                         break;
1357                 }
1358         }
1359
1360         /* Check if the required driver (engine) exist */
1361         if (map) {
1362                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
1363                         if (!strcasecmp(eng->name, map->driver))
1364                                 ret = eng;
1365                 }
1366         }
1367
1368         ast_mutex_unlock(&config_lock);
1369         
1370         /* if we found a mapping, but the engine is not available, then issue a warning */
1371         if (map && !ret)
1372                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
1373
1374         return ret;
1375 }
1376
1377 static struct ast_config_engine text_file_engine = {
1378         .name = "text",
1379         .load_func = config_text_file_load,
1380 };
1381
1382 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags)
1383 {
1384         char db[256];
1385         char table[256];
1386         struct ast_config_engine *loader = &text_file_engine;
1387         struct ast_config *result; 
1388
1389         if (cfg->include_level == cfg->max_include_level) {
1390                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
1391                 return NULL;
1392         }
1393
1394         cfg->include_level++;
1395
1396         if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
1397                 struct ast_config_engine *eng;
1398
1399                 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
1400
1401
1402                 if (eng && eng->load_func) {
1403                         loader = eng;
1404                 } else {
1405                         eng = find_engine("global", db, sizeof(db), table, sizeof(table));
1406                         if (eng && eng->load_func)
1407                                 loader = eng;
1408                 }
1409         }
1410
1411         result = loader->load_func(db, table, filename, cfg, flags);
1412
1413         if (result && result != CONFIG_STATUS_FILEUNCHANGED)
1414                 result->include_level--;
1415         else
1416                 cfg->include_level--;
1417
1418         return result;
1419 }
1420
1421 struct ast_config *ast_config_load(const char *filename, struct ast_flags flags)
1422 {
1423         struct ast_config *cfg;
1424         struct ast_config *result;
1425
1426         cfg = ast_config_new();
1427         if (!cfg)
1428                 return NULL;
1429
1430         result = ast_config_internal_load(filename, cfg, flags);
1431         if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
1432                 ast_config_destroy(cfg);
1433
1434         return result;
1435 }
1436
1437 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
1438 {
1439         struct ast_config_engine *eng;
1440         char db[256]="";
1441         char table[256]="";
1442         struct ast_variable *res=NULL;
1443
1444         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1445         if (eng && eng->realtime_func) 
1446                 res = eng->realtime_func(db, table, ap);
1447
1448         return res;
1449 }
1450
1451 struct ast_variable *ast_load_realtime_all(const char *family, ...)
1452 {
1453         struct ast_variable *res;
1454         va_list ap;
1455
1456         va_start(ap, family);
1457         res = ast_load_realtime_helper(family, ap);
1458         va_end(ap);
1459
1460         return res;
1461 }
1462
1463 struct ast_variable *ast_load_realtime(const char *family, ...)
1464 {
1465         struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
1466         va_list ap;
1467
1468         va_start(ap, family);
1469         res = ast_load_realtime_helper(family, ap);
1470         va_end(ap);
1471
1472         /* Eliminate blank entries */
1473         for (cur = res; cur; cur = cur->next) {
1474                 if (freeme) {
1475                         ast_free(freeme);
1476                         freeme = NULL;
1477                 }
1478
1479                 if (ast_strlen_zero(cur->value)) {
1480                         if (prev)
1481                                 prev->next = cur->next;
1482                         else
1483                                 res = cur->next;
1484                         freeme = cur;
1485                 } else {
1486                         prev = cur;
1487                 }
1488         }
1489         return res;
1490 }
1491
1492 /*! \brief Check if realtime engine is configured for family */
1493 int ast_check_realtime(const char *family)
1494 {
1495         struct ast_config_engine *eng;
1496
1497         eng = find_engine(family, NULL, 0, NULL, 0);
1498         if (eng)
1499                 return 1;
1500         return 0;
1501
1502 }
1503
1504 /*! \brief Check if there's any realtime engines loaded */
1505 int ast_realtime_enabled()
1506 {
1507         return config_maps ? 1 : 0;
1508 }
1509
1510 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
1511 {
1512         struct ast_config_engine *eng;
1513         char db[256]="";
1514         char table[256]="";
1515         struct ast_config *res=NULL;
1516         va_list ap;
1517
1518         va_start(ap, family);
1519         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1520         if (eng && eng->realtime_multi_func) 
1521                 res = eng->realtime_multi_func(db, table, ap);
1522         va_end(ap);
1523
1524         return res;
1525 }
1526
1527 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
1528 {
1529         struct ast_config_engine *eng;
1530         int res = -1;
1531         char db[256]="";
1532         char table[256]="";
1533         va_list ap;
1534
1535         va_start(ap, lookup);
1536         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1537         if (eng && eng->update_func) 
1538                 res = eng->update_func(db, table, keyfield, lookup, ap);
1539         va_end(ap);
1540
1541         return res;
1542 }
1543
1544 int ast_store_realtime(const char *family, ...) {
1545         struct ast_config_engine *eng;
1546         int res = -1;
1547         char db[256]="";
1548         char table[256]="";
1549         va_list ap;
1550
1551         va_start(ap, family);
1552         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1553         if (eng && eng->store_func) 
1554                 res = eng->store_func(db, table, ap);
1555         va_end(ap);
1556
1557         return res;
1558 }
1559
1560 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) {
1561         struct ast_config_engine *eng;
1562         int res = -1;
1563         char db[256]="";
1564         char table[256]="";
1565         va_list ap;
1566
1567         va_start(ap, lookup);
1568         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1569         if (eng && eng->destroy_func) 
1570                 res = eng->destroy_func(db, table, keyfield, lookup, ap);
1571         va_end(ap);
1572
1573         return res;
1574 }
1575
1576 /*! \brief Helper function to parse arguments
1577  * See documentation in config.h
1578  */
1579 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
1580         void *p_result, ...)
1581 {
1582         va_list ap;
1583         int error = 0;
1584
1585         va_start(ap, p_result);
1586         switch (flags & PARSE_TYPE) {
1587         case PARSE_INT32:
1588             {
1589                 int32_t *result = p_result;
1590                 int32_t x, def = result ? *result : 0,
1591                         high = (int32_t)0x7fffffff,
1592                         low  = (int32_t)0x80000000;
1593                 /* optional argument: first default value, then range */
1594                 if (flags & PARSE_DEFAULT)
1595                         def = va_arg(ap, int32_t);
1596                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
1597                         /* range requested, update bounds */
1598                         low = va_arg(ap, int32_t);
1599                         high = va_arg(ap, int32_t);
1600                 }
1601                 x = strtol(arg, NULL, 0);
1602                 error = (x < low) || (x > high);
1603                 if (flags & PARSE_OUT_RANGE)
1604                         error = !error;
1605                 if (result)
1606                         *result  = error ? def : x;
1607                 ast_debug(3,
1608                         "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
1609                         arg, low, high,
1610                         result ? *result : x, error);
1611                 break;
1612             }
1613
1614         case PARSE_UINT32:
1615             {
1616                 uint32_t *result = p_result;
1617                 uint32_t x, def = result ? *result : 0,
1618                         low = 0, high = (uint32_t)~0;
1619                 /* optional argument: first default value, then range */
1620                 if (flags & PARSE_DEFAULT)
1621                         def = va_arg(ap, uint32_t);
1622                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
1623                         /* range requested, update bounds */
1624                         low = va_arg(ap, uint32_t);
1625                         high = va_arg(ap, uint32_t);
1626                 }
1627                 x = strtoul(arg, NULL, 0);
1628                 error = (x < low) || (x > high);
1629                 if (flags & PARSE_OUT_RANGE)
1630                         error = !error;
1631                 if (result)
1632                         *result  = error ? def : x;
1633                 ast_debug(3,
1634                         "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
1635                         arg, low, high,
1636                         result ? *result : x, error);
1637                 break;
1638             }
1639
1640         case PARSE_INADDR:
1641             {
1642                 char *port, *buf;
1643                 struct sockaddr_in _sa_buf;     /* buffer for the result */
1644                 struct sockaddr_in *sa = p_result ?
1645                         (struct sockaddr_in *)p_result : &_sa_buf;
1646                 /* default is either the supplied value or the result itself */
1647                 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
1648                         va_arg(ap, struct sockaddr_in *) : sa;
1649                 struct hostent *hp;
1650                 struct ast_hostent ahp;
1651
1652                 bzero(&_sa_buf, sizeof(_sa_buf)); /* clear buffer */
1653                 /* duplicate the string to strip away the :port */
1654                 port = ast_strdupa(arg);
1655                 buf = strsep(&port, ":");
1656                 sa->sin_family = AF_INET;       /* assign family */
1657                 /*
1658                  * honor the ports flag setting, assign default value
1659                  * in case of errors or field unset.
1660                  */
1661                 flags &= PARSE_PORT_MASK; /* the only flags left to process */
1662                 if (port) {
1663                         if (flags == PARSE_PORT_FORBID) {
1664                                 error = 1;      /* port was forbidden */
1665                                 sa->sin_port = def->sin_port;
1666                         } else if (flags == PARSE_PORT_IGNORE)
1667                                 sa->sin_port = def->sin_port;
1668                         else /* accept or require */
1669                                 sa->sin_port = htons(strtol(port, NULL, 0));
1670                 } else {
1671                         sa->sin_port = def->sin_port;
1672                         if (flags == PARSE_PORT_REQUIRE)
1673                                 error = 1;
1674                 }
1675                 /* Now deal with host part, even if we have errors before. */
1676                 hp = ast_gethostbyname(buf, &ahp);
1677                 if (hp) /* resolved successfully */
1678                         memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
1679                 else {
1680                         error = 1;
1681                         sa->sin_addr = def->sin_addr;
1682                 }
1683                 ast_debug(3,
1684                         "extract inaddr from [%s] gives [%s:%d](%d)\n",
1685                         arg, ast_inet_ntoa(sa->sin_addr),
1686                         ntohs(sa->sin_port), error);
1687                 break;
1688             }
1689         }
1690         va_end(ap);
1691         return error;
1692 }
1693
1694 static int config_command(int fd, int argc, char **argv) 
1695 {
1696         struct ast_config_engine *eng;
1697         struct ast_config_map *map;
1698         
1699         ast_mutex_lock(&config_lock);
1700
1701         ast_cli(fd, "\n\n");
1702         for (eng = config_engine_list; eng; eng = eng->next) {
1703                 ast_cli(fd, "\nConfig Engine: %s\n", eng->name);
1704                 for (map = config_maps; map; map = map->next)
1705                         if (!strcasecmp(map->driver, eng->name)) {
1706                                 ast_cli(fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
1707                                         map->table ? map->table : map->name);
1708                         }
1709         }
1710         ast_cli(fd,"\n\n");
1711         
1712         ast_mutex_unlock(&config_lock);
1713
1714         return 0;
1715 }
1716
1717 static char show_config_help[] =
1718         "Usage: core show config mappings\n"
1719         "       Shows the filenames to config engines.\n";
1720
1721 static struct ast_cli_entry cli_config[] = {
1722         { { "core", "show", "config", "mappings", NULL },
1723         config_command, "Display config mappings (file names to config engines)",
1724         show_config_help },
1725 };
1726
1727 int register_config_cli() 
1728 {
1729         ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
1730         return 0;
1731 }