1 07212170 2022-07-18 mischa #!/usr/bin/perl -w
2 07212170 2022-07-18 mischa # -*- Perl -*-
4 07212170 2022-07-18 mischa # This is in CVS!
7 07212170 2022-07-18 mischa my($VERSION) = q$Revision$ =~ /:\s*(\d+(?:\.\d+)?)/;
9 07212170 2022-07-18 mischa use strict;
10 07212170 2022-07-18 mischa use File::Copy;
12 07212170 2022-07-18 mischa my $usage = "
13 07212170 2022-07-18 mischa Usage for rvi $VERSION:
15 07212170 2022-07-18 mischa $0 [-r] [+] [-] filename...
17 07212170 2022-07-18 mischa -r Enter RCS description
18 07212170 2022-07-18 mischa + auto-increment SOA serial number (default)
19 07212170 2022-07-18 mischa - do NOT auto-increment SOA serial number.
22 07212170 2022-07-18 mischa @ARGV || die($usage);
24 07212170 2022-07-18 mischa $ENV{EDITOR} = "vi" unless exists $ENV{EDITOR};
26 07212170 2022-07-18 mischa my $autoinc = 1; # now default
27 07212170 2022-07-18 mischa my $setdescr = 0;
29 07212170 2022-07-18 mischa my $euid = $>;
30 07212170 2022-07-18 mischa my $egid = (split(/\s+/,$)))[0];
31 07212170 2022-07-18 mischa my $user = getlogin() || $ENV{USER} || getpwuid($<);
33 07212170 2022-07-18 mischa my $namedpath = "/usr/local/etc/named";
35 07212170 2022-07-18 mischa if ($ENV{RVI_AUTO_NDC_RELOAD} or $ENV{RVI_AUTO_NDC_RECONFIG}) {
36 07212170 2022-07-18 mischa $ndc = which("ndc") or exit 1;
38 07212170 2022-07-18 mischa my $ci = which("ci") or exit 1;
39 07212170 2022-07-18 mischa my $co = which("co") or exit 1;
40 07212170 2022-07-18 mischa my $cmp = which("cmp") or exit 1;
41 07212170 2022-07-18 mischa my $rcsdiff = which("rcsdiff") or exit 1;
43 07212170 2022-07-18 mischa while ( 1 ) {
44 07212170 2022-07-18 mischa $ARGV[0] eq "+" && do {
45 07212170 2022-07-18 mischa $autoinc = 1;
49 07212170 2022-07-18 mischa $ARGV[0] eq "-" && do {
50 07212170 2022-07-18 mischa $autoinc = 0;
54 07212170 2022-07-18 mischa $ARGV[0] eq "-r" && do {
55 07212170 2022-07-18 mischa $setdescr = 1;
62 07212170 2022-07-18 mischa my($file, %serial, %origfile, %checked, %fileinfo, %rcsinfo);
64 07212170 2022-07-18 mischa my $wd = `pwd`;
65 07212170 2022-07-18 mischa chomp($wd);
67 07212170 2022-07-18 mischa foreach $file ( @ARGV ) {
68 07212170 2022-07-18 mischa my $fullname = makefullname($wd, $file);
69 07212170 2022-07-18 mischa ($fileinfo{$file}, $rcsinfo{$file}) = getinfo($fullname);
70 07212170 2022-07-18 mischa if ($rcsinfo{$file}->{'exists'}) {
71 07212170 2022-07-18 mischa unless (execute_ok($rcsdiff,"-c",$file)) {
72 07212170 2022-07-18 mischa warn "$file would change when checking out of RCS!\n";
73 07212170 2022-07-18 mischa exit cleanmess();
75 07212170 2022-07-18 mischa unless (execute_ok($co,"-l",$file)) {
76 07212170 2022-07-18 mischa exit cleanmess();
80 07212170 2022-07-18 mischa $checked{$file} = 1;
82 07212170 2022-07-18 mischa # Begin magic incrementing of serial no.
83 07212170 2022-07-18 mischa if ( $fullname =~ m-^$namedpath-o && $fileinfo{$file}->{readable} ) {
84 07212170 2022-07-18 mischa checkDNS($file, $fullname, $file, 2);
88 07212170 2022-07-18 mischa execute_ok($ENV{EDITOR},@ARGV);
90 07212170 2022-07-18 mischa FILE: foreach $file ( keys %serial ) {
91 07212170 2022-07-18 mischa unless (execute_ok($cmp,"-s",$origfile{$file},"$origfile{$file}.tmp")) {
92 07212170 2022-07-18 mischa if ( ! exists $checked{$file} && $autoinc ) {
93 07212170 2022-07-18 mischa if (execute_ok($co,"-l",$file)) {
94 07212170 2022-07-18 mischa $checked{$file} = 1;
95 07212170 2022-07-18 mischa ($fileinfo{$file}, $rcsinfo{$file})
96 07212170 2022-07-18 mischa = getinfo(makefullname($wd,$file));
99 07212170 2022-07-18 mischa warn "You'll have to update the serial number yourself.\n";
100 07212170 2022-07-18 mischa next FILE;
103 07212170 2022-07-18 mischa updateDNS($file);
104 07212170 2022-07-18 mischa $fileinfo{$file}->{changed} = 1;
106 07212170 2022-07-18 mischa unlink("$file.tmp");
109 07212170 2022-07-18 mischa my($reconfig,@zones);
110 07212170 2022-07-18 mischa foreach $file ( keys %checked ) {
111 07212170 2022-07-18 mischa my($success);
113 07212170 2022-07-18 mischa if ( $setdescr ) {
114 07212170 2022-07-18 mischa execute_ok($ci,"-u",$file);
117 07212170 2022-07-18 mischa open(CI, "|$ci -u $file") or die("Cannot fork $ci: $!\n");
118 07212170 2022-07-18 mischa print CI "updated by $user on " . (localtime) . "\n";
119 07212170 2022-07-18 mischa close(CI);
120 07212170 2022-07-18 mischa if ($?) {
121 07212170 2022-07-18 mischa warn "WARNING: $ci -u $file returned error: ", $?/256, "\n";
124 07212170 2022-07-18 mischa if ( $? ) {
125 07212170 2022-07-18 mischa delete $checked{$file};
127 07212170 2022-07-18 mischa if ($rcsinfo{$file}->{'exists'}
128 07212170 2022-07-18 mischa and $rcsinfo{$file}->{uid} != $euid
129 07212170 2022-07-18 mischa || $rcsinfo{$file}->{gid} != $egid) {
130 07212170 2022-07-18 mischa chown $rcsinfo{$file}->{uid}, $rcsinfo{$file}->{gid},
131 07212170 2022-07-18 mischa $file, $rcsinfo{$file}->{file};
134 07212170 2022-07-18 mischa if ($ENV{RVI_AUTO_NDC_RECONFIG}) {
135 07212170 2022-07-18 mischa if ($fileinfo{$file}->{changed} and $fileinfo{$file}->{src}) {
136 07212170 2022-07-18 mischa $reconfig++;
139 07212170 2022-07-18 mischa if ($ENV{RVI_AUTO_NDC_RELOAD}) {
140 07212170 2022-07-18 mischa if ($fileinfo{$file}->{changed} and $fileinfo{$file}->{zone}) {
141 07212170 2022-07-18 mischa push @zones, $fileinfo{$file}->{zone};
146 07212170 2022-07-18 mischa if ($ndc) {
147 07212170 2022-07-18 mischa execute_ok($ndc,"reconfig") if $reconfig;
148 07212170 2022-07-18 mischa foreach my $zone (@zones) {
149 07212170 2022-07-18 mischa warn "Reloading $zone:\n";
150 07212170 2022-07-18 mischa execute_ok($ndc,"reload",$zone);
154 07212170 2022-07-18 mischa sub checkDNS {
155 07212170 2022-07-18 mischa my($file, $fullname, $origfile, $maxrecurse) = @_;
156 07212170 2022-07-18 mischa my($count, $dir, $which, $command, @files);
158 07212170 2022-07-18 mischa open(FILE, $file) or die("Cannot open $file: $!\n");
159 07212170 2022-07-18 mischa $count = 0;
160 07212170 2022-07-18 mischa while ( <FILE> ) {
161 07212170 2022-07-18 mischa if ( /^\@\s+IN\s+SOA\s+\S+\s+\S+\s+\(\s*$/i ) {
162 07212170 2022-07-18 mischa $_ = <FILE>;
163 07212170 2022-07-18 mischa last if !defined $_;
164 07212170 2022-07-18 mischa if ( /^\s*(\d+)/ ) {
165 07212170 2022-07-18 mischa $serial{$file} = $1;
166 07212170 2022-07-18 mischa $origfile{$file} = $origfile;
167 07212170 2022-07-18 mischa copy($origfile,"$origfile.tmp");
171 07212170 2022-07-18 mischa last if ++$count > 10;
173 07212170 2022-07-18 mischa close(FILE);
174 07212170 2022-07-18 mischa if ( $maxrecurse > 0 && ! exists $serial{$file} ) {
175 07212170 2022-07-18 mischa # not found, try if we're included somewhere...
176 07212170 2022-07-18 mischa $fullname =~ s-^$namedpath/--o;
177 07212170 2022-07-18 mischa ($dir = $file) =~ s-[^/]*$--;
178 07212170 2022-07-18 mischa $dir .= '*';
179 07212170 2022-07-18 mischa @files = grep { !/~$/ && -f && -T } < $dir >;
180 07212170 2022-07-18 mischa $command = "grep -li \'\$INCLUDE *$fullname\' " . join(" ", @files);
181 07212170 2022-07-18 mischa $which = `$command`;
182 07212170 2022-07-18 mischa @files = split(' ', $which);
183 07212170 2022-07-18 mischa if ( @files == 1 ) {
184 07212170 2022-07-18 mischa $fullname = makefullname($wd, $files[0]);
185 07212170 2022-07-18 mischa checkDNS($files[0], $fullname, $origfile, $maxrecurse - 1);
190 07212170 2022-07-18 mischa sub updateDNS {
191 07212170 2022-07-18 mischa my($file) = @_;
192 07212170 2022-07-18 mischa my($count);
194 07212170 2022-07-18 mischa open(FILE, $file) or die("Cannot open $file: $!\n");
195 07212170 2022-07-18 mischa $count = 0;
196 07212170 2022-07-18 mischa while ( <FILE> ) {
197 07212170 2022-07-18 mischa if ( /^\@\s+IN\s+SOA\s+\S+\s+\S+\s+\(\s*$/i ) {
198 07212170 2022-07-18 mischa $_ = <FILE>;
199 07212170 2022-07-18 mischa last if !defined $_;
200 07212170 2022-07-18 mischa close(FILE);
201 07212170 2022-07-18 mischa if ( /^\s*(\d+)/ ) {
202 07212170 2022-07-18 mischa if ( $1 < $serial{$file} ) {
203 07212170 2022-07-18 mischa warn "You LOWERED the serial number for $file. Why?\n";
205 07212170 2022-07-18 mischa elsif ( $1 > $serial{$file} ) {
207 07212170 2022-07-18 mischa "I see you updated the serial number yourself. Stoer!\n";
210 07212170 2022-07-18 mischa newserialDNS($file);
215 07212170 2022-07-18 mischa last if ++$count > 10;
217 07212170 2022-07-18 mischa warn "What did you do to the serial number?\n";
218 07212170 2022-07-18 mischa close(FILE);
221 07212170 2022-07-18 mischa sub newserialDNS {
222 07212170 2022-07-18 mischa my($file) = @_;
223 07212170 2022-07-18 mischa my($serial, $new_serial);
224 07212170 2022-07-18 mischa my $hadSOA = 0;
226 07212170 2022-07-18 mischa if ( $autoinc == 0 ) {
227 07212170 2022-07-18 mischa warn "You should update the serial number in $file.\n";
230 07212170 2022-07-18 mischa use POSIX 'strftime';
231 07212170 2022-07-18 mischa warn "Automatically updating the serial number in $file for you.\n";
232 07212170 2022-07-18 mischa open(FILE, $file) or die("Cannot open $file: $!\n");
233 07212170 2022-07-18 mischa open(NEW, ">$file.magicinc") or die("Cannot write $file.magicinc: $!\n");
234 07212170 2022-07-18 mischa while ( <FILE> ) {
235 07212170 2022-07-18 mischa print NEW;
236 07212170 2022-07-18 mischa if ( ! $hadSOA && /^\@\s+IN\s+SOA\s+\S+\s+\S+\s+\(\s*$/i ) {
237 07212170 2022-07-18 mischa $hadSOA = 1;
238 07212170 2022-07-18 mischa $_ = <FILE>;
239 07212170 2022-07-18 mischa unless ( /^(\s*)\d+([\w\W]*)$/ ) {
240 07212170 2022-07-18 mischa die("Aargh! internal error, stop");
242 07212170 2022-07-18 mischa $new_serial = strftime ("%Y%m%d", localtime()) . "01";
243 07212170 2022-07-18 mischa $serial = "" . $serial{$file};
244 07212170 2022-07-18 mischa if ($new_serial > $serial) {
245 07212170 2022-07-18 mischa $serial = $new_serial;
247 07212170 2022-07-18 mischa $serial++;
249 07212170 2022-07-18 mischa print NEW $1 . $serial . $2;
252 07212170 2022-07-18 mischa close(FILE);
253 07212170 2022-07-18 mischa close(NEW) or die("Aargh! closing $file.magicinc: $!\n");
254 07212170 2022-07-18 mischa unlink($file) or die("Aargh! unlink $file: $!\n");
255 07212170 2022-07-18 mischa rename("$file.magicinc", $file) or die("Aargh! rename $file: $!\n");
258 07212170 2022-07-18 mischa sub makefullname {
259 07212170 2022-07-18 mischa my($wd, $file) = @_;
260 07212170 2022-07-18 mischa my($fullname);
262 07212170 2022-07-18 mischa if ( $file =~ m-^/- ) {
263 07212170 2022-07-18 mischa $fullname = $file;
266 07212170 2022-07-18 mischa $fullname = "$wd/$file";
268 07212170 2022-07-18 mischa 1 while $fullname =~ s-[^/]+/\.\./--;
269 07212170 2022-07-18 mischa $fullname;
272 07212170 2022-07-18 mischa sub getinfo {
273 07212170 2022-07-18 mischa my($file) = @_;
274 07212170 2022-07-18 mischa my($fileinfo,$rcsinfo);
276 07212170 2022-07-18 mischa my($path,$name);
277 07212170 2022-07-18 mischa unless ( ($path, $name) = $file =~ m-^(.*)/([^/]*)$- ) {
278 07212170 2022-07-18 mischa $path = ".";
279 07212170 2022-07-18 mischa $name = $file;
281 07212170 2022-07-18 mischa $fileinfo->{name} = $name;
282 07212170 2022-07-18 mischa if ($path =~ m-^$namedpath/(primary|secondary)-o) {
283 07212170 2022-07-18 mischa $fileinfo->{zone} = $name;
285 07212170 2022-07-18 mischa elsif ($path =~ m-^$namedpath/reverse-o) {
286 07212170 2022-07-18 mischa ($fileinfo->{zone}) = $name =~ /^(\d+\.\d+\.\d+)/;
287 07212170 2022-07-18 mischa $fileinfo->{zone} .= ".in-addr.arpa";
289 07212170 2022-07-18 mischa elsif ($path =~ m-^$namedpath/src-o) {
290 07212170 2022-07-18 mischa $fileinfo->{src} = 1;
293 07212170 2022-07-18 mischa $rcsinfo->{file} = "$path/RCS/$name,v";
294 07212170 2022-07-18 mischa if ( -f $rcsinfo->{file} ) {
295 07212170 2022-07-18 mischa $rcsinfo->{'exists'} = 1;
296 07212170 2022-07-18 mischa $rcsinfo->{uid} = (stat(_))[4];
297 07212170 2022-07-18 mischa $rcsinfo->{gid} = (stat(_))[5];
299 07212170 2022-07-18 mischa elsif ( ! -d "$path/RCS" ) {
300 07212170 2022-07-18 mischa warn "WARNING: There is no $path/RCS directory!\n";
301 07212170 2022-07-18 mischa exit cleanmess();
304 07212170 2022-07-18 mischa if ( -f $file ) {
305 07212170 2022-07-18 mischa unless ($rcsinfo->{'exists'}) {
306 07212170 2022-07-18 mischa warn "WARNING: $file already exists but is not in RCS!\n";
307 07212170 2022-07-18 mischa warn "Manually check it in if you want it to be in RCS.\n";
308 07212170 2022-07-18 mischa exit cleanmess();
310 07212170 2022-07-18 mischa $fileinfo->{mode} = (stat(_))[2];
311 07212170 2022-07-18 mischa $fileinfo->{readable} = -r _;
314 07212170 2022-07-18 mischa return $fileinfo, $rcsinfo;
317 07212170 2022-07-18 mischa sub cleanmess {
318 07212170 2022-07-18 mischa if ( keys %checked ) {
319 07212170 2022-07-18 mischa warn "Cleaning up the mess...\n";
320 07212170 2022-07-18 mischa foreach my $file ( keys %checked ) {
321 07212170 2022-07-18 mischa if ($fileinfo{$file}->{mode}) {
322 07212170 2022-07-18 mischa chmod $fileinfo{$file}->{mode} & 07577, $file;
324 07212170 2022-07-18 mischa if ($rcsinfo{$file}->{'exists'}) {
325 07212170 2022-07-18 mischa execute_ok($co,"-u",$file);
326 07212170 2022-07-18 mischa if ($rcsinfo{$file}->{uid} != $euid
327 07212170 2022-07-18 mischa or $rcsinfo{$file}->{gid} != $egid) {
328 07212170 2022-07-18 mischa chown $rcsinfo{$file}->{uid}, $rcsinfo{$file}->{gid},
329 07212170 2022-07-18 mischa $file, $rcsinfo{$file}->{file};
334 07212170 2022-07-18 mischa return 1;
337 07212170 2022-07-18 mischa sub execute_ok {
338 07212170 2022-07-18 mischa my(@command) = @_;
339 07212170 2022-07-18 mischa my $ret = system(@command) / 256;
340 07212170 2022-07-18 mischa unless ($command[0] eq $cmp) {
341 07212170 2022-07-18 mischa warn "WARNING: @command returned $ret\n" if $ret;
343 07212170 2022-07-18 mischa return not $ret;
346 07212170 2022-07-18 mischa sub which {
347 07212170 2022-07-18 mischa my($prog) = @_;
348 07212170 2022-07-18 mischa my($fullprog) = grep { -x } (
349 07212170 2022-07-18 mischa "/usr/local/bin/$prog",
350 07212170 2022-07-18 mischa "/usr/bin/$prog",
351 07212170 2022-07-18 mischa "/usr/local/sbin/$prog",
352 07212170 2022-07-18 mischa "/usr/sbin/$prog",
353 07212170 2022-07-18 mischa ) or warn "WARNING: cannot find $prog\n";
354 07212170 2022-07-18 mischa return $fullprog;