2001-08-21 16:56:08 +04:00
/*
2007-07-09 02:51:20 +04:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
2011-05-24 17:53:26 +04:00
* Copyright ( C ) 2004 - 2011 Red Hat , Inc . All rights reserved .
2001-08-21 16:56:08 +04:00
*
2004-03-30 23:35:44 +04:00
* 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
2007-08-21 00:55:30 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2004-03-30 23:35:44 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 23:35:44 +04:00
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2001-08-21 16:56:08 +04:00
*/
2011-08-30 18:55:15 +04:00
2002-11-18 17:01:16 +03:00
# include "lib.h"
2011-08-30 18:55:15 +04:00
2002-11-18 17:01:16 +03:00
# include "config.h"
# include "crc.h"
# include "device.h"
2004-05-04 22:28:15 +04:00
# include "str_list.h"
# include "toolcontext.h"
2007-04-26 00:38:39 +04:00
# include "lvm-string.h"
2007-07-24 21:48:08 +04:00
# include "lvm-file.h"
2002-11-18 17:01:16 +03:00
2001-08-21 16:56:08 +04:00
# include <sys/stat.h>
# include <sys/mman.h>
# include <unistd.h>
# include <fcntl.h>
# include <ctype.h>
2011-12-19 01:56:03 +04:00
# include <assert.h>
2001-09-13 16:38:31 +04:00
2011-12-19 01:56:03 +04:00
struct config_file {
time_t timestamp ;
off_t st_size ;
char * filename ;
int exists ;
int keep_open ;
struct device * dev ;
} ;
/*
* public interface
*/
struct dm_config_tree * config_file_open ( const char * filename , int keep_open )
2002-01-07 13:23:52 +03:00
{
2011-12-19 01:56:03 +04:00
struct dm_config_tree * cft = dm_config_create ( ) ;
struct config_file * cf ;
if ( ! cft )
return NULL ;
cf = dm_pool_zalloc ( cft - > mem , sizeof ( struct config_file ) ) ;
if ( ! cf ) goto fail ;
2002-01-07 13:23:52 +03:00
2011-12-19 01:56:03 +04:00
cf - > timestamp = 0 ;
cf - > exists = 0 ;
cf - > keep_open = keep_open ;
dm_config_set_custom ( cft , cf ) ;
if ( filename & &
! ( cf - > filename = dm_pool_strdup ( cft - > mem , filename ) ) ) {
log_error ( " Failed to duplicate filename. " ) ;
goto fail ;
}
return cft ;
fail :
dm_config_destroy ( cft ) ;
return NULL ;
}
/*
* Doesn ' t populate filename if the file is empty .
*/
int config_file_check ( struct dm_config_tree * cft , const char * * filename , struct stat * info )
{
struct config_file * cf = dm_config_get_custom ( cft ) ;
struct stat _info ;
if ( ! info )
info = & _info ;
if ( stat ( cf - > filename , info ) ) {
log_sys_error ( " stat " , cf - > filename ) ;
cf - > exists = 0 ;
return 0 ;
}
if ( ! S_ISREG ( info - > st_mode ) ) {
log_error ( " %s is not a regular file " , cf - > filename ) ;
cf - > exists = 0 ;
return 0 ;
}
cf - > exists = 1 ;
cf - > timestamp = info - > st_ctime ;
cf - > st_size = info - > st_size ;
if ( info - > st_size = = 0 )
log_verbose ( " %s is empty " , cf - > filename ) ;
else if ( filename )
* filename = cf - > filename ;
return 1 ;
}
/*
* Return 1 if config files ought to be reloaded
*/
int config_file_changed ( struct dm_config_tree * cft )
{
struct config_file * cf = dm_config_get_custom ( cft ) ;
struct stat info ;
if ( ! cf - > filename )
return 0 ;
if ( stat ( cf - > filename , & info ) = = - 1 ) {
/* Ignore a deleted config file: still use original data */
if ( errno = = ENOENT ) {
if ( ! cf - > exists )
return 0 ;
log_very_verbose ( " Config file %s has disappeared! " ,
cf - > filename ) ;
goto reload ;
}
log_sys_error ( " stat " , cf - > filename ) ;
log_error ( " Failed to reload configuration files " ) ;
return 0 ;
}
if ( ! S_ISREG ( info . st_mode ) ) {
log_error ( " Configuration file %s is not a regular file " ,
cf - > filename ) ;
goto reload ;
}
/* Unchanged? */
if ( cf - > timestamp = = info . st_ctime & & cf - > st_size = = info . st_size )
return 0 ;
reload :
log_verbose ( " Detected config file change to %s " , cf - > filename ) ;
return 1 ;
}
void config_file_destroy ( struct dm_config_tree * cft )
{
struct config_file * cf = dm_config_get_custom ( cft ) ;
if ( cf & & cf - > dev )
dev_close ( cf - > dev ) ;
2002-01-07 13:23:52 +03:00
2011-08-30 18:55:15 +04:00
dm_config_destroy ( cft ) ;
2006-05-16 20:48:31 +04:00
}
2011-09-02 05:32:08 +04:00
/*
* Returns config tree if it was removed .
*/
struct dm_config_tree * remove_overridden_config_tree ( struct cmd_context * cmd )
{
struct dm_config_tree * old_cft = cmd - > cft ;
struct dm_config_tree * cft = dm_config_remove_cascaded_tree ( cmd - > cft ) ;
if ( ! cft )
return NULL ;
cmd - > cft = cft ;
return old_cft ;
}
2009-07-28 01:01:56 +04:00
int override_config_tree_from_string ( struct cmd_context * cmd ,
const char * config_settings )
{
2011-09-02 05:32:08 +04:00
struct dm_config_tree * cft_new ;
if ( ! ( cft_new = dm_config_from_string ( config_settings ) ) ) {
2009-07-28 01:01:56 +04:00
log_error ( " Failed to set overridden configuration entries. " ) ;
return 1 ;
}
2011-09-02 05:32:08 +04:00
cmd - > cft = dm_config_insert_cascaded_tree ( cft_new , cmd - > cft ) ;
2009-07-28 01:01:56 +04:00
return 0 ;
}
2011-12-19 01:56:03 +04:00
int config_file_read_fd ( struct dm_config_tree * cft , struct device * dev ,
off_t offset , size_t size , off_t offset2 , size_t size2 ,
checksum_fn_t checksum_fn , uint32_t checksum )
2001-08-21 16:56:08 +04:00
{
2011-08-30 18:55:15 +04:00
const char * fb , * fe ;
2002-11-18 17:01:16 +03:00
int r = 0 ;
2003-07-05 02:34:56 +04:00
int use_mmap = 1 ;
off_t mmap_offset = 0 ;
2007-06-13 19:11:19 +04:00
char * buf = NULL ;
2002-04-24 22:20:51 +04:00
2003-07-05 02:34:56 +04:00
/* Only use mmap with regular files */
if ( ! ( dev - > flags & DEV_REGULAR ) | | size2 )
use_mmap = 0 ;
if ( use_mmap ) {
2006-08-17 22:23:44 +04:00
mmap_offset = offset % lvm_getpagesize ( ) ;
2002-11-18 17:01:16 +03:00
/* memory map the file */
2011-08-30 18:55:15 +04:00
fb = mmap ( ( caddr_t ) 0 , size + mmap_offset , PROT_READ ,
MAP_PRIVATE , dev_fd ( dev ) , offset - mmap_offset ) ;
if ( fb = = ( caddr_t ) ( - 1 ) ) {
2003-07-05 02:34:56 +04:00
log_sys_error ( " mmap " , dev_name ( dev ) ) ;
2002-11-18 17:01:16 +03:00
goto out ;
}
2011-08-30 18:55:15 +04:00
fb = fb + mmap_offset ;
2003-07-05 02:34:56 +04:00
} else {
2011-10-11 13:06:09 +04:00
if ( ! ( buf = dm_malloc ( size + size2 ) ) ) {
log_error ( " Failed to allocate circular buffer. " ) ;
return 0 ;
}
2007-04-19 06:10:42 +04:00
if ( ! dev_read_circular ( dev , ( uint64_t ) offset , size ,
( uint64_t ) offset2 , size2 , buf ) ) {
2003-07-05 02:34:56 +04:00
goto out ;
}
2011-08-30 18:55:15 +04:00
fb = buf ;
2002-11-18 17:01:16 +03:00
}
if ( checksum_fn & & checksum ! =
2011-08-30 18:55:15 +04:00
( checksum_fn ( checksum_fn ( INITIAL_CRC , ( const uint8_t * ) fb , size ) ,
( const uint8_t * ) ( fb + size ) , size2 ) ) ) {
2003-07-05 02:34:56 +04:00
log_error ( " %s: Checksum error " , dev_name ( dev ) ) ;
2002-11-18 17:01:16 +03:00
goto out ;
}
2011-08-30 18:55:15 +04:00
fe = fb + size + size2 ;
if ( ! dm_config_parse ( cft , fb , fe ) )
2008-01-30 16:19:47 +03:00
goto_out ;
2002-11-18 17:01:16 +03:00
r = 1 ;
out :
2003-07-05 02:34:56 +04:00
if ( ! use_mmap )
2006-05-16 20:48:31 +04:00
dm_free ( buf ) ;
2002-11-18 17:01:16 +03:00
else {
/* unmap the file */
2011-08-30 18:55:15 +04:00
if ( munmap ( ( char * ) ( fb - mmap_offset ) , size + mmap_offset ) ) {
2003-07-05 02:34:56 +04:00
log_sys_error ( " munmap " , dev_name ( dev ) ) ;
2002-11-18 17:01:16 +03:00
r = 0 ;
}
}
return r ;
}
2011-12-19 01:56:03 +04:00
int config_file_read ( struct dm_config_tree * cft )
2002-11-18 17:01:16 +03:00
{
2011-09-02 05:32:08 +04:00
const char * filename = NULL ;
2011-12-19 01:56:03 +04:00
struct config_file * cf = dm_config_get_custom ( cft ) ;
2002-11-18 17:01:16 +03:00
struct stat info ;
2011-08-30 18:55:15 +04:00
int r ;
2002-04-24 22:20:51 +04:00
2011-12-19 01:56:03 +04:00
if ( ! config_file_check ( cft , & filename , & info ) )
2011-09-02 01:04:14 +04:00
return_0 ;
2004-05-04 22:28:15 +04:00
2011-09-02 05:32:08 +04:00
/* Nothing to do. E.g. empty file. */
if ( ! filename )
return 1 ;
2011-12-19 01:56:03 +04:00
if ( ! cf - > dev ) {
if ( ! ( cf - > dev = dev_create_file ( filename , NULL , NULL , 1 ) ) )
2006-11-04 06:34:10 +03:00
return_0 ;
2003-07-05 02:34:56 +04:00
2011-12-19 01:56:03 +04:00
if ( ! dev_open_readonly_buffered ( cf - > dev ) )
2006-11-04 06:34:10 +03:00
return_0 ;
2002-04-24 22:20:51 +04:00
}
2011-12-19 01:56:03 +04:00
r = config_file_read_fd ( cft , cf - > dev , 0 , ( size_t ) info . st_size , 0 , 0 ,
( checksum_fn_t ) NULL , 0 ) ;
2002-11-18 17:01:16 +03:00
2011-12-19 01:56:03 +04:00
if ( ! cf - > keep_open ) {
dev_close ( cf - > dev ) ;
cf - > dev = NULL ;
2006-11-04 06:34:10 +03:00
}
2002-11-18 17:01:16 +03:00
return r ;
}
2011-12-19 01:56:03 +04:00
time_t config_file_timestamp ( struct dm_config_tree * cft )
{
struct config_file * cf = dm_config_get_custom ( cft ) ;
assert ( cf ) ;
return cf - > timestamp ;
}
2011-08-30 18:55:15 +04:00
const struct dm_config_node * find_config_tree_node ( struct cmd_context * cmd ,
2010-12-20 16:12:55 +03:00
const char * path )
2001-08-21 16:56:08 +04:00
{
2011-09-02 05:32:08 +04:00
return dm_config_tree_find_node ( cmd - > cft , path ) ;
2006-05-16 20:48:31 +04:00
}
const char * find_config_tree_str ( struct cmd_context * cmd ,
2007-07-09 02:51:20 +04:00
const char * path , const char * fail )
2006-05-16 20:48:31 +04:00
{
2011-09-02 05:32:08 +04:00
return dm_config_tree_find_str ( cmd - > cft , path , fail ) ;
2006-05-16 20:48:31 +04:00
}
2011-10-29 00:06:49 +04:00
const char * find_config_tree_str_allow_empty ( struct cmd_context * cmd ,
const char * path , const char * fail )
{
return dm_config_tree_find_str_allow_empty ( cmd - > cft , path , fail ) ;
}
2006-05-16 20:48:31 +04:00
int find_config_tree_int ( struct cmd_context * cmd , const char * path ,
2007-07-09 02:51:20 +04:00
int fail )
2006-05-16 20:48:31 +04:00
{
2011-09-02 05:32:08 +04:00
return dm_config_tree_find_int ( cmd - > cft , path , fail ) ;
2006-05-16 20:48:31 +04:00
}
2011-02-18 17:08:22 +03:00
int64_t find_config_tree_int64 ( struct cmd_context * cmd , const char * path , int64_t fail )
{
2011-09-02 05:32:08 +04:00
return dm_config_tree_find_int64 ( cmd - > cft , path , fail ) ;
2011-02-18 17:08:22 +03:00
}
2006-05-16 20:48:31 +04:00
float find_config_tree_float ( struct cmd_context * cmd , const char * path ,
2007-07-09 02:51:20 +04:00
float fail )
2006-05-16 20:48:31 +04:00
{
2011-09-02 05:32:08 +04:00
return dm_config_tree_find_float ( cmd - > cft , path , fail ) ;
2006-05-16 20:48:31 +04:00
}
int find_config_tree_bool ( struct cmd_context * cmd , const char * path , int fail )
{
2011-09-02 05:32:08 +04:00
return dm_config_tree_find_bool ( cmd - > cft , path , fail ) ;
2002-07-11 18:07:43 +04:00
}
2004-05-04 22:28:15 +04:00
/* Insert cn2 after cn1 */
2011-08-30 18:55:15 +04:00
static void _insert_config_node ( struct dm_config_node * * cn1 ,
struct dm_config_node * cn2 )
2004-05-04 22:28:15 +04:00
{
if ( ! * cn1 ) {
* cn1 = cn2 ;
cn2 - > sib = NULL ;
} else {
cn2 - > sib = ( * cn1 ) - > sib ;
( * cn1 ) - > sib = cn2 ;
}
}
/*
* Merge section cn2 into section cn1 ( which has the same name )
* overwriting any existing cn1 nodes with matching names .
*/
2011-08-30 18:55:15 +04:00
static void _merge_section ( struct dm_config_node * cn1 , struct dm_config_node * cn2 )
2004-05-04 22:28:15 +04:00
{
2011-08-30 18:55:15 +04:00
struct dm_config_node * cn , * nextn , * oldn ;
struct dm_config_value * cv ;
2004-05-04 22:28:15 +04:00
for ( cn = cn2 - > child ; cn ; cn = nextn ) {
nextn = cn - > sib ;
/* Skip "tags" */
if ( ! strcmp ( cn - > key , " tags " ) )
continue ;
/* Subsection? */
if ( ! cn - > v )
/* Ignore - we don't have any of these yet */
continue ;
/* Not already present? */
2011-08-31 19:19:19 +04:00
if ( ! ( oldn = dm_config_find_node ( cn1 - > child , cn - > key ) ) ) {
2004-05-04 22:28:15 +04:00
_insert_config_node ( & cn1 - > child , cn ) ;
continue ;
}
/* Merge certain value lists */
if ( ( ! strcmp ( cn1 - > key , " activation " ) & &
! strcmp ( cn - > key , " volume_list " ) ) | |
( ! strcmp ( cn1 - > key , " devices " ) & &
( ! strcmp ( cn - > key , " filter " ) | | ! strcmp ( cn - > key , " types " ) ) ) ) {
cv = cn - > v ;
while ( cv - > next )
cv = cv - > next ;
cv - > next = oldn - > v ;
}
/* Replace values */
oldn - > v = cn - > v ;
}
}
2011-08-30 18:55:15 +04:00
static int _match_host_tags ( struct dm_list * tags , const struct dm_config_node * tn )
2004-05-04 22:28:15 +04:00
{
2011-08-30 18:55:15 +04:00
const struct dm_config_value * tv ;
2004-05-04 22:28:15 +04:00
const char * str ;
for ( tv = tn - > v ; tv ; tv = tv - > next ) {
2011-08-30 18:55:15 +04:00
if ( tv - > type ! = DM_CFG_STRING )
2004-05-04 22:28:15 +04:00
continue ;
str = tv - > v . str ;
if ( * str = = ' @ ' )
str + + ;
if ( ! * str )
continue ;
if ( str_list_match_item ( tags , str ) )
return 1 ;
}
return 0 ;
}
/* Destructively merge a new config tree into an existing one */
2011-08-30 18:55:15 +04:00
int merge_config_tree ( struct cmd_context * cmd , struct dm_config_tree * cft ,
struct dm_config_tree * newdata )
2004-05-04 22:28:15 +04:00
{
2011-08-31 19:19:19 +04:00
struct dm_config_node * root = cft - > root ;
2011-08-30 18:55:15 +04:00
struct dm_config_node * cn , * nextn , * oldn , * cn2 ;
const struct dm_config_node * tn ;
2004-05-04 22:28:15 +04:00
for ( cn = newdata - > root ; cn ; cn = nextn ) {
nextn = cn - > sib ;
/* Ignore tags section */
if ( ! strcmp ( cn - > key , " tags " ) )
continue ;
/* If there's a tags node, skip if host tags don't match */
2011-08-30 18:55:15 +04:00
if ( ( tn = dm_config_find_node ( cn - > child , " tags " ) ) ) {
2004-05-04 22:28:15 +04:00
if ( ! _match_host_tags ( & cmd - > tags , tn ) )
continue ;
}
2011-08-31 19:19:19 +04:00
if ( ! ( oldn = dm_config_find_node ( root , cn - > key ) ) ) {
2004-05-04 22:28:15 +04:00
_insert_config_node ( & cft - > root , cn ) ;
/* Remove any "tags" nodes */
for ( cn2 = cn - > child ; cn2 ; cn2 = cn2 - > sib ) {
if ( ! strcmp ( cn2 - > key , " tags " ) ) {
cn - > child = cn2 - > sib ;
continue ;
}
if ( cn2 - > sib & & ! strcmp ( cn2 - > sib - > key , " tags " ) ) {
cn2 - > sib = cn2 - > sib - > sib ;
continue ;
}
}
continue ;
}
_merge_section ( oldn , cn ) ;
}
return 1 ;
}
2007-04-26 00:38:39 +04:00
2011-12-12 03:18:20 +04:00
static int _putline_fn ( const char * line , void * baton ) {
FILE * fp = baton ;
fprintf ( fp , " %s \n " , line ) ;
return 1 ;
} ;
int config_write ( struct dm_config_tree * cft , const char * file ,
int argc , char * * argv )
{
const struct dm_config_node * cn ;
int r = 1 ;
FILE * fp = NULL ;
if ( ! file ) {
fp = stdout ;
file = " stdout " ;
} else if ( ! ( fp = fopen ( file , " w " ) ) ) {
log_sys_error ( " open " , file ) ;
return 0 ;
}
log_verbose ( " Dumping configuration to %s " , file ) ;
if ( ! argc ) {
if ( ! dm_config_write_node ( cft - > root , _putline_fn , fp ) ) {
log_error ( " Failure while writing to %s " , file ) ;
r = 0 ;
}
} else while ( argc - - ) {
if ( ( cn = dm_config_find_node ( cft - > root , * argv ) ) ) {
if ( ! dm_config_write_node ( cn , _putline_fn , fp ) ) {
log_error ( " Failure while writing to %s " , file ) ;
r = 0 ;
}
} else {
log_error ( " Configuration node %s not found " , * argv ) ;
r = 0 ;
}
argv + + ;
}
if ( fp & & dm_fclose ( fp ) ) {
stack ;
r = 0 ;
}
return r ;
}