chiark / gitweb /
make man1dir
[chiark-utils.git] / scripts / named-conf
index 39bee7f9ae35c2e949077549ae752c91845b5221..0a91daf7e18db286bec1a510cbccbbf26dda5c11 100755 (executable)
@@ -1,4 +1,19 @@
 #!/usr/bin/perl -w
 #!/usr/bin/perl -w
+# This is chiark-named-conf, which is Copyright 2002 Ian Jackson.
+#
+# chiark-named-conf and its manpage are free software; you can
+# redistribute it and/or modify them under the terms of the GNU
+# General Public License as published by the Free Software Foundation;
+# either version 2, or (at your option) any later version.
+# 
+# chiark-named-conf and its manpage are distributed in the hope that
+# it will be useful, but WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE.  See the GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
 use strict;
 use IO::File;
 
 use strict;
 use IO::File;
@@ -6,7 +21,51 @@ use Data::Dumper;
 use POSIX;
 use Fcntl qw(:DEFAULT :flock);
 
 use POSIX;
 use Fcntl qw(:DEFAULT :flock);
 
-use vars qw($quis
+# bastard Perl wants me to do this now !
+sub loarg();
+sub soarg();
+sub usageerr ($);
+sub cfg_fail ($);
+sub read_config ($);
+sub qualify ($);
+sub bad_modifiers ($);
+sub zone_conf ($$$$$@);
+sub set_output($);
+sub progress ($$);
+sub verbose ($);
+sub process_zones (@);
+sub zone_warning ($$);
+sub zone_warnmore ($);
+sub zone_check_full ();
+sub zone_reset();
+sub zone_investigate();
+sub zone_check_nsrrset ($$$$);
+sub zone_ns_name ($$);
+sub zone_server_queue ($$$$$);
+sub zone_server_addr ($$$$$);
+sub zone_check_soa ($$$$);
+sub zone_consistency();
+sub zone_servers_ok ();
+sub zone_consistency_set ($%);
+sub zone_check_local ();
+sub zone_servers_simplefind ();
+sub zone_server_simple ($$$);
+sub zone_style ($$);
+sub mail_zone_before ();
+sub mail_zone_after ();
+sub pmail ($);
+sub ptime ($);
+sub mail_zone_mail ();
+sub zone_output ();
+sub output_files ();
+sub debug_dump ($);
+sub debug_trace ($);
+sub has_suffix_of ($$);
+sub lookup ($$$$);
+sub dig (&$$$);
+sub domain_canon ($$);
+
+use vars qw($quis $stdout_fh $stderr_fh
            $mode $doall $domail
            $etcfile $where
            $debug $needglue $localonly $repeat $verbosity
            $mode $doall $domail
            $etcfile $where
            $debug $needglue $localonly $repeat $verbosity
@@ -27,10 +86,14 @@ $verbosity= 2;
 $admin=''; $mail_state_dir=''; $mail_max_warnfreq= 50;
 $repeat= 0;
 $domail= '';
 $admin=''; $mail_state_dir=''; $mail_max_warnfreq= 50;
 $repeat= 0;
 $domail= '';
-$progress_fh= 'STDOUT';
-$warn_fh= 'STDERR';
 $modifiers= '';
 $modifiers= '';
+$group2modcmd{'foreign'}= '*$!@?';
+$group2used{'foreign'}= 1;
 
 
+($progress_fh= $stdout_fh= new_from_fd IO::Handle(1,'w') and
+ $warn_fh= $stderr_fh = new_from_fd IO::Handle(2,'w'))
+    or die "$quis: setup standard filehandles: $!\n";
+    
 use vars qw($dig_owner $dig_type $dig_rdata);
 
 while (@ARGV && $ARGV[0] =~ m/^\-/) {
 use vars qw($dig_owner $dig_type $dig_rdata);
 
 while (@ARGV && $ARGV[0] =~ m/^\-/) {
@@ -39,7 +102,7 @@ while (@ARGV && $ARGV[0] =~ m/^\-/) {
        last if m/^$/;
        if (m/^(yes|no|force)$/) { m/^./; $mode= $&; $domail=''; }
        elsif (m/^nothing$/) { $mode= 'x'; $domail=''; }
        last if m/^$/;
        if (m/^(yes|no|force)$/) { m/^./; $mode= $&; $domail=''; }
        elsif (m/^nothing$/) { $mode= 'x'; $domail=''; }
-       elsif (m/^mail\-(first|middle|final|final-test)/) {
+       elsif (m/^mail\-(first|middle|final|final\-test)$/) {
            $mode='n';
            $domail=$1;
        }
            $mode='n';
            $domail=$1;
        }
@@ -81,7 +144,7 @@ usageerr("-q may be specified at most twice") if $verbosity<0;
 usageerr("-v may be specified at most once") if $verbosity>3;
 usageerr("-D may be specified at most twice") if $debug>2;
 usageerr("must specify either -f|-y|-n or zones (and not both)")
 usageerr("-v may be specified at most once") if $verbosity>3;
 usageerr("-D may be specified at most twice") if $debug>2;
 usageerr("must specify either -f|-y|-n or zones (and not both)")
-    if !!$mode == !!@ARGV;
+    if !!$mode == !!@ARGV && !$domail;
 
 sub usageerr ($) {
     die <<END;
 
 sub usageerr ($) {
     die <<END;
@@ -141,10 +204,12 @@ $install= $mode =~ m/^[yf]/;
 
 read_config($etcfile);
 debug_dump('@zone_cfg_list %zone_cfg');
 
 read_config($etcfile);
 debug_dump('@zone_cfg_list %zone_cfg');
-process_zones($mode ? @zone_cfg_list : @ARGV);
+process_zones(!@ARGV ? @zone_cfg_list : @ARGV);
 debug_dump('%output_contents');
 output_files() if $install;
 
 debug_dump('%output_contents');
 output_files() if $install;
 
+$stdout_fh->close or die "$quis: write messages to stdout: $!\n";
+$stderr_fh->close or die "$quis: write messages to stderr: $!\n";
 exit 0;
 
 #-------------------- configuration reading
 exit 0;
 
 #-------------------- configuration reading
@@ -270,7 +335,7 @@ sub bad_modifiers ($) {
     local ($_) = @_;
     if (!eval {
        die "bad modifier $&" if m/[^!*\$\@~?]/;
     local ($_) = @_;
     if (!eval {
        die "bad modifier $&" if m/[^!*\$\@~?]/;
-       die "repeated modifier $1" if m/(.).*$1/;
+       die "repeated modifier $1" if m/(.).*\1/;
        1;
     }) {
        $@ =~ s/\n//;
        1;
     }) {
        $@ =~ s/\n//;
@@ -314,7 +379,7 @@ sub zone_conf ($$$$$@) {
        $zone_cfg{$zone}{$sfx}= $aref;
     }
     foreach $sfx (qw(self_soa self_ns)) {
        $zone_cfg{$zone}{$sfx}= $aref;
     }
     foreach $sfx (qw(self_soa self_ns)) {
-       $zone_cfg{$zone}{$sfx} =~ s/\*$/$zone/;
+       map { s/\*$/$zone/ } @{ $zone_cfg{$zone}{$sfx} };
     }
     $zone_cfg{$zone}{'output'}= $output;
     push @zone_cfg_list, $zone;
     }
     $zone_cfg{$zone}{'output'}= $output;
     push @zone_cfg_list, $zone;
@@ -346,11 +411,12 @@ sub process_zones (@) {
     foreach $zone (@zones) {
        $cfg= $zone_cfg{$zone} || {
            'style_p' => 'foreign',
     foreach $zone (@zones) {
        $cfg= $zone_cfg{$zone} || {
            'style_p' => 'foreign',
-           's' => 'f',
+           's' => "f $group2modcmd{'foreign'}",
            'servers' => [ ],
            };
 
            'servers' => [ ],
            };
 
-       mail_zone_before() if $domail;
+       mail_zone_before() or next
+           if $domail;
        zone_reset();
        progress(1, sprintf "%-20s %s", $zone, $$cfg{'style_p'});
        if ($check && ($doall || !zone_style('?',0))) {
        zone_reset();
        progress(1, sprintf "%-20s %s", $zone, $$cfg{'style_p'});
        if ($check && ($doall || !zone_style('?',0))) {
@@ -700,14 +766,14 @@ sub mail_zone_before () {
 
     for (;;) {
        $m_lock= new IO::File "${m_base}_lock", O_RDWR|O_CREAT, 0600
 
     for (;;) {
        $m_lock= new IO::File "${m_base}_lock", O_RDWR|O_CREAT, 0600
-           or die "$quis: create lockfile ${m_base}_lock";
+           or die "$quis: create lockfile ${m_base}_lock: $!\n";
        if (!flock($m_lock, LOCK_EX|LOCK_NB)) {
            <$m_lock> =~ m/^\d+ /;
            die "$quis: $zone: concurrrency? - flock $&$!\n";
            $m_lock->close;
        if (!flock($m_lock, LOCK_EX|LOCK_NB)) {
            <$m_lock> =~ m/^\d+ /;
            die "$quis: $zone: concurrrency? - flock $&$!\n";
            $m_lock->close;
-           next;
+           return 0;
        }
        }
-       (@s1= $m_lock->fstat) or die "$quis: fstat ${m_base}_lock: $!\n";
+       (@s1= $m_lock->stat) or die "$quis: fstat ${m_base}_lock: $!\n";
        (@s2= stat "${m_base}_lock") or
            die "$quis: stat ${m_base}_lock: $!\n";
        last if ($s1[0] eq $s2[0] && $s1[1] eq $s2[1]);
        (@s2= stat "${m_base}_lock") or
            die "$quis: stat ${m_base}_lock: $!\n";
        last if ($s1[0] eq $s2[0] && $s1[1] eq $s2[1]);
@@ -726,17 +792,17 @@ sub mail_zone_before () {
            if ($2 eq ':' && $3 eq ':') {
                warn "$quis: $zone: mid/last run, but last".
                    "run already done, ignoring zone\n";
            if ($2 eq ':' && $3 eq ':') {
                warn "$quis: $zone: mid/last run, but last".
                    "run already done, ignoring zone\n";
-               next;
+               return 0;
            }
            @m_ok= split /\:/, $2;
            @m_fail= split /\:/, $3;
        }
     } elsif ($! != &ENOENT) {
        die "$quis: open ${m_base}_info: $!\n";
            }
            @m_ok= split /\:/, $2;
            @m_fail= split /\:/, $3;
        }
     } elsif ($! != &ENOENT) {
        die "$quis: open ${m_base}_info: $!\n";
-    } elsif ($domail eq 'first') {
+    } elsif ($domail ne 'first') {
        warn "$quis: $zone: first run, but not --mail-first,".
            " ignoring zone\n";
        warn "$quis: $zone: first run, but not --mail-first,".
            " ignoring zone\n";
-       next;
+       return 0;
     }
     if ($domail eq 'first') {
        remove "${m_base}_history" or $!==&ENOENT
     }
     if ($domail eq 'first') {
        remove "${m_base}_history" or $!==&ENOENT
@@ -749,6 +815,7 @@ sub mail_zone_before () {
 
     $m_time= time;
     progress(-1, "\n".('-'x70)."\n".ptime($m_time)."\n");
 
     $m_time= time;
     progress(-1, "\n".('-'x70)."\n".ptime($m_time)."\n");
+    return 1;
 }
 
 sub mail_zone_after () {
 }
 
 sub mail_zone_after () {
@@ -761,18 +828,25 @@ sub mail_zone_after () {
        progress(-1,"everything is fine");
     }
     close $progress_fh or die "$quis: close ${m_base}_history: $!\n";
        progress(-1,"everything is fine");
     }
     close $progress_fh or die "$quis: close ${m_base}_history: $!\n";
-    $progress_fh= $warn_fh= 'STDERR';
+    $progress_fh= $warn_fh= $stderr_fh;
 
     if ($domail =~ m/^final/) {
        if (100*@m_fail <= $$cfg{'mailmwarn'}*(@m_fail + @m_ok)) {
 
     if ($domail =~ m/^final/) {
        if (100*@m_fail <= $$cfg{'mailmwarn'}*(@m_fail + @m_ok)) {
-           printf " %-40s ok\n" or die "$quis: mail ok report: $!\n";
+           printf " %-40s ok\n", $zone or die "$quis: mail ok report: $!\n";
        } elsif (zone_style('@',0)) {
        } elsif (zone_style('@',0)) {
-           printf " %-40s mail suppressed\n"
+           printf " %-40s mail suppressed\n", $zone
                or die "$quis: mail suppress report: $!\n";
        } else {
            mail_zone_mail();
        }
                or die "$quis: mail suppress report: $!\n";
        } else {
            mail_zone_mail();
        }
-    }      
+    } else {
+       printf " %-40s %d warns. OK %s Fail %s\n",
+           $zone,
+           defined $zone_warnings{$zone} ? $zone_warnings{$zone} : 0,
+           join(',', map { $_ - $m_time } @m_ok),
+           join(',', map { $_ - $m_time } @m_fail)
+               or die "$quis: checking progress report: $!\n";
+    }
 
     @m_fail= @m_ok= ('','')
        if $domail =~ m/^final/;
 
     @m_fail= @m_ok= ('','')
        if $domail =~ m/^final/;
@@ -797,15 +871,15 @@ sub ptime ($) {
 }
 
 sub mail_zone_mail () {
 }
 
 sub mail_zone_mail () {
-    my ($log, $zone_to, $zterr, $c, $r, $t);
+    my ($log, $zone_to, $zterr, $c, $r, $t, @soas);
     $m_m= new IO::File "${m_base}_mail", 'w', 0666
        or die "$quis: create ${m_base}_mail: $!\n";
     $zone_to=''; $zterr='';
     $m_m= new IO::File "${m_base}_mail", 'w', 0666
        or die "$quis: create ${m_base}_mail: $!\n";
     $zone_to=''; $zterr='';
-    if (!zone_style("\$",$$cfg{'s'} =~ m/u/)) {
+    if (!zone_style("\$", $$cfg{'s'} !~ m/[ps]/)) {
        eval {
        eval {
-           $_= lookup($zone,'soa','0','problem-addr');
-           m/\n\n/ and die "multiple soas\n";
-           m/^\S+ (\S.*\@\S+) [0-9 ]+$/ or
+           ($r,@soas)= lookup($zone,'soa','0','problem-addr');
+           @soas==1 or die "multiple soas\n";
+           $soas[0] =~ m/^\S+ (\S.*\@\S+) [0-9 ]+$/ or
                die "bad soa \`$_'\n";
            $zone_to= $1;
        };
                die "bad soa \`$_'\n";
            $zone_to= $1;
        };
@@ -813,7 +887,7 @@ sub mail_zone_mail () {
        $zterr =~ s/\n$//;
     }
     pmail <<END
        $zterr =~ s/\n$//;
     }
     pmail <<END
-From: zone check system <$$cfg{'admin'}>
+From: zone checker <$$cfg{'admin'}>
 Subject: $zone - configuration problems report
 END
 ;
 Subject: $zone - configuration problems report
 END
 ;
@@ -842,7 +916,7 @@ if length $zterr;
 The zone has had configuration errors or persistent operational
 problems during recent checks.  See the logs below for details.
 
 The zone has had configuration errors or persistent operational
 problems during recent checks.  See the logs below for details.
 
-Check history for $zone:
+Recent check history for $zone:
 END
 ;
     if ($m_lastok ne '-') {
 END
 ;
     if ($m_lastok ne '-') {
@@ -851,7 +925,7 @@ END
        pmail(" No record of this zone ever being fine.\n");
     }
     for $t (@m_fail) {
        pmail(" No record of this zone ever being fine.\n");
     }
     for $t (@m_fail) {
-       pmail(" Errors/warnings at: ".ptime($t)."\n");
+       pmail(" Zone had problems at: ".ptime($t)."\n");
     }
     for $t (@m_ok) {
        pmail(" Everything in order at: ".ptime($t)."\n");
     }
     for $t (@m_ok) {
        pmail(" Everything in order at: ".ptime($t)."\n");
@@ -860,18 +934,20 @@ END
     $log= new IO::File "${m_base}_history",'r'
        or die "$quis: reopen ${m_base}_history: $!";
     undef $/;
     $log= new IO::File "${m_base}_history",'r'
        or die "$quis: reopen ${m_base}_history: $!";
     undef $/;
-    pmail($log);
+    pmail($log->getline);
     $/= "\n";
     $/= "\n";
-    $log->error && $log->close
-       or die "$quis: reread or close ${m_base}_log: $!";
+    (!$log->error and $log->close)
+       or die "$quis: reread or close ${m_base}_log: $!\n";
+    $log->eof or die;
     $m_m->close or die "$quis: close ${m_base}_mail: $!\n";
     $m_m= new IO::File "${m_base}_mail"
        or die "$quis: reopen ${m_base}_mail: $!";
 
     defined($c= fork) or die "$quis: fork for mail: $!\n";
     if (!$c) {
     $m_m->close or die "$quis: close ${m_base}_mail: $!\n";
     $m_m= new IO::File "${m_base}_mail"
        or die "$quis: reopen ${m_base}_mail: $!";
 
     defined($c= fork) or die "$quis: fork for mail: $!\n";
     if (!$c) {
-       open STDIN, "<& ${m_m}" or die "$quis - sendmail: dup for stdin: $!\n";
-       exec (qw(/usr/sbin/sendmail -odq -oee -oi),
+       defined dup2($m_m->fileno, 0)
+           or die "$quis - sendmail: dup for stdin: $!\n";
+       exec (qw(/usr/sbin/sendmail -odb -oee -oi),
              ($domail eq 'final' ? '-t' : $$cfg{'admin'}));
        die "$quis - sendmail: exec: $!\n";
     }
              ($domail eq 'final' ? '-t' : $$cfg{'admin'}));
        die "$quis - sendmail: exec: $!\n";
     }
@@ -880,8 +956,9 @@ END
     $r == $c or die "$quis: waitpid sendmail ($c): $r $!";
     $? and warn "$quis: sendmail failed: $?\n";
 
     $r == $c or die "$quis: waitpid sendmail ($c): $r $!";
     $? and warn "$quis: sendmail failed: $?\n";
 
-    printf " %-40s %s\n", $zone, $zone_to or
-       die "$quis: write mailing report: $!\n";
+    printf " %-40s %s\n", $zone,
+        length $zone_to ? $zone_to : 'reporting to admin'
+           or die "$quis: write mailing report: $!\n";
 }
 
 #-------------------- outputting
 }
 
 #-------------------- outputting