#!/bin/sh
#
# /sbin/piboot: SliTaz Raspberry Pi bootloader and web boot!
#
# (C) 2014-2016 SliTaz GNU/Linux - ...
#
# usage: insert 'rdinit=/sbin/piboot' (if you load an initramfs)
# or 'init=/sbin/piboot' in the kernel command line /boot/cmdline.txt
#
# AUTHORS: Pascal Bellard <pascal.bellard@slitaz.org>
#

[ -z "$(which dialog)" ] && echo "Please install dialog." && exec /init

cmdlinearg()
{
	sed "/$1=/!d;s/.*$1=\([^ ]*\).*/\1/" < /proc/cmdline
}

cmdvararg()
{
	echo " $CMDLINE" | sed "/ $1=/!d;s/.* $1=\([^ ]*\).*/\1/"
}

get()
{
	grep -sq ^$1 $2 && sed "/^$1/!d;s/$1[ :=]*//" $2
}

list_entry()
{
	n=0
	while true ; do	
		n=$(($n+1))
		eval KEYWORD="\$KEYWORD_$n"
		eval ENTRY="\$ENTRY_$n"
		[ "$KEYWORD" ] || break
		[ "$KEYWORD" == "$default" ] && ENTRY="$ENTRY (default)"
		echo -n "$KEYWORD \"$ENTRY\" "
	done
}

edit_menu()
{
while true; do

	eval KERNEL="\$KERNEL_$1"
	eval INITRD="\$INITRD_$1"
	eval CMDLINE="\$CMDLINE_$1"
	extra="--extra-button --extra-label ${msgsave:-Save}"
	[ "$READONLY" ] && extra=''

	exec 3>&1
	value=$($DIALOG  --clear \
		--title "${edittitle:-Edit menu}" \
		--ok-label "${msgedit:-Edit}" \
		--cancel-label "${msgdone:-Done}" \
		$extra \
		--menu "$msgmenu" 20 72 14 \
		Kernel "$KERNEL" \
		Initramfs "$INITRD" \
		Cmdline "$CMDLINE" 2>&1 1>&3 )
	retval=$?
	exec 3>&-
	
	case $retval in
	1|255) return ;;
	3)	cp $mnt/$KEYWORD/config.txt /tmp/config.txt
		sed -i '/^kernel\|^initrd/d' /tmp/config.txt
		echo "kernel=$KERNEL" >> /tmp/config.txt
		[ "$INITRD" ] && echo "initrd $INITRD" >> /tmp/config.txt
		mount -o remount,rw $mnt
		[ "$CMDLINE" ] && echo "$CMDLINE" > $mnt/$KEYWORD/cmdline.txt ||
		rm -f $mnt/$KEYWORD/cmdline.txt
		cp /tmp/config.txt $mnt/$KEYWORD/config.txt
		mount -o remount,ro $mnt ;;
	esac

	exec 3>&1
	case $value in
	Kernel)
		data=$($DIALOG  --clear --title "${msgselectkernel:-Select the kernel}" \
			 --fselect "$mnt/$KEYWORD/$KERNEL" 10 72 2>&1 1>&3) ;;
	Initramfs)
		data=$($DIALOG  --clear --title "${msgselectinitrd:-Select the initramfs}" \
			 --fselect "$mnt/$KEYWORD/$INITRD" 10 72 2>&1 1>&3) ;;
	Cmdline)
		data=$($DIALOG  --clear --title "${msgeditcmdline:-Edit cmdline}" \
			 --inputbox "Cmdline" 20 72 2>&1 "$CMDLINE" 1>&3) ;;
	esac
	retval=$?
	exec 3>&-
	
	case $retval in
	1|255) continue ;;
	esac
	
	case $value in
	Kernel)		[ -f $data ] && eval KERNEL_$1=\$data ;;
	Initramfs)	[ -f $data ] && eval INITRD_$1=\$data ;;
	Cmdline)	eval CMDLINE_$1=\$data ;;
	esac
done
}

web_boot_entry()
{
	[ "$USING_WEBBOOT" == "1" ] ||
	echo -n 'WebBoot "boot from Internet"'
}

web_boot()
{
	USING_WEBBOOT=1
	[ -s $mnt/network.conf ] && cp $mnt/network.conf /etc
	/etc/init.d/network.sh start
	webpath=$(get webpath $mnt/menu.txt)
	[ "$webpath" ] || webpath=http://mirror.slitaz.org/pxe/arm/boot.php
	for url in ${webpath//,/ }; do
		wget -O /root/webboot.sh "$url?mac=$(cat \
			/sys/class/net/eth0/address /sys/class/net/*/address \
			| sed q)" 2>&1 > /dev/null
		[ -s /root/webboot.sh ] || continue
		. /root/webboot.sh
		webprefix=$(dirname $url)
		editbutton=""
		break
	done
}

web_get()
{
	if [ -z "$webprefix" ]; then
		cat $mnt/$1 >> $2
		return
	fi
	case "$1" in
	*torrent) # See http://sl-lab.it/dokuwiki/doku.php/tesi:boottorrent_en
		[ -z "$(which aria2)" ] &&
			echo "Can't find aria2." >> $LOG &&
			exec /init

		wget -O /root/webboot.torrent "$webprefix/$1"
		aria2c --enable-dht=false --disable-ipv6=true --seed-time=0 \
			--file-allocation=none -j5 /root/webboot.torrent

		if ls | grep -q rootfs; then
			file=$(ls | grep -q rootfs)
			cat $file >> $2
			rm -f $file
		else
			mv $(ls *mage* *linu* 2> /dev/null) $2
		fi
		rm -f /root/webboot.torrent ;;
	*)
		wget -O - "$webprefix/$1" >> $2
	esac
}

: ${DIALOG=dialog}

mount -t proc proc /proc
mount -t sysfs sys /sys
BOOTDEV=$(cmdlinearg bootdev)
mnt=/mnt
mount -t devtmpfs /dev /dev
mount -r /dev/${BOOTDEV:-mmcblk0p1} $mnt
umount /dev


# Get global variables

title="$(get title $mnt/menu.txt)"
subtitle="$(get subtitle $mnt/menu.txt)"

timeout="$(get timeout $mnt/menu.txt)"
timeout="${timeout:-30}"

default="$(get default $mnt/menu.txt)"

editbutton='--extra-button --extra-label "Edit"'
grep -qs ^noedit $mnt/menu.txt && editbutton=

READONLY=
grep -qs ^readonly $mnt/menu.txt && READONLY=ON

USING_WEBBOOT=
grep -qs ^nowebboot $mnt/menu.txt && USING_WEBBOOT=1

kmap="$(get kmap $mnt/menu.txt)"
[ "$kmap" ] && ! loadkeys $kmap && loadkmap < /usr/share/kmap/$kmap.kmap

edittitle="$(get edittitle $mnt/menu.txt)"

# Get locale

msgsave="$(get msgsave $mnt/menu.txt)"
msgedit="$(get msgedit $mnt/menu.txt)"
msgmenu="$(get msgmenu $mnt/menu.txt)"
msgdone="$(get msgdone $mnt/menu.txt)"
msgboot="$(get msgboot $mnt/menu.txt)"
msghalt="$(get msghalt $mnt/menu.txt)"
msgselectkernel="$(get msgselectkernel $mnt/menu.txt)"
msgselectinitrd="$(get msgselectinitrd $mnt/menu.txt)"
msgeditcmdline="$(get msgeditcmdline $mnt/menu.txt)"
msgkexec="$(get msgkexec $mnt/menu.txt)"

# Get OS variables

n=0
for i in $mnt/*/ ; do
	[ -s "$i/menu.txt" ] || continue
	KERNEL="$(get kernel $i/config.txt)"
	[ -z "$KERNEL" ] && [ -s "$i/kernel.img" ] && KERNEL="kernel.img"
	[ -z "$KERNEL" ] && [ -s "$i/cmdline.txt" ] && KERNEL="../kernel.img"
	n=$(($n+1))
	eval KERNEL_$n="\$KERNEL"
	eval INITRD_$n="\$(get initramfs \$i/config.txt)"
	eval CMDLINE_$n="\$(cat \$i/cmdline.txt 2> /dev/null)"
	eval SAVEDEFAULT_$n="\$(get savedefault \$i/config.txt)"
	eval KEYWORD_$n="\$(basename \$i)"
	eval ENTRY_$n="\$(get entry \$i/menu.txt)"
done


# Main loop

while true; do

	exec 3>&1
	value=$(sh 2>&1 1>&3 <<EOT
	$DIALOG  --clear \
		--title "${title:-Boot menu}" \
		$editbutton \
		--ok-label "${msgboot:-Boot}" \
		--cancel-label "${msghalt:-Halt}" \
		--timeout $timeout \
		--default-item ${default:-$KEYWORD_1} \
		--menu "${subtitle:-Default boot in $timeout seconds}" 20 72 14 \
		$(list_entry) $(web_boot_entry) \
		Continue "${msgkexec:-Execute SliTaz boot sequence}"
EOT
)
	retval=$?
	exec 3>&-
	
	n=0
	while true; do
		n=$(($n+1))
		eval KEYWORD="\$KEYWORD_$n"
		[ "$KEYWORD" ] || break
		[ "$KEYWORD" == "$value" ] && break
		case $value in
		*timeout)	[ "$KEYWORD" == "${default:-$KEYWORD}" ] && break ;;
		esac
	done
	
	case $retval in
		0)
			case "$value" in
			*WebBoot) web_boot ;;
			*)	break ;;
			esac ;;
		1|255)
			case "$value" in
			*timeout) break ;;
			esac
			umount $mnt
			umount /sys
			umount /proc
			poweroff -f ;;
		3)
			[ "$KEYWORD" ] && edit_menu $n ;;
	esac
done


# Start OS

KEXEC=
case "$KEYWORD" in

''|Continue)	;;
*)
	eval KERNEL="\$KERNEL_$n"
	eval INITRD="\$INITRD_$n"
	eval CMDLINE="\$CMDLINE_$n"
	eval SAVEDEFAULT="\$SAVEDEFAULT_$n"

	grep -qs ARMv6 /proc/cpuinfo || case "$KERNEL" in
	*BCM*|*7*)	;;
	*)	KERNEL=${KERNEL/.img/7.img}
	esac

	if [ -z "$READONLY" ] && [ "$SAVEDEFAULT" ] &&
	   [ -s $mnt/$SAVEDEFAULT/config.txt ]; then
	   	cp $mnt/menu.txt /tmp
		sed -i '/^default/d' /tmp/menu.txt
		echo "default=$SAVEDEFAULT" >> /tmp/menu.txt
		mount -o remount,rw $mnt
		cp /tmp/menu.txt $mnt/menu.txt
	fi

	[ "$INITRD" ] && ! kexec -u 2> /dev/null && mount -t tmpfs tmpfs /media

	for i in ${INITRD//,/ }; do
		echo "Loading $(basename $i)"
		web_get $KEYWORD/$i /rootfs
		if ! kexec -u; then
			( zcat /rootfs || unlzma < /rootfs ) | \
			( cd /media ; cpio -idmu )
			rm -f /rootfs
		else
			n=$(stat -c %s /rootfs)
			n=$(($n % 4))
			[ $n -ne 0 ] &&
			dd if=/dev/zero bs=1 count=$((4 - $n)) >> /rootfs
		fi 2> /dev/null
	done

	if ! kexec -u 2> /dev/null; then
		umount $mnt
		ROOTDIR=$(cmdvararg "subroot")
		TMPDIR=$ROOTDIR/dev/shm
		TMPDIR=${TMPDIR#/}
		ROOTDEV=$(cmdvararg "root")
		[ "$ROOTDEV" == "/dev/null" ] && ROOTDEV=$(cmdvararg "mount")
		K=$(cmdvararg "kmap")
		[ "$K" ] && echo $K > /etc/keymap.conf
		L=$(cmdvararg "lang")
		[ "$L" ] && echo -e "LANG=$L\nLC_ALL=$L" > /etc/locale.conf
		TZ=$(cmdvararg "tz")
		[ "$TZ" ] && echo $TZ > /etc/TZ
		RDINIT=$(cmdvararg "rdinit")
		INIT=$(cmdvararg "init")
		INIT=${INIT:-/sbin/init}
		if [ -z "$ROOTDEV" ] || ! mount $ROOTDEV $mnt; then
			INIT=${RDINIT:-/init}
			mnt=/media
			for i in $(seq 2 5); do
				mount -r /dev/mmcblk0p$i /tmp || continue
				echo "Get modules from /dev/mmcblk0p$i"
				cp -a /tmp/lib/modules /tmp/lib/firmware \
					$mnt/$ROOTDIR/lib
				umount /tmp
				break
			done
		fi
		KEXEC="exec /sbin/switch_root $mnt $INIT"
		if [ "$ROOTDIR" ] && mount -t tmpfs tmpfs $mnt/$TMPDIR ; then
			CHROOT=/usr/sbin/chroot
			cp $(LD_TRACE_LOADED_OBJECTS=1 /lib/ld*.so $CHROOT \
			     | sed 's|.*=> \(/lib/l[^ ]*\).*|\1|;/^\//!d') \
				$CHROOT $mnt/$TMPDIR
			CHROOT="$(cd $mnt ; ls $TMPDIR/ld-*so) $TMPDIR/chroot"
			export LD_LIBRARY_PATH=/$TMPDIR:/lib
			KEXEC="exec /sbin/switch_root $mnt $CHROOT $ROOTDIR/ $INIT"
		fi
		stat $mnt/$ROOTDIR/$INIT > /dev/null || KEXEC="echo 'No $ROOTDIR/$INIT'"
	else
		echo "Loading $(basename $KERNEL)"
		web_get $KEYWORD/$KERNEL /vmlinuz
		umount $mnt
		kexec -l /vmlinuz ${INITRD+=--initrd /rootfs} --command-line \
			"$(sed 's|  .*||' /proc/cmdline) $CMDLINE" --atags
		KEXEC="kexec -e"
	fi ;;
esac

umount /sys
umount /proc
$KEXEC
exec /init
