savepoint
authorMatthias Förste <foerste@schlittermann.de>
Fri, 27 May 2011 10:16:02 +0200
changeset 116 0e966f19a616
parent 115 743fd7ffb7c3
child 117 3b9250983996
savepoint
bin/update-serial
--- a/bin/update-serial	Thu May 26 09:41:30 2011 +0200
+++ b/bin/update-serial	Fri May 27 10:16:02 2011 +0200
@@ -34,6 +34,7 @@
 use POSIX qw(strftime);
 use if $ENV{DEBUG} => "Smart::Comments";
 use DNStools::Config qw(get_config);
+use DNS::ZoneParse;
 
 sub uniq(@);
 sub zones(@);
@@ -49,10 +50,12 @@
 sub sign($);
 sub update_serial($);
 
-sub mk_zone_conf;
+sub mk_zone_conf($$);
 sub file_entry;
 sub server_reload;
 
+sub dnssec_enabled($$);
+
 my %config;
 my %opt;
 
@@ -75,11 +78,10 @@
     ) or pod2usage;
 
     # merge the config and the defined options from commandline
-    %config = get_config("$ENV{DNSTOOLS_CONF}", "dnstools.conf",
-        "$ENV{HOME}/.dnstools.conf", "/etc/dnstools.conf", \%opt);
-
-    our $bind_dir = $config{bind_dir};
-    our $conf_dir = $config{zone_conf_dir};
+    my @configs = ( "dnstools.conf", "$ENV{HOME}/.dnstools.conf",
+        "/etc/dnstools.conf");
+    unshift @configs, $ENV{DNSTOOLS_CONF} if defined $ENV{DNSTOOLS_CONF};
+    %config = get_config(@configs, \%opt);
 
     my @candidates = @ARGV ? zones(@ARGV) : changed_zones;
     push @candidates, update_index($config{indexzone});
@@ -92,15 +94,15 @@
     push @candidates, end_rollover(@done_rollover);
 
     foreach my $zone (uniq(@candidates)) {
+#        say "XXX: candidate $zone";
         update_serial($zone);
-        sign($zone);
+        sign($zone) if dnssec_enabled($zone, "$config{master_dir}/$config{indexzone}/$config{indexzone}");
+#        say "XXX: $zone should be signed" if dnssec_enabled($zone, "$config{master_dir}/$config{indexzone}/$config{indexzone}");
     }
-    say "Need to ... file_entry, mk_zone_conf, server_reload";
-    exit;
 
-    file_entry;       # bearbeitet die file-eintraege der konfigurations-datei
-    mk_zone_conf;     # konfiguration zusammenfuegen
-    server_reload;    # server neu laden
+    file_entry;
+    mk_zone_conf($config{bind_dir}, $config{zone_conf_dir});
+    server_reload;
 
 }
 
@@ -143,10 +145,15 @@
             next;
         }
 
-        my $stamp_age = -M _;
-        my $file_age  = -M "$_/$zone";
+        my $stamp_mtime = (stat _)[8];
+        my $stamp_mtime2 = (stat "$_/.stamp")[8];
+        my $zone_file_mtime  = (stat "$_/$zone")[8] or die "Can't stat '$_/$zone': $!";
+        # TODO: do this here?
+        my $kc_file_mtime = 0;
+        $kc_file_mtime = (stat "$_/.keycounter")[8] or die "Can't stat '$_/.keycounter': $!" if -f "$_/.keycounter";
+#        say "XXX: zone: $zone | stamp_mtime: $stamp_mtime| stamp_mtime2: $stamp_mtime2 | zone_file_mtime: $zone_file_mtime | kc_file_mtime: $kc_file_mtime";
 
-        next if $stamp_age <= $file_age;             # should be only <
+        next unless $stamp_mtime < $zone_file_mtime or $stamp_mtime < $kc_file_mtime;
 
         push @r, $zone;
         say " * $zone: zone file modified";
@@ -210,6 +217,7 @@
 sub update_serial($) {
 
     my $zone = shift;
+#    say "XXX: $zone: updating serial number";
 
     my $file = "$config{master_dir}/$zone/$zone";
     my $in   = IO::File->new($file) or die "Can't open $file: $!\n";
@@ -232,10 +240,12 @@
     $serial =~ s/\s*//g;
     say " * $zone: serial incremented to $serial";
 
-    open(my $stamp, ">", dirname($file) . "/.stamp");
-    print $stamp time() . " " . localtime() . "\n";
+    my ($atime, $utime) = (time) x 2; 
+    my $s = (dirname $file) . '/.stamp';
+    utime $atime, $utime, $s or die "Can't 'utime $atime, $utime, $s': $!";
 
     say " * $zone: stamp aktualisiert";
+#    say " XXX $zone: stamp '$s' aktualisiert";
 }
 
 sub new_serial($) {
@@ -250,11 +260,10 @@
 
 }
 
-sub mk_zone_conf {
+sub mk_zone_conf($$) {
 
     # erzeugt eine named.conf-datei aus den entsprechenden vorlagen.
-    our $bind_dir;
-    our $conf_dir;
+    my ($bind_dir, $conf_dir) = @_;
 
     open(TO, ">$bind_dir/named.conf.zones")
       or die "$bind_dir/named.conf.zones: $!\n";
@@ -268,6 +277,7 @@
 }
 
 sub update_index($) {
+
     my $indexzone = shift;
 
     my @iz;
@@ -281,7 +291,7 @@
     for my $dir (glob "$config{master_dir}/*") {
         my $zone = basename($dir);
         my $info = -e ("$dir/.keycounter") ? "sec-on" : "sec-off";
-        push @iz, join "::", "\t\tIN TXT\t\t\"ZONE", $zone, $info;
+        push @iz, join "::", "\t\tIN TXT\t\t\"ZONE", $zone, $info . '"';
     }
 
     {
@@ -292,6 +302,7 @@
           or die "Can't rename "
           . $fh->filename
           . " to $config{master_dir}/$indexzone/$indexzone: $!\n";
+        $fh->unlink_on_destroy(0);
     }
 
     say "** index-zone aktualisiert";
@@ -303,50 +314,53 @@
     # prueft jede domain, die ein verzeichnis in $config{master_dir} hat, ob sie
     # dnssec nutzt.
     # passt die eintraege in $config_file falls noetig an.
-    our $conf_dir;
+    my $cd = $config{zone_conf_dir};
+    my $md = $config{master_dir};
 
-    while (glob "$config{master_dir}/*") {
-        s#($config{master_dir}/)(.*)#$2#;
-        my $zone      = $_;
-        my $zone_file = "$config{master_dir}/$zone/$zone";
-        my $conf_file = "$conf_dir/$zone";
-        my @c_content;
+    while (glob "$md/*") {
+        m#($md/)(.*)#;
+        my $z  = $2;
+        my $cf = "$cd/$z";
+        my $de = dnssec_enabled $z, "$md/$config{indexzone}/$config{indexzone}";
+        my $suf = $de ? '.signed' : '';
+        # TODO: assuming that paths in $md and in zone config snippets match somehow
+        my $zp = "$z/$z$suf";
+        my $zf = "$md/$z/$z$suf";
 
-        unless (-f "$conf_file") {
-            die "$conf_file: $! \n";
+        my ($files, $changed) = (0, 0);
+        my $czf;
+        open C, "+<$cf" or die "Cant't open '$cf': $!";
+        my @lines = <C>; # TODO: deal with race condition?
+        my ($mode, $uid, $gid, $atime, $mtime) = (stat C)[2, 4, 5, 8, 9] or die "Can't stat: $!";
+        $mode &= 07777;
+        for (@lines) {
+            next unless /^\s*file\s+"([^"]*)"\s*;\s*$/;
+            $czf = $1;
+            $files++;
+            $_ = qq(\tfile "$zf";) and $changed++ unless $czf =~ m#\Q$z/$z$suf\E$#;
         }
 
-        if (-e "$config{master_dir}/$zone/.keycounter") {
-            open(FILE, "<$conf_file") or die "$conf_file: $!\n";
-            @c_content = <FILE>;
-            close(FILE);
-            for (@c_content) {
-                if (m{(.*)($zone_file)(";)}) {
-                    print
-                      " * zonekonfiguration aktualisiert ($2 ==> $2.signed)\n";
-                    $_ = "$1$2.signed$3\n";
-                }
-            }
-            open(FILE, ">$conf_file") or die "$conf_file: $!\n";
-            print FILE @c_content;
-            close(FILE);
-        }
-        else {
-            open(FILE, "<$conf_file") or die "$conf_file: $!\n";
-            @c_content = <FILE>;
-            close(FILE);
-            for (@c_content) {
-                if (m{(.*)($zone_file)\.signed(.*)}) {
-                    print
-                      " * zonekonfiguration aktualisiert ($2.signed ==> $2)\n";
-                    $_ = "$1$2$3\n";
-                }
-            }
-            open(FILE, ">$conf_file") or die "$conf_file: $!\n";
-            print FILE @c_content;
-            close(FILE);
-        }
+        die "Multiple file statements found in '$cf' (maybe inside multiline comments)" if $files > 1;
+        return unless $changed;
+
+        # file statement in config snippet doesnt match, so we make a backup first and write a new config
+        my $cb = "$cf.bak";
+        open B, ">$cb" or die "Can't open '$cb': $!";
+        print B @lines;
+        close B;
+        chown $uid, $gid, $cb or die "Can't 'chown $uid, $gid, $cb': $!";
+        chmod $mode, $cb or die "Can't 'chmod $mode, $cb': $!";
+        utime $atime, $mtime, $cb or die "Can't 'utime $atime, $mtime, $cb': $!";
+
+        seek C, 0, 0 or die "Can't seek C, 0, 0: $!";
+        # write back @lines we modified earlier
+        print C @lines;
+        close C;
+
+        print " * zonekonfiguration aktualisiert ($czf ==> $zf)\n";
+
     }
+
 }
 
 sub server_reload {
@@ -534,6 +548,25 @@
     return @r;
 }
 
+# dnssec_enabled($zone, $path_to_indexzone_file)
+# return true if the index zone indicates that dnssec is enabled for a zone
+sub dnssec_enabled($$) {
+
+    my ($z, $if) = @_;
+    my $re = qr/^[^;]*IN\s+TXT\s+"ZONE::\Q$z\E::sec-(on|off)"/;
+    my $r;
+
+    open I, "<$if" or die "Can't open index zone file '<$if': $!";
+    while (<I>) {
+#        say "XXX: match: $_" if /$re/;
+        $r = $1 eq 'on' and last if /$re/;
+    }
+    close I;
+
+    return $r;
+
+}
+
 __END__
 
 =pod