#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-only
#
# vm-init: actual init script
#
# Copyright (C) 2019 Vitaly Chikunov <vt@altlinux.org>
#

[ $$ != 1 ] && exit 1
export PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin

mount -t proc -o nosuid,noexec,nodev proc /proc
read -r cmdline < /proc/cmdline
cmdline+=" " # Add separator so we can easily match complete words
if [ -z "${cmdline/* VERBOSE *}" ]; then
	V=-v
	V() { echo "- $@"; "$@"; }
else
	V=
	V() { "$@"; }
fi

SCRIPT=${cmdline#* SCRIPT=}
SCRIPT=${SCRIPT%% *}
eval "$(grep -o '  MODPROBE_OPTIONS=.*' "$SCRIPT")"
export MODPROBE_OPTIONS

# Save script if it's on /tmp before mounting tmpfs over it.
case "$SCRIPT" in /tmp/*) SCRIPT_TEXT=$(cat $SCRIPT);; esac
V mount -t tmpfs tmpfs /tmp
if [ -n "${SCRIPT_TEXT-}" ]; then
       echo "$SCRIPT_TEXT" > "$SCRIPT"
       chmod a+x "$SCRIPT"
       unset SCRIPT_TEXT
fi

V mount -t tmpfs tmpfs /var/log
install -g utmp -m660 /dev/null /var/log/btmp
install -g utmp -m664 /dev/null /var/log/wtmp

V mount -t sysfs -o nosuid,noexec,nodev sys /sys

V mount -t devtmpfs -o mode=0755,nosuid,noexec devtmpfs /dev
ln -sf /proc/kcore     /dev/core
ln -sf /proc/self/fd   /dev/fd
ln -sf /proc/self/fd/0 /dev/stdin
ln -sf /proc/self/fd/1 /dev/stdout
ln -sf /proc/self/fd/2 /dev/stderr

V mount -t configfs   configfs   /sys/kernel/config
V mount -t debugfs    debugfs    /sys/kernel/debug
V mount -t securityfs securityfs /sys/kernel/security

mkdir -p /dev/pts /dev/shm /dev/disk/by-id /dev/disk/by-path /dev/disk/by-uuid
V mount -t devpts -o gid=tty,mode=620,noexec,nosuid devpts /dev/pts
V mount -t tmpfs -o mode=1777,size=1M,noexec,nosuid,nodev tmpfs /dev/shm
V mount -t tmpfs -o mode=755,nodev,nosuid,strictatime tmpfs /run

V ip link set dev lo up 2>/dev/null

if grep -q UDEVD=y /proc/cmdline; then
	V udevd --daemon --resolve-names=never
	V udevadm trigger --type=subsystems --action=add
	V udevadm trigger --type=devices --action=add
	V udevadm settle
else
	# try to load some modules
	V modprobe --quiet --all virtio_blk scsi_mod ata_piix pata_acpi \
		ata_generic sd_mod virtio_net
fi
V modprobe virtio-rng

# test writability
if tmp=$(mktemp -p /usr/src 2>/dev/null); then
	rm $tmp
else
	echo "***************************************************"
	echo "*  Error: Impossible to create files in /usr/src  *"
	echo "*         Exit code from qemu cmd will be lost!   *"
	echo "***************************************************"
fi

# Prepare network
network_conf() {
	local iface=$1

	if type ip >/dev/null 2>&1; then
		V ip link set $iface up
		V ip addr add 10.0.2.1/24 dev $iface
		V ip rout add default via 10.0.2.2
	elif type ifconfig >/dev/null 2>&1; then
		V ifconfig lo up
		V ifconfig $iface 10.0.2.1/24 up
		V route add default gw 10.0.2.2
	fi
}
if [ $(wc -l < /proc/net/dev) -gt 3 ]; then
	network_conf $(egrep -vw 'Inter-|face|lo:' /proc/net/dev | head -1 | cut -d: -f1 | tr -d ' ')
fi

# Handle --overlay option
UMOUNT=
ov_count=0
img_count=0
prev_cmdline=$cmdline
while [ -z "${prev_cmdline/* OVERLAY=*}" ]; do
	prev_cmdline=${prev_cmdline#* OVERLAY=}
	OVERLAY=${prev_cmdline%% *}
	IFS=: read ovfs ovpath <<< "$OVERLAY"
	IFS=, read ovfs ovopts <<< "$ovfs"
	mnt=/mnt/$ov_count
	ov_count=$((ov_count+1))
	mkdir -p $mnt
	if [ -b "$ovfs" ]; then
		# full device path
		V mount $V -n "$ovfs" $mnt
	elif [ -b "/dev/$ovfs" ]; then
		# short device name
		V mount $V -n "/dev/$ovfs" $mnt
	elif [ -d "$ovfs" ]; then
		# use existing dir
		mnt=$ovfs
	elif [ "$ovfs" = tmpfs ]; then
		# auto-create tmpfs mount
		V mount $V -t tmpfs ${ovopts:+-o $ovopts} tmpfs $mnt
	elif [ "$ovfs" = ext4 ]; then
		# auto-create ext4 partition
		size=${ovopts#*size=}
		size=${size%%,*}
		img=/usr/src/ext4.$img_count.img
		# do not delete data if it already exists
		if ! /sbin/tune2fs -l $img >/dev/null 2>&1; then
			> $img
			V truncate -s ${size:-11M} $img
			V /sbin/mkfs.ext4 -q $img
		fi
		V mount $V -t ext4 -o loop $img $mnt
		img_count=$((img_count+1))
	else
		echo "Unknown how to overlay $OVERLAY"
		unset mnt
	fi
	if [ "$mnt" ]; then
		mkdir -p $mnt/upper $mnt/work
		: ${ovpath:=/usr/src}
		V mount $V -t overlay -olowerdir=$ovpath,upperdir=$mnt/upper,workdir=$mnt/work overlay $ovpath
		UMOUNT=$ovpath
	fi
	unset mnt ovfs ovopts ovpath
done
unset ov_count img_count prev_cmdline

CONSOLE=${cmdline#console=}
CONSOLE=${CONSOLE%% *}

if [ -n "${cmdline/* NOTTY *}" ]; then
	# resize trick from eudyptula-boot
	previous=$(stty -g)
	stty raw -echo min 0 time 5
	printf '\0337\033[r\033[999;999H\033[6n\0338'
	# save cursor position
	# set scroll region to default
	# move cursor to 999 999
	# report cursor position
	# restore cursor position
	IFS='[;R' read -r _ rows cols _
	stty "$previous"
	if [ "$cols" ] && [ "$rows" ]; then
		V stty cols "$cols" rows "$rows"
	else
		[ "$V" ] && echo "Terminal size is unknown"
	fi
fi

setsid --ctty --wait $SCRIPT 0<>/dev/"$CONSOLE" 1>&0 2>&0
RET=$?

[ "$UMOUNT" ] && umount $V -n --lazy $UMOUNT
case "$SCRIPT" in /tmp/*) umount $V -n --lazy /tmp;; esac
echo $RET 2>/dev/null > $SCRIPT.ret ||
	echo Exit code $RET is lost

# Disable `reboot: Power down' message
[ -z "${cmdline/* quiet *}" ] && echo 0 > /proc/sys/kernel/printk

V sync
V exec -a poweroff /usr/lib/vm-run/initrd-init
