/[BackupPC]/trunk/lib/BackupPC/Xfer/Smb.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/lib/BackupPC/Xfer/Smb.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 331 - (hide annotations)
Wed Feb 1 14:34:55 2006 UTC (18 years, 4 months ago) by dpavlin
File size: 12682 byte(s)
local modification to support SmbHostName
1 dpavlin 1 #============================================================= -*-perl-*-
2     #
3     # BackupPC::Xfer::Smb package
4     #
5     # DESCRIPTION
6     #
7     # This library defines a BackupPC::Xfer::Smb class for managing
8     # the SMB (smbclient) transport of backup data from the client.
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 dpavlin 316 # Version 2.1.2, released 5 Sep 2005.
33 dpavlin 1 #
34     # See http://backuppc.sourceforge.net.
35     #
36     #========================================================================
37    
38     package BackupPC::Xfer::Smb;
39    
40     use strict;
41    
42     sub new
43     {
44     my($class, $bpc, $args) = @_;
45    
46     $args ||= {};
47     my $t = bless {
48     bpc => $bpc,
49     conf => { $bpc->Conf },
50     host => "",
51     hostIP => "",
52     shareName => "",
53     pipeRH => undef,
54     pipeWH => undef,
55     badFiles => [],
56     %$args,
57     }, $class;
58    
59     return $t;
60     }
61    
62     sub args
63     {
64     my($t, $args) = @_;
65    
66     foreach my $arg ( keys(%$args) ) {
67     $t->{$arg} = $args->{$arg};
68     }
69     }
70    
71     sub useTar
72     {
73     return 1;
74     }
75    
76     sub start
77     {
78     my($t) = @_;
79     my $bpc = $t->{bpc};
80     my $conf = $t->{conf};
81     my $I_option = $t->{hostIP} eq $t->{host} ? [] : ['-I', $t->{hostIP}];
82     my(@fileList, $X_option, $smbClientCmd, $logMsg);
83     my($timeStampFile);
84     local(*SMB);
85    
86     #
87     # First propagate the PASSWD setting
88     #
89     $ENV{PASSWD} = $ENV{BPC_SMB_PASSWD} if ( defined($ENV{BPC_SMB_PASSWD}) );
90     $ENV{PASSWD} = $conf->{SmbSharePasswd}
91     if ( defined($conf->{SmbSharePasswd}) );
92     if ( !defined($ENV{PASSWD}) ) {
93     $t->{_errStr} = "passwd not set for smbclient";
94     return;
95     }
96     if ( !defined($conf->{SmbClientPath}) || !-x $conf->{SmbClientPath} ) {
97     $t->{_errStr} = '$Conf{SmbClientPath} is not a valid executable';
98     return;
99     }
100     if ( $t->{type} eq "restore" ) {
101     $smbClientCmd = $conf->{SmbClientRestoreCmd};
102     $logMsg = "restore started for share $t->{shareName}";
103     } else {
104     #
105     # Turn $conf->{BackupFilesOnly} and $conf->{BackupFilesExclude}
106     # into a hash of arrays of files, and $conf->{SmbShareName}
107     # to an array
108     #
109     $bpc->backupFileConfFix($conf, "SmbShareName");
110    
111     $t->{fileIncludeHash} = {};
112     if ( defined($conf->{BackupFilesOnly}{$t->{shareName}}) ) {
113     foreach my $file ( @{$conf->{BackupFilesOnly}{$t->{shareName}}} ) {
114     push(@fileList, $file);
115     $t->{fileIncludeHash}{$file} = 1;
116     }
117     } elsif ( defined($conf->{BackupFilesExclude}{$t->{shareName}}) ) {
118     foreach my $file ( @{$conf->{BackupFilesExclude}{$t->{shareName}}} )
119     {
120     push(@fileList, $file);
121     }
122     #
123     # Allow simple wildcards in exclude list by specifying "r" option.
124     #
125     $X_option = "rX";
126     }
127     if ( $t->{type} eq "full" ) {
128     $smbClientCmd = $conf->{SmbClientFullCmd};
129     $logMsg = "full backup started for share $t->{shareName}";
130     } else {
131     $timeStampFile = "$t->{outDir}/timeStamp.level0";
132     open(LEV0, ">", $timeStampFile) && close(LEV0);
133     utime($t->{lastFull} - 3600, $t->{lastFull} - 3600, $timeStampFile);
134     $smbClientCmd = $conf->{SmbClientIncrCmd};
135     $logMsg = "incr backup started back to "
136     . $bpc->timeStamp($t->{lastFull} - 3600, 0)
137     . "for share $t->{shareName}";
138     }
139     }
140     my $args = {
141     smbClientPath => $conf->{SmbClientPath},
142 dpavlin 331 #host => $t->{host},
143     # local modification to support SmbHostName
144     host => $conf->{SmbHostName} || $t->{host},
145 dpavlin 1 hostIP => $t->{hostIP},
146     client => $t->{client},
147     shareName => $t->{shareName},
148     userName => $conf->{SmbShareUserName},
149     fileList => \@fileList,
150     I_option => $I_option,
151     X_option => $X_option,
152     timeStampFile => $timeStampFile,
153     };
154     $smbClientCmd = $bpc->cmdVarSubstitute($smbClientCmd, $args);
155    
156     if ( !defined($t->{xferPid} = open(SMB, "-|")) ) {
157     $t->{_errStr} = "Can't fork to run smbclient";
158     return;
159     }
160     $t->{pipeSMB} = *SMB;
161     if ( !$t->{xferPid} ) {
162     #
163     # This is the smbclient child.
164     #
165     setpgrp 0,0;
166     if ( $t->{type} eq "restore" ) {
167     #
168     # For restores close the write end of the pipe,
169     # clone STDIN from RH, and STDERR to STDOUT
170     #
171     close($t->{pipeWH});
172     close(STDERR);
173     open(STDERR, ">&STDOUT");
174     close(STDIN);
175     open(STDIN, "<&$t->{pipeRH}");
176     } else {
177     #
178     # For backups close the read end of the pipe,
179     # clone STDOUT to WH, STDERR to STDOUT
180     #
181     close($t->{pipeRH});
182     close(STDERR);
183     open(STDERR, ">&STDOUT");
184     open(STDOUT, ">&$t->{pipeWH}");
185     }
186     #
187     # Run smbclient.
188     #
189     alarm(0);
190     $bpc->cmdExecOrEval($smbClientCmd, $args);
191     # should not be reached, but just in case...
192     $t->{_errStr} = "Can't exec $conf->{SmbClientPath}";
193     return;
194     }
195     my $str = "Running: " . $bpc->execCmd2ShellCmd(@$smbClientCmd) . "\n";
196     $t->{XferLOG}->write(\$str);
197     alarm($conf->{ClientTimeout});
198     $t->{_errStr} = undef;
199     return $logMsg;
200     }
201    
202     sub readOutput
203     {
204     my($t, $FDreadRef, $rout) = @_;
205     my $conf = $t->{conf};
206    
207     if ( vec($rout, fileno($t->{pipeSMB}), 1) ) {
208     my $mesg;
209     if ( sysread($t->{pipeSMB}, $mesg, 8192) <= 0 ) {
210     vec($$FDreadRef, fileno($t->{pipeSMB}), 1) = 0;
211     close($t->{pipeSMB});
212     } else {
213     $t->{smbOut} .= $mesg;
214     }
215     }
216     while ( $t->{smbOut} =~ /(.*?)[\n\r]+(.*)/s ) {
217     $_ = $1;
218     $t->{smbOut} = $2;
219     #
220     # ignore the log file time stamps from smbclient introduced
221     # in version 3.0.0 - don't even write them to the log file.
222     #
223     if ( m{^\[\d+/\d+/\d+ +\d+:\d+:\d+.*\] +(client/cli|lib/util_unistr).*\(\d+\)} ) {
224     $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 5 );
225     next;
226     }
227     #
228     # refresh our inactivity alarm
229     #
230     alarm($conf->{ClientTimeout}) if ( !$t->{abort} );
231     $t->{lastOutputLine} = $_ if ( !/^$/ );
232     #
233     # This section is highly dependent on the version of smbclient.
234     # If you upgrade Samba, make sure that these regexp are still valid.
235     #
236     if ( /^\s*(-?\d+) \(\s*\d+[.,]\d kb\/s\) (.*)$/ ) {
237     my $sambaFileSize = $1;
238     my $pcFileName = $2;
239     (my $fileName = $pcFileName) =~ s/\\/\//g;
240     $sambaFileSize += 1024 * 1024 * 4096 if ( $sambaFileSize < 0 );
241     $fileName =~ s/^\/*//;
242     $t->{byteCnt} += $sambaFileSize;
243     $t->{fileCnt}++;
244     $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 2 );
245     } elsif ( /restore tar file (.*) of size (\d+) bytes/ ) {
246     $t->{byteCnt} += $2;
247     $t->{fileCnt}++;
248     $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 );
249     } elsif ( /^\s*tar: dumped \d+ files/ ) {
250     $t->{xferOK} = 1;
251     $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 );
252     } elsif ( /^\s*tar: restored \d+ files/ ) {
253     $t->{xferOK} = 1;
254     $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 );
255     } elsif ( /^\s*read_socket_with_timeout: timeout read. /i ) {
256     $t->{hostAbort} = 1;
257     $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 );
258     } elsif ( /^code 0 listing /
259     || /^\s*code 0 opening /
260     || /^\s*abandoning restore/i
261     || /^\s*Error: Looping in FIND_NEXT/i
262     || /^\s*SUCCESS - 0/i
263     || /^\s*Call timed out: server did not respond/i
264     || /^\s*tree connect failed: ERRDOS - ERRnoaccess \(Access denied\.\)/
265     || /^\s*tree connect failed: NT_STATUS_BAD_NETWORK_NAME/
266     ) {
267     if ( $t->{hostError} eq "" ) {
268     $t->{XferLOG}->write(\"This backup will fail because: $_\n");
269     $t->{hostError} = $_;
270     }
271     $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 );
272     } elsif ( /^\s*NT_STATUS_ACCESS_DENIED listing (.*)/
273     || /^\s*ERRDOS - ERRnoaccess \(Access denied\.\) listing (.*)/ ) {
274     my $badDir = $1;
275     $badDir =~ s{\\}{/}g;
276     $badDir =~ s{/+}{/}g;
277     $badDir =~ s{/\*$}{};
278     if ( $t->{hostError} eq ""
279     && ($badDir eq "" || $t->{fileIncludeHash}{$badDir}) ) {
280     $t->{XferLOG}->write(\"This backup will fail because: $_\n");
281     $t->{hostError} ||= $_;
282     }
283     $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 0 );
284     } elsif ( /^\s*directory \\/i ) {
285     $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 2 );
286     } elsif ( /smb: \\>/
287     || /^\s*added interface/i
288     || /^\s*tarmode is now/i
289     || /^\s*Total bytes written/i
290     || /^\s*Domain=/i
291     || /^\([\d\.]* kb\/s\) \(average [\d\.]* kb\/s\)$/i
292     || /^\s*Getting files newer than/i
293     || /^\s*restore directory \\/i
294     || /^\s*Output is \/dev\/null/i
295     || /^\s*Timezone is/i
296     || /^\s*tar_re_search set/i
297     || /^\s*creating lame (up|low)case table/i
298     ) {
299     # ignore these messages
300     $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 );
301     } else {
302     $t->{xferErrCnt}++;
303     $t->{xferBadShareCnt}++ if ( /^ERRDOS - ERRbadshare/ );
304     $t->{xferBadFileCnt}++ if ( /^ERRDOS - ERRbadfile/ );
305     if ( $t->{xferErrCnt} > 50000 ) {
306     $t->logMsg(
307     "Too many smbtar errors ($t->{xferErrCnt})... giving up");
308     $t->{hostError} = "Too many smbtar errors ($t->{xferErrCnt})";
309     return;
310     }
311     if ( /^Error reading file (.*)\. Got 0 bytes/ ) {
312     #
313     # This happens when a Windoze application has
314     # locked the file. This is a particular problem
315     # with MS-Outlook. smbclient has already written
316     # the tar header to stdout, so all it can do is to
317     # write a dummy file with the correct size, but all
318     # zeros. BackupPC_tarExtract stores these
319     # zero-content files efficiently as a sparse file,
320     # or if compression is on the file will be small
321     # anyhow. After the dump is done we simply delete
322     # the file (it is no use) and try to link it to same
323     # file in any recent backup.
324     #
325     my $badFile = $1;
326     $badFile =~ s{\\}{/}g;
327     $badFile =~ s{^/}{};
328     push(@{$t->{badFiles}}, {
329     share => $t->{shareName},
330     file => $badFile
331     });
332     }
333     $t->{XferLOG}->write(\"$_\n") if ( $t->{logLevel} >= 1 );
334     }
335     }
336     return 1;
337     }
338    
339     sub abort
340     {
341     my($t, $reason) = @_;
342    
343     $t->{abort} = 1;
344     $t->{abortReason} = $reason;
345     }
346    
347     sub setSelectMask
348     {
349     my($t, $FDreadRef) = @_;
350    
351     vec($$FDreadRef, fileno($t->{pipeSMB}), 1) = 1;
352     }
353    
354     sub errStr
355     {
356     my($t) = @_;
357    
358     return $t->{_errStr};
359     }
360    
361     sub xferPid
362     {
363     my($t) = @_;
364    
365     return ($t->{xferPid});
366     }
367    
368     sub logMsg
369     {
370     my($t, $msg) = @_;
371    
372     push(@{$t->{_logMsg}}, $msg);
373     }
374    
375     sub logMsgGet
376     {
377     my($t) = @_;
378    
379     return shift(@{$t->{_logMsg}});
380     }
381    
382     #
383     # Returns a hash ref giving various status information about
384     # the transfer.
385     #
386     sub getStats
387     {
388     my($t) = @_;
389    
390     return { map { $_ => $t->{$_} }
391     qw(byteCnt fileCnt xferErrCnt xferBadShareCnt xferBadFileCnt
392     xferOK hostAbort hostError lastOutputLine)
393     };
394     }
395    
396     sub getBadFiles
397     {
398     my($t) = @_;
399    
400     return @{$t->{badFiles}};
401     }
402    
403     1;

  ViewVC Help
Powered by ViewVC 1.1.26