# /usr/lib/mindi/TryToBeCleverAboutInitrd
#
# Debian Plugin script for mindi to decide whether to create the initrd image
# for boot CDs as ext2 or cpio (or to die if neither is possible).
#
# Changelog:
# 21Jul04AL: - intial version
# 22Jul04AL: - even more comments
#            - deal with isolinux and sylinux configuration files
#            - break log messgae into multiple lines
# 28Jul04AL: - added missing syslinux-H.cfg config file
#            - replaced copying syslinux and isolinux config files with linking
#              to make thins clearer and to save a few bytes
#            - deal with FAILSAFE kernel here to keep mindi changes minimal
#            - create link to init explictly (init or linuxrc depending on
#              initrd image type); required for new symlinks.tgz
# 29Jul04AL: - leave mountpoint before unmounting in UseExt2 :-(
# 11Oct05AL: - check kernelpath variable as well to avoid failure when mindi is
#              run directly rather than from mondoarchive
# 29Jan06AL: - check the kernel image directly for 'magic' strings to establish
#              kernel capabilities (basically a rewrite)
# 26Feb06AL: - added functions WriteSyslinuxFile() and CreateSyslinuxFile()
#            - use above functions to create syslinux config file on the fly
#            - implement new coding rules: function definitions are prepended
#              with 'function for clarity, variables in fucntions are local,
#              variable names are <scope><type><NameInCamelCase> where scope is
#              currently one of l(ocal) and g(lobal) and <type> is currently one
#              of a(rray), c(onstant) and v(ariable)
#




###############################################################################
# Subroutines
###############################################################################


# Low-level function to create syslinux configursation file
# (returns file contents)
#
# Interface definition:
# param #1: default action (syslinux)
# param #2: timeout [1/10s] (syslinux)
# param #3: ramdisk size [kb] (kernel)
# param #4: load ramdisk [0|1] (kernel)
# param #5: prompt fpr ramdisk [0|1] (kernel)
# param #6: list of actions separated by space, actions consist of label
#           and one or multiple boot parameters all separated by commas
# param #7: additional boot parameters applicable to all actions   
#
function WriteSyslinuxFile() {

    # interface test: make sure we have seven parameters
    if [ $# -ne 7 ]; then
	Die "WriteSyslinuxFile(): Expected 7 parameters, got $#."
    fi

    # interface parameters
    local lvDefaultAction=$1
    local lvTimeout=$2
    local lvRamdiskSize=$3
    local lvLoadRamdisk=$4
    local lvPromptRamdisk=$5
    local lvActionList=$6
    local lvAdditionalParameters=$7

    # local constants (separator for parameter parsing)
    local readonly lcSeparator=","

    # internal parameters
    local lvAction=""
    local lvActionLabel=""
    

    # write header
    echo "default $lvDefaultAction"
    echo "prompt 1"
    echo "timeout $lvTimeout"
    echo "display message.txt"
    # write boot entries
    for lvAction in $lvActionList; do
	# split out label
	lvActionLabel=`echo $lvAction | cut -d"$lcSeparator" -f1`
        # remove label including separating comma from action
	lvAction=${lvAction/$lvActionLabel$lcSeparator/}
        # replace commas with spaces for actions
        lvAction=${lvAction//$lcSeparator/ }
        # write single boot entry
	echo "label $lvActionLabel"
	echo "  kernel vmlinuz"
	echo "  append initrd=initrd.img ramdisk_size=$lvRamdiskSize root=/dev/ram0 rw load_ramdisk=$lvLoadRamdisk prompt_ramdisk=$lvPromptRamdisk $lvAction $lvAdditionalParameters"
    done

}


# High-level function to create syslinux configuarion file
#
# Interface definition:
# param #1: name of output file (including path where appropriate)
# param #2: mode: 'ISO' (cd/dvd) or 'SYS' (floppy)
# param #3: ask for user confirmation (for '-H' switch): 'Y(es)' or 'N(o)'
# param #4: size of ramdisk to create in kb
# param #5: further boot parameters for all actions
#
function CreateSyslinuxFile() {

    # interface test: make sure we have five parameters
    if [ $# -ne 5 ]; then
	Die "CreateSyslinuxFile(): Expected 5 parameters, got $#."
    fi

    # interface parameters
    local lvFileName=$1
    local lvMode=$2
    local lvAsk=$3
    local lvRamdiskSize=$4
    local lvAdditionalParameters=$5

    # local constants (booleans)
    local readonly lcTrue="Y"
    local readonly lcFalse="N"


    # create files depending on whether ISO (CD-ROM) or SYS (floppy) is
    # specified and whether user wants to be prompted on restore or not
    LogIt "CreateSyslinuxFile(): Writing to file $lvFileName.\n"
    if [ $lvMode == "ISO" ]; then
	if [ $lvAsk == $lcTrue ]; then
	    WriteSyslinuxFile "interactive"                                                                         \
                              "300"                                                                                 \
                              "$lvRamdiskSize"                                                                      \
                              "1"                                                                                   \
                              "0"                                                                                   \
                              "expert,expert_mode interactive,interactive_mode compare,compare_mode nuke,nuke_mode" \
		              "$lvAdditionalParameters"                                                             \
	    > $lvFileName
	elif [ $lvAsk == $lcFalse ]; then
	    WriteSyslinuxFile "RESTORE"                                 \
                              "10000"                                   \
                              "$lvRamdiskSize"                          \
                              "1"                                       \
                              "0"                                       \
                              "RESTORE,nuke,restore expert,expert_mode" \
		              "$lvAdditionalParameters"                 \
	    > $lvFileName
	else
	    Die "CreateSyslinuxFile(): Expected 'Y' or 'N', got $lvAsk. (Mode: ISO)"
	fi
    elif [ $lvMode == "SYS" ]; then
	if [ $lvAsk == $lcTrue ]; then
	    WriteSyslinuxFile "interactive"                                                                         \
                              "300"                                                                                 \
                              "$lvRamdiskSize"                                                                      \
                              "0"                                                                                   \
                              "0"                                                                                   \
                              "expert,expert_mode interactive,interactive_mode compare,compare_mode nuke,nuke_mode" \
		              "$lvAdditionalParameters"                                                             \
	    > $lvFileName
	elif [ $lvAsk == $lcFalse ]; then
	    WriteSyslinuxFile "RESTORE"                                 \
                              "10000"                                   \
                              "$lvRamdiskSize"                          \
                              "0"                                       \
                              "0"                                       \
                              "RESTORE,nuke,restore expert,expert_mode" \
		              "$lvAdditionalParameters"                 \
	    > $lvFileName
	else
	    Die "CreateSyslinuxFile(): Expected 'Y' or 'N', got $lvAsk. (Mode: SYS)"
	fi
    else
	Die "CreateSyslinuxFile(): Expected 'ISO' or 'SYS', got $lvMode."
    fi

}


# Check kernel capabilites
#
# Interface definition:
# param #1: absolute path to kernel image
#
function GetFilesystemToUse() {

    # interface test: make sure we have one parameter
    if [ $# -ne 1 ]; then
	Die "GetFilesystemToUse(): Expected 1 parameter, got $#."
    fi

    # interface parameters
    local lvKernelImage=$1

    # local constants (filesystem magic strings)
    lcMagicCramfs="<3>cramfs: wrong magic"
    lcMagicExt2fs="<3>EXT2-fs: blocksize too small for device."
    lcMagicInitfs="<6>checking if image is initramfs..."

    # local variables
    local lvOffset
    local lvScanRes
    local lvUseFilesystem

    # say where we are.
    LogIt "  GetFilesystemToUse(): called with parameter: $lvKernelImage.\n"


    # verify that file exists
    [ ! -f $lvKernelImage ] && Die "File $lvKernelImage not found. Terminating."

    # get offet of gzip magic "1f8b0800" in file
    lvOffset=`od -vA n -t x1 $lvKernelImage | tr -d '[:space:]' | awk '{ print match($0, "1f8b0800")}'`
    [ $lvOffset -eq 0 ] && Die "gzip magic not found in file $lvKernelImage. Terminating."
    lvOffset=`expr $lvOffset / 2`
    LogIt "  GetFilesystemToUse(): gzip magic found at lvOffset $lvOffset.\n"

    # scan kernel image for initrd filessystem support
    lvScanRes=`dd ibs=1 skip=$lvOffset if=$lvKernelImage obs=1M 2>/dev/null | gunzip -c | strings | grep -e "$lcMagicCramfs" -e "$lcMagicExt2fs" -e "$lcMagicInitfs"`

    # determine which filesystem to use for initrd image: ext2fs, gzip'ed cpio (initramfs ) or cramfs
    if [ `echo $lvScanRes | grep -c "$lcMagicExt2fs"` -eq 1 ]; then
	lvUseFilesystem="ext2fs"
    elif [ `echo $lvScanRes | grep -c "$lcMagicInitfs"` -eq 1 ]; then
	lvUseFilesystem="initramfs"
    elif [ `echo $lvScanRes | grep -c "$lcMagicCramfs"` -eq 1 ]; then
	lvUseFilesystem="cramfs"
    else
	lvUseFilesystem="UNSUPPORTED"
    fi

    # say what we are using
    LogIt "  GetFilesystemToUse(): Filesytem to use for initrd is $lvUseFilesystem.\n"

    # return file system to use
    echo "$lvUseFilesystem"

}


# Setup ext2 initrd filesystem
#
function UseExt2 () {

	# say what will be used
    	LogIt "  UseExt2 ():Creating an ext2 initrd image..."

	# kernel expects linuxrc in ext2 filesystem
	( cd $mountpoint && ln -s sbin/init linuxrc )

	# unmount loop filesystem and create image file using the standard approach
	umount $mountpoint || Die "Cannot unmount $tempfile"
	dd if=$tempfile bs=1k 2> /dev/null | gzip -v9 > $rdz_fname 2> /dev/null

	# log that we are done
    	LogIt "done.\n"

}


# Setup initramfs initrd filesystem
#
function UseCpio () {

	# say what will be used
    	LogIt "  UseCpio (): Creating a gzip'ed cpio (AKA initramfs) initrd image..."

	# make sure that cpio is there
	which cpio &> /dev/null; [ $? -eq 0 ] || Die "UseCpio (): cpio not found. Please install package cpio and try again."

	# go into filesystem
	cd $mountpoint

	# kernel expects init in cpio filesystem
	ln -s sbin/init init

	# create cpio image file and unmount loop filesystem
	find . -print | cpio -o -H newc | gzip -9 > $old_pwd/$rdz_fname 2> /dev/null
	cd $old_pwd
	umount $mountpoint || Die "Cannot unmount $tempfile"

	# log that we are done
    	LogIt "done.\n"

}




###############################################################################
#Main Part
###############################################################################


# determine what filesystem to use
LogIt "Call GetFilesystemToUse() with parameter ${kernelpath} to get filesystem to use for initrd.\n"
gvFileSystem=`GetFilesystemToUse ${kernelpath}`
[ -z  gvFileSystem ] && Die "GetFilesystemToUse() failed. Terminating."

# setup things according to filsystem to use
LogIt "Creating $gvFileSystem initrd filesystem.\n"
case "$gvFileSystem" in
	"ext2fs")
		UseExt2 ;;
	"initramfs")
		UseCpio	;;
	*)
		Die "Filesystem $gvFileSystem not supported for initrd image. Terminating." ;;
esac

# create syslinux configuration files
(cd $MINDI_LIB && rm -f isolinux.cfg isolinux-H.cfg syslinux.cfg syslinux-H.cfg)
CreateSyslinuxFile "$MINDI_LIB/isolinux.cfg"   "ISO" "Y" "24000" ""
CreateSyslinuxFile "$MINDI_LIB/isolinux-H.cfg" "ISO" "N" "24000" ""
CreateSyslinuxFile "$MINDI_LIB/syslinux.cfg"   "SYS" "Y" "24000" ""
CreateSyslinuxFile "$MINDI_LIB/syslinux-H.cfg" "SYS" "N" "24000" ""

# log that we are done
LogIt "initrd filesystem created.\n"
