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