| | 360 | sub lock($;$$) { |
|---|
| | 361 | my ($host, $file, $nowait) = @_; |
|---|
| | 362 | print "Acquiring lock for $host:$file\n" if($DEBUG); |
|---|
| | 363 | $file ||= 'master.lock'; |
|---|
| | 364 | my $store = config_get($host, 'store'); |
|---|
| | 365 | $store =~ s/%h/$host/g; |
|---|
| | 366 | return 1 if(exists($locks{"$host:$file"})); |
|---|
| | 367 | open(LOCK, "+>>$store/$file") || return 0; |
|---|
| | 368 | unless(flock(LOCK, LOCK_EX | ($nowait ? LOCK_NB : 0))) { |
|---|
| | 369 | close(LOCK); |
|---|
| | 370 | return 0; |
|---|
| | 371 | } |
|---|
| | 372 | $locks{"$host:$file"} = \*LOCK; |
|---|
| | 373 | return 1; |
|---|
| | 374 | } |
|---|
| | 375 | sub unlock($;$$) { |
|---|
| | 376 | my ($host, $file, $remove) = @_; |
|---|
| | 377 | print "Releasing lock for $host:$file\n" if($DEBUG); |
|---|
| | 378 | $file ||= 'master.lock'; |
|---|
| | 379 | my $store = config_get($host, 'store'); |
|---|
| | 380 | $store =~ s/%h/$host/g; |
|---|
| | 381 | return 0 unless(exists($locks{"$host:$file"})); |
|---|
| | 382 | *UNLOCK = $locks{$file}; |
|---|
| | 383 | unlink("$store/$file") if($remove); |
|---|
| | 384 | flock(UNLOCK, LOCK_UN); |
|---|
| | 385 | close(UNLOCK); |
|---|
| | 386 | return 1; |
|---|
| | 387 | } |
|---|
| 721 | | open(ZFSLIST, "ssh $host $agent -l |") || next; |
|---|
| 722 | | foreach my $diskline (<ZFSLIST>) { |
|---|
| 723 | | chomp($diskline); |
|---|
| 724 | | next unless($diskline =~ /^(\S+) \[([^\]]*)\]/); |
|---|
| 725 | | my $diskname = $1; |
|---|
| 726 | | my %snaps; |
|---|
| 727 | | map { $snaps{$_} = 1 } (split(/,/, $2)); |
|---|
| 728 | | |
|---|
| 729 | | # If we are being selective (via -z) now is the time. |
|---|
| 730 | | next |
|---|
| 731 | | if($diskpat && # if the pattern was specified it could |
|---|
| 732 | | !($diskname eq $diskpat || # be a specific match or a |
|---|
| 733 | | ($diskpat =~ /^\/(.+)\/$/ && $diskname =~ /$1/))); # regex |
|---|
| 734 | | |
|---|
| 735 | | print " => Scanning '$store' for old backups of '$diskname'.\n" if($DEBUG); |
|---|
| 736 | | # Make directory on demand |
|---|
| 737 | | my $backup_info = scan_for_backups($store); |
|---|
| 738 | | # That gave us info on all backups, we just want this disk |
|---|
| 739 | | $backup_info = $backup_info->{$diskname} || {}; |
|---|
| 740 | | |
|---|
| 741 | | # Should we do a backup? |
|---|
| 742 | | my $backup_type = 'no'; |
|---|
| 743 | | if(time() > $backup_info->{last_backup} + config_get($host, 'backup_interval')) { |
|---|
| 744 | | $backup_type = 'incremental'; |
|---|
| 745 | | } |
|---|
| 746 | | if(time() > $backup_info->{last_full} + config_get($host, 'full_interval')) { |
|---|
| 747 | | $backup_type = 'full'; |
|---|
| 748 | | } |
|---|
| 749 | | |
|---|
| 750 | | # If we want an incremental, but have no full, then we need to upgrade to full |
|---|
| 751 | | if($backup_type eq 'incremental') { |
|---|
| 752 | | my $have_full_locally = 0; |
|---|
| 753 | | # For each local full backup, see if the full backup still exists on the other end. |
|---|
| 754 | | foreach (keys %{$backup_info->{'full'}}) { |
|---|
| 755 | | $have_full_locally = 1 if(exists($snaps{'__zb_full_' . $_})); |
|---|
| 756 | | } |
|---|
| 757 | | $backup_type = 'full' unless($have_full_locally); |
|---|
| 758 | | } |
|---|
| 759 | | |
|---|
| 760 | | print " => doing $backup_type backup\n" if($DEBUG); |
|---|
| 761 | | # We need to drop a __zb_base snap or a __zb_incr snap before we proceed |
|---|
| 762 | | unless($NUETERED) { |
|---|
| 763 | | if($backup_type eq 'full') { |
|---|
| 764 | | eval { zfs_full_backup($host, $diskname, $store); }; |
|---|
| 765 | | if ($@) { |
|---|
| 766 | | chomp(my $err = $@); |
|---|
| 767 | | print " => failure $err\n"; |
|---|
| | 750 | my $took_action = 1; |
|---|
| | 751 | while($took_action) { |
|---|
| | 752 | $took_action = 0; |
|---|
| | 753 | my @disklist; |
|---|
| | 754 | |
|---|
| | 755 | # We need a lock for the listing. |
|---|
| | 756 | return unless(lock($host, ".list")); |
|---|
| | 757 | open(ZFSLIST, "ssh $host $agent -l |") || next; |
|---|
| | 758 | @disklist = grep { chomp } (<ZFSLIST>); |
|---|
| | 759 | close(ZFSLIST); |
|---|
| | 760 | |
|---|
| | 761 | foreach my $diskline (@disklist) { |
|---|
| | 762 | chomp($diskline); |
|---|
| | 763 | next unless($diskline =~ /^(\S+) \[([^\]]*)\]/); |
|---|
| | 764 | my $diskname = $1; |
|---|
| | 765 | my %snaps; |
|---|
| | 766 | map { $snaps{$_} = 1 } (split(/,/, $2)); |
|---|
| | 767 | |
|---|
| | 768 | # If we are being selective (via -z) now is the time. |
|---|
| | 769 | next |
|---|
| | 770 | if($diskpat && # if the pattern was specified it could |
|---|
| | 771 | !($diskname eq $diskpat || # be a specific match or a |
|---|
| | 772 | ($diskpat =~ /^\/(.+)\/$/ && $diskname =~ /$1/))); # regex |
|---|
| | 773 | |
|---|
| | 774 | print " => Scanning '$store' for old backups of '$diskname'.\n" if($DEBUG); |
|---|
| | 775 | |
|---|
| | 776 | # Make directory on demand |
|---|
| | 777 | my $backup_info = scan_for_backups($store); |
|---|
| | 778 | # That gave us info on all backups, we just want this disk |
|---|
| | 779 | $backup_info = $backup_info->{$diskname} || {}; |
|---|
| | 780 | |
|---|
| | 781 | # Should we do a backup? |
|---|
| | 782 | my $backup_type = 'no'; |
|---|
| | 783 | if(time() > $backup_info->{last_backup} + config_get($host, 'backup_interval')) { |
|---|
| | 784 | $backup_type = 'incremental'; |
|---|
| | 785 | } |
|---|
| | 786 | if(time() > $backup_info->{last_full} + config_get($host, 'full_interval')) { |
|---|
| | 787 | $backup_type = 'full'; |
|---|
| | 788 | } |
|---|
| | 789 | |
|---|
| | 790 | # If we want an incremental, but have no full, then we need to upgrade to full |
|---|
| | 791 | if($backup_type eq 'incremental') { |
|---|
| | 792 | my $have_full_locally = 0; |
|---|
| | 793 | # For each local full backup, see if the full backup still exists on the other end. |
|---|
| | 794 | foreach (keys %{$backup_info->{'full'}}) { |
|---|
| | 795 | $have_full_locally = 1 if(exists($snaps{'__zb_full_' . $_})); |
|---|
| 769 | | else { |
|---|
| 770 | | # Unless there was an error backing up, remove all the other full snaps |
|---|
| 771 | | foreach (keys %snaps) { |
|---|
| 772 | | zfs_remove_snap($host, $diskname, $_) if(/^__zb_full_(\d+)/) |
|---|
| | 797 | $backup_type = 'full' unless($have_full_locally); |
|---|
| | 798 | } |
|---|
| | 799 | |
|---|
| | 800 | print " => doing $backup_type backup\n" if($DEBUG); |
|---|
| | 801 | # We need to drop a __zb_base snap or a __zb_incr snap before we proceed |
|---|
| | 802 | unless($NUETERED || $backup_type eq 'no') { |
|---|
| | 803 | # attempt to lock this action, if it fails, skip -- someone else is working it. |
|---|
| | 804 | next unless(lock($host, dir_encode($diskname), 1)); |
|---|
| | 805 | unlock($host, '.list'); |
|---|
| | 806 | |
|---|
| | 807 | if($backup_type eq 'full') { |
|---|
| | 808 | eval { zfs_full_backup($host, $diskname, $store); }; |
|---|
| | 809 | if ($@) { |
|---|
| | 810 | chomp(my $err = $@); |
|---|
| | 811 | print " => failure $err\n"; |
|---|
| 775 | | } |
|---|
| 776 | | if($backup_type eq 'incremental') { |
|---|
| 777 | | zfs_remove_snap($host, $diskname, '__zb_incr') if($snaps{'__zb_incr'}); |
|---|
| 778 | | # Find the newest full from which to do an incremental (NOTE: reverse numeric sort) |
|---|
| 779 | | my @fulls = sort { $b <=> $a } (keys %{$backup_info->{'full'}}); |
|---|
| 780 | | zfs_incremental_backup($host, $diskname, $fulls[0], $store); |
|---|
| 781 | | } |
|---|
| 782 | | } |
|---|
| 783 | | } |
|---|
| 784 | | close(ZFSLIST); |
|---|
| | 821 | if($backup_type eq 'incremental') { |
|---|
| | 822 | zfs_remove_snap($host, $diskname, '__zb_incr') if($snaps{'__zb_incr'}); |
|---|
| | 823 | # Find the newest full from which to do an incremental (NOTE: reverse numeric sort) |
|---|
| | 824 | my @fulls = sort { $b <=> $a } (keys %{$backup_info->{'full'}}); |
|---|
| | 825 | zfs_incremental_backup($host, $diskname, $fulls[0], $store); |
|---|
| | 826 | $took_action = 1; |
|---|
| | 827 | } |
|---|
| | 828 | unlock($host, dir_encode($diskname), 1); |
|---|
| | 829 | } |
|---|
| | 830 | last if($took_action); |
|---|
| | 831 | } |
|---|
| | 832 | unlock($host, '.list'); |
|---|
| | 833 | } |
|---|