2015-05-09 02:59:18 +03:00
/*
* Copyright ( C ) 2015 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
*/
# include "lib.h"
# include "daemon-io.h"
# include "lvmpolld-client.h"
# include "lvmpolld-protocol.h"
# include "metadata-exported.h"
# include "polldaemon.h"
# include "toolcontext.h"
2015-05-14 17:44:31 +03:00
# include "lvm2cmd.h"
2015-05-09 02:59:18 +03:00
struct progress_info {
unsigned error : 1 ;
unsigned finished : 1 ;
int cmd_signal ;
int cmd_retcode ;
} ;
static int _lvmpolld_use ;
static int _lvmpolld_connected ;
static const char * _lvmpolld_socket ;
static daemon_handle _lvmpolld = { . error = 0 } ;
2015-05-12 16:34:45 +03:00
static daemon_handle _lvmpolld_open ( const char * socket )
2015-05-09 02:59:18 +03:00
{
daemon_info lvmpolld_info = {
. path = " lvmpolld " ,
. socket = socket ? : LVMPOLLD_SOCKET ,
. protocol = LVMPOLLD_PROTOCOL ,
. protocol_version = LVMPOLLD_PROTOCOL_VERSION
} ;
return daemon_open ( lvmpolld_info ) ;
}
void lvmpolld_set_active ( int active )
{
_lvmpolld_use = active ;
}
void lvmpolld_set_socket ( const char * socket )
{
_lvmpolld_socket = socket ;
}
2015-05-12 16:34:45 +03:00
static void _lvmpolld_connect_or_warn ( void )
{
if ( ! _lvmpolld_connected & & ! _lvmpolld . error ) {
_lvmpolld = _lvmpolld_open ( _lvmpolld_socket ) ;
if ( _lvmpolld . socket_fd > = 0 & & ! _lvmpolld . error ) {
log_debug_lvmpolld ( " Sucessfully connected to lvmpolld on fd %d. " , _lvmpolld . socket_fd ) ;
_lvmpolld_connected = 1 ;
} else {
log_warn ( " WARNING: Failed to connect to lvmpolld. Proceeding with polling without using lvmpolld. " ) ;
log_warn ( " WARNING: Check global/use_lvmpolld in lvm.conf or the lvmpolld daemon state. " ) ;
}
}
}
2015-05-09 02:59:18 +03:00
int lvmpolld_use ( void )
{
2015-05-12 16:34:45 +03:00
if ( ! _lvmpolld_use | | ! _lvmpolld_socket )
2015-05-09 02:59:18 +03:00
return 0 ;
2015-05-12 16:34:45 +03:00
_lvmpolld_connect_or_warn ( ) ;
2015-05-09 02:59:18 +03:00
return _lvmpolld_connected ;
}
void lvmpolld_disconnect ( void )
{
if ( _lvmpolld_connected ) {
daemon_close ( _lvmpolld ) ;
_lvmpolld_connected = 0 ;
}
}
2015-05-14 17:44:31 +03:00
static void _explain_error_codes ( int retcode )
{
switch ( retcode ) {
/* LVM2 return codes */
case LVM2_NO_SUCH_COMMAND :
log_error ( " LVM command run by lvmpolld responded with: 'No such command.' " ) ;
break ;
case LVM2_INVALID_PARAMETERS :
log_error ( " LVM command run by lvmpolld failed due to invalid parameters. " ) ;
break ;
case LVM2_PROCESSING_FAILED :
log_error ( " LVM command executed by lvmpolld failed. " ) ;
break ;
/* lvmpolld specific return codes */
case LVMPD_RET_DUP_FAILED :
log_error ( " lvmpolld failed to duplicate file descriptors. " ) ;
2015-07-08 15:42:26 +03:00
/* fall through */
2015-05-14 17:44:31 +03:00
case LVMPD_RET_EXC_FAILED :
log_error ( " lvmpolld failed to exec() lvm binary. " ) ;
break ;
default :
log_error ( " lvmpolld responded with unexpected return code. " ) ;
}
log_print_unless_silent ( " For more information see lvmpolld messages in syslog or lvmpolld log file. " ) ;
}
2015-05-09 02:59:18 +03:00
static void _process_error_response ( daemon_reply rep )
{
if ( ! strcmp ( daemon_reply_str ( rep , " response " , " " ) , LVMPD_RESP_FAILED ) )
log_error ( " lvmpolld failed to process a request. The reason was: %s. " ,
daemon_reply_str ( rep , " reason " , " <empty> " ) ) ;
else if ( ! strcmp ( daemon_reply_str ( rep , " response " , " " ) , LVMPD_RESP_EINVAL ) )
log_error ( " lvmpolld couldn't handle a request. "
" It might be due to daemon internal state. The reason was: %s. " ,
daemon_reply_str ( rep , " reason " , " <empty> " ) ) ;
else
log_error ( " Unexpected response %s. The reason: %s. " ,
daemon_reply_str ( rep , " response " , " <empty> " ) ,
daemon_reply_str ( rep , " reason " , " <empty> " ) ) ;
2015-05-14 17:44:31 +03:00
log_print_unless_silent ( " For more information see lvmpolld messages in syslog or lvmpolld log file. " ) ;
2015-05-09 02:59:18 +03:00
}
static struct progress_info _request_progress_info ( const char * uuid , unsigned abort_polling )
{
daemon_reply rep ;
const char * e = getenv ( " LVM_SYSTEM_DIR " ) ;
struct progress_info ret = { . error = 1 , . finished = 1 } ;
daemon_request req = daemon_request_make ( LVMPD_REQ_PROGRESS ) ;
if ( ! daemon_request_extend ( req , LVMPD_PARM_LVID " = %s " , uuid , NULL ) ) {
log_error ( " Failed to create " LVMPD_REQ_PROGRESS " request. " ) ;
goto out_req ;
}
if ( abort_polling & &
2015-07-10 12:50:06 +03:00
! daemon_request_extend ( req , LVMPD_PARM_ABORT " = %d " , ( int64_t ) abort_polling , NULL ) ) {
2015-05-09 02:59:18 +03:00
log_error ( " Failed to create " LVMPD_REQ_PROGRESS " request. " ) ;
goto out_req ;
}
if ( e & &
! ( daemon_request_extend ( req , LVMPD_PARM_SYSDIR " = %s " ,
e , NULL ) ) ) {
log_error ( " Failed to create " LVMPD_REQ_PROGRESS " request. " ) ;
goto out_req ;
}
rep = daemon_send ( _lvmpolld , req ) ;
if ( rep . error ) {
log_error ( " Failed to process request with error %s (errno: %d). " ,
strerror ( rep . error ) , rep . error ) ;
goto out_rep ;
}
if ( ! strcmp ( daemon_reply_str ( rep , " response " , " " ) , LVMPD_RESP_IN_PROGRESS ) ) {
ret . finished = 0 ;
ret . error = 0 ;
} else if ( ! strcmp ( daemon_reply_str ( rep , " response " , " " ) , LVMPD_RESP_FINISHED ) ) {
if ( ! strcmp ( daemon_reply_str ( rep , " reason " , " " ) , LVMPD_REAS_SIGNAL ) )
ret . cmd_signal = daemon_reply_int ( rep , LVMPD_PARM_VALUE , 0 ) ;
else
ret . cmd_retcode = daemon_reply_int ( rep , LVMPD_PARM_VALUE , - 1 ) ;
ret . error = 0 ;
} else if ( ! strcmp ( daemon_reply_str ( rep , " response " , " " ) , LVMPD_RESP_NOT_FOUND ) ) {
log_verbose ( " No polling operation in progress regarding LV %s. " , uuid ) ;
ret . error = 0 ;
} else {
_process_error_response ( rep ) ;
stack ;
}
out_rep :
daemon_reply_destroy ( rep ) ;
out_req :
daemon_request_destroy ( req ) ;
return ret ;
}
/*
* interval in seconds long
* enough for more than a year
* of waiting
*/
# define INTERV_SIZE 10
static int _process_poll_init ( const struct cmd_context * cmd , const char * poll_type ,
const struct poll_operation_id * id , const struct daemon_parms * parms )
{
char * str ;
daemon_reply rep ;
daemon_request req ;
const char * e = getenv ( " LVM_SYSTEM_DIR " ) ;
int r = 0 ;
str = dm_malloc ( INTERV_SIZE * sizeof ( char ) ) ;
if ( ! str )
return r ;
if ( snprintf ( str , INTERV_SIZE , " %u " , parms - > interval ) > = INTERV_SIZE ) {
log_warn ( " Interval string conversion got truncated. " ) ;
str [ INTERV_SIZE - 1 ] = ' \0 ' ;
}
req = daemon_request_make ( poll_type ) ;
if ( ! daemon_request_extend ( req , LVMPD_PARM_LVID " = %s " , id - > uuid ,
LVMPD_PARM_VGNAME " = %s " , id - > vg_name ,
LVMPD_PARM_LVNAME " = %s " , id - > lv_name ,
LVMPD_PARM_INTERVAL " = %s " , str ,
" cmdline = %s " , cmd - > cmd_line , /* FIXME: debug param only */
NULL ) ) {
log_error ( " Failed to create %s request. " , poll_type ) ;
goto out_req ;
}
if ( parms - > aborting & &
2015-07-10 12:50:06 +03:00
! ( daemon_request_extend ( req , LVMPD_PARM_ABORT " = %d " , ( int64_t ) ( parms - > aborting ) , NULL ) ) ) {
2015-05-09 02:59:18 +03:00
log_error ( " Failed to create %s request. " , poll_type ) ;
goto out_req ;
}
if ( cmd - > handles_missing_pvs & &
! ( daemon_request_extend ( req , LVMPD_PARM_HANDLE_MISSING_PVS " = %d " ,
2015-07-10 12:50:06 +03:00
( int64_t ) ( cmd - > handles_missing_pvs ) , NULL ) ) ) {
2015-05-09 02:59:18 +03:00
log_error ( " Failed to create %s request. " , poll_type ) ;
goto out_req ;
}
if ( e & &
! ( daemon_request_extend ( req , LVMPD_PARM_SYSDIR " = %s " ,
e , NULL ) ) ) {
log_error ( " Failed to create %s request. " , poll_type ) ;
goto out_req ;
}
rep = daemon_send ( _lvmpolld , req ) ;
if ( rep . error ) {
log_error ( " Failed to process request with error %s (errno: %d). " ,
strerror ( rep . error ) , rep . error ) ;
goto out_rep ;
}
if ( ! strcmp ( daemon_reply_str ( rep , " response " , " " ) , LVMPD_RESP_OK ) )
r = 1 ;
else {
_process_error_response ( rep ) ;
stack ;
}
out_rep :
daemon_reply_destroy ( rep ) ;
out_req :
daemon_request_destroy ( req ) ;
dm_free ( str ) ;
return r ;
}
int lvmpolld_poll_init ( const struct cmd_context * cmd , const struct poll_operation_id * id ,
const struct daemon_parms * parms )
{
int r = 0 ;
if ( ! id - > uuid ) {
log_error ( INTERNAL_ERROR " Use of lvmpolld requires uuid set " ) ;
return 0 ;
}
if ( ! id - > vg_name ) {
log_error ( INTERNAL_ERROR " Use of lvmpolld requires vgname set " ) ;
return 0 ;
}
if ( ! id - > lv_name ) {
log_error ( INTERNAL_ERROR " Use of lvmpolld requires lvname set " ) ;
return 0 ;
}
if ( parms - > lv_type & PVMOVE ) {
2015-05-12 17:28:42 +03:00
log_debug_lvmpolld ( " Asking lvmpolld for pvmove%s on %s/%s. " ,
parms - > aborting ? " abort " : " " , id - > vg_name , id - > lv_name ) ;
2015-05-09 02:59:18 +03:00
r = _process_poll_init ( cmd , LVMPD_REQ_PVMOVE , id , parms ) ;
} else if ( parms - > lv_type & CONVERTING ) {
2015-05-12 17:28:42 +03:00
log_debug_lvmpolld ( " Asking lvmpolld for mirror conversion on %s/%s. " ,
id - > vg_name , id - > lv_name ) ;
2015-05-09 02:59:18 +03:00
r = _process_poll_init ( cmd , LVMPD_REQ_CONVERT , id , parms ) ;
} else if ( parms - > lv_type & MERGING ) {
if ( parms - > lv_type & SNAPSHOT ) {
2015-05-12 17:28:42 +03:00
log_debug_lvmpolld ( " Asking lvmpolld for snapshot merge on %s/%s. " ,
id - > vg_name , id - > lv_name ) ;
2015-05-09 02:59:18 +03:00
r = _process_poll_init ( cmd , LVMPD_REQ_MERGE , id , parms ) ;
}
else if ( parms - > lv_type & THIN_VOLUME ) {
2015-05-12 17:28:42 +03:00
log_debug_lvmpolld ( " Asking lvmpolld for thin snapshot merge on %s/%s. " ,
id - > vg_name , id - > lv_name ) ;
2015-05-09 02:59:18 +03:00
r = _process_poll_init ( cmd , LVMPD_REQ_MERGE_THIN , id , parms ) ;
}
else {
log_error ( INTERNAL_ERROR " Unsupported poll operation. " ) ;
}
} else
log_error ( INTERNAL_ERROR " Unsupported poll operation " ) ;
return r ;
}
int lvmpolld_request_info ( const struct poll_operation_id * id , const struct daemon_parms * parms , unsigned * finished )
{
struct progress_info info ;
int ret = 0 ;
* finished = 1 ;
if ( ! id - > uuid ) {
log_error ( INTERNAL_ERROR " use of lvmpolld requires uuid being set " ) ;
return 0 ;
}
2015-05-21 12:10:09 +03:00
log_debug_lvmpolld ( " Asking lvmpolld for progress status of an operation on %s/%s. " ,
id - > vg_name , id - > lv_name ) ;
2015-05-09 02:59:18 +03:00
info = _request_progress_info ( id - > uuid , parms - > aborting ) ;
* finished = info . finished ;
if ( info . error )
return_0 ;
if ( info . finished ) {
if ( info . cmd_signal )
2015-05-14 17:44:31 +03:00
log_error ( " Command executed by lvmpolld got terminated by signal (%d). " ,
2015-05-09 02:59:18 +03:00
info . cmd_signal ) ;
else if ( info . cmd_retcode )
2015-05-14 17:44:31 +03:00
_explain_error_codes ( info . cmd_retcode ) ;
else {
2015-05-09 02:59:18 +03:00
log_verbose ( " Polling finished successfully. " ) ;
ret = 1 ;
}
} else
ret = 1 ;
return ret ;
}