2 * Asterisk -- A telephony toolkit for Linux.
4 * Configuration File Parser
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
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)(char *, struct ast_config *,struct ast_category **,struct ast_variable **,int
33 #ifdef PRESERVE_COMMENTS
34 ,struct ast_comment_struct *
38 AST_MUTEX_DEFINE_STATIC(ast_cust_config_lock);
39 static struct ast_config_reg *ast_cust_config_list;
40 static char *config_conf_file = "extconfig.conf";
42 static char *strip(char *buf)
45 /* Strip off trailing whitespace, returns, etc */
46 while(!ast_strlen_zero(buf) && (buf[strlen(buf)-1]<33))
47 buf[strlen(buf)-1] = '\0';
49 /* Strip off leading whitespace, returns, etc */
50 while(*start && (*start < 33))
55 #ifdef PRESERVE_COMMENTS
56 static void free_comments(struct ast_comment *com)
58 struct ast_comment *l;
67 void ast_destroy(struct ast_config *ast)
69 struct ast_category *cat, *catn;
70 struct ast_variable *v, *vn;
82 #ifdef PRESERVE_COMMENTS
83 free_comments(v->precomments);
84 free_comments(v->sameline);
90 #ifdef PRESERVE_COMMENTS
91 free_comments(cat->precomments);
92 free_comments(cat->sameline);
97 #ifdef PRESERVE_COMMENTS
98 free_comments(ast->trailingcomments);
103 int ast_true(char *s)
107 /* Determine if this is a true value */
108 if (!strcasecmp(s, "yes") ||
109 !strcasecmp(s, "true") ||
110 !strcasecmp(s, "y") ||
111 !strcasecmp(s, "t") ||
117 int ast_false(char *s)
121 /* Determine if this is a false value */
122 if (!strcasecmp(s, "no") ||
123 !strcasecmp(s, "false") ||
124 !strcasecmp(s, "n") ||
125 !strcasecmp(s, "f") ||
131 struct ast_variable *ast_variable_browse(struct ast_config *config, char *category)
133 struct ast_category *cat;
136 if (cat->name == category)
142 if (!strcasecmp(cat->name, category))
149 char *ast_variable_retrieve(struct ast_config *config, char *category, char *value)
151 struct ast_variable *v;
153 v = ast_variable_browse(config, category);
155 if (value == v->name)
159 v = ast_variable_browse(config, category);
161 if (!strcasecmp(value, v->name))
166 struct ast_category *cat;
171 if (!strcasecmp(value, v->name))
181 #ifdef PRESERVE_COMMENTS
182 int ast_variable_delete(struct ast_config *cfg, char *category, char *variable, char *value)
184 struct ast_variable *v, *pv, *bv, *bpv;
185 struct ast_category *cat;
188 if (cat->name == category) {
196 if (!strcasecmp(cat->name, category)) {
207 if ((variable == v->name) && (!value || !strcmp(v->value, value)))
213 /* Get the last one that looks like it */
219 if (!strcasecmp(variable, v->name) && (!value || !strcmp(v->value, value))) {
230 /* Unlink from original position */
239 free_comments(v->sameline);
240 free_comments(v->precomments);
246 int ast_category_delete(struct ast_config *cfg, char *category)
248 struct ast_variable *v, *pv;
249 struct ast_category *cat, *cprev;
253 if (cat->name == category) {
263 if (!strcasecmp(cat->name, category)) {
274 cprev->next = cat->next;
276 cfg->root = cat->next;
285 free_comments(pv->sameline);
286 free_comments(pv->precomments);
289 free_comments(cat->sameline);
290 free_comments(cat->precomments);
295 struct ast_variable *ast_variable_append_modify(struct ast_config *config, char *category, char *variable, char *value, int newcat, int newvar, int move)
297 struct ast_variable *v, *pv=NULL, *bv, *bpv;
298 struct ast_category *cat, *pcat;
302 if (cat->name == category) {
310 if (!strcasecmp(cat->name, category)) {
318 cat = malloc(sizeof(struct ast_category));
321 memset(cat, 0, sizeof(struct ast_category));
322 strncpy(cat->name, category, sizeof(cat->name));
324 /* Put us at the end */
330 /* We're the first one */
339 if (variable == v->name)
345 /* Get the last one that looks like it */
351 if (!strcasecmp(variable, v->name)) {
362 /* Unlink from original position */
370 v = malloc(sizeof(struct ast_variable));
373 memset(v, 0, sizeof(struct ast_variable));
374 v->name = strdup(variable);
380 v->value = strdup(value);
382 v->value = strdup("");
397 int ast_category_exist(struct ast_config *config, char *category_name)
399 struct ast_category *category = NULL;
401 category = config->root;
404 if (!strcasecmp(category->name,category_name))
406 category = category->next;
412 #ifdef PRESERVE_COMMENTS
413 static struct ast_comment *build_comment(char *cmt)
415 struct ast_comment *c;
416 int len = strlen(cmt) + 1;
417 c = malloc(sizeof(struct ast_comment) + len);
419 /* Memset the header */
420 memset(c, 0, sizeof(struct ast_comment));
428 static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel
429 #ifdef PRESERVE_COMMENTS
430 , struct ast_comment_struct *acs
434 static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, char *buf, int lineno, char *configfile, int includelevel
435 #ifdef PRESERVE_COMMENTS
436 ,struct ast_comment_struct *acs
443 struct ast_config_reg *reg=NULL;
444 struct ast_config *(*load_func)(char *, struct ast_config *,struct ast_category **,struct ast_variable **,int
445 #ifdef PRESERVE_COMMENTS
446 ,struct ast_comment_struct *
450 struct ast_variable *v;
451 #ifdef PRESERVE_COMMENTS
452 struct ast_comment *com = NULL;
455 /* Strip off lines using ; as comment */
456 c = strchr(buf, ';');
459 #ifdef PRESERVE_COMMENTS
462 com = build_comment(c);
466 if (!ast_strlen_zero(cur)) {
467 /* Actually parse the entry */
469 /* A category header */
470 c = strchr(cur, ']');
473 *_tmpc = malloc(sizeof(struct ast_category));
477 "Out of memory, line %d\n", lineno);
480 memset(*_tmpc, 0, sizeof(struct ast_category));
481 strncpy((*_tmpc)->name, cur+1, sizeof((*_tmpc)->name) - 1);
482 (*_tmpc)->root = NULL;
483 #ifdef PRESERVE_COMMENTS
484 (*_tmpc)->precomments = acs->root;
485 (*_tmpc)->sameline = com;
490 tmp->prev->next = *_tmpc;
493 #ifdef PRESERVE_COMMENTS
500 "parse error: no closing ']', line %d of %s\n", lineno, configfile);
502 } else if (cur[0] == '#') {
506 while(*c && (*c > 32)) c++;
510 /* Find real argument */
511 while(*c && (*c < 33)) c++;
516 if (!strcasecmp(cur, "include")) {
519 while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
520 /* Get rid of leading mess */
522 while(!ast_strlen_zero(cur)) {
523 c = cur + strlen(cur) - 1;
524 if ((*c == '>') || (*c == '<') || (*c == '\"'))
530 if((c = strchr(cur,':'))) {
536 if (includelevel < MAX_INCLUDE_LEVEL) {
539 if(ast_cust_config_list)
540 reg = get_ast_cust_config(cur);
542 load_func = reg->func;
544 ast_log(LOG_NOTICE,"External Include '%s' via '%s' config engine\n",arg,cur);
545 load_func(configfile,tmp, _tmpc, _last, includelevel
546 #ifdef PRESERVE_COMMENTS
552 ast_log(LOG_WARNING,"Cant Find Registered Config Engine [%s] for [%s]\n",cur,arg);
555 __ast_load(cur, tmp, _tmpc, _last, includelevel + 1
556 #ifdef PRESERVE_COMMENTS
562 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", includelevel);
564 ast_log(LOG_WARNING, "Directive '#include' needs an argument (filename) at line %d of %s\n", lineno, configfile);
565 /* Strip off leading and trailing "'s and <>'s */
568 ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
570 /* Just a line (variable = value) */
573 "parse error: No category context for line %d of %s\n", lineno, configfile);
577 c = strchr(cur, '=');
587 v = malloc(sizeof(struct ast_variable));
589 memset(v, 0, sizeof(struct ast_variable));
591 v->name = strdup(strip(cur));
592 v->value = strdup(strip(c));
595 /* Put and reset comments */
596 #ifdef PRESERVE_COMMENTS
597 v->precomments = acs->root;
610 ast_log(LOG_WARNING, "Out of memory, line %d\n", lineno);
614 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
619 /* store any comments if there are any */
620 #ifdef PRESERVE_COMMENTS
623 acs->prev->next = com;
629 (*_last)->blanklines++;
637 #ifdef PRESERVE_COMMENTS
638 static void dump_comments(FILE *f, struct ast_comment *comment)
641 fprintf(f, ";%s", comment->cmt);
642 comment = comment->next;
647 int ast_save(char *configfile, struct ast_config *cfg, char *generator)
653 struct ast_variable *var;
654 struct ast_category *cat;
656 if (configfile[0] == '/') {
657 strncpy(fn, configfile, sizeof(fn)-1);
659 snprintf(fn, sizeof(fn), "%s/%s", AST_CONFIG_DIR, configfile);
662 strncpy(date, ctime(&t), sizeof(date));
663 if ((f = fopen(fn, "w"))) {
664 if ((option_verbose > 1) && !option_debug)
665 ast_verbose( VERBOSE_PREFIX_2 "Saving '%s': ", fn);
667 fprintf(f, ";! Automatically generated configuration file\n");
668 fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
669 fprintf(f, ";! Generator: %s\n", generator);
670 fprintf(f, ";! Creation Date: %s", date);
674 #ifdef PRESERVE_COMMENTS
675 /* Dump any precomments */
676 dump_comments(f, cat->precomments);
678 /* Dump section with any appropriate comment */
679 #ifdef PRESERVE_COMMENTS
681 fprintf(f, "[%s] ; %s\n", cat->name, cat->sameline->cmt);
684 fprintf(f, "[%s]\n", cat->name);
687 #ifdef PRESERVE_COMMENTS
688 dump_comments(f, var->precomments);
691 fprintf(f, "%s %s %s ; %s\n", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
693 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
694 if (var->blanklines) {
695 blanklines = var->blanklines;
705 /* Put an empty line */
710 #ifdef PRESERVE_COMMENTS
711 dump_comments(f, cfg->trailingcomments);
715 printf("Unable to open for writing: %s\n", fn);
716 else if (option_verbose > 1)
717 printf( "Unable to write (%s)", strerror(errno));
724 static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel
725 #ifdef PRESERVE_COMMENTS
726 , struct ast_comment_struct *acs
735 struct ast_config_reg *reg=NULL;
736 struct ast_config *(*load_func)(char *, struct ast_config *,struct ast_category **,struct ast_variable **,int
737 #ifdef PRESERVE_COMMENTS
738 ,struct ast_comment_struct *
744 if(strcmp(configfile,config_conf_file) && strcmp(configfile,"asterisk.conf") && ast_cust_config_list) {
746 load_func = global_load_func;
748 reg = get_ast_cust_config_keyword(configfile);
750 load_func = reg->func;
752 reg = get_ast_cust_config_keyword("global");
754 global_load_func = load_func = reg->func;
759 ast_log(LOG_NOTICE,"Loading Config %s via %s engine\n",configfile,reg && reg->name ? reg->name : "global");
760 tmp = load_func(configfile,tmp, _tmpc, _last, includelevel
761 #ifdef PRESERVE_COMMENTS
774 if (configfile[0] == '/') {
775 strncpy(fn, configfile, sizeof(fn)-1);
777 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, configfile);
779 if ((option_verbose > 1) && !option_debug) {
780 ast_verbose( VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
783 if ((f = fopen(fn, "r"))) {
785 ast_log(LOG_DEBUG, "Parsing %s\n", fn);
786 else if (option_verbose > 1)
787 ast_verbose( "Found\n");
789 tmp = malloc(sizeof(struct ast_config));
791 memset(tmp, 0, sizeof(struct ast_config));
796 ast_log(LOG_WARNING, "Out of memory\n");
802 if (fgets(buf, sizeof(buf), f)) {
803 if (cfg_process(tmp, _tmpc, _last, buf, lineno, configfile, includelevel
804 #ifdef PRESERVE_COMMENTS
816 ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
817 else if (option_verbose > 1)
818 ast_verbose( "Not found (%s)\n", strerror(errno));
820 #ifdef PRESERVE_COMMENTS
822 /* Keep trailing comments */
823 tmp->trailingcomments = acs->root;
831 struct ast_config_reg *get_ast_cust_config_keyword(char *name) {
832 struct ast_config_reg *reg,*ret=NULL;
834 ast_mutex_lock(&ast_cust_config_lock);
835 for(reg=ast_cust_config_list;reg && !ret;reg=reg->next)
836 for(x=0;x<reg->keycount && !ret ;x++)
837 if(!strcmp(reg->keywords[x],name))
839 ast_mutex_unlock(&ast_cust_config_lock);
843 struct ast_config_reg *get_ast_cust_config(char *name) {
844 struct ast_config_reg *ptr=NULL;
845 ast_mutex_lock(&ast_cust_config_lock);
846 for(ptr=ast_cust_config_list;ptr;ptr=ptr->next) {
847 if(!strcmp(name,ptr->name))
850 ast_mutex_unlock(&ast_cust_config_lock);
854 void ast_config_destroy_all(void) {
855 struct ast_config_reg *key;
856 ast_mutex_lock(&ast_cust_config_lock);
857 for(key=ast_cust_config_list;key;key=key->next) {
858 ast_config_deregister(key);
860 ast_cust_config_list = NULL;
861 ast_mutex_unlock(&ast_cust_config_lock);
864 struct ast_config_reg *get_config_registrations(void) {
865 return ast_cust_config_list;
868 int ast_config_register(struct ast_config_reg *new) {
869 struct ast_config_reg *ptr;
870 ast_mutex_lock(&ast_cust_config_lock);
872 if(!ast_cust_config_list)
873 ast_cust_config_list = new;
875 for(ptr=ast_cust_config_list;ptr->next;ptr=ptr->next);
878 ast_mutex_unlock(&ast_cust_config_lock);
879 ast_log(LOG_NOTICE,"Registered Config Engine %s\n",new->name);
883 int ast_config_deregister(struct ast_config_reg *del) {
884 struct ast_config_reg *ptr=NULL,*last=NULL;
885 ast_mutex_lock(&ast_cust_config_lock);
886 for(ptr=ast_cust_config_list;ptr;ptr=ptr->next) {
888 if(last && ptr->next) {
889 last->next = ptr->next;
891 else if(last && ! ptr->next) {
894 else if(! last && ptr->next) {
895 ast_cust_config_list = ptr->next;
897 else if(! last && ! ptr->next) {
898 ast_cust_config_list = NULL;
903 ast_mutex_unlock(&ast_cust_config_lock);
907 int ast_cust_config_active(void) {
908 return (ast_cust_config >0) ? 1 : 0;
911 struct ast_config *ast_load(char *configfile)
913 struct ast_category *tmpc=NULL;
914 struct ast_variable *last = NULL;
917 #ifdef PRESERVE_COMMENTS
918 struct ast_comment_struct acs = { NULL, NULL };
922 return __ast_load(configfile, NULL, &tmpc, &last, 0
923 #ifdef PRESERVE_COMMENTS
929 char *ast_category_browse(struct ast_config *config, char *prev)
931 struct ast_category *cat;
934 return config->root->name;
940 if (cat->name == prev) {
942 return cat->next->name;
950 if (!strcasecmp(cat->name, prev)) {
952 return cat->next->name;
962 struct ast_config *ast_new_config(void) {
963 struct ast_config *config;
964 config = malloc(sizeof(struct ast_config));
965 memset(config,0,sizeof(struct ast_config));
971 struct ast_category *ast_new_category(char *name) {
972 struct ast_category *category;
973 category = malloc(sizeof(struct ast_category));
975 memset(category,0,sizeof(struct ast_category));
976 strncpy(category->name,name,sizeof(category->name));
982 struct ast_variable *ast_new_variable(char *name,char *value) {
983 struct ast_variable *variable;
984 variable = malloc(sizeof(struct ast_variable));
986 memset(variable,0,sizeof(struct ast_variable));
988 variable->name = malloc(strlen(name)+1);
990 strcpy(variable->name,name);
991 variable->value = malloc(strlen(value)+1);
992 if(variable->value) {
993 strcpy(variable->value,value);
996 free(variable->name);
997 variable->name = NULL;
1001 if(!variable->value) {
1009 int ast_cust_config_register(struct ast_config_reg *new) {
1010 ast_config_register(new);
1011 read_ast_cust_config();
1014 int ast_cust_config_deregister(struct ast_config_reg *new) {
1015 ast_config_deregister(new);
1016 read_ast_cust_config();
1020 static void clear_cust_keywords(void) {
1021 struct ast_config_reg *key;
1023 ast_mutex_lock(&ast_cust_config_lock);
1024 for(key=get_config_registrations();key;key=key->next) {
1025 for(x=0;x<key->keycount;x++) {
1026 key->keywords[x][0] = '\0';
1030 ast_mutex_unlock(&ast_cust_config_lock);
1033 static int config_command(int fd, int argc, char **argv) {
1034 struct ast_config_reg *key;
1038 ast_mutex_lock(&ast_cust_config_lock);
1039 for(key=get_config_registrations();key;key=key->next) {
1040 ast_cli(fd,"\nConfig Engine: %s\n",key->name);
1041 for(x=0;x<key->keycount;x++)
1042 ast_cli(fd,"===>%s\n",key->keywords[x]);
1044 ast_mutex_unlock(&ast_cust_config_lock);
1050 static struct ast_cli_entry config_command_struct = {
1051 { "show","config","handles", NULL }, config_command,
1052 "Show Config Handles", NULL };
1054 int register_config_cli() {
1055 return ast_cli_register(&config_command_struct);
1058 int read_ast_cust_config(void) {
1059 char *cfg = config_conf_file;
1060 struct ast_config *config;
1061 struct ast_variable *v;
1062 struct ast_config_reg *ptr;
1063 struct ast_config_reg *test = NULL;
1064 clear_cust_keywords();
1065 config = ast_load(cfg);
1067 for(v = ast_variable_browse(config,"settings");v;v=v->next) {
1069 ptr = get_ast_cust_config(v->value);
1071 if(ptr->keycount >= CONFIG_KEYWORD_ARRAYLEN) {
1072 ast_log(LOG_WARNING,"Max Number of Bindings exceeded for %s->%s %d/%d\n",v->name,v->value,ptr->keycount,CONFIG_KEYWORD_ARRAYLEN);
1075 if(strcmp(v->name,config_conf_file) && strcmp(v->name,"asterisk.conf")) {
1076 if(!(test = get_ast_cust_config_keyword(v->name))) {
1077 ast_log(LOG_NOTICE,"Binding: %s to %s\n",v->name,v->value);
1078 strncpy(ptr->keywords[ptr->keycount],v->name,sizeof(ptr->keywords[ptr->keycount]));
1083 ast_log(LOG_WARNING,"Cannot bind %s, Permission Denied\n",v->name);
1089 ast_destroy(config);
1092 ast_log(LOG_WARNING,"config loader has no config file so nevermind.\n");