#!/bin/bash
#
# disklessrc file for initrd (Initial Ram Disk) 
#
# Copyright (C) 2003 Daniel Walsh <dwalsh@redhat.com>
#
# Copyright (C) 2005, 2006 Jason Vas Dias <jvdias@redhat.com> (Red Hat Inc.)
#
# Taken in part from James A. McQuillan <jam@McQuil.Com> linuxrc for LTSP
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#
export LD_LIBRARY_PATH=/lib:/usr/lib:/usr/kerberos/lib
MODULE=""
PCICLASS="0200 0280 0680"
#
# This function is used to load the ETHERNET module
#
loadmodules() {
    for MODULE in $1; do { 
        echo -n "Loading $MODULE module... "
        if ! `grep "^$MODULE[ \	]" /proc/modules > /dev/null`; then
            if modprobe $MODULE; then
	        echo " Loaded $MODULE OK." ;
	    else
		e=$?
		echo "Module $MODULE load failed: $e";
		return $e;
	    fi;
        else
            echo "module already loaded" 
        fi
    } done
    return 0;
}
#
# This function is used to find the correct module for hardware
# devices of PCI Class ID arguments that are on the PCI bus:
#
findModules()
{
    matchClasses=(${1})
    (/sbin/lspci -n | sed 's/Class\ //' | while read id class vendev rev;
     # pciutils < 2.2 inserts 'Class' as 2nd string; >= 2.2 does not :-( 
     do
	class=${class%:}
	if [ ${#matchClasses[@]} -gt 0 ]; then
	    found=0
	    for matchClass in ${matchClasses[@]}; 
	    do
	        [ ${matchClass} = ${class} ] &&  found=1;
	    done;
	    [ $found -eq 0 ] && continue;
	fi;
	vendor='0x'"`printf '%8s' ${vendev%:*} | /sbin/sed 's/ /0/g'`"
	device='0x'"`printf '%8s' ${vendev#*:} | /sbin/sed 's/ /0/g'`"
	(/sbin/grep '[^# ][^# ]*[\ \	][\ \	]*'$vendor'[\ \	]'$device /lib/modules/`/sbin/uname -r`/modules.pcimap) |
	(while read m rest; do echo $m; done)
     done
    ) | /sbin/uniq	    
}
#
# This function is used to load the correct ETHERNET modules
#
findhardware() {
    no_modules=13
    findModules "$PCICLASS" |
    while read MODULE; do
	echo "Found Ethernet Card Module: $MODULE"
	loadmodules $MODULE && no_modules=0 ;
    done
    return $no_modules;
}
#
# This function is used to mount files/directories from the snapshot directory 
#  over the root directory.
#
mountfile () {
    snapshotfile=/.snapshot/${2}${1}
    dir=`dirname $snapshotfile`
#
#  Check if file already exists in snapshot directory.  If not attempt to copy
#    from root directory to snapshot directory.
#
    if [ ! -e $snapshotfile ]; then
	mkdir -p $dir
	echo "${1} missing from client specific area."
	if [ -e ${1} ] ; then 
	    echo "Copying ${1}"
	    rsync -a ${1} $snapshotfile
	else 
	    echo "Creating ${1}"
	    touch $snapshotfile
	fi
    else
#  If dev directory already exists in snapshot directory, check if the root 
#   /dev directory is newer.  If it is rsync the root directory over the 
#   snapshot directory.
	if [ ${1} == "/dev" -a ${1} -nt ${dir}/dev ]; then
	    echo "RSYNC-ing /dev";
	    rsync -a ${1} $snapshotfile
	fi
    fi
#
#  Mount the snapshotfile over the root file so the client will have r/w access
#
    mount -n -o bind /.snapshot/${2}${1} ${1} 
}

echo "==============================================================================="
echo "Running /disklessrc"

echo "Mounting /proc"
/bin/mount -n -t proc /proc /proc

if [ "${INITRD_DBG}" = "1" ]; then exec /bin/bash; fi

if [ -w /proc/progress ]; then echo 40 "Detecting network card" >/proc/progress; fi

if [ -z "${ETHERNET}" ]; then export ETHERNET="eth0"; fi

#
# Bring up the loopback interface first to avoid RHEL-4-U2's kernel-2.6.9-12.2.EL
# getting an Oops panic later on when the IPv6 module is loaded
#
/sbin/ifconfig lo 127.0.0.1 netmask 255.0.0.0 up

#
#  Attempt to find and load the network card module
#
findhardware "$PCICLASS" 
#
#  load the NFS module
# 
if [ ! -d /proc/sys/fs/nfs ]; then
    loadmodules nfs
    if [ $? -ne 0 ]; then
	exec /bin/bash;
    fi;
fi;

# Mount initrd root rw, and mount /tmp
echo "Mounting /"
mount -n -o remount,rw /
mount -n -t tmpfs /dev/shm /tmp

if [ "${INITRD_DBG}" = "2" ]; then exec /bin/bash; fi

#
#  Setup dhclient to retrieve IP address
#
cat <<EOF >/tmp/dhclient.conf
interface "$ETHERNET" {
    request subnet-mask,
            broadcast-address,
            routers,
            domain-name,
            domain-name-servers,
            host-name, 
            root-path;
}
EOF

#
# dhclient runs as a daemon.  Once it acquires the info from the
# server, it runs a script called /sbin/dhclient-script.  That script
# will take care of configuring the interface.
#
if [ "${INITRD_DBG}" = "3" ]; then exec /bin/bash; fi
if [ -w /proc/progress ]; then echo 50 "Sending DHCP request" >/proc/progress; fi

# Ensure host/domain name are set to '' before running dhclient 

/sbin/hostname '(none)'
               # for network_functions' need_hostname()
/sbin/domainname ''

# Run dhclient -
#     dhclient MAY set hostname and domainname if sent in DHCP options by server
#
echo "Running dhclient"
/bin/dhclient -1 -cf /tmp/dhclient.conf -pf /tmp/dhclient.pid -lf /tmp/dhclient.leases $ETHERNET >/tmp/dhclient.out 2>&1
if [ $? -ne 0 ]; then
    exec /bin/bash
    if [ -w /proc/progress ]; then echo f >/proc/progress; fi
    echo
    cat /tmp/dhclient.out
    echo
    echo "ERROR! dhclient failed!"
    echo
    exit 1
fi
# wait for some time for dhclient to create the resolv.conf file; otherwise
# hostname lookup is pretty pointless, and we may end up doing the pivot_root
# BEFORE dhclient creates the resolv.conf file :-)
t=0
while [ "$t" -lt 20 ] && [ ! -e /etc/resolv.conf ]; do
  sleep 1;
  ((t=t+1));
done
#
if [ ! -e /etc/resolv.conf ]; then
   echo '********'
   echo 'WARNING:  /etc/resolv.conf not created - DNS service unavailable.';
   echo '********'
fi;
# Set up the hostname, domainname and SNAPSHOT name for the diskless client:
#
hname=`/bin/hostname`
dhhname="${hname}"
dname=`/bin/domainname`
if [ "${hname}" = '(none)' ]; then
   /sbin/hostname '';
   hname='';
else
   echo "DHCP host-name option received: $hname";
fi
IP=`/sbin/ip addr show ${ETHERNET} scope global primary | 
    grep '^[\ \      ]*inet.*scope global '${ETHERNET} |
    sed 's/^.*inet\ //;s/[\/\ ].*$//'`;
if [ -z "${hname}" ]; then
    if dnsptr=`(/sbin/host $IP 2>/dev/null || echo '';) | /sbin/grep 'domain name pointer' | /sbin/tail -n 1`; then 
       hname=${dnsptr//*\ };
       hname=${hname%.};
    elif [ -n "${SNAPSHOT}" ]; then
       hname=${SNAPSHOT};   
    elif [ -n "$IP" ]; then
       hname=${IP};
       echo "WARNING: no DNS PTR record found for $IP and no host-name set with DHCP";
    else
       echo "ERROR! No IP address obtained and no SNAPSHOT= setting.";
       echo;
       exit 1;
    fi;
fi;
if [ -n "${hname}" ] && [ -z "${SNAPSHOT}" ]; then
    SNAPSHOT=${hname}
elif [ -z "${SNAPSHOT}" ]; then
    SNAPSHOT=${IP}
fi;    
if [ -n "${hname}" ]; then
   if [[ $hname = *\.* ]]; then
       if [ -z "${dname}" ] && [ -z "${NISDOMAIN}" ]; then
	   # "nis-domain" dhcp option NOT sent.
 	   if [ ${hname} != ${IP} ]; then	       
	       dname=${hname#*\.}
	   else
	       # hostname was IP address.
	       # set domainname to proper dns reverse IP domain name
	       dname=${hname%\.*}
	       IFS='.' octs=($dname)
	       unset IFS
	       dname=''
	       for oct in ${octs[@]}; do
		   dname="$oct.$dname";
	       done
	       dname="${dname}in-addr.arpa."
	   fi;
	   if [ -n "${dname}" ] ; then
	       /sbin/domainname ${dname}
	       echo "Domain Name set to ${dname}"
	   fi;
       fi;      
       if [ "${NISDOMAIN}" = '(none)' ]; then
	   unset NISDOMAIN;
       fi
       if [ ${hname} != ${IP} ]; then
	   hname=${hname%%\.*};
       else
	   hname=${hname##*\.};
       fi;
   fi;
   if [ "${hname}" != "${dhhname}" ]; then
       /sbin/hostname ${hname};
       echo "Host Name set to ${hname}"
   fi;
fi;

#
# Mount the NFS root-path from the diskless server
#
if [ "${INITRD_DBG}" = "4" ]; then exec /bin/bash; fi
if [ -z "${NFSROOT}" ]; then
    if [ -w /proc/progress ]; then echo f >/proc/progress; fi
    echo
    echo "ERROR!  No root-path.  Check your DHCP configuration, to make"
    echo "        sure that the 'option root-path' is specified"
    echo
    exit 1
fi

NFS_IP=` echo ${NFSROOT} | cut -d : -f 1`
NFS_DIR=`echo ${NFSROOT} | cut -d : -f 2`

if [ -w /proc/progress ]; then echo 55 "Mounting root filesystem" >/proc/progress; fi
echo "Mounting root filesystem: ${NFS_DIR}/root from: ${NFS_IP}"

mount -n -o nolock,ro ${NFS_IP}:${NFS_DIR}/root /mnt

if [ $? -ne 0 ]; then
    if [ -w /proc/progress ]; then echo f >/proc/progress; fi
    echo
    echo "ERROR!  Failed to mount the root directory via NFS!"
    echo "        Possible reasons include:"
    echo
    echo "        1) NFS services may not be running on the server"
    echo "        2) Workstation IP does not map to a hostname, either"
    echo "           in /etc/hosts, or in DNS"
    echo "        3) Wrong address for NFS server in the DHCP config file"
    echo "        4) Wrong pathname for root directory in the DHCP config file"
    echo
    exit 1
fi

if [ "${INITRD_DBG}" = "5" ]; then exec /bin/bash; fi

#
# Now that the NFS root partition has been mounted do a pivot_root to switch
# the memory resident root with the NFS one.
#
if [ -w /proc/progress ]; then echo 60 "Pivoting root" >/proc/progress; fi

umount /proc
echo "Doing the pivot_root"

cd /mnt
/sbin/pivot_root . .oldroot

cd /
mount -n -t proc /proc /proc

#
#  Mount the snapshot directory from the server and then mount files 
#  in the snapshot/files file over the the shared ones
#

if [ "${INITRD_DBG}" = "6" ]; then exec /bin/bash; fi
echo Mounting Snapshot directories
mount -t nfs $NFS_IP:${NFS_DIR}/snapshot /.snapshot -o rw,nolock &&
{
    for i in `grep -v "^#" /.snapshot/files`; do 
	if [ -e $i ]; then 
	    mountfile $i ${SNAPSHOT}
	fi;
    done
    if [ -e /.snapshot/files.custom ]; then
	for i in `grep -v "^#" /.snapshot/files.custom`; do 
	    if [ -e $i ]; then
		mountfile $i ${SNAPSHOT}
	    fi;
	done
    fi
    RELEASE=`uname -r`
    for i in `ls /lib/modules/$RELEASE/modules.*`; do
	if [ -e $i ]; then
	    mountfile $i ${SNAPSHOT}
	fi;
    done
    /sbin/ifup lo
}

#
# Copy the files written by dhclient to new root:
#
[ -e /.oldroot/etc/resolv.conf ] &&  cp -fp /.oldroot/etc/resolv.conf /etc/resolv.conf 2>/dev/null
[ -e /.oldroot/etc/yp.conf ] && cp -fp /.oldroot/etc/yp.conf /etc/yp.conf 2>/dev/null
[ -e /.oldroot/etc/ntp.conf ] && cp -fp /.oldroot/etc/ntp.conf /etc/ntp.conf 2>/dev/null
[ -e /.oldroot/etc/ntp/step-kickers ] && cp -fp /.oldroot/etc/ntp/step-tickers /etc/ntp/step-tickers 2>/dev/null

# This will allow dhclient to be re-run for nfs interface by initscripts (keep_old_ip=yes):
cp -fp .oldroot/tmp/dhclient.leases /var/lib/dhcp/dhclient-eth0.leases 2>/dev/null
cp -fp .oldroot/tmp/dhclient.leases /var/lib/dhclient/dhclient-eth0.leases 2>/dev/null

if [ "${INITRD_DBG}" = "7" ]; then exec /bin/bash; fi

#echo "Mounting the devfs filesystem"
#mount -n -t devfs /devfs /dev
#
# Complete the network boot by killing the dhcp client.
# Umount file systems that are no longer used.
# Start the init process to run the NFS Roots bootup sequence.
#
if [ "${INITRD_DBG}" = "8" ]; then exec /bin/bash; fi

/bin/kill -TERM `cat /.oldroot/tmp/dhclient.pid`

echo "Running /sbin/init"

umount /.oldroot/tmp

echo 0x100 > /proc/sys/kernel/real-root-dev

umount /proc

# Some useful environment variables to stop initscripts / dhclient-script
# doing silly things: 
export keep_old_ip=yes
export fastboot=yes
export READONLY=yes

exec /sbin/init
