/[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.19 by dpavlin, Mon Oct 27 19:07:32 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 {                  if (! -e $real_bl) {
253                  print "no old backup, this is first run...\n";                          xlog($share,"latest link $bl -> $real_bl not valid, removing it");
254                            unlink $bl || die "can't remove link $bl: $!";
255                            undef $real_bl;
256                    }
257            }
258            if (! $real_bl) {
259                    xlog($share,"no old backup, trying to find last backup");
260                    if (opendir(BL_DIR, "$BACKUP_DEST/$host/$dir")) {
261                            my @bl_dirs = sort grep { !/^\./ && -d "$BACKUP_DEST/$host/$dir/$_" } readdir(BL_DIR);
262                            closedir(BL_DIR);
263                            $real_bl=pop @bl_dirs;
264                            xlog($share,"using $real_bl as latest...");
265                            $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
266                            if ($real_bl eq $bc) {
267                                    xlog($share,"latest from today (possible partial backup)");
268                                    rename $real_bl,$real_bl.".partial" || warn "can't reaname partial backup: $!";
269                                    $real_bl .= ".partial";
270                            }
271                    } else {
272                            xlog($share,"this is first run...");
273                    }
274          }          }
275    
276          if (-e $bc && $real_bl && $real_bl eq $bc) {          if (-l $bc && $real_bl && $real_bl eq $bc) {
277                  print "$share allready backuped...\n";                  xlog($share,"allready backuped...");
278                  return;                  return 1;
279          }          }
280    
281          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);
282    
283          if (! -e "$BACKUP_DEST/$host") {          if (! -e "$BACKUP_DEST/$host") {
284                  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: $!";
285                  print "created host directory $BACKUP_DEST/$host...\n";                  xlog($share,"created host directory $BACKUP_DEST/$host...");
286          }          }
287    
288          if (! -e "$BACKUP_DEST/$host/$dir") {          if (! -e "$BACKUP_DEST/$host/$dir") {
289                  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 $!";
290                  print "created dir for share $share, $BACKUP_DEST/$host/$dir...\n";                  xlog($share,"created dir for this share $BACKUP_DEST/$host/$dir...");
291          }          }
292    
293          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 302  sub snap_share {
302          my %file_size;          my %file_size;
303          my %file_atime;          my %file_atime;
304          my %file_mtime;          my %file_mtime;
305          my %file_md5;          #my %file_md5;
306            %file_md5 = ();
307    
308          my @smb_files;          my @smb_files;
309          my %smb_size;          my %smb_size;
310          #my %smb_atime;          #my %smb_atime;
311          #my %smb_mtime;          #my %smb_mtime;
         my %smb_md5;  
   
312    
313          sub norm_dir {          sub norm_dir {
314                  my $foo = shift;                  my $foo = shift;
# Line 225  sub snap_share { Line 324  sub snap_share {
324          my $di = 0;          my $di = 0;
325          while ($di <= $#dirs && $real_bl) {          while ($di <= $#dirs && $real_bl) {
326                  my $d=$dirs[$di++];                  my $d=$dirs[$di++];
327                  opendir(DIR,"$bl/$d") || warn "opendir($bl/$d): $!\n";                  opendir(DIR,"$real_bl/$d") || warn "opendir($real_bl/$d): $!\n";
328    
329                  # read .backupignore if exists                  # read .backupignore if exists
330                  if (-f "$bl/$d/.backupignore") {                  if (-f "$real_bl/$d/.backupignore") {
331                          open(I,"$bl/$d/.backupignore");                          open(I,"$real_bl/$d/.backupignore");
332                          while(<I>) {                          while(<I>) {
333                                  chomp;                                  chomp;
334                                  push @ignore,norm_dir("$d/$_");                                  push @ignore,norm_dir("$d/$_");
335                          }                          }
336                          close(I);                          close(I);
337  print STDERR "ignore: ",join("|",@ignore),"\n";  #print STDERR "ignore: ",join("|",@ignore),"\n";
338                          link "$bl/$d/.backupignore","$bc/$d/.backupignore" ||                          link "$real_bl/$d/.backupignore","$bc/$d/.backupignore" ||
339                                  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";
340                  }                  }
341    
342                  # read .md5sum if exists                  # read .md5sum if exists
343                  if (-f "$bl/$d/.md5sum") {                  if (-f "$real_bl/$d/.md5sum") {
344                          open(I,"$bl/$d/.md5sum");                          open(I,"$real_bl/$d/.md5sum");
345                          while(<I>) {                          while(<I>) {
346                                  chomp;                                  chomp;
347                                  my ($md5,$f) = split(/\s+/,$_,2);                                  my ($md5,$f) = split(/\s+/,$_,2);
# Line 256  print STDERR "ignore: ",join("|",@ignore Line 355  print STDERR "ignore: ",join("|",@ignore
355                          next if ($f eq '.');                          next if ($f eq '.');
356                          next if ($f eq '..');                          next if ($f eq '..');
357                          my $pr = norm_dir("$d/$f");     # path relative                          my $pr = norm_dir("$d/$f");     # path relative
358                          my $pf = norm_dir("$d/$f","$bl/");      # path full                          my $pf = norm_dir("$d/$f","$real_bl/"); # path full
359                          if (grep(/^\Q$pr\E$/,@ignore) == 0) {                          if (grep(/^\Q$pr\E$/,@ignore) == 0) {
360                                  if (-f $pf) {                                  if (-f $pf) {
361                                          push @files,$pr;                                          push @files,$pr;
# Line 266  print STDERR "ignore: ",join("|",@ignore Line 365  print STDERR "ignore: ",join("|",@ignore
365                                  } elsif (-d $pf) {                                  } elsif (-d $pf) {
366                                          push @dirs,$pr;                                          push @dirs,$pr;
367                                  } else {                                  } else {
368                                          print STDERR "unknown type: $pf\n";                                          xlog($share,"not file or directory: $pf",0);
369                                  }                                  }
370                          } else {                          } else {
371                                  print STDERR "ignored: $pr\n";                                  xlog($share,"ignored: $pr");
372                          }                          }
373                  }                  }
374          }          }
375    
376          xlog($share,($#files+1)." files and ".($#dirs+1)." dirs on local disk before backup");          # local dir always include /
377            xlog($share,($#files+1)." files and ".($#dirs)." dirs on local disk before backup");
378    
379          # read smb filesystem          # read smb filesystem
380    
# Line 285  print STDERR "ignore: ",join("|",@ignore Line 385  print STDERR "ignore: ",join("|",@ignore
385    
386          $di = 0;          $di = 0;
387          while ($di <= $#smb_dirs) {          while ($di <= $#smb_dirs) {
388                  my $d=$smb_dirs[$di++];                  my $d=$smb_dirs[$di];
389                  my $pf = norm_dir($d,"smb:$share/");    # path full                  my $pf = norm_dir($d,"smb:$share/");    # path full
390                  my $D = $smb->opendir($pf) || warn "smb->opendir($pf): $!\n";                  my $D = $smb->opendir($pf);
391                    if (! $D) {
392                            xlog($share,"FATAL: $share [$pf] as $param{username}/$param{workgroup}: $!",0);
393                            # remove failing dir
394                            delete $smb_dirs[$di];
395                            return 0;                       # failed
396                    }
397                    $di++;
398    
399                  my @clutter = $smb->readdir_struct($D);                  my @clutter = $smb->readdir_struct($D);
400                  foreach my $item (@clutter) {                  foreach my $item (@clutter) {
# Line 305  print STDERR "ignore: ",join("|",@ignore Line 412  print STDERR "ignore: ",join("|",@ignore
412                                  } elsif ($item->[0] == main::SMBC_DIR) {                                  } elsif ($item->[0] == main::SMBC_DIR) {
413                                          push @smb_dirs,$pr;                                          push @smb_dirs,$pr;
414                                  } else {                                  } else {
415                                          print STDERR "unknown type: $pf\n";                                          xlog($share,"not file or directory [".$item->[0]."]: $pf",0);
416                                  }                                  }
417                          } else {                          } else {
418                                  print STDERR "smb ignored: $pr\n";                                  xlog($share,"smb ignored: $pr");
419                          }                          }
420                  }                  }
421          }          }
422    
423          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");
424    
425          # sync dirs          # sync dirs
426          my $lc = List::Compare->new(\@dirs, \@smb_dirs);          my $lc = List::Compare->new(\@dirs, \@smb_dirs);
# Line 345  print STDERR "ignore: ",join("|",@ignore Line 452  print STDERR "ignore: ",join("|",@ignore
452                                    
453                  foreach my $f (@_) {                  foreach my $f (@_) {
454  #print "smb_copy $from/$f -> $to/$f\n";  #print "smb_copy $from/$f -> $to/$f\n";
455                          if (! open(F,"> $to/$f")) {                          my $md5 = Digest::MD5->new;
                                 print STDERR "can't open new file $to/$f: $!\n";  
                                 next;  
                         }  
456    
457                          my $fd = $smb->open("$from/$f");                          my $fd = $smb->open("$from/$f");
458                          if (! $fd) {                          if (! $fd) {
459                                  print STDERR "can't open smb file $from/$f: $!\n";                                  xlog("WARNING","can't open smb file $from/$f: $!");
460                                    next;
461                            }
462    
463                            if (! open(F,"> $to/$f")) {
464                                    xlog("WARNING","can't open new file $to/$f: $!");
465                                  next;                                  next;
466                          }                          }
467    
468                          while (defined(my $b=$smb->read($fd,4096))) {                          while (defined(my $b=$smb->read($fd,4096))) {
469                                  print F $b;                                  print F $b;
470                                  $l += length($b);                                  $l += length($b);
471                                    $md5->add($b);
472                          }                          }
473    
474                          $smb->close($fd);                          $smb->close($fd);
475                          close(F);                          close(F);
476    
477                            $file_md5{$f} = $md5->hexdigest;
478    
479                          # FIX: this fails with -T                          # FIX: this fails with -T
480                          my ($a,$m) = ($smb->stat("$from/$f"))[10,11];                          my ($a,$m) = ($smb->stat("$from/$f"))[10,11];
481                          utime $a, $m, "$to/$f" ||                          utime $a, $m, "$to/$f" ||
# Line 417  print STDERR "ignore: ",join("|",@ignore Line 529  print STDERR "ignore: ",join("|",@ignore
529          xlog($share,"$transfer bytes transfered...");          xlog($share,"$transfer bytes transfered...");
530    
531          foreach (@ln_files) {          foreach (@ln_files) {
532                  link "$bl/$_","$bc/$_" || warn "link $bl/$_ -> $bc/$_: $!\n";                  link "$real_bl/$_","$bc/$_" || warn "link $real_bl/$_ -> $bc/$_: $!\n";
533          }          }
534    
535          # remove files          # remove files
536          foreach (sort @files2erase) {          foreach (sort @files2erase) {
537                  unlink "$bc/$_" || warn "unlink $_: $!\n";                  unlink "$bc/$_" || warn "unlink $_: $!\n";
538                    delete $file_md5{$_};
539          }          }
540    
541          # remove not needed dirs (after files)          # remove not needed dirs (after files)
# Line 430  print STDERR "ignore: ",join("|",@ignore Line 543  print STDERR "ignore: ",join("|",@ignore
543                  rmdir "$bc/$_" || warn "rmdir $_: $!\n";                  rmdir "$bc/$_" || warn "rmdir $_: $!\n";
544          }          }
545    
546            # remove old .md5sum
547          # FIX: create .md5sum          foreach (sort @dirs) {
548                    unlink "$bc/$_/.md5sum" if (-e "$bc/$_/.md5sum");
549            }
550    
551            # erase stale entries in .md5sum
552            my @md5_files = keys %file_md5;
553            $lc = List::Compare->new(\@md5_files, \@smb_files);
554            foreach my $file ($lc->get_Lonly) {
555                    xlog("NOTICE","removing stale '$file' from .md5sum");
556                    delete $file_md5{$file};
557            }
558    
559            # create .md5sum
560            my $last_dir = '';
561            my $md5;
562            foreach my $f (sort { $file_md5{$a} cmp $file_md5{$b} } keys %file_md5) {
563                    my $dir = dirname($f);
564                    my $file = basename($f);
565    #print "$f -- $dir / $file<--\n";
566                    if ($dir ne $last_dir) {
567                            close($md5) if ($md5);
568                            open($md5, ">> $bc/$dir/.md5sum") || warn "can't create $bc/$dir/.md5sum: $!";
569                            $last_dir = $dir;
570    #print STDERR "writing $last_dir/.md5sum\n";
571                    }
572                    print $md5 $file_md5{$f},"  $file\n";
573            }
574            close($md5) if ($md5);
575    
576          # create leatest link          # create leatest link
577    #print "ln -s $bc $real_bl\n";
578            if (-l $bl) {
579                    unlink $bl || warn "can't remove old latest symlink $bl: $!\n";
580            }
581          symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";          symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";
582    
583            # FIX: sanity check -- remove for speedup
584            xlog($share,"failed to create latest symlink $bl -> $bc...") if (readlink($bl) ne $bc || ! -l $bl);
585    
586          xlog($share,"backup completed...");          xlog($share,"backup completed...");
 }  
587    
588            return 1;
589    }
590    __END__
591  #-------------------------------------------------------------------------  #-------------------------------------------------------------------------
592    
593    
594    =head1 NAME
595    
596    psinib - Perl Snapshot Is Not Incremental Backup
597    
598    =head1 SYNOPSIS
599    
600    ./psinib.pl [OPTION]... [mount script]
601    
602    =head1 DESCRIPTION
603    
604    Option can be one of more of following:
605    
606    =over 8
607    
608    =item C<--backupdest=dir>
609    
610    Specify backup destination directory (defaults is /data/
611    
612    =item C<--noping>
613    
614    Don't use ping to check if host is up (default is ti use tcp syn to cifs
615    port)
616    
617    =item C<--verbose -v>
618    
619    Increase verbosity level. Defailt is 1 which prints moderate amount of data
620    on STDOUT and STDERR.
621    
622    =item C<--quiet -q>
623    
624    Decrease verbosity level
625    
626    =back
627    
628    This script in current version support just backup of Samba (or Micro$oft
629    Winblowz) shares to central disk space. Central disk space is organized in
630    multiple directories named after:
631    
632    =over 4
633    
634    =item *
635    server which is sharing files to be backed up
636    
637    =item *
638    name of share on server
639    
640    =item *
641    dated directory named like standard ISO date format (YYYYMMDD).
642    
643    =back
644    
645    In each dated directory you will find I<snapshot> of all files on
646    exported share on that particular date.
647    
648    You can also use symlink I<latest> which will lead you to
649    last completed backup. After that you can use some other backup
650    software to transfer I<snapshot> to tape, CD-ROM or some other media.
651    
652    =head2 Design considerations
653    
654    Since taking of share snapshot every day requires a lot of disk space and
655    network bandwidth, B<psinib> uses several techniques to keep disk usage and
656    network traffic at acceptable level:
657    
658    =over 3
659    
660    =item - usage of hard-links to provide same files in each snapshot (as opposed
661    to have multiple copies of same file)
662    
663    =item - usage of file size, atime and mtime to find changes of files without
664    transferring whole file over network (just share browsing is transfered
665    over network)
666    
667    =item - usage of C<.md5sum> files (compatible with command-line utility
668    C<md5sum>) to keep file between snapshots hard-linked
669    
670    =back
671    
672    =head1 CONFIGURATION
673    
674    This section is not yet written.
675    
676    =head1 HACKS, TRICKS, BUGS and LIMITATIONS
677    
678    This chapter will have all content that doesn't fit anywhere else.
679    
680    =head2 Can snapshots be more frequent than daily?
681    
682    There is not real reason why you can't take snapshot more often than
683    once a day. Actually, if you are using B<psinib> to backup Windows
684    workstations you already know that they tend to come-and-go during the day
685    (reboots probably ;-), so running B<psinib> several times a day increases
686    your chance of having up-to-date backup (B<psinib> will not make multiple
687    snapshots for same day, nor will it update snapshot for current day if
688    it already exists).
689    
690    However, changing B<psinib> to produce snapshots which are, for example, hourly
691    is a simple change of C<$DIR_TIME_FMT> which is currently set to
692    C<'%Y%m%d'> (see I<strftime> documentation for explanation of that
693    format). If you change that to C<'%Y%m%d-%H> you can have hourly snapshots
694    (if your network is fast enough, that is...). Also, some of messages in
695    program will sound strange, but other than that it should work.
696    I<You have been warned>.
697    
698    =head2 Do I really need to share every directory which I want to snapshot?
699    
700    Actually, no. Due to usage of C<Filesys::SmbClient> module, you can also
701    specify sub-directory inside your share that you want to backup. This feature
702    is most useful if you want to use administrative shares (but, have in mind
703    that you have to enter your Win administrator password in unencrypted file on
704    disk to do that) like this:
705    
706            smbmount //server/c$/WinNT/fonts  /mnt  -o username=administrator%win  
707    
708    After that you will get directories with snapshots like:
709    
710            server/c_WinNT_fonts/yyyymmdd/....
711    
712    =head2 Won't I run out of disk space?
713    
714    Of course you will... Snapshots and logfiles will eventually fill-up your disk.
715    However, you can do two things to stop that:
716    
717    =head3 Clean snapshort older than x days
718    
719    You can add following command to your C<root> crontab:
720    
721            find /backup/isis_backup -type d -mindepth 3 -maxdepth 3 -mtime +11 -exec rm -Rf {} \;
722    
723    I assume that C</backup/isis_backup> is directory in which are your snapshots
724    and that you don't want to keep snapshots older than 11 days (that's
725    C<-mtime +11> part of command).
726    
727    =head3 Rotate your logs
728    
729    I will leave that to you. I relay on GNU/Debian's C<logrotate> to do it for me.
730    
731    =head2 What are I<YYYYMMDD.partial> directories?
732    
733    If there isn't I<latest> symlink in snapshot directory, it's preatty safe to
734    assume that previous backup from that day failed. So, that directory will
735    be renamed to I<YYYYMMDD.partial> and snapshot will be performed again,
736    linking same files (other alternative would be to erase that dir and find
737    second-oldest directory, but this seemed like more correct approach).
738    
739    =head2 I can't connect to any share
740    
741    Please verify that nmblookup (which is part of samba package) is in /bin or
742    /usr/bin. Also verify that nmblookup returns IP address for your server
743    using:
744    
745       $ nmblookup tvhouse
746       querying tvhouse on 192.168.34.255
747       192.168.34.30 tvhouse<00>
748    
749    If you don't get any output, your samba might not listen to correct interface
750    (see interfaces in smb.conf).
751    
752    =head1 AUTHOR
753    
754    Dobrica Pavlinusic <dpavlin@rot13.org>
755    
756    L<http:E<sol>E<sol>www.rot13.orgE<sol>~dpavlinE<sol>>
757    
758    =head1 LICENSE
759    
760    This product is licensed under GNU Public License (GPL) v2 or later.
761    
762    =cut

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

  ViewVC Help
Powered by ViewVC 1.1.26