Einige Dinge hinzugef�gt...
authorheiko
Tue, 24 Dec 2002 00:13:43 +0000
changeset 4 9665d5582b40
parent 3 2699119ec0ea
child 5 96a0d63303c6
Einige Dinge hinzugef�gt...
master_watcher
--- a/master_watcher	Mon Dec 23 11:16:58 2002 +0000
+++ b/master_watcher	Tue Dec 24 00:13:43 2002 +0000
@@ -2,20 +2,43 @@
 my $USAGE = <<'#';
 Usage: $ME [options]
        -l --logfile=s   Name of the logfile we've to read [$opt_logfile]
-       -d --dir=s       Where the named.conf's are expected [$opt_zonesdir]
-       -u --[no]update  Update the "masters"-entries [$opt_update]
-       -c --[no]create  Add newly appeared domains [$opt_create]
+       -z --zonesdir=s  Where the named.conf's are expected [$opt_zonesdir]
+       -u --[no]update  Update the "masters"-entries [$opt_update] 
        -f --[no]follow  Follow the end of the logfile [$opt_follow]
+       -d --[no]debug   extra debug output [$opt_debug]
        -h --help        This text [$opt_help]
 #
 # Es wird ein Verzeichnis geben, in diesem Verzeichnis liegt für 
 # *jede* Zone eine eigene Konfigurations-Datei.
 # Diese ganzen Konfigurationsdateien werden dann zusammengefaßt
 # und diese zusammengefaßte wird dem bind per "include" mitgeteilt.
+#
+# Wir durchsuchen den Syslog nach
+# NOTIFY von Master-Servern.
+#   o Wenn der Master nicht authorisiert ist -> ENDE GELÄNDE
+#   o Andernfalls merken wir uns diesen Master für diese Domain
+# Wenn dann mal zu lesen ist "not of our zones" und wir uns diesen
+# als einen unserer Master gemerkt haben, dann vermerken wir uns, daß
+# für diese Zone das Konfig-File fehlt.
+#
+# Sollte irgendwann mal ein "slave zone...loaded" auftauchen, ist das Konfig-File
+# inzwischen vorhanden und kein Grund zur Panik.  Wir entfernen es aus der Liste
+# der fehlenden Files.
+#
+# Sollte dieser Text ausbleiben, müssen wir ein File anlegen (wahrscheinlich).
+# -> Sollte trotzdem schon eins da sein, dann konnten wir aus irgendwelchen
+# Gründen nix laden (deshalb fehlt ja der "...loaded"-Text).  Das kann z.B. sein, weil ein 
+# Master mal keinen Bock hat oder nicht authoritativ ist.
+#
+# Etwas anders sieht's im update-Modus aus.  Hier wird *jeder* Master für jede Domain gemerkt und
+# geprüft, ob der in dem entsprechenden Konfig-File enthalten ist.
+#
+# Im täglichen Einsatz sollte es ohne Update-Modus ganz gut funktionieren.
 
-#use strict;
+use strict;
 use File::Basename;
 use IO::Handle;
+use Fcntl qw/:flock/;
 use File::Path;
 use Getopt::Long;
 use Unix::Syslog qw/:macros :subs/;
@@ -24,13 +47,12 @@
 
 
 my %auth = (
-    "212.172.233.34" => "pu.schlittermann.de",
-    "212.80.235.130" => "pu2.schlittermann.de",
+    "212.80.235.130" => "pu.schlittermann.de",
+    "212.80.235.132" => "mango.compot.com",
     "145.253.160.50" => "bastion.actech.de",
-    "212.172.233.146" => "ns.flaemingnet.de",
-    "212.172.127.34" => "mango.compot.com",
     "62.144.175.34" => "ns.add-on.de",	    
     "195.145.19.34" => "ns.datom.de",
+    # "194.162.141.17" => "dalx1.nacamar.de",
 );
 
 $SIG{__DIE__} = sub { syslog(LOG_ERR, $_[0]); exit -1; };
@@ -39,17 +61,18 @@
 my %seen;
 
 my $opt_help = 0;
-my $opt_logfile = "./syslog";
-my $opt_zonesdir = "./zones";
+my $opt_logfile = "/var/log/syslog";
+my $opt_zonesdir = "/etc/bind/zones.d";
 my $opt_follow = 0;
-my $opt_create = 1;
-my $opt_update = 1;
+my $opt_update = 0;
+my $opt_debug = 0;
 
-my $naptime = 1;
+my $naptime = 60;
+my $inode = 0;
 
 
 sub updateFile($@);
-
+sub debug($;@) { syslog(LOG_DEBUG, "DEBUG " . shift @_, @_) if $opt_debug; }
 
 MAIN: {
 
@@ -62,8 +85,8 @@
 	"logfile=s" => \$opt_logfile,
 	"follow!" => \$opt_follow,
 	"update!" => \$opt_update,
-	"create!" => \$opt_create,
-	"dir=s" => \$opt_zonesdir)
+	"debug!" => \$opt_debug,
+	"zonesdir=s" => \$opt_zonesdir)
     or die "$ME: Bad Usage\n";
 
     if ($opt_help) {
@@ -72,6 +95,7 @@
     }
 
     open (LOGFILE, $_ = "<$opt_logfile") or die "Can't open $_: $!\n";
+    $inode = (LOGFILE->stat)[1];
     for (;;) {
 	my (%masters, my %missing);
 	while (<LOGFILE>) {
@@ -84,38 +108,72 @@
 		    warn "notify for $domain from unauthorized ip $ip\n";
 		    next;
 		};
+		# also in die Liste (hier als Key eines Hashes wegen der möglichen
+		# Dopplungen) der Master für diese Domain aufnehmen.
+		debug("Master für $domain: $ip\n");
 		$masters{$domain}->{$ip} = 1;
 		next;
 	    };
 
+	    # Das müssen wir doch garnicht machen, da wir ja sowieso nach 
+	    # dem Master-Files gucken...
 	    # NOTIFY for vergessene 
 	    /NOTIFY for "(\S+?)".*not one of our zones/ and do {
-		if (not exists $masters{$1}) {
-		    warn "skipping $1 (not authorized)\n";
+		my $domain = $1;
+		if (not exists $masters{$domain}) {
+		    debug "skipping $domain (not authorized)\n";
 		    next;
 		}
-		$missing{$1} = 1;
+		# Also in die Liste der vergessenen Domains (für die wir garkeine
+		# Konfigurations-Datei haben)
+		next if exists $missing{$domain};	# schon erledigt
+
+		debug("Missing file for $domain\n");
+		$missing{$domain} = 1;
+	    };
+
+	    # Wenn wir ein "... loaded" finden, dann fehlt das File nicht gänzlich!
+	    /slave zone "(\S+?)" .*loaded/ and do {
+		my $domain = $1;
+		next if not exists $missing{$domain};	# ist noch nicht vermißt worden
+
+		debug("Missing file for $domain is not longer missing\n");
+		delete $missing{$domain};
 	    };
 	}
 
 	# Jetzt sind wir erstmal durch und verarbeiten alles
-
-	#foreach my $domain (sort keys %missing) {
-	    #updateFile($domain, keys %{$masters{$domain}});
-	    #delete $masters{$domain};
-	    #delete $missing{$domain};
-	#}
-
-	foreach my $domain (sort keys %masters) {
-	    updateFile($domain, keys %{$masters{$domain}});
+	my $changed = 0;
+	foreach my $domain (sort ($opt_update ? keys %masters : keys %missing)) {
+	    $changed += updateFile($domain, keys %{$masters{$domain}});
 	    delete $masters{$domain};
 	    delete $missing{$domain} if exists $missing{$domain};
 	}
 
+	debug "$changed changes."; 
+	if ($changed) {
+	    debug("bind reload required\n");
+	    open(ALL, $_ = ">/etc/bind/zones.all") or die "Can't open $_: $!\n";
+	    foreach (</etc/bind/zones.d/*>) {
+		open(IN, $_) or die "Can't open $_: $!\n";
+		print ALL <IN>;
+		close(IN);
+	    }
+	    system qw(ndc reload);
+	}
+
 	last if !$opt_follow;
+	debug("Sleeping for $naptime seconds\n");
 	sleep $naptime;
-	# seek(LOGFILE, 0, 1);
-	LOGFILE->clearerr();
+	if ((LOGFILE->stat())[1] != $inode) {
+	    # new file to follow
+	    syslog(LOG_NOTICE, "Logfile changed, re-open it.\n");
+	    open(LOGFILE, $_ = "<$opt_logfile") 
+		or die "Can't open $_: $!\n";
+	    $inode = (LOGFILE->stat())[1];
+	} else {
+	    LOGFILE->clearerr();
+	}
     }
 }
 
@@ -123,23 +181,41 @@
 {
     local $_;
     my $domain = shift;
-    my %masters = map { $_, 1 } @_;
+    my %new_masters = map { $_, 1 } @_;
+    my %old_masters = ();
+    my $masters;
     my $file = "$opt_zonesdir/$domain";
 
     if (-f $file) {
-	return if not $opt_update;
-	syslog(LOG_NOTICE, "Updating $file for $domain");
+	# Das File ist also schon da, wir müssen nur mal gucken, ob die Master,
+	# von denen wir ein NOTIFY erhalten haben, auch in unserer Datei stehen.
+	# Vorerst entfernen wir keinen Master, wir fügen lediglich welche hinzu,
+	# wenn Sie noch nicht dabei sind.
+	#
 	open (F, $_ = "+<$file") or die "Can't open $_: $!\n";
-	# ein etwas anderer Versuch - noch nicht fertig
+	flock(F, LOCK_EX); seek(F, 0, 0);
+
 	$_ = join "", <F>;
 
 	# Liste der Master raussuchen
-	/^(\s*masters\s*{\s*)(.*?);(\s*}\s*;)/ims;
+	/^(\s*masters\s*{\s*)(.*?);(\s*}\s*;)(\s*\/\/.*?\n)/ims;
+	%old_masters = map { $_, 1 } split/\s*;\s*/, $2;
+
+	# Aus den neuen löschen wir die, die bereits bekannt sind
+	delete @new_masters{keys %old_masters};
 
-	foreach (split /\s*;\s*/, $2) {
-	    $masters{$_} = 1;
+	# Wenn nun noch welche übring sind, dann müssen wir diese
+	# mit eintragen.  Ansonsten haben wir fertig.
+	if (not %new_masters) {
+	    debug("Uptodate file for $domain\n");
+	    syslog(LOG_NOTICE, "No changes made for $domain (no "loaded" seen, defective master?)\n")
+		unless $opt_update;
+	    close(F);
+	    return 0;
 	}
-	$masters = join "; ", keys %masters;
+
+	syslog(LOG_NOTICE, "Updated masters list for $domain\n");
+	$masters = join ";", keys %old_masters, keys %new_masters;
 	s/^(\s*masters\s*{\s*)(.*?);(\s*}\s*;)/$1$masters;$3/ims;
 
 	truncate(F, 0);
@@ -147,13 +223,12 @@
 	print F;
 	close F;
 
-	return;
+	return 1;
     } 
 
-    return if not $opt_create;
 
     my $date = localtime();
-    my $masters = join "; ", keys %masters;
+    $masters = join "; ", keys %new_masters;
 
     -d $opt_zonesdir or mkpath($opt_zonesdir, 0, 0755);
 
@@ -174,7 +249,7 @@
 _EOF_
     close OUT;
 
-    return;
+    return 1;
 }