2 * Asterisk -- A telephony toolkit for Linux.
4 * Configuration File Parser
6 * Copyright (C) 1999-2004, Digium, Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
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>
31 static int ast_cust_config=0;
32 struct ast_config *(*global_load_func)(const char *dbname, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
34 static struct ast_config_map {
35 struct ast_config_map *next;
43 AST_MUTEX_DEFINE_STATIC(ast_cust_config_lock);
44 static struct ast_config_reg *ast_cust_config_list;
45 static char *config_conf_file = "extconfig.conf";
47 void ast_destroy_realtime(struct ast_variable *v)
49 struct ast_variable *vn;
57 void ast_destroy(struct ast_config *ast)
59 struct ast_category *cat, *catn;
66 ast_destroy_realtime(cat->root);
74 int ast_true(const char *s)
78 /* Determine if this is a true value */
79 if (!strcasecmp(s, "yes") ||
80 !strcasecmp(s, "true") ||
81 !strcasecmp(s, "y") ||
82 !strcasecmp(s, "t") ||
83 !strcasecmp(s, "1") ||
89 int ast_false(const char *s)
93 /* Determine if this is a false value */
94 if (!strcasecmp(s, "no") ||
95 !strcasecmp(s, "false") ||
96 !strcasecmp(s, "n") ||
97 !strcasecmp(s, "f") ||
98 !strcasecmp(s, "0") ||
99 !strcasecmp(s, "off"))
104 struct ast_variable *ast_variable_browse(struct ast_config *config, char *category)
106 struct ast_category *cat;
109 if (cat->name == category)
115 if (!strcasecmp(cat->name, category))
122 char *ast_variable_retrieve(struct ast_config *config, char *category, char *value)
124 struct ast_variable *v;
126 v = ast_variable_browse(config, category);
128 if (value == v->name)
132 v = ast_variable_browse(config, category);
134 if (!strcasecmp(value, v->name))
139 struct ast_category *cat;
144 if (!strcasecmp(value, v->name))
154 int ast_category_exist(struct ast_config *config, char *category_name)
156 struct ast_category *category = NULL;
158 category = config->root;
161 if (!strcasecmp(category->name,category_name))
163 category = category->next;
170 static struct ast_config_reg *get_ast_cust_config_keyword(const char *name, char *database, int dbsiz, char *table, int tabsiz)
172 struct ast_config_reg *reg,*ret=NULL;
173 struct ast_config_map *map;
175 ast_mutex_lock(&ast_cust_config_lock);
178 if (!strcasecmp(name, map->name)) {
179 strncpy(database, map->database, dbsiz - 1);
181 strncpy(table, map->table, tabsiz - 1);
183 strncpy(table, name, tabsiz - 1);
189 for (reg=ast_cust_config_list;reg && !ret;reg=reg->next) {
190 if (!strcmp(reg->name,map->driver))
194 ast_mutex_unlock(&ast_cust_config_lock);
198 void ast_config_destroy_all(void)
200 struct ast_config_reg *key;
201 ast_mutex_lock(&ast_cust_config_lock);
202 for (key=ast_cust_config_list;key;key=key->next) {
203 ast_config_deregister(key);
205 ast_cust_config_list = NULL;
206 ast_mutex_unlock(&ast_cust_config_lock);
209 static struct ast_config_reg *get_config_registrations(void)
211 return ast_cust_config_list;
215 static struct ast_config *__ast_load(const char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel);
217 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 )
222 struct ast_variable *v;
224 /* Strip off lines using ; as comment */
225 c = strchr(buf, ';');
227 if ((c == buf) || (*(c-1) != '\\')) {
231 memmove(c, c + 1, strlen(c + 1));
233 c = strchr(c + 1, ';');
235 cur = ast_strip(buf);
236 if (!ast_strlen_zero(cur)) {
237 /* Actually parse the entry */
239 /* A category header */
240 c = strchr(cur, ']');
243 *_tmpc = malloc(sizeof(struct ast_category));
247 "Out of memory, line %d\n", lineno);
250 memset(*_tmpc, 0, sizeof(struct ast_category));
251 strncpy((*_tmpc)->name, cur+1, sizeof((*_tmpc)->name) - 1);
252 (*_tmpc)->root = NULL;
256 tmp->prev->next = *_tmpc;
262 "parse error: no closing ']', line %d of %s\n", lineno, configfile);
264 } else if (cur[0] == '#') {
268 while(*c && (*c > 32)) c++;
272 /* Find real argument */
273 while(*c && (*c < 33)) c++;
278 if (!strcasecmp(cur, "include")) {
281 while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
282 /* Get rid of leading mess */
284 while (!ast_strlen_zero(cur)) {
285 c = cur + strlen(cur) - 1;
286 if ((*c == '>') || (*c == '<') || (*c == '\"'))
292 if((c = strchr(cur,':'))) {
298 if (includelevel < MAX_INCLUDE_LEVEL) {
300 ast_log(LOG_WARNING, "Including files with explicit config engine no longer permitted. Please use extconfig.conf to specify all mappings\n");
302 __ast_load(cur, tmp, _tmpc, _last, includelevel + 1);
305 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", includelevel);
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 */
311 ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
313 /* Just a line (variable = value) */
316 "parse error: No category context for line %d of %s\n", lineno, configfile);
320 c = strchr(cur, '=');
330 v = ast_new_variable(ast_strip(cur), ast_strip(c));
335 /* Put and reset comments */
344 ast_log(LOG_WARNING, "Out of memory, line %d\n", lineno);
348 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
356 int ast_save(char *configfile, struct ast_config *cfg, char *generator)
362 struct ast_variable *var;
363 struct ast_category *cat;
365 if (configfile[0] == '/') {
366 strncpy(fn, configfile, sizeof(fn)-1);
368 snprintf(fn, sizeof(fn), "%s/%s", AST_CONFIG_DIR, configfile);
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);
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);
383 /* Dump section with any appropriate comment */
384 fprintf(f, "[%s]\n", cat->name);
388 fprintf(f, "%s %s %s ; %s\n", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
390 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
391 if (var->blanklines) {
392 blanklines = var->blanklines;
402 /* Put an empty line */
409 printf("Unable to open for writing: %s\n", fn);
410 else if (option_verbose > 1)
411 printf( "Unable to write (%s)", strerror(errno));
419 struct ast_variable *ast_load_realtime(const char *family, ...)
421 struct ast_config_reg *reg;
424 struct ast_variable *res=NULL;
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);
434 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
436 struct ast_config_reg *reg;
441 va_start(ap, lookup);
442 reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
443 if (reg && reg->update_func)
444 res = reg->update_func(db, table, keyfield, lookup, ap);
449 static struct ast_config *__ast_load(const char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel)
458 struct ast_config_reg *reg=NULL;
459 struct ast_config *(*load_func)(const char *database, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
462 if (strcmp(configfile,config_conf_file) && strcmp(configfile,"asterisk.conf") && ast_cust_config_list) {
463 if (global_load_func) {
464 load_func = global_load_func;
466 reg = get_ast_cust_config_keyword(configfile, db, sizeof(db), table, sizeof(table));
467 if (reg && reg->static_func) {
468 load_func = reg->static_func;
470 reg = get_ast_cust_config_keyword(configfile, db, sizeof(db), table, sizeof(table));
471 if (reg && reg->static_func)
472 global_load_func = load_func = reg->static_func;
477 ast_log(LOG_NOTICE,"Loading Config %s via %s engine\n",configfile,reg && reg->name ? reg->name : "global");
478 tmp = load_func(db, table, configfile,tmp, _tmpc, _last, includelevel);
485 if (configfile[0] == '/') {
486 strncpy(fn, configfile, sizeof(fn)-1);
488 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, configfile);
490 if ((option_verbose > 1) && !option_debug) {
491 ast_verbose( VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
494 if ((f = fopen(fn, "r"))) {
496 ast_log(LOG_DEBUG, "Parsing %s\n", fn);
497 else if (option_verbose > 1)
498 ast_verbose( "Found\n");
500 tmp = malloc(sizeof(struct ast_config));
502 memset(tmp, 0, sizeof(struct ast_config));
507 ast_log(LOG_WARNING, "Out of memory\n");
513 if (fgets(buf, sizeof(buf), f)) {
514 if (cfg_process(tmp, _tmpc, _last, buf, lineno, configfile, includelevel)) {
523 ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
524 else if (option_verbose > 1)
525 ast_verbose( "Not found (%s)\n", strerror(errno));
530 int ast_config_register(struct ast_config_reg *new)
532 struct ast_config_reg *ptr;
533 ast_mutex_lock(&ast_cust_config_lock);
534 if (!ast_cust_config_list) {
535 ast_cust_config_list = new;
537 for(ptr=ast_cust_config_list;ptr->next;ptr=ptr->next);
540 ast_mutex_unlock(&ast_cust_config_lock);
541 ast_log(LOG_NOTICE,"Registered Config Engine %s\n",new->name);
545 int ast_config_deregister(struct ast_config_reg *del)
547 struct ast_config_reg *ptr=NULL,*last=NULL;
548 ast_mutex_lock(&ast_cust_config_lock);
549 for (ptr=ast_cust_config_list;ptr;ptr=ptr->next) {
551 if (last && ptr->next) {
552 last->next = ptr->next;
553 } else if (last && ! ptr->next) {
555 } else if (!last && ptr->next) {
556 ast_cust_config_list = ptr->next;
557 } else if (!last && !ptr->next) {
558 ast_cust_config_list = NULL;
563 ast_mutex_unlock(&ast_cust_config_lock);
567 int ast_cust_config_active(void) {
568 return (ast_cust_config >0) ? 1 : 0;
571 struct ast_config *ast_load(char *configfile)
573 struct ast_category *tmpc=NULL;
574 struct ast_variable *last = NULL;
576 return __ast_load(configfile, NULL, &tmpc, &last, 0);
579 char *ast_category_browse(struct ast_config *config, char *prev)
581 struct ast_category *cat;
584 return config->root->name;
590 if (cat->name == prev) {
592 return cat->next->name;
600 if (!strcasecmp(cat->name, prev)) {
602 return cat->next->name;
612 struct ast_config *ast_new_config(void)
614 struct ast_config *config;
615 config = malloc(sizeof(struct ast_config));
616 memset(config,0,sizeof(struct ast_config));
622 struct ast_category *ast_new_category(char *name)
624 struct ast_category *category;
625 category = malloc(sizeof(struct ast_category));
627 memset(category,0,sizeof(struct ast_category));
628 strncpy(category->name,name,sizeof(category->name) - 1);
634 struct ast_variable *ast_new_variable(char *name, char *value)
636 struct ast_variable *variable;
637 int length = strlen(name) + strlen(value) + 2 + sizeof(struct ast_variable);
638 variable = malloc(length);
640 memset(variable, 0, length);
641 variable->name = variable->stuff;
642 variable->value = variable->stuff + strlen(name) + 1;
644 strcpy(variable->name,name);
645 strcpy(variable->value,value);
650 int ast_cust_config_register(struct ast_config_reg *new)
652 ast_config_register(new);
653 read_ast_cust_config();
656 int ast_cust_config_deregister(struct ast_config_reg *new)
658 ast_config_deregister(new);
659 read_ast_cust_config();
663 static void clear_cust_keywords(void)
665 struct ast_config_map *map, *prev;
666 ast_mutex_lock(&ast_cust_config_lock);
674 ast_mutex_unlock(&ast_cust_config_lock);
677 static int config_command(int fd, int argc, char **argv)
679 struct ast_config_reg *key;
680 struct ast_config_map *map;
683 ast_mutex_lock(&ast_cust_config_lock);
684 for (key=get_config_registrations();key;key=key->next) {
685 ast_cli(fd,"\nConfig Engine: %s\n",key->name);
688 if (!strcasecmp(map->driver, key->name))
689 ast_cli(fd,"===> %s (db=%s, table=%s)\n",map->name, map->database, map->table ? map->table : map->name);
693 ast_mutex_unlock(&ast_cust_config_lock);
699 static struct ast_cli_entry config_command_struct = {
700 { "show","config","handles", NULL }, config_command,
701 "Show Config Handles", NULL };
703 int register_config_cli()
705 return ast_cli_register(&config_command_struct);
708 int read_ast_cust_config(void)
710 char *cfg = config_conf_file;
711 struct ast_config *config;
712 struct ast_variable *v;
713 struct ast_config_map *map;
715 char *driver, *table, *database, *stringp;
717 clear_cust_keywords();
718 config = ast_load(cfg);
720 for (v = ast_variable_browse(config,"settings");v;v=v->next) {
722 driver = strsep(&stringp, ",");
723 database = strsep(&stringp, ",");
724 table = strsep(&stringp, ",");
726 if (!strcmp(v->name,config_conf_file) || !strcmp(v->name,"asterisk.conf")) {
727 ast_log(LOG_WARNING, "Cannot bind asterisk.conf or extconfig.conf!\n");
728 } else if (driver && database) {
729 length = sizeof(struct ast_config_map);
730 length += strlen(v->name) + 1;
731 length += strlen(driver) + 1;
732 length += strlen(database) + 1;
734 length += strlen(table) + 1;
735 map = malloc(length);
737 memset(map, 0, length);
738 map->name = map->stuff;
739 strcpy(map->name, v->name);
740 map->driver = map->name + strlen(map->name) + 1;
741 strcpy(map->driver, driver);
742 map->database = map->driver + strlen(map->driver) + 1;
743 strcpy(map->database, database);
745 map->table = map->database + strlen(map->database) + 1;
746 strcpy(map->table, table);
750 if (option_verbose > 1)
751 ast_verbose(VERBOSE_PREFIX_2 "Binding %s to %s/%s/%s\n",map->name,map->driver, map->database, map->table ? map->table : map->name);