#!/bin/ash
# changes:
# - BASESFS is fd-arm.sfs
# - SAVEFILE is fdsave.sfs
#
# init - start the system (1st stage - before switch root)
# (C) James Budiono 2012, 2013
# License: GNU GPL Version 3 or later
#
# What it does:
# - load all modules for known hardware
# - load base.sfs, load savefile, create tmpfs layer if requested
# - start network, if requested
# - build stacked fs and switch to a new root
#
# versions
# 1.0 - Mar 2012 - initial release
# 1.1 - Mar 2012 - support for kernel without devtmpfs, change file locations, no more rc.sysinit
# 1.2 - Mar 2012 - add local support for base.sfs
# 1.3 - Apr 2012 - add search support for savefile, nocoldplug
# 1.4 - Apr 2012 - add multisession support
# 1.5 - Apr 2012 - add keepvar option --- /var in savefile is now cleared by default
# 1.6 - Apr 2012 - :dev: in ;device: savefile/basesfs does not need /dev prefix		
# 1.7 - May 2012 - nocoldplug is now default using minimal modules; coldplug still available as parameter
#                  add devwait and loadmodules
# 1.8 - May 2012 - aufs-root-id now exported in BOOTSTATE, also ensure BOOTSTATE is never overwritten
# 1.9 - May 2012 - add savefile device permission config settings; remove broken usb-disk-wait; 
#                  devwait renamed to waitdev
# 1.10 - May 2012 - local basesfs device is mounted r/w, if devsave=devbase then devsave is bind-mounted to devbase
# 1.11 - June 2012 - add base2ram parameter
# 1.12 - June 2012 - multisession - instead of loading the first "n" session, "n" now means
#                    don't load the last "n" sessions (default n=0) - same meaning as in puppy
# 1.13 - July 2012 - fixes for loading read-only savefile; fixes for determining savefile filename
#                    (only last extension is now stripped); multisession savefiles already prepended with
#                    $MULTI_PREFIX
# 1.14 - Oct 2012 - for Fatdog 610; search is always enabled (search=1), use search=0 for old behaviour
# 1.15 - Nov 2012 - enable kernel polling (block.events_dfl_poll_msecs) by default
# 1.16 - Nov 2012 - error log now goes a file if showerr is not used, add debuginitrd parameter
# 1.17 - Jan 2013 - add LVM support
# 1.18 - Jan 2013 - /var will keep its symlinks
# 1.19 - Jan 2013 - add support for cryptsetup using "dmcrypt" variant of crypt, improve savefile search
# 1.20 - Jan 2013 - add support for "savedir" - save session into a directory instead of a loop mounted savefile.
#                 - uuid and label can now be prefixes instead of the fullname/uuid.
#                 - add "dofsck" - fsck (and fix) filesystems before mounting
# 1.21 - Jan 2013 - remove "savedir" - both basesfs and savefile can now use both savefile and directory.
#                   "+" option for path are now removed, to use entire device use "/" (root) as path.
# 1.22 - Jan 2013 - add support for mdadm (linux software raid)
# 1.23 - Feb 2013 - deprecate losetup_klibc, use cryptsetup even for losetup encrypted files
# 1.24 - Mar 2013 - add "lateshell"
# 1.25 - Jun 2013 - bugfix for load_session whiteout handling, also ro+wh now setup for pup_save
#                   keepvar is now on by default, only deleting /var/run /var/lock[s], /var/cache
# 1.26 - Jun 2013 - add base2ram=expand to expand basesfs before loading
# 1.27 - Jul 2013 - do not look at nand* for savefiles/sfs
#
# Once booted, boot-time information is made available in $BOOTSTATE_PATH 
# (default is /etc/BOOTSTATE)
#
#### boot parameters 
# blacklist - list of modules to be blacklisted, comma-separated (default none)
# loadmodules - list of modules to be loaded, comma-separated (default none)
# waitdev - number of seconds to wait for device to settle, so that it can be detected,
#           before continuing with boot process (default zero)
# coldplug  - probe and load all modules here and now - otherwise, only $MIN_MODULES get loaded
# withlvm   - activate lvm ("with lvm")
# withmdadm - activate mdadm linux software raid ("with mdadm")
# dofsck    - fsck filesystems before mounting (only ext2/3/4 and fat supported for now)
#
# basesfs - location of the base.sfs basesfs=type:params (default initrd)
#			allowable types & their parameters
#			- initrd:path (path optional)
#			- cifs:user:pass:unc:path (path optional, if user=+ then ask user/pass)
#			- device:dev:path[:crypt] - specific device (path optional)
#			- label:label:path[:crypt] - specific label (path optional)
#			- uuid:uuid:path[:crypt] - specific uuid (path optional)
#			- local:path[:crypt] or usb:path[:crypt] (path optional) - look in all local/usb devices
#			- nbd:host:port[:crypt]
#			- ask - ask during boot
#			- none - dont load basesfs
#			Note: basesfs is always mounted read-only except for local devices, which is mounted rw
#			Note: "crypt" denotes whether the device/partition is encrypted. Not the basesfs file.
#				  basesfs file is also assumed encrypted if it has the word "_crypt_" in it.
#           Note: "crypt" can be either "crypt" or "dmcrypt" - the first one will use cryptoloop,
#                 the second one will use dm-crypt (cryptsetup/luks)
#
# base2ram - if set to yes (base2ram=yes), the basesfs will be copied to RAM and loaded from there.
#            applicable for all basesfs except "initrd" - "initrd" basesfs is already in RAM.
#            if set to expand (base2ram=expand), the basesfs will be expanded before copied and loaded.
#            applicable for all (including initrd)
# 
# savefile - location of the save file - savefile=layer:location or "none" (default: direct:local)
# 			 - layer options
#			 	- ram - use tmpfs as intermediary layer
#			 	- direct - use savefile directly - when possible (default)
#			 	- none - use only tmpfs, ignore location
#				- ask - ask during boot
# 			 - location types
#				- local:path[:crypt] - search all local devices for save file (path optional) (default)
#				- usb:path[:crypt] - search all usb devices for save file (path optional)
#				- device:dev:path[:crypt] - specific device (path optional)
#				- label:label:path[:crypt] - specific label (path optional)
#				- uuid:uuid:path[:crypt] - specific uuid (path optional)
#				- cifs:user:pass:unc:path (path optional) 
#                   --> If user=+, ask for user/pass
#					--> Note: any + in unc or path will be replaced with username.
# 				- nbd:host:port[:crypt]
#				- multi:dev:path:n - load in multisession mode, dev & path optional 
#                   --> default dev = first dvd drive (sr0), n = don't load the last n sessions
#					--> dev isn't necessarily a dvd, it could be harddisk too ...
#			 Note: "crypt" denotes whether the device/partition is encrypted. Not the savefile file.
#				   savefile file is also assumed encrypted if it has the word "_crypt_" in it.
#            Note: "crypt" can be either "crypt" or "dmcrypt" - the first one will use cryptoloop,
#                  the second one will use dm-crypt (cryptsetup/luks)
#			 Note: read-only savefile (ie located on read-only device) can be used 
#            Note: savefile can be on the same devices as basesfs - but only for local devices.
#                  For non-local devices (cifs, nbd) - they MUST BE different devices.
#            Note: directory access on cifs and vfat requires tmpfs. Otherwise crash (cifs) or read-only (vfat)
#
# search - search for savefiles in the path. Only applicable for "savefile", and only if location is local or usb.
#		   search[=depth] - how deep the search should be, default is 1
#		   How search is done:
#		   - savefile path is broken down into its path and filename component. 
#		   - filename component is broken down to its name and extensions, only name is used
#		   - look inside the path, for all files with starting with name
#		   - repeat this in all devices.
#		   If only one savefile is found, it will automatically be used.
#		   Otherwise, the system will ask which one to use.
#          Note: search=0 will use an old, simple-minded search. Will always use the first savefile found,
#                and file searched is the exact filename specified. 
#
# noxino - aufs noxino option (=do not use external inode table). 
#		   DO NO USE THIS unless absolutely necessary (ie troubleshooting purposes)
#
# enctype - encryption type (default "aes")
#			this is used by both savefile and basesfs. 
#           for "dmcrypt", this list the options for cryptsetup
#
# net - configure network net=type:dev:params (default none)
#		allowable types & their parameters
#		- wired:dev:dhcp ("dhcp" can be omitted)
#		- wired:dev:ip:ip-address:netmask:gateway:dns (gateway and dns is optional)
#		- wpa|wpa2:ssid:pass:dev:dhcp ("dhcp" can be omitted)
#		- wpa|wpa2:ssid:pass:dev:ip:ip-address:netmask:gateway:dns
#		- ask - ask during boot
#		Note: more to be added: wep, open, adhoc
#		Note: net will automatically enable coldplug
#
# keepvar - do not remove files in /var when starting up (debugging options)
#
# showerr - display output of stderr on screen (otherwise it's logged to /dev/initrd.err)
# debuginitrd - turn on shell tracing in initrd (set -x)
# earlyshell - get a shell as early as possible (default false). Type exit to continue.
# lateshell - get a shell just after establishment of aufs new_root, before cleanup and switch root
#             (default false). Type exit to continue.
#
# these are implemented by /sbin/coldplug.sh, thus only takes effect when "coldplug" is used
# DRV_DEBUG - show coldplug activity details
# DRV_WAIT  - time to wait (in seconds) before coldplug re-scanning

### configuration parameters

### default values

# list of modules to be loaded when full-coldplugging is not used
MIN_MODULES=""		 	# "ehci-hcd usb-storage ums-realtek" if not compiled in
MIN_MODULES_DELAY=1	 	# seconds extra delay for min-modules to get ready (1 is good)
DEFAULT_DEVICE_DELAY=0	# seconds - default delay time for devices (so that they are recognised by kernel)

# save device security
SAVEDEVICE_GROUP=users	# default group with access to savedevice
SAVEDEVICE_GID=500		# gid of the above, if the group can't be found in /etc/group
SAVEDEVICE_CHMOD=775	# mode for savedevice (for ext2/3/4)
SAVEDEVICE_UMASK=002	# umask for savedevice (for vfat, ntfs)

# paths & devices for savefile, basesfs and multisession
BASE_SFS_DEFAULT=initrd
BASE_SFS_DEFAULT_PATH=/fd-arm.sfs
SAVEFILE_DEFAULT=direct:local
SAVEFILE_DEFAULT_PATH=/fdsave.ext4
MULTI_DEVICE_DEFAULT=sr0

# misc
DMCRYPT_DEVNAME_DEFAULT=dmcrypt
ENCTYPE_DEFAULT=aes
WIFI_ENABLE_TIMEOUT=100	# unit of 1/10 seconds, max time to wait for wifi connection
MODULE_SFS=kernel-modules.sfs
KERNEL_POLL_MSECS=5000	# default kernel polling value if unset

BOOTSTATE_PATH=/etc/BOOTSTATE
export BOOTSTATE_PATH

### mount locations
AUFS_ROOT=/aufs
NEW_ROOT=$AUFS_ROOT/new_root
MODULE_SFS_MOUNT=$AUFS_ROOT/${MODULE_SFS%.sfs}
BASELINE_MOUNT=$AUFS_ROOT/pup_init
TMPFS_MOUNT=$AUFS_ROOT/pup_rw
BASE_SFS_MOUNT=$AUFS_ROOT/pup_ro
SAVEFILE_MOUNT=$AUFS_ROOT/pup_save
BASE_SFS_DEV_MOUNT=$AUFS_ROOT/devbase
SAVE_DEV_MOUNT=$AUFS_ROOT/devsave
MULTI_MOUNT=$AUFS_ROOT/pup_multi
MULTI_PREFIX=multi-						# this is always exported in BOOTSTATE 

SAVE_DEV_PROTO=$SAVE_DEV_MOUNT			# this is always exported in BOOTSTATE
SAVEFILE_PROTO=$SAVEFILE_DEFAULT_PATH	# this is always exported in BOOTSTATE

### runtime variables - capture the system boot-up state
CONFIGURED_NET=		# dev:ip or dev:dhcp - network device configured by init
BASE_SFS_PATH=		# path to the actual base sfs file used
SAVEFILE_PATH=  	# path to the actual save file used
SAVEFILE_DEVICE=	# device where the save file is located
BASE_SFS_DEVICE=	# device where the base sfs is located
BASE_SFS_BIND=		# if not blank, basesfs is bind-mounted instead of loopback-mounted
SAVEFILE_BIND=		# if not blank, savefile is bind-mounted instead of loopback-mounted
ENCTYPE=			# encryption type
SEARCH_DEPTH=1		# default depth of "search" parameter
MULTI_DEVICE=		# multisession device
AUFS_ROOT_ID=		# the aufs id for the aufs root filesystem
DMCRYPT_DEVNAME=	# this will list the dmcrypt devices used by the system
DOFSCK=				# if not blank, fsck filesystem before mounting it

### utilities - decz
# decz - decrements counter, and return true when it is zero
# $1 - name of variable to be decremented
decz() {
	local curval
	eval "curval=\$$1"
	curval=$((curval-1))
	[ $curval -le 0 ] && return 0
	eval "$1=$curval"
	return 1
}

### utilities - replace marker with given string
# $1 - marker, $2 - replacement string, $3 - variable name containing the string to replace
expand_marker() {
	local curval repeat	
	
	eval "curval=\$$3"
	repeat=true
	until [ -z "$repeat" ]; do
		case $curval in
			*$1*)
				#echo replacing ${curval%%$1*}$1${curval#*$1} with ${curval%%$1*}$2${curval#*$1}
				curval=${curval%%$1*}$2${curval#*$1}
				;;
			*)
				repeat=
				;;
		esac
	done
	eval "$3=$curval"
}

### utilities - print variable name and its contents
# $1 - name of variable to print
dump_var() {
	eval "echo $1=\'\$$1\'"
}

### utilities - export important variables to BOOTSTATE
# $1 - where to export to 
export_boot_state() {
	exec 3>&1 1> $1
	dump_var AUFS_ROOT
	dump_var BASELINE_MOUNT	
	dump_var MODULE_SFS_MOUNT	
	dump_var SAVE_DEV_PROTO
	dump_var SAVEFILE_PROTO
	dump_var MULTI_PREFIX

	[ "$TMPFS_MOUNT" ] && dump_var TMPFS_MOUNT
	[ "$BASE_SFS_MOUNT" ] && dump_var BASE_SFS_MOUNT
	[ "$SAVEFILE_MOUNT" ] && dump_var SAVEFILE_MOUNT
	[ "$CONFIGURED_NET" ] && dump_var CONFIGURED_NET
	
	[ -e "$BASE_SFS_DEV_MOUNT" ] && dump_var BASE_SFS_DEV_MOUNT
	[ -e "$SAVE_DEV_MOUNT" ] && dump_var SAVE_DEV_MOUNT
	
	[ "$BASE_SFS_PATH" ] && dump_var BASE_SFS_PATH	
	[ "$SAVEFILE_PATH" ] && dump_var SAVEFILE_PATH
	[ "$SAVEFILE_DEVICE" ] && dump_var SAVEFILE_DEVICE
	[ "$BASE_SFS_DEVICE" ] && dump_var BASE_SFS_DEVICE
	[ "$BASE_SFS_BIND" ] && dump_var BASE_SFS_BIND
	[ "$SAVEFILE_BIND" ] && dump_var SAVEFILE_BIND
	
	[ "$MULTI_MOUNT" ] && dump_var MULTI_MOUNT
	[ "$MULTI_DEVICE" ] && dump_var MULTI_DEVICE
	
	[ "$AUFS_ROOT_ID" ] && dump_var AUFS_ROOT_ID
	
	[ "$ENCTYPE" ] && dump_var ENCTYPE
	[ "$DMCRYPT_DEVNAME" ] && dump_var DMCRYPT_DEVNAME
	exec 1>&- 1>&3 3>&-
}

### utilities - get user & password
# $1 prompt, $2 - default user, $3 - default password
# only prompt for password if user is "+", otherwise use defaults are used
# return: user & pass variable
get_user_pass() {
	user=$2 pass=$3
	if [ "$2" = "+" ]; then
		read -p "$1 user: " user 2>&1
		read -s -p "password: " pass 2>&1
		echo
	fi
}

### utilities - setup loop device for encrypted device
# $1 device, return value = mountdev
losetup_encrypted() {
	# convert cryptoloop enctype to cryptsetup's
	ENCTYPE="-M plain -c ${enctype:-$ENCTYPE_DEFAULT}-cbc-plain -h plain" 
	open_dmcrypt "$@"
	return
	
	### deprecated, will be removed soon
	local pass
	ENCTYPE=${enctype:-$ENCTYPE_DEFAULT} # from parameter
	modprobe cryptoloop	
	mountdev=$(losetup -f)
	read -s -p "crypt password: " pass 2>&1
	echo $pass | losetup-klibc -p 0 -e $ENCTYPE $mountdev $1
	unset pass
	echo
}

### utilities - open dm-crypt device using cryptsetup
# $1 device, return value = mountdev
open_dmcrypt() {
	local count
	count=0
	while [ -e /dev/mapper/$DMCRYPT_DEVNAME_DEFAULT$count ]; do count=$(( $count + 1 )); done
	cryptsetup open ${ENCTYPE:=$enctype} $1 $DMCRYPT_DEVNAME_DEFAULT$count 2>&1 
	mountdev=/dev/mapper/$DMCRYPT_DEVNAME_DEFAULT$count	
	DMCRYPT_DEVNAME="$DMCRYPT_DEVNAME$mountdev "
}

### utilities - perform fsck on device 
# $-fstype, $2 device
perform_fsck() {
	case $1 in
		ext*) e2fsck -y $2 ;;
		vfat) dosfsck -y $2 ;;
	esac
}

### utilities - mount helper 
### mount encrypted device & use ntfs-3g if partition type if ntfs
# if $1 is "crypt" or "dmcrypt", device is encrypted - the rest of the parameters are shifted
# otherwise, $1 device, $2 mount point, $3 "-o", $4 any other options
# return true if mount successful, otherwise false
mount_device() {
	local EXIT_CODE fstype
	
	# check for encrypted device
	local prevmount mountdev=$1 
	case "$1" in
		crypt)	  losetup_encrypted $2; shift; ;; # drop the "crypt" keyword
		dmcrypt)  open_dmcrypt $2; shift ;; # drop the "dmcrypt" keyword
	esac	
	shift # drop the original device, use mountdev instead
	
	# check if already mounted - if yes, bind mount
	prevmount=$(awk -v mountdev="$mountdev" '$1 == mountdev { print $2; exit }' /proc/mounts)
	if [ $prevmount ]; then
		echo bind-mount $prevmount to $1
		mount -o bind $prevmount $1
		return
	fi
		
	# check for ntfs, vfat & ext2
	get_savedevice_gid
	fstype=$(guess_fstype $mountdev)
	[ "$DOFSCK" ] && perform_fsck "$fstype" $mountdev
	case "$fstype" in
		ntfs)
			# ntfs-3g - if r/w fails fallback to r/o mount (then on to kernel ntfs driver)
			ntfs-3g $mountdev $1 -o    gid=$GID,umask=$SAVEDEVICE_UMASK,$3 ||
			ntfs-3g $mountdev $1 -o ro,gid=$GID,umask=$SAVEDEVICE_UMASK,$3 ||
			mount -t ntfs $mountdev $1 -o ro,gid=$GID,umask=$SAVEDEVICE_UMASK,$3
			;;
		vfat)
			mount $mountdev $1 -o umask=$SAVEDEVICE_UMASK,gid=$GID,utf8,quiet,$3
			;;
		*)
			mount $mountdev $*
			;;
	esac
	EXIT_CODE=$?
	if [ $EXIT_CODE -eq 0 ]; then
		chgrp $GID $1
		chmod $SAVEDEVICE_CHMOD $1	
	fi
	return $EXIT_CODE
}
# returns the GID of "savedev" group - if GID is already defined, will use that one
get_savedevice_gid() {
	[ "$GID" ] && return
	GID=$(awk -F: "/^$SAVEDEVICE_GROUP:/ {print \$3}" /etc/group)
	[ -z "$GID" ] && GID=$SAVEDEVICE_GID
}


### utilities - mount helper for loopback file
# if loopback filename has "_crypt_" or "_dmcrypt_" in it, assume it's encrypted
# $1 loopback file, $2 mount point, $3 "-o", $4 any other options
# return true if mount successful, otherwise false
# also returns USE_TMPFS=true if it thinks that tmpfs is needed,
#              BIND_MOUNT=true if $1 is bind-mounted instead of looped
mount_loop() {
	local mountdev exit_code
	if [ -d $1 ]; then
		### directory - perform bind-mount
		BIND_MOUNT=true
		mount $1 $2 -o bind,$4
		exit_code=$?
		if  [ $exit_code -eq 0 ]; then
			# make sure we operate with the right access rights		
			get_savedevice_gid
			chgrp $GID $2
			chmod $SAVEDEVICE_CHMOD $2
			
			# always use tmpfs for certain filesystems (otherwise kernel panic or read-only fs)
			case $(awk -v mnt=$2 '$2==mnt {print $3}' /proc/mounts) in
				vfat|cifs) USE_TMPFS=true ;;
			esac
		fi
		return $exit_code
		
	else
		### everything else, assume a file - perform loopback
		BIND_MOUNT=
		case ${1##/} in 
		*_crypt_*)
			mount_device "crypt" $* 
			;;
		*_dmcrypt_*)
			mount_device "dmcrypt" $* 
			;;	
		*)
			mountdev=$(losetup -f)
			losetup $mountdev $1 || return
			shift 	# drop the original loopback file	
			mount_device $mountdev $*
			;;
		esac		
	fi
}


### utilities - find local device that contains savefile / basesfs
# find local devices that has basesfs/savefile in it all known block devices
# $1 - name of return variable
# $2 - basesfs / savefile path (cannot be blank)
# $3 - filter based on readlink value of /sys/block
find_local_device() {
	local result dev blockdev partitions

	result=
	mkdir /mnt/tmp	
	echo -n "Looking in "
	for blockdev in in $(ls /sys/block | grep -vE "^loop|^nbd|^ram|^nand"); do
		partitions=$(ls -d /sys/block/$blockdev/${blockdev}* | sed "s_/sys/block/$blockdev/__")
		partitions=${partitions:-$blockdev}	
		for dev in $partitions; do
			echo -n "$dev "
			if readlink /sys/block/$blockdev | grep -q "$3"; then
				mount /dev/$dev /mnt/tmp # mount it to see if the file exist
				if [ -e /mnt/tmp/$2 ]; then
					result=/dev/$dev
					echo -n "- found"
					umount -f /mnt/tmp				
					break 2
				fi
				umount -f /mnt/tmp
			fi
		done
	done
	echo
	rmdir /mnt/tmp
	eval "$1=$result"
}


### utilities - find the device and savefile to use
# find local devices that has savefile pattern in all known block devices
# $1 - name of return variable (for device)
# $2 - basesfs / savefile path (cannot be blank)
# $3 - filter based on readlink value of /sys/block
# $4 - name of return variable (for savefile path)
find_and_choose_local_device() {	
	local dev spath sname blockdev partitions
	local result p count choose
	
	spath=${2%/*}; sname=${2##*/}; sname=${sname%.*}
	[ "$spath" = "$2" ] && spath=	# special case - no path specified
	count=0; result=
	
	# look for all savefiles in all devices
	mkdir -p /mnt/tmp
	echo -n "Looking in "
	for blockdev in $(ls /sys/block | grep -vE "^loop|^nbd|^ram|^nand"); do
		partitions=$(ls -d /sys/block/$blockdev/${blockdev}* | sed "s_/sys/block/$blockdev/__")
		partitions=${partitions:-$blockdev}
		for dev in $partitions; do
			echo -n "$dev "	
			if readlink /sys/block/$blockdev | grep -q "$3"; then
				# look for and record all savefiles found
				mount /dev/$dev /mnt/tmp 
				while read -r p; do
					if [ "$p" ]; then
						count=$(($count + 1))
						result="$result\n$p"
					fi
				done << EOF
				$(find /mnt/tmp/$spath -maxdepth $SEARCH_DEPTH -name "$sname*" | sed "s_^/mnt/tmp/_/dev/$dev:_")
EOF
				umount -f /mnt/tmp
			fi
		done
	done
	echo
	rmdir /mnt/tmp
	
	# choose device and savefile
	if [ $count -ne 0 ]; then
		if [ $count -eq 1 ];then
			choose=1
		else
			bold="\e[1m"; unbold="\e[0m"
			echo -e "\n${bold}Please choose savefile to use:${unbold}"
			
			# this complicated-looking code gets the device labels and prints the choices
			{ busybox blkid | sed '/LABEL/ !d; s/ .*LABEL="//; s/".*$//'
			  echo ===savefile===; echo -e $result
			} | awk -F: ' { 
				if ($0 ~ /===savefile===/) count=1
				else if (count == "") labels[$1]=$2
				else if ($0 != "") 
				printf ( "[%d] %s (%s) --> %s\n", count++, $1, labels[$1], $2 ) }'
			echo -ne "\nYour choice [none]: "
			read choose
		fi
		if [ $choose ]; then		
			eval "$1=$(echo -e $result | awk -F: "NR-1 == $choose {print \$1}")"
			[ $4 ] && eval "$4=$(echo -e $result | awk -F: "NR-1 == $choose {print \$2}")"
		fi		
	fi
}


### cmdline processing - blacklist
INITRD_CONF=/etc/modprobe.d/initrd.conf
process_blacklist() {
	[ -z "$blacklist" ] && return
	mkdir -p /etc/modprobe.d
	OIFS="$IFS"; IFS=,
	for p in $blacklist; do
		echo blacklist $p >> $INITRD_CONF
	done
	IFS="$OIFS"
	[ -e $INITRD_CONF ] && cp $INITRD_CONF $BASELINE_MOUNT/$INITRD_CONF
}


### cmdline processing - loadmodules and coldplug 
process_loadmodules() {	
	# load all modules if asked or if "net" specified, otherwise load minimal set of modules	
	if  [ $net ] || grep -q coldplug /proc/cmdline; then 
		. /sbin/coldplug.sh	
		
	elif [ "$MIN_MODULES" ]; then
		# load modules needed to access savefile and usb keyboard
		echo "Loading modules needed to access disk drives and usb keyboard ..."
		for p in $MIN_MODULES; do modprobe $p; done
		sleep $MIN_MODULES_DELAY # wait for these modules to get ready
		
		# disable loading of hid modules - either compiled in, or use loadmodules parameter
		# find /lib/modules -name "hid-*" | sed 's_.*/__; s/.ko//' | xargs -n 1 modprobe & 
	fi
	
	# load extra modules if requested - if modules need time to get ready, use "waitdev"
	[ $loadmodules ] && for p in $(echo $loadmodules | sed 'y/,/ /'); do modprobe $p; done
}


### cmdline processing - waitdev
# wait for devices: sleep a specified number of seconds
process_waitdev() {
	waitdev=${waitdev:-$DEFAULT_DEVICE_DELAY}
	if [ "$waitdev" -ne 0 ]; then
		echo "Waiting $waitdev seconds for devices to be ready..."
		sleep $waitdev
	fi
}


### cmdline processing - basesfs - mount helper
# $1 - text to print
# $2 - basesfs device
# $3 - basesfs path (if +, mount entire partition)
# $4 - if "crypt" - device is encrypted
mount_local_basesfs() {
	BASE_SFS_DEVICE=$2
	BASE_SFS_PATH=${3:-$BASE_SFS_DEFAULT_PATH}
	echo "$1$2 $4 on $BASE_SFS_PATH"			
	mkdir -p $BASE_SFS_DEV_MOUNT $BASELINE_MOUNT/$BASE_SFS_DEV_MOUNT
	mount_device $4 $2 $BASE_SFS_DEV_MOUNT # -o ro 
	! mount_loop $BASE_SFS_DEV_MOUNT/$BASE_SFS_PATH $BASE_SFS_MOUNT && BASE_SFS_MOUNT= # -o ro && BASE_SFS_MOUNT=		
}

### cmdline processing - basesfs
process_basesfs() {
	[ "$basesfs" = "ask" ] && read -p "basesfs=" basesfs 2>&1
	basesfs=${basesfs:-$BASE_SFS_DEFAULT}
	OIFS="$IFS"; IFS=:
	set -- $basesfs
	IFS="$OIFS"		
	echo -n "Loading base sfs from "
	case $1 in
		initrd)
			# initrd:/path
			[ "$base2ram" = "yes" ] && base2ram=no	# already in ram
			BASE_SFS_PATH=${2:-$BASE_SFS_DEFAULT_PATH}
			echo "initrd: file $BASE_SFS_PATH" 
			! mount_loop $BASE_SFS_PATH $BASE_SFS_MOUNT -o ro && BASE_SFS_MOUNT=
			;;
		cifs)
			# cifs:user:pass:unc:path
			BASE_SFS_PATH=${5:-$BASE_SFS_DEFAULT_PATH}
			echo "cifs: $4 on $BASE_SFS_PATH"
			get_user_pass cifs $2 $3			
			mkdir -p $BASE_SFS_DEV_MOUNT $BASELINE_MOUNT/$BASE_SFS_DEV_MOUNT
			mount.cifs $4 $BASE_SFS_DEV_MOUNT -o ro,sec=ntlmv2,user=$user,pass=$pass 
			! mount_loop $BASE_SFS_DEV_MOUNT/$BASE_SFS_PATH $BASE_SFS_MOUNT -o ro && BASE_SFS_MOUNT=
			unset user pass
			;;
		device|label|uuid)
			# device:dev:path[:crypt]
			# label:label:path[:crypt]
			# uuid:label:path[:crypt]			
			if [ "$1" = "device" ]; then
				mount_local_basesfs "" /dev/$2 $3 $4
			else
				mount_local_basesfs "$1 $2 -> "  $(blkid | grep -im 1 $1=\"$2.\*\" | sed 's/:.*//') $3 $4
			fi			
			
			;;
		local|usb)
			# local:path[:crypt] or usb:path[:crypt]
			BASE_SFS_PATH=${2:-$BASE_SFS_DEFAULT_PATH}
			echo "$1, searching for $BASE_SFS_PATH"
			case $1 in
				local) find_local_device BASE_SFS_DEVICE "$BASE_SFS_PATH" ;;
				usb) find_local_device BASE_SFS_DEVICE "$BASE_SFS_PATH" usb ;;
			esac
			if [ "$BASE_SFS_DEVICE" ]; then
				echo -n "Loading base sfs from "
				mount_local_basesfs "" $BASE_SFS_DEVICE $BASE_SFS_PATH $3
			else
				echo "Cannot find $BASE_SFS_PATH from anywhere."
				BASE_SFS_MOUNT=
			fi
			;;
		nbd)
			# nbd:host:port[:crypt]
			echo "nbd $2:$3 $4"
			modprobe nbd
			nbd-client $2 $3 /dev/nbd0
			! mount_device $4 /dev/nbd0 $BASE_SFS_MOUNT -o ro && BASE_SFS_MOUNT=
			;;
		none)
			echo -e "\r\e[KNot using base sfs."
			BASE_SFS_MOUNT=
			;;		
		*)
			echo "unknown type $1, ignored." 
			BASE_SFS_MOUNT=
			;;
	esac
	BASE_SFS_BIND=$BIND_MOUNT
	
	[ -z "$BASE_SFS_MOUNT" -a "$1" != "none" ] && echo "Cannot load base sfs."
}

### cmdline processing - base2ram
process_base2ram() {
	local mntdev size avail
	# can only do it when basesfs exist and is not bind-mounted
	if [ "$BASE_SFS_MOUNT" -a -z "$BASE_SFS_BIND" ]; then	
		# setup 
		case $base2ram in
			yes)
				echo Copying $BASE_SFS_PATH to RAM ...	
				mntdev=$(awk -v MNT=$BASE_SFS_MOUNT '$2 == MNT {print $1; exit}' /proc/mounts)
				BASE_SFS_PATH=$BASE_SFS_DEFAULT_PATH
				dd if=$mntdev of=$BASE_SFS_PATH bs=1M
				;;
			
			expand)
				size=$(du -sm $BASE_SFS_MOUNT); size=${size%%/*}
				size=$(( ( $size * 110 ) / 100 )) # make it 10% larger to account for ext2 overheads
				avail=$(df -m $BASELINE_MOUNT | awk 'NR==2 {print $2}')
				if [ $avail -gt $size ]; then
					echo "Expanding $BASE_SFS_PATH ($size MB) to RAM ..."
					BASE_SFS_PATH=${BASE_SFS_DEFAULT_PATH%.sfs}.ext2
					dd if=/dev/zero of=$BASE_SFS_PATH bs=1M count=0 seek=$size 1>&2
					
					mke2fs -m 0 -F $BASE_SFS_PATH 1>&2
					mkdir /tmp/newbase; mount -o loop $BASE_SFS_PATH /tmp/newbase
					cp -a $BASE_SFS_MOUNT/* /tmp/newbase
					umount -d /tmp/newbase; rmdir /tmp/newbase
				else 
					echo "base2ram: Not expanding, need $size MB but only $avail MB is available."
					base2ram=no
				fi
				;;
			
		esac
		
		# shared yes/expand cleanup
		case $base2ram in
			yes|expand)
				BASE_SFS_DEVICE=
				umount -d $BASE_SFS_MOUNT
				if [ -e $BASE_SFS_DEV_MOUNT ]; then
					umount -d $BASE_SFS_DEV_MOUNT
					rmdir $BASE_SFS_DEV_MOUNT
				fi
				
				# use RAM copy instead
				! mount_loop $BASE_SFS_PATH $BASE_SFS_MOUNT -o ro && BASE_SFS_MOUNT=						
				;;	
		esac
	fi
}

### cmdline processing - search
process_search() {
	if [ $search ]; then
		SEARCH_DEPTH=$search
#	else # default is now set to 1, this is no longer necessary 
#		grep -q search /proc/cmdline && SEARCH_DEPTH=1
	fi
}

### cmdline processing - savefile - mount helper
# $1 - text to print
# $2 - savefile device
# $3 - savefile path (if +, mount entire partition)
# $4 - if "crypt" - device is encrypted
mount_local_savefile() {
	SAVEFILE_DEVICE=$2
	SAVEFILE_PATH=${3:-$SAVEFILE_DEFAULT_PATH}
	echo "$1$2 $4 on $SAVEFILE_PATH"			
	mkdir -p $SAVE_DEV_MOUNT $BASELINE_MOUNT/$SAVE_DEV_MOUNT
	mount_device $4 $2 $SAVE_DEV_MOUNT
	! mount_loop $SAVE_DEV_MOUNT/$SAVEFILE_PATH $SAVEFILE_MOUNT && SAVEFILE_MOUNT=
}

### cmdline processing - savefile - load session from multisession device
# $1-device $2-mountpoint, $3-savefile path, $4-don't load loading the last n session
load_session() {
	local spath sname depth sname max_session
	spath=${3%/*}; sname=${3##*/}; sname=${MULTI_PREFIX}${sname%.*}
	[ "$spath" = "$3" ] && spath=	# special case - no path specified

	depth=1 && [ $SEARCH_DEPTH -ne 0 ] && depth=$SEARCH_DEPTH	# how deep to look for files
	mkdir -p /mnt/tmp /mnt/dvdtmp
	if mount /dev/$1 /mnt/dvdtmp; then
		# count how many sessions there are, and reduce that by the number of sessions to skip
		max_session=$(find /mnt/dvdtmp/$spath -maxdepth $depth -name "$sname*" | wc -l)
		[ "$4" ] && max_session=$(( $max_session - $4 )) # reduce by number of sessions to skip
		
		while read -r p; do
			if [ "$p" ];then
				echo -n "Loading session ${p#/mnt/dvdtmp/} ... "
				if mount -o loop "$p" /mnt/tmp; then
					cp -af /mnt/tmp/* /mnt/tmp/.[^.]* /mnt/tmp/..?* $2
					umount -d /mnt/tmp
					
					# cleanup deleted files and whiteouts
					# do it here in the loop --- slower, but we also ensure we deleted big files
					# earlier so they don't take up valuable RAM space
					find $2 -name ".wh.*" | while read -r pp; do
						fname="${pp%.wh.*}${pp#*.wh.}"	# without the .wh. part
						if [ -e "$fname" ]; then		# if both file and whiteout exist, then ...
							if [ "$pp" -nt "$fname" ]; then # delete the older one
								rm -rf "$fname" > /dev/null	# delete the file if older
							else
								rm -rf "$pp" > /dev/null	# otherwise delete the whiteout
							fi
						fi
					done
					echo "done."
					
				else
					echo "failed."
				fi
				decz max_session && break
			fi
		done << EOF
		$(find /mnt/dvdtmp/$spath -maxdepth $depth -name "$sname*" | sort)
EOF
	fi
	umount /mnt/dvdtmp	
}

### cmdline processing - savefile
process_savefile() {
	[ "$savefile" = "ask" ] && read -p "savefile=" savefile 2>&1
	savefile=${savefile:-$SAVEFILE_DEFAULT}
	OIFS="$IFS"; IFS=:	
	set -- $savefile
	IFS="$OIFS"
	
	# initial determination whether to use tmpfs & load savefile
	USE_TMPFS=
	case $1 in
		none)
			echo "Not using save file."
			mount -t tmpfs tmpfs $TMPFS_MOUNT
			SAVEFILE_MOUNT=
			MULTI_MOUNT=
			return
			;;
		ram)
			USE_TMPFS=true
			;;
	esac
	
	# try loading the save file
	echo -n "Loading savefile from "
	case $2 in
		local|usb)
			# local:path[:crypt]
			# usb:path[:crypt]
			SAVEFILE_PATH=${3:-$SAVEFILE_DEFAULT_PATH}
			echo "$2, searching for $SAVEFILE_PATH ..."	
			if [ $SEARCH_DEPTH -ne 0 ]; then
				# complex search
				case $2 in
					local) find_and_choose_local_device SAVEFILE_DEVICE "$SAVEFILE_PATH" "" SAVEFILE_PATH ;;
					usb) find_and_choose_local_device "SAVEFILE_DEVICE $SAVEFILE_PATH" usb SAVEFILE_PATH ;;
				esac				
			else
				# simple search
				case $2 in
					local) find_local_device SAVEFILE_DEVICE "$SAVEFILE_PATH" ;;
					usb) find_local_device "SAVEFILE_DEVICE $SAVEFILE_PATH" usb ;;
				esac				
			fi
			if [ "$SAVEFILE_DEVICE" ]; then
				echo -n "Loading savefile from "
				mount_local_savefile "" $SAVEFILE_DEVICE $SAVEFILE_PATH $4
			else
				echo "Cannot find $SAVEFILE_PATH from anywhere."
				SAVEFILE_MOUNT=
			fi
			;;
		device|label|uuid)
			# device:dev:path[:crypt]
			# label:label:path[:crypt]
			# uuid:label:path[:crypt]
			if [ "$2" = "device" ]; then
				mount_local_savefile "" /dev/$3 $4 $5
			else
				mount_local_savefile "$2 $3 -> " $(blkid | grep -im 1 $2=\"$3.\*\" | sed 's/:.*//') $4 $5
			fi			
			;;
		cifs)
			# cifs:user:pass:unc:path
			SAVEFILE_PATH=${6:-$SAVEFILE_DEFAULT_PATH}
			get_user_pass cifs $3 $4
			UNC_PATH=$5
			expand_marker + "$user" UNC_PATH # convert + to usernames
			expand_marker + "$user" SAVEFILE_PATH  # convert + to usernames
			echo "cifs $UNC_PATH on $SAVEFILE_PATH"				
			mkdir -p $SAVE_DEV_MOUNT $BASELINE_MOUNT/$SAVE_DEV_MOUNT
			mount.cifs $UNC_PATH $SAVE_DEV_MOUNT -o sec=ntlmv2,user=$user,pass=$pass,iocharset=utf8
			! mount_loop $SAVE_DEV_MOUNT/$SAVEFILE_PATH $SAVEFILE_MOUNT && SAVEFILE_MOUNT=
			unset user pass
			;;
		nbd)
			# nbd:host:port[:crypt]
			modprobe nbd
			echo "nbd $3:$4 $5"
			nbd-client $3 $4 /dev/nbd1
			! mount_device $5 /dev/nbd1 $SAVEFILE_MOUNT && SAVEFILE_MOUNT=
			;;
		multi)
			# multi:dev:path:n
			MULTI_DEVICE=$MULTI_DEVICE_DEFAULT
			[ $3 ] && MULTI_DEVICE=$3
			echo "multisession $MULTI_DEVICE"
			SAVEFILE_PATH=${4:-$SAVEFILE_DEFAULT_PATH}
			mount -t tmpfs tmpfs $MULTI_MOUNT
			mount -t tmpfs tmpfs $SAVEFILE_MOUNT
			load_session $MULTI_DEVICE $MULTI_MOUNT $SAVEFILE_PATH $5
			;;
		*)
			echo "unsupported type $2 - ignored."
			SAVEFILE_MOUNT=
			;;
	esac
	SAVEFILE_BIND=$BIND_MOUNT
	
	# decide whether to use tmpfs
	[ -z "$SAVEFILE_MOUNT" ] && USE_TMPFS=true # if savefile cannot be loaded, always use tmpfs
	if [ "$USE_TMPFS" ]; then
		echo "Using RAM as temporary storage."
		mount -t tmpfs tmpfs $TMPFS_MOUNT
	else
		TMPFS_MOUNT=
	fi
	
	# if not using multisession, clear it
	[ -z "$MULTI_DEVICE" ] && MULTI_MOUNT=
}


### cmdline processing - net
process_net() {
	[ "$net" = "ask" ] && read -p "net=" net 2>&1
	[ -z "$net" ] && return;
	OIFS="$IFS"; IFS=:
	set -- $net
	IFS="$OIFS"
	echo -n "Configure network "
	# network type
	case $1 in 
		wired)
			# wired:dev:dhcp
			# wired:dev:ip:ip-address:netmask:gateway:dns
			echo -n "on $2 using "
			ifconfig $2 up			
			;;
		wpa|wpa2)
			# wpa2:ssid:pass:dev:dhcp
			# wpa2:ssid:pass:dev:ip:ip-address:netmask:gateway:dns (gateway and dns is optional)
			echo -n "on $2 ($1) using "
			ifconfig $4 up
			wpa_supplicant -B -C/var/run/wpa_supplicant -Dwext -i$4
			wpa_cli ap_scan 1 > /dev/null
			wpa_cli add_net 0 > /dev/null
			wpa_cli set_net 0 ssid \"$2\" > /dev/null
			wpa_cli set_net 0 psk \"$3\" > /dev/null
			wpa_cli select_net 0 > /dev/null
			while [ "$(wpa_cli status | sed -ne '/wpa_state/ {s/wpa_state=//;p}')" != "COMPLETED" ];
			do 
				sleep 0.1; decz WIFI_ENABLE_TIMEOUT && return
			done;
			shift 2
			;;
		*)  echo "- wrong types, ignored."
			;;
	esac
	
	# connection type
	case $3 in
		""|dhcp)
			echo "dhcp"
			udhcpc -i $2 > /dev/null
			CONFIGURED_NET=$2:dhcp
			;;
		ip)
			echo "static ip $4/$5 gw $6 dns $7"
			ifconfig $2 $4 netmask $5
			[ "$6" ] && route add default gw $6
			[ "$7" ] && echo nameserver $7 > /etc/resolv.conf
			CONFIGURED_NET=$2:ip
			;;
		*)
			echo "unknown method $3, ignored."
			;;
	esac
	[ -e /etc/resolv.conf ] && cp /etc/resolv.conf $BASELINE_MOUNT/etc/resolv.conf
	ifconfig lo up	# in any case bring loopback up also	
}

### cmdline processing - keepvar
process_keepvar() {
	if  ! grep -q keepvar /proc/cmdline; then
		for p in cache lock locks run; do
			for pp in $SAVEFILE_MOUNT $MULTI_MOUNT; do
				# delete everything except dirs and symlinks
				find $pp/var/$p $pp/usr/local/var/$p ! \( -type d -o -type l \) -delete
			done
		done
	else
		echo "Keeping /var ... "
	fi	
}

### enable kernel polling
enable_kernel_polling() {
	if ! grep -q block.events_dfl_poll_msecs /proc/cmdline; then # if not explicitly set
		echo $KERNEL_POLL_MSECS > /sys/module/block/parameters/events_dfl_poll_msecs
	fi
}

### cmdline processing - activate LVM support
process_lvm() {
	if grep -q withlvm /proc/cmdline; then
		echo "Activating LVM ..."
		modprobe dm-mod
		modprobe dm-snapshot
		/sbin/lvm vgchange -ay --sysinit
		[ "$DOFSCK" ] && /sbin/lvm vgck
	fi
}

### cmdline processing - activate mdadm support
process_mdadm() {
	if grep -q withmdadm /proc/cmdline; then
		echo "Activating mdadm ..."
		/sbin/mdadm --assemble --scan	
	fi
}


######################   main   ##########################

# mount core filesystems
/bin/mount -t proc proc /proc # mount /proc first so /proc/self/exe works from now
mount -t sysfs sysfs /sys
if ! mount -t devtmpfs devtmpfs /dev 2> /null; then		# /dev/null may not exist yet
	# if no devtmpfs, use tmpfs and use mdev instead
	mount -t tmpfs tmpfs /dev
	mdev -s
fi
[ ! -e /dev/shm ] && mkdir -p /dev/shm
[ ! -e /dev/pts ] && mkdir -p /dev/pts

# debugging and error logging
! grep -q showerr /proc/cmdline && exec 2> /dev/initrd.err
grep -q debuginitrd /proc/cmdline && set -x

# set-up baseline for new root from a copy of ourself
mkdir -p $BASELINE_MOUNT $TMPFS_MOUNT $NEW_ROOT $BASE_SFS_MOUNT $SAVEFILE_MOUNT $MODULE_SFS_MOUNT $MULTI_MOUNT
mount -t tmpfs tmpfs $BASELINE_MOUNT

# copy everything which isn't a mount point
cp -a /bin /etc /home /lib /mnt /opt /root /run /sbin /usr /tmp /var $BASELINE_MOUNT 
mkdir -p $BASELINE_MOUNT/dev $BASELINE_MOUNT/proc $BASELINE_MOUNT/sys
mkdir -p $BASELINE_MOUNT/$BASELINE_MOUNT $BASELINE_MOUNT/$TMPFS_MOUNT $BASELINE_MOUNT/$BASE_SFS_MOUNT \
		 $BASELINE_MOUNT/$SAVEFILE_MOUNT $BASELINE_MOUNT/$MODULE_SFS_MOUNT $BASELINE_MOUNT/$MULTI_MOUNT

# mount kernel modules sfs 
modprobe squashfs
mount -o ro,loop /$MODULE_SFS $MODULE_SFS_MOUNT
MODULE_DIRS=$(ls $MODULE_SFS_MOUNT/lib)
for DIR in $MODULE_DIRS; do
	mkdir -p /lib/$DIR
	mount -o bind $MODULE_SFS_MOUNT/lib/$DIR /lib/$DIR
done

# blacklist before load modules
process_blacklist

# load modules & coldplug
enable_kernel_polling
process_loadmodules

# earlyshell - after load modules so that usb keyboard works
grep -q earlyshell /proc/cmdline && echo 'Starting earlyshell. Type "exit" to continue.' && setsid cttyhack /bin/sh

# waitdev and wait for usb disk
process_waitdev

# configure network - after load modules, before basesfs
process_net

# configure mdadm and lvm if requested, before basesfs and savefile
grep -q dofsck /proc/cmdline && DOFSCK=yes
process_mdadm # note: mdadm before lvm
process_lvm

# load base.sfs (from initrd, local, or cifs - thus requires network)
# BASE_SFS_MOUNT is cleared if base_sfs cannot be loaded
process_basesfs 
process_base2ram

# load savefile (from tmpfs, nbd, cifs, etc)
# TMPFS_MOUNT and SAVEFILE_MOUNT will be cleared if they are not used
process_search
process_savefile
process_keepvar

# stackable filesystem branches for the new root
BRANCHES=$MODULE_SFS_MOUNT=rr:$BASELINE_MOUNT=rr
[ "$BASE_SFS_MOUNT" ] && BRANCHES=$BASE_SFS_MOUNT=rr:$BRANCHES
[ "$MULTI_MOUNT"    ] && BRANCHES=$MULTI_MOUNT=ro+wh:$BRANCHES
[ "$SAVEFILE_MOUNT" ] &&
if [ "$TMPFS_MOUNT" ]; then BRANCHES=$SAVEFILE_MOUNT=ro+wh:$BRANCHES
else BRANCHES=$SAVEFILE_MOUNT:$BRANCHES
fi
[ "$TMPFS_MOUNT"    ] && BRANCHES=$TMPFS_MOUNT:$BRANCHES

# set up stackable filesystem for new root
grep -q noxino /proc/cmdline && NOXINO=noxino
modprobe aufs
if ! mount -t aufs -o br:$BRANCHES,$NOXINO aufs $NEW_ROOT 2>&1; then
	echo "Stackable filesystem: Failed to mount read-write, trying read-only..." 
	if ! mount -t aufs -o ro,noxino,br:$BRANCHES aufs $NEW_ROOT 2>&1; then
		echo "Stackable filesystem: Failed. Starting an emergency shell so you can fix it."
		echo 'Type "exit" to continue, "poweroff" to shutdown, "reboot" to restart system.' 
		echo "Branches: $BRANCHES" 
		export_boot_state $BASELINE_MOUNT/$BOOTSTATE_PATH # export state to help us troubleshoot
		setsid cttyhack sh
	fi	
fi
AUFS_ROOT_ID=$(awk "{ if (\$2 == \"$NEW_ROOT\") { match(\$4,/si=[0-9a-f]*/); print \"si_\" substr(\$4,RSTART+3,RLENGTH-3) }}" /proc/mounts)

# done all processing, export run-time variables before finishing
# at bottom, and then at top if it is writable
export_boot_state $BASELINE_MOUNT/$BOOTSTATE_PATH 
touch $NEW_ROOT/$BOOTSTATE_PATH && export_boot_state $NEW_ROOT/$BOOTSTATE_PATH
[ -e $BASELINE_MOUNT/etc/resolv.conf ] && cp $BASELINE_MOUNT/etc/resolv.conf $NEW_ROOT/etc/resolv.conf

# lateshell
grep -q lateshell /proc/cmdline && echo 'Starting lateshell. Type "exit" to continue.' && setsid cttyhack sh

# move our mounts to the new root
mount -o remount,ro $BASELINE_MOUNT # make the baseline readonly from now
mount -o move $BASELINE_MOUNT $NEW_ROOT/$BASELINE_MOUNT
mount -o move $MODULE_SFS_MOUNT $NEW_ROOT/$MODULE_SFS_MOUNT
[ "$BASE_SFS_MOUNT" ] && mount -o move $BASE_SFS_MOUNT $NEW_ROOT/$BASE_SFS_MOUNT
[ "$MULTI_MOUNT"    ] && mount -o move $MULTI_MOUNT $NEW_ROOT/$MULTI_MOUNT
[ "$SAVEFILE_MOUNT" ] && mount -o move $SAVEFILE_MOUNT $NEW_ROOT/$SAVEFILE_MOUNT
[ "$TMPFS_MOUNT"    ] && mount -o move $TMPFS_MOUNT $NEW_ROOT/$TMPFS_MOUNT
[ -e "$BASE_SFS_DEV_MOUNT" ] && mount -o move $BASE_SFS_DEV_MOUNT $NEW_ROOT/$BASE_SFS_DEV_MOUNT 
[ -e "$SAVE_DEV_MOUNT" ] && mount -o move $SAVE_DEV_MOUNT $NEW_ROOT/$SAVE_DEV_MOUNT 

# remove our modules bind mounts - don't need them anymore
for DIR in $MODULE_DIRS; do 
	umount /lib/$DIR
done

# proc has to be last - /proc/self/exe no longer works after this
mount -o move /sys $NEW_ROOT/sys
mount -o move /dev $NEW_ROOT/dev
mount -o move /proc $NEW_ROOT/proc 

# hotplug will be disabled by base.sfs if required.
# echo > /proc/sys/kernel/hotplug   

# jump new root and execute the real init (busybox init)
echo ==== end of initrd ==== 1>&2
exec /sbin/switch_root $NEW_ROOT $BASELINE_MOUNT/sbin/init
