/[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.1 by dpavlin, Sat Jan 4 11:42:56 2003 UTC revision 1.5 by dpavlin, Tue Jan 21 19:50:47 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 21  use POSIX qw(strftime); Line 21  use POSIX qw(strftime);
21  use List::Compare;  use List::Compare;
22  use Filesys::SmbClient;  use Filesys::SmbClient;
23  #use Taint;  #use Taint;
24    use Fcntl qw(LOCK_EX LOCK_NB);
25    use Digest::MD5;
26    use File::Basename;
27    
28  # configuration  # configuration
29  my $LOG_TIME_FMT = '%Y-%m-%d %H:%M:%S'; # strftime format for logfile  my $LOG_TIME_FMT = '%Y-%m-%d %H:%M:%S'; # strftime format for logfile
30  my $DIR_TIME_FMT = '%Y%m%d';            # strftime format for backup dir  my $DIR_TIME_FMT = '%Y%m%d';            # strftime format for backup dir
31    
32  my $LOG = '/var/log/backup.log';        # add path here...  my $LOG = '/var/log/backup.log';        # add path here...
33  $LOG = '/tmp/backup.log';  #$LOG = '/tmp/backup.log';
34    
35  # store backups in which directory  # store backups in which directory
36  my $BACKUP_DEST = '/data/isis_backup';  my $BACKUP_DEST = '/backup/isis_backup';
37    
38  # files to ignore in backup  # files to ignore in backup
39  my @ignore = ('.md5sum', '.backupignore', 'backupignore.txt');  my @ignore = ('.md5sum', '.backupignore', 'backupignore.txt');
40    
41  # open log  # open log
42  open(L, "> $LOG") || die "can't open log $LOG: $!";  open(L, ">> $LOG") || die "can't open log $LOG: $!";
43  select((select(L), $|=1)[0]);   # flush output  select((select(L), $|=1)[0]);   # flush output
44    
45    # make a lock on logfile
46    
47    my $c = 0;
48    {
49            flock L, LOCK_EX | LOCK_NB and last;
50            sleep 1;
51            redo if ++$c < 10;
52            # no response for 10 sec, bail out
53            print STDERR "can't take lock on $LOG -- another $0 running?\n";
54            exit 1;
55    }
56    
57  # taint path: nmblookup should be there!  # taint path: nmblookup should be there!
58  $ENV{'PATH'} = "/usr/bin:/bin";  $ENV{'PATH'} = "/usr/bin:/bin";
59    
# Line 56  my $backup_ok = 0; Line 71  my $backup_ok = 0;
71  my $smb;  my $smb;
72  my %smb_atime;  my %smb_atime;
73  my %smb_mtime;  my %smb_mtime;
74    my %file_md5;
75    
76  open(M, $mounts) || die "can't open $mounts: $!";  open(M, $mounts) || die "can't open $mounts: $!";
77  while(<M>) {  while(<M>) {
# Line 84  while(<M>) { Line 100  while(<M>) {
100    
101          push @in_backup,$share;          push @in_backup,$share;
102    
103    
104            my ($host,$dir,$date_dir) = share2host_dir($share);
105            my $bl = "$BACKUP_DEST/$host/$dir/latest";      # latest backup
106            my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";   # current one
107            my $real_bl;
108            if (-e $bl) {
109                    $real_bl=readlink($bl) || die "can't read link $bl: $!";
110                    $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
111                    if (-e $bc && $real_bl eq $bc) {
112                            print "$share allready backuped...\n";
113                            $backup_ok++;
114                            next;
115                    }
116    
117            }
118    
119    
120          print "working on $share\n";          print "working on $share\n";
121    
122    
123          my $ip = get_ip($share);          my $ip = get_ip($share);
124    
125          if ($ip) {          if ($ip) {
# Line 104  xlog("","$backup_ok backups completed of Line 138  xlog("","$backup_ok backups completed of
138    
139  #-------------------------------------------------------------------------  #-------------------------------------------------------------------------
140    
141    
142  # get IP number from share  # get IP number from share
143  sub get_ip {  sub get_ip {
144          my $share = shift;          my $share = shift;
# Line 116  sub get_ip { Line 151  sub get_ip {
151          }          }
152  }  }
153    
154    
155    # write entry to screen and log
156  sub xlog {  sub xlog {
157          my $share = shift;          my $share = shift;
158          my $t = strftime $LOG_TIME_FMT, localtime;          my $t = strftime $LOG_TIME_FMT, localtime;
# Line 124  sub xlog { Line 161  sub xlog {
161          print L "$t $share\t$m\n";          print L "$t $share\t$m\n";
162  }  }
163    
 sub snap_share {  
164    
165    # split share name to host, dir and currnet date dir
166    sub share2host_dir {
167          my $share = shift;          my $share = shift;
   
         my %param = ( debug => 0 );  
   
         $param{username} = shift;  
         $param{password} = shift;  
         $param{workgroup} = shift;  
   
168          my ($host,$dir);          my ($host,$dir);
169          if ($share =~ m#//([^/]+)/(.+)$#) {          if ($share =~ m#//([^/]+)/(.+)$#) {
170                  ($host,$dir) = ($1,$2);                  ($host,$dir) = ($1,$2);
# Line 144  sub snap_share { Line 175  sub snap_share {
175                  print "Can't parse share $share into host and directory!\n";                  print "Can't parse share $share into host and directory!\n";
176                  return;                  return;
177          }          }
178            return ($host,$dir,strftime $DIR_TIME_FMT, localtime);
179    }
180    
181    
182    # make a snapshot of a share
183    sub snap_share {
184    
185            my $share = shift;
186    
187          my $date_dir = strftime $DIR_TIME_FMT, localtime;          my %param = ( debug => 0 );
188    
189            $param{username} = shift;
190            $param{password} = shift;
191            $param{workgroup} = shift;
192    
193            my ($host,$dir,$date_dir) = share2host_dir($share);
194    
195          # latest backup directory          # latest backup directory
196          my $bl = "$BACKUP_DEST/$host/$dir/latest";          my $bl = "$BACKUP_DEST/$host/$dir/latest";
# Line 189  sub snap_share { Line 234  sub snap_share {
234          my %file_size;          my %file_size;
235          my %file_atime;          my %file_atime;
236          my %file_mtime;          my %file_mtime;
237          my %file_md5;          #my %file_md5;
238    
239          my @smb_files;          my @smb_files;
240          my %smb_size;          my %smb_size;
241          #my %smb_atime;          #my %smb_atime;
242          #my %smb_mtime;          #my %smb_mtime;
         my %smb_md5;  
   
243    
244          sub norm_dir {          sub norm_dir {
245                  my $foo = shift;                  my $foo = shift;
# Line 337  print STDERR "ignore: ",join("|",@ignore Line 380  print STDERR "ignore: ",join("|",@ignore
380                                  next;                                  next;
381                          }                          }
382    
383                            my $md5 = Digest::MD5->new;
384    
385                          my $fd = $smb->open("$from/$f");                          my $fd = $smb->open("$from/$f");
386                          if (! $fd) {                          if (! $fd) {
387                                  print STDERR "can't open smb file $from/$f: $!\n";                                  print STDERR "can't open smb file $from/$f: $!\n";
# Line 346  print STDERR "ignore: ",join("|",@ignore Line 391  print STDERR "ignore: ",join("|",@ignore
391                          while (defined(my $b=$smb->read($fd,4096))) {                          while (defined(my $b=$smb->read($fd,4096))) {
392                                  print F $b;                                  print F $b;
393                                  $l += length($b);                                  $l += length($b);
394                                    $md5->add($b);
395                          }                          }
396    
397                          $smb->close($fd);                          $smb->close($fd);
398                          close(F);                          close(F);
399    
400                            $file_md5{$f} = $md5->hexdigest;
401    
402                          # FIX: this fails with -T                          # FIX: this fails with -T
403                          my ($a,$m) = ($smb->stat("$from/$f"))[10,11];                          my ($a,$m) = ($smb->stat("$from/$f"))[10,11];
404                          utime $a, $m, "$to/$f" ||                          utime $a, $m, "$to/$f" ||
# Line 417  print STDERR "ignore: ",join("|",@ignore Line 465  print STDERR "ignore: ",join("|",@ignore
465                  rmdir "$bc/$_" || warn "rmdir $_: $!\n";                  rmdir "$bc/$_" || warn "rmdir $_: $!\n";
466          }          }
467    
468            # remove old .md5sum
469          # FIX: create .md5sum          foreach (sort @dirs) {
470                    unlink "$bc/$_/.md5sum" if (-e "$bc/$_/.md5sum");
471            }
472    
473            # create .md5sum
474            my $last_dir = '';
475            my $md5;
476            foreach my $f (sort { $file_md5{$a}<=>$file_md5{$b} } keys %file_md5) {
477                    my $dir = dirname($f);
478                    my $file = basename($f);
479    print "$f -- $dir / $file<--\n";
480                    if ($dir ne $last_dir) {
481                            close($md5) if ($md5);
482                            open($md5, ">> $bc/$dir/.md5sum") || warn "can't create $bc/$dir/.md5sum: $!";
483                            $last_dir = $dir;
484    print STDERR "writing $last_dir/.md5sum\n";
485                    }
486                    print $md5 $file_md5{$f},"  $file\n";
487            }
488            close($md5);
489    
490          # create leatest link          # create leatest link
491          symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";  #       symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";
492    
493          xlog($share,"backup completed...");          xlog($share,"backup completed...");
494  }  }
495    
496    __END__
497  #-------------------------------------------------------------------------  #-------------------------------------------------------------------------
498    
499    
500    =head1 NAME
501    
502    psinib - Perl Snapshot Is Not Incremental Backup
503    
504    =head1 SYNOPSIS
505    
506    ./psinib.pl
507    
508    =head1 DESCRIPTION
509    
510    This script in current version support just backup of Samba (or Micro$oft
511    Winblowz) shares to central disk space. Central disk space is organized in
512    multiple directories named after:
513    
514    =over 4
515    
516    =item *
517    server which is sharing files to be backed up
518    
519    =item *
520    name of share on server
521    
522    =item *
523    dated directory named like standard ISO date format (YYYYMMDD).
524    
525    =back
526    
527    In each dated directory you will find I<snapshot> of all files on
528    exported share on that particular date.
529    
530    You can also use symlink I<latest> which will lead you to
531    last completed backup. After that you can use some other backup
532    software to transfer I<snapshot> to tape, CD-ROM or some other media.
533    
534    =head2 Design considerations
535    
536    Since taking of share snapshot every day requires a lot of disk space and
537    network bandwidth, B<psinib> uses several techniques to keep disk usage and
538    network traffic at acceptable level:
539    
540    =over 3
541    
542    =item - usage of hard-links to provide same files in each snapshot (as opposed
543    to have multiple copies of same file)
544    
545    =item - usage of file size, atime and mtime to find changes of files without
546    transferring whole file over network (just share browsing is transfered
547    over network)
548    
549    =item - usage of C<.md5sum> files (compatible with command-line utility
550    C<md5sum> to keep file between snapshots hard-linked
551    
552    =back
553    
554    =head1 CONFIGURATION
555    
556    This section is not yet written.
557    
558    =head1 HACKS, TRICKS, BUGS and LIMITATIONS
559    
560    This chapter will have all content that doesn't fit anywhere else.
561    
562    =head2 Can snapshots be more frequent than daily?
563    
564    There is not real reason why you can't take snapshot more often than
565    once a day. Actually, if you are using B<psinib> to backup Windows
566    workstations you already know that they tend to come-and-go during the day
567    (reboots probably ;-), so running B<psinib> several times a day increases
568    your chance of having up-to-date backup (B<psinib> will not make multiple
569    snapshots for same day, nor will it update snapshot for current day if
570    it already exists).
571    
572    However, changing B<psinib> to produce snapshots which are, for example, hourly
573    is a simple change of C<$DIR_TIME_FMT> which is currently set to
574    C<'%Y%m%d'> (see I<strftime> documentation for explanation of that
575    format). If you change that to C<'%Y%m%d-%H> you can have hourly snapshots
576    (if your network is fast enough, that is...). Also, some of messages in
577    program will sound strange, but other than that it should work.
578    I<You have been warned>.
579    
580    =head2 Do I really need to share every directory which I want to snapshot?
581    
582    Actually, no. Due to usage of C<Filesys::SmbClient> module, you can also
583    specify sub-directory inside your share that you want to backup. This feature
584    is most useful if you want to use administrative shares (but, have in mind
585    that you have to enter your Win administrator password in unencrypted file on
586    disk to do that) like this:
587    
588            smbmount //server/c$/WinNT/fonts  /mnt  -o username=administrator%win  
589    
590    After that you will get directories with snapshots like:
591    
592            server/c_WinNT_fonts/yyyymmdd/....
593    
594    
595    =head1 AUTHOR
596    
597    Dobrica Pavlinusic <dpavlin@rot13.org>
598    
599    L<http://www.rot13.org/~dpavlin/>
600    
601    =head1 LICENSE
602    
603    This product is licensed under GNU Public License (GPL) v2 or later.
604    
605    =cut

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

  ViewVC Help
Powered by ViewVC 1.1.26