2008-01-09 18:32:19 +03:00
/*
2015-10-10 17:58:31 +03:00
* Copyright ( C ) 2007 - 2015 Red Hat , Inc . All rights reserved .
2008-01-09 18:32:19 +03:00
*
* 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
*/
2010-01-22 03:18:37 +03:00
# include "lib.h"
2010-01-22 01:15:45 +03:00
# include "dmeventd_lvm.h"
2015-10-10 17:58:31 +03:00
# include "libdevmapper-event.h"
2010-01-22 01:15:45 +03:00
2010-10-29 20:43:51 +04:00
# include <sys/wait.h>
2014-04-15 15:27:47 +04:00
# include <stdarg.h>
2015-10-22 16:36:21 +03:00
# include <pthread.h>
2008-01-09 18:32:19 +03:00
/* First warning when snapshot is 80% full. */
2015-10-22 16:35:55 +03:00
# define WARNING_THRESH (DM_PERCENT_1 * 80)
2010-10-15 20:28:14 +04:00
/* Run a check every 5%. */
2015-10-22 16:35:55 +03:00
# define CHECK_STEP (DM_PERCENT_1 * 5)
2010-10-15 20:28:14 +04:00
/* Do not bother checking snapshots less than 50% full. */
2015-10-22 16:35:55 +03:00
# define CHECK_MINIMUM (DM_PERCENT_1 * 50)
2008-01-09 18:32:19 +03:00
2010-10-29 20:43:51 +04:00
# define UMOUNT_COMMAND " / bin / umount"
2011-11-21 16:31:18 +04:00
struct dso_state {
2013-05-26 19:07:08 +04:00
struct dm_pool * mem ;
2015-10-22 16:35:55 +03:00
dm_percent_t percent_check ;
2013-05-26 19:07:08 +04:00
uint64_t known_size ;
2015-10-13 12:30:37 +03:00
char cmd_lvextend [ 512 ] ;
2011-11-21 16:31:18 +04:00
} ;
2015-10-10 17:58:31 +03:00
DM_EVENT_LOG_FN ( " snap " )
2010-10-29 20:43:51 +04:00
static int _run ( const char * cmd , . . . )
{
va_list ap ;
int argc = 1 ; /* for argv[0], i.e. cmd */
int i = 0 ;
const char * * argv ;
pid_t pid = fork ( ) ;
int status ;
if ( pid = = 0 ) { /* child */
va_start ( ap , cmd ) ;
while ( va_arg ( ap , const char * ) )
+ + argc ;
va_end ( ap ) ;
/* + 1 for the terminating NULL */
argv = alloca ( sizeof ( const char * ) * ( argc + 1 ) ) ;
argv [ 0 ] = cmd ;
va_start ( ap , cmd ) ;
while ( ( argv [ + + i ] = va_arg ( ap , const char * ) ) ) ;
va_end ( ap ) ;
execvp ( cmd , ( char * * ) argv ) ;
2015-10-09 22:57:48 +03:00
log_sys_error ( " exec " , cmd ) ;
2010-10-29 20:43:51 +04:00
exit ( 127 ) ;
}
if ( pid > 0 ) { /* parent */
if ( waitpid ( pid , & status , 0 ) ! = pid )
return 0 ; /* waitpid failed */
if ( ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) )
return 0 ; /* the child failed */
}
if ( pid < 0 )
return 0 ; /* fork failed */
return 1 ; /* all good */
}
2011-12-22 20:37:01 +04:00
static int _extend ( const char * cmd )
2010-10-15 20:28:14 +04:00
{
2015-10-13 12:35:03 +03:00
log_debug ( " Extending snapshot via %s. " , cmd ) ;
return dmeventd_lvm2_run_with_lock ( cmd ) ;
2010-10-15 20:28:14 +04:00
}
2015-10-22 16:38:24 +03:00
# ifdef SNAPSHOT_REMOVE
/* Remove invalid snapshot from dm-table */
/* Experimental for now and not used by default */
static int _remove ( const char * uuid )
{
int r = 1 ;
uint32_t cookie = 0 ;
struct dm_task * dmt ;
if ( ! ( dmt = dm_task_create ( DM_DEVICE_REMOVE ) ) )
return 0 ;
if ( ! dm_task_set_uuid ( dmt , uuid ) ) {
r = 0 ;
goto_out ;
}
dm_task_retry_remove ( dmt ) ;
if ( ! dm_task_set_cookie ( dmt , & cookie , 0 ) ) {
r = 0 ;
goto_out ;
}
if ( ! dm_task_run ( dmt ) ) {
r = 0 ;
goto_out ;
}
out :
dm_task_destroy ( dmt ) ;
return r ;
}
# endif /* SNAPSHOT_REMOVE */
2010-10-29 20:43:51 +04:00
static void _umount ( const char * device , int major , int minor )
{
FILE * mounts ;
char buffer [ 4096 ] ;
char * words [ 3 ] ;
struct stat st ;
2015-10-09 22:57:48 +03:00
const char procmounts [ ] = " /proc/mounts " ;
2010-10-29 20:43:51 +04:00
2015-10-09 22:57:48 +03:00
if ( ! ( mounts = fopen ( procmounts , " r " ) ) ) {
log_sys_error ( " fopen " , procmounts ) ;
log_error ( " Not umounting %s. " , device ) ;
2010-10-29 20:43:51 +04:00
return ;
}
while ( ! feof ( mounts ) ) {
/* read a line of /proc/mounts */
if ( ! fgets ( buffer , sizeof ( buffer ) , mounts ) )
break ; /* eof, likely */
/* words[0] is the mount point and words[1] is the device path */
2012-02-13 18:17:04 +04:00
if ( dm_split_words ( buffer , 3 , 0 , words ) < 2 )
continue ;
2010-10-29 20:43:51 +04:00
/* find the major/minor of the device */
if ( stat ( words [ 0 ] , & st ) )
continue ; /* can't stat, skip this one */
if ( S_ISBLK ( st . st_mode ) & &
major ( st . st_rdev ) = = major & &
minor ( st . st_rdev ) = = minor ) {
2015-10-09 22:57:48 +03:00
log_error ( " Unmounting invalid snapshot %s from %s. " , device , words [ 1 ] ) ;
if ( ! _run ( UMOUNT_COMMAND , " -fl " , words [ 1 ] , NULL ) )
log_error ( " Failed to umount snapshot %s from %s: %s. " ,
device , words [ 1 ] , strerror ( errno ) ) ;
2010-10-29 20:43:51 +04:00
}
}
2010-11-24 00:19:45 +03:00
if ( fclose ( mounts ) )
2015-10-09 22:57:48 +03:00
log_sys_error ( " close " , procmounts ) ;
2010-10-29 20:43:51 +04:00
}
2008-01-31 15:19:36 +03:00
void process_event ( struct dm_task * dmt ,
2010-07-09 19:34:40 +04:00
enum dm_event_mask event __attribute__ ( ( unused ) ) ,
2015-10-12 12:40:51 +03:00
void * * user )
2008-01-09 18:32:19 +03:00
{
2015-10-12 12:40:51 +03:00
struct dso_state * state = * user ;
2008-01-09 18:32:19 +03:00
void * next = NULL ;
uint64_t start , length ;
char * target_type = NULL ;
char * params ;
2013-05-26 19:07:08 +04:00
struct dm_status_snapshot * status = NULL ;
2008-01-09 18:32:19 +03:00
const char * device = dm_task_get_name ( dmt ) ;
2013-05-29 14:29:48 +04:00
int percent ;
2015-10-22 16:35:55 +03:00
struct dm_info info ;
2008-01-09 18:32:19 +03:00
/* No longer monitoring, waiting for remove */
2011-11-21 16:31:18 +04:00
if ( ! state - > percent_check )
2008-01-09 18:32:19 +03:00
return ;
dm_get_next_target ( dmt , next , & start , & length , & target_type , & params ) ;
2015-10-13 12:35:03 +03:00
if ( ! target_type | | strcmp ( target_type , " snapshot " ) ) {
log_error ( " Target %s is not snapshot. " , target_type ) ;
return ;
}
2008-01-09 18:32:19 +03:00
2015-10-22 16:35:55 +03:00
if ( ! dm_get_status_snapshot ( state - > mem , params , & status ) ) {
log_error ( " Cannot parse snapshot %s state: %s. " , device , params ) ;
2015-10-13 12:35:03 +03:00
return ;
2011-11-21 16:31:18 +04:00
}
2008-01-09 18:32:19 +03:00
/*
* If the snapshot has been invalidated or we failed to parse
* the status string . Report the full status string to syslog .
*/
2015-10-09 22:41:50 +03:00
if ( status - > invalid | | status - > overflow | | ! status - > total_sectors ) {
2015-10-22 16:35:55 +03:00
log_warn ( " WARNING: Snapshot %s changed state to: %s and should be removed. " ,
device , params ) ;
2011-11-21 16:31:18 +04:00
state - > percent_check = 0 ;
2015-10-22 16:35:55 +03:00
if ( dm_task_get_info ( dmt , & info ) )
_umount ( device , info . major , info . minor ) ;
2015-10-22 16:38:24 +03:00
# ifdef SNAPSHOT_REMOVE
/* Maybe configurable ? */
_remove ( dm_task_get_uuid ( dmt ) ) ;
# endif
2015-10-22 16:36:21 +03:00
pthread_kill ( pthread_self ( ) , SIGALRM ) ;
goto out ;
}
if ( length < = ( status - > used_sectors - status - > metadata_sectors ) ) {
/* TODO eventually recognize earlier when room is enough */
log_info ( " Dropping monitoring of fully provisioned snapshot %s. " ,
device ) ;
pthread_kill ( pthread_self ( ) , SIGALRM ) ;
2008-01-09 18:32:19 +03:00
goto out ;
}
2015-10-22 16:35:55 +03:00
/* Snapshot size had changed. Clear the threshold. */
if ( state - > known_size ! = status - > total_sectors ) {
state - > percent_check = CHECK_MINIMUM ;
state - > known_size = status - > total_sectors ;
}
percent = dm_make_percent ( status - > used_sectors , status - > total_sectors ) ;
2011-11-21 16:31:18 +04:00
if ( percent > = state - > percent_check ) {
2010-10-15 20:28:14 +04:00
/* Usage has raised more than CHECK_STEP since the last
time . Run actions . */
2011-11-21 16:31:18 +04:00
state - > percent_check = ( percent / CHECK_STEP ) * CHECK_STEP + CHECK_STEP ;
2010-10-15 20:28:14 +04:00
if ( percent > = WARNING_THRESH ) /* Print a warning to syslog. */
2015-10-22 16:35:55 +03:00
log_warn ( " WARNING: Snapshot %s is now %.2f%% full. " ,
device , dm_percent_to_float ( percent ) ) ;
2015-10-13 12:30:37 +03:00
2010-10-15 20:28:14 +04:00
/* Try to extend the snapshot, in accord with user-set policies */
2015-10-13 12:30:37 +03:00
if ( ! _extend ( state - > cmd_lvextend ) )
2015-10-09 22:57:48 +03:00
log_error ( " Failed to extend snapshot %s. " , device ) ;
2008-01-09 18:32:19 +03:00
}
out :
2015-10-13 12:35:03 +03:00
dm_pool_free ( state - > mem , status ) ;
2008-01-09 18:32:19 +03:00
}
2008-01-31 15:19:36 +03:00
int register_device ( const char * device ,
2010-07-09 19:34:40 +04:00
const char * uuid __attribute__ ( ( unused ) ) ,
int major __attribute__ ( ( unused ) ) ,
int minor __attribute__ ( ( unused ) ) ,
2015-10-12 12:40:51 +03:00
void * * user )
2008-01-09 18:32:19 +03:00
{
2011-12-22 20:37:01 +04:00
struct dso_state * state ;
2015-10-13 23:10:47 +03:00
if ( ! dmeventd_lvm2_init_with_pool ( " snapshot_state " , state ) )
2015-10-13 12:30:37 +03:00
goto_bad ;
2011-11-21 16:31:18 +04:00
2015-10-13 12:30:37 +03:00
if ( ! dmeventd_lvm2_command ( state - > mem , state - > cmd_lvextend ,
sizeof ( state - > cmd_lvextend ) ,
" lvextend --use-policies " , device ) ) {
dmeventd_lvm2_exit_with_pool ( state ) ;
goto_bad ;
}
2011-12-22 20:37:01 +04:00
state - > percent_check = CHECK_MINIMUM ;
2015-10-12 12:40:51 +03:00
* user = state ;
2008-01-09 18:32:19 +03:00
2015-10-09 22:57:48 +03:00
log_info ( " Monitoring snapshot %s. " , device ) ;
2011-12-22 20:37:01 +04:00
return 1 ;
bad :
2015-10-09 22:57:48 +03:00
log_error ( " Failed to monitor snapshot %s. " , device ) ;
2011-12-22 20:37:01 +04:00
return 0 ;
2008-01-09 18:32:19 +03:00
}
2008-01-31 15:19:36 +03:00
int unregister_device ( const char * device ,
2010-07-09 19:34:40 +04:00
const char * uuid __attribute__ ( ( unused ) ) ,
int major __attribute__ ( ( unused ) ) ,
int minor __attribute__ ( ( unused ) ) ,
2015-10-12 12:40:51 +03:00
void * * user )
2008-01-09 18:32:19 +03:00
{
2015-10-12 12:40:51 +03:00
struct dso_state * state = * user ;
2011-11-21 16:31:18 +04:00
2015-10-13 12:30:37 +03:00
dmeventd_lvm2_exit_with_pool ( state ) ;
2015-10-09 22:57:48 +03:00
log_info ( " No longer monitoring snapshot %s. " , device ) ;
2011-12-22 20:37:01 +04:00
2008-01-09 18:32:19 +03:00
return 1 ;
}