/[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.12 by dpavlin, Sun Oct 12 16:13:38 2003 UTC revision 1.21 by dpavlin, Sat Nov 8 01:46:53 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/';  #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 58  my $c = 0; Line 62  my $c = 0;
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("tcp", 2);  # init Net::Ping object
119  # ping will try tcp connect to netbios-ssn (139)  my $ping;
120  $p->{port_num} = getservbyname("netbios-ssn", "tcp");  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    
# Line 114  while(<M>) { Line 185  while(<M>) {
185                  $real_bl=readlink($bl) || die "can't read link $bl: $!";                  $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 "/");                  $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/");
187                  if (-l $bc && $real_bl eq $bc) {                  if (-l $bc && $real_bl eq $bc) {
188                          print "$share allready backuped...\n";                          xlog($share,"allready backuped...");
189                          $backup_ok++;                          $backup_ok++;
190                          next;                          next;
191                  }                  }
# Line 122  while(<M>) { Line 193  while(<M>) {
193          }          }
194    
195    
196          print "working on $share\n";          xlog($share,"working on $share...");
197    
198          # try to nmblookup IP          # try to nmblookup IP
199          $ip = get_ip($share) if (! $ip);          $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                          if (snap_share($share,$user,$passwd,$workgroup)) {                          if (snap_share($share,$user,$passwd,$workgroup)) {
205                                  $backup_ok++;                                  $backup_ok++;
206                          }                          }
# Line 138  while(<M>) { Line 209  while(<M>) {
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    
# Line 157  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  # 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  # dump warn and dies into log  # dump warn and dies into log
268  BEGIN { $SIG{'__WARN__'} = sub { xlog('WARN',$_[0]) ; warn $_[0] } }  BEGIN { $SIG{'__WARN__'} = sub { xlog('WARN',$_[0],1) ; exit 1 } }
269  BEGIN { $SIG{'__DIE__'} = sub { xlog('DIE',$_[0]) ; die $_[0] } }  BEGIN { $SIG{'__DIE__'} = sub { xlog('DIE',$_[0],0) ; exit 1 } }
270    
271    
272  # split share name to host, dir and currnet date dir  # split share name to host, dir and currnet date dir
# Line 182  sub share2host_dir { Line 279  sub share2host_dir {
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);          return ($host,$dir,strftime $DIR_TIME_FMT, localtime);
# Line 211  sub snap_share { Line 308  sub snap_share {
308          if (-l $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, trying to find last backup, ";                          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")) {                  if (opendir(BL_DIR, "$BACKUP_DEST/$host/$dir")) {
320                          my @bl_dirs = sort grep { !/^\./ && -d "$BACKUP_DEST/$host/$dir/$_" } readdir(BL_DIR);                          my @bl_dirs = sort grep { !/^\./ && -d "$BACKUP_DEST/$host/$dir/$_" } readdir(BL_DIR);
321                          closedir(BL_DIR);                          closedir(BL_DIR);
322                          $real_bl=pop @bl_dirs;                          $real_bl=pop @bl_dirs;
323                          print "using $real_bl as latest...\n";                          xlog($share,"using $real_bl as latest...");
324                          $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 "/");
325                          if ($real_bl eq $bc) {                          if ($real_bl eq $bc) {
326                                  xlog($share,"latest from today (possible partial backup)");                                  xlog($share,"latest from today (possible partial backup)");
# Line 225  sub snap_share { Line 328  sub snap_share {
328                                  $real_bl .= ".partial";                                  $real_bl .= ".partial";
329                          }                          }
330                  } else {                  } else {
331                          print "this is first run...\n";                          xlog($share,"this is first run...");
332                  }                  }
333          }          }
334    
335          if (-l $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 1;                  return 1;
338          }          }
339    
# Line 238  sub snap_share { Line 341  sub snap_share {
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 259  sub snap_share { Line 362  sub snap_share {
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;
# Line 320  sub snap_share { Line 424  sub snap_share {
424                                  } elsif (-d $pf) {                                  } elsif (-d $pf) {
425                                          push @dirs,$pr;                                          push @dirs,$pr;
426                                  } else {                                  } else {
427                                          print STDERR "not file or directory: $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          }          }
# Line 344  sub snap_share { Line 448  sub snap_share {
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);                  my $D = $smb->opendir($pf);
450                  if (! $D) {                  if (! $D) {
451                          xlog($share,"FATAL: $share [$pf]: $!");                          xlog($share,"FATAL: $share [$pf] as $param{username}/$param{workgroup}: $!",0);
452                          # remove failing dir                          # remove failing dir
453                          delete $smb_dirs[$di];                          delete $smb_dirs[$di];
454                          return 0;                       # failed                          return 0;                       # failed
# Line 367  sub snap_share { Line 471  sub snap_share {
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 "not file or directory [",$item->[0],"]: $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          }          }
# Line 490  sub snap_share { Line 594  sub snap_share {
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 502  sub snap_share { Line 607  sub snap_share {
607                  unlink "$bc/$_/.md5sum" if (-e "$bc/$_/.md5sum");                  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          # create .md5sum
619          my $last_dir = '';          my $last_dir = '';
620          my $md5;          my $md5;
# Line 543  psinib - Perl Snapshot Is Not Incrementa Line 656  psinib - Perl Snapshot Is Not Incrementa
656    
657  =head1 SYNOPSIS  =head1 SYNOPSIS
658    
659  ./psinib.pl  ./psinib.pl [OPTION]... [mount script]
660    
661  =head1 DESCRIPTION  =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  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  Winblowz) shares to central disk space. Central disk space is organized in
693  multiple directories named after:  multiple directories named after:
# Line 658  be renamed to I<YYYYMMDD.partial> and sn Line 799  be renamed to I<YYYYMMDD.partial> and sn
799  linking same files (other alternative would be to erase that dir and find  linking same files (other alternative would be to erase that dir and find
800  second-oldest directory, but this seemed like more correct approach).  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  =head1 AUTHOR
822    
823  Dobrica Pavlinusic <dpavlin@rot13.org>  Dobrica Pavlinusic <dpavlin@rot13.org>
824    
825  L<http://www.rot13.org/~dpavlin/>  L<http:E<sol>E<sol>www.rot13.orgE<sol>~dpavlinE<sol>>
826    
827  =head1 LICENSE  =head1 LICENSE
828    

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

  ViewVC Help
Powered by ViewVC 1.1.26