#!/bin/sh

# geninitrd
#
#	by Jacek Konieczny <jajcus@pld.org.pl>
#
# based on mkinitrd 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=2.1

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

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

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

if is_yes $USEBSP ; then
	[ ! -x /sbin/bsp ] && USEBSP="no"
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 unknown sd_mod"
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]" >&2
    echo "       [--with=<module>] [--image-version] [--fstab=<fstab>] [--nocompress]" >&2 
    echo "       [--fs=rom|ext2|cram] [--no-bsp]" >&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
}


my_find() {
	for name in `ls`
	do
	   if [ -d $name ]; then
	   	if [ "$name" != "build" ]; then
			(cd $name;my_find $1/$name $2)
		fi
	   else
	   	[ -f $name -a "$name" = "$2" ] && echo $1/$name
	   fi
	done
}

findmodule() {
    skiperrors=""
    modName=$1
    if [ "$(echo $modName | awk '{print(substr($0,1,1));}')" = "-" ]; then
	skiperrors=1
	modName="$(echo $modName | awk '{print(substr($0,2));}')"
    fi

    if [ "$modName" = "pluto" ]; then
	findmodule fc4
	findmodule soc
    fi
    if [ "$modName" = "fcal" ]; then
	findmodule fc4
	findmodule socal
    fi

    fmPath="`(cd /lib/modules/$kernel; my_find . "$modName.o")`"

    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
    tmpFmPath="`echo "$fmPath"|awk -F/ '{for(i=1;i<NF;i++) { printf("%s\\/",$i); } { print $NF; }}'`"
    if is_yes "`echo "$MODULES" | awk '/"'$tmpFmPath'"/ { print "yes"; }' `" ; 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
    	--no-bsp)
	    USEBSP="no"
	    ;;
	    
	--fstab*)
	    if is_yes "`echo $1 | awk '/=/ { print "yes"; }'`" ; then
	    	fstab="`echo $1 | awk -F= '{print $2;}'`"
	    else
		fstab="$2"
		shift
	    fi		    
	    ;;

	--with*)
	    if is_yes "`echo $1 | awk '/=/ { print "yes"; }'`" ; then
	    	modname="`echo $1 | awk -F= '{print $2;}'`"
	    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 is_yes "`echo $1 | awk '/=/ { print "yes"; }'`" ; then
	    	modname="`echo $1 | awk -F= '{print $2;}'`"
	    else
		modname="$2"
		shift
	    fi		    
	    PREMODS="$PREMODS $modname"
	    ;;
	--omit-scsi-modules)
	    PRESCSIMODS=""
	    PROBESCSI="no";
	    ;;
	--omit-raid-modules)
	    PROBERAID="no";
	    ;;
	--omit-ide-modules)
	    PROBEIDE="no";
	    ;;
	--fs)
	    FS="$2"
	    shift
	    ;;
	--fs*)
	    FS="`echo $1 | awk -F= '{print $2;}'`"
	    ;;
	--image-version)
	    img_vers=yes
	    ;;
	*)
	    if [ -z "$target" ]; then
		target="$1"
	    elif [ -z "$kernel" ]; then
		kernel="$1"
	    else
		usage
	    fi
	    ;;
    esac

    shift
done

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

case "$FS" in
	ext2)
		if [ ! -x /sbin/mke2fs ]; then
			echo "mke2fs is missing"
			exit 1
		fi
		;;
	rom)
		if [ ! -x /sbin/genromfs ]; then
			echo "genromfs is missing"
			exit 1
		fi
		;;
	cram)
		if [ ! -x /sbin/mkcramfs ]; then
			echo "mkcramfs is missing"
			exit 1
		fi
		 ;;
	*)
		echo "Filesystem $FS not supported"
		exit 1
		;;
esac

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 "$PROBESCSI" && is_yes "$(echo "$rootdev" | awk '/^\/dev\/sd/ { print "yes"; }')" ; then

    for n in $PRESCSIMODS; do
	if [ "$n" = "unknown" ] ; then
    		if [ ! -f "$modulefile" ]; then
        		modulefile=/etc/conf.modules
    		fi
    		if [ -f "$modulefile" ]; then
			scsimodules="`awk '/scsi_hostadapter/ && ! /^[\t ]*#/ { print $3; }' $modulefile| LC_ALL=C sort -u`"
			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
	else
	    findmodule "$n"
	fi
    done

fi

if is_yes "$PROBEIDE" && is_yes "`echo "$rootdev" | awk '/^\/dev\/hd/ { print "yes"; }'`" ; then
    for n in $PREIDEMODS; do
	    findmodule "$n"
    done
fi

if is_yes "$PROBERAID" ; then
    # load appropriate raid devices if necessary
    if is_yes "`awk '/^\/dev\/md/ && ! /noauto/ { print "yes"; }' "$fstab"`" ; then
	for number in $(awk '/^[\t ]*raid-level/ { print $2; }' /etc/raidtab | 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 is_yes "`echo "$rootdev" | awk -F/ '{print($3);}' | awk '/loop/ { print "yes"; }'`" ; then
    if [ ! -x /sbin/losetup ]; then
	echo "losetup is missing"
	exit 1
    fi
    key="^# $(echo $rootdev | awk -F/ '{print($3);}' | tr '[a-z]' '[A-Z]'):"
    if ! is_yes "`awk '/'$key'/ { print( "yes"); }' $fstab`" ; 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="`awk '/'$key'/ { print $0; }' $fstab`"
    loopDev="$(echo $line | awk '{print $3}')"
    loopFs="$(echo $line | awk '{print $4}')"
    loopFile="$(echo $line | awk '{print $5}')"

    BASICMODULES="$BASICMODULES -loop"
    findmodule "-$loopFs"
    BASICMODULES="$BASICMODULES -${loopFs}"
    # don't have any clue, how is this supposed to work,
    # anyway no bsp
    USEBSP="no"
else # Check for other filesystems
    rootFs="`awk '{if ($2 == "/") {print $3}}' $fstab`"
    findmodule "-$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="`mktemp -d /tmp/initrd.XXXXXX`"
IMAGE="`mktemp -u /tmp/initrd.img-XXXXXX`"
MNTPOINT="`mktemp -d /tmp/initrd.mnt-XXXXXX`"
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

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

	mkdir -p "$MNTPOINT"
	mount -o loop -t ext2 "$IMAGE" "$MNTPOINT"
else
	mkdir -p "$MNTPOINT"
fi


mkdir -p "$MNTIMAGE"/{lib,bin,etc,dev,loopfs}


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

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

if is_yes "$USEBSP" ; then
    s="$MNTIMAGE/etc/startup"
    inst /sbin/bsp "$RCFILE"
    echo "# autogenerated startup" > "$s"
    echo "" >> "$s"
    
    for MODULE in $MODULES; do
        module="`echo $MODULE | awk -F/ '{ $0=$NF } /.o$/ { $0=substr($0,1,length($0)-2); } { print $0; }'`"
        options="`awk '{ if($1 == "options" && $2 == "'${module}'") { for(i=3;i<=NF;i++) printf("%s ",$i); }}' "$modulefile"`"
    
        if [ -n "$verbose" ]; then
            echo "Loading module $module with options $options"
        fi
        echo "insmod $module.o $options" >> "$s"
    done
else
    inst /bin/trash "$MNTIMAGE/bin/sh"
    inst /sbin/insmod.static "$MNTIMAGE/bin/insmod"
    
    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
    mknod "$MNTIMAGE/dev/zero" c 1 5
    
    echo "#!/bin/sh" > "$RCFILE"
    echo "" >> "$RCFILE"
    
    for MODULE in $MODULES; do
        module="`echo $MODULE | awk -F/ '{ $0=$NF } /.o$/ { $0=substr($0,1,length($0)-2); } { print $0; }'`"
        options="`awk '{ if($1 == "options" && $2 == "'${module}'") { for(i=3;i<=NF;i++) printf("%s ",$i); }}' "$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
fi

chmod +x "$RCFILE"

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

case "$FS" in
	ext2)
		umount "$IMAGE"
		;;
	rom)
		genromfs -f "$IMAGE" -d "$MNTPOINT" -V "PLD initrd for kernel $kernel"
		;;
	cram)
		mkcramfs "$MNTPOINT" "$IMAGE"
		;;
	*)
		echo "Filesystem $FS not supported by $0";
esac		

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