/[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.21 by dpavlin, Sat Nov 8 01:46:53 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    my $email;
69    
70    my $result = GetOptions(
71            "ping!" => \$use_ping, "backupdest!" => \$BACKUP_DEST,
72            "verbose+" => \$verbose, "quiet+" => \$quiet,
73            "email=s" => \$email,
74    );
75    
76    $verbose -= $quiet;
77    
78  my $mounts = shift @ARGV ||  my $mounts = shift @ARGV ||
79          'mountscript';          'mountscript';
80  #       die "usage: $0 mountscript";  #       die "usage: $0 mountscript";
81    
82    my $basedir = $0;
83    $basedir =~ s,/?[^/]+$,,g;
84    
85    # default subject for e-mail messages
86    my @subjects = ('Backup needs your attention!');
87    my $sub_nr = 0;
88    my $email_body;
89    
90    if ($email) {
91            # It will use (and require) Tie::File only if --email=foo@bar.com
92            # arguement is used!
93            use Tie::File;
94            tie @subjects, 'Tie::File', "$basedir/subjects.txt" || xlog("CONFIG","Can't find $basedir/subjects.txt... using default (only one)");
95            chdir; # this will change directory to HOME
96            if (open(SN,".psinib.subject")) {
97                    $sub_nr = <SN>;
98                    chomp($sub_nr);
99                    close(SN);
100            }
101            $sub_nr++;
102            # skip comments in subjects.txt
103            while($subjects[$sub_nr] && $subjects[$sub_nr] =~ m/^#/) {
104                    $sub_nr++;
105            }
106            $sub_nr = 0 if (! $subjects[$sub_nr]);
107    
108            if (open(SN,"> .psinib.subject")) {
109                    print SN "$sub_nr\n";
110                    close (SN);
111            } else {
112                    xlog("CONFIG","Can't open .psinib.subject -- I can't cycle subjects...");
113            };
114    }
115    
116  my @in_backup;  # shares which are backeduped this run  my @in_backup;  # shares which are backeduped this run
117    
118  my $p = new Net::Ping->new();  # init Net::Ping object
119    my $ping;
120    if ($use_ping) {
121            $ping = new Net::Ping->new("syn", 2);
122            # ping will try tcp connect to netbios-ssn (139)
123            $ping->{port_num} = getservbyname("netbios-ssn", "tcp");
124    }
125    
126    # do syn ping to cifs port
127    sub host_up {
128            my $ping = shift || return;
129            my $host_ip = shift || xlog("host_up didn't get IP");
130            my $timeout = shift;
131            return 1 if (! $use_ping);
132    
133            $ping->ping($host_ip,$timeout);
134            my $return = 0;
135    
136            while (my ($host,$rtt,$ip) = $ping->ack) {
137                    xlog("","HOST: $host [$ip] ACKed in $rtt seconds");
138                    $return = 1 if ($ip eq $host_ip);
139            }
140            return $return;
141    }
142    
143  my $backup_ok = 0;  my $backup_ok = 0;
144    
145  my $smb;  my $smb;
146  my %smb_atime;  my %smb_atime;
147  my %smb_mtime;  my %smb_mtime;
148    my %file_md5;
149    
150  open(M, $mounts) || die "can't open $mounts: $!";  open(M, $mounts) || die "can't open $mounts: $!";
151  while(<M>) {  while(<M>) {
# Line 76  while(<M>) { Line 153  while(<M>) {
153          next if !/^\s*smbmount\s/;          next if !/^\s*smbmount\s/;
154          my (undef,$share,undef,$opt) = split(/\s+/,$_,4);          my (undef,$share,undef,$opt) = split(/\s+/,$_,4);
155    
156          my ($user,$passwd,$workgroup);          my ($user,$passwd,$workgroup,$ip);
157    
158          foreach (split(/,/,$opt)) {          foreach (split(/,/,$opt)) {
159                  my ($n,$v) = split(/=/,$_,2);                  my ($n,$v) = split(/=/,$_,2);
# Line 92  while(<M>) { Line 169  while(<M>) {
169                          }                          }
170                  } elsif ($n =~ m#workgroup#i) {                  } elsif ($n =~ m#workgroup#i) {
171                          $workgroup = $v;                          $workgroup = $v;
172                    } elsif ($n =~ m#ip#i) {
173                            $ip = $v;
174                  }                  }
175          }          }
176    
177          push @in_backup,$share;          push @in_backup,$share;
178    
         print "working on $share\n";  
179    
180          my $ip = get_ip($share);          my ($host,$dir,$date_dir) = share2host_dir($share);
181            my $bl = "$BACKUP_DEST/$host/$dir/latest";      # latest backup
182            my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";   # current one
183            my $real_bl;
184            if (-l $bl) {
185                    $real_bl=readlink($bl) || die "can't read link $bl: $!";
186                    $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
187                    if (-l $bc && $real_bl eq $bc) {
188                            xlog($share,"allready backuped...");
189                            $backup_ok++;
190                            next;
191                    }
192    
193            }
194    
195    
196            xlog($share,"working on $share...");
197    
198            # try to nmblookup IP
199            $ip = get_ip($share) if (! $ip);
200    
201          if ($ip) {          if ($ip) {
202                  xlog($share,"IP is $ip");                  xlog($share,"IP is $ip");
203                  if ($p->ping($ip)) {                  if (host_up($ping, $ip,$PING_TIMEOUT)) {
204                          snap_share($share,$user,$passwd,$workgroup);                          if (snap_share($share,$user,$passwd,$workgroup)) {
205                          $backup_ok++;                                  $backup_ok++;
206                            }
207                  }                  }
208          }          }
209  }  }
210  close(M);  close(M);
211    
212  xlog("","$backup_ok backups completed of total ".($#in_backup+1)." this time (".int($backup_ok*100/($#in_backup+1))." %)");  my $total = ($#in_backup + 1) || 0;
213    my $pcnt = "";
214    $pcnt = "(".int($backup_ok*100/$total)." %)" if ($total > 0);
215    xlog("","$backup_ok backups completed of total $total this time".$pcnt);
216    
217    send_email();
218    
219  1;  1;
220    
221  #-------------------------------------------------------------------------  #-------------------------------------------------------------------------
222    
223    
224  # get IP number from share  # get IP number from share
225  sub get_ip {  sub get_ip {
226          my $share = shift;          my $share = shift;
# Line 129  sub get_ip { Line 233  sub get_ip {
233          }          }
234  }  }
235    
236    # send e-mail with all messages
237    sub send_email {
238            return if (! $email || $email eq "" || !$email_body);
239            require Mail::Send;
240            my $msg = new Mail::Send;
241            $msg->to($email);
242            $msg->subject($subjects[$sub_nr]);
243            my $fn=$msg->open;
244            print $fn $email_body;
245            $fn->close;
246    }
247            
248    
249    # write entry to screen and log
250  sub xlog {  sub xlog {
251          my $share = shift;          my $share = shift;
252          my $t = strftime $LOG_TIME_FMT, localtime;          my $t = strftime $LOG_TIME_FMT, localtime;
253          my $m = shift || '[no log entry]';          my $m = shift || '[no log entry]';
254          print STDERR $m,"\n";          my $l = shift;
255            $l = 1 if (! defined $l);       # default verbosity is 1
256            if ($verbose >= $l) {
257                    if (! $email) {
258                            print STDERR $m,"\n";
259                    # don't e-mail mesages with verbosity < 1
260                    } elsif ($l < 1) {
261                            $email_body .= $m."\n";
262                    }
263            }
264          print L "$t $share\t$m\n";          print L "$t $share\t$m\n";
265  }  }
266    
267  sub snap_share {  # dump warn and dies into log
268    BEGIN { $SIG{'__WARN__'} = sub { xlog('WARN',$_[0],1) ; exit 1 } }
269          my $share = shift;  BEGIN { $SIG{'__DIE__'} = sub { xlog('DIE',$_[0],0) ; exit 1 } }
   
         my %param = ( debug => 0 );  
270    
         $param{username} = shift;  
         $param{password} = shift;  
         $param{workgroup} = shift;  
271    
272    # split share name to host, dir and currnet date dir
273    sub share2host_dir {
274            my $share = shift;
275          my ($host,$dir);          my ($host,$dir);
276          if ($share =~ m#//([^/]+)/(.+)$#) {          if ($share =~ m#//([^/]+)/(.+)$#) {
277                  ($host,$dir) = ($1,$2);                  ($host,$dir) = ($1,$2);
# Line 154  sub snap_share { Line 279  sub snap_share {
279                  $dir =~ s/^_+//;                  $dir =~ s/^_+//;
280                  $dir =~ s/_+$//;                  $dir =~ s/_+$//;
281          } else {          } else {
282                  print "Can't parse share $share into host and directory!\n";                  xlog($share,"Can't parse share $share into host and directory!",1);
283                  return;                  return;
284          }          }
285            return ($host,$dir,strftime $DIR_TIME_FMT, localtime);
286    }
287    
288    
289    # make a snapshot of a share
290    sub snap_share {
291    
292            my $share = shift;
293    
294            my %param = ( debug => 0 );
295    
296          my $date_dir = strftime $DIR_TIME_FMT, localtime;          $param{username} = shift || warn "can't find username for share $share";
297            $param{password} = shift || warn "can't find passwod for share $share";
298            $param{workgroup} = shift || warn "can't find workgroup for share $share";
299    
300            my ($host,$dir,$date_dir) = share2host_dir($share);
301    
302          # latest backup directory          # latest backup directory
303          my $bl = "$BACKUP_DEST/$host/$dir/latest";          my $bl = "$BACKUP_DEST/$host/$dir/latest";
# Line 166  sub snap_share { Line 305  sub snap_share {
305          my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";          my $bc = "$BACKUP_DEST/$host/$dir/$date_dir";
306    
307          my $real_bl;          my $real_bl;
308          if (-e $bl) {          if (-l $bl) {
309                  $real_bl=readlink($bl) || die "can't read link $bl: $!";                  $real_bl=readlink($bl) || die "can't read link $bl: $!";
310                  $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 "/");
311          } else {                  if (! -e $real_bl) {
312                  print "no old backup, this is first run...\n";                          xlog($share,"latest link $bl -> $real_bl not valid, removing it");
313                            unlink $bl || die "can't remove link $bl: $!";
314                            undef $real_bl;
315                    }
316            }
317            if (! $real_bl) {
318                    xlog($share,"no old backup, trying to find last backup");
319                    if (opendir(BL_DIR, "$BACKUP_DEST/$host/$dir")) {
320                            my @bl_dirs = sort grep { !/^\./ && -d "$BACKUP_DEST/$host/$dir/$_" } readdir(BL_DIR);
321                            closedir(BL_DIR);
322                            $real_bl=pop @bl_dirs;
323                            xlog($share,"using $real_bl as latest...");
324                            $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
325                            if ($real_bl eq $bc) {
326                                    xlog($share,"latest from today (possible partial backup)");
327                                    rename $real_bl,$real_bl.".partial" || warn "can't reaname partial backup: $!";
328                                    $real_bl .= ".partial";
329                            }
330                    } else {
331                            xlog($share,"this is first run...");
332                    }
333          }          }
334    
335          if (-e $bc && $real_bl && $real_bl eq $bc) {          if (-l $bc && $real_bl && $real_bl eq $bc) {
336                  print "$share allready backuped...\n";                  xlog($share,"allready backuped...");
337                  return;                  return 1;
338          }          }
339    
340          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);
341    
342          if (! -e "$BACKUP_DEST/$host") {          if (! -e "$BACKUP_DEST/$host") {
343                  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: $!";
344                  print "created host directory $BACKUP_DEST/$host...\n";                  xlog($share,"created host directory $BACKUP_DEST/$host...");
345          }          }
346    
347          if (! -e "$BACKUP_DEST/$host/$dir") {          if (! -e "$BACKUP_DEST/$host/$dir") {
348                  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 $!";
349                  print "created dir for share $share, $BACKUP_DEST/$host/$dir...\n";                  xlog($share,"created dir for this share $BACKUP_DEST/$host/$dir...");
350          }          }
351    
352          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 361  sub snap_share {
361          my %file_size;          my %file_size;
362          my %file_atime;          my %file_atime;
363          my %file_mtime;          my %file_mtime;
364          my %file_md5;          #my %file_md5;
365            %file_md5 = ();
366    
367          my @smb_files;          my @smb_files;
368          my %smb_size;          my %smb_size;
369          #my %smb_atime;          #my %smb_atime;
370          #my %smb_mtime;          #my %smb_mtime;
         my %smb_md5;  
   
371    
372          sub norm_dir {          sub norm_dir {
373                  my $foo = shift;                  my $foo = shift;
# Line 225  sub snap_share { Line 383  sub snap_share {
383          my $di = 0;          my $di = 0;
384          while ($di <= $#dirs && $real_bl) {          while ($di <= $#dirs && $real_bl) {
385                  my $d=$dirs[$di++];                  my $d=$dirs[$di++];
386                  opendir(DIR,"$bl/$d") || warn "opendir($bl/$d): $!\n";                  opendir(DIR,"$real_bl/$d") || warn "opendir($real_bl/$d): $!\n";
387    
388                  # read .backupignore if exists                  # read .backupignore if exists
389                  if (-f "$bl/$d/.backupignore") {                  if (-f "$real_bl/$d/.backupignore") {
390                          open(I,"$bl/$d/.backupignore");                          open(I,"$real_bl/$d/.backupignore");
391                          while(<I>) {                          while(<I>) {
392                                  chomp;                                  chomp;
393                                  push @ignore,norm_dir("$d/$_");                                  push @ignore,norm_dir("$d/$_");
394                          }                          }
395                          close(I);                          close(I);
396  print STDERR "ignore: ",join("|",@ignore),"\n";  #print STDERR "ignore: ",join("|",@ignore),"\n";
397                          link "$bl/$d/.backupignore","$bc/$d/.backupignore" ||                          link "$real_bl/$d/.backupignore","$bc/$d/.backupignore" ||
398                                  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";
399                  }                  }
400    
401                  # read .md5sum if exists                  # read .md5sum if exists
402                  if (-f "$bl/$d/.md5sum") {                  if (-f "$real_bl/$d/.md5sum") {
403                          open(I,"$bl/$d/.md5sum");                          open(I,"$real_bl/$d/.md5sum");
404                          while(<I>) {                          while(<I>) {
405                                  chomp;                                  chomp;
406                                  my ($md5,$f) = split(/\s+/,$_,2);                                  my ($md5,$f) = split(/\s+/,$_,2);
# Line 256  print STDERR "ignore: ",join("|",@ignore Line 414  print STDERR "ignore: ",join("|",@ignore
414                          next if ($f eq '.');                          next if ($f eq '.');
415                          next if ($f eq '..');                          next if ($f eq '..');
416                          my $pr = norm_dir("$d/$f");     # path relative                          my $pr = norm_dir("$d/$f");     # path relative
417                          my $pf = norm_dir("$d/$f","$bl/");      # path full                          my $pf = norm_dir("$d/$f","$real_bl/"); # path full
418                          if (grep(/^\Q$pr\E$/,@ignore) == 0) {                          if (grep(/^\Q$pr\E$/,@ignore) == 0) {
419                                  if (-f $pf) {                                  if (-f $pf) {
420                                          push @files,$pr;                                          push @files,$pr;
# Line 266  print STDERR "ignore: ",join("|",@ignore Line 424  print STDERR "ignore: ",join("|",@ignore
424                                  } elsif (-d $pf) {                                  } elsif (-d $pf) {
425                                          push @dirs,$pr;                                          push @dirs,$pr;
426                                  } else {                                  } else {
427                                          print STDERR "unknown type: $pf\n";                                          xlog($share,"not file or directory: $pf",0);
428                                  }                                  }
429                          } else {                          } else {
430                                  print STDERR "ignored: $pr\n";                                  xlog($share,"ignored: $pr");
431                          }                          }
432                  }                  }
433          }          }
434    
435          xlog($share,($#files+1)." files and ".($#dirs+1)." dirs on local disk before backup");          # local dir always include /
436            xlog($share,($#files+1)." files and ".($#dirs)." dirs on local disk before backup");
437    
438          # read smb filesystem          # read smb filesystem
439    
# Line 285  print STDERR "ignore: ",join("|",@ignore Line 444  print STDERR "ignore: ",join("|",@ignore
444    
445          $di = 0;          $di = 0;
446          while ($di <= $#smb_dirs) {          while ($di <= $#smb_dirs) {
447                  my $d=$smb_dirs[$di++];                  my $d=$smb_dirs[$di];
448                  my $pf = norm_dir($d,"smb:$share/");    # path full                  my $pf = norm_dir($d,"smb:$share/");    # path full
449                  my $D = $smb->opendir($pf) || warn "smb->opendir($pf): $!\n";                  my $D = $smb->opendir($pf);
450                    if (! $D) {
451                            xlog($share,"FATAL: $share [$pf] as $param{username}/$param{workgroup}: $!",0);
452                            # remove failing dir
453                            delete $smb_dirs[$di];
454                            return 0;                       # failed
455                    }
456                    $di++;
457    
458                  my @clutter = $smb->readdir_struct($D);                  my @clutter = $smb->readdir_struct($D);
459                  foreach my $item (@clutter) {                  foreach my $item (@clutter) {
# Line 305  print STDERR "ignore: ",join("|",@ignore Line 471  print STDERR "ignore: ",join("|",@ignore
471                                  } elsif ($item->[0] == main::SMBC_DIR) {                                  } elsif ($item->[0] == main::SMBC_DIR) {
472                                          push @smb_dirs,$pr;                                          push @smb_dirs,$pr;
473                                  } else {                                  } else {
474                                          print STDERR "unknown type: $pf\n";                                          xlog($share,"not file or directory [".$item->[0]."]: $pf",0);
475                                  }                                  }
476                          } else {                          } else {
477                                  print STDERR "smb ignored: $pr\n";                                  xlog($share,"smb ignored: $pr");
478                          }                          }
479                  }                  }
480          }          }
481    
482          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");
483    
484          # sync dirs          # sync dirs
485          my $lc = List::Compare->new(\@dirs, \@smb_dirs);          my $lc = List::Compare->new(\@dirs, \@smb_dirs);
# Line 345  print STDERR "ignore: ",join("|",@ignore Line 511  print STDERR "ignore: ",join("|",@ignore
511                                    
512                  foreach my $f (@_) {                  foreach my $f (@_) {
513  #print "smb_copy $from/$f -> $to/$f\n";  #print "smb_copy $from/$f -> $to/$f\n";
514                          if (! open(F,"> $to/$f")) {                          my $md5 = Digest::MD5->new;
                                 print STDERR "can't open new file $to/$f: $!\n";  
                                 next;  
                         }  
515    
516                          my $fd = $smb->open("$from/$f");                          my $fd = $smb->open("$from/$f");
517                          if (! $fd) {                          if (! $fd) {
518                                  print STDERR "can't open smb file $from/$f: $!\n";                                  xlog("WARNING","can't open smb file $from/$f: $!");
519                                    next;
520                            }
521    
522                            if (! open(F,"> $to/$f")) {
523                                    xlog("WARNING","can't open new file $to/$f: $!");
524                                  next;                                  next;
525                          }                          }
526    
527                          while (defined(my $b=$smb->read($fd,4096))) {                          while (defined(my $b=$smb->read($fd,4096))) {
528                                  print F $b;                                  print F $b;
529                                  $l += length($b);                                  $l += length($b);
530                                    $md5->add($b);
531                          }                          }
532    
533                          $smb->close($fd);                          $smb->close($fd);
534                          close(F);                          close(F);
535    
536                            $file_md5{$f} = $md5->hexdigest;
537    
538                          # FIX: this fails with -T                          # FIX: this fails with -T
539                          my ($a,$m) = ($smb->stat("$from/$f"))[10,11];                          my ($a,$m) = ($smb->stat("$from/$f"))[10,11];
540                          utime $a, $m, "$to/$f" ||                          utime $a, $m, "$to/$f" ||
# Line 417  print STDERR "ignore: ",join("|",@ignore Line 588  print STDERR "ignore: ",join("|",@ignore
588          xlog($share,"$transfer bytes transfered...");          xlog($share,"$transfer bytes transfered...");
589    
590          foreach (@ln_files) {          foreach (@ln_files) {
591                  link "$bl/$_","$bc/$_" || warn "link $bl/$_ -> $bc/$_: $!\n";                  link "$real_bl/$_","$bc/$_" || warn "link $real_bl/$_ -> $bc/$_: $!\n";
592          }          }
593    
594          # remove files          # remove files
595          foreach (sort @files2erase) {          foreach (sort @files2erase) {
596                  unlink "$bc/$_" || warn "unlink $_: $!\n";                  unlink "$bc/$_" || warn "unlink $_: $!\n";
597                    delete $file_md5{$_};
598          }          }
599    
600          # remove not needed dirs (after files)          # remove not needed dirs (after files)
# Line 430  print STDERR "ignore: ",join("|",@ignore Line 602  print STDERR "ignore: ",join("|",@ignore
602                  rmdir "$bc/$_" || warn "rmdir $_: $!\n";                  rmdir "$bc/$_" || warn "rmdir $_: $!\n";
603          }          }
604    
605            # remove old .md5sum
606          # FIX: create .md5sum          foreach (sort @dirs) {
607                    unlink "$bc/$_/.md5sum" if (-e "$bc/$_/.md5sum");
608            }
609    
610            # erase stale entries in .md5sum
611            my @md5_files = keys %file_md5;
612            $lc = List::Compare->new(\@md5_files, \@smb_files);
613            foreach my $file ($lc->get_Lonly) {
614                    xlog("NOTICE","removing stale '$file' from .md5sum");
615                    delete $file_md5{$file};
616            }
617    
618            # create .md5sum
619            my $last_dir = '';
620            my $md5;
621            foreach my $f (sort { $file_md5{$a} cmp $file_md5{$b} } keys %file_md5) {
622                    my $dir = dirname($f);
623                    my $file = basename($f);
624    #print "$f -- $dir / $file<--\n";
625                    if ($dir ne $last_dir) {
626                            close($md5) if ($md5);
627                            open($md5, ">> $bc/$dir/.md5sum") || warn "can't create $bc/$dir/.md5sum: $!";
628                            $last_dir = $dir;
629    #print STDERR "writing $last_dir/.md5sum\n";
630                    }
631                    print $md5 $file_md5{$f},"  $file\n";
632            }
633            close($md5) if ($md5);
634    
635          # create leatest link          # create leatest link
636    #print "ln -s $bc $real_bl\n";
637            if (-l $bl) {
638                    unlink $bl || warn "can't remove old latest symlink $bl: $!\n";
639            }
640          symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";          symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n";
641    
642            # FIX: sanity check -- remove for speedup
643            xlog($share,"failed to create latest symlink $bl -> $bc...") if (readlink($bl) ne $bc || ! -l $bl);
644    
645          xlog($share,"backup completed...");          xlog($share,"backup completed...");
 }  
646    
647            return 1;
648    }
649    __END__
650  #-------------------------------------------------------------------------  #-------------------------------------------------------------------------
651    
652    
653    =head1 NAME
654    
655    psinib - Perl Snapshot Is Not Incremental Backup
656    
657    =head1 SYNOPSIS
658    
659    ./psinib.pl [OPTION]... [mount script]
660    
661    =head1 DESCRIPTION
662    
663    Option can be one of more of following:
664    
665    =over 8
666    
667    =item C<--backupdest=dir>
668    
669    Specify backup destination directory (defaults is /data/
670    
671    =item C<--noping>
672    
673    Don't use ping to check if host is up (default is ti use tcp syn to cifs
674    port)
675    
676    =item C<--verbose -v>
677    
678    Increase verbosity level. Defailt is 1 which prints moderate amount of data
679    on STDOUT and STDERR.
680    
681    =item C<--quiet -q>
682    
683    Decrease verbosity level
684    
685    =item C<--email=email@domain>
686    
687    Send e-mails instead of dumping errors to STDERR. Useful for cron jobs.
688    
689    =back
690    
691    This script in current version support just backup of Samba (or Micro$oft
692    Winblowz) shares to central disk space. Central disk space is organized in
693    multiple directories named after:
694    
695    =over 4
696    
697    =item *
698    server which is sharing files to be backed up
699    
700    =item *
701    name of share on server
702    
703    =item *
704    dated directory named like standard ISO date format (YYYYMMDD).
705    
706    =back
707    
708    In each dated directory you will find I<snapshot> of all files on
709    exported share on that particular date.
710    
711    You can also use symlink I<latest> which will lead you to
712    last completed backup. After that you can use some other backup
713    software to transfer I<snapshot> to tape, CD-ROM or some other media.
714    
715    =head2 Design considerations
716    
717    Since taking of share snapshot every day requires a lot of disk space and
718    network bandwidth, B<psinib> uses several techniques to keep disk usage and
719    network traffic at acceptable level:
720    
721    =over 3
722    
723    =item - usage of hard-links to provide same files in each snapshot (as opposed
724    to have multiple copies of same file)
725    
726    =item - usage of file size, atime and mtime to find changes of files without
727    transferring whole file over network (just share browsing is transfered
728    over network)
729    
730    =item - usage of C<.md5sum> files (compatible with command-line utility
731    C<md5sum>) to keep file between snapshots hard-linked
732    
733    =back
734    
735    =head1 CONFIGURATION
736    
737    This section is not yet written.
738    
739    =head1 HACKS, TRICKS, BUGS and LIMITATIONS
740    
741    This chapter will have all content that doesn't fit anywhere else.
742    
743    =head2 Can snapshots be more frequent than daily?
744    
745    There is not real reason why you can't take snapshot more often than
746    once a day. Actually, if you are using B<psinib> to backup Windows
747    workstations you already know that they tend to come-and-go during the day
748    (reboots probably ;-), so running B<psinib> several times a day increases
749    your chance of having up-to-date backup (B<psinib> will not make multiple
750    snapshots for same day, nor will it update snapshot for current day if
751    it already exists).
752    
753    However, changing B<psinib> to produce snapshots which are, for example, hourly
754    is a simple change of C<$DIR_TIME_FMT> which is currently set to
755    C<'%Y%m%d'> (see I<strftime> documentation for explanation of that
756    format). If you change that to C<'%Y%m%d-%H> you can have hourly snapshots
757    (if your network is fast enough, that is...). Also, some of messages in
758    program will sound strange, but other than that it should work.
759    I<You have been warned>.
760    
761    =head2 Do I really need to share every directory which I want to snapshot?
762    
763    Actually, no. Due to usage of C<Filesys::SmbClient> module, you can also
764    specify sub-directory inside your share that you want to backup. This feature
765    is most useful if you want to use administrative shares (but, have in mind
766    that you have to enter your Win administrator password in unencrypted file on
767    disk to do that) like this:
768    
769            smbmount //server/c$/WinNT/fonts  /mnt  -o username=administrator%win  
770    
771    After that you will get directories with snapshots like:
772    
773            server/c_WinNT_fonts/yyyymmdd/....
774    
775    =head2 Won't I run out of disk space?
776    
777    Of course you will... Snapshots and logfiles will eventually fill-up your disk.
778    However, you can do two things to stop that:
779    
780    =head3 Clean snapshort older than x days
781    
782    You can add following command to your C<root> crontab:
783    
784            find /backup/isis_backup -type d -mindepth 3 -maxdepth 3 -mtime +11 -exec rm -Rf {} \;
785    
786    I assume that C</backup/isis_backup> is directory in which are your snapshots
787    and that you don't want to keep snapshots older than 11 days (that's
788    C<-mtime +11> part of command).
789    
790    =head3 Rotate your logs
791    
792    I will leave that to you. I relay on GNU/Debian's C<logrotate> to do it for me.
793    
794    =head2 What are I<YYYYMMDD.partial> directories?
795    
796    If there isn't I<latest> symlink in snapshot directory, it's preatty safe to
797    assume that previous backup from that day failed. So, that directory will
798    be renamed to I<YYYYMMDD.partial> and snapshot will be performed again,
799    linking same files (other alternative would be to erase that dir and find
800    second-oldest directory, but this seemed like more correct approach).
801    
802    =head2 I can't connect to any share
803    
804    Please verify that nmblookup (which is part of samba package) is in /bin or
805    /usr/bin. Also verify that nmblookup returns IP address for your server
806    using:
807    
808       $ nmblookup tvhouse
809       querying tvhouse on 192.168.34.255
810       192.168.34.30 tvhouse<00>
811    
812    If you don't get any output, your samba might not listen to correct interface
813    (see interfaces in smb.conf).
814    
815    =head2 Aren't backups boring?
816    
817    No! If you have subjects.txt in same directory as C<psinib.pl> you can get
818    various funny subjects in your mail. They change over time as long as you
819    ignore your backup.
820    
821    =head1 AUTHOR
822    
823    Dobrica Pavlinusic <dpavlin@rot13.org>
824    
825    L<http:E<sol>E<sol>www.rot13.orgE<sol>~dpavlinE<sol>>
826    
827    =head1 LICENSE
828    
829    This product is licensed under GNU Public License (GPL) v2 or later.
830    
831    =cut

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

  ViewVC Help
Powered by ViewVC 1.1.26