#!/bin/sh

# geninitrd
#
#	by Jacek Konieczny <jajcus@pld.org.pl>
#
# based on geninitrd written by Erik Troan <ewt@redhat.com>
# and contributors:
#	Elliot Lee <sopwith@cuc.edu>
#	Miguel de Icaza <miguel@nuclecu.unam.mx>
#	Christian 'Dr. Disk' Hechelmann <drdisk@ds9.au.s.shuttle.de>
#	Michael K. Johnson <johnsonm@redhat.com>
#	Pierre Habraken <Pierre.Habraken@ujf-grenoble.fr>
#	Jakub Jelinek <jj@ultra.linux.cz>
#	Carlo Arenas Belon (carenas@chasqui.lared.net.pe>
#
PATH=/sbin:$PATH
export PATH

VERSION=%VERSIONTAG%

. /etc/rc.d/init.d/functions

COMPRESS="yes"
FS="rom"
PROBESCSI="yes"
PROBEIDE="yes"
PROBERAID="yes"

if [ -f /etc/sysconfig/initrd ] ; then
	. /etc/sysconfig/initrd
fi

target=""
kernel=""
force=""
verbose=""
MODULES=""
img_vers=""
modulefile=/etc/modules.conf
if [ `uname -m` = "ia64" ]; then
  IMAGESIZE=3000
else
  IMAGESIZE=1500
fi
PRESCSIMODS="scsi_mod sd_mod unknown"
PREIDEMODS="ide-mod ide-probe-mod ide-disk"
fstab="/etc/fstab"

usage () {
    echo "usage: `basename $0` [--version] [-v] [-f] [--ifneeded] [--preload <module>]" >&2
    echo "       [--omit-scsi-modules] [--omit-raid-modules] [--omit-ide-modules] [--with=<module>]" >&2
    echo "       [--image-version] [--fstab=<fstab>] [--nocompress] [--fs=rom|ext2]" >&2
    echo "       <initrd-image> <kernel-version>" >&2
    echo "       (ex: `basename $0` /boot/initrd-2.2.5-15.img 2.2.5-15)" >&2
    exit 1
}

findmodule() {
    skiperrors=""
    modName=$2
    if [ $(echo $modName | cut -b1) = "-" ]; then
	skiperrors=1
	modName=$(echo $modName | cut -b2-)
    fi

    if [ "$modName" = "pluto" ]; then
	findmodule "" fc4
	findmodule "" soc
    fi
    if [ "$modName" = "fcal" ]; then
	findmodule "" fc4
	findmodule "" socal
    fi
    if [ -n "$1" ]; then
	fmPath="$1"/"$modName".o
    else
	fmPath=`(cd /lib/modules/$kernel; echo */$modName.o)`
    fi

    if [ "$1" = "fs" ] && [ ! -f /lib/modules/$kernel/$fmPath ]; then
	fmPath="$1"/"${modName}fs".o
    fi

    if [ ! -f /lib/modules/$kernel/$fmPath ]; then
	if [ -n "$skiperrors" ]; then
	    return
	fi

        # ignore the absence of the scsi modules
	for n in $PRESCSIMODS; do
	    if [ "$n" = "$modName" ]; then
		return;
	    fi
	done;
    
        # ignore the absence of the ide modules
	for n in $PREIDEMODS; do
	    if [ "$n" = "$modName" ]; then
		return;
	    fi
	done;
    
	echo "No module $modName found for kernel $kernel" >&2
	exit 1
    fi

    # only need to add each module once
    if echo $MODULES | grep $fmPath >/dev/null 2>&1 ; then : ; else
	MODULES="$MODULES $fmPath"
    fi
}

inst() {
    if [ "$#" != "2" ];then
        echo "usage: inst <file> <destination>"
        return
    fi 
    [ -n "$verbose" ] && echo "$1 -> $2"
    cp $1 $2
}


while [ $# -gt 0 ]; do
    case $1 in
	--fstab*)
	    if echo $1 | grep '=' >/dev/null ; then
	    	fstab=`echo $1 | sed 's/^--with=//'`
	    else
		fstab=$2
		shift
	    fi		    
	    ;;

	--with*)
	    if echo $1 | grep '=' >/dev/null ; then
	    	modname=`echo $1 | sed 's/^--with=//'`
	    else
		modname=$2
		shift
	    fi		    

	    BASICMODULES="$BASICMODULES $modname"
	    ;;

	--version)
	    echo "geninitrd: version $VERSION"
	    exit 0
	    ;;

	-v)
	    verbose=-v
	    ;;

	--nocompress)
	    COMPRESS="no"
	    ;;

	--ifneeded)
	    ifneeded=1
	    ;;

	-f)
	    force=1
	    ;;
	--preload)
	    if echo $1 | grep '=' >/dev/null ; then
	    	modname=`echo $1 | sed 's/^--preload=//'`
	    else
		modname=$2
		shift
	    fi		    
	    PREMODS="$PREMODS $modname"
	    ;;
	--omit-scsi-modules)
	    PRESCSIMODS=""
	    PROBESCSI="no";
	    ;;
	--omit-raid-modules)
	    PROBERAID="no";
	    ;;
	--omit-ide-modules)
	    PROBEIDE="no";
	    ;;
	--with-fs)
	    if echo $1 | grep '=' >/dev/null ; then
	    	FS=`echo $1 | sed 's/^--fs=//'`
	    else
		FS=$2
		shift
	    fi		    
	    ;;
	--image-version)
	    img_vers=yes
	    ;;
	*)
	    if [ -z "$target" ]; then
		target=$1
	    elif [ -z "$kernel" ]; then
		kernel=$1
	    else
		usage
	    fi
	    ;;
    esac

    shift
done

if [ "$FS" != "ext2" -a "$FS" != "rom" ] ; then
    echo "Only ext2 and rom fs are supported!"
fi

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

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

if [ -z "$force" -a -f $target ]; then
    echo "$target already exists." >&2
    exit 1
fi

if [ ! -d /lib/modules/$kernel ]; then
    echo "/lib/modules/$kernel is not a directory." >&2
    exit 1
fi

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

rootdev=$(awk '{ if ($2 == "/") { print $1; }}' $fstab)
if is_yes "$PROBSCSI" && echo "$rootdev" | grep -q "^/dev/sd" ; then

    for n in $PRESCSIMODS; do
	    findmodule scsi $n
    done

    if [ ! -f $modulefile ]; then
        modulefile=/etc/conf.modules
    fi
    if [ -f $modulefile ]; then
	scsimodules=`grep scsi_hostadapter $modulefile | grep -v '^[ 	]*#' | LC_ALL=C sort -u | awk '{ print $3 }'`
	for n in $scsimodules; do
    # for now allow scsi modules to come from anywhere.  There are some
    # RAID controllers with drivers in block/
	    findmodule "" $n
	done
    fi
fi

if is_yes "$PROBEIDE" && echo "$rootdev" | grep -q "^/dev/hd" ; then
    for n in $PREIDEMODS; do
	    findmodule block $n
    done
fi

if is_yes "$PROBERAID" ; then
    # load appropriate raid devices if necessary
    if grep '^/dev/md' $fstab | grep -v noauto >/dev/null 2>&1 ; then
	for number in $(grep '^[ 	]*raid-level' /etc/raidtab |
			  awk '{print $2}' | LC_ALL=C sort -u) ; do
	    case $number in
	    [0145])
		findmodule "" raid$number
		;;
	    *)
		echo "raid level $number (in /etc/raidtab) not recognized" >&2
		;;
	    esac
	done
    fi
fi

# check to see if we need to set up a loopback filesystem
if echo $rootdev | cut -d/ -f3 | grep loop >/dev/null; then
    key="^# $(echo $rootdev | cut -d/ -f3 | tr '[a-z]' '[A-Z]'):"
    if ! grep "$key" $fstab > /dev/null; then
	echo "The root filesystem is on a $rootdev, but there is no magic entry in $fstab" 1>&2
	echo "for this device. Consult the geninitrd man page for more information" 2>&2
	exit 1
    fi

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

    BASICMODULES="$BASICMODULES -loop"
    findmodule fs "-$loopFs"
    BASICMODULES="$BASICMODULES -${loopFs}"
else # Check for other filesystems
    rootFs=`awk '{if ($2 == "/") {print $3}}' $fstab`
    findmodule fs "-$rootFs"
fi

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

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

if [ -n "$verbose" ]; then
    echo "Using modules: $MODULES"
fi

MNTIMAGE=/tmp/initrd.$$
IMAGE=/tmp/initrd.img-$$
MNTPOINT=/tmp/initrd.mnt-$$
RCFILE=$MNTIMAGE/linuxrc

if [ -f $MNTIMAGE ]; then
    echo "$MNTIMAGE already exists.  Remove it and try again" >&2
    exit 1
fi

if [ -f $IMAGE ]; then
    echo "$IMAGE already exists. Remove it and try again" >&2
    exit 1
fi

if [ "$FS" = "ext2" ] ; then
	dd if=/dev/zero of=$IMAGE bs=1k count=$IMAGESIZE 2> /dev/null

	if [ -e /dev/.devfsd ]; then
		LOOPDEV=/dev/loop/
	else
		LOOPDEV=/dev/loop
	fi

	for devnum in 0 1 2 3 4 5 6 7 8; do
	    if losetup ${LOOPDEV}${devnum} $IMAGE 2>/dev/null ; then break; fi
	done

	if [ "$devnum" = "8" ]; then
	    rm -rf $MNTPOINT $IMAGE
	    echo "All of your loopback devices are in use!" >&2
	    exit 1
	fi

	LODEV=${LOOPDEV}${devnum}

	# We have to "echo y |" so that it doesn't complain about $IMAGE not
	# being a block device
	echo y | mke2fs $LODEV $IMAGESIZE >/dev/null 2>/dev/null

	if [ -n "$verbose" ]; then
	    echo "Using loopback device $LODEV"
	fi

	mkdir -p $MNTPOINT
	mount -t ext2 $LODEV $MNTPOINT || {
		echo "Can't get a loopback device"
		exit 1
	}
else
	mkdir -p $MNTPOINT
fi

mkdir -p $MNTIMAGE
mkdir -p $MNTIMAGE/lib
mkdir -p $MNTIMAGE/bin
mkdir -p $MNTIMAGE/etc
mkdir -p $MNTIMAGE/dev
mkdir -p $MNTIMAGE/loopfs

# We don't need this directory, so let's save space
rm -rf $MNTPOINT/lost+found

inst /bin/ash.static "$MNTIMAGE/bin/sh"
inst /sbin/insmod.static "$MNTIMAGE/bin/insmod"

for MODULE in $MODULES; do
    cp $verbose -a /lib/modules/$kernel/$MODULE $MNTIMAGE/lib
done

# mknod'ing the devices instead of copying them works both with and
# without devfs...
mknod $MNTIMAGE/dev/console c 5 1
mknod $MNTIMAGE/dev/null c 1 3
mknod $MNTIMAGE/dev/ram b 1 1
mknod $MNTIMAGE/dev/systty c 4 0
for i in 1 2 3 4; do
    mknod $MNTIMAGE/dev/tty$i c 4 1
done

echo "#!/bin/sh" > $RCFILE
echo "" >> $RCFILE

for MODULE in $MODULES; do
    module=`echo $MODULE | sed "s|.*/||" | sed "s/.o$//"`

    options=`sed -n -e "s/^options[ 	][ 	]*$module[ 	][ 	]*//p" $modulefile`

    if [ -n "$verbose" ]; then
        echo "Loading module $module with options $options"
    fi
    echo "echo \"Loading $module module\"" >> $RCFILE
    echo "insmod /lib/$module.o $options" >> $RCFILE
done

if [ -n "$loopDev" ]; then
    if [ ! -d /initrd ]; then
	mkdir /initrd
    fi

    cp -a $loopDev $MNTIMAGE/dev
    cp -a $rootdev $MNTIMAGE/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

chmod +x $RCFILE

(cd $MNTIMAGE; tar cf - .) | (cd $MNTPOINT; tar xf -)

if [ "$FS" = "ext2" ] ; then
	mount $MNTPOINT
	losetup -d $LODEV
else
	genromfs -f "$IMAGE" -d "$MNTPOINT" -V "PLD initrd for kernel $kernel"
	
fi

if is_yes "$COMPRESS" ; then
    gzip -9 < $IMAGE > $target
else
    cp -a $IMAGE $target
fi
rm -rf $MNTIMAGE $MNTPOINT $IMAGE

