#!/bin/bash

# Copyright (C) 2007, 2008, 2009, 2012 Google Inc.
# Copyright (c) 2014 Jan Rękorajski <baggins@pld-linux.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

set -e

# minimum device size is 512 MB, but we use 511 to account for
# potential rounding
declare -ri MIN_DEV_SIZE=$((511*1048576))

. common.sh

if [ "$GENERATE_CACHE" = "yes" -a ! -d "$CACHE_DIR" ]; then
	mkdir -p "$CACHE_DIR"
fi

_INIT=${INIT_STYLE:-sysvinit}
_REPOS=$(echo "$REPOS" | sha1sum 2>/dev/null | cut -c 1-8 2>/dev/null)
CACHE_FILE="$CACHE_DIR/cache-${SUITE}-${_INIT}-${ARCH:-`uname -m`}-R${_REPOS}.tar"

# If the target device is not a real block device we'll first losetup it.
# This is needed for file disks.
if [ ! -b $blockdev ]; then
	ORIGINAL_BLOCKDEV=$blockdev
	blockdev=$(losetup -sf $blockdev)
	CLEANUP+=("losetup -d $blockdev")
fi

DEVICE_SIZE=$(blockdev --getsize64 $blockdev)
if [ "$DEVICE_SIZE" -lt $MIN_DEV_SIZE ]; then
	echo "Device size is too small ($((DEVICE_SIZE/1048576)) MB)" 1>&2
	echo "Required size is at least 512MB" 1>&2
	exit 1
fi

if [ "$PARTITION_STYLE" = "none" ]; then
	filesystem_dev=$blockdev
elif [ "$PARTITION_STYLE" = "msdos" ]; then
	# Create one big partition, and make it bootable
	format_disk0 $blockdev
	filesystem_dev=$(map_disk0 $blockdev)
	CLEANUP+=("unmap_disk0 $blockdev")
else
	echo "Unknown partition style $PARTITION_STYLE" 1>&2
	exit 1
fi

case $OSP_FILESYSTEM in
	ext2|ext3|ext4)
		mke2fs -Fjqt $OSP_FILESYSTEM $filesystem_dev
		;;
	xfs)
		mkfs.xfs -fq $filesystem_dev
		;;
	*)
		echo "Invalid value '$OSP_FILESYSTEM' for the filesystem parameter" 1>&2
		exit 1
		;;
esac

root_uuid=$($VOL_ID $filesystem_dev )

if [ -n "$swapdev" ]; then
	mkswap $swapdev
	swap_uuid=$($VOL_ID $swapdev || true )
fi

TMPDIR=`mktemp -d` || exit 1
CLEANUP+=("rmdir $TMPDIR")

mount $filesystem_dev $TMPDIR
CLEANUP+=("umount $TMPDIR")

# remove the cache file if it's old (> 2 weeks) and writable by the owner (the
# default due to the standard umask)
if [ "$CLEAN_CACHE" -a -d "$CACHE_DIR" ]; then
	find "$CACHE_DIR" -name 'cache-*.tar' -type f \
		-daystart -mtime "+${CLEAN_CACHE}" -print0 | \
		xargs -r0 rm -f
fi

if [ -f "$CACHE_FILE" ]; then
	tar xf "$CACHE_FILE" -C $TMPDIR
else
	if [ -n "$ARCH" ]; then
		_SRCS="-n th-$ARCH"
	else
		_SRCS=
		for s in ${REPOS:-th}; do
			_SRCS="$_SRCS -n $s"
		done
	fi
	# create basic set of devices for chrooted install
	mkdir -m 755 $TMPDIR/dev
	mkdir -m 1777 $TMPDIR/dev/shm
	mknod -m 666 $TMPDIR/dev/null    c 1 3
	mknod -m 666 $TMPDIR/dev/zero    c 1 5
	mknod -m 666 $TMPDIR/dev/full    c 1 7
	mknod -m 666 $TMPDIR/dev/ptmx    c 5 2
	mknod -m 644 $TMPDIR/dev/random  c 1 8
	mknod -m 644 $TMPDIR/dev/urandom c 1 9
	mknod -m 666 $TMPDIR/dev/tty     c 5 0
	mknod -m 660 $TMPDIR/dev/tty0    c 4 0
	ln -s /proc/self/fd $TMPDIR/dev/fd
	ln -s fd/0 $TMPDIR/dev/stdin
	ln -s fd/1 $TMPDIR/dev/stdout
	ln -s fd/2 $TMPDIR/dev/stderr

	if [ $HYPERVISOR = "xen-hvm" -o $HYPERVISOR = "kvm" ]; then
		# make sure we have kernel and bootloader in HVM guest
		EXTRA_PKGS="$EXTRA_PKGS kernel grub2 grub2-platform-pc dracut"
	fi

	rpm --root=$TMPDIR --initdb
	poldek $_SRCS --up
	poldek $_SRCS -iGv --noask --root=$TMPDIR \
		--pmcmd=/bin/rpm \
		--pset=packages.list \
		--pset=packages.${_INIT}.list \
		$EXTRA_PKGS

	# PLD initrd/initramfs may not contain all required XEN kernel modules
	# we need to copy /lib/modules/<kernel_ver> directory for the kernel
	# used to start the PVM guest
	if [ $HYPERVISOR = "xen-pvm" -a -n "$kernel_ver" ]; then
		# Extract kernel version from vmlinuz image
		# that will be used to start the PVM guest
		_where=$(od -j 526 -N 2 -A n -d $INSTANCE_HV_kernel_path)
		_where=$(($_where + 512))
		kernel_ver=$(dd if=$INSTANCE_HV_kernel_path bs=1 skip=$_where count=128 2>/dev/null | cut -f 1 -d ' ')

		if [ -n "$kernel_ver" ]; then
			if [ -d /lib/modules/$kernel_ver ]; then
				cp -a /lib/modules/$kernel_ver $TMPDIR/lib/modules/
			else
				# ...or try to install the package if modules are not present on host
				kpkg=$(poldek $_SRCS -q --cmd "what-provides kernel*(vermagic)" | grep $kernel_ver)
				if [ -n "$kpkg" ]; then
					poldek $_SRCS -iGv --noask --root=$TMPDIR --pmcmd=/bin/rpm --pmop noscripts $kpkg || :
				fi
			fi
		fi
	fi

	if [ "$GENERATE_CACHE" = "yes" ]; then
		TMP_CACHE=`mktemp "${CACHE_FILE}.XXXXXX"`
		tar cf "$TMP_CACHE" -C $TMPDIR .
		mv -f "$TMP_CACHE" "$CACHE_FILE"
	fi
fi

# reset the root password
echo "root:pld" | chroot $TMPDIR chpasswd -c md5

cp -p /etc/resolv.conf $TMPDIR/etc/resolv.conf
echo $instance > $TMPDIR/etc/hostname

cat > $TMPDIR/etc/fstab <<EOF
#
# Created by ganeti-instance-poldek
#
UUID=$root_uuid	/	$OSP_FILESYSTEM	defaults	0 1
EOF

[ -n "$swapdev" -a -n "$swap_uuid" ] && cat >> $TMPDIR/etc/fstab <<EOF
UUID=$swap_uuid	swap	swap	defaults	0 0
EOF

cat >> $TMPDIR/etc/fstab <<EOF

none	/proc			proc	defaults,noauto,hidepid=2,gid=17	0 0
none	/sys			sysfs	defaults,noauto,gid=17			0 0
none	/sys/fs/cgroup		tmpfs	noauto,nosuid,nodev,noexec,mode=755	0 0
none	/proc/bus/usb		usbfs	defaults,noauto,devgid=78,devmode=0664	0 0
none	/sys/kernel/debug	debugfs	defaults,noauto				0 0
devpts	/dev/pts		devpts	gid=5,mode=620				0 0
none	/dev/shm		tmpfs	mode=1777,nosuid,nodev,noexec		0 0
EOF

# for kvm, we should only activate a serial console if the
# 'serial_console' parameter is set; for xen-pvm though, we should
# always define a serial console
SERIAL_PORT=""
if [ "$INSTANCE_HV_serial_console" = "True" ]; then
	SERIAL_PORT="ttyS0"
elif [ "$HYPERVISOR" = "xen-pvm" ]; then
	SERIAL_PORT="hvc0"
fi

if [ -n "$SERIAL_PORT" ]; then
	if [ -e $TMPDIR/etc/inittab ]; then
		echo "T0:23:respawn:/sbin/getty $SERIAL_PORT 38400" >> $TMPDIR/etc/inittab
	fi
	# systemd will take care of this with systemd-getty-generator
fi

if [ -f $TMPDIR/etc/syslog-ng/syslog-ng.conf ]; then
	if [ ${_INIT} = "systemd" ]; then
		sed -i -e 's|.*unix-.*("/dev/log".*|#&|' $TMPDIR/etc/syslog-ng/syslog-ng.conf
		sed -i -e 's|#\+\(.*unix-.*("/run/systemd/journal/syslog".*\)|\1|' $TMPDIR/etc/syslog-ng/syslog-ng.conf
	else
		sed -i -e 's|#\+\(.*unix-.*("/dev/log".*\)|\1|' $TMPDIR/etc/syslog-ng/syslog-ng.conf
		sed -i -e 's|.*unix-.*("/run/systemd/journal/syslog".*|#&|' $TMPDIR/etc/syslog-ng/syslog-ng.conf
	fi
fi

if [ -L $TMPDIR/etc/systemd/system/default.target ]; then
	ln -sf /lib/systemd/system/multi-user.target $TMPDIR/etc/systemd/system/default.target
fi

RUN_PARTS=`which run-parts`

if [ -n "$RUN_PARTS" -a -n "$CUSTOMIZE_DIR" -a -d "$CUSTOMIZE_DIR" ]; then
	TARGET=$TMPDIR
	BLOCKDEV=$blockdev
	FSYSDEV=$filesystem_dev
	export TARGET SUITE ARCH EXTRA_SOURCES PARTITION_STYLE EXTRA_PKGS BLOCKDEV FSYSDEV
	$RUN_PARTS $CUSTOMIZE_DIR
fi

# execute cleanups
cleanup
trap - EXIT

exit 0
