#!/usr/bin/perl
#
# This script is a component of Warewulf,
# http://www.runlevelzero.net/greg/warewulf
#
#########################################################################
#
# Copyright (c) 2003, The Regents of the University of California, through
# Lawrence Berkeley National Laboratory (subject to receipt of any
# required approvals from the U.S. Dept. of Energy).  All rights reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# The GNU GPL Document can be found at:
# http://www.gnu.org/copyleft/gpl.html
#
#########################################################################
#
# Written and maintained by:
#       Greg Kurtzer, <gmkurtzer@lbl.gov>

use lib "/usr/lib/warewulf/", "/usr/lib64/warewulf/";
use Warewulf::Util;
use Warewulf::Config;
use Warewulf::PXE;
use Getopt::Long;
use File::Path;
use IO::Socket;
use Net::hostent;
use Unix::Syslog qw(:macros);
use Unix::Syslog qw(:subs);


#use strict;

my %nodes;
my (
   $help,
   $dump,
   $dhcpd_template,
   $vnfs,
   $default,
   $debug,
);

my $usage = "USAGE: $0 [options]
  About:
    wwnewd is used to check for new nodes and configure them as they are
    found. If you wish for new nodes to be added to a particular group
    by default, create a link to the group directory that they should be
    added to and call it /etc/warewulf/nodes/default.

  Options:
    --help          Show this banner

  This tool is part of the Warewulf cluster distribution
     http://warewulf-cluster.org/
";

GetOptions(
   'help'      => \$help,
   'debug'     => \$debug
);

unless ( $debug ) {
   if ( -f '/var/run/wwnewd.pid' ) {
      print "Warewulf new node daemon already running!\n";
      open(PID, "/var/run/wwnewd.pid");
      $pid = <PID>;
      close PID;
      print "PID=$pid\n";
      exit 1;
   }
   open(STDIN, "/dev/null");
   open(STDOUT, ">/dev/null");
   open(STDERR, ">/dev/null");
   fork and exit;
}

$pid = $$;
open(PID, '> /var/run/wwnewd.pid')
   or warn "Could not create PID file at: '/var/run/wwnewd.pid'\n";
print PID $pid;
close PID;

$SIG{HUP} = sub {
   return('1');
};
$SIG{TERM} = sub {
   unlink('/var/run/wwnewd.pid');
   syslog(LOG_INFO, "wwnewd stopping on SIGTERM...");
   closelog;
   exit 1;
};
$SIG{INT} = sub {
   warn "-Cought Signal Interrupt, exiting...\n";
   unlink('/var/run/wwnewd.pid');
   syslog(LOG_INFO, "wwnewd stopping on SIGINT...");
   closelog;
   exit 1;
};

openlog("wwnewd", LOG_PID,  LOG_LOCAL7);
syslog(LOG_INFO, "wwnewd starting...");

%master = &master_config;

mkpath("/etc/warewulf/nodes/new");

$PORT = 9875;

$server = IO::Socket::INET->new( Proto     => 'tcp',
                                 LocalPort => $PORT,
                                 Listen    => SOMAXCONN,
                                 Reuse     => 1);

die "can't setup server" unless $server;

while (1) {
   while ($client = $server->accept()) {
      %nodes = &node_config();
      $count = 0;
      foreach $node ( keys %nodes ) {
         if ( $nodes{$node}{"hardware addr"} ) {
            $knownmac{"$nodes{$node}{'hardware addr'}"} = $node;
         }
         if ( $nodes{$node}{"hardware addr"} ) {
            $knownmac{"$nodes{$node}{'hardware addr2'}"} = $node;
         }
         $node =~ /^\D*(\d+)$/;
         $count = sprintf("%d", $1) if ( $1 > $count );
         $notzero = '1';
      }
      if ( $notzero ) {
         $count++;
      }
#      $count = keys %nodes;
      $admin_ip = &node_addr($count, $master{"network"}{"admin ipaddr"}, $master{"network"}{"admin netmask"});
      $cluster_ip = &node_addr($count, $master{"network"}{"cluster ipaddr"}, $master{"network"}{"cluster netmask"});
      $sharedfs_ip = &node_addr($count, $master{"network"}{"sharedfs ipaddr"}, $master{"network"}{"sharedfs netmask"});
      $nodename = sprintf("%s%$master{'nodedefaults'}{'suffix format'}", 
                          $master{'nodedefaults'}{'node prefix'}, $count);
      $client->autoflush(1);
      while ( <$client>) {
         chomp;
         if ( $_ =~ /^GET \/(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w) HTTP.+/ ) {
            $mac_addr = lc($1);
            $mac_addr =~ s/-/:/g;
            if ( $knownmac{"$mac_addr"} ) {
               $debug and warn "$mac_addr already known... ignoring\n";
               print $client "HTTP/1.0 200 OK\n";
               print $client "\nALREADYKNOWN\n";
               syslog(LOG_INFO, "Ignoring $mac_addr because it is already configured");
               last;
            } else {
               $debug and warn "$mac_addr found... adding as '$nodename'\n";
               if ( -l "/etc/warewulf/nodes/default" ) {
                  $def_group = "default";
               } else {
                  $def_group = "new";
               }
               $node_template = $node_config_template;
               $node_template =~ s/#hwaddr#/$mac_addr/g;
               $node_template =~ s/#adminip#/$admin_ip/g;
               $node_template =~ s/#clusterip#/$cluster_ip/g;
               $node_template =~ s/#sharedfsip#/$sharedfs_ip/g;
               open(NODECONF, "> /etc/warewulf/nodes/$def_group/$nodename");
               print NODECONF $node_template;
               close NODECONF;
               print $client "HTTP/1.0 200 OK\n";
               print $client "\nSUCCESSFUL\n";
               syslog(LOG_INFO, "$nodename ($mac_addr) has been added at /etc/warewulf/nodes/$def_group/$nodename");
               if ( $def_group eq "default" ) {
                  syslog(LOG_INFO, "Reconfiguring /etc/hosts and PXE config");
                  &pxe_update;
                  &write_hosts;
               }
               last;
            }
         } else {
            $debug and warn "$_ error... bad get!\n";
            print $client "HTTP/1.0 200 OK\n";
            print $client "\nBAD GET\n";
            syslog(LOG_INFO, "Bad node configuration request ($_)");
            last;
         }
      }
      close $client;
   }
}

syslog(LOG_INFO, "wwnewd stopping...");
closelog;
