2017-12-13 11:41:16 +03:00
/*
Configuration file handling on top of tini
Copyright ( C ) Amitay Isaacs 2017
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , see < http : //www.gnu.org/licenses/>.
*/
# include "replace.h"
# include "system/locale.h"
# include <talloc.h>
# include "lib/util/dlinklist.h"
# include "lib/util/tini.h"
# include "lib/util/debug.h"
2019-08-19 05:17:20 +03:00
# include "conf/conf.h"
2017-12-13 11:41:16 +03:00
struct conf_value {
enum conf_type type ;
union {
const char * string ;
int integer ;
bool boolean ;
} data ;
} ;
union conf_pointer {
const char * * string ;
int * integer ;
bool * boolean ;
} ;
struct conf_option {
struct conf_option * prev , * next ;
const char * name ;
enum conf_type type ;
void * validate ;
struct conf_value default_value ;
bool default_set ;
struct conf_value * value , * new_value ;
union conf_pointer ptr ;
bool temporary_modified ;
} ;
struct conf_section {
struct conf_section * prev , * next ;
const char * name ;
conf_validate_section_fn validate ;
struct conf_option * option ;
} ;
struct conf_context {
const char * filename ;
struct conf_section * section ;
bool define_failed ;
bool ignore_unknown ;
bool reload ;
bool validation_active ;
} ;
/*
* Functions related to conf_value
*/
static int string_to_string ( TALLOC_CTX * mem_ctx ,
const char * str ,
const char * * str_val )
{
char * t ;
if ( str = = NULL ) {
return EINVAL ;
}
t = talloc_strdup ( mem_ctx , str ) ;
if ( t = = NULL ) {
return ENOMEM ;
}
* str_val = t ;
return 0 ;
}
static int string_to_integer ( const char * str , int * int_val )
{
long t ;
char * endptr = NULL ;
if ( str = = NULL ) {
return EINVAL ;
}
t = strtol ( str , & endptr , 0 ) ;
if ( * str ! = ' \0 ' | | endptr = = NULL ) {
if ( t < 0 | | t > INT_MAX ) {
return EINVAL ;
}
* int_val = ( int ) t ;
return 0 ;
}
return EINVAL ;
}
static int string_to_boolean ( const char * str , bool * bool_val )
{
2018-08-20 06:43:38 +03:00
if ( strcasecmp ( str , " true " ) = = 0 | | strcasecmp ( str , " yes " ) = = 0 ) {
2017-12-13 11:41:16 +03:00
* bool_val = true ;
return 0 ;
}
2018-08-20 06:43:38 +03:00
if ( strcasecmp ( str , " false " ) = = 0 | | strcasecmp ( str , " no " ) = = 0 ) {
2017-12-13 11:41:16 +03:00
* bool_val = false ;
return 0 ;
}
return EINVAL ;
}
static int conf_value_from_string ( TALLOC_CTX * mem_ctx ,
const char * str ,
struct conf_value * value )
{
int ret ;
switch ( value - > type ) {
case CONF_STRING :
ret = string_to_string ( mem_ctx , str , & value - > data . string ) ;
break ;
case CONF_INTEGER :
ret = string_to_integer ( str , & value - > data . integer ) ;
break ;
case CONF_BOOLEAN :
ret = string_to_boolean ( str , & value - > data . boolean ) ;
break ;
default :
2018-08-31 01:45:25 +03:00
return EINVAL ;
2017-12-13 11:41:16 +03:00
}
return ret ;
}
2018-06-25 05:56:45 +03:00
static bool conf_value_compare ( struct conf_value * old , struct conf_value * new )
{
if ( old = = NULL | | new = = NULL ) {
return false ;
}
if ( old - > type ! = new - > type ) {
return false ;
}
switch ( old - > type ) {
case CONF_STRING :
if ( old - > data . string = = NULL & & new - > data . string = = NULL ) {
return true ;
}
if ( old - > data . string ! = NULL & & new - > data . string ! = NULL ) {
if ( strcmp ( old - > data . string , new - > data . string ) = = 0 ) {
return true ;
}
}
break ;
case CONF_INTEGER :
if ( old - > data . integer = = new - > data . integer ) {
return true ;
}
break ;
case CONF_BOOLEAN :
if ( old - > data . boolean = = new - > data . boolean ) {
return true ;
}
break ;
}
return false ;
}
2017-12-13 11:41:16 +03:00
static int conf_value_copy ( TALLOC_CTX * mem_ctx ,
struct conf_value * src ,
struct conf_value * dst )
{
if ( src - > type ! = dst - > type ) {
return EINVAL ;
}
switch ( src - > type ) {
case CONF_STRING :
if ( dst - > data . string ! = NULL ) {
talloc_free ( discard_const ( dst - > data . string ) ) ;
}
if ( src - > data . string = = NULL ) {
dst - > data . string = NULL ;
} else {
dst - > data . string = talloc_strdup (
mem_ctx , src - > data . string ) ;
if ( dst - > data . string = = NULL ) {
return ENOMEM ;
}
}
break ;
case CONF_INTEGER :
dst - > data . integer = src - > data . integer ;
break ;
case CONF_BOOLEAN :
dst - > data . boolean = src - > data . boolean ;
break ;
default :
2018-08-31 01:45:25 +03:00
return EINVAL ;
2017-12-13 11:41:16 +03:00
}
return 0 ;
}
static void conf_value_dump ( const char * key ,
struct conf_value * value ,
bool is_default ,
bool is_temporary ,
FILE * fp )
{
if ( ( value - > type = = CONF_STRING & & value - > data . string = = NULL ) | |
is_default ) {
fprintf ( fp , " \t # %s = " , key ) ;
} else {
fprintf ( fp , " \t %s = " , key ) ;
}
switch ( value - > type ) {
case CONF_STRING :
if ( value - > data . string ! = NULL ) {
fprintf ( fp , " %s " , value - > data . string ) ;
}
break ;
case CONF_INTEGER :
fprintf ( fp , " %d " , value - > data . integer ) ;
break ;
case CONF_BOOLEAN :
fprintf ( fp , " %s " , ( value - > data . boolean ? " true " : " false " ) ) ;
break ;
}
if ( is_temporary ) {
fprintf ( fp , " # temporary " ) ;
}
fprintf ( fp , " \n " ) ;
}
/*
* Functions related to conf_option
*/
static struct conf_option * conf_option_find ( struct conf_section * s ,
const char * key )
{
struct conf_option * opt ;
for ( opt = s - > option ; opt ! = NULL ; opt = opt - > next ) {
if ( strcmp ( opt - > name , key ) = = 0 ) {
return opt ;
}
}
return NULL ;
}
static void conf_option_set_ptr_value ( struct conf_option * opt )
{
switch ( opt - > type ) {
case CONF_STRING :
if ( opt - > ptr . string ! = NULL ) {
* ( opt - > ptr . string ) = opt - > value - > data . string ;
}
break ;
case CONF_INTEGER :
if ( opt - > ptr . integer ! = NULL ) {
* ( opt - > ptr . integer ) = opt - > value - > data . integer ;
}
break ;
case CONF_BOOLEAN :
if ( opt - > ptr . boolean ! = NULL ) {
* ( opt - > ptr . boolean ) = opt - > value - > data . boolean ;
}
break ;
}
}
static void conf_option_default ( struct conf_option * opt ) ;
static int conf_option_add ( struct conf_section * s ,
const char * key ,
enum conf_type type ,
void * validate ,
struct conf_option * * popt )
{
struct conf_option * opt ;
opt = conf_option_find ( s , key ) ;
if ( opt ! = NULL ) {
D_ERR ( " conf: option \" %s \" already exists \n " , key ) ;
return EEXIST ;
}
opt = talloc_zero ( s , struct conf_option ) ;
if ( opt = = NULL ) {
return ENOMEM ;
}
opt - > name = talloc_strdup ( opt , key ) ;
if ( opt - > name = = NULL ) {
talloc_free ( opt ) ;
return ENOMEM ;
}
opt - > type = type ;
opt - > validate = validate ;
DLIST_ADD_END ( s - > option , opt ) ;
if ( popt ! = NULL ) {
* popt = opt ;
}
return 0 ;
}
static int conf_option_set_default ( struct conf_option * opt ,
struct conf_value * default_value )
{
int ret ;
opt - > default_value . type = opt - > type ;
ret = conf_value_copy ( opt , default_value , & opt - > default_value ) ;
if ( ret ! = 0 ) {
return ret ;
}
opt - > default_set = true ;
opt - > temporary_modified = false ;
return 0 ;
}
static void conf_option_set_ptr ( struct conf_option * opt ,
union conf_pointer * ptr )
{
opt - > ptr = * ptr ;
}
static bool conf_option_validate_string ( struct conf_option * opt ,
struct conf_value * value ,
enum conf_update_mode mode )
{
conf_validate_string_option_fn validate =
( conf_validate_string_option_fn ) opt - > validate ;
return validate ( opt - > name ,
opt - > value - > data . string ,
value - > data . string ,
mode ) ;
}
static bool conf_option_validate_integer ( struct conf_option * opt ,
struct conf_value * value ,
enum conf_update_mode mode )
{
conf_validate_integer_option_fn validate =
( conf_validate_integer_option_fn ) opt - > validate ;
return validate ( opt - > name ,
opt - > value - > data . integer ,
value - > data . integer ,
mode ) ;
}
static bool conf_option_validate_boolean ( struct conf_option * opt ,
struct conf_value * value ,
enum conf_update_mode mode )
{
conf_validate_boolean_option_fn validate =
( conf_validate_boolean_option_fn ) opt - > validate ;
return validate ( opt - > name ,
opt - > value - > data . boolean ,
value - > data . boolean ,
mode ) ;
}
static bool conf_option_validate ( struct conf_option * opt ,
struct conf_value * value ,
enum conf_update_mode mode )
{
int ret ;
if ( opt - > validate = = NULL ) {
return true ;
}
switch ( opt - > type ) {
case CONF_STRING :
ret = conf_option_validate_string ( opt , value , mode ) ;
break ;
case CONF_INTEGER :
ret = conf_option_validate_integer ( opt , value , mode ) ;
break ;
case CONF_BOOLEAN :
ret = conf_option_validate_boolean ( opt , value , mode ) ;
break ;
default :
ret = EINVAL ;
}
return ret ;
}
2018-06-25 05:56:45 +03:00
static bool conf_option_same_value ( struct conf_option * opt ,
struct conf_value * new_value )
{
return conf_value_compare ( opt - > value , new_value ) ;
}
2017-12-13 11:41:16 +03:00
static int conf_option_new_value ( struct conf_option * opt ,
struct conf_value * new_value ,
enum conf_update_mode mode )
{
int ret ;
bool ok ;
2018-06-25 05:56:45 +03:00
if ( opt - > new_value ! = & opt - > default_value ) {
TALLOC_FREE ( opt - > new_value ) ;
2017-12-13 11:41:16 +03:00
}
2018-06-25 05:56:45 +03:00
if ( new_value = = & opt - > default_value ) {
/*
* This happens only during load / reload . Set the value to
* default value , so if the config option is dropped from
2023-03-22 11:24:04 +03:00
* config file , then it gets reset to default .
2018-06-25 05:56:45 +03:00
*/
opt - > new_value = & opt - > default_value ;
} else {
ok = conf_option_validate ( opt , new_value , mode ) ;
if ( ! ok ) {
D_ERR ( " conf: validation for option \" %s \" failed \n " ,
opt - > name ) ;
return EINVAL ;
}
2017-12-13 11:41:16 +03:00
2018-06-25 05:56:45 +03:00
opt - > new_value = talloc_zero ( opt , struct conf_value ) ;
if ( opt - > new_value = = NULL ) {
return ENOMEM ;
}
opt - > new_value - > type = opt - > value - > type ;
ret = conf_value_copy ( opt , new_value , opt - > new_value ) ;
if ( ret ! = 0 ) {
return ret ;
}
2017-12-13 11:41:16 +03:00
}
conf_option_set_ptr_value ( opt ) ;
2018-06-25 05:56:45 +03:00
if ( new_value ! = & opt - > default_value ) {
if ( mode = = CONF_MODE_API ) {
opt - > temporary_modified = true ;
} else {
opt - > temporary_modified = false ;
}
2017-12-13 11:41:16 +03:00
}
return 0 ;
}
2018-06-25 05:56:45 +03:00
static int conf_option_new_default_value ( struct conf_option * opt ,
enum conf_update_mode mode )
{
return conf_option_new_value ( opt , & opt - > default_value , mode ) ;
}
2017-12-13 11:41:16 +03:00
static void conf_option_default ( struct conf_option * opt )
{
if ( ! opt - > default_set ) {
return ;
}
if ( opt - > value ! = & opt - > default_value ) {
TALLOC_FREE ( opt - > value ) ;
}
opt - > value = & opt - > default_value ;
conf_option_set_ptr_value ( opt ) ;
}
static void conf_option_reset ( struct conf_option * opt )
{
2018-06-25 05:56:45 +03:00
if ( opt - > new_value ! = & opt - > default_value ) {
TALLOC_FREE ( opt - > new_value ) ;
}
2017-12-13 11:41:16 +03:00
conf_option_set_ptr_value ( opt ) ;
}
static void conf_option_update ( struct conf_option * opt )
{
if ( opt - > new_value = = NULL ) {
return ;
}
if ( opt - > value ! = & opt - > default_value ) {
TALLOC_FREE ( opt - > value ) ;
}
opt - > value = opt - > new_value ;
opt - > new_value = NULL ;
conf_option_set_ptr_value ( opt ) ;
}
2018-06-25 05:56:45 +03:00
static void conf_option_reset_temporary ( struct conf_option * opt )
{
opt - > temporary_modified = false ;
}
2017-12-13 11:41:16 +03:00
static bool conf_option_is_default ( struct conf_option * opt )
{
return ( opt - > value = = & opt - > default_value ) ;
}
static void conf_option_dump ( struct conf_option * opt , FILE * fp )
{
bool is_default ;
is_default = conf_option_is_default ( opt ) ;
conf_value_dump ( opt - > name ,
opt - > value ,
is_default ,
opt - > temporary_modified ,
fp ) ;
}
/*
* Functions related to conf_section
*/
static struct conf_section * conf_section_find ( struct conf_context * conf ,
const char * section )
{
struct conf_section * s ;
for ( s = conf - > section ; s ! = NULL ; s = s - > next ) {
if ( strcasecmp ( s - > name , section ) = = 0 ) {
return s ;
}
}
return NULL ;
}
static int conf_section_add ( struct conf_context * conf ,
const char * section ,
conf_validate_section_fn validate )
{
struct conf_section * s ;
s = conf_section_find ( conf , section ) ;
if ( s ! = NULL ) {
return EEXIST ;
}
s = talloc_zero ( conf , struct conf_section ) ;
if ( s = = NULL ) {
return ENOMEM ;
}
s - > name = talloc_strdup ( s , section ) ;
if ( s - > name = = NULL ) {
talloc_free ( s ) ;
return ENOMEM ;
}
s - > validate = validate ;
DLIST_ADD_END ( conf - > section , s ) ;
return 0 ;
}
static bool conf_section_validate ( struct conf_context * conf ,
struct conf_section * s ,
enum conf_update_mode mode )
{
bool ok ;
if ( s - > validate = = NULL ) {
return true ;
}
ok = s - > validate ( conf , s - > name , mode ) ;
if ( ! ok ) {
D_ERR ( " conf: validation for section [%s] failed \n " , s - > name ) ;
}
return ok ;
}
static void conf_section_dump ( struct conf_section * s , FILE * fp )
{
fprintf ( fp , " [%s] \n " , s - > name ) ;
}
/*
* Functions related to conf_context
*/
static void conf_all_default ( struct conf_context * conf )
{
struct conf_section * s ;
struct conf_option * opt ;
for ( s = conf - > section ; s ! = NULL ; s = s - > next ) {
for ( opt = s - > option ; opt ! = NULL ; opt = opt - > next ) {
conf_option_default ( opt ) ;
}
}
}
2018-06-25 05:56:45 +03:00
static int conf_all_temporary_default ( struct conf_context * conf ,
enum conf_update_mode mode )
{
struct conf_section * s ;
struct conf_option * opt ;
int ret ;
for ( s = conf - > section ; s ! = NULL ; s = s - > next ) {
for ( opt = s - > option ; opt ! = NULL ; opt = opt - > next ) {
ret = conf_option_new_default_value ( opt , mode ) ;
if ( ret ! = 0 ) {
return ret ;
}
}
}
return 0 ;
}
2017-12-13 11:41:16 +03:00
static void conf_all_reset ( struct conf_context * conf )
{
struct conf_section * s ;
struct conf_option * opt ;
for ( s = conf - > section ; s ! = NULL ; s = s - > next ) {
for ( opt = s - > option ; opt ! = NULL ; opt = opt - > next ) {
conf_option_reset ( opt ) ;
}
}
}
static void conf_all_update ( struct conf_context * conf )
{
struct conf_section * s ;
struct conf_option * opt ;
for ( s = conf - > section ; s ! = NULL ; s = s - > next ) {
for ( opt = s - > option ; opt ! = NULL ; opt = opt - > next ) {
conf_option_update ( opt ) ;
2018-06-25 05:56:45 +03:00
conf_option_reset_temporary ( opt ) ;
2017-12-13 11:41:16 +03:00
}
}
}
/*
* API functions
*/
int conf_init ( TALLOC_CTX * mem_ctx , struct conf_context * * result )
{
struct conf_context * conf ;
conf = talloc_zero ( mem_ctx , struct conf_context ) ;
if ( conf = = NULL ) {
return ENOMEM ;
}
conf - > define_failed = false ;
* result = conf ;
return 0 ;
}
void conf_define_section ( struct conf_context * conf ,
const char * section ,
conf_validate_section_fn validate )
{
int ret ;
if ( conf - > define_failed ) {
return ;
}
if ( section = = NULL ) {
conf - > define_failed = true ;
return ;
}
ret = conf_section_add ( conf , section , validate ) ;
if ( ret ! = 0 ) {
conf - > define_failed = true ;
return ;
}
}
static struct conf_option * conf_define ( struct conf_context * conf ,
const char * section ,
const char * key ,
enum conf_type type ,
conf_validate_string_option_fn validate )
{
struct conf_section * s ;
struct conf_option * opt ;
int ret ;
s = conf_section_find ( conf , section ) ;
if ( s = = NULL ) {
D_ERR ( " conf: unknown section [%s] \n " , section ) ;
return NULL ;
}
if ( key = = NULL ) {
D_ERR ( " conf: option name null in section [%s] \n " , section ) ;
return NULL ;
}
ret = conf_option_add ( s , key , type , validate , & opt ) ;
if ( ret ! = 0 ) {
return NULL ;
}
return opt ;
}
static void conf_define_post ( struct conf_context * conf ,
struct conf_option * opt ,
struct conf_value * default_value )
{
int ret ;
ret = conf_option_set_default ( opt , default_value ) ;
if ( ret ! = 0 ) {
conf - > define_failed = true ;
return ;
}
conf_option_default ( opt ) ;
}
void conf_define_string ( struct conf_context * conf ,
const char * section ,
const char * key ,
const char * default_str_val ,
conf_validate_string_option_fn validate )
{
struct conf_option * opt ;
struct conf_value default_value ;
if ( ! conf_valid ( conf ) ) {
return ;
}
opt = conf_define ( conf , section , key , CONF_STRING , validate ) ;
if ( opt = = NULL ) {
conf - > define_failed = true ;
return ;
}
default_value . type = CONF_STRING ;
default_value . data . string = default_str_val ;
conf_define_post ( conf , opt , & default_value ) ;
}
void conf_define_integer ( struct conf_context * conf ,
const char * section ,
const char * key ,
const int default_int_val ,
conf_validate_integer_option_fn validate )
{
struct conf_option * opt ;
struct conf_value default_value ;
if ( ! conf_valid ( conf ) ) {
return ;
}
opt = conf_define ( conf , section , key , CONF_INTEGER , ( void * ) validate ) ;
if ( opt = = NULL ) {
conf - > define_failed = true ;
return ;
}
default_value . type = CONF_INTEGER ;
default_value . data . integer = default_int_val ;
conf_define_post ( conf , opt , & default_value ) ;
}
void conf_define_boolean ( struct conf_context * conf ,
const char * section ,
const char * key ,
const bool default_bool_val ,
conf_validate_boolean_option_fn validate )
{
struct conf_option * opt ;
struct conf_value default_value ;
if ( ! conf_valid ( conf ) ) {
return ;
}
opt = conf_define ( conf , section , key , CONF_BOOLEAN , ( void * ) validate ) ;
if ( opt = = NULL ) {
conf - > define_failed = true ;
return ;
}
default_value . type = CONF_BOOLEAN ;
default_value . data . boolean = default_bool_val ;
conf_define_post ( conf , opt , & default_value ) ;
}
static struct conf_option * _conf_option ( struct conf_context * conf ,
const char * section ,
const char * key )
{
struct conf_section * s ;
struct conf_option * opt ;
s = conf_section_find ( conf , section ) ;
if ( s = = NULL ) {
return NULL ;
}
opt = conf_option_find ( s , key ) ;
return opt ;
}
void conf_assign_string_pointer ( struct conf_context * conf ,
const char * section ,
const char * key ,
const char * * str_ptr )
{
struct conf_option * opt ;
union conf_pointer ptr ;
opt = _conf_option ( conf , section , key ) ;
if ( opt = = NULL ) {
D_ERR ( " conf: unknown option [%s] -> \" %s \" \n " , section , key ) ;
conf - > define_failed = true ;
return ;
}
if ( opt - > type ! = CONF_STRING ) {
conf - > define_failed = true ;
return ;
}
ptr . string = str_ptr ;
conf_option_set_ptr ( opt , & ptr ) ;
conf_option_set_ptr_value ( opt ) ;
}
void conf_assign_integer_pointer ( struct conf_context * conf ,
const char * section ,
const char * key ,
int * int_ptr )
{
struct conf_option * opt ;
union conf_pointer ptr ;
opt = _conf_option ( conf , section , key ) ;
if ( opt = = NULL ) {
D_ERR ( " conf: unknown option [%s] -> \" %s \" \n " , section , key ) ;
conf - > define_failed = true ;
return ;
}
if ( opt - > type ! = CONF_INTEGER ) {
conf - > define_failed = true ;
return ;
}
ptr . integer = int_ptr ;
conf_option_set_ptr ( opt , & ptr ) ;
conf_option_set_ptr_value ( opt ) ;
}
void conf_assign_boolean_pointer ( struct conf_context * conf ,
const char * section ,
const char * key ,
bool * bool_ptr )
{
struct conf_option * opt ;
union conf_pointer ptr ;
opt = _conf_option ( conf , section , key ) ;
if ( opt = = NULL ) {
D_ERR ( " conf: unknown option [%s] -> \" %s \" \n " , section , key ) ;
conf - > define_failed = true ;
return ;
}
if ( opt - > type ! = CONF_BOOLEAN ) {
conf - > define_failed = true ;
return ;
}
ptr . boolean = bool_ptr ;
conf_option_set_ptr ( opt , & ptr ) ;
conf_option_set_ptr_value ( opt ) ;
}
bool conf_query ( struct conf_context * conf ,
const char * section ,
const char * key ,
enum conf_type * type )
{
struct conf_section * s ;
struct conf_option * opt ;
if ( ! conf_valid ( conf ) ) {
return false ;
}
s = conf_section_find ( conf , section ) ;
if ( s = = NULL ) {
return false ;
}
opt = conf_option_find ( s , key ) ;
if ( opt = = NULL ) {
return false ;
}
if ( type ! = NULL ) {
* type = opt - > type ;
}
return true ;
}
bool conf_valid ( struct conf_context * conf )
{
if ( conf - > define_failed ) {
return false ;
}
return true ;
}
void conf_set_defaults ( struct conf_context * conf )
{
conf_all_default ( conf ) ;
}
struct conf_load_state {
struct conf_context * conf ;
struct conf_section * s ;
enum conf_update_mode mode ;
int err ;
} ;
static bool conf_load_section ( const char * section , void * private_data ) ;
static bool conf_load_option ( const char * name ,
const char * value_str ,
void * private_data ) ;
static int conf_load_internal ( struct conf_context * conf )
{
struct conf_load_state state ;
FILE * fp ;
2018-06-25 05:56:45 +03:00
int ret ;
2017-12-13 11:41:16 +03:00
bool ok ;
state = ( struct conf_load_state ) {
. conf = conf ,
. mode = ( conf - > reload ? CONF_MODE_RELOAD : CONF_MODE_LOAD ) ,
} ;
2018-06-25 05:56:45 +03:00
ret = conf_all_temporary_default ( conf , state . mode ) ;
if ( ret ! = 0 ) {
return ret ;
}
2018-07-04 10:45:45 +03:00
fp = fopen ( conf - > filename , " r " ) ;
if ( fp = = NULL ) {
return errno ;
}
2017-12-13 11:41:16 +03:00
ok = tini_parse ( fp ,
false ,
conf_load_section ,
conf_load_option ,
& state ) ;
fclose ( fp ) ;
if ( ! ok ) {
goto fail ;
}
/* Process the last section */
if ( state . s ! = NULL ) {
ok = conf_section_validate ( conf , state . s , state . mode ) ;
if ( ! ok ) {
state . err = EINVAL ;
goto fail ;
}
}
2018-08-31 02:35:14 +03:00
if ( state . err ! = 0 ) {
goto fail ;
}
2017-12-13 11:41:16 +03:00
conf_all_update ( conf ) ;
return 0 ;
fail :
conf_all_reset ( conf ) ;
return state . err ;
}
static bool conf_load_section ( const char * section , void * private_data )
{
struct conf_load_state * state =
( struct conf_load_state * ) private_data ;
bool ok ;
if ( state - > s ! = NULL ) {
ok = conf_section_validate ( state - > conf , state - > s , state - > mode ) ;
if ( ! ok ) {
state - > err = EINVAL ;
2018-08-31 02:35:14 +03:00
return true ;
2017-12-13 11:41:16 +03:00
}
}
state - > s = conf_section_find ( state - > conf , section ) ;
if ( state - > s = = NULL ) {
if ( state - > conf - > ignore_unknown ) {
D_DEBUG ( " conf: ignoring unknown section [%s] \n " ,
section ) ;
} else {
D_ERR ( " conf: unknown section [%s] \n " , section ) ;
state - > err = EINVAL ;
2018-08-31 02:35:14 +03:00
return true ;
2017-12-13 11:41:16 +03:00
}
}
return true ;
}
static bool conf_load_option ( const char * name ,
const char * value_str ,
void * private_data )
{
struct conf_load_state * state =
( struct conf_load_state * ) private_data ;
struct conf_option * opt ;
TALLOC_CTX * tmp_ctx ;
struct conf_value value ;
int ret ;
2018-06-25 05:56:45 +03:00
bool ok ;
2017-12-13 11:41:16 +03:00
if ( state - > s = = NULL ) {
if ( state - > conf - > ignore_unknown ) {
2018-08-31 12:57:56 +03:00
D_DEBUG ( " conf: unknown section for option \" %s \" \n " ,
2017-12-13 11:41:16 +03:00
name ) ;
return true ;
} else {
2018-08-31 12:57:56 +03:00
D_ERR ( " conf: unknown section for option \" %s \" \n " ,
name ) ;
2017-12-13 11:41:16 +03:00
state - > err = EINVAL ;
2018-08-31 02:35:14 +03:00
return true ;
2017-12-13 11:41:16 +03:00
}
}
opt = conf_option_find ( state - > s , name ) ;
if ( opt = = NULL ) {
if ( state - > conf - > ignore_unknown ) {
2018-08-31 01:32:12 +03:00
D_DEBUG ( " conf: unknown option [%s] -> \" %s \" \n " ,
state - > s - > name ,
name ) ;
2017-12-13 11:41:16 +03:00
return true ;
} else {
2018-08-31 01:32:12 +03:00
D_ERR ( " conf: unknown option [%s] -> \" %s \" \n " ,
state - > s - > name ,
name ) ;
2018-08-31 01:42:04 +03:00
state - > err = EINVAL ;
2018-08-31 02:35:14 +03:00
return true ;
2017-12-13 11:41:16 +03:00
}
}
2019-07-25 08:14:32 +03:00
if ( strlen ( value_str ) = = 0 ) {
D_ERR ( " conf: empty value [%s] -> \" %s \" \n " ,
state - > s - > name ,
name ) ;
state - > err = EINVAL ;
return true ;
}
2017-12-13 11:41:16 +03:00
tmp_ctx = talloc_new ( state - > conf ) ;
if ( tmp_ctx = = NULL ) {
state - > err = ENOMEM ;
return false ;
}
value . type = opt - > type ;
ret = conf_value_from_string ( tmp_ctx , value_str , & value ) ;
if ( ret ! = 0 ) {
2018-08-31 02:34:12 +03:00
D_ERR ( " conf: invalid value [%s] -> \" %s \" = \" %s \" \n " ,
state - > s - > name ,
name ,
value_str ) ;
2017-12-13 11:41:16 +03:00
talloc_free ( tmp_ctx ) ;
state - > err = ret ;
2018-08-31 02:35:14 +03:00
return true ;
2017-12-13 11:41:16 +03:00
}
2018-06-25 05:56:45 +03:00
ok = conf_option_same_value ( opt , & value ) ;
if ( ok ) {
goto done ;
}
2017-12-13 11:41:16 +03:00
ret = conf_option_new_value ( opt , & value , state - > mode ) ;
if ( ret ! = 0 ) {
talloc_free ( tmp_ctx ) ;
state - > err = ret ;
2018-08-31 02:35:14 +03:00
return true ;
2017-12-13 11:41:16 +03:00
}
2018-06-25 05:56:45 +03:00
done :
2017-12-13 11:41:16 +03:00
talloc_free ( tmp_ctx ) ;
return true ;
}
int conf_load ( struct conf_context * conf ,
const char * filename ,
2024-08-01 21:25:38 +03:00
bool ignore_unknown ,
bool verbose )
2017-12-13 11:41:16 +03:00
{
conf - > filename = talloc_strdup ( conf , filename ) ;
if ( conf - > filename = = NULL ) {
return ENOMEM ;
}
conf - > ignore_unknown = ignore_unknown ;
2024-08-01 21:25:38 +03:00
if ( verbose ) {
D_NOTICE ( " Reading config file %s \n " , filename ) ;
}
2017-12-13 11:41:16 +03:00
return conf_load_internal ( conf ) ;
}
int conf_reload ( struct conf_context * conf )
{
int ret ;
if ( conf - > filename = = NULL ) {
return EPERM ;
}
D_NOTICE ( " Re-reading config file %s \n " , conf - > filename ) ;
conf - > reload = true ;
ret = conf_load_internal ( conf ) ;
conf - > reload = false ;
return ret ;
}
static int conf_set ( struct conf_context * conf ,
const char * section ,
const char * key ,
struct conf_value * value )
{
struct conf_section * s ;
struct conf_option * opt ;
int ret ;
bool ok ;
s = conf_section_find ( conf , section ) ;
if ( s = = NULL ) {
2018-08-31 01:42:04 +03:00
return EINVAL ;
2017-12-13 11:41:16 +03:00
}
opt = conf_option_find ( s , key ) ;
if ( opt = = NULL ) {
2018-08-31 01:42:04 +03:00
return EINVAL ;
2017-12-13 11:41:16 +03:00
}
if ( opt - > type ! = value - > type ) {
2018-08-31 01:42:04 +03:00
return EINVAL ;
2017-12-13 11:41:16 +03:00
}
2018-06-25 05:56:45 +03:00
ok = conf_option_same_value ( opt , value ) ;
if ( ok ) {
return 0 ;
}
2017-12-13 11:41:16 +03:00
ret = conf_option_new_value ( opt , value , CONF_MODE_API ) ;
if ( ret ! = 0 ) {
conf_option_reset ( opt ) ;
return ret ;
}
ok = conf_section_validate ( conf , s , CONF_MODE_API ) ;
if ( ! ok ) {
conf_option_reset ( opt ) ;
return EINVAL ;
}
conf_option_update ( opt ) ;
return 0 ;
}
int conf_set_string ( struct conf_context * conf ,
const char * section ,
const char * key ,
const char * str_val )
{
struct conf_value value ;
value . type = CONF_STRING ;
value . data . string = str_val ;
return conf_set ( conf , section , key , & value ) ;
}
int conf_set_integer ( struct conf_context * conf ,
const char * section ,
const char * key ,
int int_val )
{
struct conf_value value ;
value . type = CONF_INTEGER ;
value . data . integer = int_val ;
return conf_set ( conf , section , key , & value ) ;
}
int conf_set_boolean ( struct conf_context * conf ,
const char * section ,
const char * key ,
bool bool_val )
{
struct conf_value value ;
value . type = CONF_BOOLEAN ;
value . data . boolean = bool_val ;
return conf_set ( conf , section , key , & value ) ;
}
static int conf_get ( struct conf_context * conf ,
const char * section ,
const char * key ,
enum conf_type type ,
const struct conf_value * * value ,
bool * is_default )
{
struct conf_section * s ;
struct conf_option * opt ;
s = conf_section_find ( conf , section ) ;
if ( s = = NULL ) {
2018-08-31 01:42:04 +03:00
return EINVAL ;
2017-12-13 11:41:16 +03:00
}
opt = conf_option_find ( s , key ) ;
if ( opt = = NULL ) {
2018-08-31 01:42:04 +03:00
return EINVAL ;
2017-12-13 11:41:16 +03:00
}
if ( opt - > type ! = type ) {
return EINVAL ;
}
* value = opt - > value ;
if ( is_default ! = NULL ) {
* is_default = conf_option_is_default ( opt ) ;
}
return 0 ;
}
int conf_get_string ( struct conf_context * conf ,
const char * section ,
const char * key ,
const char * * str_val ,
bool * is_default )
{
const struct conf_value * value ;
int ret ;
ret = conf_get ( conf , section , key , CONF_STRING , & value , is_default ) ;
if ( ret ! = 0 ) {
return ret ;
}
* str_val = value - > data . string ;
return 0 ;
}
int conf_get_integer ( struct conf_context * conf ,
const char * section ,
const char * key ,
int * int_val ,
bool * is_default )
{
const struct conf_value * value ;
int ret ;
ret = conf_get ( conf , section , key , CONF_INTEGER , & value , is_default ) ;
if ( ret ! = 0 ) {
return ret ;
}
* int_val = value - > data . integer ;
return 0 ;
}
int conf_get_boolean ( struct conf_context * conf ,
const char * section ,
const char * key ,
bool * bool_val ,
bool * is_default )
{
const struct conf_value * value ;
int ret ;
ret = conf_get ( conf , section , key , CONF_BOOLEAN , & value , is_default ) ;
if ( ret ! = 0 ) {
return ret ;
}
* bool_val = value - > data . boolean ;
return 0 ;
}
void conf_dump ( struct conf_context * conf , FILE * fp )
{
struct conf_section * s ;
struct conf_option * opt ;
for ( s = conf - > section ; s ! = NULL ; s = s - > next ) {
conf_section_dump ( s , fp ) ;
for ( opt = s - > option ; opt ! = NULL ; opt = opt - > next ) {
conf_option_dump ( opt , fp ) ;
}
}
}