#! /bin/bash

# mkinitrd - create the inital ramdisk images
# usage: see below usage() or call with -h
#
# Copyright (C) 1999-2005 SUSE Linux Products AG, Nuernberg, Germany
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
# USA.

# This file is kept in the following CVS repository:
#
# $Source: /suse/yast2/cvsroot/mkinitrd/mkinitrd,v $
# $Revision: 1.124.2.33 $

#
# Print usage and exit
#
usage() {
    cat<<EOM
	Create initial ramdisk images that contain all kernel modules
	needed in the early boot process, before the root file system
	becomes available. This usually includes SCSI and/or RAID
	modules, a file system module for the root file system, or
	a network interface driver module for dhcp.

        mkinitrd [options] [root_dir]

        options:
          -h               This Text.
          -k "kernel list" List of kernel images for which initrd files
                           are created. Defaults to all kernels found
			   in /boot.
          -i "initrd list" List of file names for the initrd; position
	  		   have match to "kernel list". Defaults to all
			   all kernels found in /boot.
          -m "module list" Modules to include in initrd. Defaults to the
                           INITRD_MODULES variable in /etc/sysconfig/kernel.
          -b boot_dir      Boot directory. Defaults to /boot.
          -d root_device   Root device. Defaults to the device from which
                           the root_dir is mounted. Overrides the rootdev
                           enviroment variable if set.
	  -s size          Add splash animation and bootscreen to initrd.
	  -t tmp_dir       Temporary directory. Defaults to $root_dir/var/tmp.
	  -D interface     Run dhcp on the specified interface.
	  -I interface     Configure the specified interface statically.
	  -a acpi_dsdt     Attach compiled ACPI DSDT (Differentiated System
	  		   Description Table) to initrd. This replaces the
			   DSDT of the BIOS. Defaults to the ACPI_DSDT
			   variable in /etc/sysconfig/kernel.
	  -S               Load policy file for SELinux if exist.
        root_dir:          The directory the root partition is mounted on.
                           Defaults to "/".
EOM
    exit
}

# Readlink is not present on some older distributions: emulate it.
readlink() {
    local path=$1 ll

    if [ -L "$path" ]; then
	ll="$(LC_ALL=C ls -l "$path" 2> /dev/null)"
	echo "${ll/* -> }"
    else
	return 1
    fi
}

default_kernel_images() {
	local regex kernel_image kernel_version version_version initrd_image
	local qf='%{NAME}-%{VERSION}-%{RELEASE}\n'

	case "$(uname -m)" in
	    s390|s390x)
		regex='image'
		;;
	    ppc|ppc64)
		regex='vmlinux'
		;;
	    *)	regex='vmlinuz'
		;;
	esac

	# user mode linux
	if grep -q UML /proc/cpuinfo; then
		regex='linux'
	fi

	kernel_images=""
	initrd_images=""
	for kernel_image in $(ls $root_dir/boot \
		| sed -ne "\|^$regex\(-[0-9.]\+-[0-9]\+-[a-z0-9]\+$\)\?|p") ; do

		# Note that we cannot check the RPM database here -- this
		# script is itself called from within the binary kernel
		# packages, and rpm does not allow recursive calls.

		[ -L "$root_dir/boot/$kernel_image" ] && continue
		kernel_version=$(/sbin/get_kernel_version \
				 $root_dir/boot/$kernel_image 2> /dev/null)
		initrd_image=$(echo $kernel_image | sed -e "s|${regex}|initrd|")
		if [ "$kernel_image" != "$initrd_image" -a \
		     -n "$kernel_version" -a \
		     -d "$root_dir/lib/modules/$kernel_version" ]; then
			kernel_images="$kernel_images /boot/$kernel_image"
			initrd_images="$initrd_images /boot/$initrd_image"
		fi
	done
}

# You can specify the root device via the environment variable rootdev (e.g.
# "rootdev=/dev/hda mkinitrd").

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# general configurable parameters

kernel_images=
initrd_images=
modules=
modules_set=
boot_dir=
splash="auto"
use_pivot_root=
use_static_binaries=1
acpi_dsdt=
use_selinux=

# architecture dependend changes:
case "$(uname -m)" in
    s390|s390x)
	splash=off
	;;
esac

while getopts :hk:i:m:b:d:s:St:D:I:a: a ; do
    case $a in
	\:|\?)	case $OPTARG in
		k)  echo "-k requires kernel list parameter"
		    ;;
		i)  echo "-i requires initrd list parameter"
		    ;;
		m)  echo "-m requires module list parameter"
		    ;;
		b)  echo "-b requires boot dir parameter"
		    ;;
		d)  echo "-d requires root device parameter"
		    ;;
		s)  echo "-s requires image size(s)"
		    ;;
		t)  echo "-t requires tmp dir parameter"
		    ;;
		D)  echo "-D requires dhcp interface parameter"
		    ;;
		I)  echo "-I requires network interface parameter"
		    ;;
		a)  echo "-a requires a DSDT parameter"
		    ;;
		*)  echo "Unknown option: -$OPTARG"
		    echo "Try mkinitrd -h"
		    ;;
	    esac
	    exit 1
	    ;;
	k)  kernel_images=$OPTARG
	    ;;
	i)  initrd_images=$OPTARG
	    ;;
	m)  modules=$OPTARG
	    modules_set=1
	    ;;
	b)  boot_dir=$OPTARG
	    ;;
	d)  rootdev=$OPTARG
	    ;;
	s)  splash=$OPTARG
	    ;;
	t)  tmp_dir=$OPTARG
	    ;;
	D)  interface=$OPTARG
	    interface=${interface#/dev/}
	    use_dhcp=1
	    use_pivot_root=1
	    ;;
	I)  interface=$OPTARG
	    interface=${interface#/dev/}
	    use_ipconfig=1
	    use_pivot_root=1
	    ;;
	a)  acpi_dsdt="$OPTARG"
	    ;;
	S)  use_selinux=1
	    ;;
	h)  usage
	    ;;
    esac
done
shift $(expr $OPTIND - 1)

if [ -n "$1" ]; then
    root_dir=${1%/}  # strip trailing slash
else
    root_dir=
fi

if [ -n "$boot_dir" ]; then
    boot_dir="${boot_dir#/}"
    boot_dir="/${boot_dir%/}"
else
    boot_dir="/boot"
fi
if [ ! -d "$boot_dir" ]; then
    echo "$boot_dir is not a directory" >&2
    exit 1
fi

# FIXME agruen: tmp_dir is the full, absolute path to a temporary directory
# that must not be a tmpfs file system. This was not consistently used in
# the previous version of mkinitrd.

if [ -n "$tmp_dir" ]; then
    tmp_dir="/${tmp_dir#/}"  # make sure it is an absolute path
else
    tmp_dir=$root_dir/var/tmp
fi
if [ ! -d "$tmp_dir" ]; then
    echo "$tmp_dir is not a directory" >&2
    exit 1
fi

# FIXME agruen: tmp_dir defaults to $root_dir/var/tmp. Not very nice
# to write into a directory of another installation without explicitly
# being asked to.
#
# If tmp_dir is unspecified, find a non-tmpfs temporary directory:
# /var/tmp, $root_dir/var/tmp, /tmp, $root_dir/tmp, etc.

# Check if the -k and -i settings are valid.
if [ $(set -- $kernel_images ; echo $#) -ne \
     $(set -- $initrd_images ; echo $#) ]; then
    echo "You have to specify -k and -i, or none of both. The -k" \
         "and -i options must each contain the same number of items." >&2
    exit 1
fi

if [ -z "$kernel_images" -o -z "$initrd_images" ]; then
    default_kernel_images
fi


# Check which shell and insmod binaries to use in initrd.
[ -x $root_dir/bin/ash ] \
    && initrd_shell_dynamic=$root_dir/bin/ash
if [ -n "$use_static_binaries" ]; then
    if [ -x $root_dir/bin/ash.static ]; then
	initrd_shell=$root_dir/bin/ash.static
    else
	initrd_shell=/bin/ash.static
    fi
else
    initrd_shell=$initrd_shell_dynamic
fi
# The shell-bang line to use inside initrd.
shebang=${initrd_shell#$root_dir}
shebang=${shebang%.static}

if [ -x /lib/klibc/bin/sh ]; then
    initrd_shell="/lib/klibc/bin/sh"
    shebang="/bin/sh"
fi

[ -x $root_dir/sbin/insmod ] \
    && initrd_insmod_dynamic=$root_dir/sbin/insmod
if [ -n "$use_static_binaries" ]; then
    if [ -x $root_dir/sbin/insmod.static ]; then
	initrd_insmod=$root_dir/sbin/insmod.static
    else
	initrd_insmod=/sbin/insmod.static
    fi
else
    initrd_insmod=$initrd_insmod_dynamic
fi

# FIXME agruen: Is is really necessary to fall back to /bin/ash.static
# and /sbin/initrd.static outside the $root_dir?

# maximum initrd size
image_blocks=40960
image_inodes=2048

# handle splash screen
case "$splash" in
off)
    splashsizes= ;;
auto)
    unset ${!splash_size_*}
    modes=
    for file in $root_dir/{etc/lilo.conf,boot/grub/menu.lst,proc/cmdline}; do
	[ -e $file ] || continue
	modes="$modes $(sed -e '/^[ \t]*#/d' $file \
		      | sed -ne 's/^.*vga[ \t]*=[ \t]*\([^ \t]*\).*/\1/p' \
		      | sed -ne '/^\([0-9]\+\|0[xX][0-9a-fA-F]\+\)$/p')"
    done

    for mode in $modes; do
	case $(($mode)) in  # $((...)) : Convert 0xFOO to decimal
	785|786) splash_size_640x480=1 ;;
	788|789) splash_size_800x600=1 ;;
	791|792) splash_size_1024x768=1 ;;
	794|795) splash_size_1280x1024=1 ;;
	*)	 vgahex=$(printf 0x%04x "$(($vga))")
	    	 size=$(hwinfo --framebuffer | \
		     sed -ne 's/^.*Mode '$vgahex': \([^ ]\+\) .*$/\1/p' \
		     	2>/dev/null)
		 eval splash_size_$size=1 ;;
	esac
    done
    splashsizes="$(for x in ${!splash_size_*}; do
    			echo ${x#splash_size_}
		   done)"
    unset ${!splash_size_*}
    ;;
*)
    splashsizes=$splash
    ;;
esac

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# should be nothing to change below...

PATH=/sbin:/usr/sbin:$PATH

work_dir=$(mktemp -qd $tmp_dir/mkinitrd.XXXXXX)
if [ $? -ne 0 ]; then
	echo "$0: Can't create temp dir, exiting." >&2
	exit 1
fi
is_mounted=
is_mounted_small=

umount_proc() {
    [ "$mounted_proc" ] && umount $mounted_proc
    mounted_proc=
}

umount_sysfs() {
    [ "$mounted_sysfs" ] && umount $mounted_sysfs
    mounted_sysfs=
}

cleanup() {
    [ "$is_mounted" ] && umount $tmp_mnt
    is_mounted=
    [ "$is_mounted_small" ] && umount $tmp_mnt_small
    is_mounted_small=
    rm -f $tmp_initrd $tmp_initrd.gz $tmp_msg tmp_initrd_small \
	  $tmp_initrd_small.gz
    [ -d "$tmp_mnt" ] && rmdir $tmp_mnt
    [ -d "$tmp_mnt_small" ] && rmdir $tmp_mnt_small
    initrd_bins=()
}

cleanup_finish() {
    umount_proc
    umount_sysfs
    [ -d "$work_dir" ] && rmdir $work_dir
}

handle_terminate() {
    echo "(received signal)

Interrupted, cleaning up." >&2
    cleanup
    cleanup_finish
    exit 255
}

trap handle_terminate 1 2 3 15

error() {
    echo "$2" >&2
    cleanup
    cleanup_finish
    exit $1
}

oops() {
    exit_code=$1
    shift
    echo "$@" >&2
}

# Check if module $1 is listed in $modules.
has_module() {
    case " $modules " in
	*" $1 "*)   return 0 ;;
    esac
    return 1
}

# Check if any of the modules in $* are listed in $modules.
has_any_module() {
    local module
    for module in "$@"; do
	has_module "$module" && return 0
    done
}

# Add module $1 at the end of the module list.
add_module() {
    local module
    for module in "$@"; do
	has_module "$module" || modules="$modules $module"
    done
}

# Install a binary file
cp_bin() {
    cp -a "$@" \
    || exit_code=1

    # Remember the binaries installed. We need the list for checking
    # for dynamic libraries.
    while [ $# -gt 1 ]; do
	initrd_bins[${#initrd_bins[@]}]=$1
	shift
   done
}

do_chroot() {
    local CHROOT
    if [ -n "$root_dir" ]; then
	CHROOT=/usr/bin/chroot
	if [ ! -x $CHROOT ]; then
	    error 2 "I need $CHROOT."
	else
	    $CHROOT "$root_dir" "$@"
	fi
    else
	"$@"
    fi

}

# Resolve dynamic library dependencies. Returns a list of symbolic links
# to shared objects and shared object files for the binaries in $*. The
# binaries must be relative to $root_dir.
shared_object_files() {
    local LDD CHROOT initrd_libs lib_files lib_links lib link

    LDD=/usr/bin/ldd
    if [ ! -x $root_dir$LDD ]; then
	error 2 "I need $root_dir$LDD."
    fi

    initrd_libs=( $(
	do_chroot $LDD "${@#$root_dir}" \
	| sed -e '\: => :!d' -e 's:.* => \([^ ]*\).*:\1:'
    ) )

    # Evil hack: On some systems we have generic as well as optimized
    # libraries, but the optimized libraries may not work with all
    # kernel versions (e.g., the NPTL glibc libraries don't work with
    # a 2.4 kernel). Use the generic versions of the libraries in the
    # initrd (and guess the name).
    local n optimized
    for ((n=0; $n<${#initrd_libs[@]}; n++)); do
	lib=${initrd_libs[$n]}
	optimized="$(echo "$lib" | sed -e 's:.*/\([^/]\+/\)[^/]\+$:\1:')"
	lib=${lib/$optimized/}
	if [ "${optimized:0:3}" != "lib" -a -f "$lib" ]; then
	    #echo "[Using $lib instead of ${initrd_libs[$n]}]" >&2
	    initrd_libs[$n]="${lib/$optimized/}"
	fi
    done

    for lib in "${initrd_libs[@]}"; do
	case "$lib" in
	linux-gate*)
	    # This library is mapped into the process by the kernel
	    # for vsyscalls (i.e., syscalls that don't need a user/
	    # kernel address space transition) in 2.6 kernels.
	    continue ;;
	/*)
	    lib="${lib:1}" ;;
	*)
	    # Library could not be found.
	    oops 7 "Dynamic library $lib not found"
	    continue ;;
	esac

	while [ -L "$root_dir/$lib" ]; do
	    echo $lib
	    link="$(readlink "$root_dir/$lib")"
	    if [ x"${link:0:1}" == x"/" ]; then
	        lib=${link#/}
	    else
	        lib="${lib%/*}/$link"
	    fi
	done
	echo $lib
    done \
    | sort -u
}

# Resolve module dependencies and parameters. Returns a list of modules and
# their parameters. The module names returned are relative to $root_dir.
resolve_modules() {
    local kernel_version=$1 module
    shift

    for module in "$@"; do
	module=${module#.o}  # strip trailing ".o" just in case.
	module=${module#.ko}  # strip trailing ".ko" just in case.
	case $kernel_version in
	2.4.*)
	    # Really old systems don't have modprobe.old.
	    local modprobe
	    if [ -e $root_dir/sbin/modprobe.old ]; then
	       modprobe=$root_dir/sbin/modprobe.old
	    else
	       modprobe=$root_dir/sbin/modprobe
	    fi
	    tmp_conf="$(mktemp $root_dir/tmp/mkinitrd.XXXXXX)"
	    {   echo "depfile=/lib/modules/$kernel_version/modules.dep"
		[ -f $root_dir/etc/modules.conf ] \
		    && sed -e '/^[ \t]*depfile=/d' $root_dir/etc/modules.conf
	    } > "$tmp_conf"
	    module_list=$( do_chroot \
	    	${modprobe#$root_dir} -C "${tmp_conf#$root_dir}" \
				      -v -n $module 2> /dev/null \
		| sed -ne 's:.*insmod /\?::p' )
	    rm -f "$tmp_conf"
	    ;;
	*)
	    local with_modprobe_conf
	    if [ -e $root_dir/etc/modprobe.conf ]; then
		with_modprobe_conf="-C /etc/modprobe.conf"
	    fi
	    module_list=$( do_chroot \
	       /sbin/modprobe $with_modprobe_conf \
			      --ignore-install \
			      --set-version $kernel_version \
			      --show-depends $module 2> /dev/null \
	       | sed -ne 's:.*insmod /\?::p' )
	    ;;
	esac
	if [ -z "$module_list" ]; then
	    case $module in
	    scsi_mod|sd_mod|md)  # modularized in 2.4.21
		# These modules were previously compiled into the kernel,
		# and were modularized later. They will be missing in older
		# kernels; ignore error messages.
	    	;;
	    xfs_dmapi|xfs_support)  # gone in 2.4.20
		# These modules do no longer exist. Do not produce an error
		# message, but warn that they should be removed manually.
    		echo -n "Warning: Module $module no longer exists, and" \
		     "should be removed " >&2
	        if [ -e $root_dir/etc/sysconfig/kernel ]; then
		    echo "from /etc/sysconfig/kernel." >&2
	        elif [ -e $root_dir/etc/rc.config ]; then
		    echo "from /etc/rc.config." >&2
		else
		    echo "." >&2
		fi
		;;
	    *)
		oops 7 "Cannot determine dependencies of module $module." \
		       "Is modules.dep up to date?"
		;;
	    esac
	fi
	echo "$module_list"
    done \
    | awk ' # filter duplicates: we must not reorder modules here!
	$1 in seen  { next }
		    { seen[$1]=1
		      print
		    }
    '
    rm -f $temp
}

# Test if file $1 is smaller than file $2 (kilobyte granularity)
smaller_file() {
    local size1=$(ls -l "$1" |awk '{print $5}')
    local size2=$(ls -l "$2" |awk '{print $5}')
    [ $size1 -lt $size2 ]
}

# Cat from stdin to linuxrc, removing leading whitespace up to pipe symbol.
# Note that here documents can only be indented with tabs, not with spaces.
cat_linuxrc() {
    sed 's/^[ \t]*|//' >> $linuxrc
    echo >> $linuxrc
}

# Attach ACPI DSDT if necessary.
attach_dsdt() {
    local initrd_image=$1

    if [ -z "$acpi_dsdt" ]; then
	if [ -f "$root_dir/etc/sysconfig/kernel" ]; then
	    . $root_dir/etc/sysconfig/kernel
	    acpi_dsdt="$ACPI_DSDT"
	fi
    fi
    if [ -z "$acpi_dsdt" ]; then
	return
    fi
    if [ ! -f "$acpi_dsdt" ]; then
	oops 2 "ACPI DSDT $acpi_dsdt does not exist."
	return
    fi
    if ! grep -q "DSDT" "$acpi_dsdt" ; then
	oops 2 "File $acpi_dsdt is not a valid ACPI DSDT. Ignoring."
    elif grep -qE 'DefinitionBlock|char|dsl|aml' "$acpi_dsdt" ; then
    	oops 2 "ACPI DSDT $acpi_dsdt does not seem to be in binary form." \
	       "Will not attach this to $initrd_image."
    else
	{   echo -ne "INITRDDSDT123DSDT123\0"
	    cat "$acpi_dsdt"
	    echo -ne "INITRDDSDT321DSDT321\0"
	} >> $initrd_image
	echo -e "ACPI DSDT:\t$acpi_dsdt"
    fi
}

# Check for IDE module
check_ide_modules() {
    local modules_dir=$1 ide_modules

    # Check for PCI bus
    if [ ! -d /proc/bus/pci ]; then
        # Not found, no error
        return
    fi

    pcimap_file="${modules_dir}/modules.pcimap"

    if [ ! -f "$pcimap_file" ] ; then
	echo "No modules.pcimap file found" >&2
	return
    fi

    while read pci_id none class_id dev_id rev_id; do
	set -- $(IFS=: ; echo $dev_id)
	vendor_id=$1
	device_id=$2
	if [ "$class_id" == "0101:" ] ; then
	    pci_vendor_id=$(printf "0x%08x" $((0x$vendor_id)))
	    pci_device_id=$(printf "0x%08x" $((0x$device_id)))
	    line=$(grep "$pci_vendor_id $pci_device_id" $pcimap_file)
	    if [ "$line" ]; then
		set -- $line
		add_module $1
		ide_modules="$ide_modules $1"
	    fi
	fi
    done < <(/sbin/lspci -n)
    if [ "$ide_modules" ]; then
	if grep -q ide_disk ${modules_dir}/modules.dep; then
	    add_module ide-disk
	fi
	if grep -q ide_floppy ${modules_dir}/modules.dep; then
	    add_module ide-floppy
	fi
	if grep -q ide_cd ${modules_dir}/modules.dep; then
	    add_module ide-cd
	fi
	echo
    fi

}

# NOTE: The mkdevn, devmajor and block_driver functions are reused
#       inside the initrd, so keep them compatible with ash !!!
#

# Convert a major:minor pair into a device number
mkdevn() {
    local major=$1 minor=$2
    expr '(' $minor % 256 ')' + '(' $major '*' 256 ')' + \
	 '(' '(' $minor / 256 ')' '*' 256 '*' 4096 ')'
}

# Extract the major part from a device number
devmajor() {
    local devn=$1
    expr '(' $devn / 256 ')' % 4096
}

block_driver() {
    local devn block major driver
    case "$1" in
    /dev/*) devn=$(devnumber $1 2> /dev/null) ;;

    *:*)    # major:minor
	    set -- $(IFS=: ; echo $1)
	    devn=$(mkdevn $1 $2) ;;

    [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])
	    # hex device number
	    devn=$((0x$1)) ;;
    esac

    [ -z "$devn" ] && return 1
    while read major driver; do
	[ "$major" = Block ] && block=1  # in block device section
	[ -z "$driver" -o -z "$block" ] && continue
	if [ $(devmajor $devn) = $major ]; then
	    echo $driver
	    return 0
	fi
    done < /proc/devices
    return 1
}

# (We are using a devnumber binary inside the initrd.)
devnumber() {
    set -- $(ls -lL $1)
    mkdevn ${5%,} $6
}

# Get the interface information for ipconfig
get_ip_config() {
    local iface
    local iplink
    local iproute

    iface=$1
    iplink=$(ifconfig $iface | grep "inet ")

    set -- $iplink
    if [ "$1" == "inet" ]; then
	shift

	while [ "$1" ]; do
	    case "$1" in
		addr:*)
		    ipaddr=${1#addr:}
		    ;;
		Bcast:*)
		    bcast=${1#Bcast:}
		    ;;
		P-t-P:*)
		    srvaddr=${1#P-t-P:}
		    ;;
		Mask:*)
		    netmask=${1#Mask:}
		    ;;
	    esac
	    shift
	done
    fi
    iproute=$(route -n | grep $iface | grep UG)
    if [ $? -eq 0 ]; then
	set -- $iproute
	gwaddr=$2
    fi
    hostname=$(hostname)
    echo "$ipaddr:$srvaddr:$gwaddr:$netmask:$hostname:$iface:none"
}  

###################################################################
#
# S/390 specific procedures
#
s390_check_lvm2() {
    local vgname
    local devname
    
    # Check whether the LVM is on zfcp or DASD
    vgname=$(lvdisplay -c $1 | cut -d : -f 2)

    if [ "$vgname" ]; then
	for devname in $(pvdisplay -c | grep $vgname | cut -d : -f 1); do
	    case "$devname" in
		*/dasd*)
		    s390_enable_dasd=1
		    ;;
		*/sd*)
		    s390_enable_zfcp=1
		    ;;
		*)
		    ;;
	    esac
	done
    fi
}

s390_check_evms() {
    local evms_cmd
    local evms_reg
    local evms_cont
    local evms_seg
    local evms_dsk

    if [ ! -x /sbin/evms ]; then
	return 1
    fi

    if [ -n "$1" ]; then
	evms_cmd="q:r,v=$1"

	while read a b c d; do
	    if [ "$a $b" = "Region Name:" ]; then
		evms_reg="$evms_reg $c"
	    fi
	done < <( echo "$evms_cmd" | /sbin/evms -s )
    fi

    for reg in $evms_reg; do
	evms_cmd="q:c,r=$reg"
	
	while read a b c d; do
	    if [ "$a $b" = "Container Name:" ]; then
		evms_cont="$evms_cont $c"
	    fi
	done < <(echo "$evms_cmd" | /sbin/evms -s )
    done
    
    for cont in $evms_cont; do
	evms_cmd="q:s,c=$cont"
	
	while read a b c d; do
	    if [ "$a $b" = "Segment Name:" ]; then
		evms_seg="$evms_seg $c"
	    fi
	done < <(echo "$evms_cmd" | /sbin/evms -s )
    done

    for seg in $evms_seg; do
	evms_cmd="q:d,s=$seg"
	while read a b c d; do
	    if [ "$a $b $c" = "Logical Disk Name:" ]; then
		evms_dsk="$evms_dsk $d"
	    fi
	done < <(echo "$evms_cmd" | /sbin/evms -s )
    done

    for dsk in $evms_dsk; do
	case $dsk in
	    dasd*)
		s390_enable_dasd=1;
		;;
	    sd*)
		s390_enable_zfcp=1
		;;
	esac
    done
}

s390_check_dasd() {
    local devn=$(devnumber $1)

    for dir in /sys/block/*/*; do
	if [ -d "$dir" ] && [ -f "$dir/dev" ]; then
	    IFS=":" read major minor < $dir/dev
	    if (($devn == $(mkdevn $major $minor) )); then
		path=$dir
		break;
	    fi
	fi
    done
    if [ "$path" ] && [ -d ${path}/../device ]; then
	if [ -r ${path}/../device/discipline ]; then
	    s390_enable_dasd=1
	fi
    fi
}

s390_check_zfcp() {
    local devn=$(devnumber $1)

    for dir in /sys/block/*/*; do
	if [ -d "$dir" ] && [ -f "$dir/dev" ]; then
	    IFS=":" read major minor < $dir/dev
	    if (($devn == $(mkdevn $major $minor) )); then
		path=$dir
		break;
	    fi
	fi
    done
    
    if [ "$path" ] && [ -d "$path/../device" ] && [ -e "$path/../device/scsi_level" ] ; then
	s390_enable_zfcp=1
    fi
}

s390_check_dcss() {
    local dir
    local segment

    for dir in /sys/devices/dcssblk/*; do
	if [ -d "$dir" ] ; then
	    segment=${dir#/sys/devices/dcssblk/}
	    s390_dcss_segments="$s390_dcss_segments $segment"
	fi
    done
    if [ -n "$s390_dcss_segments" ]; then
	add_module dcssblk
    fi
}

s390_zfcp_proc() {
   zfcp_param=
   if [ ! -s $root_dir/etc/zfcp.conf ] && cat /proc/modules | grep -q "^zfcp"
   then
	fcpconf=$(cat /proc/scsi/zfcp/map)
	if [ -n "$fcpconf" ]; then
	    echo "$fcpconf" > $root_dir/etc/zfcp.conf
	    chmod 644 $root_dir/etc/zfcp.conf
	fi
   fi
   if [ -s $root_dir/etc/zfcp.conf ]; then
	add_module zfcp

	zfcp_param=$(sed -e 's,#.*,,' -e '/^[[:blank:]]*$/d' \
		     $root_dir/etc/zfcp.conf)

	if [ -z "$zfcp_param" ]; then
	    zfcp_param=$(modprobe -v -n zfcp|sed -n '/map=/s/.*zfcp.o //p')
	fi

	if [ -z "$zfcp_param" ]; then
	    error 1 "\
zfcp module loaded but root_dir/etc/zfcp.conf empty.
don't know how to configure zfcp in initrd.

you can save the current configuration with this command:

    cat /proc/scsi/zfcp/map >> root_dir/etc/zfcp.conf
"
	fi
    fi
}

# Detect all zfcp disks
# We need to activate all disks in the same order
# as found in the running system to get the same
# behaviour during booting.
s390_zfcp_sysfs() {
    local path

    if [ "$s390_enable_zfcp" ] ; then
	# Root is on SCSI, detect all SCSI devices
	for dev_dir in /sys/class/scsi_device/*; do
	    if [ -d "$dev_dir" ] && [ -e "$dev_dir/device" ]; then
		cd $dev_dir;
		cd $(readlink device);
		if [ -r ./hba_id ]; then
		    read fcp_disk_hba < ./hba_id
		    read fcp_disk_wwpn < ./wwpn
		    read fcp_disk_lun < ./fcp_lun
	    
		    s390_zfcp_disks="$s390_zfcp_disks $fcp_disk_hba:$fcp_disk_wwpn:$fcp_disk_lun"

		    for id in $s390_zfcp_hbas; do
			if [ "$id" == "$fcp_disk_hba" ]; then
			    fcp_disk_hba=
			fi
		    done
		    [ "$fcp_disk_hba" ] && s390_zfcp_hbas="$s390_zfcp_hbas $fcp_disk_hba"
		fi
	    fi
	done
	if [ "$s390_zfcp_hbas" ]; then
	    add_module sd_mod
	    add_module zfcp
	fi
    fi

}

s390_dasd_sysfs() {
    local type
    local use_diag

    if [ "$s390_enable_dasd" ]; then
	# Root device is on a dasd device, enable all dasd disks
	for dir in /sys/block/dasd? /sys/block/dasd?? /sys/block/dasd???; do
	    if [ -d "$dir" ] && [ -d ${dir}/device ]; then
		pushd $dir > /dev/null
		cd $(readlink device)
		if [ -r ./discipline ]; then
		    read type < ./discipline
		    
		    case $type in
			ECKD)
			    add_module dasd_eckd_mod
			    use_diag=0
			    ;;
			FBA)
			    add_module dasd_fba_mod
			    use_diag=0
			    ;;
			DIAG)
			    add_module dasd_diag_mod
			    use_diag=1
			    ;;
			*)
			    ;;
		    esac
		    s390_dasd_disks="$s390_dasd_disks $(basename $PWD):$use_diag"
		fi
		popd > /dev/null
	    fi
	done

    fi
}

s390_dasd_proc() {
    local zipl_conf_with_dasd dasd_module

    if [ -f $root_dir/etc/zipl.conf ] \
       && grep -q '^[[:space:]]*parameters[[:space:]]*=' \
	    $root_dir/etc/zipl.conf; then
	    zipl_conf_with_dasd=1
    fi

    if do_chroot modprobe -c \
       | grep -q '^[[:space:]]*options[[:space:]]\+dasd_mod' ; then
	dasd_module=1
    fi

    if grep -q -e "^dasd" /proc/modules \
       || [ -n "$zipl_conf_with_dasd" ] \
       || [ -n "$dasd_module" ] \
       || has_module dasd_mod ; then

	if [ ! "$zipl_conf_with_dasd" -a ! "$dasd_module" ]; then
	    error 1 "\
The dasd module is required, but no dasd configuration was found in
root_dir/etc/zipl.conf or root_dir/etc/modules.conf."
	fi

	if grep -q ECKD /proc/dasd/devices ; then
	    add_module dasd_eckd_mod
	fi

	if grep -q FBA  /proc/dasd/devices ; then
	    add_module dasd_fba_mod
	fi

	if grep -q DIAG /proc/dasd/devices ; then
	    add_module dasd_diag_mod
	fi
    else
	# /proc not mounted or somesuch. Enable all dasd modules
	add_module dasd_mod
	add_module dasd_eckd_mod
	add_module dasd_fba_mod
	add_module dasd_diag_mod
    fi
}

###################################################################
#
# Create the initrd image $2 for kernel $1 (both are absolute path names).
#
mkinitrd_kernel() {
    local kernel_image=$1 initrd_image=$2
    local kernel_version need_mount sysfs_root use_udev need_block_driver
    local need_raidautorun
    local need_raidstart
    local -a features

    tmp_mnt=$work_dir/mnt
    tmp_mnt_small=${tmp_mnt}_small
    tmp_msg=$work_dir/msg$$
    linuxrc=$tmp_mnt/linuxrc

    if ! [ -f "$root_dir$kernel_image" ] ; then
	echo "No kernel image $root_dir$kernel_image found" >&2
	return
    fi

    kernel_version=$(/sbin/get_kernel_version $root_dir$kernel_image)

    #echo -e "Kernel version:\t$kernel_version"
    echo -e "Kernel image:\t$root_dir$kernel_image"
    echo -e "Initrd image:\t$root_dir$initrd_image"

    if [ ! -d "$root_dir/lib/modules/$kernel_version/misc" -a \
	 ! -d "$root_dir/lib/modules/$kernel_version/kernel" ]; then
	oops 2 "No modules found for kernel $kernel_version"
        return
    fi

    # create an empty initrd
    mkdir $tmp_mnt || error 1 "could not create temporary directory"
    dd if=/dev/zero of=$tmp_initrd bs=1k count=$image_blocks 2>/dev/null
    mke2fs -q -F -b 1024 -m 0 -N $image_inodes $tmp_initrd 2>/dev/null 1>&2
    tune2fs -i 0 $tmp_initrd >/dev/null 2>&1

    # check for loop device
    grep -q loop /proc/devices
    if [ $? != 0 ] ; then
	if [ -f /lib/loop.o ] ; then
	    insmod /lib/loop.o
	else
	    modprobe loop
	fi
    fi
    # mount it
    if ! mount -t ext2 -oloop $tmp_initrd $tmp_mnt ; then
	error 3 "failed to mount image"
    fi
    is_mounted=1
    # fill the initrd
    rmdir $tmp_mnt/lost+found
    mkdir -p $tmp_mnt/{sbin,etc,bin,dev}
    mkdir -p $tmp_mnt/dev/shm
    initrd_devices=$(echo dev/{zero,null,ram0,ram1,ram2,ram,ramdisk} \
			  dev/{console,md0})

    # Create a dummy /etc/mtab for mount/umount
    echo -n > $tmp_mnt/etc/mtab

    case "$(uname -m)" in
	s390|s390x)
	    ;;
	*)  initrd_devices="$initrd_devices $(echo dev/{fb0,tty1,tty2})"
	    ;;
    esac
    (cd ${root_dir:-/} ; cp -R --parents $initrd_devices $tmp_mnt )
    mkdir -p $tmp_mnt${initrd_shell%/*}
    cp_bin $initrd_shell $tmp_mnt$initrd_shell
    if [ $shebang != $initrd_shell ]; then
	ln -s $initrd_shell $tmp_mnt$shebang
    fi
    if [ $shebang != /bin/sh ]; then
	ln -s $shebang $tmp_mnt/bin/sh
    fi
    cp_bin $initrd_insmod $tmp_mnt/sbin/insmod 2>/dev/null \
	|| error 5 "no static insmod"

    cp_bin $root_dir/sbin/blkid $tmp_mnt/sbin

    # If exists, copy also insmod.static.old, it is created by modutils for
    # 2.5.47+ kernels and it is needed by these to work with older kernels.
    if [ -r $initrd_insmod.old ] ; then
      cp_bin $initrd_insmod.old $tmp_mnt/sbin/insmod.old 2>/dev/null
    fi

   # SELinux: Binary policy file and load_policy utility for loading it.
   if [ -n "$use_selinux" ] ; then
	if [ -f $root_dir/etc/security/selinux/policy.15 -a \
	     -f $root_dir/usr/sbin/load_policy ] ; then
	   echo -e "SELinux policy:\tadded"
	   mkdir -p $tmp_mnt/selinux
	   mkdir -p $tmp_mnt/etc/security/selinux
	   cp -p $root_dir/etc/security/selinux/policy.15 \
	       $tmp_mnt/etc/security/selinux
	   cp_bin $root_dir/usr/sbin/load_policy $tmp_mnt/sbin/load_policy
	else
	   echo -e "SELinux policy:\tnot found"
	fi
    fi

    case "$kernel_version" in
    2.6*)
	sysfs_root=1
	use_udev=1
	;;
    esac

    if has_module iscsi ; then
	features=(${features[@]} iscsi)
	if test -x $root_dir/usr/sbin/iscsid; then
	    cp_bin $root_dir/usr/sbin/iscsid $tmp_mnt/sbin/iscsid
	elif test -x $root_dir/sbin/iscsid; then
	    cp_bin $root_dir/sbin/iscsid $tmp_mnt/sbin/iscsid
	else
	    echo -e "iSCSI error:\tcan't find iscsid"
	fi
	cp_bin $root_dir/bin/usleep $tmp_mnt/bin/usleep
	mkdir -p $tmp_mnt/var/run
	mkdir -p $tmp_mnt/var/lib/iscsi
	need_mount=1
	use_pivot_root=
    fi

    if [ -n "$sysfs_root" ]; then
	mkdir -p $tmp_mnt/sys
	cp_bin $root_dir/usr/bin/expr $tmp_mnt/bin
	cp_bin $root_dir/bin/cat $tmp_mnt/bin
	need_mount=1
    fi

    if [ -n "$s390_dasd_disks" ]; then
	cp_bin $root_dir/sbin/dasd_configure $tmp_mnt/sbin
	cp_bin $root_dir/sbin/dasdview $tmp_mnt/sbin
    fi

    if [ -n "$s390_zfcp_disks" ]; then
	cp_bin $root_dir/sbin/zfcp_host_configure $tmp_mnt/sbin
	cp_bin $root_dir/sbin/zfcp_disk_configure $tmp_mnt/sbin
	cp_bin $root_dir/bin/sed $tmp_mnt/bin
    fi

    if [ -n "$use_pivot_root" ] ; then
	features=(${features[@]} pivot-root)
	cp_bin $root_dir/sbin/pivot_root $tmp_mnt/bin
	cp_bin $root_dir/bin/cat $tmp_mnt/bin
	echo "#! $shebang" > $tmp_mnt/bin/init
	echo 'echo initrd failed, goodbye...' >> $tmp_mnt/bin/init
	chmod 755 $tmp_mnt/bin/init
	mkdir -p $tmp_mnt/mnt
	need_mount=1
    fi

    if [ -n "$use_udev" ] ; then
	features=(${features[@]} udev)
	mkdir -p $tmp_mnt/lib/klibc/bin
	for klibc_bin in devnumber sed sleep ata_identify scsi_id \
			 umount; do
	    if [ -e $root_dir/lib/klibc/bin/$klibc_bin ]; then
		cp_bin $root_dir/lib/klibc/bin/$klibc_bin \
		    $tmp_mnt/lib/klibc/bin
		if [ ! -x $tmp_mnt/bin/$klibc_bin ]; then
		    ln -s /lib/klibc/bin/$klibc_bin $tmp_mnt/bin
		fi
	    fi
	done
	for bin in mknod rm; do
	    cp_bin $root_dir/bin/$bin $tmp_mnt/bin
	done

	cp_bin $root_dir/sbin/udev $tmp_mnt/sbin/udev
	cp_bin $root_dir/sbin/udevinfo.static $tmp_mnt/sbin/udevinfo
	cp_bin $root_dir/sbin/udevstart $tmp_mnt/sbin/udevstart
	cp_bin $root_dir/sbin/scsi_id $tmp_mnt/sbin
	cp_bin $root_dir/sbin/udev.*.sh $tmp_mnt/sbin
	mkdir -p $tmp_mnt/etc/udev
	cp -a $root_dir/etc/udev/* $tmp_mnt/etc/udev
	need_block_driver=1
    fi

    if [ -n "$root_lvm" ] ; then
	features=(${features[@]} lvm)
	mkdir -p $tmp_mnt/etc/lvmtab.d
	[ -z "$use_udev" ] &&
	    cp -a $root_dir/dev/lvm  $tmp_mnt/dev
	cp_bin $root_dir/sbin/{vgscan,vgchange} $tmp_mnt/sbin
	cp_bin $root_dir/bin/cat $tmp_mnt/bin
	need_mount=1
	need_block_driver=1
    fi

    if [ -n "$root_dm" ] ; then
	features=(${features[@]} dm/lvm2)
	mkdir -p $tmp_mnt/etc/lvm
	mkdir -p $tmp_mnt/var/lock/lvm
	if [ -z "$use_udev" ]; then
	    cp_bin $root_dir/sbin/devmap_mknod.sh $tmp_mnt/sbin
	fi
	cp_bin $root_dir/sbin/{vgscan,vgchange,lvm} $tmp_mnt/sbin
	cp_bin $root_dir/bin/{cat,sed,mkdir,mknod,ls} $tmp_mnt/bin
	cp -a $root_dir/etc/lvm/lvm.conf $tmp_mnt/etc/lvm
	need_mount=1
	need_block_driver=1
    fi

    if [ -n "$root_evms" ] ; then
	features=(${features[@]} dm/evms2)
	if [ -z "$use_udev" ]; then
	    cp_bin $root_dir/sbin/devmap_mknod.sh $tmp_mnt/sbin
	fi
	cp_bin $root_dir/sbin/{evms_activate,dmsetup} $tmp_mnt/sbin
	cp_bin $root_dir/bin/{cat,sed,mkdir,mknod,rm} $tmp_mnt/bin
	mkdir -p $tmp_mnt/etc
	cp -a $root_dir/etc/evms.conf $tmp_mnt/etc
	if [ -d $root_dir/lib64/evms ]; then
	    evms_lib="lib64/evms"
	else
	    evms_lib="lib/evms"
	fi
	mkdir -p $tmp_mnt/$evms_lib
	SD=$(ls -A $root_dir/$evms_lib | tail -n 1)
	(cd $tmp_mnt/$evms_lib && mkdir -p $SD)
	cp_bin $root_dir/$evms_lib/$SD/* $tmp_mnt/$evms_lib/$SD
	rm -f $tmp_mnt/$evms_lib/*/*{ext2,jfs,ogfs,reiser,swap,xfs}*so
	need_mount=1
	need_block_driver=1
    fi

    if has_any_module raid0 raid1 raid5 linear multipath; then
	cp_bin $root_dir/sbin/raidautorun $tmp_mnt/sbin
	need_raidautorun=1
	if [ -s "$root_dir/etc/raidtab" ]; then
	    cp_bin $root_dir/sbin/raidstart $tmp_mnt/sbin
	    cat $root_dir/etc/raidtab > $tmp_mnt/etc/raidtab
	    need_raidstart=1
	fi
    fi

    if [ -n "$use_dhcp" ] ; then
	features=(${features[@]} dhcp\($interface\))
	cp_bin $root_dir/sbin/dhcpcd $tmp_mnt/bin
	cp_bin $root_dir/bin/kill $tmp_mnt/bin
	mkdir -p $tmp_mnt/var/lib/dhcpcd
	mkdir -p $tmp_mnt/var/run
	need_mount=1
    fi

    if [ -n "$use_ipconfig" ] ; then
	features=(${features[@]} static\($interface\))
	cp_bin $root_dir/lib/klibc/bin/ipconfig $tmp_mnt/bin
	need_mount=1
    fi

    if has_module jfs ; then
	features=(${features[@]} fsck.jfs)
	cp_bin $root_dir/sbin/fsck $tmp_mnt/bin
	cp_bin $root_dir/sbin/fsck.jfs $tmp_mnt/bin
	need_mount=1
    fi

    if has_module scsi_mod ; then
	cp_bin $root_dir/bin/cat $tmp_mnt/bin
	need_mount=1
    fi

    if [ -n "$need_mount" ] ; then
	mkdir -p $tmp_mnt/{etc,proc,mnt}
	echo 'none /proc proc defaults 0 0' > $tmp_mnt/etc/fstab
	if [ "$sysfs_root" ] ; then
	    echo 'sysfs /sys sysfs defaults 0 0' > $tmp_mnt/etc/fstab
	fi
	cp_bin $root_dir/bin/mount $tmp_mnt/bin
	
	if [ -z "$use_udev" ]; then
	    cp_bin $root_dir/bin/umount $tmp_mnt/bin
	    if [ -z "$use_pivot_root" -a \
		-b "$root_dir/$rootdev" ] ; then
		mkdir -p $tmp_mnt/${rootdev%/*}
		cp -a $root_dir/$rootdev $tmp_mnt/${rootdev%/*}
	    fi
	fi
    fi

    # DEBUG: cp_bin /bin/ls $tmp_mnt/bin

    echo -ne "Shared libs:\t"
    # Only check those binaries against dynamic libraries that were
    # taken from the root_dir: We don't want to go into library
    # hell by adding libraries in different versions!
    bin_files=$(
	for bin in "${initrd_bins[@]}" ; do
	    case $bin in
	        ($root_dir*)
		    echo "${bin#$root_dir}"
		    ;;
	    esac
	done )

    # Copy all required shared libraries and the symlinks that
    # refer to them.
    lib_files=$(shared_object_files $bin_files)
    if [ -n "$lib_files" ]; then
	for lib in $lib_files; do
	    [ -L $root_dir/$lib ] || echo -n "$lib "
	    ( cd ${root_dir:-/} ; cp -dp --parents $lib $tmp_mnt )
	done
	echo
    else
	echo "none"
    fi

    if [ -n "$lib_files" -a -n "$initrd_shell_dynamic" ]; then
	# If we have already have all dynamic libraries that
	# $initrd_shell_dynamic is using, and if $initrd_shell_dynamic
	# is smaller than $initrd_shell, we can save a little space
	# by using the dynamic version. The benefit is marginal, though.

	if smaller_file $initrd_shell_dynamic $initrd_shell ; then
	    for lib in $(shared_object_files $initrd_shell_dynamic) ; do
		case $lib_files in
		    *$lib*) ;;
		    *)	initrd_shell_dynamic=
		    	break ;;
		esac
	    done
	    if [ -n "$initrd_shell_dynamic" ]; then
		#echo " - Using dynamically linked $shebang"
		cp_bin $initrd_shell_dynamic $tmp_mnt$shebang
	    fi
	fi
    fi

    if [ -n "$lib_files" -a -n "$initrd_insmod_dynamic" ]; then
	# If we have already have all dynamic libraries that
	# $initrd_insmod_dynamic is using, and if $initrd_insmod_dynamic
	# is smaller than $initrd_insmod, we can save a little space
	# by using the dynamic version. The benefit is marginal, though.

	if smaller_file $initrd_insmod_dynamic $initrd_insmod ; then
	    for lib in $(shared_object_files $initrd_insmod_dynamic); do
		case $lib_files in
		    *$lib*) ;;
		    *)	initrd_insmod_dynamic=
		    	break ;;
		esac
	    done
	    if [ -n "$initrd_insmod_dynamic" ]; then
		#echo " - Using dynamically linked /sbin/insmod"
		cp_bin $initrd_insmod_dynamic $tmp_mnt/sbin/insmod
	    fi
	fi
    fi

    cat /dev/null > $linuxrc
    chmod 755 $linuxrc

    # Note that the in-place documents must be indented with tabs, not spaces.

    cat_linuxrc <<-EOF
	|#! $shebang
	|
	|export PATH=/sbin:/bin:/usr/bin
	|
	|mount -tproc proc /proc
	|mount -tsysfs sysfs /sys > /dev/null 2>&1
	|mount -ttmpfs tmpfs /dev/shm
	|
	|for o in \$(cat /proc/cmdline); do
	|    case \$o in
	|    linuxrc=trace)
	|	set -x
	|	;;
	|    sysrq=yes|sysrq=1)
	|	echo 1 > /proc/sys/kernel/sysrq
	|	;;
	|    esac
	|done
	|
	|die() {
	|    umount /proc
	|    umount /sys
	|    umount /dev/shm
	|    exit \$1
	|}
	|
	|# Fallback root device number
	|rootdevn=$rootdevn
	|
	|for o in \$(cat /proc/cmdline); do
	|    case \$o in
	|    root=*)
	|	rootdev=\${o#root=}
	|	rootdev_cmdline=1
	|	;;
	|    esac
	|done
	|if [ -z "\$rootdev" ]; then
	|    rootdev=$rootdev
	|else
	|    # lilo strips off the /dev/prefix from device names!
	|    case \$rootdev in
	|	/dev/*)
	|	    ;;
	|	LABEL=*|UUID=*|ROOT=*)
	|	    ;;
	|	[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])
	|	    rootdev=0\$rootdev ;;
	|	[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])
	|	    ;;
	|	*:*)
	|	    ;;
	|	*)
	|	    rootdev=/dev/\$rootdev
	|	    ;;
	|    esac
	|fi
	EOF

    if [ -n "$need_block_driver" ]; then
	# Transfer the the block_driver function into the initrd
	type mkdevn | sed -e '1d' >> $linuxrc
	echo >> $linuxrc
	type devmajor | sed -e '1d' >> $linuxrc
	echo >> $linuxrc
	type block_driver | sed -e '1d' >> $linuxrc
	echo >> $linuxrc
    fi

    # Start udev if necessary
    if [ -n "$use_udev" ]; then
	cat_linuxrc <<-EOF
	|
	|echo "Starting udev"
	|echo "/sbin/udev" > /proc/sys/kernel/hotplug
	|echo "Creating devices"
	|/sbin/udevstart
	EOF
    fi

    # Check for IDE modules, might change use_udev
    check_ide_modules $root_dir/lib/modules/$kernel_version

    resolved_modules="$(resolve_modules $kernel_version $modules)"

    # If a SCSI module is loaded, we will have a dependency on scsi_mod
    # for kernels which don't have this built in. In that case, assume
    # that the root file system is on a SCSI device, and also include
    # sd_mod.
    local have_scsi have_sd
    case "$resolved_modules" in
	*/scsi_mod.*)   have_scsi=1
			;;
    esac
    case "$resolved_modules" in
	*/sd_mod.*)	have_sd=1
			;;
    esac
    if [ -n "$have_scsi" -a -z "$have_sd" ]; then
	modules="sd_mod $modules"
	# Re-evaluate module dependencies
	resolved_modules="$(resolve_modules $kernel_version $modules)"
    fi

    # Copy the modules to the initrd
    echo -ne "Modules:\t"
    initrd_is_using_modules=
    while read module params ; do
	[ -z "$module" ] && continue
	echo -n "${module#lib/modules/$kernel_version/} "
	if [ ! -r $root_dir/$module ]; then
	    oops 9 "Module $module not found."
	    continue
	fi
	if ! ( cd ${root_dir:-/} ; cp -p --parents $module $tmp_mnt ) ; then
	    oops 6 "Failed to add module $module."
	    return
	fi

	case "$module" in
	    */dasd_mod.*)
		# kernel cmdline dasd parameter is placed into the environment.
		# This is tricky. The only reliably way to check whether the
		# dasd parameter is set is to indeed check for it within the
		# initrd environment itself. Unfortunately the dasd module
		# refuses to load when the dasd parameter is empty, so we
		# need introduce an intermediate parameter which might be
		# set to empty entirely so as not to confuse the dasd module.
		#
		# This checks whether the dasd parameter is present
		cat_linuxrc <<-'EOF'
		|# check for DASD parameter in /proc/cmdline
		|for p in $(cat /proc/cmdline) ; do
		|  case $p in
		|    dasd=*)
		|      dasd_params="$p"
		|      ;;
		|  esac
		|done
		EOF
		# Add the intermediate parameter to the modules
		params="\$dasd_params"
		;;
	    */zfcp.*)
		# zfcp_param is only used on 2.4 kernels,
		# in 2.6 disks are activated manually
		# (see s390_zfcp_sysfs)
		if [ "$zfcp_param" ]; then
			params="map=\"$zfcp_param\""
		fi
		;;
	    */scsi_mod*)
		# We may have SCSI parameters on the kernel command line,
		# but because scsi_mod is a module, those would be ignored.
		# Hack around this by scanning /proc/cmdline in linuxrc.
		params="\$extra_scsi_params${params:+ $params}"

		cat_linuxrc <<-EOF
		|# check for SCSI parameters in /proc/cmdline
		|devflags=0
		|for p in \$(cat /proc/cmdline) ; do
		|  case \$p in
		|    scsi_mod.*)
		|	extra_scsi_params="\$extra_scsi_params \${p#scsi_mod.}"
		|	;;
		|    scsi_reportlun2=1)
		|	echo "scsi_reportlun2 compat: Use scsi_mod.default_dev_flags=0x20000 instead"
		|	devflags=\$((131072+\$devflags))
		|	;;
		|    scsi_noreportlun=1)
		|	echo "scsi_noreportlun compat: Use scsi_mod.default_dev_flags=0x40000 instead"
		|	devflags=\$((262144+\$devflags))
		|	;;
		|    scsi_sparselun=1)
		|	echo "scsi_sparselun compat: Use scsi_mod.default_dev_flags=0x40 instead"
		|	devflags=\$((64+\$devflags))
		|	;;
		|    scsi_largelun=1)
		|	echo "scsi_largelun compat: Use scsi_mod.default_dev_flags=0x200 instead"
		|	devflags=\$((512+\$devflags))
		|	;;
		|    llun_blklst=*)
		|	echo "llun_blklst is not supported any more"
		|	echo "use scsi_mod.dev_flags=VENDOR:MODEL:0x240[,V:M:0x240[,...]]"
		|	;;
		|    max_ghost_devices=*)
		|	echo "max_ghost_devices is not needed any more"
		|	;;
		|    max_sparseluns=*)
		|	echo "max_sparseluns not supported any more"
		|	echo "use scsi_mod.max_luns or enable the new REPORT_LUNS scsi"
		|	echo "scanning methods; try scsi_mod.default_dev_flags=0x20000"
		|	;;
		|    max_luns=*|max_report_luns=*|inq_timeout=*|dev_flags=*|default_dev_flags=*)
		|	echo "scsi_mod compat: Please use prefix: scsi_mod.\$p"
		|	extra_scsi_params="\$extra_scsi_params \$p"
		|      ;;
		|  esac
		|done
		|if [ \$devflags != 0 ]; then 
		|    extra_scsi_params="default_dev_flags=\$devflags \$extra_scsi_params"
		|fi
		EOF
		;;
	esac

	mp="/$module${params:+ $params}"
	cat_linuxrc <<-EOF
	|echo "Loading ${mp#/lib/modules/$kernel_version/}"
	|insmod $mp
	EOF
	initrd_is_using_modules=1
    done < <(echo "$resolved_modules")
    echo

    if [ -z "$initrd_is_using_modules" ]; then
	echo "none"
    fi

    # Handling of new PCI IDs
    newid_file=/etc/sysconfig/hardware/newids
    if [ -f "$newid_file" ]; then
	while read line; do
	    saved_IFS="$IFS"
	    IFS=","
	    set -- $line
	    IFS="$saved_IFS"
	    idstring=`echo $1 | sed '/^#/d'`
	    if [ -z "$idstring" ]; then
		continue
	    fi
	    idstring="$1"
	    module="$2"
	    driver=`echo $3 | sed 's. .\\\\ .g'`
	    if [ -z "$3" ]; then
		driver="$module"
	    fi
	    if [ -z "$driver" ]; then	
		continue
	    fi
	    cat_linuxrc <<-EOF
		|if [ -d /sys/bus/pci/drivers/${driver} ]; then
		|    echo "$idstring" > /sys/bus/pci/drivers/${driver}/new_id
		|fi
		EOF
	    idstring=
	    module=
	    driver=
	done < "$newid_file"
    fi

    if [ -n "$s390_dasd_disks" ]; then
	# We only need to activate DASDs manually if it
	# is not done via the kernel command line
	echo -e -n "DASDs:\t\t"
	cat_linuxrc <<-EOF
	|if test -z "\$dasd_params"; then
	|    echo -n "Activating DASDs:"
	EOF
	s390_dasd_disk_num=0
	for disk in $s390_dasd_disks; do
	    set -- $(IFS=":"; echo $disk)
	    if [ "$2" -eq 0 ]; then
		echo -n " $1(ECKD)"
	    else
		echo -n " $1(DIAG)"
	    fi
	    cat_linuxrc <<-EOF
	|    echo -n " $disk"
	|    /sbin/dasd_configure $1 1 $2
	EOF
	    s390_dasd_disk_num=$(expr $s390_dasd_disk_num + 1)
	done
	echo ""
	cat_linuxrc <<-EOF
	|    echo " : done"
	|fi
	EOF
    fi

    if [ -n "$s390_zfcp_disks" ]; then
	echo -e -n "zfcp HBAs:\t"
	for hba in $s390_zfcp_hbas; do
	    echo -n "$hba "
	    cat_linuxrc <<-EOF
	|echo "Activating zfcp host $hba"
	|/sbin/zfcp_host_configure $hba 1
	EOF
	done
	echo

	echo -e "zfcp disks:\t"
	s390_zfcp_disk_num=0
	for disk in $s390_zfcp_disks; do
	    s390_zfcp_disk_num=$(expr $s390_zfcp_disk_num + 1)
	    set -- $(IFS=":"; echo $disk)
	    echo -e "\t\t$1:$2:$3"
	    cat_linuxrc <<-EOF
	|echo "Activating zfcp disk $1:$2:$3"
	|/sbin/zfcp_disk_configure $1 $2 $3 1
	EOF
	done
    fi

    case "$(uname -m)" in
	s390|s390x)
	    if [ -z "$s390_zfcp_disks" -a -z "$s390_dasd_disks" ]; then
		echo ""
		echo "WARNING: No boot devices found."
		echo "Make sure to add 'dasd=<dasd-range>' to the" \
		     "kernel command line"
	    fi
	    ;;
    esac

    # Check for multipathing
    cat_linuxrc <<-'EOF'
	|for o in $(cat /proc/cmdline); do
	|  case $o in
	|    multipath=*)
	|      multipath_hosts=${o#multipath=} ;;
	|  esac
	|done
	|case $multipath_hosts in
	|  all|yes)
	|      hosts_to_scan=*
	|      ;;
	|  host*)
	|      set -- $(IFS=, ; echo $multipath_hosts)
	|      hosts_to_scan=$@ 
	|      ;;
	|esac
	|old_pwd=$PWD
	|if [ -d /sys/class/scsi_host ]; then
	|cd /sys/class/scsi_host
	|for host in $hosts_to_scan; do
	|    cd /sys/class/scsi_host/$host
	|    if [ -d device ]; then
	|        for dev in device/target*; do
	|            if [ -d "$dev" ]; then
	|                have_targets=1
	|            fi
	|        done
	|    fi
	|    if [ -z "$have_targets" -a -w no_partition_check ]; then
	|        echo 1 > no_partition_check
	|        echo "- - -" > scan
	|    fi
	|done
	|cd $old_pwd
	|fi
	EOF

    if has_module dcssblk ; then
	if [ -n "$s390_dcss_segments" ] ; then
	    segments=$(echo $s390_dcss_segments | tr ' ' ',')
	    echo -e "DCSS segments:\t$segments"
	    cat_linuxrc <<-EOF
		|dcss_segments=$segments
		EOF
	fi
	cat_linuxrc <<-'EOF'
	|for o in $(cat /proc/cmdline); do
	|  case $o in
	|    dcssblk.segments=*)
	|      dcss_segments="${o#dcssblk.segments=}";;
	|  esac
	|done
	|# add each DCSS segment
	|if [ -n "$dcss_segments" ]; then
	|    for segment in $(IFS=, ; echo $dcss_segments); do
	|        echo "$segment" > /sys/devices/dcssblk/add
	|        echo "Activated DCSS segment $segment"
	|    done
	|fi
	EOF
    fi

    if [ -n "$use_dhcp" ] ; then
	cat_linuxrc <<-'EOF'
	|case $rootdev in
	|/dev/nfs|*:/*|"")
	|   dhcp_mode=1 ;;
	|esac
	EOF
    fi

    if has_module iscsi ; then
	cat_linuxrc <<-'EOF'
	|for o in $(cat /proc/cmdline); do
	|  case $o in
	|    DiscoveryAddress=*)
	|      target="$o" ; iscsi_boot=1 ;;
	|    InitiatorName=*)
	|      initiatorname="$o" ; iscsi_boot=1 ;;
	|  esac
	|done
	|# iscsi boot requires dhcp, regardless of root= format
	|if [ -n "$iscsi_boot" ]; then
	|  dhcp_mode=1
	|fi
	EOF
    fi

    if [ -n "$use_dhcp" ] ; then
	cat_linuxrc <<-EOF
	|# run dhcp
	|if [ -n "\$dhcp_mode" ]; then
	|  # ifconfig lo 127.0.0.1 netmask 255.0.0.0 broadcast 127.255.255.255 up
	|  # portmap
	|  echo "running dhcpcd on interface $interface"
	|  dhcpcd -R -Y -N -t 100000000 $interface
	|  [ -s /var/lib/dhcpcd/dhcpcd-$interface.info ] || {
	|    echo "no response from dhcp server."
	|    echo 256 > /proc/sys/kernel/real-root-dev
	|    die 0
	|  }
	|  . /var/lib/dhcpcd/dhcpcd-$interface.info
	|  kill -9 \$(cat /var/run/dhcpcd-$interface.pid)
	|  if [ -n "\$DNS" ]; then
	|    oifs="\$IFS"
	|    IFS=","
	|    for ns in \$DNS ; do
	|      echo "nameserver \$ns" >> /etc/resolv.conf
	|    done
	|    IFS="\$oifs"
	|    if [ -n "\$DOMAIN" ]; then
	|	echo "search \$DOMAIN" >> /etc/resolv.conf
	|    fi
	|    echo 'hosts: dns' > /etc/nsswitch.conf
	|  fi
	|fi
	EOF
    fi

    if [ -n "$use_ipconfig" ]; then
	ipinterface=$(get_ip_config $interface)
	cat_linuxrc <<-EOF
	|# configure interface
	|for o in \$(cat /proc/cmdline); do
	|  case \$o in
	|    ip=*)
	|      ifspec=\${o#ip=};;
	|  esac
	|done
	|if [ -z "\$ifspec" ]; then
	|    # Fallback to configured interface
	|    ifspec=$ipinterface
	|fi
	|if [ -n "\$ifspec" ]; then
	|  ipconfig \$ifspec
	|fi
	EOF
    fi

    if has_module iscsi ; then
	cat_linuxrc <<-'EOF'
	|if [ -n "$iscsi_boot" ]; then
	|  echo "Continuous=no" >> /etc/iscsi.conf
	|  echo "ImmediateData=no" >> /etc/iscsi.conf
	|  echo "$target" >> /etc/iscsi.conf
	|  echo "$initiatorname" >> /etc/initiatorname.iscsi
	|
	|  echo "Starting iSCSI"
	|  iscsid
	|  usleep 5000000
	|
	|  # clear dhcp_mode so it looks like we're disk booted...
	|  dhcp_mode=
	|fi
	EOF
	use_dhcp=
    fi

    if [ -n "$need_raidautorun" ]; then
	features=(${features[@]} raidautorun)
	cat_linuxrc <<-EOF
	|echo "raidautorun ..."
	|raidautorun
	|echo "done..."
	EOF
    fi

    if [ -n "$need_raidstart" ]; then
	features=(${features[@]} raidstart)
	cat_linuxrc <<-EOF
	|echo "raidstart ..."
	|raidstart --all
	|echo "done..."
	EOF
    fi

    if [ -n "$root_lvm" ] ; then
	# Name of the volume containing the root filesystem
        local vg_root=${rootdev#/dev/}
        vg_root=${vg_root%%/*}

	if [ -z "$use_udev" ]; then
	    cat_linuxrc <<-'EOF'
		|# scan for lvm devices
		|createpartitiondevs
		EOF
	fi
	cat_linuxrc <<-EOF
	|#need space for lvm data
	|mount -tramfs none /etc/lvmtab.d
	|vgscan
	|for o in \$(cat /proc/cmdline); do
	|  case \$o in
	|    root=/dev/*)
	|	set -- \$(IFS=/ ; echo \$o)
	|	vg_root=\$3
	|	;;
	|  esac
	|done
	|if [ -z "\$vg_root" ]; then
	|  vg_root=$vg_root
	|fi
	|
	|vgchange -a y \$vg_root
	|umount /etc/lvmtab.d
	|# set the right root device if user specified a lvm root
	|if [ "$(block_driver "\$rootdev")" = lvm ]; then
	|    case \$rootdev in
	|    /dev/*)
	|	echo \$(devnumber \$rootdev) > /proc/sys/kernel/real-root-dev ;;
	|    *)
	|	# hex number or major:minor pair
	|	echo \$rootdev > /proc/sys/kernel/real-root-dev ;;
	|    esac
	|    die 0
	|fi
	EOF
    fi

    if [ -n "$root_dm" -o -n "$root_evms" ]; then
	if [ -z "$use_udev" ]; then
	    cat_linuxrc <<-'EOF'
		|# create device mapper control device nodes
		|/sbin/devmap_mknod.sh
		EOF
	else
	    cat_linuxrc <<-'EOF'
		|echo -n "Waiting for /dev/mapper/control to appear: "
		|for i in 1 2 3 4 5; do
		|    [ -e /dev/mapper/control ] && break
		|    sleep 1
		|    echo -n "."
		|done
		|if [ -e /dev/mapper/control ]; then
		|    echo " ok"
		|else
		|    echo " failed"
		|fi
		EOF
	fi
    fi

    if [ -n "$root_dm" ] ; then
	# Name of the volume containing the root filesystem
        local vg_root=${rootdev#/dev/}
        vg_root=${vg_root%%/*}
	cat_linuxrc <<-EOF
	|vgscan
	|for o in \$(cat /proc/cmdline); do
	|  case \$o in
	|    root=/dev/*)
	|       set -- \$(IFS=/ ; echo \$o)
	|       vg_root=\$3
	|       ;;
	|  esac
	|done
	|if [ -z "\$vg_root" ]; then
	|  vg_root=$vg_root
	|fi
	|
	|vgchange -a y \$vg_root
	|# set the right root device if user specified a lvm root
	|if [ "\$(block_driver "\$rootdev")" = device-mapper ]; then
	|    case \$rootdev in
	|    /dev/*)
	|	echo \$(devnumber \$rootdev) > /proc/sys/kernel/real-root-dev ;;
	|    *)
	|	# hex number or major:minor pair
	|	echo \$rootdev > /proc/sys/kernel/real-root-dev ;;
	|    esac
	|    die 0
	|fi
	EOF
    fi

    if [ -n "$root_evms" ] ; then
	cat_linuxrc <<-EOF
	|# Extract the minor part from a device number
	|devminor() 
	|    {
	|    local devn=\$1
	|    expr '(' \$devn % 256 ')' 
	|    }
	|
	|fix_evms_root_node()
	|    {
	|    CURDEV=\$(devnumber \$2)
	|    OLDDEV=\$(devnumber \$1\$2)
	|    if [ ! -b \$1\$2 -o "\$CURDEV" -ne "\$OLDDEV" ]
	|    then
	|	mount -oremount,rw \$2 \$1
	|	rm -f \$1\$2
	|	mknod -m 640 \$1\$2 b \$(devmajor \$CURDEV) \$(devminor \$CURDEV)
	|	mount -oremount,ro \$2 \$1
	|    fi
	|    }
	|
	|remove_evms_fstab()
	|    {
	|    for i in \$*
	|    do
	|	dmsetup remove "\$i"
	|    done
	|    }
	|
	|get_fstab_entries()
	|    {
	|    FSTLIST=\$(sed -n "\:^/dev/:p" <\$1 | sed -e "\:/media/:d" -e "s:/dev/::" -e "s:[   ].*::")
	|    for i in \$FSTLIST
	|    do
	|	LINES=\$(dmsetup ls | sed -n "\:^\$i:p")
	|        [ -n "\$LINES" ] && RETLIST="\$RETLIST \$i"
	|    done
	|    echo \$RETLIST
	|    }
	|
	|create_evms_save_table()
	|    {
	|    TNAME=\$1
	|    shift
	|    COUNT=0
	|    rm -f /table_file
	|    for i in \$*
	|    do
	|	echo \$(expr \$COUNT "*" 100) 100 linear /dev/\$i 0 >> /table_file
	|	COUNT=\$(expr \$COUNT + 1)
	|    done
	|    dmsetup create \$TNAME </table_file
	|    rm -f /table_file
	|    }
	|
	|/sbin/evms_activate
	|mkdir -p /mnt
	|mount -oro \$rootdev /mnt
	|FSTAB_ITEMS=\$(get_fstab_entries /mnt/etc/fstab)
	|umount /mnt
	|dmsetup remove_all
	|create_evms_save_table wrzlbrnft \$FSTAB_ITEMS
	|/sbin/evms_activate
	|dmsetup remove wrzlbrnft
	|# set the right root device if user specified a lvm root
	|if [ "\$(block_driver "\$rootdev")" = device-mapper ]; then
	|    case \$rootdev in
	|    /dev/*)
	|	mkdir -p /mnt
	|	mount -oro \$rootdev /mnt
	|	fix_evms_root_node /mnt \$rootdev
	|	remove_evms_fstab \$FSTAB_ITEMS
	|	umount /mnt
	|	echo \$(devnumber \$rootdev) > /proc/sys/kernel/real-root-dev ;;
	|    *)
	|	# hex number or major:minor pair
	|	echo \$rootdev > /proc/sys/kernel/real-root-dev ;;
	|    esac
	|    die 0
	|fi
	EOF
    fi

    cat_linuxrc <<-EOF
	|case "\$rootdev" in
	|	LABEL=*)
	|	    rootdev=\$(/sbin/blkid -t \$rootdev)
	|	    rootdev=\${rootdev%%:*}
	|	    ;;
	|	UUID=*)
	|	    rootdev=\$(/sbin/blkid -t \$rootdev)
	|	    rootdev=\${rootdev%%:*}
	|	    ;;
	|esac
	|if [ -z "\$rootdev" ]; then
	|    rootdev=$rootdev
	|fi
	EOF

    if [ -n "$use_udev" ]; then
	cat_linuxrc <<-'EOF'
	|udev_discover_root() {
	|	local root
	|	local i
	|
	|	echo > /.udevdb.lock
	|	case "$rootdev" in
	|	/dev/*)	root=${rootdev#/dev/} ;;
	|	esac
	|	if [ -n "$root" ]; then
	|	    echo -n "Waiting for device /dev/$root to appear: "
	|	    i=0
	|	    while [ $i -lt 5 ]; do
	|		if [ -e /dev/$root ]; then
	|		    path=$(udevinfo -q path -n $root)
	|		    if [ $? -eq 0 ]; then
	|		        echo " ok"
	|			break
	|		    else
	|		        path=
	|		    fi
	|		else
	|		    if [ /dev/.udev.tdb -nt /.udevdb.lock ]; then
	|			echo > /.udevdb.lock
	|			i=0
	|		    fi
	|		fi
	|		sleep 1
	|		i=$(expr $i + 1)
	|		echo -n "."
	|	    done
	|	    if [ -n "$path" ]; then
	|	        dev=/sys$path/dev
	|		if [ -f $dev ]; then
	|		    local major minor devn
	|		    IFS=":" read major minor < $dev
	|		    devn=$(mkdevn $major $minor)
	|		    echo "rootfs: $entry major=$major minor=$minor" \
	|			 "devn=$devn"
	|		    echo $devn > /proc/sys/kernel/real-root-dev
	|		    return 0
	|		fi
	|	    fi
	|	    return 1
	|	fi
	|}
	|
	|if ! udev_discover_root ; then
	|    echo "not found -- device nodes:"
	|    cd /dev
	|    for dev in *; do
	|	if [ -e "$dev" ]; then
	|	    echo -n "$dev "
	|	fi
	|    done
	|    echo
	|    echo "No root device found; exiting to /bin/sh"
	|    cd /
	|    PATH=$PATH PS1='$ ' /bin/sh -i
	|fi
	EOF
    fi

    if [ -n "$sysfs_root" ]; then
	cat_linuxrc <<-'EOF'
	|basename() {
	|	local IFS=/
	|
	|	set -- $1
	|	eval echo \${$#}
	|}
	|
	|sysfs_set_root_dir() {
	|	local entry name dir=$1 search=$2
	|
	|	for entry in $dir/*; do
	|	  [ -L $entry ] && continue
	|	  [ -d $entry ] || continue
	|
	|	  name=$(basename $entry)
	|	  if [ -f $entry/dev -a $name = $search ]; then
	|		local major minor devn
	|		IFS=: read major minor < $entry/dev
	|		devn=$(mkdevn $major $minor)
	|		echo "rootfs: $entry major=$major minor=$minor" \
	|		     "devn=$devn"
	|		echo $devn > /proc/sys/kernel/real-root-dev
	|		return 0
	|	  else
	|	    sysfs_set_root_dir $entry $search \
	|		&& return 0
	|	  fi
	|	done
	|	return 1
	|}
	|
	|sysfs_set_root() {
	|	local root=$1
	|
	|	sysfs_set_root_dir /sys/block $root
	|}
	|
	|discover_root() {
	|	local root
	|	case $rootdev in
	|	/dev/*)	root=$(expr substr $rootdev 6 99) ;;
	|	*)	
	|	esac
	|	if [ -n "$root" ]; then
	|	    while :; do
	|		idx=$(expr index $root /)
	|		[ $idx = 0 ] && break
	|		root=$(expr substr $root 1 $(expr $idx - 1)
	|		   )!$(expr substr $root   $(expr $idx + 1) 99)
	|	    done
	|	    sysfs_set_root $root
	|	fi
	|}
	|
	|if ! discover_root ; then
	|    if [ -n "$rootdev" ]; then
	|	echo $rootdev > /proc/sys/kernel/real-root-dev
	|    elif [ -n "$rootdevn" ]; then
	|	echo $rootdevn > /proc/sys/kernel/real-root-dev
	|    else
	|	: # let the kernel do its internal root device scan
	|	  # (we don't get here.)
	|    fi
	|fi
	EOF
    fi

    if [ -n "$use_pivot_root" -a -z "$use_udev" ] ; then
	cat_linuxrc <<-'EOF'
	|# create missing devices
	|createpartitiondevs
	EOF
    fi

    if [ -n "$use_dhcp" ]; then
	cat_linuxrc <<-'EOF'
	|if [ -z "$rootdev_cmdline" ]; then
	|  case "$ROOTPATH" in
	|    "") ;;
	|    *:*)
	|	rootdev="$ROOTPATH" ;;
	|    *)
	|	if [ -n "$DHCPSIADDR" ]; then
	|	    rootdev="$DHCPSIADDR:$ROOTPATH"
	|	elif [ -n "$DHCPSNAME" ]; then
	|	    rootdev="$DHCPSNAME:$ROOTPATH"
	|	fi ;;
	|   esac
	|   if [ -z "$rootdev" ]; then
	|	echo "no local root= kernel option given and no root" \
	|	     "server set by the dhcp server."
	|	echo 256 > /proc/sys/kernel/real-root-dev
	|	die 0
	|   fi
	|fi
	EOF
    fi

    if has_module jfs && [ -n "$rootdev" ] ; then
	cat_linuxrc <<-'EOF'
	|# check filesystem if it is of type jfs
	|[ -z "$dhcp_mode" ] && fsck -t jfs $rootdev
	EOF
    fi

    if [ -n "$use_selinux" -a -d $tmp_mnt/selinux ]; then
	cat_linuxrc <<-'EOF'
	|echo -n "Loading SELinux policy	"
	|if mount -t selinuxfs none /selinux >/dev/null 2>/dev/null ; then
	|  /sbin/load_policy /etc/security/selinux/policy.15
	|  umount /selinux
	|  echo "successful"
	|else
	|  echo "skipped"
	|fi
	EOF
    fi

    if [ -n "$use_pivot_root" ] ; then
	cat_linuxrc <<-'EOF'
	|case "$rootdev" in
	|ROOT=*)
	|   rootdev=${rootdev#ROOT=}
	|   pivotroot=1 ;;
	|*)
	|   pivotroot=$dhcp_mode ;;
	|esac
	|
	|if [ -n "$pivotroot" ]; then
	|   read_write=
	|   for o in $(cat /proc/cmdline); do
	|	[ $o = rw ] && read_write=1
	|   done
	|
	|   opt=-oro
	|   [ -n "$read_write" ] && opt=-orw
	|   [ -n "$dhcp_mode" ] && opt="${opt},nolock"
	|
	|   # tell kernel root is /dev/ram0, prevents remount after initrd
	|   echo 256 > /proc/sys/kernel/real-root-dev
	|   # mount the actual root device below /mnt
	|   echo "Mounting root $rootdev"
	|   mount $opt $rootdev /mnt || die 1
	|
	|   # do pivot-root call
	|   cd /mnt
	|   exec <dev/console >dev/console 2>&1
	|   if [ -d initrd ]; then
	|	exec pivot_root . initrd
	|   elif [ -d mnt ]; then
	|	pivot_root . mnt
	|	exec /bin/umount /mnt
	|   else
	|	pivot_root . tmp
	|	exec /bin/umount /tmp
	|   fi
	|fi
	EOF
    fi

    cat_linuxrc <<-'EOF'
	|die 0
	EOF

    # Now create a second initrd with minimal size

    #echo "Creating minimum size initrd image"
    img_size=$(expr $(df -kP $tmp_mnt | sed '1d' | awk '{print $3}') + 2000)
    mkdir $tmp_mnt_small

    dd if=/dev/zero of=$tmp_initrd_small bs=1k count=$img_size 2>/dev/null
    mke2fs -q -F -b 1024 -m 0 -N $image_inodes $tmp_initrd_small \
	2>/dev/null 1>&2
    tune2fs -i 0 $tmp_initrd_small >/dev/null 2>&1

    if ! mount -t ext2 -oloop $tmp_initrd_small $tmp_mnt_small ; then
	error 3 "failed to mount image"
    fi
    is_mounted_small=1

    rmdir $tmp_mnt_small/lost+found
    cp -a $tmp_mnt/* $tmp_mnt_small \
	|| error 6 "copy big image to small image failed"
    chown -R 0:0 $tmp_mnt

    umount $tmp_mnt_small
    is_mounted_small=

    umount $tmp_mnt
    is_mounted=

    gzip -9 $tmp_initrd_small

    if ! cp -f $tmp_initrd_small.gz $initrd_image ; then
	oops 8 "Failed to install initrd"
	return
    fi

    [ ${#features[@]} -gt 0 ] \
	&& echo -e "Including:\t${features[@]}"

    splash_bin=
    [ -x /sbin/splash.bin ] && splash_bin=/sbin/splash.bin
    [ -x /bin/splash ] && splash_bin=/bin/splash
    if [ -n "$splashsizes" -a -n "$splash_bin" ]; then
	if [ -f /etc/sysconfig/bootsplash ]; then
	    . /etc/sysconfig/bootsplash
	fi

	themes_dir=
	if [ -d "$root_dir/etc/bootsplash/themes" ]; then
	    themes_dir="$root_dir/etc/bootsplash/themes"
	elif [ -d "$root_dir/usr/share/splash/themes" ]; then
	    themes_dir="$root_dir/usr/share/splash/themes"
	fi

	echo -ne "Bootsplash:\t"
	if [ -n "$themes_dir" -a \
	     -d "$themes_dir/$THEME" -o -L "$themes_dir/$THEME" ]; then
	    for size in $splashsizes; do
		bootsplash_picture="$themes_dir/$THEME/images/bootsplash-$size.jpg"
		cfgname="$themes_dir/$THEME/config/bootsplash-$size.cfg"
		if [ ! -r $cfgname ] ; then
		    echo "disabled for resolution $size"
		elif [ ! -r $bootsplash_picture ] ; then
		    echo "no image for resolution $size"
		else
		    echo "$THEME ($size)"
		    $splash_bin -s -f $cfgname >> $initrd_image
		fi
	    done
	else
	    echo "no theme selected"
	fi
    fi
}

###################################################################

# working directories
tmp_initrd=$work_dir/initrd
tmp_initrd_small=${tmp_initrd}_small

mounted_proc=
if [ ! -d /proc ]; then
    mkdir /proc
fi
if [ ! -r /proc/mounts ]; then
  mounted_proc=/proc
  mount -t proc proc $mounted_proc
fi

mounted_sysfs=
if [ ! -d /sys ]; then
    mkdir /sys
fi
if [ ! -d /sys/class ]; then
    mounted_sysfs=/sys
    mount -t sysfs none $mounted_sysfs
fi

if [ -z "$rootdev" ] ; then
  # no rootdev specified, get current root from /etc/fstab
  while read fstab_device fstab_mountpoint fstab_type fstab_options dummy ; do
    if [ "$fstab_mountpoint" = "/" ]; then
      rootdev="$fstab_device"
      rootfstype="$fstab_type"
      break
    fi
  done < <( sed -e '/^[ 	]*#/d' < $root_dir/etc/fstab)
else
  # get type from /etc/fstab or /proc/mounts (actually not needed)
  x1=$(cat $root_dir/etc/fstab /proc/mounts 2>/dev/null \
       | grep -E "$rootdev[[:space:]]" | tail -n 1)
  rootfstype=$(echo $x1 | cut -f 3 -d " ")
fi

if [ -z "$use_dhcp" ] || [ -z "$use_ipconfig" ]; then
    [ -z "$rootdev" ] \
	&& error 1 "No '/' mountpoint specified in $root_dir/etc/fstab"
else
    # Not everything is nfs these days.
    case $rootdev in
	*/nfs|nfs://*)
	    rootdev=
	    rootfstype=nfs
	    ;;
	*)
	    ;;
    esac
fi

realrootdev="$rootdev"
case "$rootdev" in
    LABEL=*|UUID=*)
	# get real root via fsck hack
	realrootdev=$(fsck -N "$rootdev" \
		      | sed -ne '2s/.* \/dev/\/dev/p' \
		      | sed -e 's/  *//g')
	[ -z "$realrootdev" ] \
	    && error 1 "Could not expand $rootdev to real device"
	;;
esac

# check if the root device is an lvm device
root_lvm=
root_dm=
root_evms=
if [ -n "$realrootdev" -a -b "$root_dir/${realrootdev#/}" ] ; then
    rootdevn=$(devnumber $root_dir/${realrootdev#/})

    [ "$(block_driver "$root_dir/${realrootdev#/}")" = lvm ] \
	&& root_lvm=1
    [ "$(block_driver "$root_dir/${realrootdev#/}")" = device-mapper ] \
    	&& root_dm=1
    [ "${realrootdev:0:10}" = /dev/evms/ ] \
    	&& root_evms=1 && root_dm=
fi

###################################################################

x="$rootdev"
[ "$rootfstype" = "nfs" ] && x="nfs-root"
[ "$rootdev" != "$realrootdev" ] && x="$x ($realrootdev)"
echo -e "Root device:\t$x (mounted on ${root_dir:-/} as $rootfstype)"

if [ -z "$modules_set" ]; then
    # get INITRD_MODULES from system configuration
    if [ -e $root_dir/etc/sysconfig/kernel ]; then
	. $root_dir/etc/sysconfig/kernel
	modules="$INITRD_MODULES"
    elif [ -e $root_dir/etc/rc.config ]; then
	. $root_dir/etc/sysconfig/kernel
	modules="$INITRD_MODULES"
    fi
fi

###################################################################
# add modules required by features
if [ -n "$root_lvm" ] ; then
    add_module lvm-mod
fi

if [ -n "$root_dm" -o -n "$root_evms" ] ; then
    add_module dm-mod
    add_module dm-snapshot
fi

if [ -n "$interface" ]; then
    # dhcpd reqires the af_packet module, we include it here
    # in case the root FS will be mounted via NFS
    add_module af_packet
    # Try to load the appropriate driver module
    if [ -d /sys/class ]; then
	netdrv_path=/sys/class/net/$interface/driver
	if [ -d "$netdrv_path" ]; then
	    netdrv=`cd -P $netdrv_path; echo $(basename $PWD)`
	    [ -n "$netdrv" ] && add_module "$netdrv"
	fi
    fi
fi

case "$(uname -m)" in
    s390|s390x)
	# Check if zfcp or dasd modules need to be added automatically:
	if [ -d /sys/block ]; then
	    # Always enable all devices, hotplug is completely garbled
	    s390_enable_dasd=1
	    s390_enable_zfcp=1
	    # if [ "$root_evms" ]; then
	    #     s390_check_evms $rootdev
	    # elif [ "$root_dm" ]; then
	    #     s390_check_lvm2 $rootdev
	    # else
	    #     s390_check_dasd $rootdev
	    #     s390_check_zfcp $rootdev
	    # fi
	    # Activate devices
	    s390_dasd_sysfs
	    s390_zfcp_sysfs
	    s390_check_dcss
	else
	    s390_dasd_proc
	    s390_zfcp_proc
	fi
	;;
esac

###################################################################

exit_code=0

initrd_images=( $initrd_images )
kernel_images=( $kernel_images )

boot_modules="$modules"
echo -e "Module list:\t$boot_modules"
for ((i=0 ; $i<${#kernel_images[@]} ; i++)); do
    echo
    modules="$boot_modules"
    kernel_image=${kernel_images[$i]}
    [ ${kernel_image:0:1} != '/' ] \
    	&& kernel_image=$boot_dir/$kernel_image

    initrd_image=${initrd_images[$i]}
    [ ${initrd_image:0:1} != '/' ] \
    	&& initrd_image=$boot_dir/$initrd_image

    mkinitrd_kernel $kernel_image $initrd_image
    attach_dsdt $initrd_image

    # If the current $kernel_image has a symlink without "-<version>" (e.g.
    # "vmlinuz") pointing to it, create an "initrd" symlink for the
    # corresponding $initrd_image.
    if [ "$(readlink ${kernel_image%%-*})" = \
	 "${kernel_image#$boot_dir/}" ]; then
	rm -f $root_dir/$boot_dir/initrd
	ln -s "${initrd_image#$boot_dir/}" $root_dir/$boot_dir/initrd
    fi
    cleanup
done


cleanup_finish

if [ -e $root_dir/etc/sysconfig/bootloader ]; then
    . $root_dir/etc/sysconfig/bootloader
fi
case $LOADER_TYPE in
  lilo)
    echo "
Run lilo now to update the boot loader configuration."
    ;;
  elilo)
    if [ -x /sbin/elilo ]; then
      /sbin/elilo
    else
      echo "
You may now have to update the elilo boot loader configuration."
    fi
    ;;
  grub)
    ;;
  *)
    if [ -f "$root_dir/etc/zipl.conf" ]; then
	echo "
initrd updated, zipl needs to update the IPL record before IPL!"
    else
	echo "
You may now have to update your boot loader configuration."
    fi
    ;;
esac

exit $exit_code
