2008-01-09 18:32:19 +03:00
/*
2010-05-11 12:32:22 +04:00
* Copyright ( C ) 2007 - 2010 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
2008-01-09 18:32:19 +03:00
# include "lvm2cmd.h"
2010-01-22 01:15:45 +03:00
# include "errors.h"
# include "libdevmapper-event.h"
# include "dmeventd_lvm.h"
2008-01-09 18:32:19 +03:00
# include "lvm-string.h"
2010-10-29 20:43:51 +04:00
# include <sys/wait.h>
2008-01-09 18:32:19 +03:00
# include <syslog.h> /* FIXME Replace syslog with multilog */
/* FIXME Missing openlog? */
/* First warning when snapshot is 80% full. */
# define WARNING_THRESH 80
2010-10-15 20:28:14 +04:00
/* Run a check every 5%. */
# define CHECK_STEP 5
/* Do not bother checking snapshots less than 50% full. */
# define CHECK_MINIMUM 50
2008-01-09 18:32:19 +03:00
2010-10-29 20:43:51 +04:00
# define UMOUNT_COMMAND " / bin / umount"
2008-01-09 18:32:19 +03:00
struct snap_status {
int invalid ;
int used ;
int max ;
} ;
/* FIXME possibly reconcile this with target_percent when we gain
access to regular LVM library here . */
2010-02-15 15:55:20 +03:00
static void _parse_snapshot_params ( char * params , struct snap_status * status )
2008-01-09 18:32:19 +03:00
{
char * p ;
/*
* xx / xx - - fractions used / max
* Invalid - - snapshot invalidated
* Unknown - - status unknown
*/
2010-02-15 15:55:20 +03:00
status - > used = status - > max = 0 ;
2008-01-09 18:32:19 +03:00
if ( ! strncmp ( params , " Invalid " , 7 ) ) {
2010-02-15 15:55:20 +03:00
status - > invalid = 1 ;
2008-01-09 18:32:19 +03:00
return ;
}
/*
* When we return without setting non - zero max , the parent is
* responsible for reporting errors .
*/
if ( ! strncmp ( params , " Unknown " , 7 ) )
return ;
if ( ! ( p = strstr ( params , " / " ) ) )
return ;
* p = ' \0 ' ;
p + + ;
2010-02-15 15:55:20 +03:00
status - > used = atoi ( params ) ;
status - > max = atoi ( p ) ;
2008-01-09 18:32:19 +03:00
}
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 ) ;
syslog ( LOG_ERR , " Failed to execute %s: %s. \n " , cmd , strerror ( errno ) ) ;
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 */
}
2010-10-15 20:28:14 +04:00
static int _extend ( const char * device )
{
char * vg = NULL , * lv = NULL , * layer = NULL ;
char cmd_str [ 1024 ] ;
int r = 0 ;
if ( ! dm_split_lvm_name ( dmeventd_lvm2_pool ( ) , device , & vg , & lv , & layer ) ) {
syslog ( LOG_ERR , " Unable to determine VG name from %s. " , device ) ;
return 0 ;
}
if ( sizeof ( cmd_str ) < = snprintf ( cmd_str , sizeof ( cmd_str ) ,
" lvextend --use-policies %s/%s " , vg , lv ) ) {
syslog ( LOG_ERR , " Unable to form LVM command: Device name too long. " ) ;
return 0 ;
}
r = dmeventd_lvm2_run ( cmd_str ) ;
syslog ( LOG_INFO , " Extension of snapshot %s/%s %s. " , vg , lv ,
( r = = ECMD_PROCESSED ) ? " finished successfully " : " failed " ) ;
return r = = ECMD_PROCESSED ;
}
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 ;
if ( ! ( mounts = fopen ( " /proc/mounts " , " r " ) ) ) {
syslog ( LOG_ERR , " Could not read /proc/mounts. Not umounting %s. \n " , device ) ;
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 */
dm_split_words ( buffer , 3 , 0 , words ) ;
/* 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 ) {
syslog ( LOG_ERR , " Unmounting invalid snapshot %s from %s. " , device , words [ 1 ] ) ;
if ( ! _run ( UMOUNT_COMMAND , " -fl " , words [ 1 ] , NULL ) )
syslog ( LOG_ERR , " Failed to umount snapshot %s from %s: %s. " ,
device , words [ 1 ] , strerror ( errno ) ) ;
}
}
2010-11-24 00:19:45 +03:00
if ( fclose ( mounts ) )
syslog ( LOG_ERR , " Failed to close /proc/mounts. \n " ) ;
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 ) ) ,
2008-01-09 18:32:19 +03:00
void * * private )
{
void * next = NULL ;
uint64_t start , length ;
char * target_type = NULL ;
char * params ;
2010-02-15 15:55:20 +03:00
struct snap_status status = { 0 } ;
2008-01-09 18:32:19 +03:00
const char * device = dm_task_get_name ( dmt ) ;
2010-10-15 20:28:14 +04:00
int percent , * percent_check = ( int * ) private ;
2008-01-09 18:32:19 +03:00
/* No longer monitoring, waiting for remove */
2010-10-15 20:28:14 +04:00
if ( ! * percent_check )
2008-01-09 18:32:19 +03:00
return ;
2010-01-22 01:15:45 +03:00
dmeventd_lvm2_lock ( ) ;
2008-01-09 18:32:19 +03:00
dm_get_next_target ( dmt , next , & start , & length , & target_type , & params ) ;
if ( ! target_type )
goto out ;
2010-02-15 15:55:20 +03:00
_parse_snapshot_params ( params , & status ) ;
2010-10-15 20:28:14 +04:00
2010-10-29 20:43:51 +04:00
if ( status . invalid ) {
struct dm_info info ;
if ( dm_task_get_info ( dmt , & info ) ) {
dmeventd_lvm2_unlock ( ) ;
_umount ( device , info . major , info . minor ) ;
return ;
} /* else; too bad, but this is best-effort thing... */
}
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 .
*/
2010-02-15 15:55:20 +03:00
if ( status . invalid | | ! status . max ) {
2008-01-09 18:32:19 +03:00
syslog ( LOG_ERR , " Snapshot %s changed state to: %s \n " , device , params ) ;
2010-10-15 20:28:14 +04:00
* percent_check = 0 ;
2008-01-09 18:32:19 +03:00
goto out ;
}
2010-02-15 15:55:20 +03:00
percent = 100 * status . used / status . max ;
2010-10-15 20:28:14 +04:00
if ( percent > = * percent_check ) {
/* Usage has raised more than CHECK_STEP since the last
time . Run actions . */
* percent_check = ( percent / CHECK_STEP ) * CHECK_STEP + CHECK_STEP ;
if ( percent > = WARNING_THRESH ) /* Print a warning to syslog. */
syslog ( LOG_WARNING , " Snapshot %s is now %i%% full. \n " , device , percent ) ;
/* Try to extend the snapshot, in accord with user-set policies */
if ( ! _extend ( device ) )
syslog ( LOG_ERR , " Failed to extend snapshot %s. " , device ) ;
2008-01-09 18:32:19 +03:00
}
out :
2010-01-22 01:15:45 +03:00
dmeventd_lvm2_unlock ( ) ;
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 ) ) ,
2008-01-31 15:19:36 +03:00
void * * private )
2008-01-09 18:32:19 +03:00
{
2010-10-15 20:28:14 +04:00
int * percent_check = ( int * ) private ;
2010-01-22 15:48:58 +03:00
int r = dmeventd_lvm2_init ( ) ;
2008-01-09 18:32:19 +03:00
2010-10-15 20:28:14 +04:00
* percent_check = CHECK_MINIMUM ;
2008-01-09 18:32:19 +03:00
syslog ( LOG_INFO , " Monitoring snapshot %s \n " , device ) ;
2010-01-22 15:48:58 +03:00
return r ;
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 ) ) ,
void * * unused __attribute__ ( ( unused ) ) )
2008-01-09 18:32:19 +03:00
{
syslog ( LOG_INFO , " No longer monitoring snapshot %s \n " ,
device ) ;
2010-01-22 01:15:45 +03:00
dmeventd_lvm2_exit ( ) ;
2008-01-09 18:32:19 +03:00
return 1 ;
}