commit 0721217080948bafa906b09b505e6caa761782c8 from: mischa date: Mon Jul 18 16:21:15 2022 UTC Initial commit commit - /dev/null commit + 0721217080948bafa906b09b505e6caa761782c8 blob - /dev/null blob + 19f5800850a55f437d9a718aad7228d18a3fe17e (mode 755) --- /dev/null +++ rvi @@ -0,0 +1,356 @@ +#!/usr/bin/perl -w +# -*- Perl -*- +# +# This is in CVS! +# $Header$ + +my($VERSION) = q$Revision$ =~ /:\s*(\d+(?:\.\d+)?)/; + +use strict; +use File::Copy; + +my $usage = " +Usage for rvi $VERSION: + + $0 [-r] [+] [-] filename... + +-r Enter RCS description ++ auto-increment SOA serial number (default) +- do NOT auto-increment SOA serial number. +"; + +@ARGV || die($usage); + +$ENV{EDITOR} = "vi" unless exists $ENV{EDITOR}; + +my $autoinc = 1; # now default +my $setdescr = 0; + +my $euid = $>; +my $egid = (split(/\s+/,$)))[0]; +my $user = getlogin() || $ENV{USER} || getpwuid($<); + +my $namedpath = "/usr/local/etc/named"; +my $ndc; +if ($ENV{RVI_AUTO_NDC_RELOAD} or $ENV{RVI_AUTO_NDC_RECONFIG}) { + $ndc = which("ndc") or exit 1; +} +my $ci = which("ci") or exit 1; +my $co = which("co") or exit 1; +my $cmp = which("cmp") or exit 1; +my $rcsdiff = which("rcsdiff") or exit 1; + +while ( 1 ) { + $ARGV[0] eq "+" && do { + $autoinc = 1; + shift; + next; + }; + $ARGV[0] eq "-" && do { + $autoinc = 0; + shift; + next; + }; + $ARGV[0] eq "-r" && do { + $setdescr = 1; + shift; + next; + }; + last; +} + +my($file, %serial, %origfile, %checked, %fileinfo, %rcsinfo); + +my $wd = `pwd`; +chomp($wd); + +foreach $file ( @ARGV ) { + my $fullname = makefullname($wd, $file); + ($fileinfo{$file}, $rcsinfo{$file}) = getinfo($fullname); + if ($rcsinfo{$file}->{'exists'}) { + unless (execute_ok($rcsdiff,"-c",$file)) { + warn "$file would change when checking out of RCS!\n"; + exit cleanmess(); + } + unless (execute_ok($co,"-l",$file)) { + exit cleanmess(); + } + } + + $checked{$file} = 1; + + # Begin magic incrementing of serial no. + if ( $fullname =~ m-^$namedpath-o && $fileinfo{$file}->{readable} ) { + checkDNS($file, $fullname, $file, 2); + } +} + +execute_ok($ENV{EDITOR},@ARGV); + +FILE: foreach $file ( keys %serial ) { + unless (execute_ok($cmp,"-s",$origfile{$file},"$origfile{$file}.tmp")) { + if ( ! exists $checked{$file} && $autoinc ) { + if (execute_ok($co,"-l",$file)) { + $checked{$file} = 1; + ($fileinfo{$file}, $rcsinfo{$file}) + = getinfo(makefullname($wd,$file)); + } + else { + warn "You'll have to update the serial number yourself.\n"; + next FILE; + } + } + updateDNS($file); + $fileinfo{$file}->{changed} = 1; + } + unlink("$file.tmp"); +} + +my($reconfig,@zones); +foreach $file ( keys %checked ) { + my($success); + + if ( $setdescr ) { + execute_ok($ci,"-u",$file); + } + else { + open(CI, "|$ci -u $file") or die("Cannot fork $ci: $!\n"); + print CI "updated by $user on " . (localtime) . "\n"; + close(CI); + if ($?) { + warn "WARNING: $ci -u $file returned error: ", $?/256, "\n"; + } + } + if ( $? ) { + delete $checked{$file}; + } else { + if ($rcsinfo{$file}->{'exists'} + and $rcsinfo{$file}->{uid} != $euid + || $rcsinfo{$file}->{gid} != $egid) { + chown $rcsinfo{$file}->{uid}, $rcsinfo{$file}->{gid}, + $file, $rcsinfo{$file}->{file}; + } + } + if ($ENV{RVI_AUTO_NDC_RECONFIG}) { + if ($fileinfo{$file}->{changed} and $fileinfo{$file}->{src}) { + $reconfig++; + } + } + if ($ENV{RVI_AUTO_NDC_RELOAD}) { + if ($fileinfo{$file}->{changed} and $fileinfo{$file}->{zone}) { + push @zones, $fileinfo{$file}->{zone}; + } + } +} + +if ($ndc) { + execute_ok($ndc,"reconfig") if $reconfig; + foreach my $zone (@zones) { + warn "Reloading $zone:\n"; + execute_ok($ndc,"reload",$zone); + } +} + +sub checkDNS { + my($file, $fullname, $origfile, $maxrecurse) = @_; + my($count, $dir, $which, $command, @files); + + open(FILE, $file) or die("Cannot open $file: $!\n"); + $count = 0; + while ( ) { + if ( /^\@\s+IN\s+SOA\s+\S+\s+\S+\s+\(\s*$/i ) { + $_ = ; + last if !defined $_; + if ( /^\s*(\d+)/ ) { + $serial{$file} = $1; + $origfile{$file} = $origfile; + copy($origfile,"$origfile.tmp"); + last; + } + } + last if ++$count > 10; + } + close(FILE); + if ( $maxrecurse > 0 && ! exists $serial{$file} ) { + # not found, try if we're included somewhere... + $fullname =~ s-^$namedpath/--o; + ($dir = $file) =~ s-[^/]*$--; + $dir .= '*'; + @files = grep { !/~$/ && -f && -T } < $dir >; + $command = "grep -li \'\$INCLUDE *$fullname\' " . join(" ", @files); + $which = `$command`; + @files = split(' ', $which); + if ( @files == 1 ) { + $fullname = makefullname($wd, $files[0]); + checkDNS($files[0], $fullname, $origfile, $maxrecurse - 1); + } + } +} + +sub updateDNS { + my($file) = @_; + my($count); + + open(FILE, $file) or die("Cannot open $file: $!\n"); + $count = 0; + while ( ) { + if ( /^\@\s+IN\s+SOA\s+\S+\s+\S+\s+\(\s*$/i ) { + $_ = ; + last if !defined $_; + close(FILE); + if ( /^\s*(\d+)/ ) { + if ( $1 < $serial{$file} ) { + warn "You LOWERED the serial number for $file. Why?\n"; + } + elsif ( $1 > $serial{$file} ) { + warn + "I see you updated the serial number yourself. Stoer!\n"; + } + else { + newserialDNS($file); + } + return; + } + } + last if ++$count > 10; + } + warn "What did you do to the serial number?\n"; + close(FILE); +} + +sub newserialDNS { + my($file) = @_; + my($serial, $new_serial); + my $hadSOA = 0; + + if ( $autoinc == 0 ) { + warn "You should update the serial number in $file.\n"; + return; + } + use POSIX 'strftime'; + warn "Automatically updating the serial number in $file for you.\n"; + open(FILE, $file) or die("Cannot open $file: $!\n"); + open(NEW, ">$file.magicinc") or die("Cannot write $file.magicinc: $!\n"); + while ( ) { + print NEW; + if ( ! $hadSOA && /^\@\s+IN\s+SOA\s+\S+\s+\S+\s+\(\s*$/i ) { + $hadSOA = 1; + $_ = ; + unless ( /^(\s*)\d+([\w\W]*)$/ ) { + die("Aargh! internal error, stop"); + } + $new_serial = strftime ("%Y%m%d", localtime()) . "01"; + $serial = "" . $serial{$file}; + if ($new_serial > $serial) { + $serial = $new_serial; + } else { + $serial++; + } + print NEW $1 . $serial . $2; + } + } + close(FILE); + close(NEW) or die("Aargh! closing $file.magicinc: $!\n"); + unlink($file) or die("Aargh! unlink $file: $!\n"); + rename("$file.magicinc", $file) or die("Aargh! rename $file: $!\n"); +} + +sub makefullname { + my($wd, $file) = @_; + my($fullname); + + if ( $file =~ m-^/- ) { + $fullname = $file; + } + else { + $fullname = "$wd/$file"; + } + 1 while $fullname =~ s-[^/]+/\.\./--; + $fullname; +} + +sub getinfo { + my($file) = @_; + my($fileinfo,$rcsinfo); + + my($path,$name); + unless ( ($path, $name) = $file =~ m-^(.*)/([^/]*)$- ) { + $path = "."; + $name = $file; + } + $fileinfo->{name} = $name; + if ($path =~ m-^$namedpath/(primary|secondary)-o) { + $fileinfo->{zone} = $name; + } + elsif ($path =~ m-^$namedpath/reverse-o) { + ($fileinfo->{zone}) = $name =~ /^(\d+\.\d+\.\d+)/; + $fileinfo->{zone} .= ".in-addr.arpa"; + } + elsif ($path =~ m-^$namedpath/src-o) { + $fileinfo->{src} = 1; + } + + $rcsinfo->{file} = "$path/RCS/$name,v"; + if ( -f $rcsinfo->{file} ) { + $rcsinfo->{'exists'} = 1; + $rcsinfo->{uid} = (stat(_))[4]; + $rcsinfo->{gid} = (stat(_))[5]; + } + elsif ( ! -d "$path/RCS" ) { + warn "WARNING: There is no $path/RCS directory!\n"; + exit cleanmess(); + } + + if ( -f $file ) { + unless ($rcsinfo->{'exists'}) { + warn "WARNING: $file already exists but is not in RCS!\n"; + warn "Manually check it in if you want it to be in RCS.\n"; + exit cleanmess(); + } + $fileinfo->{mode} = (stat(_))[2]; + $fileinfo->{readable} = -r _; + } + + return $fileinfo, $rcsinfo; +} + +sub cleanmess { + if ( keys %checked ) { + warn "Cleaning up the mess...\n"; + foreach my $file ( keys %checked ) { + if ($fileinfo{$file}->{mode}) { + chmod $fileinfo{$file}->{mode} & 07577, $file; + } + if ($rcsinfo{$file}->{'exists'}) { + execute_ok($co,"-u",$file); + if ($rcsinfo{$file}->{uid} != $euid + or $rcsinfo{$file}->{gid} != $egid) { + chown $rcsinfo{$file}->{uid}, $rcsinfo{$file}->{gid}, + $file, $rcsinfo{$file}->{file}; + } + } + } + } + return 1; +} + +sub execute_ok { + my(@command) = @_; + my $ret = system(@command) / 256; + unless ($command[0] eq $cmp) { + warn "WARNING: @command returned $ret\n" if $ret; + } + return not $ret; +} + +sub which { + my($prog) = @_; + my($fullprog) = grep { -x } ( + "/usr/local/bin/$prog", + "/usr/bin/$prog", + "/usr/local/sbin/$prog", + "/usr/sbin/$prog", + ) or warn "WARNING: cannot find $prog\n"; + return $fullprog; +} +