#! /bin/bash

# Script to create an initial ram disk.
#
# usage: see below usage() or call with -h
#
#   As an alternative, you can specify the root device via the environment
#   variable rootdev (e.g. "rootdev=/dev/hda mk_initrd").
#
# on errors:
#   exit code > 0
#

# Version 1.12
#
# Author: Steffen Winterfeldt <wfeldt@suse.de>
# (c) 1999 SuSE GmbH
# (c) 1999 Heiko Eifeldt bugfix, add module parameter passing
# (c) 2000 Karsten Keil   option and kernel 2.4 support
#
# ChangeLog:
#
# 13/1/2001:	v1.12
#  - continue with next kernel/initrd on errors
#  - create initrd even if modules are missing
#    (exit code 9 is returned in these cases)
#  - keep old initrd if a new one could not be made
#
# 18/12/2000:   v1.11 Karsten Keil <kkeil@suse.de>
#  - adapt changes from pre 7.1
#  - make it working on new 2.4 module tree
#
# 18/8/2000:    v1.10 Karsten Keil <kkeil@suse.de>
#  - support for 2.4 kernel versions
#  - optional parameter for easy use with other kernel versions
#
#  6/7/2000:	v1.09
#  - started log
#  - use get_kernel_version instead of file
#  - increased initrd size to 2000k
#

#
# Print usage and exit
#
usage() {
	cat<<EOM
      	mk_initrd creates initial ramdisk images for booting linux
                  with enviroments which need loading modules before
                  mounting the root device (e.g SCSI controller)

        mk_initrd [options] [root_dir]

        options:
          -h               This Text.
          -k "kernel list" list of kernel images for which initrd files
                           are created (relativ to boot_dir)
                           defaults to "vmlinuz vmlinuz.suse"
          -i "initrd list" list of file names (relativ to boot_dir) for
                           the initrd; position have match to "kernel list"
                           defaults to "initrd initrd.suse"
          -m "module list" modules to include in initrd, defaults to
                           INITRD_MODULES variable in /etc/rc.config
          -b boot_dir      boot dir, defaults to "/boot"
          -d root_device   root device, defaults to the device from which
                           the root_dir is mounted; overwrites the rootdev
                           enviroment variable if set

        root_dir:          the directory the root partition is mounted to
                           defaults to "/" 
EOM
	exit
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# general configurable parameters

# the kernel images to use; must be in $boot_dir
kernels_default="vmlinuz vmlinuz_24 vmlinuz.suse"
arch=`uname -m`
[ "$arch" = ia64 -o "$arch" = ppc ] && kernels_default="vmlinux vmlinux.suse"
[ "$arch" = alpha ] && kernels_default="vmlinux.gz vmlinux.suse.gz"

# initial ram disks (corresponding to $kernels); dto. in $boot_dir
initrds_default="initrd initrd_24 initrd.suse"
[ "$arch" = ia64 -o "$arch" = ppc -o "$arch" = alpha ] && initrds_default="initrd initrd.suse"

kernels=""
initrds=""
modules=""
boot_dir=""

while getopts :hk:i:m:b:d: a ; do
	case $a in
		\:|\?)	case $OPTARG in
				k)	echo "-k requires kernel list parameter"
					;;
				i)	echo "-i requires initrd list parameter"
					;;
				m)	echo "-m requires module list parameter"
					;;
				b)	echo "-b requires boot dir parameter"
					;;
				d)	echo "-d requires root device parameter"
					;;
				*)  echo "Unknown option: -$OPTARG"
					echo "Try mk_initrd -h"
					;;
			esac
			exit 1
			;;
		k)	kernels=$OPTARG
			;;
		i)	initrds=$OPTARG
			;;
		m)	modules=$OPTARG
			;;
		b)	boot_dir=$OPTARG
			;;
		d)	rootdev=$OPTARG
			;;
		h)	usage
			;;
	esac
done
shift `expr $OPTIND - 1`

if [ -n "$kernels" ] ; then
   if [ -z "$initrds" ] ; then
      echo "you have to specify -k and -i or none"
      exit 1
   fi
fi

if [ -z "$kernels" ] ; then
   if [ -n "$initrds" ] ; then
      echo "you have to specify -k and -i or none"
      exit 1
   fi
fi

# the kernel images to use; must be in $boot_dir
[ -z "$kernels" ] && kernels="$kernels_default"

# initial ram disks (corresponding to $kernels); dto. in $boot_dir
[ -z "$initrds" ] && initrds="$initrds_default"

kc=`echo $kernels |wc -w`
ic=`echo $initrds |wc -w`
if [ $kc != $ic ] ; then
    echo "item count in -k and -i lists have to match"
    exit 1
fi


# *full* paths
[ -z "$boot_dir" ] && boot_dir=/boot

static_shell=/bin/ash.static
static_insmod=/sbin/insmod.static

# initrd size
image_blocks=5000
image_inodes=100

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# should be nothing to change below...

tmp_mnt=/tmp/mnt$$
tmp_msg=/tmp/msg$$
lx_rc=$tmp_mnt/linuxrc
is_mounted=

clean_up () {
  [ "$is_mounted" ] && umount $tmp_mnt
  is_mounted=
  rm -f $tmp_initrd $tmp_initrd.gz $tmp_msg
  [ -d $tmp_mnt ] && rmdir $tmp_mnt
}

error () {
  echo "$2"
  clean_up
  exit $1
}

oops () {
  echo "$2"
  clean_up
}

[ -n "$1" ] && root_dir="$1"
[ "$root_dir" ] || root_dir=/

tmp_initrd=$root_dir/tmp/initrd$$

x1=`mount 2>/dev/null | grep "on $root_dir "`
x2=`echo \`echo "$x1" | wc -l\``
x3=`echo "$x1" | cut -f 1 -d " "`

root_lvm=0
if [ -z "$rootdev" ] 
then
    [ "$x2" = 1 -a -b "$x3" ] && rootdev="$x3"
    major=`ls -l "$rootdev" | sed -e "s/.* \\([0-9]\+\\), *[0-9]\+.*/\\1/"`
    [ "$major" -ne 58 ] || root_lvm=1
fi

if [ $root_lvm -eq 1 ]
then
    echo root device is LVM
    static_shell=/bin/ash
    static_insmod=/sbin/insmod
    image_blocks=6000
    image_inodes=2000
fi

if [ "$root_dir" != / ] ; then
  LD_LIBRARY_PATH=$root_dir/usr/lib
  PATH=$root_dir/usr/bin:$PATH
  [ -x "$root_dir$static_shell" ] && static_shell="$root_dir$static_shell"
  [ -x "$root_dir$static_insmod" ] && static_insmod="$root_dir$static_insmod"
fi

[ "$rootdev" ] || error 1 "usage: mk_initrd [root_dir]"

echo "using \"$rootdev\" as root device (mounted on \"$root_dir\")"

[ -f "$root_dir/etc/rc.config" ] && . $root_dir/etc/rc.config

[ -z "$modules" ] && modules="$INITRD_MODULES"

[ "$modules" -o $root_lvm = 1 ] || {
  ( cd $root_dir$boot_dir ; rm -f $initrds )
  error 0 "no initrd required"
}

initrd_a=($initrds)

exit_code=0

kernel_idx=0
for k in $kernels ; do

  kk="$root_dir$boot_dir/$k"

  if [ -f "$kk" ] ; then

    vv=`/sbin/get_kernel_version $kk`

    ii="$root_dir$boot_dir/${initrd_a[$kernel_idx]}"

    echo
    echo "creating initrd \"$ii\" for kernel \"$kk\" ($vv)"

    [ -d "$root_dir/lib/modules/$vv/misc" ] || \
    [ -d "$root_dir/lib/modules/$vv/kernel" ] || {
      oops 2 "no version \"$vv\" modules found"
      continue
    }

    mkdir $tmp_mnt

    dd if=/dev/zero of=$tmp_initrd bs=1k count=$image_blocks 2>/dev/null
    mke2fs -q -F -b 1024 -m 0 -N $image_inodes $tmp_initrd 2>/dev/null
    tune2fs -i 0 $tmp_initrd >/dev/null 2>&1

    mount -oloop $tmp_initrd $tmp_mnt 2>/dev/null || { 
      if [ -f /lib/loop.o ] ; then
        insmod /lib/loop.o
        mount -oloop $tmp_initrd $tmp_mnt 2>/dev/null || {
          error 3 "failed to mount image"
        }
      else
        error 3 "failed to mount image"
      fi
    }
    is_mounted=1

    rmdir $tmp_mnt/lost+found
    mkdir $tmp_mnt/{bin,dev}
    cp -a $root_dir/dev/{tty1,tty2,zero,null,ram0,ram1,ram2,ram,ramdisk,fb0,console} $tmp_mnt/dev
    cp $static_shell $tmp_mnt/bin/sh 2>/dev/null || error 4 "no static shell"
    cp $static_insmod $tmp_mnt/bin/insmod 2>/dev/null || error 5 "no static insmod"
    if [ $root_lvm -eq 1 ]
    then
	mkdir $tmp_mnt/{lib,etc,proc}
	touch $tmp_mnt/etc/fstab
	cp -a $root_dir/dev/lvm  $tmp_mnt/dev
	cp -a $root_dir/dev/[ehs]d? $root_dir/dev/[ehs]d?[0-9]* $tmp_mnt/dev
	cp -a $root_dir/dev/md? $root_dir/dev/dasd* $tmp_mnt/dev
	cp -a $root_dir/dev/{i2o,ida,cciss} $tmp_mnt/dev
	cp -a $root_dir/lib/{ld-2.2.so,ld-linux.so.2} $tmp_mnt/lib
	cp -a $root_dir/lib/{liblvm.so,libc.so.6} $tmp_mnt/lib
	cp -a $root_dir/lib/libz.so.1* $tmp_mnt/lib
	cp -a $root_dir/sbin/{vgscan,vgchange}  $tmp_mnt/bin
	cp -a $root_dir/bin/{rm,mount,umount}  $tmp_mnt/bin
    fi

    echo '#! /bin/sh' >$lx_rc
    echo >>$lx_rc
    echo "export PATH=/bin" >>$lx_rc
    echo >>$lx_rc

    chmod 755 $lx_rc

    for i in $modules; do
      x=`cd $root_dir ; find lib/modules/$vv/ -name $i.o`
      if [ "$x" ] ; then
        echo "module $i is \"/$x\""
        tar -C $root_dir -cf - $x 2>/dev/null | tar -C $tmp_mnt -xpf - 2>/dev/null
        if [ $? != 0 ] ; then
          echo "failed to add module \"/$x\""
          oops 6 "initrd too small"
          continue 2
        fi
        modparms=`grep "^[ 	]*options $i" "$root_dir"/etc/modules.conf`
        modparms="${modparms#* * }"
        echo "echo \"Loading module $i $modparms ...\"" >>$lx_rc
        echo "-> insmod $i $modparms"
        echo "insmod $i $modparms" >>$lx_rc
        echo >>$lx_rc
      else
        echo "no such module: $i"
        exit_code=9
#        oops 7 "no such module: $i"
#        continue 2
      fi
    done

    if [ $root_lvm -eq 1 ]
    then
	echo "mount -tproc none /proc" >> $lx_rc
	echo "vgscan" >> $lx_rc
	echo "vgchange -a y" >> $lx_rc
	echo "umount /proc" >> $lx_rc
    fi

    umount $tmp_mnt
    is_mounted=

    gzip -9 $tmp_initrd

    cp -f $tmp_initrd.gz $ii || {
      oops 8 "failed to install initrd"
      continue
    }

    clean_up

  else

    echo "no kernel image \"$k\""

  fi

  kernel_idx=$((kernel_idx+1))

done

exit $exit_code
