2002-02-11 18:42:34 +03:00
/*
* Copyright ( C ) 2001 Sistina Software ( UK ) Limited .
*
* This file is released under the LGPL .
*
*/
2002-11-18 17:01:16 +03:00
# include "lib.h"
2002-02-11 18:42:34 +03:00
# include "locking.h"
# include "locking_types.h"
# include "activate.h"
# include "config.h"
# include "defaults.h"
# include "lvm-file.h"
# include "lvm-string.h"
# include <limits.h>
# include <unistd.h>
# include <sys/stat.h>
# include <sys/file.h>
# include <fcntl.h>
# include <signal.h>
struct lock_list {
struct list list ;
int lf ;
char * res ;
} ;
static struct list _lock_list ;
static char _lock_dir [ NAME_LEN ] ;
2002-03-15 19:07:38 +03:00
static sig_t _oldhandler ;
static sigset_t _fullsigset , _intsigset ;
static int _handler_installed ;
2002-02-11 18:42:34 +03:00
static int _release_lock ( const char * file )
{
struct lock_list * ll ;
struct list * llh , * llt ;
2002-03-15 19:07:38 +03:00
struct stat buf1 , buf2 ;
2002-02-11 18:42:34 +03:00
list_iterate_safe ( llh , llt , & _lock_list ) {
ll = list_item ( llh , struct lock_list ) ;
if ( ! file | | ! strcmp ( ll - > res , file ) ) {
list_del ( llh ) ;
log_very_verbose ( " Unlocking %s " , ll - > res ) ;
2002-03-15 19:07:38 +03:00
if ( flock ( ll - > lf , LOCK_NB | LOCK_UN ) )
log_sys_error ( " flock " , ll - > res ) ;
if ( ! flock ( ll - > lf , LOCK_NB | LOCK_EX ) & &
! stat ( ll - > res , & buf1 ) & &
! fstat ( ll - > lf , & buf2 ) & &
! memcmp ( & buf1 . st_ino , & buf2 . st_ino , sizeof ( ino_t ) ) )
2002-02-11 18:42:34 +03:00
if ( unlink ( ll - > res ) )
log_sys_error ( " unlink " , ll - > res ) ;
if ( close ( ll - > lf ) < 0 )
log_sys_error ( " close " , ll - > res ) ;
dbg_free ( ll - > res ) ;
dbg_free ( llh ) ;
if ( file )
return 1 ;
}
}
return 0 ;
}
2002-12-20 02:25:55 +03:00
static void _fin_file_locking ( void )
2002-02-11 18:42:34 +03:00
{
_release_lock ( NULL ) ;
}
2002-03-15 19:07:38 +03:00
static void _remove_ctrl_c_handler ( )
{
siginterrupt ( SIGINT , 0 ) ;
if ( ! _handler_installed | | _oldhandler = = SIG_ERR )
return ;
sigprocmask ( SIG_SETMASK , & _fullsigset , NULL ) ;
if ( signal ( SIGINT , _oldhandler ) = = SIG_ERR )
log_sys_error ( " signal " , " _remove_ctrl_c_handler " ) ;
_handler_installed = 0 ;
}
2002-12-20 02:25:55 +03:00
static void _trap_ctrl_c ( int sig )
2002-02-11 18:42:34 +03:00
{
2002-03-15 19:07:38 +03:00
_remove_ctrl_c_handler ( ) ;
2002-02-11 18:42:34 +03:00
log_error ( " CTRL-c detected: giving up waiting for lock " ) ;
return ;
}
static void _install_ctrl_c_handler ( )
{
2002-03-15 19:07:38 +03:00
if ( ( _oldhandler = signal ( SIGINT , _trap_ctrl_c ) ) = = SIG_ERR )
return ;
sigprocmask ( SIG_SETMASK , & _intsigset , NULL ) ;
2002-02-11 18:42:34 +03:00
siginterrupt ( SIGINT , 1 ) ;
2002-03-15 19:07:38 +03:00
_handler_installed = 1 ;
2002-02-11 18:42:34 +03:00
}
static int _lock_file ( const char * file , int flags )
{
int operation ;
int r = 1 ;
struct lock_list * ll ;
2002-03-15 19:07:38 +03:00
struct stat buf1 , buf2 ;
2002-11-18 17:01:16 +03:00
char state ;
2002-02-11 18:42:34 +03:00
switch ( flags & LCK_TYPE_MASK ) {
case LCK_READ :
operation = LOCK_SH ;
2002-11-18 17:01:16 +03:00
state = ' R ' ;
2002-02-11 18:42:34 +03:00
break ;
case LCK_WRITE :
operation = LOCK_EX ;
2002-11-18 17:01:16 +03:00
state = ' W ' ;
2002-02-11 18:42:34 +03:00
break ;
2002-04-04 15:18:45 +04:00
case LCK_UNLOCK :
2002-02-11 18:42:34 +03:00
return _release_lock ( file ) ;
default :
log_error ( " Unrecognised lock type: %d " , flags & LCK_TYPE_MASK ) ;
return 0 ;
}
if ( ! ( ll = dbg_malloc ( sizeof ( struct lock_list ) ) ) )
return 0 ;
if ( ! ( ll - > res = dbg_strdup ( file ) ) ) {
dbg_free ( ll ) ;
return 0 ;
}
2002-03-15 19:07:38 +03:00
ll - > lf = - 1 ;
2002-11-18 17:01:16 +03:00
log_very_verbose ( " Locking %s %c%c " , ll - > res , state ,
flags & LCK_NONBLOCK ? ' ' : ' B ' ) ;
2002-03-15 19:07:38 +03:00
do {
if ( ll - > lf > - 1 )
close ( ll - > lf ) ;
if ( ( ll - > lf = open ( file , O_CREAT | O_APPEND | O_RDWR , 0777 ) )
< 0 ) {
log_sys_error ( " open " , file ) ;
goto err ;
}
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
if ( ( flags & LCK_NONBLOCK ) )
operation | = LOCK_NB ;
else
_install_ctrl_c_handler ( ) ;
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
r = flock ( ll - > lf , operation ) ;
if ( ! ( flags & LCK_NONBLOCK ) )
_remove_ctrl_c_handler ( ) ;
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
if ( r ) {
log_sys_error ( " flock " , ll - > res ) ;
goto err ;
}
if ( ! stat ( ll - > res , & buf1 ) & & ! fstat ( ll - > lf , & buf2 ) & &
! memcmp ( & buf1 . st_ino , & buf2 . st_ino , sizeof ( ino_t ) ) )
break ;
} while ( ! ( flags & LCK_NONBLOCK ) ) ;
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
list_add ( & _lock_list , & ll - > list ) ;
return 1 ;
2002-02-11 18:42:34 +03:00
2002-03-15 19:07:38 +03:00
err :
dbg_free ( ll - > res ) ;
dbg_free ( ll ) ;
return 0 ;
2002-02-11 18:42:34 +03:00
}
2002-12-20 02:25:55 +03:00
static int _file_lock_resource ( struct cmd_context * cmd , const char * resource ,
int flags )
2002-02-11 18:42:34 +03:00
{
char lockfile [ PATH_MAX ] ;
switch ( flags & LCK_SCOPE_MASK ) {
case LCK_VG :
if ( ! resource | | ! * resource )
lvm_snprintf ( lockfile , sizeof ( lockfile ) ,
" %s/P_orphans " , _lock_dir ) ;
else
lvm_snprintf ( lockfile , sizeof ( lockfile ) ,
" %s/V_%s " , _lock_dir , resource ) ;
2002-02-25 15:56:16 +03:00
if ( ! _lock_file ( lockfile , flags ) )
return 0 ;
2002-02-11 18:42:34 +03:00
break ;
case LCK_LV :
2002-02-21 00:30:27 +03:00
switch ( flags & LCK_TYPE_MASK ) {
2002-04-04 15:18:45 +04:00
case LCK_UNLOCK :
2002-02-25 15:56:16 +03:00
if ( ! lv_resume_if_active ( cmd , resource ) )
return 0 ;
2002-02-21 00:30:27 +03:00
break ;
2002-02-27 15:26:41 +03:00
case LCK_READ :
2002-03-11 18:08:39 +03:00
if ( ! lv_activate ( cmd , resource ) )
2002-02-27 15:26:41 +03:00
return 0 ;
break ;
2002-02-21 00:30:27 +03:00
case LCK_WRITE :
2002-02-25 15:56:16 +03:00
if ( ! lv_suspend_if_active ( cmd , resource ) )
return 0 ;
2002-02-21 00:30:27 +03:00
break ;
2002-02-27 15:26:41 +03:00
case LCK_EXCL :
2002-03-11 18:08:39 +03:00
if ( ! lv_deactivate ( cmd , resource ) )
2002-02-27 15:26:41 +03:00
return 0 ;
break ;
2002-02-21 00:30:27 +03:00
default :
break ;
}
2002-02-25 15:56:16 +03:00
break ;
default :
log_error ( " Unrecognised lock scope: %d " ,
flags & LCK_SCOPE_MASK ) ;
return 0 ;
2002-02-21 00:30:27 +03:00
}
2002-02-25 15:56:16 +03:00
return 1 ;
}
2002-11-18 17:01:16 +03:00
int init_file_locking ( struct locking_type * locking , struct config_tree * cf )
2002-02-11 18:42:34 +03:00
{
2002-12-20 02:25:55 +03:00
locking - > lock_resource = _file_lock_resource ;
locking - > fin_locking = _fin_file_locking ;
2002-02-11 18:42:34 +03:00
/* Get lockfile directory from config file */
strncpy ( _lock_dir , find_config_str ( cf - > root , " global/locking_dir " ,
' / ' , DEFAULT_LOCK_DIR ) ,
sizeof ( _lock_dir ) ) ;
if ( ! create_dir ( _lock_dir ) )
return 0 ;
2002-07-11 00:43:32 +04:00
/* Trap a read-only file system */
if ( ( access ( _lock_dir , R_OK | W_OK | X_OK ) = = - 1 ) & & ( errno = = EROFS ) )
return 0 ;
2002-02-11 18:42:34 +03:00
list_init ( & _lock_list ) ;
2002-03-15 19:07:38 +03:00
if ( sigfillset ( & _intsigset ) | | sigfillset ( & _fullsigset ) ) {
log_sys_error ( " sigfillset " , " init_file_locking " ) ;
return 0 ;
}
if ( sigdelset ( & _intsigset , SIGINT ) ) {
log_sys_error ( " sigdelset " , " init_file_locking " ) ;
return 0 ;
}
2002-02-11 18:42:34 +03:00
return 1 ;
}