Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Configuration File Parser
*
Mark Spencer
committed
* Copyright (C) 1999-2004, Digium, Inc.
Mark Spencer
committed
* Mark Spencer <markster@digium.com>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <asterisk/config_pvt.h>
#include <asterisk/cli.h>
#include <asterisk/lock.h>
#include <asterisk/options.h>
#include <asterisk/logger.h>
struct ast_config *(*global_load_func)(const char *dbname, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
static struct ast_config_map {
struct ast_config_map *next;
char *name;
char *driver;
char *database;
char *table;
char stuff[0];
} *maps = NULL;
AST_MUTEX_DEFINE_STATIC(ast_cust_config_lock);
static struct ast_config_reg *ast_cust_config_list;
static char *config_conf_file = "extconfig.conf";
void ast_destroy_realtime(struct ast_variable *v)
struct ast_variable *vn;
while(v) {
vn = v;
v = v->next;
free(vn);
void ast_destroy(struct ast_config *ast)
{
struct ast_category *cat, *catn;
ast_destroy_realtime(cat->root);
catn = cat;
cat = cat->next;
free(catn);
}
free(ast);
}
/* Determine if this is a true value */
if (!strcasecmp(s, "yes") ||
!strcasecmp(s, "true") ||
!strcasecmp(s, "y") ||
!strcasecmp(s, "t") ||
{
if (!s)
return 0;
/* Determine if this is a false value */
if (!strcasecmp(s, "no") ||
!strcasecmp(s, "false") ||
!strcasecmp(s, "n") ||
!strcasecmp(s, "f") ||
return 0;
}
struct ast_variable *ast_variable_browse(struct ast_config *config, char *category)
{
struct ast_category *cat;
cat = config->root;
while(cat) {
if (cat->name == category)
return cat->root;
cat = cat->next;
}
cat = config->root;
while(cat) {
if (!strcasecmp(cat->name, category))
return cat->root;
cat = cat->next;
}
return NULL;
}
char *ast_variable_retrieve(struct ast_config *config, char *category, char *value)
{
struct ast_variable *v;
v = ast_variable_browse(config, category);
while (v) {
if (value == v->name)
return v->value;
v=v->next;
}
v = ast_variable_browse(config, category);
while (v) {
if (!strcasecmp(value, v->name))
return v->value;
v=v->next;
}
} else {
struct ast_category *cat;
cat = config->root;
while(cat) {
v = cat->root;
while (v) {
if (!strcasecmp(value, v->name))
return v->value;
v=v->next;
}
cat = cat->next;
}
int ast_category_exist(struct ast_config *config, char *category_name)
struct ast_category *category = NULL;
category = config->root;
while(category) {
if (!strcasecmp(category->name,category_name))
return 1;
category = category->next;
}
return 0;
static struct ast_config_reg *get_ast_cust_config_keyword(const char *name, char *database, int dbsiz, char *table, int tabsiz)
struct ast_config_reg *reg,*ret=NULL;
struct ast_config_map *map;
ast_mutex_lock(&ast_cust_config_lock);
map = maps;
while(map) {
if (!strcasecmp(name, map->name)) {
strncpy(database, map->database, dbsiz - 1);
if (map->table)
strncpy(table, map->table, tabsiz - 1);
else
strncpy(table, name, tabsiz - 1);
map = map->next;
if (map) {
for (reg=ast_cust_config_list;reg && !ret;reg=reg->next) {
if (!strcmp(reg->name,map->driver))
ret=reg;
ast_mutex_unlock(&ast_cust_config_lock);
return ret;
void ast_config_destroy_all(void)
struct ast_config_reg *key;
ast_mutex_lock(&ast_cust_config_lock);
for (key=ast_cust_config_list;key;key=key->next) {
ast_config_deregister(key);
ast_cust_config_list = NULL;
ast_mutex_unlock(&ast_cust_config_lock);
static struct ast_config_reg *get_config_registrations(void)
return ast_cust_config_list;
Mark Spencer
committed
static struct ast_config *__ast_load(const char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel);
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 )
/* Strip off lines using ; as comment */
c = strchr(buf, ';');
while (c) {
if ((c == buf) || (*(c-1) != '\\')) {
*c = '\0';
} else {
*(c-1) = ';';
memmove(c, c + 1, strlen(c + 1));
}
c = strchr(c + 1, ';');
cur = ast_strip(buf);
/* Actually parse the entry */
if (cur[0] == '[') {
/* A category header */
c = strchr(cur, ']');
if (c) {
*c = 0;
*_tmpc = malloc(sizeof(struct ast_category));
if (!*_tmpc) {
ast_destroy(tmp);
ast_log(LOG_WARNING,
"Out of memory, line %d\n", lineno);
return -1;
}
strncpy((*_tmpc)->name, cur+1, sizeof((*_tmpc)->name) - 1);
(*_tmpc)->root = NULL;
if (!tmp->prev)
tmp->root = *_tmpc;
else
tmp->prev->next = *_tmpc;
tmp->prev = *_tmpc;
*_last = NULL;
} else {
ast_log(LOG_WARNING,
"parse error: no closing ']', line %d of %s\n", lineno, configfile);
}
} else if (cur[0] == '#') {
/* A directive */
cur++;
c = cur;
while(*c && (*c > 32)) c++;
if (*c) {
*c = '\0';
c++;
/* Find real argument */
while(*c && (*c < 33)) c++;
if (!*c)
c = NULL;
} else
c = NULL;
if (!strcasecmp(cur, "include")) {
/* A #include */
if (c) {
while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
/* Get rid of leading mess */
cur = c;
c = cur + strlen(cur) - 1;
if ((*c == '>') || (*c == '<') || (*c == '\"'))
*c = '\0';
else
break;
}
if((c = strchr(cur,':'))) {
*c = '\0';
ast_log(LOG_WARNING, "Including files with explicit config engine no longer permitted. Please use extconfig.conf to specify all mappings\n");
} else {
__ast_load(cur, tmp, _tmpc, _last, includelevel + 1);
ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", includelevel);
} else
ast_log(LOG_WARNING, "Directive '#include' needs an argument (filename) at line %d of %s\n", lineno, configfile);
/* Strip off leading and trailing "'s and <>'s */
ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
} else {
/* Just a line (variable = value) */
if (!*_tmpc) {
ast_log(LOG_WARNING,
"parse error: No category context for line %d of %s\n", lineno, configfile);
}
c = strchr(cur, '=');
if (c) {
*c = 0;
c++;
/* Ignore > in => */
v = ast_new_variable(ast_strip(cur), ast_strip(c));
if (v) {
v->next = NULL;
v->lineno = lineno;
v->object = object;
/* Put and reset comments */
Mark Spencer
committed
v->blanklines = 0;
(*_last)->next = v;
else
(*_tmpc)->root = v;
*_last = v;
} else {
ast_destroy(tmp);
ast_log(LOG_WARNING, "Out of memory, line %d\n", lineno);
return -1;
}
} else {
ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
}
int ast_save(char *configfile, struct ast_config *cfg, char *generator)
{
FILE *f;
char fn[256];
char date[256]="";
time_t t;
struct ast_variable *var;
struct ast_category *cat;
int blanklines = 0;
if (configfile[0] == '/') {
strncpy(fn, configfile, sizeof(fn)-1);
} else {
snprintf(fn, sizeof(fn), "%s/%s", AST_CONFIG_DIR, configfile);
}
time(&t);
strncpy(date, ctime(&t), sizeof(date) - 1);
if ((f = fopen(fn, "w"))) {
if ((option_verbose > 1) && !option_debug)
ast_verbose( VERBOSE_PREFIX_2 "Saving '%s': ", fn);
fprintf(f, ";!\n");
fprintf(f, ";! Automatically generated configuration file\n");
fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
fprintf(f, ";! Generator: %s\n", generator);
fprintf(f, ";! Creation Date: %s", date);
fprintf(f, ";!\n");
cat = cfg->root;
while(cat) {
/* Dump section with any appropriate comment */
fprintf(f, "[%s]\n", cat->name);
var = cat->root;
while(var) {
if (var->sameline)
fprintf(f, "%s %s %s ; %s\n", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
else
fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
if (var->blanklines) {
blanklines = var->blanklines;
while (blanklines) {
fprintf(f, "\n");
blanklines--;
}
}
var = var->next;
}
#if 0
/* Put an empty line */
fprintf(f, "\n");
#endif
cat = cat->next;
}
} else {
if (option_debug)
printf("Unable to open for writing: %s\n", fn);
else if (option_verbose > 1)
printf( "Unable to write (%s)", strerror(errno));
return -1;
}
fclose(f);
return 0;
}
Mark Spencer
committed
struct ast_variable *ast_load_realtime(const char *family, ...)
{
struct ast_config_reg *reg;
char db[256]="";
char table[256]="";
Mark Spencer
committed
struct ast_variable *res=NULL;
va_list ap;
va_start(ap, family);
reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
if (reg && reg->realtime_func)
Mark Spencer
committed
res = reg->realtime_func(db, table, ap);
va_end(ap);
return res;
}
struct ast_config *ast_load_realtime_multientry(const char *family, ...)
{
struct ast_config_reg *reg;
char db[256]="";
char table[256]="";
struct ast_config *res=NULL;
va_list ap;
va_start(ap, family);
reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
if (reg && reg->realtime_multi_func)
res = reg->realtime_multi_func(db, table, ap);
va_end(ap);
return res;
}
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
{
struct ast_config_reg *reg;
int res = -1;
char db[256]="";
char table[256]="";
va_list ap;
va_start(ap, lookup);
reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
if (reg && reg->update_func)
res = reg->update_func(db, table, keyfield, lookup, ap);
va_end(ap);
return res;
}
static struct ast_config *__ast_load(const char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel)
char buf[8192];
char db[256];
char table[256];
struct ast_config *(*load_func)(const char *database, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
if (strcmp(configfile,config_conf_file) && strcmp(configfile,"asterisk.conf") && ast_cust_config_list) {
if (global_load_func) {
load_func = global_load_func;
} else {
reg = get_ast_cust_config_keyword(configfile, db, sizeof(db), table, sizeof(table));
if (reg && reg->static_func) {
load_func = reg->static_func;
reg = get_ast_cust_config_keyword(configfile, db, sizeof(db), table, sizeof(table));
if (reg && reg->static_func)
global_load_func = load_func = reg->static_func;
}
}
if (load_func) {
ast_log(LOG_NOTICE,"Loading Config %s via %s engine\n",configfile,reg && reg->name ? reg->name : "global");
tmp = load_func(db, table, configfile,tmp, _tmpc, _last, includelevel);
snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, configfile);
}
if ((option_verbose > 1) && !option_debug) {
ast_verbose( VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
fflush(stdout);
}
if ((f = fopen(fn, "r"))) {
if (option_debug)
ast_log(LOG_DEBUG, "Parsing %s\n", fn);
else if (option_verbose > 1)
ast_verbose( "Found\n");
if (!tmp) {
tmp = malloc(sizeof(struct ast_config));
if (tmp)
memset(tmp, 0, sizeof(struct ast_config));
if (!tmp) {
ast_log(LOG_WARNING, "Out of memory\n");
fclose(f);
return NULL;
}
while(!feof(f)) {
lineno++;
Mark Spencer
committed
if (fgets(buf, sizeof(buf), f)) {
if (cfg_process(tmp, _tmpc, _last, buf, lineno, configfile, includelevel)) {
}
}
}
fclose(f);
} else {
if (option_debug)
ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
else if (option_verbose > 1)
ast_verbose( "Not found (%s)\n", strerror(errno));
int ast_config_register(struct ast_config_reg *new)
{
struct ast_config_reg *ptr;
ast_mutex_lock(&ast_cust_config_lock);
for(ptr=ast_cust_config_list;ptr->next;ptr=ptr->next);
}
ast_mutex_unlock(&ast_cust_config_lock);
ast_log(LOG_NOTICE,"Registered Config Engine %s\n",new->name);
return 1;
}
int ast_config_deregister(struct ast_config_reg *del)
{
struct ast_config_reg *ptr=NULL,*last=NULL;
ast_mutex_lock(&ast_cust_config_lock);
for (ptr=ast_cust_config_list;ptr;ptr=ptr->next) {
if (ptr == del) {
if (last && ptr->next) {
last->next = ptr->next;
} else if (last && ! ptr->next) {
last->next = NULL;
} else if (!last && ptr->next) {
ast_cust_config_list = ptr->next;
} else if (!last && !ptr->next) {
ast_cust_config_list = NULL;
}
}
last = ptr;
}
ast_mutex_unlock(&ast_cust_config_lock);
return 0;
}
int ast_cust_config_active(void) {
return (ast_cust_config >0) ? 1 : 0;
}
struct ast_config *ast_load(char *configfile)
{
struct ast_category *tmpc=NULL;
struct ast_variable *last = NULL;
return __ast_load(configfile, NULL, &tmpc, &last, 0);
char *ast_category_browse(struct ast_config *config, char *prev)
{
struct ast_category *cat;
if (!prev) {
if (config->root)
return config->root->name;
else
return NULL;
}
cat = config->root;
while(cat) {
if (cat->name == prev) {
if (cat->next)
return cat->next->name;
else
return NULL;
}
cat = cat->next;
}
cat = config->root;
while(cat) {
if (!strcasecmp(cat->name, prev)) {
if (cat->next)
return cat->next->name;
else
return NULL;
}
cat = cat->next;
}
return NULL;
}
struct ast_config *ast_new_config(void)
{
struct ast_config *config;
config = malloc(sizeof(struct ast_config));
memset(config,0,sizeof(struct ast_config));
return config;
}
struct ast_category *ast_new_category(char *name)
{
struct ast_category *category;
category = malloc(sizeof(struct ast_category));
memset(category,0,sizeof(struct ast_category));
strncpy(category->name,name,sizeof(category->name) - 1);
struct ast_variable *ast_new_variable(char *name, char *value)
{
int length = strlen(name) + strlen(value) + 2 + sizeof(struct ast_variable);
variable = malloc(length);
memset(variable, 0, length);
variable->name = variable->stuff;
variable->value = variable->stuff + strlen(name) + 1;
strcpy(variable->name,name);
strcpy(variable->value,value);
int ast_cust_config_register(struct ast_config_reg *new)
{
ast_config_register(new);
read_ast_cust_config();
return 1;
}
int ast_cust_config_deregister(struct ast_config_reg *new)
{
ast_config_deregister(new);
read_ast_cust_config();
return 1;
}
static void clear_cust_keywords(void)
{
struct ast_config_map *map, *prev;
map = maps;
while(map) {
prev = map;
map = map->next;
free(prev);
maps = NULL;
ast_mutex_unlock(&ast_cust_config_lock);
}
static int config_command(int fd, int argc, char **argv)
{
struct ast_config_map *map;
ast_cli(fd,"\n\n");
ast_mutex_lock(&ast_cust_config_lock);
for (key=get_config_registrations();key;key=key->next) {
ast_cli(fd,"\nConfig Engine: %s\n",key->name);
map = maps;
while(map) {
if (!strcasecmp(map->driver, key->name))
ast_cli(fd,"===> %s (db=%s, table=%s)\n",map->name, map->database, map->table ? map->table : map->name);
map = map->next;
}
}
ast_mutex_unlock(&ast_cust_config_lock);
ast_cli(fd,"\n\n");
}
static struct ast_cli_entry config_command_struct = {
{ "show","config","handles", NULL }, config_command,
"Show Config Handles", NULL };
return ast_cli_register(&config_command_struct);
}
char *cfg = config_conf_file;
struct ast_config *config;
struct ast_variable *v;
struct ast_config_map *map;
int length;
char *driver, *table, *database, *stringp;
clear_cust_keywords();
config = ast_load(cfg);
if (config) {
for (v = ast_variable_browse(config,"settings");v;v=v->next) {
stringp = v->value;
driver = strsep(&stringp, ",");
database = strsep(&stringp, ",");
table = strsep(&stringp, ",");
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
if (!strcmp(v->name,config_conf_file) || !strcmp(v->name,"asterisk.conf")) {
ast_log(LOG_WARNING, "Cannot bind asterisk.conf or extconfig.conf!\n");
} else if (driver && database) {
length = sizeof(struct ast_config_map);
length += strlen(v->name) + 1;
length += strlen(driver) + 1;
length += strlen(database) + 1;
if (table)
length += strlen(table) + 1;
map = malloc(length);
if (map) {
memset(map, 0, length);
map->name = map->stuff;
strcpy(map->name, v->name);
map->driver = map->name + strlen(map->name) + 1;
strcpy(map->driver, driver);
map->database = map->driver + strlen(map->driver) + 1;
strcpy(map->database, database);
if (table) {
map->table = map->database + strlen(map->database) + 1;
strcpy(map->table, table);
} else
map->table = NULL;
map->next = maps;
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Binding %s to %s/%s/%s\n",map->name,map->driver, map->database, map->table ? map->table : map->name);
maps = map;
}
}
}
ast_destroy(config);
}