--- 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;
}