--- a/master_watcher Mon Jan 27 15:42:03 2003 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,349 +0,0 @@
-#! /usr/bin/perl -w
-# $Id$
-my $USAGE = <<'#';
-Usage: $ME [options]
- -l --logfile=s Name of the logfile we've to read [$opt_logfile]
- -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]
- --daemon go into background [$opt_daemon]
- -p --pidfile=s file to store the pid [$opt_pidfile]
- -v --version print version [$opt_version]
-#
-# 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 File::Basename;
-use IO::Handle;
-use Fcntl qw/:flock/;
-use File::Path;
-use Getopt::Long;
-use Unix::Syslog qw/:macros :subs/;
-
-my $ME = basename $0;
-
-
-my %auth = (
- "212.80.235.130" => "pu.schlittermann.de",
- "212.80.235.132" => "mango.compot.com",
- "145.253.160.50" => "bastion.actech.de",
- "62.144.175.34" => "ns.add-on.de",
- "195.145.19.34" => "ns.datom.de",
- "62.157.194.1" => "ns.mueritzcomp.de",
- "212.80.235.137" => "ns.flaemingnet.de omni.flaemingnet.de",
- "212.80.235.152" => "www.proton24.de",
- # "194.162.141.17" => "dalx1.nacamar.de",
-);
-
-$SIG{__DIE__} = sub { syslog(LOG_ERR, $_[0]); exit -1; };
-$SIG{__WARN__} = sub { syslog(LOG_WARNING, $_[0]); };
-$SIG{TERM} = $SIG{INT} = sub { exit 0; };
-
-my %seen;
-
-my $opt_help = 0;
-my $opt_pidfile = "/var/run/$ME.pid";
-my $opt_logfile = "/var/log/syslog";
-my $opt_zonesdir = "/etc/bind/zones.d";
-my $opt_follow = 0;
-my $opt_update = 0;
-my $opt_debug = 0;
-my $opt_daemon = 1;
-my $opt_version = 0;
-
-my $naptime = 60;
-
-
-sub updateFile($$$);
-sub debug($;@) { syslog(LOG_DEBUG, "DEBUG " . shift @_, @_) if $opt_debug; }
-
-END {
- open(PID, $opt_pidfile);
- my $pid = <PID>;
- close(PID);
- unlink $opt_pidfile if $$ == $pid;
-}
-
-MAIN: {
-
-
- openlog($ME, LOG_PID | LOG_PERROR, LOG_DAEMON);
- syslog(LOG_NOTICE, "starting");
-
- GetOptions(
- "help" => \$opt_help,
- "logfile=s" => \$opt_logfile,
- "follow!" => \$opt_follow,
- "update!" => \$opt_update,
- "debug!" => \$opt_debug,
- "daemon!" => \$opt_daemon,
- "pidfile=s" => \$opt_pidfile,
- "version!" => \$opt_version,
- "zonesdir=s" => \$opt_zonesdir)
- or die "$ME: Bad Usage\n";
-
- if ($opt_help) {
- print eval "\"$USAGE\"";
- exit 0;
- }
-
- if ($opt_version) {
- print "$ME Version: ", '$Id$', "\n";
- exit 0;
- }
-
-
- # Create the PID-File
- {
- open(PID, $_ = ">$opt_pidfile.$$") or die "Can't open $opt_pidfile: $!\n";
- print PID "$$\n";
- close(PID);
-
- if (!link($_ = "$opt_pidfile.$$", $opt_pidfile)) {
- unlink "$opt_pidfile.$$";
- die "There's another $ME running. Bad. Stop.";
- }
- unlink "$opt_pidfile.$$";
- }
-
- if ($opt_daemon) {
- my $pid = fork();
-
- if ($pid < 0) {
- die "Can't fork: $!\n";
- }
-
- if ($pid) {
- open(PID, $_ = ">$opt_pidfile") or die "Can't open $_: $!\n";
- print PID "$pid\n";
- close(PID);
- exit 0;
- }
-
- close(STDIN); close(STDOUT); close(STDERR);
- }
-
-
- open (LOGFILE, $_ = "<$opt_logfile") or die "Can't open $_: $!\n";
-
- for (;;) {
- my (%masters, %missing, %nomasters);
- while (<LOGFILE>) {
-
- my ($domain, $ip);
-
- # NOTIFY-Zeilen
- ($domain, $ip) = /NOTIFY.*?\((\S+?),.*?\[([\d.]+)\]/ and do {
- if (not exists $auth{$ip}) {
- 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 {
- my $domain = $1;
- if (not exists $masters{$domain}) {
- debug "skipping $domain (not authorized)\n";
- next;
- }
- # 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;
- next;
- };
-
- # 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};
- next;
- };
-
- /\[([\d.]+)\] not authoritative for (\S+), SOA/ and do {
- my ($master, $domain) = ($1, $2);
- next if exists $nomasters{$domain}->{$master};
-
- debug "$master isn't a auth. master for $domain\n";
- $masters{$domain}->{$master} = 1; # sieht blöd aus, wird aber gebraucht,
- # weil wir nur die bearbeiten, die einen
- # master haben
- $nomasters{$domain}->{$master} = 1;
- next;
- };
- }
-
- # Jetzt sind wir erstmal durch und verarbeiten alles
- my $changed = 0;
- foreach my $domain (sort ($opt_update ? keys %masters : keys %missing)) {
- $changed += updateFile($domain, [keys %{$masters{$domain}}], [keys %{$nomasters{$domain}}]);
- delete $masters{$domain};
- delete $missing{$domain} if exists $missing{$domain};
- delete $nomasters{$domain} if exists $nomasters{$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;
- syslog LOG_INFO, "Sleeping for $naptime seconds\n";
- sleep $naptime;
- if ((LOGFILE->stat())[1] != (stat($opt_logfile))[1]) {
- # new file to follow
- syslog(LOG_NOTICE, "Logfile changed, re-open it.\n");
- open(LOGFILE, $_ = "<$opt_logfile")
- or die "Can't open $_: $!\n";
- } else {
- LOGFILE->clearerr();
- }
- }
-}
-
-sub updateFile($$$)
-{
- local $_;
- my $domain = $_[0];
- my @new_masters = @{$_[1]};
- my @no_masters = @{$_[2]};
-
- my %masters = ();
- my $masters;
-
- my $file = "$opt_zonesdir/$domain";
-
- debug "updateFile: $domain, @new_masters, @no_masters\n";
-
- if (-f $file) {
- # 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.
- #
- open (F, $_ = "+<$file") or die "Can't open $_: $!\n";
- flock(F, LOCK_EX); seek(F, 0, 0);
-
- $_ = join "", <F>;
-
- # Liste der Master raussuchen, darus noch die löschen, die uns
- # die Mitarbeit verweigert haben..
- /^(\s*masters\s*{\s*)(.*?);(\s*}\s*;)(\s*\/\/.*?\n)/ims;
- %masters = map { $_, 1 } split/\s*;\s*/, $2;
- $masters = %masters; # für den späteren Vergleich
-
- # noch unsere neuen hinzufügen...
- @masters{@new_masters} = map { 1 } @new_masters;
-
- # nun die weg, die sich nicht zuständig fühlen
- delete @masters{@no_masters};
-
- # Wenn sich nach alldem nichts verändert hat, haben wir fertig.
- if ($masters eq %masters) {
- debug("File is up-to-date for $domain\n");
- syslog(LOG_NOTICE, "No changes made for $domain (no \"loaded\" seen, defective master?)\n")
- unless $opt_update;
- close(F);
- return 0;
- }
-
- if (not %masters) {
- syslog LOG_NOTICE, "REMOVING $file (empty masters list)\n";
- close F;
- unlink $file;
- return 1;
- }
-
- $masters = join ";", keys %masters;
- syslog(LOG_NOTICE, "Updated masters ($masters) list for $domain\n");
- s/^(\s*masters\s*{\s*)(.*?);(\s*}\s*;)/$1$masters;$3/ims;
-
- truncate(F, 0);
- seek(F, 0, 0);
- print F;
- close F;
-
- return 1;
- }
-
-
- my $date = localtime();
- my %new_masters = map { $_, 1 } @new_masters;
- delete @new_masters{@no_masters};
-
- if (not %new_masters) {
- syslog LOG_INFO, "not creating $file (empty masters list)\n";
- return 0;
- }
-
- $masters = join "; ", @new_masters;
-
- -d $opt_zonesdir or mkpath($opt_zonesdir, 0, 0755);
-
- syslog(LOG_NOTICE, "Creating $file for $domain");
- open(OUT, $_ = ">$file") or die "Can't open $_: $!\n";
-
- print OUT <<_EOF_;
-// Autogenerated by $ME: $date
-zone "$domain" {
- type slave;
- masters { $masters; }; // $date
- file "/etc/bind/slave/$domain";
- allow-query { any; };
- allow-transfer { none; };
- allow-update { none; };
-};
-
-_EOF_
- close OUT;
-
- return 1;
-}
-
-
-# vim:sts=4 sw=4 aw ai sm: