#!/bin/bash
#
#       mkrescue - create a boot floppy or cd image with the current kernel
#
#       Copyright 2001-2005 John Coffman
#       Copyright 2010-2011 Joachim Wiedorn <ad_debian at joonet.de>
#       
#       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., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

debug=false
#debug=true

# set the version number on this command
version=3.8

# set the version of LILO required to run
major=22
minor=8
revision=0

log=$(pwd)/mkrescue.log
clog=$(pwd)/mkrescue.conf.log


usage () {
    cat <<EOF

usage:	`basename $0` [--help]
	`basename $0` [--version]
	`basename $0` [--device <device>] [--fast] [--fs ext2|msdos|minix]
	    [--image <label>] [--install text|menu] [--keymap <keymap.ktl>] 
	    [--initrd <file> --kernel <file>] [--append <string>]
	    [--root <device>] [--nocompact] [--noformat]
	    [--iso] [--size 1440|1200|2880|HD]

  --device   is the floppy drive; e.g.,  /dev/fd0
  --fast     specifies creation using a loopback device, which may be faster
  --fs       is the filesystem to make on the device; e.g.,  ext2
  --help     prints this helpfile
  --iso      create a bootable ISO image to burn to a CD-R or CD-RW
  --keymap   is the keyboard translation table; default to same as lilo.conf
  --noformat  bypasses creation of a new filesystem on device
  --nocompact  omits lilo map file compaction
  --size     is assumed to be 1440 (1.44M), unless 1200 or 2880 is specified
             HD may be specified for ISO images
  --image    specifies the label of the kernel/initrd if not the default
  --install  'text' is the default for floppies, 'menu' for ISO images
  --initrd   and --kernel  are the initial ramdisk & kernel files
  --append   is a string used to specify kernel options
  --root     is the root filesystem for the boot floppy; e.g., current
  --version  prints the version number of `basename $0`
  --debug    provide verbose output and pausing after defined steps

Used without any arguments, `basename $0` will use the default kernel in
/etc/lilo.conf, the companion initrd (if any), and the specified root
filesystem to make a bootable rescue floppy.

EOF
    exit $1
}

if [ $debug != false ]; then
	lilo=$(pwd)/lilo
	config=$(pwd)/lilo.conf
else
	lilo=/sbin/lilo
	config=/etc/lilo.conf
fi

if [ ! -r "$config" ] ; then
       echo "$0: Cannot read the configuration file $config (are you root?)"
       exit 1
fi


compact=-c
device=/dev/fd0
fs=ext2
tmpbase="`mktemp -dt $(basename $0).XXXXXXXXXX`" || {
    echo "Could not create temporary directory."; exit 1
}
mount="$tmpbase/mkrescue-flp"
mfile="$tmpbase/mkrescue-emu"
mtemp="$tmpbase/mkrescue-tmp"
mkdir "$mount"
touch "$mfile" "$mtemp"

loopback=loop0
looppart=loop1
install=text
isoimage=no
format=yes
image=
root=
bios=0x00
fast=slow
size=0
heads=2
sectors=18
cylinders=80
hdsize=16384
bootcmd=
append=
initrd=
boot=/boot
diag=no
master=

VERSION=$($lilo -V | awk '{print $3}' | sed -e "s/[^a-zA-Z0-9.]//g")

NVERSION=$(echo $VERSION | sed "s/\\./ /g")

DASH=$(echo $VERSION | sed "/-/s/.*-//" )
if [ "$DASH" = "$VERSION" ]; then
	DASH=0
else
	NVERSION=$(echo $NVERSION | sed "s/-.*//")
fi
MAJOR=$(echo $NVERSION | sed "s/ .*//")
MINOR=$(echo $NVERSION | sed -e "s/$MAJOR //" -e "s/ .*//" )
if [ "$MINOR" = "$NVERSION" ]; then
	MINOR=0
fi
REVISION=$(echo $NVERSION | sed "s/$MAJOR $MINOR //")
if [ "$REVISION" = "$NVERSION" ]; then
	REVISION=0
fi
REVISION=$(echo $REVISION | sed "s/ .*//")
if [ "$MINOR" -gt 49 ]; then MINOR=$(expr $MINOR % 10); fi

if [ $debug = true ]; then
echo ""
echo VERSION $VERSION
echo ""
echo MAJOR $MAJOR
echo MINOR $MINOR
echo REVISION $REVISION
echo DASH $DASH
echo ""
fi

#if [ "$MAJOR" -lt "$major" \
#	-o "$MINOR" -lt "$minor" \
# 		-o $REVISION -lt "$revision" ]
skip=false
if [ "$REVISION" -lt "$revision" ]
then
skip=true
#echo $REVISION lt $revision
fi
if [ "$MINOR" -gt "$minor" ]
then
skip=false
#echo $MINOR gt $minor
fi
if [ "$MAJOR" -gt "$major" ]
then
skip=false
#echo $MAJOR gt $major
fi
if [ "$skip" = "true" ]
then
    echo `basename $0` version $version
    echo "LILO version $major.$minor.$revision (or newer) is required."
    exit 0
fi

umount $mount 2>/dev/null
rm -rf $mount/*
> $mfile

master=`mount | grep " / " | cut -d " " -f 1`
master=`echo $master | sed "s/part[0-9]*$/disc/"`
master=`echo $master | sed "s/[0-9]*$//"`
if [ ! -b $master ]; then master=`echo $master | sed "s/p$//"`; fi
if [ ! -b $master ]; then master=""  ; fi


while [ $# -gt 0 ]; do
    case $1 in
	--append)
	    shift
	    append=$1
	    ;;
	--debug)
	    debug=true
	    ;;
	--device)
	    shift
	    device=$1
	    ;;
	--fast)
	    fast=fast
	    ;;
	--fs)
	    shift
	    fs=$1
	    ;;
	-h)
	    usage 0
	    ;;
	--help)
	    usage 0
	    ;;
	--image)
	    shift
	    image=$1
	    ;;
	--initrd)
	    shift
	    initrd=$1
	    ;;
	--install)
	    shift
	    install=$1
	    ;;
	--iso)
	    isoimage=yes
	    ;;
	--kernel)
	    shift
	    kernel=$1
	    ;;
	--keymap)
	    shift
	    keymap=$1
	    ;;
	--nocompact)
	    compact=
	    ;;
	--noformat)
	    format=no
	    ;;
	--root)
	    shift
	    root=$1
	    ;;
	--size)
	    shift
	    size=$1
	    ;;
	--version)
	    echo `basename $0` version $version
	    exit 0
	    ;;
	*)
	    echo "unrecognized argument: " $1
	    usage 1
	    ;;
    esac

    shift
done

if [ -z "$image" ]; then
#	image=`cat /proc/cmdline | sed "s/.*BOOT_IMAGE=//" | sed "s/ .*//"`
	image=`$lilo -C $config -v0 -w -I " " D`
fi

if [ -z $kernel ]; then
	kernel=`$lilo -C $config -v0 -w -I "$image" i`
	if [ "$kernel" = "" ]; then exit 1;
	elif [ $debug = "true" ]; then echo kernel = "$kernel";
	fi
fi

if [ -z $root ]; then
	root=`$lilo -C $config -v0 -w -I "$image" R`
	if [ "$root" = "No root specified" ]; then
		root=`grep </etc/fstab -v "^[ \t]*#" |
			grep "[[:space:]]/[[:space:]]" | \
				sed -e "s/^[ \t]*//" -e "s/[ \t].*//"`
		if [ -z $root ]; then
			root=`mount | grep " on / type" | sed "s/ .*//"`
		fi
		if [ -z $root ]; then
			echo "Cannot find mounted root partition"
			echo "Using current root"
			root=current
		fi
	fi
	if [ $debug = true ]; then echo root = "$root";
	fi
fi

if [ -z $initrd ]; then
	initrd=`$lilo -C $config -v0 -w -I "$image" r`
	if [ "$initrd" = "" ]; then exit 1;
	elif [ $debug = "true" ]; then echo initrd = "$initrd";
	fi
fi
if [ "$initrd" = "No initial ramdisk specified" ]; then initrd= ; fi

if [ -z $append ]; then
	append=`$lilo -C $config -v0 -w -I "$image" a`
	if [ "$append" = "" ]; then exit 1;
	elif [ $debug = "true" ]; then echo append = \"$append\";
	fi
fi
if [ "$append" = "No append= was specified" ]; then append= ; fi

if [ -z $keymap ]; then
	keymap=`$lilo -C $config -v0 -w -I "$image" k`
	if [ "$keymap" = "" ]; then exit 1;
	elif [ $debug = "true" ]; then echo keymap = "$keymap";
	fi
fi

if [ $isoimage = yes ]; then
    fast=fast
    if [ $size = 0 ]; then
	size=$hdsize
    elif [ $size = HD -o $size = hd ]; then
	size=$hdsize
    fi
    if [ $device = "/dev/fd0" ]; then
	device=rescue.iso
    fi
else
	umount $device 2>/dev/null 1>/dev/null
fi

if [ $size = 0 ]; then
    size=1440
fi

if [ $size = 1200 ]; then
    sectors=15
elif [ $size = 1440 ]; then
    sectors=18
elif [ $size = 2880 ]; then
    sectors=36
    install=menu
    if [ -f $boot/diag1.img -a -f $boot/diag2.img ]; then
	diag=yes
    fi
elif [ $size = $hdsize ]; then
    sectors=32
    heads=8
    cylinders=$(($size/$sectors/$heads*2))
    if [ $size != $(($sectors*$heads*$cylinders/2)) ]; then
	echo Internal error in HDsize
	exit 1
    fi
    install=menu
    if [ -f $boot/diag1.img -a -f $boot/diag2.img ]; then
	diag=yes
    fi
elif [ $size = HD -o $size = hd ]; then
    echo "--size $size  may only be used with the  --iso  option."
    exit 1
else
    echo "--size must be 1200 or 1440; --size 1440 assumed."
    sectors=18
    size=1440
fi

if [ $fs != msdos -a $fs != ext2 -a $fs != minix ]; then
    echo "illegal option:  --fs" $fs
    echo "   must be either  msdos  or  ext2  or  minix"
    exit 1
fi

if [ $fs = msdos ]; then
	mountconfig=$mount/lilo.cnf
else
	mountconfig=$mount/lilo.conf
fi

if [ $debug = "true" ]; then
	umount $mfile

	echo ""

	echo lilo = $lilo
	echo device = $device
	echo image = $image
	echo kernel = $kernel
	echo initrd = $initrd
	echo append = \"$append\"
	echo install = $install
	echo format = $format
	echo fs = $fs
	echo size = $size
	echo root = $root
	echo compact = $compact
	echo keymap = $keymap
	echo isoimage = $isoimage
	echo master = $master
	echo ""
	echo pause after parameter display
	read aline
fi

if [ ! -f $kernel ]; then
	echo "Kernel file " $kernel " does not exist"
	exit 1
fi

if [ ! -z $initrd ]; then
	if [ ! -f $initrd ]; then
		echo "Initial ramdisk file " $initrd " does not exist"
		exit 1
	fi
fi

if [ $isoimage != yes ]; then
       # Calculate size
        if [ -x /usr/bin/du ]; then
                totalsize=`/usr/bin/du -Dc $kernel $initrd $keymap |tail -1 | awk '{ print $1 }'`
                if [ "$totalsize" -gt "$size" ] ; then
                        echo "Sorry, the ramdisk, kernel and keymap don't fit in the floppy disk."
                        exit 1
                fi
        fi
	echo ""
	echo "Insert a blank floppy into $device"
	echo "All information on this floppy will be destroyed"
	echo "Press [Enter] to proceed, ^C to abort"
	read aline
fi

if [ "$fast" = fast ]; then

	dd bs=1024 count=$size of=$mfile if=/dev/zero
	bsize=$size
	mpart=$mfile
	if [ $size = $hdsize ]; then
	    bios=0x80
	    bsize=$(($size-$sectors))
	    cat > $mtemp <<EOF
n
p
1


a
1
w
EOF
	    echo Partitioning HD "file   (this will take a minute)"
	    fdisk -b 512 -S $sectors -H $heads -C $cylinders \
		$mfile < $mtemp > /dev/null 2> /dev/null
	    rm -f $mtemp
	    echo bsize = $bsize
#read aline
	    losetup -d /dev/$loopback 2>/dev/null
            losetup -d /dev/$looppart 2>/dev/null
            losetup /dev/$loopback $mfile
	    losetup -o $(($sectors*512)) /dev/$looppart $mfile
	    mpart=/dev/$looppart
	fi
	echo Making filesystem
	if [ "$fs" = ext2 ]; then
		echo y | mkfs.$fs -N 24 -b 1024 $mpart $bsize
	else
		echo y | mkfs.$fs $mpart
	fi
	echo Mounting filesystem
#read aline
	if [ $size != $hdsize ]; then
	    mount -t $fs -o rw,loop $mfile $mount
	    loopback=`mount | grep $mfile | sed -e "sX.*loop=/dev/XX" -e "s/).*//"`
	else
	    mount -t $fs -o rw $mpart $mount
	fi
	if [ $debug = true ]; then
		mount
	fi
	disk="/dev/$loopback"
	if [ $debug = true ]; then
		echo "disk=$disk"
	fi

else

	if [ "$format" = "yes" ]; then
		echo Formatting $device with $fs filesystem...
		dd of=$device if=/dev/zero bs=512 count=1
		if [ "$fs" = ext2 ]; then
			mkfs -t $fs -N 24 -b 1024 $device 1>/dev/null 2>/dev/null
		else
			mkfs -t $fs $device 1>/dev/null 2>/dev/null
		fi
		echo done.
		echo ""
	fi

	rm -rf $mount/*
	mount -t $fs -o rw $device $mount

	rm -rf $mount/*
	disk=$device

fi

cat > $mountconfig <<EOF
#  Begin mkrescue $version configuration file
install=$install
boot=$device
map=map
backup=/dev/null
message=message
prompt
timeout=150
nowarn
geometric
disk=$disk bios=$bios
  sectors=$sectors heads=$heads cylinders=$cylinders
EOF
if [ $size = $hdsize ]; then
	echo "  max-partitions=7" >>$mountconfig
	echo "  partition=/dev/$looppart  start=$sectors" >>$mountconfig
	echo static-bios-codes >>$mountconfig
	bios=0x81
else
	bios=0x80
fi

if [ "$master" != "" -a $isoimage = yes ]; then
	echo "disk=$master" >>$mountconfig
	echo "  bios=$bios" >>$mountconfig
elif [ "$master" != "" -a $debug = true ]; then
	echo "disk=$master" >>$mountconfig
	echo "  bios=$bios" >>$mountconfig
fi

if [ $keymap != us.ktl ]; then 
	echo "keytable=lang.ktl" >>$mountconfig
fi

if [ $isoimage = yes ]; then 
	echo "el-torito-bootable-CD" >>$mountconfig
fi

echo " " >>$mountconfig
echo "image=linux" >>$mountconfig

if [ ! -z $initrd ]; then
	echo "  initrd=initrd" >>$mountconfig
fi

if [ ! -z "$append" ]; then
	echo "  append=\"$append\"" >>$mountconfig
fi

cat >> $mountconfig <<EOF
  root="$root"
  read-only
EOF

if [ "$master" != "" -a $isoimage = yes ]; then
cat >> $mountconfig <<EOF
other=$master
  unsafe
  label=hard_disk
EOF
fi

if [ "$diag" = yes ]; then
cp -pv $boot/diag1.img $mount
cp -pv $boot/diag2.img $mount
cat >> $mountconfig <<EOF
image=diag1.img
  label=diagnostic_1
image=diag2.img
  label=diagnostic_2
EOF
fi
echo >>$mountconfig "#  End of mkrescue-generated configuration file"

if [ $isoimage = yes ]; then
	comment="El-Torito bootable-CD will boot at end of timeout"
else
	comment="floppy will boot in 15 seconds"
fi

rm -rf $mount/lost+found
cat > $mount/message <<EOF

MKRESCUE version $version $comment
Use  "boot: linux <options>"  to enter kernel options
The root device is currently configured as  root="$root"

EOF
echo `uname --sysname` `uname --release` > $mount/$(uname --release)

sync

if [ $debug = true ]; then
	echo root=\"$root\"
	echo ""
	echo "pause after writing lilo.conf & message ..."
	read aline
fi

echo "Copying files..."
if [ $keymap != us.ktl ]; then 
	cp -pv $keymap $mount/lang.ktl
fi

if [ ! -z $initrd ]; then
	cp -pv $initrd $mount/initrd
fi

cp -pv $kernel $mount/linux
sync

echo "done."
echo ""



pushd $mount >/dev/null 2>/dev/null
if [ "$fast" = fast ]; then
	bootcmd="-b /dev/$loopback"
fi

echo Running $lilo ...
if [ $debug = true ]; then

cp -pvf $mountconfig $clog
if [ -z $log ]; then
 echo	$lilo -w+ -C $mountconfig $compact $bootcmd -v5
	$lilo -w+ -C $mountconfig $compact $bootcmd -v5 || fast=error
else
 echo	$lilo -w+ -C $mountconfig $compact $bootcmd -v5 ">$log"
	$lilo -w+ -C $mountconfig $compact $bootcmd -v5 >$log || fast=error
fi

else
	$lilo -C $mountconfig $compact $bootcmd || fast=error
fi
popd >/dev/null 2>/dev/null
if [ "$fast" = error ]; then
	echo -n `$lilo -V`
	echo " failure."
else
	echo done.
fi
echo ""

umount $mount

if [ $fast = error ]; then
	exit 1
fi

if [ $isoimage = yes ]; then
	echo START MakeISOFS:
	echo
	out=$device
	opt=
	if [ $size = $hdsize ]; then
	    losetup -d /dev/$looppart
	    losetup -d /dev/$loopback
	    opt=-hard-disk-boot
	fi
	mv $mfile $mount/boot.bin
	genisoimage $opt -J -R -T \
		-V LILO_BOOT -A "Linux Boot CD created by LILO mkrescue" \
		-b boot.bin -c boot.cat -o $out $mount
	cat <<EOF

END MakeISOFS:  output is in  '$device'


The bootable CD can be burned with the 'cdrecord' or 'wodim' utility
using a command of the form:

	cdrecord [<options>] [dev=<device>] $device
	wodim [<options>] [dev=<device>] $device

EOF
elif [ "$fast" = fast ]; then
	if [ $debug = true ]; then
		echo Pause before transfering to $device
		read aline
	fi
	dd if=$mfile of=$device bs=1024
fi

echo "All done."
echo ""
exit 0

