X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=bin%2FBackupPC_dump;h=8d27db3496a76ef67507e717179c775175255de9;hb=e4a233559926fe41888b2f6c5cc716401b715162;hp=fcb0bd76d73a81d9949b8e162654202f72eaf977;hpb=3dc33e5f39430031766adf3c5bb2ffc649ee9100;p=BackupPC.git diff --git a/bin/BackupPC_dump b/bin/BackupPC_dump index fcb0bd7..8d27db3 100755 --- a/bin/BackupPC_dump +++ b/bin/BackupPC_dump @@ -1,11 +1,11 @@ #!/bin/perl -T #============================================================= -*-perl-*- # -# BackupPC_dump: Dump a single PC. +# BackupPC_dump: Dump a single client. # # DESCRIPTION # -# Usage: BackupPC_dump [-i] [-f] [-d] [-e] +# Usage: BackupPC_dump [-i] [-f] [-d] [-e] [-v] # # Flags: # @@ -14,33 +14,36 @@ # # -f Do a full dump, overriding any scheduling. # -# -d Host is a DHCP pool address, so initially we have no -# idea which machine this actually is. BackupPC_dump -# determines the actual PC host name by using the NetBios -# name. +# -d Host is a DHCP pool address, and the client argument +# just an IP address. We lookup the NetBios name from +# the IP address. # -# -e Just do an dump expiry check for the host. Don't do anything else. # This is used periodically by BackupPC to make sure that dhcp hosts -# have correctly expired old backups. Without this, dhcp hosts that -# are no longer on the network will not expire old backups. +# -e Just do an dump expiry check for the client. Don't do anything +# else. This is used periodically by BackupPC to make sure that +# dhcp hosts have correctly expired old backups. Without this, +# dhcp hosts that are no longer on the network will not expire +# old backups. # -# BackupPC_dump is run periodically by BackupPC to backup $host. -# The file $TopDir/pc/$host/backups is read to decide whether a +# -v verbose. for manual usage: prints failure reasons in more detail. +# +# BackupPC_dump is run periodically by BackupPC to backup $client. +# The file $TopDir/pc/$client/backups is read to decide whether a # full or incremental backup needs to be run. If no backup is -# scheduled, or a ping to $host fails, then BackupPC_dump quits. +# scheduled, or a ping to $client fails, then BackupPC_dump quits. # # The backup is done using the selected XferMethod (smb, tar, rsync etc), -# extracting the dump into $TopDir/pc/$host/new. The xfer output is -# put into $TopDir/pc/$host/XferLOG. +# extracting the dump into $TopDir/pc/$client/new. The xfer output is +# put into $TopDir/pc/$client/XferLOG. # # If the dump succeeds (based on parsing the output of the XferMethod): -# - $TopDir/pc/$host/new is renamed to $TopDir/pc/$host/nnn, where +# - $TopDir/pc/$client/new is renamed to $TopDir/pc/$client/nnn, where # nnn is the next sequential dump number. -# - $TopDir/pc/$host/XferLOG is renamed to $TopDir/pc/$host/XferLOG.nnn. -# - $TopDir/pc/$host/backups is updated. +# - $TopDir/pc/$client/XferLOG is renamed to $TopDir/pc/$client/XferLOG.nnn. +# - $TopDir/pc/$client/backups is updated. # # If the dump fails: -# - $TopDir/pc/$host/new is moved to $TopDir/trash for later removal. -# - $TopDir/pc/$host/XferLOG is renamed to $TopDir/pc/$host/XferLOG.bad +# - $TopDir/pc/$client/new is moved to $TopDir/trash for later removal. +# - $TopDir/pc/$client/XferLOG is renamed to $TopDir/pc/$client/XferLOG.bad # for later viewing. # # BackupPC_dump communicates to BackupPC via printing to STDOUT. @@ -67,7 +70,7 @@ # #======================================================================== # -# Version 1.6.0_CVS, released 10 Dec 2002. +# Version 2.0.0beta2, released 13 Apr 2003. # # See http://backuppc.sourceforge.net. # @@ -92,46 +95,71 @@ my $TopDir = $bpc->TopDir(); my $BinDir = $bpc->BinDir(); my %Conf = $bpc->Conf(); my $NeedPostCmd; +my $Hosts; $bpc->ChildInit(); my %opts; -getopts("defi", \%opts); -if ( @ARGV != 1 ) { - print("usage: $0 [-d] [-e] [-f] [-i] \n"); +if ( !getopts("defiv", \%opts) || @ARGV != 1 ) { + print("usage: $0 [-d] [-e] [-f] [-i] [-v] \n"); exit(1); } -if ( $ARGV[0] !~ /^([\w\.-]+)$/ ) { - print("$0: bad host name '$ARGV[0]'\n"); +if ( $ARGV[0] !~ /^([\w\.\s-]+)$/ ) { + print("$0: bad client name '$ARGV[0]'\n"); exit(1); } -my $hostIP = $1; -my($host, $user); +my $client = $1; # BackupPC's client name (might not be real host name) +my $hostIP; # this is the IP address +my $host; # this is the real host name + +my($clientURI, $user); + +$bpc->verbose(1) if ( $opts{v} ); if ( $opts{d} ) { # - # The host name $hostIP 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 # host name via NetBios using nmblookup. # - exit(1) if ( $bpc->CheckHostAlive($hostIP) < 0 ); - ($host, $user) = $bpc->NetBiosInfoGet($hostIP); - exit(1) if ( $host !~ /^([\w\.-]+)$/ ); - my $hosts = $bpc->HostInfoRead($host); - exit(1) if ( !defined($hosts->{$host}) ); + $hostIP = $client; + if ( $bpc->CheckHostAlive($hostIP) < 0 ) { + print("Exiting because CheckHostAlive($hostIP) failed\n") + if ( $opts{v} ); + exit(1); + } + if ( $Conf{NmbLookupCmd} eq "" ) { + print("Exiting because \$Conf{NmbLookupCmd} is empty\n") + if ( $opts{v} ); + exit(1); + } + ($client, $user) = $bpc->NetBiosInfoGet($hostIP); + if ( $client !~ /^([\w\.\s-]+)$/ ) { + print("Exiting because NetBiosInfoGet($hostIP) returned '$client'," + . " an invalid host name\n") + if ( $opts{v} ); + exit(1) + } + $Hosts = $bpc->HostInfoRead($client); + $host = $client; } else { - $host = $hostIP; + $Hosts = $bpc->HostInfoRead($client); +} +if ( !defined($Hosts->{$client}) ) { + print("Exiting because host $client does not exist in the hosts file\n"); + exit(1) } -my $Dir = "$TopDir/pc/$host"; -my $xferPid = -1; +my $Dir = "$TopDir/pc/$client"; +my @xferPid = (); my $tarPid = -1; # # Re-read config file, so we can include the PC-specific config # -if ( defined(my $error = $bpc->ConfigRead($host)) ) { - print("Can't read PC's config file: $error\n"); +$clientURI = $bpc->uriEsc($client); +if ( defined(my $error = $bpc->ConfigRead($client)) ) { + print("dump failed: Can't read PC's config file: $error\n"); exit(1); } %Conf = $bpc->Conf(); @@ -142,6 +170,11 @@ if ( defined(my $error = $bpc->ConfigRead($host)) ) { $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 = $$; # # Make sure we eventually timeout if there is no activity from @@ -151,23 +184,50 @@ alarm($Conf{ClientTimeout}); mkpath($Dir, 0, 0777) if ( !-d $Dir ); if ( !-f "$Dir/LOCK" ) { - open(LOCK, ">$Dir/LOCK") && close(LOCK); + open(LOCK, ">", "$Dir/LOCK") && close(LOCK); } -open(LOG, ">>$Dir/LOG"); +open(LOG, ">>", "$Dir/LOG"); select(LOG); $| = 1; select(STDOUT); -########################################################################### -# Figure out what to do and do it -########################################################################### - # # For the -e option we just expire backups and quit # if ( $opts{e} ) { - BackupExpire($host); + BackupExpire($client); exit(0); } +if ( !$opts{d} ) { + # + # In the non-DHCP case, make sure the host can be looked up + # via NS, or otherwise find the IP address via NetBios. + # + if ( $Conf{ClientNameAlias} ne "" ) { + $host = $Conf{ClientNameAlias}; + } else { + $host = $client; + } + if ( !defined(gethostbyname($host)) ) { + # + # Ok, NS doesn't know about it. Maybe it is a NetBios name + # instead. + # + print("Name server doesn't know about $host; trying NetBios\n") + if ( $opts{v} ); + if ( !defined($hostIP = $bpc->NetBiosHostIPFind($host)) ) { + print(LOG $bpc->timeStamp, "Can't find host $host via netbios\n"); + print("host not found\n"); + exit(1); + } + } else { + $hostIP = $host; + } +} + +########################################################################### +# Figure out what to do and do it +########################################################################### + # # See if we should skip this host during a certain range # of times. @@ -178,7 +238,7 @@ if ( $err ne "" ) { print(LOG $bpc->timeStamp, "Can't connect to server ($err)\n"); exit(1); } -my $reply = $bpc->ServerMesg("status host($host)"); +my $reply = $bpc->ServerMesg("status host($clientURI)"); $reply = $1 if ( $reply =~ /(.*)/s ); my(%StatusHost); eval($reply); @@ -190,9 +250,11 @@ $bpc->ServerDisconnect(); if ( $opts{d} ) { if ( $StatusHost{activeJob} ) { # oops, something is already running for this host + print("Exiting because backup is already running for $client\n") + if ( $opts{v} ); exit(0); } - print("DHCP $hostIP $host\n"); + print("DHCP $hostIP $clientURI\n"); } my($needLink, @Backups, $type, $lastBkupNum, $lastFullBkupNum); @@ -226,13 +288,13 @@ if ( !$opts{i} && !$opts{f} && $StatusHost{backoffTime} > time ) { # # Now see if there are any old backups we should delete # -BackupExpire($host); +BackupExpire($client); # # Read Backup information, and find times of the most recent full and # incremental backups # -@Backups = $bpc->BackupInfoRead($host); +@Backups = $bpc->BackupInfoRead($client); for ( my $i = 0 ; $i < @Backups ; $i++ ) { $needLink = 1 if ( $Backups[$i]{nFilesNew} eq "" || -f "$Dir/NewFileList.$Backups[$i]{num}" ); @@ -270,13 +332,13 @@ my $delay = $bpc->CheckHostAlive($hostIP); if ( $delay < 0 ) { print(LOG $bpc->timeStamp, "no ping response\n"); print("no ping response\n"); - print("link $host\n") if ( $needLink ); + print("link $clientURI\n") if ( $needLink ); exit(1); } elsif ( $delay > $Conf{PingMaxMsec} ) { printf(LOG "%sping too slow: %.4gmsec\n", $bpc->timeStamp, $delay); printf("ping too slow: %.4gmsec (threshold is %gmsec)\n", $delay, $Conf{PingMaxMsec}); - print("link $host\n") if ( $needLink ); + print("link $clientURI\n") if ( $needLink ); exit(1); } @@ -310,6 +372,7 @@ if ( !defined($XferLOG) ) { print("dump failed: unable to open/create $Dir/XferLOG$fileExt\n"); exit(1); } +$XferLOG->writeTeeStdout(1) if ( $opts{v} ); unlink("$Dir/NewFileList"); my $startTime = time(); @@ -413,8 +476,8 @@ for my $shareName ( @$ShareNames ) { open(STDERR, ">&STDOUT"); close(STDIN); open(STDIN, "<&RH"); - exec("$BinDir/BackupPC_tarExtract '$host' '$shareName'" - . " $Conf{CompressLevel}"); + exec("$BinDir/BackupPC_tarExtract", $client, $shareName, + $Conf{CompressLevel}); print(LOG $bpc->timeStamp, "can't exec $BinDir/BackupPC_tarExtract\n"); exit(0); @@ -424,8 +487,8 @@ for my $shareName ( @$ShareNames ) { # We need to create the NewFileList output file # local(*NEW_FILES); - 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"); $newFilesFH = *NEW_FILES; } @@ -434,6 +497,7 @@ for my $shareName ( @$ShareNames ) { # $xfer->args({ host => $host, + client => $client, hostIP => $hostIP, shareName => $shareName, pipeRH => *RH, @@ -448,12 +512,13 @@ for my $shareName ( @$ShareNames ) { backups => \@Backups, compress => $Conf{CompressLevel}, XferMethod => $Conf{XferMethod}, + pidHandler => \&pidHandler, }); if ( !defined($logMsg = $xfer->start()) ) { print(LOG $bpc->timeStamp, "xfer start failed: ", $xfer->errStr, "\n"); print("dump failed: ", $xfer->errStr, "\n"); - print("link $host\n") if ( $needLink ); + print("link $clientURI\n") if ( $needLink ); # # kill off the tar process, first nicely then forcefully # @@ -462,11 +527,17 @@ for my $shareName ( @$ShareNames ) { sleep(1); kill(9, $tarPid); } + if ( @xferPid ) { + kill(2, @xferPid); + sleep(1); + kill(9, @xferPid); + } UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); exit(1); } - $xferPid = $xfer->xferPid; + @xferPid = $xfer->xferPid; + if ( $useTar ) { # # The parent must close both handles on the pipe since the children @@ -474,17 +545,13 @@ for my $shareName ( @$ShareNames ) { # close(RH); close(WH); - print(LOG $bpc->timeStamp, $logMsg, - " (xferPid=$xferPid, tarPid=$tarPid)\n"); - } elsif ( $xferPid > 0 ) { - print(LOG $bpc->timeStamp, $logMsg, " (xferPid=$xferPid)\n"); - } else { - print(LOG $bpc->timeStamp, $logMsg, "\n"); } - print("started $type dump, pid=$xferPid, tarPid=$tarPid," - . " share=$shareName\n"); + print(LOG $bpc->timeStamp, $logMsg, "\n"); + print("started $type dump, share=$shareName\n"); + + pidHandler(@xferPid); - if ( $useTar || $xferPid > 0 ) { + if ( $useTar ) { # # Parse the output of the transfer program and BackupPC_tarExtract # while they run. Since we might be reading from two or more children @@ -512,12 +579,12 @@ for my $shareName ( @$ShareNames ) { $tarOut = $2; $XferLOG->write(\"tarExtract: $_\n"); if ( /^Done: (\d+) errors, (\d+) filesExist, (\d+) sizeExist, (\d+) sizeExistComp, (\d+) filesTotal, (\d+) sizeTotal/ ) { - $tarErrs = $1; - $nFilesExist = $2; - $sizeExist = $3; - $sizeExistComp = $4; - $nFilesTotal = $5; - $sizeTotal = $6; + $tarErrs += $1; + $nFilesExist += $2; + $sizeExist += $3; + $sizeExistComp += $4; + $nFilesTotal += $5; + $sizeTotal += $6; } } } @@ -542,8 +609,13 @@ for my $shareName ( @$ShareNames ) { # # otherwise the xfer module does everything for us # - ($tarErrs, $nFilesExist, $sizeExist, $sizeExistComp, - $nFilesTotal, $sizeTotal) = $xfer->run(); + my @results = $xfer->run(); + $tarErrs += $results[0]; + $nFilesExist += $results[1]; + $sizeExist += $results[2]; + $sizeExistComp += $results[3]; + $nFilesTotal += $results[4]; + $sizeTotal += $results[5]; } # @@ -568,10 +640,10 @@ for my $shareName ( @$ShareNames ) { # # kill off the tranfer program, first nicely then forcefully # - if ( $xferPid > 0 ) { - kill(2, $xferPid); + if ( @xferPid ) { + kill(2, @xferPid); sleep(1); - kill(9, $xferPid); + kill(9, @xferPid); } # # kill off the tar process, first nicely then forcefully @@ -602,7 +674,7 @@ $XferLOG->close(); close($newFilesFH) if ( defined($newFilesFH) ); if ( $stat{xferOK} ) { - @Backups = $bpc->BackupInfoRead($host); + @Backups = $bpc->BackupInfoRead($client); for ( my $i = 0 ; $i < @Backups ; $i++ ) { $lastNum = $Backups[$i]{num} if ( $lastNum < $Backups[$i]{num} ); } @@ -648,14 +720,14 @@ if ( !$stat{xferOK} ) { rename("$Dir/XferLOG$fileExt", "$Dir/XferLOG.bad$fileExt"); $bpc->RmTreeDefer("$TopDir/trash", "$Dir/new") if ( -d "$Dir/new" ); print("dump failed: $stat{hostError}\n"); - print("link $host\n") if ( $needLink ); + print("link $clientURI\n") if ( $needLink ); exit(1); } # # Add the new backup information to the backup file # -@Backups = $bpc->BackupInfoRead($host); +@Backups = $bpc->BackupInfoRead($client); my $i = @Backups; $Backups[$i]{num} = $lastNum; $Backups[$i]{type} = $type; @@ -673,7 +745,7 @@ $Backups[$i]{tarErrs} = $tarErrs; $Backups[$i]{compress} = $Conf{CompressLevel}; $Backups[$i]{noFill} = $type eq "full" ? 0 : 1; $Backups[$i]{mangle} = 1; # name mangling always on for v1.04+ -$bpc->BackupInfoWrite($host, @Backups); +$bpc->BackupInfoWrite($client, @Backups); unlink("$Dir/timeStamp.level0"); @@ -712,7 +784,7 @@ print(LOG $bpc->timeStamp, . " $stat{xferErrCnt} xferErrs ($stat{xferBadFileCnt} bad files," . " $stat{xferBadShareCnt} bad shares, $otherCount other)\n"); -BackupExpire($host); +BackupExpire($client); print("$type backup complete\n"); @@ -725,7 +797,7 @@ sub NothingToDo my($needLink) = @_; print("nothing to do\n"); - print("link $host\n") if ( $needLink ); + print("link $clientURI\n") if ( $needLink ); exit(0); } @@ -734,21 +806,25 @@ sub catch_signal my $signame = shift; my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; + # + # Ignore signals in children + # + return if ( $Pid != $$ ); + print(LOG $bpc->timeStamp, "cleaning up after signal $signame\n"); + $SIG{$signame} = 'IGNORE'; UserCommandRun("DumpPostUserCmd") if ( $NeedPostCmd ); $XferLOG->write(\"exiting after signal $signame\n"); $XferLOG->close(); - if ( $xferPid > 0 ) { - if ( kill(2, $xferPid) <= 0 ) { - sleep(1); - kill(9, $xferPid); - } + if ( @xferPid ) { + kill(2, @xferPid); + sleep(1); + kill(9, @xferPid); } if ( $tarPid > 0 ) { - if ( kill(2, $tarPid) <= 0 ) { - sleep(1); - kill(9, $tarPid); - } + kill(2, $tarPid); + sleep(1); + kill(9, $tarPid); } unlink("$Dir/timeStamp.level0"); unlink("$Dir/NewFileList"); @@ -761,7 +837,7 @@ sub catch_signal } else { print("dump failed: received signal=$signame\n"); } - print("link $host\n") if ( $needLink ); + print("link $clientURI\n") if ( $needLink ); exit(1); } @@ -771,9 +847,9 @@ sub catch_signal # sub BackupExpire { - my($host) = @_; - my($Dir) = "$TopDir/pc/$host"; - my(@Backups) = $bpc->BackupInfoRead($host); + my($client) = @_; + my($Dir) = "$TopDir/pc/$client"; + my(@Backups) = $bpc->BackupInfoRead($client); my($cntFull, $cntIncr, $firstFull, $firstIncr, $oldestIncr, $oldestFull); while ( 1 ) { @@ -847,19 +923,37 @@ sub BackupExpire last; } } - $bpc->BackupInfoWrite($host, @Backups); + $bpc->BackupInfoWrite($client, @Backups); } 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 ); + if ( $netBiosHost ne $host ); 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); + $XferLOG->write(\"Xfer PIDs are now $str\n") if ( defined($XferLOG) ); + print("xferPids $str\n"); +} + # # Run an optional pre- or post-dump command # @@ -869,17 +963,21 @@ sub UserCommandRun return if ( !defined($Conf{$type}) ); my $vars = { - xfer => $xfer, - host => $host, - hostIP => $hostIP, - share => $ShareNames->[0], - shares => $ShareNames, + xfer => $xfer, + client => $client, + host => $host, + hostIP => $hostIP, + user => $Hosts->{$client}{user}, + moreUsers => $Hosts->{$client}{moreUsers}, + share => $ShareNames->[0], + shares => $ShareNames, XferMethod => $Conf{XferMethod}, - LOG => *LOG, - XferLOG => $XferLOG, - stat => \%stat, - xferOK => $stat{xferOK}, - type => $type, + sshPath => $Conf{SshPath}, + LOG => *LOG, + XferLOG => $XferLOG, + stat => \%stat, + xferOK => $stat{xferOK}, + type => $type, }; my $cmd = $bpc->cmdVarSubstitute($Conf{$type}, $vars); $XferLOG->write(\"Executing $type: @$cmd\n");