Blame


1 07212170 2022-07-18 mischa #!/usr/bin/perl -w
2 07212170 2022-07-18 mischa # -*- Perl -*-
3 07212170 2022-07-18 mischa #
4 07212170 2022-07-18 mischa # This is in CVS!
5 07212170 2022-07-18 mischa # $Header$
6 07212170 2022-07-18 mischa
7 07212170 2022-07-18 mischa my($VERSION) = q$Revision$ =~ /:\s*(\d+(?:\.\d+)?)/;
8 07212170 2022-07-18 mischa
9 07212170 2022-07-18 mischa use strict;
10 07212170 2022-07-18 mischa use File::Copy;
11 07212170 2022-07-18 mischa
12 07212170 2022-07-18 mischa my $usage = "
13 07212170 2022-07-18 mischa Usage for rvi $VERSION:
14 07212170 2022-07-18 mischa
15 07212170 2022-07-18 mischa $0 [-r] [+] [-] filename...
16 07212170 2022-07-18 mischa
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.
20 07212170 2022-07-18 mischa ";
21 07212170 2022-07-18 mischa
22 07212170 2022-07-18 mischa @ARGV || die($usage);
23 07212170 2022-07-18 mischa
24 07212170 2022-07-18 mischa $ENV{EDITOR} = "vi" unless exists $ENV{EDITOR};
25 07212170 2022-07-18 mischa
26 07212170 2022-07-18 mischa my $autoinc = 1; # now default
27 07212170 2022-07-18 mischa my $setdescr = 0;
28 07212170 2022-07-18 mischa
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($<);
32 07212170 2022-07-18 mischa
33 07212170 2022-07-18 mischa my $namedpath = "/usr/local/etc/named";
34 07212170 2022-07-18 mischa my $ndc;
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;
37 07212170 2022-07-18 mischa }
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;
42 07212170 2022-07-18 mischa
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;
46 07212170 2022-07-18 mischa shift;
47 07212170 2022-07-18 mischa next;
48 07212170 2022-07-18 mischa };
49 07212170 2022-07-18 mischa $ARGV[0] eq "-" && do {
50 07212170 2022-07-18 mischa $autoinc = 0;
51 07212170 2022-07-18 mischa shift;
52 07212170 2022-07-18 mischa next;
53 07212170 2022-07-18 mischa };
54 07212170 2022-07-18 mischa $ARGV[0] eq "-r" && do {
55 07212170 2022-07-18 mischa $setdescr = 1;
56 07212170 2022-07-18 mischa shift;
57 07212170 2022-07-18 mischa next;
58 07212170 2022-07-18 mischa };
59 07212170 2022-07-18 mischa last;
60 07212170 2022-07-18 mischa }
61 07212170 2022-07-18 mischa
62 07212170 2022-07-18 mischa my($file, %serial, %origfile, %checked, %fileinfo, %rcsinfo);
63 07212170 2022-07-18 mischa
64 07212170 2022-07-18 mischa my $wd = `pwd`;
65 07212170 2022-07-18 mischa chomp($wd);
66 07212170 2022-07-18 mischa
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();
74 07212170 2022-07-18 mischa }
75 07212170 2022-07-18 mischa unless (execute_ok($co,"-l",$file)) {
76 07212170 2022-07-18 mischa exit cleanmess();
77 07212170 2022-07-18 mischa }
78 07212170 2022-07-18 mischa }
79 07212170 2022-07-18 mischa
80 07212170 2022-07-18 mischa $checked{$file} = 1;
81 07212170 2022-07-18 mischa
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);
85 07212170 2022-07-18 mischa }
86 07212170 2022-07-18 mischa }
87 07212170 2022-07-18 mischa
88 07212170 2022-07-18 mischa execute_ok($ENV{EDITOR},@ARGV);
89 07212170 2022-07-18 mischa
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));
97 07212170 2022-07-18 mischa }
98 07212170 2022-07-18 mischa else {
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;
101 07212170 2022-07-18 mischa }
102 07212170 2022-07-18 mischa }
103 07212170 2022-07-18 mischa updateDNS($file);
104 07212170 2022-07-18 mischa $fileinfo{$file}->{changed} = 1;
105 07212170 2022-07-18 mischa }
106 07212170 2022-07-18 mischa unlink("$file.tmp");
107 07212170 2022-07-18 mischa }
108 07212170 2022-07-18 mischa
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);
112 07212170 2022-07-18 mischa
113 07212170 2022-07-18 mischa if ( $setdescr ) {
114 07212170 2022-07-18 mischa execute_ok($ci,"-u",$file);
115 07212170 2022-07-18 mischa }
116 07212170 2022-07-18 mischa else {
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";
122 07212170 2022-07-18 mischa }
123 07212170 2022-07-18 mischa }
124 07212170 2022-07-18 mischa if ( $? ) {
125 07212170 2022-07-18 mischa delete $checked{$file};
126 07212170 2022-07-18 mischa } else {
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};
132 07212170 2022-07-18 mischa }
133 07212170 2022-07-18 mischa }
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++;
137 07212170 2022-07-18 mischa }
138 07212170 2022-07-18 mischa }
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};
142 07212170 2022-07-18 mischa }
143 07212170 2022-07-18 mischa }
144 07212170 2022-07-18 mischa }
145 07212170 2022-07-18 mischa
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);
151 07212170 2022-07-18 mischa }
152 07212170 2022-07-18 mischa }
153 07212170 2022-07-18 mischa
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);
157 07212170 2022-07-18 mischa
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");
168 07212170 2022-07-18 mischa last;
169 07212170 2022-07-18 mischa }
170 07212170 2022-07-18 mischa }
171 07212170 2022-07-18 mischa last if ++$count > 10;
172 07212170 2022-07-18 mischa }
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);
186 07212170 2022-07-18 mischa }
187 07212170 2022-07-18 mischa }
188 07212170 2022-07-18 mischa }
189 07212170 2022-07-18 mischa
190 07212170 2022-07-18 mischa sub updateDNS {
191 07212170 2022-07-18 mischa my($file) = @_;
192 07212170 2022-07-18 mischa my($count);
193 07212170 2022-07-18 mischa
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";
204 07212170 2022-07-18 mischa }
205 07212170 2022-07-18 mischa elsif ( $1 > $serial{$file} ) {
206 07212170 2022-07-18 mischa warn
207 07212170 2022-07-18 mischa "I see you updated the serial number yourself. Stoer!\n";
208 07212170 2022-07-18 mischa }
209 07212170 2022-07-18 mischa else {
210 07212170 2022-07-18 mischa newserialDNS($file);
211 07212170 2022-07-18 mischa }
212 07212170 2022-07-18 mischa return;
213 07212170 2022-07-18 mischa }
214 07212170 2022-07-18 mischa }
215 07212170 2022-07-18 mischa last if ++$count > 10;
216 07212170 2022-07-18 mischa }
217 07212170 2022-07-18 mischa warn "What did you do to the serial number?\n";
218 07212170 2022-07-18 mischa close(FILE);
219 07212170 2022-07-18 mischa }
220 07212170 2022-07-18 mischa
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;
225 07212170 2022-07-18 mischa
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";
228 07212170 2022-07-18 mischa return;
229 07212170 2022-07-18 mischa }
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");
241 07212170 2022-07-18 mischa }
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;
246 07212170 2022-07-18 mischa } else {
247 07212170 2022-07-18 mischa $serial++;
248 07212170 2022-07-18 mischa }
249 07212170 2022-07-18 mischa print NEW $1 . $serial . $2;
250 07212170 2022-07-18 mischa }
251 07212170 2022-07-18 mischa }
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");
256 07212170 2022-07-18 mischa }
257 07212170 2022-07-18 mischa
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);
261 07212170 2022-07-18 mischa
262 07212170 2022-07-18 mischa if ( $file =~ m-^/- ) {
263 07212170 2022-07-18 mischa $fullname = $file;
264 07212170 2022-07-18 mischa }
265 07212170 2022-07-18 mischa else {
266 07212170 2022-07-18 mischa $fullname = "$wd/$file";
267 07212170 2022-07-18 mischa }
268 07212170 2022-07-18 mischa 1 while $fullname =~ s-[^/]+/\.\./--;
269 07212170 2022-07-18 mischa $fullname;
270 07212170 2022-07-18 mischa }
271 07212170 2022-07-18 mischa
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);
275 07212170 2022-07-18 mischa
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;
280 07212170 2022-07-18 mischa }
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;
284 07212170 2022-07-18 mischa }
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";
288 07212170 2022-07-18 mischa }
289 07212170 2022-07-18 mischa elsif ($path =~ m-^$namedpath/src-o) {
290 07212170 2022-07-18 mischa $fileinfo->{src} = 1;
291 07212170 2022-07-18 mischa }
292 07212170 2022-07-18 mischa
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];
298 07212170 2022-07-18 mischa }
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();
302 07212170 2022-07-18 mischa }
303 07212170 2022-07-18 mischa
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();
309 07212170 2022-07-18 mischa }
310 07212170 2022-07-18 mischa $fileinfo->{mode} = (stat(_))[2];
311 07212170 2022-07-18 mischa $fileinfo->{readable} = -r _;
312 07212170 2022-07-18 mischa }
313 07212170 2022-07-18 mischa
314 07212170 2022-07-18 mischa return $fileinfo, $rcsinfo;
315 07212170 2022-07-18 mischa }
316 07212170 2022-07-18 mischa
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;
323 07212170 2022-07-18 mischa }
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};
330 07212170 2022-07-18 mischa }
331 07212170 2022-07-18 mischa }
332 07212170 2022-07-18 mischa }
333 07212170 2022-07-18 mischa }
334 07212170 2022-07-18 mischa return 1;
335 07212170 2022-07-18 mischa }
336 07212170 2022-07-18 mischa
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;
342 07212170 2022-07-18 mischa }
343 07212170 2022-07-18 mischa return not $ret;
344 07212170 2022-07-18 mischa }
345 07212170 2022-07-18 mischa
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;
355 07212170 2022-07-18 mischa }
356 07212170 2022-07-18 mischa