#!/bin/sh
# VIVER
# Copyright (C) 2005-2006 Furuhashi Sadayuki
#
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
#


# return 0:$1 contais $2   1:$1 doesn't contain $2
# ɤ餫ǤǤ0֤
strstr()
{
	[ ! ${1##*$2*} ]
}

# force make direcrory
# $1 = directory path to make
mkdir_f()
{
	[ -f "$1" ] && rm $1 -f
	[ ! -d "$1" ] && mkdir -p $1
}

# force make file
# $1 = file path to make
mkfile_f()
{
	[ -d "$1" ] && rm $1 -rf
	[ ! -f "$1" ] && touch $1
}

# list up special block device node
ls_block_nod()
{
	ls -A1d /sys/block/*/dev /sys/block/*/*/dev 2>/dev/null | grep -v "\/nbd\|\/loop\|\/ram\|\/md" | sed -r "s/.*\/(.*)\/dev/\/dev\/\\1/"
}

# load IDE device drivers
load_ide()
{
	modprobe ide-cd	      # ide cdrom device
	#modprobe floppy      # generic floppy device
	#modprobe ide-tape    # ide tape device
	#modprobe ide-generic # ide generic device
}
# load SCSI device drivers
# (hwsetup seems not able to autoprobe scsi device)
load_scsi()
{
	modprobe scsi_mod
	modprobe sr_mod    # cdrom device
	modprobe sd_mod    # hdd device
	#modprobe st       # tape device
	#modprobe sg_mod   # generic scsi device

	# from Whoppix linuxrc
	local PROCPCI SCSI_PROBE
	PROCPCI=$(cat /proc/pci 2>/dev/null)
	SCSI_PROBE="aha1542 aha152x pas16 psi240i qlogicfas qlogicfc t128 u14-34f wd7000"
	case "$PROCPCI" in *[Aa][Ii][Cc]-*|*[Aa][Hh][Aa]-*) SCSI_PROBE="$SCSI_PROBE aic7xxx" ;; esac
	case "$PROCPCI" in *[Bb][Uu][Ss][Ll][Oo][Gg][Ii][Cc]*) SCSI_PROBE="$SCSI_PROBE BusLogic" ;; esac
	case "$PROCPCI" in *53[Cc]8*) SCSI_PROBE="$SCSI_PROBE sym53c8xx" ;; esac
	case "$PROCPCI" in *53[Cc]406*) SCSI_PROBE="$SCSI_PROBE NCR53c406a" ;; esac
	case "$PROCPCI" in *[Ii][Nn][Ii][Tt][Ii][Oo]\ *|*[Ii][Nn][Ii]-[Aa]100[Uu]2[Ww]*) SCSI_PROBE="$SCSI_PROBE initio" ;; esac
	case "$PROCPCI" in *[Mm][Pp][Tt]*[Ss][Cc][Ss][Ii]*) SCSI_PROBE="$SCSI_PROBE mptscsih" ;; esac
#	case "$PROCPCI" in *[Aa][Dd][Vv][Aa][Nn][Cc][Ee][Dd]\ [Ss][Yy][Ss]*) SCSI_PROBE="$SCSI_PROBE advansys" ;; esac
	case "$PROCPCI" in *[Aa][Tt][Pp]8|*[Aa][Ee][Cc]6*) SCSI_PROBE="$SCSI_PROBE atp870u" ;; esac
	case "$PROCPCI" in *[Dd][Tt][Cc]*) SCSI_PROBE="$SCSI_PROBE dtc" ;; esac
	case "$PROCPCI" in *[Ee][Aa][Tt][Aa]*) SCSI_PROBE="$SCSI_PROBE eata" ;; esac
	case "$PROCPCI" in *[Ff]*[Dd][Oo][Mm][Aa][Ii][Nn]*) SCSI_PROBE="$SCSI_PROBE fdomain" ;; esac
	case "$PROCPCI" in *[Gg][Dd][Tt]\ *) SCSI_PROBE="$SCSI_PROBE gdth" ;; esac
#	case "$PROCPCI" in *[Mm][Ee][Gg][Aa][Rr][Aa][Ii][Dd]*) SCSI_PROBE="$SCSI_PROBE megaraid" ;; esac
	case "$PROCPCI" in *[Mm][Ee][Gg][Aa][Rr][Aa][Ii][Dd]*) SCSI_PROBE="$SCSI_PROBE megaraid_mbox" ;; esac
#	case "$PROCPCI" in *[Pp][Cc][Ii]-22*) SCSI_PROBE="$SCSI_PROBE pci2220i" ;; esac
#	case "$PROCPCI" in *[Pp][Cc][Ii]-2000*) SCSI_PROBE="$SCSI_PROBE pci2000" ;; esac
	case "$PROCPCI" in *[Qq][Ll][Oo][Gg][Ii][Cc]*) SCSI_PROBE="$SCSI_PROBE qlogicisp" ;; esac
	case "$PROCPCI" in *53[Cc]974*) SCSI_PROBE="$SCSI_PROBE tmscsim" ;; esac
	case "$PROCPCI" in *[Uu][Ll][Tt][Rr][Aa][Ss][Tt][Oo][Rr]*) SCSI_PROBE="$SCSI_PROBE ultrastor" ;; esac
	case "$PROCPCI" in *3[Ww][Aa][Rr][Ee]*) SCSI_PROBE="$SCSI_PROBE 3w-xxxx" ;; esac

	local M
	for M in $SCSI_PROBE;do
		modprobe $M >/dev/null 2>&1    # this line will show many error message.
	done
}
# load pcmcia device drivers
load_pcmcia()
{
	modprobe pcmcia_core
	# i82364 -> yenta_socket -> i82093
	modprobe i82365 >/dev/null 2>&1 || modprobe yenta_socket >/dev/null 2>&1 || modprobe i82092 >/dev/null 2>&1
	modprobe ds
	modprobe pcmcia
	cardmgr -q -o -c /etc/pcmcia -s /run/stab -p /run/cardmgr.pid
}
# load usb device drivers
# TODO: this is not tested! (hwsetup can autoprobe this?) (or hotplug is needed?)
load_usb()
{
	modprobe scsi_mod    # scsi emulation
	modprobe usbcore
	modprobe uhci-hcd    # host driver
	modprobe ohci-hcd    # host driver
	modprobe ehci-hcd    # host driver
	modprobe usb-storage
}
# load ieee1394 device drivers
# TODO: this is not tested! (hwsetup can autoprobe this?) (or hotplug is needed?)
load_ieee1394()
{
	modprobe scsi_mod   # scsi emulation
	modprobe eee1394
	modprobe ohci1394   # for ohci compatible devices
	modprobe pcilynx    # for Texas Instruments PCILynx/PCILynx2
	modprobe sbp2       # ieee1294 storage driver
}
# load Serial ATA device drivers
load_sata()
{
	modprobe libata     # TODO: I think SATA won't work only this...
}
load_fs()
{
	local fstype loadfs
	if [ -z "$cowkey" -o -n "$directcow" ];then
		usefs="$sparsefs$cowfs$diskfs$rootfs"
	else
		usefs="$cowkeyfs$sparsefs$cowfs$diskfs$rootfs"
	fi
	for fstype in vfat udf ext3 xfs nfs shfs ntfs reiserfs reiser4 minix;do
		if strstr "$usefs" "$fstype" ;then
			modprobe $fstype
		fi
	done
	return 0
}
load_plus()
{
	local line module param
	for line in $(echo $modprobe_plus | tr ',' ' ');do
		if strstr "$line" ":" ;then
			module=${line%:*}
			param=${line##*:}
		else
			module=$line
			param=
		fi
		modprobe $module $param
	done
	return 0
}

tabling()
{
	local dev offset=0
	for dev in $* ;do
		[ ! -b "$dev" ] && continue
		size=$(blockdev --getsize $dev)
		echo $offset $size linear $dev 0
		offset=$(expr $offset + $size)
	done
}

# disable kernel message and don't allow interrupt signals
disable_verbose()
{
	if [ ! $verboseboot ];then
		echo "0" > /proc/sys/kernel/printk
		trap "" 1 2 3 15
	fi
}
# enable kernel message and allow interrupt signals
enable_verbose()
{
	if [ -z "$verboseboot" ];then
		echo "0" > /proc/sys/kernel/printk
		trap "" 1 2 3 15
	fi
}

# wait until specified action ends
# $* = actions to wait
#TODO:Ȥʤ
join()
{
	local action
	for action in "$@";do
		local TIMEOUT=50	# 10 seconds
		while [ ! -f /run/$action ];do
			usleep 200000	# 0.2 seconds
			TIMEOUT=$(expr $TIMEOUT - 1)
			[ "$TIMEOUT" -le 0  ] && return 1
		done
	done
	return 0
}

#----------------------------------------------#
#                 VIVER ACTION                 #
#----------------------------------------------#
# set frame buffer control code parameters
set_fb_param()
{
	if [ "$param_vga" -o -z "$MTSYS" ];then
		RESET="]R"		# Reset fb color mode
		CRE="[K"		# Erase to end of line
		CLEAR="c"		# Clear and reset Screen
		NORMAL="[0;39m"	# Normal color
		RED="[1;31m"		# RED: Failure or error message
		GREEN="[1;32m"	# GREEN: Success message
		YELLOW="[1;33m"	# YELLOW: Descriptions
		BLUE="[1;34m"		# BLUE: System mesages
		MAGENTA="[1;35m"	# MAGENTA: Found devices or drivers
		CYAN="[1;36m"		# CYAN: Questions
		WHITE="[1;37m"	# BOLD WHITE: Hint

		MSG=$BLUE		# Normal message
		INFO=$YELLOW		# Not so important Information
		KERN=$RED		# Kernel message
		OK=$GREEN		# Success message
		FAILED=$RED		# Failed message
		WARN=$MAGENTA		# Warning message

		MOVE_UP="[1A"		# move cursor up
		MOVE_TO_COL="[80G"	# move cursor right edge
	else
		RESET=
		CRE=
		CLEAR=
		NORMAL=
		RED=
		GREEN=
		YELLOW=
		BLUE=
		MAGENTA=
		CYAN=
		WHITE=

		MSG=
		INFO=
		KERN=
		OK=
		FAILED=
		WARN=

		MOVE_UP=
		MOVE_TO_COL=
	fi
}


# mount /proc and /sys
# reuturn 1:/procΥޥȤ˼    +2:/sysΥޥȤ˼
mount_proc_sys()
{
	local retval=0
	echo "${MSG}Mounting proc and sys filesystem.${KERN}"
	mount -t proc  none /proc || retval=1
	mount -t sysfs none /sys  || retval=$(expr $retval + 2)
	return $retval
}

parse_command_line()
{
	local line key val
	paramlist=
	echo "${MSG}Parsing command line parameters.${KERN}"

	for line in $(cat /proc/cmdline);do
		if strstr "$line" "=" ;then
			key=${line%%=*}
			val=${line#*=}
			if [ -n "$key" ];then
				eval param_$key=$val
				paramlist="$paramlist $key"
			fi
		else
			eval param_$line=1
			paramlist="$paramlist $line"
		fi
	done

	set_fb_param	# ƥå
}

set_params()
{
	echo "${MSG}Setting default system parameters.${KERN}"

	# ֡ȥѥ᡼ǻǤѥ᡼
	role=${param_role:-''}			# ŬѤrole̾ (CSV-٤ƻ)

	password=${param_password:-''}			# Ź沽줿squashrootΥѥ (ꤷʤä硢Ź沽Ƥʤȸʤ)

	default_ramsize=64
	ramsize=${param_ramsize:-"$default_ramsize"}	# ʪǥХcowڡȤƻȤʤȤcowڡȤƳƤMBñ̤Υ

	cowkey=${param_cowkey:-''}			# cowե$cowkeyfsǥեޥåȤ줿롼ץХåե (cowdev/ȤХեѥ - viver/cow1.img.ext3)
	cowkeyfs=${param_cowkeyfs:='ext3'}		# cowkeyΥե륷ƥ
	cowdev=${param_cowdev:-'detect'}		# cowkeyʪǥХudevΥǥХΡ̾ (CSV-ѲǽǺǽ˸ĤäǥХ) ü:cowdev=detect - ٤ƤΥǥХ
	cowfs=${param_cowfs:-'vfat,ext3,ntfs'}		# cowdevΥե륷ƥॿ (CSV-ư)
	cowoption=${param_cowoption:-''}		# cowdevΥޥȥץ 'rw'ϻꤷƤϤʤ (mountޥɤ˥˥ʤľŬ)
	[ -z "$param_nodirectcow" -a "$param_directcow" ] && directcow=1	# cowkey롼ץХåޥȤƤcowեΤǤϤʤcowdevľcowե

	rootfs=${param_rootfs:-'xfs'}			# ǽŪrootե륷ƥΥե륷ƥॿ
	rootoption=${param_rootoption:-'rw'}		# ޥȥץ (Ĥɬ)

	sparsefs=${param_sparsefs:-'xfs'}		# sparse.baseΥե륷ƥॿ

	staticip=${param_staticip:-''}			# ͥåȥưȤ˸IP "aaa.bbb.ccc.ddd" # TODO:IPv6̤б
	netmask=${param_netmask:-''}			# IPѤȤΥ֥ͥåȥޥ # TODO:IPv4Τ
	gateway=${param_gateway:-''}			# IPѤȤΥǥեȥȥ

	#[ -z "$param_noswap" -a "$param_swap" ] && swap=${param_swap:-'detect'}	# swap˻ȤudevΥǥХΡ̾ (CSV-٤ƻ) ü:swap=detect - ٤ƤΥѡƥ
	#[ -z "$param_nodirectswap" -a "$param_directswap" ] && directswap=1	# swap롼ץХåޥȤΤǤϤʤľʪǥХswapѤ
	#swapkey=${param_swapkey:-''}			# swapѤ롼ץХåե̾ (CSV-٤ƻ)
	swapdev=${param_swapdev:-''}			# swapľѤudevΥǥХΡ̾ (CVS-٤ƻ)

	[ -z "$param_ide"        -a "$param_noide"    ] && noide=1	# IDEȤ (ǥե:Ȥ) (noideideѥ᡼ͥ)
	[ -z "$param_nousb"      -a "$param_usb"      ] && useusb=1	# Ƽ³Ȥ (ǥե:Ȥʤ) (***no***ѥ᡼ͥ)
	[ -z "$param_noieee1394" -a "$param_ieee1394" ] && useieee1394=1
	[ -z "$param_nopcmcia"   -a "$param_pcmcia"   ] && usepcmcia=1
	[ -z "$param_nosata"     -a "$param_sata"     ] && usesata=1
	[ -z "$param_noscsi"     -a "$param_scsi"     ] && usescsi=1
	[ -z "$param_hwsetup"  -a "$param_nohwsetup"  ] && nohwsetup=1  # hwsetup¹Ԥʤ (nohwsetuphwsetupѥ᡼ͥ)
	[ -z "$param_setaudio" -a "$param_nosetaudio" ] && nosetaudio=1	# hwsetupǥǥǥХ̵ (nosetaudiosetaudioѥ᡼ͥ)
	[ -z "$param_setscsi"  -a "$param_nosetscsi"  ] && nosetscsi=1	# hwsetupSCSIǥХ̵ (TODO:ɬס)

	[ -z "$param_notoram" -a "$param_toram" ] && toram=1		# squashrootCopy-on-Writeڡ˥ԡ

	modprobe_plus=${param_modprobe:-''}				# ɲäǥɤ⥸塼̾ "⥸塼̾:ѥ᡼" (CSV-٤ƥ)

	remoterc=${param_remoterc:-''}					# tftpviverrcɡ¹ԤȤtftpСIPɥ쥹

	verboseboot=${param_verboseboot:-''}		# ͥå򱣤ʤ (ñΥѥ᡼)

	# disk鵯ưü
	diskdev=${param_diskdev:-'detect'}		# squashrootޤǥХudevΥǥХΡ̾ (CSV-ѲǽǺǽ˸ĤäǥХ) ü:detect - ٤ƤΥǥХ
	diskfs=${param_diskfs:-'iso9660,ext2,ntfs'}	# diskdevΥե륷ƥॿ (CSV-ư)
	diskoption=${param_diskoption:-'ro'}		# diskdevΥޥȥץ (mountޥɤ˥˥ʤľŬ)
	looppath=${param_looppath:-'/cryptroot'}	# diskdevΥե륷ƥ/ȤsquashrootХեѥ
	wakenet=${param_wakenet:-''}			# ͥåȥư (ñΥѥ᡼)

	# nbd鵯ưü
	rootserver=${param_rootserver:-''}		# NBDСIPɥ쥹 "aaa.bbb.ccc.ddd:port" (CVS-饹ƻ) # TODO:IPv6̤б
	netdev=${param_netdev:-''}			# NICΥǥХ̾(eth0ʤ) (ǥե:ư)



	# ѥ᡼
	# ޥȥݥ
	MTSYS=/sysroot		# pivot_root
	MTDISK=/disk		# cryptrootޤǥΥޥȥݥ
	MTSQUASH=/looproot	# squashrootΥޥȥݥ (root.* sparse.baseޤ)
	MTSPARSE=/sparse	# sparse.baseΥޥȥݥ
	MTCOWBASE=/cowbase	# cowեǥХʥ롼ץХåեˤĥǥХΥޥȥݥ
	MTCOW=/cow		# cowեǥХʥ롼ץХåեˤΥޥȥݥ
	MTNOAH=/VIVER		# ֡Ȼ饷åȥޤǻtmpfsΥޥȥݥ
	RCDIR=/etc/VIVER	# bootrcǼƤǥ쥯ȥ
	OUTPUT_DIR=$RCDIR/var/VIVER	# ư̤νǥ쥯ȥ
	DEVDIR=/VIVER		# 롼ȥե륷ƥǥХΡɤݻǥ쥯ȥ

	PATH=/bin:/sbin		# PATH

	return 0
}


# mount module disk
mount_module()
{
	echo "${MSG}Mounting module disk.${KERN}"
	# insmod /modules/loop.ko max_loop=12 || retval=1	# ͥbuilt-in
	mount -t squashfs -o loop /modules.squash /lib/modules
}


# load device driver modules
load_drivers()
{
	echo "${MSG}Loading device driver modules.${KERN}"
	[ $noide       ] || load_ide
	[ $usesata     ] && load_sata
	[ $usescsi     ] && load_scsi
	[ $useusb      ] && load_usb
	[ $useieee1394 ] && load_ieee1394
	[ $usepcmcia   ] && load_pcmcia
	load_fs
	[ $modprobe_plus ] && load_plus
	return 0
}

# run hwsetup
# reuturn 1:hwsetup    +2:udevstart
do_hwsetup()
{
	local retval=0
	local hwoption="-p"
	if [ ! $nohwsetup ];then
		echo -n "${MSG}"
		[ $nosetaudio ] && hwoption="$hwoption -a"
		[ $nosetscsi  ] && hwoption="$hwoption -s"
		hwsetup $hwoption || retval=1
		echo -n "${KERN}"
	else
		echo "${MSG}Skipping hardware autoprobing.${KERN}"
	fi
	# hwsetupȯʬޤƥǥХΡɤ
	udevstart || retval=$(expr $retval + 2)
	return $retval
}

# return 1:tmpfsޥȤǤʤ
mknoah()
{
	local retval=0
	echo "${MSG}Preparing work space RAM disk.${KERN}"
	mount -t tmpfs tmpfs $MTNOAH -o size=2m || retval=1    # TODO:MTNOAHΥϡ
	mkdir -p $MTNOAH/dev/mapper
	mkdir -p $MTNOAH/dev/cow
	mkdir -p $MTNOAH/dev$DEVDIR
	mknod -m 600 $MTNOAH/dev/console c 5 1
	mknod -m 660 $MTNOAH/dev/mapper/control c 10 63
	mknod -m 660 /dev/mapper/control c 10 63 2> /dev/null
	mknod -m 640 $MTNOAH/dev/cow/ctl b 241 255
	mknod -m 640 /dev/cow/ctl b 241 255 2> /dev/null
	return $retval
}

# wake up network device
# return 1:ĤͥåȥǥХⵯưǤʤ
# TODO:֥å
# TODO:Zeroconf ad-hoc IP
wake_network()
{
	local err dev
	echo "${MSG}Starting network.${KERN}"

	modprobe af_packet
	ifconfig lo 127.0.0.1 netmask 255.255.255.0 broadcast 127.255.255.255 up
	for dev in $netdev $(ifconfig -a 2>/dev/null | grep "HWaddr" | sed "s/[[:space:]].*//");do
		err=0
		if [ $staticip ];then
			[ ! $netmask ] && netmask=255.255.255.0
			ifconfig $dev $staticip netmask $netmask || err=1
			if [ $gateway ];then
			       	route add default gw $gateway || err=1
			fi
		else
			echo -n "${INFO}"
			udhcpc -nfq -i $dev -s /etc/dhcp-script || err=1
			echo -n "${KERN}"
		fi
		if [ $err -eq 0 ];then
			netdev=$dev
			touch /run/wake_network  # TODO:ʤ?
			return 0
		fi
	done
	return 1
}


# detect and mount copy-on-write device which contains $cowkey
# return: 1:Ĥʤ 2:rwǥޥȤǤʤ(directcow) 3:rwǥޥȤǤʤ 4:cokeyޥȤǤʤ
mount_cow()
{
	local dev fs
	echo "${MSG}Detecting physical Copy-on-Write device${KERN}"
	[ "$cowdev" = "detect" ] && cowdev=$(ls_block_nod)    # ٤ƤΥǥХ
	[ -n "$cowoption" ] && cowoption="${cowoption},"
	for dev in $(echo $cowdev | tr ',' ' ');do
		for fs in $(echo $cowfs  | tr ',' ' ');do
			mount -t $fs -o ${cowoption}ro $dev $MTCOWBASE >/dev/null 2>&1
			if [ $? -eq 0 ];then
				if [ -e "$MTCOWBASE/$cowkey" ];then
					umount $MTCOWBASE
					cp -pf $dev $MTNOAH/dev$DEVDIR/cowbase    # ܥå󥯤äƥԡ
					if [ -n "$directcow" ];then
						mount -t $fs -o ${cowoption}rw $MTNOAH/dev$DEVDIR/cowbase $MTCOW || return 2
						echo "${OK}Mounted $dev type $fs (${cowoption}rw)${KERN}"
					else
						mount -t $fs -o ${cowoption}rw $MTNOAH/dev$DEVDIR/cowbase $MTCOWBASE || return 3
						echo "${OK}Mounted $dev type $fs (${cowoption}rw)${KERN}"

						mknod -m 660 $MTNOAH/dev$DEVDIR/cowkey b 7 16    # loop16
						losetup $MTNOAH/dev$DEVDIR/cowkey $MTCOWBASE/$cowkey
						mount -t $cowkeyfs -o rw $MTNOAH/dev$DEVDIR/cowkey $MTCOW || return 4
						echo "${OK}Mounted $cowkey type $cowkeyfs (rw)${KERN}"
					fi
					return 0
				else
					umount $MTCOWBASE
				fi
			fi
		done
	done
	return 1
}

# mount tmpfs
mount_tmp()
{
	echo "${MSG}Mounting tmpfs on $MTCOW as Copy-on-Write disk.${KERN}"
	if [ $(cat /proc/meminfo | grep MemTotal | sed -r "s/[^0-9]*([0-9]*).*/\\1/") -lt $(expr $ramsize "*" 1024) ];then
		echo "${WARN}Specified ramsize (${ramsize}MB) is too large"
		echo "Setting ramsize=${default_ramsize}${KERN}"
		ramsize="$default_ramsize"	# ǥեȤ
	fi
	mount -t tmpfs -o "size=${ramsize}m" tmpfs $MTCOW >/dev/null
}

# detect and mount device which contains loopback root filesystem
mount_disk()
{
	echo "${MSG}Detecting root disk.${KERN}"
	[ "$diskdev" = "detect" ] && diskdev=$(ls_block_nod)    # ٤ƤΥǥХ
	# hwsetup may symlink /dev/cdrom to cd device
	[ -b /dev/cdrom ] && diskdev="/dev/cdrom $diskdev"
	local dev fs
	for dev in $diskdev;do
		for fs in $(echo $diskfs | tr ',' ' ');do
			mount -t $fs -o $diskoption $dev $MTDISK >/dev/null 2>&1
			if [ $? -eq 0 ];then
				if [ -f "$MTDISK$looppath" ];then
					umount $MTDISK
					cp -pf $dev $MTNOAH/dev$DEVDIR/rootdisk    # ܥå󥯤äƥԡ
					mount -t $fs -o $diskoption $MTNOAH/dev$DEVDIR/rootdisk $MTDISK >/dev/null 2>&1
					echo "${OK}Mounted $dev on $MTDISK type $fs ($diskoption)${KERN}"
					mknod -m 660 $MTNOAH/dev$DEVDIR/cryptroot b 7 18       # loop18
					losetup $MTNOAH/dev$DEVDIR/cryptroot $MTDISK$looppath  # cryptrootޥå
					return 0
				else
					umount $MTDISK
				fi
			fi
		done
	done
	return 1
}

# connect nbd
# return 1:$MTNOAHؤtmpfsΥޥȤ˼ +2:nbd-clientΥԡ˼ +4:NBDͥ󤬰Ĥʤ +8:multipathǤʤ
connect_rootserver()
{
	local retval=0
	local server addr port nbdnum=0 device msg=
	local nbdnum=0 rootdevice=
	echo "${MSG}Connecting to rootservers.${KERN}"
	modprobe nbd  # TODO: nbdǥХκλϡ
	cp -f /bin/nbd-client $MTNOAH/nbd-client || retval=$(expr $retval + 2)
	chmod 750 $MTNOAH/nbd-client
	for server in $(echo $rootserver | tr ',' ' ');do
		# TODO:IPv4¸
		if strstr "$server" ":" ;then
			addr=${server%:*}
			port=${server##*:}
		else
			addr=$server
			port=9650
		fi
		[ ! -b $MTNOAH/dev$DEVDIR/rootserver$nbdnum ] && mknod -m 660 $MTNOAH/dev$DEVDIR/rootserver$nbdnum b 43 $nbdnum

		cd $MTNOAH
		chroot . ./nbd-client $addr $port dev$DEVDIR/rootserver$nbdnum <dev/console >dev/console 2>nbdmsg
		cd /

		if [ -z "$(cat $MTNOAH/nbdmsg)" ] ;then
			echo "${OK}Added rootserver $addr:$port${KERN}"
			rootdevice="$rootdevice,/dev$DEVDIR/rootserver$nbdnum"
			nbdnum=$(expr $nbdnum + 1)
		else
			echo "${FAILED}Can't connect to $addr:$port${KERN}"
			kill -9 $(ps | grep "nbd-client $addr $port" | head -n 1 | sed -r "s/[^0-9]*([0-9]*).*/\\1/")
			# ³ǤʤƤ³
		fi
		rm -f $MTNOAH/nbdmsg
	done

	sleep 2    # TODO:ɤ줯餤Ԥġ
	[ "$nbdnum" -eq 0 ] && return $(expr $retval + 4)
	if [ $nbdnum -ge 2 ];then
		modprobe multipath
		mknod -m 660 $MTNOAH/dev$DEVDIR/rootcluster b 9 10    # md10
		echo -n "${INFO}"
		mdadm --create $MTNOAH/dev$DEVDIR/rootcluster --level=multipath --raid-devices=$nbdnum \
		   $(for device in $(echo $rootdevice | tr ',' ' '); do echo $MTNOAH$device; done) || retval=$(expr $retval + 8)
		echo -n "${KERN}"
		ln -s rootcluster $MTNOAH/dev$DEVDIR/cryptroot
	else
		ln -s rootserver0   $MTNOAH/dev$DEVDIR/cryptroot
	fi

	return $retval
}

#return 1:cryptsetupԡʥѥɤ㤦
map_crypt()
{
	local retval=0
	echo "${MSG}Mapping crypted filesystem.${KERN}"
	modprobe dm-crypt
	modprobe aes-i586	# TODO:Ź沽ϡ ƥ¸
	if [ $password ];then
		echo -n "${INFO}"
		echo $password | cryptsetup luksOpen $MTNOAH/dev$DEVDIR/cryptroot squashroot || retval=1
		echo -n "${KERN}"
		cp -pf /dev/mapper/squashroot $MTNOAH/dev/mapper/
		ln -s ../mapper/squashroot $MTNOAH/dev$DEVDIR/squashroot
	else
		ln -s cryptroot $MTNOAH/dev$DEVDIR/squashroot
	fi
	return $retval
}

toram()
{
	local retval=0
	echo "${MSG}Copying root filesystem to Copy-on-Write sparce.${KERN}"
	cat $MTNOAH/dev$DEVDIR/squashroot > $MTCOW$looppath || return 1

	[ $password ] && cryptsetup luksClose squashroot
	rm -f $MTNOAH/dev$DEVDIR/squashroot
	rm -f $MTNOAH/dev/mapper/squashroot

	mknod -m 660 $MTNOAH/dev$DEVDIR/squashroot b 7 17      # loop17
	losetup $MTNOAH/dev$DEVDIR/squashroot $MTCOW$looppath  # ԡsquashrootޥå

	if [ ! $rootserver ];then
		losetup -d $MTNOAH/dev$DEVDIR/cryptroot
		rm -f $MTNOAH/dev$DEVDIR/cryptroot
		umount $MTDISK || retval=2
		echo "${OK}Now you can eject root disk.${KERN}"
	else
		local dev
		if [ -b $MTNOAH/dev$DEVDIR/rootcluster ];then
			mdadm --stop $MTNOAH/dev$DEVDIR/rootcluster
			rm -f $MTNOAH/dev$DEVDIR/rootcluster
			rmmod multipath
		fi
		for dev in $(ls $MTNOAH/dev$DEVDIR/rootserver? $MTNOAH/dev$DEVDIR/rootserver?? $MTNOAH/dev$DEVDIR/rootserver??? 2>/dev/null) ;do
			echo -n ${INFO}
			nbd-client -d $dev
			echo -n ${KERN}
			rm -f $dev
		done
		killall nbd-client
		killall -9 nbd-client 2>/dev/null
		rmmod nbd
		rm -f $MTNOAH/dev$DEVDIR/cryptroot
	fi

	return $retval
}

mount_squash()
{
	echo "${MSG}Mounting squashfs.${KERN}"
	# TODO Specify Parameter option...
	mount -t squashfs -o ro $MTNOAH/dev$DEVDIR/squashroot $MTSQUASH
}

# return:   1:$MTSQUASH/sparse.base̵ 3:root᡼Ĥʤ +2:$MTSQUASH/sparse.basʤ +4,+8:sparse롼ץХåޥȤǤʤ +12:dmsetup
linearize_root()
{
	local retval=0
	local num=1    #TODO:ֹդ1Ϥ뤫0Ϥ뤫
	echo "${MSG}Linearizing sliced root filesystem images.${KERN}"
	while [ $num -le 245 ];do
		if [ -f $MTSQUASH/root.$num ];then
			mknod -m 660 $MTNOAH/dev$DEVDIR/root.$num b 7 $(expr $num + 20)   # loop21 - loop255
			losetup $MTNOAH/dev$DEVDIR/root.$num $MTSQUASH/root.$num
			num=$(expr $num + 1)
		else
			break
		fi
	done
	if [ -f $MTSQUASH/sparse.base ];then
		mknod -m 660 $MTNOAH/dev$DEVDIR/sparse.base b 7 19     # loop19
		losetup $MTNOAH/dev$DEVDIR/sparse.base $MTSQUASH/sparse.base || retval=$(expr $retval + 4)
		mount -t $sparsefs -o ro $MTNOAH/dev$DEVDIR/sparse.base $MTSPARSE || retval=$(expr $retval + 8)
		mknod -m 660 $MTNOAH/dev$DEVDIR/sparse b 7 20     # loop20
		losetup $MTNOAH/dev$DEVDIR/sparse $MTSPARSE/sparse || retval=$(expr $retval + 12)
	else
		# TODO:̤ꤷƤɤ
		retval=1
	fi
	[ "$num" -eq 1 ] && return 3

	# root0 root1 ... root10 root11 ... root255 ֹlstablingϤ
	tabling $(ls $MTNOAH/dev$DEVDIR/root.? $MTNOAH/dev$DEVDIR/root.?? $MTNOAH/dev$DEVDIR/root.??? 2>/dev/null) \
	   $MTNOAH/dev$DEVDIR/sparse \
	   | dmsetup create linearroot || retval=$(expr $retval + 8)
	cp -pf /dev/mapper/linearroot $MTNOAH/dev/mapper/
	ln -s ../mapper/linearroot $MTNOAH/dev$DEVDIR/linearroot
	return $retval
}

#
map_cow()
{
	local img
	echo "${MSG}Mapping Copy-on-Write device.${KERN}"
	if [ -n "$cowkey" ];then
		img=$cowkey
	else
		img=cow.img
	fi
	modprobe cowloop
	mknod -m 660 $MTNOAH/dev$DEVDIR/cowroot b 241 10
	# busybox can't deal $MTCOW/$img because it gets "Value too large for defined data type" error.
	#if [ -f $MTCOW/$img ];then
	#fi
	# busybox can deal $MTCOW/$img if it is small.
	# but if it is large, busybox shows an error.
	if cmp -s /empty $MTCOW/$img 2>/dev/null;then
		# $MTCOW/$img is empty
		rm -f $MTCOW/$img
	else
		echo "${INFO}Checking existent Copy-on-Write file."
		# even if $MTCOW/$img doesn't exist, cowrepair is run.
		# so ignore the error.
		# cowrepair can deal $MTCOW/$img even if it is large.
		cowrepair $MTCOW/$img 2>/dev/null
		echo -n "${KERN}"
	fi
	cowdev -a $MTNOAH/dev$DEVDIR/linearroot $MTCOW/$img $MTNOAH/dev$DEVDIR/cowroot >/dev/null
}

#
mount_root()
{
	local retval=0
	echo "${MSG}Mounting root filesystem.${KERN}"
	mount -t $rootfs -o $rootoption $MTNOAH/dev$DEVDIR/cowroot $MTSYS
}

# TODO: swapμưС롼ץХååסͥåȥۤå
find_swap()
{
	local swap
	echo "${MSG}Enabling swap.${KERN}"
	for swap in $(echo $swapdev | tr ',' ' ');do
		swapon $swap
	done
	return 0  # 顼̵
}

# edit files and directories to fit it VIVER
edit_fs()
{
	echo "${MSG}Editing files.${KERN}"

	# ɬܥǥ쥯ȥ
	mkdir_f $MTSYS/etc
	mkdir_f $MTSYS/bin
	mkdir_f $MTSYS/lib

	# inherit /dev
	cp -af /dev/* $MTSYS/dev/

	#
	echo 0x0100 > /proc/sys/kernel/real-root-dev

	# fastboot (prevent fsck)
	touch $MTSYS/fastboot

	# fstab (cowroot and swap)
	echo "\
# Added by VIVER
$MTNOAH/dev$DEVDIR/cowroot	/		$rootfs		$rootoption	0 0
" > $MTSYS/etc/fstab
#TODO
#$MTNOAH/dev$DEVDIR/rootdisk	$MTNOAH$MTSQUASH	iso9660		ro	0 0
#$MTNOAH/dev$DEVDIR/squashroot	$MTNOAH$MTSQUASH	squashfs	ro	0 0
#$MTNOAH/dev$DEVDIR/sparse	$MTNOAH$TSPARSE		$sparsefs	ro	0 0
#none				$MTNOAH$MTCOW		tmpfs		rw	0 0

	# mtab (symlink to /proc/mounts)
	[ -e $MTSYS/etc/mtab ] && rm -rf $MTSYS/etc/mtab
	ln -s /proc/mounts $MTSYS/etc/mtab

	# resolv.conf
	[ -f /etc/resolv.conf ] && cp -f /etc/resolv.conf $MTSYS/etc/

	# install busybox so that ash can use it after pivot_root
	cp -f /bin/busybox $MTSYS/bin/
	[ ! -f $MTSYS/lib/libc.so.6     ] && cp -pf /lib/libc.so.6     $MTSYS/lib/    # ܥå󥯤äƥԡ
	[ ! -f $MTSYS/lib/ld-linux.so.2 ] && cp -pf /lib/ld-linux.so.2 $MTSYS/lib/    # ܥå󥯤äƥԡ


	# TODO:init
	cp /bin/init $MTNOAH/
	
	return 0
}

#
move_mtpoint()
{
	local retval=0
	echo "${MSG}Moving mount points.${KERN}"

	mkdir_f  $MTSYS$MTNOAH
	mount -o move $MTNOAH $MTSYS$MTNOAH    || return 1

	if [ -z "$rootserver" -a -z "$toram" ];then
		mkdir $MTSYS$MTNOAH$MTDISK
		chmod 640 $MTSYS$MTNOAH$MTDISK
		mount -o move $MTDISK $MTSYS$MTNOAH$MTDISK || retval=2
	fi

	mkdir $MTSYS$MTNOAH$MTSQUASH
	chmod 640 $MTSYS$MTNOAH$MTSQUASH
	mount -o move $MTSQUASH $MTSYS$MTNOAH$MTSQUASH || retval=$(expr $retval + 4)

	mkdir $MTSYS$MTNOAH$MTSPARSE
	chmod 640 $MTSYS$MTNOAH$MTSPARSE
	mount -o move $MTSPARSE $MTSYS$MTNOAH$MTSPARSE # 顼̵

	mkdir $MTSYS$MTNOAH$MTCOW
	chmod 640 $MTSYS$MTNOAH$MTCOW
	mount -o move $MTCOW $MTSYS$MTNOAH$MTCOW || retval=$(expr $retval + 8)

	if [ -n "$cowkey" -a -z "$directcow" ];then
		mkdir $MTSYS$MTNOAH$MTCOWBASE
		chmod 640 $MTSYS$MTNOAH$MTCOWBASE
		mount -o move $MTCOWBASE $MTSYS$MTNOAH$MTCOWBASE || retval=$(expr $retval + 16)
	fi

	# TODO:
	return $retval
}

# modules are no longer needed
umount_module()
{
	echo "${MSG}Unmounting module disk.${KERN}"
	umount /lib/modules
}

# pivot_root
do_pivot_root()
{
	echo "${MSG}Moving root filesystem.${KERN}"
	mkdir_f $MTSYS/initrd
	mkdir_f $MTSYS/sys
	mkdir_f $MTSYS/proc
	mount -o move /sys $MTSYS/sys
	mount -o move /proc $MTSYS/proc
	pivot_root $MTSYS $MTSYS/initrd
}

# run bootrc script
bootrc()
{
	local key
	local optional_role
	local preset_role
	echo "${MSG}Running bootup rc scripts.${KERN}"

	mkdir_f $OUTPUT_DIR

	# ѡѤߤΥ֡ȥѥ᡼
	mkfile_f $OUTPUT_DIR/bootparams
	echo "" > $OUTPUT_DIR/bootparams
	chmod 600 $OUTPUT_DIR/bootparams
	for key in $paramlist;do
		eval echo "param_$key=\'\$param_$key\'" >> $OUTPUT_DIR/bootparams
	done


	mkfile_f $OUTPUT_DIR/sysparams
	chmod 600 $OUTPUT_DIR/sysparams
	echo "
MTSYS='$MTSYS'
MTDISK='$MTDISK'
MTSQUASH='$MTSQUASH'
MTSPARSE='$MTSPARSE'
MTCOWBASE='$MTCOWBASE'
MTCOW='$MTCOW'
MTNOAH='$MTNOAH'
RCDIR='$RCDIR'
DEVDIR='$DEVDIR'

looppath='$looppath'
rootserver='$rootserver'
remoterc='$remoterc'

cowkey='$cowkey'
directcow='$directcow'

password='$password'

ramsize='$ramsize'

toram='$toram'

NORMAL='$NORMAL'
MSG='$MSG'
INFO='$INFO'
KERN='$KERN'
OK='$OK'
FAILED='$FAILED'
WARN='$WARN'

BB='/bin/busybox'
" > $OUTPUT_DIR/sysparams


	mkfile_f $OUTPUT_DIR/netparams
	chmod 600 $OUTPUT_DIR/netparams
	echo "
wakenet='$wakenet'
netdev='$netdev'

staticip='$staticip'
netmask='$netmask'
gateway='$gateway'
" > $OUTPUT_DIR/netparams


	mkfile_f $OUTPUT_DIR/hardparams
	chmod 600 $OUTPUT_DIR/hardparams
# TODO:swap
#swap='$swap'
#directswap='$directswap'

	echo "
cowkeyfs='$cowkeyfs'
cowdev='$cowdev'
cowfs='$cowfs'
cowoption='$cowoption'

rootfs='$rootfs'
rootoption='$rootoption'

sparsefs='$sparsefs'

swapdev='$swapdev'

noide='$noide'
usesata='$usesata'
usepcmcia='$usepcmcia'
nohwsetup='$nohwsetup'
nosetaudio='$nosetaudio'
nosetscsi='$nosetscsi'

modprobe_plus='$modprobe_plus'

verboseboot='$verboseboot'

diskdev='$diskdev'
diskfs='$diskfs'
diskoption='$diskoption'
" > $OUTPUT_DIR/hardparams

	# copy hwsetup information
	mkdir_f $OUTPUT_DIR/hwsetup
	chmod 700 $OUTPUT_DIR/hwsetup
	# TODO:hwsetup¸
	cp -f /initrd/etc/sysconfig/xserver  $OUTPUT_DIR/hwsetup/ >/dev/null 2>&1
	cp -f /initrd/etc/sysconfig/keyboard $OUTPUT_DIR/hwsetup/ >/dev/null 2>&1
	cp -f /initrd/etc/sysconfig/mouse    $OUTPUT_DIR/hwsetup/ >/dev/null 2>&1
	cp -f /initrd/etc/sysconfig/hwsetup  $OUTPUT_DIR/hwsetup/ >/dev/null 2>&1


	if [ $remoterc ];then
		echo "${INFO}Downloading rc script from $remoterc.${KERN}"
		cd $RCDIR
		tftp -g -r viverrc $remoterc
		chmod 755 viverrc
		cd /
	fi

	preset_role="all"
	if [ ! "$rootserver" ];then
		preset_role="$preset_role,disk"
	else
		preset_role="$preset_role,netboot"
	fi
	if [ "$cowkey" ];then
		preset_role="$preset_role,phycow"
	else
		preset_role="$preset_role,ramcow"
	fi
	optional_role=$(echo $role | tr ',' ' ')

	enable_verbose
	echo -n "${NORMAL}"
	[ -x $RCDIR/viverrc ] && $RCDIR/viverrc $RCDIR $preset_role $optional_role
	echo -n "${KERN}"
	disable_verbose

	return 0
}

# umount /proc and /sys
umount_proc_sys()
{
	echo "${MSG}Unmounting proc and sys filesystem.${KERN}"
	umount /sys
	umount /proc
}



# run intaractive shell to debug
# $1 = parameter which is set at VIVERDEBUG=
debug()
{
	enable_verbose
	set > /viver.params
	echo "${INFO}Entering debug mode.${NORMAL}"
	sh
}


# catch error and run intaractive shell
# $1 = action for checking
catch()
{
	local code=$?
	if [ "$code" -eq 0 ];then
		echo "${MOVE_TO_COL}${MOVE_UP}${NORMAL}[${OK}  OK  ${NORMAL}]${KERN}"
		[ "$VIVERDEBUG" = "$1" ] && debug
		return 0
	fi
	echo "${MOVE_TO_COL}${NORMAL}[${FAILED}FAILED${NORMAL}]${KERN}"
	enable_verbose
	set > /viver.params

	echo ""
	echo "${FAILED}ERROR ($1 $code)"
	errormsg $1 $code
	echo ""
	echo "Try to recover and type 'exit' and press enter key.${NORMAL}"

	sh

	# retry the action
	$1
}

