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

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

  ViewVC Help
Powered by ViewVC 1.1.26