2004-06-24 12:02:38 +04:00
/*
* Copyright ( C ) 2002 - 2004 Sistina Software , Inc . All rights reserved .
* Copyright ( C ) 2004 Red Hat , Inc . All rights reserved .
*
* 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 General Public License v .2 .
*
* You should have received a copy of the GNU 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
*/
/*
CLVMD Cluster LVM daemon command processor .
To add commands to the daemon simply add a processor in do_command and return
and messages back in buf and the length in * retlen . The initial value of
buflen is the maximum size of the buffer . if buf is not large enough then it
may be reallocated by the functions in here to a suitable size bearing in
mind that anything larger than the passed - in size will have to be returned
using the system LV and so performance will suffer .
The status return will be negated and passed back to the originating node .
pre - and post - command routines are called only on the local node . The
purpose is primarily to get and release locks , though the pre - routine should
also do any other local setups required by the command ( if any ) and can
return a failure code that prevents the command from being distributed around
the cluster
The pre - and post - routines are run in their own thread so can block as long
they like , do_command is run in the main clvmd thread so should not block for
too long . If the pre - command returns an error code ( ! = 0 ) then the command
will not be propogated around the cluster but the post - command WILL be called
Also note that the pre and post routine are * always * called on the local
node , even if the command to be executed was only requested to run on a
remote node . It may peek inside the client structure to check the status of
the command .
The clients of the daemon must , naturally , understand the return messages and
codes .
Routines in here may only READ the values in the client structure passed in
apart from client - > private which they are free to do what they like with .
*/
# include <pthread.h>
# include <sys/types.h>
# include <sys/utsname.h>
# include <sys/ioctl.h>
# include <sys/socket.h>
# include <sys/stat.h>
# include <stdio.h>
# include <stdlib.h>
# include <stdint.h>
# include <fcntl.h>
# include <string.h>
# include <stddef.h>
# include <unistd.h>
# include <errno.h>
2007-04-27 21:46:16 +04:00
# include <libdevmapper.h>
# include <libdlm.h>
2004-06-24 12:02:38 +04:00
# include "list.h"
# include "locking.h"
# include "log.h"
# include "lvm-functions.h"
# include "clvmd-comms.h"
# include "clvm.h"
# include "clvmd.h"
2007-08-17 15:51:23 +04:00
extern debug_t debug ;
2006-10-09 18:11:57 +04:00
extern struct cluster_ops * clops ;
2004-06-24 12:02:38 +04:00
/* This is where all the real work happens:
NOTE : client will be NULL when this is executed on a remote node */
int do_command ( struct local_client * client , struct clvm_header * msg , int msglen ,
char * * buf , int buflen , int * retlen )
{
char * args = msg - > node + strlen ( msg - > node ) + 1 ;
int arglen = msglen - sizeof ( struct clvm_header ) - strlen ( msg - > node ) ;
int status = 0 ;
char * lockname ;
struct utsname nodeinfo ;
unsigned char lock_cmd ;
unsigned char lock_flags ;
/* Do the command */
switch ( msg - > cmd ) {
/* Just a test message */
case CLVMD_CMD_TEST :
if ( arglen > buflen ) {
2007-05-02 16:22:40 +04:00
char * new_buf ;
2004-06-24 12:02:38 +04:00
buflen = arglen + 200 ;
2007-05-02 16:22:40 +04:00
new_buf = realloc ( * buf , buflen ) ;
if ( new_buf = = NULL ) {
status = errno ;
free ( * buf ) ;
}
* buf = new_buf ;
}
if ( * buf ) {
uname ( & nodeinfo ) ;
* retlen = 1 + snprintf ( * buf , buflen ,
" TEST from %s: %s v%s " ,
nodeinfo . nodename , args ,
nodeinfo . release ) ;
2004-06-24 12:02:38 +04:00
}
break ;
case CLVMD_CMD_LOCK_VG :
2007-08-23 19:43:20 +04:00
lockname = & args [ 2 ] ;
2004-06-24 12:02:38 +04:00
/* Check to see if the VG is in use by LVM1 */
2007-08-23 19:43:20 +04:00
status = do_check_lvm1 ( lockname ) ;
/* P_global causes a cache refresh */
if ( strcmp ( lockname , " P_global " ) = = 0 )
do_refresh_cache ( ) ;
2004-06-24 12:02:38 +04:00
break ;
case CLVMD_CMD_LOCK_LV :
/* This is the biggie */
2005-08-16 12:25:09 +04:00
lock_cmd = args [ 0 ] & 0x3F ;
2004-06-24 12:02:38 +04:00
lock_flags = args [ 1 ] ;
lockname = & args [ 2 ] ;
status = do_lock_lv ( lock_cmd , lock_flags , lockname ) ;
/* Replace EIO with something less scary */
if ( status = = EIO ) {
* retlen =
2007-05-02 16:22:40 +04:00
1 + snprintf ( * buf , buflen , " %s " ,
2006-10-05 17:55:50 +04:00
get_last_lvm_error ( ) ) ;
2004-06-24 12:02:38 +04:00
return EIO ;
}
break ;
2006-10-04 12:22:16 +04:00
case CLVMD_CMD_REFRESH :
do_refresh_cache ( ) ;
break ;
2007-08-17 15:51:23 +04:00
case CLVMD_CMD_SET_DEBUG :
debug = args [ 0 ] ;
break ;
2006-10-09 18:11:57 +04:00
case CLVMD_CMD_GET_CLUSTERNAME :
status = clops - > get_cluster_name ( * buf , buflen ) ;
if ( ! status )
2007-05-02 16:22:40 +04:00
* retlen = strlen ( * buf ) + 1 ;
2006-10-09 18:11:57 +04:00
break ;
2007-12-04 18:39:26 +03:00
case CLVMD_CMD_VG_BACKUP :
lvm_do_backup ( & args [ 2 ] ) ;
break ;
2004-06-24 12:02:38 +04:00
default :
/* Won't get here because command is validated in pre_command */
break ;
}
/* Check the status of the command and return the error text */
if ( status ) {
2007-05-02 16:22:40 +04:00
* retlen = 1 + snprintf ( * buf , buflen , " %s " , strerror ( status ) ) ;
2004-06-24 12:02:38 +04:00
}
return status ;
}
2004-09-24 13:39:57 +04:00
static int lock_vg ( struct local_client * client )
{
2005-11-09 12:24:10 +03:00
struct dm_hash_table * lock_hash ;
2004-09-24 13:39:57 +04:00
struct clvm_header * header =
( struct clvm_header * ) client - > bits . localsock . cmd ;
unsigned char lock_cmd ;
unsigned char lock_flags ;
char * args = header - > node + strlen ( header - > node ) + 1 ;
int lkid ;
int status = 0 ;
char * lockname ;
/* Keep a track of VG locks in our own hash table. In current
practice there should only ever be more than two VGs locked
if a user tries to merge lots of them at once */
if ( client - > bits . localsock . private ) {
2005-11-09 12:24:10 +03:00
lock_hash = ( struct dm_hash_table * ) client - > bits . localsock . private ;
2004-09-24 13:39:57 +04:00
}
else {
2005-11-09 12:24:10 +03:00
lock_hash = dm_hash_create ( 3 ) ;
2004-09-24 13:39:57 +04:00
if ( ! lock_hash )
return ENOMEM ;
client - > bits . localsock . private = ( void * ) lock_hash ;
}
2005-08-16 12:25:09 +04:00
lock_cmd = args [ 0 ] & 0x3F ;
2004-09-24 13:39:57 +04:00
lock_flags = args [ 1 ] ;
lockname = & args [ 2 ] ;
DEBUGLOG ( " doing PRE command LOCK_VG '%s' at %x (client=%p) \n " , lockname , lock_cmd , client ) ;
if ( lock_cmd = = LCK_UNLOCK ) {
2005-11-09 12:24:10 +03:00
lkid = ( int ) ( long ) dm_hash_lookup ( lock_hash , lockname ) ;
2004-09-24 13:39:57 +04:00
if ( lkid = = 0 )
return EINVAL ;
status = sync_unlock ( lockname , lkid ) ;
if ( status )
status = errno ;
else
2005-11-09 12:24:10 +03:00
dm_hash_remove ( lock_hash , lockname ) ;
2004-09-24 13:39:57 +04:00
}
else {
2006-12-02 01:48:47 +03:00
/* Read locks need to be PR; other modes get passed through */
if ( ( lock_cmd & LCK_TYPE_MASK ) = = LCK_READ ) {
lock_cmd & = ~ LCK_TYPE_MASK ;
2006-12-11 17:00:26 +03:00
lock_cmd | = LCK_PREAD ;
2006-12-02 01:48:47 +03:00
}
2006-05-12 23:16:48 +04:00
status = sync_lock ( lockname , ( int ) lock_cmd , ( lock_flags & LCK_NONBLOCK ) ? LKF_NOQUEUE : 0 , & lkid ) ;
2004-09-24 13:39:57 +04:00
if ( status )
status = errno ;
else
2006-12-02 02:10:26 +03:00
dm_hash_insert ( lock_hash , lockname , ( void * ) ( long ) lkid ) ;
2004-09-24 13:39:57 +04:00
}
return status ;
}
2004-06-24 12:02:38 +04:00
/* Pre-command is a good place to get locks that are needed only for the duration
of the commands around the cluster ( don ' t forget to free them in post - command ) ,
and to sanity check the command arguments */
int do_pre_command ( struct local_client * client )
{
struct clvm_header * header =
( struct clvm_header * ) client - > bits . localsock . cmd ;
unsigned char lock_cmd ;
unsigned char lock_flags ;
char * args = header - > node + strlen ( header - > node ) + 1 ;
int lockid ;
int status = 0 ;
char * lockname ;
switch ( header - > cmd ) {
case CLVMD_CMD_TEST :
status = sync_lock ( " CLVMD_TEST " , LKM_EXMODE , 0 , & lockid ) ;
2006-12-02 02:10:26 +03:00
client - > bits . localsock . private = ( void * ) ( long ) lockid ;
2004-06-24 12:02:38 +04:00
break ;
case CLVMD_CMD_LOCK_VG :
2004-09-24 13:39:57 +04:00
status = lock_vg ( client ) ;
2004-06-24 12:02:38 +04:00
break ;
case CLVMD_CMD_LOCK_LV :
lock_cmd = args [ 0 ] ;
lock_flags = args [ 1 ] ;
lockname = & args [ 2 ] ;
status = pre_lock_lv ( lock_cmd , lock_flags , lockname ) ;
break ;
2006-10-04 12:22:16 +04:00
case CLVMD_CMD_REFRESH :
2006-10-09 18:11:57 +04:00
case CLVMD_CMD_GET_CLUSTERNAME :
2007-08-17 15:51:23 +04:00
case CLVMD_CMD_SET_DEBUG :
2007-12-04 18:39:26 +03:00
case CLVMD_CMD_VG_BACKUP :
2006-10-04 12:22:16 +04:00
break ;
2004-06-24 12:02:38 +04:00
default :
log_error ( " Unknown command %d received \n " , header - > cmd ) ;
status = EINVAL ;
}
return status ;
}
/* Note that the post-command routine is called even if the pre-command or the real command
failed */
int do_post_command ( struct local_client * client )
{
struct clvm_header * header =
( struct clvm_header * ) client - > bits . localsock . cmd ;
int status = 0 ;
unsigned char lock_cmd ;
unsigned char lock_flags ;
char * args = header - > node + strlen ( header - > node ) + 1 ;
char * lockname ;
switch ( header - > cmd ) {
case CLVMD_CMD_TEST :
status =
sync_unlock ( " CLVMD_TEST " , ( int ) ( long ) client - > bits . localsock . private ) ;
2004-09-24 13:39:57 +04:00
client - > bits . localsock . private = 0 ;
2004-06-24 12:02:38 +04:00
break ;
case CLVMD_CMD_LOCK_VG :
2007-12-04 18:39:26 +03:00
case CLVMD_CMD_VG_BACKUP :
2004-06-24 12:02:38 +04:00
/* Nothing to do here */
break ;
case CLVMD_CMD_LOCK_LV :
lock_cmd = args [ 0 ] ;
lock_flags = args [ 1 ] ;
lockname = & args [ 2 ] ;
status = post_lock_lv ( lock_cmd , lock_flags , lockname ) ;
break ;
}
return status ;
}
2004-09-24 13:39:57 +04:00
/* Called when the client is about to be deleted */
void cmd_client_cleanup ( struct local_client * client )
{
if ( client - > bits . localsock . private ) {
2005-11-09 12:24:10 +03:00
struct dm_hash_node * v ;
struct dm_hash_table * lock_hash =
( struct dm_hash_table * ) client - > bits . localsock . private ;
2004-09-24 13:39:57 +04:00
2005-11-09 12:24:10 +03:00
dm_hash_iterate ( v , lock_hash ) {
int lkid = ( int ) ( long ) dm_hash_get_data ( lock_hash , v ) ;
char * lockname = dm_hash_get_key ( lock_hash , v ) ;
2004-09-24 13:39:57 +04:00
2005-03-07 20:03:44 +03:00
DEBUGLOG ( " cleanup: Unlocking lock %s %x \n " , lockname , lkid ) ;
sync_unlock ( lockname , lkid ) ;
2004-09-24 13:39:57 +04:00
}
2005-11-09 12:24:10 +03:00
dm_hash_destroy ( lock_hash ) ;
2004-09-24 13:39:57 +04:00
client - > bits . localsock . private = 0 ;
}
}