#!/usr/local/bin/perl
# update-dns.pl
# Generates a bootptab from the information in the HINFO fields
# within a DNS database file and a header stub. Also generates
# all appropriate in-addr.arpa files.
#
# Expected input:
# Path to a domain file (d|domain)
# Path to an ISC-formatted bootptab file (i|isc)
# Path to a CMU-formatted bootptab file (c|cmu)
# Path to the bootptab file we'll install
# CMU: (oc|install-cmu)
# ISC: (oi|install-isc)
#
# Expected output:
# Updated bootptab files (same selection as fed as input)
# An ISC- or a CMU-formatted bootptab installed
# All appropriate in-addr.arpa files
# A list of all free addresses within known ranges
#
#
# 05/03/2000 Version 1.4.2a
# Use and distribute this script as per the Artistic License
# Copyright (C) 2001 Igor S. Livshits <mailto:igorl@ayradyss.org>
# Define some global variables
#
$true= (1==1);
$false= (1==0);
$commentDelimiter= "#";
$dnsCommentDelimiter= ";";
$directoryDelimiter= "\/";
$nodeDelimiter= "\.";
$nodeDelimiterMatch= '\.';
$newLine= "\n";
$tab= "\t";
$dnsFieldDelimiter= $tab;
$dnsInformationField= "HINFO";
$space= " ";
$psCommand= "ps -eaf -o pid,user,time,stime,comm |";
$psCommandLinux= "ps ax |";
$iscDHCPConfFile= "/etc/dhcpd.conf";
$iscEthernetAddressDelimiter= ":";
$iscOptionTag= "option";
$iscRoutersTag= "routers";
$iscDomainTag= "domain-name";
$iscMaskTag= "subnet-mask";
$iscListDelimiter= ",";
$iscListTerminator= ";";
$bootptabFile= "/etc/bootptab";
$newFileSuffix= ".new"; # suffix for the generated files
$oldFileSuffix= ".old"; # suffix for preserved copies
$domainFilePrefix= "db."; # prefix for in-addr.arpa tables
$specialSuffix= ".special"; # for special entries
$spareAddressesSuffix= ".spares"; # for all spare addresses
$serialNumberFileSuffix= ".serial"; # stores previous serial number
$domainHeaderDelimiter= "; General header end";
$bootpHeaderDelimiter= "# Header end";
$serialNumberComment= "; Serial #";
$idTagOther= "Generated by ";
$idTag= "Generated by update-dns.pl ";
$dnsTag= "; DO NOT DELETE THIS LINE";
$dnsProcess= "named"; # name of our DNS daemon
$dhcpdProcess= "dhcpd"; # name of our DHCP daemon
$restartSignal= 'HUP'; # a kill signal to restart a process
$terminateSignal= 'TERM'; # a kill signal to terminate a process
$originARPARoot= ".in-addr.arpa.";
$originTag= "\$ORIGIN ";
$originTagMatch= '\$ORIGIN ';
$authorityTag= "IN\tSOA";
$ethernetAddressLength= 12; # Ethernet addresses are 12 bytes long
$ethernetAddressIndex= 4; # The last position of the HINFO line
# Define libraries and modules
#
use Getopt::Long; # command line options processor
# Initialize
#
GetOptions("d|domain=s" => \$domainFile,
"i|isc=s" => \$iscFile,
"c|cmu=s" => \$cmuFile,
"oc|install-cmu" => \$installCMU,
"oi|install-isc" => \$installISC
);
&Initialize();
umask(0177);
# Generate our data
#
$newDNS= &Generate();
# Activate new data
#
&Activate();
# Terminate gracefully
#
exit;
#
# Subroutines
#
# Initialize
#
# Learn setting and configure defaults
#
sub Initialize
{
my($lackingInput)= $false; # input validity flag
$lackingInput= $true, print "You did not specify a domain file", $newLine
unless $domainFile;
$lackingInput= $true, print "You did not specify any bootptab files",
$newLine
unless ($iscFile or $cmuFile);
$lackingInput= $true,
print "Cannot install or send an ISC dhcpd.conf file without a template",
$newLine
if ($installISC and !$iscFile);
$lackingInput= $true,
print "Cannot install or send a CMU bootptab file without a template",
$newLine
if ($installCMU and !$cmuFile);
if ($lackingInput)
{ # exit with a usage message
print $newLine, "Usage:", $newLine,
"update-dns.pl -d path/to/domain/file -i path/to/isc-dhcpd.conf -oi",
$newLine,
"update-dns.pl -d path/to/domain/file -c path/to/bootptab -oc",
$newLine;
exit;
}
if ($ENV{OSTYPE} eq "linux") # tested under Red Hat 6.x
{ # try using a Linux-flavored ps argument list
$psCommand= $psCommandLinux;
}
}
# Generate()
# Cycles through the DNS entries, generating BootP entries, as appropriate
#
sub Generate
{
local %subnets; # an array of our subnets and their tags
local %numbers= (); # an array of all IP numbers
my(%addresses)= (); # an array of all ethernet addresses
my(%names)= (); # an array of all IP names
my($address)= 0; # temporary buckets for DNS info
my($name, $formerNumber, $multiHomedName, $dummy, $number, $subnet);
my($serialNumber, $oldSerialNumber, $validSerialNumber);
my(@rest); # temporary storage
# Prepare the ISC dhcpd.conf file if one exists
# process subnet information only if we do not have a bootptab file
%subnets= &SetupDHCP(!$cmuFile) if $iscFile;
# Prepare and process the bootptab file if one exists
%subnets= &SetupBootP() if $cmuFile;
open(DNS, $domainFile) or die "Could not access $domainFile!$newLine";
print $newLine, "Examining $domainFile...$newLine";
open(BootPTab, ">>$cmuFile$newFileSuffix") # bootptab
or die "Could not access $cmuFile$newFileSuffix!$newLine"
if $cmuFile;
open(DHCPdConf, ">>$iscFile$newFileSuffix") # dhcpd.conf
or die "Could not access $iscFile$newFileSuffix!$newLine"
if $iscFile;
print BootPTab "$commentDelimiter$newLine" # just a separator
if $cmuFile;
print DHCPdConf "$commentDelimiter$newLine" # just a separator
if $iscFile;
while (<DNS>)
{ # fast forward to useful info
if (/$serialNumberComment$/)
{ # but remember to grab the serial number
($serialNumber, @rest)= split();
}
last if (/^$dnsTag/);
}
$validSerialNumber= &ValidateSerialNumber($serialNumber);
while (<DNS>)
{
next if (/^$dnsCommentDelimiter/); # skip comments
if (/$dnsInformationField/)
{ # grab the name and the ethernet address
($name, @rest)= split(); # the name is always first
if ($#rest == $ethernetAddressIndex)
{ # grab the ethernet address
$address= @rest[$#rest];
if ((length($address) != $ethernetAddressLength)
|| ($address=~ /[^a-fA-F0-9]/))
{
print $tab,
"Malformed ethernet address [$address] blasted.", $newLine;
$address= "";
}
}
else { $address= ""; }
unless ($name=~ /\w/)
{ # have to have at least one alphanumeric
print $tab, "Malformed IP name [$name] blasted.", $newLine;
$name= "";
}
}
if (/IN\s+A\s+/)
{ # grab the IP number
if (/^\s/)
{ # name preserved from a previous line
($dummy, $dummy, $number, @rest)= split();
}
else
{ # ignore the redundant (multi-homed?) name
($multiHomedName, $dummy, $dummy, $number, @rest)= split();
$multiHomedName= "" # have to have at least one alphanumeric
unless ($multiHomedName=~ /\w/);
}
if ($name)
{ # we have both a name and a number
if ($numbers{$number})
{ # a new number, add its name to our list
print $tab,
"Duplicate IP number [$number] assigned to [$name];", $newLine,
$tab, $tab, "preserving original name [$numbers{$number}]...",
$newLine;
}
else
{
$numbers{$number}= $name;
if ($names{$name})
{ # check for multi-home entries and report them
print $tab, "[$name] previously used for [$names{$name}]",
" (multi-homed?).", $newLine;
}
else
{ # remember this new name
$names{$name}= $number;
}
undef $name; # clear for silence on multi-homed entries
}
}
elsif ($multiHomedName)
{
if ($names{$multiHomedName})
{ # check for multi-home entries and report them
print $tab, "[$multiHomedName] previously used for ",
"[$names{$multiHomedName}] (multi-homed?).", $newLine;
}
else
{ # remember this new name
$names{$multiHomedName}= $number;
}
undef $multiHomedName; # ignore silently for now
}
else
{
print $tab, "No name defined for [$number]!", $newLine;
}
if ($address)
{ # we have all the info, make a bootptab entry
$number=~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
$subnet= "$1.$2.$3";
if ($addresses{$address})
{ # preserve original entry, but warn
print $tab, "Ethernet address [$address] used again for ";
print "$numbers{$number} " if $numbers{$number};
print "[$number];", $newLine, $tab, $tab,
"[$address] is already assigned to $addresses{$address}";
foreach $formerNumber (keys(%numbers))
{ # reverse lookup the IP number from a name
if ($numbers{$formerNumber} eq $addresses{$address})
{
print " [$formerNumber]";
last;
}
}
print ".", $newLine;
}
else
{ # a brand new entry
$addresses{$address}= $numbers{$number};
if ($cmuFile)
{ # a bootptab (CMU) compliant entry
print BootPTab "$numbers{$number}:",
$tab, "ip=$number:",
$tab, "tc=$subnets{$subnet}:",
$tab, "ha=$address:", $newLine;
}
if ($iscFile)
{ # a dhcpd.conf (ISC) compliant entry
$address=~ # insert delimiters into the address
/(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)/;
$address= # we've previously sanity-checked the address
join($iscEthernetAddressDelimiter, $1, $2, $3, $4, $5, $6);
print DHCPdConf "host $numbers{$number}", $newLine,
"{", $newLine,
$tab, "hardware ethernet $address;", $newLine,
$tab, "fixed-address $number;", $newLine,
"}", $newLine;
}
undef $address; # invalidate for next iteration
}
}
}
}
close(BootPTab) if $cmuFile;
close(DHCPdConf) if $iscFile;
close(DNS);
# Preserve a copy of our originals
# and replace the originals with new versions
if ($cmuFile)
{
rename($cmuFile, $cmuFile.$oldFileSuffix)
or die "Could not preserve $cmuFile: $!";
rename($cmuFile.$newFileSuffix, $cmuFile)
or die "Could not rename $cmuFile$newFileSuffix: $!$newLine",
"But continuing with the rest...$newLine";
}
if ($iscFile)
{
rename("$iscFile", "$iscFile$oldFileSuffix")
or die "Could not preserve $iscFile: $!";
rename("$iscFile$newFileSuffix", "$iscFile")
or die "Could not rename $iscFile$newFileSuffix: $!$newLine",
"But continuing with the rest...$newLine";
}
&GenerateARPA($domainFile, %numbers, %subnets);
return $validSerialNumber;
}
# ValidateSerialNumber($filePath, $currentSerialNumber)
# Reads in the previous serial number for a given domain
#
sub ValidateSerialNumber
{
my($currentSerialNumber)= shift;
my($previousSerialNumber);
return $false unless $currentSerialNumber;
print "Current serial number: ", $currentSerialNumber;
if ($currentSerialNumber=~ /\D/)
{ # must be an integer!
print " *invalid*", $newLine;
return $false;
}
else { print $newLine; }
if (-e $domainFile.$serialNumberFileSuffix)
{
open(Serial, $domainFile.$serialNumberFileSuffix)
or die "Could not access $domainFile$serialNumberFileSuffix$newLine";
$previousSerialNumber= <Serial>; # read in the previous serial number
close(Serial);
}
if ($previousSerialNumber)
{
print "Previous serial number: ", $previousSerialNumber, $newLine;
$previousSerialNumber= 0 if $previousSerialNumber=~ /\D/;
}
else { $previousSerialNumber= 0; }
open(Serial, ">$domainFile$serialNumberFileSuffix")
or die "Could not access $domainFile$serialNumberFileSuffix$newLine";
print Serial $currentSerialNumber; # preserve current serial number
close(Serial);
return ($previousSerialNumber < $currentSerialNumber);
}
# SetupDHCP()
# Reads the header of the current dhcpd.conf seeking subnet information
# Also preserves the header in the generated copy
#
# Format compatible with ISC DHCPd
#
sub SetupDHCP
{
my($parseSubnets)= shift; # should I parse subnet data or just preserve
my($filePath)= $iscFile;
my(%subnets)= (); # an array of legitimate subnets
my($domain, $mask, $gateway, $subnet, $byte);
my($one, $two, $three, $four, $count);
my($second, $minute, $hour, $day, $month, $year);
open(DHCPdConfIn, $filePath) or die "Could not access $filePath!$newLine";
print $newLine, "Examining $filePath...$newLine" if $parseSubnets;
open(DHCPdConfOut, ">$filePath$newFileSuffix")
or die "Could not access $filePath$newFileSuffix!$newLine";
($second, $minute, $hour, $day, $month, $year)= localtime();
$month++; # correct for 0 based count
# and pad single digits
$minute= "0".$minute if ($minute < 10);
$second= "0".$second if ($second < 10);
$year+= 1900; # add centuries
print DHCPdConfOut "# $idTag"; # stamp with our tag
print DHCPdConfOut "$hour:$minute:$second $month/$day/$year$newLine";
while (<DHCPdConfIn>)
{
next if /^$commentDelimiter $idTag/; # skip previous tag line
print DHCPdConfOut;
last if /^$bootpHeaderDelimiter/; # end of useful info
if ($parseSubnets)
{ # find information about subnets
$mask= $1
if /^\s+$iscOptionTag\s+$iscMaskTag\s+(\d+\.\d+\.\d+\.\d+)/;
$gateway= $1
if /^\s+$iscOptionTag\s+$iscRoutersTag\s+(\d+\.\d+\.\d+\.\d+)/;
$domain= $1
if /^\s+$iscOptionTag\s+$iscDomainTag\s+\"(.+)\"/;
}
}
close(DHCPdConfIn);
close(DHCPdConfOut);
if ($parseSubnets)
{
$domain= $true unless $domain;
if ($mask and $gateway)
{ # we have all the info to compute our subnets
($one, $two, $three, $four)= split(/\./, $mask);
$gateway=~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
if ($four)
{ # a partial subnet
$subnet= "$1.$2.$3";
if ($subnets{$subnet})
{
print $tab, "Duplicate subnet found! [$subnet]", $newLine,
$tab, $tab, "Not replacing...", $newLine;
}
else
{
$subnets{$subnet}= $domain;
print $tab, "$domain; $subnet", $newLine;
}
}
else
{
for ($count= $three, $byte= $3; $count < 256; $count++, $byte++)
{
$subnet= "$1.$2.$byte";
if ($subnets{$subnet})
{
print $tab, "Duplicate subnet found! [$subnet]", $newLine,
$tab, $tab, "Not replacing...", $newLine;
}
else
{
$subnets{$subnet}= $domain;
print $tab, "$domain; $subnet", $newLine;
}
}
}
}
else
{
print $tab, "Mask definition missing in <$filePath>.", $newLine
unless $mask;
print $tab, "Router definition missing in <$filePath>.", $newLine
unless $gateway;
}
}
return %subnets;
}
# SetupBootP()
# Reads the header of the current bootptab seeking subnet information
# Also preserves the header in the generated copy
#
# Format compatible with BootP and CMU DHCPd
#
sub SetupBootP
{
my($filePath)= $cmuFile;
my(%subnets)= (); # an array of legitimate subnets
my($token, $mask, $gateway, $subnet, $byte, $field);
my($one, $two, $three, $four, $count);
my($second, $minute, $hour, $day, $month, $year);
open(BootPTabIn, $filePath) or die "Could not access $filePath!$newLine";
print $newLine, "Examining $filePath...$newLine";
open(BootPTabOut, ">$filePath$newFileSuffix")
or die "Could not access $filePath$newFileSuffix!$newLine";
($second, $minute, $hour, $day, $month, $year)= localtime();
$month++; # correct for 0 based count
# and pad single digits
$minute= "0".$minute if ($minute < 10);
$second= "0".$second if ($second < 10);
$year+= 1900; # add centuries
print BootPTabOut "# $idTag"; # stamp with our tag
print BootPTabOut "$hour:$minute:$second $month/$day/$year$newLine";
while (<BootPTabIn>)
{
if (/^$commentDelimiter/ or /^$newLine/)
{ # preserve comments and blank lines
next if (/^$commentDelimiter $idTag/); # skip previous tag line
print BootPTabOut;
last if (/^$bootpHeaderDelimiter/); # end of useful info
next;
}
if (/^\.\w+/)
{ # found a template definition
print BootPTabOut;
($token)= split(":");
}
if (/^\s+:/)
{ # found a template field or several
print BootPTabOut;
foreach $field (split(":"))
{ # examine each field, looking for the subnet
if ($field=~ /^sm=/)
{ # excise the subnet mask IP
($field, $mask)= split("=", $field);
}
if ($field=~ /^gw=/)
{ # excise the gateway IP
($field, $gateway)= split("=", $field);
if ($mask and $gateway)
{
($one, $two, $three, $four)= split(/\./, $mask);
$gateway=~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/;
if ($four)
{ # a partial subnet
$subnet= "$1.$2.$3";
if ($subnets{$subnet})
{
print $tab, "Duplicate subnet found! [$subnet]", $newLine,
$tab, $tab, "Not replacing...", $newLine;
}
else
{
$subnets{$subnet}= $token;
print $tab, "$token; $subnet", $newLine;
}
}
else
{
for ($count= $three, $byte= $3; $count < 256; $count++, $byte++)
{
$subnet= "$1.$2.$byte";
if ($subnets{$subnet})
{
print $tab, "Duplicate subnet found! [$subnet]", $newLine,
$tab, $tab, "Not replacing...", $newLine;
}
else
{
$subnets{$subnet}= $token;
print $tab, "$token; $subnet", $newLine;
}
}
}
}
else
{
print $newLine, $tab, "Mask missing in $token's definition.",
unless $mask;
print $newLine, $tab, "Router missing in $token's definition.",
unless $gateway;
}
}
}
}
}
close(BootPTabOut);
close(BootPTabIn);
return %subnets;
}
# GenerateARPA($numbers, $subnets)
# Reads the header of the current domain file seeking standard information
# and creates appropriate in-addr.arpa files with the same header
#
# $numbers is the array of all IP name and IP address pairs
# $subnets is the array of all valid subnets
#
sub GenerateARPA
{
my($numbers)= shift;
my($subnets)= shift;
my(@dnsHeader)= (); # standard DNS header
my($dnsDirectoryPath); # where all the DNS files live
my(%aSubnet); # an array of values for a given subnet
my($dummy, $dirty, $domain, $subnet, $number, $name);
my($line, $domainOrigin, $subnetOrigin, $subnetNumber);
my(@subnetLeaves, @rest, @spares, @extraLines);
# Read in the standard header
open(DNS, $domainFile) or die "Could not access $domainFile!$newLine";
print $newLine, "Getting DNS header from $domainFile...$newLine";
while (<DNS>)
{ # gather the standard header
next if (/$idTagOther/); # skip other auto-generation stamps
push(@dnsHeader, $_);
last if (/^$domainHeaderDelimiter/);
}
close(DNS);
$domain= shift @dnsHeader; # grab the domain name
chop($domain);
$domain=~ s/^$dnsCommentDelimiter $domainFilePrefix//;
# Generate the DNS directory path
$dummy= $domainFile;
while ($dummy=~ s/(.+\/)//)
{ # accummulate directory leaves
$dnsDirectoryPath.= $1;
}
print $newLine,
"Comparing current in-addr.arpa files to $domainFile...", $newLine;
foreach $subnet (sort keys(%subnets))
{
undef %aSubnet; # clear before accumulating a subnet
$dirty= $false; # assume no changes
@extraLines= (); # clear any previosuly accumulated lines
if (-e $dnsDirectoryPath.$domainFilePrefix.$subnet.$specialSuffix)
{ # we have special entries, add them
open(Special, $dnsDirectoryPath.$domainFilePrefix.$subnet.$specialSuffix)
or die "Could not access "
. $dnsDirectoryPath.$domainFilePrefix.$subnet.$specialSuffix
. "!$newLine";
while(<Special>)
{
next if /^$newLine/; # skip blank lines
next if /^$dnsCommentDelimiter/; # skip comments
if (/\s+PTR\s+/)
{ # only notice PTR lines
($node, $dummy, $dummy, $name)= split;
if ($numbers{"$subnet.$node"})
{ # duplicate node assignment
print $tab, "Overwriting ", $numbers{"$subnet.$node"},
" with reserved $name [$subnet.$node]!", $newLine;
}
$numbers{"$subnet.$node"}= $name;
next; # skip to next line
}
push(@extraLines, $_); # accumulate extra lines for this subnet
}
close(Special);
}
if (open(Subnet, $dnsDirectoryPath.$domainFilePrefix.$subnet))
{ # compare to previous data
while(<Subnet>)
{
if (/\s+PTR\s+/)
{ # only notice PTR lines
($number, $dummy, $dummy, $name)= split;
($name)= split(/\./, $name) if (/\.$domain\.$/);
$aSubnet{$number}= $name;
}
}
close(Subnet);
}
foreach $node (0..255)
{
if ($aSubnet{$node} ne $numbers{"$subnet.$node"})
{
$aSubnet{$node}= $numbers{"$subnet.$node"};
$dirty= $true; # mark this subnet file for generation
last;
}
}
if ($dirty)
{ # generate a new version
undef @spares; # clear our list of spare addresses
open(Subnet, ">$dnsDirectoryPath$domainFilePrefix$subnet$newFileSuffix")
or die "Could not access "
. "$dnsDirectoryPath$domainFilePrefix$subnet$newFileSuffix!$newLine";
print $tab, "Generating $dnsDirectoryPath$domainFilePrefix$subnet...",
$newLine;
# stamp with our tag
print Subnet "$dnsCommentDelimiter $idTag";
($second, $minute, $hour, $day, $month, $year)= localtime(time());
$month++; # correct for 0 based count
# and pad single digits
$minute= "0".$minute if ($minute < 10);
$second= "0".$second if ($second < 10);
$year+= 1900; # add centuries
print Subnet "$hour:$minute:$second $month/$day/$year$newLine";
print Subnet $dnsCommentDelimiter, " ",
$domainFilePrefix, $domain, $newLine;
# Fix $ORIGIN tags and prepend the standard header
@subnetLeaves= split (/$nodeDelimiterMatch/, $subnet);
@subnetLeaves= reverse(@subnetLeaves);
$subnetOrigin= join($nodeDelimiter, @subnetLeaves);
$subnetNumber= shift(@subnetLeaves);
$domainOrigin= join($nodeDelimiter, @subnetLeaves);
foreach $line (@dnsHeader)
{
if ($line=~ /^$originTagMatch/)
{ # assign correct domain name
print Subnet
$originTag, $domainOrigin, $originARPARoot, $newLine;
}
elsif ($line=~ /$authorityTag/)
{ # assign correct subnet number
$line=~ s/\w+/$subnetNumber/;
print Subnet $line;
}
else
{ # simply preserve this line
print Subnet $line;
}
}
print Subnet
$originTag, $subnetOrigin, $originARPARoot, $newLine;
print Subnet $newLine, @extraLines;
foreach $node (0..255)
{
if ($name= $numbers{"$subnet.$node"})
{
# fully qualify the name unless already so
$name.= ".$domain." unless ($name=~ /\.$/);
print Subnet $newLine, "$node\tIN\tPTR\t$name";
}
else
{
push (@spares, $node.$newLine);
}
}
close(Subnet);
# preserve a copy and replace the original with the new version
rename("$dnsDirectoryPath$domainFilePrefix$subnet",
"$dnsDirectoryPath$domainFilePrefix$subnet$oldFileSuffix")
or print $tab, "Could not rename ",
"$dnsDirectoryPath$domainFilePrefix$subnet: $!",
$newLine, $tab, $tab, "But continuing with the rest...", $newLine;
rename("$dnsDirectoryPath$domainFilePrefix$subnet$newFileSuffix",
"$dnsDirectoryPath$domainFilePrefix$subnet")
or print $tab, "Could not rename ",
"$dnsDirectoryPath$domainFilePrefix$subnet$newFileSuffix: $!",
$newLine, $tab, $tab, "But continuing with the rest...", $newLine;
# update our list of spare addresses
if (@spares)
{
open(Spares,
">$dnsDirectoryPath$domainFilePrefix$subnet$spareAddressesSuffix")
or die "Could not access "
. "$dnsDirectoryPath$domainFilePrefix$subnet$spareAddressesSuffix!"
. $newLine;
print $tab, "Listing $subnet spares...", $newLine;
# stamp with our tag
print Spares "$dnsCommentDelimiter $idTag";
($second, $minute, $hour, $day, $month, $year)= localtime(time());
$month++; # correct for 0 based count
# and pad single digits
$minute= "0".$minute if ($minute < 10);
$second= "0".$second if ($second < 10);
$year+= 1900; # add centuries
print Spares "$hour:$minute:$second $month/$day/$year$newLine";
print Spares $dnsCommentDelimiter, " ",
$domainFilePrefix, $domain, $newLine;
print Spares @spares;
close(Spares);
}
else
{
if (-e $dnsDirectoryPath.$domainFilePrefix.$subnet
.$spareAddressesSuffix)
{
system("rm ".$dnsDirectoryPath.$domainFilePrefix.$subnet
.$spareAddressesSuffix) or "Could not clear old spares.";
}
}
}
}
}
# Activate()
# Replace current files with new data and restart daemons as necessary
#
sub Activate
{
my($result); # an error condition
&SignalProcess($dnsProcess, $restartSignal)
if $newDNS; # restart the DNS server
if ($installCMU)
{ # update the bootptab
$result= system("cp $cmuFile $bootptabFile");
print $newLine,
"Failed to replace <$bootptabFile>.", $newLine
if $result;
# CMU DHCP server should notice the new file automatically
}
if ($installISC)
{ # update the dhcpd.conf
$result= system("cp $iscFile $iscDHCPConfFile");
print $newLine,
"Failed to replace <$iscDHCPConfFile>.", $newLine
if $result;
# First terminate and then start the DHCP daemon
# as the ISC DHCP server cannot handle a restart
&SignalProcess($dhcpdProcess, $terminateSignal);
$result= system("$dhcpdProcess -q");
print $newLine,
"Failed to start <$dhcpdProcess>.", $newLine
if $result;
}
}
# SignalProcess($process, $signal)
# Finds a designated process and sends it a signal
#
sub SignalProcess
{
my($process)= shift; # the name of our target process
my($signal)= shift; # the signal to send to said process
my($candidatePID); # id of a running DNS process
my(@candidates)= (); # a list of possible current DNS processes
my($result); # result of the restart signal
open(Processes, $psCommand)
or die "Cannot gather a list of current processes.";
while (<Processes>)
{
push(@candidates, $_) if (/\b$process\b/);
}
close(Processes);
if (@candidates)
{
die "Multiple instances of $process are running!" if (@candidates > 1);
$candidates[0]=~ s/^$space+//; # kill leading spaces
($candidatePID)= split($space, $candidates[0]);
$result= kill $signal, $candidatePID;
print $newLine,
"$process [$candidatePID] successfully signaled ($signal).", $newLine
if ($result);
}
else
{
print $newLine, "$process is not running!$newLine";
exit;
}
}
# A helper for the sort function
#
sub numerically { $a <=> $b; }