--- psinib.pl 2003/01/31 22:31:41 1.7 +++ psinib.pl 2003/10/26 14:04:17 1.17 @@ -24,16 +24,21 @@ use Fcntl qw(LOCK_EX LOCK_NB); use Digest::MD5; use File::Basename; +use Getopt::Long; # configuration my $LOG_TIME_FMT = '%Y-%m-%d %H:%M:%S'; # strftime format for logfile my $DIR_TIME_FMT = '%Y%m%d'; # strftime format for backup dir +# define timeout for ping +my $PING_TIMEOUT = 5; + my $LOG = '/var/log/backup.log'; # add path here... #$LOG = '/tmp/backup.log'; # store backups in which directory my $BACKUP_DEST = '/backup/isis_backup'; +#my $BACKUP_DEST = '/tmp/backup/'; # files to ignore in backup my @ignore = ('.md5sum', '.backupignore', 'backupignore.txt'); @@ -50,13 +55,24 @@ sleep 1; redo if ++$c < 10; # no response for 10 sec, bail out - print STDERR "can't take lock on $LOG -- another $0 running?\n"; + xlog("ABORT","can't take lock on $LOG -- another $0 running?"); exit 1; } # taint path: nmblookup should be there! $ENV{'PATH'} = "/usr/bin:/bin"; +my $use_ping = 1; # default: use syn tcp ping to verify that host is up +my $verbose = 1; # default verbosity level +my $quiet = 0; + +my $result = GetOptions( + "ping!" => \$use_ping, "backupdest!" => \$BACKUP_DEST, + "verbose+" => \$verbose, "quiet+" => \$quiet, +); + +$verbose -= $quiet; + my $mounts = shift @ARGV || 'mountscript'; # die "usage: $0 mountscript"; @@ -64,9 +80,30 @@ my @in_backup; # shares which are backeduped this run -my $p = new Net::Ping->new("tcp", 2); -# ping will try tcp connect to netbios-ssn (139) -$p->{port_num} = getservbyname("netbios-ssn", "tcp"); +# init Net::Ping object +my $ping; +if ($use_ping) { + $ping = new Net::Ping->new("syn", 2); + # ping will try tcp connect to netbios-ssn (139) + $ping->{port_num} = getservbyname("netbios-ssn", "tcp"); +} + +# do syn ping to cifs port +sub host_up { + my $ping = shift || return; + my $host_ip = shift || xlog("host_up didn't get IP"); + my $timeout = shift; + return 1 if (! $use_ping); + + $ping->ping($host_ip,$timeout); + my $return = 0; + + while (my ($host,$rtt,$ip) = $ping->ack) { + xlog("","HOST: $host [$ip] ACKed in $rtt seconds"); + $return = 1 if ($ip eq $host_ip); + } + return $return; +} my $backup_ok = 0; @@ -81,7 +118,7 @@ next if !/^\s*smbmount\s/; my (undef,$share,undef,$opt) = split(/\s+/,$_,4); - my ($user,$passwd,$workgroup); + my ($user,$passwd,$workgroup,$ip); foreach (split(/,/,$opt)) { my ($n,$v) = split(/=/,$_,2); @@ -97,6 +134,8 @@ } } elsif ($n =~ m#workgroup#i) { $workgroup = $v; + } elsif ($n =~ m#ip#i) { + $ip = $v; } } @@ -107,11 +146,11 @@ my $bl = "$BACKUP_DEST/$host/$dir/latest"; # latest backup my $bc = "$BACKUP_DEST/$host/$dir/$date_dir"; # current one my $real_bl; - if (-e $bl) { + if (-l $bl) { $real_bl=readlink($bl) || die "can't read link $bl: $!"; $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/"); - if (-e $bc && $real_bl eq $bc) { - print "$share allready backuped...\n"; + if (-l $bc && $real_bl eq $bc) { + xlog($share,"allready backuped..."); $backup_ok++; next; } @@ -119,16 +158,17 @@ } - print "working on $share\n"; - + xlog($share,"working on $share..."); - my $ip = get_ip($share); + # try to nmblookup IP + $ip = get_ip($share) if (! $ip); if ($ip) { xlog($share,"IP is $ip"); - if ($p->ping($ip)) { - snap_share($share,$user,$passwd,$workgroup); - $backup_ok++; + if (host_up($ping, $ip,$PING_TIMEOUT)) { + if (snap_share($share,$user,$passwd,$workgroup)) { + $backup_ok++; + } } } } @@ -159,7 +199,8 @@ my $share = shift; my $t = strftime $LOG_TIME_FMT, localtime; my $m = shift || '[no log entry]'; - print STDERR $m,"\n"; + my $l = shift || 1; + print STDERR $m,"\n" if ($verbose >= $l); print L "$t $share\t$m\n"; } @@ -178,7 +219,7 @@ $dir =~ s/^_+//; $dir =~ s/_+$//; } else { - print "Can't parse share $share into host and directory!\n"; + xlog($share,"Can't parse share $share into host and directory!",1); return; } return ($host,$dir,strftime $DIR_TIME_FMT, localtime); @@ -192,9 +233,9 @@ my %param = ( debug => 0 ); - $param{username} = shift; - $param{password} = shift; - $param{workgroup} = shift; + $param{username} = shift || warn "can't find username for share $share"; + $param{password} = shift || warn "can't find passwod for share $share"; + $param{workgroup} = shift || warn "can't find workgroup for share $share"; my ($host,$dir,$date_dir) = share2host_dir($share); @@ -204,16 +245,16 @@ my $bc = "$BACKUP_DEST/$host/$dir/$date_dir"; my $real_bl; - if (-e $bl) { + if (-l $bl) { $real_bl=readlink($bl) || die "can't read link $bl: $!"; $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/"); } else { - print "no old backup, trying to find last backup, "; + xlog($share,"no old backup, trying to find last backup,"); if (opendir(BL_DIR, "$BACKUP_DEST/$host/$dir")) { my @bl_dirs = sort grep { !/^\./ && -d "$BACKUP_DEST/$host/$dir/$_" } readdir(BL_DIR); closedir(BL_DIR); $real_bl=pop @bl_dirs; - print "using $real_bl as latest...\n"; + xlog($share,"using $real_bl as latest..."); $real_bl="$BACKUP_DEST/$host/$dir/$real_bl" if (substr($real_bl,0,1) ne "/"); if ($real_bl eq $bc) { xlog($share,"latest from today (possible partial backup)"); @@ -221,25 +262,25 @@ $real_bl .= ".partial"; } } else { - print "this is first run...\n"; + xlog($share,"this is first run..."); } } - if (-e $bc && $real_bl && $real_bl eq $bc) { - print "$share allready backuped...\n"; - return; + if (-l $bc && $real_bl && $real_bl eq $bc) { + xlog($share,"allready backuped..."); + return 1; } die "You should really create BACKUP_DEST [$BACKUP_DEST] by hand! " if (!-e $BACKUP_DEST); if (! -e "$BACKUP_DEST/$host") { mkdir "$BACKUP_DEST/$host" || die "can't make dir for host $host, $BACKUP_DEST/$host: $!"; - print "created host directory $BACKUP_DEST/$host...\n"; + xlog($share,"created host directory $BACKUP_DEST/$host..."); } if (! -e "$BACKUP_DEST/$host/$dir") { mkdir "$BACKUP_DEST/$host/$dir" || die "can't make dir for share $share, $BACKUP_DEST/$host/$dir $!"; - print "created dir for share $share, $BACKUP_DEST/$host/$dir...\n"; + xlog($share,"created dir for this share $BACKUP_DEST/$host/$dir..."); } mkdir $bc || die "can't make dir for current backup $bc: $!"; @@ -255,6 +296,7 @@ my %file_atime; my %file_mtime; #my %file_md5; + %file_md5 = (); my @smb_files; my %smb_size; @@ -285,7 +327,7 @@ push @ignore,norm_dir("$d/$_"); } close(I); -print STDERR "ignore: ",join("|",@ignore),"\n"; +#print STDERR "ignore: ",join("|",@ignore),"\n"; link "$real_bl/$d/.backupignore","$bc/$d/.backupignore" || warn "can't copy $real_bl/$d/.backupignore to current backup dir: $!\n"; } @@ -316,15 +358,16 @@ } elsif (-d $pf) { push @dirs,$pr; } else { - print STDERR "unknown type: $pf\n"; + xlog($share,"not file or directory: $pf",0); } } else { - print STDERR "ignored: $pr\n"; + xlog($share,"ignored: $pr"); } } } - xlog($share,($#files+1)." files and ".($#dirs+1)." dirs on local disk before backup"); + # local dir always include / + xlog($share,($#files+1)." files and ".($#dirs)." dirs on local disk before backup"); # read smb filesystem @@ -335,9 +378,16 @@ $di = 0; while ($di <= $#smb_dirs) { - my $d=$smb_dirs[$di++]; + my $d=$smb_dirs[$di]; my $pf = norm_dir($d,"smb:$share/"); # path full - my $D = $smb->opendir($pf) || warn "smb->opendir($pf): $!\n"; + my $D = $smb->opendir($pf); + if (! $D) { + xlog($share,"FATAL: $share [$pf]: $!"); + # remove failing dir + delete $smb_dirs[$di]; + return 0; # failed + } + $di++; my @clutter = $smb->readdir_struct($D); foreach my $item (@clutter) { @@ -355,15 +405,15 @@ } elsif ($item->[0] == main::SMBC_DIR) { push @smb_dirs,$pr; } else { - print STDERR "unknown type: $pf\n"; + xlog($share,"not file or directory [".$item->[0]."]: $pf",0); } } else { - print STDERR "smb ignored: $pr\n"; + xlog($share,"smb ignored: $pr"); } } } - 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"); # sync dirs my $lc = List::Compare->new(\@dirs, \@smb_dirs); @@ -395,16 +445,16 @@ foreach my $f (@_) { #print "smb_copy $from/$f -> $to/$f\n"; - if (! open(F,"> $to/$f")) { - print STDERR "can't open new file $to/$f: $!\n"; - next; - } - my $md5 = Digest::MD5->new; my $fd = $smb->open("$from/$f"); if (! $fd) { - print STDERR "can't open smb file $from/$f: $!\n"; + xlog("WARNING","can't open smb file $from/$f: $!"); + next; + } + + if (! open(F,"> $to/$f")) { + xlog("WARNING","can't open new file $to/$f: $!"); next; } @@ -478,6 +528,7 @@ # remove files foreach (sort @files2erase) { unlink "$bc/$_" || warn "unlink $_: $!\n"; + delete $file_md5{$_}; } # remove not needed dirs (after files) @@ -490,6 +541,14 @@ unlink "$bc/$_/.md5sum" if (-e "$bc/$_/.md5sum"); } + # erase stale entries in .md5sum + my @md5_files = keys %file_md5; + $lc = List::Compare->new(\@md5_files, \@smb_files); + foreach my $file ($lc->get_Lonly) { + xlog("NOTICE","removing stale '$file' from .md5sum"); + delete $file_md5{$file}; + } + # create .md5sum my $last_dir = ''; my $md5; @@ -505,21 +564,22 @@ } print $md5 $file_md5{$f}," $file\n"; } - close($md5); + close($md5) if ($md5); # create leatest link #print "ln -s $bc $real_bl\n"; - if (-e $bl) { + if (-l $bl) { unlink $bl || warn "can't remove old latest symlink $bl: $!\n"; } symlink $bc,$bl || warn "can't create latest symlink $bl -> $bc: $!\n"; # FIX: sanity check -- remove for speedup - xlog($share,"failed to create latest symlink...") if (readlink($bl) ne $bc || ! -e $bl); + xlog($share,"failed to create latest symlink $bl -> $bc...") if (readlink($bl) ne $bc || ! -l $bl); xlog($share,"backup completed..."); -} + return 1; +} __END__ #------------------------------------------------------------------------- @@ -530,10 +590,34 @@ =head1 SYNOPSIS -./psinib.pl +./psinib.pl [OPTION]... [mount script] =head1 DESCRIPTION +Option can be one of more of following: + +=over 8 + +=item C<--backupdest=dir> + +Specify backup destination directory (defaults is /data/ + +=item C<--noping> + +Don't use ping to check if host is up (default is ti use tcp syn to cifs +port) + +=item C<--verbose -v> + +Increase verbosity level. Defailt is 1 which prints moderate amount of data +on STDOUT and STDERR. + +=item C<--quiet -q> + +Decrease verbosity level + +=back + This script in current version support just backup of Samba (or Micro$oft Winblowz) shares to central disk space. Central disk space is organized in multiple directories named after: @@ -645,11 +729,24 @@ linking same files (other alternative would be to erase that dir and find second-oldest directory, but this seemed like more correct approach). +=head2 I can't connect to any share + +Please verify that nmblookup (which is part of samba package) is in /bin or +/usr/bin. Also verify that nmblookup returns IP address for your server +using: + + $ nmblookup tvhouse + querying tvhouse on 192.168.34.255 + 192.168.34.30 tvhouse<00> + +If you don't get any output, your samba might not listen to correct interface +(see interfaces in smb.conf). + =head1 AUTHOR Dobrica Pavlinusic -L +LEwww.rot13.orgE~dpavlinE> =head1 LICENSE