#!/usr/bin/perl -w

#
# "SystemImager" 
#
#  Copyright (C) 1999-2001 Brian Elliott Finley 
#                          <brian@bgsw.net>
#  Copyright (C) 2002-2003 Bald Guy Software 
#                          <brian@bgsw.net>
#
#  $Id: si_mkdhcpserver,v 1.2 2004/09/29 18:34:09 brianfinley Exp $
#
#  Others who have contributed to this code (in alphabetical order):
#   John Goebel <jgoebel@valinux.com>
#   Curtis Zinzilieta <czinzilieta@valinux.com>
#
#   See http://www.iana.org/assignments/bootp-dhcp-parameters for info on
#   custom option numbers.
#
 
use lib "USR_PREFIX/lib/systemimager/perl";
use strict;
use SystemImager::Common;
use vars qw($VERSION);

# set some variables
$VERSION="SYSTEMIMAGER_VERSION_STRING";
my $program_name="si_mkdhcpserver";
my $get_help = "  Try \"$program_name -help\" for more options.";


my $version_info = <<"EOF";
$program_name (part of SystemImager) version $VERSION

Copyright (C) 2002 Bald Guy Software <brian.finley\@baldguysoftware.com>
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
EOF

# set help information
my $help_info = $version_info . <<"EOF";

Usage: $program_name

Download, report bugs, and make suggestions at:
http://systemimager.org/
EOF

SystemImager::Common->check_if_root();



### BEGIN variable for parts of the dhcpd.conf file ###
my $header = <<'EOF';
#
# "SystemImager" 
#
#  Copyright (C) 1999-2001 Brian Elliott Finley 
#                          <brian.finley@baldguysoftware.com>
#  Copyright (C) 2002 Bald Guy Software 
#                     <brian.finley@baldguysoftware.com>
#
#  This file was created with "si_mkdhcpserver", which is part of SystemImager.
#  See http://systemimager.org/ for more information.
#
EOF


# associated configs for the dhcpd.conf file
my $dhcpd_conf_v3_part1 = <<"EOF";
# This is an ISC DHCP v3 configuration file.

# general options
authoritative;
ddns-update-style none;

EOF


my $dhcpd_conf_v3_part2 = <<"EOF";

# set lease time to infinite (-1)
default-lease-time -1;

# Uncomment one of the two following lines.  The first, if you need to
# boot i386 clients, or the second for booting ia64 clients.
filename "pxelinux.bin";   # i386
#filename "elilo.efi";   # ia64

EOF

#XXX include elilo.efi and elilo.conf in systemimager bundle.  Also include pxelinux.bin, methinks. -BEF-

my $dhcpd_conf_v2_part1 = <<'EOF';
# This is an ISC DHCP v2 configuration file.

# set lease time to infinite (-1)
default-lease-time -1;

# Uncomment one of the two following lines.  The first, if you need to
# boot i386 clients, or the second for booting ia64 clients.
filename "pxelinux.bin";   # i386
#filename "elilo.efi";   # ia64

EOF

### END variables for parts of dhcpd.conf file ###



# if not run as root, this script will surely fail
unless($< == 0) { die "Must be run as root!\n"; }

system("clear");

# give warning
print <<"EOF";
Welcome to the SystemImager "si_mkdhcpserver" command.  This command will
prepare this computer to be a DHCP server by creating a dhcpd.conf file
for use with your ISC DHCP server (v2 or v3).

If there is an existing file, it will be backed up with the 
.beforesystemimager extension.

EOF

print "Continue? (y/[n]): ";
my $continue=<STDIN>;
chomp $continue;
($continue eq "y") or die "\n$program_name No files were modified.\n";

# set some default values
my $dhcpd_conf;
my $dhcpd_version = "2";
my $dhcpd_patch = "n";
my $dnsdomainname = "localdomain.domain";
my $netnumber = "192.168.1.0";
my $netmask = "255.255.255.0";
my $rangebegin = "192.168.1.1";
my $rangeend = "192.168.1.100";
my $dnsserver1 = "";
my $dnsserver2 = "";
my $dnsserver3 = "";
my $log_server = "";
my $log_server_port = "";
my $router = "192.168.1.254";
my $imageserver = "192.168.1.254";
my $boot_server = "";
my $ssh_download_url = "";
my $flamethrower_directory_portbase = "";
my $tmpfs_staging = "n";
my $mcast_install = "n";
my $swallow = "";

# Oh where oh where could my dhcpd.conf file be?
my @dhcpd_conf_file_locations_du_jour = ( 
    "/etc/dhcp3/dhcpd.conf",
    "/etc/dhcpd.conf"
);
foreach my $file (@dhcpd_conf_file_locations_du_jour) {
    # does *this* one exist?
    if( -e $file ) { 
        $dhcpd_conf = $file;
        last;
    }
}

if("$dhcpd_conf" eq "/etc/dhcp3/dhcpd.conf") { $dhcpd_version = "3"; }


### Begin questionnaire ###
my $satisfied = "n";
while ($satisfied ne "y") {
 system("clear");
 print << "EOF";
Type your response or hit <Enter> to accept [defaults].  If you don't
have a response, such as no first or second DNS server, just hit 
<Enter> and none will be used.

EOF

    print "What is your DHCP daemon major version number (2 or 3)? [$dhcpd_version]: ";
    $dhcpd_version = get_response($dhcpd_version);

    if ("$dhcpd_version" eq "3") {
      print "Are you using the fixed-address patch (Y or N)? [$dhcpd_patch]: ";
      $dhcpd_patch = get_response($dhcpd_patch);
      $dhcpd_patch = uc $dhcpd_patch;
    }
   
    print "What is the name of your DHCP daemon config file? [$dhcpd_conf]: ";
    $dhcpd_conf = get_response($dhcpd_conf);
   
    print "What is your domain name? [$dnsdomainname]: ";
    $dnsdomainname = get_response($dnsdomainname);
   
    print "What is your network number? [$netnumber]: ";
    $netnumber = get_response($netnumber);
   
    print "What is your netmask? [$netmask]: ";
    $netmask = get_response($netmask);
   
    print "What is the starting IP address for your dhcp range? [$rangebegin]: ";
    $rangebegin = get_response($rangebegin);
   
    print "What is the ending IP address for your dhcp range? [$rangeend]: ";
    $rangeend = get_response($rangeend);
   
    print "What is the IP address of your first DNS server? [$dnsserver1]: ";
    $dnsserver1 = get_response($dnsserver1);
   
    if ($dnsserver1) {
      print "What is the IP address of your second DNS server? [$dnsserver2]: ";
      $dnsserver2 = get_response($dnsserver2);
    }
   
    if ($dnsserver2) {
      print "What is the IP address of your third DNS server? [$dnsserver3]: ";
      $dnsserver3=get_response($dnsserver3);
    }
   
    print "What is the IP address of your default gateway? [$router]: ";
    $router=get_response($router);
   
    print "What is the IP address of your image server? [$imageserver]: ";
    $imageserver=get_response($imageserver);
   
    print "What is the IP address of your boot server? [$boot_server]: ";
    $boot_server = get_response($boot_server);
   
    print "What is the IP address of your log server? [$log_server]: ";
    $log_server = get_response($log_server);
   
    if($log_server) {
        print "If your log server uses a non-standard port, enter it here: [$log_server_port]: ";
        $log_server_port = get_response($log_server_port);
    }
   
    print qq/Use tmpfs staging on client?  (If unsure, choose "n") [$tmpfs_staging]: /;
    $tmpfs_staging = get_response($tmpfs_staging);
    $tmpfs_staging = lc $tmpfs_staging;
    if("$tmpfs_staging" eq "y") {
        $tmpfs_staging = "yes";
    }
   
    print "Do you want to use Flamethrower (multicast) to install your clients? [$mcast_install]: ";
    $mcast_install = get_response($mcast_install);
    if ($mcast_install eq "y") {
        if ($flamethrower_directory_portbase eq "") { $flamethrower_directory_portbase="9000"; }
        print "On which port is your Flamethrower directory? [$flamethrower_directory_portbase]: ";
        $flamethrower_directory_portbase = get_response($flamethrower_directory_portbase);

    } else {
        print "Do you want your clients installed over SSH? (y/[n]): ";
        my $ssh_install=<STDIN>;
        chomp $ssh_install;
        if ($ssh_install eq "y") {
          if ($ssh_download_url eq "") { $ssh_download_url="http://$imageserver/systemimager/boot/"; }
          print "What is the base URL to use for ssh installs? [$ssh_download_url]: ";
          $ssh_download_url=get_response($ssh_download_url);
        }
    }
   
    # see http://www.armory.com/swallowscenes.html for details
    print "\nWhat... is the air-speed velocity of an unladen swallow? [$swallow]: ";
    $swallow=<STDIN>;
    system("clear");
    chomp $swallow;
    $swallow = lc $swallow;
    if($swallow =~ /african/) {
     print "Aaaaaaaaaaaaaaaaaaaaaaaaaaaah!\n";
    } else {
     print "Wrong!!! (with a Monty Python(TM) accent...)\n";
    }
    print "\nPress <Enter> to continue...";
    <STDIN>;
   
    system("clear");
    print "Ahh, but seriously folks...\n";
    print "Here are the values you have chosen:\n\n";
    print "#######################################################################\n";
    print "ISC DHCP daemon version:                  $dhcpd_version\n";
    print "DHCP daemon using fixed-address patch:    $dhcpd_patch\n";
    print "ISC DHCP daemon config file:              $dhcpd_conf\n";
    print "DNS domain name:                          $dnsdomainname\n";
    print "Network number:                           $netnumber\n";
    print "Netmask:                                  $netmask\n";
    print "Starting IP address for your DHCP range:  $rangebegin\n";
    print "Ending IP address for your DHCP range:    $rangeend\n";
    print "First DNS server:                         $dnsserver1\n";
    print "Second DNS server:                        $dnsserver2\n";
    print "Third DNS server:                         $dnsserver3\n";
    print "Default gateway:                          $router\n";
    print "Image server:                             $imageserver\n";
    print "Boot server:                              $boot_server\n";
    print "Log server:                               $log_server\n";
    print "Log server port:                          $log_server_port\n";
    print "Flamethrower directory port:              $flamethrower_directory_portbase\n";
    print "Use tmpfs staging on client:              $tmpfs_staging\n";
    print "SSH files download URL:                   $ssh_download_url\n";
    print "#######################################################################\n";
    
    print "\nAre you satisfied? (y/[n]): ";
    $satisfied=<STDIN>;
    chomp $satisfied;
}

### End questionnaire ###


# backup dhcpd.conf if necessary
if( -e "$dhcpd_conf" && ! -e "${dhcpd_conf}.beforesystemimager") { 
 rename("$dhcpd_conf", "${dhcpd_conf}.beforesystemimager");
}

# open dhcpd.conf for writing
open(DHCPDCONF, "> $dhcpd_conf") || die "Couldn't open $dhcpd_conf for writing: $!\n";
print DHCPDCONF $header;

# create a configuration file, depending on which version of dhcpd we found
if ($dhcpd_version eq "2") {
    # version 2, at least for now
    print DHCPDCONF $dhcpd_conf_v2_part1;
  
    print DHCPDCONF qq(subnet $netnumber netmask $netmask { \n);
    print DHCPDCONF qq(  range  $rangebegin $rangeend; \n);
    print DHCPDCONF qq(  option domain-name "$dnsdomainname"; \n);

    if($dnsserver1 ne "") { 
        print DHCPDCONF qq(  option domain-name-servers $dnsserver1); 
    } else {
        print DHCPDCONF qq(  #option domain-name-servers <my_name_server_ip_address>); 
    }
    if($dnsserver2 ne "") { 
        print DHCPDCONF qq(, $dnsserver2);
    }
    if($dnsserver3 ne "") { 
        print DHCPDCONF qq(, $dnsserver3);
    }
    print DHCPDCONF qq(;\n);

    print DHCPDCONF qq(  option routers $router; \n);

    print DHCPDCONF qq(  # option-140 specifies the IP address of your SystemImager image server\n);
    print DHCPDCONF qq(  option option-140 "$imageserver"; \n);
  
    print DHCPDCONF qq(  # option-142 specifies the URL address of your ssh download\n);
    print DHCPDCONF qq(  # This should be in the format of "http://$imageserver/systemimager/boot/".\n);
    if($ssh_download_url eq "") { 
        print DHCPDCONF qq(  #option option-142 "http://$imageserver/systemimager/boot/"; \n);
    } else {
        print DHCPDCONF qq(  option option-142 "$ssh_download_url"; \n);
    }

    print DHCPDCONF qq(  # option-143 specifies the Flamethrower directory port.\n);
    print DHCPDCONF qq(  # The default is "9000".\n);
    if($flamethrower_directory_portbase eq "") { 
        print DHCPDCONF qq(  #option option-143 "9000"; \n);
    } else {
        print DHCPDCONF qq(  option option-143 "$flamethrower_directory_portbase"; \n);
    }

    option_144($tmpfs_staging);

    print DHCPDCONF qq(  # next-server is your network boot server\n);
    if($boot_server eq "") { 
        print DHCPDCONF qq(  #next-server $boot_server; \n);
    } else {
        print DHCPDCONF qq(  next-server $boot_server; \n);
    }

    print DHCPDCONF qq(\n  # log-servers\n);
    if($log_server eq "") { 
        print DHCPDCONF qq(  #option log-servers $log_server; \n);
    } else {
        print DHCPDCONF qq(  option log-servers $log_server; \n);
    }

    print DHCPDCONF qq(  # option-141 is the port number your log server uses\n);
    if($log_server_port eq "") { 
        print DHCPDCONF qq(  #option-141 "$log_server_port"; \n);
    } else {
        print DHCPDCONF qq(  option-141 "$log_server_port"; \n);
    }

    print DHCPDCONF qq(} \n);
  
} else {
    # version 3, at least for now
    print DHCPDCONF $dhcpd_conf_v3_part1;
  
    print DHCPDCONF qq(\n# The "host-checking" option is only used by Curtis Zinzilieta's DHCP patch.\n);
    print DHCPDCONF qq(# The patch adds functionality to the server for removing specified host\n);
    print DHCPDCONF qq(# addresses from ranges of available ip addresses at startup.  This addresses\n);
    print DHCPDCONF qq(# the issue where a fixed-address reserved for a specific machine gets\n);
    print DHCPDCONF qq(# assigned to a different machine.\n);
    print DHCPDCONF qq(#\n);
    print DHCPDCONF qq(# If you are running a version of dhcpd with this patch, then you will want\n);
    print DHCPDCONF qq(# the "host-checking" option below to be uncommented.  If you are not running\n);
    print DHCPDCONF qq(# the patched dhcpd, you should leave it commented out.  You can find the patch\n);
    print DHCPDCONF qq(# and the matched DHCP v3.0 source code here:  http://systemimager.org/download/\n);

    if("$dhcpd_patch" eq "Y") { 
        print DHCPDCONF qq(host-checking true;\n);
    } else {
        print DHCPDCONF qq(#host-checking true;\n);
    }

    print DHCPDCONF qq(# Imageserver\n);
    print DHCPDCONF qq(option option-140 code 140 = text;\n);
    print DHCPDCONF qq(\n);

    print DHCPDCONF qq(# log_server_port\n);
    print DHCPDCONF qq(option option-141 code 141 = unsigned integer 32;\n);
    print DHCPDCONF qq(\n);

    print DHCPDCONF qq(# ssh_download_url\n);
    print DHCPDCONF qq(option option-142 code 142 = string;\n);
    print DHCPDCONF qq(\n);

    print DHCPDCONF qq(# flamethrower_directory_portbase\n);
    print DHCPDCONF qq(option option-143 code 143 = string;\n);
    print DHCPDCONF qq(\n);

    print DHCPDCONF qq(# tmpfs_staging\n);
    print DHCPDCONF qq(option option-144 code 144 = string;\n);
    print DHCPDCONF qq(\n);

    print DHCPDCONF qq(# option-140 is the IP address of your SystemImager image server\n);
    print DHCPDCONF qq(option option-140 "$imageserver";\n);
    print DHCPDCONF qq(\n);

    print DHCPDCONF qq(# option-142 specifies the URL address of your ssh download\n);
    print DHCPDCONF qq(# This should be in the format of "http://$imageserver/systemimager/boot/".\n);
    if($ssh_download_url eq "") { 
        print DHCPDCONF qq(#option option-142 "http://$imageserver/systemimager/boot/"; \n);
    } else {
        print DHCPDCONF qq(option option-142 "$ssh_download_url"; \n);
    }
    print DHCPDCONF qq(\n);

    print DHCPDCONF qq(# option-143 specifies the Flamethrower directory port.\n);
    print DHCPDCONF qq(# The default is "9000".\n);
    if($flamethrower_directory_portbase eq "") { 
        print DHCPDCONF qq(#option option-143 "9000"; \n);
    } else {
        print DHCPDCONF qq(option option-143 "$flamethrower_directory_portbase"; \n);
    }
    print DHCPDCONF qq(\n);

    option_144($tmpfs_staging);

    print DHCPDCONF qq(# next-server is your network boot server\n);
    if($boot_server eq "") { 
        print DHCPDCONF qq(#next-server 1.2.3.4; \n);
    } else {
        print DHCPDCONF qq(next-server $boot_server; \n);
    }
    print DHCPDCONF qq(\n);

    print DHCPDCONF qq(# log-servers\n);
    if($log_server eq "") { 
        print DHCPDCONF qq(#option log-servers 1.2.3.4; \n);
    } else {
        print DHCPDCONF qq(option log-servers $log_server; \n);
    }
    print DHCPDCONF qq(\n);

    print DHCPDCONF qq(# option-141 is the port number your log server uses\n);
    if($log_server_port eq "") { 
        print DHCPDCONF qq(#option option-141 514; \n);
    } else {
        print DHCPDCONF qq(option option-141 $log_server_port; \n);
    }
    print DHCPDCONF qq(\n);

    print DHCPDCONF $dhcpd_conf_v3_part2;
   
    print DHCPDCONF qq(subnet $netnumber netmask $netmask { \n);
    print DHCPDCONF qq(  range  $rangebegin $rangeend; \n);
    print DHCPDCONF qq(  option domain-name "$dnsdomainname"; \n);
    if($dnsserver1 ne "") { print DHCPDCONF qq(  option domain-name-servers $dnsserver1); }
    if($dnsserver2 ne "") { print DHCPDCONF qq(, $dnsserver2); }
    if($dnsserver3 ne "") { print DHCPDCONF qq(, $dnsserver3); }
    if($dnsserver1 ne "") { print DHCPDCONF qq(;\n); }
    print DHCPDCONF qq(  option routers $router; \n);
    print DHCPDCONF qq( } \n);

}


# close and chmod the new dhcpd.conf file
close(DHCPDCONF);
system("chmod 600 $dhcpd_conf");


# Oh where oh where could my leases file directory be?
my @lease_file_directories_du_jour = ( 
  "/var/dhcp",
  "/var/state/dhcp",
  "/etc"
);

my $dhcpd_lease_file_directory;
foreach my $directory (@lease_file_directories_du_jour) {
    # does *this* one exist?
    if ( -d "$directory" ) { 
        $dhcpd_lease_file_directory = $directory;
    }
    last if ( $dhcpd_lease_file_directory );
}

# Touch dhcpd.leases file so that dhcpd won't fart when started.
# Different installs put the file in different places so we touch
# one of several potential files and a monkey.
if( -d "$dhcpd_lease_file_directory" )
{
     my $file="$dhcpd_lease_file_directory/dhcpd.leases";
     system("touch $file");
     ($?) && die "\n$program_name Couldn't touch $file.\n";
}

# words of wisdom
system("clear");
print <<"EOF";

The dhcp server configuration file ($dhcpd_conf) file has been 
created for you.  Please verify it for accuracy.

If this file does not look satisfactory, you can run this command again
to re-create it: "si_mkdhcpserver"

WARNING!:  If you have multiple physical network interfaces, be sure to 
edit the init script that starts dhcpd to specify the interface that 
is connected to your DHCP clients.  Here's an example:

 Change "/usr/sbin/dhcpd" to "/usr/sbin/dhcpd eth1".

Depending on your distribution, you may be able to set this with the 
"INTERFACES" variable in either "/etc/default/dhcp" or in your dhcpd
initialization script (usually "/etc/init.d/dhcpd").

Also, be sure to start or restart your dhcpd daemon.  This can usually
be done with a command like "/etc/init.d/dhcpd restart" or similar.

EOF

# Oh where oh where could my init script be?
my @init_scripts_du_jour = ( 
  "/etc/init.d/dhcp3-server",
  "/etc/init.d/dhcp",
  "/etc/init.d/dhcpd",
  "/etc/rc.d/init.d/dhcpd"
);

foreach my $file (@init_scripts_du_jour) {
    if( -e $file ){
        print "Would you like me to restart your DHCP server software now? (y/[n]): ";
        $continue=get_response();
        $continue=lc($continue);

        if( $continue eq "y" ){
            my $cmd="$file restart";
            system($cmd);
            exit 0;
        }
    }
}

exit 0;



# Subroutines
sub get_response {
    my $garbage_out=$_[0];
    my $garbage_in=<STDIN>;
    chomp $garbage_in;
    unless($garbage_in eq "") { $garbage_out = $garbage_in; }
    return $garbage_out;
}

# Usage: option_144($tmpfs_staging);
sub option_144 {

    my $value = shift;

    print DHCPDCONF qq(  # \n);
    print DHCPDCONF qq(  # option-144 tells your autoinstallclient to spool the image into a tmpfs\n);
    print DHCPDCONF qq(  # prior to laying it down on disk.  It is not certain that this is always\n);
    print DHCPDCONF qq(  # a good thing to do.  And if you're feeling gutsy and want to try it, be\n);
    print DHCPDCONF qq(  # sure that your (memory + swap) is at least twice the size of your image\n);
    print DHCPDCONF qq(  # or your image transfer will fail when the tmpfs filesystem gets full!!!\n);
    print DHCPDCONF qq(  # If unsure, say "no".\n);
    print DHCPDCONF qq(  # \n);
    print DHCPDCONF qq(  option option-144 "$value"; \n);
    print DHCPDCONF qq(\n);
}

