/[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.5 by dpavlin, Tue Jan 21 19:50:47 2003 UTC revision 1.16 by dpavlin, Sun Oct 26 12:55:56 2003 UTC
# Line 24  use Filesys::SmbClient; Line 24  use Filesys::SmbClient;
24  use Fcntl qw(LOCK_EX LOCK_NB);  use Fcntl qw(LOCK_EX LOCK_NB);
25  use Digest::MD5;  use Digest::MD5;
26  use File::Basename;  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 = '/backup/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');
# Line 50  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;       # deault: use ping to verify that host is up
66    
67    my $result = GetOptions(
68            "ping!" => \$use_ping, "backupdest!" => \$BACKUP_DEST,
69    );
70    
71  my $mounts = shift @ARGV ||  my $mounts = shift @ARGV ||
72          'mountscript';          'mountscript';
73  #       die "usage: $0 mountscript";  #       die "usage: $0 mountscript";
# Line 64  my $mounts = shift @ARGV || Line 75  my $mounts = shift @ARGV ||
75    
76  my @in_backup;  # shares which are backeduped this run  my @in_backup;  # shares which are backeduped this run
77    
78  my $p = new Net::Ping->new();  # init Net::Ping object
79    my $ping;
80    if ($use_ping) {
81            $ping = new Net::Ping->new("syn", 2);
82            # ping will try tcp connect to netbios-ssn (139)
83            $ping->{port_num} = getservbyname("netbios-ssn", "tcp");
84    }
85    
86    # do syn ping to cifs port
87    sub host_up {
88            my $ping = shift || return;
89            my $host_ip = shift || xlog("host_up didn't get IP");
90            my $timeout = shift;
91            return 1 if (! $use_ping);
92    
93            $ping->ping($host_ip,$timeout);
94            my $return = 0;
95    
96            while (my ($host,$rtt,$ip) = $ping->ack) {
97                    xlog("","HOST: $host [$ip] ACKed in $rtt seconds");
98                    $return = 1 if ($ip eq $host_ip);
99            }
100            return $return;
101    }
102    
103  my $backup_ok = 0;  my $backup_ok = 0;
104    
# Line 79  while(<M>) { Line 113  while(<M>) {
113          next if !/^\s*smbmount\s/;          next if !/^\s*smbmount\s/;
114          my (undef,$share,undef,$opt) = split(/\s+/,$_,4);          my (undef,$share,undef,$opt) = split(/\s+/,$_,4);
115    
116          my ($user,$passwd,$workgroup);          my ($user,$passwd,$workgroup,$ip);
117    
118          foreach (split(/,/,$opt)) {          foreach (split(/,/,$opt)) {
119                  my ($n,$v) = split(/=/,$_,2);                  my ($n,$v) = split(/=/,$_,2);
# Line 95  while(<M>) { Line 129  while(<M>) {
129                          }                          }
130                  } elsif ($n =~ m#workgroup#i) {                  } elsif ($n =~ m#workgroup#i) {
131                          $workgroup = $v;                          $workgroup = $v;
132                    } elsif ($n =~ m#ip#i) {
133                            $ip = $v;
134                  }                  }
135          }          }
136    
# Line 105  while(<M>) { Line 141  while(<M>) {
141          my $bl = "$BACKUP_DEST/$host/$dir/latest";      # latest backup          my $bl = "$BACKUP_DEST/$host/$dir/latest";      # latest backup
142          my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";   # current one          my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";   # current one
143          my $real_bl;          my $real_bl;
144          if (-e $bl) {          if (-l $bl) {
145                  $real_bl=readlink($bl) || die "can't read link $bl: $!";                  $real_bl=readlink($bl) || die "can't read link $bl: $!";
146                  $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");                  $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
147                  if (-e $bc && $real_bl eq $bc) {                  if (-l $bc && $real_bl eq $bc) {
148                          print "$share allready backuped...\n";                          print "$share allready backuped...\n";
149                          $backup_ok++;                          $backup_ok++;
150                          next;                          next;
# Line 119  while(<M>) { Line 155  while(<M>) {
155    
156          print "working on $share\n";          print "working on $share\n";
157    
158            # try to nmblookup IP
159          my $ip = get_ip($share);          $ip = get_ip($share) if (! $ip);
160    
161          if ($ip) {          if ($ip) {
162                  xlog($share,"IP is $ip");                  xlog($share,"IP is $ip");
163                  if ($p->ping($ip)) {                  if (host_up($ping, $ip,$PING_TIMEOUT)) {
164                          snap_share($share,$user,$passwd,$workgroup);                          if (snap_share($share,$user,$passwd,$workgroup)) {
165                          $backup_ok++;                                  $backup_ok++;
166                            }
167                  }                  }
168          }          }
169  }  }
# Line 161  sub xlog { Line 198  sub xlog {
198          print L "$t $share\t$m\n";          print L "$t $share\t$m\n";
199  }  }
200    
201    # dump warn and dies into log
202    BEGIN { $SIG{'__WARN__'} = sub { xlog('WARN',$_[0]) ; warn $_[0] } }
203    BEGIN { $SIG{'__DIE__'} = sub { xlog('DIE',$_[0]) ; die $_[0] } }
204    
205    
206  # split share name to host, dir and currnet date dir  # split share name to host, dir and currnet date dir
207  sub share2host_dir {  sub share2host_dir {
# Line 186  sub snap_share { Line 227  sub snap_share {
227    
228          my %param = ( debug => 0 );          my %param = ( debug => 0 );
229    
230          $param{username} = shift;          $param{username} = shift || warn "can't find username for share $share";
231          $param{password} = shift;          $param{password} = shift || warn "can't find passwod for share $share";
232          $param{workgroup} = shift;          $param{workgroup} = shift || warn "can't find workgroup for share $share";
233    
234          my ($host,$dir,$date_dir) = share2host_dir($share);          my ($host,$dir,$date_dir) = share2host_dir($share);
235    
# Line 198  sub snap_share { Line 239  sub snap_share {
239          my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";          my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";
240    
241          my $real_bl;          my $real_bl;
242          if (-e $bl) {          if (-l $bl) {
243                  $real_bl=readlink($bl) || die "can't read link $bl: $!";                  $real_bl=readlink($bl) || die "can't read link $bl: $!";
244                  $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");                  $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
245          } else {          } else {
246                  print "no old backup, this is first run...\n";                  print "no old backup, trying to find last backup, ";
247                    if (opendir(BL_DIR, "$BACKUP_DEST/$host/$dir")) {
248                            my @bl_dirs = sort grep { !/^\./ && -d "$BACKUP_DEST/$host/$dir/$_" } readdir(BL_DIR);
249                            closedir(BL_DIR);
250                            $real_bl=pop @bl_dirs;
251                            print "using $real_bl as latest...\n";
252                            $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
253                            if ($real_bl eq $bc) {
254                                    xlog($share,"latest from today (possible partial backup)");
255                                    rename $real_bl,$real_bl.".partial" || warn "can't reaname partial backup: $!";
256                                    $real_bl .= ".partial";
257                            }
258                    } else {
259                            print "this is first run...\n";
260                    }
261          }          }
262    
263          if (-e $bc && $real_bl && $real_bl eq $bc) {          if (-l $bc && $real_bl && $real_bl eq $bc) {
264                  print "$share allready backuped...\n";                  print "$share allready backuped...\n";
265                  return;                  return 1;
266          }          }
267    
268          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 235  sub snap_share { Line 290  sub snap_share {
290          my %file_atime;          my %file_atime;
291          my %file_mtime;          my %file_mtime;
292          #my %file_md5;          #my %file_md5;
293            %file_md5 = ();
294    
295          my @smb_files;          my @smb_files;
296          my %smb_size;          my %smb_size;
# Line 255  sub snap_share { Line 311  sub snap_share {
311          my $di = 0;          my $di = 0;
312          while ($di <= $#dirs && $real_bl) {          while ($di <= $#dirs && $real_bl) {
313                  my $d=$dirs[$di++];                  my $d=$dirs[$di++];
314                  opendir(DIR,"$bl/$d") || warn "opendir($bl/$d): $!\n";                  opendir(DIR,"$real_bl/$d") || warn "opendir($real_bl/$d): $!\n";
315    
316                  # read .backupignore if exists                  # read .backupignore if exists
317                  if (-f "$bl/$d/.backupignore") {                  if (-f "$real_bl/$d/.backupignore") {
318                          open(I,"$bl/$d/.backupignore");                          open(I,"$real_bl/$d/.backupignore");
319                          while(<I>) {                          while(<I>) {
320                                  chomp;                                  chomp;
321                                  push @ignore,norm_dir("$d/$_");                                  push @ignore,norm_dir("$d/$_");
322                          }                          }
323                          close(I);                          close(I);
324  print STDERR "ignore: ",join("|",@ignore),"\n";  #print STDERR "ignore: ",join("|",@ignore),"\n";
325                          link "$bl/$d/.backupignore","$bc/$d/.backupignore" ||                          link "$real_bl/$d/.backupignore","$bc/$d/.backupignore" ||
326                                  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";
327                  }                  }
328    
329                  # read .md5sum if exists                  # read .md5sum if exists
330                  if (-f "$bl/$d/.md5sum") {                  if (-f "$real_bl/$d/.md5sum") {
331                          open(I,"$bl/$d/.md5sum");                          open(I,"$real_bl/$d/.md5sum");
332                          while(<I>) {                          while(<I>) {
333                                  chomp;                                  chomp;
334                                  my ($md5,$f) = split(/\s+/,$_,2);                                  my ($md5,$f) = split(/\s+/,$_,2);
# Line 286  print STDERR "ignore: ",join("|",@ignore Line 342  print STDERR "ignore: ",join("|",@ignore
342                          next if ($f eq '.');                          next if ($f eq '.');
343                          next if ($f eq '..');                          next if ($f eq '..');
344                          my $pr = norm_dir("$d/$f");     # path relative                          my $pr = norm_dir("$d/$f");     # path relative
345                          my $pf = norm_dir("$d/$f","$bl/");      # path full                          my $pf = norm_dir("$d/$f","$real_bl/"); # path full
346                          if (grep(/^\Q$pr\E$/,@ignore) == 0) {                          if (grep(/^\Q$pr\E$/,@ignore) == 0) {
347                                  if (-f $pf) {                                  if (-f $pf) {
348                                          push @files,$pr;                                          push @files,$pr;
# Line 296  print STDERR "ignore: ",join("|",@ignore Line 352  print STDERR "ignore: ",join("|",@ignore
352                                  } elsif (-d $pf) {                                  } elsif (-d $pf) {
353                                          push @dirs,$pr;                                          push @dirs,$pr;
354                                  } else {                                  } else {
355                                          print STDERR "unknown type: $pf\n";                                          print STDERR "not file or directory: $pf\n";
356                                  }                                  }
357                          } else {                          } else {
358                                  print STDERR "ignored: $pr\n";                                  print STDERR "ignored: $pr\n";
# Line 304  print STDERR "ignore: ",join("|",@ignore Line 360  print STDERR "ignore: ",join("|",@ignore
360                  }                  }
361          }          }
362    
363          xlog($share,($#files+1)." files and ".($#dirs+1)." dirs on local disk before backup");          # local dir always include /
364            xlog($share,($#files+1)." files and ".($#dirs)." dirs on local disk before backup");
365    
366          # read smb filesystem          # read smb filesystem
367    
# Line 315  print STDERR "ignore: ",join("|",@ignore Line 372  print STDERR "ignore: ",join("|",@ignore
372    
373          $di = 0;          $di = 0;
374          while ($di <= $#smb_dirs) {          while ($di <= $#smb_dirs) {
375                  my $d=$smb_dirs[$di++];                  my $d=$smb_dirs[$di];
376                  my $pf = norm_dir($d,"smb:$share/");    # path full                  my $pf = norm_dir($d,"smb:$share/");    # path full
377                  my $D = $smb->opendir($pf) || warn "smb->opendir($pf): $!\n";                  my $D = $smb->opendir($pf);
378                    if (! $D) {
379                            xlog($share,"FATAL: $share [$pf]: $!");
380                            # remove failing dir
381                            delete $smb_dirs[$di];
382                            return 0;                       # failed
383                    }
384                    $di++;
385    
386                  my @clutter = $smb->readdir_struct($D);                  my @clutter = $smb->readdir_struct($D);
387                  foreach my $item (@clutter) {                  foreach my $item (@clutter) {
# Line 335  print STDERR "ignore: ",join("|",@ignore Line 399  print STDERR "ignore: ",join("|",@ignore
399                                  } elsif ($item->[0] == main::SMBC_DIR) {                                  } elsif ($item->[0] == main::SMBC_DIR) {
400                                          push @smb_dirs,$pr;                                          push @smb_dirs,$pr;
401                                  } else {                                  } else {
402                                          print STDERR "unknown type: $pf\n";                                          print STDERR "not file or directory [",$item->[0],"]: $pf\n";
403                                  }                                  }
404                          } else {                          } else {
405                                  print STDERR "smb ignored: $pr\n";                                  print STDERR "smb ignored: $pr\n";
# Line 343  print STDERR "ignore: ",join("|",@ignore Line 407  print STDERR "ignore: ",join("|",@ignore
407                  }                  }
408          }          }
409    
410          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");
411    
412          # sync dirs          # sync dirs
413          my $lc = List::Compare->new(\@dirs, \@smb_dirs);          my $lc = List::Compare->new(\@dirs, \@smb_dirs);
# Line 375  print STDERR "ignore: ",join("|",@ignore Line 439  print STDERR "ignore: ",join("|",@ignore
439                                    
440                  foreach my $f (@_) {                  foreach my $f (@_) {
441  #print "smb_copy $from/$f -> $to/$f\n";  #print "smb_copy $from/$f -> $to/$f\n";
                         if (! open(F,"> $to/$f")) {  
                                 print STDERR "can't open new file $to/$f: $!\n";  
                                 next;  
                         }  
   
442                          my $md5 = Digest::MD5->new;                          my $md5 = Digest::MD5->new;
443    
444                          my $fd = $smb->open("$from/$f");                          my $fd = $smb->open("$from/$f");
445                          if (! $fd) {                          if (! $fd) {
446                                  print STDERR "can't open smb file $from/$f: $!\n";                                  xlog("WARNING","can't open smb file $from/$f: $!");
447                                    next;
448                            }
449    
450                            if (! open(F,"> $to/$f")) {
451                                    xlog("WARNING","can't open new file $to/$f: $!");
452                                  next;                                  next;
453                          }                          }
454    
# Line 452  print STDERR "ignore: ",join("|",@ignore Line 516  print STDERR "ignore: ",join("|",@ignore
516          xlog($share,"$transfer bytes transfered...");          xlog($share,"$transfer bytes transfered...");
517    
518          foreach (@ln_files) {          foreach (@ln_files) {
519                  link "$bl/$_","$bc/$_" || warn "link $bl/$_ -> $bc/$_: $!\n";                  link "$real_bl/$_","$bc/$_" || warn "link $real_bl/$_ -> $bc/$_: $!\n";
520          }          }
521    
522          # remove files          # remove files
523          foreach (sort @files2erase) {          foreach (sort @files2erase) {
524                  unlink "$bc/$_" || warn "unlink $_: $!\n";                  unlink "$bc/$_" || warn "unlink $_: $!\n";
525                    delete $file_md5{$_};
526          }          }
527    
528          # remove not needed dirs (after files)          # remove not needed dirs (after files)
# Line 470  print STDERR "ignore: ",join("|",@ignore Line 535  print STDERR "ignore: ",join("|",@ignore
535                  unlink "$bc/$_/.md5sum" if (-e "$bc/$_/.md5sum");                  unlink "$bc/$_/.md5sum" if (-e "$bc/$_/.md5sum");
536          }          }
537    
538            # erase stale entries in .md5sum
539            my @md5_files = keys %file_md5;
540            $lc = List::Compare->new(\@md5_files, \@smb_files);
541            foreach my $file ($lc->get_Lonly) {
542                    xlog("NOTICE","removing stale '$file' from .md5sum");
543                    delete $file_md5{$file};
544            }
545    
546          # create .md5sum          # create .md5sum
547          my $last_dir = '';          my $last_dir = '';
548          my $md5;          my $md5;
549          foreach my $f (sort { $file_md5{$a}<=>$file_md5{$b} } keys %file_md5) {          foreach my $f (sort { $file_md5{$a} cmp $file_md5{$b} } keys %file_md5) {
550                  my $dir = dirname($f);                  my $dir = dirname($f);
551                  my $file = basename($f);                  my $file = basename($f);
552  print "$f -- $dir / $file<--\n";  #print "$f -- $dir / $file<--\n";
553                  if ($dir ne $last_dir) {                  if ($dir ne $last_dir) {
554                          close($md5) if ($md5);                          close($md5) if ($md5);
555                          open($md5, ">> $bc/$dir/.md5sum") || warn "can't create $bc/$dir/.md5sum: $!";                          open($md5, ">> $bc/$dir/.md5sum") || warn "can't create $bc/$dir/.md5sum: $!";
556                          $last_dir = $dir;                          $last_dir = $dir;
557  print STDERR "writing $last_dir/.md5sum\n";  #print STDERR "writing $last_dir/.md5sum\n";
558                  }                  }
559                  print $md5 $file_md5{$f},"  $file\n";                  print $md5 $file_md5{$f},"  $file\n";
560          }          }
561          close($md5);          close($md5) if ($md5);
562    
563          # create leatest link          # create leatest link
564  #       symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";  #print "ln -s $bc $real_bl\n";
565            if (-l $bl) {
566                    unlink $bl || warn "can't remove old latest symlink $bl: $!\n";
567            }
568            symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";
569    
570            # FIX: sanity check -- remove for speedup
571            xlog($share,"failed to create latest symlink $bl -> $bc...") if (readlink($bl) ne $bc || ! -l $bl);
572    
573          xlog($share,"backup completed...");          xlog($share,"backup completed...");
 }  
574    
575            return 1;
576    }
577  __END__  __END__
578  #-------------------------------------------------------------------------  #-------------------------------------------------------------------------
579    
# Line 547  transferring whole file over network (ju Line 628  transferring whole file over network (ju
628  over network)  over network)
629    
630  =item - usage of C<.md5sum> files (compatible with command-line utility  =item - usage of C<.md5sum> files (compatible with command-line utility
631  C<md5sum> to keep file between snapshots hard-linked  C<md5sum>) to keep file between snapshots hard-linked
632    
633  =back  =back
634    
# Line 591  After that you will get directories with Line 672  After that you will get directories with
672    
673          server/c_WinNT_fonts/yyyymmdd/....          server/c_WinNT_fonts/yyyymmdd/....
674    
675    =head2 Won't I run out of disk space?
676    
677    Of course you will... Snapshots and logfiles will eventually fill-up your disk.
678    However, you can do two things to stop that:
679    
680    =head3 Clean snapshort older than x days
681    
682    You can add following command to your C<root> crontab:
683    
684            find /backup/isis_backup -type d -mindepth 3 -maxdepth 3 -mtime +11 -exec rm -Rf {} \;
685    
686    I assume that C</backup/isis_backup> is directory in which are your snapshots
687    and that you don't want to keep snapshots older than 11 days (that's
688    C<-mtime +11> part of command).
689    
690    =head3 Rotate your logs
691    
692    I will leave that to you. I relay on GNU/Debian's C<logrotate> to do it for me.
693    
694    =head2 What are I<YYYYMMDD.partial> directories?
695    
696    If there isn't I<latest> symlink in snapshot directory, it's preatty safe to
697    assume that previous backup from that day failed. So, that directory will
698    be renamed to I<YYYYMMDD.partial> and snapshot will be performed again,
699    linking same files (other alternative would be to erase that dir and find
700    second-oldest directory, but this seemed like more correct approach).
701    
702    =head2 I can't connect to any share
703    
704    Please verify that nmblookup (which is part of samba package) is in /bin or
705    /usr/bin. Also verify that nmblookup returns IP address for your server
706    using:
707    
708       $ nmblookup tvhouse
709       querying tvhouse on 192.168.34.255
710       192.168.34.30 tvhouse<00>
711    
712    If you don't get any output, your samba might not listen to correct interface
713    (see interfaces in smb.conf).
714    
715  =head1 AUTHOR  =head1 AUTHOR
716    

Legend:
Removed from v.1.5  
changed lines
  Added in v.1.16

  ViewVC Help
Powered by ViewVC 1.1.26