2012-02-28 22:35:04 +04:00
/*
* Copyright ( C ) 2012 Red Hat , Inc .
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2011-09-17 17:33:51 +04:00
# define _XOPEN_SOURCE 500 /* pthread */
2012-02-28 22:35:04 +04:00
# include "configure.h"
2012-09-26 16:44:03 +04:00
# include "daemon-io.h"
# include "config-util.h"
2012-02-28 22:35:04 +04:00
# include "daemon-server.h"
2012-08-08 11:41:01 +04:00
# include "daemon-log.h"
2012-10-15 05:06:27 +04:00
# include "lvm-version.h"
2012-02-28 22:35:04 +04:00
2011-07-18 18:48:30 +04:00
# include <assert.h>
2011-07-21 01:23:43 +04:00
# include <pthread.h>
2011-07-18 18:48:30 +04:00
# include <stdint.h>
2011-08-31 16:39:58 +04:00
# include <unistd.h>
2011-07-18 18:48:30 +04:00
2013-10-25 12:38:09 +04:00
# include <math.h> /* fabs() */
# include <float.h> /* DBL_EPSILON */
2013-11-26 17:51:23 +04:00
# define LVMETAD_SOCKET DEFAULT_RUN_DIR " / lvmetad.socket"
2012-02-15 15:43:06 +04:00
typedef struct {
2012-08-08 11:41:01 +04:00
log_state * log ; /* convenience */
2012-10-15 13:44:43 +04:00
const char * log_config ;
2012-08-08 11:41:01 +04:00
2012-02-15 21:37:09 +04:00
struct dm_hash_table * pvid_to_pvmeta ;
struct dm_hash_table * device_to_pvid ; /* shares locks with above */
2012-02-15 21:30:07 +04:00
2012-02-15 15:43:06 +04:00
struct dm_hash_table * vgid_to_metadata ;
struct dm_hash_table * vgid_to_vgname ;
struct dm_hash_table * vgname_to_vgid ;
struct dm_hash_table * pvid_to_vgid ;
2011-07-21 01:23:43 +04:00
struct {
struct dm_hash_table * vg ;
2012-10-08 11:12:51 +04:00
pthread_mutex_t vg_lock_map ;
2012-02-15 21:37:09 +04:00
pthread_mutex_t pvid_to_pvmeta ;
2012-02-15 15:43:06 +04:00
pthread_mutex_t vgid_to_metadata ;
pthread_mutex_t pvid_to_vgid ;
2011-07-21 01:23:43 +04:00
} lock ;
2012-08-13 21:44:10 +04:00
char token [ 128 ] ;
pthread_mutex_t token_lock ;
2011-06-14 06:36:38 +04:00
} lvmetad_state ;
2012-09-20 01:18:28 +04:00
static void destroy_metadata_hashes ( lvmetad_state * s )
{
struct dm_hash_node * n = NULL ;
2014-03-26 16:59:13 +04:00
dm_hash_iterate ( n , s - > vgid_to_metadata )
2012-09-20 01:18:28 +04:00
dm_config_destroy ( dm_hash_get_data ( s - > vgid_to_metadata , n ) ) ;
2014-03-26 16:59:13 +04:00
dm_hash_iterate ( n , s - > pvid_to_pvmeta )
2012-09-20 01:18:28 +04:00
dm_config_destroy ( dm_hash_get_data ( s - > pvid_to_pvmeta , n ) ) ;
2014-03-26 16:59:13 +04:00
2012-09-20 01:18:28 +04:00
dm_hash_destroy ( s - > pvid_to_pvmeta ) ;
dm_hash_destroy ( s - > vgid_to_metadata ) ;
dm_hash_destroy ( s - > vgid_to_vgname ) ;
dm_hash_destroy ( s - > vgname_to_vgid ) ;
2012-12-17 16:49:19 +04:00
2014-03-26 16:59:13 +04:00
dm_hash_iterate ( n , s - > device_to_pvid )
2012-12-17 16:49:19 +04:00
dm_free ( dm_hash_get_data ( s - > device_to_pvid , n ) ) ;
2012-09-20 01:18:28 +04:00
dm_hash_destroy ( s - > device_to_pvid ) ;
dm_hash_destroy ( s - > pvid_to_vgid ) ;
}
static void create_metadata_hashes ( lvmetad_state * s )
{
s - > pvid_to_pvmeta = dm_hash_create ( 32 ) ;
s - > device_to_pvid = dm_hash_create ( 32 ) ;
s - > vgid_to_metadata = dm_hash_create ( 32 ) ;
s - > vgid_to_vgname = dm_hash_create ( 32 ) ;
s - > pvid_to_vgid = dm_hash_create ( 32 ) ;
s - > vgname_to_vgid = dm_hash_create ( 32 ) ;
}
2012-02-15 21:37:09 +04:00
static void lock_pvid_to_pvmeta ( lvmetad_state * s ) {
pthread_mutex_lock ( & s - > lock . pvid_to_pvmeta ) ; }
static void unlock_pvid_to_pvmeta ( lvmetad_state * s ) {
pthread_mutex_unlock ( & s - > lock . pvid_to_pvmeta ) ; }
2011-07-21 01:23:43 +04:00
2012-02-15 15:43:06 +04:00
static void lock_vgid_to_metadata ( lvmetad_state * s ) {
pthread_mutex_lock ( & s - > lock . vgid_to_metadata ) ; }
static void unlock_vgid_to_metadata ( lvmetad_state * s ) {
pthread_mutex_unlock ( & s - > lock . vgid_to_metadata ) ; }
2011-07-21 01:23:43 +04:00
2012-02-15 15:43:06 +04:00
static void lock_pvid_to_vgid ( lvmetad_state * s ) {
pthread_mutex_lock ( & s - > lock . pvid_to_vgid ) ; }
static void unlock_pvid_to_vgid ( lvmetad_state * s ) {
pthread_mutex_unlock ( & s - > lock . pvid_to_vgid ) ; }
2011-07-21 01:23:43 +04:00
2012-08-13 21:44:10 +04:00
static response reply_fail ( const char * reason )
{
return daemon_reply_simple ( " failed " , " reason = %s " , reason , NULL ) ;
}
static response reply_unknown ( const char * reason )
{
return daemon_reply_simple ( " unknown " , " reason = %s " , reason , NULL ) ;
}
2011-07-25 19:33:04 +04:00
/*
* TODO : It may be beneficial to clean up the vg lock hash from time to time ,
* since if we have many " rogue " requests for nonexistent things , we will keep
* allocating memory that we never release . Not good .
*/
2011-08-31 16:39:58 +04:00
static struct dm_config_tree * lock_vg ( lvmetad_state * s , const char * id ) {
2011-09-17 17:33:51 +04:00
pthread_mutex_t * vg ;
struct dm_config_tree * cft ;
2012-12-14 19:35:26 +04:00
pthread_mutexattr_t rec ;
2011-09-17 17:33:51 +04:00
2012-10-08 11:12:51 +04:00
pthread_mutex_lock ( & s - > lock . vg_lock_map ) ;
2012-12-14 19:35:26 +04:00
if ( ! ( vg = dm_hash_lookup ( s - > lock . vg , id ) ) ) {
if ( ! ( vg = malloc ( sizeof ( pthread_mutex_t ) ) ) | |
pthread_mutexattr_init ( & rec ) | |
pthread_mutexattr_settype ( & rec , PTHREAD_MUTEX_RECURSIVE_NP ) | |
pthread_mutex_init ( vg , & rec ) )
goto bad ;
2012-03-02 02:52:59 +04:00
if ( ! dm_hash_insert ( s - > lock . vg , id , vg ) ) {
2012-12-14 19:35:26 +04:00
pthread_mutex_destroy ( vg ) ;
goto bad ;
2012-03-02 02:52:59 +04:00
}
2011-07-21 01:23:43 +04:00
}
2012-10-08 11:12:51 +04:00
/* We never remove items from s->lock.vg => the pointer remains valid. */
pthread_mutex_unlock ( & s - > lock . vg_lock_map ) ;
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " locking VG %s " , id ) ;
2011-07-21 01:23:43 +04:00
pthread_mutex_lock ( vg ) ;
2012-10-08 11:12:51 +04:00
/* Protect against structure changes of the vgid_to_metadata hash. */
lock_vgid_to_metadata ( s ) ;
2012-02-15 15:43:06 +04:00
cft = dm_hash_lookup ( s - > vgid_to_metadata , id ) ;
unlock_vgid_to_metadata ( s ) ;
2011-07-21 01:23:43 +04:00
return cft ;
2012-12-14 19:35:26 +04:00
bad :
pthread_mutex_unlock ( & s - > lock . vg_lock_map ) ;
free ( vg ) ;
ERROR ( s , " Out of memory " ) ;
return NULL ;
2011-07-21 01:23:43 +04:00
}
2011-08-31 16:39:58 +04:00
static void unlock_vg ( lvmetad_state * s , const char * id ) {
2012-03-02 02:52:59 +04:00
pthread_mutex_t * vg ;
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " unlocking VG %s " , id ) ;
2012-10-08 11:12:51 +04:00
/* Protect the s->lock.vg structure from concurrent access. */
pthread_mutex_lock ( & s - > lock . vg_lock_map ) ;
2012-03-02 02:52:59 +04:00
if ( ( vg = dm_hash_lookup ( s - > lock . vg , id ) ) )
pthread_mutex_unlock ( vg ) ;
2012-10-08 11:12:51 +04:00
pthread_mutex_unlock ( & s - > lock . vg_lock_map ) ;
2011-07-21 01:23:43 +04:00
}
2011-08-30 19:44:01 +04:00
static struct dm_config_node * pvs ( struct dm_config_node * vg )
2011-07-25 21:59:50 +04:00
{
2011-08-30 19:44:01 +04:00
struct dm_config_node * pv = dm_config_find_node ( vg , " metadata/physical_volumes " ) ;
2011-07-25 21:59:50 +04:00
if ( pv )
pv = pv - > child ;
return pv ;
}
2012-02-15 15:43:06 +04:00
static void filter_metadata ( struct dm_config_node * vg ) {
struct dm_config_node * pv = pvs ( vg ) ;
while ( pv ) {
struct dm_config_node * item = pv - > child ;
while ( item ) {
/* Remove the advisory device nodes. */
if ( item - > sib & & ! strcmp ( item - > sib - > key , " device " ) )
item - > sib = item - > sib - > sib ;
item = item - > sib ;
}
pv = pv - > sib ;
}
vg - > sib = NULL ; /* Drop any trailing garbage. */
}
2012-02-21 13:19:08 +04:00
static void merge_pvmeta ( struct dm_config_node * pv , struct dm_config_node * pvmeta )
{
2012-02-23 21:59:32 +04:00
struct dm_config_node * tmp ;
2012-02-21 13:19:08 +04:00
if ( ! pvmeta )
return ;
2012-02-23 21:59:32 +04:00
tmp = pvmeta ;
2012-02-21 13:19:08 +04:00
while ( tmp - > sib ) {
/* drop the redundant ID and dev_size nodes */
if ( ! strcmp ( tmp - > sib - > key , " id " ) | | ! strcmp ( tmp - > sib - > key , " dev_size " ) )
tmp - > sib = tmp - > sib - > sib ;
if ( ! tmp - > sib ) break ;
tmp = tmp - > sib ;
tmp - > parent = pv ;
}
tmp - > sib = pv - > child ;
pv - > child = pvmeta ;
pvmeta - > parent = pv ;
}
2011-07-25 21:59:50 +04:00
/* Either the "big" vgs lock, or a per-vg lock needs to be held before entering
* this function . */
2011-08-30 19:44:01 +04:00
static int update_pv_status ( lvmetad_state * s ,
struct dm_config_tree * cft ,
struct dm_config_node * vg , int act )
2011-07-25 21:59:50 +04:00
{
2011-09-17 17:33:51 +04:00
struct dm_config_node * pv ;
2011-07-25 21:59:50 +04:00
int complete = 1 ;
2012-03-02 02:52:59 +04:00
const char * uuid ;
struct dm_config_tree * pvmeta ;
2011-07-25 21:59:50 +04:00
2012-02-15 21:37:09 +04:00
lock_pvid_to_pvmeta ( s ) ;
2012-03-02 02:52:59 +04:00
for ( pv = pvs ( vg ) ; pv ; pv = pv - > sib ) {
if ( ! ( uuid = dm_config_find_str ( pv - > child , " id " , NULL ) ) )
continue ;
pvmeta = dm_hash_lookup ( s - > pvid_to_pvmeta , uuid ) ;
2012-02-15 15:43:06 +04:00
if ( act ) {
2012-02-15 21:37:09 +04:00
set_flag ( cft , pv , " status " , " MISSING " , ! pvmeta ) ;
2012-02-15 21:30:07 +04:00
if ( pvmeta ) {
2012-02-21 13:19:08 +04:00
struct dm_config_node * pvmeta_cn =
dm_config_clone_node ( cft , pvmeta - > root - > child , 1 ) ;
merge_pvmeta ( pv , pvmeta_cn ) ;
2012-02-15 15:43:06 +04:00
}
2012-02-13 18:25:14 +04:00
}
2012-02-15 21:37:09 +04:00
if ( ! pvmeta ) {
2011-07-25 21:59:50 +04:00
complete = 0 ;
2012-02-15 15:43:06 +04:00
if ( ! act ) { /* optimisation */
2012-02-15 21:37:09 +04:00
unlock_pvid_to_pvmeta ( s ) ;
2011-07-25 21:59:50 +04:00
return complete ;
}
}
}
2012-02-15 21:37:09 +04:00
unlock_pvid_to_pvmeta ( s ) ;
2011-07-25 21:59:50 +04:00
return complete ;
}
2012-02-15 15:43:06 +04:00
static struct dm_config_node * make_pv_node ( lvmetad_state * s , const char * pvid ,
struct dm_config_tree * cft ,
struct dm_config_node * parent ,
struct dm_config_node * pre_sib )
{
2012-02-15 21:30:07 +04:00
struct dm_config_tree * pvmeta = dm_hash_lookup ( s - > pvid_to_pvmeta , pvid ) ;
const char * vgid = dm_hash_lookup ( s - > pvid_to_vgid , pvid ) , * vgname = NULL ;
2012-02-23 21:59:32 +04:00
struct dm_config_node * pv ;
struct dm_config_node * cn = NULL ;
2012-02-15 21:30:07 +04:00
if ( ! pvmeta )
return NULL ;
2012-02-15 15:43:06 +04:00
if ( vgid ) {
lock_vgid_to_metadata ( s ) ; // XXX
vgname = dm_hash_lookup ( s - > vgid_to_vgname , vgid ) ;
unlock_vgid_to_metadata ( s ) ;
}
2012-02-15 21:30:07 +04:00
/* Nick the pvmeta config tree. */
2012-03-02 02:52:59 +04:00
if ( ! ( pv = dm_config_clone_node ( cft , pvmeta - > root , 0 ) ) )
return 0 ;
2012-02-15 21:30:07 +04:00
if ( pre_sib )
pre_sib - > sib = pv ;
if ( parent & & ! parent - > child )
parent - > child = pv ;
pv - > parent = parent ;
2012-02-21 13:19:08 +04:00
pv - > key = pvid ;
2012-02-15 21:30:07 +04:00
/* Add the "variable" bits to it. */
2012-02-15 15:43:06 +04:00
if ( vgid & & strcmp ( vgid , " #orphan " ) )
cn = make_text_node ( cft , " vgid " , vgid , pv , cn ) ;
if ( vgname )
cn = make_text_node ( cft , " vgname " , vgname , pv , cn ) ;
return pv ;
}
static response pv_list ( lvmetad_state * s , request r )
{
2012-02-15 18:15:50 +04:00
struct dm_config_node * cn = NULL , * cn_pvs ;
2012-02-23 21:59:32 +04:00
struct dm_hash_node * n ;
const char * id ;
2012-12-14 19:38:07 +04:00
response res = { 0 } ;
2012-10-11 16:17:17 +04:00
buffer_init ( & res . buffer ) ;
2012-03-02 02:52:59 +04:00
if ( ! ( res . cft = dm_config_create ( ) ) )
return res ; /* FIXME error reporting */
2012-02-15 15:43:06 +04:00
/* The response field */
res . cft - > root = make_text_node ( res . cft , " response " , " OK " , NULL , NULL ) ;
cn_pvs = make_config_node ( res . cft , " physical_volumes " , NULL , res . cft - > root ) ;
2012-02-15 21:37:09 +04:00
lock_pvid_to_pvmeta ( s ) ;
2012-02-15 15:43:06 +04:00
2014-03-26 16:59:13 +04:00
dm_hash_iterate ( n , s - > pvid_to_pvmeta ) {
2012-02-23 21:59:32 +04:00
id = dm_hash_get_key ( s - > pvid_to_pvmeta , n ) ;
2012-02-15 15:43:06 +04:00
cn = make_pv_node ( s , id , res . cft , cn_pvs , cn ) ;
}
2012-02-15 21:37:09 +04:00
unlock_pvid_to_pvmeta ( s ) ;
2012-02-15 15:43:06 +04:00
return res ;
}
static response pv_lookup ( lvmetad_state * s , request r )
{
const char * pvid = daemon_request_str ( r , " uuid " , NULL ) ;
2012-02-21 13:19:08 +04:00
int64_t devt = daemon_request_int ( r , " device " , 0 ) ;
2012-12-14 19:38:07 +04:00
response res = { 0 } ;
2012-02-23 21:59:32 +04:00
struct dm_config_node * pv ;
2012-10-11 16:17:17 +04:00
buffer_init ( & res . buffer ) ;
2012-02-21 13:19:08 +04:00
if ( ! pvid & & ! devt )
2012-08-13 21:44:10 +04:00
return reply_fail ( " need PVID or device " ) ;
2012-02-15 15:43:06 +04:00
2012-03-02 02:52:59 +04:00
if ( ! ( res . cft = dm_config_create ( ) ) )
2012-08-13 21:44:10 +04:00
return reply_fail ( " out of memory " ) ;
2012-03-02 02:52:59 +04:00
if ( ! ( res . cft - > root = make_text_node ( res . cft , " response " , " OK " , NULL , NULL ) ) )
2012-08-13 21:44:10 +04:00
return reply_fail ( " out of memory " ) ;
2012-02-15 15:43:06 +04:00
2012-02-15 21:37:09 +04:00
lock_pvid_to_pvmeta ( s ) ;
2012-02-21 13:19:08 +04:00
if ( ! pvid & & devt )
pvid = dm_hash_lookup_binary ( s - > device_to_pvid , & devt , sizeof ( devt ) ) ;
if ( ! pvid ) {
unlock_pvid_to_pvmeta ( s ) ;
2014-03-01 14:38:41 +04:00
WARN ( s , " pv_lookup: could not find device % " PRIu64 , devt ) ;
2012-02-24 04:24:37 +04:00
dm_config_destroy ( res . cft ) ;
2012-08-13 21:44:10 +04:00
return reply_unknown ( " device not found " ) ;
2012-02-21 13:19:08 +04:00
}
2012-02-15 15:43:06 +04:00
pv = make_pv_node ( s , pvid , res . cft , NULL , res . cft - > root ) ;
2012-02-15 21:30:07 +04:00
if ( ! pv ) {
2012-02-15 21:37:09 +04:00
unlock_pvid_to_pvmeta ( s ) ;
2012-02-24 04:24:37 +04:00
dm_config_destroy ( res . cft ) ;
2012-08-13 21:44:10 +04:00
return reply_unknown ( " PV not found " ) ;
2012-02-15 21:30:07 +04:00
}
2012-02-15 15:43:06 +04:00
pv - > key = " physical_volume " ;
2012-02-15 21:37:09 +04:00
unlock_pvid_to_pvmeta ( s ) ;
2012-02-15 15:43:06 +04:00
return res ;
}
static response vg_list ( lvmetad_state * s , request r )
{
struct dm_config_node * cn , * cn_vgs , * cn_last = NULL ;
2012-02-23 21:59:32 +04:00
struct dm_hash_node * n ;
const char * id ;
const char * name ;
2012-12-14 19:38:07 +04:00
response res = { 0 } ;
2012-10-11 16:17:17 +04:00
buffer_init ( & res . buffer ) ;
2012-03-02 02:52:59 +04:00
if ( ! ( res . cft = dm_config_create ( ) ) )
goto bad ; /* FIXME: better error reporting */
2012-02-15 15:43:06 +04:00
/* The response field */
res . cft - > root = cn = dm_config_create_node ( res . cft , " response " ) ;
2012-03-02 02:52:59 +04:00
if ( ! cn )
goto bad ; /* FIXME */
2012-02-15 15:43:06 +04:00
cn - > parent = res . cft - > root ;
2012-03-02 02:52:59 +04:00
if ( ! ( cn - > v = dm_config_create_value ( res . cft ) ) )
goto bad ; /* FIXME */
2012-02-15 15:43:06 +04:00
cn - > v - > type = DM_CFG_STRING ;
cn - > v - > v . str = " OK " ;
cn_vgs = cn = cn - > sib = dm_config_create_node ( res . cft , " volume_groups " ) ;
2012-03-02 02:52:59 +04:00
if ( ! cn_vgs )
goto bad ; /* FIXME */
2012-02-15 15:43:06 +04:00
cn - > parent = res . cft - > root ;
cn - > v = NULL ;
cn - > child = NULL ;
lock_vgid_to_metadata ( s ) ;
2014-03-26 16:59:13 +04:00
dm_hash_iterate ( n , s - > vgid_to_vgname ) {
2012-02-23 21:59:32 +04:00
id = dm_hash_get_key ( s - > vgid_to_vgname , n ) ,
name = dm_hash_get_data ( s - > vgid_to_vgname , n ) ;
2012-02-15 15:43:06 +04:00
2012-03-02 02:52:59 +04:00
if ( ! ( cn = dm_config_create_node ( res . cft , id ) ) )
goto bad ; /* FIXME */
2012-02-15 15:43:06 +04:00
if ( cn_last )
cn_last - > sib = cn ;
cn - > parent = cn_vgs ;
cn - > sib = NULL ;
cn - > v = NULL ;
2012-03-02 02:52:59 +04:00
if ( ! ( cn - > child = dm_config_create_node ( res . cft , " name " ) ) )
goto bad ; /* FIXME */
2012-02-15 15:43:06 +04:00
cn - > child - > parent = cn ;
cn - > child - > sib = 0 ;
2012-03-02 02:52:59 +04:00
if ( ! ( cn - > child - > v = dm_config_create_value ( res . cft ) ) )
goto bad ; /* FIXME */
2012-02-15 15:43:06 +04:00
cn - > child - > v - > type = DM_CFG_STRING ;
cn - > child - > v - > v . str = name ;
if ( ! cn_vgs - > child )
cn_vgs - > child = cn ;
cn_last = cn ;
}
unlock_vgid_to_metadata ( s ) ;
2012-03-02 02:52:59 +04:00
bad :
2012-02-15 15:43:06 +04:00
return res ;
}
2012-01-16 12:25:32 +04:00
static response vg_lookup ( lvmetad_state * s , request r )
2011-07-18 18:48:30 +04:00
{
2012-01-25 17:06:57 +04:00
struct dm_config_tree * cft ;
struct dm_config_node * metadata , * n ;
2012-12-14 19:38:07 +04:00
response res = { 0 } ;
2012-01-25 17:06:57 +04:00
2012-02-23 21:59:32 +04:00
const char * uuid = daemon_request_str ( r , " uuid " , NULL ) ;
const char * name = daemon_request_str ( r , " name " , NULL ) ;
2012-02-15 15:43:06 +04:00
2012-10-11 16:17:17 +04:00
buffer_init ( & res . buffer ) ;
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " vg_lookup: uuid = %s, name = %s " , uuid , name ) ;
2012-01-16 12:25:32 +04:00
if ( ! uuid | | ! name ) {
2012-02-15 15:43:06 +04:00
lock_vgid_to_metadata ( s ) ;
2012-01-16 12:25:32 +04:00
if ( name & & ! uuid )
2012-02-23 21:59:32 +04:00
uuid = dm_hash_lookup ( s - > vgname_to_vgid , name ) ;
2012-01-16 12:25:32 +04:00
if ( uuid & & ! name )
2012-02-23 21:59:32 +04:00
name = dm_hash_lookup ( s - > vgid_to_vgname , uuid ) ;
2012-02-15 15:43:06 +04:00
unlock_vgid_to_metadata ( s ) ;
2012-01-16 12:25:32 +04:00
}
2011-09-17 17:33:51 +04:00
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " vg_lookup: updated uuid = %s, name = %s " , uuid , name ) ;
2012-01-16 12:25:32 +04:00
2013-01-16 14:09:37 +04:00
/* Check the name here. */
if ( ! uuid | | ! name )
2012-08-13 21:44:10 +04:00
return reply_unknown ( " VG not found " ) ;
2012-01-16 12:25:32 +04:00
2012-01-25 17:06:57 +04:00
cft = lock_vg ( s , uuid ) ;
2011-07-21 01:23:43 +04:00
if ( ! cft | | ! cft - > root ) {
unlock_vg ( s , uuid ) ;
2012-08-13 21:44:10 +04:00
return reply_unknown ( " UUID not found " ) ;
2011-07-21 01:23:43 +04:00
}
2011-07-19 20:48:13 +04:00
2012-01-25 17:06:57 +04:00
metadata = cft - > root ;
2012-03-02 02:52:59 +04:00
if ( ! ( res . cft = dm_config_create ( ) ) )
goto bad ;
2011-07-18 18:48:30 +04:00
/* The response field */
2012-03-02 02:52:59 +04:00
if ( ! ( res . cft - > root = n = dm_config_create_node ( res . cft , " response " ) ) )
goto bad ;
2012-08-27 16:19:30 +04:00
if ( ! ( n - > v = dm_config_create_value ( cft ) ) )
goto bad ;
2012-01-16 12:25:32 +04:00
n - > parent = res . cft - > root ;
2011-08-30 19:44:01 +04:00
n - > v - > type = DM_CFG_STRING ;
2011-07-18 18:48:30 +04:00
n - > v - > v . str = " OK " ;
2012-03-02 02:52:59 +04:00
if ( ! ( n = n - > sib = dm_config_create_node ( res . cft , " name " ) ) )
goto bad ;
2012-08-27 16:19:30 +04:00
if ( ! ( n - > v = dm_config_create_value ( res . cft ) ) )
goto bad ;
2012-01-16 12:25:32 +04:00
n - > parent = res . cft - > root ;
n - > v - > type = DM_CFG_STRING ;
n - > v - > v . str = name ;
2011-07-18 18:48:30 +04:00
/* The metadata section */
2012-03-02 02:52:59 +04:00
if ( ! ( n = n - > sib = dm_config_clone_node ( res . cft , metadata , 1 ) ) )
goto bad ;
2011-07-18 18:48:30 +04:00
n - > parent = res . cft - > root ;
2011-07-21 01:23:43 +04:00
unlock_vg ( s , uuid ) ;
2012-02-15 15:43:06 +04:00
update_pv_status ( s , res . cft , n , 1 ) ; /* FIXME report errors */
2011-07-18 18:48:30 +04:00
return res ;
2012-03-02 02:52:59 +04:00
bad :
unlock_vg ( s , uuid ) ;
2012-08-13 21:44:10 +04:00
return reply_fail ( " out of memory " ) ;
2011-07-18 18:48:30 +04:00
}
2013-10-25 12:38:09 +04:00
/* Test if the doubles are close enough to be considered equal */
static int close_enough ( double d1 , double d2 )
{
return fabs ( d1 - d2 ) < DBL_EPSILON ;
}
2011-08-30 19:44:01 +04:00
static int compare_value ( struct dm_config_value * a , struct dm_config_value * b )
2011-07-25 19:51:51 +04:00
{
2011-08-31 16:39:58 +04:00
int r = 0 ;
2011-07-25 19:51:51 +04:00
if ( a - > type > b - > type )
return 1 ;
if ( a - > type < b - > type )
return - 1 ;
switch ( a - > type ) {
2012-01-26 01:42:09 +04:00
case DM_CFG_STRING : r = strcmp ( a - > v . str , b - > v . str ) ; break ;
2013-10-25 12:38:09 +04:00
case DM_CFG_FLOAT : r = close_enough ( a - > v . f , b - > v . f ) ? 0 : ( a - > v . f > b - > v . f ) ? 1 : - 1 ; break ;
2012-02-15 15:43:06 +04:00
case DM_CFG_INT : r = ( a - > v . i = = b - > v . i ) ? 0 : ( a - > v . i > b - > v . i ) ? 1 : - 1 ; break ;
2011-08-30 19:44:01 +04:00
case DM_CFG_EMPTY_ARRAY : return 0 ;
2011-07-25 19:51:51 +04:00
}
2011-08-31 16:39:58 +04:00
if ( r = = 0 & & a - > next & & b - > next )
r = compare_value ( a - > next , b - > next ) ;
return r ;
2011-07-25 19:51:51 +04:00
}
2011-08-30 19:44:01 +04:00
static int compare_config ( struct dm_config_node * a , struct dm_config_node * b )
2011-07-25 19:51:51 +04:00
{
int result = 0 ;
if ( a - > v & & b - > v )
result = compare_value ( a - > v , b - > v ) ;
if ( a - > v & & ! b - > v )
result = 1 ;
if ( ! a - > v & & b - > v )
result = - 1 ;
if ( a - > child & & b - > child )
result = compare_config ( a - > child , b - > child ) ;
2012-02-15 15:43:06 +04:00
if ( result ) {
2012-10-15 17:20:11 +04:00
// DEBUGLOG("config inequality at %s / %s", a->key, b->key);
2011-07-25 19:51:51 +04:00
return result ;
2012-02-15 15:43:06 +04:00
}
2011-07-25 19:51:51 +04:00
if ( a - > sib & & b - > sib )
result = compare_config ( a - > sib , b - > sib ) ;
if ( a - > sib & & ! b - > sib )
result = 1 ;
if ( ! a - > sib & & b - > sib )
result = - 1 ;
return result ;
}
2013-10-09 01:14:55 +04:00
static int vg_remove_if_missing ( lvmetad_state * s , const char * vgid , int update_pvids ) ;
2012-02-15 18:06:48 +04:00
2012-02-15 15:43:06 +04:00
/* You need to be holding the pvid_to_vgid lock already to call this. */
2012-02-15 18:06:48 +04:00
static int update_pvid_to_vgid ( lvmetad_state * s , struct dm_config_tree * vg ,
const char * vgid , int nuke_empty )
2011-07-20 20:46:40 +04:00
{
2012-03-02 02:52:59 +04:00
struct dm_config_node * pv ;
2012-02-27 14:10:43 +04:00
struct dm_hash_table * to_check ;
2012-02-23 21:59:32 +04:00
struct dm_hash_node * n ;
const char * pvid ;
const char * vgid_old ;
2012-03-02 02:52:59 +04:00
const char * check_vgid ;
2012-03-23 14:33:26 +04:00
int r = 0 ;
2011-07-20 20:46:40 +04:00
2011-07-20 22:34:57 +04:00
if ( ! vgid )
return 0 ;
2011-07-20 20:46:40 +04:00
2012-02-27 14:10:43 +04:00
if ( ! ( to_check = dm_hash_create ( 32 ) ) )
return 0 ;
2012-03-02 02:52:59 +04:00
for ( pv = pvs ( vg - > root ) ; pv ; pv = pv - > sib ) {
if ( ! ( pvid = dm_config_find_str ( pv - > child , " id " , NULL ) ) )
continue ;
if ( nuke_empty & &
( vgid_old = dm_hash_lookup ( s - > pvid_to_vgid , pvid ) ) & &
! dm_hash_insert ( to_check , vgid_old , ( void * ) 1 ) )
2012-03-23 14:33:26 +04:00
goto out ;
2012-03-02 02:52:59 +04:00
if ( ! dm_hash_insert ( s - > pvid_to_vgid , pvid , ( void * ) vgid ) )
2012-03-23 14:33:26 +04:00
goto out ;
2012-03-02 02:52:59 +04:00
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " moving PV %s to VG %s " , pvid , vgid ) ;
2011-07-20 20:46:40 +04:00
}
2011-07-20 22:34:57 +04:00
2014-03-26 16:59:13 +04:00
dm_hash_iterate ( n , to_check ) {
2012-03-02 02:52:59 +04:00
check_vgid = dm_hash_get_key ( to_check , n ) ;
2012-02-15 18:06:48 +04:00
lock_vg ( s , check_vgid ) ;
2013-10-09 01:14:55 +04:00
vg_remove_if_missing ( s , check_vgid , 0 ) ;
2012-02-15 18:06:48 +04:00
unlock_vg ( s , check_vgid ) ;
}
2012-03-23 14:33:26 +04:00
r = 1 ;
out :
2012-02-15 18:06:48 +04:00
dm_hash_destroy ( to_check ) ;
2012-03-23 14:33:26 +04:00
return r ;
2011-07-20 20:46:40 +04:00
}
2012-02-15 18:06:48 +04:00
/* A pvid map lock needs to be held if update_pvids = 1. */
static int remove_metadata ( lvmetad_state * s , const char * vgid , int update_pvids )
2012-01-16 12:25:32 +04:00
{
2012-01-25 17:06:57 +04:00
struct dm_config_tree * old ;
const char * oldname ;
2012-02-15 15:43:06 +04:00
lock_vgid_to_metadata ( s ) ;
old = dm_hash_lookup ( s - > vgid_to_metadata , vgid ) ;
oldname = dm_hash_lookup ( s - > vgid_to_vgname , vgid ) ;
2012-01-16 12:25:32 +04:00
2012-12-17 03:43:18 +04:00
if ( ! old ) {
unlock_vgid_to_metadata ( s ) ;
2012-01-16 12:25:32 +04:00
return 0 ;
2012-12-17 03:43:18 +04:00
}
2012-02-27 14:19:00 +04:00
assert ( oldname ) ;
2012-01-16 12:25:32 +04:00
/* need to update what we have since we found a newer version */
2012-02-15 15:43:06 +04:00
dm_hash_remove ( s - > vgid_to_metadata , vgid ) ;
dm_hash_remove ( s - > vgid_to_vgname , vgid ) ;
dm_hash_remove ( s - > vgname_to_vgid , oldname ) ;
2012-12-17 03:43:18 +04:00
unlock_vgid_to_metadata ( s ) ;
if ( update_pvids )
/* FIXME: What should happen when update fails */
update_pvid_to_vgid ( s , old , " #orphan " , 0 ) ;
2012-01-16 12:25:32 +04:00
dm_config_destroy ( old ) ;
return 1 ;
}
2012-02-15 15:43:06 +04:00
/* The VG must be locked. */
2013-10-09 01:14:55 +04:00
static int vg_remove_if_missing ( lvmetad_state * s , const char * vgid , int update_pvids )
2012-02-15 15:43:06 +04:00
{
2012-02-23 21:59:32 +04:00
struct dm_config_tree * vg ;
struct dm_config_node * pv ;
2012-03-02 02:52:59 +04:00
const char * vgid_check ;
const char * pvid ;
2012-02-23 21:59:32 +04:00
int missing = 1 ;
2012-02-15 15:43:06 +04:00
if ( ! vgid )
return 0 ;
2012-02-23 21:59:32 +04:00
if ( ! ( vg = dm_hash_lookup ( s - > vgid_to_metadata , vgid ) ) )
2012-02-15 15:43:06 +04:00
return 1 ;
2012-02-15 21:37:09 +04:00
lock_pvid_to_pvmeta ( s ) ;
2012-03-02 02:52:59 +04:00
for ( pv = pvs ( vg - > root ) ; pv ; pv = pv - > sib ) {
if ( ! ( pvid = dm_config_find_str ( pv - > child , " id " , NULL ) ) )
continue ;
2012-02-15 15:43:06 +04:00
2012-03-02 02:52:59 +04:00
if ( ( vgid_check = dm_hash_lookup ( s - > pvid_to_vgid , pvid ) ) & &
dm_hash_lookup ( s - > pvid_to_pvmeta , pvid ) & &
! strcmp ( vgid , vgid_check ) )
2012-02-15 15:43:06 +04:00
missing = 0 ; /* at least one PV is around */
}
if ( missing ) {
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " removing empty VG %s " , vgid ) ;
2013-10-09 01:14:55 +04:00
remove_metadata ( s , vgid , update_pvids ) ;
2012-02-15 15:43:06 +04:00
}
2012-02-15 21:37:09 +04:00
unlock_pvid_to_pvmeta ( s ) ;
2012-02-15 15:43:06 +04:00
return 1 ;
}
2011-07-21 01:23:43 +04:00
/* No locks need to be held. The pointers are never used outside of the scope of
* this function , so they can be safely destroyed after update_metadata returns
* ( anything that might have been retained is copied ) . */
2012-01-16 12:25:32 +04:00
static int update_metadata ( lvmetad_state * s , const char * name , const char * _vgid ,
2012-09-20 01:45:51 +04:00
struct dm_config_node * metadata , int64_t * oldseq )
2011-07-19 18:13:59 +04:00
{
2012-10-12 12:35:01 +04:00
struct dm_config_tree * cft = NULL ;
2011-09-17 17:33:51 +04:00
struct dm_config_tree * old ;
2011-07-21 01:23:43 +04:00
int retval = 0 ;
2012-01-25 17:06:57 +04:00
int seq ;
int haveseq = - 1 ;
const char * oldname = NULL ;
const char * vgid ;
2012-03-02 02:52:59 +04:00
char * cfgname ;
2011-09-17 17:33:51 +04:00
2012-02-15 15:43:06 +04:00
lock_vgid_to_metadata ( s ) ;
old = dm_hash_lookup ( s - > vgid_to_metadata , _vgid ) ;
2013-01-16 14:09:37 +04:00
oldname = dm_hash_lookup ( s - > vgid_to_vgname , _vgid ) ;
2012-02-15 15:43:06 +04:00
unlock_vgid_to_metadata ( s ) ;
2012-12-17 03:39:00 +04:00
lock_vg ( s , _vgid ) ;
2011-07-21 01:23:43 +04:00
2012-01-25 17:06:57 +04:00
seq = dm_config_find_int ( metadata , " metadata/seqno " , - 1 ) ;
2011-07-19 20:48:13 +04:00
2013-01-16 14:09:37 +04:00
if ( old )
2011-08-30 19:44:01 +04:00
haveseq = dm_config_find_int ( old - > root , " metadata/seqno " , - 1 ) ;
2011-07-19 20:48:13 +04:00
if ( seq < 0 )
2011-07-21 01:23:43 +04:00
goto out ;
2011-07-19 20:48:13 +04:00
2012-02-15 15:43:06 +04:00
filter_metadata ( metadata ) ; /* sanitize */
2012-09-20 01:45:51 +04:00
if ( oldseq ) {
if ( old )
* oldseq = haveseq ;
else
* oldseq = seq ;
}
2011-07-19 20:48:13 +04:00
if ( seq = = haveseq ) {
2011-07-21 01:23:43 +04:00
retval = 1 ;
2011-07-25 19:51:51 +04:00
if ( compare_config ( metadata , old - > root ) )
retval = 0 ;
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " Not updating metadata for %s at %d (%s) " , _vgid , haveseq ,
2012-02-15 15:43:06 +04:00
retval ? " ok " : " MISMATCH " ) ;
if ( ! retval ) {
2012-10-15 17:20:11 +04:00
DEBUGLOG_cft ( s , " OLD: " , old - > root ) ;
DEBUGLOG_cft ( s , " NEW: " , metadata ) ;
2012-02-15 15:43:06 +04:00
}
2011-07-21 01:23:43 +04:00
goto out ;
2011-07-19 20:48:13 +04:00
}
if ( seq < haveseq ) {
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " Refusing to update metadata for %s (at %d) to %d " , _vgid , haveseq , seq ) ;
2012-02-15 15:43:06 +04:00
/* TODO: notify the client that their metadata is out of date? */
2011-07-21 01:23:43 +04:00
retval = 1 ;
goto out ;
2011-07-19 20:48:13 +04:00
}
2012-03-02 02:52:59 +04:00
if ( ! ( cft = dm_config_create ( ) ) | |
! ( cft - > root = dm_config_clone_node ( cft , metadata , 0 ) ) ) {
2012-08-08 11:41:01 +04:00
ERROR ( s , " Out of memory " ) ;
2012-03-02 02:52:59 +04:00
goto out ;
}
2011-07-20 22:45:32 +04:00
2012-01-25 17:06:57 +04:00
vgid = dm_config_find_str ( cft - > root , " metadata/id " , NULL ) ;
2012-01-16 12:25:32 +04:00
if ( ! vgid | | ! name ) {
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " Name '%s' or uuid '%s' missing! " , name , vgid ) ;
2011-07-21 01:23:43 +04:00
goto out ;
2012-01-16 12:25:32 +04:00
}
2011-07-21 01:23:43 +04:00
2012-02-15 15:43:06 +04:00
lock_pvid_to_vgid ( s ) ;
2011-07-20 22:45:32 +04:00
2011-07-19 20:48:13 +04:00
if ( haveseq > = 0 & & haveseq < seq ) {
2012-08-08 11:41:01 +04:00
INFO ( s , " Updating metadata for %s at %d to %d " , _vgid , haveseq , seq ) ;
2011-07-20 22:45:32 +04:00
/* temporarily orphan all of our PVs */
2013-01-16 14:09:37 +04:00
update_pvid_to_vgid ( s , old , " #orphan " , 0 ) ;
2011-07-19 20:48:13 +04:00
}
2012-02-15 15:43:06 +04:00
lock_vgid_to_metadata ( s ) ;
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " Mapping %s to %s " , vgid , name ) ;
2012-03-02 02:52:59 +04:00
retval = ( ( cfgname = dm_pool_strdup ( dm_config_memory ( cft ) , name ) ) & &
dm_hash_insert ( s - > vgid_to_metadata , vgid , cft ) & &
dm_hash_insert ( s - > vgid_to_vgname , vgid , cfgname ) & &
dm_hash_insert ( s - > vgname_to_vgid , name , ( void * ) vgid ) ) ? 1 : 0 ;
2013-01-16 14:09:37 +04:00
2013-10-04 16:30:00 +04:00
if ( retval & & oldname & & strcmp ( name , oldname ) ) {
const char * vgid_prev = dm_hash_lookup ( s - > vgname_to_vgid , oldname ) ;
if ( vgid_prev & & ! strcmp ( vgid_prev , vgid ) )
dm_hash_remove ( s - > vgname_to_vgid , oldname ) ;
}
2013-01-16 14:09:37 +04:00
2013-04-03 15:46:12 +04:00
if ( haveseq > = 0 & & haveseq < seq )
dm_config_destroy ( old ) ;
2012-02-15 15:43:06 +04:00
unlock_vgid_to_metadata ( s ) ;
2011-07-21 01:23:43 +04:00
2012-03-02 02:52:59 +04:00
if ( retval )
2012-03-23 14:34:51 +04:00
retval = update_pvid_to_vgid ( s , cft , vgid , 1 ) ;
2011-07-19 20:48:13 +04:00
2012-02-15 15:43:06 +04:00
unlock_pvid_to_vgid ( s ) ;
2013-01-16 14:09:37 +04:00
out : /* FIXME: We should probably abort() on partial failures. */
2012-10-08 22:33:03 +04:00
if ( ! retval & & cft )
dm_config_destroy ( cft ) ;
2011-07-21 01:23:43 +04:00
unlock_vg ( s , _vgid ) ;
return retval ;
2011-07-19 18:13:59 +04:00
}
2012-01-16 12:25:32 +04:00
static response pv_gone ( lvmetad_state * s , request r )
{
2012-02-15 21:30:07 +04:00
const char * pvid = daemon_request_str ( r , " uuid " , NULL ) ;
int64_t device = daemon_request_int ( r , " device " , 0 ) ;
2012-02-23 21:59:32 +04:00
struct dm_config_tree * pvmeta ;
2013-11-25 16:43:25 +04:00
char * pvid_old , * vgid ;
2012-02-15 15:43:06 +04:00
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " pv_gone: %s / % " PRIu64 , pvid , device ) ;
2012-02-15 15:43:06 +04:00
2012-02-15 21:37:09 +04:00
lock_pvid_to_pvmeta ( s ) ;
2012-02-15 21:30:07 +04:00
if ( ! pvid & & device > 0 )
pvid = dm_hash_lookup_binary ( s - > device_to_pvid , & device , sizeof ( device ) ) ;
2012-02-15 15:43:06 +04:00
if ( ! pvid ) {
2012-02-15 21:37:09 +04:00
unlock_pvid_to_pvmeta ( s ) ;
2012-08-13 21:44:10 +04:00
return reply_unknown ( " device not in cache " ) ;
2012-02-15 15:43:06 +04:00
}
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " pv_gone (updated): %s / % " PRIu64 , pvid , device ) ;
2012-02-15 15:43:06 +04:00
2012-02-23 21:59:32 +04:00
pvmeta = dm_hash_lookup ( s - > pvid_to_pvmeta , pvid ) ;
2012-12-17 16:49:19 +04:00
pvid_old = dm_hash_lookup_binary ( s - > device_to_pvid , & device , sizeof ( device ) ) ;
2013-11-25 16:43:25 +04:00
vgid = dm_hash_lookup ( s - > pvid_to_vgid , pvid ) ;
2013-10-09 01:16:41 +04:00
2012-02-15 21:30:07 +04:00
dm_hash_remove_binary ( s - > device_to_pvid , & device , sizeof ( device ) ) ;
dm_hash_remove ( s - > pvid_to_pvmeta , pvid ) ;
2012-02-15 21:37:09 +04:00
unlock_pvid_to_pvmeta ( s ) ;
2012-01-16 12:25:32 +04:00
2014-03-01 14:38:41 +04:00
dm_free ( pvid_old ) ;
2013-10-09 16:53:58 +04:00
if ( vgid ) {
2014-03-01 14:38:41 +04:00
if ( ! ( vgid = dm_strdup ( vgid ) ) )
return reply_fail ( " out of memory " ) ;
2013-10-09 16:53:58 +04:00
lock_vg ( s , vgid ) ;
vg_remove_if_missing ( s , vgid , 1 ) ;
unlock_vg ( s , vgid ) ;
dm_free ( vgid ) ;
}
2013-10-09 01:16:41 +04:00
2014-03-01 14:38:41 +04:00
if ( ! pvmeta )
2012-08-13 21:44:10 +04:00
return reply_unknown ( " PVID does not exist " ) ;
2014-03-01 14:38:41 +04:00
dm_config_destroy ( pvmeta ) ;
return daemon_reply_simple ( " OK " , NULL ) ;
2012-01-16 12:25:32 +04:00
}
2012-09-20 01:18:28 +04:00
static response pv_clear_all ( lvmetad_state * s , request r )
{
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " pv_clear_all " ) ;
2012-09-20 01:18:28 +04:00
lock_pvid_to_pvmeta ( s ) ;
lock_pvid_to_vgid ( s ) ;
2013-10-10 00:19:51 +04:00
lock_vgid_to_metadata ( s ) ;
2012-09-20 01:18:28 +04:00
destroy_metadata_hashes ( s ) ;
create_metadata_hashes ( s ) ;
unlock_pvid_to_vgid ( s ) ;
unlock_vgid_to_metadata ( s ) ;
unlock_pvid_to_pvmeta ( s ) ;
return daemon_reply_simple ( " OK " , NULL ) ;
}
2012-01-16 12:25:32 +04:00
static response pv_found ( lvmetad_state * s , request r )
2011-07-18 18:48:30 +04:00
{
2011-08-30 19:44:01 +04:00
struct dm_config_node * metadata = dm_config_find_node ( r . cft - > root , " metadata " ) ;
2012-02-15 21:30:07 +04:00
const char * pvid = daemon_request_str ( r , " pvmeta/id " , NULL ) ;
2012-01-16 12:25:32 +04:00
const char * vgname = daemon_request_str ( r , " vgname " , NULL ) ;
2011-07-20 19:14:17 +04:00
const char * vgid = daemon_request_str ( r , " metadata/id " , NULL ) ;
2014-01-08 17:54:26 +04:00
const char * vgid_old = NULL ;
2012-02-15 21:30:07 +04:00
struct dm_config_node * pvmeta = dm_config_find_node ( r . cft - > root , " pvmeta " ) ;
2014-02-28 13:59:12 +04:00
uint64_t device , device_old_pvid = 0 ;
2012-10-08 22:31:12 +04:00
struct dm_config_tree * cft , * pvmeta_old_dev = NULL , * pvmeta_old_pvid = NULL ;
2012-12-17 16:49:19 +04:00
char * old ;
char * pvid_dup ;
2014-03-14 11:09:37 +04:00
int complete = 0 , orphan = 0 ;
int64_t seqno = - 1 , seqno_old = - 1 , changed = 0 ;
2011-07-18 18:48:30 +04:00
if ( ! pvid )
2012-08-13 21:44:10 +04:00
return reply_fail ( " need PV UUID " ) ;
2012-02-15 21:30:07 +04:00
if ( ! pvmeta )
2012-08-13 21:44:10 +04:00
return reply_fail ( " need PV metadata " ) ;
2012-02-15 21:30:07 +04:00
if ( ! dm_config_get_uint64 ( pvmeta , " pvmeta/device " , & device ) )
2012-08-13 21:44:10 +04:00
return reply_fail ( " need PV device number " ) ;
2011-07-18 18:48:30 +04:00
2014-03-01 14:38:41 +04:00
if ( ! ( cft = dm_config_create ( ) ) | |
( ! ( pvid_dup = dm_strdup ( pvid ) ) ) ) {
if ( cft )
dm_config_destroy ( cft ) ;
return reply_fail ( " out of memory " ) ;
}
2012-02-15 21:37:09 +04:00
lock_pvid_to_pvmeta ( s ) ;
2012-02-23 21:59:32 +04:00
2014-03-14 13:06:27 +04:00
if ( ( pvmeta_old_pvid = dm_hash_lookup ( s - > pvid_to_pvmeta , pvid ) ) )
dm_config_get_uint64 ( pvmeta_old_pvid - > root , " pvmeta/device " , & device_old_pvid ) ;
2012-02-24 04:24:37 +04:00
if ( ( old = dm_hash_lookup_binary ( s - > device_to_pvid , & device , sizeof ( device ) ) ) ) {
2012-10-08 22:31:12 +04:00
pvmeta_old_dev = dm_hash_lookup ( s - > pvid_to_pvmeta , old ) ;
2012-02-23 21:59:32 +04:00
dm_hash_remove ( s - > pvid_to_pvmeta , old ) ;
2014-01-08 17:54:26 +04:00
vgid_old = dm_hash_lookup ( s - > pvid_to_vgid , old ) ;
2012-02-24 04:24:37 +04:00
}
2012-10-08 22:31:12 +04:00
2014-02-28 13:59:12 +04:00
DEBUGLOG ( s , " pv_found %s, vgid = %s, device = % " PRIu64 " (previously % " PRIu64 " ), old = %s " ,
pvid , vgid , device , device_old_pvid , old ) ;
2012-10-08 22:31:12 +04:00
2014-03-01 14:38:41 +04:00
if ( ! ( cft - > root = dm_config_clone_node ( cft , pvmeta , 0 ) ) )
goto out_of_mem ;
2012-03-02 02:52:59 +04:00
2014-03-14 13:06:27 +04:00
if ( ! pvmeta_old_pvid | | compare_config ( pvmeta_old_pvid - > root , cft - > root ) )
2014-03-14 06:04:09 +04:00
changed | = 1 ;
2014-02-28 13:59:12 +04:00
if ( pvmeta_old_pvid & & device ! = device_old_pvid ) {
DEBUGLOG ( s , " pv %s no longer on device % " PRIu64 , pvid , device_old_pvid ) ;
2014-03-01 14:36:05 +04:00
dm_free ( dm_hash_lookup_binary ( s - > device_to_pvid , & device_old_pvid , sizeof ( device_old_pvid ) ) ) ;
2014-02-28 13:59:12 +04:00
dm_hash_remove_binary ( s - > device_to_pvid , & device_old_pvid , sizeof ( device_old_pvid ) ) ;
2014-03-14 06:04:09 +04:00
changed | = 1 ;
2014-02-28 13:59:12 +04:00
}
2012-03-02 02:52:59 +04:00
if ( ! dm_hash_insert ( s - > pvid_to_pvmeta , pvid , cft ) | |
! dm_hash_insert_binary ( s - > device_to_pvid , & device , sizeof ( device ) , ( void * ) pvid_dup ) ) {
2012-12-14 19:43:42 +04:00
dm_hash_remove ( s - > pvid_to_pvmeta , pvid ) ;
2014-03-01 14:38:41 +04:00
out_of_mem :
2014-01-07 06:04:14 +04:00
unlock_pvid_to_pvmeta ( s ) ;
2012-12-14 19:43:42 +04:00
dm_config_destroy ( cft ) ;
2012-12-17 16:49:19 +04:00
dm_free ( pvid_dup ) ;
2014-03-01 14:38:41 +04:00
dm_free ( old ) ;
2012-08-13 21:44:10 +04:00
return reply_fail ( " out of memory " ) ;
2012-03-02 02:52:59 +04:00
}
2014-02-28 13:59:12 +04:00
2014-03-01 14:38:41 +04:00
unlock_pvid_to_pvmeta ( s ) ;
dm_free ( old ) ;
2012-10-08 22:31:12 +04:00
if ( pvmeta_old_pvid )
dm_config_destroy ( pvmeta_old_pvid ) ;
if ( pvmeta_old_dev & & pvmeta_old_dev ! = pvmeta_old_pvid )
dm_config_destroy ( pvmeta_old_dev ) ;
2012-02-23 21:59:32 +04:00
2011-07-18 18:48:30 +04:00
if ( metadata ) {
if ( ! vgid )
2012-08-13 21:44:10 +04:00
return reply_fail ( " need VG UUID " ) ;
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " obtained vgid = %s, vgname = %s " , vgid , vgname ) ;
2012-01-16 12:25:32 +04:00
if ( ! vgname )
2012-08-13 21:44:10 +04:00
return reply_fail ( " need VG name " ) ;
2011-07-19 20:48:13 +04:00
if ( daemon_request_int ( r , " metadata/seqno " , - 1 ) < 0 )
2012-08-13 21:44:10 +04:00
return reply_fail ( " need VG seqno " ) ;
2011-07-19 18:13:59 +04:00
2012-09-20 01:45:51 +04:00
if ( ! update_metadata ( s , vgname , vgid , metadata , & seqno_old ) )
2012-08-13 21:44:10 +04:00
return reply_fail ( " metadata update failed " ) ;
2014-03-14 06:04:09 +04:00
changed | = ( seqno_old ! = dm_config_find_int ( metadata , " metadata/seqno " , - 1 ) ) ;
2011-07-21 01:23:43 +04:00
} else {
2012-02-15 15:43:06 +04:00
lock_pvid_to_vgid ( s ) ;
vgid = dm_hash_lookup ( s - > pvid_to_vgid , pvid ) ;
unlock_pvid_to_vgid ( s ) ;
2011-07-21 01:23:43 +04:00
}
if ( vgid ) {
2012-09-20 01:45:51 +04:00
if ( ( cft = lock_vg ( s , vgid ) ) ) {
2012-02-15 15:43:06 +04:00
complete = update_pv_status ( s , cft , cft - > root , 0 ) ;
2012-09-20 01:45:51 +04:00
seqno = dm_config_find_int ( cft - > root , " metadata/seqno " , - 1 ) ;
} else if ( ! strcmp ( vgid , " #orphan " ) )
2012-02-15 15:43:06 +04:00
orphan = 1 ;
2012-02-23 21:59:32 +04:00
else {
2012-01-16 12:25:32 +04:00
unlock_vg ( s , vgid ) ;
2012-08-13 21:44:10 +04:00
return reply_fail ( " non-orphan VG without metadata encountered " ) ;
2012-01-16 12:25:32 +04:00
}
2011-07-21 01:23:43 +04:00
unlock_vg ( s , vgid ) ;
2014-03-14 05:40:20 +04:00
// TODO: separate vgid->vgname lock
lock_vgid_to_metadata ( s ) ;
vgname = dm_hash_lookup ( s - > vgid_to_vgname , vgid ) ;
unlock_vgid_to_metadata ( s ) ;
2011-07-21 01:23:43 +04:00
}
2011-07-18 18:48:30 +04:00
2014-01-08 17:54:26 +04:00
if ( vgid_old & & ( ! vgid | | strcmp ( vgid , vgid_old ) ) ) {
2014-03-01 03:42:09 +04:00
/* make a copy, because vg_remove_if_missing will deallocate the
* storage behind vgid_old */
vgid_old = dm_strdup ( vgid_old ) ;
2014-01-08 17:54:26 +04:00
lock_vg ( s , vgid_old ) ;
vg_remove_if_missing ( s , vgid_old , 1 ) ;
unlock_vg ( s , vgid_old ) ;
2014-03-03 01:26:21 +04:00
dm_free ( ( char * ) vgid_old ) ;
2014-01-08 17:54:26 +04:00
}
2011-07-20 19:14:17 +04:00
return daemon_reply_simple ( " OK " ,
2012-02-15 15:43:06 +04:00
" status = %s " , orphan ? " orphan " :
( complete ? " complete " : " partial " ) ,
2014-03-14 06:04:09 +04:00
" changed = %d " , changed ,
2011-07-20 20:46:40 +04:00
" vgid = %s " , vgid ? vgid : " #orphan " ,
2014-03-14 05:40:20 +04:00
" vgname = %s " , vgname ? vgname : " #orphan " ,
2012-09-20 01:45:51 +04:00
" seqno_before = % " PRId64 , seqno_old ,
" seqno_after = % " PRId64 , seqno ,
2011-07-20 19:14:17 +04:00
NULL ) ;
2011-07-18 18:48:30 +04:00
}
2012-01-16 12:25:32 +04:00
static response vg_update ( lvmetad_state * s , request r )
{
struct dm_config_node * metadata = dm_config_find_node ( r . cft - > root , " metadata " ) ;
const char * vgid = daemon_request_str ( r , " metadata/id " , NULL ) ;
const char * vgname = daemon_request_str ( r , " vgname " , NULL ) ;
if ( metadata ) {
if ( ! vgid )
2012-08-13 21:44:10 +04:00
return reply_fail ( " need VG UUID " ) ;
2012-01-16 12:25:32 +04:00
if ( ! vgname )
2012-08-13 21:44:10 +04:00
return reply_fail ( " need VG name " ) ;
2012-01-16 12:25:32 +04:00
if ( daemon_request_int ( r , " metadata/seqno " , - 1 ) < 0 )
2012-08-13 21:44:10 +04:00
return reply_fail ( " need VG seqno " ) ;
2012-01-16 12:25:32 +04:00
2012-02-15 15:43:06 +04:00
/* TODO defer metadata update here; add a separate vg_commit
* call ; if client does not commit , die */
2012-09-20 01:45:51 +04:00
if ( ! update_metadata ( s , vgname , vgid , metadata , NULL ) )
2012-08-13 21:44:10 +04:00
return reply_fail ( " metadata update failed " ) ;
2012-01-16 12:25:32 +04:00
}
return daemon_reply_simple ( " OK " , NULL ) ;
}
static response vg_remove ( lvmetad_state * s , request r )
2011-07-18 18:48:30 +04:00
{
2012-01-16 12:25:32 +04:00
const char * vgid = daemon_request_str ( r , " uuid " , NULL ) ;
if ( ! vgid )
2012-08-13 21:44:10 +04:00
return reply_fail ( " need VG UUID " ) ;
2012-01-16 12:25:32 +04:00
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " vg_remove: %s " , vgid ) ;
2012-01-16 12:25:32 +04:00
2012-02-15 15:43:06 +04:00
lock_pvid_to_vgid ( s ) ;
2012-02-15 18:06:48 +04:00
remove_metadata ( s , vgid , 1 ) ;
2012-02-15 15:43:06 +04:00
unlock_pvid_to_vgid ( s ) ;
2012-01-16 12:25:32 +04:00
return daemon_reply_simple ( " OK " , NULL ) ;
2011-07-18 18:48:30 +04:00
}
2012-10-11 22:31:29 +04:00
static void _dump_cft ( struct buffer * buf , struct dm_hash_table * ht , const char * key_addr )
{
2014-03-26 16:59:13 +04:00
struct dm_hash_node * n ;
dm_hash_iterate ( n , ht ) {
2012-10-11 22:31:29 +04:00
struct dm_config_tree * cft = dm_hash_get_data ( ht , n ) ;
const char * key_backup = cft - > root - > key ;
cft - > root - > key = dm_config_find_str ( cft - > root , key_addr , " unknown " ) ;
2012-12-13 22:55:33 +04:00
( void ) dm_config_write_node ( cft - > root , buffer_line , buf ) ;
2012-10-11 22:31:29 +04:00
cft - > root - > key = key_backup ;
}
}
static void _dump_pairs ( struct buffer * buf , struct dm_hash_table * ht , const char * name , int int_key )
{
char * append ;
2014-03-26 16:59:13 +04:00
struct dm_hash_node * n ;
2012-10-11 22:31:29 +04:00
buffer_append ( buf , name ) ;
buffer_append ( buf , " { \n " ) ;
2014-03-26 16:59:13 +04:00
dm_hash_iterate ( n , ht ) {
2012-10-11 22:31:29 +04:00
const char * key = dm_hash_get_key ( ht , n ) ,
* val = dm_hash_get_data ( ht , n ) ;
buffer_append ( buf , " " ) ;
if ( int_key )
2014-11-08 17:33:17 +03:00
( void ) dm_asprintf ( & append , " %d = \" %s \" " , * ( const int * ) key , val ) ;
2012-10-11 22:31:29 +04:00
else
2012-12-13 22:55:33 +04:00
( void ) dm_asprintf ( & append , " %s = \" %s \" " , key , val ) ;
2012-10-11 22:31:29 +04:00
if ( append )
buffer_append ( buf , append ) ;
buffer_append ( buf , " \n " ) ;
dm_free ( append ) ;
}
buffer_append ( buf , " } \n " ) ;
}
static response dump ( lvmetad_state * s )
{
2012-12-14 19:38:07 +04:00
response res = { 0 } ;
2012-10-11 22:31:29 +04:00
struct buffer * b = & res . buffer ;
buffer_init ( b ) ;
/* Lock everything so that we get a consistent dump. */
lock_vgid_to_metadata ( s ) ;
lock_pvid_to_pvmeta ( s ) ;
lock_pvid_to_vgid ( s ) ;
buffer_append ( b , " # VG METADATA \n \n " ) ;
_dump_cft ( b , s - > vgid_to_metadata , " metadata/id " ) ;
buffer_append ( b , " \n # PV METADATA \n \n " ) ;
_dump_cft ( b , s - > pvid_to_pvmeta , " pvmeta/id " ) ;
buffer_append ( b , " \n # VGID to VGNAME mapping \n \n " ) ;
_dump_pairs ( b , s - > vgid_to_vgname , " vgid_to_vgname " , 0 ) ;
buffer_append ( b , " \n # VGNAME to VGID mapping \n \n " ) ;
_dump_pairs ( b , s - > vgname_to_vgid , " vgname_to_vgid " , 0 ) ;
buffer_append ( b , " \n # PVID to VGID mapping \n \n " ) ;
_dump_pairs ( b , s - > pvid_to_vgid , " pvid_to_vgid " , 0 ) ;
buffer_append ( b , " \n # DEVICE to PVID mapping \n \n " ) ;
_dump_pairs ( b , s - > device_to_pvid , " device_to_pvid " , 1 ) ;
unlock_pvid_to_vgid ( s ) ;
unlock_pvid_to_pvmeta ( s ) ;
unlock_vgid_to_metadata ( s ) ;
return res ;
}
2011-06-14 06:36:38 +04:00
static response handler ( daemon_state s , client_handle h , request r )
{
2011-07-18 18:48:30 +04:00
lvmetad_state * state = s . private ;
const char * rq = daemon_request_str ( r , " request " , " NONE " ) ;
2012-08-13 21:44:10 +04:00
const char * token = daemon_request_str ( r , " token " , " NONE " ) ;
pthread_mutex_lock ( & state - > token_lock ) ;
if ( ! strcmp ( rq , " token_update " ) ) {
strncpy ( state - > token , token , 128 ) ;
state - > token [ 127 ] = 0 ;
pthread_mutex_unlock ( & state - > token_lock ) ;
return daemon_reply_simple ( " OK " , NULL ) ;
}
2012-10-11 22:31:29 +04:00
if ( strcmp ( token , state - > token ) & & strcmp ( rq , " dump " ) ) {
2012-08-13 21:44:10 +04:00
pthread_mutex_unlock ( & state - > token_lock ) ;
return daemon_reply_simple ( " token_mismatch " ,
" expected = %s " , state - > token ,
" received = %s " , token ,
2014-02-26 18:10:21 +04:00
" reason = %s " ,
" lvmetad cache is invalid due to a global_filter change or due to a running rescan " , NULL ) ;
2012-08-13 21:44:10 +04:00
}
pthread_mutex_unlock ( & state - > token_lock ) ;
2011-07-18 18:48:30 +04:00
2012-02-15 15:43:06 +04:00
/*
* TODO Add a stats call , with transaction count / rate , time since last
* update & c .
*/
2012-01-16 12:25:32 +04:00
if ( ! strcmp ( rq , " pv_found " ) )
return pv_found ( state , r ) ;
2011-07-18 18:48:30 +04:00
2012-01-16 12:25:32 +04:00
if ( ! strcmp ( rq , " pv_gone " ) )
2012-02-15 15:43:06 +04:00
return pv_gone ( state , r ) ;
2012-09-20 01:18:28 +04:00
if ( ! strcmp ( rq , " pv_clear_all " ) )
return pv_clear_all ( state , r ) ;
2012-02-15 15:43:06 +04:00
if ( ! strcmp ( rq , " pv_lookup " ) )
return pv_lookup ( state , r ) ;
2012-01-16 12:25:32 +04:00
if ( ! strcmp ( rq , " vg_update " ) )
return vg_update ( state , r ) ;
if ( ! strcmp ( rq , " vg_remove " ) )
return vg_remove ( state , r ) ;
if ( ! strcmp ( rq , " vg_lookup " ) )
return vg_lookup ( state , r ) ;
2012-10-15 05:06:27 +04:00
if ( ! strcmp ( rq , " pv_list " ) )
2012-02-15 15:43:06 +04:00
return pv_list ( state , r ) ;
if ( ! strcmp ( rq , " vg_list " ) )
return vg_list ( state , r ) ;
2012-10-11 22:31:29 +04:00
if ( ! strcmp ( rq , " dump " ) )
return dump ( state ) ;
2012-08-13 21:44:10 +04:00
return reply_fail ( " request not implemented " ) ;
2011-06-14 06:36:38 +04:00
}
2011-07-18 18:48:30 +04:00
static int init ( daemon_state * s )
2011-06-14 06:36:38 +04:00
{
2011-09-17 17:33:51 +04:00
pthread_mutexattr_t rec ;
2011-06-14 06:36:38 +04:00
lvmetad_state * ls = s - > private ;
2012-08-08 11:41:01 +04:00
ls - > log = s - > log ;
2011-06-14 06:36:38 +04:00
2011-07-21 01:23:43 +04:00
pthread_mutexattr_init ( & rec ) ;
pthread_mutexattr_settype ( & rec , PTHREAD_MUTEX_RECURSIVE_NP ) ;
2012-02-15 21:37:09 +04:00
pthread_mutex_init ( & ls - > lock . pvid_to_pvmeta , & rec ) ;
2012-02-15 15:43:06 +04:00
pthread_mutex_init ( & ls - > lock . vgid_to_metadata , & rec ) ;
pthread_mutex_init ( & ls - > lock . pvid_to_vgid , NULL ) ;
2012-10-08 11:12:51 +04:00
pthread_mutex_init ( & ls - > lock . vg_lock_map , NULL ) ;
2012-08-13 21:44:10 +04:00
pthread_mutex_init ( & ls - > token_lock , NULL ) ;
2012-09-20 01:18:28 +04:00
create_metadata_hashes ( ls ) ;
ls - > lock . vg = dm_hash_create ( 32 ) ;
ls - > token [ 0 ] = 0 ;
2011-07-21 01:23:43 +04:00
2012-10-15 13:44:43 +04:00
/* Set up stderr logging depending on the -l option. */
if ( ! daemon_log_parse ( ls - > log , DAEMON_LOG_OUTLET_STDERR , ls - > log_config , 1 ) )
2012-10-15 05:06:27 +04:00
return 0 ;
2012-08-08 11:41:01 +04:00
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " initialised state: vgid_to_metadata = %p " , ls - > vgid_to_metadata ) ;
2012-02-15 15:43:06 +04:00
if ( ! ls - > pvid_to_vgid | | ! ls - > vgid_to_metadata )
2011-07-18 18:48:30 +04:00
return 0 ;
2011-06-14 06:36:38 +04:00
/* if (ls->initial_registrations)
_process_initial_registrations ( ds - > initial_registrations ) ; */
return 1 ;
}
2011-07-18 18:48:30 +04:00
static int fini ( daemon_state * s )
{
lvmetad_state * ls = s - > private ;
2012-10-13 21:17:10 +04:00
struct dm_hash_node * n ;
2011-09-17 17:33:51 +04:00
2012-10-15 17:20:11 +04:00
DEBUGLOG ( s , " fini " ) ;
2011-07-25 19:33:04 +04:00
2012-09-20 01:18:28 +04:00
destroy_metadata_hashes ( ls ) ;
2012-02-24 04:11:59 +04:00
2012-09-20 01:18:28 +04:00
/* Destroy the lock hashes now. */
2014-03-26 16:59:13 +04:00
dm_hash_iterate ( n , ls - > lock . vg ) {
2011-07-25 19:33:04 +04:00
pthread_mutex_destroy ( dm_hash_get_data ( ls - > lock . vg , n ) ) ;
free ( dm_hash_get_data ( ls - > lock . vg , n ) ) ;
}
dm_hash_destroy ( ls - > lock . vg ) ;
2011-07-18 18:48:30 +04:00
return 1 ;
}
2014-02-22 23:28:30 +04:00
static void usage ( const char * prog , FILE * file )
2011-06-14 06:36:38 +04:00
{
fprintf ( file , " Usage: \n "
2012-10-15 13:44:43 +04:00
" %s [-V] [-h] [-f] [-l {all|wire|debug}] [-s path] \n \n "
2011-06-14 06:36:38 +04:00
" -V Show version of lvmetad \n "
" -h Show this help information \n "
2012-10-15 05:06:27 +04:00
" -f Don't fork, run in the foreground \n "
2012-10-15 13:44:43 +04:00
" -l Logging message level (-l {all|wire|debug}) \n "
2013-11-30 00:56:29 +04:00
" -p Set path to the pidfile \n "
2012-10-15 05:06:27 +04:00
" -s Set path to the socket to listen on \n \n " , prog ) ;
2011-06-14 06:36:38 +04:00
}
int main ( int argc , char * argv [ ] )
{
signed char opt ;
2014-02-22 23:28:30 +04:00
lvmetad_state ls = { . log_config = " " } ;
2013-04-19 22:52:38 +04:00
daemon_state s = {
. daemon_fini = fini ,
. daemon_init = init ,
. handler = handler ,
. name = " lvmetad " ,
2014-02-22 23:28:30 +04:00
. pidfile = getenv ( " LVM_LVMETAD_PIDFILE " ) ? : LVMETAD_PIDFILE ,
2013-04-19 22:52:38 +04:00
. private = & ls ,
. protocol = " lvmetad " ,
. protocol_version = 1 ,
2014-02-22 23:28:30 +04:00
. socket_path = getenv ( " LVM_LVMETAD_SOCKET " ) ? : LVMETAD_SOCKET ,
2013-04-19 22:52:38 +04:00
} ;
2011-06-14 06:36:38 +04:00
2012-02-15 15:43:06 +04:00
// use getopt_long
2013-11-30 00:56:29 +04:00
while ( ( opt = getopt ( argc , argv , " ?fhVl:p:s: " ) ) ! = EOF ) {
2011-06-14 06:36:38 +04:00
switch ( opt ) {
case ' h ' :
usage ( argv [ 0 ] , stdout ) ;
exit ( 0 ) ;
case ' ? ' :
usage ( argv [ 0 ] , stderr ) ;
exit ( 0 ) ;
case ' f ' :
s . foreground = 1 ;
break ;
2012-10-15 13:44:43 +04:00
case ' l ' :
ls . log_config = optarg ;
2011-06-14 06:36:38 +04:00
break ;
2013-11-30 00:56:29 +04:00
case ' p ' :
s . pidfile = optarg ;
break ;
2012-02-15 15:43:06 +04:00
case ' s ' : // --socket
2012-01-16 12:25:32 +04:00
s . socket_path = optarg ;
break ;
2011-06-14 06:36:38 +04:00
case ' V ' :
2012-10-15 05:06:27 +04:00
printf ( " lvmetad version: " LVM_VERSION " \n " ) ;
2011-06-14 06:36:38 +04:00
exit ( 1 ) ;
}
}
daemon_start ( s ) ;
2014-02-22 23:28:30 +04:00
2011-06-14 06:36:38 +04:00
return 0 ;
}