#!/bin/bash
#
# prepinit
# Copyright 2005 VMware, Inc.  All rights reserved.
#
# Initrd preparation script 
# - Copies binaries to the right places in the initrd fs. 
# - Creates the init scripts to be run at boot time.

PATH=/sbin:/usr/sbin:$PATH
export PATH

rdroot=""
kernel=""
force=""
verbose=""
showrc=""
dbg_rd=${DEBUG_ESX:-}

# What?
PREMODULES="vmnixmod.o"
VMSBINAPPS="vmkloader vmkload_mod esxcfg-init vsd vmkchdev vmkfstools"
VMBINAPPS=""
VMKERNEL="vmkernel"
VMWMOD="lvmdriver vmfs3" 

VMWDRVS=
VMSOLIBS="ld-linux.so.2 libdl.so.2 libc.so.6"
VMUSR_SOLIBS="libvmksysinfo.so"
VMWCONF="esx.conf simple.map" 

# Whence? 
KMODDIR=
VMWLIBDIR=/usr/lib/vmware
VMWETCDIR=/etc/vmware
SBINDIR=/usr/sbin
SOLIBDIR=/lib
USR_SOLIBDIR=/usr/lib
BINDIR=/usr/bin

# Whither? 
RDLIBDIR=lib/vmware
RDBINDIR=bin/vmware
RDETCDIR=etc/vmware
RDLOGDIR=log
RDINITDIR=init
RDSOLIBDIR=lib
RDUSR_SOLIBDIR=usr/lib

msgfile="/$RDLOGDIR/messages"
pcilist="/$RDETCDIR/devlist"

usage () {
    echo "Usage: `basename $0` [-r] <ramdisk root> <kernel>" 
    exit 1
}

inst () {
   if [ "$#" != "3" -a $# != "4" ];then
       echo "inst usage: inst <file> <from> <to> <is_solib>"
       exit 1 
   fi
   if [ "$4" == "solib" ]; then
      opt="-maxdepth 1 ( -type f -o -type l )"
   else
      opt="( -type f -o -type l )"
   fi
   f=`find $2 $opt -name $1 | xargs` 
   # Must find something, but not two things.
   if [ ! -n "$f" ]; then
      echo "$1 not found in $2." 
      exit 1
   fi
   if echo "$f" | grep -q ' '; then
      echo "More than one $1 found." 
      exit 1
   fi
   cp $verbose $f $3
   if [ $? -ne 0 ]; then
      echo "Failed to copy $1." 
      exit 1
   fi
}

main () {

   if [ $(id -u) != 0 ]; then
      echo "Must be run as root."
      exit 1
   fi

   # Arg parsing.
   while [ $# -gt 0 ]; do
      case $1 in
         -v)
               verbose=-v
               ;;
         -vv)
               verbose=-v
               showrc="yes"
               ;;

         --debug-esx)
               dbg_rd=yes
               ;;
         -r)
               refresh=1
               ;;
         *)
               if [ -z "$rdroot" ]; then
                  rdroot=$1
               elif [ -z "$kernel" ]; then
                  kernel=$1
               else
                  usage
               fi
               ;;
      esac

      shift
   done

   # Sanity.
   if [ -z "$rdroot" -o -z "$kernel" ]; then
      usage
   fi

   if [ ! -d "$rdroot" ]; then
      echo "Bad root dir." >&2
      exit 1
   fi

   KMODDIR=/lib/modules/$kernel/misc
   if [ -n "$dbg_rd" ]; then
      KMODDIR=/lib/modules/$kernel/misc-debug
      VMWLIBDIR=/usr/lib/vmware-debug
      SBINDIR=/usr/lib/vmware-debug/bin
   fi

   if [ -n "$refresh" ]; then
      update_config || exit 1  
   else
      create_new_image || exit 1 
   fi

   exit 0 
}

#
# Populates ramdisk afresh.
#
create_new_image () 
{
   local VMKLOADRC=$rdroot/$RDINITDIR/1.vmkernel
   local VMKDRVRC=$rdroot/$RDINITDIR/40.vmkdrivers
   local VMKMODRC=$rdroot/$RDINITDIR/45.vmkmod

   mkdir -p $rdroot/$RDLIBDIR
   mkdir -p $rdroot/$RDBINDIR 
   mkdir -p $rdroot/$RDETCDIR
   mkdir -p $rdroot/vmfs

   # Copy vmware binaries. 
   # vmnixmod. 
   for mod in $PREMODULES; do
      inst "$mod" "$KMODDIR" "$rdroot/$RDLIBDIR"
   done

   # copy vmkernel from exact location.
   # Passing "solib" to inst() so that '-maxdepth 1' is used with the find call.
   inst "$VMKERNEL" "$VMWLIBDIR" "$rdroot/$RDLIBDIR" "solib"
   strip --strip-debug "$rdroot/$RDLIBDIR/$VMKERNEL"

   # need to do this here so we get the correct definition
   # (debug vs non-debug) of VMWLIBDIR
   VMWDRVS=`ls $VMWLIBDIR/vmkmod/`

   # vmklinux and basic modules. $VMWMOD is a subset of $VMWDRVS
   for mod in $VMWDRVS; do
      cp $verbose "$VMWLIBDIR/vmkmod/$mod" "$rdroot/$RDLIBDIR" || exit 1
      strip --strip-debug "$rdroot/$RDLIBDIR/$mod"
   done

   # sbin Apps/tools. 
   for app in $VMSBINAPPS; do
      inst "$app" "$SBINDIR" "$rdroot/$RDBINDIR"
   done

   # bin Apps/tools. 
   for app in $VMBINAPPS; do
      inst "$app" "$BINDIR" "$rdroot/$RDBINDIR"
   done

   # Shared libraries
   for lib in $VMSOLIBS; do
      inst "$lib" "$SOLIBDIR" "$rdroot/$RDSOLIBDIR" solib
   done

   mkdir -p "$rdroot/$RDUSR_SOLIBDIR"
   for lib in $VMUSR_SOLIBS; do
      inst "$lib" "$USR_SOLIBDIR" "$rdroot/$RDUSR_SOLIBDIR" solib
   done
   
   # Create initrd load script.
   [ -n "$verbose" ] && echo "Writing $VMKLOADRC"

   cat > $rdroot/$RDBINDIR/func <<EOF
export PATH=\$PATH:/$RDBINDIR

Log () {
   echo \$*
   echo \$* >> $msgfile
}

LOG () {
   echo \$* >> $msgfile
}
EOF
   
   newlist=/$RDLOGDIR/devlist.new
   intersect=/$RDLOGDIR/intersect

   cat > $VMKLOADRC <<EOF
#!/bin/ash
. /$RDBINDIR/func

Log "Loading vmnixmod" 
insmod /$RDLIBDIR/vmnixmod.o >>$msgfile 2>&1
Log "Loading VMkernel"
vmkloader /$RDLIBDIR/vmkernel >>$msgfile 2>&1
chvt 1
Log "Loading vmklinux"
vmkload_mod -e /$RDLIBDIR/vmklinux >>$msgfile 2>&1

# Check h/w config
initopt=""

cut -f1,2 /proc/bus/pci/devices | sort -n > $newlist
cmp -s $pcilist $newlist 
if [ \$? -eq 0 ]; then
   rm $newlist
else
   Log "Hardware has changed."
   touch /$RDLOGDIR/namegen
   
   removed=0

   sort -n $pcilist $newlist | uniq -d > $intersect 
   cmp -s $intersect $newlist && removed=1
   rm -f $intersect

   if [ \$removed -eq 0 ]; then
      initopt="-a"
      touch /$RDLOGDIR/namereboot 
   fi
fi

Log "Loading configuration"
esxcfg-init \$initopt >>$msgfile 2>&1

err=\$?
cp /proc/vmware/log /$RDLOGDIR/vmklog.vmk
exit \$err
EOF

   chmod +x $VMKLOADRC

   [ -n "$showrc" ] && echo "" && /bin/cat $VMKLOADRC && echo ""

   # Driver loading script.
   [ -n "$verbose" ] && echo "Writing $VMKDRVRC"

   # Load drivers using /proc/vmware/pci and driver map file.
   simpleMap="/$RDETCDIR/simple.map" 
   esxConf="/$RDETCDIR/esx.conf" 

   cat > $VMKDRVRC <<EOF
#!/bin/ash
. /$RDBINDIR/func

getval() {
   local module=\$1 
   local key=\$2
   local val=

   # Get value for key.
   val=\`grep "/vmkernel/module/\$module/\$key" $esxConf | cut -f2- -d'='\` 
   # Strip leading/trailing quotes.
   val=\`echo \$val | sed 's/^ *"//g' | sed 's/" *$//g'\`
   echo \$val
}

process_device() {
   local failed_modules=""
   local driver_list=""
   local module=

   # Divvy and determine drivers to load.
   while read -r bsf id sub junk ; do
      deviceLine=\`grep "^\$id \$sub" $simpleMap | head -1\`
      if [ -z "\$deviceLine" ]; then
         LOG "No driver found for '\$id' '\$sub' at \$bsf.  Trying just id"
         deviceLine=\`grep "^\$id 0000:0000" $simpleMap | head -1 \`
      fi
      if [ -n "\$deviceLine" ]; then
         # Give device to vmkernel.
	 vmkchdev -v \$bsf >> $msgfile 2>&1
         if [ \$? -eq 0 ]; then
            # Add driver to list if not already present.
            module=\`echo \$deviceLine | cut -d ' ' -f 3\`
            echo \$driver_list | grep -q "\$module" 
            if [ \$? -ne 0 ]; then
               driver_list="\$driver_list \$module"
            fi
         else
            LOG "Couldn't assign device at \$bsf to vmkernel."
         fi
      else
         LOG "No vmkernel driver found for '\$id' '\$sub' at \$bsf."
      fi 
   done
   
   # Load drivers.
   for module in \$driver_list; do
      local enabled=\`getval \$module enabled\`
      [ "\$enabled" = "false" ] && continue

      local modopt=\`getval \$module options\`

      Log "Loading VMkernel \$module (options: '\$modopt')"
      vmkload_mod $RDLIBDIR/\$module \$modopt >> $msgfile 2>&1
      err=\$?
      if [ \$err -ne 0 ]; then
         Log "\$module failed to load in VMkernel."
         failed_modules="\$module(\$err) \$failed_modules"
      fi
   done

   if [ -n "\$failed_modules" ]; then
      Log "The following modules failed to load: \$failed_modules"
      return 1
   else
      Log "All drivers successfully loaded."
      return 0
   fi
}

cat /proc/vmware/pci | process_device 
ret=\$?
cp /proc/vmware/log /$RDLOGDIR/vmklog.drivers
exit \$ret
EOF

   chmod +x $VMKDRVRC
   [ -n "$showrc" ] && echo "" && /bin/cat $VMKDRVRC && echo ""

   # VMkernel modules loading script.
   # This comes after the device drivers because some
   # modules depend on MAC addresses and such.

   [ -n "$verbose" ] && echo "Writing $VMKMODRC"

   cat > $VMKMODRC <<EOF
#!/bin/ash
. /$RDBINDIR/func

failed_modules=""
for module in $VMWMOD; do
   Log "Loading \$module"
   vmkload_mod /$RDLIBDIR/\$module >> $msgfile 2>&1
   err=\$?
   if [ \$err -ne 0 ]; then
      Log "\$mod failed to load in VMkernel."
      failed_modules="\$module(\$err) \$failed_modules"
   fi
done
cp /proc/vmware/log /$RDLOGDIR/vmklog.modules
if [ -n "\$failed_modules" ]; then
   return 1
else
   return 0
fi
EOF

   chmod +x $VMKMODRC
   [ -n "$showrc" ] && echo "" && /bin/cat $VMKMODRC && echo ""

   update_config || return 1
   return 0
}

#
# Updates only those parts of the initrd that are affected by config changes. 
#
update_config () {
   # Config files themselves.
   for conf in $VMWCONF; do
      inst "$conf" "$VMWETCDIR" "$rdroot/$RDETCDIR" 
   done

   # Devlist.
   cut -f1,2 /proc/bus/pci/devices | sort -n > $rdroot/$pcilist

   return 0
}

main $*

