6fbef3e188200a7997a2f5384c3c3bdb28f39f8e
[asterisk/asterisk.git] / config.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Configuration File Parser
5  * 
6  * Copyright (C) 1999-2004, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <time.h>
20 #include <asterisk/config.h>
21 #include <asterisk/config_pvt.h>
22 #include <asterisk/cli.h>
23 #include <asterisk/lock.h>
24 #include <asterisk/options.h>
25 #include <asterisk/logger.h>
26 #include <asterisk/utils.h>
27 #include "asterisk.h"
28 #include "astconf.h"
29
30 #define MAX_NESTED_COMMENTS 128
31 #define COMMENT_START ";--"
32 #define COMMENT_END "--;"
33 #define COMMENT_META ';'
34 #define COMMENT_TAG '-'
35
36 static int ast_cust_config=0;
37 struct ast_config *(*global_load_func)(const char *dbname, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
38
39 static struct ast_config_map {
40         struct ast_config_map *next;
41         char *name;
42         char *driver;
43         char *database;
44         char *table;
45         char stuff[0];
46 } *maps = NULL;
47
48 AST_MUTEX_DEFINE_STATIC(ast_cust_config_lock);
49 static struct ast_config_reg *ast_cust_config_list;
50 static char *config_conf_file = "extconfig.conf";
51
52 void ast_destroy_realtime(struct ast_variable *v)
53 {
54         struct ast_variable *vn;
55         while(v) {
56                 vn = v;
57                 v = v->next;
58                 free(vn);
59         }
60 }
61
62 void ast_category_destroy(struct ast_category *cat)
63 {
64         ast_destroy_realtime(cat->root);
65         free(cat);
66 }
67
68 void ast_destroy(struct ast_config *ast)
69 {
70         struct ast_category *cat, *catn;
71
72         if (!ast)
73                 return;
74
75         cat = ast->root;
76         while(cat) {
77                 ast_destroy_realtime(cat->root);
78                 catn = cat;
79                 cat = cat->next;
80                 free(catn);
81         }
82         free(ast);
83 }
84
85 int ast_true(const char *s)
86 {
87         if (!s)
88                 return 0;
89         /* Determine if this is a true value */
90         if (!strcasecmp(s, "yes") ||
91             !strcasecmp(s, "true") ||
92                 !strcasecmp(s, "y") ||
93                 !strcasecmp(s, "t") ||
94                 !strcasecmp(s, "1") ||
95                 !strcasecmp(s, "on"))
96                         return -1;
97         return 0;
98 }
99
100 int ast_false(const char *s)
101 {
102         if (!s)
103                 return 0;
104         /* Determine if this is a false value */
105         if (!strcasecmp(s, "no") ||
106             !strcasecmp(s, "false") ||
107                 !strcasecmp(s, "n") ||
108                 !strcasecmp(s, "f") ||
109                 !strcasecmp(s, "0") ||
110                 !strcasecmp(s, "off"))
111                         return -1;
112         return 0;
113 }
114
115 struct ast_variable *ast_variable_browse(struct ast_config *config, char *category)
116 {
117         struct ast_category *cat;
118         cat = config->root;
119         while(cat) {
120                 if (cat->name == category)
121                         return cat->root;
122                 cat = cat->next;
123         }
124         cat = config->root;
125         while(cat) {
126                 if (!strcasecmp(cat->name, category))
127                         return cat->root;
128                 cat = cat->next;
129         }
130         return NULL;
131 }
132
133 char *ast_variable_retrieve(struct ast_config *config, char *category, char *value)
134 {
135         struct ast_variable *v;
136         if (category) {
137                 v = ast_variable_browse(config, category);
138                 while (v) {
139                         if (value == v->name)
140                                 return v->value;
141                         v=v->next;
142                 }
143                 v = ast_variable_browse(config, category);
144                 while (v) {
145                         if (!strcasecmp(value, v->name))
146                                 return v->value;
147                         v=v->next;
148                 }
149         } else {
150                 struct ast_category *cat;
151                 cat = config->root;
152                 while(cat) {
153                         v = cat->root;
154                         while (v) {
155                                 if (!strcasecmp(value, v->name))
156                                         return v->value;
157                                 v=v->next;
158                         }
159                         cat = cat->next;
160                 }
161         }
162         return NULL;
163 }
164
165 int ast_category_exist(struct ast_config *config, char *category_name)
166 {
167         struct ast_category *category = NULL;
168
169         category = config->root;
170
171         while(category) {
172                 if (!strcasecmp(category->name,category_name)) 
173                         return 1;
174                 category = category->next;
175         } 
176
177         return 0;
178 }
179
180
181 static struct ast_config_reg *get_ast_cust_config_keyword(const char *name, char *database, int dbsiz, char *table, int tabsiz) 
182 {
183         struct ast_config_reg *reg,*ret=NULL;
184         struct ast_config_map *map;
185
186         ast_mutex_lock(&ast_cust_config_lock);
187         map = maps;
188         while(map) {
189                 if (!strcasecmp(name, map->name)) {
190                         strncpy(database, map->database, dbsiz - 1);
191                         if (map->table)
192                                 strncpy(table, map->table, tabsiz - 1);
193                         else
194                                 strncpy(table, name, tabsiz - 1);
195                         break;
196                 }
197                 map = map->next;
198         }
199         if (map) {
200                 for (reg=ast_cust_config_list;reg && !ret;reg=reg->next) {
201                         if (!strcmp(reg->name,map->driver))
202                                 ret=reg;
203                 }
204         }
205         ast_mutex_unlock(&ast_cust_config_lock);
206         return ret;
207 }
208
209 void ast_config_destroy_all(void) 
210 {
211         struct ast_config_reg *key;
212         ast_mutex_lock(&ast_cust_config_lock);
213         for (key=ast_cust_config_list;key;key=key->next) {
214                 ast_config_deregister(key);
215         }
216         ast_cust_config_list = NULL;
217         ast_mutex_unlock(&ast_cust_config_lock);
218 }
219
220 static struct ast_config_reg *get_config_registrations(void) 
221 {
222         return ast_cust_config_list;
223 }
224
225
226 static struct ast_config *__ast_load(const char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel);
227
228 static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, char *buf, int lineno, const char *configfile, int includelevel )
229 {
230         char *c;
231         char *cur;
232         char *arg=NULL;
233         struct ast_variable *v;
234         int object;
235         cur = ast_strip(buf);
236         if (!ast_strlen_zero(cur)) {
237                 /* Actually parse the entry */
238                 if (cur[0] == '[') {
239                         /* A category header */
240                         c = strchr(cur, ']');
241                         if (c) {
242                                 *c = 0;
243                                 *_tmpc = malloc(sizeof(struct ast_category));
244                                 if (!*_tmpc) {
245                                         ast_destroy(tmp);
246                                         ast_log(LOG_WARNING,
247                                                 "Out of memory, line %d\n", lineno);
248                                         return -1;
249                                 }
250                                 memset(*_tmpc, 0, sizeof(struct ast_category));
251                                 strncpy((*_tmpc)->name, cur+1, sizeof((*_tmpc)->name) - 1);
252                                 (*_tmpc)->root =  NULL;
253                                 if (!tmp->prev)
254                                         tmp->root = *_tmpc;
255                                 else
256                                         tmp->prev->next = *_tmpc;
257
258                                 tmp->prev = *_tmpc;
259                                 *_last =  NULL;
260                         } else {
261                                 ast_log(LOG_WARNING, 
262                                         "parse error: no closing ']', line %d of %s\n", lineno, configfile);
263                         }
264                 } else if (cur[0] == '#') {
265                         /* A directive */
266                         cur++;
267                         c = cur;
268                         while(*c && (*c > 32)) c++;
269                         if (*c) {
270                                 *c = '\0';
271                                 c++;
272                                 /* Find real argument */
273                                 while(*c  && (*c < 33)) c++;
274                                 if (!*c)
275                                         c = NULL;
276                         } else 
277                                 c = NULL;
278                         if (!strcasecmp(cur, "include")) {
279                                 /* A #include */
280                                 if (c) {
281                                         while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
282                                         /* Get rid of leading mess */
283                                         cur = c;
284                                         while (!ast_strlen_zero(cur)) {
285                                                 c = cur + strlen(cur) - 1;
286                                                 if ((*c == '>') || (*c == '<') || (*c == '\"'))
287                                                         *c = '\0';
288                                                 else
289                                                         break;
290                                         }
291                                         
292                                         if((c = strchr(cur,':'))) {
293                                                 *c = '\0';
294                                                 c++;
295                                                 arg = c;
296                                         }
297                                         
298                                         if (includelevel < MAX_INCLUDE_LEVEL) {
299                                                 if(arg && cur) {
300                                                         ast_log(LOG_WARNING, "Including files with explicit config engine no longer permitted.  Please use extconfig.conf to specify all mappings\n");
301                                                 } else {
302                                                         __ast_load(cur, tmp, _tmpc, _last, includelevel + 1);
303                                                 }
304                                         } else
305                                                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", includelevel);
306                                 } else
307                                         ast_log(LOG_WARNING, "Directive '#include' needs an argument (filename) at line %d of %s\n", lineno, configfile);
308                                 /* Strip off leading and trailing "'s and <>'s */
309                         }
310                         else 
311                                 ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
312                 } else {
313                         /* Just a line (variable = value) */
314                         if (!*_tmpc) {
315                                 ast_log(LOG_WARNING,
316                                         "parse error: No category context for line %d of %s\n", lineno, configfile);
317                                 ast_destroy(tmp);
318                                 return -1;
319                         }
320                         c = strchr(cur, '=');
321                         if (c) {
322                                 *c = 0;
323                                 c++;
324                                 /* Ignore > in => */
325                                 if (*c== '>') {
326                                         object = 1;
327                                         c++;
328                                 } else
329                                         object = 0;
330                                 v = ast_new_variable(ast_strip(cur), ast_strip(c));
331                                 if (v) {
332                                         v->next = NULL;
333                                         v->lineno = lineno;
334                                         v->object = object;
335                                         /* Put and reset comments */
336                                         v->blanklines = 0;
337                                         if (*_last)
338                                                 (*_last)->next = v;
339                                         else
340                                                 (*_tmpc)->root = v;
341                                         *_last = v;
342                                 } else {
343                                         ast_destroy(tmp);
344                                         ast_log(LOG_WARNING, "Out of memory, line %d\n", lineno);
345                                         return -1;
346                                 }
347                         } else {
348                                 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
349                         }
350                                                                                                                 
351                 }
352         }
353         return 0;
354 }
355
356 int ast_save(char *configfile, struct ast_config *cfg, char *generator)
357 {
358         FILE *f;
359         char fn[256];
360         char date[256]="";
361         time_t t;
362         struct ast_variable *var;
363         struct ast_category *cat;
364         int blanklines = 0;
365         if (configfile[0] == '/') {
366                 strncpy(fn, configfile, sizeof(fn)-1);
367         } else {
368                 snprintf(fn, sizeof(fn), "%s/%s", AST_CONFIG_DIR, configfile);
369         }
370         time(&t);
371         strncpy(date, ctime(&t), sizeof(date) - 1);
372         if ((f = fopen(fn, "w"))) {
373                 if ((option_verbose > 1) && !option_debug)
374                         ast_verbose(  VERBOSE_PREFIX_2 "Saving '%s': ", fn);
375                 fprintf(f, ";!\n");
376                 fprintf(f, ";! Automatically generated configuration file\n");
377                 fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
378                 fprintf(f, ";! Generator: %s\n", generator);
379                 fprintf(f, ";! Creation Date: %s", date);
380                 fprintf(f, ";!\n");
381                 cat = cfg->root;
382                 while(cat) {
383                         /* Dump section with any appropriate comment */
384                         fprintf(f, "[%s]\n", cat->name);
385                         var = cat->root;
386                         while(var) {
387                                 if (var->sameline) 
388                                         fprintf(f, "%s %s %s  ; %s\n", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
389                                 else    
390                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
391                                 if (var->blanklines) {
392                                         blanklines = var->blanklines;
393                                         while (blanklines) {
394                                                 fprintf(f, "\n");
395                                                 blanklines--;
396                                         }
397                                 }
398                                         
399                                 var = var->next;
400                         }
401 #if 0
402                         /* Put an empty line */
403                         fprintf(f, "\n");
404 #endif
405                         cat = cat->next;
406                 }
407         } else {
408                 if (option_debug)
409                         printf("Unable to open for writing: %s\n", fn);
410                 else if (option_verbose > 1)
411                         printf( "Unable to write (%s)", strerror(errno));
412                 return -1;
413         }
414         fclose(f);
415         return 0;
416 }
417
418
419 struct ast_variable *ast_load_realtime(const char *family, ...)
420 {
421         struct ast_config_reg *reg;
422         char db[256]="";
423         char table[256]="";
424         struct ast_variable *res=NULL;
425         va_list ap;
426         va_start(ap, family);
427         reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
428         if (reg && reg->realtime_func) 
429                 res = reg->realtime_func(db, table, ap);
430         va_end(ap);
431         return res;
432 }
433
434 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
435 {
436         struct ast_config_reg *reg;
437         char db[256]="";
438         char table[256]="";
439         struct ast_config *res=NULL;
440         va_list ap;
441         va_start(ap, family);
442         reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
443         if (reg && reg->realtime_multi_func) 
444                 res = reg->realtime_multi_func(db, table, ap);
445         va_end(ap);
446         return res;
447 }
448
449 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
450 {
451         struct ast_config_reg *reg;
452         int res = -1;
453         char db[256]="";
454         char table[256]="";
455         va_list ap;
456         va_start(ap, lookup);
457         reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
458         if (reg && reg->update_func) 
459                 res = reg->update_func(db, table, keyfield, lookup, ap);
460         va_end(ap);
461         return res;
462 }
463
464 static struct ast_config *__ast_load(const char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel)
465 {
466         char fn[256];
467         char buf[8192];
468         char db[256];
469         char table[256];
470         FILE *f;
471         int lineno=0;
472         int master=0;
473         int comment = 0, nest[MAX_NESTED_COMMENTS];
474         
475         struct ast_config_reg *reg=NULL;
476         struct ast_config *(*load_func)(const char *database, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
477         char *comment_p, *process_buf, *new_buf=NULL;
478
479         load_func=NULL;
480         if (strcmp(configfile,config_conf_file) && strcmp(configfile,"asterisk.conf") && ast_cust_config_list) {
481                 if (global_load_func) {
482                         load_func = global_load_func;
483                 } else {
484                         reg = get_ast_cust_config_keyword(configfile, db, sizeof(db), table, sizeof(table));
485                         if (reg && reg->static_func) {
486                                 load_func = reg->static_func;
487                         } else {
488                                 reg = get_ast_cust_config_keyword(configfile, db, sizeof(db), table, sizeof(table));
489                                 if (reg && reg->static_func)
490                                         global_load_func = load_func = reg->static_func;
491                         }
492                 }
493
494                 if (load_func) {
495                         ast_log(LOG_NOTICE,"Loading Config %s via %s engine\n",configfile,reg && reg->name ? reg->name : "global");
496                         if((tmp = load_func(db, table, configfile,tmp, _tmpc, _last, includelevel)))
497                                 return tmp;
498                 }
499         }
500
501         if (configfile[0] == '/') {
502                 strncpy(fn, configfile, sizeof(fn)-1);
503         } else {
504                 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, configfile);
505         }
506         if ((option_verbose > 1) && !option_debug) {
507                 ast_verbose(  VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
508                 fflush(stdout);
509         }
510         if ((f = fopen(fn, "r"))) {
511                 if (option_debug)
512                         ast_log(LOG_DEBUG, "Parsing %s\n", fn);
513                 else if (option_verbose > 2)
514                         ast_verbose( "Found\n");
515                 if (!tmp) {
516                         if((tmp = malloc(sizeof(struct ast_config)))) {
517                                 memset(tmp, 0, sizeof(struct ast_config));
518                                 master = 1;
519                         }
520                 }
521                 if (tmp) {
522                         while(!feof(f)) {
523                                 lineno++;
524                                 if (fgets(buf, sizeof(buf), f)) {
525                                         new_buf = buf;
526                                         if (comment)
527                                                 process_buf = NULL;
528                                         else
529                                                 process_buf = buf;
530                                         while ((comment_p = strchr(new_buf, COMMENT_META))) {
531                                                 if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
532                                                         /* Yuck, gotta memmove */
533                                                         memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
534                                                         new_buf = comment_p;
535                                                 } else if(comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG) {
536                                                         /* Meta-Comment start detected ";--" */
537                                                         if (comment < MAX_NESTED_COMMENTS) {
538                                                                 *comment_p = '\0';
539                                                                 new_buf = comment_p + 3;
540                                                                 comment++;
541                                                                 nest[comment-1] = lineno;
542                                                         } else {
543                                                                 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
544                                                         }
545                                                 } else if ((comment_p >= new_buf + 2) &&
546                                                                    (*(comment_p - 1) == COMMENT_TAG) &&
547                                                                    (*(comment_p - 2) == COMMENT_TAG)) {
548                                                         /* Meta-Comment end detected */
549                                                         comment--;
550                                                         new_buf = comment_p + 1;
551                                                         if (!comment) {
552                                                                 /* Back to non-comment now */
553                                                                 if (process_buf) {
554                                                                         /* Actually have to move what's left over the top, then continue */
555                                                                         char *oldptr;
556                                                                         oldptr = process_buf + strlen(process_buf);
557                                                                         memmove(oldptr, new_buf, strlen(new_buf) + 1);
558                                                                         new_buf = oldptr;
559                                                                 } else
560                                                                         process_buf = new_buf;
561                                                         }
562                                                 } else {
563                                                         if (!comment) {
564                                                                 /* If ; is found, and we are not nested in a comment, 
565                                                                    we immediately stop all comment processing */
566                                                                 *comment_p = '\0'; 
567                                                                 new_buf = comment_p;
568                                                         } else
569                                                                 new_buf = comment_p + 1;
570                                                 }
571                                         }
572                                         if (process_buf && cfg_process(tmp, _tmpc, _last, process_buf, lineno, configfile, includelevel)) {
573                                                 tmp = NULL;
574                                                 break;
575                                         }
576                                 }
577                         }
578                 } else 
579                         ast_log(LOG_WARNING, "Out of memory\n");
580                 
581                 fclose(f);              
582         } else { /* can't open file */
583                 if (option_debug)
584                         ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
585                 else if (option_verbose > 2)
586                         ast_verbose( "Not found (%s)\n", strerror(errno));
587         }
588         if (comment) {
589                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment]);
590         }
591         return tmp;
592 }
593
594 int ast_config_register(struct ast_config_reg *new) 
595 {
596         struct ast_config_reg *ptr;
597         ast_mutex_lock(&ast_cust_config_lock);
598         if (!ast_cust_config_list) {
599                 ast_cust_config_list = new;
600         } else {
601                 for(ptr=ast_cust_config_list;ptr->next;ptr=ptr->next);
602                         ptr->next = new;
603         }
604         ast_mutex_unlock(&ast_cust_config_lock);
605         ast_log(LOG_NOTICE,"Registered Config Engine %s\n",new->name);
606         return 1;
607 }
608
609 int ast_config_deregister(struct ast_config_reg *del) 
610 {
611         struct ast_config_reg *ptr=NULL,*last=NULL;
612         ast_mutex_lock(&ast_cust_config_lock);
613         for (ptr=ast_cust_config_list;ptr;ptr=ptr->next) {
614                 if (ptr == del) {
615                         if (last && ptr->next) {
616                                 last->next = ptr->next;
617                         } else if (last && ! ptr->next) {
618                                 last->next = NULL;
619                         } else if (!last && ptr->next) {
620                                 ast_cust_config_list = ptr->next;
621                         } else if (!last && !ptr->next) {
622                                 ast_cust_config_list = NULL;
623                         }
624                 }
625                 last = ptr;
626         }
627         ast_mutex_unlock(&ast_cust_config_lock);
628         return 0;
629 }
630
631 int ast_cust_config_active(void) {
632         return (ast_cust_config >0) ? 1 : 0;
633 }
634
635 struct ast_config *ast_load(char *configfile)
636 {
637         struct ast_category *tmpc=NULL;
638         struct ast_variable *last = NULL;
639
640         return __ast_load(configfile, NULL, &tmpc, &last, 0);
641 }
642
643 void ast_category_append(struct ast_config *config, struct ast_category *cat)
644 {
645         struct ast_category *prev = NULL;
646         cat->next = NULL;
647         if (config->root) {
648                 prev = config->root;
649                 while(prev->next) prev = prev->next;
650                 prev->next = cat;
651         } else
652                 config->root = cat;
653 }
654
655 char *ast_category_browse(struct ast_config *config, char *prev)
656 {       
657         struct ast_category *cat;
658         if (!prev) {
659                 if (config->root)
660                         return config->root->name;
661                 else
662                         return NULL;
663         }
664         cat = config->root;
665         while(cat) {
666                 if (cat->name == prev) {
667                         if (cat->next)
668                                 return cat->next->name;
669                         else
670                                 return NULL;
671                 }
672                 cat = cat->next;
673         }
674         cat = config->root;
675         while(cat) {
676                 if (!strcasecmp(cat->name, prev)) {
677                         if (cat->next)
678                                 return cat->next->name;
679                         else
680                                 return NULL;
681                 }
682                 cat = cat->next;
683         }
684         return NULL;
685 }
686
687
688 struct ast_config *ast_new_config(void) 
689 {
690         struct ast_config *config;
691         config = malloc(sizeof(struct ast_config));
692         memset(config,0,sizeof(struct ast_config));
693         return config;
694 }
695
696
697
698 struct ast_category *ast_new_category(char *name) 
699 {
700         struct ast_category *category;
701         category = malloc(sizeof(struct ast_category));
702         if (category) {
703                 memset(category,0,sizeof(struct ast_category));
704                 strncpy(category->name,name,sizeof(category->name) - 1);
705         }
706         return category;
707 }
708
709
710 struct ast_variable *ast_new_variable(char *name, char *value) 
711 {
712         struct ast_variable *variable;
713         int length = strlen(name) + strlen(value) + 2 + sizeof(struct ast_variable);
714         variable = malloc(length);
715         if (variable) {
716                 memset(variable, 0, length);
717                 variable->name = variable->stuff;
718                 variable->value = variable->stuff + strlen(name) + 1;           
719                 variable->object=0;
720                 strcpy(variable->name,name);
721                 strcpy(variable->value,value);
722         }
723         return variable;
724 }
725
726 int ast_cust_config_register(struct ast_config_reg *new) 
727 {
728         ast_config_register(new);
729         read_ast_cust_config();
730         return 1;
731 }
732 int ast_cust_config_deregister(struct ast_config_reg *new) 
733 {
734         ast_config_deregister(new);
735         read_ast_cust_config();
736         return 1;
737 }
738
739 static void clear_cust_keywords(void) 
740 {
741         struct ast_config_map *map, *prev;
742         ast_mutex_lock(&ast_cust_config_lock);
743         map = maps;
744         while(map) {
745                 prev = map;
746                 map = map->next;
747                 free(prev);
748         }
749         maps = NULL;
750         ast_mutex_unlock(&ast_cust_config_lock);
751 }
752
753 static int config_command(int fd, int argc, char **argv) 
754 {
755         struct ast_config_reg *key;
756         struct ast_config_map *map;
757         
758         ast_cli(fd,"\n\n");
759         ast_mutex_lock(&ast_cust_config_lock);
760         for (key=get_config_registrations();key;key=key->next) {
761                 ast_cli(fd,"\nConfig Engine: %s\n",key->name);
762                 map = maps;
763                 while(map) {
764                         if (!strcasecmp(map->driver, key->name))
765                                 ast_cli(fd,"===> %s (db=%s, table=%s)\n",map->name, map->database, map->table ? map->table : map->name);
766                         map = map->next;
767                 }
768         }
769         ast_mutex_unlock(&ast_cust_config_lock);
770         ast_cli(fd,"\n\n");
771         
772         return 0;
773 }
774
775 static struct ast_cli_entry config_command_struct = {
776   { "show","config","handles", NULL }, config_command,
777   "Show Config Handles", NULL };
778
779 int register_config_cli() 
780 {
781         return ast_cli_register(&config_command_struct);
782 }
783
784 int read_ast_cust_config(void) 
785 {
786         char *cfg = config_conf_file;
787         struct ast_config *config;
788         struct ast_variable *v;
789         struct ast_config_map *map;
790         int length;
791         char *driver, *table, *database, *stringp;
792
793         clear_cust_keywords();
794         config = ast_load(cfg);
795         if (config) {
796                 for (v = ast_variable_browse(config,"settings");v;v=v->next) {
797                         stringp = v->value;
798                         driver = strsep(&stringp, ",");
799                         database = strsep(&stringp, ",");
800                         table = strsep(&stringp, ",");
801                         
802                         if (!strcmp(v->name,config_conf_file) || !strcmp(v->name,"asterisk.conf")) {
803                                 ast_log(LOG_WARNING, "Cannot bind asterisk.conf or extconfig.conf!\n");
804                         } else if (driver && database) {
805                                 length = sizeof(struct ast_config_map);
806                                 length += strlen(v->name) + 1;
807                                 length += strlen(driver) + 1;
808                                 length += strlen(database) + 1;
809                                 if (table)
810                                         length += strlen(table) + 1;
811                                 map = malloc(length);
812                                 if (map) {
813                                         memset(map, 0, length);
814                                         map->name = map->stuff;
815                                         strcpy(map->name, v->name);
816                                         map->driver = map->name + strlen(map->name) + 1;
817                                         strcpy(map->driver, driver);
818                                         map->database = map->driver + strlen(map->driver) + 1;
819                                         strcpy(map->database, database);
820                                         if (table) {
821                                                 map->table = map->database + strlen(map->database) + 1;
822                                                 strcpy(map->table, table);
823                                         } else
824                                                 map->table = NULL;
825                                         map->next = maps;
826                                         if (option_verbose > 1)
827                                                 ast_verbose(VERBOSE_PREFIX_2 "Binding %s to %s/%s/%s\n",map->name,map->driver, map->database, map->table ? map->table : map->name);
828                                         maps = map;
829                                 }
830                         }
831                 }
832                 
833                 ast_destroy(config);
834         }
835
836         return 0;
837 }