#!/bin/sh

#
# "SystemImager" - Copyright (C) 1999-2001 Brian Elliott Finley <brian@systemimager.org>
#
#   Others who have contributed to this code:
#     Curtis Zinzilieta <czinzilieta@valinux.com>
#
# This file is: rcS
#

PATH=/sbin:/bin:/usr/bin:/usr/sbin:/tmp
SCRIPTS=scripts

shellout() {
  exec cat /etc/issue ; exit 1
}

write_variables() {
  # pass all variables set here on to the hostname.sh script
  echo "HOSTNAME=$HOSTNAME" 		> /tmp/variables.txt
  echo "DOMAINNAME=$DOMAINNAME" 	>> /tmp/variables.txt
  echo "DEVICE=$DEVICE" 		>> /tmp/variables.txt
  echo "IPADDR=$IPADDR" 		>> /tmp/variables.txt
  echo "NETMASK=$NETMASK" 		>> /tmp/variables.txt
  echo "NETWORK=$NETWORK" 		>> /tmp/variables.txt
  echo "BROADCAST=$BROADCAST" 		>> /tmp/variables.txt
  echo "GATEWAY=$GATEWAY" 		>> /tmp/variables.txt
  echo "GATEWAYDEV=$GATEWAYDEV" 	>> /tmp/variables.txt
  echo "IMAGESERVER=$IMAGESERVER" 	>> /tmp/variables.txt
  echo "SSH_USER=$SSH_USER"		>> /tmp/variables.txt
  echo "SSH_DOWNLOAD_URL=$SSH_DOWNLOAD_URL"	>> /tmp/variables.txt
}

get_hostname_by_dns() {
  # get the nameserver from the resolv.conf file
  NAMESERVER=`cat /etc/resolv.conf \
  | mawk '/nameserver/ {print $2}'`

  # get base hostname.  For example, b2b99.domain.com will become b2b99
  HOSTNAME=`host $IPADDR \
  | grep "^Name:.*" \
  | cut -d " " -f 2 \
  | cut -d "." -f 1`
  
  ### got rid of massdns to save some space
  # HOSTNAME=`echo $IPADDR \
  # | massdns $NAMESERVER \
  # | mawk -F "." '{print $1}'`
}
    
# Mount file systems in /etc/fstab.
mount proc /proc -t proc

if [ -x /bin/hostname ]; then
  hostname 'SystemImager-autoinstall-system'
fi

# Add necessary entries to /etc/services
cat << EOF >> /etc/services 
ssh		22/tcp
ssh		22/udp
tftp            69/udp
sftp		115/tcp
rsync           873/tcp
rsync           873/udp
EOF

# Create and mount ram filesystem for extra executables
echo "BEGIN Creating ram disk for /tmp to hold extra binaries"
mke2fs /dev/ram1
mount /dev/ram1 /tmp
echo "END Creating ram disk for /tmp to hold extra binaries"

# Configure loopback interface (may as well)
ifconfig lo 127.0.0.1

### BEGIN look for local.cfg file ###
# This code inspired by Ian McLeod <ian@valinux.com>
#
# BEGIN try hard drive
#
# this must be on two lines in order to substitute the newline
cat /proc/cmdline | sed "s/ /\\
/g" | sed -n "s/=/=/p" > /tmp/last_root.$$
# suck in variable
. /tmp/last_root.$$
if [ ! -z "$LAST_ROOT" ]; then
  echo "Checking for configuration file on hard drive..."
  mkdir /last_root
  # kernel must have autoconfig/autostart enabled in order to
  # pull local.cfg off a software raid drive
  echo "Mounting hard drive..."
  mount $LAST_ROOT /last_root -o ro > /dev/null 2>&1 || echo "Couldn't mount hard drive."
  if [ -f /last_root/local.cfg ]; then
    echo "Found /local.cfg on hard drive..."
    echo "Reading configuration from /local.cfg on hard drive..."
    . /last_root/local.cfg
  else
    echo "No /local.cfg on hard drive..."
  fi
  echo "Unmounting hard drive..."
  umount /last_root || shellout
fi
# END try hard drive

# BEGIN try floppy
echo "Checking for configuration file on floppy..."
echo
mkdir /floppy
mount /dev/fd0 /floppy -o ro > /dev/null 2>&1 || echo "No floppy in drive."
if [ -f /floppy/local.cfg ]; then
  echo
  echo "Reading local configuration from floppy..."
  . /floppy/local.cfg
else
  echo
  echo "No /local.cfg on floppy drive..."
fi
# END try floppy

# in case someone is using the deprecated DHCPSIADDR in their local.cfg file...
[ ! -z $DHCPSIADDR ] && IMAGESERVER=$DHCPSIADDR

if [ ! -z $IPADDR ]; then
  # configure interface and add default gateway
  ifconfig $DEVICE $IPADDR  netmask $NETMASK  broadcast $BROADCAST
  route add default gw $GATEWAY

  if [ $? != 0 ]; then
    echo
    echo "I couldn't configure the network interface using the local.cfg file."
    echo "Check the entries in the local.cfg file on your floppy."
    echo
    shellout
  fi
### END look for local.cfg file ###

else
  ### BEGIN dhcp ###
  echo "IP Address not set by local.cfg.  I will use DHCP."

  ### BEGIN ether sleep ###
  # Give the switch time to start passing packets.  Some switches won't
  # forward packets until 30 seconds or so after an interface comes up.
  # This means the dhcp server won't even get the request for 30 seconds.
  # Many ethernet cards aren't considered "up" by the switch until the
  # driver is loaded.  Because the driver is compiled directly into the
  # kernel here, the driver is definitely loaded at this point.
  count=35
  echo
  echo "sleep $count:  This is to give your switch (if you're using one) time to"
  echo "           recognize your ethernet card before we try the network."
  echo
  I=1; while [ $I != $(( $count + 1)) ]; do echo -n "$I "; sleep 1s; I=$(( $I + 1 )) ; done
  echo
  ### END ether sleep ###

  # create directory to catch dhcp information
  mkdir /var/dhcp

  # combine systemimager code to the stock debian dhclient-script
  # and make executable
  cat /etc/dhclient-script.systemimager-prefix \
      /etc/dhclient-script.debian-dist \
      > /etc/dhclient-script
  chmod +x /etc/dhclient-script

  # get info via dhcp
  echo
  echo "dhclient"
  dhclient
  if [ ! -s /var/dhcp/dhclient.leases ]; then
    echo
    echo "I couldn't configure the network interface using DHCP."
    echo
    shellout
  fi

  # Figure out which interface actually got configured.
  # Suggested by James Oakley.
  INTERFACE=`grep interface /var/dhcp/dhclient.leases | mawk -F'"' '{print $2; exit}'`

  # read dhcp info in as variables -- this file will be created by 
  # the /etc/dhclient-start script that is run automatically by
  # dhclient.
  . /tmp/dhcp_info.${INTERFACE} || shellout
  ### END dhcp ###
fi

# in case someone is using a dhcpd.conf created by an older makedhcpserver
# (that doesn't contain explicit specification of the imageserver)
[ -z $IMAGESERVER ] && IMAGESERVER=$DHCPSIADDR

### BEGIN ping test for switch initialization ###
# ping test code submitted by Grant Noruschat <grant@eigen.ee.ualberta.ca>
# modified slightly by <brian@systemimager.org>
echo
echo "  Pinging image server \"$IMAGESERVER\""
echo "  to ensure we have network connectivity."
echo

PING_COUNT=1
PING_EXIT_STATUS=1

while [ $PING_EXIT_STATUS != 0 ]
do
  echo "  Ping attempt number $PING_COUNT: "
  ping -c 1 $IMAGESERVER; PING_EXIT_STATUS=$?; PING_COUNT=$(( $PING_COUNT + 1 ))
  if [ $PING_COUNT = 21 ]; then
    echo
    echo "  FATAL:  Cannot ping the image server \"$IMAGESERVER\""
    echo "          Be sure that your networking equipment is"
    echo "          working properly!"
    echo
    shellout
  fi
done

if [ $PING_EXIT_STATUS = 0 ]; then
  echo
  echo "  We have connectivity to the Image Server!"
  echo
fi
### END ping test ###

# Are we installing over SSH?
if [ ! -z $SSH_DOWNLOAD_URL ]; then
  echo
  echo "SSH_DOWNLOAD_URL variable is set, so we will install over SSH!"

  # Remove possible trailing / from URL
  SSH_DOWNLOAD_URL=`echo $SSH_DOWNLOAD_URL | sed 's/\/$//'`

  # download binaries for ssh
  files="ssh sshd scp ssh-keygen"
  for file in $files
  do
    echo
    echo "snarfing $SSH_DOWNLOAD_URL/$file..."
    snarf $SSH_DOWNLOAD_URL/$file /tmp/$file

    if [ $? != 0 ]; then
      echo
      echo "snarf of $file failed!!!"
      echo
      shellout
    fi

    # create links in /usr/bin directory so that binaries can find themselves ;)
    # (they look for themselves in a particular location defined at compile time)
    echo "Linking /tmp/$file to /usr/bin/$file"
    ln -s -f /tmp/$file /usr/bin/$file
    if [ $? != 0 ]; then
      echo
      echo "Soft link of $file failed!!!"
      echo
      shellout
    fi

    # mark each binary executable
    echo "Setting permissions on /tmp/$file..."
    chmod 700 /tmp/$file || shellout
  done

  # get list of libraries needed for ssh
  files="libs.list"
  for file in $files
  do
    echo
    echo "snarfing $SSH_DOWNLOAD_URL/$file..."
    snarf $SSH_DOWNLOAD_URL/$file /tmp/$file
    if [ $? != 0 ]; then
      echo
      echo "snarf of $file failed!!!"
      echo
      shellout
    fi
  done

  # make room for new libraries
  mke2fs /dev/ram2         || shellout
  mkdir /tmp/lib           || shellout
  mount /dev/ram2 /tmp/lib || shellout

  # download libraries for binaries for ssh
  files=`cat /tmp/libs.list`
  for file in $files
  do
    echo
    echo "snarfing $SSH_DOWNLOAD_URL/$file..."
    snarf $SSH_DOWNLOAD_URL/$file /tmp/lib/$file
    if [ $? != 0 ]; then
      echo
      echo "snarf of $file failed!!!"
      echo
      shellout
    fi
  done

  # get list of links to point to libraries with
  files="libs.links"
  for file in $files
  do
    echo
    echo "snarfing $SSH_DOWNLOAD_URL/$file..."
    snarf $SSH_DOWNLOAD_URL/$file /tmp/$file
    if [ $? != 0 ]; then
      echo
      echo "snarf of $file failed!!!"
      echo
      shellout
    fi
  done

  # recreate links
  cd /tmp/lib	            || shellout
  /bin/sh /tmp/libs.links   || shellout
  cd -	                    || shellout

  # mount the device with the new libraries over the old ones
  umount /tmp/lib/	|| shellout
  mount /dev/ram2 /lib	|| shellout

  # download the required configuration files for ssh
  mkdir /tmp/etc     || shellout
  mkdir /tmp/etc/ssh || shellout

  # link /etc/ssh to /tmp/ssh so binaries can find the config files
  # (one link for the directory take up fewer inodes)
  ln -s /tmp/etc/ssh /etc/ssh || shellout

  # download config files needed by ssh
  files="ssh_config sshd_config nsswitch.conf"
  for file in $files
  do
    echo
    echo "snarfing $SSH_DOWNLOAD_URL/$file..."
    snarf $SSH_DOWNLOAD_URL/$file /etc/ssh/$file
    if [ $? != 0 ]; then
      echo
      echo "snarf of $SSH_DOWNLOAD_URL/$file failed!!!"
      echo
      shellout
    fi
  done

  # create root's ssh dir
  mkdir /root/.ssh

  # If a private key exists, put it in the right place so this autoinstall
  # client can use it to authenticate itself to the imageserver.
  # (ssh1 style user private key)
  if [ -e /floppy/identity ]; then
    PRIVATE_KEY=/root/.ssh/identity
    cp /floppy/identity $PRIVATE_KEY || shellout
    chmod 600 $PRIVATE_KEY           || shellout
  fi
  # (ssh2 style user private key)
  if [ -e /floppy/id_dsa ]; then
    PRIVATE_KEY=/root/.ssh/id_dsa
    cp /floppy/id_dsa $PRIVATE_KEY || shellout
    chmod 600 $PRIVATE_KEY         || shellout
  fi

  # make urandom device, needed by ssh
  mknod /dev/urandom c 1 9 || shellout

  # If we have a private key from the media above, go ahead and open secure tunnel
  # to the imageserver and continue with the autoinstall like normal.
  if [ ! -z $PRIVATE_KEY ]; then
    # with the prep ready, start the ssh tunnel connection.
    # the sleep command executes remotely.  Just need something long here
    # as the connection will be severed when the newly imaged client reboots
    # 14400 = 4 hours...if we're not imaged by then....oh boy!
    #
    # Determine if we should run interactive and set redirection options appropriately.
    # So if the key is blank, go interactive. (Suggested by Don Stocks <don_stocks@leaseloan.com>)
    if [ -s $PRIVATE_KEY ]; then
      # key is *not* blank
      REDIRECTION_OPTIONS="> /dev/null 2>&1"
    else
      # key is blank - go interactive
      REDIRECTION_OPTIONS=""
    fi
    ssh -l $SSH_USER -n -f -L873:127.0.0.1:873 $IMAGESERVER sleep 14400 $REDIRECTION_OPTIONS
    if [ $? != 0 ]; then
      echo
      echo "ssh tunnel command to $IMAGESERVER failed!!!"
      echo "The command was: ssh -l $SSH_USER -n -f -L873:127.0.0.1:873 $IMAGESERVER sleep 14400"
      echo
      shellout
    fi

    # Since we're using SSH, change the $IMAGESERVER variable to reflect
    # the forwarded connection.
    IMAGESERVER=127.0.0.1
  else
    # Looks like we didn't get a private key from the floppy, so let's just
    # fire up sshd and wait for someone to connect to us to initiate the
    # next step of the autoinstall.

    # download authorized_keys
    # (public keys of users allowed to ssh *in* to this machine)
    files="authorized_keys"
    for file in $files
    do
      echo
      echo "snarfing $SSH_DOWNLOAD_URL/$file..."
      snarf $SSH_DOWNLOAD_URL/$file /root/.ssh/$file
      if [ $? != 0 ]; then
        echo
        echo "snarf of $SSH_DOWNLOAD_URL/$file failed!!!"
        echo
        shellout
      fi
    done

    # set permissions to 600 -- otherwise, sshd will refuse to use it
    chmod 600 /root/.ssh/authorized_keys || shellout

    # Since we're using SSH, change the $IMAGESERVER variable to reflect
    # the forwarded connection.
    IMAGESERVER=127.0.0.1

    # save variables for autoinstall script
    write_variables || shellout

    # create a private host key for this autoinstall client
    echo
    echo "Using ssh-keygen to create this hosts private key"
    echo
    ssh-keygen -N "" -f /etc/ssh/ssh_host_key || shellout

    # create necessary ptys, etc. for sshd
    mknod /dev/ptmx c 5 2                    || shellout
    chmod 666 /dev/ptmx                      || shellout
    mkdir /dev/pts                           || shellout
    echo "none /dev/pts devpts" > /etc/fstab || shellout
    mount /dev/pts                           || shellout

    # if hostname not set, try DNS
    if [ -z $HOSTNAME ]; then
      echo
      echo "Trying to get hostname via DNS..."
      echo
      get_hostname_by_dns
    fi

    if [ -z $HOSTNAME ]; then
      HOST_OR_IP=$IPADDR
    else
      HOST_OR_IP=$HOSTNAME
    fi

    echo
    echo
    echo "Starting sshd.  You must now go to your imageserver and issue"
    echo "the following command:"
    echo
    echo " \"pushupdate -continue-install -image <IMAGE> -client ${HOST_OR_IP}\"."
    echo
    echo
    
    # fire up sshd and wait
    sshd -f /etc/ssh/sshd_config -h /etc/ssh/ssh_host_key || shellout

    # Give sshd time to initialize before we yank the parent process
    # rug out from underneath it.
    sleep 15

    # remove rug
    exit 1
  fi
fi

# If hostname was not set in /floppy/local.cfg or by DHCP, figure it now
# get hosts file if necessary
if [ -z $HOSTNAME ]; then
  file="hosts"
  echo "Using rsync to copy $IMAGESERVER::$SCRIPTS/$file..."
  rsync -aL $IMAGESERVER::$SCRIPTS/$file /tmp/

  if [ -e /tmp/hosts ]; then
    # add escape characters to IPADDR so that it can be used to find HOSTNAME below
    IPADDR_ESCAPED=`echo "$IPADDR" | sed -e 's/\./\\\./g'`

    # get HOSTNAME by parsing hosts file
    echo "Looking for hostname of this host int /tmp/hosts by IP: $IPADDR ..."

    # Command summary by line:
    # 1: convert tabs to spaces -- contains a literal tab: <ctrl>+<v> then <tab>
    # 2: remove comments
    # 3: add a space at the beginning of every line
    # 4: get line with IP address (no more no less)
    # 5: get hostname or hostname.domain.name
    # 6: strip .domain.name if necessary -- $DOMAINNAME was set earlier by dhcp
    HOSTNAME=`
      sed 's/	/ /g' /tmp/hosts \
      | sed 's/#.*//g' \
      | sed 's/^/ /' \
      | grep " $IPADDR_ESCAPED " \
      | mawk '{print $2}' \
      | sed "s/.$DOMAINNAME//g"
    `
  fi

  # if hostname not set, try DNS
  if [ -z $HOSTNAME ]; then
    echo
    echo "Trying to get hostname via DNS..."
    echo
    get_hostname_by_dns
  fi
      
  if [ -z $HOSTNAME ]; then
    echo
    echo "Couldn't find this hosts name in /tmp/hosts or via DNS!!!"
    echo
    shellout
  fi
fi

echo
echo "This host's name is ${HOSTNAME}."

# try to get an autoinstall script based on $HOSTNAME
file="${HOSTNAME}.sh"
echo
echo "I will now try to get the autoinstall script:  $file"
rsync -aL $IMAGESERVER::$SCRIPTS/$file /tmp/
if [ $? != 0 ]; then
  echo "rsync copy of $IMAGESERVER::$SCRIPTS/$file failed!!!"

  # try to get the generic master file, since no specific file was found
  # strip off trailing numerics, and use that for an autoinstall script name
  BASE_HOSTNAME=`echo $HOSTNAME \
  | sed "s/[0-9]*$//"` 

  file="${BASE_HOSTNAME}.master"
  echo
  echo "I will now try to get the autoinstall script:  $file"
  rsync -aL $IMAGESERVER::$SCRIPTS/$file /tmp/
  if [ $? != 0 ]; then
    # we really failed, no more fallback
    echo
    echo "rsync copy of $IMAGESERVER::$SCRIPTS/$file failed!!!"
    echo
    echo "All attempts to get an autoinstall script have failed!!!"
    echo
    shellout
  fi
fi

# pass all variables set here on to the hostname.sh script
write_variables || shellout

# run the autoinstall script that was dynamically created by getimage
if [ -f /tmp/$file ]; then
  chmod 755 /tmp/$file || shellout
  echo
  echo "I will now run the autoinstall script: $file"
  echo
  /tmp/$file || shellout
fi
exit 0
