#!/bin/sh -efux
#
# Copyright (c) 2014  Sulev-Madis Silber <ketas@si.pri.ee>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#


set -efux


src_host='bbb-install bbb-install.si.pri.ee'

src_dist='/bbb-dist/minimal'

src_root="$src_dist/root"

src_boot="$src_dist/boot"


#uboot_mmc_dev_num='0'
uboot_mmc_dev_num='1'

hybrid_emmc_sd_boot=''
#hybrid_emmc_sd_boot='yes'


rsync_ips=''


sshd_pidfile='/var/run/sshd.pid'

sshd_pid=''


_make_part_and_fs()
{
	local dev="$1"
	local l_boot="$2"
	local d_boot="$3"
	local l_root="$4"
	local d_root="$5"
	local l_rootalt="$6"
	local d_rootalt="$7"
	
	local l_boot_uc="`echo -n \"$l_boot\" | tr a-z A-Z`"
	
	
	gpart create -s mbr "$dev"
	
	gpart add -t !12     -a 4m -s 4m   -i 1 "$dev"
	gpart add -t freebsd -a 4m -s 512m -i 2 "$dev"
	gpart add -t freebsd -a 4m -s 512m -i 3 "$dev"
	
	gpart set -a active -i 1 "$dev"
	
	gpart create -s bsd "${dev}s2"
	gpart create -s bsd "${dev}s3"
	
	gpart add -t freebsd-ufs -a 4m -i 1 "${dev}s2"
	gpart add -t freebsd-ufs -a 4m -i 1 "${dev}s3"
	
	newfs_msdos -F 12 -L "$l_boot_uc" "/dev/${dev}s1"
	
	newfs -L "$l_root"    -U "/dev/${dev}s2a"
	newfs -L "$l_rootalt" -U "/dev/${dev}s3a"
	
	mkdir -v "$d_boot" "$d_root" "$d_rootalt"
	
	mount -v -t msdosfs -o noatime "/dev/msdosfs/$l_boot_uc" "$d_boot"
	mount -v            -o noatime "/dev/ufs/$l_root"        "$d_root"
	mount -v            -o noatime "/dev/ufs/$l_rootalt"     "$d_rootalt"
}


___rsync()
{
	local type="$1"
	local dest="$2"
	local host="$3"
	local opts="$4"
	
	local rsync_src=''
	local rsync_extra_opts=''
	
	
	rsync_extra_opts="$rsync_extra_opts -c $opts"
	
	
	if [ "$type" = root -o "$type" = rootinactive ]
	then
		rsync_src="$src_root/"
		
		rsync_extra_opts="$rsync_extra_opts --exclude .snap"
		
		[ "$type" = root -a "`mount -t devfs | grep '^devfs on /dev '`" ] \
		&& rsync_extra_opts="$rsync_extra_opts --exclude dev"
	elif [ "$type" = ufsboot ]
	then
		rsync_src="$src_root/"
		
		rsync_extra_opts="$rsync_extra_opts --include /boot/*** --include /dev/ --include /etc/ --include /etc/fstab --include /rescue/*** --include /tmp/ --include /var/ --exclude * --filter P_/.snap --delete-excluded"
	elif [ "$type" = boot ]
	then
		rsync_src="$src_boot/"
		
		rsync_extra_opts="$rsync_extra_opts -O --modify-window=1 --no-p"
	fi
	
	
	rsync \
	\
	-vhhixaHAXS \
	\
	$rsync_extra_opts \
	\
	--stats        \
	--progress     \
	--numeric-ids  \
	--delete       \
	--force        \
	--force-delete \
	--force-change \
	\
	"[$host]:$rsync_src" \
	\
	"$dest"
}


__rsync()
{
	local type="$1"
	local dest="$2"
	local host="$3"
	
	local rc='0'
	
	
	___rsync "$type" "$dest" "$host" '--fileflags' || rc="$?"
	
	
	[ "$rc" = 0 ] && return "$rc"
	
	
	[ "$type" = root -o "$type" = rootinactive ] && chflags -vvR 0 "$dest"
	
	
	rc='0'
	
	___rsync "$type" "$dest" "$host" '' || rc="$?"
	
	
	[ "$type" = boot ] && return "$rc"
	
	
	rc='0'
	
	___rsync "$type" "$dest" "$host" '--fileflags'
}


_rsync()
{
	local type="$1"
	local dest="$2"
	
	local rc='0'
	local host=''
	
	
	for host in $rsync_ips $src_host
	do
		rc='0'
		
		__rsync "$type" "$dest" "$host" || rc="$?"
		
		[ "$rc" = 0 ] && break
	done
	
	
	return "$rc"
}


_service()
{
	local mode="$1"
	
	
	case "$mode"
	in
		start)
			for service in var ldconfig ldconfig
			do
				service "$service" start
			done
			
			[ "$sshd_pid" ] && echo -n "$sshd_pid" > "$sshd_pidfile"
			
			[ "${2:-}" != noreboot ] && return
			
			for service in newsyslog devd rtsold syslogd casperd
			do
				service "$service" start
			done
			;;
		stop)
			[ -f "$sshd_pidfile" ] && sshd_pid="`cat \"$sshd_pidfile\"`"
			;;
	esac
	
	
	grep -i '^ifconfig_.*dhcp' /etc/rc.conf | cut -d = -f 1 | cut -d _ -f 2 \
	| xargs -n 1 service dhclient "$mode" || true
	
	
	case "$mode"
	in
		start)
			for service in ntpd cron
			do
				service "$service" start || true
			done
			
			service sshd restart || true
			
			poe stop
			poe start
			;;
		stop)
			for service in devd rtsold syslogd casperd ntpd cron
			do
				service "$service" stop || true
			done
			;;
	esac
}


_sysinfo()
{
	ps wwaux
	
	ps wwauxd
	
	df -H
	
	mdconfig -vl
}


_ufslabel_to_slicenum()
{
	local label="$1"
	
	local num="`glabel status | awk -v \"fs=$label\" '$1 == fs { print $3 }' | sed 's|^mmcsd[01]s||; s|a$||'`"
	
	
	case "$num"
	in
		[0-9]) echo "$num" ;;
		*)     exit 1      ;;
	esac
}


_check_and_config_root()
{
	local dir="$1"
	local rootfs="$2"
	
	
	(
		cd "$dir"
		
		[ -d boot -a -d etc -a -d dev -a -d var -a -f boot/kernel/kernel ] ||
		{
			cat <<- EOF
			
			
			UPGRADE ERROR: ROOT FILESYSTEM IS NOT SWITCHED
			
			
			EOF
			exit 1
		}
		
		cd etc
		
		egrep -q "^.+[[:space:]]+/[[:space:]]+" fstab &&
		{
			egrep -q "^$rootfs[[:space:]]+/" fstab ||
			{
				sed -E "s|^/dev/ufs/bbbemmcroot(alt)?|$rootfs|" < fstab > fstab.new
				
				mv -v fstab.new fstab
			}
		} \
		|| true
	)
}


_check_and_config_boot()
{
	local dir="$1"
	local slicenum="$2"
	local uboot_rootfs="$3"
	
	
	(
		cd "$dir"
		
		[ -f mlo -a -f u-boot.img -a -f uenv.txt -a -f ubldr ] ||
		{
			cat <<- EOF
			
			
			SERIOUS UPGRADE ERROR: BOARD WILL NOT BOOT, FIX IT!!!
			
			
			EOF
			exit 1
		}
		
		grep -q ^loaderdev= uenv.txt &&
		{
			grep -q "^loaderdev=mmc$uboot_mmc_dev_num:$slicenum\.0\$" uenv.txt ||
			{
				sed -E "s|^(loaderdev=mmc).*|\1$uboot_mmc_dev_num:$slicenum.0|" < uenv.txt > uenv.new
				
				mv -v uenv.new uenv.txt
			}
		} \
		|| true
		
		grep -q ^bbb_bootmode_default= uenv.txt &&
		{
			grep -q "^bbb_bootmode_default=$uboot_rootfs\$" uenv.txt ||
			{
				sed -E "s|^(bbb_bootmode_default=).*|\1$uboot_rootfs|" < uenv.txt > uenv.new
				
				mv -v uenv.new uenv.txt
			}
		} \
		|| true
	)
}


fsck_pids="`pgrep -f fsck || true`"

if [ "$fsck_pids" ]
then
	echo "Found currently running fsck processes: $fsck_pids"
	
	for fsck_pid in $fsck_pids
	do
		echo "Wait until fsck (PID '$fsck_pid') exits..."
		
		pwait -v "$fsck_pid"
	done
fi


case "${1:-}"
in
	selfcopy)
		dest_boot='/mnt'
		dest_root=''
		
		
		bootfs='/dev/msdosfs/BBBEMMCBOOT'
		
		
		_sysinfo
		
		
		_service stop
		
		for host in $src_host
		do
			rsync_ips="$rsync_ips `getent hosts \"$host\" | awk '{print $1}'`"
		done
		
		umount -vf /tmp /var || true
		
		umount -v "$dest_boot" || true
		
		mdconfig -l | xargs -n 1 mdconfig -du
		
		
		_sysinfo
		
		
		mount -vuw -o noatime "$dest_root"
		
		_rsync root "$dest_root/"
		
		time -h mount -vfur -o noatime "$dest_root"
		
		for fs in /tmp /var
		do
			mount -v "$fs" || true
		done
		
		_service start "${2:-}"
		
		
		mount -v -t msdosfs -o noatime "$bootfs" "$dest_boot"
		
		_rsync boot "$dest_boot/"
		
		umount -v "$dest_boot"
		
		
		_sysinfo
		
		
		[ "${2:-}" != noreboot ] && shutdown -r now
		;;
	
	
	copyinactive|'')
		dest_boot='/mnt'
		dest_ufsboot='/mnt'
		dest_root='/mnt'
		
		
		bootfs='/dev/msdosfs/BBBEMMCBOOT'
		
		
		active_rootfs="`mount | fgrep ' on / ' | cut -d ' ' -f 1`"
		
		
		case "$active_rootfs"
		in
			/dev/ufs/bbbemmcroot)
				inactive_rootfs='/dev/ufs/bbbemmcrootalt'
				inactive_ufsbootfs='/dev/ufs/bbbsdufsbootalt'
				
				inactive_slicenum="`_ufslabel_to_slicenum \"ufs/bbbemmcrootalt\"`"
				inactive_uboot_rootfs='rootalt'
				;;
			
			/dev/ufs/bbbemmcrootalt)
				inactive_rootfs='/dev/ufs/bbbemmcroot'
				inactive_ufsbootfs='/dev/ufs/bbbsdufsboot'
				
				inactive_slicenum="`_ufslabel_to_slicenum \"ufs/bbbemmcroot\"`"
				inactive_uboot_rootfs='root'
				;;
		esac
		
		
		for fs in `echo "$dest_boot" "$dest_ufsboot" "$dest_root" | xargs -n 1 | sort | uniq`
		do
			umount -v "$fs" || true
		done
		
		
		fsck_ufs -yR "$inactive_rootfs"
		
		mount -v -o noatime "$inactive_rootfs" "$dest_root"
		
		_rsync rootinactive "$dest_root/"
		
		_check_and_config_root "$dest_root" "$inactive_rootfs"
		
		time -h umount -v "$dest_root"
		
		
		[ "$hybrid_emmc_sd_boot" = yes ] &&
		{
			fsck_ufs -yR "$inactive_ufsbootfs"
			
			mount -v -o noatime "$inactive_ufsbootfs" "$dest_ufsboot"
			
			_rsync ufsboot "$dest_ufsboot/"
			
			_check_and_config_root "$dest_ufsboot" "$inactive_rootfs"
			
			time -h umount -v "$dest_ufsboot"
		}
		
		
		fsck_msdosfs -y "$bootfs"
		
		mount -v -t msdosfs -o noatime "$bootfs" "$dest_boot"
		
		_rsync boot "$dest_boot/"
		
		_check_and_config_boot "$dest_boot" "$inactive_slicenum" "$inactive_uboot_rootfs"
		
		umount -v "$dest_boot"
		
		
		cat <<- EOF
		
		
		======================================================
		======================================================
		======================================================
		
		
		UPGRADE OK
		
		
		active_rootfs      = '$active_rootfs'
		
		inactive_rootfs    = '$inactive_rootfs'
		inactive_ufsbootfs = '$inactive_ufsbootfs'
		inactive_slicenum  = '$inactive_slicenum'
		
		uboot_mmc_dev_num  = '$uboot_mmc_dev_num'
		
		
		SWITCHED BOOT FILESYSTEM TO:
		
		   '$uboot_mmc_dev_num' / '$inactive_slicenum' / '$inactive_ufsbootfs' / '$inactive_rootfs'
		
		
		======================================================
		======================================================
		======================================================
		
		
		EOF
		
		
		[ "${2:-}" != noreboot ] && shutdown -r now
		;;
	
	
	copy)
		dest_boot="$2"
		dest_root="$3"
		
		
		_rsync rootinactive "$dest_root/"
		
		
		_rsync boot "$dest_boot/"
		;;
	
	
	newfs)
		dest_disk='mmcsd1'
		
		label_boot='bbbemmcboot'
		label_root='bbbemmcroot'
		label_rootalt='bbbemmcrootalt'
		
		dest_boot="/tmp/$label_boot"
		dest_root="/tmp/$label_root"
		dest_rootalt="/tmp/$label_rootalt"
		
		
		_make_part_and_fs "$dest_disk" "$label_boot" "$dest_boot" "$label_root" "$dest_root" "$label_rootalt" "$dest_rootalt"
		
		
		_rsync boot         "$dest_boot/"
		_rsync rootinactive "$dest_root/"
		_rsync rootinactive "$dest_rootalt/"
		
		_check_and_config_boot "$dest_boot"    "`_ufslabel_to_slicenum \"ufs/$label_root\"`"
		_check_and_config_root "$dest_root"    "/dev/ufs/$label_root"
		_check_and_config_root "$dest_rootalt" "/dev/ufs/$label_rootalt"
		
		umount -v "$dest_boot" "$dest_root" "$dest_rootalt"
		;;
esac
