merged v1.1
authorHeiko Schlittermann (JUMPER) <hs@schlittermann.de>
Mon, 02 May 2011 14:41:49 +0200
changeset 80 1958c7ab13f1
parent 79 57c147981554 (current diff)
parent 68 73e32571b22f (diff)
child 81 b796ab5a7362
merged
bin/ftbackup
ftbackup.conf.example
--- a/bin/ftbackup	Fri Apr 29 17:43:52 2011 +0200
+++ b/bin/ftbackup	Mon May 02 14:41:49 2011 +0200
@@ -14,6 +14,7 @@
 use Cwd qw(realpath);
 use English qw(-no_match_vars);
 use if $ENV{DEBUG} => qw(Smart::Comments);
+use File::Temp;
 
 $ENV{LC_ALL} = "C";
 
@@ -25,7 +26,7 @@
 my $HOSTNAME = hostname;
 my $NOW      = time();
 
-my $opt_level     = 7;
+my $opt_level     = undef;
 my $opt_today     = strftime("%F", localtime $NOW);
 my @opt_debug     = ();
 my $opt_verbose   = 0;
@@ -40,6 +41,7 @@
 sub slurp($);
 sub get_configs(@);
 sub get_candidates();
+sub get_excludes($@);
 sub verbose(@);
 sub update_devnames($$$);
 sub get_history(@);
@@ -94,9 +96,11 @@
     # get the backup candiates -> all file systems from /etc/fstab
     # with a dump frequence > 0
     my @devs = get_candidates();
+    get_excludes(\%cf, @devs);
 
     ### %cf
     ### @devs
+    ### x: exit
 
     verbose +(map { "candidate: $_->{dev} as $_->{rdev}\n" } @devs), "\n";
 
@@ -175,7 +179,6 @@
         $dir =~ s/_/__/g;
         $dir =~ s/\//_/g;
         $dir = "$cf{FTP_DIR}/$dir";
-
         my @last;
         if ($ftp) {
             $ftp->home();
@@ -299,7 +302,7 @@
               if $opt_dumpdates;
 
             exec
-"dump -$dev->{level} -L $label -f- -u -z$cf{COMPRESSION_LEVEL} $dev->{dump}"
+"dump -$dev->{level} -L $label -f- -u -z$cf{COMPRESSION_LEVEL} -E $dev->{exclude}{inodes} $dev->{dump}"
               . "| openssl enc -pass env:key -salt -blowfish";
             die "Can't exec dumper\n";
         };
@@ -330,6 +333,49 @@
     print STDERR @_;
 }
 
+sub get_excludes($@) {
+    my $cf = shift;
+    my @devs = @_;
+
+    foreach my $dev (@devs) {
+
+	my $exclude_files0 = File::Temp->new;
+	my $exclude_inodes = File::Temp->new;
+
+	if (my $excludelist = $cf->{EXCLUDE}{$dev->{dev}}) {
+	    my (%files, %inodes);
+	    if (-x $excludelist) {
+		# executable exclude list
+		# <inum><space><filename><NULL>
+		local $/ = "\0";
+		open(my $ex, "-|", "$excludelist") or die "Can't open $excludelist: $!\n";
+		while (<$ex>) {
+		    chomp;
+		    my ($i, $f) = split " ", $_;
+		    $inodes{$i} = undef;
+		    $files{$f} = undef;
+		}
+	    }
+	    else {
+		open(my $ex, "<", $excludelist) or die "Can't open $excludelist: $!\n";
+		while (<$ex>) { chomp; @files{(glob)} = () }
+		@inodes{ map { (stat)[1] } keys %files} = ();
+
+	    }
+
+	    foreach (keys %files) { print {$exclude_files0} $_, "\0" }
+	    foreach (keys %inodes) { say {$exclude_inodes} $_ }
+	}
+	$exclude_files0->flush;
+	$exclude_inodes->flush;
+
+	# keep the FH to avoid removing the tmp files
+	$dev->{exclude}{files0} = $exclude_files0;
+	$dev->{exclude}{inodes} = $exclude_inodes;
+    }
+    return;
+}
+
 sub get_candidates() {
 
     # return the list of backup candidates
@@ -395,6 +441,11 @@
         map { chomp } values %h;
         %r = (%r, %h);
     }
+    foreach (grep /^EXCLUDE:/ => keys %r) {
+	/^EXCLUDE:(\S+)/;
+	$r{EXCLUDE}{$1} = delete $r{$_};
+    }
+    ### %r
     return %r;
 }
 
@@ -528,7 +579,7 @@
 sub get_estimate($$) {
     my ($dev, $level) = @_;
     print STDERR "% estimating $dev->{rdev} at level $level: ";
-    chomp(my $_ = `dump -S -$level $dev->{rdev}`);
+    chomp(my $_ = `dump -S -$level -E $dev->{exclude}{inodes} $dev->{rdev}`);
     print STDERR human_number($_) . "Byte\n";
     return $_;
 }
@@ -538,25 +589,25 @@
 
 
     foreach my $dev (@devs) {
-        if (defined $opt_level) {
-            $dev->{level} = $opt_level;
-        }
-        elsif (!$dev->{last}
+        if (!$dev->{last}
             or not $dev->{last}[0]
             or $NOW - $dev->{last}[0] > ($cycle * 86_400))
         {
             $dev->{level} = 0;
         }
-        else { $dev->{level} = 0 }
-
-        # now we'll see if the level really saves space compared
-        # with the next lower level
-        my @estimates;
-        while ((my $l = $dev->{level}) > 0) {
-            $estimates[$l]     //= get_estimate($dev, $l);
-            $estimates[$l - 1] //= get_estimate($dev, $l - 1);
-	    last if ($estimates[$l - 1] - $estimates[$l]) / $estimates[$l - 1] >= 0.10;
-            --$dev->{level};
+	elsif (defined $opt_level) {
+	    $dev->{level} = $opt_level
+	}
+	else {
+	    my @estimates;
+	    for (my $l = 0; $l < 9; ++$l) {
+		$estimates[$l]    //= get_estimate($dev, $l);
+		$estimates[$l + 1] //= get_estimate($dev, $l + 1);
+		if (($estimates[$l] - $estimates[$l + 1]) / $estimates[$l] < 0.1) {
+		    $dev->{level} = $l;
+		    last;
+		}
+	    }
         }
 	warn "% $dev->{dev} will use level $dev->{level}\n";
     }
@@ -719,6 +770,7 @@
     COMPRESSION_LEVEL = 6
     FULL_CYCLE	= 7
     KEEP        = 2
+    EXCLUDE:<devpath> = <exclude file>
 
 =over
 
@@ -754,6 +806,19 @@
 normally you'll get KEEP backups in your backup directory. Useless
 incremental backups are deleted automgically.
 
+=item EXCLUDE
+
+Here you may configure a list of files to be excluded on a per file system base
+
+    EXCLUDE:/dev/md1 = /etc/ftbackup.d/md1.exclude
+
+This file should contain a list (line by line) of files to be excluded from the 
+backup. The file may be executable (similiar to the automounter executable maps), 
+in that case, the file is expected to print a list of records. Each record contains of 
+two space separated fields, inode number and the file name. The records are delimited by
+a NULL character.
+
+
 =back
 
 =head2 F<.netrc>
--- a/ftbackup.conf.example	Fri Apr 29 17:43:52 2011 +0200
+++ b/ftbackup.conf.example	Mon May 02 14:41:49 2011 +0200
@@ -27,3 +27,5 @@
 # Always keep this number of (full) backups
 # (including the current one)
 # KEEP = 2
+
+# EXCLUDE:<fs> = <name of exclude list>