#!/bin/bash

# mkinitrd
#
# Written by Erik Troan <ewt@redhat.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 <jakub@redhat.com>
#	Carlo Arenas Belon (carenas@chasqui.lared.net.pe>
#	Keith Owens <kaos@ocs.com.au>
#	Bernhard Rosenkraenzer <bero@redhat.com>
#	Matt Wilson <msw@redhat.com>
#
# Patched for Mandrake by Guillaume Cottenceau <gc at mandrakesoft.com>
#

PATH=/sbin:$PATH
export PATH

VERSION=3.1.6-mdk

compress=1
target=""
kernel=""
force=""
verbose=""
MODULES=""
img_vers=""
modulefile=/etc/modules.conf
tmpdir=

splash_dir=/usr/share/bootsplash/
splash=auto
theme=Mandrake

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

[[ -f /etc/sysconfig/bootsplash ]] && source /etc/sysconfig/bootsplash
[[ -n $SPLASH ]] && splash=$SPLASH
[[ -n $THEME ]] && theme=$THEME
[[ $splash == no ]] && splash=

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

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

    if [ "$modName" = "i2o_block" ]; then
	findmodule i2o_pci
	findmodule i2o_core
	modName="i2o_block"
    fi

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

    if [ "$modName" = "mptscsih" ]; then
	findmodule mptbase
	modName="mptscsih"
    fi

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

    if [ "$modName" = "xfs" ]; then
	findmodule -pagebuf
	findmodule -xfs_support
	findmodule -xfs_dmapi
	modName="xfs"
    fi

    if [ "$modName" = "ext3" ]; then
	if [ -n "$skiperrors" ]; then
		findmodule -jbd
	else
		findmodule jbd
	fi
	modName="ext3"
    fi

    # support generation of initrd suitable for a raid system on a non raid system
    if echo $modName | egrep '^raid[0-5]$|^linear$' >/dev/null 2>&1; then
	startraid=1
    fi

    for i in $IGNOREMODS; do
 	[ "$i" = "$modName" ] && return
    done

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

    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 /lib/modules/$kernel; find -type f -name $modName.o.gz | grep -v build)`

	if [ -n "$fmPath" ]; then
	    # only need to add each module once
	    if echo $MODULES | grep $fmPath >/dev/null 2>&1 ; then : ; else	
		zfmPath=`echo $fmPath | sed s/\.gz//`
		MODULES="$MODULES $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 $POSTSCSIMODS; do
	    if [ "$n" = "$modName" ]; then
		return;
	    fi
	done;
    
	echo "No module $modName found for kernel $kernel" >&2
	exit 1
    fi

}

is_good_fs() {
    local parttype= tmpname=
    local dir=$1
    [[ -d $dir ]] || return 1
    [[ -w $dir ]] || return 1
    [[ $dir == */ ]] && dir=${dir%/}
    parttype=$(awk "{if (\$2 == \""$dir"\") print \$3 }" /proc/mounts)

    while tmpname=${dir%/*} && [[ -z $parttype ]];do
	[[ -z $tmpname ]] && tmpname=/
	parttype=$(awk "{if (\$2 == \""$tmpname"\") print \$3 }" /proc/mounts)
	dir=$tmpname
    done

    case $parttype in
	nfs|tmpfs) return 1;;
	*) return 0;
    esac

}

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
}

is_ppc() {
    if [ -n "`uname -m | grep ppc`" ]; then return 0; fi
    return 1
}

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		    
	    ;;

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

    shift
done

if [[ $splash == auto && -f $splash_dir/scripts/detect-resolution ]];then
    splash=$( $splash_dir/scripts/detect-resolution )
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

if [ -z "$noscsi" ]; then
    if [ ! -f $modulefile ]; then
        modulefile=/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 }'`
	# support probeall form as found with Mandrake 8.1 and superior
	scsimodules="$scsimodules `sed -n 's/^ *probeall  *scsi_hostadapter//p' $modulefile`"

	if [ -n "$scsimodules" ]; then
	    SAVEMODULES=$MODULES

	    for n in $PRESCSIMODS; do
		findmodule $n
	    done

	    SAVEMODULESCMP=$MODULES

	    for n in $(for m in $scsimodules; do echo $m; done | awk 'a[$1]=="" {print;a[$1]=1}'); do
    # for now allow scsi modules to come from anywhere.  There are some
    # RAID controllers with drivers in block/
		findmodule $n
	    done

	    if [ "$SAVEMODULESCMP" = "$MODULES" ]; then
		MODULES=$SAVEMODULES
	    else
		for n in $POSTSCSIMODS; do
		    findmodule $n
		done
	    fi

	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
fi

if [ -z "$noraid" ]; then
    # load appropriate raid devices if necessary, this means that the
    # /etc/raidtab *must* be up to date for raided root to work.
    #if grep '^/dev/md' $fstab | grep -v noauto >/dev/null 2>&1 ; then
	for number in $(grep '^[ 	]*raid-level' /etc/raidtab 2>/dev/null |
			  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 /etc/raidtab) not recognized" >&2
		;;
	    esac
	done
    #fi
fi


# check to see if we need to set up a loopback filesystem
fullloopfile=$(awk '$2 == "/" && $4 ~ "loop" { print $1 }' /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 }' /etc/fstab)	
    done
    if [ -z "$line" -o "$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||")

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

rootfs=$(awk '{ if ($1 !~ /^[ \t]*#/ && $2 == "/") { print $3; }}' $fstab)
rootfsopts=$(awk '{ if ($1 !~ /^[ \t]*#/ && $2 == "/" && $6) { print $4; }}' $fstab | sed "s|defaults||;s|auto||;s|loop||;s|,,|,|;s|,$||;s|^,||")

rootdev=$(awk '{ if ($1 !~ /^[ \t]*#/ && $2 == "/") { print $1; }}' $fstab)
if [ ${rootdev:0:6} = "LABEL=" ]; then
    rootdev=${rootdev/LABEL=/}
    if [ $(e2label /dev/root) = ${rootdev} ]; then
	rootdev=/dev/root
    fi
    echo "rootdev=${rootdev}"
fi
root_major=$(/bin/ls -l $rootdev | awk '{ print $5 }')

if [ $root_major = 58, ]; then
# root is on an LVM LV
    lvmroot=1
    findmodule lvm-mod
fi

# in case the root filesystem is modular
findmodule -${rootfs}

if [ "$rootfs" = "ext3" ] && echo $kernel | grep -q "^2\.2"; then
    echo "Mandrake 2.2 kernels don't have ext3 compiled in, falling back to ext2 for you."
    rootfs=ext2
fi

for n in $basicmodules; do 
    findmodule $n
done

[[ -d $splash_dir/scripts ]] || splash=

if [ "$rootfs" != "ext3" -a -z "$rootfsopts" -a -n "$ifneeded" -a -z "$MODULES" -a -z "$splash" ]; then
    if [ -n "$verbose" ]; then
	echo "Rootfs is not ext3, there is no rootfs special options, and"
	echo "no modules are needed -- not building initrd image."
    fi
    exit 0
fi

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

[[ -n $tmpdir ]] && { is_good_fs $tmpdir || tmpdir= ;} #command-line
[[ -z $tmpdir && -n $TMPDIR ]] && { is_good_fs $TMPDIR || tmpdir= && tmpdir=$TMPDIR ;} #environement
if [[ -z $tmpdir ]];then
    if is_good_fs /tmp;then
	tmpdir=/tmp
    elif is_good_fs /var/tmp;then
	tmpdir=/var/tmp
    elif is_good_fs /root/tmp;then
	tmpdir=/root/tmp
    else
	echo "Cannot use a tmp directory" >&2
	exit 1
    fi
fi
[[ -n $verbose ]] && echo "Using $tmpdir as temporary directory."

MNTIMAGE=`mktemp -d ${tmpdir}/initrd.XXXXXX`
IMAGE=`mktemp ${tmpdir}/initrd.img.XXXXXX`
MNTPOINT=`mktemp -d ${tmpdir}/initrd.mnt.XXXXXX`
RCFILE=$MNTIMAGE/linuxrc
# cleanup on exit, hangup, interrupt, quit, termination
trap 'rm -rf $MNTIMAGE $MNTPOINT $IMAGE' 0 1 2 3 15

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
mkdir -p $MNTIMAGE/proc
mkdir -p $MNTIMAGE/sysroot
ln -s bin $MNTIMAGE/sbin
# devfs will shadow some of my dev entries, I'll put them here
mkdir -p $MNTIMAGE/safedev   

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

inst /sbin/nash "$MNTIMAGE/bin/nash"
if is_ix86; then
    inst /sbin/insmod-DIET "$MNTIMAGE/bin/insmod"
else
    inst /sbin/insmod.static "$MNTIMAGE/bin/insmod"
fi
ln -s ../bin/nash $MNTIMAGE/sbin/modprobe
if [ -n "$lvmroot" ]; then
    INITRDFILES="/sbin/vgchange /sbin/vgscan"
    cp -aL /sbin/vgchange $MNTIMAGE/sbin
    cp -aL /sbin/vgscan $MNTIMAGE/sbin
    cp -aL /lib/libc.so.6 $MNTIMAGE/lib
    cp -aL /lib/ld-linux.so.2 $MNTIMAGE/lib
fi

for MODULE in $MODULES; do
    f="/lib/modules/$kernel/$MODULE"
    if [ -e $f ]; then
	cp $verbose -a $f $MNTIMAGE/lib
    else
	gunzip -c $f.gz > $MNTIMAGE/lib/`basename $MODULE`
    fi
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 $i
done

echo "#!/bin/nash" > $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 2>/dev/null`

    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

echo "echo Mounting /proc filesystem" >> $RCFILE
echo "mount -t proc /proc /proc" >> $RCFILE

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

    # our dear friend devfsd will create links for us if devfs in use
    cp -aL $loopDev $MNTIMAGE/safedev
    cp -aL /dev/loop7 $MNTIMAGE/safedev

    loopDev=`echo $loopDev | sed "s|/dev/|/safedev/|"`

    echo "echo Mounting device containing loopback root filesystem" >> $RCFILE
    echo "mount -t $loopFs $loopDev /loopfs" >> $RCFILE
    echo "echo Setting up loopback device on $loopFile" >> $RCFILE
    echo "losetup /safedev/loop7 /loopfs$loopFile" >> $RCFILE
    rootdev=/safedev/loop7
else
    echo "echo Creating root device" >> $RCFILE
    echo "mkrootdev /dev/root" >> $RCFILE
    rootdev=/dev/root
fi

if [ -n "$startraid" ]; then
    cp -aL /dev/md0 $MNTIMAGE/safedev
    echo "raidautorun /safedev/md0" >> $RCFILE
fi

if echo $kernel | grep -q "^2\.2"; then
if is_ppc; then
[ -n "$loopDev" ] && echo "echo 0x07070000 > /proc/sys/kernel/real-root-dev" >> $RCFILE
else
[ -n "$loopDev" ] && echo "echo 0x0707 > /proc/sys/kernel/real-root-dev" >> $RCFILE
fi
echo "umount /proc" >> $RCFILE

else
# Linux-2.4
echo "echo 0x0100 > /proc/sys/kernel/real-root-dev" >> $RCFILE
if [ -n "$lvmroot" ]; then
    echo "echo Configuring LVM" >> $RCFILE
    echo "/sbin/vgscan" >> $RCFILE
    echo "/sbin/vgchange -a y" >> $RCFILE
    IMAGESIZE=$[IMAGESIZE + 5000]
fi
echo "umount /proc" >> $RCFILE

[ -n "$rootfsopts" ] && rootfsopts_msg="with flags $rootfsopts"
echo "echo Mounting root filesystem $rootfsopts_msg" >> $RCFILE
echo "mount --ro -t $rootfs $rootdev /sysroot $rootfsopts" >> $RCFILE
echo "pivot_root /sysroot /sysroot/initrd" >> $RCFILE
echo "echo Remounting devfs at correct place if necessary" >> $RCFILE
echo "handledevfs" >> $RCFILE
fi

chmod +x $RCFILE


for i in `/bin/ls -sk $MNTIMAGE/*/* | sed 's/ [^ ]*$//'`; do
    IMAGESIZE=$[IMAGESIZE + $i]
done
NB_INODES=20
for i in `find $MNTIMAGE`; do NB_INODES=$[NB_INODES + 1]; done
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
    echo "Contents of RCFILE:"
    cat $RCFILE 2> /dev/null
    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

if [[ -n "$splash" && -f $splash_dir/scripts/make-boot-splash ]];then
    $splash_dir/scripts/make-boot-splash $target $splash
fi

exit 0
