#!/bin/sh

# geninitrd
#
#	by PLD Linux Team
#
# based on mkinitrd from RedHat Linux
#
# TODO:
# - make udev start before insmods
# - make proper use of USE_UDEV - don't copy rules if USE_UDEV is off no!
#

RCSID='$Id: geninitrd 9995 2008-11-16 19:33:30Z glen $'
R=${RCSID#* * }; VERSION=${R%% *}
PROGRAM=${0##*/}

. /etc/rc.d/init.d/functions
. /etc/geninitrd/functions
. /etc/sysconfig/system

COMPRESS=yes
USERAIDSTART=yes
USEMDADMSTATIC=no
USEINSMODSTATIC=no
USE_SUSPEND=yes
USE_TUXONICE=no
# it should be safe to remove scsi_mod from here, but I'm not sure...
PRESCSIMODS="-scsi_mod unknown -sd_mod"
PREIDEMODS="-ide-core unknown -ide-detect -ide-disk"
PREIDEMODSOLD="-ide-probe -ide-probe-mod -ide-disk"
target=""
kernel=""
force=""
verbose=""
MODULES=""
img_vers=""
fstab=/etc/fstab
insmod=insmod
modext=.o
rootdev_nr=0
# device node for rootfs from fstab
rootdev=""
# requires bootsplash package to operate
BOOT_SPLASH=no
# requires splashutils package to operate
FB_SPLASH=no
# requires splashutils package to operate
FB_CON_DECOR=no

# internal variables
# is /dev on tmpfs
dev_mounted=no
# is /proc mounted
proc_mounted=no
# is /sys mounted
sys_mounted=no
# is /tmp mounted on tmpfs
tmp_mounted=no

# are /dev nodes already created from /proc/devices info?
proc_partitions=no

# LVM devices that should not be included in vgscan on initrd
lvm_ignore_devices=''

# whether v86d should be installed
need_v86d=0

# LVM volumes that are needed to activate
# VG for root dev
ROOTVG=""
# VG for suspend resume dev
SUSPENDVG=""

# resume device
resume_dev=""

# if we should init NFS at boot
have_nfs=no
# if we should init LVM at boot
have_lvm=no
# if we should init md (softraid) at boot
have_md=no
# if we should init dmraid at boot
have_dmraid=no

usage() {
	uname_r=$(uname -r)
	echo "usage: $PROGRAM [--version] [-v] [-f] [--ifneeded] [--preload <module>]"
	echo "       [--with=<module>] [--image-version] [--fstab=<fstab>] [--nocompress]"
	echo "       [--initrdfs=rom|initramfs|ext2|cram] [--modules-conf=<modules.conf>]"
	echo "       [--with-raidstart] [--without-raidstart] [--with-insmod-static]"
	echo "       [--with-bootsplash] [--without-bootsplash]"
	echo "       [--with-fbsplash] [--without-fbsplash]"
	echo "       [--with-fbcondecor] [--without-fbcondecor]"
	echo "       [--lvmtoolsversion=1|2] [--with-udev] [--without-udev]"
	echo "       [--with-suspend] [--without-suspend]"
	echo "       [--with-tuxonice] [--without-tuxonice]"
	echo "       [--without-dmraid] [--without-multipath]"
	echo "       [--without-blkid] [--without-luks]"
	echo "       <initrd-image> <kernel-version>"
	echo ""
	echo "example:"
	echo "  $PROGRAM -f --initrdfs=rom /boot/initrd-$uname_r.gz $uname_r"
	exit 1
}

msg() {
	echo "$PROGRAM: $*"
}

warn() {
	msg "WARNING: $*" >&2
}

debug() {
	[ -n "$verbose" ] && msg "$*" >&2
}

# aborts program abnormally
die() {
	local rc=${2:-1}
	msg "ERROR: $1" 2>&2
	exit $rc
}

# append text to /linuxrc
# takes STDIN as input
add_linuxrc() {
	cat >> "$RCFILE"
}

# generate code to mount /dev on tmpfs and create initial nodes
# can be called multiple times. /dev is cleaned up (umounted) automatically at
# the end of script.
mount_dev() {
    if [ "$INITRDFS" = "initramfs" ]; then
		# initramfs is read-write filesystem, no need for tmpfs
		return
	fi

	# we already generated tmpfs code; return
	if is_yes "$dev_mounted"; then
		return
	fi

	dev_mounted=yes

	busybox_applet mount mknod mkdir
	add_linuxrc <<-EOF
		: 'Creating /dev'
		mount -o mode=0755 -t tmpfs none /dev
		mknod /dev/console c 5 1
		mknod /dev/null c 1 3
		mknod /dev/zero c 1 5
		mkdir /dev/pts
		mkdir /dev/shm
	EOF
}

# generate code to mount /proc on initrd
# can be called multiple times
mount_proc() {
	if is_yes "$proc_mounted"; then
		return
	fi

	proc_mounted=yes
    if [ "$INITRDFS" = "initramfs" ]; then
		# /proc is mounted with initramfs 2.6.22.14 kernel
		# XXX: remove when it is clear why proc was already mounted
		echo "[ -f /proc/cmdline ] || mount -t proc none /proc" | add_linuxrc
	else
		echo "mount -t proc none /proc" | add_linuxrc
	fi
}

# generate code to mount /sys on initrd
# can be called multiple times
mount_sys() {
	if is_yes "$sys_mounted"; then
		return
	fi

	sys_mounted=yes
	echo "mount -t sysfs none /sys" | add_linuxrc
}

# generate code to mount /tmp on initrd
# can be called multiple times
mount_tmp() {
    if [ "$INITRDFS" = "initramfs" ]; then
		# initramfs is read-write filesystem, no need for tmpfs
		return
	fi

	if is_yes "$tmp_mounted"; then
		return
	fi

	tmp_mounted=yes
	echo "mount -t tmpfs none /tmp" | add_linuxrc
}

# unmount all mountpoints mounted by geninitrd
umount_all() {

	add_linuxrc <<-'EOF'
	: Last shell before umounting all and giving control over to real init.
	debugshell
	EOF

	if is_yes "$dev_mounted"; then
		echo 'umount /dev' | add_linuxrc
		dev_mounted=no
	fi
	if is_yes "$proc_mounted"; then
		echo 'umount /proc' | add_linuxrc
		proc_mounted=no
	fi
	if is_yes "$sys_mounted"; then
		echo 'umount /sys' | add_linuxrc
		sys_mounted=no
	fi
	if is_yes "$tmp_mounted"; then
		echo 'umount /tmp' | add_linuxrc
		tmp_mounted=no
	fi
}


# Checks if busybox has support for APPLET(s)
# Exits from geninitrd if the support is not present.
#
# NB! XXX do not output to STDOUT, it will appear in initrd images in some cases!
busybox_applet() {
	local err=0

	if [ -z "$busybox_functions" ]; then
		local tmp=$(/bin/initrd-busybox 2>&1)

		# BusyBox v1.1.3 says applet not found if it's not called 'busybox'.
		if [[ "$tmp" = *applet\ not\ found* ]]; then
			local t=$(mktemp -d)
			ln -s /bin/initrd-busybox $t/busybox
			local tmp=$($t/busybox 2>&1)
			rm -rf $t
		fi

		busybox_functions=$(echo "$tmp" | \
			sed -ne '/Currently defined functions:/,$p' | \
		   	xargs | sed -e 's,.*Currently defined functions: ,,'
		)
	fi
	for applet in $*; do
		local have
		# try cache
		eval have='$'busybox_have_$applet
		if [ -z "$have" ]; then
			have=$(echo "$busybox_functions" | egrep -c "( |^)$applet(,|$)")
			if [ "$have" = 0 ]; then
				warn "This setup requires busybox-initrd compiled with applet '$applet' support"
				err=1
			fi
			eval busybox_have_$applet=$have
		fi
	done
	if [ $err = 1 ]; then
		die "Aborted"
	fi
}

# Finds module dependencies
#
# @param	$module
# @param	$mode	[silent]
#
# Outputs each dependant module full path including the module itself.
find_depmod() {
	local module="$1"
	local mode="$2"

	# backwards compatible, process $mode
	local modfile=$(modinfo -k $kernel -n $module 2>/dev/null)
	if [ ! -f "$modfile" ]; then
		# module not found. try aliases!
		local alias aliases=$(modprobe_conf | awk -vm="$module" '$1 == "alias" && $2 == m { print $3 }')
		if [ "$aliases" ]; then
			for alias in $aliases; do
				debug "Try $alias alias for $module"
				find_depmod "$alias"
			done
			return
		fi

		if [ "$mode" != "silent" ]; then
			warn "$module: module not found for $kernel kernel"
		fi
		if ! is_no "$EXIT_IF_MISSING"; then
			exit 1
		else
			warn "If $module isn't compiled in kernel then this initrd may not start your system."
		fi
	fi

	# This works when user has module-init-tools installed even on 2.4 kernels
	modprobe --set-version $kernel --show-depends $module | \
	while read insmod modpath options; do
		echo $modpath
	done
}

find_firmware() {
	local module="$1"
	echo -n $(modinfo -k $kernel -F firmware $module 2>/dev/null | xargs)
}

findmodule() {
	local skiperrors=""
	local modName=$1

	if [ ${modName#-} != $modName ]; then
		skiperrors=1
		modName=${modName#-}
	fi

	# what's that?
	if [ "$modName" = "pluto" ]; then
		findmodule fc4
		findmodule soc
	fi
	if [ "$modName" = "fcal" ]; then
		findmodule fc4
		findmodule socal
	fi

	local mod depmod
	if [ "$skiperrors" = 1 ]; then
		depmod=$(find_depmod $modName silent)
	else
		depmod=$(find_depmod $modName)
		if [ $? != 0 ]; then
			exit 1
		fi
	fi

	for mod in $depmod; do
		mod=${mod#/lib/modules/$kernel/}

		# add each module only once
		local m have=0
		for m in $MODULES; do
			[ $m = $mod ] && have=1
		done
		if [ $have = 0 ]; then
			MODULES="$MODULES $mod" 

			# if we are adding uvesafb, we need v86d as well
			if [ "$modName" = "uvesafb" ]; then
				need_v86d=yes
			fi				
		fi
	done
}

# install a file to temporary mount image.
# it will operate recursively (copying directories)
# and will symlink destinations if source is symlink.
inst() {
	if [ $# -lt 2 ]; then
		die 'Usage: inst <file> [<file>] <destination>'
	fi

	local src i=0 c=$(($# - 1))
	while [ $i -lt $c ]; do
		src="$src $1"
		i=$((i + 1))
		shift
	done
	local dest=$1
	set -- $src
	parentDir=$(dirname $DESTDIR$dest)
	[ ! -d "$parentDir" ] && (debug "+ mkdir -p $parentDir"; mkdir -p $parentDir)
	debug "+ cp $* $DESTDIR$dest"
	cp -HR "$@" "$DESTDIR$dest"
}

inst_d() {
	if [ $# = 0 ]; then
		die 'Usage: inst_d <destination> <destination>'
	fi
	for dir in "$@"; do
		install -d "$DESTDIR$dir"
	done
}

# install executable and it's shared libraries
inst_exec() {
	if [ $# -lt 2 ]; then
		die 'Usage: inst_exec <file>[, <file>] <destination>'
	fi
	local src i=0 c=$(($# - 1))
	while [ $i -lt $c ]; do
		src="$src $1"
		i=$((i + 1))
		shift
	done
	local dest=$1
	set -- $src

	inst "$@" $dest

	local lib libs=$(ldd "$@" | awk '/statically|linux-(gate|vdso)\.so/{next} NF == 2 {print $1} /=/{print $3}' | sort -u)
	for lib in $libs; do
		if [ ! -f "$DESTDIR/$_lib/${lib##*/}" ]; then
			inst_d /$_lib
			inst_exec $lib /$_lib
		fi
	done

	# hack for uclibc linked binaries requiring this fixed path
	# XXX: shouldn't rpath be used here instead so th
	if [ -f $DESTDIR/$_lib/libc.so.0 ]; then
		local lib=$DESTDIR/$_lib/libc.so.0
		lib=$(ldd "$lib" | awk '/statically|linux-(gate|vdso)\.so/{next} NF == 2 {print $1} /=/{print $3}' | sort -u)
		local libdir=$(cd $(dirname "$lib"); pwd)
		if [ ! -e $DESTDIR$libdir ]; then
			libdir=$(dirname "$libdir")
			inst_d $libdir
			debug "+ ln -s /$_lib $DESTDIR$libdir"
			ln -s /$_lib $DESTDIR$libdir
		fi
	fi
}

# output modules.conf / modprobe.conf
modprobe_conf() {
	echo "$modprobe_conf_cache"
}

#
# defaults to modprobe -c if not told otherwise, this means include statements
# work from there.
cache_modprobe_conf() {
	if [ "$pack_version" -lt "002005" ]; then
		modulefile=/etc/modules.conf
		if [ ! -f "$modulefile" -a -f /etc/conf.modules ]; then
			modulefile=/etc/conf.modules
		fi
	fi

	if [ -n "$modulefile" ]; then
		debug "Using $modulefile for modules config"
		modprobe_conf_cache=$(cat $modulefile)
	else
		debug "Using modprobe -c to get modules config"
		modprobe_conf_cache=$(modprobe -c --set-version $kernel)
	fi
}

find_modules_md() {
	local found raidlevel

	if [ -f /etc/mdadm.conf ]; then
		debug "Finding RAID details using mdadm for rootdev=$1"
		eval `/sbin/mdadm -v --examine --scan --config=/etc/mdadm.conf | awk -v rootdev="$1" '
		BEGIN {
			found = "no";
			dev_list = "";
			raidlevel = ""
			rootdev_devfs = rootdev;
			if (rootdev ~ /\/dev\/md\/[0-9]/) {
				gsub(/\/dev\/md\//,"/dev/md",rootdev_devfs);
			}
		}

		/^ARRAY/ {
			if (($2 == rootdev) || ($2 == rootdev_devfs)) {
				raidlevel=$3;
				gsub(/level=/,NUL,raidlevel);
				if (raidlevel ~ /^raid([0-6]|10)/) {
					gsub(/raid/,NUL,raidlevel);
				};
				found="yes";
				getline x;
				if (x ~ /devices=/) {
					dev_list = x;
					gsub(".*devices=", NUL, dev_list);
					gsub(",", " ", dev_list);
				}
			}
		}

		END {
			print "have_md=" found;
			print "raidlevel=" raidlevel;
			print "dev_list=\"" dev_list "\"";
		}'`
	fi

	if [ "$have_md" != "yes" -a -f /etc/raidtab ]; then
		die "raidtools are not longer supported. Please migrate to mdadm setup!"
	fi

	if is_yes "$have_md"; then
		case "$raidlevel" in
		[01]|10)
			findmodule "raid$raidlevel"
			;;
		[456])
			findmodule "-raid$raidlevel"
			findmodule "-raid456"
			;;
		linear)
			findmodule "linear"
			;;
		*)
			warn "raid level $number (in mdadm config) not recognized"
			;;
		esac
	else
		die "RAID devices not found for \"$1\", check your configuration!"
	fi

	rootdev_nr=$(( $rootdev_nr + 1 ))
	eval "rootdev${rootdev_nr}=\"$1\""
	eval "dev_list${rootdev_nr}=\"${dev_list}\""

	for device in $dev_list; do
		find_modules_for_devpath $device
	done
}

find_modules_scsi() {
	local n
	for n in $PRESCSIMODS; do
		if [ "X$n" = "Xunknown" ]; then
			debug "Finding SCSI modules using scsi_hostadapter"
			local mod scsimodules=$(modprobe_conf | awk '/scsi_hostadapter/ && ! /^[\t ]*#/ { print $3; }')
			for mod in $scsimodules; do
				# for now allow scsi modules to come from anywhere. There are some
				# RAID controllers with drivers in block
				findmodule "$mod"
			done
		else
			findmodule "$n"
		fi
	done
	findmodule "-scsi_wait_scan"
}

find_modules_ide() {
	local devpath=$1
	# remove partition, if any
	local disk=${devpath%[0-9]*}
	# set blockdev for rootfs (hda, sdc, ...)
	local rootblkdev=${disk#/dev/}

	local n
	if [ "$pack_version_long" -lt "002004021" ]; then
		debug "Finding IDE modules for kernels <= 2.4.20"
		for n in $PREIDEMODSOLD; do
			findmodule "$n"
		done
	else
		local tryauto=1
		for n in $PREIDEMODS; do
			if [ "X$n" = "Xunknown" ]; then
				debug "Finding IDE modules using ide_hostadapter"
				local mod idemodules=$(modprobe_conf | awk '/ide_hostadapter/ && ! /^[\t ]*#/ { print $3; }')
				for mod in $idemodules; do
					tryauto=0;
					findmodule "$mod"
				done

				if [ "$tryauto" -eq 1 ]; then
					# If tryauto {{{
					if [ -r /usr/share/pci-database/ide.pci -a -r /proc/bus/pci/devices ]; then
						debug "Finding IDE modules using PCI ID database"
						# Finding IDE modules using PCI ID database {{{
						if is_yes "${ide_only_root}"; then
							if [ -f /sys/block/${rootblkdev}/device/../../vendor -a -f /sys/block/${rootblkdev}/device/../../device ]; then
								vendorid="$(awk ' { gsub(/0x/,NUL); print $0 } ' /sys/block/${rootblkdev}/device/../../vendor)"
								deviceid="$(awk ' { gsub(/0x/,NUL); print $0 } ' /sys/block/${rootblkdev}/device/../../device)"
								searchpciid="${vendorid}${deviceid}"
							elif [ -f /proc/ide/${rootblkdev}/../config ]; then
								searchpciid="$(awk ' /pci bus/ { print $7$9 } ' /proc/ide/${rootblkdev}/../config)"
							fi
						fi

						if [ -z "${searchpciid}" ]; then
							searchpciid="$(awk ' { print $2 } ' /proc/bus/pci/devices)"
						fi

						idemodules=""

						for nb in $searchpciid; do
							eval `awk -v pciid="$nb" '{
								gsub("\t"," ");
								gsub("  +", " ");
								gsub("^ ","");
								if (/^[\t ]*#/)
									next;
								compmod = $1 "";	# make sure comparison type will be string
											# cause pci IDs are hexadecimal numeric
								if (compmod == pciid) {
									module=$2;
							#		min_kernel=$3;  # now in ide.pci $3,$4 = vendor and device name
							#		max_kernel=$4;  #
									exit 0;
								}
							}

							END {
								print "module=" module "\nmin_kernel=" min_kernel "\nmax_kernel=\"" max_kernel "\"\n";
							}' /usr/share/pci-database/ide.pci`
							[ -n "$module" ] && idemodules="$idemodules $module"
						done
						if is_yes "$(awk ' /ide=reverse/ { print "yes" } ' /proc/cmdline)"; then
							new_idemodules=""
							for nc in idemodules; do
								new_idemodules="$nc $new_idemodules"
							done
							idemodules="${new_idemodules}"
						fi

						if [ -z "$idemodules" ]; then
							warn "rootfs on IDE device but no related modules found, loading ide-generic."
							idemodules="ide-generic"
						fi

						# }}}
						for nd in $idemodules; do
							findmodule "-$nd"
						done
					# }}}
					# else tryauto {{{
					else
						[ -r /usr/share/pci-database/ide.pci ] || warn "/usr/share/pci-database/ide.pci missing."
						[ -r /proc/bus/pci/devices ] || warn "/proc/bus/pci/devices missing."
						warn "Automatic IDE modules finding not available."
					fi
					# }}}
				fi
			else
				findmodule "$n"
			fi
		done
	fi
}

# return true if node is lvm node
is_lvm_node() {
	local node="$1"
	if [ ! -e "$node" ]; then
		warn "check_lvm(): node $node doesn't exist!"
		return 1
	fi

	# block-major-58 is lvm1
	ls -lL "$node" 2> /dev/null | awk '{if (/^b/) { if ($5 == "58,") { exit 0; } else { exit 1; } } else { exit 1; }}'
	rc=$?

	if [ $rc = 0 ]; then
		debug "LVM check: $node is LVM v1 node"
		# is lvm1
		return 0
	fi

	if [ ! -x /sbin/lvm ]; then
		debug "LVM check: no lvm2 tools present to check"
		return 0
	fi

	/sbin/lvm lvdisplay "$node" > /dev/null 2>&1
	rc=$?
	if [ $rc -gt 127 ]; then
		# lvdisplay terminated by signal! most likely it segfaulted.
		die "Unexpected exit from 'lvdisplay $node': $rc - are your lvm tools broken?"
	fi

	if [ $rc = 0 ]; then
		debug "LVM check: $node is LVM v2 node"
	else
		debug "LVM check: $node is not any LVM node"
	fi
	return $rc
}


# return dependencies MAJOR:MINOR [MAJOR:MINOR] for DM_NAME
# TODO: patch `dmsetup export`
dm_deps() {
	local dm_name="$1"
	dmsetup deps $dm_name | sed -e 's/, /:/g;s/^.\+ dependencies[^:]: //;s/[()]//g;'
}

# export info from dmsetup
# param can be:
# - MAJOR:MINOR
# - /dev/dm-MINOR
# - /dev/mapper/DM_NAME
dm_export() {
	local arg="$1"

	case "$arg" in
	*:*)
		local maj=${arg%:*} min=${arg#*:}
		dmsetup -j $maj -m $min export
		;;
	/dev/dm-*)
		local min=${arg#*dm-}
		local maj=$(awk '$2 == "device-mapper" {print $1}' /proc/devices)
		dm_export $maj:$min
		;;
	/dev/mapper/*)
		local dm_name=${arg#/dev/mapper/}
		dmsetup export $dm_name
		;;
	*)
		die "dm_export: unexpected $arg"
		;;
	esac
}


# find dmraid modules for $devpath
# returns false if $devpath is not on dmraid
find_modules_dmraid() {
	local devpath="$1"

	# get blockdev itself (without partition)
	# /dev/mapper/sil_ahbgadcbchfc3 -> /dev/mapper/sil_ahbgadcbchfc
	local blockdev=${devpath%%[0-9]*}
	local raidname=${blockdev#/dev/mapper/}
	local found=0

	local dev phydevs=$(dmraid -r -cdevpath,raidname | awk -F, -vv="$raidname" '{if ($2 == v) print $1}')
	for dev in $phydevs; do
		find_modules_for_devpath $dev
		lvm_ignore_devices="$lvm_ignore_devices $dev"
		found=1
	done

	if [ $found = 0 ]; then
		return 1
	fi

	findmodule "dm-mirror"
	have_dmraid=yes
	return 0
}

# find modules for $devpath
find_modules_for_devpath() {
	local devpath="$1"
	if [ -z "$devpath" ]; then
		die "No argument passed to find_modules_for_devpath() - is your /etc/fstab correct?"
	fi

	# /dev/dm-3 -> /dev/mapper/sil_ahbgadcbchfc3
	case "$devpath" in
	/dev/dm-*)
		devpath=$(dm_longname "$devpath")
		;;
	esac
	debug "Finding modules for device path $devpath"

	if is_luks "$devpath"; then
		find_modules_luks "$devpath"
		return
	fi

	if is_yes "`echo "$devpath" | awk '/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:|\/dev\/nfs)/ { print "yes"; }'`"; then
		if [ ! -x /usr/bin/pcidev -a -z "$NFS_ETH_MODULES" ]; then
			die "root on NFS but /usr/bin/pcidev not found. Please install correct pci-database package and rerun $PROGRAM."
		fi
		if [ ! -f /proc/bus/pci/devices ]; then
			warn "Remember to add network card modules in /etc/sysconfig/geninitrd, example:"
			warn "BASICMODULES=\"e1000 ne2k-pci mii 8139too 3c59x\""
		else
			local m
			[ -z "$NFS_ETH_MODULES" ] && NFS_ETH_MODULES=$(/usr/bin/pcidev /m net | xargs)
			warn "NOTE: Network card(s) module(s) $NFS_ETH_MODULES is for this machine"
			for m in $NFS_ETH_MODULES; do
				findmodule "$m"
			done
		fi
		findmodule "-ipv4"
		findmodule "nfs"
		have_nfs=yes
		warn "Remember to use \`root=/dev/ram0 init=/linuxrc' when starting kernel"
		warn "or you will have problems like init(xx) being child process of swapper(1)."
		return
	fi

	if [[ "$devpath" == /dev/md* ]]; then
		find_modules_md "$devpath"
		return
	fi

	if is_multipath "$devpath"; then
		if find_modules_multipath "$devpath"; then
			return
		fi
		# fallback
	fi

	if is_yes "$USE_DMRAID" && is_yes "$(echo "$devpath" | awk '/^\/dev\/mapper\/(sil|hpt37x|hpt45x|isw|lsi|nvidia|pdc|sil|via|dos)_/ { print "yes"; }')"; then
		if find_modules_dmraid "$devpath"; then
			return
		fi
		# fallback
	fi

	if is_yes "$(echo "$devpath" | awk '/^\/dev\/(sd|scsi)/ { print "yes"; }')"; then
		find_modules_scsi
		return
	fi

	if is_yes "$(echo "$devpath" | awk '/^\/dev\/(hd|ide)/ { print "yes"; }')"; then
		find_modules_ide "$devpath"
		return
	fi

	if [[ "$devpath" == /dev/rd/* ]]; then
		findmodule "DAC960"
		rootdev_add="/dev/rd/"
		return
	fi

	if [[ "$devpath" == /dev/ida/* ]]; then
		findmodule "cpqarray"
		rootdev_add="/dev/ida/"
		return
	fi

	if [[ "$devpath" == /dev/cciss/* ]]; then
		findmodule "cciss"
		rootdev_add="/dev/cciss/"
		return
	fi

	if [[ "$devpath" == /dev/ataraid/* ]]; then
		find_modules_ide
		findmodule "ataraid"
		ataraidmodules=$(modprobe_conf | awk '/ataraid_hostadapter/ && ! /^[\t ]*#/ { print $3; }')
		if [ -n "$ataraidmodules" ]; then
			# FIXME: think about modules compiled in kernel
			die "ataraid_hostadapter alias not defined in modprobe.conf! Please set it and run $PROGRAM again."
		fi
		for n in $ataraidmodules; do
			findmodule "$n"
		done
		rootdev_add="/dev/ataraid/"
		return
	fi

	# check to see if we need to set up a loopback filesystem
	if [[ "$devpath" == /dev/loop*  ]]; then
		die "Sorry, root on loop device isn't supported."
		# TODO: rewrite for bsp and make nfs ready
		if [ ! -x /sbin/losetup ]; then
			die "losetup is missing"
		fi
		key="^# $(echo $devpath | awk -F/ '{print($3);}' | tr '[a-z]' '[A-Z]'):"
		if ! is_yes "`awk '/'$key'/ { print( "yes"); }' $fstab`"; then
			die "The root filesystem is on a $devpath, but there is no magic entry in $fstab for this device. Consult the $PROGRAM man page for more information"
		fi

		line="`awk '/'$key'/ { print $0; }' $fstab`"
		loopDev="$(echo $line | awk '{print $3}')"
		loopFs="$(echo $line | awk '{print $4}')"
		loopFile="$(echo $line | awk '{print $5}')"

		BASICMODULES="$BASICMODULES -loop"
		findmodule "-$loopFs"
		BASICMODULES="$BASICMODULES -${loopFs}"
		return
	fi

	if is_lvm_node "$devpath"; then
		node="$devpath"

		if [ ! -f /sbin/initrd-lvm -o ! -x /sbin/lvdisplay -o ! -x /sbin/pvdisplay ]; then
			die "root on LVM but /sbin/initrd-lvm, /sbin/lvdisplay and /sbin/pvdisplay not found. Please install lvm(2) and lvm(2)-initrd package and rerun $PROGRAM."
		fi
		if [ -z "$LVMTOOLSVERSION" ]; then
			LVMTOOLSVERSION=$(/sbin/initrd-lvm vgchange --version 2>/dev/null|head -n 1|awk '{gsub("vgchange: Logical Volume Manager ",NIL); gsub("LVM version:     ",NIL); gsub(/\..*/,NIL); print $1}')
			if [ -z "$LVMTOOLSVERSION" ]; then
				die "Can't determine LVM tools version. Please set LVMTOOLSVERSION and rerun $PROGRAM."
			fi
		fi

		local vg=$(/sbin/lvdisplay -c "$node" 2> /dev/null | awk -F":" ' { print $2 } ')
		VGVOLUMES=$(echo $VGVOLUMES $vg | tr ' ' '\n' | sort -u)
		local pv=$(/sbin/pvdisplay -c 2>/dev/null | awk -F":" -v vg="$vg" ' BEGIN { devices="" } { if ($2 == vg) { devices = devices " " $1 } } END { print devices } ')
		PVDEVICES=$(echo $PVDEVICES $pv | tr ' ' '\n' | sort -u)

		if [ -n "$PVDEVICES" ]; then
			for device in $PVDEVICES; do
				find_modules_for_devpath $device
			done
		else
			die "I wasn't able to find PV (via lvdisplay and pvdisplay). You can try to set PVDEVICES in /etc/sysconfig/geninitrd."
		fi
		if [ "$LVMTOOLSVERSION" = "2" ]; then
			findmodule "-dm-mod"
		elif [ "$LVMTOOLSVERSION" = "1" ]; then
			findmodule "-lvm"
			findmodule "-lvm-mod"
		else
			die "LVM version $LVMTOOLSVERSION is not supported yet."
		fi
		debug "LVM v$LVMTOOLSVERSION enabled"
		have_lvm=yes
		return
	fi
}

firmware_install_module() {
	local module="$1"
	local firmware_files="$2"

	debug "Adding Firmwares ($firmware_files) to initrd for module $module"
	# firmware not yet installed
	if [ ! -f "$DESTDIR/lib/firmware/firmware.sh" ]; then
		inst_d /lib/firmware
cat << 'EOF' >> "$DESTDIR/lib/firmware/firmware.sh"
#!/bin/sh -e
echo 1 > /sys$DEVPATH/loading
cat "/lib/firmware/$FIRMWARE" > /sys$DEVPATH/data
echo 0 > /sys$DEVPATH/loading
exit 0
EOF
	 	chmod 755 "$DESTDIR/lib/firmware/firmware.sh"
	fi

	for firmware in $firmware_files; do
		if [ ! -f "/lib/firmware/$firmware" ]; then
				die "firmware file /lib/firmware/$firmware doesn't exist."
		else
			FIRMWAREDIR=${firmware%/*}
			[ "$FIRMWAREDIR" != "$firmware" ] && inst_d /lib/firmware/$FIRMWAREDIR
			inst /lib/firmware/$firmware /lib/firmware/$firmware
		fi
	done

	mount_sys
	echo "echo -n "/lib/firmware/firmware.sh" > /proc/sys/kernel/hotplug" | add_linuxrc
}

modules_install() {
	local modules="$1"
	local mod

	for mod in $modules; do
		MODULEDIR=${mod%/*}
		inst_d "/lib/modules/$kernel/$MODULEDIR"
		cp -a "/lib/modules/$kernel/$mod" "$DESTDIR/lib/modules/$kernel/$mod"
		gunzip "$DESTDIR/lib/modules/$kernel/$mod" 2> /dev/null
	done
}

modules_add_linuxrc() {
	local mod modpath

	for mod in "$@"; do
		# module path without optional compression
		modpath=${mod%.gz}

		# name of the module
		module=${modpath##*/}
		module=${module%$modext}

		options=$(modprobe_conf | awk -vmodule="$module" '{ if ($1 == "options" && $2 == module) { for(i=3;i<=NF;i++) printf("%s ",$i); }}' | xargs)

		generic_module=$(echo $module | tr - _)
		sleep_var=$(eval echo \$MODULE_${generic_module}_USLEEP)
		firmware_var=$(eval echo \$MODULE_${generic_module}_FIRMWARE)

		if [ -n "$verbose" ]; then
			s=""
			if [ "$options" ]; then
				s="$s with options [$options]"
			fi
			if [ "$sleep_var" ]; then
				s="$s and $sleep_var usleep"
			fi
			debug "Loading module [$module]$s"
		fi

		if [ -n "$firmware_var" ]; then
			firmware_install_module "$module" "$firmware_var"
		else
			firmware_files=$(find_firmware "$module")
			if [ -n "$firmware_files" ]; then
				for file in $firmware_files; do
					firmware_install_module "$module" "$file"
				done
			fi
		fi

		echo "$insmod /lib/modules/$kernel/$modpath $options" | add_linuxrc
		if [ -n "$sleep_var" ]; then
			echo "usleep $sleep_var" | add_linuxrc
		fi
	done
}

find_modules_suspend() {
	if [ ! -x /usr/${_lib}/suspend/resume -a ! -x /usr/sbin/resume ]; then
		die "/usr/${_lib}/suspend/resume is missing!"
	fi
	resume_dev="$(awk '/^resume device =/ { print $4 } ' /etc/suspend.conf)"

	local vgvolumes=$VGVOLUMES
	find_modules_for_devpath $resume_dev

	if [ "$VGVOLUMES" != "$vgvolumes" ]; then
		# save our VG to be able to differenciate between rootfs VG
		SUSPENDVG=$VGVOLUMES
		VGVOLUMES=$vgvolumes
		debug "suspend device is on LVM"
	fi
}

initrd_gen_suspend() {
	mknod $DESTDIR/dev/snapshot c 10 231
	mkdir -p $DESTDIR${resume_dev%/*}
	inst $resume_dev $resume_dev

	inst /etc/suspend.conf /etc/suspend.conf
	if [ -x /usr/${_lib}/suspend/resume ]; then
		inst_exec /usr/${_lib}/suspend/resume /bin/resume
	else
		inst_exec /usr/sbin/resume /bin/resume
	fi

	add_linuxrc <<-'EOF'
	resume=no
	for arg in $CMDLINE; do
		if [ "${arg##resume=}" != "${arg}" ]; then
			resume=yes
		fi
	done
	if [ "$resume" = "yes" ]; then
		resume
	fi
	EOF
}

initrd_gen_tuxonice() {
	mount_sys
	add_linuxrc <<-'EOF'
		resume=no
		for arg in $CMDLINE; do
			if [ "${arg##resume=}" != "${arg}" -o "${arg##resume2=}" != "${arg}" ]; then
				resume=yes
			fi
		done
		if [ "$resume" = "yes" ]; then
			[ -e /proc/suspend2/do_resume ] && echo > /proc/suspend2/do_resume
			[ -e /sys/power/suspend2/do_resume ] && echo > /sys/power/suspend2/do_resume
			[ -e /sys/power/tuxonice/do_resume ] && echo > /sys/power/tuxonice/do_resume
		fi
	EOF
}

initrd_gen_v86d() {
	debug "initrd_gen_v86d"
	mknod $DESTDIR/dev/mem c 1 1
	mknod $DESTDIR/dev/tty1 c 4 1
	inst_d /sbin
	inst_exec /sbin/v86d /sbin
}

initrd_gen_udev() {
	debug "Setting up udev..."
	inst_d /sbin /etc/udev

	if [ ! -x /sbin/initrd-udevd ]; then
		die "/sbin/initrd-udevd not present"
	fi

	inst_exec /sbin/initrd-udevd /sbin/udevd
	inst /etc/udev/udev.conf /etc/udev/udev.conf

	mount_dev
	mount_sys
	add_linuxrc <<-'EOF'
		: 'Starting udev'
		/sbin/udevd --daemon
	EOF

	inst_exec /sbin/initrd-udevadm /sbin/udevadm
	add_linuxrc <<-'EOF'
		/sbin/udevadm trigger
		/sbin/udevadm settle
	EOF

}

initrd_gen_stop_udevd() {
	busybox_applet killall
	add_linuxrc	<<-'EOF'
		killall udevd
	EOF
}


initrd_gen_dmraid() {
	if [ ! -x /sbin/dmraid-initrd ]; then
		die "/sbin/dmraid-initrd is missing!"
	fi

	inst_d /sbin
	inst_exec /sbin/dmraid-initrd /sbin/dmraid

	mount_dev
	mount_sys
	initrd_gen_devices
	add_linuxrc <<-EOF
		# 2 secs was enough for my system to initialize. but really this is udev issue?
		usleep 2000000
		: 'Activating Device-Mapper RAID(s)'
		/sbin/dmraid -ay -i

		debugshell
	EOF
}

initrd_gen_bootsplash() {
	local target="$1"

	debug "Generating bootsplash"

	if [ ! -x /bin/splash.bin ]; then
		warn "Failed to execute /bin/splash.bin. Is bootsplash package installed?"
		return
	fi

	if [ -r /etc/sysconfig/bootsplash ]; then
		. /etc/sysconfig/bootsplash
	fi

	if [ -z "$THEME" ]; then
		warn "Please configure your /etc/sysconfig/bootsplash first."
		warn "Generating bootsplashes skipped."
		return
	fi

	if [ -z "$BOOT_SPLASH_RESOLUTIONS" ]; then
		warn "No BOOT_SPLASH_RESOLUTIONS specified in /etc/sysconfig/bootsplash."
		warn "Not adding bootsplash to initrd."
	fi

	for res in $BOOT_SPLASH_RESOLUTIONS; do
		if [ -f "/etc/bootsplash/themes/$THEME/config/bootsplash-$res.cfg" ]; then
			/bin/splash.bin -s -f "/etc/bootsplash/themes/$THEME/config/bootsplash-$res.cfg" >> "$target" && \
			debug "Added $res $THEME theme to initrd."
		else
			warn "/etc/bootsplash/themes/$THEME/config/bootsplash-$res.cfg doesn't exist, skipped"
		fi
	done
}

initrd_gen_fbsplash() {
	debug "Generating fbsplash"

	if [ ! -x /usr/bin/splash_geninitramfs -a ! -x /usr/sbin/splash_geninitramfs ]; then
		warn "Failed to find splash_geninitramfs. Is splashutils package installed?"
		return
	fi
	splash_geninitramfs_bin=/usr/sbin/splash_geninitramfs
	[ -f /usr/bin/splash_geninitramfs ] && splash_geninitramfs_bin=/usr/bin/splash_geninitramfs

	if [ -r /etc/sysconfig/fbsplash ]; then
		. /etc/sysconfig/fbsplash
	fi

	if [ -z "$SPLASH_THEME" ]; then
		warn "Please configure your /etc/sysconfig/fbsplash first."
		warn "Generating fbsplashes skipped."
		return
	fi

	if [ -z "$FB_SPLASH_RESOLUTIONS" ]; then
		warn "No FB_SPLASH_RESOLUTIONS specified in /etc/sysconfig/fbsplash."
		warn "Not adding fbsplash to initramfs."
		return
	fi

	for res in $FB_SPLASH_RESOLUTIONS; do
		if [ -f "/etc/splash/$SPLASH_THEME/$res.cfg" ]; then
			$splash_geninitramfs_bin -c $DESTDIR -r $res $SPLASH_THEME && \
			debug "Added $res $SPLASH_THEME theme to initramfs."
		else
			warn "/etc/splash/$SPLASH_THEME/$res.cfg doesn't exist, skipped"
		fi
	done
}

initrd_gen_fbcondecor() {
	debug "Generating fbcondecor"

	if [ ! -x /usr/bin/splash_geninitramfs -a ! -x /usr/sbin/splash_geninitramfs ]; then
		warn "Failed to find splash_geninitramfs. Is splashutils package installed?"
		return
	fi
	splash_geninitramfs_bin=/usr/sbin/splash_geninitramfs
	[ -f /usr/bin/splash_geninitramfs ] && splash_geninitramfs_bin=/usr/bin/splash_geninitramfs

	if [ -r /etc/sysconfig/splash ]; then
		. /etc/sysconfig/splash
	fi

	if [ -z "$SPLASH_THEME" ]; then
		warn "Please configure your /etc/sysconfig/splash first."
		warn "Generating of splashes skipped."
		return
	fi

	if [ -z "$FB_SPLASH_RESOLUTIONS" ]; then
		warn "No FB_SPLASH_RESOLUTIONS specified in /etc/sysconfig/splash."
		warn "Not adding fbcondecor to initramfs."
		return
	fi

	for res in $FB_SPLASH_RESOLUTIONS; do
		if [ -f "/etc/splash/$SPLASH_THEME/$res.cfg" ]; then
			$splash_geninitramfs_bin -c $DESTDIR -r $res $SPLASH_THEME && \
			debug "Added $res $SPLASH_THEME theme to initramfs."
		else
			warn "/etc/splash/$SPLASH_THEME/$res.cfg doesn't exist, skipped"
		fi
	done
}

# Generates /dev nodes based on /proc/partitions information.
# Needs /proc mounted.
# Can be called multiple times.
initrd_gen_devices() {
	if is_yes "$proc_partitions"; then
		return
	fi
	proc_partitions=yes

	mount_dev
	add_linuxrc <<-'EOF'
		: 'Making device nodes'
		cat /proc/partitions | (
			# ignore first two lines: header, empty line
			read b; read b

			while read major minor blocks dev rest; do
				node=/dev/$dev
				mkdir -p ${node%/*}
				[ -e $node ] || mknod $node b $major $minor
			done
		)
	EOF
}

initrd_gen_md() {
	debug "Setting up mdadm..."

	if [ ! -x /sbin/mdadm -o ! -x /sbin/initrd-mdassemble ]; then
		die "/sbin/mdadm or /sbin/initrd-mdassemble is missing!"
	fi

	inst_exec /sbin/initrd-mdassemble /bin/mdassemble

	# LVM on RAID case
	dev_list_extra=$(awk '/^DEVICE / { for (i=2; i<=NF; i++) { printf "%s ", $i; }; } ' /etc/mdadm.conf | xargs)
	if [ "$dev_list_extra" = "partitions" ]; then
		# FIXME: handle this case (see man mdadm.conf)
		echo "DEVICE partitions" >> "$DESTDIR/etc/mdadm.conf"
		dev_list_extra=""
	else
		for ex_dev in $dev_list_extra; do
			echo "DEVICE $ex_dev" >> "$DESTDIR/etc/mdadm.conf"
		done
	fi
	do_md0=1
	for nr in `seq 1 $rootdev_nr`; do
		eval cr_rootdev="\$rootdev${nr}"
		eval cr_dev_list="\$dev_list${nr}"
		debug echo "Setting up array ($cr_rootdev = $cr_dev_list)"

		[ "$cr_rootdev" = "/dev/md0" ] && do_md0=0

		echo "DEVICE $cr_dev_list" >> "$DESTDIR/etc/mdadm.conf"
		cr_dev_list_md="$(echo "$cr_dev_list" | xargs | awk ' { gsub(/ +/,",",$0); print $0; }')"
		cr_md_conf=$(/sbin/mdadm --detail --brief --config=/etc/mdadm.conf $cr_rootdev | awk ' { gsub(/spares=[0-9]+/, "", $0); print $0; }')
		if [ -n "$cr_md_conf" ]; then
			echo "$cr_md_conf" >> "$DESTDIR/etc/mdadm.conf"
		else
			echo "ARRAY $cr_rootdev devices=$cr_dev_list_md" >> "$DESTDIR/etc/mdadm.conf"
		fi

		for f in $cr_dev_list $cr_rootdev $dev_list_extra; do
			# mkdir in case of devfs name
			inst_d $(dirname $f)
			[ -e "$DESTDIR/$f" ] && continue
			# this works fine with and without devfs
			inst $f $f
		done
	done

	echo "mdassemble" | add_linuxrc

	# needed to determine md-version
	if [ "$do_md0" -eq 1 ]; then
		mknod $DESTDIR/dev/md0 b 9 0
	fi
}

initrd_gen_nfs() {
	# use root=/dev/ram0 init=/linuxrc when starting kernel or you will
	# have problems like init(XX) being child process of swapper(1).
	debug "Adding rootfs on NFS support to initrd (dhcp)"
	mknod "$DESTDIR/dev/urandom" c 1 9
	mkdir "$DESTDIR/newroot"
	add_linuxrc <<-'EOF'
		ifconfig lo 127.0.0.1 up
		route add -net 127.0.0.0 mask 255.0.0.0 lo
		ifconfig eth0 up
		udhcpc -i eth0 -f -q -s /bin/setdhcp

		cd /newroot
		pivot_root . initrd
		[ -x /sbin/chroot ] && exec /sbin/chroot . /sbin/init -i < dev/console > dev/console 2>&1
		exec /usr/sbin/chroot . /sbin/init -i < dev/console > dev/console 2>&1
	EOF

	cat <<-'EOF' > "$DESTDIR/bin/setdhcp"
		#!/bin/sh
		[ "$1" != "bound" ] && exit
		[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
		[ -n "$subnet" ] && NETMASK="netmask $subnet"
		ifconfig $interface $ip $BROADCAST $NETMASK up
		if [ -n "$router" ]; then
			for r in $router; do
				route add default gw $r dev $interface
			done
		fi

		for o in $CMDLINE; do
			case $o in
			nfsroot=*)
				rootpath=${o#nfsroot=}
				;;
			esac
		done

		if [ -n "$rootpath" ]; then
			mount -n -t nfs -o ro,nolock,posix,tcp,wsize=8192,rsize=8192 $rootpath /newroot
		else
			echo "Missing rootpath in what DHCP server sent to us. Failing..."
			echo "All seen variables are listed below:"
			set
		fi
	EOF

	chmod 755 "$DESTDIR/bin/setdhcp"
}

initrd_gen_lvm() {
	debug "Adding LVM support to initrd"
	inst_d /tmp /newroot
	inst_exec /sbin/initrd-lvm /bin/lvm.static

	# always make /dev on tmpfs for LVM2
	if [ "$LVMTOOLSVERSION" = "2" ]; then
		mount_dev
	fi

	if ! is_yes "$dev_mounted"; then
		inst_d /dev/mapper
		mknod $DESTDIR/dev/mapper/control c 10 63
		for device in $PVDEVICES; do
			# if LVM on RAID then device might be copied already in gen_md
			[ -e "$DESTDIR/dev/$(basename $device)" ] && continue
			inst $device /dev
		done
	fi

	mount_tmp
	if [ "$LVMTOOLSVERSION" = "1" ]; then
		add_linuxrc <<-EOF
			lvm vgscan -T
			for vg in $VGVOLUMES; do
				lvm vgchange -T -a y $vg
			done
		EOF
	else
		echo "cat /etc/lvm.conf > /tmp/lvm.conf" | add_linuxrc
		echo "global {" > "$DESTDIR/etc/lvm.conf"
		echo "	locking_type = 0" >> "$DESTDIR/etc/lvm.conf"
		echo "	locking_dir = \"/tmp\"" >> "$DESTDIR/etc/lvm.conf"
		echo "}" >> "$DESTDIR/etc/lvm.conf"
		echo "devices {" >> "$DESTDIR/etc/lvm.conf"
		echo "	sysfs_scan=0" >> "$DESTDIR/etc/lvm.conf"
		if is_yes "$have_md"; then
			echo "	md_component_detection = 1" >> "$DESTDIR/etc/lvm.conf"
		fi
		if is_yes "$have_dmraid" || is_yes "$have_multipath"; then
			echo '	types = [ "device-mapper", 254 ]' >> "$DESTDIR/etc/lvm.conf"
		fi
		if [ "$lvm_ignore_devices" ]; then
			# TODO: think of merging with lvm dumpconfig output
			echo '	filter = [' >> "$DESTDIR/etc/lvm.conf"
			local dev
			for dev in $lvm_ignore_devices; do
				debug "LVM v2: ignore device $dev"
				printf '  "r|^%s.*|",\n' $dev
			done >> "$DESTDIR/etc/lvm.conf"
			echo ']' >> "$DESTDIR/etc/lvm.conf"
		fi
		# XXX filter= must be on one line!
		lvm dumpconfig | awk '/filter=/' >> "$DESTDIR/etc/lvm.conf"
		echo "}" >> "$DESTDIR/etc/lvm.conf"

		initrd_gen_devices

		add_linuxrc <<-EOF
			export ROOTDEV=$rootdev
			export ROOTVG="$VGVOLUMES"
			export SUSPENDVG=$SUSPENDVG
		EOF
		add_linuxrc <<-'EOF'
			# parse rootdev from kernel commandline if it begins with /
			case "$ROOT" in
				/*)
				if [ "$ROOT" != "$ROOTDEV" ]; then
					ROOTDEV=$ROOT
					echo "LVM: Using 'root=$ROOTDEV' from kernel commandline"
					local tmp=${ROOTDEV#/dev/}
					if [ "$tmp" != "$ROOTDEV" ]; then
						ROOTVG=${tmp%/*}
						echo "LVM: Using Volume Group '$ROOTVG' for rootfs"
					fi
				fi
				;;
			esac

			# skip duplicate VG
			if [ "$SUSPENDVG" = "$ROOTVG" ]; then
				export VGVOLUMES="$ROOTVG"
			else
				export VGVOLUMES="$SUSPENDVG $ROOTVG"
			fi

			# disable noise from LVM accessing devices that aren't ready.
			read printk < /proc/sys/kernel/printk
			if [ ! "$DEBUGINITRD" ]; then
				echo 0 > /proc/sys/kernel/printk
			fi

			export LVM_SYSTEM_DIR=/tmp
			: 'Scanning for Volume Groups'
			lvm.static vgscan --mknodes --ignorelockingfailure 2>/dev/null

			: 'Activating Volume Groups'
			for vol in $VGVOLUMES; do
				lvm.static vgchange --ignorelockingfailure -a y $vol 2>/dev/null
			done

			echo "$printk" > /proc/sys/kernel/printk

			# Find out major/minor
			attrs="$(lvm.static lvdisplay --ignorelockingfailure -c $ROOTDEV 2>/dev/null)"
			if [ "$attrs" ]; then
				majmin="${attrs#*$ROOTDEV*:*:*:*:*:*:*:*:*:*:*:*}"
				if [ "$majmin" != "$attrs" ]; then
					major="${majmin%:*}"
					minor="${majmin#*:}"
				fi
			fi

			if [ "$major" -a "$minor" ]; then
				# Pass it to kernel
				echo $((256 * $major + $minor)) > /proc/sys/kernel/real-root-dev
			else
				echo 2>&1 "Error figuring out real root device for $ROOTDEV!"
				echo 2>&1 "System will not most likely boot up! So dropping you to a shell!"
				echo 2>&1 ""
				sh
			fi
		EOF
	fi
}

initrd_gen_blkid()
{
	debug "Adding BLKID support to initrd"
	inst_exec /sbin/initrd-blkid /bin/blkid
	initrd_gen_devices
	add_linuxrc <<-'EOF'
		# if built with blkid change ROOT=LABEL=something into ROOT=/dev/somethingelse -
		# parsed by blkid
		if [ "${ROOT##LABEL=}" != "${ROOT}" -o "${ROOT##UUID=}" != "${ROOT}" ]; then
			ROOT="$(/bin/blkid -t $ROOT -o device -l)"
		fi
	EOF
}

initrd_gen_setrootdev() {
	debug "Adding rootfs finding based on kernel cmdline root= option support."
	add_linuxrc <<-'EOF'
		if [ "${ROOT##/dev/}" != "${ROOT}" ]; then
			rootnr="$(busybox awk -v rootnode="${ROOT##/dev/}" '$4 == rootnode { print 256 * $1 + $2 }' /proc/partitions)"
			if [ -n "$rootnr" ]; then
				echo "$rootnr" > /proc/sys/kernel/real-root-dev
			fi
		fi
	EOF
}

initrd_gen_initramfs_switchroot() {
	inst_d /newroot
	if [ "$rootdev" = "/dev/nfs" ]; then
		echo "rootfs on NFS root=/dev/nfs"
	else
		[ ! -e "$DESTDIR/$rootdev" ] && inst $rootdev $rootdev
	fi
	# Parsing root parameter
	# We support passing root as hda3 /dev/hda3 0303 0x0303 and 303
	add_linuxrc <<-'EOF'
		device=/dev/no_partition_found
		eval "$(busybox awk -v c="$ROOT" '
			BEGIN {
				num_pattern_short = "[0-9a-f][0-9a-f][0-9a-f]";
				num_pattern = "[0-9a-f]" num_pattern_short;
				dev_pattern = "[hms][a-z][a-z]([0-9])+";
				partition = "no_partition_found";
				min = -1; maj = -1;

				sub("^0x", "", c);
				if (c ~ "^" num_pattern_short "$") sub("^", "0", c);
				if (c ~ "^" num_pattern  "$") {
					maj = sprintf("%s",substr(c,1,2));
					min = sprintf("%s",substr(c,3));
				}
				if (c ~ "^\/dev\/" dev_pattern "$") sub("^/dev/","", c);
				if (c ~ "^" dev_pattern "$") partition = c;
			}

			$4 ~ partition { maj = $1; min = $2; }
			$1 ~ maj && $2 ~ min { partition = $4; }

			END {
				print sprintf("device=/dev/%s\nmaj=%s\nmin=%s",
					partition, maj, min);
			}
			' /proc/partitions)"
		if [ "$device" != '/dev/no_partition_found' -a ! -b $device ]; then
			mknod $device b $maj $min
		fi
	EOF

	add_linuxrc <<-EOF
		rootdev=$rootdev
		rootfs=$rootFs
	EOF

	add_linuxrc <<-'EOF'
		if [ "$device" = '/dev/no_partition_found' ]; then
			device=$rootdev
		fi

		mount -t $rootfs -r $device /newroot
		init="$(echo "$CMDLINE" | busybox awk '/init=\// { gsub(/.*init=/,NIL,$0); gsub(/ .*/,NIL,$0); print }')"
		if [ -z "$init" -o ! -x "/newroot$init" ]; then
			init=/sbin/init
		fi
	EOF

	umount_all
	busybox_applet switch_root
	add_linuxrc <<-'EOF'
		exec switch_root /newroot $init

		echo "Error! initramfs should not reach this place."
		echo "It probably means you've got old version of busybox, with broken"
		echo "initramfs support. Trying to boot anyway, but won't promise anything."

		exec chroot /newroot $init

		echo "Failed to chroot!"
	EOF
	# we need /init being real file, not symlink, otherwise the initramfs will
	# not be ran by pid 1 which is required for switch_root
	mv $DESTDIR/linuxrc $DESTDIR/init
	ln -s init $DESTDIR/linuxrc
}

# main()
if [ "$(id -u)" != 0 ]; then
	die "You need to be root to generate initrd"
fi

if [ -f /etc/udev/udev.conf -a -x /sbin/initrd-udevd ]; then
	USE_UDEV=yes
	. /etc/udev/udev.conf
fi

if [ -x /sbin/dmraid-initrd ]; then
	USE_DMRAID=yes
fi

# cryptsetup luks addon
. /etc/geninitrd/mod-luks.sh
# dm-multipath addon
. /etc/geninitrd/mod-multipath.sh

if [ -x /sbin/initrd-blkid ]; then
	USE_BLKID=yes
fi

if [ -r /etc/sysconfig/geninitrd ]; then
	. /etc/sysconfig/geninitrd
fi

if [ ! -x /bin/initrd-busybox ]; then
	die "/bin/initrd-busybox is missing!"
fi

# backwards compatible
if [ "$USE_SUSPEND2" ]; then
	USE_TUXONICE=$USE_SUSPEND2
	warn "USE_SUSPEND2 is deprecated, use USE_TUXONICE now instead."
fi

while [ $# -gt 0 ]; do
	case $1 in
	--fstab=*)
		fstab=${1#--fstab=}
		;;
	--fstab)
		fstab=$2
		shift
		;;
	--modules-conf=*)
		modulefile=${1#--modules-conf=}
		;;
	--modules-conf)
		modulefile=$2
		shift
		;;
	--use-raidstart|--with-raidstart)
		USERAIDSTART=yes
		;;
	--without-raidstart)
		USERAIDSTART=no
		;;
	--use-insmod-static|--with-insmod-static)
		USEINSMODSTATIC=yes
		;;
	--without-insmod-static)
		USEINSMODSTATIC=no
		;;
	--with-bootsplash)
		BOOT_SPLASH=yes
		;;
	--without-bootsplash)
		BOOT_SPLASH=no
		;;
	--with-fbsplash)
		FB_SPLASH=yes
		;;
	--without-fbsplash)
		FB_SPLASH=no
		;;
	--with-fbcondecor)
		FB_CON_DECOR=yes
		;;
	--without-fbcondecor)
		FB_CON_DECOR=no
		;;
	--with-suspend)
		USE_SUSPEND=yes
		;;
	--without-suspend)
		USE_SUSPEND=no
		;;
	--with-suspend2 | --with-tuxonice)
		USE_TUXONICE=yes
		;;
	--without-suspend2 | --without-tuxonice)
		USE_TUXONICE=no
		;;
	--lvmtoolsversion=|--lvmversion=)
		LVMTOOLSVERSION=${1#--lvmtoolsversion=}
		LVMTOOLSVERSION=${1#--lvmversion=}
		;;
	--lvmtoolsversion|--lvmversion)
		LVMTOOLSVERSION=$2
		shift
		;;
	--without-udev)
		USE_UDEV=no
		;;
	--with-udev)
		USE_UDEV=yes
		;;
	--without-dmraid)
		USE_DMRAID=no
		;;
	--without-multipath)
		USE_MULTPATH=no
		;;
	--without-blkid)
		USE_BLKID=no
		;;
	--without-luks)
		USE_LUKS=no
		;;
	--with=*)
		BASICMODULES="$BASICMODULES ${1#--with=}"
		;;
	--with)
		BASICMODULES="$BASICMODULES $2"
		shift
		;;
	--version)
		echo "$PROGRAM: version $VERSION"
		exit 0
		;;
	-v)
		verbose=-v
		;;
	--nocompress)
		COMPRESS=no
		;;
	--ifneeded)
		ifneeded=1
		;;
	-f)
		force=1
		;;
	--preload=*)
		PREMODS="$PREMODS ${1#--preload=}"
		;;
	--preload)
		PREMODS="$PREMODS $2"
		shift
		;;
	--fs=*)
		warn "Warning: --fs option is obsoleted. Use --initrdfs instead"
		INITRDFS=${1#--fs=}
		;;
	--fs)
		warn "Warning: --fs option is obsoleted. Use --initrdfs instead"
		INITRDFS=$2
		shift
		;;
	--initrdfs=*)
		INITRDFS=${1#--initrdfs=}
		;;
	--initrdfs)
		INITRDFS=$2
		shift
		;;
	--image-version)
		img_vers=yes
		;;
	--ide-only-root)
		ide_only_root="yes"
		;;
	*)
		if [ -z "$target" ]; then
			target="$1"
		elif [ -z "$kernel" ]; then
			kernel="$1"
		else
			usage
		fi
		;;
	esac

	shift
done

if [ -z "$target" -o -z "$kernel" ]; then
	usage
fi

if [ ! -f /boot/vmlinuz-"$kernel" ]; then
	warn "/boot/vmlinuz-$kernel doesn't exist, is your /boot mounted?"
fi

pack_version=$(echo "$kernel" | awk -F. '{print sprintf("%03d%03d",$1,$2)}')
pack_version_long=$(echo "$kernel" | awk -F. '{print sprintf("%03d%03d%03d",$1,$2,$3)}')

if [ -z "$INITRDFS" ]; then
	if [ -z "$FS" ]; then
		# default value
		if [ "$pack_version" -ge "002005" ]; then
			INITRDFS="initramfs"
		else
			INITRDFS="rom"
		fi
	else
		warn "Warning: FS configuration options is obsoleted. Use INITRDFS instead"
		INITRDFS="$FS"
	fi
fi

if [ "$pack_version" -lt "002006" ]; then
	USE_UDEV=no
	USE_DMRAID=no
fi

if [ "$pack_version" -ge "002005" ]; then
	modext=".ko"
	insmod="insmod"
fi

if is_yes "$USEINSMODSTATIC"; then
	insmod="insmod.static"
	INSMOD="/sbin/insmod.static"
	if [ "$pack_version" -lt "002005" -a -f /sbin/insmod.static.modutils ]; then
		INSMOD="/sbin/insmod.static.modutils"
	fi
	if [ ! -f "$INSMOD" ]; then
		die "insmod.static requested but /sbin/insmod.static not found!"
	fi
fi

case "$INITRDFS" in
	ext2)
		if [ ! -x /sbin/mke2fs ]; then
			die "/sbin/mke2fs is missing"
		fi
		;;
	rom|romfs)
		if [ ! -x /sbin/genromfs ]; then
			die "/sbin/genromfs is missing"
		fi
		;;
	cram|cramfs)
		if [ ! -x /sbin/mkcramfs ]; then
			die "/sbin/mkcramfs is missing"
		fi
		;;
	initramfs)
		if [ ! -x /bin/cpio ]; then
			die "/bin/cpio is missing"
		fi
		if [ ! -x /usr/bin/find ]; then
			die "/usr/bin/find is missing"
		fi
		;;
	*)
		die "Filesystem $INITRDFS on initrd is not supported"
		;;
esac

if [ -L "$target" ]; then
	target=$(readlink -f "$target")
fi

if [ -n "$img_vers" ]; then
	target="$target-$kernel"
fi

if [ -z "$force" -a -f "$target" ]; then
	die "$target already exists."
fi

if [ ! -d "/lib/modules/$kernel" ]; then
	die "/lib/modules/$kernel is not a directory."
fi

if [ "$INITRDFS" != "initramfs" ]; then
	if is_yes "$FB_SPLASH"; then
		warn "Using fbsplash requires INITRDFS=initramfs; skipping fbsplash generation"
		FB_SPLASH=no
	fi
	if is_yes "$FB_CON_DECOR"; then
		warn "Using fbcondecor requires INITRDFS=initramfs; skipping fbcondecor generation"
		FB_CON_DECOR=no
	fi
fi

if is_yes "$USE_SUSPEND" && is_yes "$USE_TUXONICE"; then
	warn "Tuxonice and mainline suspend are exclusive!"
fi

if is_yes "$FB_SPLASH"  && is_yes "$BOOT_SPLASH"; then
	warn "bootsplash and fbsplash are exclusive!"
fi

if [ ! -f /proc/mounts ]; then
	warn "/proc filesystem not mounted, may cause wrong results or failure."
fi

if [ -d /usr/lib64 ]; then
	_lib=lib64
else
	_lib=lib
fi
debug "Using libdir: $_lib"

cache_modprobe_conf

for n in $PREMODS; do
	findmodule "$n"
done

if [ "$FBMODULE" ]; then
	findmodule "$FBMODULE"
fi

# allow forcing loading SCSI and/or IDE modules
if is_yes "$ADDSCSI"; then
	find_modules_scsi
fi

if is_yes "$ADDIDE"; then
	find_modules_ide
fi

if is_yes "$USE_SUSPEND"; then
	find_modules_suspend
fi

find_root "$fstab" || exit
debug "Using $rootdev as device for rootfs"

find_modules_for_devpath "$rootdev"
[ -n "$rootdev_add" ] && find_modules_for_devpath "$rootdev_add"

findmodule "-$rootFs"

for n in $BASICMODULES; do
	findmodule "$n"
done

if is_yes "$USE_TUXONICE"; then
	findmodule "-lzf"
fi

if is_yes "$FB_SPLASH"; then
	findmodule "-evdev"
fi

if [ -n "$ifneeded" -a -z "$MODULES" ]; then
	debug "No modules are needed -- not building initrd image."
	exit 0
fi

DESTDIR=$(mktemp -d -t initrd.XXXXXX) || die "mktemp failed"
RCFILE="$DESTDIR/linuxrc"
> "$RCFILE"
chmod a+rx "$RCFILE"
ln -s linuxrc $DESTDIR/init

# create dirs that we really need
inst_d /{lib,bin,etc,dev{,/pts,/shm},loopfs,var,proc,sys}

modules_install "$MODULES"

# mknod'ing the devices instead of copying them works both with and
# without devfs...
mknod "$DESTDIR/dev/console" c 5 1
mknod "$DESTDIR/dev/null" c 1 3
mknod "$DESTDIR/dev/zero" c 1 5

inst_exec /bin/initrd-busybox /bin/initrd-busybox
ln -s initrd-busybox $DESTDIR/bin/sh
ln -s initrd-busybox $DESTDIR/bin/busybox # for older busyboxes who had /bin/busybox as EXEPATH

if is_yes "$USEINSMODSTATIC"; then
	inst_exec "$INSMOD" /bin/insmod.static
fi

add_linuxrc <<EOF
#!/bin/sh
# initrd generated by:
# $RCSID

EOF
mount_proc
add_linuxrc <<-'EOF'
	read CMDLINE < /proc/cmdline; export CMDLINE

	for arg in $CMDLINE; do
		if [ "${arg}" = "debuginitrd" ]; then
			DEBUGINITRD=yes
		fi
		if [ "${arg##debuginitrd=}" != "${arg}" ]; then
			DEBUGINITRD=${arg##debuginitrd=}
		fi
		if [ "${arg##root=}" != "${arg}" ]; then
			ROOT=${arg##root=}
		fi
	done

	# make debugshell() invoke subshell if $DEBUGINITRD=sh
	if [ "$DEBUGINITRD" = "sh" ]; then
		debugshell() {
EOF
if is_yes "$RUN_SULOGIN_ON_ERR"; then
add_linuxrc <<-'EOF'
	echo "debug shell disabled by /etc/sysconfig/system:RUN_SULOGIN_ON_ERR setting"
EOF
else
add_linuxrc <<-'EOF'
	sh
EOF
fi
add_linuxrc <<-'EOF'
		}
	else
		debugshell() {
			:
		}
	fi

	if [ "$DEBUGINITRD" ]; then
		set -x
	fi
EOF

modules_add_linuxrc $MODULES

# TODO: rewrite for busybox
#if [ -n "$loopDev" ]; then
#	if [ ! -d /initrd ]; then
#		mkdir /initrd
#	fi
#
#	cp -a "$loopDev" "$DESTDIR/dev"
#	cp -a "$rootdev" "$DESTDIR/dev"
#	echo "echo Mounting device containing loopback root filesystem" >> "$RCFILE"
#	echo "mount -t $loopFs $loopDev /loopfs" >> "$RCFILE"
#	echo "echo Setting up loopback device $rootdev" >> $RCFILE
#	echo "losetup $rootdev /loopfs$loopFile" >> "$RCFILE"
#fi

if is_yes "$USE_UDEV"; then
	initrd_gen_udev
fi

if is_yes "$USE_TUXONICE"; then
	initrd_gen_tuxonice
fi

if is_yes "$need_v86d"; then
	initrd_gen_v86d
fi

if is_yes "$have_luks"; then
	initrd_gen_luks
fi

if is_yes "$have_dmraid"; then
	initrd_gen_dmraid
fi

if is_yes "$have_multipath"; then
	initrd_gen_multipath
fi

if is_yes "$USE_BLKID"; then
	initrd_gen_blkid
fi

if is_yes "$have_nfs"; then
	initrd_gen_nfs
elif is_yes "$USERAIDSTART" && is_yes "$have_md"; then
	initrd_gen_md
	if is_yes "$have_lvm"; then
		initrd_gen_lvm
	else
		initrd_gen_setrootdev
	fi
elif is_yes "$have_lvm"; then
	initrd_gen_lvm
else
	initrd_gen_setrootdev
fi

if is_yes "$USE_SUSPEND"; then
	initrd_gen_suspend
fi

# additional devs always needed
[ ! -e "$DESTDIR/$rootdev_add" ] && inst $rootdev_add /dev

if is_yes "$USE_UDEV"; then
	initrd_gen_stop_udevd
fi

if [ "$INITRDFS" = "initramfs" ]; then
	initrd_gen_initramfs_switchroot
else
	umount_all
fi

if is_yes "$FB_SPLASH"; then
	initrd_gen_fbsplash
fi

if is_yes "$FB_CON_DECOR"; then
	initrd_gen_fbcondecor
fi

IMAGE=$(mktemp -t initrd.img-XXXXXX) || die "mktemp failed"

debug "Creating $INITRDFS image $IMAGE"
case "$INITRDFS" in
	ext2)
		IMAGESIZE=$(du -ks $DESTDIR | awk '{print int(($1+1023+512)/1024)*1024}')
		debug 	"ext2 image size: $IMAGESIZE ($DESTDIR)"
		if [ "$IMAGESIZE" -gt 4096 ]; then
			warn "Your image size is larger than 4096, Be sure to boot kernel with ramdisk_size=$IMAGESIZE!"
		fi

		dd if=/dev/zero of="$IMAGE" bs=1k count="$IMAGESIZE" 2> /dev/null
		mke2fs -q -F -b 1024 -m 0 "$IMAGE" 2>/dev/null 1>&2
		tune2fs -i 0 "$IMAGE" >/dev/null 2>&1

		local tmpmnt=$(mktemp -d -t initrd.mnt-XXXXXX)
		debug "Mounting ext2 image $IMAGE to $tmpmnt"
		mount -o loop -t ext2 "$IMAGE" "$tmpmnt"
		# We don't need this directory, so let's save space
		rm -rf "$tmpmnt"/lost+found

		debug "Copy recursively $DESTDIR -> $tmpmnt"
		cp -a $DESTDIR/* $tmpmnt
		umount "$IMAGE"
		rmdir "$tmpmnt"

		;;
	rom|romfs)
		genromfs -f "$IMAGE" -d "$DESTDIR" -V "PLD initrd for kernel $kernel"
		IMAGESIZE=$(stat -c %s $IMAGE | awk '{print int((($1/1024)+1023)/1024)*1024}')
		debug "Image size for romfs: ${IMAGESIZE}KiB ($IMAGE)"
		if [ "$IMAGESIZE" -gt 4096 ]; then
			warn "Your image size is larger than 4096, Be sure to boot kernel with ramdisk_size=$IMAGESIZE!"
		fi
		;;
	cram|cramfs)
		mkcramfs "$DESTDIR" "$IMAGE"
		;;
	initramfs)
		(cd $DESTDIR; find . | cpio --quiet -H newc -o > "$IMAGE")
		;;
	*)
		echo "Filesystem $INITRDFS not supported by $PROGRAM";
esac

if is_yes "$COMPRESS"; then
	tmp=$(mktemp "$target".XXXXXX) || die "mktemp failed"
	debug "Compressing $target"
	gzip -9 < "$IMAGE" > "$tmp"
	mv -f "$tmp" "$target"
else
	cp -a "$IMAGE" "$target"
fi

# XXX. check if bootsplash can output data to tmp dir not directly to initramfs image.
if is_yes "$BOOT_SPLASH"; then
	initrd_gen_bootsplash "$target"
fi

rm -rf "$DESTDIR" "$IMAGE"

# vim:ts=4:sw=4:noet:fdm=marker
