/[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

Diff of /psinib.pl

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.2 by dpavlin, Sat Jan 4 12:14:54 2003 UTC revision 1.13 by dpavlin, Sun Oct 12 17:44:21 2003 UTC
# Line 12  Line 12 
12  #  #
13  #  #
14  # usage:  # usage:
15  #       $ backup.pl mountscript  #       $ psinib.pl mountscript
16    
17  use strict 'vars';  use strict 'vars';
18  use Data::Dumper;  use Data::Dumper;
# Line 22  use List::Compare; Line 22  use List::Compare;
22  use Filesys::SmbClient;  use Filesys::SmbClient;
23  #use Taint;  #use Taint;
24  use Fcntl qw(LOCK_EX LOCK_NB);  use Fcntl qw(LOCK_EX LOCK_NB);
25    use Digest::MD5;
26    use File::Basename;
27    
28  # configuration  # configuration
29  my $LOG_TIME_FMT = '%Y-%m-%d %H:%M:%S'; # strftime format for logfile  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  my $DIR_TIME_FMT = '%Y%m%d';            # strftime format for backup dir
31    
32  my $LOG = '/var/log/backup.log';        # add path here...  my $LOG = '/var/log/backup.log';        # add path here...
33  $LOG = '/tmp/backup.log';  #$LOG = '/tmp/backup.log';
34    
35  # store backups in which directory  # store backups in which directory
36  my $BACKUP_DEST = '/data/isis_backup';  my $BACKUP_DEST = '/backup/isis_backup';
37    #my $BACKUP_DEST = '/tmp/backup/';
38    
39  # files to ignore in backup  # files to ignore in backup
40  my @ignore = ('.md5sum', '.backupignore', 'backupignore.txt');  my @ignore = ('.md5sum', '.backupignore', 'backupignore.txt');
41    
42  # open log  # open log
43  open(L, "> $LOG") || die "can't open log $LOG: $!";  open(L, ">> $LOG") || die "can't open log $LOG: $!";
44  select((select(L), $|=1)[0]);   # flush output  select((select(L), $|=1)[0]);   # flush output
45    
46  # make a lock on logfile  # make a lock on logfile
# Line 48  my $c = 0; Line 51  my $c = 0;
51          sleep 1;          sleep 1;
52          redo if ++$c < 10;          redo if ++$c < 10;
53          # no response for 10 sec, bail out          # no response for 10 sec, bail out
54          print STDERR "can't take lock on $LOG -- another $0 running?\n";          xlog("ABORT","can't take lock on $LOG -- another $0 running?");
55          exit 1;          exit 1;
56  }  }
57    
# Line 62  my $mounts = shift @ARGV || Line 65  my $mounts = shift @ARGV ||
65    
66  my @in_backup;  # shares which are backeduped this run  my @in_backup;  # shares which are backeduped this run
67    
68  my $p = new Net::Ping->new();  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    
72  my $backup_ok = 0;  my $backup_ok = 0;
73    
74  my $smb;  my $smb;
75  my %smb_atime;  my %smb_atime;
76  my %smb_mtime;  my %smb_mtime;
77    my %file_md5;
78    
79  open(M, $mounts) || die "can't open $mounts: $!";  open(M, $mounts) || die "can't open $mounts: $!";
80  while(<M>) {  while(<M>) {
# Line 76  while(<M>) { Line 82  while(<M>) {
82          next if !/^\s*smbmount\s/;          next if !/^\s*smbmount\s/;
83          my (undef,$share,undef,$opt) = split(/\s+/,$_,4);          my (undef,$share,undef,$opt) = split(/\s+/,$_,4);
84    
85          my ($user,$passwd,$workgroup);          my ($user,$passwd,$workgroup,$ip);
86    
87          foreach (split(/,/,$opt)) {          foreach (split(/,/,$opt)) {
88                  my ($n,$v) = split(/=/,$_,2);                  my ($n,$v) = split(/=/,$_,2);
# Line 92  while(<M>) { Line 98  while(<M>) {
98                          }                          }
99                  } elsif ($n =~ m#workgroup#i) {                  } elsif ($n =~ m#workgroup#i) {
100                          $workgroup = $v;                          $workgroup = $v;
101                    } elsif ($n =~ m#ip#i) {
102                            $ip = $v;
103                  }                  }
104          }          }
105    
106          push @in_backup,$share;          push @in_backup,$share;
107    
108    
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            if (-l $bl) {
114                    $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                    if (-l $bc && $real_bl eq $bc) {
117                            print "$share allready backuped...\n";
118                            $backup_ok++;
119                            next;
120                    }
121    
122            }
123    
124    
125          print "working on $share\n";          print "working on $share\n";
126    
127          my $ip = get_ip($share);          # try to nmblookup IP
128            $ip = get_ip($share) if (! $ip);
129    
130          if ($ip) {          if ($ip) {
131                  xlog($share,"IP is $ip");                  xlog($share,"IP is $ip");
132                  if ($p->ping($ip)) {                  if ($p->ping($ip)) {
133                          snap_share($share,$user,$passwd,$workgroup);                          if (snap_share($share,$user,$passwd,$workgroup)) {
134                          $backup_ok++;                                  $backup_ok++;
135                            }
136                  }                  }
137          }          }
138  }  }
# Line 117  xlog("","$backup_ok backups completed of Line 144  xlog("","$backup_ok backups completed of
144    
145  #-------------------------------------------------------------------------  #-------------------------------------------------------------------------
146    
147    
148  # get IP number from share  # get IP number from share
149  sub get_ip {  sub get_ip {
150          my $share = shift;          my $share = shift;
# Line 129  sub get_ip { Line 157  sub get_ip {
157          }          }
158  }  }
159    
160    
161    # write entry to screen and log
162  sub xlog {  sub xlog {
163          my $share = shift;          my $share = shift;
164          my $t = strftime $LOG_TIME_FMT, localtime;          my $t = strftime $LOG_TIME_FMT, localtime;
# Line 137  sub xlog { Line 167  sub xlog {
167          print L "$t $share\t$m\n";          print L "$t $share\t$m\n";
168  }  }
169    
170  sub snap_share {  # dump warn and dies into log
171    BEGIN { $SIG{'__WARN__'} = sub { xlog('WARN',$_[0]) ; warn $_[0] } }
172          my $share = shift;  BEGIN { $SIG{'__DIE__'} = sub { xlog('DIE',$_[0]) ; die $_[0] } }
   
         my %param = ( debug => 0 );  
173    
         $param{username} = shift;  
         $param{password} = shift;  
         $param{workgroup} = shift;  
174    
175    # split share name to host, dir and currnet date dir
176    sub share2host_dir {
177            my $share = shift;
178          my ($host,$dir);          my ($host,$dir);
179          if ($share =~ m#//([^/]+)/(.+)$#) {          if ($share =~ m#//([^/]+)/(.+)$#) {
180                  ($host,$dir) = ($1,$2);                  ($host,$dir) = ($1,$2);
# Line 157  sub snap_share { Line 185  sub snap_share {
185                  print "Can't parse share $share into host and directory!\n";                  print "Can't parse share $share into host and directory!\n";
186                  return;                  return;
187          }          }
188            return ($host,$dir,strftime $DIR_TIME_FMT, localtime);
189    }
190    
191    
192          my $date_dir = strftime $DIR_TIME_FMT, localtime;  # make a snapshot of a share
193    sub snap_share {
194    
195            my $share = shift;
196    
197            my %param = ( debug => 0 );
198    
199            $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    
203            my ($host,$dir,$date_dir) = share2host_dir($share);
204    
205          # latest backup directory          # latest backup directory
206          my $bl = "$BACKUP_DEST/$host/$dir/latest";          my $bl = "$BACKUP_DEST/$host/$dir/latest";
# Line 166  sub snap_share { Line 208  sub snap_share {
208          my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";          my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";
209    
210          my $real_bl;          my $real_bl;
211          if (-e $bl) {          if (-l $bl) {
212                  $real_bl=readlink($bl) || die "can't read link $bl: $!";                  $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 "/");                  $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
214          } else {          } else {
215                  print "no old backup, this is first run...\n";                  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          }          }
231    
232          if (-e $bc && $real_bl && $real_bl eq $bc) {          if (-l $bc && $real_bl && $real_bl eq $bc) {
233                  print "$share allready backuped...\n";                  print "$share allready backuped...\n";
234                  return;                  return 1;
235          }          }
236    
237          die "You should really create BACKUP_DEST [$BACKUP_DEST] by hand! " if (!-e $BACKUP_DEST);          die "You should really create BACKUP_DEST [$BACKUP_DEST] by hand! " if (!-e $BACKUP_DEST);
# Line 202  sub snap_share { Line 258  sub snap_share {
258          my %file_size;          my %file_size;
259          my %file_atime;          my %file_atime;
260          my %file_mtime;          my %file_mtime;
261          my %file_md5;          #my %file_md5;
262            %file_md5 = ();
263    
264          my @smb_files;          my @smb_files;
265          my %smb_size;          my %smb_size;
266          #my %smb_atime;          #my %smb_atime;
267          #my %smb_mtime;          #my %smb_mtime;
         my %smb_md5;  
   
268    
269          sub norm_dir {          sub norm_dir {
270                  my $foo = shift;                  my $foo = shift;
# Line 225  sub snap_share { Line 280  sub snap_share {
280          my $di = 0;          my $di = 0;
281          while ($di <= $#dirs && $real_bl) {          while ($di <= $#dirs && $real_bl) {
282                  my $d=$dirs[$di++];                  my $d=$dirs[$di++];
283                  opendir(DIR,"$bl/$d") || warn "opendir($bl/$d): $!\n";                  opendir(DIR,"$real_bl/$d") || warn "opendir($real_bl/$d): $!\n";
284    
285                  # read .backupignore if exists                  # read .backupignore if exists
286                  if (-f "$bl/$d/.backupignore") {                  if (-f "$real_bl/$d/.backupignore") {
287                          open(I,"$bl/$d/.backupignore");                          open(I,"$real_bl/$d/.backupignore");
288                          while(<I>) {                          while(<I>) {
289                                  chomp;                                  chomp;
290                                  push @ignore,norm_dir("$d/$_");                                  push @ignore,norm_dir("$d/$_");
291                          }                          }
292                          close(I);                          close(I);
293  print STDERR "ignore: ",join("|",@ignore),"\n";  #print STDERR "ignore: ",join("|",@ignore),"\n";
294                          link "$bl/$d/.backupignore","$bc/$d/.backupignore" ||                          link "$real_bl/$d/.backupignore","$bc/$d/.backupignore" ||
295                                  warn "can't copy $bl/$d/.backupignore to current backup dir: $!\n";                                  warn "can't copy $real_bl/$d/.backupignore to current backup dir: $!\n";
296                  }                  }
297    
298                  # read .md5sum if exists                  # read .md5sum if exists
299                  if (-f "$bl/$d/.md5sum") {                  if (-f "$real_bl/$d/.md5sum") {
300                          open(I,"$bl/$d/.md5sum");                          open(I,"$real_bl/$d/.md5sum");
301                          while(<I>) {                          while(<I>) {
302                                  chomp;                                  chomp;
303                                  my ($md5,$f) = split(/\s+/,$_,2);                                  my ($md5,$f) = split(/\s+/,$_,2);
# Line 256  print STDERR "ignore: ",join("|",@ignore Line 311  print STDERR "ignore: ",join("|",@ignore
311                          next if ($f eq '.');                          next if ($f eq '.');
312                          next if ($f eq '..');                          next if ($f eq '..');
313                          my $pr = norm_dir("$d/$f");     # path relative                          my $pr = norm_dir("$d/$f");     # path relative
314                          my $pf = norm_dir("$d/$f","$bl/");      # path full                          my $pf = norm_dir("$d/$f","$real_bl/"); # path full
315                          if (grep(/^\Q$pr\E$/,@ignore) == 0) {                          if (grep(/^\Q$pr\E$/,@ignore) == 0) {
316                                  if (-f $pf) {                                  if (-f $pf) {
317                                          push @files,$pr;                                          push @files,$pr;
# Line 266  print STDERR "ignore: ",join("|",@ignore Line 321  print STDERR "ignore: ",join("|",@ignore
321                                  } elsif (-d $pf) {                                  } elsif (-d $pf) {
322                                          push @dirs,$pr;                                          push @dirs,$pr;
323                                  } else {                                  } else {
324                                          print STDERR "unknown type: $pf\n";                                          print STDERR "not file or directory: $pf\n";
325                                  }                                  }
326                          } else {                          } else {
327                                  print STDERR "ignored: $pr\n";                                  print STDERR "ignored: $pr\n";
# Line 274  print STDERR "ignore: ",join("|",@ignore Line 329  print STDERR "ignore: ",join("|",@ignore
329                  }                  }
330          }          }
331    
332          xlog($share,($#files+1)." files and ".($#dirs+1)." dirs on local disk before backup");          # local dir always include /
333            xlog($share,($#files+1)." files and ".($#dirs)." dirs on local disk before backup");
334    
335          # read smb filesystem          # read smb filesystem
336    
# Line 285  print STDERR "ignore: ",join("|",@ignore Line 341  print STDERR "ignore: ",join("|",@ignore
341    
342          $di = 0;          $di = 0;
343          while ($di <= $#smb_dirs) {          while ($di <= $#smb_dirs) {
344                  my $d=$smb_dirs[$di++];                  my $d=$smb_dirs[$di];
345                  my $pf = norm_dir($d,"smb:$share/");    # path full                  my $pf = norm_dir($d,"smb:$share/");    # path full
346                  my $D = $smb->opendir($pf) || warn "smb->opendir($pf): $!\n";                  my $D = $smb->opendir($pf);
347                    if (! $D) {
348                            xlog($share,"FATAL: $share [$pf]: $!");
349                            # remove failing dir
350                            delete $smb_dirs[$di];
351                            return 0;                       # failed
352                    }
353                    $di++;
354    
355                  my @clutter = $smb->readdir_struct($D);                  my @clutter = $smb->readdir_struct($D);
356                  foreach my $item (@clutter) {                  foreach my $item (@clutter) {
# Line 305  print STDERR "ignore: ",join("|",@ignore Line 368  print STDERR "ignore: ",join("|",@ignore
368                                  } elsif ($item->[0] == main::SMBC_DIR) {                                  } elsif ($item->[0] == main::SMBC_DIR) {
369                                          push @smb_dirs,$pr;                                          push @smb_dirs,$pr;
370                                  } else {                                  } else {
371                                          print STDERR "unknown type: $pf\n";                                          print STDERR "not file or directory [",$item->[0],"]: $pf\n";
372                                  }                                  }
373                          } else {                          } else {
374                                  print STDERR "smb ignored: $pr\n";                                  print STDERR "smb ignored: $pr\n";
# Line 313  print STDERR "ignore: ",join("|",@ignore Line 376  print STDERR "ignore: ",join("|",@ignore
376                  }                  }
377          }          }
378    
379          xlog($share,($#smb_files+1)." files and ".($#smb_dirs+1)." dirs on remote share");          xlog($share,($#smb_files+1)." files and ".($#smb_dirs)." dirs on remote share");
380    
381          # sync dirs          # sync dirs
382          my $lc = List::Compare->new(\@dirs, \@smb_dirs);          my $lc = List::Compare->new(\@dirs, \@smb_dirs);
# Line 345  print STDERR "ignore: ",join("|",@ignore Line 408  print STDERR "ignore: ",join("|",@ignore
408                                    
409                  foreach my $f (@_) {                  foreach my $f (@_) {
410  #print "smb_copy $from/$f -> $to/$f\n";  #print "smb_copy $from/$f -> $to/$f\n";
411                          if (! open(F,"> $to/$f")) {                          my $md5 = Digest::MD5->new;
                                 print STDERR "can't open new file $to/$f: $!\n";  
                                 next;  
                         }  
412    
413                          my $fd = $smb->open("$from/$f");                          my $fd = $smb->open("$from/$f");
414                          if (! $fd) {                          if (! $fd) {
415                                  print STDERR "can't open smb file $from/$f: $!\n";                                  xlog("WARNING","can't open smb file $from/$f: $!");
416                                    next;
417                            }
418    
419                            if (! open(F,"> $to/$f")) {
420                                    xlog("WARNING","can't open new file $to/$f: $!");
421                                  next;                                  next;
422                          }                          }
423    
424                          while (defined(my $b=$smb->read($fd,4096))) {                          while (defined(my $b=$smb->read($fd,4096))) {
425                                  print F $b;                                  print F $b;
426                                  $l += length($b);                                  $l += length($b);
427                                    $md5->add($b);
428                          }                          }
429    
430                          $smb->close($fd);                          $smb->close($fd);
431                          close(F);                          close(F);
432    
433                            $file_md5{$f} = $md5->hexdigest;
434    
435                          # FIX: this fails with -T                          # FIX: this fails with -T
436                          my ($a,$m) = ($smb->stat("$from/$f"))[10,11];                          my ($a,$m) = ($smb->stat("$from/$f"))[10,11];
437                          utime $a, $m, "$to/$f" ||                          utime $a, $m, "$to/$f" ||
# Line 417  print STDERR "ignore: ",join("|",@ignore Line 485  print STDERR "ignore: ",join("|",@ignore
485          xlog($share,"$transfer bytes transfered...");          xlog($share,"$transfer bytes transfered...");
486    
487          foreach (@ln_files) {          foreach (@ln_files) {
488                  link "$bl/$_","$bc/$_" || warn "link $bl/$_ -> $bc/$_: $!\n";                  link "$real_bl/$_","$bc/$_" || warn "link $real_bl/$_ -> $bc/$_: $!\n";
489          }          }
490    
491          # remove files          # remove files
492          foreach (sort @files2erase) {          foreach (sort @files2erase) {
493                  unlink "$bc/$_" || warn "unlink $_: $!\n";                  unlink "$bc/$_" || warn "unlink $_: $!\n";
494                    delete $file_md5{$_};
495          }          }
496    
497          # remove not needed dirs (after files)          # remove not needed dirs (after files)
# Line 430  print STDERR "ignore: ",join("|",@ignore Line 499  print STDERR "ignore: ",join("|",@ignore
499                  rmdir "$bc/$_" || warn "rmdir $_: $!\n";                  rmdir "$bc/$_" || warn "rmdir $_: $!\n";
500          }          }
501    
502            # remove old .md5sum
503          # FIX: create .md5sum          foreach (sort @dirs) {
504                    unlink "$bc/$_/.md5sum" if (-e "$bc/$_/.md5sum");
505            }
506    
507            # erase stale entries in .md5sum
508            my @md5_files = keys %file_md5;
509            $lc = List::Compare->new(\@md5_files, \@smb_files);
510            foreach my $file ($lc->get_Lonly) {
511                    xlog("NOTICE","removing stale '$file' from .md5sum");
512                    delete $file_md5{$file};
513            }
514    
515            # create .md5sum
516            my $last_dir = '';
517            my $md5;
518            foreach my $f (sort { $file_md5{$a} cmp $file_md5{$b} } keys %file_md5) {
519                    my $dir = dirname($f);
520                    my $file = basename($f);
521    #print "$f -- $dir / $file<--\n";
522                    if ($dir ne $last_dir) {
523                            close($md5) if ($md5);
524                            open($md5, ">> $bc/$dir/.md5sum") || warn "can't create $bc/$dir/.md5sum: $!";
525                            $last_dir = $dir;
526    #print STDERR "writing $last_dir/.md5sum\n";
527                    }
528                    print $md5 $file_md5{$f},"  $file\n";
529            }
530            close($md5) if ($md5);
531    
532          # create leatest link          # create leatest link
533    #print "ln -s $bc $real_bl\n";
534            if (-l $bl) {
535                    unlink $bl || warn "can't remove old latest symlink $bl: $!\n";
536            }
537          symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";          symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";
538    
539            # FIX: sanity check -- remove for speedup
540            xlog($share,"failed to create latest symlink $bl -> $bc...") if (readlink($bl) ne $bc || ! -l $bl);
541    
542          xlog($share,"backup completed...");          xlog($share,"backup completed...");
 }  
543    
544            return 1;
545    }
546    __END__
547  #-------------------------------------------------------------------------  #-------------------------------------------------------------------------
548    
549    
550    =head1 NAME
551    
552    psinib - Perl Snapshot Is Not Incremental Backup
553    
554    =head1 SYNOPSIS
555    
556    ./psinib.pl
557    
558    =head1 DESCRIPTION
559    
560    This script in current version support just backup of Samba (or Micro$oft
561    Winblowz) shares to central disk space. Central disk space is organized in
562    multiple directories named after:
563    
564    =over 4
565    
566    =item *
567    server which is sharing files to be backed up
568    
569    =item *
570    name of share on server
571    
572    =item *
573    dated directory named like standard ISO date format (YYYYMMDD).
574    
575    =back
576    
577    In each dated directory you will find I<snapshot> of all files on
578    exported share on that particular date.
579    
580    You can also use symlink I<latest> which will lead you to
581    last completed backup. After that you can use some other backup
582    software to transfer I<snapshot> to tape, CD-ROM or some other media.
583    
584    =head2 Design considerations
585    
586    Since taking of share snapshot every day requires a lot of disk space and
587    network bandwidth, B<psinib> uses several techniques to keep disk usage and
588    network traffic at acceptable level:
589    
590    =over 3
591    
592    =item - usage of hard-links to provide same files in each snapshot (as opposed
593    to have multiple copies of same file)
594    
595    =item - usage of file size, atime and mtime to find changes of files without
596    transferring whole file over network (just share browsing is transfered
597    over network)
598    
599    =item - usage of C<.md5sum> files (compatible with command-line utility
600    C<md5sum>) to keep file between snapshots hard-linked
601    
602    =back
603    
604    =head1 CONFIGURATION
605    
606    This section is not yet written.
607    
608    =head1 HACKS, TRICKS, BUGS and LIMITATIONS
609    
610    This chapter will have all content that doesn't fit anywhere else.
611    
612    =head2 Can snapshots be more frequent than daily?
613    
614    There is not real reason why you can't take snapshot more often than
615    once a day. Actually, if you are using B<psinib> to backup Windows
616    workstations you already know that they tend to come-and-go during the day
617    (reboots probably ;-), so running B<psinib> several times a day increases
618    your chance of having up-to-date backup (B<psinib> will not make multiple
619    snapshots for same day, nor will it update snapshot for current day if
620    it already exists).
621    
622    However, changing B<psinib> to produce snapshots which are, for example, hourly
623    is a simple change of C<$DIR_TIME_FMT> which is currently set to
624    C<'%Y%m%d'> (see I<strftime> documentation for explanation of that
625    format). If you change that to C<'%Y%m%d-%H> you can have hourly snapshots
626    (if your network is fast enough, that is...). Also, some of messages in
627    program will sound strange, but other than that it should work.
628    I<You have been warned>.
629    
630    =head2 Do I really need to share every directory which I want to snapshot?
631    
632    Actually, no. Due to usage of C<Filesys::SmbClient> module, you can also
633    specify sub-directory inside your share that you want to backup. This feature
634    is most useful if you want to use administrative shares (but, have in mind
635    that you have to enter your Win administrator password in unencrypted file on
636    disk to do that) like this:
637    
638            smbmount //server/c$/WinNT/fonts  /mnt  -o username=administrator%win  
639    
640    After that you will get directories with snapshots like:
641    
642            server/c_WinNT_fonts/yyyymmdd/....
643    
644    =head2 Won't I run out of disk space?
645    
646    Of course you will... Snapshots and logfiles will eventually fill-up your disk.
647    However, you can do two things to stop that:
648    
649    =head3 Clean snapshort older than x days
650    
651    You can add following command to your C<root> crontab:
652    
653            find /backup/isis_backup -type d -mindepth 3 -maxdepth 3 -mtime +11 -exec rm -Rf {} \;
654    
655    I assume that C</backup/isis_backup> is directory in which are your snapshots
656    and that you don't want to keep snapshots older than 11 days (that's
657    C<-mtime +11> part of command).
658    
659    =head3 Rotate your logs
660    
661    I will leave that to you. I relay on GNU/Debian's C<logrotate> to do it for me.
662    
663    =head2 What are I<YYYYMMDD.partial> directories?
664    
665    If there isn't I<latest> symlink in snapshot directory, it's preatty safe to
666    assume that previous backup from that day failed. So, that directory will
667    be renamed to I<YYYYMMDD.partial> and snapshot will be performed again,
668    linking same files (other alternative would be to erase that dir and find
669    second-oldest directory, but this seemed like more correct approach).
670    
671    =head1 AUTHOR
672    
673    Dobrica Pavlinusic <dpavlin@rot13.org>
674    
675    L<http://www.rot13.org/~dpavlin/>
676    
677    =head1 LICENSE
678    
679    This product is licensed under GNU Public License (GPL) v2 or later.
680    
681    =cut

Legend:
Removed from v.1.2  
changed lines
  Added in v.1.13

  ViewVC Help
Powered by ViewVC 1.1.26