#!/bin/sh

# geninitrd
#
#	by PLD Linux Team
#
# based on mkinitrd from RedHat Linux
#

RCSID='$Id: geninitrd 10353 2009-05-03 17:56:21Z glen $'
R=${RCSID#* * }; VERSION=${R%% *}
PROGRAM=${0##*/}

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

# list of geninitrd modules which need setup routine after commandline args parsing
GENINITRD_MODS=""
COMPRESS=yes
target=""
kernel=""
force=""
verbose=""
MODULES=""
img_vers=""
fstab=/etc/fstab
modext=.o
rootdev_nr=0
# device node for rootfs from fstab
rootdev=""

# 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

usage() {
	uname_r=$(uname -r)
	echo "usage: $PROGRAM [--version] [-v] [-f] [--ifneeded] [--preload <module>]"
	echo "       [--with=<module>] [--image-version] [--fstab=<fstab>] [--nocompress]"
	echo "       [--compress=yes|lzma|bzip2|gzip]"
	echo "       [--initrdfs=rom|initramfs|ext2|cram] [--modules-conf=<modules.conf>]"
	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 0
}

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

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

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

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

# find program from specified paths
find_tool() {
	local x
	for x in "$@"; do
		if [ -x "$x" ]; then
			echo $x
			debug "find_tool: found $x"
			return 0
		fi
	done
	debug "find_tool: did not found any of: $@"
	return 1
}

# loads geninitrd modules
geninitrd_load_mods() {
	local mod
	for mod in "$@"; do
		if [ ! -f /lib/geninitrd/mod-$mod.sh ]; then
			die "$mod geninitrd module can't be loaded"
		fi
		. /lib/geninitrd/mod-$mod.sh

		if type setup_mod_$mod > /dev/null; then
			# add to list which need init
			GENINITRD_MODS="$GENINITRD_MODS $mod"
		fi
	done
}

# setup geninitrd modules
geninitrd_setup_mods() {
	local mod

	for mod in $GENINITRD_MODS; do
		eval setup_mod_$mod
	done
}

# 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=$($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 $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
#
# Outputs full path to module and it's dependencies
find_depmod() {
	local module="$1"
	local skiperrors=0

	# if module is prefixed with dash, we should ignore errors if the module
	# can't be found.
	if [ ${module#-} != $module ]; then
		skiperrors=1
		module=${module#-}
	fi

	# This works when user has module-init-tools installed even on 2.4 kernels
	local modprobe
	modprobe=$(modprobe --set-version $kernel --show-depends $module --ignore-install 2>&1)

	if [ $? != 0 ]; then
		if [ $skiperrors = 1 ]; then
			return
		fi
		echo >&2 "$modprobe"

		if ! is_no "$EXIT_IF_MISSING"; then
			die "$module: module not found for $kernel kernel"
		fi

		warn "$module: module not found for $kernel kernel"
		warn "If $module isn't compiled in kernel then this initrd may not start your system."
	fi

	echo "$modprobe" | \
	while read insmod modpath options; do
		[ "$insmod" = "insmod" ] && echo $modpath
	done
}

find_firmware() {
	local module="$1"

	# no firmware support in 2.4 kernels
	if [ "$kernel_version_long" -lt "002005048" ]; then
		return
	fi
	echo -n $(NEW_MODINFO=1 modinfo -k $kernel -F firmware $module 2>/dev/null | xargs)
}

# @param	$module
find_module() {
	local mod depmod module=$1

	depmod=$(find_depmod $module) || exit 1
	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"
		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 "Invalid params ($@), 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 [ "$kernel_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 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

	if [[ "$devpath" = /dev/dm-* ]]; then
		# /dev/dm-3 -> /dev/mapper/sil_ahbgadcbchfc3
		devpath=$(dm_longname "$devpath")
	fi

	if [ -L "$devpath" ] && ! is_lvm "$devpath"; then
		# sanitize things like:
		# /dev/block/104:2 -> /dev/cciss/c0d0p2
		devpath=$(readlink -f "$devpath")
	fi

	debug "Finding modules for device path $devpath"

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

	if is_nfs "$devpath"; then
		find_modules_nfs "$devpath"
		return
	fi

	if is_md "$devpath"; then
		find_modules_md "$devpath"
		return
	fi

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

	if is_dmraid "$devpath"; then
		if find_modules_dmraid "$devpath"; then
			return
		fi
		# fallback
	fi

	if is_scsi "$devpath"; then
		find_modules_scsi "$devpath"
		return
	fi

	if is_ide "$devpath"; then
		find_modules_ide "$devpath"
		return
	fi

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

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

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

	if [[ "$devpath" == /dev/ataraid/* ]]; then
		find_modules_ide
		find_module "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
			find_module "$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"
		find_module "-$loopFs"
		BASICMODULES="$BASICMODULES -${loopFs}"
		return
	fi

	if is_lvm "$devpath"; then
		find_modules_lvm "$devpath"
		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/$kernel/$firmware" ]; then
			FIRMWAREDIR=${firmware%/*}
			[ "$FIRMWAREDIR" != "$firmware" ] && inst_d /lib/firmware/$FIRMWAREDIR
			inst /lib/firmware/$kernel/$firmware /lib/firmware/$firmware
		elif [ -f "/lib/firmware/$firmware" ]; then
			FIRMWAREDIR=${firmware%/*}
			[ "$FIRMWAREDIR" != "$firmware" ] && inst_d /lib/firmware/$FIRMWAREDIR
			inst /lib/firmware/$firmware /lib/firmware/$firmware
		else
			die "firmware file /lib/firmware/$firmware nor /lib/firmware/$kernel/$firmware found."
		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
}

# 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_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

	# parse 'root=xxx' kernel commandline
	# 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 {
				if (maj >= 0 && min >= 0) {
					printf("device=/dev/%s; maj=%s; min=%s;\n", 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 ${1:+"$@"}

		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 ${1:+"$@"}

		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
}

# find if $symbol exists in System.map $mapfile
sym_exists() {
	local mapfile="$1"
	local symbol="$2"
	if [ ! -f $mapfile ]; then
		# missing mapfile (not a pld kernel?)
		return 1
	fi

	awk -vc=1 -vsymbol="$symbol" '$2 == "T" && $3 == symbol {c = 0} END {exit c}' $mapfile
}

# find best compressor (or forced one) for initrd
find_compressor() {
	local mode="$1"
	# the best compressor list
	local compressors='lzma bzip2 gzip'

	# a specified one, take it
	if ! is_yes "$mode"; then
		compressors=gzip
	fi

	debug "finding compressor: $compressors (via $mode)"
	# check for compressor validity
	local c prog map=/boot/System.map-$kernel
	for c in $compressors; do
		case $c in
		lzma)
			sym=unlzma
			prog=/usr/bin/lzma
			;;
		bzip2)
			sym=bzip2
			prog=/usr/bin/bzip2
			;;
		gzip)
			sym=gunzip
			prog=/bin/gzip
			;;
		*)
			die "Unknown compressor $c"
			;;
		esac
		if sym_exists $map $sum && [ -x $prog ]; then
			echo $c
			return
		fi
	done

	debug "using gzip for compressor (fallback)"
	echo gzip
}

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

geninitrd_load_mods ide luks multipath dmraid lvm md blkid udev tuxonice suspend fbsplash condecor bootsplash uvesafb nfs scsi

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
		;;
	--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
		;;
	--lvmversion=*)
		LVMTOOLSVERSION=${1#--lvmversion=}
		;;
	--lvmtoolsversion=*)
		LVMTOOLSVERSION=${1#--lvmtoolsversion=}
		;;
	--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
		;;
	--compress)
		COMPRESS=$2
		;;
	--compress=*)
		COMPRESS="${1#--compress=}"
		;;
	--nocompress)
		COMPRESS=no
		;;
	--ifneeded)
		ifneeded=1
		;;
	-f)
		force=1
		;;
	--preload=*)
		PREMODS="$PREMODS ${1#--preload=}"
		;;
	--preload)
		PREMODS="$PREMODS $2"
		shift
		;;
	--fs=* | --fs)
		die "--fs option is obsoleted. Use --initrdfs instead"
		;;
	--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 >&2
			exit 1
		fi
		;;
	esac

	shift
done

if [ -z "$target" -o -z "$kernel" ]; then
	usage >&2
	exit 1
fi

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

if [ -d /usr/lib64 ]; then
	_lib=lib64
else
	_lib=lib
fi

initrd_dir=/usr/$_lib/initrd
kernel_version=$(echo "$kernel" | awk -F. '{print sprintf("%03d%03d",$1,$2)}')
kernel_version_long=$(echo "$kernel" | awk -F. '{print sprintf("%03d%03d%03d",$1,$2,$3)}')

debug "# $RCSID"
debug "Using _lib: $_lib"
debug "Using initrd_dir: $initrd_dir"

busybox=$(find_tool $initrd_dir/initrd-busybox /bin/initrd-busybox) || die "Couldn't find busybox suitable for initrd"

# we setup mods after parsing command line args
geninitrd_setup_mods

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

if [ -z "$INITRDFS" ]; then
	if [ -n "$FS" ]; then
		# FS= can came only via /etc/sysconfig/geninitrd likely?
		die "FS configuration option is obsoleted. Use INITRDFS instead"
	fi

	# default value
	if [ "$kernel_version" -ge "002005" ]; then
		INITRDFS="initramfs"
	else
		INITRDFS="rom"
	fi
fi

case "$INITRDFS" in
  ext2)
	[ -x /sbin/mke2fs ] || die "/sbin/mke2fs is missing"
	;;
  rom|romfs)
	[ -x /sbin/genromfs ] || die "/sbin/genromfs is missing"
	;;
  cram|cramfs)
	[ -x /sbin/mkcramfs ] || die "/sbin/mkcramfs is missing"
	;;
  initramfs)
	[ -x /bin/cpio ] || die "/bin/cpio is missing"
	[ -x /usr/bin/find ] || die "/usr/bin/find is missing"
	;;
  *)
	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 [ ! -f /proc/mounts ]; then
	warn "/proc filesystem not mounted, may cause wrong results or failure."
fi

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

cache_modprobe_conf

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

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

# allow forcing loading SCSI and/or IDE modules
# XXX: where ADDSCSI cames from? drop?
if is_yes "$ADDSCSI"; then
	find_modules_scsi
fi

# XXX: where ADDIDE cames from? drop?
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"

find_module "-$rootFs"

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

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

find_modules_fbsplash

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

debug "Building initrd..."
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 $busybox /bin/initrd-busybox
ln -s initrd-busybox $DESTDIR/bin/sh
# for older busyboxes who had /bin/busybox as EXEPATH
ln -s initrd-busybox $DESTDIR/bin/busybox

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

find_modules_uvesafb
initrd_gen_uvesafb

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 "$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")
	;;
  *)
	die "Filesystem $INITRDFS not supported by $PROGRAM"
esac

if ! is_no "$COMPRESS"; then
	tmp=$(mktemp "$target".XXXXXX) || die "mktemp failed"
	compressor=$(find_compressor "$COMPRESS")
	debug "Compressing $target with $compressor"

	# TODO: the image name (specified from kernel.spec) already contains
	# extension, which is .gz most of the time.
	case "$compressor" in
	lzma)
		lzma -9 < "$IMAGE" > "$tmp"
		;;
	bzip2)
		bzip2 -9 < "$IMAGE" > "$tmp"
		;;
	gzip)
		gzip -9 < "$IMAGE" > "$tmp"
		;;
	esac
	mv -f "$tmp" "$target"
else
	cp -a "$IMAGE" "$target"
fi

size=$(stat -c %s $target | awk '{print int((($1/1024)+1023)/1024)*1024}')
debug "Minimum RAM size that could be used for $target is: ramdisk_size=$size"

# 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
