#!/bin/bash

# mkinitrd
#
# Written by Erik Troan <ewt@redhat.com>
# Modified by Deanna Bonds <Deanna_Bonds@adaptec.com>
#
# 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>
#	Keith Owens <kaos@ocs.com.au>

PATH=/sbin:$PATH
export PATH

VERSION=2.6adaptec

IS_IX86=`uname -m | grep -q i.86`

compress=1
target=""
kernel=""
force=""
verbose=""
MODULES=""
ZMODULES=""
img_vers=""

IMAGESIZE=100
PRESCSIMODS="scsi_mod sd_mod unknown"
IGNOREMODS="$IGNOREMODS ppa imm ide-scsi ext2 usb-storage"

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

findmodule() {
    skiperrors=""
    modName=$1
    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
    for i in $IGNOREMODS; do
 	[ "$i" = "$modName" ] && return
    done

    fmPath=`(cd $root_dir/lib/modules/$kernel; find -type f -name $modName.o)`

    if [ -n "$fmPath" ]; then
	# only need to add each module once
	if echo $MODULES | grep $fmPath >/dev/null 2>&1 ; then : ; else
	    MODULES="$MODULES $fmPath"
	fi
    else
	fmPath=`(cd $root_dir/lib/modules/$kernel; find -type f -name $modName.o.gz)`

	if [ -n "$fmPath" ]; then
	    # only need to add each module once
	    if echo $ZMODULES | grep $fmPath >/dev/null 2>&1 ; then : ; else	
		zfmPath=`echo $fmPath | sed s/\.gz//`
		ZMODULES="$ZMODULES $zfmPath"
	    fi
	fi
    fi

    if [ -z "$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;
    
	echo "No module $modName found for kernel $kernel" >&2
	exit 1
    fi

}

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

is_ix86() {
    if [ -n "`uname -m | grep i.86`" ]
    then return 0
    fi
    return 1
}

root_dir=""
while [ $# -gt 0 ]; do
    case $1 in
	--fstab*)
	    if echo $1 | grep '=' >/dev/null ; then
	    	fstab=`echo $1 | sed 's/^--fstab=//'`
	    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 "mkinitrd: version $VERSION"
	    exit 0
	    ;;

	-v)
	    verbose=-v
	    ;;

	--nocompress)
	    compress=""
	    ;;

	--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=""
	    noscsi=1;
	    ;;
	--omit-raid-modules)
	    noraid=1;
	    ;;
	--image-version)
	    img_vers=yes
	    ;;
	--root)
	    if echo $1 | grep '=' >/dev/null ; then
	    	root_dir=`echo $1 | sed 's/^--root=//'`
	    else
		root_dir=$2
		shift
	    fi		
	    ;;
	*)
	    if [ -z "$target" ]; then
		target=$1
	    elif [ -z "$kernel" ]; then
		kernel=$1
	    else
		usage
	    fi
	    ;;
    esac

    shift
done

if [ -z "$fstab" ] ; then
	fstab=$root_dir"/etc/fstab"
fi

if [ -z "$modulefile" ] ; then
	modulefile=$root_dir"/etc/modules.conf"
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 $root_dir/lib/modules/$kernel ]; then
    echo $root_dir"/lib/modules/$kernel is not a directory." >&2
    exit 1
fi

for n in $PREMODS; do
	findmodule $n
done

if [ -z "$noscsi" ]; then
    if [ ! -f $modulefile ]; then
        modulefile=$root_dir/etc/conf.modules
    fi

    if [ -f $modulefile ]; then
	scsimodules=`grep -E "alias[ 	]+scsi_hostadapter" $modulefile | grep -v '^[ 	]*#' | LC_ALL=C sort -u | awk '{ print $3 }'`

	if [ -n "$scsimodules" ]; then
	    for n in $PRESCSIMODS; do
		findmodule $n
	    done

	    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
fi

# If we have ide devices and module ide, do the right thing
ide=/proc/ide/ide*
if [ -n "$ide" ]; then
    findmodule -ide-mod
    findmodule -ide-probe-mod
    findmodule -ide-disk
    findmodule -ide-cd
fi

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

fs=$(awk '$2 == "/" { print $3 }' $root_dir/etc/fstab)
[ -n "$fs" -a "$fs" != "ext2" ] && findmodule $fs

# check to see if we need to set up a loopback filesystem

fullloopfile=$(awk '$2 == "/" && $4 ~ "loop" { print $1 }' $root_dir/etc/fstab)

if [ -n "$fullloopfile" ]; then
    dir=$fullloopfile
    while [ -n "$dir" -a -z "$line" ]; do
        dir=$(dirname $dir)
	line=$(awk -v dir=$dir '$2 == dir { print $0 }' $root_dir/etc/fstab)	
    done
    if [ -z "$line" -o "$dir" = "$root_dir/" ]; then
	echo "bad fstab, loopback file doesn't belong to any device"
	exit 1
    fi
    loopdev=$(echo $line | awk '{ print $1 }')
    loopfs=$(echo $line | awk '{print $3 }')
    loopfile=$(echo $fullloopfile | sed "s|$dir||")

    if [ "$loopfs" = "vfat" -o "$loopfs" = "msdos" ]; then
	basicmodules="$basicmodules fat"
    fi
    basicmodules="$basicmodules ${loopfs} loop"
fi

for n in $basicmodules; do 
    findmodule $n
done

if [ -n "$ifneeded" -a -z "$MODULES" -a -z "$ZMODULES" ]; 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 $ZMODULES"
fi

MNTIMAGE=`mktemp -d /tmp/initrd.XXXXXX`
IMAGE=`mktemp /tmp/initrd.img.XXXXXX`
MNTPOINT=`mktemp -d /tmp/initrd.mnt.XXXXXX`
RCFILE=$MNTIMAGE/linuxrc

if [ -z "$MNTIMAGE" -o -z "$IMAGE" -o -z "$MNTPOINT" ]; then
    echo "Error creating temporaries.  Try again" >&2
    exit 1
fi

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

for MODULE in $MODULES; do
    cp $verbose -a $root_dir/lib/modules/$kernel/$MODULE $MNTIMAGE/lib
done
for MODULE in $ZMODULES; do
    gunzip -c $root_dir/lib/modules/$kernel/$MODULE.gz > $MNTIMAGE/lib/`basename $MODULE`
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

if is_ix86; then
    inst $root_dir/sbin/mkinitrd_helper $RCFILE
    ln -s bin $MNTIMAGE/sbin
    ln -s $root_dir/linuxrc $MNTIMAGE/sbin/modprobe
    CONF_HELPER=$MNTIMAGE/mkinitrd_helper.conf
    rm -f $CONF_HELPER
else
    inst $root_dir/sbin/sash "$MNTIMAGE/bin/sash"
    inst $root_dir/sbin/insmod.static "$MNTIMAGE/bin/insmod"

    echo "#!/bin/sash" > $RCFILE
    echo "" >> $RCFILE
    echo "aliasall" >> $RCFILE
    echo "" >> $RCFILE
    chmod +x $RCFILE
fi

for MODULE in $MODULES $ZMODULES; 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
    if is_ix86; then
	echo "insmod" >> $CONF_HELPER
	echo "$root_dir/lib/$module.o $options" >> $CONF_HELPER
    else
	echo "echo \"Loading $module module\"" >> $RCFILE
	echo "insmod -f $root_dir/lib/$module.o $options" >> $RCFILE
    fi
done

if [ -n "$loopdev" ]; then
    [ -d $root_dir/initrd ] || mkdir $root_dir/initrd

    cp -a $loopdev $MNTIMAGE/dev
    cp -a $root_dir/dev/loop7 $MNTIMAGE/dev

    if is_ix86; then
	echo "mount" >> $CONF_HELPER
	echo "$loopdev $root_dir/loopfs $loopfs" >> $CONF_HELPER
	echo "set_loop" >> $CONF_HELPER
	echo "$root_dir/dev/loop7 $root_dir/loopfs/$loopfile" >> $CONF_HELPER
    else
	echo "echo Mounting device containing loopback root filesystem" >> $RCFILE
	echo "mount -t $loopfs $loopdev $root_dir/loopfs" >> $RCFILE
	echo "echo Setting up loopback file $loopfile" >> $RCFILE
	echo "losetup $root_dir/dev/loop7 $root_dir/loopfs/$loopfile" >> $RCFILE
    fi
fi

if [ -n "$startraid" ]; then
    if is_ix86; then
	cp -a $root_dir/dev/md0 $MNTIMAGE/dev
	echo "raidautorun" >> $CONF_HELPER
	echo "$root_dir/dev/md0" >> $CONF_HELPER
    else
	echo "Warning, I can't raidautorun on your arch, raid-on-/ won't work." >&2
    fi
fi

for i in `ls -sk $MNTIMAGE/*/* | sed 's/ [^ ]*$//'`; do
    IMAGESIZE=$[IMAGESIZE + $i]
done
NB_INODES=$[`find $MNTIMAGE | wc -w` + 20];
IMAGESIZE=$[IMAGESIZE + NBFILES / 10]  # 10 inodes needs 1k

dd if=/dev/zero of=$IMAGE bs=1k count=$IMAGESIZE 2> /dev/null

if [ -n "$verbose" ]; then
    if is_ix86; then
	echo "Contents of CONF_HELPER:"
	cat $CONF_HELPER
    else
	echo "Contents of RCFILE:"
	cat $RCFILE
    fi
    echo "Creating filesystem with size ${IMAGESIZE}KB and $NB_INODES inodes"
fi
mke2fs -q -m 0 -F -N $NB_INODES -s 1 $IMAGE

mkdir -p $MNTPOINT
mount -t ext2 $IMAGE $MNTPOINT -o loop || {
	echo "Can't get a loopback device"
	exit 1
}

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

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

umount $MNTPOINT

if [ -n "$compress" ]; then
    gzip -9 < $IMAGE > $target
else
    cp -a $IMAGE $target
fi
rm -rf $MNTIMAGE $MNTPOINT $IMAGE
