#!/bin/sh

# geninitrd
#
#	by Jacek Konieczny <jajcus@pld.org.pl>
#
# based on mkinitrd from RedHat
#
RCSID='$Id: geninitrd,v 2.6 2001/09/07 13:25:36 wiget Exp $'
PATH=/sbin:$PATH
export PATH

VERSION="`echo "$RCSID"|awk '{print $3}'`"

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

COMPRESS="yes"
FS="rom"
USEBSP="yes"
USERAIDSTART="no"

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
raidtab=/etc/raidtab
if [ "`uname -m`" = "ia64" ]; then
	IMAGESIZE=3000
else
	IMAGESIZE=1500
fi
PRESCSIMODS="scsi_mod unknown sd_mod"
PREIDEMODS="ide-mod ide-probe ide-probe-mod ide-disk"
fstab="/etc/fstab"

usage () {
	echo "usage: `basename $0` [--version] [-v] [-f] [--ifneeded] [--preload <module>]" 1>&2
	echo "       [--with=<module>] [--image-version] [--fstab=<fstab>] [--nocompress]" 1>&2 
	echo "       [--fs=rom|ext2|cram] [--no-bsp] [--modules-conf=<modules.conf>" 1>&2
	echo "       <initrd-image> <kernel-version>" 1>&2
	echo "       (ex: `basename $0` /boot/initrd-2.2.5-15.img 2.2.5-15)" 1>&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
			if [ -f $name -a "$name" = "$2" ]; then
				echo $1/$name
			fi
		fi
	done
}

my_dirname() {
	echo $1|awk -F/ '{print substr($0, 0, length($0) - length($NF));}'
}

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" 1>&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"
}

get_label_ext2 () {
	/sbin/e2label $1 2> /dev/null
}

get_uuid_ext2 () {
	/sbin/tune2fs -l $1 2> /dev/null | awk  -F: '/UUID:/ {gsub(" ",""); print $2}'
}

get_label_xfs () {
	/usr/sbin/xfs_db -x -p xfs_admin -c label -r "$1"|awk -F= '{sub("^\"","", $2); sub("\"$", "", $2); print $2}'
	
}

get_uuid_xfs () {
	/usr/sbin/xfs_db -x -p xfs_admin -c uuid -r "$1"|awk -F= '{print $2}'
}

find_root() { 
	eval `awk '/^[\t ]*#/ {next} {if ( $2 == "/" ) {print "rootdev=\"" $1 "\"\nrootFs=\"" $3 "\""}}' $fstab`
	case $rootdev in
	LABEL=*)
		case $rootFs in
		ext2) 
			if [ ! -x /sbin/e2label ] ; then
				echo "/sbin/e2label is missing" 1>&2
				exit 1
			fi
			get_label="get_label_ext2"
			;;
		xfs)
			if [ ! -x /usr/sbin/xfs_db ] ; then
				echo "/usr/sbin/xfs_db is missing" 1>&2
				exit 1
			fi
			get_label="get_label_xfs"
			;;
		*)
			echo "LABEL on $rootFs in not supported by geninitrd" 1>&2
			exit 1
			;;
		esac
		if [ ! -r /proc/partitions ] ; then
			echo '/proc/partitions is not readable'; 1>&2
			exit 1
		fi
		label=${rootdev#"LABEL="}
		for dev in `awk 'BEGIN {getline;getline} {print "/dev/" $4}' /proc/partitions` ; do
			if [ -r $dev ] && [ "$label" = "`$get_label $dev`" ] ; then
				if [ -n "$verbose" ] ; then
					echo "Using $dev as device for rootfs"
				fi
			    rootdev=$dev
			    rootdev_found=1
			    break
 			fi
		done
		if [ "$rootdev_found" != 1 ] ; then
		    echo "geninitrd can't find real device for LABEL=$label" 1>&2
		    exit 1
		fi
		;;
	UUID=*)
		case $rootFs in
		ext2) 
			if [ ! -x /sbin/tune2fs ] ; then
				echo "/sbin/tune2fs is missing" 1>&2
				exit 1
			fi
			get_uuid="get_uuid_ext2"
			;;
		xfs)
			if [ ! -x /usr/sbin/xfs_db ] ; then
				echo "/usr/sbin/xfs_db is missing" 1>&2
				exit 1
			fi
			get_label="get_uuid_xfs"
			;;
		*)
			echo "UUID on $rootFs in not supported by geninitrd" 1>&2
			exit 1
			;;
		esac
		if [ ! -r /proc/partitions ] ; then
			echo '/proc/partitions is not readable'; 1>&2
			exit 1
		fi
		uuid=${rootdev#"UUID="}
		for dev in `awk 'BEGIN {getline;getline} {print "/dev/" $4}' /proc/partitions` ; do
			if [ -r $dev ] && [ "$uuid" = "`$get_uuid $dev`" ] ; then
				if [ -n "$verbose" ] ; then
					echo "Using $dev as device for rootfs"
				fi
			    rootdev=$dev
			    rootdev_found=1
			    break
 			fi
		done
		if [ "$rootdev_found" != 1 ] ; then
		    echo "geninitrd can't find real device for UUID=$uuid" 1>&2
		    exit 1
		fi
		;;
	esac

}

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		    
		;;
	--modules-conf=*)
		modulefile="`echo $1 | awk -F= '{print $2;}'`"
		;;
	--modukes-conf)
		modulefile="$2"
		shift
		;;
	--raidtab=*)
		raidtab="`echo $1 | awk -F= '{print $2;}'`"
		;;
	--raidtab)
		raidtab="$2"
		shift
		;;
	--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"
		;;
	--fs=*)
		FS="`echo $1 | awk -F= '{print $2;}'`"
		;;
	--fs)
		FS="$2"
		shift
		;;
	--image-version)
		img_vers=yes
		;;
	--use-raidstart)
		USERAIDSTART=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 "/sbin/mke2fs is missing" 1>&2
			exit 1
		fi
		;;
	rom)
		if [ ! -x /sbin/genromfs ]; then
			echo "/sbin/genromfs is missing" 1>&2
			exit 1
		fi
		;;
	cram)
		if [ ! -x /sbin/mkcramfs ]; then
			echo "/sbin/mkcramfs is missing" 1>&2
			exit 1
		fi
		 ;;
	*)
		echo "Filesystem $FS on initrd is not supported" 1>&2
		exit 1
		;;
esac

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

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

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

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

find_root

if is_yes "`echo "$rootdev" | awk '/^\/dev\/md/ { print "yes"; }'`"; then
	eval `awk -v rootdev=$rootdev 'BEGIN {
	addide="no"; 
	addscsi="no";
	found="no";
} 

{ 
	gsub("\t"," "); 
	gsub("  +", " "); 
	gsub("^ ",""); 
	if (/^[\t ]*#/) 
		next; 
	if (/^raiddev/) { 
		if ($2 == rootdev) {
			found="yes";
		} else {
			if (found == "yes") {
				exit 0;
			};
		}; 
	}; 
	if (found == "yes") {
		if ($1 == "device") {
			if ($2 ~ /\/dev\/(sd|scsi)/) {
				addscsi="yes";
			};
			if ($2 ~ /\/dev\/(hd|ide)/) {
				addide="yes";
			};
		};
		if ($1 == "raid-level") {
			raidlevel=$2;
		}
	};
}

END {
	print "raidfound=" found "\nADDSCSI=" addscsi "\nADDIDE=" addide "\nraidlevel=" raidlevel "\n";
}' $raidtab `
    if is_yes "$raidfound" ; then
	    case "$raidlevel" in
	    [0145])
			findmodule "raid$raidlevel"
			;;
		linear)
			findmodule "linear"
			;;
	    *)
			echo "raid level $number (in $raidtab) not recognized" 1>&2
			;;
	    esac
    fi

else
	USERAIDSTART="no"
fi

if is_yes "$ADDSCSI" || is_yes "$(echo "$rootdev" | awk '/^\/dev\/(sd|scsi)/ { 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 "$ADDIDE" || is_yes "`echo "$rootdev" | awk '/^\/dev\/(hd|ide)/ { print "yes"; }'`" ; then
    for n in $PREIDEMODS; do
	    findmodule "$n"
    done
fi

if is_yes "`echo "$rootdev" | awk '/\/dev\/rd\// { print "yes"; }'`" ; then
	findmodule "DAC960"
fi

if is_yes "`echo "$rootdev" | awk '/\/dev\/ida\// { print "yes"; }'`" ; then
	findmodule "cpqarray"
fi

if is_yes "`echo "$rootdev" | awk '/\/dev\/cciss\// { print "yes"; }'`" ; then
	findmodule "cciss"
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" 1>&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
	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" 1>&2
	exit 1
fi

if [ -f "$IMAGE" ]; then
	echo "$IMAGE already exists. Remove it and try again" 1>&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
	MODULEDIR="`my_dirname "$MODULE"`"
	mkdir -p "$MNTIMAGE/lib/modules/$kernel/$MODULEDIR"
	cp $verbose -a "/lib/modules/$kernel/$MODULE" "$MNTIMAGE/lib/modules/$kernel/$MODULE"
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
			/bin/echo -n "Loading module $module "
			if [ -n "$options" ] ; then
				echo "with options $options."
			else
				echo "without options."
			fi
		fi
		echo "insmod /lib/modules/$kernel/$MODULE $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; }'`"
		if [ -f "$modulefile" ] ; then
			options="`awk '{ if($1 == "options" && $2 == "'${module}'") { for(i=3;i<=NF;i++) printf("%s ",$i); }}' "$modulefile"`"
		else
			options=
		fi

		if [ -n "$verbose" ]; then
			echo "Loading module $module with options $options"
		fi
		echo "Add module $module to initrd."
		echo "echo \"Loading $module module\"" >> "$RCFILE"
		echo "insmod -k /lib/modules/$kernel/$MODULE $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

if is_yes "$USERAIDSTART" ; then 
	if [ -x /sbin/raidstart.static ] ; then
		[ -n "$verbose" ] && echo "Add raidstart to initrd"
		mkdir -p "$MNTIMAGE/etc" || true
		inst /sbin/raidstart.static "$MNTIMAGE/bin/raidstart"
		inst "$raidtab" "$MNTIMAGE/etc/raidtab"
		echo "echo \"Starting RAID\"" >> "$RCFILE"
		echo "/bin/raidstart $rootdev" >> "$RCFILE"
	else
		echo "/sbin/raidstart.static is missing" 1>&2
		exit 1
	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"
