#!/usr/bin/bash
#
# sb-make-boot-img -- Generate kernel images for UEFI on Arch Linux
# Copyright (C) 2016 Antoine Damhet <antoine.damhet@lse.epita.fr>
#
# 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 3 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, see <http://www.gnu.org/licenses/>.

getopt --test > /dev/null
if [[ $? != 4 ]]; then
	echo "I’m sorry, `getopt --test` failed in this environment." >&2
	exit 1
fi

function usage {
	echo "Usage:"
	echo -ne "\t${0} -i <initramfs> [-i <initramfs>]" >&2
	echo -n " [(-c <cmd>|--cmdFile <cmdfile>)] -o <output>" >&2
	echo " [--osrel <os-release>] [--efistub <efistub>] [--compress <utility>] [-v] (-k <kernel>|<kernel>)" >&2
	echo "" >&2
	echo "Combines the kernel, initramfs and boot args into a single efi binary." >&2
	echo "" >&2
	echo -e "  -k, --kernel\tThe kernel to embed" >&2
	echo -e "  -i, --initrd\tThe initramfs to embed (may be repeated)." >&2
	echo -e "  -c, --cmd\t\tThe kernel boot arguments." >&2
	echo -e "      --cmdFile\tThe file containing the boot args." >&2
	echo -e "      --osrel\t\tThe os release file (default to \"/etc/os-release\"" >&2
	echo -e "      --efistub\tThe efi stub file (default to \"/usr/lib/systemd/boot/efi/systemd-bootx64.efi\"" >&2
	echo -e "      --compress\tWitch utility is used to compress the initramfs (default to \"gzip\", \"cat\" to disable compression)" >&2
	echo -e "      --config\tOverride the default config file." >&2
	echo -e "  -h, --help\t\tPrints this help." >&2
	echo -e "      --hook\t\tUsed for the hook." >&2
	echo -e "  -v, --verbose\tVerbose mode." >&2
	echo "" >&2
	echo "Example:" >&2
	echo -e "\t${0} -k /boot/vmlinuz-linux -i /boot/intel-ucode.img -i /boot/initramfs-linux.img --cmdFile /etc/cmdline -o /boot/efi/linux.efi"

	exit $1
}

typeset -A options
typeset -A default_options
default_options=(
	[efistub]="/usr/lib/systemd/boot/efi/linuxx64.efi.stub"
	[osrel]="/etc/os-release"
	[compress]="gzip"
)

SHORT=k:o:i:c:hv
LONG=kernel:,output:,initrd:,cmd:,cmdFile:,osrel:,efistub:,help,config:,hook,verbose,compress:

# -temporarily store output to be able to check for errors
# -activate advanced mode getopt quoting e.g. via “--options”
# -pass arguments only via   -- "$@"   to separate them correctly
PARSED=`getopt --options $SHORT --longoptions $LONG --name "$0" -- "$@"`
if [[ $? != 0 ]]; then
	# e.g. $? == 1
	#  then getopt has complained about wrong arguments to stdout
	exit 2
fi
# use eval with "$PARSED" to properly handle the quoting
eval set -- "$PARSED"

CONFIG="/etc/sbtools.conf"

# now enjoy the options in order and nicely split until we see --
while true; do
	case "$1" in
	-k|--kernel)
		options[kernel]="$2"
		shift 2
		;;
	-o|--output)
		options[out]="$2"
		shift 2
		;;
	-i|--initrd)
		options[initrd]="${options[initrd]} $2"
		shift 2
		;;
	-c|--cmd)
		options[cmd]="$(mktemp)"
		echo -n "$2" > "${options[cmd]}"
		TMPFILES="${TMPFILES} ${options[cmd]}"
		shift 2
		;;
	--cmdFile)
		options[cmd]="$2"
		shift 2
		;;
	--efistub)
		options[efistub]="$2"
		shift 2
		;;
	--osrel)
		options[osrel]="$2"
		shift 2
		;;
	--compress)
		options[compress]="$2"
		shift 2
		;;
	--config)
		CONFIG="$2"
		shift 2
		;;
	--hook)
		HOOK="true"
		shift 1
		;;
	-h|--help)
		usage 0
		;;
	-v|--verbose)
		VERBOSE="true"
		shift 1
		;;
	--)
		shift
		break
		;;
	*)
		echo "Unnkown argument: $1" >&2
		usage 3
		;;
	esac
done

for i in /usr/local/share/sbtools /usr/share/sbtools .; do
	[ -e $i/loadconfig.sh ] && . $i/loadconfig.sh && break
done

if [ -n "$HOOK" ] || [ -z "${options[kernel]}" ]; then
	options[kernel]="$1"
fi

if [ ! -f "${options[kernel]}" ]; then
	echo "$0: Kernel not found." >&2
	usage 2
fi

if [ -n "$HOOK" ]; then
	NAME="$(echo ${options[kernel]} | sed 's/\(.\|\)boot\/vmlinuz-//')"
	options[initrd]="/boot/initramfs-${NAME}.img"
	options[out]="${options[outdir]}/${NAME}.efi"
fi

# handle non-option arguments
if [ -z "${options[out]}" ]; then
	echo "$0: Could not determine the outputfile." >&2
	usage 2
fi
if [ -z "${options[initrd]}" ]; then
	echo "$0: Could not determine the initramfs to use." >&2
	usage 2
fi
if [ ! -f "${options[efistub]}" ]; then
	echo "$0: Could not find the efistub." >&2
	usage 20
fi
if [ ! -f "${options[osrel]}" ]; then
	echo "$0: Could not find the osrel file." >&2
	usage 20
fi
if [ ! -f "${options[cmd]}" ]; then
	echo "$0: Could not find command line file." >&2
	usage 2
fi

INITRAMFS=$(mktemp)
TMPFILES="${TMPFILES} ${INITRAMFS}"

if [ -n "$VERBOSE" ]; then
	for i in ${options[initrd]} ${options[addinitrd]}; do
		echo "Initramfs: $i"
	done
	echo "Kernel: ${options[kernel]}"
	echo -n "Command line: \""
	cat "${options[cmd]}"
	echo "\""
	echo "osrel file: ${options[osrel]}"
	echo "efistub file: ${options[efistub]}"
	echo "Output file: ${options[out]}"
	echo "Compression utility: ${options[compress]}"
fi

uncat ${options[initrd]} ${options[addinitrd]} | ${options[compress]} > ${INITRAMFS}

sb-link-boot-img "${options[efistub]}" "${options[kernel]}" "${options[out]}" \
	.osrel "${options[osrel]}" \
	.cmdline "${options[cmd]}" \
	.initrd "${INITRAMFS}"
ret=$?

rm -f -- ${TMPFILES}

if [ -n "$HOOK" ]; then
	echo -n "${options[out]}"
fi

exit $ret
