X-Git-Url: http://git.rot13.org/?p=BackupPC.git;a=blobdiff_plain;f=bin%2FBackupPC_restore;h=8af7550d4f1c1f0f792ac9d26aea53ea93a96cdf;hp=7746a361110cdeb2f376c3cb0ca8950c0419c401;hb=refs%2Ftags%2Fv3_0_0beta1;hpb=0697368bbcef14908cd4684cf07744dc840464de diff --git a/bin/BackupPC_restore b/bin/BackupPC_restore index 7746a36..8af7550 100755 --- a/bin/BackupPC_restore +++ b/bin/BackupPC_restore @@ -1,4 +1,4 @@ -#!/bin/perl -T +#!/bin/perl #============================================================= -*-perl-*- # # BackupPC_restore: Restore files to a client. @@ -11,7 +11,7 @@ # Craig Barratt # # COPYRIGHT -# Copyright (C) 2001 Craig Barratt +# Copyright (C) 2001-2003 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 @@ -29,19 +29,22 @@ # #======================================================================== # -# Version 2.0.0_CVS, released 18 Jan 2003. +# Version 3.0.0beta1, released 30 Jul 2006. # # See http://backuppc.sourceforge.net. # #======================================================================== use strict; +no utf8; use lib "/usr/local/BackupPC/lib"; use BackupPC::Lib; use BackupPC::FileZIO; use BackupPC::Xfer::Smb; use BackupPC::Xfer::Tar; use BackupPC::Xfer::Rsync; +use BackupPC::Xfer::BackupPCd; +use Socket; use File::Path; use Getopt::Std; @@ -76,10 +79,10 @@ $reqFileName = $1; my $startTime = time(); -my $Hosts = $bpc->HostInfoRead(); +my $Hosts = $bpc->HostInfoRead($client); my $Dir = "$TopDir/pc/$client"; -my $xferPid = -1; +my @xferPid = (); my $tarPid = -1; # @@ -88,12 +91,39 @@ my $tarPid = -1; $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; +my $Pid = $$; mkpath($Dir, 0, 0777) if ( !-d $Dir ); if ( !-f "$Dir/LOCK" ) { open(LOCK, ">", "$Dir/LOCK") && close(LOCK); } -open(LOG, ">>", "$Dir/LOG"); + +my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +my $logPath = sprintf("$Dir/LOG.%02d%04d", $mon + 1, $year + 1900); + +if ( !-f $logPath ) { + # + # Compress and prune old log files + # + my $lastLog = $Conf{MaxOldPerPCLogFiles} - 1; + foreach my $file ( $bpc->sortedPCLogFiles($client) ) { + if ( $lastLog <= 0 ) { + unlink($file); + next; + } + $lastLog--; + next if ( $file =~ /\.z$/ || !$Conf{CompressLevel} ); + BackupPC::FileZIO->compressCopy($file, + "$file.z", + undef, + $Conf{CompressLevel}, 1); + } +} +open(LOG, ">>", $logPath); select(LOG); $| = 1; select(STDOUT); # @@ -178,12 +208,16 @@ if ( (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { # # Setup file extension for compression and open RestoreLOG output file # -$Conf{CompressLevel} = 0 if ( !BackupPC::FileZIO->compOk ); +if ( $Conf{CompressLevel} && !BackupPC::FileZIO->compOk ) { + $stat{hostError} = "Compress:Zlib not found"; + exit(RestoreCleanup($client)); +} my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; my $RestoreLOG = BackupPC::FileZIO->open("$Dir/RestoreLOG$fileExt", 1, $Conf{CompressLevel}); my $tarCreateFileCnt = 0; my $tarCreateByteCnt = 0; +my $tarCreateDirCnt = 0; my $tarCreateErrCnt = 1; # assume not ok until we learn otherwise my $tarCreateErr; my($logMsg, $xfer); @@ -196,6 +230,10 @@ local(*RH, *WH); # Run an optional pre-restore command # UserCommandRun("RestorePreUserCmd"); +if ( $? && $Conf{UserCmdCheckStatus} ) { + $stat{hostError} = "RestorePreUserCmd returned error status $?"; + exit(RestoreCleanup($client)); +} $NeedPostCmd = 1; if ( $Conf{XferMethod} eq "tar" ) { @@ -213,6 +251,16 @@ if ( $Conf{XferMethod} eq "tar" ) { $stat{hostError} = $errStr; exit(RestoreCleanup($client)); } +} elsif ( $Conf{XferMethod} eq "backuppcd" ) { + # + # Use backuppcd as the transport program. + # + if ( !defined($xfer = BackupPC::Xfer::BackupPCd->new($bpc)) ) { + my $errStr = BackupPC::Xfer::BackupPCd->errStr; + UserCommandRun("RestorePostUserCmd") if ( $NeedPostCmd ); + $stat{hostError} = $errStr; + exit(RestoreCleanup($client)); + } } else { # # Default is to use smbclient (smb) as the transport program. @@ -223,13 +271,23 @@ my $useTar = $xfer->useTar; if ( $useTar ) { # - # Create a pipe to connect BackupPC_tarCreate to the transport program - # (smbclient, tar, etc). + # Create a socketpair to connect BackupPC_tarCreate to the transport + # program (smbclient, tar, etc). # WH is the write handle for writing, provided to BackupPC_tarCreate # and RH is the other end of the pipe for reading provided to the # transport program. # - pipe(RH, WH); + if ( socketpair(RH, WH, AF_UNIX, SOCK_STREAM, PF_UNSPEC) ) { + shutdown(RH, 1); # no writing to this socket + shutdown(WH, 0); # no reading from this socket + setsockopt(RH, SOL_SOCKET, SO_RCVBUF, 8 * 65536); + setsockopt(WH, SOL_SOCKET, SO_SNDBUF, 8 * 65536); + } else { + # + # Default to pipe() if socketpair() doesn't work. + # + pipe(RH, WH); + } } # @@ -246,6 +304,7 @@ my $xferArgs = { pipeWH => *WH, XferLOG => $RestoreLOG, XferMethod => $Conf{XferMethod}, + logLevel => $Conf{XferLogLevel}, bkupSrcHost => $RestoreReq{hostSrc}, bkupSrcShare => $RestoreReq{shareSrc}, bkupSrcNum => $RestoreReq{num}, @@ -253,6 +312,7 @@ my $xferArgs = { pathHdrSrc => $RestoreReq{pathHdrSrc}, pathHdrDest => $RestoreReq{pathHdrDest}, fileList => $RestoreReq{fileList}, + pidHandler => \&pidHandler, }; $xfer->args($xferArgs); @@ -291,10 +351,10 @@ if ( $useTar ) { @tarPathOpts, @{$RestoreReq{fileList}}, ); - my $logMsg = "Running: " + my $runMsg = "Running: " . $bpc->execCmd2ShellCmd("$BinDir/BackupPC_tarCreate", @tarArgs) . "\n"; - $RestoreLOG->write(\$logMsg); + $RestoreLOG->write(\$runMsg); if ( !defined($tarPid = open(TAR, "-|")) ) { close(WH); # FIX: need to cleanup xfer @@ -302,6 +362,7 @@ if ( $useTar ) { $stat{hostError} = "Can't fork to run tar"; exit(RestoreCleanup($client)); } + binmode(TAR); if ( !$tarPid ) { # # This is the tarCreate child. Clone STDERR to STDOUT, @@ -312,6 +373,7 @@ if ( $useTar ) { open(STDERR, ">&STDOUT"); close(STDOUT); open(STDOUT, ">&WH"); + alarm(0); exec("$BinDir/BackupPC_tarCreate", @tarArgs); print(LOG $bpc->timeStamp, "can't exec $BinDir/BackupPC_tarCreate\n"); # FIX: need to cleanup xfer @@ -323,9 +385,12 @@ if ( $useTar ) { # close(WH); - $xferPid = $xfer->xferPid; - print(LOG $bpc->timeStamp, $logMsg, " (tarPid=$tarPid, xferPid=$xferPid)\n"); - print("started restore, tarPid=$tarPid, xferPid=$xferPid\n"); + @xferPid = $xfer->xferPid; + + print(LOG $bpc->timeStamp, $logMsg, "\n"); + print("started_restore\n"); + + pidHandler(@xferPid); # # Parse the output of the transfer program and BackupPC_tarCreate @@ -359,6 +424,7 @@ if ( $useTar ) { if ( /^Done: (\d+) files, (\d+) bytes, (\d+) dirs, (\d+) specials, (\d+) errors/ ) { $tarCreateFileCnt = $1; $tarCreateByteCnt = $2; + $tarCreateDirCnt = $3; $tarCreateErrCnt = $5; } } @@ -383,8 +449,8 @@ if ( $useTar ) { # # otherwise the xfer module does everything for us # - print(LOG $bpc->timeStamp, "Starting restore (tarPid=-1, xferPid=-1)\n"); - print("started restore, tarPid=-1, xferPid=-1\n"); + print(LOG $bpc->timeStamp, $logMsg . "\n"); + print("started_restore\n"); ($tarCreateFileCnt, $tarCreateByteCnt, $tarCreateErrCnt, $tarCreateErr) = $xfer->run(); } @@ -417,7 +483,8 @@ exit(RestoreCleanup($client)); sub CorrectHostCheck { my($hostIP, $host) = @_; - return if ( $hostIP eq $host && !$Conf{FixedIPNetBiosNameCheck} ); + return if ( $hostIP eq $host && !$Conf{FixedIPNetBiosNameCheck} + || $Conf{NmbLookupCmd} eq "" ); my($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($hostIP); return "host $host has mismatching netbios name $netBiosHost" if ( $netBiosHost ne $host ); @@ -428,24 +495,29 @@ sub catch_signal { my $signame = shift; + # + # Children quit quietly on ALRM + # + exit(1) if ( $Pid != $$ && $signame eq "ALRM" ); + + # + # Ignore signals in children + # + return if ( $Pid != $$ ); + # # Note: needs to be tested for each kind of XferMethod # print(LOG $bpc->timeStamp, "cleaning up after signal $signame\n"); - if ( $xferPid > 0 ) { - if ( kill(2, $xferPid) <= 0 ) { - sleep(1); - kill(9, $xferPid); - } - } - if ( $tarPid > 0 ) { - if ( kill(2, $tarPid) <= 0 ) { - sleep(1); - kill(9, $tarPid); - } - } + $SIG{$signame} = 'IGNORE'; + $RestoreLOG->write(\"exiting after signal $signame\n"); $stat{xferOK} = 0; - $stat{hostError} = "aborted by signal $signame"; + if ( $signame eq "INT" ) { + $stat{hostError} = "aborted by user (signal=$signame)"; + } else { + $stat{hostError} = "aborted by signal=$signame"; + } + exit(RestoreCleanup($client)); } # @@ -462,15 +534,19 @@ sub RestoreCleanup # # kill off the tranfer program, first nicely then forcefully # - kill(2, $xferPid) if ( $xferPid > 0 ); - sleep(1); - kill(9, $xferPid) if ( $xferPid > 0 ); + if ( @xferPid ) { + kill($bpc->sigName2num("INT"), @xferPid); + sleep(1); + kill($bpc->sigName2num("KILL"), @xferPid); + } # # kill off the tar process, first nicely then forcefully # - kill(2, $tarPid) if ( $tarPid > 0 ); - sleep(1); - kill(9, $tarPid) if ( $tarPid > 0 ); + if ( $tarPid > 0 ) { + kill($bpc->sigName2num("INT"), $tarPid); + sleep(1); + kill($bpc->sigName2num("KILL"), $tarPid); + } } my $lastNum = -1; @@ -492,9 +568,14 @@ sub RestoreCleanup # # Run an optional post-restore command # - UserCommandRun("RestorePostUserCmd") if ( $NeedPostCmd ); + if ( $NeedPostCmd ) { + UserCommandRun("RestorePostUserCmd"); + if ( $? && $Conf{UserCmdCheckStatus} ) { + $stat{hostError} = "RestorePostUserCmd returned error status $?"; + $stat{xferOK} = 0; + } + } - $RestoreLOG->close() if ( defined($RestoreLOG) ); rename("$Dir/RestoreLOG$fileExt", "$Dir/RestoreLOG.$lastNum$fileExt"); rename("$Dir/$reqFileName", "$Dir/RestoreInfo.$lastNum"); my $endTime = time(); @@ -515,8 +596,12 @@ sub RestoreCleanup if ( $stat{hostAbort} && $stat{hostError} eq "" ) { $stat{hostError} = "lost network connection during restore"; } + $RestoreLOG->write(\"restore failed: $stat{hostError}\n") + if ( defined($RestoreLOG) ); } + $RestoreLOG->close() if ( defined($RestoreLOG) ); + # # Add the new restore information to the restore file # @@ -542,34 +627,60 @@ sub RestoreCleanup $bpc->RestoreInfoWrite($client, @Restores); if ( !$stat{xferOK} ) { - print(LOG $bpc->timeStamp, "Restore aborted ($stat{hostError})\n"); + print(LOG $bpc->timeStamp, "restore failed ($stat{hostError})\n"); print("restore failed: $stat{hostError}\n"); return 1; } else { + $stat{xferErrCnt} ||= 0; + print(LOG $bpc->timeStamp, "restore $lastNum complete" + . " ($tarCreateFileCnt files, $tarCreateByteCnt bytes," + . " $tarCreateDirCnt dirs, $stat{xferErrCnt} xferErrs)\n"); print("restore complete\n"); return; } } +# +# The Xfer method might tell us from time to time about processes +# it forks. We tell BackupPC about this (for status displays) and +# keep track of the pids in case we cancel the backup +# +sub pidHandler +{ + @xferPid = @_; + @xferPid = grep(/./, @xferPid); + return if ( !@xferPid && $tarPid < 0 ); + my @pids = @xferPid; + push(@pids, $tarPid) if ( $tarPid > 0 ); + my $str = join(",", @pids); + $RestoreLOG->write(\"Xfer PIDs are now $str\n") if ( defined($RestoreLOG) ); + print("xferPids $str\n"); +} + # # Run an optional pre- or post-dump command # sub UserCommandRun { - my($type) = @_; + my($cmdType) = @_; - return if ( !defined($Conf{$type}) ); + return if ( !defined($Conf{$cmdType}) ); my $vars = { xfer => $xfer, + client => $client, host => $host, hostIP => $hostIP, share => $RestoreReq{shareDest}, XferMethod => $Conf{XferMethod}, + sshPath => $Conf{SshPath}, LOG => *LOG, + user => $Hosts->{$client}{user}, + moreUsers => $Hosts->{$client}{moreUsers}, XferLOG => $RestoreLOG, stat => \%stat, - xferOK => $stat{xferOK}, - type => $type, + xferOK => $stat{xferOK} || 0, + hostError => $stat{hostError}, + type => "restore", bkupSrcHost => $RestoreReq{hostSrc}, bkupSrcShare => $RestoreReq{shareSrc}, bkupSrcNum => $RestoreReq{num}, @@ -577,9 +688,10 @@ sub UserCommandRun pathHdrSrc => $RestoreReq{pathHdrSrc}, pathHdrDest => $RestoreReq{pathHdrDest}, fileList => $RestoreReq{fileList}, + cmdType => $cmdType, }; - my $cmd = $bpc->cmdVarSubstitute($Conf{$type}, $vars); - $RestoreLOG->write(\"Executing $type: @$cmd\n"); + my $cmd = $bpc->cmdVarSubstitute($Conf{$cmdType}, $vars); + $RestoreLOG->write(\"Executing $cmdType: @$cmd\n"); # # Run the user's command, dumping the stdout/stderr into the # Xfer log file. Also supply the optional $vars and %Conf in