--- a/bin/ftbackup Mon Aug 01 14:11:04 2011 +0200
+++ b/bin/ftbackup Mon Aug 01 15:18:30 2011 +0200
@@ -17,6 +17,7 @@
use File::Temp;
use Log::Log4perl qw(:easy);
use Data::Dumper;
+use Hash::Util qw(lock_keys);
$ENV{LC_ALL} = "C";
@@ -35,17 +36,19 @@
my $HOSTNAME = hostname;
+my %o = (
+ level => undef,
+ debug => 0,
+ verbose => 0,
+ dry => 0,
+ force => 0,
+ label => "daily",
+ info => 0,
+ config => "",
+ clean => 1,
+ dumpdates => "/var/lib/dumpdates",
+); lock_keys(%o);
-my $opt_level = undef;
-my $opt_debug = 0;
-my $opt_verbose = 0;
-my $opt_dry = 0;
-my $opt_force = 0;
-my $opt_label = "daily";
-my $opt_info = 0;
-my $opt_config = "";
-my $opt_clean = 1;
-my $opt_dumpdates = "/var/lib/dumpdates";
sub slurp($);
sub get_configs(@);
@@ -81,20 +84,20 @@
Getopt::Long::Configure("bundling");
GetOptions(
- "l|level=i" => \$opt_level,
- "L|label=s" => \$opt_label,
- "d|debug!" => \$opt_debug,
- "v|verbose" => \$opt_verbose,
- "i|info" => \$opt_info,
- "dry" => sub { $opt_dry = 1; $opt_verbose = 1 },
+ "l|level=i" => \$o{level},
+ "L|label=s" => \$o{label},
+ "d|debug!" => \$o{debug},
+ "v|verbose" => \$o{verbose},
+ "i|info" => \$o{info},
+ "dry" => sub { $o{dry} = 1; $o{verbose} = 1 },
- #"f|force" => \$opt_force,
+ #"f|force" => \$o{force},
"h|help" => sub { pod2usage(-exit => 0, -verbose => 1) },
"m|man" => sub { pod2usage(-exit => 0, -verbose => 3) },
"C|config=s" => sub { @CONFIGS = ($_[1]) },
"V|version" => sub { print "$ME: $VERSION\n"; exit 0 },
- "c|clean!" => \$opt_clean,
- "D|dumpdates=s" => \$opt_dumpdates,
+ "c|clean!" => \$o{clean},
+ "D|dumpdates=s" => \$o{dumpdates},
) or pod2usage;
$started = time;
@@ -103,7 +106,7 @@
my %cf = (%CONFIG, get_configs(@CONFIGS));
$cf{FTP_DIR} =~ s/<HOSTNAME>/$HOSTNAME/g;
- $cf{FTP_DIR} =~ s/<LABEL>/$opt_label/g;
+ $cf{FTP_DIR} =~ s/<LABEL>/$o{label}/g;
# get the backup candiates -> all file systems from /etc/fstab
# with a dump frequence > 0
@@ -129,13 +132,13 @@
my $ftp;
- # get_history the situation - we rely on $opt_dumpdates
+ # get_history the situation - we rely on $o{dumpdates}
@devs = get_history(@devs);
@devs = calculate_level($cf{FULL_CYCLE}, @devs);
### @devs
- if ($opt_info) {
+ if ($o{info}) {
my $lr =
(reverse sort { $a <=> $b } map { length $_->{rdev} } @devs)[0];
my $ld = (reverse sort { $a <=> $b } map { length $_->{dev} } @devs)[0];
@@ -174,7 +177,7 @@
$ftp = new FTP(
$cf{FTP_HOST},
Passive => $cf{FTP_PASSIVE},
- Debug => $opt_debug,
+ Debug => $o{debug},
) or LOGDIE $@;
$ftp->login or LOGDIE $ftp->message;
$ftp->home($ftp->try(pwd => ()));
@@ -197,13 +200,14 @@
#verbose "Now in @{[$ftp->pwd]}.\n" if $ftp;
unlink_old_dumps($ftp, $cf{KEEP}, 1)
- if $opt_clean;
+ if $o{clean};
# examine the situation and decide about the level
# FIXME: currently we simply run a full dump every FULL_CYCLE
# days, the intermediate dumps are level 1
foreach (reverse sort $ftp->ls) {
- /^(?<date>.*)\.(?<level>\d+)$/ or next;
+ next if not /^(?<date>.*)\.(?<level>\d+)$/;
+ next if not $ftp->size(0) and str2time($+{date}) < $^T;
$last[$+{level}] = str2time $+{date};
}
}
@@ -226,7 +230,7 @@
my $label = basename($dev->{rdev});
verbose
"> $dev->{dev} ($dev->{rdev}\@$dev->{mountpoint}) to @{[$ftp->pwd]}/$file\n";
- next if $opt_dry;
+ next if $o{dry};
# For LVM do a snapshot, for regular partitions
# do nothing. But anyway the device to dump is named in $dev->{dump}
@@ -234,7 +238,7 @@
# we can do a snapshot
# FIXME: check the snapshot name is not used already
- my $snap = "$dev->{lvm}{path}-snap.0";
+ my $snap = "$dev->{lvm}{path}-snap." . time;
verbose "Creating snapshot $snap\n";
system($_ =
@@ -252,7 +256,7 @@
for (my $retries = 3 ; $retries ; $retries--) {
system($_ =
- "fsck -f @{[$opt_verbose ? '-C0' : '']} -y $device");
+ "fsck -f @{[$o{verbose} ? '-C0' : '']} -y $device");
last if not $?;
INFO "fsck on $device (using: $_) failed"
. ($retries > 1 ? ", retrying…\n" : "") . "\n";
@@ -308,8 +312,8 @@
#-- START
__HEAD
- update_devnames($opt_dumpdates, $dev->{rdev} => $dev->{dump})
- if $opt_dumpdates;
+ update_devnames($o{dumpdates}, $dev->{rdev} => $dev->{dump})
+ if $o{dumpdates};
exec
"dump -$dev->{level} -L $label -f- -u -z$cf{COMPRESSION_LEVEL} -E $dev->{exclude}{inodes} $dev->{dump}"
@@ -329,17 +333,17 @@
$dev->{cleanup}->() if $dev->{cleanup};
verbose "Done.\n";
- update_devnames($opt_dumpdates, $dev->{dump} => $dev->{rdev})
- if $opt_dumpdates;
+ update_devnames($o{dumpdates}, $dev->{dump} => $dev->{rdev})
+ if $o{dumpdates};
unlink_old_dumps($ftp, $cf{KEEP}, 0)
- if $ftp and $opt_clean;
+ if $ftp and $o{clean};
}
}
sub verbose(@) {
- return if not $opt_verbose;
+ return if not $o{verbose};
print STDERR @_;
}
@@ -537,16 +541,16 @@
}
# put the last dump information (level and date) into
-# the device structure - information is obtained from $opt_dumpdates
+# the device structure - information is obtained from $o{dumpdates}
sub get_history(@) {
my @devs = @_;
my %dd;
- open(my $dd, "+>>", $opt_dumpdates);
+ open(my $dd, "+>>", $o{dumpdates});
seek($dd, 0, 0);
while (<$dd>) {
my ($dev, $level, $date) = /^(\S+)\s+(\d+)\s+(.{30})/
- or LOGDIE "Can't parse $opt_dumpdates: `$_'\n";
+ or LOGDIE "Can't parse $o{dumpdates}: `$_'\n";
my $rdev = real_device($dev);
my $devno = devno($rdev);
@@ -606,8 +610,8 @@
{
$dev->{level} = 0;
}
- elsif (defined $opt_level) {
- $dev->{level} = $opt_level
+ elsif (defined $o{level}) {
+ $dev->{level} = $o{level}
}
else {
my @estimates;
@@ -638,11 +642,15 @@
foreach ($ftp->ls) {
/^(?<date>.*)\.(?<level>\d+)$/ or next;
+ if (not $ftp->size($_) and str2time($+{date}) < $^T) {
+ INFO "removing old zero size backup $_";
+ $ftp->delete($_);
+ next;
+ }
push @{ $dumps[$+{level}] } =>
{ file => $_, date => $+{date}, stamp => str2time($+{date}) };
}
-
# sort the level 0 dumps by date and remove all but the last $keep
# ones.
# if we found level 0 dumps, we remove all level 1+ dumps older than
@@ -684,9 +692,10 @@
TRACE("UNLINK:\n", Dumper \@unlink);
foreach (@unlink) {
- next if $opt_dry;
+ next if $o{dry};
$ftp->delete($_->{file});
}
+exit 0;
}
#/dev/vda1 0 Thu Apr 14 12:54:31 2011 +0200