#! /bin/bash

# $Id: make-fai-nfsroot 3027 2005-11-10 22:37:16Z lange $
#*********************************************************************
#
# make-fai-nfsroot -- create nfsroot directory and add additional packages
#
# This script is part of FAI (Fully Automatic Installation)
# (c) 2000-2005 by Thomas Lange, lange@informatik.uni-koeln.de
# Universitaet zu Koeln
#
#*********************************************************************
# 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.
# 
# A copy of the GNU General Public License is available as
# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html.  You
# can also obtain it by writing to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#*********************************************************************


PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin

if [ `id -u` -ne 0 ]; then
    echo "Run this program as root."
    exit 9
fi

merror="properly"
# option e currently does nothing
while getopts ervc:f:kK opt ; do
    case "$opt" in
        c) cfdir=$OPTARG ;;
        v) verbose=1 ; v=-v ;;
        r) recover=1 ;;
        f) cfg=$OPTARG ;;
        k) kinstall=1 ;;
        K) kremove=1; kinstall=1 ;;
	e) expert=1 ;;
        ?) exit 5 ;; # error in option parsing
    esac
done

set -e

[ -z "$cfdir" ] && cfdir=/etc/fai
if [ ! -d "$cfdir" ]; then
    echo "$cfdir is not a directory"
    exit 6
fi
[ "$verbose" ] && echo "Using configuration files from directory $cfdir"
if [ -n "$cfg" ]; then
    . $cfdir/$cfg
else
    . $cfdir/fai.conf
fi

# read config file for this tool
if [ -f "$cfdir/make-fai-nfsroot.conf" ]; then
    . $cfdir/make-fai-nfsroot.conf
else
    echo "Can't read $cfdir/make-fai-nfsroot.conf"
    exit 8
fi

if [ -z "$NFSROOT" ]; then
    echo "\$NFSROOT is not set. Please check your settings in $cfdir/fai.conf."
    exit 4
fi

if [ "$FAI_SOURCES_LIST" ]; then
    echo "The usage of the variable \$FAI_SOURCES_LIST is deprecated. Please use sources.list now."
    exit 3
fi

if [ ! -s "$cfdir/sources.list" -a ! -f /etc/apt/sources.list ]; then
    echo "Neither $cfdir/sources.list nor /etc/apt/sources.list exists."
    echo "I think something is wrong. Can't continue."
    exit 7
fi

kfile="vmlinuz"
case `dpkg --print-installation-architecture` in
    i386)
	arch_packages="grub lilo kudzu dmidecode hwtools read-edid" ;;

    amd64)
	arch_packages="grub lilo kudzu dmidecode" ;;

    ia64)
	arch_packages="elilo gnu-efi efibootmgr" ;;

    sparc)
	arch_packages="silo sparc-utils" ;;

    powerpc)
	arch_packages="" ;;

    alpha)
	arch_packages="aboot" ;;

    *)  arch_packages="" ;;
esac
packages="$packages
$arch_packages"
 
ROOTCMD="chroot $NFSROOT"

RUNDIR=/var/run/fai/make-fai-nfsroot
[ ! -d $RUNDIR ] && mkdir -p $RUNDIR
LIBFAI=/usr/lib/fai
SHAREFAI=/usr/share/fai
export DEBIAN_FRONTEND=noninteractive
[ "$recover" ] || rm -rf $RUNDIR/*

trap "umount_dirs" EXIT
trap "bad_exit" ERR
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bad_exit() {

    merror="with errors"
    echo "An error occured during make-fai-nfsroot."
    echo "Please fix the error or try make-fai-nfsroot -v"
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
die() {

    echo "$@"
    exit 99
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
call_with_stamp() {

    local func=$1
    local stamp=$RUNDIR/$func
    # call subroutine
    [ "$recover" -a -f $stamp ] && return 0 
    "$@"
    > $stamp
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
call_verbose() {

    if [ "$verbose" ]; then
        "$@"
    else
        "$@" > /dev/null
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
install_kernel_nfsroot() {

    rm -rf $NFSROOT/boot/*-$KERNELVERSION $NFSROOT/lib/modules/$KERNELVERSION
    # since woody we can install the kernel using dpkg -i
    echo "do_boot_enable=no" > $NFSROOT/etc/kernel-img.conf
    dpkg -x $KERNELPACKAGE $NFSROOT
    # if $NFROOT/proc/modules exists, then update-modules calls depmod -a without
    # these special flags; so umount first
    [ -e $NFSROOT/proc/modules ] && umount $NFSROOT/proc
    chroot $NFSROOT update-modules
    chroot $NFSROOT depmod -qaF /boot/System.map-$KERNELVERSION $KERNELVERSION || true
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
setup_ssh() {

    # nothing to do if no ssh is available in nfsroot
    [ -f $NFSROOT/var/lib/dpkg/info/ssh.list ] || return 0
    mkdir -p -m 700 $NFSROOT/root/.ssh
    if [ -n "$LOGUSER" ] ; then
        loguserhome=`eval "cd ~$LOGUSER 2>/dev/null && pwd;true"`
    # is copying of *.pub important?
        [ -f $loguserhome/.ssh/known_hosts ] && cp $loguserhome/.ssh/known_hosts $NFSROOT/root/.ssh/known_hosts
        [ -d $loguserhome/.ssh ] && {
	    [ -f $loguserhome/.ssh/id_dsa ] &&
	       cp -p $loguserhome/.ssh/id_dsa* $NFSROOT/root/.ssh/
	    [ -f $loguserhome/.ssh/id_rsa ] &&
	       cp -p $loguserhome/.ssh/id_rsa* $NFSROOT/root/.ssh/
	    cp -p $loguserhome/.ssh/*.pub $NFSROOT/root/.ssh/
	}
    fi

    # enable root login
    perl -pi -e 's/PermitRootLogin no/PermitRootLogin yes/' $NFSROOT/etc/ssh/sshd_config
    if [ -f "$SSH_IDENTITY" ]; then
        cp $SSH_IDENTITY $NFSROOT/root/.ssh/authorized_keys
	chmod 0644 $NFSROOT/root/.ssh/authorized_keys
        echo You can log into install clients without password using $SSH_IDENTITY
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
copy_fai_files() {

    # copy to nfsroot
    perl -pi -e "s#^root::#root:${FAI_ROOTPW}:#" etc/passwd
    mkdir -p $NFSROOT/$SHAREFAI $NFSROOT/$LIBFAI $NFSROOT/etc/fai
    cd $NFSROOT
    fdivert /etc/dhcp3/dhclient-script /etc/dhcp3/dhclient.conf
    cp -p $SHAREFAI/etc/dhclient.conf etc/dhcp3
    cp -Rpv $cfdir/* $NFSROOT/etc/fai
    # remove some files that should not be copied
    rm -f $NFSROOT/etc/fai/make-fai-nfsroot.conf
    [ -f $cfdir/.cvspass ] && cp -p $v $cfdir/.cvspass $NFSROOT/.cvspass
    cp -p $LIBFAI/sbin/* sbin/
    cp -p /usr/sbin/fai usr/sbin/
    mv sbin/dhclient-script etc/dhcp3
    cp -p /usr/sbin/{ftar,fcopy,install_packages} usr/sbin/
    cp -p /usr/bin/{fai-do-scripts,fai-class,fai-debconf} usr/bin/
    cp -p $LIBFAI/* usr/lib/fai 2>/dev/null || true  # cp will complain about directories
    cp -p $SHAREFAI/etc/fai_modules_off etc/modutils/

    cp -p $SHAREFAI/subroutines* usr/share/fai
    cp -p $v $SHAREFAI/etc/apt.conf $NFSROOT/etc/apt
    cp -p /usr/share/perl5/Debian/Fai.pm usr/share/perl5/Debian/
    $ROOTCMD shadowconfig on
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
call_debootstrap() {
    
    echo "Creating nfsroot for $1 using debootstrap"
    [ "$verbose" ] && echo "Calling debootstrap $1 $NFSROOT $2"
    yes '' | LC_ALL=C call_verbose debootstrap $FAI_DEBOOTSTRAP_OPTS $1 $NFSROOT $2 || true
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
create_base() {

    if [ "$FAI_DEBOOTSTRAP" ]; then
        call_with_stamp call_debootstrap $FAI_DEBOOTSTRAP
        $ROOTCMD apt-get clean
	rm -f $NFSROOT/etc/resolv.conf
        echo "Creating base.tgz"
        tar -l -C $NFSROOT -cf - --exclude var/tmp/base.tgz . | gzip > $NFSROOT/var/tmp/base.tgz
    else
	die "\$FAI_DEBOOTSTRAP not defined."
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
create_nfsroot() {

    mkdir -p $NFSROOT/$FAI
    cd $NFSROOT || die "Error: Can't cd to $NFSROOT"

    call_with_stamp create_base
    # save the list of all packages in the base.tgz
    $ROOTCMD dpkg --get-selections | egrep 'install$' | awk '{print $1}' > var/tmp/base-pkgs.lis
    echo $arch_packages > $NFSROOT/var/tmp/packages.arch

    if [ "$FAI_DEBMIRROR" ]; then
	[ "$verbose" ] && echo "Mounting $FAI_DEBMIRROR to $NFSROOT/$MNTPOINT."
        mkdir -p $NFSROOT/$MNTPOINT
        mount -o ro,noatime,rsize=8192 $FAI_DEBMIRROR $NFSROOT/$MNTPOINT || \
                die "Can't mount $FAI_DEBMIRROR to $NFSROOT/$MNTPOINT."
        fi

    # hoaks some packages
    # liloconfig, dump and raidtool2 needs these files
    echo "# UNCONFIGURED FSTAB FOR BASE SYSTEM" > etc/fstab
    > etc/raidtab
    mkdir -p lib/modules/$KERNELVERSION           # dirty trick to hoax lvm
    >  lib/modules/$KERNELVERSION/modules.dep  # dirty trick to hoax lvm
    echo 'NTPSERVERS=""' > etc/default/ntp-servers

    [ -d $NFSROOT/var/state ] || mkdir $NFSROOT/var/state
    [ "$verbose" ] && echo "Try to copy $cfdir/sources.list or /etc/apt/sources.list."
    cp -v $cfdir/sources.list $NFSROOT/etc/apt/sources.list || \
	cp -v /etc/apt/sources.list $NFSROOT/etc/apt/sources.list || \
	{ echo "No sources.list file found."
	  echo "I think something is wrong. But I'll try to continue."
        }

    echo "127.0.0.1 localhost" >> etc/hosts
    echo "$NFSROOT_ETC_HOSTS" >> etc/hosts
    [ -f /etc/apt/preferences ] && cp -v /etc/apt/preferences $NFSROOT/etc/apt
    [ -f $cfdir/preferences ] && cp -v $cfdir/preferences $NFSROOT/etc/apt
    echo "Upgrading $NFSROOT"
    LC_ALL=C call_verbose call_with_stamp upgrade_nfsroot  
    echo "Adding additional packages to $NFSROOT:"
    echo "$packages"
    LC_ALL=C call_verbose call_with_stamp add_packages_nfsroot
    call_with_stamp copy_fai_files

    # set timezone in nfsroot
    timezone=$(readlink /etc/localtime | sed 's%^/usr/share/zoneinfo/%%')
    echo $timezone > etc/timezone 
    rm -f etc/localtime && ln -sf /usr/share/zoneinfo/$timezone etc/localtime

    # make little changes to nfsroot, because nfsroot is
    # read only for the install clients 
    rm -rf etc/mtab var/run etc/sysconfig
    ln -s /proc/mounts etc/mtab
    ln -s /tmp/var/run var/run
    ln -sf /tmp/var/state/discover var/state/discover
    ln -sf /tmp/var/lib/discover var/lib/discover
    ln -s /tmp/etc/syslogsocket dev/log
    ln -sf /tmp/etc/resolv.conf etc/resolv.conf
    ln -sf /tmp etc/sysconfig
    ln -s /usr/sbin/fai etc/init.d/rcS
    ln -sf /dev/null etc/network/ifstate
    # for nis
    [ -d var/yp ] && ln -s /tmp/binding var/yp/binding

    # turn off logging of loading kernel modules
    [ -d var/log/ksymoops/ ] && rmdir var/log/ksymoops/
    ln -s /dev/null var/log/ksymoops

    # definition for loopback device
    echo "iface lo inet loopback" > etc/network/interfaces

    echo "*.* /tmp/fai/syslog.log" > etc/syslog.conf
    cat >> root/.profile <<-EOF
        PATH=/usr/local/sbin:/usr/local/bin:/usr/lib/fai:/bin:/sbin:/usr/bin:/usr/sbin:
        export PATH
	. $SHAREFAI/subroutines
	. $SHAREFAI/subroutines-$OS_TYPE
	set -a
        . /tmp/fai/variables.sh 2>/dev/null
EOF

    call_verbose call_with_stamp setup_ssh

    cat >$NFSROOT/etc/rc2.d/S01fai_abort <<-EOF
        #!/bin/sh
        echo FAI: installation aborted.
        echo reboot with: faireboot
        echo or after a logout
        sh
        cd /
        umount -ar
        reboot -dfi
EOF
    chmod a+rx $NFSROOT/etc/rc2.d/S01fai_abort

    echo -e "\n$FAI_LOCAL_REPOSITORY" >> etc/apt/sources.list
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
upgrade_nfsroot() {

    cp -p $v /etc/resolv.conf $NFSROOT/etc/resolv.conf-installserver
    cp -p $v /etc/resolv.conf $NFSROOT/etc/resolv.conf # this is needed during make-fai-nfsroot
    $ROOTCMD apt-get update
    $ROOTCMD apt-get -fyu install
    $ROOTCMD apt-get check
    rm -rf $NFSROOT/etc/apm
    mount -t proc /proc $NFSROOT/proc

    # fake start-stop-dameon
    fdivert /etc/init.d/rcS /sbin/start-stop-daemon /sbin/discover-modprobe
    cp /sbin/fai-start-stop-daemon $NFSROOT/sbin/
    ln -s /sbin/fai-start-stop-daemon $NFSROOT/sbin/start-stop-daemon
    $ROOTCMD apt-get -y dist-upgrade
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
add_packages_nfsroot() {

    $ROOTCMD apt-get -y --fix-missing install $packages </dev/null
    if [ -n "$NFSROOT_PACKAGES" ] ; then
        LC_ALL=C $ROOTCMD apt-get -y --fix-missing install $NFSROOT_PACKAGES </dev/null
    fi
    $ROOTCMD apt-get clean
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
umount_dirs() {

    [ -f $NFSROOT/usr/sbin/dpkg-divert ] && 
       LC_ALL=C $ROOTCMD dpkg-divert --package fai --rename --remove /sbin/discover-modprobe
    cd /
    sleep 2
    [ -d $NFSROOT/proc/self ] && umount $NFSROOT/proc 
    [ -d $NFSROOT/proc/self ] && umount $NFSROOT/dev/pts
    if [ "$FAI_DEBMIRROR" ]; then
        test -d $NFSROOT/$MNTPOINT && umount $NFSROOT/$MNTPOINT || true
    fi
    # show directories still mounted on nfsroot
    mount | grep " on $NFSROOT " || true
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
get_kernel_version() {

    local package=$1
    KERNELVERSION=`dpkg --info $package | grep "Package: kernel-image" | sed -e 's/.*kernel-image-'//`
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
setup_bootp(){

    if [ -x "`which mkelf-linux`" ]; then
	mkelf-linux --ip=any --output=/boot/fai/installimage \
	  $NFSROOT/boot/$kfile-$KERNELVERSION 
    else
        echo "Command mkelf-linux not found. Can not set up BOOTP booting. Please install the package mknbi and rerun fai-setup."
	return
    fi

    # imggen is free software from 3com - use ver1.00: 1.01 produces "Image too Big" errors.
    # it converts netboot images to images which are bootable by 3com network cards
    if [ -x "`which imggen`" ]; then
	imggen -a /boot/fai/installimage /boot/fai/installimage_3com
    fi
    echo "BOOTP environment prepared."
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
setup_dhcp(){

    # pxe and dhcp environment
    local pxebin=/usr/lib/syslinux/pxelinux.0
    cp -p $NFSROOT/boot/$kfile-$KERNELVERSION /boot/fai/$kfile-install
    [ -f $pxebin ] && cp $pxebin /boot/fai
    [ -d /boot/fai/pxelinux.cfg ] || mkdir -p /boot/fai/pxelinux.cfg || true
    echo "DHCP environment prepared. If you want to use it, you have to enable the dhcpd and the tftp-hpa daemon."
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
fdivert() {

    local item
    for item in "$@"; do
	LC_ALL=C $ROOTCMD dpkg-divert --quiet --package fai --add --rename $item
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
kernel_install() {

# Install the kernel package
if [ -f $KERNELPACKAGE ]; then
    # determine kernel version
    get_kernel_version $KERNELPACKAGE

    # create tftp boot images
    call_with_stamp install_kernel_nfsroot

    # setup for DHCP, BOOTP or both
    [ "x$FAI_BOOT" = "x" ] && FAI_BOOT="dhcp bootp"
    
    for bootopt in $FAI_BOOT; do
    	case $bootopt in
    		dhcp|DHCP)      
			call_with_stamp setup_dhcp ;;
    		bootp|BOOTP)      
			call_with_stamp setup_bootp ;;
		*)
			echo "Unknown boot option" ;;
    	esac
    done
else
    merror="with errors"
    echo "Error. Kernel package $KERNELPACKAGE not found."
    echo "No install kernel installed in /boot/fai."
    echo "No kernel modules available in nfsroot."
fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# main routine

[ -z "$KERNELPACKAGE" ] && die "\$KERNELPACKAGE is not set. Aborting."

# remove all kernels from nfsroot
[ -n "$kremove" ] && {
    echo "Removing all kernels from NFSROOT."
    rm -f $NFSROOT/boot/{System.map,vmlinuz,config}*
    rm -rf $NFSROOT/lib/modules/2.*
}

# just install a new kernel to the nfsroot
[ -n "$kinstall" ] && {
    trap "true" EXIT
    echo "Installing new kernel into the nfsroot."
    kernel_install
    echo "New kernel from" $KERNELPACKAGE "installed into the nfsroot."
    exit
}

echo Creating FAI nfsroot can take a long time and will
echo need more than $nfssize disk space in $NFSROOT.

# Kill the directory if not in recover mode
if [ -d $NFSROOT/$FAI -a ! "$recover" ]
then
    echo $NFSROOT already exists. Removing $NFSROOT
    umount $NFSROOT/dev/pts 1>/dev/null 2>&1 || true
    rm -rf $NFSROOT/.??* $NFSROOT/*
    # also remove files $NFSROOT/.? but not . and ..
    find $NFSROOT ! -type d -xdev -maxdepth 1 | xargs -r rm -f
fi

# Create a new nfsroot
if [ ! -x "`which debootstrap`" ]; then
    die "Can't find debootstrap command. Aborting."
fi
call_with_stamp create_nfsroot

kernel_install

umount_dirs
trap "true" EXIT
echo "make-fai-nfsroot finished $merror."
exit 0
