1 |
#!/bin/perl |
2 |
#============================================================= -*-perl-*- |
3 |
# |
4 |
# BackupPC_restore: Restore files to a client. |
5 |
# |
6 |
# DESCRIPTION |
7 |
# |
8 |
# Usage: BackupPC_restore <hostIP> <client> <reqFileName> |
9 |
# |
10 |
# AUTHOR |
11 |
# Craig Barratt <cbarratt@users.sourceforge.net> |
12 |
# |
13 |
# COPYRIGHT |
14 |
# Copyright (C) 2001-2003 Craig Barratt |
15 |
# |
16 |
# This program is free software; you can redistribute it and/or modify |
17 |
# it under the terms of the GNU General Public License as published by |
18 |
# the Free Software Foundation; either version 2 of the License, or |
19 |
# (at your option) any later version. |
20 |
# |
21 |
# This program is distributed in the hope that it will be useful, |
22 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
23 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
24 |
# GNU General Public License for more details. |
25 |
# |
26 |
# You should have received a copy of the GNU General Public License |
27 |
# along with this program; if not, write to the Free Software |
28 |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
29 |
# |
30 |
#======================================================================== |
31 |
# |
32 |
# Version 2.1.2, released 5 Sep 2005. |
33 |
# |
34 |
# See http://backuppc.sourceforge.net. |
35 |
# |
36 |
#======================================================================== |
37 |
|
38 |
use strict; |
39 |
no utf8; |
40 |
use lib "__INSTALLDIR__/lib"; |
41 |
use BackupPC::Lib; |
42 |
use BackupPC::FileZIO; |
43 |
use BackupPC::Xfer::Smb; |
44 |
use BackupPC::Xfer::Tar; |
45 |
use BackupPC::Xfer::Rsync; |
46 |
use Socket; |
47 |
|
48 |
use File::Path; |
49 |
use Getopt::Std; |
50 |
|
51 |
use vars qw( %RestoreReq ); |
52 |
|
53 |
########################################################################### |
54 |
# Initialize |
55 |
########################################################################### |
56 |
|
57 |
die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); |
58 |
my $TopDir = $bpc->TopDir(); |
59 |
my $BinDir = $bpc->BinDir(); |
60 |
my %Conf = $bpc->Conf(); |
61 |
my $NeedPostCmd; |
62 |
|
63 |
my($hostIP, $host, $client, $reqFileName, %stat); |
64 |
|
65 |
$bpc->ChildInit(); |
66 |
|
67 |
if ( @ARGV != 3 ) { |
68 |
print("usage: $0 <hostIP> <client> <reqFileName>\n"); |
69 |
exit(1); |
70 |
} |
71 |
$hostIP = $1 if ( $ARGV[0] =~ /(.+)/ ); |
72 |
$client = $1 if ( $ARGV[1] =~ /(.+)/ ); |
73 |
if ( $ARGV[2] !~ /^([\w.]+)$/ ) { |
74 |
print("$0: bad reqFileName (arg #3): $ARGV[2]\n"); |
75 |
exit(1); |
76 |
} |
77 |
$reqFileName = $1; |
78 |
|
79 |
my $startTime = time(); |
80 |
|
81 |
my $Hosts = $bpc->HostInfoRead($client); |
82 |
|
83 |
my $Dir = "$TopDir/pc/$client"; |
84 |
my @xferPid = (); |
85 |
my $tarPid = -1; |
86 |
|
87 |
# |
88 |
# Catch various signals |
89 |
# |
90 |
$SIG{INT} = \&catch_signal; |
91 |
$SIG{ALRM} = \&catch_signal; |
92 |
$SIG{TERM} = \&catch_signal; |
93 |
$SIG{PIPE} = \&catch_signal; |
94 |
$SIG{STOP} = \&catch_signal; |
95 |
$SIG{TSTP} = \&catch_signal; |
96 |
$SIG{TTIN} = \&catch_signal; |
97 |
my $Pid = $$; |
98 |
|
99 |
mkpath($Dir, 0, 0777) if ( !-d $Dir ); |
100 |
if ( !-f "$Dir/LOCK" ) { |
101 |
open(LOCK, ">", "$Dir/LOCK") && close(LOCK); |
102 |
} |
103 |
open(LOG, ">>", "$Dir/LOG"); |
104 |
select(LOG); $| = 1; select(STDOUT); |
105 |
|
106 |
|
107 |
# |
108 |
# Read the request file |
109 |
# |
110 |
if ( !(my $ret = do "$Dir/$reqFileName") ) { |
111 |
my $err; |
112 |
if ( $@ ) { |
113 |
$err = "couldn't parse $Dir/$reqFileName: $@"; |
114 |
} elsif ( !defined($ret) ) { |
115 |
$err = "couldn't do $Dir/$reqFileName: $!"; |
116 |
} else { |
117 |
$err = "couldn't run $Dir/$reqFileName"; |
118 |
} |
119 |
$stat{hostError} = $err; |
120 |
exit(RestoreCleanup($client)); |
121 |
} |
122 |
|
123 |
# |
124 |
# Re-read config file, so we can include the PC-specific config |
125 |
# |
126 |
if ( defined(my $error = $bpc->ConfigRead($client)) ) { |
127 |
$stat{hostError} = "Can't read PC's config file: $error"; |
128 |
exit(RestoreCleanup($client)); |
129 |
} |
130 |
%Conf = $bpc->Conf(); |
131 |
|
132 |
# |
133 |
# Make sure we eventually timeout if there is no activity from |
134 |
# the data transport program. |
135 |
# |
136 |
alarm($Conf{ClientTimeout}); |
137 |
|
138 |
# |
139 |
# See if the host name is aliased |
140 |
# |
141 |
if ( $Conf{ClientNameAlias} ne "" ) { |
142 |
$host = $Conf{ClientNameAlias}; |
143 |
} else { |
144 |
$host = $client; |
145 |
} |
146 |
|
147 |
# |
148 |
# Find its IP address |
149 |
# |
150 |
if ( $hostIP !~ /\d+\.\d+\.\d+\.\d+/ ) { |
151 |
if ( !defined(gethostbyname($host)) ) { |
152 |
# |
153 |
# Ok, NS doesn't know about it. Maybe it is a NetBios name |
154 |
# instead. |
155 |
# |
156 |
if ( !defined($hostIP = $bpc->NetBiosHostIPFind($host)) ) { |
157 |
$stat{hostError} = "Can't find host $host"; |
158 |
exit(RestoreCleanup($client)); |
159 |
} |
160 |
} else { |
161 |
$hostIP = $host; |
162 |
} |
163 |
} |
164 |
|
165 |
# |
166 |
# Check if $host is alive |
167 |
# |
168 |
my $delay = $bpc->CheckHostAlive($hostIP); |
169 |
if ( $delay < 0 ) { |
170 |
$stat{hostError} = "no ping response from $host ($hostIP)"; |
171 |
exit(RestoreCleanup($client)); |
172 |
} elsif ( $delay > $Conf{PingMaxMsec} ) { |
173 |
$stat{hostError} = sprintf("ping too slow: %.4gmsec (max is %gmsec)\n", |
174 |
$delay, $Conf{PingMaxMsec}); |
175 |
exit(RestoreCleanup($client)); |
176 |
} |
177 |
|
178 |
# |
179 |
# Make sure it is really the machine we expect |
180 |
# |
181 |
if ( (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { |
182 |
$stat{hostError} = $errMsg; |
183 |
exit(RestoreCleanup($client)); |
184 |
} |
185 |
|
186 |
# |
187 |
# Setup file extension for compression and open RestoreLOG output file |
188 |
# |
189 |
if ( $Conf{CompressLevel} && !BackupPC::FileZIO->compOk ) { |
190 |
$stat{hostError} = "Compress:Zlib not found"; |
191 |
exit(RestoreCleanup($client)); |
192 |
} |
193 |
my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; |
194 |
my $RestoreLOG = BackupPC::FileZIO->open("$Dir/RestoreLOG$fileExt", 1, |
195 |
$Conf{CompressLevel}); |
196 |
my $tarCreateFileCnt = 0; |
197 |
my $tarCreateByteCnt = 0; |
198 |
my $tarCreateErrCnt = 1; # assume not ok until we learn otherwise |
199 |
my $tarCreateErr; |
200 |
my($logMsg, $xfer); |
201 |
|
202 |
$stat{xferOK} = $stat{hostAbort} = undef; |
203 |
$stat{hostError} = $stat{lastOutputLine} = undef; |
204 |
local(*RH, *WH); |
205 |
|
206 |
# |
207 |
# Run an optional pre-restore command |
208 |
# |
209 |
UserCommandRun("RestorePreUserCmd"); |
210 |
$NeedPostCmd = 1; |
211 |
|
212 |
if ( $Conf{XferMethod} eq "tar" ) { |
213 |
# |
214 |
# Use tar (eg: tar/ssh) as the transport program. |
215 |
# |
216 |
$xfer = BackupPC::Xfer::Tar->new($bpc); |
217 |
} elsif ( $Conf{XferMethod} eq "rsync" || $Conf{XferMethod} eq "rsyncd" ) { |
218 |
# |
219 |
# Use rsync as the transport program. |
220 |
# |
221 |
if ( !defined($xfer = BackupPC::Xfer::Rsync->new($bpc)) ) { |
222 |
my $errStr = BackupPC::Xfer::Rsync->errStr; |
223 |
UserCommandRun("RestorePostUserCmd") if ( $NeedPostCmd ); |
224 |
$stat{hostError} = $errStr; |
225 |
exit(RestoreCleanup($client)); |
226 |
} |
227 |
} else { |
228 |
# |
229 |
# Default is to use smbclient (smb) as the transport program. |
230 |
# |
231 |
$xfer = BackupPC::Xfer::Smb->new($bpc); |
232 |
} |
233 |
my $useTar = $xfer->useTar; |
234 |
|
235 |
if ( $useTar ) { |
236 |
# |
237 |
# Create a socketpair to connect BackupPC_tarCreate to the transport |
238 |
# program (smbclient, tar, etc). |
239 |
# WH is the write handle for writing, provided to BackupPC_tarCreate |
240 |
# and RH is the other end of the pipe for reading provided to the |
241 |
# transport program. |
242 |
# |
243 |
if ( socketpair(RH, WH, AF_UNIX, SOCK_STREAM, PF_UNSPEC) ) { |
244 |
shutdown(RH, 1); # no writing to this socket |
245 |
shutdown(WH, 0); # no reading from this socket |
246 |
setsockopt(RH, SOL_SOCKET, SO_RCVBUF, 8 * 65536); |
247 |
setsockopt(WH, SOL_SOCKET, SO_SNDBUF, 8 * 65536); |
248 |
} else { |
249 |
# |
250 |
# Default to pipe() if socketpair() doesn't work. |
251 |
# |
252 |
pipe(RH, WH); |
253 |
} |
254 |
} |
255 |
|
256 |
# |
257 |
# Run the transport program, which reads from RH and extracts the data. |
258 |
# |
259 |
my @Backups = $bpc->BackupInfoRead($RestoreReq{hostSrc}); |
260 |
my $xferArgs = { |
261 |
client => $client, |
262 |
host => $host, |
263 |
hostIP => $hostIP, |
264 |
type => "restore", |
265 |
shareName => $RestoreReq{shareDest}, |
266 |
pipeRH => *RH, |
267 |
pipeWH => *WH, |
268 |
XferLOG => $RestoreLOG, |
269 |
XferMethod => $Conf{XferMethod}, |
270 |
logLevel => $Conf{XferLogLevel}, |
271 |
bkupSrcHost => $RestoreReq{hostSrc}, |
272 |
bkupSrcShare => $RestoreReq{shareSrc}, |
273 |
bkupSrcNum => $RestoreReq{num}, |
274 |
backups => \@Backups, |
275 |
pathHdrSrc => $RestoreReq{pathHdrSrc}, |
276 |
pathHdrDest => $RestoreReq{pathHdrDest}, |
277 |
fileList => $RestoreReq{fileList}, |
278 |
pidHandler => \&pidHandler, |
279 |
}; |
280 |
|
281 |
$xfer->args($xferArgs); |
282 |
|
283 |
if ( !defined($logMsg = $xfer->start()) ) { |
284 |
UserCommandRun("RestorePostUserCmd") if ( $NeedPostCmd ); |
285 |
$stat{hostError} = "xfer start failed: ", $xfer->errStr; |
286 |
exit(RestoreCleanup($client)); |
287 |
} |
288 |
|
289 |
if ( $useTar ) { |
290 |
# |
291 |
# Now do the restore by running BackupPC_tarCreate |
292 |
# |
293 |
# The parent must close the read handle since the transport program |
294 |
# is using it. |
295 |
# |
296 |
close(RH); |
297 |
|
298 |
# |
299 |
# fork a child for BackupPC_tarCreate. TAR is a file handle |
300 |
# on which we (the parent) read the stderr from BackupPC_tarCreate. |
301 |
# |
302 |
my @tarPathOpts; |
303 |
if ( defined($RestoreReq{pathHdrDest}) |
304 |
&& $RestoreReq{pathHdrDest} ne $RestoreReq{pathHdrSrc} ) { |
305 |
@tarPathOpts = ("-r", $RestoreReq{pathHdrSrc}, |
306 |
"-p", $RestoreReq{pathHdrDest} |
307 |
); |
308 |
} |
309 |
my @tarArgs = ( |
310 |
"-h", $RestoreReq{hostSrc}, |
311 |
"-n", $RestoreReq{num}, |
312 |
"-s", $RestoreReq{shareSrc}, |
313 |
"-t", |
314 |
@tarPathOpts, |
315 |
@{$RestoreReq{fileList}}, |
316 |
); |
317 |
my $logMsg = "Running: " |
318 |
. $bpc->execCmd2ShellCmd("$BinDir/BackupPC_tarCreate", @tarArgs) |
319 |
. "\n"; |
320 |
$RestoreLOG->write(\$logMsg); |
321 |
if ( !defined($tarPid = open(TAR, "-|")) ) { |
322 |
close(WH); |
323 |
# FIX: need to cleanup xfer |
324 |
UserCommandRun("RestorePostUserCmd") if ( $NeedPostCmd ); |
325 |
$stat{hostError} = "Can't fork to run tar"; |
326 |
exit(RestoreCleanup($client)); |
327 |
} |
328 |
binmode(TAR); |
329 |
if ( !$tarPid ) { |
330 |
# |
331 |
# This is the tarCreate child. Clone STDERR to STDOUT, |
332 |
# STDOUT to WH, and then exec BackupPC_tarCreate. |
333 |
# |
334 |
setpgrp 0,0; |
335 |
close(STDERR); |
336 |
open(STDERR, ">&STDOUT"); |
337 |
close(STDOUT); |
338 |
open(STDOUT, ">&WH"); |
339 |
alarm(0); |
340 |
exec("$BinDir/BackupPC_tarCreate", @tarArgs); |
341 |
print(LOG $bpc->timeStamp, "can't exec $BinDir/BackupPC_tarCreate\n"); |
342 |
# FIX: need to cleanup xfer |
343 |
exit(0); |
344 |
} |
345 |
# |
346 |
# The parent must close the write handle since BackupPC_tarCreate |
347 |
# is using it. |
348 |
# |
349 |
close(WH); |
350 |
|
351 |
@xferPid = $xfer->xferPid; |
352 |
|
353 |
print(LOG $bpc->timeStamp, $logMsg, "\n"); |
354 |
print("started_restore\n"); |
355 |
|
356 |
pidHandler(@xferPid); |
357 |
|
358 |
# |
359 |
# Parse the output of the transfer program and BackupPC_tarCreate |
360 |
# while they run. Since we are reading from two or more children |
361 |
# we use a select. |
362 |
# |
363 |
my($FDread, $tarOut, $mesg); |
364 |
vec($FDread, fileno(TAR), 1) = 1; |
365 |
$xfer->setSelectMask(\$FDread); |
366 |
|
367 |
SCAN: while ( 1 ) { |
368 |
my $ein = $FDread; |
369 |
last if ( $FDread =~ /^\0*$/ ); |
370 |
alarm($Conf{ClientTimeout}); |
371 |
select(my $rout = $FDread, undef, $ein, undef); |
372 |
if ( vec($rout, fileno(TAR), 1) ) { |
373 |
if ( sysread(TAR, $mesg, 8192) <= 0 ) { |
374 |
vec($FDread, fileno(TAR), 1) = 0; |
375 |
if ( !close(TAR) ) { |
376 |
$tarCreateErrCnt = 1; |
377 |
$tarCreateErr = "BackupPC_tarCreate failed"; |
378 |
} |
379 |
} else { |
380 |
$tarOut .= $mesg; |
381 |
} |
382 |
} |
383 |
while ( $tarOut =~ /(.*?)[\n\r]+(.*)/s ) { |
384 |
$_ = $1; |
385 |
$tarOut = $2; |
386 |
$RestoreLOG->write(\"tarCreate: $_\n"); |
387 |
if ( /^Done: (\d+) files, (\d+) bytes, (\d+) dirs, (\d+) specials, (\d+) errors/ ) { |
388 |
$tarCreateFileCnt = $1; |
389 |
$tarCreateByteCnt = $2; |
390 |
$tarCreateErrCnt = $5; |
391 |
} |
392 |
} |
393 |
last if ( !$xfer->readOutput(\$FDread, $rout) ); |
394 |
while ( my $str = $xfer->logMsgGet ) { |
395 |
print(LOG $bpc->timeStamp, "xfer: $str\n"); |
396 |
} |
397 |
if ( $xfer->getStats->{fileCnt} == 1 ) { |
398 |
# |
399 |
# Make sure it is still the machine we expect. We do this while |
400 |
# the transfer is running to avoid a potential race condition if |
401 |
# the ip address was reassigned by dhcp just before we started |
402 |
# the transfer. |
403 |
# |
404 |
if ( my $errMsg = CorrectHostCheck($hostIP, $host) ) { |
405 |
$stat{hostError} = $errMsg; |
406 |
last SCAN; |
407 |
} |
408 |
} |
409 |
} |
410 |
} else { |
411 |
# |
412 |
# otherwise the xfer module does everything for us |
413 |
# |
414 |
print(LOG $bpc->timeStamp, "Starting restore\n"); |
415 |
print("started_restore\n"); |
416 |
($tarCreateFileCnt, $tarCreateByteCnt, |
417 |
$tarCreateErrCnt, $tarCreateErr) = $xfer->run(); |
418 |
} |
419 |
alarm(0); |
420 |
|
421 |
# |
422 |
# Merge the xfer status (need to accumulate counts) |
423 |
# |
424 |
my $newStat = $xfer->getStats; |
425 |
foreach my $k ( (keys(%stat), keys(%$newStat)) ) { |
426 |
next if ( !defined($newStat->{$k}) ); |
427 |
if ( $k =~ /Cnt$/ ) { |
428 |
$stat{$k} += $newStat->{$k}; |
429 |
delete($newStat->{$k}); |
430 |
next; |
431 |
} |
432 |
if ( !defined($stat{$k}) ) { |
433 |
$stat{$k} = $newStat->{$k}; |
434 |
delete($newStat->{$k}); |
435 |
next; |
436 |
} |
437 |
} |
438 |
|
439 |
exit(RestoreCleanup($client)); |
440 |
|
441 |
########################################################################### |
442 |
# Subroutines |
443 |
########################################################################### |
444 |
|
445 |
sub CorrectHostCheck |
446 |
{ |
447 |
my($hostIP, $host) = @_; |
448 |
return if ( $hostIP eq $host && !$Conf{FixedIPNetBiosNameCheck} |
449 |
|| $Conf{NmbLookupCmd} eq "" ); |
450 |
my($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($hostIP); |
451 |
return "host $host has mismatching netbios name $netBiosHost" |
452 |
if ( $netBiosHost ne $host ); |
453 |
return; |
454 |
} |
455 |
|
456 |
sub catch_signal |
457 |
{ |
458 |
my $signame = shift; |
459 |
|
460 |
# |
461 |
# Children quit quietly on ALRM |
462 |
# |
463 |
exit(1) if ( $Pid != $$ && $signame eq "ALRM" ); |
464 |
|
465 |
# |
466 |
# Ignore signals in children |
467 |
# |
468 |
return if ( $Pid != $$ ); |
469 |
|
470 |
# |
471 |
# Note: needs to be tested for each kind of XferMethod |
472 |
# |
473 |
print(LOG $bpc->timeStamp, "cleaning up after signal $signame\n"); |
474 |
$SIG{$signame} = 'IGNORE'; |
475 |
$RestoreLOG->write(\"exiting after signal $signame\n"); |
476 |
$stat{xferOK} = 0; |
477 |
if ( $signame eq "INT" ) { |
478 |
$stat{hostError} = "aborted by user (signal=$signame)"; |
479 |
} else { |
480 |
$stat{hostError} = "aborted by signal=$signame"; |
481 |
} |
482 |
exit(RestoreCleanup($client)); |
483 |
} |
484 |
|
485 |
# |
486 |
# Cleanup and update the restore status |
487 |
# |
488 |
sub RestoreCleanup |
489 |
{ |
490 |
my($client) = @_; |
491 |
|
492 |
$stat{xferOK} = 0 if ( $stat{hostError} || $stat{hostAbort} |
493 |
|| $tarCreateErr ); |
494 |
|
495 |
if ( !$stat{xferOK} ) { |
496 |
# |
497 |
# kill off the tranfer program, first nicely then forcefully |
498 |
# |
499 |
if ( @xferPid ) { |
500 |
kill($bpc->sigName2num("INT"), @xferPid); |
501 |
sleep(1); |
502 |
kill($bpc->sigName2num("KILL"), @xferPid); |
503 |
} |
504 |
# |
505 |
# kill off the tar process, first nicely then forcefully |
506 |
# |
507 |
if ( $tarPid > 0 ) { |
508 |
kill($bpc->sigName2num("INT"), $tarPid); |
509 |
sleep(1); |
510 |
kill($bpc->sigName2num("KILL"), $tarPid); |
511 |
} |
512 |
} |
513 |
|
514 |
my $lastNum = -1; |
515 |
my @Restores; |
516 |
|
517 |
# |
518 |
# Do one last check to make sure it is still the machine we expect. |
519 |
# |
520 |
if ( $stat{xferOK} && (my $errMsg = CorrectHostCheck($hostIP, $host)) ) { |
521 |
$stat{hostError} = $errMsg; |
522 |
$stat{xferOK} = 0; |
523 |
} |
524 |
@Restores = $bpc->RestoreInfoRead($client); |
525 |
for ( my $i = 0 ; $i < @Restores ; $i++ ) { |
526 |
$lastNum = $Restores[$i]{num} if ( $lastNum < $Restores[$i]{num} ); |
527 |
} |
528 |
$lastNum++; |
529 |
|
530 |
# |
531 |
# Run an optional post-restore command |
532 |
# |
533 |
UserCommandRun("RestorePostUserCmd") if ( $NeedPostCmd ); |
534 |
|
535 |
rename("$Dir/RestoreLOG$fileExt", "$Dir/RestoreLOG.$lastNum$fileExt"); |
536 |
rename("$Dir/$reqFileName", "$Dir/RestoreInfo.$lastNum"); |
537 |
my $endTime = time(); |
538 |
|
539 |
# |
540 |
# If the restore failed, clean up |
541 |
# |
542 |
if ( !$stat{xferOK} ) { |
543 |
# |
544 |
# wait a short while and see if the system is still alive |
545 |
# |
546 |
$stat{hostError} ||= $tarCreateErr if ( $tarCreateErr ne "" ); |
547 |
$stat{hostError} = $stat{lastOutputLine} if ( $stat{hostError} eq "" ); |
548 |
sleep(2); |
549 |
if ( $bpc->CheckHostAlive($hostIP) < 0 ) { |
550 |
$stat{hostAbort} = 1; |
551 |
} |
552 |
if ( $stat{hostAbort} && $stat{hostError} eq "" ) { |
553 |
$stat{hostError} = "lost network connection during restore"; |
554 |
} |
555 |
$RestoreLOG->write(\"Restore failed: $stat{hostError}\n") |
556 |
if ( defined($RestoreLOG) ); |
557 |
} |
558 |
|
559 |
$RestoreLOG->close() if ( defined($RestoreLOG) ); |
560 |
|
561 |
# |
562 |
# Add the new restore information to the restore file |
563 |
# |
564 |
@Restores = $bpc->RestoreInfoRead($client); |
565 |
my $i = @Restores; |
566 |
$Restores[$i]{num} = $lastNum; |
567 |
$Restores[$i]{startTime} = $startTime; |
568 |
$Restores[$i]{endTime} = $endTime; |
569 |
$Restores[$i]{result} = $stat{xferOK} ? "ok" : "failed"; |
570 |
$Restores[$i]{errorMsg} = $stat{hostError}; |
571 |
$Restores[$i]{nFiles} = $tarCreateFileCnt; |
572 |
$Restores[$i]{size} = $tarCreateByteCnt; |
573 |
$Restores[$i]{tarCreateErrs} = $tarCreateErrCnt; |
574 |
$Restores[$i]{xferErrs} = $stat{xferErrCnt} || 0; |
575 |
|
576 |
while ( @Restores > $Conf{RestoreInfoKeepCnt} ) { |
577 |
my $num = $Restores[0]{num}; |
578 |
unlink("$Dir/RestoreLOG.$num.z"); |
579 |
unlink("$Dir/RestoreLOG.$num"); |
580 |
unlink("$Dir/RestoreInfo.$num"); |
581 |
shift(@Restores); |
582 |
} |
583 |
$bpc->RestoreInfoWrite($client, @Restores); |
584 |
|
585 |
if ( !$stat{xferOK} ) { |
586 |
print(LOG $bpc->timeStamp, "Restore failed ($stat{hostError})\n"); |
587 |
print("restore failed: $stat{hostError}\n"); |
588 |
return 1; |
589 |
} else { |
590 |
print("restore complete\n"); |
591 |
return; |
592 |
} |
593 |
} |
594 |
|
595 |
# |
596 |
# The Xfer method might tell us from time to time about processes |
597 |
# it forks. We tell BackupPC about this (for status displays) and |
598 |
# keep track of the pids in case we cancel the backup |
599 |
# |
600 |
sub pidHandler |
601 |
{ |
602 |
@xferPid = @_; |
603 |
@xferPid = grep(/./, @xferPid); |
604 |
return if ( !@xferPid && $tarPid < 0 ); |
605 |
my @pids = @xferPid; |
606 |
push(@pids, $tarPid) if ( $tarPid > 0 ); |
607 |
my $str = join(",", @pids); |
608 |
$RestoreLOG->write(\"Xfer PIDs are now $str\n") if ( defined($RestoreLOG) ); |
609 |
print("xferPids $str\n"); |
610 |
} |
611 |
|
612 |
# |
613 |
# Run an optional pre- or post-dump command |
614 |
# |
615 |
sub UserCommandRun |
616 |
{ |
617 |
my($cmdType) = @_; |
618 |
|
619 |
return if ( !defined($Conf{$cmdType}) ); |
620 |
my $vars = { |
621 |
xfer => $xfer, |
622 |
client => $client, |
623 |
host => $host, |
624 |
hostIP => $hostIP, |
625 |
share => $RestoreReq{shareDest}, |
626 |
XferMethod => $Conf{XferMethod}, |
627 |
sshPath => $Conf{SshPath}, |
628 |
LOG => *LOG, |
629 |
user => $Hosts->{$client}{user}, |
630 |
moreUsers => $Hosts->{$client}{moreUsers}, |
631 |
XferLOG => $RestoreLOG, |
632 |
stat => \%stat, |
633 |
xferOK => $stat{xferOK} || 0, |
634 |
hostError => $stat{hostError}, |
635 |
type => "restore", |
636 |
bkupSrcHost => $RestoreReq{hostSrc}, |
637 |
bkupSrcShare => $RestoreReq{shareSrc}, |
638 |
bkupSrcNum => $RestoreReq{num}, |
639 |
backups => \@Backups, |
640 |
pathHdrSrc => $RestoreReq{pathHdrSrc}, |
641 |
pathHdrDest => $RestoreReq{pathHdrDest}, |
642 |
fileList => $RestoreReq{fileList}, |
643 |
cmdType => $cmdType, |
644 |
}; |
645 |
my $cmd = $bpc->cmdVarSubstitute($Conf{$cmdType}, $vars); |
646 |
$RestoreLOG->write(\"Executing $cmdType: @$cmd\n"); |
647 |
# |
648 |
# Run the user's command, dumping the stdout/stderr into the |
649 |
# Xfer log file. Also supply the optional $vars and %Conf in |
650 |
# case the command is really perl code instead of a shell |
651 |
# command. |
652 |
# |
653 |
$bpc->cmdSystemOrEval($cmd, |
654 |
sub { |
655 |
$RestoreLOG->write(\$_[0]); |
656 |
}, |
657 |
$vars, \%Conf); |
658 |
} |