#!/usr/bin/perl ################################################################################ # # Perl implementation of rsync-backup.sh # ################################################################################ use Mojo::Base -strict; use Data::Dumper; $Data::Dumper::Sortkeys = 1; use File::Basename; my $program_name = basename($0,q{.pl}); ############################################################################################## # # Rarely changed system configuration variables # # Backup volumnes, sources, and exclusions are defined in a config file # $DRIVES_CONF # Note: for sources and exclusions in config, bin lib lib64 and sbin are symlinks to /usr # ############################################################################################## my $DRIVES_CONF = q{/root/etc/rsync-backup/drives.conf}; my $FS_MOUNTPOINT = q{/mnt}; my $LOG_DIR = q{/root/log}; my $VERBOSE = q{-v}; # # Init logging # use Log::Log4perl qw(:easy); Log::Log4perl->easy_init( { level => $DEBUG, file => ">>$LOG_DIR/$program_name.log"}, { level => $DEBUG, file => "STDOUT" } ); INFO("$0 - Begin Program"); # # Load Drives config # my $backup_vols = do($DRIVES_CONF); die "Error parsing config file: $@" if $@; die "Error reading config file: $!" unless defined $backup_vols; TRACE(Dumper $backup_vols); # # Setup Commands # my $cmds = setup_commands(); TRACE(Dumper $cmds); ################################################################################ # # Step 1: Let's find out what backup volume ( if any ) is mounted. # ################################################################################ # Initialize Target variables my $T_UUID; my $T_FSTYPE; my $T_LABEL; # Loop through lsblk foreach my $line ( qx{ $cmds->{'lsblk'}->{'cmd'} } ) { # while we don't have a T_UUID if ( not $T_UUID ) { # Extract data from lsblk row my ($UUID) = split(/\s+/,$line); # if we match one of the UUIDs on our list if ( grep (/$UUID/, sort keys %{$backup_vols}) ) { # save off the device information ($T_UUID, $T_FSTYPE, $T_LABEL ) = split(/\s+/,$line); }; # if grep }; # if not T_UUID } # foreach $line # Check to make sure we have a device attached if ( not $T_UUID ) { FATAL("Fatal Error: Could not find a drive to use."); FATAL qx{ $cmds->{'lsblk'}->{'cmd'} }; FATAL "$0 - End Program"; exit 1; } INFO "Target Volume: $T_UUID, $T_FSTYPE, $T_LABEL"; ################################################################################ # # We now have the $T_UUID, $T_FSTYPE, $T_LABEL # so we're ready to construct the rest of the commands and options # # mount->cmd needs T_FSTYPE and T_UUID # rsync-opt needs excludes filename, derived from T_UUID # ################################################################################ $cmds->{'mount'}->{'cmd'} = $cmds->{'mount'}->{'bin'} . qq{ $VERBOSE -t $T_FSTYPE -U $T_UUID $FS_MOUNTPOINT } . $cmds->{'mount'}->{'opt'}; # force checksums = -c $cmds->{'rsync'}->{'opt'} = qq{-a $VERBOSE --delete --delete-excluded --delete-before --exclude-from=$backup_vols->{$T_UUID}->{'EXCLUDES'} }; $cmds->{'rsync'}->{'cmd'} = qq{$cmds->{'ionice'}->{'bin'} $cmds->{'ionice'}->{'opt'} } . qq{$cmds->{'rsync'}->{'bin'} $cmds->{'rsync'}->{'opt'} } . qq{$backup_vols->{$T_UUID}->{'SOURCES'} $FS_MOUNTPOINT}; TRACE(Dumper $cmds); ############################################################################################## # # Begin # ############################################################################################## INFO "Beginning rsync backup of data to portable drive."; INFO "--------------------------------------------------------------------------------"; INFO " Backup up directories: $backup_vols->{$T_UUID}->{'SOURCES'}"; INFO " to: $FS_MOUNTPOINT"; INFO " with rsync options: $cmds->{'rsync'}->{'opt'}"; INFO "---- Mounting Portable Drive ---"; my $rtn_code = execute_cmd ( $cmds->{'mount'}->{'cmd'} ); if ( $rtn_code == 0 ) { INFO "---- Beginning Sync Operation ---"; execute_cmd ( $cmds->{'rsync'}->{'cmd'} ); }; INFO "---- Unmounting Portable Drive ---"; execute_cmd ( $cmds->{'umount'}->{'cmd'} ); INFO("$0 - End Program"); exit 0; ############################################################################################## # # Subroutine: setup_commands() # ############################################################################################## sub setup_commands { my $c; # lsblk my $retval = qx{ which lsblk }; chomp $retval; $c->{'lsblk'}->{'bin'} = $retval; $c->{'lsblk'}->{'opt'} = q{-bnp -o UUID,FSTYPE,LABEL}; $c->{'lsblk'}->{'cmd'} = qq{$c->{'lsblk'}->{'bin'} $c->{'lsblk'}->{'opt'}}; # mount $retval = qx{ which mount }; chomp $retval; $c->{'mount'}->{'bin'} = $retval; $c->{'mount'}->{'opt'} = qq{-o context="system_u:object_r:removable_t:s0"}; # cmd needs $T_FSTYPE and $T_UUID, defer undtil drive selected # umount $retval = qx{ which umount }; chomp $retval; $c->{'umount'}->{'bin'} = $retval; $c->{'umount'}->{'opt'} = $VERBOSE; $c->{'umount'}->{'cmd'} = qq{$c->{'umount'}->{'bin'} $c->{'umount'}->{'opt'} $FS_MOUNTPOINT}; # ionice $retval = qx{ which ionice }; chomp $retval; $c->{'ionice'}->{'bin'} = $retval; $c->{'ionice'}->{'opt'} = qq{-c 3}; $c->{'ionice'}->{'cmd'} = qq{$c->{'ionice'}->{'bin'} $c->{'ionice'}->{'opt'}}; # rsync $retval = qx{ which rsync }; chomp $retval; $c->{'rsync'}->{'bin'} = $retval; # rsync->opt needs $backup_vols, defer # rsync-cmd needs rsync-opt, defer return $c; }; # sub setup_commands ############################################################################################## # # Subroutine: execute_cmd( qq{ some command text $variable } ); # ############################################################################################## sub execute_cmd { my $cmd = shift; INFO " Execute Command='$cmd'"; DEBUG "---- Command Output ----"; open(STATUS, "$cmd 2>&1 |") or die "can't fork: $!"; while (my $line = ) { DEBUG $line; } close STATUS or die "bad $cmd $! $?"; DEBUG "CHILD_ERROR_NATIVE=${^CHILD_ERROR_NATIVE}"; DEBUG "--------------------------"; return ${^CHILD_ERROR_NATIVE}; }; # sub execute_cmd ############################################################################################## __END__ ############################################################################################## =pod =head1 Old error handling ############################################### # Subroutine sad_trombone (signifies failure) ############################################### sad_trombone () { umount $FS_MOUNTPOINT; # umount $MNT_LAURIE_C; exit; } =head1 Old automation instrucations Sticking: rsync-backup.sh >> logfile.txt 2>&1 into your crontab should take care of this. As long as everything's hooked up. There is NO error checking beyond rudimentary checks for the presence of a backup drive. =head1 Sample Output from lsblk [root@orion bin]# lsblk -bnp -o KNAME,MAJ:MIN,SIZE,UUID,FSTYPE,LABEL /dev/sda 8:0 1000204886016 /dev/sda1 8:1 524288000 a33dd657-0219-45ac-b972-b2776d544119 ext4 /dev/sda2 8:2 999678803968 P4Igma-nf61-96np-EDMc-Grio-BUIL-j12JgR LVM2_member /dev/dm-0 253:0 53687091200 e4a64529-37ca-4006-917b-b480e98886cc ext4 /dev/dm-1 253:1 4227858432 faa6b4f4-1697-409e-be8a-aea4e31f028b swap /dev/dm-2 253:2 679615660032 7ef0fc8b-f444-4e68-b54b-5e38eff7ed84 ext4 /dev/dm-3 253:3 262144000000 7629eb3d-7fb2-46f1-8d9a-244fd3960fb7 ext4 /dev/sdb 8:16 1000204886016 /dev/sdb1 8:17 1000203837440 6720f72b-86ad-4568-b8dc-b22f69cf2923 btrfs backup02 /dev/sr0 11:0 1073741312 =cut