diff -r 2699119ec0ea -r 9665d5582b40 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 () { @@ -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 () { + open(IN, $_) or die "Can't open $_: $!\n"; + print ALL ; + 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 "", ; # 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; }