#!/bin/bash
#================
# FILE          : linuxrc
#----------------
# PROJECT       : OpenSuSE KIWI Image System
# COPYRIGHT     : (c) 2006 SUSE LINUX Products GmbH. All rights reserved
#               :
# AUTHOR        : Marcus Schaefer <ms@suse.de>
#               :
# BELONGS TO    : Operating System images
#               :
# DESCRIPTION   : This file includes functions for the OEM
#               : installation mode. Installation means a dump of
#               : the virtual disk file onto a preselected disk
#               :
#               :
# STATUS        : BETA
#----------------
#
#======================================
# OEMNetwork
#--------------------------------------
function OEMNetwork {
	# /.../
	# setup network if pxe network installation mode was
	# requested
	# ----
	#======================================
	# load nics detected by hwinfo
	#--------------------------------------
	local loaded=0
	probeNetworkCard
	IFS=":"
	for i in $networkModule;do
		if [ ! -z $i ];then
			modprobe $i 2>/dev/null
			if test $? = 0;then
				loaded=1
			fi
		fi
	done
	IFS=$IFS_ORIG
	if test $loaded = 0;then
		systemException \
			"Network module: Failed to load network module !" \
		"reboot"
	fi
	#======================================
	# setup network via DHCP
	#--------------------------------------
	setupNetwork
	#======================================
	# find TFTP server from lease file
	#--------------------------------------
	checkServer
	if [ -z $SERVER ];then
		SERVER=tftp.$DOMAIN
	fi
	Echo "Checking Server name: $SERVER"
	if ! ping -c 1 $SERVER >/dev/null 2>&1;then
		Echo "Server: $SERVER not found"
		if [ -z "$SERVERTYPE" ] || [ "$SERVERTYPE" = "tftp" ]; then
			if [ ! -z "$DHCPSIADDR" ];then
				Echo "Using: $DHCPSIADDR from dhcpcd-info"
				SERVER=$DHCPSIADDR
			elif [ ! -z "$DHCPSID" ];then
				Echo "Using: $DHCPSID from dhcpcd-info"
				SERVER=$DHCPSID
			else
				systemException \
					"Can't assign SERVER IP/name... fatal !" \
				"reboot"
			fi
		fi
	fi
	#======================================
	# setup server name and blocksize
	#--------------------------------------
	imageBlkSize=8192
	imageServer=$SERVER
}
#======================================
# OEMFindPartitions
#--------------------------------------
function OEMFindPartitions {
	# /.../
	# find partitions without known filesystem on the given
	# disk device and return a list containing the information
	# device:size-KB,device:size-KB,...
	# ----
	local disk=$1
	local result
	disk=$(echo $disk | sed -e s@^/dev/@@)
	for i in $(cat /proc/partitions |\
		grep -E $disk.+ | sed "s/^ *//;s/ *$//;s/ \{1,\}/:/g" |\
		cut -f3,4 -d:)
	do
		local device=/dev/$(echo $i | cut -f2 -d:)
		local kbsize=$(echo $i | cut -f1 -d:)
		local mbsize=$((kbsize / 1024))
		local partid=$(partitionID /dev/$disk $(nd $device))
		if [ ! "$partid" = "83" ] && [ ! "$partid" = "8e" ] ;then
			continue
		fi
		probeFileSystem $device
		if [ ! -z "$FSTYPE" ] && [ "$FSTYPE" != "unknown" ];then
			continue
		fi
		if [ -z "$result" ];then
			result=$device:$mbsize
		else
			result=$result,$device:$mbsize
		fi
	done
	echo $result
}
#======================================
# OEMInstall
#--------------------------------------
function OEMInstall {
	# /.../
	# Installation mode: find a usable disk to install the image
	# on. The install image is a virtual disk. The system will be
	# rebooted at the end of this function
	# ----
	local count=0
	local dsize=0
	local index=0
	local message
	local prefix=/cdrom
	#======================================
	# Check for install mode indicator file
	#--------------------------------------
	if [ ! -f $VMX_SYSTEM ];then
		return
	fi
	#======================================
	# Check for install mode combination
	#--------------------------------------
	if [ ! -z "$OEM_PARTITION_INSTALL" ] && [ ! -z "$pxe" ];then
		systemException \
			"Partition install via remote interface is not supported" \
		"reboot"
	fi
	#======================================
	# Display license if text exists
	#--------------------------------------
	displayEULA
	#======================================
	# Search CD/DVD/USB stick and mount it
	#--------------------------------------
	if [ -z "$pxe" ];then
		USBStickDevice install
		if [ $stickFound = 0 ];then
			Echo "Search for USB stick failed, checking CD/DVD drive"
			CDMount
			export OEMInstallType=CD
		else
			Echo "Found Stick: $stickRoot -> $stickSerial"
			mkdir -p /cdrom && mount $(ddn $stickRoot 2) /cdrom
			export OEMInstallType=USB
			unset stickSerial
		fi
	else
		OEMNetwork
		export OEMInstallType=PXE
	fi
	#======================================
	# Search and ask for the install disk
	#--------------------------------------
	IFS=$IFS_ORIG
	Echo "Searching harddrive for $OEMInstallType installation"
	hwinfo=/usr/sbin/hwinfo
	if [ "$OEMInstallType" = "USB" ];then
		deviceDisks=`$hwinfo --disk |\
			grep "Device File:" | cut -f2 -d: |\
			cut -f1 -d"(" | sed -e s"@$imageDiskDevice@@"`
	else
		deviceDisks=`$hwinfo --disk |\
			grep "Device File:" | cut -f2 -d: |\
			cut -f1 -d"("`
	fi
	export deviceDisks=`echo $deviceDisks`
	if [ -z "$deviceDisks" ];then
		systemException \
			"No device(s) for installation found... abort" \
		"reboot"
	fi
	Echo "Found following disk device(s)"
	count=0
	for i in $deviceDisks;do
		dsize=`partitionSize $i`
		dsizeMB=`expr $dsize / 1024`
		if [ "$dsizeMB" -gt 0 ];then
			Echo -b "Disk $count -> $i [ $dsizeMB MB ]"
			deviceArray[$count]=$i
			deviceDSize[$count]=$dsize
			count=`expr $count + 1`
		fi
	done
	if [ "$count" = "1" ];then
		#======================================
		# Found one single disk... use it
		#--------------------------------------
		instDisk=${deviceArray[0]}
	else
		#======================================
		# Found multiple disks...
		#--------------------------------------
		hd="\"$TEXT_SELECT\""
		count=0
		for i in $deviceDisks;do
			dsize=`partitionSize $i`
			dsizeMB=`expr $dsize / 1024`
			if [ "$dsizeMB" -gt 0 ];then
				dname=`getDiskID $i`
				dname=`basename $dname | cut -c1-20`"..."
				if [ $count = 0 ];then
					dpara="$i \"$dname [ $dsizeMB MB ]\" on"
					count=1
				else
					dpara="$dpara $i \"$dname [ $dsizeMB MB ]\" off"
				fi
			fi
		done
		instDisk=$(runInteractive \
			"--stdout --radiolist $hd 20 75 15 $dpara"
		)
		if [ ! $? = 0 ];then
			systemException \
				"System installation canceled" \
			"reboot"
		fi
	fi
	imageDevice=$instDisk
	#======================================
	# Setup device names for further boot
	#--------------------------------------
	export imageDiskExclude=$instDisk
	export imageDiskDevice=$instDisk
	#======================================
	# Import vmx configuration file
	#--------------------------------------
	importFile < $VMX_SYSTEM
	#======================================
	# Evaluate OEM install file
	#--------------------------------------
	field=0
	imageZipped="uncompressed"
	IFS=";" ; for n in $IMAGE;do
	case $field in
		0) field=1 ;; 
		1) imageName=$n   ; field=2 ;;
		2) imageVersion=$n; field=3 ;;
		3) imageZipped=$n ;
	esac
	done
	if [ ! -z "$pxe" ];then
		prefix=/image
	fi
	if [ "$imageZipped" = "compressed" ];then
		imageName="$prefix/$imageName.gz"
		imageMD5="$imageName.md5"
	else
		imageName="$prefix/$imageName"
		imageMD5="$imageName.md5"
	fi
	IFS=$IFS_ORIG
	#======================================
	# Search, ask for the install partition
	#--------------------------------------
	if [ ! -z "$OEM_PARTITION_INSTALL" ];then
		#======================================
		# Loop mount disk image file
		#--------------------------------------
		reqpart=1
		loop=$(losetup -s -f $imageName)
		loop=$(echo $loop | sed -e s@^/dev/@@)
		if ! kpartx -a /dev/$loop;then
			systemException \
				"Loop setup for $instDisk failed" \
			"reboot"
		fi
		imageName=/dev/mapper/${loop}p1
		if [ -e /dev/mapper/${loop}p2 ];then
			reqpart=2
		fi
		#======================================
		# Lookup/Check available partitions
		#--------------------------------------
		if [ $reqpart -gt 2 ];then
			Echo "Sorry only disk images with a root and optional boot"
			Echo "partition are supported in partition based install mode"
			Echo "I found $reqpart partitions in the disk image"
			systemException \
				"Installation aborted..." \
			"reboot"
		fi
		partitions=$(OEMFindPartitions $instDisk)
		if [ ! -z "$partitions" ];then
			count=0
			IFS=","
			for i in $partitions;do
				count=$((count + 1))
			done
			IFS=$IFS_ORIG
		fi
		if [ -z "$partitions" ] || [ $count -lt $reqpart ];then
			if [ -z "$partitions" ];then
				Echo "No suitable partition(s) for installation found:"
			else
				Echo "Not enough partitions for installation found:"
				Echo "Found $count but required $reqpart"
			fi
			Echo "Please prepare your disk first:"
			echo
			Echo -b "kiwi requires $reqpart partition(s) of the types below"
			Echo -b "without any filesystem inside. I recommend to use YaST"
			Echo -b "to do the re-partitioning. YaST also allows you to shrink"
			Echo -b "existing partitions without loosing data."
			echo
			count=0
			for i in /dev/mapper/${loop}*;do
				psize=`partitionSize $i`
				partid=$(partitionID /dev/$loop $(nd $i))
				if [ $count = 0 ];then
					Echo -b "* Root partition requires at least $psize KB"
				fi
				if [ $count = 1 ];then
					Echo -b "* Boot partition requires at least $psize KB"
				fi
				Echo -b "  Partition Type: 0x$partid"
				count=$((count + 1))
			done
			echo
			systemException \
				"Installation aborted..." \
			"reboot"
		fi
		#======================================
		# Do we have a LVM image...
		#--------------------------------------
		for i in /dev/mapper/${loop}*;do
			partid=$(partitionID /dev/$loop $(nd $i))
			if [ "$partid" = "8e" ];then
				export haveLVM=yes
			fi
		done
		#======================================
		# Select all required partitions
		#--------------------------------------
		if [ $count -eq $reqpart ] && [ $reqpart -eq 1 ];then
			#======================================
			# There is only one free and required
			#--------------------------------------
			pname=`echo $partitions | cut -f1 -d:`
			imageDevice=$pname
			rID=$(nd $pname)
			instItems[0]=$imageName:$imageDevice
		else
			#======================================
			# There is a choice, let the user do it
			#--------------------------------------
			for p in /dev/mapper/${loop}*;do
				if [ $p = /dev/mapper/${loop}p1 ];then
					select="root"
				elif [ $p = /dev/mapper/${loop}p2 ];then
					select="boot"
				fi
				TEXT_SELECT=$(
					getText "Select %1 partition for installation:" $select)
				count=0
				IFS=","
				for i in $partitions;do
					psize=`echo $i | cut -f2 -d:`
					pname=`echo $i | cut -f1 -d:`
					if [ $count = 0 ];then
						dpara="$pname \"[ $psize MB ]\" on"
						count=1
					else
						dpara="$dpara $pname \"[ $psize MB ]\" off"
					fi
					count=$((count + 1))
				done
				IFS=$IFS_ORIG
				selectedPart=$(runInteractive \
					"--stdout --radiolist \"$TEXT_SELECT\" 20 75 15 $dpara"
				)
				if [ ! $? = 0 ];then
					systemException \
						"System installation canceled" \
					"reboot"
				fi
				count=0
				IFS=","
				for i in $partitions;do
					psize=`echo $i | cut -f2 -d:`
					pname=`echo $i | cut -f1 -d:`
					if [ ! $pname = $selectedPart ];then
						if [ $count = 0 ];then
							partitions_next=$pname:$psize
						else
							partitions_next=$partitions_next,$pname:$psize
						fi
					fi
					count=$((count + 1))
				done
				IFS=$IFS_ORIG
				partitions=$partitions_next
				if [ $p = /dev/mapper/${loop}p1 ];then
					imageDevice=$selectedPart
					rID=$(nd $selectedPart)
					instItems[0]=$imageName:$imageDevice
				elif [ $p = /dev/mapper/${loop}p2 ];then
					bID=$(nd $selectedPart)
					instItems[1]=/dev/mapper/${loop}p2:$selectedPart
				fi
			done
		fi
		#======================================
		# Search for a swap space
		#--------------------------------------
		sID=$(searchSwapSpace)
		if [ ! -z "$sID" ];then
			sID=$(nd $sID)
		else
			sID=no
		fi
		#======================================
		# Setup device names for further boot
		#--------------------------------------
		# /.../
		# no support for reocvery and extra home partition in
		# partition install mode
		# ----
		export DONT_PARTITION=1
		export OEM_WITHOUTHOME=1
		unset  OEM_RECOVERY
		if [ -z "$bID" ];then
			bID=$rID
		fi
		if [ "$haveLVM" = "yes" ];then
			setupDeviceNames $rID $sID no no $bID no $VGROUP
		else
			setupDeviceNames $rID $sID no no $bID no
		fi
	else
		instItems[0]=$imageName:$imageDevice
	fi
	#======================================
	# Checks and MD5 sums...
	#--------------------------------------
	if [ -z "$OEM_PARTITION_INSTALL" ];then
		#======================================
		# Check MBR ID's...
		#--------------------------------------
		# /.../
		# mbr ID check is deactivated by default
		# see bug #525682 for details
		# ----
		nombridcheck=1
		if [ -z "$nombridcheck" ];then
			mbrD=$instDisk
			mbrI="cat $imageName"
			if [ "$imageZipped" = "compressed" ];then
				mbrI="gzip -cd $imageName"
			fi
			mbrM=$(dd if=$mbrD bs=1 count=4 \
				skip=$((0x1b8))|hexdump -n4 -e '"0x%x"')
			mbrI=$($mbrI | dd  bs=1 count=4 \
				skip=$((0x1b8))|hexdump -n4 -e '"0x%x"')
			if [ $mbrM = $mbrI ];then
				systemException \
					"Base system already installed" \
				"reboot"
			fi
		fi
		#======================================
		# read MD5 information if PXE install
		#--------------------------------------
		if [ ! -z "$pxe" ];then
			multicast="disable"
			fetchFile $imageMD5 /image.md5 uncompressed $imageServer
			if test $loadCode != 0 || ! loadOK "$loadStatus";then
				systemException \
					"Download of $imageMD5 failed: $loadStatus" \
				"reboot"
			fi
			imageMD5=/image.md5
		fi
	fi
	#======================================
	# Warn me before performing the install
	#--------------------------------------
	for i in ${instItems[*]}; do
		Target=$(echo $i | cut -f2 -d:)
		items="$items,$Target"
	done
	items=$(echo $items | sed -e s@^,@@)
	Echo "Entering installation mode for disk: $items"
	if [ -z "$kiwi_oemunattended" ];then
		TEXT_INST=$(
			getText "Destroying ALL data on %1, continue ?" $items)
		Dialog --yesno "\"$TEXT_INST\"" 5 70
		if [ ! $? = 0 ];then
			systemException \
				"System installation canceled" \
			"user_reboot"
		fi
	fi
	clear
	for i in ${instItems[*]};do
		#======================================
		# Setup source and target data
		#--------------------------------------
		Source=$(echo $i | cut -f1 -d:)
		Target=$(echo $i | cut -f2 -d:)
		if [ -z "$OEM_PARTITION_INSTALL" ];then
			read sum1 blocks blocksize zblocks zblocksize < $imageMD5
		else
			blocksize=4096
			partKB=$(partitionSize $Source)
			partBT=$((partKB * 1024))
			blocks=$((partBT / blocksize))
		fi
		#======================================
		# Get available disk space
		#--------------------------------------
		haveKByte=`partitionSize $Target`
		#======================================
		# Get required disk space, setup I/O
		#--------------------------------------
		needBytes=$(expr $blocks \* $blocksize)
		needKByte=`expr $needBytes / 1024`
		needMByte=`expr $needKByte / 1024`
		#======================================
		# Check disk space...
		#--------------------------------------
		haveMByte=`expr $haveKByte / 1024`
		needMByte=`expr $needKByte / 1024`
		Echo "Have size: $Target -> $haveMByte MB"
		Echo "Need size: $Source -> $needMByte MB"
		if [ $needMByte -gt $haveMByte ];then
			systemException \
				"Not enough space available for this image" \
			"reboot"
		fi
		#======================================
		# Dump image(s) on disk
		#--------------------------------------
		if [ -z "$pxe" ];then
			dump="cat $Source"
			if test "$imageZipped" = "compressed"; then
				dump="gzip -cd $Source"
			fi
			if [ -x /usr/bin/dcounter ];then
				progressBaseName=$(basename $Source)
				TEXT_LOAD=$(getText "Loading %1" $progressBaseName)
				dump="$dump | dcounter -s $needMByte -l \"$TEXT_LOAD \""
			fi
			Echo "Loading $Source [$Target] "
			if [ -x /usr/bin/dcounter ];then
				test -e /progress || mkfifo /progress
				errorLogStop
				(
					if ! eval $dump 2>/progress|dd bs=32k of=$Target &>/dev/null
					then
						errorLogContinue
						systemException \
							"Failed to install image: $Source -> $Target" \
						"reboot"
					fi
				)&
				echo "cat /progress | dialog \
					--backtitle \"$TEXT_INSTALLTITLE\" \
					--progressbox 3 65
				" > /tmp/progress.sh
				if [ -e /dev/fb0 ];then
					fbiterm -m $UFONT -- bash -e /tmp/progress.sh
				else
					bash -e /tmp/progress.sh
				fi
				clear
			else
				if ! eval $dump | dd bs=32k of=$Target &>/dev/null; then
					systemException \
						"Failed to install image: $Source -> $Target" \
					"reboot"
				fi
			fi
			if [ -x /usr/bin/dcounter ];then
				errorLogContinue
			fi
		else
			multicast="disable"
			Echo "Loading $Source [$Target BS:$imageBlkSize Byte]..."
			fetchFile $Source $Target $imageZipped $imageServer
			if test $loadCode != 0 || ! loadOK "$loadStatus";then
				systemException \
					"Download of $imageName failed: $loadStatus" \
				"reboot"
			fi
		fi
	done
	#======================================
	# Clear loops if required
	#--------------------------------------
	if [ ! -z "$OEM_PARTITION_INSTALL" ];then
		dmsetup remove_all
		losetup -d /dev/$loop
	fi
	#======================================
	# Check the md5sum of the raw disk
	#--------------------------------------
	if [ -z "$OEM_PARTITION_INSTALL" ];then
		Echo "Install complete, checking data..."
		verifyBytes=$((blocks * blocksize))
		verifyMByte=$((verifyBytes / 1048576))
		if [ -x /usr/bin/dcounter ];then
			test -e /progress || mkfifo /progress
			TEXT_VERIFY=$(getText "Verifying %1" $imageDevice)
			dump="cat $imageDevice"
			dump="$dump | dcounter -s $verifyMByte -l \"$TEXT_VERIFY \""
			errorLogStop
			(
				eval $dump 2>/progress |\
					head --bytes=$verifyBytes | md5sum - > /etc/ireal.md5
			)&
			echo "cat /progress | dialog \
				--backtitle \"$TEXT_INSTALLTITLE\" \
				--progressbox 3 65
			" > /tmp/progress.sh
			if [ -e /dev/fb0 ];then
				fbiterm -m $UFONT -- bash -e /tmp/progress.sh
			else
				bash -e /tmp/progress.sh
			fi
			clear
			errorLogContinue
		else
			dd if=$imageDevice bs=1024 |\
				head --bytes=$verifyBytes |\
				md5sum - > /etc/ireal.md5
		fi
		read sum2 dumy < /etc/ireal.md5
		if [ $sum1 != $sum2 ];then
			systemException \
				"Image checksum test failed" \
			"reboot"
		fi
		Echo "Image checksum test: fine :-)"
		Echo "System installation has finished"
	fi
	#======================================
	# Umount CD/DVD USB
	#--------------------------------------
	umount /cdrom
	#======================================
	# Reread partition table
	#--------------------------------------
	if [ -z "$OEM_PARTITION_INSTALL" ];then
		blockdev --rereadpt $imageDevice
		deviceTest=$(ddn $imageDevice 1)
		if ! waitForStorageDevice $deviceTest;then
			systemException \
				"Partition $deviceTest doesn't appear... fatal !" \
			"reboot"
		fi
	fi
	#======================================
	# Release network on PXE install
	#--------------------------------------
	if [ ! -z "$pxe" ];then
		dhcpcd -p -k $PXE_IFACE
	fi
	#======================================
	# create recovery archive if requested
	#--------------------------------------
	if [ ! -z "$OEM_RECOVERY_INPLACE" ];then
		setupInitialDeviceNames
		if ! mountSystem $imageRootDevice;then
			systemException "Failed to mount root filesystem" "reboot"
		fi
		if [ ! -f /mnt/recovery.partition.size ];then
			systemException "Can't find recovery part size info" "reboot"
		fi
		recoMByte=$(cat /mnt/recovery.partition.size)
		recoBytes=$((recoMByte - 100))
		recoBytes=$((recoBytes * 1048576))
		haveMByte=$(
			df -B1M $imageRootDevice | tail -n 1 | column -t |\
			sed -e s@"  "@:@g | cut -f 4 -d:
		)
		if [ "$haveMByte" -gt "$recoMByte" ];then
			pushd /mnt &>/dev/null
			Echo "Creating recovery root tarball..."
			test -e /progress || mkfifo /progress
			test -e /usr/bin/mst || cp /usr/bin/tail /usr/bin/mst
			(
				touch recovery.tar.gz
				tar --numeric-owner -czpf recovery.tar.gz . \
					--exclude "./dev" \
					--exclude "./proc" \
					--exclude "./sys" \
					--exclude "./recovery.*" &
				rPID=$!
				while kill -0 $rPID &>/dev/null;do
					rReady=$(stat --format="%s" ./recovery.tar.gz)
					if [ $rReady -eq 0 ];then
						continue
					fi
					rPDone=$(echo "scale=4; $recoBytes / $rReady" | bc)
					rPDone=$(echo "scale=0; 100 / $rPDone" | bc)
					getText "archiving: %1..." "$rPDone%" > /progress
					sleep 1
				done
				dPID=$(pidof mst)
				kill $dPID
			)&
			echo "mst -f /progress | dialog \
				--backtitle \"$TEXT_INSTALLTITLE\" \
				--progressbox 3 50
			" > /tmp/progress.sh
			if [ -e /dev/fb0 ];then
				fbiterm -m $UFONT -- bash -e /tmp/progress.sh
			else
				bash -e /tmp/progress.sh
			fi
			popd &>/dev/null
			clear
		else
			Echo "Not enough space left to create recovery archive"
			Echo "=> Warning: Postponed after repartitioning is done"
			Echo "=> Warning: This moves the archive creation to first boot"
		fi
		umountSystem
	fi
	#======================================
	# Check for halt request
	#--------------------------------------
	if [ ! -z "$OEM_DUMPHALT" ];then
		if [ "$OEMInstallType" = "CD" ];then
			TEXT_DUMP=$TEXT_CDPULL
		else
			TEXT_DUMP=$TEXT_USBPULL
		fi
		if [ "$OEMInstallType" = "CD" ];then
			CDEject
		fi
		Dialog \
			--backtitle \"$TEXT_INSTALLTITLE\" \
			--msgbox "\"$TEXT_DUMP\"" 5 70
		systemException \
			"Reboot requested after image installation" \
		"user_reboot"
	fi
}

# vim: set noexpandtab:
