#!/bin/sh
# Author: Blake, Kuo-Lien Huang
# License: GPL
# Description:
#  install debian in local hd  and
#  use the configuration of DRBL client to setup debian 

debian_mirror="http://opensource.nchc.org.tw/debian"
drbl_mirror="http://drbl.nchc.org.tw/"
target_hd=""
target_dir="/tmp/target"
drblhost="/var/lib/diskless/default/"

# function: task_hdinstall 
task_hdinstall() {

  # swapoff
  /sbin/swapoff -a

  # instmgrd
  ip=""
  instmgrd=""
  netdevices="$(cat /proc/net/dev | grep eth | cut -d":" -f1 | sed 's/ *//')"
  for device in $netdevices
  do
    ip=`/sbin/ifconfig $device | grep "inet addr" | awk -F: '{ print $2; }' | awk '{ print $1; }'`
    if [ "$ip" != "" ]; then
      network=`echo $ip | awk -F. '{print $1"."$2"."$3; }'`
      while read line
      do
        if [ "$(echo $line | grep "$network")" != "" ]; then
	      instmgrd=$line
          break
        fi
      done < /etc/hdinstall/config
    fi
  done

  # hd partition and file system
  TMP="/tmp/hdinstall.$$"
  if [ -f /proc/partitions ]; then
    while read x x x p x 
    do
      case "$p" in
	    hd?)
	      if [ "`cat /proc/ide/$p/media`" = "disk" ]; then
		    target_hd=$p
		    break
		  fi
	     ;;
	    sd?)
         target_hd=$p
	     break
	     ;;
	  esac
    done < /proc/partitions
  fi

  if [ "$target_hd" = "" ]; then exit; fi

  hd_size=`/sbin/sfdisk -s /dev/$target_hd`
  cylinders=`/sbin/sfdisk -g /dev/$target_hd | awk '{ print $2; }'`
  ## swap 
  cat <<-EOF > $TMP
$cylinders * 5 / 100
quit
EOF
  swap=`bc $TMP`
  cat <<-EOF > $TMP
$hd_size * 5 / 100
quit
EOF
  swap_size=`bc $TMP`
  if [ $swap_size -gt 2000000 ]; then
    cat <<-EOF > $TMP
2000000 * $cylinders / $hd_size
quit
EOF
    swap=`bc $TMP`
  fi
  ## root
  cat <<-EOF > $TMP
$cylinders - $swap
quit
EOF
  root=`bc $TMP`
  ## sfdisk
  cat <<-EOF > $TMP
0,$root,L,*
$root,,S
EOF
  /sbin/sfdisk /dev/$target_hd < $TMP

  ## filesystem
  root_partition="/dev/"$target_hd"1"
  swap_partition="/dev/"$target_hd"2"
  /sbin/mkfs.reiserfs -f $root_partition > /dev/null 2>&1
  dd if=/dev/zero of=/dev/$swap_partition bs=1k count=16 > /dev/null 2>&1
  sync
  /sbin/mkswap $swap_partition > /dev/null 2>&1
  /sbin/swapon $swap_partition > /dev/null 2>&1

  rm -f $TMP

  # mount the partition for installation
  mkdir -p $target_dir
  mount -t reiserfs $root_partition $target_dir > /dev/null 2>&1

  # install base system, kernel, and packages
  export PATH="/bin:/sbin:/usr/bin:/usr/sbin"
  /usr/sbin/debootstrap --arch i386 woody $target_dir $debian_mirror
  #kver=`uname -r`
  #cp -a /lib/modules/$kver $target_dir/lib/modules
  #cp -a /boot/* $target_dir/boot
  #ln -s /boot/vmlinux-$kver  $target_dir/vmlinuz
  #rm -f $target_dir/lib/modules/$kver/modules.*
  #/usr/sbin/chroot $target_dir /sbin/depmod -F /boot/System.map-$kver -a $kver

  cp /usr/sbin/setdebconf-v0.9 $target_dir/usr/sbin
  cp /usr/sbin/readdebconf-v0.9 $target_dir/usr/sbin
  /usr/sbin/readdebconf-v0.9 $target_dir/root/drbl-debconf

  ## networking
  cp /etc/hosts $target_dir/etc
  cat <<-EOF > $target_dir/etc/network/interfaces
auto lo
iface lo inet loopback

EOF
  netdevices="$(cat /proc/net/dev | grep eth | cut -d":" -f1 | sed 's/ *//')"
  for device in $netdevices
  do
    cat <<-EOF >> $target_dir/etc/network/interfaces
auto $device
iface $device inet dhcp

EOF
  done

  ## apt.conf & sources.list
  cp /etc/apt/sources.list $target_dir/etc/apt/
  cat <<-EOF > $target_dir/root/etc/apt/apt.conf
APT 
{
  // Options for apt-get
  Get 
  {
     Download-Only "false";
	 Assume-Yes "true";
	 Fix-Broken "true";
  };

  Immediate-Configure "false";
  Force-LoopBreak "true";
};

// Options for the downloading routines
Acquire
{
  Retries "0";
};

// Things that effect the APT dselect method
DSelect 
{
  Clean "auto";   // always|auto|prompt|never
};

DPkg 
{
  // Probably don't want to use force-downgrade..
  Options {"--force-overwrite";}
}
EOF

  ## auto debconf
  cat <<-EOF > $target_dir/root/debconf_noniterative
#!/bin/sh
# \\
exec expect "\$0" \${1+"\$@"}
spawn /usr/sbin/dpkg-reconfigure --frontend=readline debconf
expect "What interface should be used for configuring packages?"
send "5\r"
#expect "Ignore questions with a priority less than.."
expect "See only questions that are of what prioriry and higher?"
send "1\r"
expect "Show all old questions again and again?"
send "no\r"
expect eof
EOF
  chmod 755 $target_dir/root/debconf_noniterative

  cat <<-EOF > $target_dir/root/debconf_dialog
#!/bin/sh
# \\
exec expect "\$0" \${1+"\$@"}
spawn /usr/sbin/dpkg-reconfigure --frontend=readline debconf
expect "What interface should be used for configuring packages?"
send "1\r"
#expect "Ignore questions with a priority less than.."
expect "See only questions that are of what priority and higher?"
send "3\r"
expect "Show all old questions again and again?"
send "no\r"
expect eof
EOF
  chmod 755 $target_dir/root/debconf_dialog

  ## pkg.sh
  cat <<-EOF > $target_dir/root/pkg.sh
#!/bin/sh
apt-get update
apt-get --force-yes -y install expect

/root/debconf_noniterative

EOF
  # install kernel-image & kernel-headers first
  dpkg --get-selections | awk '/kernel-image|kernel-headers/ { print "apt-get -y install "$1" </dev/null"; }' >> $target_dir/root/pkg.sh
  # install other packages expect some fancy packages ..
  dpkg --get-selections | awk '$1 !~/kernel-image|kernel-headers|acpid/ { print "apt-get -y install "$1" </dev/null" ; }' >> $target_dir/root/pkg.sh
  # install fancy packages: acpid
  dpkg --get-selections | awk '/acpid/ { print "apt-get -y install "$1" </dev/null"; }' >> $target_dir/root/pkg.sh
##cat <<-EOF >> $target_dir/root/pkg.sh
##apt-get -y install nis
##apt-get -y install ntpdate
##apt-get -y install discover
##EOF
  cat <<-EOF >> $target_dir/root/pkg.sh
/usr/bin/setdebconf-v0.9 /root/drbl-debconf

/root/debconf_dialog
EOF
  chmod 755 $target_dir/root/pkg.sh

  mount -t proc proc $target_dir/proc
  /usr/sbin/chroot $target_dir /root/pkg.sh
  umount $target_dir/proc

  rm -f $target_dir/root/pkg.sh
  rm -f $target_dir/root/debconf_dialog
  rm -f $target_dir/root/debconf_noniterative

  mkdir -p $target_dir/opt

  # setup node
  ## fstab
  cat <<-EOF > $target_dir/etc/fstab
$root_partition	/	reiserfs	defaults,rw	0 1
$swap_partition	none	swap	sw	0 0
proc /proc	proc defaults 0 0
EOF
  while read spec file vfstype others; do
    if [ "$file" = "/" ]; then
      nfsserver="$(echo "$spec" | cut -d: -f1)"
      break
    fi
  done < /etc/fstab

  ## passwd, group, shadow
  cp /etc/passwd $target_dir/etc
  cp /etc/group $target_dir/etc
  cp /etc/shadow $target_dir/etc
  ## nis
  cp /etc/yp.conf $target_dir/etc
  cp /etc/ypserv.conf $target_dir/etc
  cp /etc/ypserv.securenets $target_dir/etc
  cp /etc/defaultdomain $target_dir/etc
  cp /etc/default/nis $target_dir/etc/default
  ## ntpdate
  cp /etc/default/ntp-servers $target_dir/etc/default
  ## discover
  #cp /usr/share/discover/* $target_dir/usr/share/discover
  ## openmosix
  cp /etc/openmosix.map $target_dir/etc
  ## setupx
  cp /etc/init.d/setupx $target_dir/etc/init.d
  ln -fs ../init.d/setupx $target_dir/etc/rc2.d/S98setupx
  rm -f $target_dir/etc/init.d/XF86_DEFAULT
  rm -f $target_dir/etc/init.d/XF86_DDC
  ## ssh
  #if [ -e /root/.ssh ]; then
  #  cp -a /root/.ssh $target_dir/root/
  #fi

  # make the system bootable
  USE_LILO=0

  /sbin/grub-install --root-directory=$target_dir --no-floppy /dev/$target_hd
  if [ -e $target_dir/boot/grub/device.map ]; then

    GRUBDEV=""
    while read grubdev osdev; do
      if [ "$osdev" = "/dev/$target_hd" ]; then
        GRUBDEV=$grubdev
        GRUBDEV=${GRUBDEV/(/}
        GRUBDEV="${GRUBDEV/)/}"
      fi
    done < $target_dir/boot/grub/device.map

    if [ "$GRUBDEV" != "" ]; then
      cp /usr/share/icons/grub-debian.xpm.gz $target_dir/boot/grub/
      cat <<-EOF > $target_dir/boot/grub/menu.lst
default=0
timeout=10
splashimage=($GRUBDEV,0)/boot/grub/grub-debian.xpm.gz
title Debian
  root ($GRUBDEV,0)
  kernel /vmlinuz ro root=$root_partition
EOF
     USE_LILO=0
    else
      USE_LILO=1
    fi

  else
    USE_LILO=1
  fi

  if [ $USE_LILO -eq 1 ]; then
    cat <<-EOF > $target_dir/etc/lilo.conf
lba32
boot=/dev/$target_hd
root=$root_partition
install=/boot/boot-menu.b
map=/boot/map
delay=0
vga=normal
default=Linux

image=/vmlinuz
	label=Linux
	read-only
EOF
    lilo -r $target_dir
  fi

  # unmount the partition, notify, reboot
  umount $target_dir
  sync
  if [ "$instmgrd" != "" ]; then
    echo "$ip" | /usr/bin/socket -q $instmgrd
  fi
  /sbin/reboot
}

# create inittab for hdinstall
hdinstall_inittab() {
  inittab=$1
  cat <<-EOF > $inittab
# /etc/inittab: init(8) configuration.

# The default runlevel.
id:1:initdefault:

# Boot-time system configuration/initialization script.
# This is run first except when booting in emergency (-b) mode.
si::sysinit:/etc/init.d/rcS

# What to do in single-user mode.
#~~:S:wait:/sbin/sulogin
~~:S:wait:/sbin/drbl-hdinstall hdinstall

# /etc/init.d executes the S and K scripts upon change
# of runlevel.
#
# Runlevel 0 is halt.
# Runlevel 1 is single-user.
# Runlevels 2-5 are multi-user.
# Runlevel 6 is reboot.

l0:0:wait:/etc/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6
# Normally not reached, but fallthrough in case of emergency.
z6:6:respawn:/sbin/sulogin

# What to do when CTRL-ALT-DEL is pressed.
ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now

# Action on special keypress (ALT-UpArrow).
#kb::kbrequest:/bin/echo "Keyboard Request--edit /etc/inittab to let this work."

# What to do when the power fails/returns.
pf::powerwait:/etc/init.d/powerfail start
pn::powerfailnow:/etc/init.d/powerfail now
po::powerokwait:/etc/init.d/powerfail stop

# /sbin/getty invocations for the runlevels.
#
# The "id" field MUST be the same as the last
# characters of the device (after "tty").
#
# Format:
#  <id>:<runlevels>:<action>:<process>
#
# Note that on most Debian systems tty7 is used by the X Window System,
# so if you want to add more getty's go ahead but skip tty7 if you run X.
#
1:2345:respawn:/sbin/getty 38400 tty1
2:23:respawn:/sbin/getty 38400 tty2
3:23:respawn:/sbin/getty 38400 tty3
4:23:respawn:/sbin/getty 38400 tty4
5:23:respawn:/sbin/getty 38400 tty5
6:23:respawn:/sbin/getty 38400 tty6

# Example how to put a getty on a serial line (for a terminal)
#
#T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100
#T1:23:respawn:/sbin/getty -L ttyS1 9600 vt100

# Example how to put a getty on a modem line.
#
#T3:23:respawn:/sbin/mgetty -x0 -s 57600 ttyS3
EOF
}

case "$1" in 
  "start")    
    ### create all hosts before setup autologin ..
    if [ -f /etc/default/drbl ]; then
      . /etc/default/drbl
    fi

    if [ "$NO_DRBLMGRD" = "true" -o "$NO_DRBLMGRD" = "" ]; then
      ## no drblmgrd
      if [ ! -f /opt/drbl/sbin/drbl_deploy.sh ]; then
        echo "DRBL Configuration is not correct!"
        echo "Please use '/opt/drbl/sbin/drblpush-desktop' to setup!!"
        exit
      else
        for ip in `cat /etc/dhcp3/dhcpd.conf | grep "fixed-address" | cut -d\; -f1 | awk '{ print $2; }'`
        do
          ## if one host lost in $imagehost, regenerate all ..
          if [ ! -d $imagehost/$ip ]; then
            /opt/drbl/sbin/drbl_deploy.sh
            break
          fi
        done
      fi
    else
      ## drblmgrd
      drblmgrd=`/sbin/ifconfig eth0 | grep "inet addr" | cut -d: -f2 | awk '{ print $1; }'`
      drblmgrd_ps=`ps -e 2> /dev/null | grep "drblmgrd"`
      if [ "$drblmgrd" = "" -o "$drblmgrd_ps" = "" ]; then
        echo "DRBL Configuration is not correct!"
        echo "Please use '/opt/drbl/sbin/drblpush-desktop' to setup!!"
        exit
      fi

      for ip in `cat /etc/dhcp3/dhcpd.conf | grep "fixed-address" | cut -d\; -f1 | awk '{ print $2; }'`
      do
        if [ ! -d $imagehost/$ip ]; then
          echo "create host: $ip"
          echo "$ip" | /usr/bin/socket $drblmgrd 6460 
        fi
      done
    fi

    ##
    head -n $(expr 519 - 1) $0 > $drblhost/root/sbin/drbl-hdinstall
    chmod 755 $drblhost/root/sbin/drbl-hdinstall
    for host in `ls $drblhost`; do
      # hdinstall config
      INSTMGRD_PORT="6463"
      if [ "$host" != "." -a "$host" != ".." ]; then
        mkdir -p $drblhost/$host/etc/hdinstall
        . /etc/default/dhcp3-server
        for eth in `echo $INTERFACES`
        do
          IP=`/sbin/ifconfig $eth | grep "inet addr" | awk -F: '{ print $2; }' | awk '{ print $1; }'`
          echo "$IP $CLUSTERMGRD_PORT" >> $drblhost/$host/etc/hdinstall/config
        done
      fi
      # inittab for hdinstall
      if [ "$host" != "." -a "$host" != ".." -a "$host" != "root" ]; then
        if [ -e $drblhost/$host/etc/inittab.hdinstall ]; then
          echo -n "."
        else
          mv $drblhost/$host/etc/inittab $drblhost/$host/etc/inittab.hdinstall
          hdinstall_inittab $drblhost/$host/etc/inittab
          echo -n "."
        fi
      fi
    done
    echo
    # start the daemon: instmgrd
    tail +519 $0 > /var/run/instmgrd
    chmod 755 /var/run/instmgrd
    /var/run/instmgrd &
    ;;
  "stop")
    for host in `ls $drblhost`; do
      if [ "$host" != "." -a "$host" != ".." -a "$host" != "root" ]; then
        if [ -e $drblhost/$host/etc/inittab.hdinstall ]; then
          mv $drblhost/$host/etc/inittab.hdinstall $drblhost/$host/etc/inittab
          echo -n "."
        fi
      fi
    done
    echo
    # kill the deamon
    pid=`/bin/ps -e 2> /dev/null | grep 'instmgrd' | sed -e 's/^ *//' -e 's/ .*//'`
    if [ "$pid" != "" ]; then kill -9 $pid; fi
    rm -f /var/run/instmgrd
    ;;
  'hdinstall')
    task_hdinstall
    ;;
  *)
    echo "Usage: $0 [start|stop]"
    ;;
esac
exit 0
#!/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 );
}
