/[psinib]/psinib.pl
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 /psinib.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.16 - (hide annotations)
Sun Oct 26 12:55:56 2003 UTC (20 years, 6 months ago) by dpavlin
Branch: MAIN
Changes since 1.15: +23 -2 lines
File MIME type: text/plain
use tcp syn ping to check if host if up

1 dpavlin 1.1 #!/usr/bin/perl -w
2     #
3     # psinib - Perl Snapshot Is Not Incremental Backup
4     #
5     # written by Dobrica Pavlinusic <dpavlin@rot13.org> 2003-01-03
6     # released under GPL v2 or later.
7     #
8     # Backup SMB directories using file produced by LinNeighbourhood (or some
9     # other program [vi :-)] which produces file in format:
10     #
11     # smbmount service mountpoint options
12     #
13     #
14     # usage:
15 dpavlin 1.4 # $ psinib.pl mountscript
16 dpavlin 1.1
17     use strict 'vars';
18     use Data::Dumper;
19     use Net::Ping;
20     use POSIX qw(strftime);
21     use List::Compare;
22     use Filesys::SmbClient;
23     #use Taint;
24 dpavlin 1.2 use Fcntl qw(LOCK_EX LOCK_NB);
25 dpavlin 1.4 use Digest::MD5;
26     use File::Basename;
27 dpavlin 1.14 use Getopt::Long;
28 dpavlin 1.1
29     # configuration
30     my $LOG_TIME_FMT = '%Y-%m-%d %H:%M:%S'; # strftime format for logfile
31     my $DIR_TIME_FMT = '%Y%m%d'; # strftime format for backup dir
32    
33 dpavlin 1.16 # define timeout for ping
34     my $PING_TIMEOUT = 5;
35    
36 dpavlin 1.1 my $LOG = '/var/log/backup.log'; # add path here...
37 dpavlin 1.5 #$LOG = '/tmp/backup.log';
38 dpavlin 1.1
39     # store backups in which directory
40 dpavlin 1.13 my $BACKUP_DEST = '/backup/isis_backup';
41     #my $BACKUP_DEST = '/tmp/backup/';
42 dpavlin 1.1
43     # files to ignore in backup
44     my @ignore = ('.md5sum', '.backupignore', 'backupignore.txt');
45    
46     # open log
47 dpavlin 1.5 open(L, ">> $LOG") || die "can't open log $LOG: $!";
48 dpavlin 1.1 select((select(L), $|=1)[0]); # flush output
49 dpavlin 1.2
50     # make a lock on logfile
51    
52     my $c = 0;
53     {
54     flock L, LOCK_EX | LOCK_NB and last;
55     sleep 1;
56     redo if ++$c < 10;
57     # no response for 10 sec, bail out
58 dpavlin 1.12 xlog("ABORT","can't take lock on $LOG -- another $0 running?");
59 dpavlin 1.2 exit 1;
60     }
61 dpavlin 1.1
62     # taint path: nmblookup should be there!
63     $ENV{'PATH'} = "/usr/bin:/bin";
64    
65 dpavlin 1.14 my $use_ping = 1; # deault: use ping to verify that host is up
66    
67     my $result = GetOptions(
68     "ping!" => \$use_ping, "backupdest!" => \$BACKUP_DEST,
69     );
70    
71 dpavlin 1.1 my $mounts = shift @ARGV ||
72     'mountscript';
73     # die "usage: $0 mountscript";
74    
75    
76     my @in_backup; # shares which are backeduped this run
77    
78 dpavlin 1.16 # init Net::Ping object
79 dpavlin 1.14 my $ping;
80     if ($use_ping) {
81 dpavlin 1.16 $ping = new Net::Ping->new("syn", 2);
82 dpavlin 1.14 # ping will try tcp connect to netbios-ssn (139)
83     $ping->{port_num} = getservbyname("netbios-ssn", "tcp");
84     }
85 dpavlin 1.1
86 dpavlin 1.16 # do syn ping to cifs port
87     sub host_up {
88     my $ping = shift || return;
89     my $host_ip = shift || xlog("host_up didn't get IP");
90     my $timeout = shift;
91     return 1 if (! $use_ping);
92    
93     $ping->ping($host_ip,$timeout);
94     my $return = 0;
95    
96     while (my ($host,$rtt,$ip) = $ping->ack) {
97     xlog("","HOST: $host [$ip] ACKed in $rtt seconds");
98     $return = 1 if ($ip eq $host_ip);
99     }
100     return $return;
101     }
102    
103 dpavlin 1.1 my $backup_ok = 0;
104    
105     my $smb;
106     my %smb_atime;
107     my %smb_mtime;
108 dpavlin 1.4 my %file_md5;
109 dpavlin 1.1
110     open(M, $mounts) || die "can't open $mounts: $!";
111     while(<M>) {
112     chomp;
113     next if !/^\s*smbmount\s/;
114     my (undef,$share,undef,$opt) = split(/\s+/,$_,4);
115    
116 dpavlin 1.11 my ($user,$passwd,$workgroup,$ip);
117 dpavlin 1.1
118     foreach (split(/,/,$opt)) {
119     my ($n,$v) = split(/=/,$_,2);
120     if ($n =~ m/username/i) {
121     if ($v =~ m#^(.+)/(.+)%(.+)$#) {
122     ($user,$passwd,$workgroup) = ($1,$2,$3);
123     } elsif ($v =~ m#^(.+)/(.+)$#) {
124     ($user,$workgroup) = ($1,$2);
125     } elsif ($v =~ m#^(.+)%(.+)$#) {
126     ($user,$passwd) = ($1,$2);
127     } else {
128     $user = $v;
129     }
130     } elsif ($n =~ m#workgroup#i) {
131     $workgroup = $v;
132 dpavlin 1.11 } elsif ($n =~ m#ip#i) {
133     $ip = $v;
134 dpavlin 1.1 }
135     }
136    
137     push @in_backup,$share;
138    
139 dpavlin 1.4
140     my ($host,$dir,$date_dir) = share2host_dir($share);
141     my $bl = "$BACKUP_DEST/$host/$dir/latest"; # latest backup
142     my $bc = "$BACKUP_DEST/$host/$dir/$date_dir"; # current one
143     my $real_bl;
144 dpavlin 1.9 if (-l $bl) {
145 dpavlin 1.4 $real_bl=readlink($bl) || die "can't read link $bl: $!";
146     $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
147 dpavlin 1.9 if (-l $bc && $real_bl eq $bc) {
148 dpavlin 1.4 print "$share allready backuped...\n";
149     $backup_ok++;
150     next;
151     }
152    
153     }
154    
155    
156 dpavlin 1.1 print "working on $share\n";
157    
158 dpavlin 1.11 # try to nmblookup IP
159     $ip = get_ip($share) if (! $ip);
160 dpavlin 1.1
161     if ($ip) {
162     xlog($share,"IP is $ip");
163 dpavlin 1.16 if (host_up($ping, $ip,$PING_TIMEOUT)) {
164 dpavlin 1.12 if (snap_share($share,$user,$passwd,$workgroup)) {
165     $backup_ok++;
166     }
167 dpavlin 1.1 }
168     }
169     }
170     close(M);
171    
172     xlog("","$backup_ok backups completed of total ".($#in_backup+1)." this time (".int($backup_ok*100/($#in_backup+1))." %)");
173    
174     1;
175    
176     #-------------------------------------------------------------------------
177    
178 dpavlin 1.4
179 dpavlin 1.1 # get IP number from share
180     sub get_ip {
181     my $share = shift;
182    
183     my $host = $1 if ($share =~ m#//([^/]+)/#);
184    
185     my $ip = `nmblookup $host`;
186     if ($ip =~ m/(\d+\.\d+\.\d+\.\d+)\s$host/i) {
187     return $1;
188     }
189     }
190    
191 dpavlin 1.4
192     # write entry to screen and log
193 dpavlin 1.1 sub xlog {
194     my $share = shift;
195     my $t = strftime $LOG_TIME_FMT, localtime;
196     my $m = shift || '[no log entry]';
197     print STDERR $m,"\n";
198     print L "$t $share\t$m\n";
199     }
200    
201 dpavlin 1.7 # dump warn and dies into log
202     BEGIN { $SIG{'__WARN__'} = sub { xlog('WARN',$_[0]) ; warn $_[0] } }
203     BEGIN { $SIG{'__DIE__'} = sub { xlog('DIE',$_[0]) ; die $_[0] } }
204    
205 dpavlin 1.1
206 dpavlin 1.4 # split share name to host, dir and currnet date dir
207     sub share2host_dir {
208 dpavlin 1.1 my $share = shift;
209     my ($host,$dir);
210     if ($share =~ m#//([^/]+)/(.+)$#) {
211     ($host,$dir) = ($1,$2);
212     $dir =~ s/\W/_/g;
213     $dir =~ s/^_+//;
214     $dir =~ s/_+$//;
215     } else {
216     print "Can't parse share $share into host and directory!\n";
217     return;
218     }
219 dpavlin 1.4 return ($host,$dir,strftime $DIR_TIME_FMT, localtime);
220     }
221    
222 dpavlin 1.1
223 dpavlin 1.4 # make a snapshot of a share
224     sub snap_share {
225    
226     my $share = shift;
227    
228     my %param = ( debug => 0 );
229    
230 dpavlin 1.8 $param{username} = shift || warn "can't find username for share $share";
231     $param{password} = shift || warn "can't find passwod for share $share";
232     $param{workgroup} = shift || warn "can't find workgroup for share $share";
233 dpavlin 1.4
234     my ($host,$dir,$date_dir) = share2host_dir($share);
235 dpavlin 1.1
236     # latest backup directory
237     my $bl = "$BACKUP_DEST/$host/$dir/latest";
238     # current backup directory
239     my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";
240    
241     my $real_bl;
242 dpavlin 1.9 if (-l $bl) {
243 dpavlin 1.1 $real_bl=readlink($bl) || die "can't read link $bl: $!";
244     $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
245     } else {
246 dpavlin 1.7 print "no old backup, trying to find last backup, ";
247     if (opendir(BL_DIR, "$BACKUP_DEST/$host/$dir")) {
248     my @bl_dirs = sort grep { !/^\./ && -d "$BACKUP_DEST/$host/$dir/$_" } readdir(BL_DIR);
249     closedir(BL_DIR);
250     $real_bl=pop @bl_dirs;
251     print "using $real_bl as latest...\n";
252     $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
253     if ($real_bl eq $bc) {
254     xlog($share,"latest from today (possible partial backup)");
255     rename $real_bl,$real_bl.".partial" || warn "can't reaname partial backup: $!";
256     $real_bl .= ".partial";
257     }
258     } else {
259     print "this is first run...\n";
260     }
261 dpavlin 1.1 }
262    
263 dpavlin 1.9 if (-l $bc && $real_bl && $real_bl eq $bc) {
264 dpavlin 1.1 print "$share allready backuped...\n";
265 dpavlin 1.12 return 1;
266 dpavlin 1.1 }
267    
268     die "You should really create BACKUP_DEST [$BACKUP_DEST] by hand! " if (!-e $BACKUP_DEST);
269    
270     if (! -e "$BACKUP_DEST/$host") {
271     mkdir "$BACKUP_DEST/$host" || die "can't make dir for host $host, $BACKUP_DEST/$host: $!";
272     print "created host directory $BACKUP_DEST/$host...\n";
273     }
274    
275     if (! -e "$BACKUP_DEST/$host/$dir") {
276     mkdir "$BACKUP_DEST/$host/$dir" || die "can't make dir for share $share, $BACKUP_DEST/$host/$dir $!";
277     print "created dir for share $share, $BACKUP_DEST/$host/$dir...\n";
278     }
279    
280     mkdir $bc || die "can't make dir for current backup $bc: $!";
281    
282     my @dirs = ( "/" );
283     my @smb_dirs = ( "/" );
284    
285     my $transfer = 0; # bytes transfered over network
286    
287     # this will store all available files and sizes
288     my @files;
289     my %file_size;
290     my %file_atime;
291     my %file_mtime;
292 dpavlin 1.4 #my %file_md5;
293 dpavlin 1.13 %file_md5 = ();
294 dpavlin 1.1
295     my @smb_files;
296     my %smb_size;
297     #my %smb_atime;
298     #my %smb_mtime;
299    
300     sub norm_dir {
301     my $foo = shift;
302     my $prefix = shift;
303     $foo =~ s#//+#/#g;
304     $foo =~ s#/+$##g;
305     $foo =~ s#^/+##g;
306     return $prefix.$foo if ($prefix);
307     return $foo;
308     }
309    
310     # read local filesystem
311     my $di = 0;
312     while ($di <= $#dirs && $real_bl) {
313     my $d=$dirs[$di++];
314 dpavlin 1.7 opendir(DIR,"$real_bl/$d") || warn "opendir($real_bl/$d): $!\n";
315 dpavlin 1.1
316     # read .backupignore if exists
317 dpavlin 1.7 if (-f "$real_bl/$d/.backupignore") {
318     open(I,"$real_bl/$d/.backupignore");
319 dpavlin 1.1 while(<I>) {
320     chomp;
321     push @ignore,norm_dir("$d/$_");
322     }
323     close(I);
324 dpavlin 1.9 #print STDERR "ignore: ",join("|",@ignore),"\n";
325 dpavlin 1.7 link "$real_bl/$d/.backupignore","$bc/$d/.backupignore" ||
326     warn "can't copy $real_bl/$d/.backupignore to current backup dir: $!\n";
327 dpavlin 1.1 }
328    
329     # read .md5sum if exists
330 dpavlin 1.7 if (-f "$real_bl/$d/.md5sum") {
331     open(I,"$real_bl/$d/.md5sum");
332 dpavlin 1.1 while(<I>) {
333     chomp;
334     my ($md5,$f) = split(/\s+/,$_,2);
335     $file_md5{$f}=$md5;
336     }
337     close(I);
338     }
339    
340     my @clutter = readdir(DIR);
341     foreach my $f (@clutter) {
342     next if ($f eq '.');
343     next if ($f eq '..');
344     my $pr = norm_dir("$d/$f"); # path relative
345 dpavlin 1.7 my $pf = norm_dir("$d/$f","$real_bl/"); # path full
346 dpavlin 1.1 if (grep(/^\Q$pr\E$/,@ignore) == 0) {
347     if (-f $pf) {
348     push @files,$pr;
349     $file_size{$pr}=(stat($pf))[7];
350     $file_atime{$pr}=(stat($pf))[8];
351     $file_mtime{$pr}=(stat($pf))[9];
352     } elsif (-d $pf) {
353     push @dirs,$pr;
354     } else {
355 dpavlin 1.12 print STDERR "not file or directory: $pf\n";
356 dpavlin 1.1 }
357     } else {
358     print STDERR "ignored: $pr\n";
359     }
360     }
361     }
362    
363 dpavlin 1.12 # local dir always include /
364     xlog($share,($#files+1)." files and ".($#dirs)." dirs on local disk before backup");
365 dpavlin 1.1
366     # read smb filesystem
367    
368     xlog($share,"smb to $share as $param{username}/$param{workgroup}");
369    
370     # FIX: how to aviod creation of ~/.smb/smb.conf ?
371     $smb = new Filesys::SmbClient(%param) || die "SmbClient :$!\n";
372    
373     $di = 0;
374     while ($di <= $#smb_dirs) {
375 dpavlin 1.9 my $d=$smb_dirs[$di];
376 dpavlin 1.1 my $pf = norm_dir($d,"smb:$share/"); # path full
377 dpavlin 1.9 my $D = $smb->opendir($pf);
378     if (! $D) {
379 dpavlin 1.12 xlog($share,"FATAL: $share [$pf]: $!");
380 dpavlin 1.9 # remove failing dir
381     delete $smb_dirs[$di];
382 dpavlin 1.12 return 0; # failed
383 dpavlin 1.9 }
384     $di++;
385 dpavlin 1.1
386     my @clutter = $smb->readdir_struct($D);
387     foreach my $item (@clutter) {
388     my $f = $item->[1];
389     next if ($f eq '.');
390     next if ($f eq '..');
391     my $pr = norm_dir("$d/$f"); # path relative
392     my $pf = norm_dir("$d/$f","smb:$share/"); # path full
393     if (grep(/^\Q$pr\E$/,@ignore) == 0) {
394     if ($item->[0] == main::SMBC_FILE) {
395     push @smb_files,$pr;
396     $smb_size{$pr}=($smb->stat($pf))[7];
397     $smb_atime{$pr}=($smb->stat($pf))[10];
398     $smb_mtime{$pr}=($smb->stat($pf))[11];
399     } elsif ($item->[0] == main::SMBC_DIR) {
400     push @smb_dirs,$pr;
401     } else {
402 dpavlin 1.12 print STDERR "not file or directory [",$item->[0],"]: $pf\n";
403 dpavlin 1.1 }
404     } else {
405     print STDERR "smb ignored: $pr\n";
406     }
407     }
408     }
409    
410 dpavlin 1.12 xlog($share,($#smb_files+1)." files and ".($#smb_dirs)." dirs on remote share");
411 dpavlin 1.1
412     # sync dirs
413     my $lc = List::Compare->new(\@dirs, \@smb_dirs);
414    
415     my @dirs2erase = $lc->get_Lonly;
416     my @dirs2create = $lc->get_Ronly;
417     xlog($share,($#dirs2erase+1)." dirs to erase and ".($#dirs2create+1)." dirs to create");
418    
419     # create new dirs
420     foreach (sort @smb_dirs) {
421     mkdir "$bc/$_" || warn "mkdir $_: $!\n";
422     }
423    
424     # sync files
425     $lc = List::Compare->new(\@files, \@smb_files);
426    
427     my @files2erase = $lc->get_Lonly;
428     my @files2create = $lc->get_Ronly;
429     xlog($share,($#files2erase+1)." files to erase and ".($#files2create+1)." files to create");
430    
431     sub smb_copy {
432     my $smb = shift;
433    
434     my $from = shift;
435     my $to = shift;
436    
437    
438     my $l = 0;
439    
440     foreach my $f (@_) {
441     #print "smb_copy $from/$f -> $to/$f\n";
442 dpavlin 1.4 my $md5 = Digest::MD5->new;
443    
444 dpavlin 1.1 my $fd = $smb->open("$from/$f");
445     if (! $fd) {
446 dpavlin 1.12 xlog("WARNING","can't open smb file $from/$f: $!");
447     next;
448     }
449    
450     if (! open(F,"> $to/$f")) {
451     xlog("WARNING","can't open new file $to/$f: $!");
452 dpavlin 1.1 next;
453     }
454    
455     while (defined(my $b=$smb->read($fd,4096))) {
456     print F $b;
457     $l += length($b);
458 dpavlin 1.4 $md5->add($b);
459 dpavlin 1.1 }
460    
461     $smb->close($fd);
462     close(F);
463    
464 dpavlin 1.4 $file_md5{$f} = $md5->hexdigest;
465    
466 dpavlin 1.1 # FIX: this fails with -T
467     my ($a,$m) = ($smb->stat("$from/$f"))[10,11];
468     utime $a, $m, "$to/$f" ||
469     warn "can't update utime on $to/$f: $!\n";
470    
471     }
472     return $l;
473     }
474    
475     # copy new files
476     foreach (@files2create) {
477     $transfer += smb_copy($smb,"smb:$share",$bc,$_);
478     }
479    
480     my $size_sync = 0;
481     my $atime_sync = 0;
482     my $mtime_sync = 0;
483     my @sync_files;
484     my @ln_files;
485    
486     foreach ($lc->get_intersection) {
487    
488     my $f;
489    
490     if ($file_size{$_} != $smb_size{$_}) {
491     $f=$_;
492     $size_sync++;
493     }
494     if ($file_atime{$_} != $smb_atime{$_}) {
495     $f=$_;
496     $atime_sync++;
497     }
498     if ($file_mtime{$_} != $smb_mtime{$_}) {
499     $f=$_;
500     $mtime_sync++;
501     }
502    
503     if ($f) {
504     push @sync_files, $f;
505     } else {
506     push @ln_files, $_;
507     }
508     }
509    
510     xlog($share,($#sync_files+1)." files will be updated (diff: $size_sync size, $atime_sync atime, $mtime_sync mtime), ".($#ln_files+1)." will be linked.");
511    
512     foreach (@sync_files) {
513     $transfer += smb_copy($smb,"smb:$share",$bc,$_);
514     }
515    
516     xlog($share,"$transfer bytes transfered...");
517    
518     foreach (@ln_files) {
519 dpavlin 1.7 link "$real_bl/$_","$bc/$_" || warn "link $real_bl/$_ -> $bc/$_: $!\n";
520 dpavlin 1.1 }
521    
522     # remove files
523     foreach (sort @files2erase) {
524     unlink "$bc/$_" || warn "unlink $_: $!\n";
525 dpavlin 1.13 delete $file_md5{$_};
526 dpavlin 1.1 }
527    
528     # remove not needed dirs (after files)
529     foreach (sort @dirs2erase) {
530     rmdir "$bc/$_" || warn "rmdir $_: $!\n";
531     }
532    
533 dpavlin 1.4 # remove old .md5sum
534     foreach (sort @dirs) {
535     unlink "$bc/$_/.md5sum" if (-e "$bc/$_/.md5sum");
536     }
537    
538 dpavlin 1.13 # erase stale entries in .md5sum
539     my @md5_files = keys %file_md5;
540     $lc = List::Compare->new(\@md5_files, \@smb_files);
541     foreach my $file ($lc->get_Lonly) {
542     xlog("NOTICE","removing stale '$file' from .md5sum");
543     delete $file_md5{$file};
544     }
545    
546 dpavlin 1.4 # create .md5sum
547     my $last_dir = '';
548     my $md5;
549 dpavlin 1.7 foreach my $f (sort { $file_md5{$a} cmp $file_md5{$b} } keys %file_md5) {
550 dpavlin 1.4 my $dir = dirname($f);
551     my $file = basename($f);
552 dpavlin 1.10 #print "$f -- $dir / $file<--\n";
553 dpavlin 1.4 if ($dir ne $last_dir) {
554     close($md5) if ($md5);
555     open($md5, ">> $bc/$dir/.md5sum") || warn "can't create $bc/$dir/.md5sum: $!";
556     $last_dir = $dir;
557 dpavlin 1.7 #print STDERR "writing $last_dir/.md5sum\n";
558 dpavlin 1.4 }
559     print $md5 $file_md5{$f}," $file\n";
560     }
561 dpavlin 1.11 close($md5) if ($md5);
562 dpavlin 1.1
563     # create leatest link
564 dpavlin 1.7 #print "ln -s $bc $real_bl\n";
565 dpavlin 1.9 if (-l $bl) {
566 dpavlin 1.7 unlink $bl || warn "can't remove old latest symlink $bl: $!\n";
567     }
568     symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";
569    
570     # FIX: sanity check -- remove for speedup
571 dpavlin 1.9 xlog($share,"failed to create latest symlink $bl -> $bc...") if (readlink($bl) ne $bc || ! -l $bl);
572 dpavlin 1.1
573     xlog($share,"backup completed...");
574 dpavlin 1.12
575     return 1;
576 dpavlin 1.1 }
577 dpavlin 1.3 __END__
578 dpavlin 1.1 #-------------------------------------------------------------------------
579    
580 dpavlin 1.3
581     =head1 NAME
582    
583     psinib - Perl Snapshot Is Not Incremental Backup
584    
585     =head1 SYNOPSIS
586    
587     ./psinib.pl
588    
589     =head1 DESCRIPTION
590    
591     This script in current version support just backup of Samba (or Micro$oft
592     Winblowz) shares to central disk space. Central disk space is organized in
593     multiple directories named after:
594    
595     =over 4
596    
597     =item *
598     server which is sharing files to be backed up
599    
600     =item *
601     name of share on server
602    
603     =item *
604     dated directory named like standard ISO date format (YYYYMMDD).
605    
606     =back
607    
608     In each dated directory you will find I<snapshot> of all files on
609     exported share on that particular date.
610    
611     You can also use symlink I<latest> which will lead you to
612     last completed backup. After that you can use some other backup
613     software to transfer I<snapshot> to tape, CD-ROM or some other media.
614    
615     =head2 Design considerations
616    
617     Since taking of share snapshot every day requires a lot of disk space and
618     network bandwidth, B<psinib> uses several techniques to keep disk usage and
619     network traffic at acceptable level:
620    
621     =over 3
622    
623     =item - usage of hard-links to provide same files in each snapshot (as opposed
624     to have multiple copies of same file)
625    
626     =item - usage of file size, atime and mtime to find changes of files without
627     transferring whole file over network (just share browsing is transfered
628     over network)
629    
630     =item - usage of C<.md5sum> files (compatible with command-line utility
631 dpavlin 1.6 C<md5sum>) to keep file between snapshots hard-linked
632 dpavlin 1.3
633     =back
634    
635     =head1 CONFIGURATION
636    
637     This section is not yet written.
638    
639 dpavlin 1.4 =head1 HACKS, TRICKS, BUGS and LIMITATIONS
640    
641     This chapter will have all content that doesn't fit anywhere else.
642    
643     =head2 Can snapshots be more frequent than daily?
644 dpavlin 1.3
645     There is not real reason why you can't take snapshot more often than
646 dpavlin 1.4 once a day. Actually, if you are using B<psinib> to backup Windows
647     workstations you already know that they tend to come-and-go during the day
648     (reboots probably ;-), so running B<psinib> several times a day increases
649     your chance of having up-to-date backup (B<psinib> will not make multiple
650     snapshots for same day, nor will it update snapshot for current day if
651     it already exists).
652 dpavlin 1.3
653 dpavlin 1.4 However, changing B<psinib> to produce snapshots which are, for example, hourly
654 dpavlin 1.3 is a simple change of C<$DIR_TIME_FMT> which is currently set to
655     C<'%Y%m%d'> (see I<strftime> documentation for explanation of that
656     format). If you change that to C<'%Y%m%d-%H> you can have hourly snapshots
657     (if your network is fast enough, that is...). Also, some of messages in
658     program will sound strange, but other than that it should work.
659     I<You have been warned>.
660 dpavlin 1.4
661     =head2 Do I really need to share every directory which I want to snapshot?
662    
663     Actually, no. Due to usage of C<Filesys::SmbClient> module, you can also
664     specify sub-directory inside your share that you want to backup. This feature
665     is most useful if you want to use administrative shares (but, have in mind
666     that you have to enter your Win administrator password in unencrypted file on
667     disk to do that) like this:
668    
669     smbmount //server/c$/WinNT/fonts /mnt -o username=administrator%win
670    
671     After that you will get directories with snapshots like:
672    
673     server/c_WinNT_fonts/yyyymmdd/....
674    
675 dpavlin 1.6 =head2 Won't I run out of disk space?
676    
677     Of course you will... Snapshots and logfiles will eventually fill-up your disk.
678     However, you can do two things to stop that:
679    
680     =head3 Clean snapshort older than x days
681    
682     You can add following command to your C<root> crontab:
683    
684     find /backup/isis_backup -type d -mindepth 3 -maxdepth 3 -mtime +11 -exec rm -Rf {} \;
685    
686     I assume that C</backup/isis_backup> is directory in which are your snapshots
687     and that you don't want to keep snapshots older than 11 days (that's
688     C<-mtime +11> part of command).
689    
690     =head3 Rotate your logs
691    
692     I will leave that to you. I relay on GNU/Debian's C<logrotate> to do it for me.
693 dpavlin 1.7
694     =head2 What are I<YYYYMMDD.partial> directories?
695    
696     If there isn't I<latest> symlink in snapshot directory, it's preatty safe to
697     assume that previous backup from that day failed. So, that directory will
698     be renamed to I<YYYYMMDD.partial> and snapshot will be performed again,
699     linking same files (other alternative would be to erase that dir and find
700     second-oldest directory, but this seemed like more correct approach).
701 dpavlin 1.3
702 dpavlin 1.15 =head2 I can't connect to any share
703    
704     Please verify that nmblookup (which is part of samba package) is in /bin or
705     /usr/bin. Also verify that nmblookup returns IP address for your server
706     using:
707    
708     $ nmblookup tvhouse
709     querying tvhouse on 192.168.34.255
710     192.168.34.30 tvhouse<00>
711    
712     If you don't get any output, your samba might not listen to correct interface
713     (see interfaces in smb.conf).
714    
715 dpavlin 1.3 =head1 AUTHOR
716    
717     Dobrica Pavlinusic <dpavlin@rot13.org>
718    
719     L<http://www.rot13.org/~dpavlin/>
720    
721     =head1 LICENSE
722    
723     This product is licensed under GNU Public License (GPL) v2 or later.
724    
725     =cut

  ViewVC Help
Powered by ViewVC 1.1.26