#!/bin/sh

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

VERSION='12829'
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
LILO_MICROCODE=yes
STRIP=/usr/bin/strip
target=""
kernel=""
force=""
verbose=""
MODULES_TO_INSTALL=""
MODULES_TO_LOAD=""
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() {
	echo "Usage: $PROGRAM [--version] [-v] [-f] [--ifneeded] [--preload <module>]"
	echo "       [--with=<module>] [--image-version] [--fstab=<fstab>] [--nocompress]"
	echo "       [--compress=yes|zstd|xz|lzma|bzip2|gzip|lzo|lz4]"
	echo "       [--nostrip ] [--strip PATH/strip] [--strip=PATH/strip]"
	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]"
	echo "       [--with-multipath=DEVPATH] [--without-multipath]"
	echo "       [--without-blkid] [--without-luks]"
	echo "       <initrd-image> <kernel-version>"
	echo ""
	echo "Example:"

	local kdir kver dir=${target:-/boot}
	for kdir in /lib/modules/*; do
		[ -d $kdir ] || continue
		kver=${kdir##*/}
		echo "  $0 -f --initrdfs=initramfs $dir/initrd-$kver.gz $kver $verbose"
	done | sort -V
}

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

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

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

debug() {
	[ x"$verbose" = x"-v -v" ] && msg "$*" >&3
}

# add initrd code to print to kmsg
# @param string message
# @param int loglevel. defaults to "6" (info)
# Log levels can be:
# Name		String	Meaning
# KERN_EMERG	"0"	Emergency messages, system is about to crash or is unstable
# KERN_ALERT	"1"	Something bad happened and action must be taken immediately
# KERN_CRIT	"2"	A critical condition occurred like a serious hardware/software failure
# KERN_ERR	"3"	An error condition, often used by drivers to indicate difficulties with the hardware
# KERN_WARNING	"4"	A warning, meaning nothing serious by itself but might indicate problems
# KERN_NOTICE	"5"	Nothing serious, but notably nevertheless. Often used to report security events.
# KERN_INFO	"6"	Informational message e.g. startup information at driver initialization
# KERN_DEBUG	"7"	Debug messages
# KERN_CONT	"c"	"continued" line of log printout (only done after a line that had no enclosing \n)
kmsg() {
	local msg="$1" level=${2:-6}
	echo "echo '<$level>$msg' > /dev/kmsg" | add_linuxrc
}

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

# find program from specified paths
find_tool() {
	local x p b n
	local paths="$initrd_dirs /bin /sbin /usr/bin /usr/sbin"
	for x in "$@"; do
		debug "find_tool: checking $x"
		if [ -x "$x" ]; then
			echo $x
			verbose "find_tool: found $x"
			return 0
		fi
		n="$x"
		for p in $paths; do
			b=$(basename $x)
			debug "find_tool: checking $p/$b"
			if [ -x "$p/$b" ]; then
				echo $p/$b
				verbose "find_tool: found $p/$b"
				return 0
			fi
			n="$n $p/$b"
		done
	done
	debug "find_tool: did not find any of: $n"
	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

		GENINITRD_MODS="$GENINITRD_MODS $mod"
	done
}

# setup geninitrd modules
geninitrd_setup_mods() {
	local mod

	for mod in $GENINITRD_MODS; do
		debug "# $mod"

		# some mods want init
		if type setup_mod_$mod > /dev/null; then
			eval setup_mod_$mod
		fi
	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() {
	# 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'
		if ! mount -t devtmpfs -o mode=0755,nosuid devtmpfs /dev > /dev/null 2>&1; then
			mount -o mode=0755,nosuid -t tmpfs tmpfs /dev
			mknod -m 600 /dev/console c 5 1
			mknod -m 666 /dev/null c 1 3
			mknod -m 666 /dev/zero c 1 5
			mknod -m 666 /dev/random c 1 8
			mknod -m 600 /dev/snapshot c 10 231
			mknod -m 666 /dev/urandom c 1 9
			mknod -m 666 /dev/ptmx c 5 2
			mknod -m 644 /dev/kmsg c 1 11
		fi
		mkdir /dev/pts
		mkdir /dev/shm
	EOF
}

# load font
load_font() {
	local font
	[ ! -r /etc/sysconfig/console ] && return
	. /etc/sysconfig/console
	if [ -n "$CONSOLEFONT" ]; then
		font=$(ls -1 /lib/kbd/consolefonts/${CONSOLEFONT}*.gz 2> /dev/null)
		if [ -n "$font" ]; then
			verbose "Loading font $font"
			busybox_applet loadfont
			inst_d "/lib/kbd/consolefonts"
			cp -a "$font" "$DESTDIR/lib/kbd/consolefonts/"
			gunzip ${DESTDIR}/lib/kbd/consolefonts/${CONSOLEFONT}*.gz
			font=${font%.gz}
			echo "loadfont < $font" | add_linuxrc
		fi
	fi
}

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

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

	run_mounted=yes
	echo "mount -t tmpfs run /run -o mode=0755,noexec,nosuid,nodev" | add_linuxrc
}

# unmount all mountpoints mounted by geninitrd
# try to move pseudo filesystems to newroot if possible
umount_all() {

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

	if is_yes "$run_mounted"; then
		add_linuxrc <<-EOF
		mount -n --move /run /newroot/run
		EOF
		run_mounted=no
	fi
	if is_yes "$dev_mounted"; then
		add_linuxrc <<-EOF
		mount --bind /dev /newroot/dev
		umount /dev
		EOF
		dev_mounted=no
	fi
	if is_yes "$sys_mounted"; then
		add_linuxrc <<-EOF
		mount --bind /sys /newroot/sys
		umount /sys
		EOF
		sys_mounted=no
	fi
	if is_yes "$proc_mounted"; then
		add_linuxrc <<-EOF
		mount --bind /proc /newroot/proc
		umount /proc
		EOF
		proc_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 applet

	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" | grep -Ec "( |^)$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
}

# Extract the .config file from a kernel image
# uses extract-ikconfig from kernel sources (scripts/extract-ikconfig)
ikconfig() {
	local kofile=$(modinfo -k $kernel -n configs 2> /dev/null)
	if [ -n "$kofile" ]; then
		/lib/geninitrd/extract-ikconfig $kofile
		return
	fi

	# see if config available as separate file
	if [ -f /boot/config-$kernel ]; then
	   cat /boot/config-$kernel
	   return
   	fi

	# finally try vmlinuz itself
	/lib/geninitrd/extract-ikconfig /boot/vmlinuz-$kernel
}

# @param    $module
basename_module() {
	local module=$1

	module=${module##*/}
	module=${module%$modext*}
	echo $module
}

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

	local smodule

	echo "$modprobe" | \
	while read insmod modpath options; do
		if [ "$insmod" = "insmod" ]; then

			# XXX: find a away to autodetect
			smodule=$(basename_module $modpath)
			case "$smodule" in
				btrfs)
					warn "mounting multidevice btrfs volume requires rootfsflags=device=/dev/...,device=/dev/... kernel option"
					find_depmod "-libcrc32c"
					;;
				ext4)
					find_depmod "-libcrc32c"
					;;
				crc-t10dif)
					find_depmod "-crct10dif-pclmul"
					find_depmod "-crct10dif"
					;;
				libcrc32c)
					find_depmod "-crc32c-intel"
					find_depmod "-crc32c"
					;;
				virtio_blk|virtio_scsi)
					find_depmod "-virtio_pci"
					find_depmod "-virtio_mmio"
					;;
			esac

			echo $modpath
		fi
	done
	return 0
}

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_TO_INSTALL; do
			[ $m = $mod ] && have=1
		done
		if [ $have = 0 ]; then
			MODULES_TO_INSTALL="$MODULES_TO_INSTALL $mod"
			if ! is_yes "$USE_MODPROBE"; then
				MODULES_TO_LOAD="$MODULES_TO_LOAD $mod"
			fi
		fi
	done
	if is_yes "$USE_MODPROBE" && [ -n "$mod" ]; then
		MODULES_TO_LOAD="$MODULES_TO_LOAD $mod"
	fi
}

# 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
	local parentDir=$(dirname $DESTDIR$dest)
	if [ ! -d "$parentDir" ]; then
		verbose "+ mkdir -p DESTDIR${parentDir#$DESTDIR}"
		mkdir -p $parentDir
	fi
	verbose "+ cp $* DESTDIR$dest"
	cp -HRp "$@" "$DESTDIR$dest"
}

inst_d() {
	if [ $# = 0 ]; then
		die 'Usage: inst_d <destination> <destination>'
	fi
	local dir
	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 obj lib libs libs_additional libdir
	for obj in "$@"; do
		case "$obj" in
			/lib/ld-linux.so.2 | /lib64/ld-linux-x86-64.so.2 | /libx32/ld-linux-x32.so.2)
			continue
			;;
		    /lib/libpthread.so* | /lib64/libpthread.so* | /libx32/libpthread.so*)
				libs_additional="${obj%/libpthread*}/libgcc_s.so.1"
			;;
		esac


		libs=$(ldd "$obj" 2> /dev/null | awk '/statically|linux-(gate|vdso)\.so/{next} NF == 2 {print $1} /=/{print $3}' | sort -u)
		for lib in $libs $libs_additional; do
			libdir=$(cd $(dirname "$lib"); pwd)
			if [ ! -f "$DESTDIR/$lib" ]; then
				inst_d $libdir
				inst_exec $lib $libdir
			fi
		done
	done

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

files_exist() {
	local start_point="$1" pattern="$2"
	shift
	test -n "$(find "$start_point" -mindepth 1 -maxdepth 1 -name "$pattern" -print -quit)"
}

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

# return options for MODULE
# @param $1 module name
modprobe_options() {
	local module=$1
	local options=$(modprobe_conf | awk -vmodule="$module" '{ if ($1 == "options" && $2 == module) { for(i=3;i<=NF;i++) printf("%s ",$i); }}')
	echo ${options# }
}

#
# 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 | awk '!/^[\t ]*#/ { print }')

	else
		debug "Using modprobe -c to get modules config"
		modprobe_conf_cache=$(modprobe -c --set-version $kernel | awk '!/^[\t ]*#/ { print }')
	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_node "$devpath")
	fi

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

	verbose "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/nvme* ]]; then
		find_module "nvme"
		return
	fi

	if [[ "$devpath" == /dev/bcache* ]]; then
		find_modules_bcache "$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
		rootdev_add=/dev/cciss/

		# load hpsa for future kernels, cciss for backwards compat
		if [ "$kernel_version_long" -ge "003000000" ]; then
			find_module "hpsa" "-cciss"
			find_modules_scsi "$devpath"
		else
			find_module "cciss"
		fi

		return
	fi

	if [[ "$devpath" == /dev/ataraid/* ]]; then
		find_modules_ide
		find_module "ataraid"
		ataraidmodules=$(modprobe_conf | awk '/ataraid_hostadapter/ { 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

	if is_lvm "$devpath"; then
		find_modules_lvm "$devpath"
		return
	fi
}

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

	verbose "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
# handle only firmware add requests
if [ "$SUBSYSTEM" != "firmware" ]; then
	exit 0
fi
if [ "$ACTION" != "add" ]; then
	exit 0
fi
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"

		# setup firmware loader agent
		echo "echo -n "/lib/firmware/firmware.sh" > /proc/sys/kernel/hotplug" | add_linuxrc
	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
			warn "Possible missing firmware file /lib/firmware/$firmware or /lib/firmware/$kernel/$firmware for module $module."
		fi
	done

	mount_sys
}

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"
		case $mod in
			*.gz)
				gunzip "$DESTDIR/lib/modules/$kernel/$mod" || die "Can't uncompress gz"
				mod=${mod%.gz}
				;;
			*.xz)
				xz -d "$DESTDIR/lib/modules/$kernel/$mod" || die "Can't uncompress xz"
				mod=${mod%.xz}
				;;
			*.bz2)
				bzip2 -d "$DESTDIR/lib/modules/$kernel/$mod" || die "Can't uncompress bz2"
				mod=${mod%.bz2}
				;;
		esac
		if [ "$STRIP" ] && [ -x "$STRIP" ]; then
			$STRIP -g --remove-section=.comment "$DESTDIR/lib/modules/$kernel/${mod}"
		fi
	done
}

modules_add_linuxrc() {
	local mod modpath

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

		# name of the module
		local module=${modpath##*/}; module=${module%$modext}
		local options
		if ! is_yes "$USE_MODPROBE"; then
			options=$(modprobe_options "$module")
		else
			options=
		fi
		local genericname=$(echo $module | tr - _)
		local usleep=$(eval echo \$MODULE_${genericname}_USLEEP)
		local firmware=$(eval echo \$MODULE_${genericname}_FIRMWARE)

		if [ "$module" = "scsi_mod" -a "$kernel_version_long" -ge "002006030" ]; then
			options="scan=sync $options"
		fi

		if [ x"$verbose" = x"-v" ]; then
			s=""
			if [ "$options" ]; then
				s="$s with options [$options]"
			fi
			if [ "$usleep" ]; then
				s="$s and $usleep usleep"
			fi
			verbose "Loading module [$module]$s"
		fi

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

		if is_yes "$USE_MODPROBE"; then
			echo "/sbin/modprobe $module $options" | add_linuxrc
		else
			echo "insmod /lib/modules/$kernel/$modpath $options" | add_linuxrc
		fi
		if [ -n "$usleep" ]; then
			echo "usleep $usleep" | add_linuxrc
		fi
		if [ "$module" = "scsi_wait_scan" ]; then
			if [ "$(busybox_applet rmmod 2>/dev/null; echo $?)" = 0 ]; then
				echo "rmmod scsi_wait_scan" | add_linuxrc
			fi
		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 -m 660 $node b $major $minor
			done
		)
	EOF
}


initrd_gen_setrootdev() {
	verbose "Adding rootfs finding based on kernel cmdline root= option support."
	busybox_applet ls
	debug "Current /proc/partitions:\n$(sed -e 's,^,| ,' /proc/partitions)"
	add_linuxrc <<-'EOF'
		if [ "${ROOT##/dev/}" != "${ROOT}" ]; then
			rootnr="$(busybox awk -v rootnode="${ROOT##/dev/}" '$4 == rootnode { print 256 * $1 + $2 }' /proc/partitions)"
			# fallback to ls, try two different formats
			# http://lists.pld-linux.org/mailman/pipermail/pld-devel-en/2014-May/023915.html
			if [ "${rootnr:-0}" = 0 -a -e "$ROOT" ]; then
				# busybox up to 1.22
				rootnr="$(busybox ls -lL ${ROOT} | busybox awk '{if (/^b/) { print 256 * $3 + $4; }}')"
			fi
			if [ "${rootnr:-0}" = 0 -a -e "$ROOT" ]; then
				# busybox 1.22 and upwards
				rootnr="$(busybox ls -lL ${ROOT} | busybox awk '{if (/^b/) { print 256 * $5 + $6; }}')"
			fi
			if [ "${rootnr:-0}" -gt 0 ]; 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 $rootdev ] && [ ! -e "$DESTDIR/$rootdev" ] && inst $rootdev $rootdev
	fi

	# parse 'root=xxx' kernel commandline
	# We support passing root as hda3 /dev/hda3 0303 0x0303 and 303

	# from lilo-23.2/readme/README:
	# root=<device> changes the root device. This overrides settings that may
	# have been made in the boot image and on the LILO command line. <device> is
	# either the hexadecimal device number or the full path name of the device,
	# e.g. /dev/hda3 [*]
	#
	#  *  The device names are hard-coded in the kernel. Therefore, only the
	#	  "standard" names are supported and some less common devices may not be
	#	  recognized. In those cases, only numbers can be used.
	busybox_applet cat
	add_linuxrc <<-'EOF'
		device=
		eval "$(busybox awk -v root="$ROOT" '
			function h2d(str, hstr, res, num, n, digit, i) {	# http://9fans.net/archive/2006/09/261
				hstr = "0123456789abdcef"; res = 0;
				n = split(tolower(str), digit, "");

				for (i = 1; i <= n; i++) {
					num = index(hstr, digit[i]) - 1;
					res = res + (num * 16 ^ (n - i));
				}
				return res;
			}
			BEGIN {
				num_pattern_short = "[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]";
				num_pattern = "[0-9a-fA-F]" num_pattern_short;
				dev_pattern = "[hms][a-z][a-z]([0-9])+";
				partition = ""; min = -1; maj = -1;

				if (root ~ "^\/dev\/" dev_pattern "$" || root ~ "^" dev_pattern "$") {	# see if we have /dev/hdX or hdX, we can just take partition name
					partition = root; sub("^/dev/", "", partition);
				} else {	# unify values first
					if (root ~ "^" num_pattern_short "$")  {	# change "303" => "0x0303"
						root = "0x0" root
					} else if (root ~ "^" num_pattern "$")  {	# change "0303" => "0x0303"
						root = "0x" root
					}
					maj = h2d(substr(root, 3, 2));
					min = h2d(substr(root, 5, 2));
				}
			}
			partition && $4 == partition { maj = $1; min = $2; }
			$1 == maj && $2 == min { partition = $4; }
			END {
				if (maj >= 0 && min >= 0) {	printf("maj=%s; min=%s;\n", maj, min);	}
				if (partition) {		printf("device=/dev/%s;\n", partition);	}
			}' /proc/partitions)"

		if [ -z "$device" ]; then
			if [ "$DEBUGINITRD" -a "$DEBUGINITRD" != 'sh' ]; then
				cat /proc/partitions
			fi
			device=$ROOT
		fi

		if [ "$device" -a ! -b $device -a "$maj$min" ]; then
			mknod -m 660 $device b $maj $min
		fi

		# XXX hack, fallback to rootdev from geninitrd time
		if [ ! -e "$device" ]; then
	EOF
	add_linuxrc <<-EOF
			device="$rootdev"
	EOF
	add_linuxrc <<-'EOF'
			echo "DEVICE set to $device based on fstab entry from initrd gen time"
		fi

		# XXX hack, if no device, try to parse it from /proc/partitions using /proc/sys/kernel/real-root-dev
		if [ ! -e "$device" ]; then
			rrd=$(cat /proc/sys/kernel/real-root-dev)
			major=$(($rrd / 256))
			minor=$(($rrd % 256))

			while read pmajor pminor blocks dev rest; do
				# skip header and empty line
				[ -z "$pmajor" -o "$pmajor" = "major" ] && continue

				if [ $pmajor = $major -a $pminor = $minor ]; then
					device=/dev/$dev
					echo "DEVICE set to $device based on real-root-dev"
				fi
			done < /proc/partitions
		fi

		[ -n "$ROOTFLAGS" ] && ROOTFLAGS="-o $ROOTFLAGS"

		mount -t $ROOTFS -r $device $ROOTFLAGS /newroot || echo "Mount of rootfs failed."
		init=$INIT
		if [ -z "$init" -o ! -x "/newroot$init" ]; then
			init=/sbin/init
		fi
	EOF

	busybox_applet dmesg
	busybox_applet tail
	add_linuxrc <<-'EOF'
		if [ "$DEBUGINITRD" -a "$DEBUGINITRD" != 'sh' ]; then
			echo "Last 20 lines of dmesg:"
			dmesg | tail -n 20
		fi

	EOF

	kmsg "geninitrd/$VERSION switching root"

	umount_all
	busybox_applet switch_root usleep mountpoint
	add_linuxrc <<-'EOF'
		[ ! -e /newroot/dev/console ] && mountpoint -q /newroot/dev && mknod -m 660 /newroot/dev/console c 5 1

		# switch root to empty dir will make kernel panic, so sleep 10s before it
		# switch_root needs to be pid 1, so there's no other way to recover from here
		# if /dev is missing, switch root will likely fail, give debug shell before that
		if [ ! -d /newroot/dev ]; then
			echo "/dev is missing, switch_root will likely fail"
			echo "if you booted with debugrd=sh, then you be given shell and you might able to recover this situation"
			debugshell
			[ "$DEBUGINITRD" ] || usleep 10000000
		fi

		# systemd[1]: /usr appears to be on its own filesytem and is not
		# already mounted. This is not a supported setup. Some things will
		# probably break (sometimes even silently) in mysterious ways. Consult
		# http://freedesktop.org/wiki/Software/systemd/separate-usr-is-broken
		# for more information.
		if chroot /newroot findmnt -s -n /usr > /dev/null 2> /dev/null; then
			echo trying to mount /usr
			chroot /newroot mount -n /usr
		fi

		exec switch_root /newroot $init ${1:+"$@"}

		# FIXME: this code is never executed, as "exec" does not return!

		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!"
		debugshell
	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" '(tolower($2) == "t" || tolower($2) == "d") && $3 == symbol {c = 0} END {exit c}' $mapfile
}

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

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

	verbose "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
		xz)
			sym=unxz
			prog=/usr/bin/xz
			;;
		lzma)
			sym=unlzma
			prog=/usr/bin/xz
			;;
		bzip2)
			sym=bzip2
			prog=/usr/bin/bzip2
			;;
		gzip)
			sym=gunzip
			prog=/bin/gzip
			;;
		lzo)
			sym=unlzo
			prog=/usr/bin/lzop
			;;
		zstd)
			sym=unzstd
			prog=/usr/bin/zstd
			;;
		lz4)
			sym=unlz4
			prog=/usr/bin/lz4
			;;
		none|no)
			# any existing sym will work
			sym=initrd_load
			prog=/bin/cat
			;;
		*)
			die "Unknown compressor $c"
			;;
		esac
		if sym_exists $map $sym && [ -x $prog ]; then
			echo $c
			return
		fi
	done

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

# compresses kernel image image
# in function so we could retry with other compressor on failure
compress_image() {
	local compressor="$1" IMAGE="$2" target="$3" tmp
	tmp=$(mktemp "$target".XXXXXX) || die "mktemp failed"

	case "$compressor" in
	xz)
		# don't use -9 here since kernel won't understand it
		xz --format=xz --check=crc32 --lzma2=preset=6e,dict=1MiB < "$IMAGE" > "$tmp" || return $?
		;;
	lzma)
		xz --format=lzma -9 < "$IMAGE" > "$tmp" || return $?
		;;
	bzip2)
		bzip2 -9 < "$IMAGE" > "$tmp" || return $?
		;;
	gzip)
		gzip -9 < "$IMAGE" > "$tmp" || return $?
		;;
	lzo)
		lzop -9 < "$IMAGE" > "$tmp" || return $?
		;;
	zstd)
		zstd -9 < "$IMAGE" > "$tmp" || return $?
		;;
	lz4)
		lz4 -9 -l < "$IMAGE" > "$tmp" || return $?
		;;
	none|no)
		cat < "$IMAGE" > "$tmp" || return $?
		;;
	esac

	mv -f "$tmp" "$target"
}

# prepend file to image
prepend_file_to_image() {
	local file="$1" target="$2" tmp
	tmp=$(mktemp "$target".XXXXXX) || die "mktemp failed"

	cat "$file" "$target" > "$tmp" || return $?

	mv -f "$tmp" "$target"
}

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

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

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

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-modprobe)
		USE_MODPROBE=yes
		;;
	--without-modprobe)
		USE_MODPROBE=no
		;;
	--without-swsusp)
		USE_SWSUSP=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_MULTIPATH=no
		;;
	--with-multipath=*)
		USE_MULTIPATH=${1#--with-multipath=}
		;;
	--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)
		if [ x"$verbose" = x"-v" ]; then
			verbose="-v -v"
		else
			verbose="-v"
		fi
		exec 3>&1
		;;
	--compress)
		COMPRESS=$2
		;;
	--compress=*)
		COMPRESS="${1#--compress=}"
		;;
	--nocompress)
		COMPRESS=no
		;;
	--nostrip)
		STRIP=
		;;
	--strip=*)
		STRIP="${1#--strip=}"
		;;
	--strip)
		STRIP=$2
		shift
		;;
	--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"
		;;
	--wait-block-dev)
		WAIT_BLOCK_DEV=$2
		shift
		;;
	*)
		if [ -z "$target" ]; then
			target="$1"
		elif [ -z "$kernel" ]; then
			kernel="$1"
		else
			usage
			exit 1
		fi
		;;
	esac

	shift
done

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

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

for dir in libx32 lib64 lib; do
	initrd_dir=/usr/$dir/initrd
	if [ -d "$initrd_dir" ]; then
		initrd_dirs="$initrd_dirs $initrd_dir"
	fi
done

kernel_version=$(echo "$kernel" | awk -F. '{gsub(/[_-].*/, "", $0); print sprintf("%03d%03d",$1,$2)}')
kernel_version_long=$(echo "$kernel" | awk -F. '{gsub(/[_-].*/, "", $0); print sprintf("%03d%03d%03d",$1,$2,$3)}')

verbose "# geninitrd $VERSION"
debug "Using initrd_dir: $initrd_dir"

busybox=$(find_tool $initrd_dir/busybox $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

check_initrd_fs() {
	local s sfound sym p prog map=/boot/System.map-$kernel
	case "$INITRDFS" in
		ext2)
			# TODO: symbols to check in case of ext2 used via ext3/4 subsystem
			sym=init_ext2_fs
			prog=/sbin/mke2fs
			;;
		rom|romfs)
			sym=init_romfs_fs
			prog=/sbin/genromfs
			;;
		cram|cramfs)
			sym=init_cramfs_fs
			prog=/sbin/mkcramfs
			;;
		initramfs)
			sym=__initramfs_start
			prog="/bin/cpio /usr/bin/find"
			;;
		*)
			die "Filesystem $INITRDFS on initrd is not supported by geninitrd"
			;;
	esac

	# only one is needed (for cases like ext2 via ext2 or via ext3 or via ext4 subsysytem)
	sfound=0
	for s in $sym; do
		sym_exists $map $s && sfound=1
		break
	done
	if [ "$sfound" -eq "0" ]; then
		die "Filesystem $INITRDFS on initrd is not supported by kernel"
	fi

	for p in $prog; do
		[ ! -x "$p" ] && die "$prog is missing"
	done
}
check_initrd_fs

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 [ "$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

# autodetect USB keyboards
find_modules_usbkbd

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

# autodetect SATA modules
find_modules_sata

# 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

if is_yes "$USE_SWSUSP"; then
	find_modules_swsusp
fi

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

find_modules_for_devpath "$rootdev"

# if USE_MULTIPATH is path to device, scan that too
# this is to bootstrap multipath setup into initrd.
if ! is_no "$USE_MULTIPATH" && ! is_yes "$USE_MULTIPATH"; then
	find_modules_multipath $USE_MULTIPATH
fi

find_module "-$rootFs"

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

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

find_modules_uvesafb
find_modules_fbsplash

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

verbose "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,sbin,etc,dev{,/pts,/shm},loopfs,var,proc,run,sys,tmp}

modules_install "$MODULES_TO_INSTALL"

if is_yes "$USE_MODPROBE"; then
	inst /lib/modules/$kernel/modules.{builtin,order} /lib/modules/$kernel
	depmod -b "$DESTDIR" $kernel
fi

# mknod'ing the devices instead of copying them works both with and
# without devfs...
mknod -m 600 "$DESTDIR/dev/console" c 5 1
mknod -m 666 "$DESTDIR/dev/null" c 1 3
mknod -m 666 "$DESTDIR/dev/zero" c 1 5
mknod -m 666 "$DESTDIR/dev/random" c 1 8
mknod -m 666 "$DESTDIR/dev/urandom" c 1 9
mknod -m 644 "$DESTDIR/dev/kmsg" c 1 11

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

add_linuxrc <<EOF
#!/bin/sh
# initrd generated by geninitrd/$VERSION
# on $(LC_ALL=C date)

EOF
load_font
mount_proc

kmsg "geninitrd/$VERSION starting"

inst_d /lib/geninitrd/
inst /lib/geninitrd/functions.initrd /lib/geninitrd/functions.initrd

add_linuxrc <<-EOF
	. /lib/geninitrd/functions.initrd
	# builtin defaults from geninitrd
	ROOT=$rootdev
	ROOTFS=$rootFs
EOF
add_linuxrc <<-'EOF'
	read CMDLINE < /proc/cmdline

	for arg in $CMDLINE; do
		if [ "${arg}" = "debuginitrd" ] || [ "${arg}" = "debugrd" ]; then
			DEBUGINITRD=yes
		fi
		if [ "${arg##debuginitrd=}" != "${arg}" ] || [ "${arg##debugrd=}" != "${arg}" ]; then
			DEBUGINITRD=${arg##debug*rd=}
		fi
		if [ "${arg##root=}" != "${arg}" ]; then
			ROOT=${arg##root=}
		fi
		if [ "${arg##rootfs=}" != "${arg}" ]; then
			ROOTFS=${arg##rootfs=}
		fi
		if [ "${arg##rootflags=}" != "${arg}" ]; then
			ROOTFLAGS=${arg##rootflags=}
		fi
		if [ "${arg##rootfsflags=}" != "${arg}" ]; then
			ROOTFSFLAGS=${arg##rootfsflags=}
		fi
		if [ "${arg##init=}" != "${arg}" ]; then
			INIT=${arg##init=}
		fi
	done

	# handling of invalid, rootfsflags, option
	if [ -n "$ROOTFSFLAGS" ]; then
		if [ -n "$ROOTFLAGS" ]; then
			ROOTFLAGS="$ROOTFLAGS,$ROOTFSFLAGS"
		else
			ROOTFLAGS="$ROOTFSFLAGS"
		fi
	fi

	if [ "$DEBUGINITRD" = "sh" ]; then
		# export some vars to subshell for debug to work
		export CMDLINE ROOT ROOTFS ROOTDEV ROOTFLAGS DEBUGINITRD INIT
		export LVM_ROOTVG LVM_SUSPENDVG LVM_VGVOLUMES
		export rootnr attrs majmin major minor device

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

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

# mount early
mount_tmp
mount_run

if is_yes "$USE_MODPROBE"; then
	if files_exist /lib/modprobe.d '*.conf'; then
		inst_d /lib/modprobe.d
		inst /lib/modprobe.d/*.conf /lib/modprobe.d
	fi
	if files_exist /etc/modprobe.d '*.conf'; then
		inst_d /etc/modprobe.d
		inst /etc/modprobe.d/*.conf /etc/modprobe.d
	fi
	if files_exist /etc/modprobe.d/$kernel '*.conf'; then
		inst_d /etc/modprobe.d/$kernel
		inst /etc/modprobe.d/$kernel/*.conf /etc/modprobe.d/$kernel
	fi
	inst_exec /sbin/modprobe /sbin
fi

modules_add_linuxrc $MODULES_TO_LOAD

# 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
else
	initrd_gen_mdev
fi

initrd_gen_uvesafb

initrd_gen_blkid_functions
busybox_applet usleep
echo "WAIT_BLOCK_DEV=${WAIT_BLOCK_DEV:-0}" | add_linuxrc
add_linuxrc <<-'EOF'
	wait_blk_dev() {
		local delay=0 dev="$1"
		while [ $delay -lt $WAIT_BLOCK_DEV ]; do
			if [ "${1#/dev/}" != "$1" ]; then
				dev="$1"
			else
				dev=$(find_blk_dev_blkid "$1")

				if [ "$?" = "2" ]; then
					break
				fi
			fi
			if [ -n "$dev" ] && [ -b "$dev" ]; then
				return 0
			else
				usleep 1000000
				delay=$((delay+1))
			fi
		done
		if [ $delay -gt 0 ]; then
			echo "Timeout reached while waiting for device '$1'"
		fi
		[ "${1#/dev/}" != "$1" ] && test -b "$dev"
	}
EOF

initrd_gen_luks
initrd_gen_dmraid
initrd_gen_multipath

if is_yes "$have_nfs"; then
	initrd_gen_nfs
else
	initrd_gen_md
	initrd_gen_lvm
	initrd_gen_bcache
	initrd_gen_luks
	is_dm "$rootdev" || echo 'wait_blk_dev "$ROOT"' | add_linuxrc
	initrd_gen_blkid
	initrd_gen_setrootdev
fi

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

initrd_gen_stop_udevd
initrd_gen_stop_mdev
initrd_gen_stop_uvesafb

# resume after killing local processes
initrd_gen_tuxonice
initrd_gen_suspend
initrd_gen_swsusp

# clean up env
add_linuxrc <<-'EOF'
if [ ! "$DEBUGINITRD" ]; then
	ifs=$IFS
	IFS="
	"
	for i in $(export -p); do
		i=${i#declare -x } # ksh/bash
		i=${i#export } # busybox

		case "$i" in
		*=*)
			: ;;
		*)
			continue ;;
		esac

		i=${i%%=*}

		[ -z "$i" ] && continue

		case "$i" in
			ROOT|PATH|HOME|TERM)
				:
				;;
			*)
				unset $i
				;;
		esac
	done
	IFS=$ifs
fi
EOF

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

initrd_gen_fbsplash
initrd_gen_fbcondecor

debug "Current /linuxrc:\n$(sed -e 's,^,| ,' $DESTDIR/linuxrc)"

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

IMAGESIZE=$(du -ks $DESTDIR | awk '{print int(($1+1023+512)/1024)*1024}')
verbose "image size: $IMAGESIZE KiB ($DESTDIR)"

verbose "Creating $INITRDFS image $IMAGE"
case "$INITRDFS" in
  ext2)
	dd if=/dev/zero of="$IMAGE" bs=1k count="$IMAGESIZE" 2> /dev/null
	# NOTE: ext2 label is max 16 chars
	mke2fs -q -F -b 1024 -m 0 -L "PLD/$kernel" "$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" || die "mount failed, check dmesg(1)"
	# 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 Linux/$kernel (geninitrd/$VERSION)"
	;;
  cram|cramfs)
	mkcramfs "$DESTDIR" "$IMAGE"
	;;
  initramfs)
	(cd $DESTDIR; find . | cpio --quiet -H newc -o > "$IMAGE")
	;;
  *)
	die "Filesystem $INITRDFS not supported by $PROGRAM"
esac

if [ "$INITRDFS" != "initramfs" ]; then
	CONFIG_BLK_DEV_RAM_SIZE=$(ikconfig | awk -F= '/^CONFIG_BLK_DEV_RAM_SIZE/{print $2}')
	if [ -z "$CONFIG_BLK_DEV_RAM_SIZE" ]; then
		CONFIG_BLK_DEV_RAM_SIZE=4096
		warn "No CONFIG_BLK_DEV_RAM_SIZE detected, fallback to $CONFIG_BLK_DEV_RAM_SIZE"
	fi

	if [ "$IMAGESIZE" -gt $CONFIG_BLK_DEV_RAM_SIZE ]; then
		warn "Your image size is larger than $CONFIG_BLK_DEV_RAM_SIZE, Be sure to boot kernel with ramdisk_size=$IMAGESIZE!"
	fi
fi

if ! is_no "$COMPRESS"; then
	compressor=$(find_compressor "$COMPRESS")
	verbose "Compressing $target with $compressor"

	# TODO: the image name (specified from kernel.spec) already contains
	# extension, which is .gz most of the time.
	compress_image "$compressor" "$IMAGE" "$target" || {
		if [ $compressor != gzip ]; then
			warn "Could not compress with $compressor, retrying with gzip"
			compress_image gzip "$IMAGE" "$target" || die "compress failed with gzip" $?
		else
			die "Could not compress image with $compressor"
		fi
	}
else
	cp -a "$IMAGE" "$target"
fi

# microcode support for lilo
if ! is_no "$LILO_MICROCODE"; then
	if [ -x /sbin/lilo -a -f "/boot/intel-ucode.img" ]; then
		verbose "Prepending $target with microcode image /boot/intel-ucode.img for LILO"
		prepend_file_to_image "/boot/intel-ucode.img" "$target"
	fi
fi

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

rm -rf "$DESTDIR" "$IMAGE"

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