1999-10-24 18:00:20 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk -- An open source telephony toolkit.
1999-10-24 18:00:20 +00:00
*
2005-01-21 07:06:25 +00:00
* Copyright (C) 1999 - 2005, Digium, Inc.
1999-10-24 18:00:20 +00:00
*
2004-10-02 17:16:36 +00:00
* Mark Spencer <markster@digium.com>
1999-10-24 18:00:20 +00:00
*
2005-09-14 20:46:50 +00:00
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
1999-10-24 18:00:20 +00:00
* This program is free software, distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
2005-10-24 20:12:06 +00:00
/*! \file
*
* \brief Configuration File Parser
2005-09-14 20:46:50 +00:00
*
2005-12-30 21:18:06 +00:00
* \author Mark Spencer <markster@digium.com>
*
2005-10-24 20:12:06 +00:00
* Includes the Asterisk Realtime API - ARA
2006-03-12 17:27:57 +00:00
* See doc/realtime.txt and doc/extconfig.txt
1999-10-24 18:00:20 +00:00
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
2003-03-09 06:00:18 +00:00
#include <time.h>
2005-10-31 21:08:55 +00:00
#include <sys/stat.h>
2004-12-11 05:16:30 +00:00
#define AST_INCLUDE_GLOB 1
#ifdef AST_INCLUDE_GLOB
2005-11-01 21:53:30 +00:00
#if defined(__Darwin__) || defined(__CYGWIN__)
2004-12-19 17:33:19 +00:00
#define GLOB_ABORTED GLOB_ABEND
#endif
2004-12-11 05:16:30 +00:00
# include <glob.h>
#endif
2005-04-22 13:11:34 +00:00
2005-06-06 20:27:51 +00:00
#include "asterisk.h"
2005-06-06 22:12:19 +00:00
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" )
2005-06-06 20:27:51 +00:00
2005-04-21 06:02:45 +00:00
#include "asterisk/config.h"
#include "asterisk/cli.h"
#include "asterisk/lock.h"
#include "asterisk/options.h"
#include "asterisk/logger.h"
#include "asterisk/utils.h"
#include "asterisk/channel.h"
#include "asterisk/app.h"
1999-10-24 18:00:20 +00:00
2004-12-04 16:09:45 +00:00
#define MAX_NESTED_COMMENTS 128
#define COMMENT_START ";--"
#define COMMENT_END "--;"
#define COMMENT_META ';'
#define COMMENT_TAG '-'
2002-03-05 23:57:31 +00:00
2005-01-25 06:10:20 +00:00
static char * extconfig_conf = "extconfig.conf" ;
2004-10-05 06:46:11 +00:00
static struct ast_config_map {
struct ast_config_map * next ;
char * name ;
char * driver ;
char * database ;
char * table ;
char stuff [ 0 ];
2005-01-25 06:10:20 +00:00
} * config_maps = NULL ;
AST_MUTEX_DEFINE_STATIC ( config_lock );
static struct ast_config_engine * config_engine_list ;
#define MAX_INCLUDE_LEVEL 10
struct ast_comment {
struct ast_comment * next ;
char cmt [ 0 ];
};
struct ast_category {
2006-02-24 10:50:43 +00:00
char name [ 80 ];
2005-01-30 06:26:19 +00:00
int ignored ; /* do not let user of the config see this category */
2005-01-25 06:10:20 +00:00
struct ast_variable * root ;
struct ast_variable * last ;
struct ast_category * next ;
};
struct ast_config {
struct ast_category * root ;
struct ast_category * last ;
struct ast_category * current ;
struct ast_category * last_browse ; /* used to cache the last category supplied via category_browse */
int include_level ;
int max_include_level ;
};
1999-10-24 18:00:20 +00:00
2005-01-25 06:10:20 +00:00
struct ast_variable * ast_variable_new ( const char * name , const char * value )
1999-10-24 18:00:20 +00:00
{
2005-01-25 06:10:20 +00:00
struct ast_variable * variable ;
2006-02-09 16:59:50 +00:00
int name_len = strlen ( name ) + 1 ;
2005-01-25 06:10:20 +00:00
2006-02-09 16:59:50 +00:00
if (( variable = ast_calloc ( 1 , name_len + strlen ( value ) + 1 + sizeof ( * variable )))) {
2005-01-25 06:10:20 +00:00
variable -> name = variable -> stuff ;
2006-02-09 16:59:50 +00:00
variable -> value = variable -> stuff + name_len ;
2005-01-25 06:10:20 +00:00
strcpy ( variable -> name , name );
strcpy ( variable -> value , value );
2001-10-09 14:54:42 +00:00
}
2005-01-25 06:10:20 +00:00
return variable ;
}
void ast_variable_append ( struct ast_category * category , struct ast_variable * variable )
{
2006-04-06 16:06:57 +00:00
if ( ! variable )
return ;
2005-01-30 17:34:44 +00:00
if ( category -> last )
category -> last -> next = variable ;
else
category -> root = variable ;
category -> last = variable ;
2005-01-25 06:10:20 +00:00
}
void ast_variables_destroy ( struct ast_variable * v )
{
struct ast_variable * vn ;
while ( v ) {
vn = v ;
v = v -> next ;
free ( vn );
1999-10-24 18:00:20 +00:00
}
}
2005-01-25 06:10:20 +00:00
struct ast_variable * ast_variable_browse ( const struct ast_config * config , const char * category )
{
struct ast_category * cat = NULL ;
if ( category && config -> last_browse && ( config -> last_browse -> name == category ))
cat = config -> last_browse ;
else
cat = ast_category_get ( config , category );
2006-04-06 16:06:57 +00:00
return ( cat ) ? cat -> root : NULL ;
2005-01-25 06:10:20 +00:00
}
char * ast_variable_retrieve ( const struct ast_config * config , const char * category , const char * variable )
1999-10-24 18:00:20 +00:00
{
struct ast_variable * v ;
2005-01-25 06:10:20 +00:00
1999-12-11 20:09:45 +00:00
if ( category ) {
2005-10-17 03:53:27 +00:00
for ( v = ast_variable_browse ( config , category ); v ; v = v -> next ) {
2005-01-25 06:10:20 +00:00
if ( ! strcasecmp ( variable , v -> name ))
2005-10-17 13:53:50 +00:00
return v -> value ;
2005-10-17 03:53:27 +00:00
}
1999-12-11 20:09:45 +00:00
} else {
struct ast_category * cat ;
2005-01-25 06:10:20 +00:00
for ( cat = config -> root ; cat ; cat = cat -> next )
for ( v = cat -> root ; v ; v = v -> next )
if ( ! strcasecmp ( variable , v -> name ))
1999-12-11 20:09:45 +00:00
return v -> value ;
1999-10-24 18:00:20 +00:00
}
2005-01-25 06:10:20 +00:00
1999-10-24 18:00:20 +00:00
return NULL ;
}
2005-01-30 06:26:19 +00:00
static struct ast_variable * variable_clone ( const struct ast_variable * old )
{
struct ast_variable * new = ast_variable_new ( old -> name , old -> v alue );
if ( new ) {
new -> lineno = old -> lineno ;
new -> object = old -> object ;
new -> blanklines = old -> blanklines ;
/* TODO: clone comments? */
}
return new ;
}
static void move_variables ( struct ast_category * old , struct ast_category * new )
{
2006-04-06 16:06:57 +00:00
struct ast_variable * var = old -> root ;
2005-01-30 06:26:19 +00:00
old -> root = NULL ;
2006-04-06 16:06:57 +00:00
#if 1
/* we can just move the entire list in a single op */
ast_variable_append ( new , var );
#else
while ( var ) {
struct ast_variable * next = var -> next ;
2005-01-30 06:26:19 +00:00
var -> next = NULL ;
ast_variable_append ( new , var );
2006-04-06 16:06:57 +00:00
var = next ;
2005-01-30 06:26:19 +00:00
}
2006-04-06 16:06:57 +00:00
#endif
2005-01-30 06:26:19 +00:00
}
2005-01-25 06:10:20 +00:00
struct ast_category * ast_category_new ( const char * name )
{
struct ast_category * category ;
2006-04-06 16:06:57 +00:00
if (( category = ast_calloc ( 1 , sizeof ( * category ))))
2005-07-10 22:56:21 +00:00
ast_copy_string ( category -> name , name , sizeof ( category -> name ));
2005-01-25 06:10:20 +00:00
return category ;
}
2005-01-30 06:26:19 +00:00
static struct ast_category * category_get ( const struct ast_config * config , const char * category_name , int ignored )
2003-03-09 06:00:18 +00:00
{
2005-01-25 06:10:20 +00:00
struct ast_category * cat ;
2004-10-05 06:46:11 +00:00
2006-04-06 16:06:57 +00:00
/* try exact match first, then case-insensitive match */
2005-01-25 06:10:20 +00:00
for ( cat = config -> root ; cat ; cat = cat -> next ) {
2005-01-30 06:26:19 +00:00
if ( cat -> name == category_name && ( ignored || ! cat -> ignored ))
2005-01-25 06:10:20 +00:00
return cat ;
}
for ( cat = config -> root ; cat ; cat = cat -> next ) {
2005-01-30 06:26:19 +00:00
if ( ! strcasecmp ( cat -> name , category_name ) && ( ignored || ! cat -> ignored ))
2005-01-25 06:10:20 +00:00
return cat ;
2005-01-22 22:13:11 +00:00
}
2004-10-05 06:46:11 +00:00
2005-01-22 22:13:11 +00:00
return NULL ;
}
2005-01-30 06:26:19 +00:00
struct ast_category * ast_category_get ( const struct ast_config * config , const char * category_name )
{
return category_get ( config , category_name , 0 );
}
2005-01-25 06:10:20 +00:00
int ast_category_exist ( const struct ast_config * config , const char * category_name )
2005-01-22 22:13:11 +00:00
{
return !! ast_category_get ( config , category_name );
2003-03-09 06:00:18 +00:00
}
2005-01-25 06:10:20 +00:00
void ast_category_append ( struct ast_config * config , struct ast_category * category )
{
if ( config -> last )
config -> last -> next = category ;
else
config -> root = category ;
config -> last = category ;
config -> current = category ;
}
2004-10-05 06:46:11 +00:00
2005-01-25 06:10:20 +00:00
void ast_category_destroy ( struct ast_category * cat )
2003-03-09 06:00:18 +00:00
{
2005-01-25 06:10:20 +00:00
ast_variables_destroy ( cat -> root );
free ( cat );
}
2004-10-05 06:46:11 +00:00
2005-01-30 06:26:19 +00:00
static struct ast_category * next_available_category ( struct ast_category * cat )
{
for (; cat && cat -> ignored ; cat = cat -> next );
return cat ;
}
2005-01-25 06:10:20 +00:00
char * ast_category_browse ( struct ast_config * config , const char * prev )
{
struct ast_category * cat = NULL ;
if ( prev && config -> last_browse && ( config -> last_browse -> name == prev ))
cat = config -> last_browse -> next ;
else if ( ! prev && config -> root )
2006-04-06 16:06:57 +00:00
cat = config -> root ;
2005-01-25 06:10:20 +00:00
else if ( prev ) {
for ( cat = config -> root ; cat ; cat = cat -> next ) {
if ( cat -> name == prev ) {
cat = cat -> next ;
break ;
}
2003-03-09 06:00:18 +00:00
}
2005-01-25 06:10:20 +00:00
if ( ! cat ) {
for ( cat = config -> root ; cat ; cat = cat -> next ) {
if ( ! strcasecmp ( cat -> name , prev )) {
cat = cat -> next ;
break ;
}
}
2003-03-09 06:00:18 +00:00
}
}
2005-01-30 06:26:19 +00:00
if ( cat )
cat = next_available_category ( cat );
2005-01-25 06:10:20 +00:00
config -> last_browse = cat ;
2006-04-06 16:06:57 +00:00
return ( cat ) ? cat -> name : NULL ;
2003-03-09 06:00:18 +00:00
}
2005-01-25 06:10:20 +00:00
struct ast_variable * ast_category_detach_variables ( struct ast_category * cat )
2003-03-09 06:00:18 +00:00
{
2005-01-25 06:10:20 +00:00
struct ast_variable * v ;
v = cat -> root ;
cat -> root = NULL ;
return v ;
}
void ast_category_rename ( struct ast_category * cat , const char * name )
{
2005-07-10 22:56:21 +00:00
ast_copy_string ( cat -> name , name , sizeof ( cat -> name ));
2005-01-25 06:10:20 +00:00
}
2005-01-30 06:26:19 +00:00
static void inherit_category ( struct ast_category * new , const struct ast_category * base )
{
struct ast_variable * var ;
2006-04-06 16:06:57 +00:00
for ( var = base -> root ; var ; var = var -> next )
ast_variable_append ( new , variable_clone ( var ));
2005-01-30 06:26:19 +00:00
}
2005-01-25 06:10:20 +00:00
struct ast_config * ast_config_new ( void )
{
struct ast_config * config ;
2006-04-06 16:06:57 +00:00
if (( config = ast_calloc ( 1 , sizeof ( * config ))))
2005-01-25 06:10:20 +00:00
config -> max_include_level = MAX_INCLUDE_LEVEL ;
return config ;
2003-03-09 06:00:18 +00:00
}
2005-01-25 06:10:20 +00:00
void ast_config_destroy ( struct ast_config * cfg )
2001-04-23 16:50:12 +00:00
{
2005-01-25 06:10:20 +00:00
struct ast_category * cat , * catn ;
if ( ! cfg )
return ;
cat = cfg -> root ;
while ( cat ) {
ast_variables_destroy ( cat -> root );
catn = cat ;
cat = cat -> next ;
free ( catn );
}
free ( cfg );
2001-04-23 16:50:12 +00:00
}
2005-01-25 06:10:20 +00:00
struct ast_category * ast_config_get_current_category ( const struct ast_config * cfg )
2005-01-22 22:13:11 +00:00
{
2005-01-25 06:10:20 +00:00
return cfg -> current ;
2005-01-22 22:13:11 +00:00
}
2003-04-28 19:58:43 +00:00
2005-01-25 06:10:20 +00:00
void ast_config_set_current_category ( struct ast_config * cfg , const struct ast_category * cat )
2005-01-22 22:13:11 +00:00
{
2005-01-25 06:10:20 +00:00
/* cast below is just to silence compiler warning about dropping "const" */
cfg -> current = ( struct ast_category * ) cat ;
2005-01-22 22:13:11 +00:00
}
2004-12-28 14:53:40 +00:00
2005-01-25 06:10:20 +00:00
static int process_text_line ( struct ast_config * cfg , struct ast_category ** cat , char * buf , int lineno , const char * configfile )
2002-03-05 23:57:31 +00:00
{
char * c ;
2005-01-30 06:26:19 +00:00
char * cur = buf ;
2002-03-05 23:57:31 +00:00
struct ast_variable * v ;
2005-02-02 03:38:24 +00:00
char cmd [ 512 ], exec_file [ 512 ];
int object , do_exec , do_include ;
2005-01-22 22:13:11 +00:00
/* Actually parse the entry */
if ( cur [ 0 ] == '[' ) {
2005-01-30 06:26:19 +00:00
struct ast_category * newcat = NULL ;
char * catname ;
2005-01-22 22:13:11 +00:00
/* A category header */
c = strchr ( cur , ']' );
if ( ! c ) {
ast_log ( LOG_WARNING , "parse error: no closing ']', line %d of %s \n " , lineno , configfile );
return - 1 ;
}
2005-01-30 06:26:19 +00:00
* c ++ = '\0' ;
2005-01-22 22:13:11 +00:00
cur ++ ;
2005-01-30 06:26:19 +00:00
if ( * c ++ != '(' )
c = NULL ;
catname = cur ;
2006-02-09 16:59:50 +00:00
if ( ! ( * cat = newcat = ast_category_new ( catname ))) {
2005-01-22 22:13:11 +00:00
return - 1 ;
}
2005-01-30 06:26:19 +00:00
/* If there are options or categories to inherit from, process them now */
if ( c ) {
if ( ! ( cur = strchr ( c , ')' ))) {
ast_log ( LOG_WARNING , "parse error: no closing ')', line %d of %s \n " , lineno , configfile );
return - 1 ;
}
* cur = '\0' ;
while (( cur = strsep ( & c , "," ))) {
if ( ! strcasecmp ( cur , "!" )) {
( * cat ) -> ignored = 1 ;
} else if ( ! strcasecmp ( cur , "+" )) {
* cat = category_get ( cfg , catname , 1 );
if ( !* cat ) {
2005-08-30 18:32:10 +00:00
ast_config_destroy ( cfg );
2005-01-30 06:26:19 +00:00
if ( newcat )
ast_category_destroy ( newcat );
ast_log ( LOG_WARNING , "Category addition requested, but category '%s' does not exist, line %d of %s \n " , catname , lineno , configfile );
return - 1 ;
}
if ( newcat ) {
move_variables ( newcat , * cat );
ast_category_destroy ( newcat );
newcat = NULL ;
}
} else {
struct ast_category * base ;
base = category_get ( cfg , cur , 1 );
if ( ! base ) {
ast_log ( LOG_WARNING , "Inheritance requested, but category '%s' does not exist, line %d of %s \n " , cur , lineno , configfile );
return - 1 ;
}
inherit_category ( * cat , base );
}
}
}
if ( newcat )
ast_category_append ( cfg , * cat );
2005-01-22 22:13:11 +00:00
} else if ( cur [ 0 ] == '#' ) {
/* A directive */
cur ++ ;
c = cur ;
while ( * c && ( * c > 32 )) c ++ ;
if ( * c ) {
* c = '\0' ;
/* Find real argument */
2006-01-07 15:18:23 +00:00
c = ast_skip_blanks ( c + 1 );
2005-01-22 22:13:11 +00:00
if ( !* c )
2005-01-22 21:00:31 +00:00
c = NULL ;
2005-01-22 22:13:11 +00:00
} else
c = NULL ;
2005-02-02 03:38:24 +00:00
do_include = ! strcasecmp ( cur , "include" );
if ( ! do_include )
do_exec = ! strcasecmp ( cur , "exec" );
else
do_exec = 0 ;
2005-12-04 20:40:46 +00:00
if ( do_exec && ! ast_opt_exec_includes ) {
2005-02-02 19:48:53 +00:00
ast_log ( LOG_WARNING , "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)! \n " );
2005-02-02 03:38:24 +00:00
do_exec = 0 ;
}
if ( do_include || do_exec ) {
2005-01-22 22:13:11 +00:00
if ( c ) {
/* Strip off leading and trailing "'s and <>'s */
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 == '\"' ))
2005-01-22 21:00:31 +00:00
* c = '\0' ;
2005-01-22 22:13:11 +00:00
else
break ;
}
2005-02-02 03:38:24 +00:00
/* #exec </path/to/executable>
We create a tmp file, then we #include it, then we delete it. */
if ( do_exec ) {
2005-12-26 18:35:28 +00:00
snprintf ( exec_file , sizeof ( exec_file ), "/var/tmp/exec.%d.%ld" , ( int ) time ( NULL ), ( long ) pthread_self ());
2005-02-02 03:38:24 +00:00
snprintf ( cmd , sizeof ( cmd ), "%s > %s 2>&1" , cur , exec_file );
ast_safe_system ( cmd );
cur = exec_file ;
} else
exec_file [ 0 ] = '\0' ;
/* A #include */
do_include = ast_config_internal_load ( cur , cfg ) ? 1 : 0 ;
if ( ! ast_strlen_zero ( exec_file ))
unlink ( exec_file );
if ( ! do_include )
2005-06-07 21:28:04 +00:00
return 0 ;
2005-02-02 03:38:24 +00:00
} else {
ast_log ( LOG_WARNING , "Directive '#%s' needs an argument (%s) at line %d of %s \n " ,
do_exec ? "exec" : "include" ,
do_exec ? "/path/to/executable" : "filename" ,
lineno ,
configfile );
}
2005-01-22 22:13:11 +00:00
}
else
ast_log ( LOG_WARNING , "Unknown directive '%s' at line %d of %s \n " , cur , lineno , configfile );
} else {
/* Just a line (variable = value) */
if ( !* cat ) {
ast_log ( LOG_WARNING ,
"parse error: No category context for line %d of %s \n " , lineno , configfile );
return - 1 ;
}
c = strchr ( cur , '=' );
if ( c ) {
* c = 0 ;
c ++ ;
/* Ignore > in => */
if ( * c == '>' ) {
object = 1 ;
2005-01-22 21:00:31 +00:00
c ++ ;
2005-01-22 22:13:11 +00:00
} else
object = 0 ;
2006-02-09 16:59:50 +00:00
if (( v = ast_variable_new ( ast_strip ( cur ), ast_strip ( c )))) {
2005-01-22 22:13:11 +00:00
v -> lineno = lineno ;
v -> object = object ;
/* Put and reset comments */
v -> blanklines = 0 ;
2005-01-25 06:10:20 +00:00
ast_variable_append ( * cat , v );
2005-01-22 21:00:31 +00:00
} else {
2005-01-22 22:13:11 +00:00
return - 1 ;
2005-01-22 21:00:31 +00:00
}
2005-01-22 22:13:11 +00:00
} else {
ast_log ( LOG_WARNING , "No '=' (equal sign) in line %d of %s \n " , lineno , configfile );
2003-03-09 06:00:18 +00:00
}
2005-01-22 22:13:11 +00:00
2002-03-05 23:57:31 +00:00
}
return 0 ;
}
2005-01-25 06:10:20 +00:00
static struct ast_config * config_text_file_load ( const char * database , const char * table , const char * filename , struct ast_config * cfg )
1999-10-24 18:00:20 +00:00
{
2006-02-24 10:50:43 +00:00
char fn [ 256 ];
2004-06-12 14:34:15 +00:00
char buf [ 8192 ];
2005-01-25 06:10:20 +00:00
char * new_buf , * comment_p , * process_buf ;
1999-10-24 18:00:20 +00:00
FILE * f ;
2006-02-24 10:50:43 +00:00
int lineno = 0 ;
2004-12-04 16:09:45 +00:00
int comment = 0 , nest [ MAX_NESTED_COMMENTS ];
2005-01-25 06:10:20 +00:00
struct ast_category * cat = NULL ;
2005-06-07 21:28:04 +00:00
int count = 0 ;
2005-10-31 21:08:55 +00:00
struct stat statbuf ;
2004-12-04 16:09:45 +00:00
2005-01-25 06:10:20 +00:00
cat = ast_config_get_current_category ( cfg );
2004-06-17 04:53:55 +00:00
2005-01-25 06:10:20 +00:00
if ( filename [ 0 ] == '/' ) {
2005-07-10 22:56:21 +00:00
ast_copy_string ( fn , filename , sizeof ( fn ));
1999-10-24 18:00:20 +00:00
} else {
2005-01-25 06:10:20 +00:00
snprintf ( fn , sizeof ( fn ), "%s/%s" , ( char * ) ast_config_AST_CONFIG_DIR , filename );
1999-10-24 18:00:20 +00:00
}
2005-01-25 06:10:20 +00:00
2004-12-11 05:16:30 +00:00
#ifdef AST_INCLUDE_GLOB
{
int glob_ret ;
glob_t globbuf ;
globbuf . gl_offs = 0 ; /* initialize it to silence gcc */
2004-12-15 16:00:10 +00:00
#ifdef SOLARIS
2004-12-15 16:01:13 +00:00
glob_ret = glob ( fn , GLOB_NOCHECK , NULL , & globbuf );
2004-12-15 16:00:10 +00:00
#else
glob_ret = glob ( fn , GLOB_NOMAGIC | GLOB_BRACE , NULL , & globbuf );
#endif
2004-12-11 05:16:30 +00:00
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 ++ ) {
2005-07-10 22:56:21 +00:00
ast_copy_string ( fn , globbuf . gl_pathv [ i ], sizeof ( fn ));
2004-12-11 05:16:30 +00:00
#endif
2005-10-31 21:08:55 +00:00
do {
2005-11-07 22:05:31 +00:00
if ( stat ( fn , & statbuf ))
2005-10-31 21:08:55 +00:00
continue ;
2005-11-07 22:05:31 +00:00
2005-10-31 21:08:55 +00:00
if ( ! S_ISREG ( statbuf . st_mode )) {
ast_log ( LOG_WARNING , "'%s' is not a regular file, ignoring \n " , fn );
continue ;
}
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 , "No file to parse: %s \n " , fn );
else if ( option_verbose > 1 )
ast_verbose ( "Not found (%s) \n " , strerror ( errno ));
continue ;
}
2005-06-07 21:28:04 +00:00
count ++ ;
1999-10-24 18:00:20 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , "Parsing %s \n " , fn );
2005-01-28 00:31:50 +00:00
else if ( option_verbose > 1 )
2005-01-22 22:13:11 +00:00
ast_verbose ( "Found \n " );
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 ;
2004-12-04 16:09:45 +00:00
} else {
2005-01-22 22:13:11 +00:00
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 ;
2004-12-04 16:09:45 +00:00
} else
2005-01-22 22:13:11 +00:00
process_buf = new_buf ;
2004-12-04 16:09:45 +00:00
}
2005-01-22 22:13:11 +00:00
} 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
new_buf = comment_p + 1 ;
2004-12-04 16:09:45 +00:00
}
2005-01-22 22:13:11 +00:00
}
2005-01-30 06:26:19 +00:00
if ( process_buf ) {
char * buf = ast_strip ( process_buf );
2005-10-26 18:54:24 +00:00
if ( ! ast_strlen_zero ( buf )) {
2005-01-30 06:26:19 +00:00
if ( process_text_line ( cfg , & cat , buf , lineno , filename )) {
cfg = NULL ;
break ;
}
2005-10-26 18:54:24 +00:00
}
1999-10-24 18:00:20 +00:00
}
}
2005-01-22 22:13:11 +00:00
}
1999-10-24 18:00:20 +00:00
fclose ( f );
2005-10-31 21:08:55 +00:00
} while ( 0 );
2004-12-04 16:09:45 +00:00
if ( comment ) {
ast_log ( LOG_WARNING , "Unterminated comment detected beginning on line %d \n " , nest [ comment ]);
}
2004-12-11 05:16:30 +00:00
#ifdef AST_INCLUDE_GLOB
2005-01-25 06:10:20 +00:00
if ( ! cfg )
2004-12-11 05:16:30 +00:00
break ;
}
globfree ( & globbuf );
}
}
#endif
2005-06-07 21:28:04 +00:00
if ( count == 0 )
return NULL ;
2004-12-11 05:16:30 +00:00
2005-01-25 06:10:20 +00:00
return cfg ;
1999-10-24 18:00:20 +00:00
}
2005-01-25 06:10:20 +00:00
int config_text_file_save ( const char * configfile , const struct ast_config * cfg , const char * generator )
2004-06-17 04:53:55 +00:00
{
2005-01-25 06:10:20 +00:00
FILE * f ;
2006-02-24 10:50:43 +00:00
char fn [ 256 ];
2005-01-25 06:10:20 +00:00
char date [ 256 ] = "" ;
time_t t ;
struct ast_variable * var ;
struct ast_category * cat ;
int blanklines = 0 ;
if ( configfile [ 0 ] == '/' ) {
2005-06-06 03:04:58 +00:00
ast_copy_string ( fn , configfile , sizeof ( fn ));
2004-06-17 04:53:55 +00:00
} else {
2005-05-08 17:31:09 +00:00
snprintf ( fn , sizeof ( fn ), "%s/%s" , ast_config_AST_CONFIG_DIR , configfile );
2004-06-11 00:12:35 +00:00
}
2005-01-25 06:10:20 +00:00
time ( & t );
2005-07-10 22:56:21 +00:00
ast_copy_string ( date , ctime ( & t ), sizeof ( date ));
2005-11-01 21:53:30 +00:00
#ifdef __CYGWIN__
if (( f = fopen ( fn , "w+" ))) {
#else
2005-01-25 06:10:20 +00:00
if (( f = fopen ( fn , "w" ))) {
2005-11-01 21:53:30 +00:00
#endif
2005-01-25 06:10:20 +00:00
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 );
else
fprintf ( f , "%s %s %s \n " , var -> name , ( var -> object ? "=>" : "=" ), var -> value );
if ( var -> blanklines ) {
blanklines = var -> blanklines ;
while ( blanklines -- )
fprintf ( f , " \n " );
}
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 ;
}
static void clear_config_maps ( void )
{
struct ast_config_map * map ;
ast_mutex_lock ( & config_lock );
while ( config_maps ) {
map = config_maps ;
config_maps = config_maps -> next ;
free ( map );
}
ast_mutex_unlock ( & config_lock );
}
2005-02-10 21:14:11 +00:00
static int append_mapping ( char * name , char * driver , char * database , char * table )
{
struct ast_config_map * map ;
int length ;
length = sizeof ( * map );
length += strlen ( name ) + 1 ;
length += strlen ( driver ) + 1 ;
length += strlen ( database ) + 1 ;
if ( table )
length += strlen ( table ) + 1 ;
2006-02-09 16:59:50 +00:00
if ( ! ( map = ast_calloc ( 1 , length )))
2005-02-10 21:14:11 +00:00
return - 1 ;
map -> name = map -> stuff ;
strcpy ( map -> name , 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 );
}
map -> next = config_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 );
config_maps = map ;
return 0 ;
}
2006-02-14 23:08:06 +00:00
int read_config_maps ( void )
2005-01-25 06:10:20 +00:00
{
2005-06-07 21:28:04 +00:00
struct ast_config * config , * configtmp ;
2005-01-25 06:10:20 +00:00
struct ast_variable * v ;
char * driver , * table , * database , * stringp ;
clear_config_maps ();
2005-06-07 21:28:04 +00:00
configtmp = ast_config_new ();
configtmp -> max_include_level = 1 ;
config = ast_config_internal_load ( extconfig_conf , configtmp );
if ( ! config ) {
ast_config_destroy ( configtmp );
2006-02-14 23:08:06 +00:00
return 0 ;
2005-06-07 21:28:04 +00:00
}
2005-01-25 06:10:20 +00:00
for ( v = ast_variable_browse ( config , "settings" ); v ; v = v -> next ) {
stringp = v -> value ;
driver = strsep ( & stringp , "," );
2005-12-27 06:42:29 +00:00
/* check if the database text starts with a double quote */
if ( * stringp == '"' ) {
stringp ++ ;
database = strsep ( & stringp , " \" " );
strsep ( & stringp , "," );
} else {
/* apparently this text has no quotes */
database = strsep ( & stringp , "," );
}
2005-01-25 06:10:20 +00:00
table = strsep ( & stringp , "," );
2005-12-27 06:42:29 +00:00
2005-07-05 22:11:43 +00:00
if ( ! strcmp ( v -> name , extconfig_conf )) {
ast_log ( LOG_WARNING , "Cannot bind '%s'! \n " , extconfig_conf );
continue ;
}
if ( ! strcmp ( v -> name , "asterisk.conf" )) {
ast_log ( LOG_WARNING , "Cannot bind 'asterisk.conf'! \n " );
continue ;
}
if ( ! strcmp ( v -> name , "logger.conf" )) {
ast_log ( LOG_WARNING , "Cannot bind 'logger.conf'! \n " );
2005-01-25 06:10:20 +00:00
continue ;
}
if ( ! driver || ! database )
continue ;
2005-02-10 21:14:11 +00:00
if ( ! strcasecmp ( v -> name , "sipfriends" )) {
ast_log ( LOG_WARNING , "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table. \n " );
append_mapping ( "sipusers" , driver , database , table ? table : "sipfriends" );
append_mapping ( "sippeers" , driver , database , table ? table : "sipfriends" );
} else if ( ! strcasecmp ( v -> name , "iaxfriends" )) {
ast_log ( LOG_WARNING , "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table. \n " );
append_mapping ( "iaxusers" , driver , database , table ? table : "iaxfriends" );
append_mapping ( "iaxpeers" , driver , database , table ? table : "iaxfriends" );
} else
append_mapping ( v -> name , driver , database , table );
2005-01-25 06:10:20 +00:00
}
ast_config_destroy ( config );
2006-02-14 23:08:06 +00:00
return 0 ;
2005-01-25 06:10:20 +00:00
}
int ast_config_engine_register ( struct ast_config_engine * new )
{
struct ast_config_engine * ptr ;
ast_mutex_lock ( & config_lock );
if ( ! config_engine_list ) {
config_engine_list = new ;
} else {
for ( ptr = config_engine_list ; ptr -> next ; ptr = ptr -> next );
ptr -> next = new ;
}
ast_mutex_unlock ( & config_lock );
ast_log ( LOG_NOTICE , "Registered Config Engine %s \n " , new -> name );
2004-06-11 00:12:35 +00:00
return 1 ;
}
2005-01-25 06:10:20 +00:00
int ast_config_engine_deregister ( struct ast_config_engine * del )
2004-06-17 04:53:55 +00:00
{
2005-01-25 06:10:20 +00:00
struct ast_config_engine * ptr , * last = NULL ;
ast_mutex_lock ( & config_lock );
for ( ptr = config_engine_list ; ptr ; ptr = ptr -> next ) {
2004-06-17 04:53:55 +00:00
if ( ptr == del ) {
2005-01-25 06:10:20 +00:00
if ( last )
2004-06-17 04:53:55 +00:00
last -> next = ptr -> next ;
2005-01-25 06:10:20 +00:00
else
config_engine_list = ptr -> next ;
break ;
2004-06-17 04:53:55 +00:00
}
last = ptr ;
}
2004-06-11 00:12:35 +00:00
2005-01-25 06:10:20 +00:00
ast_mutex_unlock ( & config_lock );
return 0 ;
2004-06-11 00:12:35 +00:00
}
2006-01-20 00:18:42 +00:00
/*! \brief Find realtime engine for realtime family */
2005-08-23 01:44:28 +00:00
static struct ast_config_engine * find_engine ( const char * family , char * database , int dbsiz , char * table , int tabsiz )
2002-03-05 23:57:31 +00:00
{
2005-07-05 21:17:58 +00:00
struct ast_config_engine * eng , * ret = NULL ;
2005-01-25 06:10:20 +00:00
struct ast_config_map * map ;
2002-03-05 23:57:31 +00:00
2005-01-25 06:10:20 +00:00
ast_mutex_lock ( & config_lock );
2004-11-23 17:58:59 +00:00
2005-07-05 21:17:58 +00:00
for ( map = config_maps ; map ; map = map -> next ) {
2005-08-23 01:44:28 +00:00
if ( ! strcasecmp ( family , map -> name )) {
if ( database )
ast_copy_string ( database , map -> database , dbsiz );
if ( table )
ast_copy_string ( table , map -> table ? map -> table : family , tabsiz );
2005-01-25 06:10:20 +00:00
break ;
2001-10-09 14:54:42 +00:00
}
}
2005-08-23 01:44:28 +00:00
/* Check if the required driver (engine) exist */
2005-01-25 06:10:20 +00:00
if ( map ) {
2005-07-05 21:17:58 +00:00
for ( eng = config_engine_list ; ! ret && eng ; eng = eng -> next ) {
if ( ! strcasecmp ( eng -> name , map -> driver ))
2005-01-25 06:10:20 +00:00
ret = eng ;
1999-10-24 18:00:20 +00:00
}
}
2005-07-05 21:17:58 +00:00
2005-01-25 06:10:20 +00:00
ast_mutex_unlock ( & config_lock );
2005-07-05 21:17:58 +00:00
/* if we found a mapping, but the engine is not available, then issue a warning */
if ( map && ! ret )
2005-08-23 01:44:28 +00:00
ast_log ( LOG_WARNING , "Realtime mapping for '%s' found to engine '%s', but the engine is not available \n " , map -> name , map -> driver );
2005-01-25 06:10:20 +00:00
return ret ;
1999-10-24 18:00:20 +00:00
}
2004-06-11 00:12:35 +00:00
2005-01-25 06:10:20 +00:00
static struct ast_config_engine text_file_engine = {
. name = "text" ,
. load_func = config_text_file_load ,
};
2004-06-11 00:12:35 +00:00
2005-01-25 06:10:20 +00:00
struct ast_config * ast_config_internal_load ( const char * filename , struct ast_config * cfg )
2004-06-17 04:53:55 +00:00
{
2006-02-24 10:50:43 +00:00
char db [ 256 ];
char table [ 256 ];
2005-01-25 06:10:20 +00:00
struct ast_config_engine * loader = & text_file_engine ;
struct ast_config * result ;
2004-06-11 00:12:35 +00:00
2005-01-25 06:10:20 +00:00
if ( cfg -> include_level == cfg -> max_include_level ) {
ast_log ( LOG_WARNING , "Maximum Include level (%d) exceeded \n " , cfg -> max_include_level );
return NULL ;
2004-06-11 00:12:35 +00:00
}
2005-01-25 06:10:20 +00:00
cfg -> include_level ++ ;
if ( strcmp ( filename , extconfig_conf ) && strcmp ( filename , "asterisk.conf" ) && config_engine_list ) {
struct ast_config_engine * eng ;
eng = find_engine ( filename , db , sizeof ( db ), table , sizeof ( table ));
2005-02-02 03:38:24 +00:00
2005-01-25 06:10:20 +00:00
if ( eng && eng -> load_func ) {
loader = eng ;
} else {
eng = find_engine ( "global" , db , sizeof ( db ), table , sizeof ( table ));
if ( eng && eng -> load_func )
loader = eng ;
}
}
result = loader -> load_func ( db , table , filename , cfg );
2005-02-02 03:38:24 +00:00
2005-01-25 06:10:20 +00:00
if ( result )
result -> include_level -- ;
return result ;
2004-06-11 00:12:35 +00:00
}
2005-01-25 06:10:20 +00:00
struct ast_config * ast_config_load ( const char * filename )
2004-06-17 04:53:55 +00:00
{
2005-01-25 06:10:20 +00:00
struct ast_config * cfg ;
struct ast_config * result ;
cfg = ast_config_new ();
if ( ! cfg )
return NULL ;
result = ast_config_internal_load ( filename , cfg );
if ( ! result )
ast_config_destroy ( cfg );
return result ;
2004-06-11 00:12:35 +00:00
}
2005-01-25 06:10:20 +00:00
struct ast_variable * ast_load_realtime ( const char * family , ...)
2004-06-17 04:53:55 +00:00
{
2005-01-25 06:10:20 +00:00
struct ast_config_engine * eng ;
2006-02-24 10:50:43 +00:00
char db [ 256 ] = "" ;
char table [ 256 ] = "" ;
2005-01-25 06:10:20 +00:00
struct ast_variable * res = NULL ;
va_list ap ;
va_start ( ap , family );
eng = find_engine ( family , db , sizeof ( db ), table , sizeof ( table ));
if ( eng && eng -> realtime_func )
res = eng -> realtime_func ( db , table , ap );
va_end ( ap );
return res ;
2004-06-11 00:12:35 +00:00
}
2005-01-25 06:10:20 +00:00
2006-01-20 00:18:42 +00:00
/*! \brief Check if realtime engine is configured for family */
2005-08-23 01:44:28 +00:00
int ast_check_realtime ( const char * family )
{
struct ast_config_engine * eng ;
eng = find_engine ( family , NULL , 0 , NULL , 0 );
if ( eng )
return 1 ;
return 0 ;
}
2005-01-25 06:10:20 +00:00
struct ast_config * ast_load_realtime_multientry ( const char * family , ...)
2004-06-17 04:53:55 +00:00
{
2005-01-25 06:10:20 +00:00
struct ast_config_engine * eng ;
2006-02-24 10:50:43 +00:00
char db [ 256 ] = "" ;
char table [ 256 ] = "" ;
2005-01-25 06:10:20 +00:00
struct ast_config * res = NULL ;
va_list ap ;
va_start ( ap , family );
eng = find_engine ( family , db , sizeof ( db ), table , sizeof ( table ));
if ( eng && eng -> realtime_multi_func )
res = eng -> realtime_multi_func ( db , table , ap );
va_end ( ap );
return res ;
2004-06-11 00:12:35 +00:00
}
2005-01-25 06:10:20 +00:00
int ast_update_realtime ( const char * family , const char * keyfield , const char * lookup , ...)
2004-06-17 04:53:55 +00:00
{
2005-01-25 06:10:20 +00:00
struct ast_config_engine * eng ;
int res = - 1 ;
2006-02-24 10:50:43 +00:00
char db [ 256 ] = "" ;
char table [ 256 ] = "" ;
2005-01-25 06:10:20 +00:00
va_list ap ;
va_start ( ap , lookup );
eng = find_engine ( family , db , sizeof ( db ), table , sizeof ( table ));
if ( eng && eng -> update_func )
res = eng -> update_func ( db , table , keyfield , lookup , ap );
va_end ( ap );
return res ;
2004-06-11 00:12:35 +00:00
}
2004-06-17 04:53:55 +00:00
static int config_command ( int fd , int argc , char ** argv )
{
2005-01-25 06:10:20 +00:00
struct ast_config_engine * eng ;
2004-10-05 06:46:11 +00:00
struct ast_config_map * map ;
2004-06-11 00:12:35 +00:00
2005-01-25 06:10:20 +00:00
ast_mutex_lock ( & config_lock );
ast_cli ( fd , " \n\n " );
for ( eng = config_engine_list ; eng ; eng = eng -> next ) {
ast_cli ( fd , " \n Config Engine: %s \n " , eng -> name );
for ( map = config_maps ; map ; map = map -> next )
if ( ! strcasecmp ( map -> driver , eng -> name )) {
ast_cli ( fd , "===> %s (db=%s, table=%s) \n " , map -> name , map -> database ,
map -> table ? map -> table : map -> name );
}
2004-06-11 00:12:35 +00:00
}
ast_cli ( fd , " \n\n " );
2005-01-25 06:10:20 +00:00
ast_mutex_unlock ( & config_lock );
2004-06-17 04:53:55 +00:00
return 0 ;
2004-06-11 00:12:35 +00:00
}
2005-03-17 00:35:06 +00:00
static char show_config_help [] =
"Usage: show config mappings \n "
" Shows the filenames to config engines. \n " ;
2004-06-11 00:12:35 +00:00
static struct ast_cli_entry config_command_struct = {
2005-03-17 00:35:06 +00:00
{ "show" , "config" , "mappings" , NULL }, config_command , "Show Config mappings (file names to config engines)" , show_config_help , NULL
2005-01-25 06:10:20 +00:00
};
2004-06-11 00:12:35 +00:00
2004-06-17 04:53:55 +00:00
int register_config_cli ()
{
2004-06-11 00:12:35 +00:00
return ast_cli_register ( & config_command_struct );
}