/[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.12 - (hide annotations)
Sun Oct 12 16:13:38 2003 UTC (20 years, 6 months ago) by dpavlin
Branch: MAIN
Changes since 1.11: +22 -18 lines
File MIME type: text/plain
better logging, report correct number of dirs, don't create local file if
remote permission doesn't allow reading

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

  ViewVC Help
Powered by ViewVC 1.1.26