From d6dd48e6594d937dea49e3dd1c5ddaba6719df0d Mon Sep 17 00:00:00 2001 From: cbarratt Date: Mon, 15 Mar 2004 03:12:42 +0000 Subject: [PATCH] * Failed dumps now cleanup correctly, deleting in-progress file and correctly saving attribs. * Updates to de.pm from Manfred. * Minor updates to other lang files. * Support for multiple blackouts added. * Moved lib/BackupPC/RsyncDigest.pm to lib/BackupPC/Xfer/RsyncDigest.pm * Patches to configure.pl and makeDist from gfk. --- bin/BackupPC | 62 ++++-- bin/BackupPC_archive | 4 +- bin/BackupPC_dump | 272 ++++++++++++++++++++----- bin/BackupPC_link | 2 +- bin/BackupPC_restore | 9 +- bin/BackupPC_tarExtract | 140 +++++++++++-- conf/config.pl | 16 +- configure.pl | 8 +- lib/BackupPC/CGI/HostInfo.pm | 52 ++++- lib/BackupPC/Lang/de.pm | 210 ++++++++++--------- lib/BackupPC/Lang/en.pm | 4 +- lib/BackupPC/Lang/es.pm | 4 +- lib/BackupPC/Lang/fr.pm | 4 +- lib/BackupPC/Lang/it.pm | 158 +++++++------- lib/BackupPC/Lib.pm | 23 ++- lib/BackupPC/PoolWrite.pm | 79 ++++--- lib/BackupPC/Xfer/Archive.pm | 4 + lib/BackupPC/Xfer/Rsync.pm | 38 +++- lib/BackupPC/{ => Xfer}/RsyncDigest.pm | 13 +- lib/BackupPC/Xfer/RsyncFileIO.pm | 64 +++--- lib/BackupPC/Xfer/Smb.pm | 30 ++- lib/BackupPC/Xfer/Tar.pm | 29 ++- makeDist | 6 +- 23 files changed, 853 insertions(+), 378 deletions(-) rename lib/BackupPC/{ => Xfer}/RsyncDigest.pm (96%) diff --git a/bin/BackupPC b/bin/BackupPC index 61276e2..ba6cde9 100755 --- a/bin/BackupPC +++ b/bin/BackupPC @@ -47,7 +47,7 @@ # #======================================================================== # -# Version 2.1.0_CVS, released 8 Feb 2004. +# Version 2.1.0_CVS, released 13 Mar 2004. # # See http://backuppc.sourceforge.net. # @@ -772,7 +772,7 @@ sub Main_Check_Job_Messages if ( defined($Jobs{$newHost}) ) { print(LOG $bpc->timeStamp, "Backup on $newHost is already running\n"); - kill(2, $Jobs{$host}{pid}); + kill($bpc->sigName2num("INT"), $Jobs{$host}{pid}); $nbytes = 0; last; } @@ -835,6 +835,13 @@ sub Main_Check_Job_Messages delete($Status{$host}{error}); delete($Status{$host}{errorTime}); $Status{$host}{endTime} = time; + } elsif ( $mesg =~ /^backups disabled/ ) { + print(LOG $bpc->timeStamp, + "Ignoring old backup error on $host\n"); + $Status{$host}{reason} = "Reason_backup_done"; + delete($Status{$host}{error}); + delete($Status{$host}{errorTime}); + $Status{$host}{endTime} = time; } elsif ( $mesg =~ /^restore complete/ ) { print(LOG $bpc->timeStamp, "Finished restore on $host\n"); $Status{$host}{reason} = "Reason_restore_done"; @@ -873,25 +880,46 @@ sub Main_Check_Job_Messages } } elsif ( $mesg =~ /^dump failed: (.*)/ ) { $Status{$host}{state} = "Status_idle"; - $Status{$host}{reason} = "Reason_backup_failed"; - $Status{$host}{error} = $1; - $Status{$host}{errorTime} = time; - $Status{$host}{endTime} = time; - print(LOG $bpc->timeStamp, "Backup failed on $host ($1)\n"); + $Status{$host}{error} = $1; + $Status{$host}{errorTime} = time; + $Status{$host}{endTime} = time; + if ( $Status{$host}{reason} + eq "Reason_backup_canceled_by_user" ) { + print(LOG $bpc->timeStamp, + "Backup canceled on $host ($1)\n"); + } else { + $Status{$host}{reason} = "Reason_backup_failed"; + print(LOG $bpc->timeStamp, + "Backup failed on $host ($1)\n"); + } } elsif ( $mesg =~ /^restore failed: (.*)/ ) { $Status{$host}{state} = "Status_idle"; - $Status{$host}{reason} = "Reason_restore_failed"; $Status{$host}{error} = $1; $Status{$host}{errorTime} = time; $Status{$host}{endTime} = time; - print(LOG $bpc->timeStamp, "Restore failed on $host ($1)\n"); + if ( $Status{$host}{reason} + eq "Reason_restore_canceled_by_user" ) { + print(LOG $bpc->timeStamp, + "Restore canceled on $host ($1)\n"); + } else { + $Status{$host}{reason} = "Reason_restore_failed"; + print(LOG $bpc->timeStamp, + "Restore failed on $host ($1)\n"); + } } elsif ( $mesg =~ /^archive failed: (.*)/ ) { $Status{$host}{state} = "Status_idle"; - $Status{$host}{reason} = "Reason_archive_failed"; $Status{$host}{error} = $1; $Status{$host}{errorTime} = time; $Status{$host}{endTime} = time; - print(LOG $bpc->timeStamp, "Archive failed on $host ($1)\n"); + if ( $Status{$host}{reason} + eq "Reason_archive_canceled_by_user" ) { + print(LOG $bpc->timeStamp, + "Archive canceled on $host ($1)\n"); + } else { + $Status{$host}{reason} = "Reason_archive_failed"; + print(LOG $bpc->timeStamp, + "Archive failed on $host ($1)\n"); + } } elsif ( $mesg =~ /^log\s+(.*)/ ) { print(LOG $bpc->timeStamp, "$1\n"); } elsif ( $mesg =~ /^BackupPC_stats = (.*)/ ) { @@ -1043,7 +1071,7 @@ sub Main_Check_Client_Messages print(LOG $bpc->timeStamp, "Stopping current $Jobs{$host}{type} of $host," . " request by $user (backoff=$backoff)\n"); - kill(2, $Jobs{$host}{pid}); + kill($bpc->sigName2num("INT"), $Jobs{$host}{pid}); # # Don't close the pipe now; wait until the child # really exits later. Otherwise close() will @@ -1066,7 +1094,7 @@ sub Main_Check_Client_Messages } $Status{$host}{activeJob} = 0; $Status{$host}{startTime} = time; - $reply = "ok: $Jobs{$host}{type} of $host cancelled"; + $reply = "ok: $Jobs{$host}{type} of $host canceled"; } elsif ( $BgQueueOn{$host} || $UserQueueOn{$host} ) { print(LOG $bpc->timeStamp, "Stopping pending backup of $host," @@ -1074,7 +1102,7 @@ sub Main_Check_Client_Messages @BgQueue = grep($_->{host} ne $host, @BgQueue); @UserQueue = grep($_->{host} ne $host, @UserQueue); $BgQueueOn{$host} = $UserQueueOn{$host} = 0; - $reply = "ok: pending backup of $host cancelled"; + $reply = "ok: pending backup of $host canceled"; } else { print(LOG $bpc->timeStamp, "Nothing to do for stop backup of $host," @@ -1484,7 +1512,7 @@ sub catch_signal if ( $SigName ) { $SigName = shift; foreach my $host ( keys(%Jobs) ) { - kill(2, $Jobs{$host}{pid}); + kill($bpc->sigName2num("INT"), $Jobs{$host}{pid}); } # # In case we are inside the exit handler, reopen the log file @@ -1604,11 +1632,11 @@ sub ServerShutdown print(LOG $bpc->timeStamp, "$mesg\n"); if ( keys(%Jobs) ) { foreach my $host ( keys(%Jobs) ) { - kill(2, $Jobs{$host}{pid}); + kill($bpc->sigName2num("INT"), $Jobs{$host}{pid}); } sleep(1); foreach my $host ( keys(%Jobs) ) { - kill(9, $Jobs{$host}{pid}); + kill($bpc->sigName2num("KILL"), $Jobs{$host}{pid}); } %Jobs = (); } diff --git a/bin/BackupPC_archive b/bin/BackupPC_archive index c61e5cf..ebdc103 100644 --- a/bin/BackupPC_archive +++ b/bin/BackupPC_archive @@ -244,9 +244,9 @@ sub ArchiveCleanup # kill off the tranfer program, first nicely then forcefully # if ( @xferPid ) { - kill(2, @xferPid); + kill($bpc->sigName2num("INT"), @xferPid); sleep(1); - kill(9, @xferPid); + kill($bpc->sigName2num("KILL"), @xferPid); } } diff --git a/bin/BackupPC_dump b/bin/BackupPC_dump index dbc2ee0..733ceae 100755 --- a/bin/BackupPC_dump +++ b/bin/BackupPC_dump @@ -70,7 +70,7 @@ # #======================================================================== # -# Version 2.1.0_CVS, released 8 Feb 2004. +# Version 2.1.0_CVS, released 13 Mar 2004. # # See http://backuppc.sourceforge.net. # @@ -86,6 +86,7 @@ use BackupPC::Xfer::Tar; use BackupPC::Xfer::Rsync; use Socket; use File::Path; +use File::Find; use Getopt::Std; ########################################################################### @@ -98,6 +99,8 @@ my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); my $NeedPostCmd; my $Hosts; +my $SigName; +my $Abort; $bpc->ChildInit(); @@ -203,6 +206,8 @@ if ( $opts{e} ) { # For archive hosts we don't bother any further # if ($Conf{XferMethod} eq "archive" ) { + print(STDERR "Exiting because the XferMethod is set to archive\n") + if ( $opts{v} ); exit(0); } @@ -274,6 +279,15 @@ my $partialNum; if ( $Conf{FullPeriod} == -1 && !$opts{f} && !$opts{i} || $Conf{FullPeriod} == -2 ) { + print(STDERR "Exiting because backups are disabled with" + . "\$Conf{FullPeriod} = $Conf{FullPeriod}\n") if ( $opts{v} ); + # + # Tell BackupPC to ignore old failed backups on hosts that + # have backups disabled. + # + print("backups disabled\n") + if ( $StatusHost{reason} ne "Reason_backup_done" + && time - $StatusHost{startTime} > 2 * 24 * 3600 ); NothingToDo($needLink); } @@ -301,6 +315,10 @@ if ( !$opts{i} && !$opts{f} && $Conf{BlackoutGoodCnt} >= 0 # Allow blackout to span midnight (specified by BlackoutHourBegin # being greater than BlackoutHourEnd) # + next if ( ref($p->{weekDays}) ne "ARRAY" + || !defined($p->{hourBegin}) + || !defined($p->{hourEnd}) + ); if ( $p->{hourBegin} > $p->{hourEnd} ) { $blackout = $p->{hourBegin} <= $currHours || $currHours <= $p->{hourEnd}; @@ -423,10 +441,41 @@ if ( !defined($XferLOG) ) { print("dump failed: unable to open/create $Dir/XferLOG$fileExt\n"); exit(1); } + +# +# Ignore the partial dump in the case of an incremental. +# A partial is a partial full. +# +if ( $type ne "full" ) { + $partialNum = undef; + $partialIdx = -1; +} + +# +# If this is a partial, copy the old XferLOG file +# +if ( $partialNum ) { + my($compress, $fileName); + if ( -f "$Dir/XferLOG.$partialNum.z" ) { + $fileName = "$Dir/XferLOG.$partialNum.z"; + $compress = 1; + } elsif ( -f "$Dir/XferLOG.$partialNum" ) { + $fileName = "$Dir/XferLOG.$partialNum"; + $compress = 0; + } + if ( my $oldLOG = BackupPC::FileZIO->open($fileName, 0, $compress) ) { + my $data; + while ( $oldLOG->read(\$data, 65536) > 0 ) { + $XferLOG->write(\$data); + } + $oldLOG->close; + } +} + $XferLOG->writeTeeStderr(1) if ( $opts{v} ); unlink("$Dir/NewFileList") if ( -f "$Dir/NewFileList" ); -my $startTime = time(); +my $startTime = time(); my $tarErrs = 0; my $nFilesExist = 0; my $sizeExist = 0; @@ -576,6 +625,7 @@ for my $shareName ( @$ShareNames ) { backups => \@Backups, compress => $Conf{CompressLevel}, XferMethod => $Conf{XferMethod}, + logLevel => $Conf{XferLogLevel}, pidHandler => \&pidHandler, partialNum => $partialNum, }); @@ -588,14 +638,14 @@ for my $shareName ( @$ShareNames ) { # kill off the tar process, first nicely then forcefully # if ( $tarPid > 0 ) { - kill(2, $tarPid); + kill($bpc->sigName2num("INT"), $tarPid); sleep(1); - kill(9, $tarPid); + kill($bpc->sigName2num("KILL"), $tarPid); } if ( @xferPid ) { - kill(2, @xferPid); + kill($bpc->sigName2num("INT"), @xferPid); sleep(1); - kill(9, @xferPid); + kill($bpc->sigName2num("KILL"), @xferPid); } UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); exit(1); @@ -642,7 +692,14 @@ for my $shareName ( @$ShareNames ) { while ( $tarOut =~ /(.*?)[\n\r]+(.*)/s ) { $_ = $1; $tarOut = $2; - $XferLOG->write(\"tarExtract: $_\n"); + if ( /^ / ) { + $XferLOG->write(\"$_\n"); + } else { + $XferLOG->write(\"tarExtract: $_\n"); + } + if ( /^BackupPC_tarExtact aborting \((.*)\)/ ) { + $stat{hostError} = $1; + } if ( /^Done: (\d+) errors, (\d+) filesExist, (\d+) sizeExist, (\d+) sizeExistComp, (\d+) filesTotal, (\d+) sizeTotal/ ) { $tarErrs += $1; $nFilesExist += $2; @@ -709,17 +766,17 @@ for my $shareName ( @$ShareNames ) { # kill off the tranfer program, first nicely then forcefully # if ( @xferPid ) { - kill(2, @xferPid); + kill($bpc->sigName2num("INT"), @xferPid); sleep(1); - kill(9, @xferPid); + kill($bpc->sigName2num("KILL"), @xferPid); } # # kill off the tar process, first nicely then forcefully # if ( $tarPid > 0 ) { - kill(2, $tarPid); + kill($bpc->sigName2num("INT"), $tarPid); sleep(1); - kill(9, $tarPid); + kill($bpc->sigName2num("KILL"), $tarPid); } # # don't do any more shares on this host @@ -737,6 +794,8 @@ if ( $type eq "full" && $stat{hostError} eq "" $stat{xferOK} = 0; } +$stat{xferOK} = 0 if ( $Abort ); + # # Do one last check to make sure it is still the machine we expect. # @@ -746,7 +805,6 @@ if ( $stat{xferOK} && (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { } UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); -$XferLOG->close(); close($newFilesFH) if ( defined($newFilesFH) ); my $endTime = time(); @@ -755,25 +813,31 @@ my $endTime = time(); # If the dump failed, clean up # if ( !$stat{xferOK} ) { - # - # wait a short while and see if the system is still alive - # $stat{hostError} = $stat{lastOutputLine} if ( $stat{hostError} eq "" ); if ( $stat{hostError} ) { print(LOG $bpc->timeStamp, "Got fatal error during xfer ($stat{hostError})\n"); + $XferLOG->write(\"Got fatal error during xfer ($stat{hostError})\n"); } - sleep(10); - if ( $bpc->CheckHostAlive($hostIP) < 0 ) { - $stat{hostAbort} = 1; - } - if ( $stat{hostAbort} ) { - $stat{hostError} = "lost network connection during backup"; + if ( !$Abort ) { + # + # wait a short while and see if the system is still alive + # + sleep(5); + if ( $bpc->CheckHostAlive($hostIP) < 0 ) { + $stat{hostAbort} = 1; + } + if ( $stat{hostAbort} ) { + $stat{hostError} = "lost network connection during backup"; + } + print(LOG $bpc->timeStamp, "Backup aborted ($stat{hostError})\n"); + $XferLOG->write(\"Backup aborted ($stat{hostError})\n"); + } else { + $XferLOG->write(\"Backup aborted by user signal\n"); } - print(LOG $bpc->timeStamp, "Dump aborted ($stat{hostError})\n"); # - # This exits. + # Close the log file and call BackupFailCleanup, which exits. # BackupFailCleanup(); } @@ -807,50 +871,147 @@ sub NothingToDo sub catch_signal { - my $signame = shift; + my $sigName = shift; # - # Children quit quietly on ALRM + # The first time we receive a signal we try to gracefully + # abort the backup. This allows us to keep a partial dump + # with the in-progress file deleted and attribute caches + # flushed to disk etc. # - exit(1) if ( $Pid != $$ && $signame eq "ALRM" ); + if ( !length($SigName) ) { + my $reason; + if ( $sigName eq "INT" ) { + $reason = "aborted by user (signal=$sigName)"; + } else { + $reason = "aborted by signal=$sigName"; + } + if ( $Pid == $$ ) { + # + # Parent logs a message + # + print(LOG $bpc->timeStamp, + "Aborting backup up after signal $sigName\n"); + + # + # Tell xfer to abort + # + $xfer->abort($reason); + + # + # Send ALRMs to BackupPC_tarExtract if we are using it + # + if ( $tarPid > 0 ) { + kill($bpc->sigName2num("ARLM"), $tarPid); + } + + # + # Schedule a 20 second timer in case the clean + # abort doesn't complete + # + alarm(20); + } else { + # + # Children ignore anything other than ALRM and INT + # + if ( $sigName ne "ALRM" && $sigName ne "INT" ) { + return; + } + + # + # The child also tells xfer to abort + # + $xfer->abort($reason); + + # + # Schedule a 15 second timer in case the clean + # abort doesn't complete + # + alarm(15); + } + $SigName = $sigName; + $Abort = 1; + return; + } + + # + # This is a second signal: time to clean up. + # + if ( $Pid != $$ && ($sigName eq "ALRM" || $sigName eq "INT") ) { + # + # Children quit quietly on ALRM or INT + # + exit(1) + } # # Ignore other signals in children # return if ( $Pid != $$ ); - print(LOG $bpc->timeStamp, "cleaning up after signal $signame\n"); - $SIG{$signame} = 'IGNORE'; + $SIG{$sigName} = 'IGNORE'; UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); - $XferLOG->write(\"exiting after signal $signame\n"); - $XferLOG->close(); + $XferLOG->write(\"exiting after signal $sigName\n"); if ( @xferPid ) { - kill(2, @xferPid); + kill($bpc->sigName2num("INT"), @xferPid); sleep(1); - kill(9, @xferPid); + kill($bpc->sigName2num("KILL"), @xferPid); } if ( $tarPid > 0 ) { - kill(2, $tarPid); + kill($bpc->sigName2num("INT"), $tarPid); sleep(1); - kill(9, $tarPid); + kill($bpc->sigName2num("KILL"), $tarPid); } - if ( $signame eq "INT" ) { - $stat{hostError} = "aborted by user (signal=$signame)"; + if ( $sigName eq "INT" ) { + $stat{hostError} = "aborted by user (signal=$sigName)"; } else { - $stat{hostError} = "received signal=$signame"; + $stat{hostError} = "received signal=$sigName"; } BackupFailCleanup(); } +sub CheckForNewFiles +{ + if ( -f _ && $File::Find::name !~ /\/fattrib$/ ) { + $nFilesTotal++; + } elsif ( -d _ ) { + # + # No need to check entire tree + # + $File::Find::prune = 1 if ( $nFilesTotal ); + } +} + sub BackupFailCleanup { my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; + my $keepPartial = 0; + + # + # We keep this backup if it is a full and we actually backed + # up some files. + # + if ( $type eq "full" ) { + if ( $nFilesTotal == 0 && $xfer->getStats->{fileCnt} == 0 ) { + # + # Xfer didn't report any files, but check in the new + # directory just in case. + # + find(\&CheckForNewFiles, "$Dir/new"); + $keepPartial = 1 if ( $nFilesTotal ); + } else { + # + # Xfer reported some files + # + $keepPartial = 1; + } + } - if ( $type ne "full" ) { -# || ($nFilesTotal == 0 && $xfer->getStats->{fileCnt} == 0) ) { + if ( !$keepPartial ) { # # No point in saving this dump; get rid of eveything. # + $XferLOG->close(); unlink("$Dir/timeStamp.level0") if ( -f "$Dir/timeStamp.level0" ); unlink("$Dir/SmbLOG.bad") if ( -f "$Dir/SmbLOG.bad" ); unlink("$Dir/SmbLOG.bad$fileExt") if ( -f "$Dir/SmbLOG.bad$fileExt" ); @@ -860,6 +1021,7 @@ sub BackupFailCleanup rename("$Dir/XferLOG$fileExt", "$Dir/XferLOG.bad$fileExt"); $bpc->RmTreeDefer("$TopDir/trash", "$Dir/new") if ( -d "$Dir/new" ); print("dump failed: $stat{hostError}\n"); + $XferLOG->close(); print("link $clientURI\n") if ( $needLink ); exit(1); } @@ -1063,15 +1225,12 @@ sub BackupSave $num = $Backups[$i]{num} if ( $num < $Backups[$i]{num} ); } $num++; - $bpc->RmTreeDefer("$TopDir/trash", "$Dir/$num") - if ( -d "$Dir/$num" ); + $bpc->RmTreeDefer("$TopDir/trash", "$Dir/$num") if ( -d "$Dir/$num" ); if ( !rename("$Dir/new", "$Dir/$num") ) { - print(LOG $bpc->timeStamp, - "Rename $Dir/new -> $Dir/$num failed\n"); + print(LOG $bpc->timeStamp, "Rename $Dir/new -> $Dir/$num failed\n"); $stat{xferOK} = 0; } - rename("$Dir/XferLOG$fileExt", "$Dir/XferLOG.$num$fileExt"); - rename("$Dir/NewFileList", "$Dir/NewFileList.$num"); + $needLink = 1 if ( -f "$Dir/NewFileList" ); # # Add the new backup information to the backup file @@ -1122,22 +1281,27 @@ sub BackupSave next if ( !-f "$Dir/$Backups[$j]{num}/$file" ); if ( !link("$Dir/$Backups[$j]{num}/$file", "$Dir/$num/$shareM/$fileM") ) { - print(LOG $bpc->timeStamp, - "Unable to link $num/$shareM/$fileM to" - . " $Backups[$j]{num}/$file\n"); + my $str = \"Unable to link $num/$f->{share}/$f->{file} to" + . " $Backups[$j]{num}/$f->{share}/$f->{file}\n"; + $XferLOG->write(\$str); } else { - print(LOG $bpc->timeStamp, - "Bad file $num/$shareM/$fileM replaced by link to" - . " $Backups[$j]{num}/$file\n"); + my $str = "Bad file $num/$f->{share}/$f->{file} replaced" + . " by link to" + . " $Backups[$j]{num}/$f->{share}/$f->{file}\n"; + $XferLOG->write(\$str); } last; } if ( $j < 0 ) { - print(LOG $bpc->timeStamp, - "Removed bad file $num/$shareM/$fileM (no older" - . " copy to link to)\n"); + my $str = "Removed bad file $num/$f->{share}/$f->{file}" + . " (no older copy to link to)\n"; + $XferLOG->write(\$str); } } + $XferLOG->close(); + rename("$Dir/XferLOG$fileExt", "$Dir/XferLOG.$num$fileExt"); + rename("$Dir/NewFileList", "$Dir/NewFileList.$num"); + return $num; } diff --git a/bin/BackupPC_link b/bin/BackupPC_link index 98bada9..b86b7e4 100755 --- a/bin/BackupPC_link +++ b/bin/BackupPC_link @@ -128,7 +128,7 @@ while ( 1 ) { # my $noFill = 1; my $fillFromNum; - if ( $Backups[$num]{type} eq "full" ) { + if ( $Backups[$num]{type} ne "incr" ) { $noFill = 0 } elsif ( $Conf{IncrFill} ) { my $i; diff --git a/bin/BackupPC_restore b/bin/BackupPC_restore index 9ebd8c4..a17fbcc 100755 --- a/bin/BackupPC_restore +++ b/bin/BackupPC_restore @@ -264,6 +264,7 @@ my $xferArgs = { pipeWH => *WH, XferLOG => $RestoreLOG, XferMethod => $Conf{XferMethod}, + logLevel => $Conf{XferLogLevel}, bkupSrcHost => $RestoreReq{hostSrc}, bkupSrcShare => $RestoreReq{shareSrc}, bkupSrcNum => $RestoreReq{num}, @@ -493,17 +494,17 @@ sub RestoreCleanup # kill off the tranfer program, first nicely then forcefully # if ( @xferPid ) { - kill(2, @xferPid); + kill($bpc->sigName2num("INT"), @xferPid); sleep(1); - kill(9, @xferPid); + kill($bpc->sigName2num("KILL"), @xferPid); } # # kill off the tar process, first nicely then forcefully # if ( $tarPid > 0 ) { - kill(2, $tarPid); + kill($bpc->sigName2num("INT"), $tarPid); sleep(1); - kill(9, $tarPid); + kill($bpc->sigName2num("KILL"), $tarPid); } } diff --git a/bin/BackupPC_tarExtract b/bin/BackupPC_tarExtract index 9a2872b..cb2d54a 100755 --- a/bin/BackupPC_tarExtract +++ b/bin/BackupPC_tarExtract @@ -42,20 +42,22 @@ use BackupPC::FileZIO; use BackupPC::PoolWrite; use File::Path; +use constant S_IFMT => 0170000; # type of file + die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); if ( @ARGV != 3 ) { - print("usage: $0 \n"); + print("usage: $0 \n"); exit(1); } if ( $ARGV[0] !~ /^([\w\.\s-]+)$/ ) { - print("$0: bad host name '$ARGV[0]'\n"); + print("$0: bad client name '$ARGV[0]'\n"); exit(1); } -my $host = $1; +my $client = $1; if ( $ARGV[1] !~ /^([\w\s\.\/\$-]+)$/ ) { print("$0: bad share name '$ARGV[1]'\n"); exit(1); @@ -67,6 +69,28 @@ if ( $ARGV[2] !~ /^(\d+)$/ ) { exit(1); } my $Compress = $1; +my $Abort = 0; +my $AbortReason; + +# +# Re-read config file, so we can include the PC-specific config +# +if ( defined(my $error = $bpc->ConfigRead($client)) ) { + print("BackupPC_tarExtract: Can't read PC's config file: $error\n"); + exit(1); +} +%Conf = $bpc->Conf(); + +# +# Catch various signals +# +$SIG{INT} = \&catch_signal; +$SIG{ALRM} = \&catch_signal; +$SIG{TERM} = \&catch_signal; +$SIG{PIPE} = \&catch_signal; +$SIG{STOP} = \&catch_signal; +$SIG{TSTP} = \&catch_signal; +$SIG{TTIN} = \&catch_signal; # # This constant and the line of code below that uses it is borrowed @@ -83,7 +107,7 @@ my $tar_header_length = 512; my $BufSize = 1048576; # 1MB or 2^20 my $MaxFiles = 20; my $Errors = 0; -my $OutDir = "$TopDir/pc/$host/new"; +my $OutDir = "$TopDir/pc/$client/new"; my %Attrib = (); my $ExistFileCnt = 0; @@ -99,12 +123,15 @@ sub TarRead $data = "\0" x $totBytes; while ( $numBytes < $totBytes ) { + return if ( $Abort ); $newBytes = sysread($fh, substr($data, $numBytes, $totBytes - $numBytes), $totBytes - $numBytes); if ( $newBytes <= 0 ) { - print(STDERR "Unexpected end of tar archive (tot = $totBytes," + print("Unexpected end of tar archive (tot = $totBytes," . " num = $numBytes, posn = " . sysseek($fh, 0, 1) . ")\n"); + $Abort = 1; + $AbortReason = "Unexpected end of tar archive"; $Errors++; return; } @@ -140,7 +167,8 @@ sub TarReadFileInfo while ( 1 ) { $head = TarReadHeader($fh); - return if ( $head eq "" || $head eq "\0" x $tar_header_length ); + return if ( $Abort || $head eq "" + || $head eq "\0" x $tar_header_length ); ($name, # string $mode, # octal number $uid, # octal number @@ -198,7 +226,7 @@ sub TarReadFileInfo $prefix = ""; substr ($head, 148, 8) = " "; if (unpack ("%16C*", $head) != $chksum) { - print(STDERR "$name: checksum error at " + print("$name: checksum error at " . sysseek($fh, 0, 1) , "\n"); $Errors++; } @@ -215,6 +243,8 @@ sub TarReadFileInfo TarFlush($fh, $size); next; } + printf("Got file '%s', mode 0%o, size %g, type %d\n", + $name, $mode, $size, $type) if ( $Conf{XferLogLevel} >= 3 ); $name = $longName if ( defined($longName) ); $linkname = $longLink if ( defined($longLink) ); $name =~ s{^\./+}{}; @@ -262,7 +292,7 @@ sub TarReadFile $Attrib{$dir} = BackupPC::Attrib->new({ compress => $Compress }); if ( -f $Attrib{$dir}->fileName("$OutDir/$dir") && !$Attrib{$dir}->read("$OutDir/$dir") ) { - printf(STDERR "Unable to read attribute file %s\n", + printf("Unable to read attribute file %s\n", $Attrib{$dir}->fileName("$OutDir/$dir")); $Errors++; } @@ -271,6 +301,7 @@ sub TarReadFile # # Directory # + logFileAction("create", $f) if ( $Conf{XferLogLevel} >= 1 ); mkpath("$OutDir/$ShareName/$f->{mangleName}", 0, 0777) if ( !-d "$OutDir/$ShareName/$f->{mangleName}" ); } elsif ( $f->{type} == BPC_FTYPE_FILE ) { @@ -287,14 +318,24 @@ sub TarReadFile ? $f->{size} - $nRead : $BufSize; my $data = TarRead($fh, $thisRead); if ( $data eq "" ) { - print(STDERR "Unexpected end of tar archive during read\n"); - $Errors++; + if ( !$Abort ) { + print("Unexpected end of tar archive during read\n"); + $AbortReason = "Unexpected end of tar archive"; + $Errors++; + } + $poolWrite->abort; + $Abort = 1; + unlink("$OutDir/$ShareName/$f->{mangleName}"); + print("Removing partial file $f->{name}\n"); return; } $poolWrite->write(\$data); $nRead += $thisRead; } - processClose($poolWrite, "$ShareName/$f->{mangleName}", $f->{size}); + my $exist = processClose($poolWrite, "$ShareName/$f->{mangleName}", + $f->{size}); + logFileAction($exist ? "pool" : "create", $f) + if ( $Conf{XferLogLevel} >= 1 ); TarFlush($fh, $f->{size}); } elsif ( $f->{type} == BPC_FTYPE_HARDLINK ) { # @@ -310,7 +351,10 @@ sub TarReadFile "$OutDir/$ShareName/$f->{mangleName}", $f->{size}, $Compress); $poolWrite->write(\$f->{linkname}); - processClose($poolWrite, "$ShareName/$f->{mangleName}", $f->{size}); + my $exist = processClose($poolWrite, "$ShareName/$f->{mangleName}", + $f->{size}); + logFileAction($exist ? "pool" : "create", $f) + if ( $Conf{XferLogLevel} >= 1 ); } elsif ( $f->{type} == BPC_FTYPE_SYMLINK ) { # # Symbolic link: write the value of the link to a plain file, @@ -324,7 +368,10 @@ sub TarReadFile "$OutDir/$ShareName/$f->{mangleName}", $f->{size}, $Compress); $poolWrite->write(\$f->{linkname}); - processClose($poolWrite, "$ShareName/$f->{mangleName}", $f->{size}); + my $exist = processClose($poolWrite, "$ShareName/$f->{mangleName}", + $f->{size}); + logFileAction($exist ? "pool" : "create", $f) + if ( $Conf{XferLogLevel} >= 1 ); } elsif ( $f->{type} == BPC_FTYPE_CHARDEV || $f->{type} == BPC_FTYPE_BLOCKDEV || $f->{type} == BPC_FTYPE_FIFO ) { @@ -345,7 +392,10 @@ sub TarReadFile length($data), $Compress); $poolWrite->write(\$data); $f->{size} = length($data); - processClose($poolWrite, "$ShareName/$f->{mangleName}", length($data)); + my $exist = processClose($poolWrite, "$ShareName/$f->{mangleName}", + length($data)); + logFileAction($exist ? "pool" : "create", $f) + if ( $Conf{XferLogLevel} >= 1 ); } else { print("Got unknown type $f->{type} for $f->{name}\n"); $Errors++; @@ -384,7 +434,7 @@ sub processClose my($exists, $digest, $outSize, $errs) = $poolWrite->close; if ( @$errs ) { - print(STDERR join("", @$errs)); + print(join("", @$errs)); $Errors += @$errs; } $TotalFileCnt++; @@ -396,15 +446,63 @@ sub processClose } elsif ( $outSize > 0 ) { print(NEW_FILES "$digest $origSize $fileName\n"); } + return $exists && $origSize > 0; +} + +# +# Generate a log file message for a completed file +# +sub logFileAction +{ + my($action, $f) = @_; + my $owner = "$f->{uid}/$f->{gid}"; + my $name = $f->{name}; + $name = "." if ( $name eq "" ); + my $type = (("", "p", "c", "", "d", "", "b", "", "", "", "l", "", "s")) + [($f->{mode} & S_IFMT) >> 12]; + $type = "h" if ( $f->{type} == BPC_FTYPE_HARDLINK ); + + printf(" %-6s %1s%4o %9s %11.0f %s\n", + $action, + $type, + $f->{mode} & 07777, + $owner, + $f->{size}, + $name); +} + +sub catch_signal +{ + my $sigName = shift; + + # + # The first time we receive a signal we try to gracefully + # abort the backup. This allows us to keep a partial dump + # with the in-progress file deleted and attribute caches + # flushed to disk etc. + # + print("BackupPC_tarExtract: got signal $sigName\n"); + if ( !$Abort ) { + $Abort++; + $AbortReason = "received signal $sigName"; + return; + } + + # + # This is a second signal: time to clean up. + # + print("BackupPC_tarExtract: quitting on second signal $sigName\n"); + close(NEW_FILES); + exit(1) } mkpath("$OutDir/$ShareName", 0, 0777); -open(NEW_FILES, ">>", "$TopDir/pc/$host/NewFileList") - || die("can't open $TopDir/pc/$host/NewFileList"); +open(NEW_FILES, ">>", "$TopDir/pc/$client/NewFileList") + || die("can't open $TopDir/pc/$client/NewFileList"); binmode(NEW_FILES); binmode(STDIN); -1 while ( TarReadFile(*STDIN) ); -1 while ( sysread(STDIN, my $discard, 1024) ); +1 while ( !$Abort && TarReadFile(*STDIN) ); +1 while ( !$Abort && sysread(STDIN, my $discard, 1024) ); # # Flush out remaining attributes. @@ -414,6 +512,10 @@ foreach my $d ( keys(%Attrib) ) { } close(NEW_FILES); +if ( $Abort ) { + print("BackupPC_tarExtact aborting ($AbortReason)\n"); +} + # # Report results to BackupPC_dump # diff --git a/conf/config.pl b/conf/config.pl index 714f1d7..ff5090e 100644 --- a/conf/config.pl +++ b/conf/config.pl @@ -688,6 +688,13 @@ $Conf{BackupZeroFilesIsFatal} = 1; # $Conf{XferMethod} = 'smb'; +# +# Level of verbosity in Xfer log files. 0 means be quiet, 1 will give +# will give one line per file, 2 will also show skipped files on +# incrementals, higher values give more output. +# +$Conf{XferLogLevel} = 1; + # # Full path for smbclient. Security caution: normal users should not # allowed to write to this file or directory. @@ -981,15 +988,6 @@ $Conf{RsyncRestoreArgs} = [ # ]; -# -# Amount of verbosity in Rsync Xfer log files. 0 means be quiet, -# 1 will give will give one line per file, 2 will also show skipped -# files on incrementals, higher values give more output. 10 will -# include byte dumps of all data read/written, which will make the -# log files huge. -# -$Conf{RsyncLogLevel} = 1; - # # Archive Destination # diff --git a/configure.pl b/configure.pl index fe04994..a48ffc6 100755 --- a/configure.pl +++ b/configure.pl @@ -15,7 +15,7 @@ # Craig Barratt # # COPYRIGHT -# Copyright (C) 2001-2003 Craig Barratt +# Copyright (C) 2001-2004 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -432,6 +432,7 @@ foreach my $lib ( qw( BackupPC/Xfer/Tar.pm BackupPC/Xfer/Smb.pm BackupPC/Xfer/Rsync.pm + BackupPC/Xfer/RsyncDigest.pm BackupPC/Xfer/RsyncFileIO.pm BackupPC/Zip/FileMember.pm BackupPC/Lang/en.pm @@ -549,6 +550,11 @@ if ( defined($Conf{BlackoutHourBegin}) ) { delete($Conf{BlackoutWeekDays}); } +# +# $Conf{RsyncLogLevel} has been replaced by $Conf{XferLogLevel} +# +$Conf{XferLogLevel} = $Conf{RsyncLogLevel}; + # # IncrFill should now be off # diff --git a/lib/BackupPC/CGI/HostInfo.pm b/lib/BackupPC/CGI/HostInfo.pm index cf58ad7..ae3a5db 100644 --- a/lib/BackupPC/CGI/HostInfo.pm +++ b/lib/BackupPC/CGI/HostInfo.pm @@ -287,16 +287,50 @@ EOF $statusStr .= eval("qq{$Lang->{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times}}"); if ( $StatusHost{aliveCnt} >= $Conf{BlackoutGoodCnt} - && $Conf{BlackoutGoodCnt} >= 0 && $Conf{BlackoutHourBegin} >= 0 - && $Conf{BlackoutHourEnd} >= 0 ) { + && $Conf{BlackoutGoodCnt} >= 0 ) { + # + # Handle backward compatibility with original separate scalar + # blackout parameters. + # + if ( defined($Conf{BlackoutHourBegin}) ) { + push(@{$Conf{BlackoutPeriods}}, + { + hourBegin => $Conf{BlackoutHourBegin}, + hourEnd => $Conf{BlackoutHourEnd}, + weekDays => $Conf{BlackoutWeekDays}, + } + ); + } + + # + # TODO: this string needs i18n. Also, comma-separated + # list with "and" for the last element might not translate + # correctly. + # my(@days) = qw(Sun Mon Tue Wed Thu Fri Sat); - my($days) = join(", ", @days[@{$Conf{BlackoutWeekDays}}]); - my($t0) = sprintf("%d:%02d", $Conf{BlackoutHourBegin}, - 60 * ($Conf{BlackoutHourBegin} - - int($Conf{BlackoutHourBegin}))); - my($t1) = sprintf("%d:%02d", $Conf{BlackoutHourEnd}, - 60 * ($Conf{BlackoutHourEnd} - - int($Conf{BlackoutHourEnd}))); + my $blackoutStr; + my $periodCnt = 0; + foreach my $p ( @{$Conf{BlackoutPeriods}} ) { + next if ( ref($p->{weekDays}) ne "ARRAY" + || !defined($p->{hourBegin}) + || !defined($p->{hourEnd}) + ); + my $days = join(", ", @days[@{$p->{weekDays}}]); + my $t0 = sprintf("%d:%02d", $p->{hourBegin}, + 60 * ($p->{hourBegin} - int($p->{hourBegin}))); + my $t1 = sprintf("%d:%02d", $p->{hourEnd}, + 60 * ($p->{hourEnd} - int($p->{hourEnd}))); + if ( $periodCnt ) { + $blackoutStr .= ", "; + if ( $periodCnt == @{$Conf{BlackoutPeriods}} - 1 ) { + $blackoutStr .= eval("qq{$Lang->{and}}"); + $blackoutStr .= " "; + } + } + $blackoutStr + .= eval("qq{$Lang->{__time0_to__time1_on__days}}"); + $periodCnt++; + } $statusStr .= eval("qq{$Lang->{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___}}"); } } diff --git a/lib/BackupPC/Lang/de.pm b/lib/BackupPC/Lang/de.pm index 7b546a9..c119f42 100644 --- a/lib/BackupPC/Lang/de.pm +++ b/lib/BackupPC/Lang/de.pm @@ -1,5 +1,6 @@ #!/bin/perl # +# by Manfred Herrmann (11.03.2004 for V2.1.0beta0) # by Manfred Herrmann (V1.1) (some typo errors + 3 new strings) # CVS-> Revision ??? # @@ -9,36 +10,35 @@ # -------------------------------- -$Lang{Start_Archive} = "ENG Start Archive"; -$Lang{Stop_Dequeue_Archive} = "ENG Stop/Dequeue Archive"; +$Lang{Start_Archive} = "Archivierung starten"; +$Lang{Stop_Dequeue_Archive} = "Archivierung stoppen"; $Lang{Start_Full_Backup} = "Starte Backup vollständig"; $Lang{Start_Incr_Backup} = "Starte Backup incrementell"; $Lang{Stop_Dequeue_Backup} = "Stoppen/Aussetzen Backup"; $Lang{Restore} = "Wiederherstellung"; -$Lang{Type_full} = "ENG full"; -$Lang{Type_incr} = "ENG incremental"; +$Lang{Type_full} = "voll"; +$Lang{Type_incr} = "inkrementell"; # ----- -$Lang{Only_privileged_users_can_view_admin_options} = "ENG Only privileged users can view admin options."; -$Lang{H_Admin_Options} = "ENG BackupPC Server: Admin Options"; -$Lang{Admin_Options} = "ENG Admin Options"; +$Lang{Only_privileged_users_can_view_admin_options} = "Nur privilegierte Nutzer können Admin Optionen einsehen."; +$Lang{H_Admin_Options} = "BackupPC Server: Admin Optionen"; +$Lang{Admin_Options} = "Admin Optionen"; $Lang{Admin_Options_Page} = < -\${h1("Server Control")} +\${h1("Server Steuerung")}
-
Stop the server: -
Reload the server configuration: +
Server stoppen: +
Server Konfiguration neu laden:
-\${h1("Server Configuration")} +\${h1("Server Konfiguration")}
    -
  • Other options can go here... e.g., -
  • Edit server configuration +
  • Andere Optionen sind hier möglich ... z.B., +
  • Server Konfiguration editieren
EOF $Lang{Unable_to_connect_to_BackupPC_server} = "Kann keine Verbindung zu BackupPC server herstellen", @@ -48,11 +48,11 @@ $Lang{Unable_to_connect_to_BackupPC_server} = "Kann keine Verbindung zu BackupPC "Möglicherweise ist der BackupPC server Prozess nicht gestartet oder es besteht ein" . " Konfigurationsfehler. Bitte teilen Sie diese Fehlermeldung dem Systemadministrator mit."; $Lang{Admin_Start_Server} = < -The BackupPC server at \$Conf{ServerHost} port \$Conf{ServerPort} -is not currently running (maybe you just stopped it, or haven't yet started it).
-Do you want to start it? +Der BackupPC Server auf \$Conf{ServerHost} port \$Conf{ServerPort} +ist momentan nicht aktiv (möglicherweise wurde er gestoppt, oder noch nicht gestartet).
+Möchten Sie den Server starten? @@ -72,7 +72,7 @@ $Lang{BackupPC_Server_Status}= < Die Server Prozess ID (PID) ist \$Info{pid}, auf Computer \$Conf{ServerHost}, Version \$Info{Version}, gestartet am \$serverStartTime.
  • Dieser Status wurde am \$now generiert. -
  • ENG The configuration was last loaded at \$configLoadTime. +
  • Die Konfiguration wurde neu geladen am \$configLoadTime.
  • Computer werden am \$nextWakeupTime auf neue Aufträge geprüft.
  • Weitere Informationen:
      @@ -118,7 +118,7 @@ EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupServer: Übersicht"; -$Lang{BackupPC__Archive} = "ENG BackupPC: Archive"; +$Lang{BackupPC__Archive} = "BackupPC: Archivierung"; $Lang{BackupPC_Summary}=< - -There are \$hostCntGood hosts that have been backed up for a total size of \${fullSizeTot}GB +Es gibt \$hostCntGood Computer die gesichert wurden, mit insgesamt \${fullSizeTot}GB

      @@ -206,9 +205,9 @@ There are \$hostCntGood hosts that have been backed up for a total size of \${fu - + - + \$strGood \$checkAllHosts
      Host
      Computer User Backup Size Backup Größe
      @@ -218,8 +217,8 @@ There are \$hostCntGood hosts that have been backed up for a total size of \${fu EOF $Lang{BackupPC_Archive2} = < \$HostListStr
    @@ -232,22 +231,22 @@ About to archive the following hosts \$paramStr - +
    EOF $Lang{BackupPC_Archive2_location} = < - Archive Location/Device + + Archivierung Ort/Gerät EOF $Lang{BackupPC_Archive2_compression} = < - Compression + + Kompression None
    gzip
    @@ -257,15 +256,15 @@ ENG EOF $Lang{BackupPC_Archive2_parity} = < - Number of Parity Files + + Anzahl Parität-Dateien EOF $Lang{BackupPC_Archive2_split} = < - Split output into + + Aufteilen in Megabytes EOF @@ -331,7 +330,7 @@ EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Nur berechtigte User können die Warteschlangen einsehen."; # -------------------------------- -$Lang{Only_privileged_users_can_archive} = "ENG Only privileged users can Archive."; +$Lang{Only_privileged_users_can_archive} = "Nur berechtigte Personen könnnen archivieren."; # -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupServer: Warteschlangen Übersicht"; # -------------------------------- @@ -465,7 +464,7 @@ Wiederherstellung korrekt ist) window.open(URL,'','width=500,height=400'); } - Search for available shares (NOT IMPLEMENTED) + Suche nach verfügbaren Freigaben (NICHT IMPLEMENTIERT) Restore auf Freigabe \${h2("Möglichkeit 2: Download als Zip Archiv Datei")}

    -Archive::Zip is not installed so you will not be able to download a -zip archive. -Please ask your system adminstrator to install Archive::Zip from -www.cpan.org. +Archive::Zip ist nicht installiert. Der Download als Zip Archiv Datei ist daher nicht möglich. +Bitte lassen Sie bei Bedarf von Ihrem Administrator die Perl-Erweiterung Archive::Zip von +www.cpan.org installieren.

    EOF @@ -599,9 +597,9 @@ Zur EOF $Lang{BackupPC_Archive_Reply_from_server} = < -Reply from server was: \$reply +Die Antwort vom Server war: \$reply EOF # ------------------------- @@ -712,14 +710,14 @@ EOF $Lang{Host__host_Archive_Summary} = "BackupPC: Host \$host Archive Summary"; $Lang{Host__host_Archive_Summary2} = < \$warnStr
      \$statusStr
    -\${h2("User Actions")} +\${h2("User Aktionen")}

    @@ -782,10 +780,10 @@ $Lang{Backup_browse_for__host} = < Sie browsen das Backup #\$num, erstellt am \$backupTime (vor \$backupAge Tagen), \$filledBackup -
  • ENG Enter directory: +
  • Verzeichnis eingeben:
  • Klicken Sie auf ein Verzeichnis um dieses zu durchsuchen,
  • Klicken Sie auf eine Datei um diese per download wiederherzustellen, -
  • ENG You can view the backup history of the current directory. +
  • Einsehen der Backup Historie des aktuellen Verzeichnisses.
  • @@ -821,40 +819,35 @@ This is now in the checkAll row EOF # ------------------------------ -$Lang{DirHistory_backup_for__host} = "(ENGLISH) BackupPC: Directory backup history for \$host"; +$Lang{DirHistory_backup_for__host} = "BackupPC: Verzeichnis Historie für \$host"; # # These two strings are used to build the links for directories and # file versions. Files are appended with a version number. # -$Lang{DirHistory_dirLink} = "ENG dir"; -$Lang{DirHistory_fileLink} = "ENG v"; +$Lang{DirHistory_dirLink} = "Verzeichnis"; +$Lang{DirHistory_fileLink} = "V"; $Lang{DirHistory_for__host} = < -This display shows each unique version of files across all -the backups: +Diese Ansicht zeigt alle unterschiedlichen Versionen der Dateien in den Datensicherungen:
      -
    • Click on a backup number to return to the backup browser, -
    • Click on a directory link (\$Lang->{DirHistory_dirLink}) to navigate - into that directory, -
    • Click on a file version link (\$Lang->{DirHistory_fileLink}0, - \$Lang->{DirHistory_fileLink}1, ...) to download that file, -
    • Files with the same contents between different backups have the same - version number, -
    • Files or directories not present in a particular backup have an - empty box. -
    • Files shown with the same version might have different attributes. - Select the backup number to see the file attributes. +
    • Klicken Sie eine Datensicherungs Nummer für die Datensicherungs Übersicht, +
    • Wählen Sie hier einen Verzeichnis Namen: (\$Lang->{DirHistory_dirLink}) um Verzeichnisse anzuzeigen, +
    • Klicken Sie auf eine Datei Version (\$Lang->{DirHistory_fileLink}0, + \$Lang->{DirHistory_fileLink}1, ...) für einen Download der Datei, +
    • Dateien mit dem gleichen Inhalt in verschiedenen Datensicherungen haben die gleiche Versionsnummer, +
    • Dateien oder Verzeichnisse, die in einer Datensicherung nicht vorhanden sind, haben dort keinen Eintrag. +
    • Dateien mit der gleichen Version können unterschiedliche Attribute haben. Wählen Sie die Datensicherungsnummer um die Attribute anzuzeigen.
    -\${h2("History of \${EscHTML(\$dirDisplay)}")} +\${h2("Historie von \${EscHTML(\$dirDisplay)}")}
    -\$backupNumStr -\$backupTimeStr +\$backupNumStr +\$backupTimeStr \$fileStr
    Backup number
    Backup time
    Datensicherung Nummer
    Sicherung Zeitpunkt
    EOF @@ -898,29 +891,29 @@ $Lang{Restore___num_details_for__host2 } = < - - - - - - - - + + + + + + +
    Number \$Archives[\$i]{num}
    Requested by \$ArchiveReq{user}
    Request time \$reqTime
    Result \$Archives[\$i]{result}
    Error Message \$Archives[\$i]{errorMsg}
    Start time \$startTime
    Duration \$duration min
    Xfer log file -View, -Errors +
    Nummer \$Archives[\$i]{num}
    beauftragt von \$ArchiveReq{user}
    Auftrag Zeitpunkt \$reqTime
    Ergebnis \$Archives[\$i]{result}
    Fehlermeldung \$Archives[\$i]{errorMsg}
    Start Zeitpunkt \$startTime
    Dauer \$duration min
    Xfer LOG Datei +Anzeigen, +Fehler

    -\${h1("Host list")} +\${h1("Computer Liste")}

    - + \$HostListStr
    HostBackup Number
    HostDatensicherung Nummer
    EOF @@ -956,8 +949,7 @@ $Lang{Only_privileged_users_can_restore_backup_files} = "Nur berechtigte User k $Lang{Bad_host_name} = "Falscher Computer Name \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "Sie haben keine Dateien selektiert; bitte gehen Sie zurück um" . " Dateien zu selektieren."; -$Lang{You_haven_t_selected_any_hosts} = "ENG You haven\'t selected any hosts; please go Back to" - . " select some hosts."; +$Lang{You_haven_t_selected_any_hosts} = "Sie haben keinen Computer gewählt, bitte zurückgehen um einen auszuwählen."; $Lang{Nice_try__but_you_can_t_put} = "Sie dürfen \'..\' nicht in Dateinamen verwenden"; $Lang{Host__doesn_t_exist} = "Computer \${EscHTML(\$In{hostDest})} existiert nicht"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "Sie haben keine Berechtigung zum Restore auf Computer" @@ -970,12 +962,12 @@ $Lang{Empty_host_name} = "leerer Computer Name"; $Lang{Unknown_host_or_user} = "Unbekannter Computer oder User \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Nur berechtigte User können Informationen sehen über" . " Computer \${EscHTML(\$host)}." ; -$Lang{Only_privileged_users_can_view_archive_information} = "ENG Only privileged users can view archive information."; +$Lang{Only_privileged_users_can_view_archive_information} = "Nur berechtigte User können Archiv Informationen einsehen."; $Lang{Only_privileged_users_can_view_restore_information} = "Nur berechtigte User können Restore Informationen einsehen."; $Lang{Restore_number__num_for_host__does_not_exist} = "Restore Nummer \$num für Computer \${EscHTML(\$host)} existiert" . " nicht."; -$Lang{Archive_number__num_for_host__does_not_exist} = "ENG Archive number \$num for host \${EscHTML(\$host)} does" - . " not exist."; +$Lang{Archive_number__num_for_host__does_not_exist} = "Archiv Nummer \$num für Computer \${EscHTML(\$host)} existiert" + . " nicht."; $Lang{Can_t_find_IP_address_for} = "Kann IP-Adresse für \${EscHTML(\$host)} nicht finden"; $Lang{host_is_a_DHCP_host} = <Da Computer \$host mindestens \$Conf{BlackoutGoodCnt} -mal fortlaufend erreichbar war, wird er in der Zeit von \$t0 bis \$t1 am \$days nicht gesichert. (Die Sicherung +mal fortlaufend erreichbar war, wird er in der Zeit von \$blackoutStr nicht gesichert. (Die Sicherung erfolgt automatisch außerhalb der konfigurierten Betriebszeit) EOF +$Lang{__time0_to__time1_on__days} = "\$t0 bis \$t1 am \$days"; + $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Backups sind für die nächsten \$hours Stunden deaktiviert. ({Stop_Dequeue_Archive})}&host=\$host\">diese Zeit ändern). @@ -1098,9 +1092,9 @@ EOF $Lang{checkAllHosts} = < - ENG Select all + alle auswählen - + EOF @@ -1115,7 +1109,7 @@ $Lang{fileHeader} = < ENG Dieses Verzeichnis in Backup browsen. +

  • Wählen Sie die anzuzeigende Datensicherung: EOF $Lang{Restore_Summary} = < -Click on the archive number for more details. +Klicken Sie auf die Archiv Nummer um die Details anzuzeigen. - - - - + + + + \$ArchiveStr
    Archive# Result Start Date Dur/mins
    Archiv# Ergebnis Start Zeitpunkt Dauer/min.
    @@ -1175,8 +1169,8 @@ EOF $Lang{off} = "aus"; $Lang{backupType_full} = "voll"; -$Lang{backupType_incr} = "incr"; -$Lang{backupType_partial} = "ENG partial"; +$Lang{backupType_incr} = "inkrementell"; +$Lang{backupType_partial} = "partiell"; $Lang{failed} = "fehler"; $Lang{success} = "erfolgreich"; @@ -1194,15 +1188,15 @@ $Lang{Status_link_running} = "Link l $Lang{Reason_backup_done} = "Backup durchgeführt"; $Lang{Reason_restore_done} = "Restore durchgeführt"; -$Lang{Reason_archive_done} = "ENG archive done"; +$Lang{Reason_archive_done} = "Archivierung durchgeführt"; $Lang{Reason_nothing_to_do} = "kein Auftrag"; $Lang{Reason_backup_failed} = "Backup Fehler"; $Lang{Reason_restore_failed} = "Restore Fehler"; -$Lang{Reason_archive_failed} = "ENG archive failed"; +$Lang{Reason_archive_failed} = "Archivierung Fehler"; $Lang{Reason_no_ping} = "nicht erreichbar"; $Lang{Reason_backup_canceled_by_user} = "Abbruch durch User"; $Lang{Reason_restore_canceled_by_user} = "Abbruch durch User"; -$Lang{Reason_archive_canceled_by_user} = "ENG archive canceled by user"; +$Lang{Reason_archive_canceled_by_user} = "Archivierung abgebrochen durch User"; # --------- # Email messages diff --git a/lib/BackupPC/Lang/en.pm b/lib/BackupPC/Lang/en.pm index 78e1e73..dea65b8 100644 --- a/lib/BackupPC/Lang/en.pm +++ b/lib/BackupPC/Lang/en.pm @@ -1072,9 +1072,11 @@ EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Because \$host has been on the network at least \$Conf{BlackoutGoodCnt} -consecutive times, it will not be backed up from \$t0 to \$t1 on \$days. +consecutive times, it will not be backed up from \$blackoutStr. EOF +$Lang{__time0_to__time1_on__days} = "\$t0 to \$t1 on \$days"; + $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Backups are deferred for \$hours hours ({Stop_Dequeue_Archive})}&host=\$host\">change this number). diff --git a/lib/BackupPC/Lang/es.pm b/lib/BackupPC/Lang/es.pm index e96b331..1d8e591 100644 --- a/lib/BackupPC/Lang/es.pm +++ b/lib/BackupPC/Lang/es.pm @@ -1074,9 +1074,11 @@ EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Dado que \$host ha estado en la red al menos \$Conf{BlackoutGoodCnt} -veces consecutivas, no se le realizará copia de seguridad desde \$t0 hasta \$t1 en \$days. +veces consecutivas, no se le realizará copia de seguridad desde \$blackoutStr. EOF +$Lang{__time0_to__time1_on__days} = "\$t0 hasta \$t1 en \$days"; + $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Las copias de seguridad se retrasarán durante \$hours hours ({Stop_Dequeue_Archive})}&host=\$host\">Cambie este número). diff --git a/lib/BackupPC/Lang/fr.pm b/lib/BackupPC/Lang/fr.pm index 8d32c24..37829dc 100644 --- a/lib/BackupPC/Lang/fr.pm +++ b/lib/BackupPC/Lang/fr.pm @@ -1070,9 +1070,11 @@ EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Du fait que \$host a été présent sur le réseau au moins \$Conf{BlackoutGoodCnt} -fois consécutives, il ne sera pas sauvegardé de \$t0 à \$t1 pendant \$days. +fois consécutives, il ne sera pas sauvegardé de \$blackoutStr. EOF +$Lang{__time0_to__time1_on__days} = "\$t0 à \$t1 pendant \$days"; + $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <Les sauvegardes sont reportées pour \$hours heures ({Stop_Dequeue_Archive})}&host=\$host\">changer ce nombre). diff --git a/lib/BackupPC/Lang/it.pm b/lib/BackupPC/Lang/it.pm index 76e6e54..de5b11b 100644 --- a/lib/BackupPC/Lang/it.pm +++ b/lib/BackupPC/Lang/it.pm @@ -1,6 +1,6 @@ #!/usr/bin/perl # -# $Id: it.pm,v 1.2 2004/03/13 20:57:05 gfk Exp $ +# $Id: it.pm,v 1.3 2004/03/15 03:12:43 cbarratt Exp $ # # Italian i18n file # @@ -119,13 +119,13 @@ EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: prospetto server"; -+$Lang{BackupPC__Archive} = "BackupPC: Archive"; -+$Lang{BackupPC_Summary} = < Questo rapporto di stato è stato generato il \$now. -

    +

    \${h2("Host con backup buoni")}

    @@ -136,15 +136,16 @@ Ci sono \$hostCntGood host sottoposti a backup per un totale di:

  • \$incrTot backup incrementali per una dimensione totale di \${incrSizeTot}GB (prima del processo di pooling e compressione). - - +

    +
    Host
    + - - - + + + - + \$strGood @@ -158,11 +159,11 @@ Ci sono \$hostCntNone host senza alcun backup. - - - + + + - + \$strNone @@ -209,7 +210,7 @@ totale di \${fullSizeTot}GB
    Host Utente Completi Età completi
    (giorni)
    Dimensione completi
    (GB)
    Velocità
    (MB/s)
    Età completi (giorni) Dimensione completi (GB) Velocità (MB/s) Incrementali Età incrementali
    (giorni)
    Età incrementali (giorni) Stato Ultimo tentativo
    Host Utente Completi Età completi
    (giorni)
    Dimensione completi
    (GB)
    Velocità
    (MB/s)
    Età completi (giorni) Dimensione completi (GB) Velocità (MB/s) Incrementali Età incrementali
    (giorni)
    Età incrementali (giorni) Stato Ultimo tentativo
    - + \$strGood \$checkAllHosts
    Host Utente Dimensione
    backup
    Dimensione backup
    @@ -355,6 +356,7 @@ Sono state accodate le seguenti richieste degli utenti: \${h2("Prospetto code in background")}

    Sono attualmente in coda le seguenti richieste di background: +

    @@ -365,6 +367,7 @@ Sono attualmente in coda le seguenti richieste di background: \${h2("Prospetto coda comandi")}

    Sono attualmente in coda le seguenti richieste di comandi: +

    Host Data richiesta
    @@ -444,7 +447,6 @@ su \$host.

    Attenzione: ogni file esistente che corrisponde ai file selezionati sarà sovrascritto! -

    @@ -465,13 +467,13 @@ sarà sovrascritto! window.open(URL,'','width=500,height=400'); } - Search for available shares (NOT IMPLEMENTED) + Search for available shares (NOT IMPLEMENTED) - @@ -488,6 +490,7 @@ EOF # ------------------------------ $Lang{Option_2__Download_Zip_archive} = < \${h2("Opzione 2: scaricamento archivio zip")}

    È possibile scaricare un archivio zip contenente tutti i @@ -525,7 +528,7 @@ $Lang{Option_2__Download_Zip_archive2} = <www.cpan.org. +www.cpan.org.

    EOF @@ -595,7 +598,7 @@ $Lang{Reply_from_server_was___reply} = < La risposta del server ` stata: \$reply

    -Ritorna alla homepage di \$hostDest. +Ritorna alla homepage di \$hostDest. EOF $Lang{BackupPC_Archive_Reply_from_server} = < \$statusStr +

    \${h2("Azioni utente")}

    @@ -628,12 +632,12 @@ $Lang{Host__host_Backup_Summary2} = <

    Host Data richiesta
    Ripristino dei file sulla condivisione
    Ripristino dei file al di sotto della directory
    +
    Ripristino dei file al di sotto della directory (relativa alla condivisione)
    - + - - + + \$str @@ -646,13 +650,13 @@ Cliccare sul numero di bakcup per sfogliare e ripristinare i file di backup. \${h2("Prospetto errori trasferimento")}

    Numero
    backup
    Numero backup Tipo Completo Data avvio Durata
    (minuti)
    Età
    (giorni)
    Durata (minuti) Età (giorni) Percorso backup server
    - + - - - - + + + + \$errStr
    Numero
    backup
    Numero backup Tipo Vedere Numero errori
    trasferimento
    Numero file
    con problemi
    Numero condivisioni
    con problemi
    Numero
    errori tar
    Numero errori trasferimento Numero file con problemi Numero condivisioni con problemi Numero errori tar
    @@ -664,6 +668,7 @@ I file esistenti sono quelli già presenti nel pool; i file nuovi sono quelli aggiunti al pool. I file vuoti e gli errori SMB non sono conteggiati nei contatori di riutilizzo e file nuovi. +

    @@ -671,15 +676,15 @@ riutilizzo e file nuovi. - + - - - - - - - + + + + + + + \$sizeStr
    Totali File nuovi
    Numero
    backup
    Numero backup Tipo Numero
    file
    Dimensione
    (MB)
    Velocità
    (MB/s)
    Numero
    file
    Dimensione
    (MB)
    Numero
    file
    Dimensione
    (MB)
    Numero file Dimensione (MB) Velocità (MB/s) Numero file Dimensione (MB) Numero file Dimensione (MB)
    @@ -695,15 +700,15 @@ nuovi. File esistenti File nuovi - Numero
    backup + Numero backup Tipo - Livello
    compressione - Dimensione
    (MB) - Compresso
    (MB) - Tasso
    compressione - Dimensione
    (MB) - Compresso
    (MB) - Tasso
    compressione + Livello compressione + Dimensione (MB) + Compresso (MB) + Tasso compressione + Dimensione (MB) + Compresso (MB) + Tasso compressione \$compStr @@ -799,16 +804,13 @@ $Lang{Backup_browse_for__host} = <
    -
    \$dirStr

    - -
    +
    \$fileHeader \$topCheckAll \$fileStr @@ -820,7 +822,6 @@ $Lang{Backup_browse_for__host} = < --> -
    EOF @@ -870,24 +871,24 @@ $Lang{Restore___num_details_for__host2 } = < - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -1082,9 +1083,11 @@ EOF $Lang{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___} = <Poiché \$host è rimasto in rete per almeno \$Conf{BlackoutGoodCnt} -volte consecutive, il backup non sarà effettuato dalle \$t0 alle \$t1 di \$days. +volte consecutive, il backup non sarà effettuato dalle \$blackoutStr. EOF +$Lang{__time0_to__time1_on__days} = "\$t0 alle \$t1 di \$days"; + $Lang{Backups_are_deferred_for_hours_hours_change_this_number} = <I backup sono stati posticipati per \$hours ore ({Stop_Dequeue_Archive})}&host=\$host\">modifica questo numero). @@ -1138,14 +1141,14 @@ $Lang{Restore_Summary} = < Fare clic sul numero del ripristino per maggiori dettagli.
    Numero \$Restores[\$i]{num}
    Richiesto da \$RestoreReq{user}
    Data richiesta \$reqTime
    Risultato \$Restores[\$i]{result}
    Messaggio d\'errore \$Restores[\$i]{errorMsg}
    Host sorgente \$RestoreReq{hostSrc}
    Numero backup
    sorgente
    \$RestoreReq{num}
    Condivisione
    sorgente
    \$RestoreReq{shareSrc}
    Host destinazione \$RestoreReq{hostDest}
    Condivisione
    destinazione
    \$RestoreReq{shareDest}
    Data avvio \$startTime
    Durata \$duration min
    Numero file \$Restores[\$i]{nFiles}
    Dimensione totale \${MB}MB
    Tasso
    trasferimento
    \$MBperSecMB/s
    Errori
    creazione tar
    \$Restores[\$i]{tarCreateErrs}
    Errori
    trasferimento
    \$Restores[\$i]{xferErrs}
    File log
    trasferimento
    +
    Numero \$Restores[\$i]{num}
    Richiesto da \$RestoreReq{user}
    Data richiesta \$reqTime
    Risultato \$Restores[\$i]{result}
    Messaggio d\'errore \$Restores[\$i]{errorMsg}
    Host sorgente \$RestoreReq{hostSrc}
    Numero backup sorgente \$RestoreReq{num}
    Condivisione sorgente \$RestoreReq{shareSrc}
    Host destinazione \$RestoreReq{hostDest}
    Condivisione destinazione \$RestoreReq{shareDest}
    Data avvio \$startTime
    Durata \$duration min
    Numero file \$Restores[\$i]{nFiles}
    Dimensione totale \${MB}MB
    Tasso trasferimento \$MBperSecMB/s
    Errori creazione tar \$Restores[\$i]{tarCreateErrs}
    Errori trasferimento \$Restores[\$i]{xferErrs}
    File log trasferimento Vedi, Errori
    - + - - - - - + + + + + \$restoreStr
    Numero
    ripristino
    Numero ripristino Risultato Data avvio Durata
    (minuti)
    Numero
    file
    Dimensione
    (MB)
    Numero
    errori tar
    Numero errori
    trasferimento
    Durata (minuti) Numero file Dimensione (MB) Numero errori tar Numero errori trasferimento
    @@ -1157,10 +1160,10 @@ $Lang{Archive_Summary} = < Fare clic sul numero di archivio per maggiori dettagli. - + - - + + \$ArchiveStr
    Numero
    archivio
    Numero archivio Risultato Data
    avvio
    Durata
    minuti
    Data avvio Durata minuti
    @@ -1200,6 +1203,7 @@ $Lang{Status_link_running} = "collegamenti in esecuzione"; $Lang{Reason_backup_done} = "backup eseguito"; $Lang{Reason_restore_done} = "restore eseguito"; +$Lang{Reason_archive_done} = "archivio eseguito"; $Lang{Reason_nothing_to_do} = "nulla da fare"; $Lang{Reason_backup_failed} = "backup fallito"; $Lang{Reason_restore_failed} = "restore fallito"; diff --git a/lib/BackupPC/Lib.pm b/lib/BackupPC/Lib.pm index edd50dc..39f9680 100644 --- a/lib/BackupPC/Lib.pm +++ b/lib/BackupPC/Lib.pm @@ -48,6 +48,7 @@ use File::Compare; use Socket; use Cwd; use Digest::MD5; +use Config; sub new { @@ -92,7 +93,7 @@ sub new if ( !$noUserCheck && $bpc->{Conf}{BackupPCUserVerify} && $> != (my $uid = (getpwnam($bpc->{Conf}{BackupPCUser}))[2]) ) { - print("Wrong user: my userid is $>, instead of $uid" + print(STDERR "Wrong user: my userid is $>, instead of $uid" . " ($bpc->{Conf}{BackupPCUser})\n"); return; } @@ -154,6 +155,20 @@ sub verbose return $bpc->{verbose}; } +sub sigName2num +{ + my($bpc, $sig) = @_; + + if ( !defined($bpc->{SigName2Num}) ) { + my $i = 0; + foreach my $name ( split(' ', $Config{sig_name}) ) { + $bpc->{SigName2Num}{$name} = $i; + $i++; + } + } + return $bpc->{SigName2Num}{$sig}; +} + # # Generate an ISO 8601 format timeStamp (but without the "T"). # See http://www.w3.org/TR/NOTE-datetime and @@ -434,7 +449,7 @@ sub RmTreeQuiet if ( defined($roots) && length($roots) ) { $roots = [$roots] unless ref $roots; } else { - print "RmTreeQuiet: No root path(s) specified\n"; + print(STDERR "RmTreeQuiet: No root path(s) specified\n"); } chdir($pwd); foreach $root (@{$roots}) { @@ -448,7 +463,7 @@ sub RmTreeQuiet if ( !unlink($root) ) { if ( -d $root ) { my $d = DirHandle->new($root) - or print "Can't read $pwd/$root: $!"; + or print(STDERR "Can't read $pwd/$root: $!"); @files = $d->read; $d->close; @files = grep $_!~/^\.{1,2}$/, @files; @@ -1154,7 +1169,7 @@ sub cmdSystemOrEval # force list-form of exec(), ie: no shell even for 1 arg # exec { $cmd->[0] } @$cmd; - print("Exec of @$cmd failed\n"); + print(STDERR "Exec of @$cmd failed\n"); exit(1); } # diff --git a/lib/BackupPC/PoolWrite.pm b/lib/BackupPC/PoolWrite.pm index d77b7af..01d89a7 100644 --- a/lib/BackupPC/PoolWrite.pm +++ b/lib/BackupPC/PoolWrite.pm @@ -115,7 +115,7 @@ sub write # file list if the file changes between the file list sending # and the file sending). Here we only catch the case where # we haven't computed the digest (ie: we have written no more - # than $BufSize. We catch the big file case below. + # than $BufSize). We catch the big file case below. # if ( !defined($dataRef) && !defined($a->{digest}) && $a->{fileSize} != length($a->{data}) ) { @@ -270,9 +270,6 @@ sub write # We are at EOF, so finish up # $a->{eof} = 1; - foreach my $f ( @{$a->{files}} ) { - $f->{fh}->close(); - } # # Make sure the fileSize was correct. See above for comments about @@ -291,27 +288,37 @@ sub write my($fh, $fileName); $a->{fileSize} = $a->{nWrite}; - if ( $a->{fileName} =~ /(.*)\// ) { - $fileName = $1; - } else { - $fileName = "."; - } - # - # Find a unique target temporary file name - # - my $i = 0; - while ( -f "$fileName/t$$.$i" ) { - $i++; + if ( defined($a->{fhOut}) ) { + if ( $a->{fileName} =~ /(.*)\// ) { + $fileName = $1; + } else { + $fileName = "."; + } + # + # Find a unique target temporary file name + # + my $i = 0; + while ( -f "$fileName/t$$.$i" ) { + $i++; + } + $fileName = "$fileName/t$$.$i"; + $a->{fhOut}->close(); + if ( !rename($a->{fileName}, $fileName) + || !defined($fh = BackupPC::FileZIO->open($fileName, 0, + $a->{compress})) ) { + push(@{$a->{errors}}, "Can't rename $a->{fileName} -> $fileName" + . " or open during size fixup\n"); + } + } elsif ( defined($a->{files}) && defined($a->{files}[0]) ) { + # + # We haven't written anything yet, so just use the + # compare file to copy from. + # + $fh = $a->{files}[0]->{fh}; + $fh->rewind; } - $fileName = "$fileName/t$$.$i"; - $a->{fhOut}->close(); - if ( !rename($a->{fileName}, $fileName) - || !defined($fh = BackupPC::FileZIO->open($fileName, 0, - $a->{compress})) ) { - push(@{$a->{errors}}, "Can't rename $a->{fileName} -> $fileName" - . " or open during size fixup\n"); - } else { + if ( defined($fh) ) { my $poolWrite = BackupPC::PoolWrite->new($a->{bpc}, $a->{fileName}, $a->{fileSize}, $a->{compress}); my $nRead = 0; @@ -331,7 +338,7 @@ sub write $nRead += $thisRead; } $fh->close; - unlink($fileName); + unlink($fileName) if ( defined($fileName) ); if ( @{$a->{errors}} ) { $poolWrite->close; return (0, $a->{digest}, -s $a->{fileName}, $a->{errors}); @@ -341,6 +348,13 @@ sub write } } + # + # Close the compare files + # + foreach my $f ( @{$a->{files}} ) { + $f->{fh}->close(); + } + if ( $a->{fileSize} == 0 ) { # # Simply create an empty file @@ -394,6 +408,23 @@ sub close return $a->write(undef); } +# +# Abort a pool write +# +sub abort +{ + my($a) = @_; + + if ( defined($a->{fhOut}) ) { + $a->{fhOut}->close(); + unlink($a->{fileName}); + } + foreach my $f ( @{$a->{files}} ) { + $f->{fh}->close(); + } + $a->{files} = []; +} + # # Copy $nBytes from files $fhIn to $fhOut. # diff --git a/lib/BackupPC/Xfer/Archive.pm b/lib/BackupPC/Xfer/Archive.pm index 841f016..613ae45 100644 --- a/lib/BackupPC/Xfer/Archive.pm +++ b/lib/BackupPC/Xfer/Archive.pm @@ -137,6 +137,10 @@ sub errStr return $t->{_errStr}; } +sub abort +{ +} + sub xferPid { my($t) = @_; diff --git a/lib/BackupPC/Xfer/Rsync.pm b/lib/BackupPC/Xfer/Rsync.pm index e8b605a..d629cd3 100644 --- a/lib/BackupPC/Xfer/Rsync.pm +++ b/lib/BackupPC/Xfer/Rsync.pm @@ -278,7 +278,7 @@ sub start # $t->{rsyncClientCmd} = $rsyncClientCmd; $t->{rs} = File::RsyncP->new({ - logLevel => $conf->{RsyncLogLevel}, + logLevel => $t->{logLevel} || $conf->{RsyncLogLevel}, rsyncCmd => sub { $bpc->verbose(0); $bpc->cmdExecOrEval($rsyncClientCmd, $args); @@ -287,11 +287,25 @@ sub start rsyncArgs => $rsyncArgs, timeout => $conf->{ClientTimeout}, doPartial => defined($t->{partialNum}) ? 1 : undef, - logHandler => sub { - my($str) = @_; - $str .= "\n"; - $t->{XferLOG}->write(\$str); - }, + logHandler => + sub { + my($str) = @_; + $str .= "\n"; + $t->{XferLOG}->write(\$str); + if ( $str =~ /^Remote\[1\]: read errors mapping "(.*)"/ ) { + # + # Files with read errors (eg: region locked files + # on WinXX) are filled with 0 by rsync. Remember + # them and delete them later. + # + my $badFile = $1; + $badFile =~ s/^\/+//; + push(@{$t->{badFiles}}, { + share => $t->{shareName}, + file => $badFile + }); + } + }, pidHandler => sub { $t->{pidHandler}(@_); }, @@ -300,7 +314,8 @@ sub start bpc => $t->{bpc}, conf => $t->{conf}, backups => $t->{backups}, - logLevel => $conf->{RsyncLogLevel}, + logLevel => $t->{logLevel} + || $conf->{RsyncLogLevel}, logHandler => sub { my($str) = @_; $str .= "\n"; @@ -427,6 +442,15 @@ sub run } } +sub abort +{ + my($t, $reason) = @_; + my $rs = $t->{rs}; + + $rs->abort($reason); + return 1; +} + sub setSelectMask { my($t, $FDreadRef) = @_; diff --git a/lib/BackupPC/RsyncDigest.pm b/lib/BackupPC/Xfer/RsyncDigest.pm similarity index 96% rename from lib/BackupPC/RsyncDigest.pm rename to lib/BackupPC/Xfer/RsyncDigest.pm index 3dd0896..def46a0 100644 --- a/lib/BackupPC/RsyncDigest.pm +++ b/lib/BackupPC/Xfer/RsyncDigest.pm @@ -1,10 +1,10 @@ #============================================================= -*-perl-*- # -# BackupPC::RsyncDigest package +# BackupPC::Xfer::RsyncDigest package # # DESCRIPTION # -# This library defines a BackupPC::RsyncDigest class for computing +# This library defines a BackupPC::Xfer::RsyncDigest class for computing # and caching rsync checksums. # # AUTHOR @@ -35,7 +35,7 @@ # #======================================================================== -package BackupPC::RsyncDigest; +package BackupPC::Xfer::RsyncDigest; use strict; @@ -166,7 +166,7 @@ sub digestStart close($fh); $fio->digestAdd($fileName, $blockSize - || BackupPC::RsyncDigest->blockSize($fileSize, + || BackupPC::Xfer::RsyncDigest->blockSize($fileSize, $defBlkSize), $checksumSeed); # @@ -211,8 +211,9 @@ sub digestStart # or blocksize doesn't match. Open the file and prepare to # compute the checksums. # - $blockSize = BackupPC::RsyncDigest->blockSize($fileSize, $defBlkSize) - if ( $blockSize == 0 ); + $blockSize + = BackupPC::Xfer::RsyncDigest->blockSize($fileSize, $defBlkSize) + if ( $blockSize == 0 ); $fio->{checksumSeed} = $checksumSeed; $fio->{blockSize} = $blockSize; $fio->{fh} = BackupPC::FileZIO->open($fileName, 0, $compress); diff --git a/lib/BackupPC/Xfer/RsyncFileIO.pm b/lib/BackupPC/Xfer/RsyncFileIO.pm index db43a5d..3a0e826 100644 --- a/lib/BackupPC/Xfer/RsyncFileIO.pm +++ b/lib/BackupPC/Xfer/RsyncFileIO.pm @@ -12,7 +12,7 @@ # #======================================================================== # -# Version 2.1.0_CVS, released 8 Feb 2004. +# Version 2.1.0_CVS, released 13 Mar 2004. # # See http://backuppc.sourceforge.net. # @@ -24,9 +24,8 @@ use strict; use File::Path; use BackupPC::Attrib qw(:all); use BackupPC::View; -use BackupPC::RsyncDigest; +use BackupPC::Xfer::RsyncDigest; use BackupPC::PoolWrite; -use Data::Dumper; use constant S_IFMT => 0170000; # type of file use constant S_IFDIR => 0040000; # directory @@ -112,9 +111,9 @@ sub csumStart $fio->csumEnd if ( defined($fio->{csum}) ); return -1 if ( $attr->{type} != BPC_FTYPE_FILE ); (my $err, $fio->{csum}, my $blkSize) - = BackupPC::RsyncDigest->digestStart($attr->{fullPath}, $attr->{size}, - 0, $defBlkSize, $fio->{checksumSeed}, $needMD4, - $attr->{compress}, 1); + = BackupPC::Xfer::RsyncDigest->digestStart($attr->{fullPath}, + $attr->{size}, 0, $defBlkSize, $fio->{checksumSeed}, + $needMD4, $attr->{compress}, 1); if ( $err ) { $fio->log("Can't get rsync digests from $attr->{fullPath}" . " (err=$err, name=$f->{name})"); @@ -878,6 +877,7 @@ sub fileDeltaRxDone { my($fio, $md4) = @_; my $name = $1 if ( $fio->{rxFile}{name} =~ /(.*)/ ); + my $ret; close($fio->{rxInFd}) if ( defined($fio->{rxInFd}) ); unlink("$fio->{outDirSh}RStmp") if ( -f "$fio->{outDirSh}RStmp" ); @@ -899,7 +899,7 @@ sub fileDeltaRxDone # fetch the md4 file digest, not the block digests. # my($err, $csum, $blkSize) - = BackupPC::RsyncDigest->digestStart( + = BackupPC::Xfer::RsyncDigest->digestStart( $attr->{fullPath}, $attr->{size}, 0, 2048, $fio->{checksumSeed}, 1, $attr->{compress}); @@ -935,6 +935,8 @@ sub fileDeltaRxDone $fio->{rxOutFd}->close; unlink($fio->{rxOutFile}); } + delete($fio->{rxFile}); + delete($fio->{rxOutFile}); return 1; } } @@ -976,32 +978,34 @@ sub fileDeltaRxDone if ( !link($attr->{fullPath}, $rxOutFile) ) { $fio->log("Unable to link $attr->{fullPath} to $rxOutFile"); $fio->{stats}{errorCnt}++; - return -1; - } - # - # Cumulate the stats - # - $fio->{stats}{TotalFileCnt}++; - $fio->{stats}{TotalFileSize} += $fio->{rxSize}; - $fio->{stats}{ExistFileCnt}++; - $fio->{stats}{ExistFileSize} += $fio->{rxSize}; - $fio->{stats}{ExistFileCompSize} += -s $rxOutFile; - $fio->{rxFile}{size} = $fio->{rxSize}; - return $fio->attribSet($fio->{rxFile}); + $ret = -1; + } else { + # + # Cumulate the stats + # + $fio->{stats}{TotalFileCnt}++; + $fio->{stats}{TotalFileSize} += $fio->{rxSize}; + $fio->{stats}{ExistFileCnt}++; + $fio->{stats}{ExistFileSize} += $fio->{rxSize}; + $fio->{stats}{ExistFileCompSize} += -s $rxOutFile; + $fio->{rxFile}{size} = $fio->{rxSize}; + $ret = $fio->attribSet($fio->{rxFile}); + } } - } - if ( defined($fio->{rxOutFd}) ) { + } else { my $exist = $fio->processClose($fio->{rxOutFd}, $fio->{rxOutFileRel}, $fio->{rxSize}, 1); $fio->logFileAction($exist ? "pool" : "create", $fio->{rxFile}) if ( $fio->{logLevel} >= 1 ); $fio->{rxFile}{size} = $fio->{rxSize}; - return $fio->attribSet($fio->{rxFile}); + $ret = $fio->attribSet($fio->{rxFile}); } delete($fio->{rxDigest}); delete($fio->{rxInData}); - return; + delete($fio->{rxFile}); + delete($fio->{rxOutFile}); + return $ret; } # @@ -1101,10 +1105,22 @@ sub finish { my($fio, $isChild) = @_; + # + # If we are aborting early, remove the last file since + # it was not complete + # + if ( $isChild && defined($fio->{rxFile}) ) { + unlink("$fio->{outDirSh}RStmp") if ( -f "$fio->{outDirSh}RStmp" ); + if ( defined($fio->{rxFile}) ) { + unlink($fio->{rxOutFile}); + $fio->log("finish: removing in-process file $fio->{rxFile}{name}"); + } + } + # # Flush the attributes if this is the child # - $fio->attribWrite(undef); + $fio->attribWrite(undef) if ( $isChild ); } #sub is_tainted diff --git a/lib/BackupPC/Xfer/Smb.pm b/lib/BackupPC/Xfer/Smb.pm index 7cee1ef..3d6dd52 100644 --- a/lib/BackupPC/Xfer/Smb.pm +++ b/lib/BackupPC/Xfer/Smb.pm @@ -29,7 +29,7 @@ # #======================================================================== # -# Version 2.1.0_CVS, released 8 Feb 2004. +# Version 2.1.0_CVS, released 13 Mar 2004. # # See http://backuppc.sourceforge.net. # @@ -231,12 +231,14 @@ sub readOutput # ignore the log file time stamps from smbclient introduced # in version 3.0.0 - don't even write them to the log file. # - next if ( m{^\[\d+/\d+/\d+ +\d+:\d+:\d+.*\] +(client/cli|lib/util_unistr).*\(\d+\)} ); - $t->{XferLOG}->write(\"$_\n"); + if ( m{^\[\d+/\d+/\d+ +\d+:\d+:\d+.*\] +(client/cli|lib/util_unistr).*\(\d+\)} ) { + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 5 ); + next; + } # # refresh our inactivity alarm # - alarm($conf->{ClientTimeout}); + alarm($conf->{ClientTimeout}) if ( !$t->{abort} ); $t->{lastOutputLine} = $_ if ( !/^$/ ); # # This section is highly dependent on the version of smbclient. @@ -250,15 +252,20 @@ sub readOutput $fileName =~ s/^\/*//; $t->{byteCnt} += $sambaFileSize; $t->{fileCnt}++; + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 2 ); } elsif ( /restore tar file (.*) of size (\d+) bytes/ ) { $t->{byteCnt} += $2; $t->{fileCnt}++; + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 ); } elsif ( /^\s*tar: dumped \d+ files/ ) { $t->{xferOK} = 1; + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); } elsif ( /^\s*tar: restored \d+ files/ ) { $t->{xferOK} = 1; + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); } elsif ( /^\s*read_socket_with_timeout: timeout read. /i ) { $t->{hostAbort} = 1; + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); } elsif ( /^code 0 listing / || /^\s*code 0 opening / || /^\s*abandoning restore/i @@ -272,6 +279,7 @@ sub readOutput $t->{XferLOG}->write(\"This backup will fail because: $_\n"); $t->{hostError} = $_; } + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); } elsif ( /^\s*NT_STATUS_ACCESS_DENIED listing (.*)/ || /^\s*ERRDOS - ERRnoaccess \(Access denied\.\) listing (.*)/ ) { my $badDir = $1; @@ -283,6 +291,9 @@ sub readOutput $t->{XferLOG}->write(\"This backup will fail because: $_\n"); $t->{hostError} ||= $_; } + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); + } elsif ( /^\s*directory \\/i ) { + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 2 ); } elsif ( /smb: \\>/ || /^\s*added interface/i || /^\s*tarmode is now/i @@ -290,13 +301,13 @@ sub readOutput || /^\s*Domain=/i || /^\([\d\.]* kb\/s\) \(average [\d\.]* kb\/s\)$/i || /^\s*Getting files newer than/i - || /^\s*directory \\/i || /^\s*restore directory \\/i || /^\s*Output is \/dev\/null/i || /^\s*Timezone is/i || /^\s*creating lame (up|low)case table/i ) { # ignore these messages + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 ); } else { $t->{xferErrCnt}++; $t->{xferBadShareCnt}++ if ( /^ERRDOS - ERRbadshare/ ); @@ -329,11 +340,20 @@ sub readOutput file => $badFile }); } + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 ); } } return 1; } +sub abort +{ + my($t, $reason) = @_; + + $t->{abort} = 1; + $t->{abortReason} = $reason; +} + sub setSelectMask { my($t, $FDreadRef) = @_; diff --git a/lib/BackupPC/Xfer/Tar.pm b/lib/BackupPC/Xfer/Tar.pm index 576b5ae..8c05492 100644 --- a/lib/BackupPC/Xfer/Tar.pm +++ b/lib/BackupPC/Xfer/Tar.pm @@ -222,17 +222,19 @@ sub readOutput while ( $t->{tarOut} =~ /(.*?)[\n\r]+(.*)/s ) { $_ = $1; $t->{tarOut} = $2; - $t->{XferLOG}->write(\"$_\n"); # # refresh our inactivity alarm # - alarm($conf->{ClientTimeout}); + alarm($conf->{ClientTimeout}) if ( !$t->{abort} ); $t->{lastOutputLine} = $_ if ( !/^$/ ); if ( /^Total bytes written: / ) { + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 ); $t->{xferOK} = 1; } elsif ( /^\./ ) { + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 2 ); $t->{fileCnt}++; } else { + $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 ); $t->{xferErrCnt}++; # # If tar encounters a minor error, it will exit with a non-zero @@ -241,11 +243,34 @@ sub readOutput # $t->{tarBadExitOk} = 1 if ( $t->{xferOK} && /Error exit delayed from previous / ); + # + # Also remember files that had read errors + # + if ( /: \.\/(.*): Read error at byte / ) { + my $badFile = $1; + push(@{$t->{badFiles}}, { + share => $t->{shareName}, + file => $badFile + }); + } + } } return 1; } +sub abort +{ + my($t, $reason) = @_; + my @xferPid = $t->xferPid; + + $t->{abort} = 1; + $t->{abortReason} = $reason; + if ( @xferPid ) { + kill($t->{bpc}->sigName2num("INT"), @xferPid); + } +} + sub setSelectMask { my($t, $FDreadRef) = @_; diff --git a/makeDist b/makeDist index 7492a03..0a54477 100755 --- a/makeDist +++ b/makeDist @@ -16,7 +16,7 @@ # Craig Barratt # # COPYRIGHT -# Copyright (C) 2001-2003 Craig Barratt +# Copyright (C) 2001-2004 Craig Barratt # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -43,7 +43,7 @@ use Getopt::Std; umask(0022); my $Version = "2.1.0_CVS"; -my $ReleaseDate = "8 Feb 2004"; +my $ReleaseDate = "13 Mar 2004"; my $DistDir = "dist/BackupPC-$Version"; my @PerlSrc = qw( @@ -96,6 +96,7 @@ my @PerlSrc = qw( lib/BackupPC/Xfer/Smb.pm lib/BackupPC/Xfer/Tar.pm lib/BackupPC/Xfer/Rsync.pm + lib/BackupPC/Xfer/RsyncDigest.pm lib/BackupPC/Xfer/RsyncFileIO.pm lib/BackupPC/Zip/FileMember.pm cgi-bin/BackupPC_Admin @@ -130,6 +131,7 @@ $ConfVars->{CgiImageDir} = 2; $ConfVars->{BlackoutHourBegin} = 2; $ConfVars->{BlackoutHourEnd} = 2; $ConfVars->{BlackoutWeekDays} = 2; +$ConfVars->{RsyncLogLevel} = 2; foreach my $file ( @PerlSrc ) { $errCnt += CheckConfigParams($file, $ConfVars, 1); -- 2.20.1