#!/bin/sh

STATEDIR=/var/lib/initramfs-tools
BOOTDIR=/boot

set -e

usage()
{
	if [ -n "${1}" ]; then
		printf "${@}\n\n" >&2
	fi
	cat >&2 << EOF
Usage: ${0} [OPTION]...

Options:
 -k [version]	Specify kernel version or all
 -c		Create a new initramfs
 -u		Update an existing initramfs
 -d		Remove an existing initramfs
 -t		Take over a custom initramfs with this one
 -b		Set alternate boot directory
 -v		Be verbose
 -h		This message

EOF
	exit 1
}

mild_panic()
{
	if [ -n "${1}" ]; then
		printf "${@}\n" >&2
	fi
	exit 0
}

panic()
{
	if [ -n "${1}" ]; then
		printf "${@}\n" >&2
	fi
	exit 1
}

verbose()
{
	if [ "${verbose}" = 1 ]; then
		printf "${@}\n"
	fi
}

version_exists()
{
	[ -e "${STATEDIR}/${1}" ]
	return $?
}

set_initramfs()
{
	initramfs="${BOOTDIR}/initrd.img-${version}"
}

generate_initramfs()
{
	echo "update-initramfs: Generating ${initramfs}"
	OPTS="-o"
	if [ "${verbose}" = 1 ]; then
		OPTS="-v $OPTS"
	fi
	if mkinitramfs "${OPTS}" "${initramfs}" "${version}"; then
		set_sha1
	else
		mkinitramfs_return="$?"
		if [ "$mkinitramfs_return" = "2" ]; then
			# minversion wasn't met, exit 0
			exit 0
		fi
		verbose "mkinitramfs failed for ${initramfs}"
		exit $mkinitramfs_return
	fi
}

# only run lilo if no grub is around
run_bootloader()
{
	if [ -x /sbin/grub -o -e /boot/grub/menu.lst ]; then
		if [ -e /etc/lilo.conf ]; then
			echo
			echo "WARNING: grub and lilo installed."
			echo "If you use grub as bootloader everything is fine."
			echo "If you use lilo as bootloader you must run lilo!"
			echo
		fi
		return 0
	fi
	if [ -e /etc/lilo.conf ]; then
		lilo -t > /dev/null
		if [ $? -eq 0 ]; then
			lilo
		fi
	fi
}

compare_sha1()
{
	sha1sum "${initramfs}" | diff "${STATEDIR}/${version}" - >/dev/null 2>&1
	return $?
}

# Note that this must overwrite so that updates work.
set_sha1()
{
	sha1sum "${initramfs}" > "${STATEDIR}/${version}"
}

delete_sha1()
{
	rm -f "${STATEDIR}/${version}"
}

get_sorted_versions()
{
	version_list=""

	for gsv_x in "${STATEDIR}"/*; do
		gsv_x="$(basename "${gsv_x}")"
		if [ "${gsv_x}" = '*' ]; then
			return 0
		fi
		worklist=""
		for gsv_i in $version_list; do
			if dpkg --compare-versions "${gsv_x}" '>' "${gsv_i}"; then
				worklist="${worklist} ${gsv_x} ${gsv_i}"
				gsv_x=""
			else
				worklist="${worklist} ${gsv_i}"
			fi
		done
		if [ "${gsv_x}" != "" ]; then
			worklist="${worklist} ${gsv_x}"
		fi
		version_list="${worklist}"
	done

	verbose "Available versions: ${version_list}"
}

set_current_version()
{
	if [ -f /boot/initrd.img-`uname -r` ]; then
		version=`uname -r`
	fi
}

set_linked_version()
{
	if [ -L /initrd.img ]; then
		linktarget="$(basename "$(readlink /initrd.img)")"
	fi

	if [ -L /boot/initrd.img ]; then
		linktarget="$(basename "$(readlink /boot/initrd.img)")"
	fi

	if [ -z "${linktarget}" ]; then
		return
	fi

	version="${linktarget##initrd.img-}"
}

set_highest_version()
{
	get_sorted_versions
	set - ${version_list}
	version=${1}
}

create()
{
	if [ -z "${version}" ]; then
		usage "Create mode requires a version argument"
	fi

	set_initramfs

	if [ "${takeover}" = 0 ]; then
		if version_exists "${version}"; then
			panic "Cannot create version ${version}: already exists"
		fi
	
		if [ -e "${initramfs}" ]; then
			panic "${initramfs} already exists, cannot create."
		fi
	fi

	generate_initramfs
}

update()
{
	if [ -z "${version}" ]; then
		set_linked_version
	fi

	if [ -z "${version}" ]; then
		set_highest_version
	fi

	if [ -z "${version}" ]; then
		set_current_version
	fi

	if [ -z "${version}" ]; then
		verbose "Nothing to do, exiting."
		exit 0
	fi

	if [ "${version}" = "all" ]; then
		: FIXME check for --yes, and if not ask are you sure
		get_sorted_versions
		if [ -z "${version_list}" ]; then
			verbose "Nothing to do, exiting."
			exit 0
		fi
		for u_version in ${version_list}; do
			if [ "${verbose}" = "1" ]; then
				vflag="-v"
			fi
			# Don't stop if one version doesn't work.
			set +e
			"${0}" -u -k "${u_version}" ${vflag}
			set -e
		done
		exit 0
	fi

	set_initramfs

	altered_check

	generate_initramfs

	run_bootloader

}

delete()
{
	if [ -z "${version}" ]; then
		usage "Delete mode requires a version argument"
	fi

	set_initramfs

	if [ ! -e "${initramfs}" ]; then
		panic "Cannot delete ${initramfs}, doesn't exist."
	fi
	
	if ! version_exists "${version}"; then
		panic "Cannot delete version ${version}: Not created by this utility."
	fi

	altered_check

	delete_sha1

	rm -f "${initramfs}"
}


altered_check()
{
	if [ "${takeover}" = 0 ]; then
		if ! compare_sha1; then
			delete_sha1
			mild_panic "${initramfs} has been altered.  Cannot update."
		fi
	fi
}

# Defaults
verbose=0
yes=0
# We default to takeover=1 in Ubuntu, but not Debian
takeover=1

##

while getopts "k:cudyvtb:h?" flag; do
	case "${flag}" in
	k)
		version="${OPTARG}"	
		;;
	c)
		mode="c"
		;;
	d)
		mode="d"
		;;
	u)
		mode="u"
		;;
	v)
		verbose="1"
		;;
	y)
		yes="1"
		;;
	t)
		takeover="1"
		;;
	b)
		BOOTDIR="${OPTARG}"
		if [ ! -d "${BOOTDIR}" ]; then
			echo "Error: ${BOOTDIR} is not a directory."
			exit 1
		fi
		;;
	h|?)
		usage
		;;
	esac
done

# Validate arguments

if [ -z "${mode}" ]; then
	usage "You must specify at least one of -c, -u, or -d."
fi

case "${mode}" in
	c)
		create
		;;
	d)
		delete
		;;
	u)
		update
		;;
esac


