#!/usr/bin/perl
#
# Author: Blake, Kuo-Lien Huang
# License: GPL
# Description:
#   2004/05/10 the first version
#
# Reference: 
#  perldoc IO::Select
#  perldoc IO::Socket:INET
#

use strict;
use IO::Select;
use IO::Socket;

$|++;

our %config = (
  port => 6463,
  log => 1,
  logfile => '/var/log/instmgrd.log',
  tftpboot => '/tftpboot',
  dhcpd_conf => '/etc/dhcp3-server/dhcpd.conf'
);

&start_server(%config);

sub log($) {
  my $line = shift;
  return unless $config{log} and $config{logfile};
  open(LOG, ">> $config{logfile}") || die $!;
  my $now_string = localtime;
  print LOG $now_string, ":", $line, "\n";
  close(LOG);
}

sub start_server(%) {
  my %config = @_;
  die unless $config{port} > 0;
  my $lsn = new IO::Socket::INET(Listen => 1, LocalPort => $config{port}, Reuse => 1);
  my $sel = new IO::Select($lsn);

  &log("server started");
  $SIG{CHLD}='IGNORE';

  while(my @ready = $sel->can_read) {
    foreach my $fh (@ready) {
      my $new;
      if($fh == $lsn) {
        # create a new socket
        $new = $lsn->accept;
        $sel->add($new);
      }
      else {
        # process socket
        if(my $pid = fork()) {
          # parent: close the connection so we can keep listening
          $sel->remove($fh);
          $fh->close();
        }
        else {
          # child: deal with the connection
          my $validated="pass";
          my $peeraddr = $fh->getline;
          chomp($peeraddr);
          &log("connection opened: ".$fh->peerhost());
		  
#          # check if the connection is validated
#          if( $fh->peerhost() =~ /^$peeraddr$/) {
#             # check /etc/hosts
#             my $hostname="";
#             open(HOSTS,"/etc/hosts");
#             while(<HOSTS>) {
#               my @line = split(' ',$_,3);
#               if($line[0]=~/^$ip$/) { $hostname=$line[2]; chomp($hostname); last; }
#             }
#             close(HOSTS);
#             if($hostname=~/^$/) { $validated="fail"; }
#
#			 # check dhcpd.conf
#             my $dhcpconf="";
#             my @ip=split(/\./,$peeraddr);
#             my $subnet=$ip[0].".".$ip[1].".".$ip[2].".0";
#             my $rHoH=&read_dhcpd($config{dhcpd_conf});
#             my @acls=split(' ',$rHoH->{$subnet}{'acl'});
#             foreach my $acl (@acls) {
#               if( $peeraddr =~ /^$acl$/ ) {
#			     $dhcpconf="$acl";
#			     last;
#               }
#             }
#             if($dhcpconf=~/^$/) { $validated="fail"; }
#          }
#          else {
#            $validated="fail";
#          }
		  
          # create PXE config file in $config{tftpboot}/pxelinux.cfg/ 
          if( $validated =~ /^pass$/ ) {
            my $PXECFGFN=`/usr/bin/gethostip $peeraddr | cut -d" " -f3`;
            chomp($PXECFGFN);
            open(PXECFG,">".$config{tftpboot}."/pxelinux.cfg/".$PXECFGFN);
            print PXECFG q{
default local

label local
  localboot 0
};
            close(PXECFG);
          }
          &log("connection closed: ".$fh->peerhost());
          $fh->close();
        }
      }
    }
  }
}

sub read_dhcpd($) {
  my $dhcpd_conf = shift;
  my %HoH = ();
  my $KoH = "";
  my $key = "";
  my $value = "";

  open(CONFIG,$dhcpd_conf);
  while(<CONFIG>) {
    my $tmp;
	my @line;
    if( $_ =~ /subnet/ && $_ =~ /netmask/ ) {
	  @line=split(' ');
	  $KoH=$line[1];
	} 
	elsif( $_ =~ /next-server/ ) {
	  $key = 'routers';
	  s/;//g;
	  @line = split(' ');
	  $value = $line[1];
	  $HoH{ $KoH }{ $key } = $value;
	}
	elsif( $_ =~ /fixed-address/ ) {
	  $key = 'acl';
	  s/;//g;
	  @line = split(' ');
	  $value = $line[1];
	  if( $HoH{ $KoH }{ $key } =~ /^$/ ) {
	    $HoH{ $KoH }{ $key } = $value;
	  }
	  else {
	    $HoH{ $KoH }{ $key } .= " $value";
	  }
	}
	elsif( $_ =~ /range/ ) {
	  $key = 'acl';
	  $value = '';
	  s/;//g;
	  my @subnet = split(/\./,$KoH);
	  my $range_start;
	  my @line = split(' ');
	  my $range_start = $line[1];
	  my $range_end = $line[2];
	  @line = split(/\./,$range_start);
	  my $start=$line[3];
	  @line = split(/\./,$range_end);
	  my $end=$line[3];
	  $value = $range_start;
	  for(my $i=$start+1;$i<=$end;$i++) {
	    $value = $value." ".$subnet[0].".".$subnet[1].".".$subnet[2].".".$i;
	  }
	  $HoH{ $KoH }{ $key } = $value;
	}
  }
  close(CONFIG);
  return( \%HoH );
}
