# HG changeset patch # User Heiko Schlittermann (JUMPER) # Date 1304340109 -7200 # Node ID 1958c7ab13f159242a16ed979b3c019da8bac43a # Parent 57c147981554105235ce2346831cb4e21c6394f4# Parent 73e32571b22fa1f77c0186b8671129c70c010e4f merged diff -r 57c147981554 -r 1958c7ab13f1 bin/ftbackup --- 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 + # + 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: = =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> diff -r 57c147981554 -r 1958c7ab13f1 ftbackup.conf.example --- 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: =