BackupPC_dump (BackupPC-4.3.2) | : | BackupPC_dump (BackupPC-4.4.0) | ||
---|---|---|---|---|
skipping to change at line 93 | skipping to change at line 93 | |||
# This program is distributed in the hope that it will be useful, | # This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | # GNU General Public License for more details. | |||
# | # | |||
# You should have received a copy of the GNU General Public License | # You should have received a copy of the GNU General Public License | |||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
# | # | |||
#======================================================================== | #======================================================================== | |||
# | # | |||
# Version 4.3.2, released 17 Feb 2020. | # Version 4.4.0, released 20 Jun 2020. | |||
# | # | |||
# See http://backuppc.sourceforge.net. | # See http://backuppc.sourceforge.net. | |||
# | # | |||
#======================================================================== | #======================================================================== | |||
use strict; | use strict; | |||
no utf8; | no utf8; | |||
use lib "__INSTALLDIR__/lib"; | use lib "__INSTALLDIR__/lib"; | |||
use BackupPC::DirOps; | use BackupPC::DirOps; | |||
use BackupPC::Lib; | use BackupPC::Lib; | |||
use BackupPC::Storage; | use BackupPC::Storage; | |||
use BackupPC::Xfer; | use BackupPC::Xfer; | |||
use BackupPC::XS; | use BackupPC::XS; | |||
use Data::Dumper; | use Data::Dumper; | |||
use Encode; | use Encode; | |||
use Errno qw(EINTR); | use Errno qw(EINTR); | |||
use Socket; | use Socket; | |||
use File::Path; | use File::Path; | |||
use File::Find; | use File::Find; | |||
use Getopt::Std; | use Getopt::Std; | |||
########################################################################### | ########################################################################### | |||
# Initialize | # Initialize | |||
########################################################################### | ########################################################################### | |||
die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); | die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); | |||
my $TopDir = $bpc->TopDir(); | my $TopDir = $bpc->TopDir(); | |||
my $BinDir = $bpc->BinDir(); | my $BinDir = $bpc->BinDir(); | |||
my %Conf = $bpc->Conf(); | my %Conf = $bpc->Conf(); | |||
my $NeedPostCmd; | my $NeedPostCmd; | |||
my $Hosts; | my $Hosts; | |||
my $SigName; | my $SigName; | |||
my $Abort = 0; | my $Abort = 0; | |||
my $LockFd; | my $LockFd; | |||
$bpc->ChildInit(); | $bpc->ChildInit(); | |||
skipping to change at line 168 | skipping to change at line 170 | |||
} | } | |||
if ( $ARGV[0] !~ /^([\w\.\s-]+)$/ ) { | if ( $ARGV[0] !~ /^([\w\.\s-]+)$/ ) { | |||
print("$0: bad client name '$ARGV[0]'\n"); | print("$0: bad client name '$ARGV[0]'\n"); | |||
exit(1); | exit(1); | |||
} | } | |||
if ( (defined($opts{f}) + defined($opts{i}) + defined($opts{F}) + defined($opts{ I})) > 1 ) { | if ( (defined($opts{f}) + defined($opts{i}) + defined($opts{F}) + defined($opts{ I})) > 1 ) { | |||
print("$0: exiting because you can only use one of -f, -i, -F, and -I\n"); | print("$0: exiting because you can only use one of -f, -i, -F, and -I\n"); | |||
exit(1); | exit(1); | |||
} | } | |||
my $client = $1; # BackupPC's client name (might not be real host name) | my $client = $1; # BackupPC's client name (might not be real host name) | |||
my $hostIP; # this is the IP address | my $hostIP; # this is the IP address | |||
my $host; # this is the real host name | my $host; # this is the real host name | |||
my($clientURI, $user); | my($clientURI, $user); | |||
$bpc->verbose(1) if ( $opts{v} ); | $bpc->verbose(1) if ( $opts{v} ); | |||
if ( $opts{d} ) { | if ( $opts{d} ) { | |||
# | # | |||
# The client name $client is simply a DHCP address. We need to check | # The client name $client is simply a DHCP address. We need to check | |||
# if there is any machine at this address, and if so, get the actual | # if there is any machine at this address, and if so, get the actual | |||
# host name via NetBios using nmblookup. | # host name via NetBios using nmblookup. | |||
# | # | |||
$hostIP = $client; | $hostIP = $client; | |||
if ( $bpc->CheckHostAlive($hostIP) < 0 ) { | if ( $bpc->CheckHostAlive($hostIP) < 0 ) { | |||
print("Exiting because CheckHostAlive($hostIP) failed\n") | print("Exiting because CheckHostAlive($hostIP) failed\n") | |||
if ( $opts{v} ); | if ( $opts{v} ); | |||
exit(1); | exit(1); | |||
} | } | |||
if ( $Conf{NmbLookupCmd} eq "" ) { | if ( $Conf{NmbLookupCmd} eq "" ) { | |||
print("Exiting because \$Conf{NmbLookupCmd} is empty\n") | print("Exiting because \$Conf{NmbLookupCmd} is empty\n") | |||
if ( $opts{v} ); | if ( $opts{v} ); | |||
exit(1); | exit(1); | |||
} | } | |||
($client, $user) = $bpc->NetBiosInfoGet($hostIP); | ($client, $user) = $bpc->NetBiosInfoGet($hostIP); | |||
if ( $client !~ /^([\w\.\s-]+)$/ ) { | if ( $client !~ /^([\w\.\s-]+)$/ ) { | |||
print("Exiting because NetBiosInfoGet($hostIP) returned" | print("Exiting because NetBiosInfoGet($hostIP) returned '$client', an in | |||
. " '$client', an invalid host name\n") if ( $opts{v} ); | valid host name\n") | |||
exit(1) | if ( $opts{v} ); | |||
exit(1); | ||||
} | } | |||
$Hosts = $bpc->HostInfoRead($client); | $Hosts = $bpc->HostInfoRead($client); | |||
$host = $client; | $host = $client; | |||
} else { | } else { | |||
$Hosts = $bpc->HostInfoRead($client); | $Hosts = $bpc->HostInfoRead($client); | |||
} | } | |||
if ( !defined($Hosts->{$client}) ) { | if ( !defined($Hosts->{$client}) ) { | |||
print("Exiting because host $client does not exist in the" | print("Exiting because host $client does not exist in the hosts file\n") if | |||
. " hosts file\n") if ( $opts{v} ); | ( $opts{v} ); | |||
exit(1) | exit(1); | |||
} | } | |||
my $Dir = "$TopDir/pc/$client"; | my $Dir = "$TopDir/pc/$client"; | |||
my @xferPid = (); | my @xferPid = (); | |||
my $tarPid = -1; | my $tarPid = -1; | |||
# | # | |||
# Re-read config file, so we can include the PC-specific config | # Re-read config file, so we can include the PC-specific config | |||
# | # | |||
$clientURI = $bpc->uriEsc($client); | $clientURI = $bpc->uriEsc($client); | |||
skipping to change at line 265 | skipping to change at line 266 | |||
# For the -e option we just expire backups and quit | # For the -e option we just expire backups and quit | |||
# | # | |||
if ( $opts{e} ) { | if ( $opts{e} ) { | |||
BackupExpire($client); | BackupExpire($client); | |||
exit(0); | exit(0); | |||
} | } | |||
# | # | |||
# For archive hosts we don't bother any further | # For archive hosts we don't bother any further | |||
# | # | |||
if ($Conf{XferMethod} eq "archive" ) { | if ( $Conf{XferMethod} eq "archive" ) { | |||
print("Exiting because the XferMethod is set to archive\n") | print("Exiting because the XferMethod is set to archive\n") | |||
if ( $opts{v} ); | if ( $opts{v} ); | |||
exit(0); | exit(0); | |||
} | } | |||
########################################################################### | ########################################################################### | |||
# Figure out what to do and do it | # Figure out what to do and do it | |||
########################################################################### | ########################################################################### | |||
# | # | |||
# See if we should skip this host during a certain range | # See if we should skip this host during a certain range | |||
# of times. | # of times. | |||
skipping to change at line 289 | skipping to change at line 290 | |||
my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}); | my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}); | |||
if ( $err ne "" ) { | if ( $err ne "" ) { | |||
print("Can't connect to server ($err)\n"); | print("Can't connect to server ($err)\n"); | |||
print($LogFd $bpc->timeStamp, "Can't connect to server ($err)\n"); | print($LogFd $bpc->timeStamp, "Can't connect to server ($err)\n"); | |||
exit(1); | exit(1); | |||
} | } | |||
my $reply = $bpc->ServerMesg("status host($clientURI)"); | my $reply = $bpc->ServerMesg("status host($clientURI)"); | |||
$reply = $1 if ( $reply =~ /(.*)/s ); | $reply = $1 if ( $reply =~ /(.*)/s ); | |||
my(%StatusHost); | my(%StatusHost); | |||
eval($reply); | eval($reply); | |||
if ( !$opts{m} && (my $status = $bpc->ServerMesg("hostMutex $client -1 BackupPC_ | ||||
dump")) =~ /fail/ ) { | ||||
print(STDERR "$0: $status (use -m option to force running)\n"); | ||||
exit(1); | ||||
} | ||||
# | # | |||
# For DHCP tell BackupPC which host this is | # For DHCP tell BackupPC which host this is | |||
# | # | |||
if ( $opts{d} ) { | if ( $opts{d} ) { | |||
if ( $StatusHost{activeJob} ) { | if ( $StatusHost{activeJob} ) { | |||
# oops, something is already running for this host | print(STDERR "$0: exiting because backup is already running for $client\ | |||
print("Exiting because backup is already running for $client\n") | n"); | |||
if ( $opts{v} ); | exit(1); | |||
exit(0); | ||||
} | } | |||
print("DHCP $hostIP $clientURI\n"); | if ( (my $status = $bpc->ServerMesg("DHCP $hostIP $clientURI")) =~ /fail/ ) | |||
{ | ||||
print(STDERR "$0: $status; exiting\n"); | ||||
exit(1); | ||||
} | ||||
} | ||||
if ( !$opts{m} && (my $status = $bpc->ServerMesg("hostMutex $client -1 BackupPC_ | ||||
dump")) =~ /fail/ ) { | ||||
print(STDERR "$0: $status (use -m option to force running)\n"); | ||||
exit(1); | ||||
} | } | |||
my(@Backups, $type); | my(@Backups, $type); | |||
my $lastFullTime = 0; | my $lastFullTime = 0; | |||
my $lastIncrTime = 0; | my $lastIncrTime = 0; | |||
my($incrBaseTime, $incrBaseBkupNum); | my($incrBaseTime, $incrBaseBkupNum); | |||
my($lastBkupIdx, $lastBkupNum, $lastBkupType, $lastBkupCompressLevel, $prevBkupI dx); | my($lastBkupIdx, $lastBkupNum, $lastBkupType, $lastBkupCompressLevel, $prevBkupI dx); | |||
my($newBkupNum, $newBkupIdx, $preV4, $noFillCnt); | my($newBkupNum, $newBkupIdx, $preV4, $noFillCnt); | |||
my $inodeLast = 1; | my $inodeLast = 1; | |||
# | # | |||
# Maintain backward compatibility with $Conf{FullPeriod} == -1 or -2 | # Maintain backward compatibility with $Conf{FullPeriod} == -1 or -2 | |||
# meaning disable backups | # meaning disable backups | |||
# | # | |||
$Conf{BackupsDisable} = -$Conf{FullPeriod} | $Conf{BackupsDisable} = -$Conf{FullPeriod} | |||
if ( !$Conf{BackupsDisable} && $Conf{FullPeriod} < 0 ); | if ( !$Conf{BackupsDisable} && $Conf{FullPeriod} < 0 ); | |||
if ( $Conf{BackupsDisable} == 1 && !$opts{f} && !$opts{i} | if ( $Conf{BackupsDisable} == 1 && !$opts{f} && !$opts{i} | |||
|| $Conf{BackupsDisable} == 2 ) { | || $Conf{BackupsDisable} == 2 ) { | |||
print("Exiting because backups are disabled with" | print("Exiting because backups are disabled with \$Conf{BackupsDisable} = $C | |||
. " \$Conf{BackupsDisable} = $Conf{BackupsDisable}\n") if ( $opts{v} ); | onf{BackupsDisable}\n") | |||
if ( $opts{v} ); | ||||
# | # | |||
# Tell BackupPC to ignore old failed backups on hosts that | # Tell BackupPC to ignore old failed backups on hosts that | |||
# have backups disabled. | # have backups disabled. | |||
# | # | |||
print("backups disabled\n") | print("backups disabled\n") | |||
if ( defined($StatusHost{errorTime}) | if ( defined($StatusHost{errorTime}) | |||
&& $StatusHost{reason} ne "Reason_backup_done" | && $StatusHost{reason} ne "Reason_backup_done" | |||
&& time - $StatusHost{errorTime} > 4 * 24 * 3600 ); | && time - $StatusHost{errorTime} > 4 * 24 * 3600 ); | |||
NothingToDo(); | NothingToDo(); | |||
} | } | |||
if ( !$opts{i} && !$opts{f} && $Conf{BlackoutGoodCnt} >= 0 | if ( !$opts{i} | |||
&& $StatusHost{aliveCnt} >= $Conf{BlackoutGoodCnt} ) { | && !$opts{f} | |||
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); | && $Conf{BlackoutGoodCnt} >= 0 | |||
&& $StatusHost{aliveCnt} >= $Conf{BlackoutGoodCnt} ) { | ||||
my($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime( | ||||
time); | ||||
my($currHours) = $hour + $min / 60 + $sec / 3600; | my($currHours) = $hour + $min / 60 + $sec / 3600; | |||
my $blackout; | my $blackout; | |||
foreach my $p ( @{$Conf{BlackoutPeriods}} ) { | foreach my $p ( @{$Conf{BlackoutPeriods}} ) { | |||
# | # | |||
# Allow blackout to span midnight (specified by hourBegin | # Allow blackout to span midnight (specified by hourBegin | |||
# being greater than hourEnd) | # being greater than hourEnd) | |||
# | # | |||
next if ( ref($p->{weekDays}) ne "ARRAY" | next if ( ref($p->{weekDays}) ne "ARRAY" || !defined($p->{hourBegin}) || | |||
|| !defined($p->{hourBegin}) | !defined($p->{hourEnd}) ); | |||
|| !defined($p->{hourEnd}) | ||||
); | ||||
my $matchWday = $wday; | my $matchWday = $wday; | |||
if ( $p->{hourBegin} > $p->{hourEnd} ) { | if ( $p->{hourBegin} > $p->{hourEnd} ) { | |||
$blackout = $p->{hourBegin} <= $currHours | $blackout = $p->{hourBegin} <= $currHours | |||
|| $currHours <= $p->{hourEnd}; | || $currHours <= $p->{hourEnd}; | |||
if ( $currHours <= $p->{hourEnd} ) { | if ( $currHours <= $p->{hourEnd} ) { | |||
# | # | |||
# This is after midnight, so decrement the weekday for the | # This is after midnight, so decrement the weekday for the | |||
# weekday check (eg: Monday 11pm-1am means Monday 2300 to | # weekday check (eg: Monday 11pm-1am means Monday 2300 to | |||
# Tuesday 0100, not Monday 2300-2400 plus Monday 0000-0100). | # Tuesday 0100, not Monday 2300-2400 plus Monday 0000-0100). | |||
# | # | |||
$matchWday--; | $matchWday--; | |||
$matchWday += 7 if ( $matchWday < 0 ); | $matchWday += 7 if ( $matchWday < 0 ); | |||
} | } | |||
} else { | } else { | |||
$blackout = $p->{hourBegin} <= $currHours | $blackout = $p->{hourBegin} <= $currHours | |||
&& $currHours <= $p->{hourEnd}; | && $currHours <= $p->{hourEnd}; | |||
} | } | |||
if ( $blackout && grep($_ == $matchWday, @{$p->{weekDays}}) ) { | if ( $blackout && grep($_ == $matchWday, @{$p->{weekDays}}) ) { | |||
# print($LogFd $bpc->timeStamp, "skipping because of blackout" | ||||
# . " (alive $StatusHost{aliveCnt} times)\n"); | # print($LogFd $bpc->timeStamp, "skipping because of black | |||
out" | ||||
# . " (alive $StatusHost{aliveCnt} times)\n"); | ||||
print("Skipping $client because of blackout\n") | print("Skipping $client because of blackout\n") | |||
if ( $opts{v} ); | if ( $opts{v} ); | |||
NothingToDo(); | NothingToDo(); | |||
} | } | |||
} | } | |||
} | } | |||
if ( !$opts{i} && !$opts{f} && $StatusHost{backoffTime} > time ) { | if ( !$opts{i} && !$opts{f} && $StatusHost{backoffTime} > time ) { | |||
printf($LogFd "%sskipping because of user requested delay (%.1f hours left)\ n", | printf($LogFd "%sskipping because of user requested delay (%.1f hours left)\ n", | |||
$bpc->timeStamp, ($StatusHost{backoffTime} - time) / 3600); | $bpc->timeStamp, ($StatusHost{backoffTime} - time) / 3600); | |||
NothingToDo(); | NothingToDo(); | |||
} | } | |||
# | # | |||
# Now see if there are any old backups we should delete | # Now see if there are any old backups we should delete | |||
# | # | |||
BackupExpire($client); | BackupExpire($client); | |||
# | # | |||
# Read Backup information, and if the most recent backup is v4, check | # Read Backup information, and if the most recent backup is v4, check | |||
# that it is filled and it exists. | # that it is filled and it exists. | |||
# | # | |||
@Backups = $bpc->BackupInfoRead($client); | @Backups = $bpc->BackupInfoRead($client); | |||
if ( @Backups | if ( @Backups | |||
&& ($Backups[-1]{version} ne "" && $Backups[-1]{version} !~ /^[23]\./) | && ($Backups[-1]{version} ne "" && $Backups[-1]{version} !~ /^[23]\./) | |||
&& ($Backups[-1]{noFill} || !-d "$Dir/$Backups[-1]{num}") ) { | && ($Backups[-1]{noFill} || !-d "$Dir/$Backups[-1]{num}") ) { | |||
my $i; | my $i; | |||
if ( $Backups[-1]{noFill} ) { | if ( $Backups[-1]{noFill} ) { | |||
printf($LogFd "%sSerious error: last backup %d is not filled! Need to r emove back to last filled backup\n", | printf($LogFd "%sSerious error: last backup %d is not filled! Need to r emove back to last filled backup\n", | |||
$bpc->timeStamp, $Backups[-1]{num}); | $bpc->timeStamp, $Backups[-1]{num}); | |||
printf(STDERR "Serious error: last backup %d is not filled! Need to rem | printf("Serious error: last backup %d is not filled! Need to remove bac | |||
ove back to last filled backup\n", | k to last filled backup\n", | |||
$Backups[-1]{num}) if ( $opts{v} ); | $Backups[-1]{num}) | |||
if ( $opts{v} ); | ||||
} else { | } else { | |||
printf($LogFd "%sSerious error: last backup %s directory doesn't exist!! | printf($LogFd | |||
! Need to remove back to last filled backup\n", | "%sSerious error: last backup %s directory doesn't exist!!! Need | |||
$bpc->timeStamp, "$Dir/$Backups[-1]{num}"); | to remove back to last filled backup\n", | |||
printf(STDERR "%sSerious error: last backup %s directory doesn't exist!! | $bpc->timeStamp, "$Dir/$Backups[-1]{num}"); | |||
! Need to remove back to last filled backup\n", | printf( | |||
$bpc->timeStamp, "$Dir/$Backups[-1]{num}") if ( $opts{v} ); | "%sSerious error: last backup %s directory doesn't exist!!! Need to | |||
remove back to last filled backup\n", | ||||
$bpc->timeStamp, "$Dir/$Backups[-1]{num}") | ||||
if ( $opts{v} ); | ||||
} | } | |||
for ( $i = @Backups - 1 ; $i >= 0 ; $i-- ) { | for ( $i = @Backups - 1 ; $i >= 0 ; $i-- ) { | |||
last if ( $Backups[$i]{version} eq "" || $Backups[$i]{version} =~ /^[23] \./ ); | last if ( $Backups[$i]{version} eq "" || $Backups[$i]{version} =~ /^[23] \./ ); | |||
last if ( !$Backups[$i]{noFill} && -d "$Dir/$Backups[$i]{num}"); | last if ( !$Backups[$i]{noFill} && -d "$Dir/$Backups[$i]{num}" ); | |||
} | } | |||
$i++; | $i++; | |||
while ( $i < @Backups ) { | while ( $i < @Backups ) { | |||
printf($LogFd "%sDeleting backup %d\n", $bpc->timeStamp, $Backups[$i]{nu m}); | printf($LogFd "%sDeleting backup %d\n", $bpc->timeStamp, $Backups[$i]{nu m}); | |||
printf(STDERR "%sDeleting backup %d\n", $bpc->timeStamp, $Backups[$i]{nu m}) if ( $opts{v} ); | printf("%sDeleting backup %d\n", $bpc->timeStamp, $Backups[$i]{nu m}) if ( $opts{v} ); | |||
BackupRemove($client, $i, 1); | BackupRemove($client, $i, 1); | |||
$bpc->BackupInfoWrite($client, @Backups); | $bpc->BackupInfoWrite($client, @Backups); | |||
} | } | |||
exit(0); | exit(0); | |||
} | } | |||
# | # | |||
# Find the most recent backup, times of the most recent full and incremental bac kups, | # Find the most recent backup, times of the most recent full and incremental bac kups, | |||
# and check if the most recent backup is V4+ or prior to V4 (ie: preV4). | # and check if the most recent backup is V4+ or prior to V4 (ie: preV4). | |||
# | # | |||
skipping to change at line 446 | skipping to change at line 453 | |||
$lastBkupIdx = $i; | $lastBkupIdx = $i; | |||
$incrBaseBkupNum = $Backups[$i]{num}; | $incrBaseBkupNum = $Backups[$i]{num}; | |||
$incrBaseTime = $Backups[$i]{startTime}; | $incrBaseTime = $Backups[$i]{startTime}; | |||
$lastBkupType = $Backups[$i]{type}; | $lastBkupType = $Backups[$i]{type}; | |||
$lastBkupCompressLevel = $Backups[$i]{compress}; | $lastBkupCompressLevel = $Backups[$i]{compress}; | |||
$preV4 = $Backups[$i]{preV4}; | $preV4 = $Backups[$i]{preV4}; | |||
$noFillCnt = $thisNoFillCnt; | $noFillCnt = $thisNoFillCnt; | |||
} | } | |||
$thisNoFillCnt = 0 if ( !$Backups[$i]{noFill} ); | $thisNoFillCnt = 0 if ( !$Backups[$i]{noFill} ); | |||
if ( $Backups[$i]{type} eq "full" ) { | if ( $Backups[$i]{type} eq "full" ) { | |||
if ( $lastFullTime < $Backups[$i]{startTime} ) { | if ( $lastFullTime < $Backups[$i]{startTime} ) { | |||
$lastFullTime = $Backups[$i]{startTime}; | $lastFullTime = $Backups[$i]{startTime}; | |||
} | } | |||
} elsif ( $Backups[$i]{type} eq "incr" ) { | } elsif ( $Backups[$i]{type} eq "incr" ) { | |||
$lastIncrTime = $Backups[$i]{startTime} | $lastIncrTime = $Backups[$i]{startTime} | |||
if ( $lastIncrTime < $Backups[$i]{startTime} ); | if ( $lastIncrTime < $Backups[$i]{startTime} ); | |||
} | } | |||
} | } | |||
# | # | |||
# Decide whether we do nothing, or a full or incremental backup. | # Decide whether we do nothing, or a full or incremental backup. | |||
# | # | |||
my $needs_full = (time - $lastFullTime > $Conf{FullPeriod} * 24 * 3600 | my $needs_full = | |||
&& time - $lastIncrTime > $Conf{IncrPeriod} * 24 * 3600); | (time - $lastFullTime > $Conf{FullPeriod} * 24 * 3600 && time - $lastIncrTime | |||
my $needs_incr = (time - $lastIncrTime > $Conf{IncrPeriod} * 24 * 3600 | > $Conf{IncrPeriod} * 24 * 3600); | |||
&& time - $lastFullTime > $Conf{IncrPeriod} * 24 * 3600); | my $needs_incr = | |||
(time - $lastIncrTime > $Conf{IncrPeriod} * 24 * 3600 && time - $lastFullTime | ||||
> $Conf{IncrPeriod} * 24 * 3600); | ||||
$needs_full = 0 if ( $Conf{FullPeriod} == 0 ); | $needs_full = 0 if ( $Conf{FullPeriod} == 0 ); | |||
if ( $opts{f} | if ( $opts{f} || (!$opts{i} && !$opts{I} && $needs_full) || ($opts{F} && $needs_ | |||
|| (!$opts{i} && !$opts{I} && $needs_full) | incr) ) { | |||
|| ( $opts{F} && $needs_incr) ) { | ||||
$type = "full"; | $type = "full"; | |||
} elsif ( $opts{i} | } elsif ( $opts{i} || $needs_incr || ($opts{I} && $needs_full) ) { | |||
|| $needs_incr | ||||
|| ($opts{I} && $needs_full) ) { | ||||
$type = "incr"; | $type = "incr"; | |||
} else { | } else { | |||
NothingToDo(); | NothingToDo(); | |||
} | } | |||
print("Backup type: type = $type, needs_full = $needs_full, needs_incr = $needs_ | print( "Backup type: type = $type, needs_full = $needs_full, needs_incr = $need | |||
incr, lastFullTime = $lastFullTime," | s_incr, lastFullTime = $lastFullTime," | |||
. " opts{f} = $opts{f}, opts{i} = $opts{i}, opts{F} = $opts{F}\n") if ( $opt | . " opts{f} = $opts{f}, opts{i} = $opts{i}, opts{F} = $opts{F}\n") | |||
s{v} ); | if ( $opts{v} ); | |||
# | # | |||
# Create top-level directories if they don't exist | # Create top-level directories if they don't exist | |||
# | # | |||
foreach my $dir ( ( | foreach my $dir ( ("$Conf{TopDir}/pool", "$Conf{TopDir}/cpool", $Dir, "$Dir/refC | |||
"$Conf{TopDir}/pool", | nt") ) { | |||
"$Conf{TopDir}/cpool", | ||||
$Dir, | ||||
"$Dir/refCnt", | ||||
) ) { | ||||
next if ( -d $dir ); | next if ( -d $dir ); | |||
mkpath($dir, 0, 0777); | mkpath($dir, 0, 0777); | |||
if ( !-d $dir ) { | if ( !-d $dir ) { | |||
print("Failed to create $dir\n"); | print("Failed to create $dir\n"); | |||
printf($LogFd "%sFailed to create directory %s\n", $bpc->timeStamp, $dir ); | printf($LogFd "%sFailed to create directory %s\n", $bpc->timeStamp, $dir ); | |||
exit(1); | exit(1); | |||
} else { | } else { | |||
printf($LogFd "%sCreated directory %s\n", $bpc->timeStamp, $dir); | printf($LogFd "%sCreated directory %s\n", $bpc->timeStamp, $dir); | |||
} | } | |||
} | } | |||
skipping to change at line 523 | skipping to change at line 522 | |||
} elsif ( $Conf{ClientNameAlias} ne "" ) { | } elsif ( $Conf{ClientNameAlias} ne "" ) { | |||
push(@hostsToCheck, $Conf{ClientNameAlias}); | push(@hostsToCheck, $Conf{ClientNameAlias}); | |||
} else { | } else { | |||
push(@hostsToCheck, $client); | push(@hostsToCheck, $client); | |||
} | } | |||
foreach my $hostToCheck ( @hostsToCheck ) { | foreach my $hostToCheck ( @hostsToCheck ) { | |||
$hostIP = HostLookupCheck($hostToCheck); | $hostIP = HostLookupCheck($hostToCheck); | |||
push(@validHosts, [$hostToCheck, $hostIP]) if ( defined($hostIP) ); | push(@validHosts, [$hostToCheck, $hostIP]) if ( defined($hostIP) ); | |||
} | } | |||
} else { | } else { | |||
# In DHCP case we've already found $hostIP | # In DHCP case we've already found $hostIP | |||
push(@validHosts, [$host, $hostIP]); | push(@validHosts, [$host, $hostIP]); | |||
} | } | |||
# | # | |||
# Find the first entry in @validHosts that we can ping | # Find the first entry in @validHosts that we can ping | |||
# | # | |||
foreach my $h ( @validHosts ) { | foreach my $h ( @validHosts ) { | |||
($host, $hostIP) = @$h; | ($host, $hostIP) = @$h; | |||
my $delay = $bpc->CheckHostAlive($hostIP); | my $delay = $bpc->CheckHostAlive($hostIP); | |||
if ( $delay < 0 ) { | if ( $delay < 0 ) { | |||
print($LogFd $bpc->timeStamp, "no ping response from $hostIP\n"); | print($LogFd $bpc->timeStamp, "no ping response from $hostIP\n"); | |||
print("no ping response from $hostIP\n"); | print("no ping response from $hostIP\n"); | |||
$hostIP = undef; | $hostIP = undef; | |||
next; | next; | |||
} elsif ( $delay > $Conf{PingMaxMsec} ) { | } elsif ( $delay > $Conf{PingMaxMsec} ) { | |||
printf($LogFd "%sping too slow on $hostIP %.4gmsec\n", $bpc->timeStamp, | printf($LogFd "%sping too slow on $hostIP %.4gmsec\n", $bpc | |||
$delay); | ->timeStamp, $delay); | |||
printf("ping too slow on $hostIP %.4gmsec (threshold is %gmsec)\n", | printf("ping too slow on $hostIP %.4gmsec (threshold is %gmsec)\n", $del | |||
$delay, $Conf{PingMaxMsec}); | ay, $Conf{PingMaxMsec}); | |||
$hostIP = undef; | $hostIP = undef; | |||
next; | next; | |||
} | } | |||
last; | last; | |||
} | } | |||
if ( !defined($hostIP) ) { | if ( !defined($hostIP) ) { | |||
print($LogFd $bpc->timeStamp, "can't ping $host (client = $client); exiting\ n"); | print($LogFd $bpc->timeStamp, "can't ping $host (client = $client); exiting\ n"); | |||
print("can't ping $host (client = $client); exiting\n"); | print("can't ping $host (client = $client); exiting\n") if ( $opts{v} ); | |||
exit(1); | exit(1); | |||
} | } | |||
# | # | |||
# Make sure it is really the machine we expect (only for fixed addresses, | # Make sure it is really the machine we expect (only for fixed addresses, | |||
# since we got the DHCP address above). | # since we got the DHCP address above). | |||
# | # | |||
if ( !$opts{d} && (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { | if ( !$opts{d} && (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { | |||
print($LogFd $bpc->timeStamp, "dump failed: $errMsg\n"); | print($LogFd $bpc->timeStamp, "dump failed: $errMsg\n"); | |||
print("dump failed: $errMsg\n"); | print("dump failed: $errMsg\n"); | |||
skipping to change at line 603 | skipping to change at line 602 | |||
# up on a failed backup. | # up on a failed backup. | |||
# | # | |||
# Note: $Conf{FillCycle} == 0, then the V4 fill cycle matches the | # Note: $Conf{FillCycle} == 0, then the V4 fill cycle matches the | |||
# full/incr cycle. | # full/incr cycle. | |||
# | # | |||
# | # | |||
# $inPlace means the Xfer method makes all the changes in place, without | # $inPlace means the Xfer method makes all the changes in place, without | |||
# stored any reverse deltas in a prior backup tree. | # stored any reverse deltas in a prior backup tree. | |||
# | # | |||
my $inPlace = 0; | my $inPlace = 0; | |||
# | # | |||
# doDuplicate means we run BackupPC_backupDuplicate, which duplicates the | # doDuplicate means we run BackupPC_backupDuplicate, which duplicates the | |||
# most recent v3 and v4 backup. | # most recent v3 and v4 backup. | |||
# | # | |||
my $doDuplicate = 0; | my $doDuplicate = 0; | |||
my $copyXferLOG; | my $copyXferLOG; | |||
# | # | |||
# Remember which case, so we can figure out what to clean up on a failed backup. | # Remember which case, so we can figure out what to clean up on a failed backup. | |||
skipping to change at line 653 | skipping to change at line 652 | |||
# | # | |||
$BackupCase = 2; | $BackupCase = 2; | |||
$inPlace = 1; | $inPlace = 1; | |||
$doDuplicate = 1; | $doDuplicate = 1; | |||
$newBkupNum = $lastBkupNum + 1; # will exist after BackupPC_backupDuplic ate | $newBkupNum = $lastBkupNum + 1; # will exist after BackupPC_backupDuplic ate | |||
$newBkupIdx = @Backups; | $newBkupIdx = @Backups; | |||
$lastBkupNum = undef; | $lastBkupNum = undef; | |||
$lastBkupIdx = undef; | $lastBkupIdx = undef; | |||
$type = "full"; | $type = "full"; | |||
} else { | } else { | |||
if ( ($lastBkupType eq "partial" || $lastBkupType eq "active") | if ( ($lastBkupType eq "partial" || $lastBkupType eq "active") | |||
&& (!defined($prevBkupIdx) || $Backups[$prevBkupIdx]{preV4} || !$Backu | && (!defined($prevBkupIdx) || $Backups[$prevBkupIdx]{preV4} || !$Backups | |||
ps[$prevBkupIdx]{noFill}) ) { | [$prevBkupIdx]{noFill}) ) { | |||
# | # | |||
# case 6: partial, and either no previous, V3 previous, or filled previo us: simply update in place, | # case 6: partial, and either no previous, V3 previous, or filled previo us: simply update in place, | |||
# with no prior deltas. | # with no prior deltas. | |||
# | # | |||
$BackupCase = 6; | $BackupCase = 6; | |||
$inPlace = 1; | $inPlace = 1; | |||
$doDuplicate = 0; | $doDuplicate = 0; | |||
# | # | |||
# We need to append the current XferLOG file. Rename it and copy it bel ow. | # We need to append the current XferLOG file. Rename it and copy it bel ow. | |||
# | # | |||
my $fileExt = $lastBkupCompressLevel > 0 ? ".z" : ""; | my $fileExt = $lastBkupCompressLevel > 0 ? ".z" : ""; | |||
if ( -f "$Dir/XferLOG.$lastBkupNum$fileExt" ) { | if ( -f "$Dir/XferLOG.$lastBkupNum$fileExt" ) { | |||
print($LogFd $bpc->timeStamp, "Renaming $Dir/XferLOG.$lastBkupNum$fi | print($LogFd $bpc->timeStamp, | |||
leExt -> $Dir/XferLOG.$lastBkupNum$fileExt.tmp\n"); | "Renaming $Dir/XferLOG.$lastBkupNum$fileExt -> $Dir/XferLOG.$las | |||
tBkupNum$fileExt.tmp\n"); | ||||
rename("$Dir/XferLOG.$lastBkupNum$fileExt", "$Dir/XferLOG.$lastBkupN um$fileExt.tmp"); | rename("$Dir/XferLOG.$lastBkupNum$fileExt", "$Dir/XferLOG.$lastBkupN um$fileExt.tmp"); | |||
$copyXferLOG = ["$Dir/XferLOG.$lastBkupNum$fileExt.tmp", $lastBkupCo mpressLevel, 1]; | $copyXferLOG = ["$Dir/XferLOG.$lastBkupNum$fileExt.tmp", $lastBkupCo mpressLevel, 1]; | |||
} | } | |||
$newBkupNum = $lastBkupNum; | $newBkupNum = $lastBkupNum; | |||
$newBkupIdx = $lastBkupIdx; | $newBkupIdx = $lastBkupIdx; | |||
$lastBkupNum = undef; | $lastBkupNum = undef; | |||
$lastBkupIdx = undef; | $lastBkupIdx = undef; | |||
} elsif ( $lastBkupType eq "full" || ($Conf{FillCycle} > 0 && $noFillCnt >= $Conf{FillCycle} - 1) ) { | } elsif ( $lastBkupType eq "full" || ($Conf{FillCycle} > 0 && $noFillCnt >= $Conf{FillCycle} - 1) ) { | |||
# | # | |||
# case 3: V4; last is filled, so duplicate and do in place | # case 3: V4; last is filled, so duplicate and do in place | |||
skipping to change at line 701 | skipping to change at line 701 | |||
$inPlace = 0; | $inPlace = 0; | |||
$doDuplicate = 0; | $doDuplicate = 0; | |||
$newBkupNum = $lastBkupNum; | $newBkupNum = $lastBkupNum; | |||
$newBkupIdx = @Backups; | $newBkupIdx = @Backups; | |||
do { | do { | |||
$newBkupNum++; | $newBkupNum++; | |||
} while ( -d "$Dir/$newBkupNum" ); | } while ( -d "$Dir/$newBkupNum" ); | |||
if ( !rename("$Dir/$lastBkupNum", "$Dir/$newBkupNum") ) { | if ( !rename("$Dir/$lastBkupNum", "$Dir/$newBkupNum") ) { | |||
print($LogFd $bpc->timeStamp, "Can't rename $Dir/$lastBkupNum to $Di r/$newBkupNum\n"); | print($LogFd $bpc->timeStamp, "Can't rename $Dir/$lastBkupNum to $Di r/$newBkupNum\n"); | |||
print("Exiting because rename $Dir/$lastBkupNum to $Dir/$newBkupNum failed\n") | print("Exiting because rename $Dir/$lastBkupNum to $Dir/$newBkupNum failed\n") | |||
if ( $opts{v} ); | if ( $opts{v} ); | |||
exit(1); | exit(1); | |||
} | } | |||
$Backups[$lastBkupIdx]{noFill} = 1; | $Backups[$lastBkupIdx]{noFill} = 1; | |||
# | # | |||
# Create the lastBkupNum and refCnt directory and flag it needing an fsc k | # Create the lastBkupNum and refCnt directory and flag it needing an fsc k | |||
# in case we exit without cleanup. Also add a flag that it's ok not | # in case we exit without cleanup. Also add a flag that it's ok not | |||
# having any current poolCnt files. | # having any current poolCnt files. | |||
# | # | |||
eval { mkpath("$Dir/$lastBkupNum/refCnt", 0, 0777) }; | eval { mkpath("$Dir/$lastBkupNum/refCnt", 0, 0777) }; | |||
if ( $@ ) { | if ( $@ ) { | |||
print($LogFd $bpc->timeStamp, "Can't create $Dir/$lastBkupNum/refCnt \n"); | print($LogFd $bpc->timeStamp, "Can't create $Dir/$lastBkupNum/refCnt \n"); | |||
print("Can't create backup directory $Dir/$lastBkupNum/refCnt") | print("Can't create backup directory $Dir/$lastBkupNum/refCnt") | |||
if ( $opts{v} ); | if ( $opts{v} ); | |||
exit(1); | exit(1); | |||
} | } | |||
my $newFH; | my $newFH; | |||
if ( !(open($newFH, ">", "$Dir/$lastBkupNum/refCnt/needFsck.newDir") && close($newFH)) ) { | if ( !(open($newFH, ">", "$Dir/$lastBkupNum/refCnt/needFsck.newDir") && close($newFH)) ) { | |||
print($LogFd $bpc->timeStamp, "Can't create $Dir/$lastBkupNum/refCnt /needFsck.newDir ($?)\n"); | print($LogFd $bpc->timeStamp, "Can't create $Dir/$lastBkupNum/refCnt /needFsck.newDir ($?)\n"); | |||
print("Can't create $Dir/$lastBkupNum/refCnt/needFsck.newDir ($?)\n" ); | print("Can't create $Dir/$lastBkupNum/refCnt/needFsck.newDir ($?)\n" ); | |||
} | } | |||
if ( !(open($newFH, ">", "$Dir/$lastBkupNum/refCnt/noPoolCntOk") && clos e($newFH)) ) { | if ( !(open($newFH, ">", "$Dir/$lastBkupNum/refCnt/noPoolCntOk") && clos e($newFH)) ) { | |||
print($LogFd $bpc->timeStamp, "Can't create $Dir/$lastBkupNum/refCnt /noPoolCntOk ($?)\n"); | print($LogFd $bpc->timeStamp, "Can't create $Dir/$lastBkupNum/refCnt /noPoolCntOk ($?)\n"); | |||
print("Can't create $Dir/$lastBkupNum/refCnt/noPoolCntOk ($?)\n"); | print("Can't create $Dir/$lastBkupNum/refCnt/noPoolCntOk ($?)\n"); | |||
skipping to change at line 755 | skipping to change at line 755 | |||
} | } | |||
} | } | |||
# | # | |||
# Setup file extension for compression and open XferLOG output file | # Setup file extension for compression and open XferLOG output file | |||
# | # | |||
my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; | my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; | |||
unlink("$Dir/XferLOG.$newBkupNum$fileExt") if ( -e "$Dir/XferLOG.$newBkupNum$fil eExt" ); | unlink("$Dir/XferLOG.$newBkupNum$fileExt") if ( -e "$Dir/XferLOG.$newBkupNum$fil eExt" ); | |||
my $XferLOG = BackupPC::XS::FileZIO::open("$Dir/XferLOG.$newBkupNum$fileExt", 1, $Conf{CompressLevel}); | my $XferLOG = BackupPC::XS::FileZIO::open("$Dir/XferLOG.$newBkupNum$fileExt", 1, $Conf{CompressLevel}); | |||
if ( !defined($XferLOG) ) { | if ( !defined($XferLOG) ) { | |||
print($LogFd $bpc->timeStamp, "dump failed: unable to open/create" | print($LogFd $bpc->timeStamp, "dump failed: unable to open/create $Dir/XferL | |||
. " $Dir/XferLOG.$newBkupNum$fileExt\n"); | OG.$newBkupNum$fileExt\n"); | |||
print("dump failed: unable to open/create $Dir/XferLOG.$newBkupNum$fileExt\n "); | print("dump failed: unable to open/create $Dir/XferLOG.$newBkupNum$fileExt\n "); | |||
exit(1); | exit(1); | |||
} | } | |||
xferLOGCopyFile(@$copyXferLOG) if ( defined($copyXferLOG) ); | xferLOGCopyFile(@$copyXferLOG) if ( defined($copyXferLOG) ); | |||
$XferLOG->writeTeeStderr(1) if ( $opts{v} ); | $XferLOG->writeTeeStderr(1) if ( $opts{v} ); | |||
my $str = "XferLOG file $Dir/XferLOG.$newBkupNum$fileExt created " . $bpc->timeS tamp . "\n"; | my $str = "XferLOG file $Dir/XferLOG.$newBkupNum$fileExt created " . $bpc->timeS tamp . "\n"; | |||
$XferLOG->write(\$str); | $XferLOG->write(\$str); | |||
if ( $Conf{XferLogLevel} >= 1 || $opts{v} ) { | if ( $Conf{XferLogLevel} >= 1 || $opts{v} ) { | |||
$str = "Backup prep: type = $type, case = $BackupCase, inPlace = $inPlace, d | $str = | |||
oDuplicate = $doDuplicate," | "Backup prep: type = $type, case = $BackupCase, inPlace = $inPlace, doDu | |||
. " newBkupNum = $newBkupNum, newBkupIdx = $newBkupIdx, lastBkupNum = $ | plicate = $doDuplicate," | |||
lastBkupNum, lastBkupIdx = $lastBkupIdx" | . " newBkupNum = $newBkupNum, newBkupIdx = $newBkupIdx, lastBkupNum = $las | |||
. " (FillCycle = $Conf{FillCycle}, noFillCnt = $noFillCnt)\n"; | tBkupNum, lastBkupIdx = $lastBkupIdx" | |||
. " (FillCycle = $Conf{FillCycle}, noFillCnt = $noFillCnt)\n"; | ||||
$XferLOG->write(\$str); | $XferLOG->write(\$str); | |||
} | } | |||
# | # | |||
# See if this client needs an fsck. | # See if this client needs an fsck. | |||
# | # | |||
my $needFsck = 0; | my $needFsck = 0; | |||
my $refCntFiles = BackupPC::DirOps::dirRead($bpc, "$Dir/$newBkupNum/refCnt"); | my $refCntFiles = BackupPC::DirOps::dirRead($bpc, "$Dir/$newBkupNum/refCnt"); | |||
foreach my $file ( @$refCntFiles ) { | foreach my $file ( @$refCntFiles ) { | |||
next if ( $file !~ /^needFsck/ ); | next if ( $file !~ /^needFsck/ ); | |||
$needFsck = 1; | $needFsck = 1; | |||
last; | last; | |||
} | } | |||
RefCountUpdate(1, 0) if ( $needFsck ); | RefCountUpdate(1, 0) if ( $needFsck ); | |||
# | # | |||
# Duplicate the most recent backup, if required | # Duplicate the most recent backup, if required | |||
# | # | |||
if ( $doDuplicate ) { | if ( $doDuplicate ) { | |||
my $t = time; | my $t = time; | |||
my $pids = {}; | my $pids = {}; | |||
# | # | |||
# Run BackupPC_backupDuplicate, then re-read the backups file. | # Run BackupPC_backupDuplicate, then re-read the backups file. | |||
# | # | |||
my $cmd = ["$BinDir/BackupPC_backupDuplicate", "-m", "-h", $client]; | my $cmd = ["$BinDir/BackupPC_backupDuplicate", "-m", "-h", $client]; | |||
push(@$cmd, "-p") if ( $opts{p} ); | push(@$cmd, "-p") if ( $opts{p} ); | |||
$XferLOG->write(\"Executing @$cmd\n"); | $XferLOG->write(\"Executing @$cmd\n"); | |||
$bpc->cmdSystemOrEval($cmd, | $bpc->cmdSystemOrEval( | |||
sub { | $cmd, | |||
if ( $_[0] =~ /^__bpc_progress_/ ) { | sub { | |||
print($_[0]); | if ( $_[0] =~ /^__bpc_progress_/ ) { | |||
} elsif ( $_[0] =~ /^__bpc_pidStart__ (\d+)/ ) { | print($_[0]); | |||
$pids->{$1} = 1; | } elsif ( $_[0] =~ /^__bpc_pidStart__ (\d+)/ ) { | |||
pidHandler(keys(%$pids)); | $pids->{$1} = 1; | |||
} elsif ( $_[0] =~ /^__bpc_pidEnd__ (\d+)/ ) { | pidHandler(keys(%$pids)); | |||
delete($pids->{$1}); | } elsif ( $_[0] =~ /^__bpc_pidEnd__ (\d+)/ ) { | |||
pidHandler(keys(%$pids)); | delete($pids->{$1}); | |||
pidHandler(keys(%$pids)); | ||||
} else { | ||||
if ( defined($XferLOG) ) { | ||||
$XferLOG->write(\$_[0]); | ||||
} else { | } else { | |||
if ( defined($XferLOG) ) { | print($LogFd $bpc->timeStamp, $_[0]); | |||
$XferLOG->write(\$_[0]); | ||||
} else { | ||||
print($LogFd $bpc->timeStamp, $_[0]); | ||||
} | ||||
} | } | |||
}); | } | |||
} | ||||
); | ||||
$t = time - $t; | $t = time - $t; | |||
$XferLOG->write(\"Finished BackupPC_backupDuplicate (running time: $t sec)\n "); | $XferLOG->write(\"Finished BackupPC_backupDuplicate (running time: $t sec)\n "); | |||
@Backups = $bpc->BackupInfoRead($client); | @Backups = $bpc->BackupInfoRead($client); | |||
pidHandler(); | pidHandler(); | |||
} | } | |||
# | # | |||
# Create a placeholder backups entry if needed | # Create a placeholder backups entry if needed | |||
# | # | |||
if ( $newBkupIdx >= @Backups ) { | if ( $newBkupIdx >= @Backups ) { | |||
$Backups[$newBkupIdx]{num} = $newBkupNum; | $Backups[$newBkupIdx]{num} = $newBkupNum; | |||
$Backups[$newBkupIdx]{level} = $type eq "incr" ? 1 : 0; | $Backups[$newBkupIdx]{level} = $type eq "incr" ? 1 : 0; | |||
$Backups[$newBkupIdx]{noFill} = 0; | $Backups[$newBkupIdx]{noFill} = 0; | |||
$Backups[$newBkupIdx]{mangle} = 1; # name mangling always on for v1. 04+ | $Backups[$newBkupIdx]{mangle} = 1; # name mangli ng always on for v1.04+ | |||
$Backups[$newBkupIdx]{xferMethod} = $Conf{XferMethod}; | $Backups[$newBkupIdx]{xferMethod} = $Conf{XferMethod}; | |||
$Backups[$newBkupIdx]{charset} = $Conf{ClientCharset}; | $Backups[$newBkupIdx]{charset} = $Conf{ClientCharset}; | |||
$Backups[$newBkupIdx]{version} = $bpc->Version(); | $Backups[$newBkupIdx]{version} = $bpc->Version(); | |||
$Backups[$newBkupIdx]{compress} = $Conf{CompressLevel}; | $Backups[$newBkupIdx]{compress} = $Conf{CompressLevel}; | |||
$Backups[$newBkupIdx]{keep} = 0; | ||||
} | } | |||
# | # | |||
# New backup shows as "active" while running | # New backup shows as "active" while running | |||
# | # | |||
$Backups[$newBkupIdx]{type} = "active"; | $Backups[$newBkupIdx]{type} = "active"; | |||
$Backups[$newBkupIdx]{startTime} = time(); | $Backups[$newBkupIdx]{startTime} = time(); | |||
# | # | |||
# Create new directory | # Create new directory | |||
# | # | |||
if ( defined($newBkupNum) && !-d "$Dir/$newBkupNum" ) { | if ( defined($newBkupNum) && !-d "$Dir/$newBkupNum" ) { | |||
# | # | |||
# Create the new backup directory | # Create the new backup directory | |||
# | # | |||
eval { mkpath("$Dir/$newBkupNum", 0, 0777) }; | eval { mkpath("$Dir/$newBkupNum", 0, 0777) }; | |||
if ( $@ ) { | if ( $@ ) { | |||
print($LogFd $bpc->timeStamp, "Can't create $Dir/$newBkupNum\n"); | print($LogFd $bpc->timeStamp, "Can't create $Dir/$newBkupNum\n"); | |||
print("Can't create backup directory $Dir/$newBkupNum") | print("Can't create backup directory $Dir/$newBkupNum") | |||
if ( $opts{v} ); | if ( $opts{v} ); | |||
exit(1); | exit(1); | |||
} | } | |||
} | } | |||
if ( defined($newBkupNum) && !-d "$Dir/$newBkupNum/refCnt" ) { | if ( defined($newBkupNum) && !-d "$Dir/$newBkupNum/refCnt" ) { | |||
# | # | |||
# Create the new refCnt directory and flag it needing an fsck | # Create the new refCnt directory and flag it needing an fsck | |||
# in case we exit without cleanup. | # in case we exit without cleanup. | |||
# | # | |||
eval { mkpath("$Dir/$newBkupNum/refCnt", 0, 0777) }; | eval { mkpath("$Dir/$newBkupNum/refCnt", 0, 0777) }; | |||
if ( $@ ) { | if ( $@ ) { | |||
print($LogFd $bpc->timeStamp, "Can't create $Dir/$newBkupNum/refCnt\n"); | print($LogFd $bpc->timeStamp, "Can't create $Dir/$newBkupNum/refCnt\n"); | |||
print("Can't create backup directory $Dir/$newBkupNum/refCnt") | print("Can't create backup directory $Dir/$newBkupNum/refCnt") | |||
if ( $opts{v} ); | if ( $opts{v} ); | |||
exit(1); | exit(1); | |||
} | } | |||
my $needFsckFH; | my $needFsckFH; | |||
if ( !(open($needFsckFH, ">", "$Dir/$newBkupNum/refCnt/needFsck.newDir") && close($needFsckFH)) ) { | if ( !(open($needFsckFH, ">", "$Dir/$newBkupNum/refCnt/needFsck.newDir") && close($needFsckFH)) ) { | |||
$XferLOG->write(\"Can't create $Dir/$newBkupNum/refCnt/needFsck.newDir ( $?)\n"); | $XferLOG->write(\"Can't create $Dir/$newBkupNum/refCnt/needFsck.newDir ( $?)\n"); | |||
} | } | |||
} | } | |||
# | # | |||
# Save backupInfo and backups | # Save backupInfo and backups | |||
skipping to change at line 897 | skipping to change at line 900 | |||
my $sizeTotal = 0; | my $sizeTotal = 0; | |||
my($logMsg, %stat, $xfer, $ShareNames, $noFilesErr); | my($logMsg, %stat, $xfer, $ShareNames, $noFilesErr); | |||
$ShareNames = BackupPC::Xfer::getShareNames(\%Conf); | $ShareNames = BackupPC::Xfer::getShareNames(\%Conf); | |||
# | # | |||
# Run an optional pre-dump command | # Run an optional pre-dump command | |||
# | # | |||
UserCommandRun("DumpPreUserCmd"); | UserCommandRun("DumpPreUserCmd"); | |||
if ( $? && $Conf{UserCmdCheckStatus} ) { | if ( $? && $Conf{UserCmdCheckStatus} ) { | |||
print($LogFd $bpc->timeStamp, | print($LogFd $bpc->timeStamp, "DumpPreUserCmd returned error status $?... ex | |||
"DumpPreUserCmd returned error status $?... exiting\n"); | iting\n"); | |||
$XferLOG->write(\"DumpPreUserCmd returned error status $?... exiting\n"); | $XferLOG->write(\"DumpPreUserCmd returned error status $?... exiting\n"); | |||
$stat{hostError} = "DumpPreUserCmd returned error status $?"; | $stat{hostError} = "DumpPreUserCmd returned error status $?"; | |||
BackupFailCleanup(); | BackupFailCleanup(); | |||
} | } | |||
$NeedPostCmd = 1; | $NeedPostCmd = 1; | |||
# | # | |||
# Now backup each of the shares | # Now backup each of the shares | |||
# | # | |||
my $shareDuplicate = {}; | my $shareDuplicate = {}; | |||
skipping to change at line 922 | skipping to change at line 924 | |||
# the data transport program. | # the data transport program. | |||
# | # | |||
alarm($Conf{ClientTimeout}); | alarm($Conf{ClientTimeout}); | |||
local(*RH, *WH); | local(*RH, *WH); | |||
# | # | |||
# Convert $shareName to utf8 octets | # Convert $shareName to utf8 octets | |||
# | # | |||
$shareName = encode("utf8", $shareName); | $shareName = encode("utf8", $shareName); | |||
$stat{xferOK} = $stat{hostAbort} = undef; | $stat{xferOK} = $stat{hostAbort} = undef; | |||
$stat{hostError} = $stat{lastOutputLine} = undef; | $stat{hostError} = $stat{lastOutputLine} = undef; | |||
if ( $shareName eq "" ) { | if ( $shareName eq "" ) { | |||
print($LogFd $bpc->timeStamp, "unexpected empty share name skipped\n"); | print($LogFd $bpc->timeStamp, "unexpected empty share name skipped\n"); | |||
next; | next; | |||
} | } | |||
if ( $shareDuplicate->{$shareName} ) { | if ( $shareDuplicate->{$shareName} ) { | |||
print($LogFd $bpc->timeStamp, "unexpected repeated share name $shareName skipped\n"); | print($LogFd $bpc->timeStamp, "unexpected repeated share name $shareName skipped\n"); | |||
next; | next; | |||
} | } | |||
$shareDuplicate->{$shareName} = 1; | $shareDuplicate->{$shareName} = 1; | |||
skipping to change at line 958 | skipping to change at line 960 | |||
@Backups = $bpc->BackupInfoRead($client); | @Backups = $bpc->BackupInfoRead($client); | |||
$Backups[$newBkupIdx]{inodeLast} = $inodeLast; | $Backups[$newBkupIdx]{inodeLast} = $inodeLast; | |||
BackupPC::Storage->backupInfoWrite($Dir, $newBkupNum, $Backups[$newBkupIdx], 1); | BackupPC::Storage->backupInfoWrite($Dir, $newBkupNum, $Backups[$newBkupIdx], 1); | |||
$bpc->BackupInfoWrite($client, @Backups); | $bpc->BackupInfoWrite($client, @Backups); | |||
$xfer = BackupPC::Xfer::create($Conf{XferMethod}, $bpc); | $xfer = BackupPC::Xfer::create($Conf{XferMethod}, $bpc); | |||
if ( !defined($xfer) ) { | if ( !defined($xfer) ) { | |||
my $errStr = BackupPC::Xfer::errStr(); | my $errStr = BackupPC::Xfer::errStr(); | |||
print($LogFd $bpc->timeStamp, "dump failed: $errStr\n"); | print($LogFd $bpc->timeStamp, "dump failed: $errStr\n"); | |||
UserCommandRun("DumpPostShareCmd", $shareName) if ( $NeedPostCmd ); | UserCommandRun("DumpPostShareCmd", $shareName) if ( $NeedPostCmd ); | |||
UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); | UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); | |||
$XferLOG->write(\"BackupPC::Xfer::create failed: $errStr\n"); | $XferLOG->write(\"BackupPC::Xfer::create failed: $errStr\n"); | |||
$stat{hostError} = $errStr; | $stat{hostError} = $errStr; | |||
BackupFailCleanup(); | BackupFailCleanup(); | |||
} | } | |||
my $useTar = $xfer->useTar; | my $useTar = $xfer->useTar; | |||
if ( $useTar ) { | if ( $useTar ) { | |||
# | # | |||
# This xfer method outputs a tar format file, so we start a | # This xfer method outputs a tar format file, so we start a | |||
# BackupPC_tarExtract to extract the data. | # BackupPC_tarExtract to extract the data. | |||
# | # | |||
# Create a socketpair to connect the Xfer method to BackupPC_tarExtract | # Create a socketpair to connect the Xfer method to BackupPC_tarExtract | |||
# WH is the write handle for writing, provided to the transport | # WH is the write handle for writing, provided to the transport | |||
# program, and RH is the other end of the socket for reading, | # program, and RH is the other end of the socket for reading, | |||
# provided to BackupPC_tarExtract. | # provided to BackupPC_tarExtract. | |||
# | # | |||
if ( socketpair(RH, WH, AF_UNIX, SOCK_STREAM, PF_UNSPEC) ) { | if ( socketpair(RH, WH, AF_UNIX, SOCK_STREAM, PF_UNSPEC) ) { | |||
shutdown(RH, 1); # no writing to this socket | shutdown(RH, 1); # no writing to this socket | |||
shutdown(WH, 0); # no reading from this socket | shutdown(WH, 0); # no reading from this socket | |||
setsockopt(RH, SOL_SOCKET, SO_RCVBUF, 8 * 65536); | setsockopt(RH, SOL_SOCKET, SO_RCVBUF, 8 * 65536); | |||
setsockopt(WH, SOL_SOCKET, SO_SNDBUF, 8 * 65536); | setsockopt(WH, SOL_SOCKET, SO_SNDBUF, 8 * 65536); | |||
} else { | } else { | |||
# | # | |||
# Default to pipe() if socketpair() doesn't work. | # Default to pipe() if socketpair() doesn't work. | |||
# | # | |||
pipe(RH, WH); | pipe(RH, WH); | |||
} | } | |||
# | # | |||
# fork a child for BackupPC_tarExtract. TAR is a file handle | # fork a child for BackupPC_tarExtract. TAR is a file handle | |||
# on which we (the parent) read the stdout & stderr from | # on which we (the parent) read the stdout & stderr from | |||
# BackupPC_tarExtract. | # BackupPC_tarExtract. | |||
# | # | |||
if ( !defined($tarPid = open(TAR, "-|")) ) { | if ( !defined($tarPid = open(TAR, "-|")) ) { | |||
print($LogFd $bpc->timeStamp, "can't fork to run tar\n"); | print($LogFd $bpc->timeStamp, "can't fork to run tar\n"); | |||
print("can't fork to run tar\n"); | print("can't fork to run tar\n"); | |||
close(RH); | close(RH); | |||
close(WH); | close(WH); | |||
last; | last; | |||
} | } | |||
binmode(TAR); | binmode(TAR); | |||
if ( !$tarPid ) { | if ( !$tarPid ) { | |||
# | # | |||
# This is the tar child. Close the write end of the pipe, | # This is the tar child. Close the write end of the pipe, | |||
# clone STDERR to STDOUT, clone STDIN from RH, and then | # clone STDERR to STDOUT, clone STDIN from RH, and then | |||
# exec BackupPC_tarExtract. | # exec BackupPC_tarExtract. | |||
# | # | |||
setpgrp 0,0; | setpgrp 0, 0; | |||
close(WH); | close(WH); | |||
close(STDERR); | close(STDERR); | |||
open(STDERR, ">&STDOUT"); | open(STDERR, ">&STDOUT"); | |||
close(STDIN); | close(STDIN); | |||
open(STDIN, "<&RH"); | open(STDIN, "<&RH"); | |||
alarm(0); | alarm(0); | |||
my @tarOpts = ("-h", $client, "-s", $shareName); | my @tarOpts = ("-h", $client, "-s", $shareName); | |||
push(@tarOpts, "-f") if ( $type eq "full" ); | push(@tarOpts, "-f") if ( $type eq "full" ); | |||
push(@tarOpts, "-P") if ( $inPlace ); | push(@tarOpts, "-P") if ( $inPlace ); | |||
push(@tarOpts, "-p") if ( $opts{p} ); | push(@tarOpts, "-p") if ( $opts{p} ); | |||
exec("$BinDir/BackupPC_tarExtract", @tarOpts); | exec("$BinDir/BackupPC_tarExtract", @tarOpts); | |||
print($LogFd $bpc->timeStamp, "can't exec $BinDir/BackupPC_tarExtract | print($LogFd $bpc->timeStamp, "can't exec $BinDir/BackupPC_tarExtrac | |||
\n"); | t\n"); | |||
exit(0); | exit(0); | |||
} | } | |||
} | } | |||
# | # | |||
# Run the transport program | # Run the transport program | |||
# | # | |||
$xfer->args({ | $xfer->args({ | |||
host => $host, | host => $host, | |||
client => $client, | client => $client, | |||
hostIP => $hostIP, | hostIP => $hostIP, | |||
shareName => $shareName, | shareName => $shareName, | |||
pipeRH => *RH, | pipeRH => *RH, | |||
pipeWH => *WH, | pipeWH => *WH, | |||
XferLOG => $XferLOG, | XferLOG => $XferLOG, | |||
outDir => $Dir, | outDir => $Dir, | |||
type => $type, | type => $type, | |||
backups => \@Backups, | backups => \@Backups, | |||
compress => $Conf{CompressLevel}, | compress => $Conf{CompressLevel}, | |||
XferMethod => $Conf{XferMethod}, | XferMethod => $Conf{XferMethod}, | |||
logLevel => $Conf{XferLogLevel}, | logLevel => $Conf{XferLogLevel}, | |||
inPlace => $inPlace, | inPlace => $inPlace, | |||
newBkupIdx => $newBkupIdx, | newBkupIdx => $newBkupIdx, | |||
lastBkupIdx => $lastBkupIdx, | lastBkupIdx => $lastBkupIdx, | |||
incrBaseBkupNum => $incrBaseBkupNum, | incrBaseBkupNum => $incrBaseBkupNum, | |||
incrBaseTime => $incrBaseTime, | incrBaseTime => $incrBaseTime, | |||
pidHandler => \&pidHandler, | pidHandler => \&pidHandler, | |||
noProgressPrint => $opts{p}, | noProgressPrint => $opts{p}, | |||
}); | }); | |||
if ( !defined($logMsg = $xfer->start()) ) { | if ( !defined($logMsg = $xfer->start()) ) { | |||
my $errStr = "xfer start failed: " . $xfer->errStr . "\n"; | my $errStr = "xfer start failed: " . $xfer->errStr . "\n"; | |||
print($LogFd $bpc->timeStamp, $errStr); | print($LogFd $bpc->timeStamp, $errStr); | |||
# | # | |||
# kill off the tar process, first nicely then forcefully | # kill off the tar process, first nicely then forcefully | |||
# | # | |||
if ( $tarPid > 0 ) { | if ( $tarPid > 0 ) { | |||
kill($bpc->sigName2num("INT"), $tarPid); | kill($bpc->sigName2num("INT"), $tarPid); | |||
sleep(1); | sleep(1); | |||
kill($bpc->sigName2num("KILL"), $tarPid); | kill($bpc->sigName2num("KILL"), $tarPid); | |||
} | } | |||
if ( @xferPid ) { | if ( @xferPid ) { | |||
sleep(1); | sleep(1); | |||
kill($bpc->sigName2num("INT"), @xferPid); | kill($bpc->sigName2num("INT"), @xferPid); | |||
sleep(1); | sleep(1); | |||
kill($bpc->sigName2num("KILL"), @xferPid); | kill($bpc->sigName2num("KILL"), @xferPid); | |||
} | } | |||
UserCommandRun("DumpPostShareCmd", $shareName) if ( $NeedPostCmd ); | UserCommandRun("DumpPostShareCmd", $shareName) if ( $NeedPostCmd ); | |||
UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); | UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); | |||
$XferLOG->write(\$errStr); | $XferLOG->write(\$errStr); | |||
$stat{hostError} = $errStr; | $stat{hostError} = $errStr; | |||
BackupFailCleanup(); | BackupFailCleanup(); | |||
} | } | |||
# | # | |||
# Create a needFsck file, so if we are killed and can't recover, we can | # Create a needFsck file, so if we are killed and can't recover, we can | |||
# make sure an fsck is run next time. | # make sure an fsck is run next time. | |||
# | # | |||
my $needFsckFH; | my $needFsckFH; | |||
skipping to change at line 1092 | skipping to change at line 1094 | |||
# | # | |||
# Also grab a lock file, so we can serialize any fsck that might be | # Also grab a lock file, so we can serialize any fsck that might be | |||
# running. | # running. | |||
# | # | |||
$LockFd = BackupPC::XS::DirOps::lockRangeFile("$Dir/refCnt/LOCK", 0, 1, 1); | $LockFd = BackupPC::XS::DirOps::lockRangeFile("$Dir/refCnt/LOCK", 0, 1, 1); | |||
@xferPid = $xfer->xferPid; | @xferPid = $xfer->xferPid; | |||
if ( $useTar ) { | if ( $useTar ) { | |||
# | # | |||
# The parent must close both handles on the pipe since the children | # The parent must close both handles on the pipe since the children | |||
# are using these handles now. | # are using these handles now. | |||
# | # | |||
close(RH); | close(RH); | |||
close(WH); | close(WH); | |||
} | } | |||
print($LogFd $bpc->timeStamp, $logMsg, "\n"); | print($LogFd $bpc->timeStamp, $logMsg, "\n"); | |||
$XferLOG->write(\"$logMsg\n"); | $XferLOG->write(\"$logMsg\n"); | |||
print("started $type dump, share=$shareName\n"); | print("started $type dump, share=$shareName\n"); | |||
pidHandler(@xferPid); | pidHandler(@xferPid); | |||
if ( $useTar ) { | if ( $useTar ) { | |||
# | # | |||
# Parse the output of the transfer program and BackupPC_tarExtract | # Parse the output of the transfer program and BackupPC_tarExtract | |||
# while they run. Since we might be reading from two or more children | # while they run. Since we might be reading from two or more children | |||
# we use a select. | # we use a select. | |||
# | # | |||
my($FDread, $tarOut, $mesg); | my($FDread, $tarOut, $mesg); | |||
vec($FDread, fileno(TAR), 1) = 1; | vec($FDread, fileno(TAR), 1) = 1; | |||
$xfer->setSelectMask(\$FDread); | $xfer->setSelectMask(\$FDread); | |||
SCAN: while ( 1 ) { | SCAN: while ( 1 ) { | |||
my $ein = $FDread; | my $ein = $FDread; | |||
last if ( $FDread =~ /^\0*$/ ); | last if ( $FDread =~ /^\0*$/ ); | |||
select(my $rout = $FDread, undef, $ein, undef); | select(my $rout = $FDread, undef, $ein, undef); | |||
if ( vec($rout, fileno(TAR), 1) ) { | if ( vec($rout, fileno(TAR), 1) ) { | |||
if ( sysread(TAR, $mesg, 8192) <= 0 ) { | if ( sysread(TAR, $mesg, 8192) <= 0 ) { | |||
next if ( $!{EINTR} ); | next if ( $!{EINTR} ); | |||
vec($FDread, fileno(TAR), 1) = 0; | vec($FDread, fileno(TAR), 1) = 0; | |||
close(TAR); | close(TAR); | |||
if ( $? ) { | if ( $? ) { | |||
$XferLOG->write(\"BackupPC_tarExtract exited with fail s tatus $?\n"); | $XferLOG->write(\"BackupPC_tarExtract exited with fail s tatus $?\n"); | |||
$stat{hostError} = "BackupPC_tarExtract exited with fail status $?"; | $stat{hostError} = "BackupPC_tarExtract exited with fail status $?"; | |||
} | } | |||
} else { | } else { | |||
$tarOut .= $mesg; | $tarOut .= $mesg; | |||
} | } | |||
} | } | |||
while ( $tarOut =~ /(.*?)[\n\r]+(.*)/s ) { | while ( $tarOut =~ /(.*?)[\n\r]+(.*)/s ) { | |||
$_ = $1; | $_ = $1; | |||
$tarOut = $2; | $tarOut = $2; | |||
if ( /^ / ) { | if ( /^ / ) { | |||
$XferLOG->write(\"$_\n"); | $XferLOG->write(\"$_\n"); | |||
} elsif ( /^__bpc_progress_fileCnt__/ ) { | } elsif ( /^__bpc_progress_fileCnt__/ ) { | |||
print("$_\n"); | print("$_\n"); | |||
} else { | } else { | |||
$XferLOG->write(\"tarExtract: $_\n"); | $XferLOG->write(\"tarExtract: $_\n"); | |||
} | } | |||
if ( /^BackupPC_tarExtact aborting \((.*)\)/ ) { | if ( /^BackupPC_tarExtact aborting \((.*)\)/ ) { | |||
$stat{hostError} = $1; | $stat{hostError} = $1; | |||
} | } | |||
if ( /^Done: (\d+) errors, (\d+) filesExist, (\d+) sizeExist, (\ | if ( | |||
d+) sizeExistComp, (\d+) filesTotal, (\d+) sizeTotal, (\d+) filesNew, (\d+) size | /^Done: (\d+) errors, (\d+) filesExist, (\d+) sizeExist, (\d | |||
New, (\d+) sizeNewComp, (\d+) inodeLast/ ) { | +) sizeExistComp, (\d+) filesTotal, (\d+) sizeTotal, (\d+) filesNew, (\d+) sizeN | |||
ew, (\d+) sizeNewComp, (\d+) inodeLast/ | ||||
) { | ||||
$tarErrs += $1; | $tarErrs += $1; | |||
$nFilesExist += $2; | $nFilesExist += $2; | |||
$sizeExist += $3; | $sizeExist += $3; | |||
$sizeExistComp += $4; | $sizeExistComp += $4; | |||
$nFilesTotal += $5; | $nFilesTotal += $5; | |||
$sizeTotal += $6; | $sizeTotal += $6; | |||
$nFilesNew += $7; | $nFilesNew += $7; | |||
$sizeNew += $8; | $sizeNew += $8; | |||
$sizeNewComp += $9; | $sizeNewComp += $9; | |||
$inodeLast = $10; | $inodeLast = $10; | |||
} | } | |||
} | } | |||
last if ( !$xfer->readOutput(\$FDread, $rout) ); | last if ( !$xfer->readOutput(\$FDread, $rout) ); | |||
while ( my $str = $xfer->logMsgGet ) { | while ( my $str = $xfer->logMsgGet ) { | |||
print($LogFd $bpc->timeStamp, "xfer: $str\n"); | print($LogFd $bpc->timeStamp, "xfer: $str\n"); | |||
} | } | |||
if ( $xfer->getStats->{fileCnt} == 1 ) { | if ( $xfer->getStats->{fileCnt} == 1 ) { | |||
# | # | |||
# Make sure it is still the machine we expect. We do this while | # Make sure it is still the machine we expect. We do this while | |||
# the transfer is running to avoid a potential race condition if | # the transfer is running to avoid a potential race condition if | |||
# the ip address was reassigned by dhcp just before we started | # the ip address was reassigned by dhcp just before we started | |||
# the transfer. | # the transfer. | |||
# | # | |||
if ( my $errMsg = CorrectHostCheck($hostIP, $host) ) { | if ( my $errMsg = CorrectHostCheck($hostIP, $host) ) { | |||
$stat{hostError} = $errMsg if ( $stat{hostError} eq "" ); | $stat{hostError} = $errMsg if ( $stat{hostError} eq "" ); | |||
last SCAN; | last SCAN; | |||
} | } | |||
} | } | |||
} | } | |||
} else { | } else { | |||
# | # | |||
# otherwise the xfer module does everything for us | # otherwise the xfer module does everything for us | |||
# | # | |||
my @results = $xfer->run(); | my @results = $xfer->run(); | |||
$tarErrs += $results[0]; | $tarErrs += $results[0]; | |||
$nFilesExist += $results[1]; | $nFilesExist += $results[1]; | |||
$sizeExist += $results[2]; | $sizeExist += $results[2]; | |||
$sizeExistComp += $results[3]; | $sizeExistComp += $results[3]; | |||
$nFilesTotal += $results[4]; | $nFilesTotal += $results[4]; | |||
$sizeTotal += $results[5]; | $sizeTotal += $results[5]; | |||
$nFilesNew += $results[6]; | $nFilesNew += $results[6]; | |||
$sizeNew += $results[7]; | $sizeNew += $results[7]; | |||
$sizeNewComp += $results[8]; | $sizeNewComp += $results[8]; | |||
$inodeLast = $results[9]; | $inodeLast = $results[9]; | |||
} | } | |||
alarm(0); | alarm(0); | |||
# | # | |||
# Merge the xfer status (need to accumulate counts) | # Merge the xfer status (need to accumulate counts) | |||
# | # | |||
my $newStat = $xfer->getStats; | my $newStat = $xfer->getStats; | |||
# MAKSYM 14082016: forcing the right file count if some bytes were transferr ed; ensures compatibility with at least Samba-4.3 | # MAKSYM 14082016: forcing the right file count if some bytes were transferr ed; ensures compatibility with at least Samba-4.3 | |||
$newStat->{fileCnt} = $nFilesTotal if ( $useTar && $newStat->{fileCnt} == 0 && $xfer->getStats->{byteCnt} > 0 ); | $newStat->{fileCnt} = $nFilesTotal if ( $useTar && $newStat->{fileCnt} == 0 && $xfer->getStats->{byteCnt} > 0 ); | |||
if ( $newStat->{fileCnt} == 0 ) { | if ( $newStat->{fileCnt} == 0 ) { | |||
$noFilesErr ||= "No files dumped for share $shareName"; | $noFilesErr ||= "No files dumped for share $shareName"; | |||
} | } | |||
foreach my $k ( (keys(%stat), keys(%$newStat)) ) { | foreach my $k ( (keys(%stat), keys(%$newStat)) ) { | |||
next if ( !defined($newStat->{$k}) ); | next if ( !defined($newStat->{$k}) ); | |||
if ( $k =~ /Cnt$/ ) { | if ( $k =~ /Cnt$/ ) { | |||
$stat{$k} += $newStat->{$k}; | $stat{$k} += $newStat->{$k}; | |||
delete($newStat->{$k}); | delete($newStat->{$k}); | |||
next; | next; | |||
} | } | |||
if ( !defined($stat{$k}) ) { | if ( !defined($stat{$k}) ) { | |||
$stat{$k} = $newStat->{$k}; | $stat{$k} = $newStat->{$k}; | |||
delete($newStat->{$k}); | delete($newStat->{$k}); | |||
next; | next; | |||
} | } | |||
} | } | |||
if ( $NeedPostCmd ) { | if ( $NeedPostCmd ) { | |||
UserCommandRun("DumpPostShareCmd", $shareName); | UserCommandRun("DumpPostShareCmd", $shareName); | |||
if ( $? && $Conf{UserCmdCheckStatus} ) { | if ( $? && $Conf{UserCmdCheckStatus} ) { | |||
print($LogFd $bpc->timeStamp, | print($LogFd $bpc->timeStamp, "DumpPostShareCmd returned error statu | |||
"DumpPostShareCmd returned error status $?... exiting\n"); | s $?... exiting\n"); | |||
$stat{hostError} = "DumpPostShareCmd returned error status $?"; | $stat{hostError} = "DumpPostShareCmd returned error status $?"; | |||
} | } | |||
} | } | |||
$stat{xferOK} = 0 if ( $stat{hostError} || $stat{hostAbort} ); | $stat{xferOK} = 0 if ( $stat{hostError} || $stat{hostAbort} ); | |||
if ( !$stat{xferOK} ) { | if ( !$stat{xferOK} ) { | |||
# | # | |||
# kill off the transfer program, first nicely then forcefully | # kill off the transfer program, first nicely then forcefully | |||
# | # | |||
if ( @xferPid ) { | if ( @xferPid ) { | |||
kill($bpc->sigName2num("INT"), @xferPid); | kill($bpc->sigName2num("INT"), @xferPid); | |||
sleep(1); | sleep(1); | |||
kill($bpc->sigName2num("KILL"), @xferPid); | kill($bpc->sigName2num("KILL"), @xferPid); | |||
} | } | |||
# | # | |||
# kill off the tar process, first nicely then forcefully | # kill off the tar process, first nicely then forcefully | |||
# | # | |||
if ( $tarPid > 0 ) { | if ( $tarPid > 0 ) { | |||
sleep(1); | sleep(1); | |||
kill($bpc->sigName2num("INT"), $tarPid); | kill($bpc->sigName2num("INT"), $tarPid); | |||
sleep(1); | sleep(1); | |||
kill($bpc->sigName2num("KILL"), $tarPid); | kill($bpc->sigName2num("KILL"), $tarPid); | |||
} | } | |||
# | # | |||
# don't do any more shares on this host | # don't do any more shares on this host | |||
# | # | |||
BackupPC::XS::DirOps::unlockRangeFile($LockFd); | BackupPC::XS::DirOps::unlockRangeFile($LockFd); | |||
$LockFd = undef; | $LockFd = undef; | |||
last; | last; | |||
} | } | |||
# | # | |||
# Wait for any child processes to exit | # Wait for any child processes to exit | |||
# | # | |||
skipping to change at line 1266 | skipping to change at line 1270 | |||
unlink("$Dir/$newBkupNum/refCnt/needFsck.dump"); | unlink("$Dir/$newBkupNum/refCnt/needFsck.dump"); | |||
BackupPC::XS::DirOps::unlockRangeFile($LockFd); | BackupPC::XS::DirOps::unlockRangeFile($LockFd); | |||
$LockFd = undef; | $LockFd = undef; | |||
} | } | |||
$tarPid = 0; | $tarPid = 0; | |||
pidHandler(); | pidHandler(); | |||
# | # | |||
# If this is a full, and any share had zero files then consider the dump bad | # If this is a full, and any share had zero files then consider the dump bad | |||
# | # | |||
if ( $type eq "full" && $stat{hostError} eq "" | if ( $type eq "full" | |||
&& length($noFilesErr) && $Conf{BackupZeroFilesIsFatal} ) { | && $stat{hostError} eq "" | |||
&& length($noFilesErr) | ||||
&& $Conf{BackupZeroFilesIsFatal} ) { | ||||
$stat{hostError} = $noFilesErr; | $stat{hostError} = $noFilesErr; | |||
$stat{xferOK} = 0; | $stat{xferOK} = 0; | |||
} | } | |||
$stat{xferOK} = 0 if ( $Abort ); | $stat{xferOK} = 0 if ( $Abort ); | |||
# | # | |||
# Do one last check to make sure it is still the machine we expect. | # Do one last check to make sure it is still the machine we expect. | |||
# | # | |||
if ( $stat{xferOK} && (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { | if ( $stat{xferOK} && (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { | |||
$stat{hostError} = $errMsg; | $stat{hostError} = $errMsg; | |||
$stat{xferOK} = 0; | $stat{xferOK} = 0; | |||
} | } | |||
# | # | |||
# Remove any shares that exist in the backup, but aren't in $ShareNames | # Remove any shares that exist in the backup, but aren't in $ShareNames | |||
# | # | |||
if ( $stat{xferOK} ) { | if ( $stat{xferOK} ) { | |||
OrphanShareNameClean("$Dir/$newBkupNum", $ShareNames, $Backups[$newBkupIdx]{ compress}); | OrphanShareNameClean("$Dir/$newBkupNum", $ShareNames, $Backups[$newBkupIdx]{ compress}); | |||
} | } | |||
UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); | UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); | |||
if ( $? && $Conf{UserCmdCheckStatus} ) { | if ( $? && $Conf{UserCmdCheckStatus} ) { | |||
print($LogFd $bpc->timeStamp, | print($LogFd $bpc->timeStamp, "DumpPostUserCmd returned error status $?... e | |||
"DumpPostUserCmd returned error status $?... exiting\n"); | xiting\n"); | |||
$stat{hostError} = "DumpPostUserCmd returned error status $?"; | $stat{hostError} = "DumpPostUserCmd returned error status $?"; | |||
$stat{xferOK} = 0; | $stat{xferOK} = 0; | |||
} | } | |||
my $endTime = time(); | my $endTime = time(); | |||
# | # | |||
# If the dump failed, clean up | # If the dump failed, clean up | |||
# | # | |||
if ( !$stat{xferOK} ) { | if ( !$stat{xferOK} ) { | |||
$stat{hostError} = $stat{lastOutputLine} if ( $stat{hostError} eq "" ); | $stat{hostError} = $stat{lastOutputLine} if ( $stat{hostError} eq "" ); | |||
if ( $stat{hostError} ) { | if ( $stat{hostError} ) { | |||
print($LogFd $bpc->timeStamp, | print($LogFd $bpc->timeStamp, "Got fatal error during xfer ($stat{hostEr | |||
"Got fatal error during xfer ($stat{hostError})\n"); | ror})\n"); | |||
$XferLOG->write(\"Got fatal error during xfer ($stat{hostError})\n"); | $XferLOG->write(\"Got fatal error during xfer ($stat{hostError})\n"); | |||
} | } | |||
if ( !$Abort ) { | if ( !$Abort ) { | |||
# | # | |||
# wait a short while and see if the system is still alive | # wait a short while and see if the system is still alive | |||
# | # | |||
sleep(5); | sleep(5); | |||
if ( $bpc->CheckHostAlive($hostIP) < 0 ) { | if ( $bpc->CheckHostAlive($hostIP) < 0 ) { | |||
$stat{hostAbort} = 1; | $stat{hostAbort} = 1; | |||
} | } | |||
if ( $stat{hostAbort} ) { | if ( $stat{hostAbort} ) { | |||
$stat{hostError} = "lost network connection during backup"; | $stat{hostError} = "lost network connection during backup"; | |||
} | } | |||
print($LogFd $bpc->timeStamp, "Backup aborted ($stat{hostError})\n"); | print($LogFd $bpc->timeStamp, "Backup aborted ($stat{hostError})\n"); | |||
$XferLOG->write(\"Backup aborted ($stat{hostError})\n"); | $XferLOG->write(\"Backup aborted ($stat{hostError})\n"); | |||
} else { | } else { | |||
$XferLOG->write(\"Backup aborted by user signal\n"); | if ( $XferLOG ) { | |||
$XferLOG->write(\"Backup aborted by user signal\n"); | ||||
} elsif ( $opts{v} ) { | ||||
print("Backup aborted by user signal\n"); | ||||
} | ||||
} | } | |||
# | # | |||
# Close the log file and call BackupFailCleanup, which exits. | # Close the log file and call BackupFailCleanup, which exits. | |||
# | # | |||
BackupFailCleanup(); | BackupFailCleanup(); | |||
} | } | |||
if ( $BackupCase == 4 && ($lastBkupType eq "partial" || $lastBkupType eq "active ") ) { | if ( $BackupCase == 4 && ($lastBkupType eq "partial" || $lastBkupType eq "active ") ) { | |||
# | # | |||
# Delete the prior backup #n, so that its deltas are merged into #n-1 | # Delete the prior backup #n, so that its deltas are merged into #n-1 | |||
# | # | |||
print($LogFd $bpc->timeStamp, "Removing prior partial backup #$lastBkupNum\n "); | print($LogFd $bpc->timeStamp, "Removing prior partial backup #$lastBkupNum\n "); | |||
$XferLOG->write(\"Removing prior partial backup #$lastBkupNum\n"); | $XferLOG->write(\"Removing prior partial backup #$lastBkupNum\n"); | |||
BackupRemove($client, $lastBkupIdx, 1); | BackupRemove($client, $lastBkupIdx, 1); | |||
} | } | |||
my $newNum = BackupSave(); | my $newNum = BackupSave(); | |||
my $otherCount = $stat{xferErrCnt} - $stat{xferBadFileCnt} | my $otherCount = $stat{xferErrCnt} - $stat{xferBadFileCnt} - $stat{xferBadShareC | |||
- $stat{xferBadShareCnt}; | nt}; | |||
$stat{fileCnt} ||= 0; | $stat{fileCnt} ||= 0; | |||
$stat{byteCnt} ||= 0; | $stat{byteCnt} ||= 0; | |||
$stat{xferErrCnt} ||= 0; | $stat{xferErrCnt} ||= 0; | |||
$stat{xferBadFileCnt} ||= 0; | $stat{xferBadFileCnt} ||= 0; | |||
$stat{xferBadShareCnt} ||= 0; | $stat{xferBadShareCnt} ||= 0; | |||
print($LogFd $bpc->timeStamp, | my $logStr = | |||
"$type backup $newNum complete, $stat{fileCnt} files," | "$type backup $newNum complete, $stat{fileCnt} files, $stat{byteCnt} bytes," | |||
. " $stat{byteCnt} bytes," | . " $stat{xferErrCnt} xferErrs ($stat{xferBadFileCnt} bad files," | |||
. " $stat{xferErrCnt} xferErrs ($stat{xferBadFileCnt} bad files," | . " $stat{xferBadShareCnt} bad shares, $otherCount other)\n"; | |||
. " $stat{xferBadShareCnt} bad shares, $otherCount other)\n"); | print($LogFd $bpc->timeStamp, $logStr); | |||
$XferLOG->write(\"$type backup $newNum complete, $stat{fileCnt} files," | $XferLOG->write(\$logStr) if ( $XferLOG && $Conf{XferLogLevel} >= 1 ); | |||
. " $stat{byteCnt} bytes," | ||||
. " $stat{xferErrCnt} xferErrs ($stat{xferBadFileCnt} bad files," | ||||
. " $stat{xferBadShareCnt} bad shares, $otherCount other)\n") | ||||
if ( $XferLOG && $Conf{XferLogLevel} >= 1 ); | ||||
if ( $stat{xferOK} && $BackupCase == 4 && -f "$Dir/$lastBkupNum/refCnt/needFsck. newDir" ) { | if ( $stat{xferOK} && $BackupCase == 4 && -f "$Dir/$lastBkupNum/refCnt/needFsck. newDir" ) { | |||
# | # | |||
# remove temporary needFsck file on previous backup since backup succeeded o k | # remove temporary needFsck file on previous backup since backup succeeded o k | |||
# | # | |||
unlink("$Dir/$lastBkupNum/refCnt/needFsck.newDir"); | unlink("$Dir/$lastBkupNum/refCnt/needFsck.newDir"); | |||
} | } | |||
BackupExpire($client); | BackupExpire($client); | |||
skipping to change at line 1411 | skipping to change at line 1414 | |||
{ | { | |||
my $sigName = shift; | my $sigName = shift; | |||
# | # | |||
# The first time we receive a signal we try to gracefully | # The first time we receive a signal we try to gracefully | |||
# abort the backup. This allows us to keep a partial dump | # abort the backup. This allows us to keep a partial dump | |||
# with the in-progress file deleted and attribute caches | # with the in-progress file deleted and attribute caches | |||
# flushed to disk etc. | # flushed to disk etc. | |||
# | # | |||
if ( !length($SigName) ) { | if ( !length($SigName) ) { | |||
my $reason; | my $reason; | |||
if ( $sigName eq "INT" ) { | if ( $sigName eq "INT" ) { | |||
$reason = "aborted by user (signal=$sigName)"; | $reason = "aborted by user (signal=$sigName)"; | |||
} else { | } else { | |||
$reason = "aborted by signal=$sigName"; | $reason = "aborted by signal=$sigName"; | |||
} | } | |||
$stat{hostError} = $reason; | $stat{hostError} = $reason; | |||
if ( $Pid == $$ ) { | print("Received signal: $reason\n") if ( $opts{v} ); | |||
# | if ( $Pid == $$ ) { | |||
# Parent logs a message | # | |||
# | # Parent logs a message | |||
print($LogFd $bpc->timeStamp, | # | |||
"Aborting backup up after signal $sigName\n"); | print($LogFd $bpc->timeStamp, "Aborting backup up after signal $sigN | |||
ame\n"); | ||||
# | print("Aborting backup up after signal $sigName\n") if ( $opts{v} ); | |||
# Tell xfer to abort, but only if we actually started one | ||||
# | # | |||
$xfer->abort($reason) if ( defined($xfer) ); | # Tell xfer to abort, but only if we actually started one | |||
# | ||||
# | $xfer->abort($reason) if ( defined($xfer) ); | |||
# Send ALRMs to BackupPC_tarExtract if we are using it | ||||
# | # | |||
if ( $tarPid > 0 ) { | # Send ALRMs to BackupPC_tarExtract if we are using it | |||
kill($bpc->sigName2num("ARLM"), $tarPid); | # | |||
} | if ( $tarPid > 0 ) { | |||
kill($bpc->sigName2num("ALRM"), $tarPid); | ||||
} else { | } | |||
# | ||||
# Children ignore anything other than ALRM and INT | } else { | |||
# | # | |||
if ( $sigName ne "ALRM" && $sigName ne "INT" ) { | # Children ignore anything other than ALRM and INT | |||
return; | # | |||
} | if ( $sigName ne "ALRM" && $sigName ne "INT" ) { | |||
return; | ||||
# | } | |||
# The child also tells xfer to abort | ||||
# | # | |||
$xfer->abort($reason); | # The child also tells xfer to abort | |||
# | ||||
} | $xfer->abort($reason); | |||
$SigName = $sigName; | ||||
$Abort = 1; | } | |||
return; | $SigName = $sigName; | |||
$Abort = 1; | ||||
return; | ||||
} | } | |||
# | # | |||
# This is a second signal: time to clean up. | # This is a second signal: time to clean up. | |||
# | # | |||
if ( $Pid != $$ && ($sigName eq "ALRM" || $sigName eq "INT") ) { | if ( $Pid != $$ && ($sigName eq "ALRM" || $sigName eq "INT") ) { | |||
# | # | |||
# Children quit quietly on ALRM or INT | # Children quit quietly on ALRM or INT | |||
# | # | |||
exit(1) | exit(1); | |||
} | } | |||
# | # | |||
# Ignore other signals in children | # Ignore other signals in children | |||
# | # | |||
return if ( $Pid != $$ ); | return if ( $Pid != $$ ); | |||
$SIG{$sigName} = 'IGNORE'; | $SIG{$sigName} = 'IGNORE'; | |||
UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); | UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); | |||
$XferLOG->write(\"exiting after signal $sigName\n"); | if ( $XferLOG ) { | |||
$XferLOG->write(\"exiting after signal $sigName\n"); | ||||
} elsif ( $opts{v} ) { | ||||
print("exiting after signal $sigName\n"); | ||||
} | ||||
if ( @xferPid ) { | if ( @xferPid ) { | |||
printf("killing xfer pids %s\n", join(",", @xferPid)) if ( $opts{v} ); | ||||
kill($bpc->sigName2num("INT"), @xferPid); | kill($bpc->sigName2num("INT"), @xferPid); | |||
sleep(1); | sleep(1); | |||
kill($bpc->sigName2num("KILL"), @xferPid); | kill($bpc->sigName2num("KILL"), @xferPid); | |||
} | } | |||
if ( $tarPid > 0 ) { | if ( $tarPid > 0 ) { | |||
sleep(1); | printf("killing tar pid $tarPid\n") if ( $opts{v} ); | |||
sleep(1); | ||||
kill($bpc->sigName2num("INT"), $tarPid); | kill($bpc->sigName2num("INT"), $tarPid); | |||
sleep(1); | sleep(1); | |||
kill($bpc->sigName2num("KILL"), $tarPid); | kill($bpc->sigName2num("KILL"), $tarPid); | |||
} | } | |||
if ( $sigName eq "INT" ) { | if ( $sigName eq "INT" ) { | |||
$stat{hostError} = "aborted by user (signal=$sigName)"; | $stat{hostError} = "aborted by user (signal=$sigName)"; | |||
} else { | } else { | |||
$stat{hostError} = "received signal=$sigName"; | $stat{hostError} = "received signal=$sigName"; | |||
} | } | |||
BackupFailCleanup(); | BackupFailCleanup(); | |||
} | } | |||
sub CheckForAnyFiles | sub CheckForAnyFiles | |||
skipping to change at line 1571 | skipping to change at line 1581 | |||
$compress = $Backups[$newBkupIdx]{compress}; | $compress = $Backups[$newBkupIdx]{compress}; | |||
$removeIdx = $newBkupIdx; | $removeIdx = $newBkupIdx; | |||
$removeNum = $newBkupNum; | $removeNum = $newBkupNum; | |||
} elsif ( $BackupCase == 4 && $lastBkupType ne "partial" ) { | } elsif ( $BackupCase == 4 && $lastBkupType ne "partial" ) { | |||
$checkDir = "$Dir/$lastBkupNum"; | $checkDir = "$Dir/$lastBkupNum"; | |||
$compress = $Backups[$lastBkupIdx]{compress}; | $compress = $Backups[$lastBkupIdx]{compress}; | |||
$removeIdx = $lastBkupIdx; | $removeIdx = $lastBkupIdx; | |||
$removeNum = $lastBkupNum; | $removeNum = $lastBkupNum; | |||
} | } | |||
if ( $nFilesTotal == 0 && $checkDir ne "" ) { | if ( $nFilesTotal == 0 && $checkDir ne "" ) { | |||
BackupPC::DirOps::find($bpc, {wanted => | BackupPC::DirOps::find( | |||
sub { | $bpc, | |||
{ | ||||
wanted => sub { | ||||
CheckForAnyFiles(@_, $compress); | CheckForAnyFiles(@_, $compress); | |||
}}, $checkDir); | } | |||
}, | ||||
$checkDir | ||||
); | ||||
} | } | |||
$XferLOG->write(\"BackupFailCleanup: nFilesTotal = $nFilesTotal, type = $typ | $XferLOG->write( | |||
e, BackupCase = $BackupCase, inPlace = $inPlace, lastBkupNum = $lastBkupNum\n"); | \"BackupFailCleanup: nFilesTotal = $nFilesTotal, type = $type, BackupCas | |||
e = $BackupCase, inPlace = $inPlace, lastBkupNum = $lastBkupNum\n" | ||||
); | ||||
if ( $BackupCase == 4 && ($lastBkupType eq "partial" || $lastBkupType eq "ac tive") ) { | if ( $BackupCase == 4 && ($lastBkupType eq "partial" || $lastBkupType eq "ac tive") ) { | |||
# | # | |||
# Delete the prior backup #n, so that its deltas are merged into #n-1 | # Delete the prior backup #n, so that its deltas are merged into #n-1 | |||
# | # | |||
print($LogFd $bpc->timeStamp, "Removing prior partial backup #$lastBkupN um\n"); | print($LogFd $bpc->timeStamp, "Removing prior partial backup #$lastBkupN um\n"); | |||
$XferLOG->write(\"Removing prior partial backup #$lastBkupNum\n"); | $XferLOG->write(\"Removing prior partial backup #$lastBkupNum\n"); | |||
BackupRemove($client, $lastBkupIdx, 1); | BackupRemove($client, $lastBkupIdx, 1); | |||
$Backups[-1]{type} = "partial" if ( @Backups ); | $Backups[-1]{type} = "partial" if ( @Backups ); | |||
} elsif ( $BackupCase == 4 ) { | } elsif ( $BackupCase == 4 ) { | |||
if ( $nFilesTotal == 0 ) { | if ( $nFilesTotal == 0 ) { | |||
# | # | |||
# Remove the empty backup directory. The new XferLOG file gets rena med | # Remove the empty backup directory. The new XferLOG file gets rena med | |||
# to bad. | # to bad. | |||
# | # | |||
$Backups[$newBkupIdx] = {%{$Backups[$lastBkupIdx]}}; | $Backups[$newBkupIdx] = {%{$Backups[$lastBkupIdx]}}; | |||
$Backups[$newBkupIdx]{num} = $newBkupNum; | $Backups[$newBkupIdx]{num} = $newBkupNum; | |||
$Backups[$newBkupIdx]{noFill} = 0; | $Backups[$newBkupIdx]{noFill} = 0; | |||
$Backups[$newBkupIdx]{keep} = 0; | ||||
print($LogFd $bpc->timeStamp, "Removing empty backup #$removeNum\n") ; | print($LogFd $bpc->timeStamp, "Removing empty backup #$removeNum\n") ; | |||
$XferLOG->write(\"Removing empty backup #$removeNum\n"); | $XferLOG->write(\"Removing empty backup #$removeNum\n"); | |||
BackupRemove($client, $removeIdx, 0); | BackupRemove($client, $removeIdx, 0); | |||
push(@logRenames, | push(@logRenames, | |||
{from => "$Dir/XferLOG.$newBkupNum$fileExt", to => "$Dir/XferLO G.bad$fileExt"}, | {from => "$Dir/XferLOG.$newBkupNum$fileExt", to => "$Dir/XferLO G.bad$fileExt"}, | |||
{from => "$Dir/XferLOG.$lastBkupNum$fileExt", to => "$Dir/XferLO | {from => "$Dir/XferLOG.$lastBkupNum$fileExt", to => "$Dir/XferLO | |||
G.$newBkupNum$fileExt"} | G.$newBkupNum$fileExt"}); | |||
); | ||||
} else { | } else { | |||
$XferLOG->write(\"Keeping non-empty backup #$removeNum ($checkDir)\n "); | $XferLOG->write(\"Keeping non-empty backup #$removeNum ($checkDir)\n "); | |||
$Backups[-1]{type} = "partial" if ( @Backups ); | $Backups[-1]{type} = "partial" if ( @Backups ); | |||
} | } | |||
} elsif ( $BackupCase == 1 || $BackupCase == 5 ) { | } elsif ( $BackupCase == 1 || $BackupCase == 5 ) { | |||
if ( $nFilesTotal == 0 ) { | if ( $nFilesTotal == 0 ) { | |||
# | # | |||
# Remove the empty backup directory. The new XferLOG file gets rena med | # Remove the empty backup directory. The new XferLOG file gets rena med | |||
# to bad. | # to bad. | |||
# | # | |||
print($LogFd $bpc->timeStamp, "Removing empty backup #$removeNum\n") ; | print($LogFd $bpc->timeStamp, "Removing empty backup #$removeNum\n") ; | |||
$XferLOG->write(\"Removing empty backup #$removeNum\n"); | $XferLOG->write(\"Removing empty backup #$removeNum\n"); | |||
push(@logRenames, | push(@logRenames, {from => "$Dir/XferLOG.$newBkupNum$fileExt", to => | |||
{from => "$Dir/XferLOG.$newBkupNum$fileExt", to => "$Dir/XferLO | "$Dir/XferLOG.bad$fileExt"}); | |||
G.bad$fileExt"}, | ||||
); | ||||
BackupRemove($client, $removeIdx, 0); | BackupRemove($client, $removeIdx, 0); | |||
} else { | } else { | |||
$XferLOG->write(\"Keeping non-empty backup #$removeNum ($checkDir)\n "); | $XferLOG->write(\"Keeping non-empty backup #$removeNum ($checkDir)\n "); | |||
$Backups[-1]{type} = "partial" if ( @Backups ); | $Backups[-1]{type} = "partial" if ( @Backups ); | |||
} | } | |||
} else { | } else { | |||
if ( $nFilesTotal == 0 ) { | if ( $nFilesTotal == 0 ) { | |||
$XferLOG->write(\"BackupFailCleanup: inPlace with no new files... no cleanup\n"); | $XferLOG->write(\"BackupFailCleanup: inPlace with no new files... no cleanup\n"); | |||
if ( $BackupCase == 6 || $BackupCase == 2 || $BackupCase == 3 ) { | if ( $BackupCase == 6 || $BackupCase == 2 || $BackupCase == 3 ) { | |||
$Backups[-1]{type} = "partial" if ( @Backups ); | $Backups[-1]{type} = "partial" if ( @Backups ); | |||
skipping to change at line 1672 | skipping to change at line 1687 | |||
print("dump failed: $stat{hostError}\n"); | print("dump failed: $stat{hostError}\n"); | |||
exit(1); | exit(1); | |||
} | } | |||
# | # | |||
# Decide which old backups should be expired. | # Decide which old backups should be expired. | |||
# | # | |||
sub BackupExpire | sub BackupExpire | |||
{ | { | |||
my($client) = @_; | my($client) = @_; | |||
my($Dir) = "$TopDir/pc/$client"; | my($Dir) = "$TopDir/pc/$client"; | |||
my($cntFull, $cntIncr, $firstFull, $firstIncr, $oldestIncr, | my($cntFull, $cntIncr, $firstFull, $firstIncr, $oldestIncr, $oldestFull, $ch | |||
$oldestFull, $changes); | anges); | |||
@Backups = $bpc->BackupInfoRead($client); | @Backups = $bpc->BackupInfoRead($client); | |||
if ( (ref($Conf{FullKeepCnt}) eq "ARRAY" ? @{$Conf{FullKeepCnt}} : $Conf{Ful lKeepCnt}) <= 0 ) { | if ( (ref($Conf{FullKeepCnt}) eq "ARRAY" ? @{$Conf{FullKeepCnt}} : $Conf{Ful lKeepCnt}) <= 0 ) { | |||
print($LogFd $bpc->timeStamp, | print($LogFd $bpc->timeStamp, | |||
"Invalid value for \$Conf{FullKeepCnt}=$Conf{FullKeepCnt}; not | "Invalid value for \$Conf{FullKeepCnt}=$Conf{FullKeepCnt}; not expir | |||
expiring any backups\n"); | ing any backups\n"); | |||
print("Invalid value for \$Conf{FullKeepCnt}=$Conf{FullKeepCnt}; not expi | print("Invalid value for \$Conf{FullKeepCnt}=$Conf{FullKeepCnt}; not exp | |||
ring any backups\n") | iring any backups\n") | |||
if ( $opts{v} ); | if ( $opts{v} ); | |||
return; | return; | |||
} | } | |||
while ( 1 ) { | while ( 1 ) { | |||
$cntFull = $cntIncr = 0; | $cntFull = $cntIncr = 0; | |||
$oldestIncr = $oldestFull = 0; | $oldestIncr = $oldestFull = 0; | |||
for ( my $i = 0 ; $i < @Backups ; $i++ ) { | for ( my $i = 0 ; $i < @Backups ; $i++ ) { | |||
# | ||||
# don't consider any backups marked keep | ||||
# | ||||
next if ( $Backups[$i]{keep} ); | ||||
$Backups[$i]{preV4} = ($Backups[$i]{version} eq "" || $Backups[$i]{v ersion} =~ /^[23]\./) ? 1 : 0; | $Backups[$i]{preV4} = ($Backups[$i]{version} eq "" || $Backups[$i]{v ersion} =~ /^[23]\./) ? 1 : 0; | |||
if ( $Backups[$i]{preV4} ) { | if ( $Backups[$i]{preV4} ) { | |||
if ( $Backups[$i]{type} eq "full" ) { | if ( $Backups[$i]{type} eq "full" ) { | |||
$firstFull = $i if ( $cntFull == 0 ); | $firstFull = $i if ( $cntFull == 0 ); | |||
$cntFull++; | $cntFull++; | |||
} elsif ( $Backups[$i]{type} eq "incr" ) { | } elsif ( $Backups[$i]{type} eq "incr" ) { | |||
$firstIncr = $i if ( $cntIncr == 0 ); | $firstIncr = $i if ( $cntIncr == 0 ); | |||
$cntIncr++; | $cntIncr++; | |||
} | } | |||
} else { | } else { | |||
if ( !$Backups[$i]{noFill} ) { | if ( !$Backups[$i]{noFill} ) { | |||
$firstFull = $i if ( $cntFull == 0 ); | $firstFull = $i if ( $cntFull == 0 ); | |||
$cntFull++; | $cntFull++; | |||
} else { | } else { | |||
$firstIncr = $i if ( $cntIncr == 0 ); | $firstIncr = $i if ( $cntIncr == 0 ); | |||
$cntIncr++; | $cntIncr++; | |||
} | } | |||
} | } | |||
} | } | |||
$oldestIncr = (time - $Backups[$firstIncr]{startTime}) / (24 * 3600) | $oldestIncr = (time - $Backups[$firstIncr]{startTime}) / (24 * 3600) | |||
if ( $cntIncr > 0 ); | if ( $cntIncr > 0 ); | |||
$oldestFull = (time - $Backups[$firstFull]{startTime}) / (24 * 3600) | $oldestFull = (time - $Backups[$firstFull]{startTime}) / (24 * 3600) | |||
if ( $cntFull > 0 ); | if ( $cntFull > 0 ); | |||
$XferLOG->write(\"BackupExpire: cntFull = $cntFull, cntIncr = $cntIncr, | if ( $XferLOG ) { | |||
firstFull = $firstFull," | my $logStr = "BackupExpire: cntFull = $cntFull, cntIncr = $cntIncr, | |||
. " firstIncr = $firstIncr, oldestIncr = $oldestIncr, oldestF | firstFull = $firstFull," | |||
ull = $oldestFull\n") | . " firstIncr = $firstIncr, oldestIncr = $oldestIncr, oldestFull = | |||
if ( $XferLOG ); | $oldestFull\n"; | |||
$XferLOG->write(\$logStr); | ||||
} | ||||
# | # | |||
# In <= 3.x, with multi-level incrementals, several of the | # In <= 3.x, with multi-level incrementals, several of the | |||
# following incrementals might depend upon this one, so we | # following incrementals might depend upon this one, so we | |||
# have to delete all of the them. Figure out if that is | # have to delete all of the them. Figure out if that is | |||
# possible by counting the number of consecutive incrementals | # possible by counting the number of consecutive incrementals | |||
# that are unfilled and have a level higher than this one. | # that are unfilled and have a level higher than this one. | |||
# | # | |||
# In >= 4.x any backup can be deleted since the changes get | # In >= 4.x any backup can be deleted since the changes get | |||
# merged with the next older deltas, so we just do one at | # merged with the next older deltas, so we just do one at | |||
# a time. | # a time. | |||
# | # | |||
my $cntIncrDel = 1; | my $cntIncrDel = 1; | |||
my $earliestIncr = $oldestIncr; | my $earliestIncr = $oldestIncr; | |||
if ( defined($firstIncr) ) { | if ( defined($firstIncr) ) { | |||
for ( my $i = $firstIncr + 1 ; $i < @Backups ; $i++ ) { | for ( my $i = $firstIncr + 1 ; $i < @Backups ; $i++ ) { | |||
last if ( !$Backups[$i]{preV4} || $Backups[$i]{level} <= $Backup | last | |||
s[$firstIncr]{level} | if ( !$Backups[$i]{preV4} | |||
|| !$Backups[$i]{noFill} ); | || $Backups[$i]{level} <= $Backups[$firstIncr]{level} | |||
|| !$Backups[$i]{noFill} ); | ||||
$cntIncrDel++; | $cntIncrDel++; | |||
$earliestIncr = (time - $Backups[$i]{startTime}) / (24 * 3600); | $earliestIncr = (time - $Backups[$i]{startTime}) / (24 * 3600); | |||
} | } | |||
} | } | |||
if ( $cntIncr >= $Conf{IncrKeepCnt} + $cntIncrDel | if ( | |||
|| ($cntIncr >= $Conf{IncrKeepCntMin} + $cntIncrDel | $cntIncr >= $Conf{IncrKeepCnt} + $cntIncrDel | |||
&& $earliestIncr > $Conf{IncrAgeMax}) ) { | || ( $cntIncr >= $Conf{IncrKeepCntMin} + $cntIncrDel | |||
&& $earliestIncr > $Conf{IncrAgeMax}) | ||||
) { | ||||
# | # | |||
# Only delete an incr backup if the Conf settings are satisfied | # Only delete an incr backup if the Conf settings are satisfied | |||
# for all $cntIncrDel incrementals. Since BackupRemove() updates | # for all $cntIncrDel incrementals. Since BackupRemove() updates | |||
# the @Backups array we need to do the deletes in the reverse order. | # the @Backups array we need to do the deletes in the reverse order. | |||
# | # | |||
for ( my $i = $firstIncr + $cntIncrDel - 1 ; | for ( my $i = $firstIncr + $cntIncrDel - 1 ; $i >= $firstIncr ; $i-- | |||
$i >= $firstIncr ; $i-- ) { | ) { | |||
print($LogFd $bpc->timeStamp, "Removing unfilled backup $Backups [$i]{num}\n"); | print($LogFd $bpc->timeStamp, "Removing unfilled backup $Backups [$i]{num}\n"); | |||
$XferLOG->write(\"Removing unfilled backup $Backups[$i]{num}\n") if ( $XferLOG ); | $XferLOG->write(\"Removing unfilled backup $Backups[$i]{num}\n") if ( $XferLOG ); | |||
last if ( BackupRemove($client, $i, 1) ); | last if ( BackupRemove($client, $i, 1) ); | |||
$changes++; | $changes++; | |||
} | } | |||
next; | next; | |||
} | } | |||
# | # | |||
# Delete any old full backups, according to $Conf{FullKeepCntMin} | # Delete any old full backups, according to $Conf{FullKeepCntMin} | |||
# and $Conf{FullAgeMax}. | # and $Conf{FullAgeMax}. | |||
# | # | |||
# First make sure that $Conf{FullAgeMax} is at least bigger | # First make sure that $Conf{FullAgeMax} is at least bigger | |||
# than $Conf{FullPeriod} * $Conf{FullKeepCnt}, including | # than $Conf{FullPeriod} * $Conf{FullKeepCnt}, including | |||
# the exponential array case. | # the exponential array case. | |||
# | # | |||
my $fullKeepCnt = $Conf{FullKeepCnt}; | my $fullKeepCnt = $Conf{FullKeepCnt}; | |||
$fullKeepCnt = [$fullKeepCnt] if ( ref($fullKeepCnt) ne "ARRAY" ); | $fullKeepCnt = [$fullKeepCnt] if ( ref($fullKeepCnt) ne "ARRAY" ); | |||
# | # | |||
# Always save one more than what the user configured to account for the most | # Always save one more than what the user configured to account for the most | |||
# recent backup which is always filled (whether incr or full); also copy the | # recent backup which is always filled (whether incr or full); also copy the | |||
# array so we don't update $Conf{FullKeepCnt}. | # array so we don't update $Conf{FullKeepCnt}. | |||
# | # | |||
$fullKeepCnt = [@$fullKeepCnt]; | $fullKeepCnt = [@$fullKeepCnt]; | |||
$fullKeepCnt->[0]++; | $fullKeepCnt->[0]++; | |||
my $fullAgeMax; | my $fullAgeMax; | |||
my $fullPeriod = int(0.5 + $Conf{FullPeriod}); | my $fullPeriod = int(0.5 + $Conf{FullPeriod}); | |||
$fullPeriod = 7 if ( $fullPeriod <= 0 ); | $fullPeriod = 7 if ( $fullPeriod <= 0 ); | |||
for ( my $i = 0 ; $i < @$fullKeepCnt ; $i++ ) { | for ( my $i = 0 ; $i < @$fullKeepCnt ; $i++ ) { | |||
$fullAgeMax += $fullKeepCnt->[$i] * $fullPeriod; | $fullAgeMax += $fullKeepCnt->[$i] * $fullPeriod; | |||
$fullPeriod *= 2; | $fullPeriod *= 2; | |||
} | } | |||
$fullAgeMax += $fullPeriod; # add some buffer | $fullAgeMax += $fullPeriod; # add some buffer | |||
if ( $cntFull > $Conf{FullKeepCntMin} + 1 | if ( $cntFull > $Conf{FullKeepCntMin} + 1 | |||
&& $oldestFull > $Conf{FullAgeMax} | && $oldestFull > $Conf{FullAgeMax} | |||
&& $oldestFull > $fullAgeMax | && $oldestFull > $fullAgeMax | |||
&& $Conf{FullKeepCntMin} >= 0 | && $Conf{FullKeepCntMin} >= 0 | |||
&& $Conf{FullAgeMax} > 0 ) { | && $Conf{FullAgeMax} > 0 ) { | |||
# | # | |||
# Only delete a full backup if the Conf settings are satisfied. | # Only delete a full backup if the Conf settings are satisfied. | |||
# | # | |||
# For pre-V4 we also must make sure that either this backup is the | # For pre-V4 we also must make sure that either this backup is the | |||
# most recent one, or the next backup is filled. | # most recent one, or the next backup is filled. | |||
# (In pre-V4 we can't deleted a full backup if the next backup is no t | # (In pre-V4 we can't deleted a full backup if the next backup is no t | |||
# filled.) | # filled.) | |||
# | # | |||
if ( !$Backups[$firstFull]{preV4} || (@Backups <= $firstFull + 1 | if ( | |||
|| !$Backups[$firstFull + 1]{noFill}) ) { | !$Backups[$firstFull]{preV4} | |||
|| (@Backups <= $firstFull + 1 | ||||
|| !$Backups[$firstFull + 1]{noFill}) | ||||
) { | ||||
print($LogFd $bpc->timeStamp, "Removing filled backup $Backups[$ firstFull]{num}\n"); | print($LogFd $bpc->timeStamp, "Removing filled backup $Backups[$ firstFull]{num}\n"); | |||
$XferLOG->write(\"Removing filled backup $Backups[$firstFull]{nu m}\n") if ( $XferLOG ); | $XferLOG->write(\"Removing filled backup $Backups[$firstFull]{nu m}\n") if ( $XferLOG ); | |||
last if ( BackupRemove($client, $firstFull, 1) ); | last if ( BackupRemove($client, $firstFull, 1) ); | |||
$changes++; | $changes++; | |||
next; | next; | |||
} | } | |||
} | } | |||
# | # | |||
# Do new-style full backup expiry, which includes the the case | # Do new-style full backup expiry, which includes the the case | |||
# where $Conf{FullKeepCnt} is an array. | # where $Conf{FullKeepCnt} is an array. | |||
# | # | |||
last if ( !BackupFullExpire($client, \@Backups) ); | last if ( !BackupFullExpire($client, \@Backups) ); | |||
$changes++; | $changes++; | |||
} | } | |||
$bpc->BackupInfoWrite($client, @Backups) if ( $changes ); | $bpc->BackupInfoWrite($client, @Backups) if ( $changes ); | |||
} | } | |||
# | # | |||
# Handle full backup expiry, using exponential periods. | # Handle full backup expiry, using exponential periods. | |||
# | # | |||
sub BackupFullExpire | sub BackupFullExpire | |||
{ | { | |||
my($client, $Backups) = @_; | my($client, $Backups) = @_; | |||
my $fullCnt = 0; | my $fullCnt = 0; | |||
my $fullPeriod = $Conf{FillCycle} <= 0 ? $Conf{FullPeriod} : $Conf{FillCycle }; | my $fullPeriod = $Conf{FillCycle} <= 0 ? $Conf{FullPeriod} : $Conf{FillCycle }; | |||
my $nextFull; | my $nextFull; | |||
my $fullKeepCnt = $Conf{FullKeepCnt}; | my $fullKeepCnt = $Conf{FullKeepCnt}; | |||
my $fullKeepIdx = 0; | my $fullKeepIdx = 0; | |||
my(@delete, @fullList); | my(@delete, @fullList); | |||
# | # | |||
# Don't delete anything if $Conf{FillCycle}, $Conf{FullPeriod} or $Conf{Full KeepCnt} | # Don't delete anything if $Conf{FillCycle}, $Conf{FullPeriod} or $Conf{Full KeepCnt} | |||
# are not defined - possibly a corrupted config.pl file. | # are not defined - possibly a corrupted config.pl file. | |||
# | # | |||
return if ( !defined($Conf{FillCycle}) || !defined($Conf{FullPeriod}) | return if ( !defined($Conf{FillCycle}) | |||
|| !defined($Conf{FullKeepCnt}) ); | || !defined($Conf{FullPeriod}) | |||
|| !defined($Conf{FullKeepCnt}) ); | ||||
# | # | |||
# Always save one more than what the user configured to account for the most | # Always save one more than what the user configured to account for the most | |||
# recent backup which is always filled (whether incr or full); also copy the | # recent backup which is always filled (whether incr or full); also copy the | |||
# array so we don't update $Conf{FullKeepCnt}. | # array so we don't update $Conf{FullKeepCnt}. | |||
# | # | |||
$fullKeepCnt = [$fullKeepCnt] if ( ref($fullKeepCnt) ne "ARRAY" ); | $fullKeepCnt = [$fullKeepCnt] if ( ref($fullKeepCnt) ne "ARRAY" ); | |||
$fullKeepCnt = [@$fullKeepCnt]; | $fullKeepCnt = [@$fullKeepCnt]; | |||
$fullKeepCnt->[0]++; | $fullKeepCnt->[0]++; | |||
# | # | |||
# If regular backups are still disabled with $Conf{FullPeriod} < 0, | # If regular backups are still disabled with $Conf{FullPeriod} < 0, | |||
# we still expire backups based on a safe FullPeriod value - daily. | # we still expire backups based on a safe FullPeriod value - daily. | |||
# | # | |||
$fullPeriod = 1 if ( $fullPeriod <= 0 ); | $fullPeriod = 1 if ( $fullPeriod <= 0 ); | |||
my $startTimeDeviation = $fullPeriod < 1 ? $fullPeriod / 2 : 0.5; | my $startTimeDeviation = $fullPeriod < 1 ? $fullPeriod / 2 : 0.5; | |||
my $keepPeriod = ($fullPeriod * ($fullKeepCnt->[0] - 1) - $startTimeDeviatio n) * 24 * 3600; | my $keepPeriod = ($fullPeriod * ($fullKeepCnt->[0] - 1) - $startTime Deviation) * 24 * 3600; | |||
for ( my $i = 0 ; $i < @$Backups ; $i++ ) { | for ( my $i = 0 ; $i < @$Backups ; $i++ ) { | |||
# | ||||
# don't consider any backups marked keep | ||||
# | ||||
next if ( $Backups[$i]{keep} ); | ||||
if ( $Backups[$i]{preV4} ) { | if ( $Backups[$i]{preV4} ) { | |||
next if ( $Backups->[$i]{type} ne "full" ); | next if ( $Backups->[$i]{type} ne "full" ); | |||
} else { | } else { | |||
next if ( $Backups->[$i]{noFill} ); | next if ( $Backups->[$i]{noFill} ); | |||
} | } | |||
push(@fullList, $i); | push(@fullList, $i); | |||
} | } | |||
for ( my $k = @fullList - 1 ; $k >= 0 ; $k-- ) { | for ( my $k = @fullList - 1 ; $k >= 0 ; $k-- ) { | |||
my $i = $fullList[$k]; | my $i = $fullList[$k]; | |||
my $prevFull = $fullList[$k-1] if ( $k > 0 ); | my $prevFull = $fullList[$k - 1] if ( $k > 0 ); | |||
# | # | |||
# For pre-V4 don't delete any full that is followed by an unfilled backu p, | # For pre-V4 don't delete any full that is followed by an unfilled backu p, | |||
# since it is needed for restore. | # since it is needed for restore. | |||
# | # | |||
my $noDelete = $i + 1 < @$Backups ? $Backups->[$i+1]{noFill} : 0; | my $noDelete = $i + 1 < @$Backups ? $Backups->[$i + 1]{noFill} : 0; | |||
$noDelete = 0 if ( !$Backups[$i]{preV4} ); | $noDelete = 0 if ( !$Backups[$i]{preV4} ); | |||
if ( !$noDelete && | if ( | |||
($fullKeepIdx >= @$fullKeepCnt | !$noDelete | |||
|| $k > 0 | && ( $fullKeepIdx >= @$fullKeepCnt | |||
&& $fullKeepIdx > 0 | || $k > 0 | |||
&& defined($nextFull) | && $fullKeepIdx > 0 | |||
&& $Backups->[$nextFull]{startTime} - $Backups->[$prevFull]{sta | && defined($nextFull) | |||
rtTime} | && $Backups->[$nextFull]{startTime} - $Backups->[$prevFull]{star | |||
< ($fullPeriod + $startTimeDeviation) * 24 * 3600 | tTime} < | |||
) | ($fullPeriod + $startTimeDeviation) * 24 * 3600) | |||
) { | ) { | |||
# | # | |||
# Delete the full backup | # Delete the full backup | |||
# | # | |||
#print("Deleting backup $Backups->[$i]{num} (i = $i, k = $k, nextFul l = $nextFull, prevFull = $prevFull, fullKeepIdx = $fullKeepIdx, fullCnt = $full Cnt)\n"); | #print("Deleting backup $Backups->[$i]{num} (i = $i, k = $k, nextFul l = $nextFull, prevFull = $prevFull, fullKeepIdx = $fullKeepIdx, fullCnt = $full Cnt)\n"); | |||
unshift(@delete, $i); | unshift(@delete, $i); | |||
} else { | } else { | |||
#printf("Keeping backup $Backups->[$i]{num} (i = $i, k = $k, nextFul l = $nextFull, prevFull = $prevFull, fullKeepIdx = $fullKeepIdx, fullCnt = $full Cnt, keepPeriod = %.3g, delta = %.3g)\n", $keepPeriod / (24 * 3600), (time - $Ba ckups->[$i]{startTime}) / (24 * 3600)); | #printf("Keeping backup $Backups->[$i]{num} (i = $i, k = $k, nextFul l = $nextFull, prevFull = $prevFull, fullKeepIdx = $fullKeepIdx, fullCnt = $full Cnt, keepPeriod = %.3g, delta = %.3g)\n", $keepPeriod / (24 * 3600), (time - $Ba ckups->[$i]{startTime}) / (24 * 3600)); | |||
$fullCnt++; | $fullCnt++; | |||
$nextFull = $i; | $nextFull = $i; | |||
while ( $fullKeepIdx < @$fullKeepCnt | while ( $fullKeepIdx < @$fullKeepCnt | |||
&& $k > 0 | && $k > 0 | |||
&& time - $Backups->[$prevFull]{startTime} > $keepPeriod | && time - $Backups->[$prevFull]{startTime} > $keepPeriod | |||
&& $fullCnt >= $fullKeepCnt->[$fullKeepIdx] ) { | && $fullCnt >= $fullKeepCnt->[$fullKeepIdx] ) { | |||
$fullKeepIdx++; | $fullKeepIdx++; | |||
$fullCnt = 0; | $fullCnt = 0; | |||
$fullPeriod = 2 * $fullPeriod; | $fullPeriod = 2 * $fullPeriod; | |||
} | } | |||
#print(" (now nextFull = $nextFull, prevFull = $prevFull, fullKee pIdx = $fullKeepIdx, fullCnt = $fullCnt, fullPeriod = $fullPeriod, fullKeepCnt[i dx] = $fullKeepCnt->[$fullKeepIdx])\n"); | #print(" (now nextFull = $nextFull, prevFull = $prevFull, fullKee pIdx = $fullKeepIdx, fullCnt = $fullCnt, fullPeriod = $fullPeriod, fullKeepCnt[i dx] = $fullKeepCnt->[$fullKeepIdx])\n"); | |||
} | } | |||
} | } | |||
# | # | |||
# Now actually delete the backups | # Now actually delete the backups | |||
# | # | |||
for ( my $i = @delete - 1 ; $i >= 0 ; $i-- ) { | for ( my $i = @delete - 1 ; $i >= 0 ; $i-- ) { | |||
print($LogFd $bpc->timeStamp, "Removing filled backup $Backups->[$delete [$i]]{num}\n"); | print($LogFd $bpc->timeStamp, "Removing filled backup $Backups->[$delete [$i]]{num}\n"); | |||
skipping to change at line 1924 | skipping to change at line 1955 | |||
sub BackupSave | sub BackupSave | |||
{ | { | |||
my($noWrite) = @_; | my($noWrite) = @_; | |||
# | # | |||
# Update the new backup information to the backup file. | # Update the new backup information to the backup file. | |||
# A new entry in @Backups was created at the start of the backup, | # A new entry in @Backups was created at the start of the backup, | |||
# so we update the last entry of @Backups. | # so we update the last entry of @Backups. | |||
# | # | |||
@Backups = $bpc->BackupInfoRead($client); | @Backups = $bpc->BackupInfoRead($client); | |||
my $i = @Backups - 1; | my $i = @Backups - 1; | |||
$i = 0 if ( $i < 0 ); | $i = 0 if ( $i < 0 ); | |||
$Backups[$i]{num} = $newBkupNum if ( !defined($Backups[$i]{num}) ); | $Backups[$i]{num} = $newBkupNum if ( !defined($Backups[$i]{num}) ); | |||
my $num = $Backups[$i]{num}; | my $num = $Backups[$i]{num}; | |||
$Backups[$i]{type} = $type; | $Backups[$i]{type} = $type; | |||
$Backups[$i]{level} = $type eq "incr" ? 1 : 0; | $Backups[$i]{level} = $type eq "incr" ? 1 : 0; | |||
$Backups[$i]{startTime} = $startTime; | $Backups[$i]{startTime} = $startTime; | |||
$Backups[$i]{endTime} = $endTime; | $Backups[$i]{endTime} = $endTime; | |||
$Backups[$i]{size} = $sizeTotal; | $Backups[$i]{size} = $sizeTotal; | |||
$Backups[$i]{nFiles} = $nFilesTotal; | $Backups[$i]{nFiles} = $nFilesTotal; | |||
$Backups[$i]{xferErrs} = $stat{xferErrCnt} || 0; | $Backups[$i]{xferErrs} = $stat{xferErrCnt} || 0; | |||
$Backups[$i]{xferBadFile} = $stat{xferBadFileCnt} || 0; | $Backups[$i]{xferBadFile} = $stat{xferBadFileCnt} || 0; | |||
$Backups[$i]{xferBadShare} = $stat{xferBadShareCnt} || 0; | $Backups[$i]{xferBadShare} = $stat{xferBadShareCnt} || 0; | |||
$Backups[$i]{nFilesExist} = $nFilesExist; | $Backups[$i]{nFilesExist} = $nFilesExist; | |||
$Backups[$i]{sizeExist} = $sizeExist; | $Backups[$i]{sizeExist} = $sizeExist; | |||
$Backups[$i]{sizeExistComp} = $sizeExistComp; | $Backups[$i]{sizeExistComp} = $sizeExistComp; | |||
$Backups[$i]{nFilesNew} = $nFilesNew; | $Backups[$i]{nFilesNew} = $nFilesNew; | |||
$Backups[$i]{sizeNew} = $sizeNew; | $Backups[$i]{sizeNew} = $sizeNew; | |||
$Backups[$i]{sizeNewComp} = $sizeNewComp; | $Backups[$i]{sizeNewComp} = $sizeNewComp; | |||
$Backups[$i]{tarErrs} = $tarErrs; | $Backups[$i]{tarErrs} = $tarErrs; | |||
$Backups[$i]{compress} = $Conf{CompressLevel}; | $Backups[$i]{compress} = $Conf{CompressLevel}; | |||
$Backups[$i]{noFill} = 0; | $Backups[$i]{noFill} = 0; | |||
$Backups[$i]{mangle} = 1; # name mangling always on for v1.04+ | $Backups[$i]{mangle} = 1; # name mangling always on for v1.04+ | |||
$Backups[$i]{xferMethod} = $Conf{XferMethod}; | $Backups[$i]{xferMethod} = $Conf{XferMethod}; | |||
$Backups[$i]{charset} = $Conf{ClientCharset}; | $Backups[$i]{charset} = $Conf{ClientCharset}; | |||
$Backups[$i]{version} = $bpc->Version(); | $Backups[$i]{version} = $bpc->Version(); | |||
$Backups[$i]{inodeLast} = $inodeLast; | $Backups[$i]{inodeLast} = $inodeLast; | |||
$Backups[$i]{keep} = 0; | ||||
$Backups[$i]{share2path} = $Conf{ClientShareName2Path}; | ||||
$Backups[$i]{comment} = ""; | ||||
return if ( $noWrite ); | return if ( $noWrite ); | |||
# | # | |||
# Save the main backups file | # Save the main backups file | |||
# | # | |||
$bpc->BackupInfoWrite($client, @Backups); | $bpc->BackupInfoWrite($client, @Backups); | |||
# | # | |||
# Save just this backup's info in case the main backups file | # Save just this backup's info in case the main backups file | |||
# gets corrupted | # gets corrupted | |||
# | # | |||
BackupPC::Storage->backupInfoWrite($Dir, $Backups[$i]{num}, | BackupPC::Storage->backupInfoWrite($Dir, $Backups[$i]{num}, $Backups[$i], 1) | |||
$Backups[$i], 1); | ; | |||
unlink("$Dir/timeStamp.level0") if ( -f "$Dir/timeStamp.level0" ); | unlink("$Dir/timeStamp.level0") if ( -f "$Dir/timeStamp.level0" ); | |||
foreach my $ext ( qw(bad bad.z) ) { | foreach my $ext ( qw(bad bad.z) ) { | |||
next if ( !-f "$Dir/XferLOG.$ext" ); | next if ( !-f "$Dir/XferLOG.$ext" ); | |||
unlink("$Dir/XferLOG.$ext.old") if ( -f "$Dir/XferLOG.$ext" ); | unlink("$Dir/XferLOG.$ext.old") if ( -f "$Dir/XferLOG.$ext" ); | |||
rename("$Dir/XferLOG.$ext", "$Dir/XferLOG.$ext.old"); | rename("$Dir/XferLOG.$ext", "$Dir/XferLOG.$ext.old"); | |||
} | } | |||
return $num; | return $num; | |||
} | } | |||
# | # | |||
# Removes a specific backup, or a sharename within a backup | # Removes a specific backup, or a sharename within a backup | |||
# | # | |||
sub BackupRemove | sub BackupRemove | |||
{ | { | |||
my($client, $idx, $removeXferLOG, $shareName) = @_; | my($client, $idx, $removeXferLOG, $shareName) = @_; | |||
my $t = time; | my $t = time; | |||
my $pids = {}; | my $pids = {}; | |||
my $fileExt = $Backups[$idx]{compress} > 0 ? ".z" : ""; | my $fileExt = $Backups[$idx]{compress} > 0 ? ".z" : ""; | |||
my $bkupNum = $Backups[$idx]{num}; | my $bkupNum = $Backups[$idx]{num}; | |||
my @args = ("-h", $client, "-n", $Backups[$idx]{num}, "-l", "-m"); | my @args = ("-h", $client, "-n", $Backups[$idx]{num}, "-l", "-m"); | |||
if ( $Backups[$idx]{keep} ) { | ||||
# | ||||
# this shouldn't happen, but report an error if it does | ||||
# | ||||
$XferLOG->write(\"BackupRemove(num = $bkupNum) -> skipping because keep | ||||
is set\n") | ||||
if ( defined($XferLOG) ); | ||||
return 1; | ||||
} | ||||
if ( defined($shareName) ) { | if ( defined($shareName) ) { | |||
push(@args, "-s", $shareName, "/"); | push(@args, "-s", $shareName, "/"); | |||
print("__bpc_progress_state__ delete share #$bkupNum/$shareName\n") if ( !$opts{p} ); | print("__bpc_progress_state__ delete share #$bkupNum/$shareName\n") if ( !$opts{p} ); | |||
} else { | } else { | |||
print("__bpc_progress_state__ delete #$bkupNum\n") if ( !$opts{p} ); | print("__bpc_progress_state__ delete #$bkupNum\n") if ( !$opts{p} ); | |||
} | } | |||
push(@args, "-p") if ( $opts{p} ); | push(@args, "-p") if ( $opts{p} ); | |||
unlink("$Dir/XferLOG.$bkupNum$fileExt") if ( !defined($shareName) && $remove XferLOG ); | unlink("$Dir/XferLOG.$bkupNum$fileExt") if ( !defined($shareName) && $remove XferLOG ); | |||
skipping to change at line 2011 | skipping to change at line 2053 | |||
} elsif ( $_[0] =~ /^__bpc_pidStart__ (\d+)/ ) { | } elsif ( $_[0] =~ /^__bpc_pidStart__ (\d+)/ ) { | |||
$pids->{$1} = 1; | $pids->{$1} = 1; | |||
pidHandler(keys(%$pids)); | pidHandler(keys(%$pids)); | |||
} elsif ( $_[0] =~ /^__bpc_pidEnd__ (\d+)/ ) { | } elsif ( $_[0] =~ /^__bpc_pidEnd__ (\d+)/ ) { | |||
delete($pids->{$1}); | delete($pids->{$1}); | |||
pidHandler(keys(%$pids)); | pidHandler(keys(%$pids)); | |||
} else { | } else { | |||
print($LogFd $bpc->timeStamp, $_[0]); | print($LogFd $bpc->timeStamp, $_[0]); | |||
$XferLOG->write(\$_[0]) if ( defined($XferLOG) ); | $XferLOG->write(\$_[0]) if ( defined($XferLOG) ); | |||
} | } | |||
}); | } | |||
); | ||||
my $ret = $?; | my $ret = $?; | |||
$t = time - $t; | $t = time - $t; | |||
print($LogFd $bpc->timeStamp, "Finished BackupPC_backupDelete, status = $ret (running time: $t sec)\n"); | print($LogFd $bpc->timeStamp, "Finished BackupPC_backupDelete, status = $ret (running time: $t sec)\n"); | |||
$XferLOG->write(\"Finished BackupPC_backupDelete, status = $ret (running tim e: $t sec)\n") | $XferLOG->write(\"Finished BackupPC_backupDelete, status = $ret (running tim e: $t sec)\n") | |||
if ( defined($XferLOG) ); | if ( defined($XferLOG) ); | |||
pidHandler(); | pidHandler(); | |||
if ( !defined($shareName) ) { | if ( !defined($shareName) ) { | |||
splice(@Backups, $idx, 1); | splice(@Backups, $idx, 1); | |||
} | } | |||
return $ret; | return $ret; | |||
} | } | |||
sub CorrectHostCheck | sub CorrectHostCheck | |||
{ | { | |||
my($hostIP, $host) = @_; | my($hostIP, $host) = @_; | |||
return if ( $hostIP eq $host || !$Conf{FixedIPNetBiosNameCheck} | return if ( $hostIP eq $host || !$Conf{FixedIPNetBiosNameCheck} || $Conf{Nmb | |||
|| $Conf{NmbLookupCmd} eq "" ); | LookupCmd} eq "" ); | |||
if (ref($Conf{ClientNameAlias}) eq "ARRAY") { | if ( ref($Conf{ClientNameAlias}) eq "ARRAY" ) { | |||
return if ( grep /^$hostIP$/, @{ $Conf{ClientNameAlias} } ); | return if ( grep /^$hostIP$/, @{$Conf{ClientNameAlias}} ); | |||
} | } | |||
my($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($hostIP); | my($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($hostIP); | |||
return "host $host has mismatching netbios name $netBiosHost" | return "host $host has mismatching netbios name $netBiosHost" | |||
if ( lc($netBiosHost) ne lc(substr($host, 0, 15)) ); | if ( lc($netBiosHost) ne lc(substr($host, 0, 15)) ); | |||
return; | return; | |||
} | } | |||
# | # | |||
# Returns $host if $bpc->getHostAddrInfo() knows about it. | # Returns $host if $bpc->getHostAddrInfo() knows about it. | |||
# Otherwise tries to find the hostIP via NetBios, and returns | # Otherwise tries to find the hostIP via NetBios, and returns | |||
# the hostIP if successful. | # the hostIP if successful. | |||
# | # | |||
# Returns undef if both $bpc->getHostAddrInfo() and NetBios fail | # Returns undef if both $bpc->getHostAddrInfo() and NetBios fail | |||
# | # | |||
skipping to change at line 2106 | skipping to change at line 2148 | |||
# | # | |||
# With $doFsck = 1 and $doCheck = 0, the pool count files are rebuilt, | # With $doFsck = 1 and $doCheck = 0, the pool count files are rebuilt, | |||
# without checking the old one. | # without checking the old one. | |||
# | # | |||
# With $doFsck = 1 and $doCheck = 1, the pool count files are rebuilt, | # With $doFsck = 1 and $doCheck = 1, the pool count files are rebuilt, | |||
# and differences to the current ones are listed. | # and differences to the current ones are listed. | |||
# | # | |||
sub RefCountUpdate | sub RefCountUpdate | |||
{ | { | |||
my($doFsck, $doCheck) = @_; | my($doFsck, $doCheck) = @_; | |||
my $t = time; | my $t = time; | |||
my $pids = {}; | my $pids = {}; | |||
my $args = ["-h", $client]; | my $args = ["-h", $client]; | |||
push(@$args, "-f") if ( $doFsck ); | push(@$args, "-f") if ( $doFsck ); | |||
push(@$args, "-c") if ( $doCheck ); | push(@$args, "-c") if ( $doCheck ); | |||
push(@$args, "-p") if ( $opts{p} ); | push(@$args, "-p") if ( $opts{p} ); | |||
print("__bpc_progress_state__ fsck\n") if ( !$opts{p} ); | print("__bpc_progress_state__ fsck\n") if ( !$opts{p} ); | |||
$XferLOG->write(\"Running BackupPC_refCountUpdate @$args on $client\n"); | $XferLOG->write(\"Running BackupPC_refCountUpdate @$args on $client\n"); | |||
$bpc->cmdSystemOrEval(["$BinDir/BackupPC_refCountUpdate", @$args], | $bpc->cmdSystemOrEval( | |||
sub { | ["$BinDir/BackupPC_refCountUpdate", @$args], | |||
if ( $_[0] =~ /^__bpc_progress_/ ) { | sub { | |||
print($_[0]); | if ( $_[0] =~ /^__bpc_progress_/ ) { | |||
} elsif ( $_[0] =~ /^__bpc_pidStart__ (\d+)/ ) { | print($_[0]); | |||
$pids->{$1} = 1; | } elsif ( $_[0] =~ /^__bpc_pidStart__ (\d+)/ ) { | |||
pidHandler(keys(%$pids)); | $pids->{$1} = 1; | |||
} elsif ( $_[0] =~ /^__bpc_pidEnd__ (\d+)/ ) { | pidHandler(keys(%$pids)); | |||
delete($pids->{$1}); | } elsif ( $_[0] =~ /^__bpc_pidEnd__ (\d+)/ ) { | |||
pidHandler(keys(%$pids)); | delete($pids->{$1}); | |||
} else { | pidHandler(keys(%$pids)); | |||
$XferLOG->write(\$_[0]); | } else { | |||
} | $XferLOG->write(\$_[0]); | |||
}); | } | |||
} | ||||
); | ||||
$t = time - $t; | $t = time - $t; | |||
$XferLOG->write(\"Finished BackupPC_refCountUpdate (running time: $t sec)\n" ); | $XferLOG->write(\"Finished BackupPC_refCountUpdate (running time: $t sec)\n" ); | |||
pidHandler(); | pidHandler(); | |||
} | } | |||
# | # | |||
# The Xfer method might tell us from time to time about processes | # The Xfer method might tell us from time to time about processes | |||
# it forks. We tell BackupPC about this (for status displays) and | # it forks. We tell BackupPC about this (for status displays) and | |||
# keep track of the pids in case we cancel the backup | # keep track of the pids in case we cancel the backup | |||
# | # | |||
skipping to change at line 2165 | skipping to change at line 2209 | |||
{ | { | |||
my($cmdType, $sharename) = @_; | my($cmdType, $sharename) = @_; | |||
$? = 0; | $? = 0; | |||
return if ( !defined($Conf{$cmdType}) ); | return if ( !defined($Conf{$cmdType}) ); | |||
my $vars = { | my $vars = { | |||
xfer => $xfer, | xfer => $xfer, | |||
client => $client, | client => $client, | |||
host => $host, | host => $host, | |||
hostIP => $hostIP, | hostIP => $hostIP, | |||
user => $Hosts->{$client}{user}, | user => $Hosts->{$client}{user}, | |||
moreUsers => $Hosts->{$client}{moreUsers}, | moreUsers => $Hosts->{$client}{moreUsers}, | |||
share => $ShareNames->[0], | share => $ShareNames->[0], | |||
shares => $ShareNames, | shares => $ShareNames, | |||
XferMethod => $Conf{XferMethod}, | XferMethod => $Conf{XferMethod}, | |||
sshPath => $Conf{SshPath}, | sshPath => $Conf{SshPath}, | |||
LOG => $LogFd, | LOG => $LogFd, | |||
XferLOG => $XferLOG, | XferLOG => $XferLOG, | |||
stat => \%stat, | stat => \%stat, | |||
xferOK => $stat{xferOK} || 0, | xferOK => $stat{xferOK} || 0, | |||
hostError => $stat{hostError}, | hostError => $stat{hostError}, | |||
type => $type, | type => $type, | |||
cmdType => $cmdType, | cmdType => $cmdType, | |||
}; | }; | |||
if ($cmdType eq 'DumpPreShareCmd' || $cmdType eq 'DumpPostShareCmd') { | if ( $cmdType eq 'DumpPreShareCmd' || $cmdType eq 'DumpPostShareCmd' ) { | |||
$vars->{share} = $sharename; | $vars->{share} = $sharename; | |||
if ( $cmdType =~ /Post/ ) { | if ( $cmdType =~ /Post/ ) { | |||
print("__bpc_progress_state__ post-cmd $sharename\n") if ( !$opts{p} ); | print("__bpc_progress_state__ post-cmd $sharename\n") if ( !$opts{p} ); | |||
} else { | } else { | |||
print("__bpc_progress_state__ pre-cmd $sharename\n") if ( !$opts{p} ); | print("__bpc_progress_state__ pre-cmd $sharename\n") if ( !$opts{p} ); | |||
} | } | |||
} else { | } else { | |||
if ( $cmdType =~ /Post/ ) { | if ( $cmdType =~ /Post/ ) { | |||
print("__bpc_progress_state__ post-cmd\n") if ( !$opts{p} ); | print("__bpc_progress_state__ post-cmd\n") if ( !$opts{p} ); | |||
} else { | } else { | |||
print("__bpc_progress_state__ pre-cmd\n") if ( !$opts{p} ); | print("__bpc_progress_state__ pre-cmd\n") if ( !$opts{p} ); | |||
} | } | |||
} | } | |||
my $cmd = $bpc->cmdVarSubstitute($Conf{$cmdType}, $vars); | my $cmd = $bpc->cmdVarSubstitute($Conf{$cmdType}, $vars); | |||
$XferLOG->write(\"Executing $cmdType: @$cmd\n"); | if ( $XferLOG ) { | |||
$XferLOG->write(\"Executing $cmdType: @$cmd\n"); | ||||
} elsif ( $opts{v} ) { | ||||
print("Executing $cmdType: @$cmd\n"); | ||||
} | ||||
# | # | |||
# Run the user's command, dumping the stdout/stderr into the | # Run the user's command, dumping the stdout/stderr into the | |||
# Xfer log file. Also supply the optional $vars and %Conf in | # Xfer log file. Also supply the optional $vars and %Conf in | |||
# case the command is really perl code instead of a shell | # case the command is really perl code instead of a shell | |||
# command. | # command. | |||
# | # | |||
$bpc->cmdSystemOrEval($cmd, | $bpc->cmdSystemOrEval( | |||
sub { | $cmd, | |||
if ( $XferLOG && length($_[0]) ) { | sub { | |||
$XferLOG->write(\$_[0]); | if ( $XferLOG && length($_[0]) ) { | |||
} elsif ( $LogFd && length($_[0]) ) { | $XferLOG->write(\$_[0]); | |||
print($LogFd $bpc->timeStamp, "Output from $cmdType: ", $_[0 | } elsif ( $LogFd && length($_[0]) ) { | |||
]); | print($LogFd $bpc->timeStamp, "Output from $cmdType: ", $_[0]); | |||
} | } | |||
}, | }, | |||
$vars, \%Conf | $vars, | |||
); | \%Conf | |||
); | ||||
} | } | |||
sub flushLibMessages() | sub flushLibMessages() | |||
{ | { | |||
my $msg = BackupPC::XS::Lib::logMsgGet(); | my $msg = BackupPC::XS::Lib::logMsgGet(); | |||
return if ( !defined($msg) ); | return if ( !defined($msg) ); | |||
if ( $XferLOG ) { | if ( $XferLOG ) { | |||
foreach my $m ( @$msg ) { | foreach my $m ( @$msg ) { | |||
$XferLOG->write(\$m); | $XferLOG->write(\$m); | |||
} | } | |||
End of changes. 144 change blocks. | ||||
513 lines changed or deleted | 585 lines changed or added |