#!/bin/sh

# Copyright 2016,2020 Cumulus Networks, Inc.
# All rights reserved.

# This script is run *after* the root file system is mounted on
# ${rootmnt}.

PREREQ="cumulus-bottom"

prereqs()
{
	echo "$PREREQ"
}

case $1 in
	# get pre-requisites
	prereqs)
		prereqs
		exit 0
		;;
esac

set -e

. /scripts/functions

# Save these for error messages
prog=$0
scriptargs="$@"

# Make sure any errors stop the installation here.
# Don't save it for a reboot.
trapFxn()
{
	if [ "$?" != "0" ];then
		"$prog $scriptargs Failed!"
		# Disable trap before exit.
		trap '' 0
		panic
	fi
}

# run trap to error check on any exit
trap trapFxn 0

stage_dir="${rootmnt}/var/lib/cumulus/installer"
install_file="${stage_dir}/onie-installer"
launch_file="${stage_dir}/launch"

# x86_64 ONIE reboot helper.  Assumes a chroot environment is already
# setup on ${rootmnt}.
x86_64_onie_reboot()
{
	# Depends on legacy BIOS vs UEFI...
	if [ -d "/sys/firmware/efi/efivars" ] ; then
		# nothing to do on UEFI platforms, as onie-select
		# already does the job.
		true
	else
		# Legacy BIOS GRUB.  Install grub in the CL-BOOT
		# partition as we will be trashing the CL-SYSTEM
		# partition.
		local tmp_boot="${rootmnt}/mnt/boot-install"
		mkdir -p $tmp_boot
		mount LABEL=CL-BOOT $tmp_boot
		rm -rf ${tmp_boot}/grub
		cp -a ${rootmnt}/boot/grub $tmp_boot
		# Create a minimal grub.cfg that boots into ONIE.
		# Include serial console configuration if present.
		grep serial ${rootmnt}/boot/grub/grub.cfg > ${tmp_boot}/grub/grub.cfg || true
		cat <<EOF >> ${tmp_boot}/grub/grub.cfg
search --no-floppy --label --set=root ONIE-BOOT
echo    'Loading ONIE ...'
chainloader +1
boot
EOF
		chroot ${rootmnt} grub-install \
		       --force --recheck \
		       --boot-directory=/mnt/boot-install $device
		umount $tmp_boot
	fi

}

# armel ONIE reboot helper.  Assumes a chroot environment is already
# setup on ${rootmnt}.
armel_onie_reboot()
{
	# nothing to do on u-boot based platforms, as onie-select
	# already does the job.
	true
}

# partprobe can't find dmidecode (it's not in the initrd)
# and prints a warning, but returns $? = 0
# This situation does not affect fucntionality, so
# this function filters the dmidecode warning message but
# allows any other text/errors to pass through
partProbe()
{
	local returnCode

	# Disable fail on any error during filtering
	set +e
	result=$(partprobe $1 2>&1 )
	# Save the result before any other commands execute
	returnCode="$?"
	# Print the error, if any, and filter dmidecode.
	# Grep may fail, so keep it within the set +e
	echo "$result" |  grep -v  "dmidecode: not found"
	set -e

	return $returnCode
}
# Test for staged cumulus installer image
if [ -r "${launch_file}" -a -r "${install_file}" ] ; then

	echo "Installer found.  Configuring ONIE for image install ..."

	if [ "${readonly}" = "y" ]; then
		# remount read/write as we need to modify a few files
		if [ "${ROOTFSTYPE}" = "ubifs" ] ; then
			mount -t ubifs -o remount,rw ${ROOT} ${rootmnt}
		else
			mount -o remount,rw ${ROOT} ${rootmnt}
		fi
	fi
	# prevent this logic from possibly repeating
	rm -f ${launch_file}

	# Check for supported CPU architectures
	case "$(uname -m)" in
		x86_64|armv7l)
		;;
		*)
			echo "ERROR: unsupported CPU architecture: $(uname -m)"
			exit 1
	esac

	# Tally up the size of all components to make sure the
	# install partition is big enough. ZTP script could
	# be unexpectedly large.
	file_size=0
	total_size=0
	cl_sysroot_size=0

	# copy installer (and perhaps an associated preseed file) out
	# to ramdisk.
	rm -rf /tmp/oi
	mkdir -p /tmp/oi

	# Install_file will be something like 'onie-installer', with
	# an absolute path
	file_size=$(stat -c '%s' "${install_file}")
	cp ${install_file} /tmp/oi

	size_msg="Installer $file_size: "

	# Allocate space that is double the size of the
	# installer to have some extra room
	total_size=$(( total_size + ( file_size * 2 ) ))

	# preseed, if present
	[ -r "${install_file}.preseed" ] && {
		file_size=$(stat -c '%s' "${install_file}.preseed" )
		size_msg="$size_msg Preseed $file_size: "
		total_size=$(( total_size + file_size ))
		cp "${install_file}.preseed" /tmp/oi
	}

	# ztp script, if present
	[ -r "${install_file}.ztp" ] && {
		file_size=$(stat -c '%s' "${install_file}.ztp" )
		size_msg="$size_msg ZTP $file_size: "
		total_size=$(( total_size + file_size ))
		cp "${install_file}.ztp" /tmp/oi
	}

	[ -r "${install_file}.optional_pkgs" ] && {
		file_size=$(stat -c '%s' "${install_file}.optional_pkgs" )
		size_msg="$size_msg Pkgs $file_size: "
		total_size=$(( total_size + file_size ))
		cp "${install_file}.optional_pkgs" /tmp/oi
	}

	# Convert size to kibibytes.
	cl_sysroot_size=$(( 1 + ($total_size / 1024 ) ))

	# Log information for post analysis if there is a failure
	echo "CL-SYSROOT $size_msg"
	echo "CL-SYSROOT $total_size B Partition: $cl_sysroot_size KB"
	# determine CL-SYSROOT block device and partition number.  Be
	# mindful of mmc devices that have a different format,
	# mmcblk0pN.
	if [ "${ROOTFSTYPE}" = "ubifs" ] ; then
		for syspart in /sys/class/ubi/ubi?_? ; do
			test $(cat $syspart/name) = "CL-SYSTEM" && {
				device=/dev/$(basename ${syspart})
				blk_suffix="_"
				part=$(echo -n $device | tail -c 1)
				device=${device%%_${part}}
			}
		done
	else
		device=$(blkid -t LABEL=CL-SYSTEM -o device)
		part=$(echo -n $device | tail -c 1)
		device=$(echo -n $device | sed -e 's/[0-9]$//')
		case "$device" in
			/dev/mmcblk*)
				device=${device%%p}
				blk_suffix="p"
				;;
			*)
				blk_suffix=""
		esac
	fi

	# mount /dev, /sys, /proc and /run into chroot environment as
	# GRUB and onie-select need all of these.
	mount -o nodev,noexec,nosuid -t proc proc ${rootmnt}/proc
	mount -o nodev,noexec,nosuid -t sysfs sysfs ${rootmnt}/sys
	mount --bind /dev ${rootmnt}/dev
	mount -o defaults,noatime,size=10M,mode=1777 -t tmpfs tmpfs ${rootmnt}/run
	mkdir -p ${rootmnt}/run/lock

	# set ONIE to boot in installer mode
	chroot ${rootmnt} /usr/cumulus/bin/onie-select -fi

	# Configure system to boot into ONIE -- depends on CPU architecture
	case "$(uname -m)" in
		x86_64)
			x86_64_onie_reboot
			;;
		armv7l)
			armel_onie_reboot
			;;
		*)
			echo "ERROR: unsupported CPU architecture: $(uname -m)"
			exit 1
	esac

	# clean-up chroot environment
	for m in ${rootmnt}/dev ${rootmnt}/sys ${rootmnt}/proc ${rootmnt}/run ${rootmnt} ; do
		umount $m
	done

	echo "Configuring disk partitions for image install ..."
	# Clobber CL-SYSROOT partition
	if [ "${ROOTFSTYPE}" = "ubifs" ] ; then
		ubirmvol $device -n $part
		ubimkvol $device -n $part -N CL-INSTALLER -s ${cl_sysroot_size}KiB
	else
		sgdisk -d $part $device
		# Filter dmidecode warnings
		partprobe $device
		sleep 0.5
		sgdisk -n "${part}:0:+${cl_sysroot_size}K" $device
		# Filter dmidecode warnings
		partprobe $device
		sleep 0.5
	fi

	onie_dev="${device}${blk_suffix}${part}"

	# Wait for block device to show up
	count=0
	while [ $count -lt 100 ] ; do
		sleep 0.1
		if [ -b "$onie_dev" ] || [ -c "$onie_dev" ] ; then
			sleep 0.5
			if [ -b "$onie_dev" ] || [ -c "$onie_dev" ] ; then
				break
			fi
		fi
		count=$(( $count + 1 ))
	done
	if [ $count -eq 100 ] ; then
		panic "Unable to find block device: $onie_dev"
		exit 1
	fi

	# Put a file system on the partition that ONIE can read
	if [ "${ROOTFSTYPE}" = "ubifs" ] ; then
		mkfs.ubifs "$onie_dev"
		mount -t ubifs "$onie_dev" $rootmnt
	else
		mkfs.ext2 -L CL-INSTALLER -F "$onie_dev"
		mount -t ext2 "$onie_dev" $rootmnt
	fi

	cp /tmp/oi/* $rootmnt
	sync ; sync ; sync
	umount $rootmnt

	echo "Rebooting into ONIE to complete the installation ..."
	reboot -f
fi

exit 0
