#!/bin/ash

. /etc/initrd.defaults
backup() {
	echo -ne "\033[0G\033[0K"
}

strlen()
{
	if [ -z "$1" ]
	then
		echo "usage: strlen <variable_name>"
		die
	fi
	eval echo "\${#${1}}"
}

parse_opt() {
	case "$1" in
		*\=*)
			local key_name="`echo "$1" | cut -f1 -d=`"
			local key_len=`strlen key_name`
			local value_start=$((key_len+2))
			echo "$1" | cut -c ${value_start}-
		;;
	esac
}

modules_load() {
	for module in $*
	do
		echo ${module} >> /etc/modules/extra_load
	done

	modules_scan extra_load
}

modules_scan() {
	local MODS
	[ -d /etc/modules/${1} ] || touch /etc/modules/${1}

	MODS=`cat /etc/modules/${1}`
	for x in ${MODS}
	do
		MLOAD=`echo ${MLIST} | sed -e "s/.*${x}.*/${x}/"`
		if [ "${MLIST}" = "${x}" ] # Only module to no-load
		then
			echo -e "${BOLD}   ::${NORMAL} Skipping ${x}..."
		elif [ "${MLOAD}" = "${MLIST}" ] # == No change == No specified no-load
		then
			[ -n "${DEBUG}" ] && echo -ne "${BOLD}   ::${NORMAL} Checking for ${x}..."
			# find -name does not work since the return status is always zero
			if find /lib/modules | grep "${x}${KSUFF}" >/dev/null 2>&1
			then
				echo -ne "${BOLD}   ::${NORMAL} Scanning for ${x}..."
				modprobe ${x} -n
				backup
				echo -ne "${NORMAL}"
			fi
		else
			echo -e "${BOLD}   ::${NORMAL} Skipping ${x}..."
		fi
	done
}

findcdmount() {
	if [ "$#" -gt "0" ]
	then
		for x in $*
		do
			# Check for a block device to mount
			if [ -b "${x}" ]
			then
				good_msg "Attempting to mount CD:- ${x}"
				mount -r ${x} ${NEW_ROOT}/mnt/cdrom > /dev/null 2>&1
				if [ "$?" = '0' ]
				then
					# Check for a LiveCD
					if [ -e ${NEW_ROOT}/mnt/cdrom/livecd ]
					then
						REAL_ROOT="${x}"
						break
					else
						umount ${NEW_ROOT}/mnt/cdrom
					fi
				fi
			fi
		done
		if [ "${REAL_ROOT}" != '' ]
		then
			good_msg "CD medium found on ${x}"
		fi
	fi
}
cache_cd_contents() {
	# Check loop file exists and cache to ramdisk if DO_cache is enabled
	if [ "${LOOPTYPE}" != 'noloop' ]
	then
		check_loop
		if [ "${DO_cache}" ]
		then
			good_msg "Copying loop file for caching..."
			cp -a ${NEW_ROOT}/mnt/cdrom/${LOOP} ${NEW_ROOT}/mnt/${LOOP}
			if [ $? -ne 0 ]
			then
				bad_msg "Failed to cache the loop file! Lack of space?"
				rm -rf ${NEW_ROOT}/mnt/livecd.* 2>/dev/null
				rm -rf ${NEW_ROOT}/mnt/zisofs 2>/dev/null
			else
				LOOPEXT='../'
			fi
		fi
	fi
}

mount_sysfs(){
    if [ "${KV_2_6_OR_GREATER}" ]
    then
	# Udev is semi-broken on non /sys sysfs mount points.
	mount -t sysfs /sys /sys >/dev/null 2>&1
	ret=$?
	
	# sysfs mount failed .. udev wont work fall back to devfs if available
	[ "$ret" -eq '0' ] || USE_UDEV_NORMAL=0
    fi
}

# Insert a directory tree $2 to an union specified by $1
# Top-level read-write branch is specified by it's index 0
# $1 = union absolute path (starting with /)
# $2 = path to data directory
#
union_insert_dir()
{
   /sbin/unionctl $1 --add --after 0 --mode ro $2
   if [ $? = '0' ]
   then
   	good_msg "Addition of $2 to $1 successful"
   fi
}

findnfsmount() {
	if [ "${IP}" != '' ]; then
		if [ "${NFSROOT}" = '' ]; then
			# Obtain NFSIP	
			OPTIONS=`busybox dmesg | grep rootserver | sed -e "s/,/ /g"`
			for OPTION in $OPTIONS
			do
				if [ `echo $OPTION | sed -e "s/=/ /g" | cut -d " " -f 1` = 'rootserver' ]; then
					NFSIP=`echo $OPTION | sed -e "s/=/ /g" | cut -d " " -f 2`;
				fi 
			done
			
			# Obtain NFSPATH
			OPTIONS=`busybox dmesg | grep rootpath | sed -e "s/,/ /g"`	
			for OPTION in $OPTIONS
			do
				if [ `echo $OPTION | sed -e "s/=/ /g" | cut -d " " -f 1` = 'rootpath' ]; then
					NFSPATH=`echo $OPTION | sed -e "s/=/ /g" | cut -d " " -f 2`;
		 		fi 
			done
		
			# Setup NFSROOT
			if [ "${NFSIP}" != '' ] && [ "$NFSPATH" != '' ]
			then
				NFSROOT="${NFSIP}:${NFSPATH}"
			else
				bad_msg "The DHCP Server did not send a valid root-path."
				bad_msg "Please check your DHCP setup, or provide a nfsroot=<...> parameter."
			fi
		fi

		if [ "${NFSROOT}" != '' ]; then
			if [ "${CDROOT}" != '0' ]; then
				good_msg "Attempting to mount NFS CD image on ${NFSROOT}"
	                        mount -t nfs -o ro,nolock,rsize=1024,wsize=1024 ${NFSROOT} ${NEW_ROOT}/mnt/cdrom
				if [ "$?" = '0' ]; then
					REAL_ROOT="/dev/nfs"
				else
					bad_msg "NFS Mounting failed. Is the path corrent ?"
				fi
			else	
				good_msg "Attemping to mount NFS root on ${NFSROOT}"
				mount -t nfs -o ro,nolock,rsize=1024,wsize=1024 ${NFSROOT} ${NEW_ROOT}
                                if [ "$?" = '0' ]; then
					REAL_ROOT="/dev/nfs"
				else
					bad_msg "NFS Mounting failed. Is the path correct ?"
				fi
				# FIXME: Need to start portmap and the other rpc daemons in order to
				# FIXME: remount rw.
			fi

		fi
	fi
}

kill_devfsd() {
	killall devfsd > /dev/null 2>&1
}

check_loop() {
    if [ "${LOOP}" = '' -o ! -e "mnt/cdrom/${LOOP}" ]
    then
	
	bad_msg "Invalid loop location: ${LOOP}"
	bad_msg 'Please export LOOP with a valid location, or reboot and pass a proper loop=...'
	bad_msg 'kernel command line!'
	
	run_shell
    fi
}

run_shell() {
	/bin/ash
}

runUdev() {
	mount -t tmpfs -o size=100k udev /dev
	mkdir /dev/pts
	mkdir /dev/shm
	echo /sbin/udevsend > /proc/sys/kernel/hotplug 
	/sbin/udevstart
	ln -snf /proc/self/fd /dev/fd
	ln -snf /proc/self/fd/0 /dev/stdin
	ln -snf /proc/self/fd/1 /dev/stdout
	ln -snf /proc/self/fd/2 /dev/stderr
	ln -snf /proc/kcore /dev/core
}

test_success() {
	error_string=$1
	error_string="${error_string:-run command}"
	# If last command failed send error message and fall back to a shell	
	if [ "$?" != '0' ]
	then
		splash 'verbose'
		bad_msg 'Failed to $1; failing back to the shell...'
		run_shell
	fi
}

good_msg() {
	msg_string=$1
	msg_string="${msg_string:-...}"
	echo -e "${GOOD}>>${NORMAL}${BOLD} ${msg_string} ${NORMAL}"
}

bad_msg() {
	msg_string=$1
	msg_string="${msg_string:-...}"
	echo -e "${BAD}!!${NORMAL}${BOLD} ${msg_string} ${NORMAL}"
} 

warn_msg() {
	msg_string=$1
	msg_string="${msg_string:-...}"
	echo -e "${WARN}**${NORMAL}${BOLD} ${msg_string} ${NORMAL}"
} 

bind_mount_dev() {
	# bind-mount /dev/ so that loop devices can be found
	mount -o bind ${NEW_ROOT}/dev /dev
}

start_dev_mgr(){
    # Check udev is available...
    if [ "${KV_2_6_OR_GREATER}" -a ! "${USE_UDEV_NORMAL}" -eq '0' -a -x /sbin/udev ]
    then
		USE_UDEV_NORMAL=1
    else
		USE_UDEV_NORMAL=0
    fi

    if [ "${USE_UDEV_NORMAL}" -eq '1' ]
    then
		cd /sys
		[ "${DO_bladecenter}" ] && sleep 10
		kill_devfsd
		good_msg 'Activating udev'
		runUdev
		[ "${DO_bladecenter}" ] && sleep 20
		cd /
    else
		if [ ! -e /dev/.devfsd ]
		then
		    good_msg 'Activating devfs'
			mount -t devfs devfs /dev
			devfsd /dev -np
		fi
    fi
}

bootstrapCD() {
    # Locate the cdrom device with our media on it.
    [ -n "${CDROOT_DEV}" ] && DEVICES="$DEVICES ${CDROOT_DEV}" # Device specified on the command line
    DEVICES="$DEVICES /dev/cdroms/*" # CDROM DEVICES
    DEVICES="$DEVICES /dev/ide/cd/*" # CDROM DEVICES
    DEVICES="$DEVICES /dev/sr*" # UML DEVICES
    DEVICES="$DEVICES /dev/sd*" # USB Keychain
    DEVICES="$DEVICES /dev/hd*" # IDE devices
    DEVICES="$DEVICES /dev/ubd* /dev/ubd/*" # UML DEVICES
    DEVICES="$DEVICES /dev/iseries/vcd*" # iSeries Devices
	
    findcdmount $DEVICES
}

cmdline_hwopts() {
    # Scan CMDLINE for any "doscsi" or "noscsi"-type arguments
    
    local FOUND
    local TMP_HWOPTS

    for x in $HWOPTS
    do
	for y in $CMDLINE
	do
	    if [ "${y}" = "do${x}" ]
	    then
		MY_HWOPTS="${MY_HWOPTS} $x"
	    elif [ "${y}" = "no${x}" ]
	    then
		MY_HWOPTS="`echo ${MY_HWOPTS} | sed -e \"s/${x}//g\" -`"
	    fi
	done
    done
   
    # Shouldnt need to sort this the following loop should figure out the duplicates and strip them out
    #MY_HWOPTS=`echo ${MY_HWOPTS}|  sort`
    
    for x in ${MY_HWOPTS}
    do
	FOUND=0
    	for y in ${TMP_HWOPTS}
	do
		if [ "${y}" = "${x}" ]
		then 
			FOUND=1
		fi
	done
	if [ ! "${FOUND}" = '1' ]
	then
		TMP_HWOPTS="${TMP_HWOPTS} ${x}"
	fi
    done

    MY_HWOPTS=${TMP_HWOPTS}
}

load_modules() {
    # Load modules listed in MY_HWOPTS if /lib/modules exists
    
    if [ -d '/lib/modules' ]
    then
    	good_msg 'Loading modules'
	# Load appropriate kernel modules
	for modules in $MY_HWOPTS
	do
	    modules_scan $modules
	    eval DO_`echo $modules | sed 's/-//'`=1
	done
    else
    	good_msg 'Skipping module load; no modules in the initrd!'
    fi
}

detect_sbp2_devices() {
    # http://www.linux1394.org/sbp2.php
    
    # /proc
    # /proc/scsi/sbp2/0, /proc/scsi/sbp2/1, etc.
    #
    # You may manually add/remove SBP-2 devices via the procfs with add-single-device <h> <b> <t> <l> or remove-single-device <h> <b> <t> <l>, where:
    #
    #
    # <h> = host (starting at zero for first SCSI adapter)
    # <b> = bus (normally zero)
    # <t> = target (starting at zero for first SBP-2 device)
    # <l> - lun (normally zero) 
    # e.g. To manually add/detect a new SBP-2 device
    #   echo "scsi add-single-device 0 0 0 0" > /proc/scsi/scsi
    #   e.g. To manually remove a SBP-2 device after it's been unplugged
    #     echo "scsi remove-single-device 0 0 0 0" > /proc/scsi/scsi
    #     e.g. To check to see which SBP-2/SCSI devices are currently registered
    #       cat /proc/scsi/scsi 
    
    [ -e /proc/scsi/scsi ] && echo 'scsi add-single-device 0 0 0 0' > /proc/scsi/scsi
}

setup_keymap() {
	if [ "${DO_keymap}" ]
	then
		if [ ! -e /dev/vc/0 ]
		then
			DEVBIND=1
			mount -o bind ${NEW_ROOT}/dev /dev
		fi
	
		chooseKeymap

		[ "${DEVBIND}" -eq '1' ] && umount /dev
		
		if [ -e /etc/sysconfig/keyboard -a "${CDROOT}" -eq '1' ]
		then
			mkdir -p ${NEW_ROOT}/etc/sysconfig/
			cp /etc/sysconfig/keyboard ${NEW_ROOT}/etc/sysconfig/keyboard
		fi
	fi
}
chooseKeymap() {
    good_msg "Loading keymaps"
    cat /lib/keymaps/keymapList
    read -t 10 -p '<< Load keymap (Enter for default): ' keymap
    if [ -e /lib/keymaps/${keymap}.map ]
    then
	good_msg "Loading the ''${keymap}'' keymap"
	loadkmap < /lib/keymaps/${keymap}.map
	xkeymap=${keymap}
	echo ${keymap} | egrep -e "[0-9]+" >/dev/null 2>&1
	if [ "$?" -eq '0'  ]; then
	    xkeymap=`tail -n 7 /lib/keymaps/keymapList | grep ${keymap} | sed -r "s/.*\s+${keymap}\s+([a-z-]+).*/\1/g" | egrep -v 1`
	fi
	mkdir -p /etc/sysconfig
	echo "XKEYBOARD=${xkeymap}" > /etc/sysconfig/keyboard
    elif [ "$keymap" = '' ]
    then
    	echo
	good_msg "Keeping default keymap"
    else
    	bad_msg "Sorry, but keymap ''${keymap}'' is invalid!"
	chooseKeymap
    fi
}

startVolumes() {
    #good_msg 'Checking if volumes need to be started...'
    
    if [ "${USE_DMRAID_NORMAL}" -eq '1' ]
    then
	if [ -e '/sbin/dmraid' ]
	then
	    good_msg "Activating Device-Mapper RAID(s)"
	    if [ '${DMRAID_OPTS}' = '' ]
	    then
		/sbin/dmraid -ay
	    else
		/sbin/dmraid -ay ${DMRAID_OPTS}
	    fi
	fi
    fi

    if [ "${USE_LVM2_NORMAL}" -eq '1' ]
    then
	if [ -e '/bin/vgscan' -a -e '/bin/vgchange' ]
	then
	    for dev in ${RAID_DEVICES}
	    do
		setup_md_device "${dev}"
	    done

	    good_msg "Scanning for Volume Groups"
	    /bin/vgscan --ignorelockingfailure --mknodes 2>/dev/null
	    good_msg "Activating Volume Groups"
	    /bin/vgchange -ay --ignorelockingfailure 2>/dev/null

	    # Disable EVMS since lvm2 is activated and they dont work together.
	    if [ "${USE_EVMS2_NORMAL}" -eq '1' ]
	    then
	       bad_msg "Disabling EVMS Support because LVM2 started"
	       bad_msg "Do not add dolvm2 to the cmdline if this is not what you want"
	       bad_msg "LVM2 and EVMS do not work well together"
	       USE_EVMS2_NORMAL=0
	    fi
	
	else
	    bad_msg "vgscan or vgchange not found: skipping LVM2 volume group activation!"
	fi

    fi

    if [ "${USE_EVMS2_NORMAL}" -eq '1' ]
    then
	if [ -e '/sbin/evms_activate' ]
	then
	    good_msg "Activating EVMS"
	    evms_activate
	fi
    fi
}

sdelay() {
	# Sleep a specific number of seconds if SDELAY is set otherwise only 1 second
	if [ -n "${SDELAY}" ]; then
		sleep ${SDELAY}
	else
		sleep 1
	fi
}

quiet_kmsg() {
    # if QUIET is set make the kernel less chatty
    [ -n "$QUIET" ] && echo '0' > /proc/sys/kernel/printk
}

verbose_kmsg() {
    # if QUIET is set make the kernel less chatty
    [ -n "$QUIET" ] && echo '6' > /proc/sys/kernel/printk
}


cdupdate() {
	if [ "${CDROOT}" -eq '1' ]
	then
    		if [ -x /${NEW_ROOT}/mnt/cdrom/cdupdate.sh ]
    		then
	    		good_msg "Running cdupdate.sh"
			${NEW_ROOT}/mnt/cdrom/cdupdate.sh
			if [ "$?" != '0' ]
			then
	    			splash 'verbose'
	    			bad_msg "FAILED TO EXECUTE cdupdate.sh"
	    			run_shell
			fi
		else
	    		good_msg 'No cdupdate.sh script found, skipping...'
    		fi
    	fi
}

setup_md_device() {
	local device

	[ -z "$1" ] && device="${REAL_ROOT}" || device="$1"
	[ -z "${device}" ] && return # LiveCD

	if [ `echo ${device}|sed -e 's#\(/dev/md\)[[:digit:]]\+#\1#'` = "/dev/md" ]
	then
		good_msg 'Detected real_root as a md device. Setting up the device node...'
		MD_NUMBER=`echo ${device}|sed -e 's#/dev/md\([[:digit:]]\+\)#\1#'`
		if [ ! -e /dev/md${MD_NUMBER} ]
		then
			mknod /dev/md${MD_NUMBER} b 9 ${MD_NUMBER} >/dev/null 2>&1
			[ "$?" -ne 0 ] && bad_msg "Creation of /dev/md${MD_NUMBER} failed..."
        	fi
		mdstart ${MDPART} /dev/md${MD_NUMBER}
    	fi
}

rundebugshell(){
    if [ -n "$DEBUG" ]; then
	good_msg 'Starting debug shell as requested by "debug" option.'
	good_msg 'Type "exit" to continue with normal bootup.'
	[ -x /bin/sh ] && /bin/sh || /bin/ash
    fi
}

setup_unionfs(){
	if [ "${USE_UNIONFS_NORMAL}" -eq '1' -a "${CDROOT}" -eq '1' ]
	then
		# Directory used for rw changes in union mount filesystem
		UNION=/union
		MEMORY=/memory
		if [ -n "$UID" ]
		then
			CHANGES=$MEMORY/unionfs_changes/default
		else
			CHANGES=$MEMORY/unionfs_changes/$UID
		fi

		
		mkdir -p ${MEMORY}
		mkdir -p ${UNION}
		good_msg "Loading unionfs module"
		modprobe unionfs > /dev/null 2>&1
		if [ -n "${UNIONFS}" ]
		then
			CHANGESDEV=${UNIONFS}
			good_msg "mounting $CHANGESDEV to $MEMORY for unionfs support"
			mount -t auto $CHANGESDEV $MEMORY
			# mount tmpfs only in the case when changes= boot parameter was empty
			# or we were not able to mount the storage device
			ret=$?
			if [ "${ret}" -ne 0 ]
			then
				bad_msg "mount of $CHANGESDEV failed falling back to ramdisk based unionfs"
				mount -t tmpfs tmpfs $MEMORY
			fi
			if [ ! -f ${MEMORY}/livecd.unionfs ]
			then
				umount $CHANGESDEV
				bad_msg "failed to find livecd.unionfs file on $CHANGESDEV"
				bad_msg "create a livecd.unionfs file on this device if you wish to use it for unionfs"
				bad_msg "falling back to ramdisk based unionfs for safety"
				mount -t tmpfs tmpfs $MEMORY
			fi
		else 
			good_msg "Mounting ramdisk to $MEMORY for unionfs support..."
			mount -t tmpfs tmpfs $MEMORY 
		fi 
		
		mkdir -p $CHANGES 
		mount -t unionfs -o dirs=$CHANGES=rw unionfs ${UNION}
		ret=$?
		if [ "${ret}" -ne 0 ]
		then 
			die "Can't setup union ${UNION} in  directory!"
		fi
	else
		USE_UNIONFS_NORMAL=0
	fi
}
