Commit Diff


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 ( <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;
+}
+