#!/bin/sh

. /lib/functions.sh
. /lib/functions/system.sh

export IMAGE="$1"
COMMAND="$2"

export INTERACTIVE=0
export VERBOSE=1
export CONFFILES=/tmp/sysupgrade.conffiles

RAMFS_COPY_BIN=		# extra programs for temporary ramfs root
RAMFS_COPY_DATA=	# extra data files

include /lib/upgrade


supivot() { # <new_root> <old_root>
	/bin/mount | grep "on $1 type" 2>&- 1>&- || /bin/mount -o bind $1 $1
	mkdir -p $1$2 $1/proc $1/sys $1/dev $1/tmp $1/overlay $1/ext_overlay && \
	/bin/mount -o noatime,rbind /proc $1/proc && \
	pivot_root $1 $1$2 || {
		/bin/umount -l $1 $1
		return 1
	}

	/bin/mount -o noatime,move $2/sys /sys || /bin/mount -o noatime,rbind $2/sys /sys
	/bin/mount -o noatime,move $2/dev /dev || /bin/mount -o noatime,rbind $2/dev /dev
	/bin/mount -o noatime,move $2/tmp /tmp || /bin/mount -o noatime,bind $2/tmp /tmp
	/bin/mount -o noatime,move $2/overlay /overlay 2>&-
	/bin/mount -o noatime,move $2/ext_overlay /ext_overlay 2>&-
	/bin/umount -l $2/proc $2/sys $2/dev $2/tmp $2/overlay $2/ext_overlay 2>&-
	/bin/umount -l $2/boot 2>&-
	/bin/umount -l $2/boot 2>&- # some platforms like x86 have rebinded /boot
	return 0
}

switch_to_ramfs() {
	RAMFS_COPY_LOSETUP="$(command -v /usr/sbin/losetup)"
	RAMFS_COPY_LVM="$(command -v lvm)"

	for binary in \
		/bin/busybox /bin/ash /bin/sh /bin/mount /bin/umount	\
		pivot_root mount_root reboot sync kill sleep		\
		md5sum hexdump cat zcat dd tar gzip			\
		ls basename find cp mv rm mkdir rmdir mknod touch chmod \
		'[' printf wc grep awk sed cut sort tail		\
		mtd partx losetup mkfs.ext4 nandwrite flash_erase	\
		ubiupdatevol ubiattach ubiblock ubiformat		\
		ubidetach ubirsvol ubirmvol ubimkvol			\
		snapshot snapshot_tool date logger			\
		/usr/sbin/fw_printenv /usr/bin/fwtool			\
		/usr/bin/umount \
		$RAMFS_COPY_LOSETUP $RAMFS_COPY_LVM			\
		$RAMFS_COPY_BIN
	do
		local file="$(command -v "$binary" 2>/dev/null)"
		[ -n "$file" ] && install_bin "$file"
	done
	install_file /etc/resolv.conf /lib/*.sh /lib/functions/*.sh	\
		/lib/upgrade/*.sh /lib/upgrade/do_stage2 		\
		/usr/share/libubox/jshn.sh /usr/sbin/fw_setenv		\
		/etc/fw_env.config $RAMFS_COPY_DATA

	mkdir -p $RAM_ROOT/var/lock

	[ -L "/lib64" ] && ln -s /lib $RAM_ROOT/lib64

	# avoid shared rootfs, which makes `/bin/mount -o noatime,move * *` fail
	# https://github.com/istoreos/istoreos/issues/1200
	mount --make-private / 2>&-
	mount --make-private /tmp 2>&-
	mount --make-private /mnt 2>&-
	mount --make-private /overlay 2>&-
	mount --make-private /ext_overlay 2>&-

	supivot $RAM_ROOT /mnt || {
		v "Failed to switch over to ramfs. Please reboot."
		exit 1
	}

	/bin/mount -o remount,ro /mnt
	/usr/bin/umount -R -d -l /mnt 2>&- || /bin/umount -l /mnt

	grep -e "^/dev/dm-.*" -e "^/dev/loop.*" /proc/mounts | while read bdev mp _r; do
		umount $mp
	done

	[ "$RAMFS_COPY_LOSETUP" ] && losetup -D
	[ "$RAMFS_COPY_LVM" ] && {
		mkdir -p /tmp/lvm/cache
		$RAMFS_COPY_LVM vgchange -aln --ignorelockingfailure
	}
}

kill_remaining() { # [ <signal> [ <loop> ] ]
	local loop_limit=10

	local sig="${1:-TERM}"
	local loop="${2:-0}"
	local run=true
	local stat
	local proc_ppid=$(cut -d' ' -f4  /proc/$$/stat)

	v "Sending $sig to remaining processes ..."

	while $run; do
		run=false
		for stat in /proc/[0-9]*/stat; do
			[ -f "$stat" ] || continue

			local pid name state ppid rest
			read pid rest < $stat
			name="${rest#\(}" ; rest="${name##*\) }" ; name="${name%\)*}"
			set -- $rest ; state="$1" ; ppid="$2"

			# Skip PID1, our parent, ourself and our children
			[ $pid -ne 1 -a $pid -ne $proc_ppid -a $pid -ne $$ -a $ppid -ne $$ ] || continue

			[ -f "/proc/$pid/cmdline" ] || continue

			local cmdline
			read cmdline < /proc/$pid/cmdline

			# Skip kernel threads
			[ -n "$cmdline" ] || continue

			v "Sending signal $sig to $name ($pid)"
			kill -$sig $pid 2>/dev/null

			[ $loop -eq 1 ] && sleep 2 && run=true
		done

		let loop_limit--
		[ $loop_limit -eq 0 ] && {
			v "Failed to kill all processes."
			exit 1
		}
	done
}

istoreos_pre_upgrade() {
	if [ -n "$UPGRADE_BACKUP" ]; then
		local running_ver=$(grep 'DISTRIB_RELEASE=' /rom/etc/openwrt_release /etc/openwrt_release 2>/dev/null | head -n1 | sed -E "s/.*='(.+)'.*/\1/")
		touch /usr/lib/opkg/.upgrading 2>/dev/null && sync /
		# reset rom uuid in ext_overlay
		if [ -e /ext_overlay/etc ]; then
			[ -f /ext_overlay/upper/usr/lib/opkg/.upgrading ] || {
				rm -f /ext_overlay/upper/usr/lib/opkg/.upgrading
				mkdir -p /ext_overlay/upper/usr/lib/opkg
				touch /ext_overlay/upper/usr/lib/opkg/.upgrading 2>/dev/null
			}
			echo "$running_ver" >/ext_overlay/.upgrading
			[ -f /ext_overlay/upper/usr/lib/opkg/.upgrading ] && rm -f /ext_overlay/etc/.extroot-uuid
			sync /ext_overlay
		fi
		echo "$running_ver" >/overlay/.upgrading
		[ -f /overlay/upper/usr/lib/opkg/.upgrading ] || {
			rm -f /overlay/upper/usr/lib/opkg/.upgrading
			mkdir -p /overlay/upper/usr/lib/opkg
			touch /overlay/upper/usr/lib/opkg/.upgrading 2>/dev/null
		}
		sync /overlay
	fi
}

istoreos_show_new_firmware_version() {
	local dist version revision
	json_load "$(cat /tmp/sysupgrade.meta)" || {
		return 1
	}
	json_select "version"
	json_get_var dist "dist"
	json_get_var version "version"
	json_get_var revision "revision"

	v "Flashing Version: '$dist $version $revision'"
}

date '+%Y-%m-%d %H:%M:%S' >/overlay/upgrade.log
export SAVE_LOG=1
v "Running Version: $(grep DISTRIB_DESCRIPTION= /rom/etc/openwrt_release /etc/openwrt_release 2>/dev/null | head -n1 | cut -d= -f2)"
if [ -s /tmp/sysupgrade.meta ]; then
	(istoreos_show_new_firmware_version)
fi

indicate_upgrade

# hide icons/loading.svg, so frontend thinks we're offline
[ -f /www/luci-static/resources/icons/loading.svg ] && mount --bind /dev/full /www/luci-static/resources/icons/loading.svg

while read -r a b c; do
	case "$a" in
		MemT*) mem="$b" ;; esac
done < /proc/meminfo

[ "$mem" -gt 32768 ] && \
	skip_services="dnsmasq log network"
for service in /etc/init.d/*; do
	service=${service##*/}

	case " $skip_services " in
		*" $service "*) continue ;; esac

	ubus call service delete '{ "name": "'"$service"'" }' 2>/dev/null
done

killall -9 telnetd 2>/dev/null
killall -9 dropbear 2>/dev/null
killall -9 ash 2>/dev/null

kill_remaining TERM
sleep 4
sync
kill_remaining KILL 1

sleep 6

# web server stopped now, we can safely unmount the loading icon
umount /www/luci-static/resources/icons/loading.svg 2>/dev/null

echo 3 > /proc/sys/vm/drop_caches

[ -n "$IMAGE" ] && istoreos_pre_upgrade

if [ -n "$IMAGE" ] && type 'platform_pre_upgrade' >/dev/null 2>/dev/null; then
	platform_pre_upgrade "$IMAGE"
fi

ROOTFS_TYPE=$(rootfs_type)
if [ -n "$ROOTFS_TYPE" -a "$ROOTFS_TYPE" != "tmpfs" -a "$ROOTFS_TYPE" != "rootfs" ]; then
	date '+%Y-%m-%d %H:%M:%S' >>/overlay/upgrade.log

	v "Switching to ramdisk..."
	switch_to_ramfs
fi

grep /ext_overlay /proc/mounts > /dev/null && {
	/bin/mount -o noatime,remount,ro /ext_overlay
	/usr/bin/umount -R -d -l /ext_overlay 2>/dev/null || /bin/umount -l /ext_overlay
}

if [ -z "$UPGRADE_BACKUP" ]; then
grep /overlay /proc/mounts > /dev/null && {
	/bin/mount -o noatime,remount,ro /overlay
	/usr/bin/umount -R -d -l /overlay 2>/dev/null || /bin/umount -l /overlay
}
fi

# Exec new shell from ramfs
exec /bin/busybox ash -c "$COMMAND"
