2015-07-03 18:34:40 +03:00
/*
* Copyright ( C ) 2014 - 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 .
*/
2018-05-14 12:30:20 +03:00
# include "tools/tool.h"
2015-07-06 19:30:18 +03:00
2018-05-14 12:30:20 +03:00
# include "daemons/lvmlockd/lvmlockd-client.h"
2015-03-05 23:00:44 +03:00
# include <stddef.h>
# include <getopt.h>
# include <signal.h>
# include <errno.h>
# include <fcntl.h>
2015-07-31 21:38:38 +03:00
# include <syslog.h>
2021-03-04 02:36:42 +03:00
# include <ctype.h>
2015-03-05 23:00:44 +03:00
# include <sys/socket.h>
# include <sys/un.h>
2021-03-04 02:36:42 +03:00
# include <unistd.h>
# include <sys/wait.h>
2015-03-05 23:00:44 +03:00
2015-07-08 14:56:06 +03:00
static int quit = 0 ;
static int info = 0 ;
static int dump = 0 ;
2018-11-27 22:41:33 +03:00
static int wait_opt = 1 ;
2015-07-08 14:56:06 +03:00
static int force_opt = 0 ;
2015-07-31 21:38:38 +03:00
static int kill_vg = 0 ;
static int drop_vg = 0 ;
2015-07-08 14:56:06 +03:00
static int gl_enable = 0 ;
static int gl_disable = 0 ;
2021-03-02 00:22:54 +03:00
static int use_stderr = 0 ;
2015-07-08 14:56:06 +03:00
static int stop_lockspaces = 0 ;
2015-07-31 21:38:38 +03:00
static char * arg_vg_name = NULL ;
2015-03-05 23:00:44 +03:00
# define DUMP_SOCKET_NAME "lvmlockd-dump.sock"
# define DUMP_BUF_SIZE (1024 * 1024)
2015-07-31 21:38:38 +03:00
static char dump_buf [ DUMP_BUF_SIZE + 1 ] ;
2015-03-05 23:00:44 +03:00
static int dump_len ;
static struct sockaddr_un dump_addr ;
static socklen_t dump_addrlen ;
daemon_handle _lvmlockd ;
# define log_error(fmt, args...) \
do { \
printf ( fmt " \n " , # # args ) ; \
} while ( 0 )
2021-03-02 00:22:54 +03:00
# define log_sys_emerg(fmt, args...) \
do { \
if ( use_stderr ) \
fprintf ( stderr , fmt " \n " , # # args ) ; \
else \
syslog ( LOG_EMERG , fmt , # # args ) ; \
} while ( 0 )
# define log_sys_warn(fmt, args...) \
do { \
if ( use_stderr ) \
fprintf ( stderr , fmt " \n " , # # args ) ; \
else \
syslog ( LOG_WARNING , fmt , # # args ) ; \
} while ( 0 )
2015-03-05 23:00:44 +03:00
# define MAX_LINE 512
/* copied from lvmlockd-internal.h */
# define MAX_NAME 64
# define MAX_ARGS 64
/*
* lvmlockd dumps the client info before the lockspaces ,
* so we can look up client info when printing lockspace info .
*/
# define MAX_CLIENTS 100
struct client_info {
uint32_t client_id ;
int pid ;
char name [ MAX_NAME + 1 ] ;
} ;
static struct client_info clients [ MAX_CLIENTS ] ;
static int num_clients ;
static void save_client_info ( char * line )
{
uint32_t pid = 0 ;
int fd = 0 ;
int pi = 0 ;
uint32_t client_id = 0 ;
char name [ MAX_NAME + 1 ] = { 0 } ;
2016-05-31 11:06:19 +03:00
( void ) sscanf ( line , " info=client pid=%u fd=%d pi=%d id=%u name=%s " ,
2015-03-05 23:00:44 +03:00
& pid , & fd , & pi , & client_id , name ) ;
clients [ num_clients ] . client_id = client_id ;
clients [ num_clients ] . pid = pid ;
strcpy ( clients [ num_clients ] . name , name ) ;
num_clients + + ;
}
static void find_client_info ( uint32_t client_id , uint32_t * pid , char * cl_name )
{
int i ;
for ( i = 0 ; i < num_clients ; i + + ) {
if ( clients [ i ] . client_id = = client_id ) {
* pid = clients [ i ] . pid ;
strcpy ( cl_name , clients [ i ] . name ) ;
return ;
}
}
}
2015-07-30 18:47:41 +03:00
static int first_ls = 1 ;
2015-03-05 23:00:44 +03:00
static void format_info_ls ( char * line )
{
char ls_name [ MAX_NAME + 1 ] = { 0 } ;
char vg_name [ MAX_NAME + 1 ] = { 0 } ;
char vg_uuid [ MAX_NAME + 1 ] = { 0 } ;
char vg_sysid [ MAX_NAME + 1 ] = { 0 } ;
char lock_args [ MAX_ARGS + 1 ] = { 0 } ;
char lock_type [ MAX_NAME + 1 ] = { 0 } ;
2016-05-31 11:06:19 +03:00
( void ) sscanf ( line , " info=ls ls_name=%s vg_name=%s vg_uuid=%s vg_sysid=%s vg_args=%s lm_type=%s " ,
2015-03-05 23:00:44 +03:00
ls_name , vg_name , vg_uuid , vg_sysid , lock_args , lock_type ) ;
2015-07-30 18:47:41 +03:00
if ( ! first_ls )
printf ( " \n " ) ;
first_ls = 0 ;
2015-03-05 23:00:44 +03:00
printf ( " VG %s lock_type=%s %s \n " , vg_name , lock_type , vg_uuid ) ;
printf ( " LS %s %s \n " , lock_type , ls_name ) ;
}
static void format_info_ls_action ( char * line )
{
uint32_t client_id = 0 ;
char flags [ MAX_NAME + 1 ] = { 0 } ;
char version [ MAX_NAME + 1 ] = { 0 } ;
char op [ MAX_NAME + 1 ] = { 0 } ;
uint32_t pid = 0 ;
char cl_name [ MAX_NAME + 1 ] = { 0 } ;
2016-05-31 11:06:19 +03:00
( void ) sscanf ( line , " info=ls_action client_id=%u %s %s op=%s " ,
2015-03-05 23:00:44 +03:00
& client_id , flags , version , op ) ;
find_client_info ( client_id , & pid , cl_name ) ;
2015-07-08 18:57:57 +03:00
printf ( " OP %s pid %u (%s) \n " , op , pid , cl_name ) ;
2015-03-05 23:00:44 +03:00
}
static void format_info_r ( char * line , char * r_name_out , char * r_type_out )
{
char r_name [ MAX_NAME + 1 ] = { 0 } ;
char r_type [ 4 ] = { 0 } ;
char mode [ 4 ] = { 0 } ;
char sh_count [ MAX_NAME + 1 ] = { 0 } ;
uint32_t ver = 0 ;
2016-05-31 11:06:19 +03:00
( void ) sscanf ( line , " info=r name=%s type=%s mode=%s %s version=%u " ,
2015-03-05 23:00:44 +03:00
r_name , r_type , mode , sh_count , & ver ) ;
2015-08-19 00:49:33 +03:00
strcpy ( r_name_out , r_name ) ;
strcpy ( r_type_out , r_type ) ;
2015-03-05 23:00:44 +03:00
2015-08-19 00:49:33 +03:00
/* when mode is not un, wait and print each lk line */
if ( strcmp ( mode , " un " ) )
2015-03-05 23:00:44 +03:00
return ;
/* when mode is un, there will be no lk lines, so print now */
if ( ! strcmp ( r_type , " gl " ) ) {
2015-07-30 18:47:41 +03:00
printf ( " LK GL un ver %u \n " , ver ) ;
2015-03-05 23:00:44 +03:00
} else if ( ! strcmp ( r_type , " vg " ) ) {
2015-07-30 18:47:41 +03:00
printf ( " LK VG un ver %u \n " , ver ) ;
2015-03-05 23:00:44 +03:00
} else if ( ! strcmp ( r_type , " lv " ) ) {
printf ( " LK LV un %s \n " , r_name ) ;
}
}
static void format_info_lk ( char * line , char * r_name , char * r_type )
{
char mode [ 4 ] = { 0 } ;
uint32_t ver = 0 ;
char flags [ MAX_NAME + 1 ] = { 0 } ;
uint32_t client_id = 0 ;
uint32_t pid = 0 ;
char cl_name [ MAX_NAME + 1 ] = { 0 } ;
if ( ! r_name [ 0 ] | | ! r_type [ 0 ] ) {
printf ( " format_info_lk error r_name %s r_type %s \n " , r_name , r_type ) ;
printf ( " %s \n " , line ) ;
return ;
}
2016-05-31 11:06:19 +03:00
( void ) sscanf ( line , " info=lk mode=%s version=%u %s client_id=%u " ,
2015-03-05 23:00:44 +03:00
mode , & ver , flags , & client_id ) ;
find_client_info ( client_id , & pid , cl_name ) ;
if ( ! strcmp ( r_type , " gl " ) ) {
2015-07-30 18:47:41 +03:00
printf ( " LK GL %s ver %u pid %u (%s) \n " , mode , ver , pid , cl_name ) ;
2015-03-05 23:00:44 +03:00
} else if ( ! strcmp ( r_type , " vg " ) ) {
2015-07-30 18:47:41 +03:00
printf ( " LK VG %s ver %u pid %u (%s) \n " , mode , ver , pid , cl_name ) ;
2015-03-05 23:00:44 +03:00
} else if ( ! strcmp ( r_type , " lv " ) ) {
printf ( " LK LV %s %s \n " , mode , r_name ) ;
}
}
static void format_info_r_action ( char * line , char * r_name , char * r_type )
{
uint32_t client_id = 0 ;
char flags [ MAX_NAME + 1 ] = { 0 } ;
char version [ MAX_NAME + 1 ] = { 0 } ;
char op [ MAX_NAME + 1 ] = { 0 } ;
char rt [ 4 ] = { 0 } ;
char mode [ 4 ] = { 0 } ;
char lm [ MAX_NAME + 1 ] = { 0 } ;
char result [ MAX_NAME + 1 ] = { 0 } ;
char lm_rv [ MAX_NAME + 1 ] = { 0 } ;
uint32_t pid = 0 ;
char cl_name [ MAX_NAME + 1 ] = { 0 } ;
if ( ! r_name [ 0 ] | | ! r_type [ 0 ] ) {
printf ( " format_info_r_action error r_name %s r_type %s \n " , r_name , r_type ) ;
printf ( " %s \n " , line ) ;
return ;
}
2016-05-31 11:06:19 +03:00
( void ) sscanf ( line , " info=r_action client_id=%u %s %s op=%s rt=%s mode=%s %s %s %s " ,
2015-03-05 23:00:44 +03:00
& client_id , flags , version , op , rt , mode , lm , result , lm_rv ) ;
find_client_info ( client_id , & pid , cl_name ) ;
if ( strcmp ( op , " lock " ) ) {
2015-08-19 00:49:33 +03:00
printf ( " OP %s pid %u (%s) \n " , op , pid , cl_name ) ;
2015-03-05 23:00:44 +03:00
return ;
}
if ( ! strcmp ( r_type , " gl " ) ) {
2015-07-30 18:47:41 +03:00
printf ( " LW GL %s ver %u pid %u (%s) \n " , mode , 0 , pid , cl_name ) ;
2015-03-05 23:00:44 +03:00
} else if ( ! strcmp ( r_type , " vg " ) ) {
2015-07-30 18:47:41 +03:00
printf ( " LW VG %s ver %u pid %u (%s) \n " , mode , 0 , pid , cl_name ) ;
2015-03-05 23:00:44 +03:00
} else if ( ! strcmp ( r_type , " lv " ) ) {
printf ( " LW LV %s %s \n " , mode , r_name ) ;
}
}
2015-07-08 18:57:57 +03:00
static void format_info_line ( char * line , char * r_name , char * r_type )
2015-03-05 23:00:44 +03:00
{
if ( ! strncmp ( line , " info=structs " , strlen ( " info=structs " ) ) ) {
2015-07-30 18:47:41 +03:00
/* only print this in the raw info dump */
2015-03-05 23:00:44 +03:00
} else if ( ! strncmp ( line , " info=client " , strlen ( " info=client " ) ) ) {
save_client_info ( line ) ;
} else if ( ! strncmp ( line , " info=ls " , strlen ( " info=ls " ) ) ) {
format_info_ls ( line ) ;
} else if ( ! strncmp ( line , " info=ls_action " , strlen ( " info=ls_action " ) ) ) {
format_info_ls_action ( line ) ;
} else if ( ! strncmp ( line , " info=r " , strlen ( " info=r " ) ) ) {
2015-07-08 18:57:57 +03:00
/*
* r_name / r_type are reset when a new resource is found .
* They are reused for the lock and action lines that
* follow a resource line .
*/
memset ( r_name , 0 , MAX_NAME + 1 ) ;
memset ( r_type , 0 , MAX_NAME + 1 ) ;
2015-03-05 23:00:44 +03:00
format_info_r ( line , r_name , r_type ) ;
} else if ( ! strncmp ( line , " info=lk " , strlen ( " info=lk " ) ) ) {
/* will use info from previous r */
format_info_lk ( line , r_name , r_type ) ;
} else if ( ! strncmp ( line , " info=r_action " , strlen ( " info=r_action " ) ) ) {
/* will use info from previous r */
format_info_r_action ( line , r_name , r_type ) ;
} else {
printf ( " UN %s \n " , line ) ;
}
}
static void format_info ( void )
{
2020-01-29 17:47:40 +03:00
char line [ MAX_LINE ] = { 0 } ;
char r_name [ MAX_NAME + 1 ] = { 0 } ;
char r_type [ MAX_NAME + 1 ] = { 0 } ;
2015-03-05 23:00:44 +03:00
int i , j ;
j = 0 ;
for ( i = 0 ; i < dump_len ; i + + ) {
line [ j + + ] = dump_buf [ i ] ;
if ( ( line [ j - 1 ] = = ' \n ' ) | | ( line [ j - 1 ] = = ' \0 ' ) ) {
2015-07-08 18:57:57 +03:00
format_info_line ( line , r_name , r_type ) ;
2015-03-05 23:00:44 +03:00
j = 0 ;
memset ( line , 0 , sizeof ( line ) ) ;
}
}
}
static daemon_reply _lvmlockd_send ( const char * req_name , . . . )
{
va_list ap ;
daemon_reply repl ;
daemon_request req ;
req = daemon_request_make ( req_name ) ;
va_start ( ap , req_name ) ;
daemon_request_extend_v ( req , ap ) ;
va_end ( ap ) ;
repl = daemon_send ( _lvmlockd , req ) ;
daemon_request_destroy ( req ) ;
return repl ;
}
/* See the same in lib/locking/lvmlockd.c */
# define NO_LOCKD_RESULT -1000
static int _lvmlockd_result ( daemon_reply reply , int * result )
{
int reply_result ;
2020-02-04 17:33:36 +03:00
* result = NO_LOCKD_RESULT ;
2015-03-05 23:00:44 +03:00
if ( reply . error ) {
log_error ( " lvmlockd_result reply error %d " , reply . error ) ;
return 0 ;
}
if ( strcmp ( daemon_reply_str ( reply , " response " , " " ) , " OK " ) ) {
log_error ( " lvmlockd_result bad response " ) ;
return 0 ;
}
reply_result = daemon_reply_int ( reply , " op_result " , NO_LOCKD_RESULT ) ;
2020-02-04 17:33:36 +03:00
if ( reply_result = = NO_LOCKD_RESULT ) {
2015-03-05 23:00:44 +03:00
log_error ( " lvmlockd_result no op_result " ) ;
return 0 ;
}
* result = reply_result ;
return 1 ;
}
static int do_quit ( void )
{
daemon_reply reply ;
int rv = 0 ;
reply = daemon_send_simple ( _lvmlockd , " quit " , NULL ) ;
if ( reply . error ) {
log_error ( " reply error %d " , reply . error ) ;
rv = reply . error ;
}
daemon_reply_destroy ( reply ) ;
return rv ;
}
static int setup_dump_socket ( void )
{
int s , rv ;
s = socket ( AF_LOCAL , SOCK_DGRAM , 0 ) ;
if ( s < 0 )
return s ;
memset ( & dump_addr , 0 , sizeof ( dump_addr ) ) ;
dump_addr . sun_family = AF_LOCAL ;
strcpy ( & dump_addr . sun_path [ 1 ] , DUMP_SOCKET_NAME ) ;
dump_addrlen = sizeof ( sa_family_t ) + strlen ( dump_addr . sun_path + 1 ) + 1 ;
rv = bind ( s , ( struct sockaddr * ) & dump_addr , dump_addrlen ) ;
2015-07-08 14:56:06 +03:00
if ( rv < 0 ) {
2015-08-20 22:00:12 +03:00
rv = - errno ;
2017-08-25 12:57:49 +03:00
if ( close ( s ) )
2015-07-08 14:56:06 +03:00
log_error ( " failed to close dump socket " ) ;
2015-03-05 23:00:44 +03:00
return rv ;
2015-07-08 14:56:06 +03:00
}
2015-03-05 23:00:44 +03:00
return s ;
}
static int do_dump ( const char * req_name )
{
daemon_reply reply ;
int result ;
int fd , rv = 0 ;
2015-08-20 22:00:12 +03:00
int count = 0 ;
2015-03-05 23:00:44 +03:00
fd = setup_dump_socket ( ) ;
if ( fd < 0 ) {
log_error ( " socket error %d " , fd ) ;
return fd ;
}
reply = daemon_send_simple ( _lvmlockd , req_name , NULL ) ;
if ( reply . error ) {
log_error ( " reply error %d " , reply . error ) ;
rv = reply . error ;
goto out ;
}
result = daemon_reply_int ( reply , " result " , 0 ) ;
dump_len = daemon_reply_int ( reply , " dump_len " , 0 ) ;
daemon_reply_destroy ( reply ) ;
if ( result < 0 ) {
rv = result ;
log_error ( " result %d " , result ) ;
}
if ( ! dump_len )
goto out ;
memset ( dump_buf , 0 , sizeof ( dump_buf ) ) ;
2015-08-20 22:00:12 +03:00
retry :
rv = recvfrom ( fd , dump_buf + count , dump_len - count , MSG_WAITALL ,
2015-03-05 23:00:44 +03:00
( struct sockaddr * ) & dump_addr , & dump_addrlen ) ;
if ( rv < 0 ) {
log_error ( " recvfrom error %d %d " , rv , errno ) ;
rv = - errno ;
goto out ;
}
2015-08-20 22:00:12 +03:00
count + = rv ;
if ( count < dump_len )
goto retry ;
2015-03-05 23:00:44 +03:00
2021-09-21 19:13:05 +03:00
dump_buf [ count ] = 0 ;
2015-03-05 23:00:44 +03:00
rv = 0 ;
if ( ( info & & dump ) | | ! strcmp ( req_name , " dump " ) )
printf ( " %s \n " , dump_buf ) ;
else
format_info ( ) ;
out :
2015-07-09 16:15:15 +03:00
if ( close ( fd ) )
log_error ( " failed to close dump socket %d " , fd ) ;
2015-03-05 23:00:44 +03:00
return rv ;
}
static int do_able ( const char * req_name )
{
daemon_reply reply ;
int result ;
int rv ;
reply = _lvmlockd_send ( req_name ,
2015-07-31 21:38:38 +03:00
" cmd = %s " , " lvmlockctl " ,
2015-11-09 17:15:37 +03:00
" pid = " FMTd64 , ( int64_t ) getpid ( ) ,
2015-07-31 21:38:38 +03:00
" vg_name = %s " , arg_vg_name ,
2015-03-05 23:00:44 +03:00
NULL ) ;
if ( ! _lvmlockd_result ( reply , & result ) ) {
log_error ( " lvmlockd result %d " , result ) ;
rv = result ;
} else {
rv = 0 ;
}
daemon_reply_destroy ( reply ) ;
return rv ;
}
static int do_stop_lockspaces ( void )
{
daemon_reply reply ;
char opts [ 32 ] ;
int result ;
int rv ;
memset ( opts , 0 , sizeof ( opts ) ) ;
if ( wait_opt )
strcat ( opts , " wait " ) ;
if ( force_opt )
strcat ( opts , " force " ) ;
reply = _lvmlockd_send ( " stop_all " ,
2015-07-31 21:38:38 +03:00
" cmd = %s " , " lvmlockctl " ,
2015-11-09 17:15:37 +03:00
" pid = " FMTd64 , ( int64_t ) getpid ( ) ,
2015-03-05 23:00:44 +03:00
" opts = %s " , opts [ 0 ] ? opts : " none " ,
NULL ) ;
if ( ! _lvmlockd_result ( reply , & result ) ) {
log_error ( " lvmlockd result %d " , result ) ;
rv = result ;
} else {
rv = 0 ;
}
daemon_reply_destroy ( reply ) ;
return rv ;
}
2021-03-04 02:36:42 +03:00
static int _reopen_fd_to_null ( int fd )
{
int null_fd ;
int r = 0 ;
if ( ( null_fd = open ( " /dev/null " , O_RDWR ) ) = = - 1 ) {
log_error ( " open error /dev/null %d " , errno ) ;
return 0 ;
}
if ( close ( fd ) ) {
log_error ( " close error fd %d %d " , fd , errno ) ;
goto out ;
}
if ( dup2 ( null_fd , fd ) = = - 1 ) {
log_error ( " dup2 error %d " , errno ) ;
goto out ;
}
r = 1 ;
out :
if ( close ( null_fd ) ) {
log_error ( " close error fd %d %d " , null_fd , errno ) ;
return 0 ;
}
return r ;
}
# define MAX_AV_COUNT 32
# define ONE_ARG_LEN 1024
static void _run_command_pipe ( const char * cmd_str , pid_t * pid_out , FILE * * fp_out )
{
char arg [ ONE_ARG_LEN ] ;
char * av [ MAX_AV_COUNT + 1 ] ; /* +1 for NULL */
char * arg_dup ;
int av_count = 0 ;
int cmd_len ;
int arg_len ;
pid_t pid = 0 ;
FILE * fp = NULL ;
int pipefd [ 2 ] ;
int i ;
for ( i = 0 ; i < MAX_AV_COUNT + 1 ; i + + )
av [ i ] = NULL ;
cmd_len = strlen ( cmd_str ) ;
memset ( & arg , 0 , sizeof ( arg ) ) ;
arg_len = 0 ;
for ( i = 0 ; i < cmd_len ; i + + ) {
if ( ! cmd_str [ i ] )
break ;
if ( av_count = = MAX_AV_COUNT )
goto out ;
if ( cmd_str [ i ] = = ' \\ ' ) {
if ( i = = ( cmd_len - 1 ) )
break ;
i + + ;
if ( cmd_str [ i ] = = ' \\ ' ) {
arg [ arg_len + + ] = cmd_str [ i ] ;
continue ;
}
if ( isspace ( cmd_str [ i ] ) ) {
arg [ arg_len + + ] = cmd_str [ i ] ;
continue ;
} else {
break ;
}
}
if ( isalnum ( cmd_str [ i ] ) | | ispunct ( cmd_str [ i ] ) ) {
arg [ arg_len + + ] = cmd_str [ i ] ;
} else if ( isspace ( cmd_str [ i ] ) ) {
if ( arg_len ) {
if ( ! ( arg_dup = strdup ( arg ) ) )
goto out ;
av [ av_count + + ] = arg_dup ;
}
memset ( arg , 0 , sizeof ( arg ) ) ;
arg_len = 0 ;
} else {
break ;
}
}
if ( arg_len ) {
if ( av_count > = MAX_AV_COUNT )
goto out ;
if ( ! ( arg_dup = strdup ( arg ) ) )
goto out ;
av [ av_count + + ] = arg_dup ;
}
if ( pipe ( pipefd ) ) {
log_error ( " pipe error %d " , errno ) ;
goto out ;
}
pid = fork ( ) ;
if ( pid < 0 ) {
log_error ( " fork error %d " , errno ) ;
pid = 0 ;
goto out ;
}
if ( pid = = 0 ) {
/* Child -> writer, convert pipe[0] to STDOUT */
if ( ! _reopen_fd_to_null ( STDIN_FILENO ) )
log_error ( " reopen STDIN error " ) ;
else if ( close ( pipefd [ 0 /*read*/ ] ) )
log_error ( " close error pipe[0] %d " , errno ) ;
else if ( close ( STDOUT_FILENO ) )
log_error ( " close error STDOUT %d " , errno ) ;
else if ( dup2 ( pipefd [ 1 /*write*/ ] , STDOUT_FILENO ) = = - 1 )
log_error ( " dup2 error STDOUT %d " , errno ) ;
else if ( close ( pipefd [ 1 ] ) )
log_error ( " close error pipe[1] %d " , errno ) ;
else {
execvp ( av [ 0 ] , av ) ;
log_error ( " execvp error %d " , errno ) ;
}
_exit ( errno ) ;
}
/* Parent -> reader */
if ( close ( pipefd [ 1 /*write*/ ] ) )
log_error ( " close error STDOUT %d " , errno ) ;
if ( ! ( fp = fdopen ( pipefd [ 0 /*read*/ ] , " r " ) ) ) {
log_error ( " fdopen STDIN error %d " , errno ) ;
if ( close ( pipefd [ 0 ] ) )
log_error ( " close error STDIN %d " , errno ) ;
}
out :
for ( i = 0 ; i < MAX_AV_COUNT + 1 ; i + + )
free ( av [ i ] ) ;
* pid_out = pid ;
* fp_out = fp ;
}
/* Returns -1 on error, 0 on success. */
static int _close_command_pipe ( pid_t pid , FILE * fp )
{
int status , estatus ;
int ret = - 1 ;
if ( waitpid ( pid , & status , 0 ) ! = pid ) {
log_error ( " waitpid error pid %d %d " , pid , errno ) ;
goto out ;
}
if ( WIFEXITED ( status ) ) {
/* pid exited with an exit code */
estatus = WEXITSTATUS ( status ) ;
/* exit status 0: child success */
if ( ! estatus ) {
ret = 0 ;
goto out ;
}
/* exit status not zero: child error */
log_error ( " child exit error %d " , estatus ) ;
goto out ;
}
if ( WIFSIGNALED ( status ) ) {
/* pid terminated due to a signal */
log_error ( " child exit from signal " ) ;
goto out ;
}
log_error ( " child exit problem " ) ;
out :
if ( fp & & fclose ( fp ) )
log_error ( " fclose error STDIN %d " , errno ) ;
return ret ;
}
2021-03-02 00:22:54 +03:00
/* Returns -1 on error, 0 on success. */
static int _get_kill_command ( char * kill_cmd )
{
2021-03-04 02:36:42 +03:00
char config_cmd [ PATH_MAX + 128 ] = { 0 } ;
2021-03-02 00:22:54 +03:00
char config_val [ 1024 ] = { 0 } ;
char line [ PATH_MAX ] = { 0 } ;
2021-03-04 02:36:42 +03:00
pid_t pid = 0 ;
FILE * fp = NULL ;
snprintf ( config_cmd , PATH_MAX , " %s config --typeconfig full global/lvmlockctl_kill_command " , LVM_PATH ) ;
2021-03-02 00:22:54 +03:00
2021-03-04 02:36:42 +03:00
_run_command_pipe ( config_cmd , & pid , & fp ) ;
2021-03-02 00:22:54 +03:00
2021-03-04 02:36:42 +03:00
if ( ! pid ) {
2021-03-02 00:22:54 +03:00
log_error ( " failed to run %s " , config_cmd ) ;
return - 1 ;
}
2021-03-04 02:36:42 +03:00
if ( ! fp ) {
log_error ( " failed to get output %s " , config_cmd ) ;
_close_command_pipe ( pid , fp ) ;
return - 1 ;
}
2021-03-02 00:22:54 +03:00
if ( ! fgets ( line , sizeof ( line ) , fp ) ) {
log_error ( " no output from %s " , config_cmd ) ;
goto bad ;
}
if ( sscanf ( line , " lvmlockctl_kill_command= \" %256[^ \n \" ] \" " , config_val ) ! = 1 ) {
log_error ( " unrecognized config value from %s " , config_cmd ) ;
goto bad ;
}
if ( ! config_val [ 0 ] | | ( config_val [ 0 ] = = ' ' ) ) {
log_error ( " invalid config value from %s " , config_cmd ) ;
goto bad ;
}
2021-03-04 02:36:42 +03:00
if ( config_val [ 0 ] ! = ' / ' ) {
log_error ( " lvmlockctl_kill_command must be full path " ) ;
goto bad ;
}
2021-03-02 00:22:54 +03:00
printf ( " Found lvmlockctl_kill_command: %s \n " , config_val ) ;
snprintf ( kill_cmd , PATH_MAX , " %s %s " , config_val , arg_vg_name ) ;
2021-03-04 02:36:42 +03:00
kill_cmd [ PATH_MAX - 1 ] = ' \0 ' ;
2021-03-02 00:22:54 +03:00
2021-03-04 02:36:42 +03:00
_close_command_pipe ( pid , fp ) ;
2021-03-02 00:22:54 +03:00
return 0 ;
bad :
2021-03-04 02:36:42 +03:00
_close_command_pipe ( pid , fp ) ;
2021-03-02 00:22:54 +03:00
return - 1 ;
}
/* Returns -1 on error, 0 on success. */
static int _run_kill_command ( char * kill_cmd )
{
2021-03-04 02:36:42 +03:00
pid_t pid = 0 ;
FILE * fp = NULL ;
int rv ;
2021-03-02 00:22:54 +03:00
2021-03-04 02:36:42 +03:00
_run_command_pipe ( kill_cmd , & pid , & fp ) ;
rv = _close_command_pipe ( pid , fp ) ;
2021-03-02 00:22:54 +03:00
2021-03-04 02:36:42 +03:00
if ( ! pid )
return - 1 ;
2021-03-02 00:22:54 +03:00
2021-03-04 02:36:42 +03:00
if ( rv < 0 )
return - 1 ;
return 0 ;
2021-03-02 00:22:54 +03:00
}
static int do_drop ( void )
2015-07-31 21:38:38 +03:00
{
daemon_reply reply ;
int result ;
int rv ;
2021-03-02 00:22:54 +03:00
log_sys_warn ( " Dropping locks for VG %s. " , arg_vg_name ) ;
2015-07-31 21:38:38 +03:00
/*
2021-03-02 00:22:54 +03:00
* Check for misuse by looking for any active LVs in the VG
* and refusing this operation if found ? One possible way
* to kill LVs ( e . g . if fs cannot be unmounted ) is to suspend
* them , or replace them with the error target . In that
* case the LV will still appear to be active , but it is
* safe to release the lock .
2015-07-31 21:38:38 +03:00
*/
2021-03-02 00:22:54 +03:00
reply = _lvmlockd_send ( " drop_vg " ,
2015-07-31 21:38:38 +03:00
" cmd = %s " , " lvmlockctl " ,
2015-11-09 17:15:37 +03:00
" pid = " FMTd64 , ( int64_t ) getpid ( ) ,
2015-07-31 21:38:38 +03:00
" vg_name = %s " , arg_vg_name ,
NULL ) ;
if ( ! _lvmlockd_result ( reply , & result ) ) {
log_error ( " lvmlockd result %d " , result ) ;
rv = result ;
} else {
rv = 0 ;
}
daemon_reply_destroy ( reply ) ;
return rv ;
}
2021-03-02 00:22:54 +03:00
static int do_kill ( void )
2015-07-31 21:38:38 +03:00
{
2021-03-02 00:22:54 +03:00
char kill_cmd [ PATH_MAX ] = { 0 } ;
2015-07-31 21:38:38 +03:00
daemon_reply reply ;
2021-03-02 00:22:54 +03:00
int no_kill_command = 0 ;
2015-07-31 21:38:38 +03:00
int result ;
int rv ;
2021-03-02 00:22:54 +03:00
log_sys_emerg ( " lvmlockd lost access to locks in VG %s. " , arg_vg_name ) ;
rv = _get_kill_command ( kill_cmd ) ;
if ( rv < 0 ) {
log_sys_emerg ( " Immediately deactivate LVs in VG %s. " , arg_vg_name ) ;
log_sys_emerg ( " Once VG is unused, run lvmlockctl --drop %s. " , arg_vg_name ) ;
no_kill_command = 1 ;
}
2015-07-31 21:38:38 +03:00
/*
2021-03-02 00:22:54 +03:00
* It may not be strictly necessary to notify lvmlockd of the kill , but
* lvmlockd can use this information to avoid attempting any new lock
* requests in the VG ( which would fail anyway ) , and can return an
* error indicating that the VG has been killed .
2015-07-31 21:38:38 +03:00
*/
2021-03-02 00:22:54 +03:00
_lvmlockd = lvmlockd_open ( NULL ) ;
if ( _lvmlockd . socket_fd < 0 | | _lvmlockd . error ) {
log_error ( " Cannot connect to lvmlockd for kill_vg. " ) ;
goto run ;
}
reply = _lvmlockd_send ( " kill_vg " ,
" cmd = %s " , " lvmlockctl " ,
" pid = " FMTd64 , ( int64_t ) getpid ( ) ,
" vg_name = %s " , arg_vg_name ,
NULL ) ;
if ( ! _lvmlockd_result ( reply , & result ) )
log_error ( " lvmlockd result %d kill_vg " , result ) ;
daemon_reply_destroy ( reply ) ;
lvmlockd_close ( _lvmlockd ) ;
2015-07-31 21:38:38 +03:00
2021-03-02 00:22:54 +03:00
run :
if ( no_kill_command )
return 0 ;
rv = _run_kill_command ( kill_cmd ) ;
if ( rv < 0 ) {
log_sys_emerg ( " Failed to run VG %s kill command %s " , arg_vg_name , kill_cmd ) ;
log_sys_emerg ( " Immediately deactivate LVs in VG %s. " , arg_vg_name ) ;
log_sys_emerg ( " Once VG is unused, run lvmlockctl --drop %s. " , arg_vg_name ) ;
return - 1 ;
}
log_sys_warn ( " Successful VG %s kill command %s " , arg_vg_name , kill_cmd ) ;
/*
* If kill command was successfully , call do_drop ( ) . ( The drop step
* may not always be necessary if the lvm commands run while shutting
* things down release all the leases . )
*/
rv = 0 ;
_lvmlockd = lvmlockd_open ( NULL ) ;
if ( _lvmlockd . socket_fd < 0 | | _lvmlockd . error ) {
log_sys_emerg ( " Failed to connect to lvmlockd to drop locks in VG %s. " , arg_vg_name ) ;
return - 1 ;
}
2015-07-31 21:38:38 +03:00
reply = _lvmlockd_send ( " drop_vg " ,
" cmd = %s " , " lvmlockctl " ,
2015-11-09 17:15:37 +03:00
" pid = " FMTd64 , ( int64_t ) getpid ( ) ,
2015-07-31 21:38:38 +03:00
" vg_name = %s " , arg_vg_name ,
NULL ) ;
if ( ! _lvmlockd_result ( reply , & result ) ) {
2021-03-02 00:22:54 +03:00
log_sys_emerg ( " Failed to drop locks in VG %s " , arg_vg_name ) ;
2015-07-31 21:38:38 +03:00
rv = result ;
}
daemon_reply_destroy ( reply ) ;
2021-03-02 00:22:54 +03:00
lvmlockd_close ( _lvmlockd ) ;
2015-07-31 21:38:38 +03:00
return rv ;
}
2015-03-05 23:00:44 +03:00
static void print_usage ( void )
{
printf ( " lvmlockctl options \n " ) ;
printf ( " Options: \n " ) ;
printf ( " --help | -h \n " ) ;
printf ( " Show this help information. \n " ) ;
printf ( " --quit | -q \n " ) ;
printf ( " Tell lvmlockd to quit. \n " ) ;
printf ( " --info | -i \n " ) ;
printf ( " Print lock state information from lvmlockd. \n " ) ;
printf ( " --dump | -d \n " ) ;
printf ( " Print log buffer from lvmlockd. \n " ) ;
printf ( " --wait | -w 0|1 \n " ) ;
printf ( " Wait option for other commands. \n " ) ;
printf ( " --force | -f 0|1> \n " ) ;
printf ( " Force option for other commands. \n " ) ;
2015-09-04 19:01:25 +03:00
printf ( " --kill | -k <vgname> \n " ) ;
2021-03-02 00:22:54 +03:00
printf ( " Kill access to the VG locks are lost (see lvmlockctl_kill_command). \n " ) ;
2015-09-04 19:01:25 +03:00
printf ( " --drop | -r <vgname> \n " ) ;
printf ( " Clear locks for the VG when it is unused after kill (-k). \n " ) ;
printf ( " --gl-enable | -E <vgname> \n " ) ;
printf ( " Tell lvmlockd to enable the global lock in a sanlock VG. \n " ) ;
printf ( " --gl-disable | -D <vgname> \n " ) ;
printf ( " Tell lvmlockd to disable the global lock in a sanlock VG. \n " ) ;
2015-07-31 21:38:38 +03:00
printf ( " --stop-lockspaces | -S \n " ) ;
printf ( " Stop all lockspaces. \n " ) ;
2021-03-02 00:22:54 +03:00
printf ( " --stderr | -e \n " ) ;
printf ( " Send kill and drop messages to stderr instead of syslog \n " ) ;
2015-03-05 23:00:44 +03:00
}
static int read_options ( int argc , char * argv [ ] )
{
int option_index = 0 ;
int c ;
2024-05-03 21:17:39 +03:00
static const struct option _long_options [ ] = {
2015-03-05 23:00:44 +03:00
{ " help " , no_argument , 0 , ' h ' } ,
{ " quit " , no_argument , 0 , ' q ' } ,
{ " info " , no_argument , 0 , ' i ' } ,
{ " dump " , no_argument , 0 , ' d ' } ,
{ " wait " , required_argument , 0 , ' w ' } ,
{ " force " , required_argument , 0 , ' f ' } ,
2015-07-31 21:38:38 +03:00
{ " kill " , required_argument , 0 , ' k ' } ,
{ " drop " , required_argument , 0 , ' r ' } ,
2015-03-05 23:00:44 +03:00
{ " gl-enable " , required_argument , 0 , ' E ' } ,
{ " gl-disable " , required_argument , 0 , ' D ' } ,
{ " stop-lockspaces " , no_argument , 0 , ' S ' } ,
2021-03-02 00:22:54 +03:00
{ " stderr " , no_argument , 0 , ' e ' } ,
2015-03-05 23:00:44 +03:00
{ 0 , 0 , 0 , 0 }
} ;
if ( argc = = 1 ) {
print_usage ( ) ;
exit ( 0 ) ;
}
while ( 1 ) {
2024-05-03 21:17:39 +03:00
c = getopt_long ( argc , argv , " hqidE:D:w:k:r:Se " , _long_options , & option_index ) ;
2015-03-05 23:00:44 +03:00
if ( c = = - 1 )
break ;
switch ( c ) {
case ' h ' :
/* --help */
print_usage ( ) ;
exit ( 0 ) ;
case ' q ' :
/* --quit */
quit = 1 ;
break ;
case ' i ' :
/* --info */
info = 1 ;
break ;
case ' d ' :
/* --dump */
dump = 1 ;
break ;
case ' w ' :
wait_opt = atoi ( optarg ) ;
break ;
2015-07-31 21:38:38 +03:00
case ' k ' :
kill_vg = 1 ;
2021-04-22 18:53:45 +03:00
free ( arg_vg_name ) ;
2015-07-31 21:38:38 +03:00
arg_vg_name = strdup ( optarg ) ;
break ;
case ' r ' :
drop_vg = 1 ;
2021-04-22 18:53:45 +03:00
free ( arg_vg_name ) ;
2015-07-31 21:38:38 +03:00
arg_vg_name = strdup ( optarg ) ;
break ;
2015-03-05 23:00:44 +03:00
case ' E ' :
gl_enable = 1 ;
2021-04-22 18:53:45 +03:00
free ( arg_vg_name ) ;
2015-07-31 21:38:38 +03:00
arg_vg_name = strdup ( optarg ) ;
2015-03-05 23:00:44 +03:00
break ;
case ' D ' :
gl_disable = 1 ;
2021-04-22 18:53:45 +03:00
free ( arg_vg_name ) ;
2015-07-31 21:38:38 +03:00
arg_vg_name = strdup ( optarg ) ;
2015-03-05 23:00:44 +03:00
break ;
case ' S ' :
stop_lockspaces = 1 ;
break ;
2021-03-02 00:22:54 +03:00
case ' e ' :
use_stderr = 1 ;
break ;
2015-03-05 23:00:44 +03:00
default :
print_usage ( ) ;
exit ( 1 ) ;
}
}
return 0 ;
}
int main ( int argc , char * * argv )
{
int rv = 0 ;
rv = read_options ( argc , argv ) ;
if ( rv < 0 )
return rv ;
2021-03-02 00:22:54 +03:00
/* do_kill handles lvmlockd connections itself */
if ( kill_vg )
return do_kill ( ) ;
2015-03-05 23:00:44 +03:00
2021-03-02 00:22:54 +03:00
_lvmlockd = lvmlockd_open ( NULL ) ;
2015-03-05 23:00:44 +03:00
if ( _lvmlockd . socket_fd < 0 | | _lvmlockd . error ) {
2015-07-30 18:47:41 +03:00
log_error ( " Cannot connect to lvmlockd. " ) ;
2015-03-05 23:00:44 +03:00
return - 1 ;
}
if ( quit ) {
rv = do_quit ( ) ;
goto out ;
}
if ( info ) {
rv = do_dump ( " info " ) ;
goto out ;
}
if ( dump ) {
rv = do_dump ( " dump " ) ;
goto out ;
}
2015-07-31 21:38:38 +03:00
if ( drop_vg ) {
rv = do_drop ( ) ;
goto out ;
}
2015-03-05 23:00:44 +03:00
if ( gl_enable ) {
2015-09-04 19:01:25 +03:00
syslog ( LOG_INFO , " Enabling global lock in VG %s. " , arg_vg_name ) ;
2015-03-05 23:00:44 +03:00
rv = do_able ( " enable_gl " ) ;
goto out ;
}
if ( gl_disable ) {
2015-09-04 19:01:25 +03:00
syslog ( LOG_INFO , " Disabling global lock in VG %s. " , arg_vg_name ) ;
2015-03-05 23:00:44 +03:00
rv = do_able ( " disable_gl " ) ;
goto out ;
}
if ( stop_lockspaces ) {
rv = do_stop_lockspaces ( ) ;
goto out ;
}
out :
lvmlockd_close ( _lvmlockd ) ;
return rv ;
}