2011-07-18 14:48:30 +00:00
# include <assert.h>
2011-07-20 21:23:43 +00:00
# include <pthread.h>
2011-07-18 14:48:30 +00:00
# include "libdevmapper.h"
# include <malloc.h>
# include <stdint.h>
2011-06-14 02:36:38 +00:00
# include "../common/daemon-server.h"
typedef struct {
2011-07-19 14:13:59 +00:00
struct dm_hash_table * pvs ;
2011-07-18 14:48:30 +00:00
struct dm_hash_table * vgs ;
2011-07-20 18:34:57 +00:00
struct dm_hash_table * pvid_map ;
2011-07-20 21:23:43 +00:00
struct {
struct dm_hash_table * vg ;
pthread_mutex_t pvs ;
pthread_mutex_t vgs ;
pthread_mutex_t pvid_map ;
} lock ;
2011-06-14 02:36:38 +00:00
} lvmetad_state ;
2011-07-20 21:23:43 +00:00
void debug ( const char * fmt , . . . ) {
va_list ap ;
va_start ( ap , fmt ) ;
fprintf ( stderr , " [D %u] " , pthread_self ( ) ) ;
vfprintf ( stderr , fmt , ap ) ;
va_end ( ap ) ;
} ;
void lock_pvs ( lvmetad_state * s ) { pthread_mutex_lock ( & s - > lock . pvs ) ; }
void unlock_pvs ( lvmetad_state * s ) { pthread_mutex_unlock ( & s - > lock . pvs ) ; }
void lock_vgs ( lvmetad_state * s ) { pthread_mutex_lock ( & s - > lock . vgs ) ; }
void unlock_vgs ( lvmetad_state * s ) { pthread_mutex_unlock ( & s - > lock . vgs ) ; }
void lock_pvid_map ( lvmetad_state * s ) { pthread_mutex_lock ( & s - > lock . pvid_map ) ; }
void unlock_pvid_map ( lvmetad_state * s ) { pthread_mutex_unlock ( & s - > lock . pvid_map ) ; }
2011-07-25 15:33:04 +00: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-07-20 21:23:43 +00:00
struct config_tree * lock_vg ( lvmetad_state * s , const char * id ) {
lock_vgs ( s ) ;
pthread_mutex_t * vg = dm_hash_lookup ( s - > lock . vg , id ) ;
if ( ! vg ) {
pthread_mutexattr_t rec ;
pthread_mutexattr_init ( & rec ) ;
pthread_mutexattr_settype ( & rec , PTHREAD_MUTEX_RECURSIVE_NP ) ;
vg = malloc ( sizeof ( pthread_mutex_t ) ) ;
pthread_mutex_init ( vg , & rec ) ;
dm_hash_insert ( s - > lock . vg , id , vg ) ;
}
pthread_mutex_lock ( vg ) ;
struct config_tree * cft = dm_hash_lookup ( s - > vgs , id ) ;
unlock_vgs ( s ) ;
return cft ;
}
void unlock_vg ( lvmetad_state * s , const char * id ) {
lock_vgs ( s ) ; /* someone might be changing the s->lock.vg structure right
* now , so avoid stepping on each other ' s toes */
pthread_mutex_unlock ( dm_hash_lookup ( s - > lock . vg , id ) ) ;
unlock_vgs ( s ) ;
}
2011-07-25 17:59:50 +00:00
static struct config_node * pvs ( struct config_node * vg )
{
struct config_node * pv = find_config_node ( vg , " metadata/physical_volumes " ) ;
if ( pv )
pv = pv - > child ;
return pv ;
}
/*
* TODO : This set_flag function is pretty generic and might make sense in a
* library here or there .
*/
static void set_flag ( struct config_tree * cft , struct config_node * parent ,
char * field , const char * flag , int want ) {
struct config_value * value = NULL , * pred = NULL ;
struct config_node * node = find_config_node ( parent - > child , field ) ;
int found = 0 ;
if ( node )
value = node - > v ;
while ( value & & value - > type ! = CFG_EMPTY_ARRAY & & strcmp ( value - > v . str , flag ) ) {
pred = value ;
value = value - > next ;
}
if ( value & & want )
return ;
if ( ! value & & ! want )
return ;
if ( value & & ! want ) {
if ( pred )
pred - > next = value - > next ;
if ( value = = node - > v )
node - > v = value - > next ;
}
if ( ! value & & want ) {
if ( ! node ) {
node = create_config_node ( cft , field ) ;
node - > sib = parent - > child ;
node - > v = create_config_value ( cft ) ;
node - > v - > type = CFG_EMPTY_ARRAY ;
node - > parent = parent ;
parent - > child = node ;
}
struct config_value * new = create_config_value ( cft ) ;
new - > type = CFG_STRING ;
new - > v . str = flag ;
new - > next = node - > v ;
node - > v = new ;
}
}
/* Either the "big" vgs lock, or a per-vg lock needs to be held before entering
* this function . */
static int update_pv_status ( lvmetad_state * s , struct config_tree * cft , struct config_node * vg , int act )
{
int complete = 1 ;
lock_pvs ( s ) ;
struct config_node * pv = pvs ( vg ) ;
while ( pv ) {
const char * uuid = find_config_str ( pv - > child , " id " , NULL ) ;
int found = uuid ? ( dm_hash_lookup ( s - > pvs , uuid ) ? 1 : 0 ) : 0 ;
if ( act )
set_flag ( cft , pv , " status " , " MISSING " , ! found ) ;
if ( ! found ) {
complete = 0 ;
if ( ! act ) { // optimisation
unlock_pvs ( s ) ;
return complete ;
}
}
pv = pv - > sib ;
}
unlock_pvs ( s ) ;
return complete ;
}
2011-07-18 14:48:30 +00:00
static response vg_by_uuid ( lvmetad_state * s , request r )
{
const char * uuid = daemon_request_str ( r , " uuid " , " NONE " ) ;
2011-07-20 21:23:43 +00:00
debug ( " vg_by_uuid: %s (vgs = %p) \n " , uuid , s - > vgs ) ;
struct config_tree * cft = lock_vg ( s , uuid ) ;
if ( ! cft | | ! cft - > root ) {
unlock_vg ( s , uuid ) ;
2011-07-18 14:48:30 +00:00
return daemon_reply_simple ( " failed " , " reason = %s " , " uuid not found " , NULL ) ;
2011-07-20 21:23:43 +00:00
}
2011-07-19 16:48:13 +00:00
struct config_node * metadata = cft - > root ;
2011-07-18 14:48:30 +00:00
response res = { . buffer = NULL } ;
struct config_node * n ;
res . cft = create_config_tree ( NULL , 0 ) ;
/* The response field */
res . cft - > root = n = create_config_node ( res . cft , " response " ) ;
n - > v - > type = CFG_STRING ;
n - > v - > v . str = " OK " ;
/* The metadata section */
2011-07-19 14:13:59 +00:00
n = n - > sib = clone_config_node ( res . cft , metadata , 1 ) ;
2011-07-18 14:48:30 +00:00
n - > parent = res . cft - > root ;
res . error = 0 ;
2011-07-20 21:23:43 +00:00
unlock_vg ( s , uuid ) ;
2011-07-25 17:59:50 +00:00
update_pv_status ( s , cft , n , 1 ) ;
2011-07-18 14:48:30 +00:00
return res ;
}
2011-07-25 15:51:51 +00:00
static int compare_value ( struct config_value * a , struct config_value * b )
{
if ( a - > type > b - > type )
return 1 ;
if ( a - > type < b - > type )
return - 1 ;
switch ( a - > type ) {
case CFG_STRING : return strcmp ( a - > v . str , b - > v . str ) ;
case CFG_FLOAT : return a - > v . r = = b - > v . r ;
case CFG_INT : return a - > v . i = = b - > v . i ;
case CFG_EMPTY_ARRAY : return 0 ;
}
if ( a - > next & & b - > next )
return compare_value ( a - > next , b - > next ) ;
}
static int compare_config ( struct config_node * a , struct config_node * b )
{
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 ) ;
if ( result )
return result ;
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 ;
}
2011-07-20 21:23:43 +00:00
/* You need to be holding the pvid_map lock already to call this. */
2011-07-20 18:45:32 +00:00
int update_pvid_map ( lvmetad_state * s , struct config_tree * vg , const char * vgid )
2011-07-20 16:46:40 +00:00
{
2011-07-20 18:34:57 +00:00
struct config_node * pv = pvs ( vg ) ;
2011-07-20 16:46:40 +00:00
2011-07-20 18:34:57 +00:00
if ( ! vgid )
return 0 ;
2011-07-20 16:46:40 +00:00
2011-07-20 18:34:57 +00:00
while ( pv ) {
char * pvid = find_config_str ( pv - > child , " id " , NULL ) ;
dm_hash_insert ( s - > pvid_map , pvid , vgid ) ;
pv = pv - > sib ;
2011-07-20 16:46:40 +00:00
}
2011-07-20 18:34:57 +00:00
return 1 ;
2011-07-20 16:46:40 +00:00
}
2011-07-20 21:23:43 +00: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 ) . */
2011-07-20 18:45:32 +00:00
static int update_metadata ( lvmetad_state * s , const char * _vgid , struct config_node * metadata )
2011-07-19 14:13:59 +00:00
{
2011-07-20 21:23:43 +00:00
int retval = 0 ;
lock_vgs ( s ) ;
2011-07-20 18:45:32 +00:00
struct config_tree * old = dm_hash_lookup ( s - > vgs , _vgid ) ;
2011-07-20 21:23:43 +00:00
lock_vg ( s , _vgid ) ;
unlock_vgs ( s ) ;
2011-07-19 16:48:13 +00:00
int seq = find_config_int ( metadata , " metadata/seqno " , - 1 ) ;
int haveseq = - 1 ;
if ( old )
haveseq = find_config_int ( old - > root , " metadata/seqno " , - 1 ) ;
if ( seq < 0 )
2011-07-20 21:23:43 +00:00
goto out ;
2011-07-19 16:48:13 +00:00
if ( seq = = haveseq ) {
2011-07-20 21:23:43 +00:00
retval = 1 ;
2011-07-25 15:51:51 +00:00
if ( compare_config ( metadata , old - > root ) )
retval = 0 ;
2011-07-20 21:23:43 +00:00
goto out ;
2011-07-19 16:48:13 +00:00
}
if ( seq < haveseq ) {
// TODO: we may want to notify the client that their metadata is
// out of date?
2011-07-20 21:23:43 +00:00
retval = 1 ;
goto out ;
2011-07-19 16:48:13 +00:00
}
2011-07-20 18:45:32 +00:00
struct config_tree * cft = create_config_tree ( NULL , 0 ) ;
cft - > root = clone_config_node ( cft , metadata , 0 ) ;
const char * vgid = find_config_str ( cft - > root , " metadata/id " , NULL ) ;
if ( ! vgid )
2011-07-20 21:23:43 +00:00
goto out ;
lock_pvid_map ( s ) ;
2011-07-20 18:45:32 +00:00
2011-07-19 16:48:13 +00:00
if ( haveseq > = 0 & & haveseq < seq ) {
2011-07-20 18:45:32 +00:00
/* temporarily orphan all of our PVs */
update_pvid_map ( s , old , " #orphan " ) ;
2011-07-19 16:48:13 +00:00
/* need to update what we have since we found a newer version */
destroy_config_tree ( old ) ;
dm_hash_remove ( s - > vgs , vgid ) ;
}
2011-07-20 21:23:43 +00:00
lock_vgs ( s ) ;
2011-07-19 16:48:13 +00:00
dm_hash_insert ( s - > vgs , vgid , cft ) ;
2011-07-20 21:23:43 +00:00
unlock_vgs ( s ) ;
2011-07-20 18:45:32 +00:00
update_pvid_map ( s , cft , vgid ) ;
2011-07-19 16:48:13 +00:00
2011-07-20 21:23:43 +00:00
unlock_pvid_map ( s ) ;
retval = 1 ;
out :
unlock_vg ( s , _vgid ) ;
return retval ;
2011-07-19 14:13:59 +00:00
}
2011-07-18 14:48:30 +00:00
static response pv_add ( lvmetad_state * s , request r )
{
2011-07-19 14:13:59 +00:00
struct config_node * metadata = find_config_node ( r . cft - > root , " metadata " ) ;
2011-07-18 14:48:30 +00:00
const char * pvid = daemon_request_str ( r , " uuid " , NULL ) ;
2011-07-20 15:14:17 +00:00
const char * vgid = daemon_request_str ( r , " metadata/id " , NULL ) ;
2011-07-18 14:48:30 +00:00
if ( ! pvid )
return daemon_reply_simple ( " failed " , " reason = %s " , " need PV UUID " , NULL ) ;
2011-07-20 21:33:41 +00:00
debug ( " pv_add %s, vgid = %s \n " , pvid , vgid ) ;
2011-07-20 21:23:43 +00:00
lock_pvs ( s ) ;
2011-07-20 18:34:57 +00:00
dm_hash_insert ( s - > pvs , pvid , ( void * ) 1 ) ;
2011-07-20 21:23:43 +00:00
unlock_pvs ( s ) ;
2011-07-19 14:13:59 +00:00
2011-07-18 14:48:30 +00:00
if ( metadata ) {
if ( ! vgid )
return daemon_reply_simple ( " failed " , " reason = %s " , " need VG UUID " , NULL ) ;
2011-07-19 16:48:13 +00:00
if ( daemon_request_int ( r , " metadata/seqno " , - 1 ) < 0 )
return daemon_reply_simple ( " failed " , " reason = %s " , " need VG seqno " , NULL ) ;
2011-07-19 14:13:59 +00:00
2011-07-19 16:48:13 +00:00
if ( ! update_metadata ( s , vgid , metadata ) )
return daemon_reply_simple ( " failed " , " reason = %s " ,
" metadata update failed " , NULL ) ;
2011-07-20 21:23:43 +00:00
} else {
lock_pvid_map ( s ) ;
2011-07-20 18:34:57 +00:00
vgid = dm_hash_lookup ( s - > pvid_map , pvid ) ;
2011-07-20 21:23:43 +00:00
unlock_pvid_map ( s ) ;
}
2011-07-20 21:33:41 +00:00
int complete = 0 ;
2011-07-20 21:23:43 +00:00
if ( vgid ) {
struct config_tree * cft = lock_vg ( s , vgid ) ;
2011-07-25 17:59:50 +00:00
complete = update_pv_status ( s , cft , cft - > root , 0 ) ;
2011-07-20 21:23:43 +00:00
unlock_vg ( s , vgid ) ;
}
2011-07-18 14:48:30 +00:00
2011-07-20 15:14:17 +00:00
return daemon_reply_simple ( " OK " ,
" status = %s " , complete ? " complete " : " partial " ,
2011-07-20 16:46:40 +00:00
" vgid = %s " , vgid ? vgid : " #orphan " ,
2011-07-20 15:14:17 +00:00
NULL ) ;
2011-07-18 14:48:30 +00:00
}
static void pv_del ( lvmetad_state * s , request r )
{
}
2011-06-14 02:36:38 +00:00
static response handler ( daemon_state s , client_handle h , request r )
{
2011-07-18 14:48:30 +00:00
lvmetad_state * state = s . private ;
const char * rq = daemon_request_str ( r , " request " , " NONE " ) ;
2011-07-20 21:23:43 +00:00
debug ( " REQUEST: %s \n " , rq ) ;
2011-07-18 14:48:30 +00:00
if ( ! strcmp ( rq , " pv_add " ) )
return pv_add ( state , r ) ;
else if ( ! strcmp ( rq , " pv_del " ) )
pv_del ( state , r ) ;
else if ( ! strcmp ( rq , " vg_by_uuid " ) )
return vg_by_uuid ( state , r ) ;
return daemon_reply_simple ( " OK " , NULL ) ;
2011-06-14 02:36:38 +00:00
}
2011-07-18 14:48:30 +00:00
static int init ( daemon_state * s )
2011-06-14 02:36:38 +00:00
{
lvmetad_state * ls = s - > private ;
2011-07-19 14:13:59 +00:00
ls - > pvs = dm_hash_create ( 32 ) ;
2011-07-18 14:48:30 +00:00
ls - > vgs = dm_hash_create ( 32 ) ;
2011-07-20 18:34:57 +00:00
ls - > pvid_map = dm_hash_create ( 32 ) ;
2011-07-20 21:23:43 +00:00
ls - > lock . vg = dm_hash_create ( 32 ) ;
pthread_mutexattr_t rec ;
pthread_mutexattr_init ( & rec ) ;
pthread_mutexattr_settype ( & rec , PTHREAD_MUTEX_RECURSIVE_NP ) ;
pthread_mutex_init ( & ls - > lock . pvs , NULL ) ;
pthread_mutex_init ( & ls - > lock . vgs , & rec ) ;
pthread_mutex_init ( & ls - > lock . pvid_map , NULL ) ;
debug ( " initialised state: vgs = %p \n " , ls - > vgs ) ;
2011-07-20 16:49:21 +00:00
if ( ! ls - > pvs | | ! ls - > vgs )
2011-07-18 14:48:30 +00:00
return 0 ;
2011-06-14 02:36:38 +00:00
/* if (ls->initial_registrations)
_process_initial_registrations ( ds - > initial_registrations ) ; */
return 1 ;
}
2011-07-18 14:48:30 +00:00
static int fini ( daemon_state * s )
{
2011-07-25 15:33:04 +00:00
debug ( " fini \n " ) ;
2011-07-18 14:48:30 +00:00
lvmetad_state * ls = s - > private ;
2011-07-20 18:24:49 +00:00
struct dm_hash_node * n = dm_hash_get_first ( ls - > vgs ) ;
while ( n ) {
destroy_config_tree ( dm_hash_get_data ( ls - > vgs , n ) ) ;
n = dm_hash_get_next ( ls - > vgs , n ) ;
}
2011-07-25 15:33:04 +00:00
n = dm_hash_get_first ( ls - > lock . vg ) ;
while ( n ) {
pthread_mutex_destroy ( dm_hash_get_data ( ls - > lock . vg , n ) ) ;
free ( dm_hash_get_data ( ls - > lock . vg , n ) ) ;
n = dm_hash_get_next ( ls - > lock . vg , n ) ;
}
dm_hash_destroy ( ls - > lock . vg ) ;
2011-07-20 18:24:49 +00:00
dm_hash_destroy ( ls - > pvs ) ;
dm_hash_destroy ( ls - > vgs ) ;
2011-07-25 15:33:04 +00:00
dm_hash_destroy ( ls - > pvid_map ) ;
2011-07-18 14:48:30 +00:00
return 1 ;
}
2011-06-14 02:36:38 +00:00
static void usage ( char * prog , FILE * file )
{
fprintf ( file , " Usage: \n "
" %s [-V] [-h] [-d] [-d] [-d] [-f] \n \n "
" -V Show version of lvmetad \n "
" -h Show this help information \n "
" -d Log debug messages to syslog (-d, -dd, -ddd) \n "
" -R Replace a running lvmetad instance, loading its data \n "
" -f Don't fork, run in the foreground \n \n " , prog ) ;
}
int main ( int argc , char * argv [ ] )
{
signed char opt ;
daemon_state s ;
lvmetad_state ls ;
int _restart = 0 ;
2011-07-18 14:48:30 +00:00
s . name = " lvmetad " ;
2011-06-14 02:36:38 +00:00
s . private = & ls ;
2011-07-18 14:48:30 +00:00
s . daemon_init = init ;
s . daemon_fini = fini ;
2011-06-14 02:36:38 +00:00
s . handler = handler ;
s . socket_path = " /var/run/lvm/lvmetad.socket " ;
s . pidfile = " /var/run/lvm/lvmetad.pid " ;
while ( ( opt = getopt ( argc , argv , " ?fhVdR " ) ) ! = EOF ) {
switch ( opt ) {
case ' h ' :
usage ( argv [ 0 ] , stdout ) ;
exit ( 0 ) ;
case ' ? ' :
usage ( argv [ 0 ] , stderr ) ;
exit ( 0 ) ;
case ' R ' :
_restart + + ;
break ;
case ' f ' :
s . foreground = 1 ;
break ;
case ' d ' :
s . log_level + + ;
break ;
case ' V ' :
printf ( " lvmetad version 0 \n " ) ;
exit ( 1 ) ;
break ;
}
}
daemon_start ( s ) ;
return 0 ;
}