master_watcher
changeset 6 f7de068b5bea
parent 5 96a0d63303c6
child 7 836e273e9992
equal deleted inserted replaced
5:96a0d63303c6 6:f7de068b5bea
     1 #! /usr/bin/perl -w
     1 #! /usr/bin/perl -w
     2 my $USAGE = <<'#';
     2 my $USAGE = <<'#';
     3 Usage: $ME [options]
     3 Usage: $ME [options]
     4        -l --logfile=s   Name of the logfile we've to read [$opt_logfile]
     4        -l --logfile=s   Name of the logfile we've to read [$opt_logfile]
     5        -z --zonesdir=s  Where the named.conf's are expected [$opt_zonesdir]
     5        -z --zonesdir=s  Where the named.conf's are expected [$opt_zonesdir]
     6        -u --[no]update  Update the "masters"-entries [$opt_update] 
     6        -u --[no]update  Update the \"masters\"-entries [$opt_update] 
     7        -f --[no]follow  Follow the end of the logfile [$opt_follow]
     7        -f --[no]follow  Follow the end of the logfile [$opt_follow]
     8        -d --[no]debug   extra debug output [$opt_debug]
     8        -d --[no]debug   extra debug output [$opt_debug]
     9        -h --help        This text [$opt_help]
     9        -h --help        This text [$opt_help]
    10 #
    10 #
    11 # Es wird ein Verzeichnis geben, in diesem Verzeichnis liegt für 
    11 # Es wird ein Verzeichnis geben, in diesem Verzeichnis liegt für 
    66 my $opt_follow = 0;
    66 my $opt_follow = 0;
    67 my $opt_update = 0;
    67 my $opt_update = 0;
    68 my $opt_debug = 0;
    68 my $opt_debug = 0;
    69 
    69 
    70 my $naptime = 60;
    70 my $naptime = 60;
    71 my $inode = 0;
    71 
    72 
    72 
    73 
    73 sub updateFile($$$);
    74 sub updateFile($@);
       
    75 sub debug($;@) { syslog(LOG_DEBUG, "DEBUG " . shift @_, @_) if $opt_debug; }
    74 sub debug($;@) { syslog(LOG_DEBUG, "DEBUG " . shift @_, @_) if $opt_debug; }
    76 
    75 
    77 MAIN: {
    76 MAIN: {
    78 
    77 
    79 
    78 
    93 	print eval "\"$USAGE\"";
    92 	print eval "\"$USAGE\"";
    94 	exit 0;
    93 	exit 0;
    95     }
    94     }
    96 
    95 
    97     open (LOGFILE, $_ = "<$opt_logfile") or die "Can't open $_: $!\n";
    96     open (LOGFILE, $_ = "<$opt_logfile") or die "Can't open $_: $!\n";
    98     $inode = (LOGFILE->stat)[1];
       
    99     for (;;) {
    97     for (;;) {
   100 	my (%masters, my %missing);
    98 	my (%masters, %missing, %nomasters);
   101 	while (<LOGFILE>) {
    99 	while (<LOGFILE>) {
   102 	    
   100 	    
   103 	    my ($domain, $ip);
   101 	    my ($domain, $ip);
   104 	    
   102 	    
   105 	    # NOTIFY-Zeilen
   103 	    # NOTIFY-Zeilen
   128 		# Konfigurations-Datei haben)
   126 		# Konfigurations-Datei haben)
   129 		next if exists $missing{$domain};	# schon erledigt
   127 		next if exists $missing{$domain};	# schon erledigt
   130 
   128 
   131 		debug("Missing file for $domain\n");
   129 		debug("Missing file for $domain\n");
   132 		$missing{$domain} = 1;
   130 		$missing{$domain} = 1;
       
   131 		next;
   133 	    };
   132 	    };
   134 
   133 
   135 	    # Wenn wir ein "... loaded" finden, dann fehlt das File nicht gänzlich!
   134 	    # Wenn wir ein "... loaded" finden, dann fehlt das File nicht gänzlich!
   136 	    /slave zone "(\S+?)" .*loaded/ and do {
   135 	    /slave zone "(\S+?)" .*loaded/ and do {
   137 		my $domain = $1;
   136 		my $domain = $1;
   138 		next if not exists $missing{$domain};	# ist noch nicht vermißt worden
   137 		next if not exists $missing{$domain};	# ist noch nicht vermißt worden
   139 
   138 
   140 		debug("Missing file for $domain is not longer missing\n");
   139 		debug("Missing file for $domain is not longer missing\n");
   141 		delete $missing{$domain};
   140 		delete $missing{$domain};
       
   141 		next;
       
   142 	    };
       
   143 
       
   144 	    /\[([\d.]+)\] not authoritative for (\S+), SOA/ and do {
       
   145 		my ($master, $domain) = ($1, $2);
       
   146 		next if exists $nomasters{$domain}->{$master};
       
   147 
       
   148 		debug "$master isn't a auth. master for $domain\n";
       
   149 		$masters{$domain}->{$master} = 1;	# sieht blöd aus, wird aber gebraucht,
       
   150 							# weil wir nur die bearbeiten, die einen
       
   151 							# master haben
       
   152 		$nomasters{$domain}->{$master} = 1;
       
   153 		next;
   142 	    };
   154 	    };
   143 	}
   155 	}
   144 
   156 
   145 	# Jetzt sind wir erstmal durch und verarbeiten alles
   157 	# Jetzt sind wir erstmal durch und verarbeiten alles
   146 	my $changed = 0;
   158 	my $changed = 0;
   147 	foreach my $domain (sort ($opt_update ? keys %masters : keys %missing)) {
   159 	foreach my $domain (sort ($opt_update ? keys %masters : keys %missing)) {
   148 	    $changed += updateFile($domain, keys %{$masters{$domain}});
   160 	    $changed += updateFile($domain, [keys %{$masters{$domain}}], [keys %{$nomasters{$domain}}]);
   149 	    delete $masters{$domain};
   161 	    delete $masters{$domain};
   150 	    delete $missing{$domain} if exists $missing{$domain};
   162 	    delete $missing{$domain} if exists $missing{$domain};
       
   163 	    delete $nomasters{$domain} if exists $nomasters{$domain};
   151 	}
   164 	}
   152 
   165 
   153 	debug "$changed changes."; 
   166 	debug "$changed changes."; 
   154 	if ($changed) {
   167 	if ($changed) {
   155 	    debug("bind reload required\n");
   168 	    debug("bind reload required\n");
   161 	    }
   174 	    }
   162 	    system qw(ndc reload);
   175 	    system qw(ndc reload);
   163 	}
   176 	}
   164 
   177 
   165 	last if !$opt_follow;
   178 	last if !$opt_follow;
   166 	debug("Sleeping for $naptime seconds\n");
   179 	syslog LOG_INFO, "Sleeping for $naptime seconds\n";
   167 	sleep $naptime;
   180 	sleep $naptime;
   168 	if ((LOGFILE->stat())[1] != $inode) {
   181 	if ((LOGFILE->stat())[1] != (stat($opt_logfile))[1]) {
   169 	    # new file to follow
   182 	    # new file to follow
   170 	    syslog(LOG_NOTICE, "Logfile changed, re-open it.\n");
   183 	    syslog(LOG_NOTICE, "Logfile changed, re-open it.\n");
   171 	    open(LOGFILE, $_ = "<$opt_logfile") 
   184 	    open(LOGFILE, $_ = "<$opt_logfile") 
   172 		or die "Can't open $_: $!\n";
   185 		or die "Can't open $_: $!\n";
   173 	    $inode = (LOGFILE->stat())[1];
       
   174 	} else {
   186 	} else {
   175 	    LOGFILE->clearerr();
   187 	    LOGFILE->clearerr();
   176 	}
   188 	}
   177     }
   189     }
   178 }
   190 }
   179 
   191 
   180 sub updateFile($@)
   192 sub updateFile($$$)
   181 {
   193 {
   182     local $_;
   194     local $_;
   183     my $domain = shift;
   195     my $domain = $_[0];
   184     my %new_masters = map { $_, 1 } @_;
   196     my @new_masters = @{$_[1]};
   185     my %old_masters = ();
   197     my @no_masters = @{$_[2]};
       
   198 
       
   199     my %masters = ();
   186     my $masters;
   200     my $masters;
       
   201 
   187     my $file = "$opt_zonesdir/$domain";
   202     my $file = "$opt_zonesdir/$domain";
       
   203 
       
   204     debug "updateFile: $domain, @new_masters, @no_masters\n";
   188 
   205 
   189     if (-f $file) {
   206     if (-f $file) {
   190 	# Das File ist also schon da, wir müssen nur mal gucken, ob die Master,
   207 	# Das File ist also schon da, wir müssen nur mal gucken, ob die Master,
   191 	# von denen wir ein NOTIFY erhalten haben, auch in unserer Datei stehen.
   208 	# von denen wir ein NOTIFY erhalten haben, auch in unserer Datei stehen.
   192 	# Vorerst entfernen wir keinen Master, wir fügen lediglich welche hinzu,
       
   193 	# wenn Sie noch nicht dabei sind.
       
   194 	#
   209 	#
   195 	open (F, $_ = "+<$file") or die "Can't open $_: $!\n";
   210 	open (F, $_ = "+<$file") or die "Can't open $_: $!\n";
   196 	flock(F, LOCK_EX); seek(F, 0, 0);
   211 	flock(F, LOCK_EX); seek(F, 0, 0);
   197 
   212 
   198 	$_ = join "", <F>;
   213 	$_ = join "", <F>;
   199 
   214 
   200 	# Liste der Master raussuchen
   215 	# Liste der Master raussuchen, darus noch die löschen, die uns
       
   216 	# die Mitarbeit verweigert haben..
   201 	/^(\s*masters\s*{\s*)(.*?);(\s*}\s*;)(\s*\/\/.*?\n)/ims;
   217 	/^(\s*masters\s*{\s*)(.*?);(\s*}\s*;)(\s*\/\/.*?\n)/ims;
   202 	%old_masters = map { $_, 1 } split/\s*;\s*/, $2;
   218 	%masters = map { $_, 1 } split/\s*;\s*/, $2;
   203 
   219 	$masters = %masters;	# für den späteren Vergleich
   204 	# Aus den neuen löschen wir die, die bereits bekannt sind
   220 
   205 	delete @new_masters{keys %old_masters};
   221 	# noch unsere neuen hinzufügen...
   206 
   222 	@masters{@new_masters} = map { 1 } @new_masters;
   207 	# Wenn nun noch welche übring sind, dann müssen wir diese
   223 
   208 	# mit eintragen.  Ansonsten haben wir fertig.
   224 	# nun die weg, die sich nicht zuständig fühlen
   209 	if (not %new_masters) {
   225 	delete @masters{@no_masters};
   210 	    debug("Uptodate file for $domain\n");
   226 
       
   227 	# Wenn sich nach alldem nichts verändert hat, haben wir fertig.
       
   228 	if ($masters eq %masters) {
       
   229 	    debug("File is up-to-date for $domain\n");
   211 	    syslog(LOG_NOTICE, "No changes made for $domain (no \"loaded\" seen, defective master?)\n")
   230 	    syslog(LOG_NOTICE, "No changes made for $domain (no \"loaded\" seen, defective master?)\n")
   212 		unless $opt_update;
   231 		unless $opt_update;
   213 	    close(F);
   232 	    close(F);
   214 	    return 0;
   233 	    return 0;
   215 	}
   234 	}
   216 
   235 
   217 	syslog(LOG_NOTICE, "Updated masters list for $domain\n");
   236 	if (not %masters) {
   218 	$masters = join ";", keys %old_masters, keys %new_masters;
   237 	    syslog LOG_NOTICE, "REMOVING $file (empty masters list)\n";
       
   238 	    close F;
       
   239 	    unlink $file;
       
   240 	    return 1;
       
   241 	}
       
   242 
       
   243 	$masters = join ";", keys %masters;
       
   244 	syslog(LOG_NOTICE, "Updated masters ($masters) list for $domain\n");
   219 	s/^(\s*masters\s*{\s*)(.*?);(\s*}\s*;)/$1$masters;$3/ims;
   245 	s/^(\s*masters\s*{\s*)(.*?);(\s*}\s*;)/$1$masters;$3/ims;
   220 
   246 
   221 	truncate(F, 0);
   247 	truncate(F, 0);
   222 	seek(F, 0, 0);
   248 	seek(F, 0, 0);
   223 	print F;
   249 	print F;
   226 	return 1;
   252 	return 1;
   227     } 
   253     } 
   228 
   254 
   229 
   255 
   230     my $date = localtime();
   256     my $date = localtime();
   231     $masters = join "; ", keys %new_masters;
   257     my %new_masters = map { $_, 1 } @new_masters;
       
   258     delete @new_masters{@no_masters};
       
   259 
       
   260     if (not %new_masters) {
       
   261 	syslog LOG_INFO, "not creating $file (empty masters list)\n";
       
   262 	return 0;
       
   263     }
       
   264 
       
   265     $masters = join "; ", @new_masters;
   232 
   266 
   233     -d $opt_zonesdir or mkpath($opt_zonesdir, 0, 0755);
   267     -d $opt_zonesdir or mkpath($opt_zonesdir, 0, 0755);
   234 
   268 
   235     syslog(LOG_NOTICE, "Creating $file for $domain");
   269     syslog(LOG_NOTICE, "Creating $file for $domain");
   236     open(OUT, $_ = ">$file") or die "Can't open $_: $!\n";
   270     open(OUT, $_ = ">$file") or die "Can't open $_: $!\n";