#! /usr/bin/perl -w use strict; use English; # This copes with the MS-DOS file endings, but not with Unix files. # $/ = "\r\n"; # $/ = "\n"; my $usage = "Usage: $0 inputFiles...\n"; die "$usage" unless @ARGV >= 1; my $debug = 0; my $domainName = "tyict.vtc.edu.hk"; my $ns = "ns"; my $hostfile = "hostfile"; our $orig_hostfile = "/var/named/hosts/hosts"; my $commentfile = "commentfile"; my $notInDomain = "Not in the domain"; # string of subnets in $hosts not to be included. my @secondaries = ( "ns1.tyict.vtc.edu.hk", "dns1.vtc.edu.hk", "dns2.vtc.edu.hk" ); my ( $equipType, $brand, $dataPoint, $location, $os, $ethernetAddr, $ipAddr, $name, ); my ( $fixed, $entry, @five, %rp, # a list of responsible people, indexed by a "domain name" p. 82 %ip, %names, %networks, $hosts, ) = (); # Here we just read the contents of the input file(s) into an array. print "About to read @ARGV...\n" if $debug; while ( <> ) { # chomp; # Better than using chomp with $/ = "\r\n"; can cope with either line format. print; s/\r?\n?$//; ( $location, $name, $ethernetAddr, $equipType, $ipAddr, ) = split "\t", $_, -9999; print <<"DEBUG" if $debug; \$location = $location \$name = $name \$ethernetAddr = $ethernetAddr \$equipType = $equipType \$ipAddr = $ipAddr DEBUG # Skip header: next if $INPUT_LINE_NUMBER == 1; next unless $name; next unless defined $ipAddr; if ( $ipAddr !~ /^(\d+\.){3}\d+$/ ) { $ipAddr = "?.?.?.?"; next; } # strip out leading zeros, put in to help Excel sort IP addresses: $ipAddr =~ s/\.0+([1-9])/.$1/g; $entry = "$name\t$ipAddr\t$equipType\t$location"; push @five, $entry; # Should check whether any duplicates of IP or ethernet address are present.. my ( $canonical, @aliases ) = split( /\s+/, $name ); print STDERR "Duplicate IP address \"$ipAddr\"\n" if ( ++$ip{$ipAddr} > 1 ); print STDERR "Duplicate name \"$name\"\n" if ( ++$names{$canonical} > 1 ); # See under eof in Programming Perl (p. 704 of 3rd edition). # This resets the line numbering, so can check for first line of file. } continue { close ARGV if eof; } open COMMENTFILE, ">$commentfile" or die "Cannot open $commentfile: $!"; open HOSTFILE, ">$hostfile" or die "Cannot open $hostfile for writing: $!"; open ORIG_HF, "< $orig_hostfile" or die "Cannot read $orig_hostfile: $!"; while ( ) { next if /^\s*#/; next if /^\s*$/; print HOSTFILE $_; } my $personDN; while ( scalar @five ) { ( $name, $ipAddr, $equipType, $location, ) = split '\t', shift @five; $name =~ tr/A-Z/a-z/; # translate to lower case $name =~ s/_/-/g; # underscores are not allowed in names. # Leading or trailing hyphens are not permitted. Really should write a # subroutine to completely check names for rfc compliance. $name =~ s/^-+//; $name =~ s/-+$//; my ( $canonical, @aliases ) = split( /\s+/, $name ); print COMMENTFILE "$canonical:IN\tHINFO\t\"$equipType\"\n"; # Let Unix machines use their ability to receive and process their # own mail. #if ( $os =~ /Linux|Solaris/i ) #{ # print COMMENTFILE # "$canonical:\tIN\tMX\t10 $canonical\n" # . "$canonical:\tIN\tMX\t20 is3.vtc.edu.hk.\n"; #} # MakeRP( $person ); # print COMMENTFILE "$canonical:IN\tRP\t\"$personDN.www.$domainName\" \"$personDN\""\n"; print HOSTFILE "$ipAddr\t$canonical.$domainName @aliases \# $canonical\n"; print COMMENTFILE "$canonical:IN\tTXT\t\"Location: $location\"\n"; } close HOSTFILE or die "Can't close $hostfile: $!"; close COMMENTFILE or die "Can't close file $commentfile: $!"; # Now lets find all the networks in the hostsfile: open HOSTFILE, "<$hostfile" or die "Cannot open $hostfile for reading: $!"; %networks = (); while ( ) { next if /^\s*#/; # skip comment lines in hostfile. next if /^$/; # skip empty lines chop; # print; my ( $addr, $names ) = split; # print "Address = $addr; network = "; $addr =~ s/^(\d+\.\d+\.\d+).\d+$/$1/; # print "$addr\n"; next if ( $addr =~ /127.0.0/ or $addr =~ $notInDomain ); ++$networks{$addr}; } close HOSTFILE or die "Can't close $hostfile: $!"; my $h2nCall = "/usr/local/bin/h2n -d $domainName -s $ns.$domainName "; foreach my $network ( keys %networks ) { $h2nCall .= " -n $network"; } foreach my $sec ( @secondaries ) { $h2nCall .= " -s $sec"; } $h2nCall .= " -h $ns -u nicku\@vtc.edu.hk -y -C $commentfile -H $hostfile -M" . " -o 3600:1800:604800:86400 -b named.conf"; print "$h2nCall\n"; system( $h2nCall ) == 0 or die "h2n failed: $?"; # h2n -d tyee.vtc.edu.hk -h www.tyee.vtc.edu.hk -u nicku@vtc.edu.hk -y -C commentfile -H hostfile -M -n 192.168.129 exit; # The RP record contains 2 fields: # 1. email of responsible person, # 2. domain name for a TXT record with name of the RP and other contact info. # The domain name for the TXT record does not have to be the name of a # computer; it can just be made up as a placeholder. See p. 82 of cricket bk. # The second field for the RP record: # Takes one parameter, the full name of a person responsible for a computer. # This function sub MakeRP( $ ) { my $person = shift; $personDN = $person; my $offset = index( $person, " ", 0 ); # Is there a space in the name? if ( $offset == -1 ) { # The full name is one word. if ( $rp{$personDN} && $rp{$personDN} ne $person ) { print STDERR "Oh dear, have two names for $person; also $rp{$personDN}.\n"; } $rp{$person} = $person; return; } my $nameNoSpace = $person; $nameNoSpace =~ s/ //g; # remove all spaces. # Now this loop does something rather strange: it takes the first name # from the responsible person field, and if the hash already has an # entry with a different name under that first name, then it adds letters # from the second name one by one to the end of the first name until # there is a match or there is no hash entry. # It probably makes more sense to allow for typing errors in the # input file. do { $personDN = substr( $nameNoSpace, 0, $offset++ ); } while ( $rp{$personDN} && $rp{$personDN} ne $person ); $rp{$personDN} = $person; }