#!/bin/bash

# $Id: fai-cd 4820 2007-12-12 14:19:12Z lange $
#*********************************************************************
#
# fai-cd -- make a fai CD, a bootable CD that performs the FAI
#
# This script is part of FAI (Fully Automatic Installation)
# (c) 2004-2007 by Thomas Lange, lange@informatik.uni-koeln.de
# Universitaet zu Koeln
#
# based on a script called make-fai-bootcd by Niall Young <niall@holbytla.org>
#
#*********************************************************************
# 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.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#*********************************************************************

set -e 
version="fai-cd 3.3, 26 november 2007"

forceremoval=0;
burn=0
keep=0
makeiso=1
makeusb=0
hidedirs="/usr/share/locale /usr/share/doc /var/lib/apt /var/cache/apt /usr/share/man /var/lib/dpkg/info /media/mirror/aptcache /media/mirror/.apt-move"

# we need FAI_CONFIGDIR, NFSROOT

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
usage() {

    cat <<-EOF
	$version. Copyright (C) 2004-2007 Thomas Lange
	Report bugs to <fai@informatik.uni-koeln.de>.

	Usage: fai-cd [OPTIONS] -m MIRRORDIR ISONAME
	Create a fai CD, a bootable CD that performs the FAI.
	Read the man pages pages fai-cd(8).
EOF
exit 0
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
die() {

    local e=$1   # first parameter is the exit code
    shift

    echo "ERROR: $@"    # print error message
    exit $e
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
create_grub_image() {

    mkdir -p $tmp/boot/grub $nfsrootdir/live/filesystem.dir

    [ -d $NFSROOT/usr/lib/grub ] && cp $NFSROOT/usr/lib/grub/*-pc/stage2_eltorito $tmp/boot/grub/
    [ -d $NFSROOT/usr/lib/grub ] && cp $NFSROOT/usr/lib/grub/*-pc/stage{1,2} $tmp/boot/grub/
    cp $grub_config $tmp/boot/grub/menu.lst
    # insert date into grub menu
    perl -pi -e "s/_VERSIONSTRING_/   $isoversion     /" $tmp/boot/grub/menu.lst
    cp $NFSROOT/boot/vmlinuz-$kernelversion $tmp/boot/vmlinuz
    cp $NFSROOT/boot/initrd.img-$kernelversion $tmp/boot/initrd.img
    cp $NFSROOT/boot/config-$kernelversion $tmp/boot/
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
customize_nfsroot() {

    # hide some dirs to save space and make the CD image smaller
    local d

    mkdir $tmp/empty
    for d in $hidedirs; do
	if [ -d $nfsrootdir/$d ]; then
	    [ "$debug" ] && echo "hiding $d"
	    mount --bind $tmp/empty $nfsrootdir/$d
	fi
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
unhide_dirs() {

    set +e
    for d in $hidedirs; do
	if [ -d $nfsrootdir/$d ]; then
	    [ "$debug" ] && echo "disclosing $d"
	    umount $nfsrootdir/$d 2>/dev/null
	fi
    done
    set -e
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
umount_dirs() {

    set +e
    local d
    local dirs="boot $FAI media/mirror etc/apt/sources.list"
    for d in $dirs; do
	umount $nfsrootdir/$d 2>/dev/null
    done
    rm -f $nfsrootdir/etc/RUNNING_FROM_FAICD
    [ -d $nfsrootdir ] && umount $nfsrootdir
    set -e
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
sleep_now() {

	sleep 6666 || true
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
mkiso() {

    if [ $makeiso -eq 1 ]; then
	echo "Writing FAI CD-ROM image to $isoname. This may need some time."
	mkisofs -V "$vname" -A "$aname" -log-file /dev/null -quiet -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 -boot-info-table -o $isoname $tmp || die 12 "mkisofs failed." 
	echo -n "ISO image size and filename: "; du -h $isoname
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
mkusb(){

    if [ $makeusb -eq 1 ]; then
	# TODO: If usbdir is a device (matches /dev/) mount it
	[ -d $usbdir ] || die 17 "$usbdir does not exist."
	echo "Writing FAI data to USB device $usbdir"
	cp -a $tmp/live $tmp/boot $usbdir 2>/dev/null || true
	echo $isoversion > $usbdir/.FAI-CD-VERSION

	# now make the USB device bootable
	rootpartition=$(find_fai_data 2>/dev/null | grep -A 1 'find /.FAI-CD-VERSION' | grep -v 'find /.FAI-CD-VERSION')
	usbdev=$(echo $rootpartition | sed -e 's/,[[:digit:]]//')
	echo "Root partition is $rootpartition, device is: $usbdev"
	if [ -n "$rootpartition" -a -n "$usbdev" ]; then
	    echo "Installing grub."
	    grub --batch >/dev/null << EOM
root $rootpartition
setup $usbdev
EOM
	else
	    echo "Device could not be detemined. Installing grub will be skipped."
	fi
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
create_iso() {

    # Create the El Torito bootable iso9660 cdrom image
    # or copy all FAI data to directory (for USB)

    echo "Bind mounting all required parts"

    mount --bind $NFSROOT $nfsrootdir && echo "NFSROOT $NFSROOT mounted"
    mkdir -p $nfsrootdir/media/mirror || true

    > $nfsrootdir/etc/RUNNING_FROM_FAICD
    mount --bind $FAI_CONFIGDIR $nfsrootdir/$FAI && echo "Config space $FAI_CONFIGDIR mounted"
    mount --bind $mirrordir $nfsrootdir/media/mirror && echo "Mirror $mirrordir mounted"
# TODO: customize /etc/apt, copy apt preferences etc.

    # this will be the sources.list for the CD
    tmp1=$(mktemp) || exit 12
    cat > $tmp1 <<EOF
# mirror location for fai CD, file generated by fai-cd
EOF

    dists=$(find $mirrordir -name "Packages*" | grep binary | sed 's/binary-.*//' | \
         sed "s#$mirrordir/*dists/##" | xargs -n 1 dirname | uniq )

    for i in $dists ; do
	comp=$(find $mirrordir/dists/$i -maxdepth 2 -type d -name "binary-*" | \
        sed -e "s#$mirrordir/*dists/$i/##" -e 's#/binary-.*##' | tr '\n' " ")
	echo "deb file:/media/mirror $i $comp" >> $tmp1
    done

    mount --bind $tmp1 $nfsrootdir/etc/apt/sources.list
    customize_nfsroot

    if [ $keep -eq 1 ]; then
	echo "fai-cd script stopped for 6666 seconds. The filesystem is now available in $tmp."
	echo "To continue the script and call the cleanup call: pkill -f 'sleep 6666'"
	sleep_now 2>/dev/null || true
	echo "Continue cleanup."
    fi

    mkiso
    mkusb
    rm $tmp1
    unhide_dirs
    umount_dirs
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
find_fai_data() {

  grub --batch <<EOM
find /.FAI-CD-VERSION
EOM
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
burniso() {

    cdrecord -v -eject $isoname
}
# - - - - - - - - - - - - - - - - - - - - - - - - - -
# main program

# Parse commandline options
while getopts "nkfhg:bm:C:u:" opt ; do
    case "$opt" in
        C)  cfdir=$OPTARG ;;
	f)  forceremoval=1 ;;
	h)  usage ;;
	g)  grub_config="$OPTARG" ;;
	k)  keep=1 ;;
	m)  mirrordir="$OPTARG" ;;
	n)  makeiso=0 ;;
	b)  burn=1 ;;
	u)  usbdir="$OPTARG"
	    makeusb=1
	    makeiso=0
	    [ -e $usbdir/live ] && die 11 "Please remove $usbdir/live. Aborting."
	    ;;
	?)  usage ;;
    esac
done
shift $(($OPTIND - 1))
isoname=$1

[ $makeiso -eq 1 -a "$#" -eq 0 ]        && die 2 "Please specify the output file for the ISO image."
[ -z "$mirrordir" ]   && die 4 "Please specify the directory of your mirror using -m"
[ -d "$mirrordir" ]   || die 5 "$mirrordir is not a directory"
[ -f "$isoname" ]     && [ $forceremoval -eq 1 ] && rm $isoname
[ -f "$isoname" ]     && die 3 "Outputfile $isoname already exists. Please remove it or use -f."
[ $(id -u) != "0" ]   && die 9 "Run this program as root."

if [ $makeiso -eq 1 ]; then
    [ -x "$(which mkisofs)" ] || die 8 "mkisofs not found. Please install package."
fi

# use FAI_ETC_DIR from environment variable
if [ -n "$FAI_ETC_DIR" -a -z "$cfdir" ]; then
    echo "Using environment variable \$FAI_ETC_DIR."
fi
cfdir=${FAI_ETC_DIR:=/etc/fai}
cfdir=$(readlink -f $cfdir) # canonicalize path
if [ ! -d "$cfdir" ]; then
    echo "$cfdir is not a directory"
    exit 6
fi
[ "$verbose" ] && echo "Using configuration files from $cfdir"
. $cfdir/fai.conf
export NFSROOT=$(source $cfdir/make-fai-nfsroot.conf; echo $NFSROOT)
NFSROOT="$NFSROOT/live/filesystem.dir"

[ -d "$NFSROOT/etc/fai" ] || die 10 "Please create NFSROOT by calling make-fai-nfsroot or fai-setup."

# if -g is a file name, add prefix

[ -z "$grub_config" ] && grub_config="$cfdir/menu.lst" # set default if undefined
# if grub_config contains / do not change it, else add prefix $cfdir
echo $grub_config | grep -q '/' || grub_config="$cfdir/$grub_config"
[ -f "$grub_config" ] || die 13 "Grub menu file $grub_config not found."

[ -z "$FAI_CONFIGDIR" ]  && die 14 "Variable \$FAI_CONFIG not set."
[ -d $FAI_CONFIGDIR ] || die 15 "Can't find config space $FAI_CONFIGDIR."
[ -d $FAI_CONFIGDIR/class ] || die 16 "Config space $FAI_CONFIGDIR seems to be empty."

tmp=$(mktemp -t -d fai-cd.XXXXXX) || exit 13
nfsrootdir=$tmp/live/filesystem.dir
kernelversion=$(ls -tr $NFSROOT/boot/vmlinu?-* | tail -1 | sed -e 's#.*/vmlinuz-##')

faiversion=$(dpkg --root=$NFSROOT -l fai-client|grep fai-client|awk '{print $3}')
isoversion="FAI $faiversion -- build $(date '+%c')"
vname="Fully Automatic Installation CD"
aname="Fully Automatic Installation by Thomas Lange, $isoversion"

create_grub_image

trap "unhide_dirs;umount_dirs" EXIT ERR
create_iso
rm -rf $tmp
[ "$burn" -eq 1 ] && burniso
exit 0
