2018-05-14 14:16:43 +03:00
/*
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
* Copyright ( C ) 2004 - 2011 Red Hat , Inc . All rights reserved .
*
* This file is part of LVM2 .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v .2 .1 .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include "misc/dmlib.h"
# include <sys/stat.h>
# include <sys/mman.h>
# include <unistd.h>
# include <fcntl.h>
# include <ctype.h>
# include <stdarg.h>
# define SECTION_B_CHAR '{'
# define SECTION_E_CHAR '}'
enum {
TOK_INT ,
TOK_FLOAT ,
TOK_STRING , /* Single quotes */
TOK_STRING_ESCAPED , /* Double quotes */
TOK_STRING_BARE , /* No quotes */
TOK_EQ ,
TOK_SECTION_B ,
TOK_SECTION_E ,
TOK_ARRAY_B ,
TOK_ARRAY_E ,
TOK_IDENTIFIER ,
TOK_COMMA ,
TOK_EOF
} ;
struct parser {
const char * fb , * fe ; /* file limits */
int t ; /* token limits and type */
const char * tb , * te ;
int line ; /* line number we are on */
struct dm_pool * mem ;
int no_dup_node_check ; /* whether to disable dup node checking */
2019-05-10 15:40:11 +03:00
const char * key ; /* last obtained key */
unsigned ignored_creation_time ;
2018-05-14 14:16:43 +03:00
} ;
struct config_output {
struct dm_pool * mem ;
dm_putline_fn putline ;
const struct dm_config_node_out_spec * spec ;
void * baton ;
} ;
static void _get_token ( struct parser * p , int tok_prev ) ;
static void _eat_space ( struct parser * p ) ;
static struct dm_config_node * _file ( struct parser * p ) ;
static struct dm_config_node * _section ( struct parser * p , struct dm_config_node * parent ) ;
static struct dm_config_value * _value ( struct parser * p ) ;
static struct dm_config_value * _type ( struct parser * p ) ;
static int _match_aux ( struct parser * p , int t ) ;
static struct dm_config_value * _create_value ( struct dm_pool * mem ) ;
2024-10-09 15:23:51 +03:00
static struct dm_config_value * _create_str_value ( struct dm_pool * mem , const char * str , size_t str_len ) ;
static struct dm_config_node * _create_node ( struct dm_pool * mem , const char * key , size_t key_len ) ;
2018-05-14 14:16:43 +03:00
static char * _dup_tok ( struct parser * p ) ;
static char * _dup_token ( struct dm_pool * mem , const char * b , const char * e ) ;
# define MAX_INDENT 32
# define match(t) do {\
if ( ! _match_aux ( p , ( t ) ) ) { \
log_error ( " Parse error at byte % " PRIptrdiff_t " (line %d): unexpected token " , \
p - > tb - p - > fb + 1 , p - > line ) ; \
return 0 ; \
} \
} while ( 0 )
static int _tok_match ( const char * str , const char * b , const char * e )
{
while ( * str & & ( b ! = e ) ) {
if ( * str + + ! = * b + + )
return 0 ;
}
return ! ( * str | | ( b ! = e ) ) ;
}
struct dm_config_tree * dm_config_create ( void )
{
struct dm_config_tree * cft ;
struct dm_pool * mem = dm_pool_create ( " config " , 10 * 1024 ) ;
if ( ! mem ) {
log_error ( " Failed to allocate config pool. " ) ;
return 0 ;
}
if ( ! ( cft = dm_pool_zalloc ( mem , sizeof ( * cft ) ) ) ) {
log_error ( " Failed to allocate config tree. " ) ;
dm_pool_destroy ( mem ) ;
return 0 ;
}
cft - > mem = mem ;
return cft ;
}
void dm_config_set_custom ( struct dm_config_tree * cft , void * custom )
{
cft - > custom = custom ;
}
void * dm_config_get_custom ( struct dm_config_tree * cft )
{
return cft - > custom ;
}
void dm_config_destroy ( struct dm_config_tree * cft )
{
dm_pool_destroy ( cft - > mem ) ;
}
/*
* If there ' s a cascaded dm_config_tree , remove and return it , otherwise
* return NULL .
*/
struct dm_config_tree * dm_config_remove_cascaded_tree ( struct dm_config_tree * cft )
{
struct dm_config_tree * second_cft ;
if ( ! cft )
return NULL ;
second_cft = cft - > cascade ;
cft - > cascade = NULL ;
return second_cft ;
}
/*
* When searching , first_cft is checked before second_cft .
*/
struct dm_config_tree * dm_config_insert_cascaded_tree ( struct dm_config_tree * first_cft , struct dm_config_tree * second_cft )
{
first_cft - > cascade = second_cft ;
return first_cft ;
}
static struct dm_config_node * _config_reverse ( struct dm_config_node * head )
{
struct dm_config_node * left = head , * middle = NULL , * right = NULL ;
while ( left ) {
right = middle ;
middle = left ;
left = left - > sib ;
middle - > sib = right ;
middle - > child = _config_reverse ( middle - > child ) ;
}
return middle ;
}
static int _do_dm_config_parse ( struct dm_config_tree * cft , const char * start , const char * end , int no_dup_node_check )
{
/* TODO? if (start == end) return 1; */
2024-05-17 17:50:49 +03:00
struct parser p = {
. mem = cft - > mem ,
. tb = start ,
. te = start ,
. fb = start ,
. fe = end ,
. line = 1 ,
. no_dup_node_check = no_dup_node_check
} ;
_get_token ( & p , TOK_SECTION_E ) ;
if ( ! ( cft - > root = _file ( & p ) ) )
2018-05-14 14:16:43 +03:00
return_0 ;
cft - > root = _config_reverse ( cft - > root ) ;
return 1 ;
}
int dm_config_parse ( struct dm_config_tree * cft , const char * start , const char * end )
{
return _do_dm_config_parse ( cft , start , end , 0 ) ;
}
int dm_config_parse_without_dup_node_check ( struct dm_config_tree * cft , const char * start , const char * end )
{
return _do_dm_config_parse ( cft , start , end , 1 ) ;
}
struct dm_config_tree * dm_config_from_string ( const char * config_settings )
{
struct dm_config_tree * cft ;
if ( ! ( cft = dm_config_create ( ) ) )
return_NULL ;
if ( ! dm_config_parse ( cft , config_settings , config_settings + strlen ( config_settings ) ) ) {
dm_config_destroy ( cft ) ;
return_NULL ;
}
return cft ;
}
static int _line_start ( struct config_output * out )
{
if ( ! dm_pool_begin_object ( out - > mem , 128 ) ) {
log_error ( " dm_pool_begin_object failed for config line " ) ;
return 0 ;
}
return 1 ;
}
__attribute__ ( ( format ( printf , 2 , 3 ) ) )
static int _line_append ( struct config_output * out , const char * fmt , . . . )
{
char buf [ 4096 ] ;
char * dyn_buf = NULL ;
va_list ap ;
int n ;
/*
* We should be fine with the 4096 char buffer 99 % of the time ,
* but if we need to go beyond that , allocate the buffer dynamically .
*/
va_start ( ap , fmt ) ;
n = vsnprintf ( buf , sizeof ( buf ) , fmt , ap ) ;
va_end ( ap ) ;
if ( n < 0 ) {
log_error ( " vsnprintf failed for config line " ) ;
return 0 ;
}
if ( n > ( int ) sizeof buf - 1 ) {
/*
* Fixed size buffer with sizeof buf is not enough ,
* so try dynamically allocated buffer now . . .
*/
va_start ( ap , fmt ) ;
n = dm_vasprintf ( & dyn_buf , fmt , ap ) ;
va_end ( ap ) ;
if ( n < 0 ) {
log_error ( " dm_vasprintf failed for config line " ) ;
return 0 ;
}
}
if ( ! dm_pool_grow_object ( out - > mem , dyn_buf ? : buf , 0 ) ) {
log_error ( " dm_pool_grow_object failed for config line " ) ;
2018-06-08 15:40:53 +03:00
free ( dyn_buf ) ;
2018-05-14 14:16:43 +03:00
return 0 ;
}
2018-06-08 15:40:53 +03:00
free ( dyn_buf ) ;
2018-05-14 14:16:43 +03:00
return 1 ;
}
# define line_append(args...) do {if (!_line_append(out, args)) {return_0;}} while (0)
static int _line_end ( const struct dm_config_node * cn , struct config_output * out )
{
const char * line ;
if ( ! dm_pool_grow_object ( out - > mem , " \0 " , 1 ) ) {
log_error ( " dm_pool_grow_object failed for config line " ) ;
return 0 ;
}
line = dm_pool_end_object ( out - > mem ) ;
if ( ! out - > putline & & ! out - > spec )
return 0 ;
if ( out - > putline )
out - > putline ( line , out - > baton ) ;
if ( out - > spec & & out - > spec - > line_fn )
out - > spec - > line_fn ( cn , line , out - > baton ) ;
return 1 ;
}
static int _write_value ( struct config_output * out , const struct dm_config_value * v )
{
char * buf ;
const char * s ;
switch ( v - > type ) {
case DM_CFG_STRING :
buf = alloca ( dm_escaped_len ( v - > v . str ) ) ;
s = ( v - > format_flags & DM_CONFIG_VALUE_FMT_STRING_NO_QUOTES ) ? " " : " \" " ;
line_append ( " %s%s%s " , s , dm_escape_double_quotes ( buf , v - > v . str ) , s ) ;
break ;
case DM_CFG_FLOAT :
line_append ( " %f " , v - > v . f ) ;
break ;
case DM_CFG_INT :
if ( v - > format_flags & DM_CONFIG_VALUE_FMT_INT_OCTAL )
line_append ( " 0% " PRIo64 , v - > v . i ) ;
else
line_append ( FMTd64 , v - > v . i ) ;
break ;
case DM_CFG_EMPTY_ARRAY :
s = ( v - > format_flags & DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES ) ? " " : " " ;
line_append ( " [%s] " , s ) ;
break ;
default :
log_error ( " _write_value: Unknown value type: %d " , v - > type ) ;
}
return 1 ;
}
static int _write_config ( const struct dm_config_node * n , int only_one ,
struct config_output * out , int level )
{
const char * extra_space ;
int format_array ;
char space [ MAX_INDENT + 1 ] ;
int l = ( level < MAX_INDENT ) ? level : MAX_INDENT ;
int i ;
char * escaped_key = NULL ;
if ( ! n )
return 1 ;
for ( i = 0 ; i < l ; i + + )
space [ i ] = ' \t ' ;
space [ i ] = ' \0 ' ;
do {
extra_space = ( n - > v & & ( n - > v - > format_flags & DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES ) ) ? " " : " " ;
format_array = ( n - > v & & ( n - > v - > format_flags & DM_CONFIG_VALUE_FMT_COMMON_ARRAY ) ) ;
if ( out - > spec & & out - > spec - > prefix_fn )
out - > spec - > prefix_fn ( n , space , out - > baton ) ;
if ( ! _line_start ( out ) )
return_0 ;
if ( strchr ( n - > key , ' # ' ) | | strchr ( n - > key , ' " ' ) | | strchr ( n - > key , ' ! ' ) ) {
escaped_key = alloca ( dm_escaped_len ( n - > key ) + 2 ) ;
* escaped_key = ' " ' ;
dm_escape_double_quotes ( escaped_key + 1 , n - > key ) ;
strcat ( escaped_key , " \" " ) ;
}
line_append ( " %s%s " , space , escaped_key ? escaped_key : n - > key ) ;
escaped_key = NULL ;
if ( ! n - > v ) {
/* it's a sub section */
line_append ( " { " ) ;
if ( ! _line_end ( n , out ) )
return_0 ;
if ( ! _write_config ( n - > child , 0 , out , level + 1 ) )
return_0 ;
if ( ! _line_start ( out ) )
return_0 ;
line_append ( " %s} " , space ) ;
} else {
/* it's a value */
const struct dm_config_value * v = n - > v ;
line_append ( " %s=%s " , extra_space , extra_space ) ;
if ( v - > next ) {
line_append ( " [%s " , extra_space ) ;
while ( v & & v - > type ! = DM_CFG_EMPTY_ARRAY ) {
if ( ! _write_value ( out , v ) )
return_0 ;
v = v - > next ;
if ( v & & v - > type ! = DM_CFG_EMPTY_ARRAY )
line_append ( " ,%s " , extra_space ) ;
}
line_append ( " %s] " , extra_space ) ;
} else {
if ( format_array & & ( v - > type ! = DM_CFG_EMPTY_ARRAY ) )
line_append ( " [%s " , extra_space ) ;
if ( ! _write_value ( out , v ) )
return_0 ;
if ( format_array & & ( v - > type ! = DM_CFG_EMPTY_ARRAY ) )
line_append ( " %s] " , extra_space ) ;
}
}
if ( ! _line_end ( n , out ) )
return_0 ;
if ( out - > spec & & out - > spec - > suffix_fn )
out - > spec - > suffix_fn ( n , space , out - > baton ) ;
n = n - > sib ;
} while ( n & & ! only_one ) ;
/* FIXME: add error checking */
return 1 ;
}
static int _write_node ( const struct dm_config_node * cn , int only_one ,
dm_putline_fn putline ,
const struct dm_config_node_out_spec * out_spec ,
void * baton )
{
struct config_output out = {
. mem = dm_pool_create ( " config_output " , 1024 ) ,
. putline = putline ,
. spec = out_spec ,
. baton = baton
} ;
if ( ! out . mem )
return_0 ;
if ( ! _write_config ( cn , only_one , & out , 0 ) ) {
dm_pool_destroy ( out . mem ) ;
return_0 ;
}
dm_pool_destroy ( out . mem ) ;
return 1 ;
}
int dm_config_write_one_node ( const struct dm_config_node * cn , dm_putline_fn putline , void * baton )
{
return _write_node ( cn , 1 , putline , NULL , baton ) ;
}
int dm_config_write_node ( const struct dm_config_node * cn , dm_putline_fn putline , void * baton )
{
return _write_node ( cn , 0 , putline , NULL , baton ) ;
}
int dm_config_write_one_node_out ( const struct dm_config_node * cn ,
const struct dm_config_node_out_spec * out_spec ,
void * baton )
{
return _write_node ( cn , 1 , NULL , out_spec , baton ) ;
}
int dm_config_write_node_out ( const struct dm_config_node * cn ,
const struct dm_config_node_out_spec * out_spec ,
void * baton )
{
return _write_node ( cn , 0 , NULL , out_spec , baton ) ;
}
/*
* parser
*/
2024-10-09 15:23:51 +03:00
static const char * _string_tok ( struct parser * p , size_t * len )
2018-05-14 14:16:43 +03:00
{
2024-10-09 15:23:51 +03:00
ptrdiff_t d = p - > te - p - > tb ;
2018-05-14 14:16:43 +03:00
2024-10-09 15:23:51 +03:00
if ( d < 2 ) {
2018-05-14 14:16:43 +03:00
log_error ( " Parse error at byte % " PRIptrdiff_t " (line %d): "
" expected a string token. " ,
p - > tb - p - > fb + 1 , p - > line ) ;
return NULL ;
}
2024-10-09 15:23:51 +03:00
* len = ( size_t ) ( d - 2 ) ; /* strip "'s */
return p - > tb + 1 ;
}
static char * _dup_string_tok ( struct parser * p )
{
const char * tok ;
size_t len ;
char * str ;
if ( ! ( tok = _string_tok ( p , & len ) ) )
2018-05-14 14:16:43 +03:00
return_NULL ;
2024-10-09 15:23:51 +03:00
if ( ! ( str = _dup_token ( p - > mem , tok , tok + len ) ) )
return_NULL ;
2018-05-14 14:16:43 +03:00
return str ;
}
static struct dm_config_node * _file ( struct parser * p )
{
struct dm_config_node root = { 0 } ;
root . key = " <root> " ;
while ( p - > t ! = TOK_EOF )
if ( ! _section ( p , & root ) )
return_NULL ;
return root . child ;
}
static struct dm_config_node * _make_node ( struct dm_pool * mem ,
const char * key_b , const char * key_e ,
struct dm_config_node * parent )
{
struct dm_config_node * n ;
2024-10-09 15:23:51 +03:00
if ( ! ( n = _create_node ( mem , key_b , key_e - key_b ) ) )
2018-05-14 14:16:43 +03:00
return_NULL ;
if ( parent ) {
n - > parent = parent ;
n - > sib = parent - > child ;
parent - > child = n ;
}
return n ;
}
/* when mem is not NULL, we create the path if it doesn't exist yet */
static struct dm_config_node * _find_or_make_node ( struct dm_pool * mem ,
struct dm_config_node * parent ,
const char * path ,
int no_dup_node_check )
{
2024-05-13 12:36:54 +03:00
const int sep = ' / ' ;
2018-05-14 14:16:43 +03:00
const char * e ;
struct dm_config_node * cn = parent ? parent - > child : NULL ;
struct dm_config_node * cn_found = NULL ;
while ( cn | | mem ) {
/* trim any leading slashes */
2024-05-13 12:36:54 +03:00
while ( * path & & ( * path = = sep ) )
2018-05-14 14:16:43 +03:00
path + + ;
/* find the end of this segment */
2024-05-13 12:36:54 +03:00
for ( e = path ; * e & & ( * e ! = sep ) ; e + + ) ;
2018-05-14 14:16:43 +03:00
/* hunt for the node */
cn_found = NULL ;
if ( ! no_dup_node_check ) {
while ( cn ) {
if ( _tok_match ( cn - > key , path , e ) ) {
/* Inefficient */
if ( ! cn_found )
cn_found = cn ;
else
log_warn ( " WARNING: Ignoring duplicate "
" config node: %s ( "
" seeking %s) " , cn - > key , path ) ;
}
cn = cn - > sib ;
}
}
if ( ! cn_found & & mem ) {
if ( ! ( cn_found = _make_node ( mem , path , e , parent ) ) )
return_NULL ;
}
if ( cn_found & & * e ) {
parent = cn_found ;
cn = cn_found - > child ;
} else
return cn_found ;
path = e ;
}
return NULL ;
}
static struct dm_config_node * _section ( struct parser * p , struct dm_config_node * parent )
{
/* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */
struct dm_config_node * root ;
struct dm_config_value * value ;
char * str ;
if ( p - > t = = TOK_STRING_ESCAPED ) {
if ( ! ( str = _dup_string_tok ( p ) ) )
return_NULL ;
dm_unescape_double_quotes ( str ) ;
match ( TOK_STRING_ESCAPED ) ;
} else if ( p - > t = = TOK_STRING ) {
if ( ! ( str = _dup_string_tok ( p ) ) )
return_NULL ;
match ( TOK_STRING ) ;
} else {
if ( ! ( str = _dup_tok ( p ) ) )
return_NULL ;
match ( TOK_IDENTIFIER ) ;
}
2021-03-07 23:52:10 +03:00
if ( ! * str ) {
2018-05-14 14:16:43 +03:00
log_error ( " Parse error at byte % " PRIptrdiff_t " (line %d): empty section identifier " ,
p - > tb - p - > fb + 1 , p - > line ) ;
return NULL ;
}
if ( ! ( root = _find_or_make_node ( p - > mem , parent , str , p - > no_dup_node_check ) ) )
return_NULL ;
if ( p - > t = = TOK_SECTION_B ) {
match ( TOK_SECTION_B ) ;
while ( p - > t ! = TOK_SECTION_E ) {
if ( ! ( _section ( p , root ) ) )
return_NULL ;
}
match ( TOK_SECTION_E ) ;
} else {
match ( TOK_EQ ) ;
2019-05-10 15:40:11 +03:00
p - > key = root - > key ;
2018-05-14 14:16:43 +03:00
if ( ! ( value = _value ( p ) ) )
return_NULL ;
if ( root - > v )
log_warn ( " WARNING: Ignoring duplicate "
" config value: %s " , str ) ;
root - > v = value ;
}
return root ;
}
static struct dm_config_value * _value ( struct parser * p )
{
/* '[' TYPE* ']' | TYPE */
struct dm_config_value * h = NULL , * l , * ll = NULL ;
if ( p - > t = = TOK_ARRAY_B ) {
match ( TOK_ARRAY_B ) ;
while ( p - > t ! = TOK_ARRAY_E ) {
if ( ! ( l = _type ( p ) ) )
return_NULL ;
if ( ! h )
h = l ;
else
ll - > next = l ;
ll = l ;
if ( p - > t = = TOK_COMMA )
match ( TOK_COMMA ) ;
}
match ( TOK_ARRAY_E ) ;
/*
* Special case for an empty array .
*/
if ( ! h ) {
if ( ! ( h = _create_value ( p - > mem ) ) ) {
log_error ( " Failed to allocate value " ) ;
return NULL ;
}
h - > type = DM_CFG_EMPTY_ARRAY ;
}
} else
if ( ! ( h = _type ( p ) ) )
return_NULL ;
return h ;
}
static struct dm_config_value * _type ( struct parser * p )
{
/* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */
2024-10-09 15:23:51 +03:00
struct dm_config_value * v ;
const char * str ;
size_t len ;
2018-05-14 14:16:43 +03:00
switch ( p - > t ) {
case TOK_INT :
2024-10-09 15:23:51 +03:00
if ( ! ( v = _create_value ( p - > mem ) ) )
break ;
2018-05-14 14:16:43 +03:00
v - > type = DM_CFG_INT ;
errno = 0 ;
v - > v . i = strtoll ( p - > tb , NULL , 0 ) ; /* FIXME: check error */
if ( errno ) {
2019-05-10 15:40:11 +03:00
if ( errno = = ERANGE & & p - > key & &
strcmp ( " creation_time " , p - > key ) = = 0 ) {
/* Due to a bug in some older 32bit builds (<2.02.169),
* lvm was able to produce invalid creation_time string */
v - > v . i = 1527120000 ; /* Pick 2018-05-24 day instead */
if ( ! p - > ignored_creation_time + + )
log_warn ( " WARNING: Invalid creation_time found in metadata (repaired with next metadata update). " ) ;
} else {
log_error ( " Failed to read int token. " ) ;
return NULL ;
}
2018-05-14 14:16:43 +03:00
}
match ( TOK_INT ) ;
break ;
case TOK_FLOAT :
2024-10-09 15:23:51 +03:00
if ( ! ( v = _create_value ( p - > mem ) ) )
break ;
2018-05-14 14:16:43 +03:00
v - > type = DM_CFG_FLOAT ;
errno = 0 ;
v - > v . f = strtod ( p - > tb , NULL ) ; /* FIXME: check error */
if ( errno ) {
log_error ( " Failed to read float token. " ) ;
return NULL ;
}
match ( TOK_FLOAT ) ;
break ;
case TOK_STRING :
2024-10-09 15:23:51 +03:00
if ( ! ( str = _string_tok ( p , & len ) ) )
2018-05-14 14:16:43 +03:00
return_NULL ;
2024-10-09 15:23:51 +03:00
if ( ( v = _create_str_value ( p - > mem , str , len ) ) ) {
v - > type = DM_CFG_STRING ;
match ( TOK_STRING ) ;
}
2018-05-14 14:16:43 +03:00
break ;
case TOK_STRING_BARE :
2024-10-09 15:23:51 +03:00
if ( ( v = _create_str_value ( p - > mem , p - > tb , p - > te - p - > tb ) ) ) {
v - > type = DM_CFG_STRING ;
match ( TOK_STRING_BARE ) ;
}
2018-05-14 14:16:43 +03:00
break ;
case TOK_STRING_ESCAPED :
2024-10-09 15:23:51 +03:00
if ( ! ( str = _string_tok ( p , & len ) ) )
2018-05-14 14:16:43 +03:00
return_NULL ;
2024-10-09 15:23:51 +03:00
if ( ( v = _create_str_value ( p - > mem , str , len ) ) ) {
v - > type = DM_CFG_STRING ;
dm_unescape_double_quotes ( ( char * ) v - > v . str ) ;
match ( TOK_STRING_ESCAPED ) ;
}
2018-05-14 14:16:43 +03:00
break ;
default :
log_error ( " Parse error at byte % " PRIptrdiff_t " (line %d): expected a value " ,
p - > tb - p - > fb + 1 , p - > line ) ;
return NULL ;
}
2024-10-09 15:23:51 +03:00
if ( ! v ) {
log_error ( " Failed to allocate type value. " ) ;
return NULL ;
}
2018-05-14 14:16:43 +03:00
return v ;
}
static int _match_aux ( struct parser * p , int t )
{
if ( p - > t ! = t )
return 0 ;
_get_token ( p , t ) ;
return 1 ;
}
/*
* tokeniser
*/
static void _get_token ( struct parser * p , int tok_prev )
{
int values_allowed = 0 ;
const char * te ;
p - > tb = p - > te ;
_eat_space ( p ) ;
if ( p - > tb = = p - > fe | | ! * p - > tb ) {
p - > t = TOK_EOF ;
return ;
}
/* Should next token be interpreted as value instead of identifier? */
if ( tok_prev = = TOK_EQ | | tok_prev = = TOK_ARRAY_B | |
tok_prev = = TOK_COMMA )
values_allowed = 1 ;
p - > t = TOK_INT ; /* fudge so the fall through for
floats works */
te = p - > te ;
switch ( * te ) {
case SECTION_B_CHAR :
p - > t = TOK_SECTION_B ;
te + + ;
break ;
case SECTION_E_CHAR :
p - > t = TOK_SECTION_E ;
te + + ;
break ;
case ' [ ' :
p - > t = TOK_ARRAY_B ;
te + + ;
break ;
case ' ] ' :
p - > t = TOK_ARRAY_E ;
te + + ;
break ;
case ' , ' :
p - > t = TOK_COMMA ;
te + + ;
break ;
case ' = ' :
p - > t = TOK_EQ ;
te + + ;
break ;
case ' " ' :
p - > t = TOK_STRING_ESCAPED ;
te + + ;
while ( ( te ! = p - > fe ) & & ( * te ) & & ( * te ! = ' " ' ) ) {
if ( ( * te = = ' \\ ' ) & & ( te + 1 ! = p - > fe ) & &
* ( te + 1 ) )
te + + ;
te + + ;
}
if ( ( te ! = p - > fe ) & & ( * te ) )
te + + ;
break ;
case ' \' ' :
p - > t = TOK_STRING ;
te + + ;
while ( ( te ! = p - > fe ) & & ( * te ) & & ( * te ! = ' \' ' ) )
te + + ;
if ( ( te ! = p - > fe ) & & ( * te ) )
te + + ;
break ;
case ' . ' :
p - > t = TOK_FLOAT ;
/* Fall through */
case ' 0 ' :
case ' 1 ' :
case ' 2 ' :
case ' 3 ' :
case ' 4 ' :
case ' 5 ' :
case ' 6 ' :
case ' 7 ' :
case ' 8 ' :
case ' 9 ' :
case ' + ' :
case ' - ' :
if ( values_allowed ) {
while ( + + te ! = p - > fe ) {
if ( ! isdigit ( ( int ) * te ) ) {
if ( * te = = ' . ' ) {
if ( p - > t ! = TOK_FLOAT ) {
p - > t = TOK_FLOAT ;
continue ;
}
}
break ;
}
}
break ;
}
/* fall through */
default :
p - > t = TOK_IDENTIFIER ;
while ( ( te ! = p - > fe ) & & ( * te ) & & ! isspace ( * te ) & &
( * te ! = ' # ' ) & & ( * te ! = ' = ' ) & &
( * te ! = SECTION_B_CHAR ) & &
( * te ! = SECTION_E_CHAR ) )
te + + ;
if ( values_allowed )
p - > t = TOK_STRING_BARE ;
break ;
}
p - > te = te ;
}
static void _eat_space ( struct parser * p )
{
while ( p - > tb ! = p - > fe ) {
if ( * p - > te = = ' # ' )
while ( ( p - > te ! = p - > fe ) & & ( * p - > te ! = ' \n ' ) & & ( * p - > te ) )
+ + p - > te ;
else if ( ! isspace ( * p - > te ) )
break ;
while ( ( p - > te ! = p - > fe ) & & isspace ( * p - > te ) ) {
if ( * p - > te = = ' \n ' )
+ + p - > line ;
+ + p - > te ;
}
p - > tb = p - > te ;
}
}
/*
* memory management
*/
static struct dm_config_value * _create_value ( struct dm_pool * mem )
{
return dm_pool_zalloc ( mem , sizeof ( struct dm_config_value ) ) ;
}
2024-10-09 15:23:51 +03:00
static struct dm_config_value * _create_str_value ( struct dm_pool * mem , const char * str , size_t str_len )
2018-05-14 14:16:43 +03:00
{
2024-10-09 15:23:51 +03:00
struct dm_config_value * cv ;
char * str_buf ;
if ( ! ( cv = dm_pool_alloc ( mem , sizeof ( struct dm_config_value ) + str_len + 1 ) ) )
return_NULL ;
memset ( cv , 0 , sizeof ( * cv ) ) ;
if ( str ) {
str_buf = ( char * ) ( cv + 1 ) ;
memcpy ( str_buf , str , str_len ) ;
str_buf [ str_len ] = ' \0 ' ;
cv - > v . str = str_buf ;
}
return cv ;
}
static struct dm_config_node * _create_node ( struct dm_pool * mem , const char * key , size_t key_len )
{
struct dm_config_node * cn ;
char * key_buf ;
if ( ! ( cn = dm_pool_alloc ( mem , sizeof ( struct dm_config_node ) + key_len + 1 ) ) )
return_NULL ;
memset ( cn , 0 , sizeof ( * cn ) ) ;
if ( key ) {
key_buf = ( char * ) ( cn + 1 ) ;
memcpy ( key_buf , key , key_len ) ;
key_buf [ key_len ] = ' \0 ' ;
cn - > key = key_buf ;
}
return cn ;
2018-05-14 14:16:43 +03:00
}
static char * _dup_token ( struct dm_pool * mem , const char * b , const char * e )
{
size_t len = e - b ;
char * str = dm_pool_alloc ( mem , len + 1 ) ;
if ( ! str ) {
log_error ( " Failed to duplicate token. " ) ;
return 0 ;
}
memcpy ( str , b , len ) ;
str [ len ] = ' \0 ' ;
return str ;
}
static char * _dup_tok ( struct parser * p )
{
return _dup_token ( p - > mem , p - > tb , p - > te ) ;
}
/*
* Utility functions
*/
/*
* node_lookup_fn is either :
* _find_config_node to perform a lookup starting from a given config_node
* in a config_tree ;
* or
* _find_first_config_node to find the first config_node in a set of
* cascaded trees .
*/
typedef const struct dm_config_node * node_lookup_fn ( const void * start , const char * path ) ;
static const struct dm_config_node * _find_config_node ( const void * start , const char * path ) {
struct dm_config_node dummy = { . child = ( void * ) start } ;
return _find_or_make_node ( NULL , & dummy , path , 0 ) ;
}
static const struct dm_config_node * _find_first_config_node ( const void * start , const char * path )
{
const struct dm_config_tree * cft = start ;
const struct dm_config_node * cn = NULL ;
while ( cft ) {
if ( ( cn = _find_config_node ( cft - > root , path ) ) )
return cn ;
cft = cft - > cascade ;
}
return NULL ;
}
static const char * _find_config_str ( const void * start , node_lookup_fn find_fn ,
const char * path , const char * fail , int allow_empty )
{
const struct dm_config_node * n = find_fn ( start , path ) ;
/* Empty strings are ignored if allow_empty is set */
if ( n & & n - > v ) {
if ( ( n - > v - > type = = DM_CFG_STRING ) & &
( allow_empty | | ( * n - > v - > v . str ) ) ) {
/* log_very_verbose("Setting %s to %s", path, n->v->v.str); */
return n - > v - > v . str ;
}
if ( ( n - > v - > type ! = DM_CFG_STRING ) | | ( ! allow_empty & & fail ) )
log_warn ( " WARNING: Ignoring unsupported value for %s. " , path ) ;
}
if ( fail )
2021-02-27 19:08:52 +03:00
log_very_verbose ( " %s not found in config: defaulting to \" %s \" " ,
2018-05-14 14:16:43 +03:00
path , fail ) ;
return fail ;
}
const char * dm_config_find_str ( const struct dm_config_node * cn ,
const char * path , const char * fail )
{
return _find_config_str ( cn , _find_config_node , path , fail , 0 ) ;
}
const char * dm_config_find_str_allow_empty ( const struct dm_config_node * cn ,
const char * path , const char * fail )
{
return _find_config_str ( cn , _find_config_node , path , fail , 1 ) ;
}
static int64_t _find_config_int64 ( const void * start , node_lookup_fn find ,
const char * path , int64_t fail )
{
const struct dm_config_node * n = find ( start , path ) ;
if ( n & & n - > v & & n - > v - > type = = DM_CFG_INT ) {
/* log_very_verbose("Setting %s to %" PRId64, path, n->v->v.i); */
return n - > v - > v . i ;
}
log_very_verbose ( " %s not found in config: defaulting to % " PRId64 ,
path , fail ) ;
return fail ;
}
static float _find_config_float ( const void * start , node_lookup_fn find ,
const char * path , float fail )
{
const struct dm_config_node * n = find ( start , path ) ;
if ( n & & n - > v & & n - > v - > type = = DM_CFG_FLOAT ) {
/* log_very_verbose("Setting %s to %f", path, n->v->v.f); */
return n - > v - > v . f ;
}
log_very_verbose ( " %s not found in config: defaulting to %f " ,
path , fail ) ;
return fail ;
}
static int _str_in_array ( const char * str , const char * const values [ ] )
{
int i ;
for ( i = 0 ; values [ i ] ; i + + )
if ( ! strcasecmp ( str , values [ i ] ) )
return 1 ;
return 0 ;
}
static int _str_to_bool ( const char * str , int fail )
{
const char * const _true_values [ ] = { " y " , " yes " , " on " , " true " , NULL } ;
const char * const _false_values [ ] = { " n " , " no " , " off " , " false " , NULL } ;
if ( _str_in_array ( str , _true_values ) )
return 1 ;
if ( _str_in_array ( str , _false_values ) )
return 0 ;
return fail ;
}
static int _find_config_bool ( const void * start , node_lookup_fn find ,
const char * path , int fail )
{
const struct dm_config_node * n = find ( start , path ) ;
const struct dm_config_value * v ;
int b ;
if ( n ) {
v = n - > v ;
switch ( v - > type ) {
case DM_CFG_INT :
b = v - > v . i ? 1 : 0 ;
/* log_very_verbose("Setting %s to %d", path, b); */
return b ;
case DM_CFG_STRING :
b = _str_to_bool ( v - > v . str , fail ) ;
/* log_very_verbose("Setting %s to %d", path, b); */
return b ;
default :
;
}
}
log_very_verbose ( " %s not found in config: defaulting to %d " ,
path , fail ) ;
return fail ;
}
/***********************************
* node - based lookup
* */
struct dm_config_node * dm_config_find_node ( const struct dm_config_node * cn ,
const char * path )
{
return ( struct dm_config_node * ) _find_config_node ( cn , path ) ;
}
int dm_config_find_int ( const struct dm_config_node * cn , const char * path , int fail )
{
/* FIXME Add log_error message on overflow */
return ( int ) _find_config_int64 ( cn , _find_config_node , path , ( int64_t ) fail ) ;
}
int64_t dm_config_find_int64 ( const struct dm_config_node * cn , const char * path , int64_t fail )
{
return _find_config_int64 ( cn , _find_config_node , path , fail ) ;
}
float dm_config_find_float ( const struct dm_config_node * cn , const char * path ,
float fail )
{
return _find_config_float ( cn , _find_config_node , path , fail ) ;
}
int dm_config_find_bool ( const struct dm_config_node * cn , const char * path , int fail )
{
return _find_config_bool ( cn , _find_config_node , path , fail ) ;
}
int dm_config_value_is_bool ( const struct dm_config_value * v ) {
if ( ! v )
return 0 ;
switch ( v - > type ) {
case DM_CFG_INT :
return 1 ;
case DM_CFG_STRING :
return _str_to_bool ( v - > v . str , - 1 ) ! = - 1 ;
default :
return 0 ;
}
}
/***********************************
* tree - based lookup
* */
const struct dm_config_node * dm_config_tree_find_node ( const struct dm_config_tree * cft ,
const char * path )
{
return _find_first_config_node ( cft , path ) ;
}
const char * dm_config_tree_find_str ( const struct dm_config_tree * cft , const char * path ,
const char * fail )
{
return _find_config_str ( cft , _find_first_config_node , path , fail , 0 ) ;
}
const char * dm_config_tree_find_str_allow_empty ( const struct dm_config_tree * cft , const char * path ,
const char * fail )
{
return _find_config_str ( cft , _find_first_config_node , path , fail , 1 ) ;
}
int dm_config_tree_find_int ( const struct dm_config_tree * cft , const char * path , int fail )
{
/* FIXME Add log_error message on overflow */
return ( int ) _find_config_int64 ( cft , _find_first_config_node , path , ( int64_t ) fail ) ;
}
int64_t dm_config_tree_find_int64 ( const struct dm_config_tree * cft , const char * path , int64_t fail )
{
return _find_config_int64 ( cft , _find_first_config_node , path , fail ) ;
}
float dm_config_tree_find_float ( const struct dm_config_tree * cft , const char * path ,
float fail )
{
return _find_config_float ( cft , _find_first_config_node , path , fail ) ;
}
int dm_config_tree_find_bool ( const struct dm_config_tree * cft , const char * path , int fail )
{
return _find_config_bool ( cft , _find_first_config_node , path , fail ) ;
}
/************************************/
int dm_config_get_uint32 ( const struct dm_config_node * cn , const char * path ,
uint32_t * result )
{
const struct dm_config_node * n ;
n = _find_config_node ( cn , path ) ;
if ( ! n | | ! n - > v | | n - > v - > type ! = DM_CFG_INT )
return 0 ;
if ( result )
* result = n - > v - > v . i ;
return 1 ;
}
int dm_config_get_uint64 ( const struct dm_config_node * cn , const char * path ,
uint64_t * result )
{
const struct dm_config_node * n ;
n = _find_config_node ( cn , path ) ;
if ( ! n | | ! n - > v | | n - > v - > type ! = DM_CFG_INT )
return 0 ;
if ( result )
* result = ( uint64_t ) n - > v - > v . i ;
return 1 ;
}
int dm_config_get_str ( const struct dm_config_node * cn , const char * path ,
const char * * result )
{
const struct dm_config_node * n ;
n = _find_config_node ( cn , path ) ;
if ( ! n | | ! n - > v | | n - > v - > type ! = DM_CFG_STRING )
return 0 ;
if ( result )
* result = n - > v - > v . str ;
return 1 ;
}
int dm_config_get_list ( const struct dm_config_node * cn , const char * path ,
const struct dm_config_value * * result )
{
const struct dm_config_node * n ;
n = _find_config_node ( cn , path ) ;
/* TODO when we represent single-item lists consistently, add a check
* for n - > v - > next ! = NULL */
if ( ! n | | ! n - > v )
return 0 ;
if ( result )
* result = n - > v ;
return 1 ;
}
int dm_config_get_section ( const struct dm_config_node * cn , const char * path ,
const struct dm_config_node * * result )
{
const struct dm_config_node * n ;
n = _find_config_node ( cn , path ) ;
if ( ! n | | n - > v )
return 0 ;
if ( result )
* result = n ;
return 1 ;
}
int dm_config_has_node ( const struct dm_config_node * cn , const char * path )
{
return _find_config_node ( cn , path ) ? 1 : 0 ;
}
/*
* Convert a token type to the char it represents .
*/
static char _token_type_to_char ( int type )
{
switch ( type ) {
case TOK_SECTION_B :
return SECTION_B_CHAR ;
case TOK_SECTION_E :
return SECTION_E_CHAR ;
default :
return 0 ;
}
}
/*
* Returns :
* # of ' type ' tokens in ' str ' .
*/
static unsigned _count_tokens ( const char * str , unsigned len , int type )
{
char c ;
c = _token_type_to_char ( type ) ;
return dm_count_chars ( str , len , c ) ;
}
const char * dm_config_parent_name ( const struct dm_config_node * n )
{
return ( n - > parent ? n - > parent - > key : " (root) " ) ;
}
/*
* Heuristic function to make a quick guess as to whether a text
* region probably contains a valid config " section " . ( Useful for
* scanning areas of the disk for old metadata . )
* Config sections contain various tokens , may contain other sections
* and strings , and are delimited by begin ( type ' TOK_SECTION_B ' ) and
* end ( type ' TOK_SECTION_E ' ) tokens . As a quick heuristic , we just
* count the number of begin and end tokens , and see if they are
* non - zero and the counts match .
* Full validation of the section should be done with another function
* ( for example , read_config_fd ) .
*
* Returns :
* 0 - probably is not a valid config section
* 1 - probably _is_ a valid config section
*/
unsigned dm_config_maybe_section ( const char * str , unsigned len )
{
int begin_count ;
int end_count ;
begin_count = _count_tokens ( str , len , TOK_SECTION_B ) ;
end_count = _count_tokens ( str , len , TOK_SECTION_E ) ;
if ( begin_count & & end_count & & ( begin_count = = end_count ) )
return 1 ;
else
return 0 ;
}
__attribute__ ( ( nonnull ( 1 , 2 ) ) )
static struct dm_config_value * _clone_config_value ( struct dm_pool * mem ,
const struct dm_config_value * v )
{
struct dm_config_value * new_cv ;
if ( v - > type = = DM_CFG_STRING ) {
2024-10-09 15:23:51 +03:00
if ( ! ( new_cv = _create_str_value ( mem , v - > v . str , strlen ( v - > v . str ) ) ) ) {
}
} else {
if ( ! ( new_cv = _create_value ( mem ) ) ) {
log_error ( " Failed to clone config value. " ) ;
2018-05-14 14:16:43 +03:00
return NULL ;
}
new_cv - > v = v - > v ;
2024-10-09 15:23:51 +03:00
}
new_cv - > type = v - > type ;
2018-05-14 14:16:43 +03:00
if ( v - > next & & ! ( new_cv - > next = _clone_config_value ( mem , v - > next ) ) )
return_NULL ;
return new_cv ;
}
struct dm_config_node * dm_config_clone_node_with_mem ( struct dm_pool * mem , const struct dm_config_node * cn , int siblings )
{
struct dm_config_node * new_cn ;
if ( ! cn ) {
log_error ( " Cannot clone NULL config node. " ) ;
return NULL ;
}
2024-10-09 15:23:51 +03:00
if ( ! ( new_cn = _create_node ( mem , cn - > key , cn - > key ? strlen ( cn - > key ) : 0 ) ) ) {
2018-05-14 14:16:43 +03:00
log_error ( " Failed to clone config node. " ) ;
return NULL ;
}
new_cn - > id = cn - > id ;
if ( ( cn - > v & & ! ( new_cn - > v = _clone_config_value ( mem , cn - > v ) ) ) | |
( cn - > child & & ! ( new_cn - > child = dm_config_clone_node_with_mem ( mem , cn - > child , 1 ) ) ) | |
( siblings & & cn - > sib & & ! ( new_cn - > sib = dm_config_clone_node_with_mem ( mem , cn - > sib , siblings ) ) ) )
return_NULL ; /* 'new_cn' released with mem pool */
return new_cn ;
}
struct dm_config_node * dm_config_clone_node ( struct dm_config_tree * cft , const struct dm_config_node * node , int sib )
{
return dm_config_clone_node_with_mem ( cft - > mem , node , sib ) ;
}
struct dm_config_node * dm_config_create_node ( struct dm_config_tree * cft , const char * key )
{
struct dm_config_node * cn ;
2024-10-09 15:23:51 +03:00
if ( ! ( cn = _create_node ( cft - > mem , key , strlen ( key ) ) ) ) {
2018-05-14 14:16:43 +03:00
log_error ( " Failed to create config node. " ) ;
return NULL ;
}
2024-10-09 15:23:51 +03:00
2018-05-14 14:16:43 +03:00
cn - > parent = NULL ;
cn - > v = NULL ;
return cn ;
}
struct dm_config_value * dm_config_create_value ( struct dm_config_tree * cft )
{
return _create_value ( cft - > mem ) ;
}
void dm_config_value_set_format_flags ( struct dm_config_value * cv , uint32_t format_flags )
{
if ( ! cv )
return ;
cv - > format_flags = format_flags ;
}
uint32_t dm_config_value_get_format_flags ( struct dm_config_value * cv )
{
if ( ! cv )
return 0 ;
return cv - > format_flags ;
}
struct dm_pool * dm_config_memory ( struct dm_config_tree * cft )
{
return cft - > mem ;
}
static int _override_path ( const char * path , struct dm_config_node * node , void * baton )
{
struct dm_config_tree * cft = baton ;
struct dm_config_node dummy , * target ;
dummy . child = cft - > root ;
if ( ! ( target = _find_or_make_node ( cft - > mem , & dummy , path , 0 ) ) )
return_0 ;
if ( ! ( target - > v = _clone_config_value ( cft - > mem , node - > v ) ) )
return_0 ;
cft - > root = dummy . child ;
return 1 ;
}
static int _enumerate ( const char * path , struct dm_config_node * cn , int ( * cb ) ( const char * , struct dm_config_node * , void * ) , void * baton )
{
char * sub = NULL ;
while ( cn ) {
if ( dm_asprintf ( & sub , " %s/%s " , path , cn - > key ) < 0 )
return_0 ;
if ( cn - > child ) {
if ( ! _enumerate ( sub , cn - > child , cb , baton ) )
goto_bad ;
} else
if ( ! cb ( sub , cn , baton ) )
goto_bad ;
2018-06-08 15:40:53 +03:00
free ( sub ) ;
2018-05-14 14:16:43 +03:00
cn = cn - > sib ;
}
return 1 ;
bad :
2018-06-08 15:40:53 +03:00
free ( sub ) ;
2018-05-14 14:16:43 +03:00
return 0 ;
}
struct dm_config_tree * dm_config_flatten ( struct dm_config_tree * cft )
{
struct dm_config_tree * res = dm_config_create ( ) , * done = NULL , * current = NULL ;
if ( ! res )
return_NULL ;
while ( done ! = cft ) {
current = cft ;
while ( current - > cascade ! = done )
current = current - > cascade ;
_enumerate ( " " , current - > root , _override_path , res ) ;
done = current ;
}
return res ;
}
int dm_config_remove_node ( struct dm_config_node * parent , struct dm_config_node * rem_node )
{
struct dm_config_node * cn = parent - > child , * last = NULL ;
while ( cn ) {
if ( cn = = rem_node ) {
if ( last )
last - > sib = cn - > sib ;
else
parent - > child = cn - > sib ;
return 1 ;
}
last = cn ;
cn = cn - > sib ;
}
return 0 ;
}