Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Configuration File Parser
*
* Copyright (C) 1999 - 2005, 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>
#define AST_INCLUDE_GLOB 1
#ifdef AST_INCLUDE_GLOB
#ifdef __OSX__
#define GLOB_ABORTED GLOB_ABEND
#endif
#include <asterisk/config_pvt.h>
#include <asterisk/cli.h>
#include <asterisk/lock.h>
#include <asterisk/options.h>
#include <asterisk/logger.h>
#define MAX_NESTED_COMMENTS 128
#define COMMENT_START ";--"
#define COMMENT_END "--;"
#define COMMENT_META ';'
#define COMMENT_TAG '-'
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);
Mark Spencer
committed
void ast_category_destroy(struct ast_category *cat)
{
ast_destroy_realtime(cat->root);
free(cat);
}
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") ||
!strcasecmp(s, "1") ||
!strcasecmp(s, "on"))
{
if (!s)
return 0;
/* Determine if this is a false value */
if (!strcasecmp(s, "no") ||
!strcasecmp(s, "false") ||
!strcasecmp(s, "n") ||
!strcasecmp(s, "f") ||
!strcasecmp(s, "0") ||
!strcasecmp(s, "off"))
struct ast_variable *ast_variable_browse(const struct ast_config *config, const 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(const struct ast_config *config, const char *category, const char *value)
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;
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 int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, char *buf, int lineno, const char *configfile, int includelevel )
cur = ast_strip(buf);
if (!ast_strlen_zero(cur)) {
/* Actually parse the entry */
if (cur[0] == '[') {
/* A category header */
c = strchr(cur, ']');
*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;
memset(*_tmpc, 0, sizeof(struct ast_category));
strncpy((*_tmpc)->name, cur+1, sizeof((*_tmpc)->name) - 1);
(*_tmpc)->root = NULL;
if (!tmp->prev)
tmp->root = *_tmpc;
else
tmp->prev->next = *_tmpc;
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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;
while (!ast_strlen_zero(cur)) {
c = cur + strlen(cur) - 1;
if ((*c == '>') || (*c == '<') || (*c == '\"'))
*c = '\0';
else
break;
}
if((c = strchr(cur,':'))) {
*c = '\0';
c++;
arg = c;
if (includelevel < MAX_INCLUDE_LEVEL) {
if(arg && cur) {
ast_log(LOG_WARNING, "Including files with explicit config engine no longer permitted. Please use extconfig.conf to specify all mappings\n");
} else {
if (!ast_internal_load(cur, tmp, _tmpc, _last, includelevel + 1))
return -1;
}
} else
ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", includelevel);
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 */
}
else
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);
ast_destroy(tmp);
return -1;
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
c = strchr(cur, '=');
if (c) {
*c = 0;
c++;
/* Ignore > in => */
if (*c== '>') {
object = 1;
c++;
} else
object = 0;
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 */
v->blanklines = 0;
if (*_last)
(*_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);
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
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;
}
struct ast_config *ast_internal_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];
int comment = 0, nest[MAX_NESTED_COMMENTS];
struct ast_config *(*load_func)(const char *database, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
char *comment_p, *process_buf, *new_buf=NULL;
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");
if((tmp = load_func(db, table, configfile,tmp, _tmpc, _last, includelevel)))
snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, configfile);
#ifdef AST_INCLUDE_GLOB
{
int glob_ret;
glob_t globbuf;
globbuf.gl_offs = 0; /* initialize it to silence gcc */
#else
glob_ret = glob(fn, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
#endif
if (glob_ret == GLOB_NOSPACE)
ast_log(LOG_WARNING,
"Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
else if (glob_ret == GLOB_ABORTED)
ast_log(LOG_WARNING,
"Glob Expansion of pattern '%s' failed: Read error\n", fn);
else {
/* loop over expanded files */
int i;
for (i=0; i<globbuf.gl_pathc; i++) {
strncpy(fn, globbuf.gl_pathv[i], sizeof(fn)-1);
#endif
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 > 2)
if((tmp = malloc(sizeof(struct ast_config)))) {
memset(tmp, 0, sizeof(struct ast_config));
master = 1;
}
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
if (tmp) {
while(!feof(f)) {
lineno++;
if (fgets(buf, sizeof(buf), f)) {
new_buf = buf;
if (comment)
process_buf = NULL;
else
process_buf = buf;
while ((comment_p = strchr(new_buf, COMMENT_META))) {
if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
/* Yuck, gotta memmove */
memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
new_buf = comment_p;
} else if(comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
/* Meta-Comment start detected ";--" */
if (comment < MAX_NESTED_COMMENTS) {
*comment_p = '\0';
new_buf = comment_p + 3;
comment++;
nest[comment-1] = lineno;
} else {
ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
}
} else if ((comment_p >= new_buf + 2) &&
(*(comment_p - 1) == COMMENT_TAG) &&
(*(comment_p - 2) == COMMENT_TAG)) {
/* Meta-Comment end detected */
comment--;
new_buf = comment_p + 1;
if (!comment) {
/* Back to non-comment now */
if (process_buf) {
/* Actually have to move what's left over the top, then continue */
char *oldptr;
oldptr = process_buf + strlen(process_buf);
memmove(oldptr, new_buf, strlen(new_buf) + 1);
new_buf = oldptr;
} else
process_buf = new_buf;
}
} else {
if (!comment) {
/* If ; is found, and we are not nested in a comment,
we immediately stop all comment processing */
*comment_p = '\0';
new_buf = comment_p;
} else
}
}
if (process_buf && cfg_process(tmp, _tmpc, _last, process_buf, lineno, fn, includelevel)) {
tmp = NULL;
break;
}
} else
ast_log(LOG_WARNING, "Out of memory\n");
} else { /* can't open file */
if (option_debug)
ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
else if (option_verbose > 2)
ast_verbose( "Not found (%s)\n", strerror(errno));
if (comment) {
ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment]);
}
#ifdef AST_INCLUDE_GLOB
if (!tmp)
break;
}
globfree(&globbuf);
}
}
#endif
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_internal_load(configfile, NULL, &tmpc, &last, 0);
}
void ast_category_append(struct ast_config *config, struct ast_category *cat)
{
struct ast_category *prev = NULL;
cat->next = NULL;
if (config->root) {
prev = config->root;
while(prev->next) prev = prev->next;
prev->next = cat;
} else
config->root = cat;
Mark Spencer
committed
}
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, ",");
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
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);
}