commit - /dev/null
commit + 0721217080948bafa906b09b505e6caa761782c8
blob - /dev/null
blob + 19f5800850a55f437d9a718aad7228d18a3fe17e (mode 755)
--- /dev/null
+++ rvi
+#!/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 ( <FILE> ) {
+ if ( /^\@\s+IN\s+SOA\s+\S+\s+\S+\s+\(\s*$/i ) {
+ $_ = <FILE>;
+ 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 ( <FILE> ) {
+ if ( /^\@\s+IN\s+SOA\s+\S+\s+\S+\s+\(\s*$/i ) {
+ $_ = <FILE>;
+ 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 ( <FILE> ) {
+ print NEW;
+ if ( ! $hadSOA && /^\@\s+IN\s+SOA\s+\S+\s+\S+\s+\(\s*$/i ) {
+ $hadSOA = 1;
+ $_ = <FILE>;
+ 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;
+}
+