#!/bin/sh

PREFIX=
. "$PREFIX/lib/libalpine.sh"

MBR=${MBR:-"/usr/share/syslinux/mbr.bin"}

in_list() {
	local i="$1"
	shift
	while [ $# -gt 0 ]; do
		[ "$i" = "$1" ] && return 0
		shift
	done
	return 1
}

enumerate_fstab() {
	local mnt="$1"
	[ -z "$mnt" ] && return
	local escaped_mnt=$(echo $mnt | sed 's:/:\\/:g')
	awk "\$2 ~ /^$escaped_mnt/ {print \$0}" /proc/mounts | \
		sed "s:$mnt:/:g; s: :\t:g" | sed 's:/\+:/:g'
}

is_vmware() {
	grep -q VMware /proc/scsi/scsi 2>/dev/null \
		|| grep -q VMware /proc/ide/hd*/model 2>/dev/null
}

install_mounted_root() {
	local mnt="$1"
	local features="ata base bootchart cdrom ext2 ext3 ide scsi usb"

	rootdev=$(awk "\$2 == \"$mnt\" { print \$1 }" /proc/mounts)
	if [ -z "$rootdev" ]; then
		echo "$mnt does not seem to be a mount point" >&2
		return 1
	fi

	local fs=$(awk "\$1 == \"$rootdev\" {print \$3}" /proc/mounts)
	if [ "$fs" != "ext2" ] && [ "$fs" != "ext3" ]; then
		echo "$fs is not supported. Only ext2 and ext3 are supported" >&2
		return 1
	fi
		
	rootdisk=${rootdev%[0-9]*}

	echon "Installing system on $rootdev: "
	lbu package - | tar -C "$mnt" -zx
	# apk reads config from target root so we need to copy the config
	mkdir -p "$mnt"/etc/apk/keys/
	cp /etc/apk/keys/* "$mnt"/etc/apk/keys/

	apk add -q --progress --update-cache --root "$mnt" \
		$(cat "$mnt"/var/lib/apk/world) \
		acct linux-grsec alpine-base >/dev/null || return 1
	echo ""
	# make things bootable
	kernel=$(ls "$mnt"/lib/modules)
	if [ "$rootdisk" = "/dev/md" ]; then
		local md=${rootdev#/dev/}
		features="$features raid"
		raidmod=$(cat /sys/block/$md/md/level)
		raidmod=",$raidmod"
		raidopt="-r"
		# get a list of slaves
		rootdisk=
		for i in /sys/block/$md/slaves/*; do
			j=${i##*/}
			i=${j%[0-9]*}
			rootdisk="$rootdisk /dev/${i}"
		done
	fi

	ln -s boot/grsec.gz "$mnt"/grsec.gz
	ln -s boot/grsec "$mnt"/grsec

	if is_vmware; then
		pax_nouderef="pax_nouderef "
	else
		pax_nouderef=
	fi

	# create an extlinux.conf
	cat >"$mnt"/boot/extlinux.conf <<EOF
timeout 20
prompt 1
default grsec
label grsec
	kernel /grsec
	append initrd=/grsec.gz root=$rootdev modules=sd-mod,usb-storage,ext3$raidmod ${pax_nouderef}quiet
EOF
	# fix the fstab
	enumerate_fstab "$mnt" >> "$mnt"/etc/fstab

	# install extlinux
	apk add -q syslinux
	extlinux -i $raidopt "$mnt"/boot/

	# unmount the partitions
	umount $(awk '{print $2}' /proc/mounts | grep ^"$mnt" | sort -r)

	# fix mbr for all disk devices
	for i in $rootdisk; do
		local errmsg
		echo "Writing MBR to $i"
		errmsg=$(dd if="$MBR" of=$i 2>&1) \
			|| echo "$errmsg"
	done
	echo ""
	echo "Installation is done. Please reboot."
	apk del -q syslinux
}



# figure out decent default swap size in mega bytes
find_swap_size() {
	local memtotal_kb=$(awk '$1 == "MemTotal:" {print $2}' /proc/meminfo)
	# use 2 * avaiable ram
	echo $(( $memtotal_kb * 2 / 1024 ))
}

has_mounted_part() {
	local p
	# parse /proc/mounts for moutned devices
	for p in $(awk '$1 ~ /^\/dev\// {gsub("/dev/", "", $1); print $1}' \
			/proc/mounts); do
		if [ -e /sys/block/$1/$p ]; then
			return 0
		fi
	done
	return 1
}

find_disks() {
	local p= disks=
	for p in $(awk '$1 ~ /[0-9]+/ {print $4}' /proc/partitions); do
		b=$(echo $p | sed 's:/:!:g')
		if [ -e /sys/block/$b/device ] && ! has_mounted_part $p; then
			disks="$disks $p"
		fi
	done
	echo $disks
}

useall() {

	local rootdisk_dev="$1"
	local i size
	local boot_size=100 boot_part_type="83" 
	local swap_size=$(find_swap_size) swap_part_type="82"
	local root_part_type="83"
	local raidpkg= partitions=
	local minimum_root_size=$(($boot_size * 2));

	if [ -n "$USE_RAID" ]; then
		boot_part_type="fd"
		swap_part_type="fd"
		root_part_type="fd"
		raidpkg="mdadm"
	fi

	dmesg -n1
	apk_add -q sfdisk e2fsprogs $raidpkg || return 1
	local root_size=$(( $(sfdisk -s $rootdisk_dev) / 1024 - $swap_size - $boot_size))
	if [ "$root_size" -lt "$minimum_root_size" ]; then
		echo "The $rootdisk_dev is too small. At least $(( $boot_size + $swap_size + $minimum_root_size)) is needed." >&2
		return 1
	fi

	echo ""
	echo "Creating the following partitions on $rootdisk_dev:"
	echo " /boot	${boot_size}MB"
	echo " swap	${swap_size}MB"
	echo " /	${root_size}MB"
	echo ""
	echo -n "WARNING: All contents of $rootdisk_dev will be erased. Continue? [y/N]: "
	read i
	case "$i" in
		y*|Y*);;
		*) return 1;;
	esac
	
	echo "Initializing partitions..."
	if [ -n "$USE_RAID" ]; then
		local rd
		for rd in md0 md1 md2; do
			[ -b /dev/$rd ] && mdadm --stop /dev/$rd
		done
	fi

	# new disks does not have an DOS signature in sector 0
	# this makes sfdisk complain. We can workaround this by letting
	# fdisk create that DOS signature, by just do a "w", a write.
	# http://bugs.alpinelinux.org/issues/show/145
	echo "w" | fdisk $rootdisk_dev >/dev/null

	# create new partitions
	(cat <<EOF
0,$boot_size,$boot_part_type,*
,$swap_size,$swap_part_type
,,$root_part_type
EOF
	) | sfdisk -q -L -uM $rootdisk_dev >>/tmp/sfdisk.out || return 1

	# create device nodes if not exist
	mdev -s

	if [ -n "$USE_RAID" ]; then
		local p= rd=
		for p in $(sfdisk -l $rootdisk_dev 2>/dev/null \
				| awk '/Linux raid/ {print $1}'); do
			case "$p" in
				*1) rd=/dev/md0; boot_dev=/dev/md0;;
				*2) rd=/dev/md1; swap_dev=/dev/md1;;
				*3) rd=/dev/md2; root_dev=/dev/md2;;
			esac
			mdadm --create $rd --level=1 --raid-devices=2 \
				--quiet --run $p missing
		done
	else
		local p=
		for p in $(sfdisk -l $rootdisk_dev 2>/dev/null \
				| awk '$1 ~ /^\/dev/ {print $1}'); do
			case "$p" in
				*1) boot_dev=$p;;
				*2) swap_dev=$p;;
				*3) root_dev=$p;;
			esac
		done
	fi
	mkfs.ext3 -q $boot_dev >/dev/null \
		&& mkswap $swap_dev >/dev/null \
		&& mkfs.ext3 -q >/dev/null $root_dev \
		|| return 1

	mkdir -p /mnt
	mount -t ext3 $root_dev /mnt || return 1
	mkdir -p /mnt/boot
	mount -t ext3 $boot_dev /mnt/boot || return 1
	if [ -n "$USE_RAID" ]; then
		mdadm --detail --scan > /etc/mdadm.conf
		rc-update --quiet add mdadm-raid boot
	fi
	rc-update --quiet add swap boot
	# the func to generate fstab does not detect swap. add it manually
	sed -i -e '/swap/d' /etc/fstab
	echo -e "$swap_dev\tswap\t\tswap\tdefaults 0 0" >> /etc/fstab
	install_mounted_root /mnt
}

# Parse args
while getopts "r" opt; do
	case $opt in
		r) USE_RAID=1;;
	esac
done
shift $(( OPTIND - 1))

if [ -d "$1" ]; then
	# install to given mounted root
	install_mounted_root "${1%/}"
	exit $?
fi

disks=$(find_disks)

# no disks so lets exit quietly.
[ -z "$disks" ] && exit 0

if [ $# -gt 0 ]; then
	# check that they are 
	for i in "$@"; do
		j=$(readlink -f "$i" | sed 's:^/dev/::; s:/:!:g')
		if ! [ -e "/sys/block/$j/device" ]; then
			echo "$i is not a suitable for partitioning"
			exit 1
		fi
	done

else
	set -- $disks
	rootdisk=
	while ! in_list "$rootdisk" $disks "none" "abort"; do
		echo "Available disks are: $disks"
		echon "Which one is the root disk? (or none) [$1] "
		default_read rootdisk $1
	done
	case "$rootdisk" in
		none|abort) exit 0;;
	esac
fi

#echon "Do you want use *all* of $rootdisk for Alpine? (y/n) [n] "
#default_read useall "n"
#case "$useall" in
#	[Yy]*) useall="yes";;
#esac
#
#if [ "x$useall" != "xyes" ]; then
#	echo "Only 'use all' option is available at the moment. Sorry"
#	exit 1
#fi

rootdisk_dev=${rootdisk_dev:-"/dev/$rootdisk"}

if ! [ -b "$rootdisk_dev" ]; then
	echo "$rootdisk_dev is not a block device" >&2
	exit 1
fi

useall $rootdisk_dev
