#!/bin/sh
#
# --------------------------------------------------------------------------
# Copyright notice
# --------------------------------------------------------------------------
# Copyright: Rene Mayrhofer, Mar. 2000
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.  If not, write to
# the Free Software Foundation, 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
# On Debian GNU/Linux systems, the complete text of the GNU General
# Public License can be found in `/usr/share/common-licenses/GPL'.
# --------------------------------------------------------------------------
#

PATH=.:/bin:/sbin
export PATH

CHECK_FOR_IDENTIFIER=1
VERBOSE=no
SKIPCD=no

LOOPBACK_FILENAME_ISO=gibraltar.iso
LOOPBACK_FILENAME_CRAMFS=gibraltar.bin
MEDIA_MNTPOINT=/mnt/media
ROOT_MNTPOINT=/mnt/root
ROOTDEVVAR=/proc/sys/kernel/real-root-dev
CDROMDEVVAR=/proc/sys/dev/cdrom/info
HDBOOT_FILESYSTEMS="jbd ext3 ext2 reiserfs xfs fat vfat"

FOUND_ROOT=0

# initial setup
mount -t proc proc /proc 2> /dev/null || \
  echo "Error: mounting proc filesystem failed"

# some definitions
if [ $TERM = "linux" ] && ! grep "console=ttyS" /proc/cmdline >/dev/null; then
  COLOR_NORMAL="\033[0m"
  COLOR_BLUE="\033[1;34m"
  COLOR_GREEN="\033[1;32m"
  COLOR_RED="\033[1;31m"
else
  COLOR_NORMAL=""
  COLOR_BLUE=""
  COLOR_GREEN=""
  COLOR_RED=""
fi

. /initrd-common-definitions.sh
. /probe-devs.sh

# get the list of found cdrom drives
get_cdromlist() {
  if [ ! -r $CDROMDEVVAR ] ; then
    cdroms=""
    return 0
  fi

  if [ $devfs_support -eq 0 ]; then
    local cdromline="`cat $CDROMDEVVAR | grep \"drive name\"`"
    set $cdromline
    shift 2
    cdroms=$@
  else
    # a lot easier with devfs support ....
    olddir="`pwd`"
    cd /dev
    cdroms="`find cdroms -name \"cdrom*\" | grep '/' | tr '\n' ' '`"
    cd $olddir
  fi
}

## deep magic to create the number for the kernel's real_root_dev variable,
## pointing to the given device
#calc_rootdev() {
#  local maj=`ls -lL /dev/$1 | cut -c 33-37 | tr -d " "`
#  local min=`ls -lL /dev/$1 | cut -c 39-42 | tr -d " "`
#  rootdev="`expr $maj \* 256 \+ $min`"
#}

# check for a given cdrom drive if the correct cdrom is inserted
# expects the device name (relative to /dev), either "CD" or "USB medium" and
# the mountpoint as parameters and returns
# 0....correct cdrom found
# 1....not found
check_cdrom_or_usb() {
  local dev=$1
  local type="$2"
  local fstype
  if [ "$type" = "CD" ]; then
    fstype="-t iso9660"
  else
    insmod_helper fat
    insmod_helper vfat
    fstype=""
  fi
  echo -n "  checking $dev ... "

  # try to mount the device
  if mount $fstype -o ro "/dev/$dev" "$3" 2>/dev/null; then
    if [ $CHECK_FOR_IDENTIFIER -eq 1 ]; then
      # check if the ID matches
      if [ ! -r $3/id.txt ]; then
        echo "$type inserted, but no identifier found"
        umount $3
        return 1
      fi
      cd_id="`cat $3/id.txt`"
      if [ ! "$initrd_id" = "$cd_id" ]; then
        echo "$type inserted, but identifier does not match"
        umount $3
        return 1
      fi
    fi

    # please go away with that unmount - when will pivot_root work ??
    ##umount $3
    FOUND_ROOT=1
    echo -e "${COLOR_GREEN}found${COLOR_NORMAL}"

    ##echo -n "Using root device /dev/$dev = "
    ##calc_rootdev $dev
    ##echo "$rootdev"
    ##echo "$rootdev" > $ROOTDEVVAR
    ## yes, no more deep magic needed - calc_rootdev has gone away since it
    ## it a lot easier with the pivot_root call now. we just have to leave the
    ## correct root device mounted and use pivot_root. there is no need to
    ## calculate some special root device number anymore.
    return 0
  else
    echo "no $type inserted"
    return 1
  fi
}

# try to find the cdrom
locate_cdrom() {
  # check every device
  for dev in $cdroms ; do
    if [ $devfs_support -eq 0 ]; then
      # special for scsi cdroms - the kernel reports sr[0-9],
      # but the device files are named scd[0-9]
      if [ `expr $dev : "sr[0-9]*$"` -gt 2 ] ; then
        dev="scd`expr $dev : \"sr\([0-9]*\)$\"`"
      fi

      # if the corresponding device file isn't there, create it
      if [ ! -b /dev/$dev ] ; then
        MAKEDEV $dev >/dev/null 2>/dev/null
      fi
    #else
      # with devfs support it is easier - no need to kludge around with SCSI
      # device names
    fi

    if check_cdrom_or_usb $dev "CD" $MEDIA_MNTPOINT; then
      # found the correct cdrom, no need to search any more
      break
    fi
  done
}

# try to find the USB media
locate_usb() {
  install_usb_storage_mods
  install_usb_controller_mods
  # this seems to be necessary for the kernel to get a list of devices
  sleep 2
  for dev in `get_usb_storage_partitions`; do
    if check_cdrom_or_usb $dev "USB media" $MEDIA_MNTPOINT; then
      # found the correct USB media, no need to search further
      if [ -e $MEDIA_MNTPOINT/system/etc-static/gibraltar/version ]; then
        # ok, this seems to be an USB CD-ROM, no need to do anything else
        break
      else
        # this might be an USB stick, try to loopback-mount the image
        if [ ! -e $MEDIA_MNTPOINT/$LOOPBACK_FILENAME_ISO ] &&
           [ ! -e $MEDIA_MNTPOINT/$LOOPBACK_FILENAME_CRAMFS ]; then
          echo -e "${COLOR_RED}Error:${COLOR_NORMAL} The USB media does not contain all needed files !"
          # it's the best to stop here
          stopit
        fi
        insmod_helper loop
        if [ -e $MEDIA_MNTPOINT/$LOOPBACK_FILENAME_ISO ]; then
          if ! mount -o loop,ro -t iso9660 $MEDIA_MNTPOINT/$LOOPBACK_FILENAME_ISO $ROOT_MNTPOINT 2>/dev/null; then
            echo -e "${COLOR_RED}Error:${COLOR_NORMAL} Unable to mount Gibraltar image from USB media !"
            # it's the best to stop here
            stopit
          fi
        else
          if ! mount -o loop,ro -t cramfs $MEDIA_MNTPOINT/$LOOPBACK_FILENAME_CRAMFS $ROOT_MNTPOINT 2>/dev/null; then
            echo -e "${COLOR_RED}Error:${COLOR_NORMAL} Unable to mount Gibraltar image from USB media !"
            # it's the best to stop here
            stopit
          fi
        fi
        break
      fi
    fi
  done
}

killallbyname() {
  if [ $# -lt 1 ]; then
    echo "Usage: killallbyname <process(es) name>"
    return 1
  fi

  (ps | grep $1) |
  while read pid tty time desc; do
    kill $pid
  done
}

trymountrootdev() { 
  if mount -o ro $cmdlineroot $MEDIA_MNTPOINT 2>/dev/null; then
    local fsprocentry="`grep \"$cmdlineroot\" /proc/mounts`"
    set $fsprocentry
    local filesystem=$3
    echo -n "($filesystem) "
    ## the same goes here - no more calc_rootdev
    ##umount $cmdlineroot
    FOUND_ROOT=1
    return 0
  else
    return 1
  fi
}

add_link() {
  dev=$1
  n_part=`expr $dev : "/dev/...\(\w*\)"`
  letter=`expr $dev : "/dev/..\(\w\)"`

  # ide devices:
  if [ "$load_support" = "ide" ]; then
    case $letter in
       a) dest=/dev/ide/host0/bus0/target0/lun0
           ;;
       b) dest=/dev/ide/host0/bus0/target1/lun0
           ;;
       c) dest=/dev/ide/host0/bus1/target0/lun0
           ;;
       d) dest=/dev/ide/host0/bus1/target1/lun0
           ;;
    esac
  elif  [ "$load_support" = "hd" ]; then
    echo "Currently not ready."
    return
  fi
  ln -s $dest/part$n_part /dev/hd${letter}$n_part
}

parse_old_dev_name() {
  # it's a bit compilated to determine which modules should be loaded
  # with the old device naming scheme
  case `expr "$cmdlineroot" : "/dev/\(\w\)\d*"` in
    h) load_support="ide"
       ;;
    s) load_support="scsi"
       ;;
    m) load_support="md"
       ;;
    r) load_support="rd"
       ;;
    c) load_support="cciss"
       ;;
    *) load_support="unknown"
       ;;
  esac
}

stopit() {
  while true; do
    read key
  done
}

error() {
  echo -e "${COLOR_RED}Error:${COLOR_NORMAL} $1"
  exit 1
}



# do not allow the script to be killed
trap '' 2 3 15

initrd_id="`cat /etc/id.txt`"
echo
echo -n -e "${COLOR_BLUE}${initrd_id}${COLOR_NORMAL} loading, "

old_printk="`cat /proc/sys/kernel/printk`"
echo 0 0 0 0 > /proc/sys/kernel/printk

# now complete devfs support
devfs_support=0
if grep "devfs" /proc/filesystems >/dev/null; then
  # ahh, the kernel supports devfs, but is it mounted too ?
  if grep "devfs" /proc/mounts >/dev/null; then
    # The syslogd from busybox can not handle to have its socket on devfs
    # (I have no idea why there is a problem, the standard syslogd works
    # perfectly well with devfs mounted on /dev. It creates the socket without
    # complaining, but the busybox syslogd is unable to do that.).
    ln -s /tmp/syslog.socket /dev/log
    devfs_support=1
  fi
fi

#dmesg -n 1
# syslog is only needed so that the kernel does not print error messages
# to the console during insmod probing
syslogd -O messages

# set the real root filesystem invalid now
if [ ! -w $ROOTDEVVAR ] ; then
  echo "Error: $ROOTDEVVAR is not writeable - kernel problem ?"
  exit 1;
fi
echo "0" > $ROOTDEVVAR

# is this a normal CD boot or a rescue boot of an installed system ?
# (when an installed system should be booted, root=xxx must have been given
# on the kernel command line)
cmdline="`cat /proc/cmdline`"
boot_uml_hostfs=0
boot_image=0
for option in $cmdline; do
  if [ `expr $option : "verbose"` -gt 0 ]; then
    VERBOSE=yes
  elif [ `expr $option : "skipcd"` -gt 0 ]; then
    SKIPCD=yes
  elif [ `expr $option : "boot=/dev/.*"` -gt 0 ]; then
    # found the root option
    cmdlineroot="`expr $option : \"boot=\(/dev/.*\)\"`"
  elif [ `expr $option : "boot=image:/.*"` -gt 0 ]; then
    cmdlineroot="`expr $option : \"boot=image:\(/.*\)//.*"`"
    image_file="`expr $option : \"boot=image:/.*//\(.*\)"`"
    boot_image=1
  elif [ `expr $option : "boot=umlhost:/.*"` -gt 0 ]; then
    cmdlineroot="`expr $option : \"boot=umlhost:\(/.*\)\"`"
    boot_uml_hostfs=1
  fi
done

export VERBOSE

if [ -z $cmdlineroot ]; then
  # no root device given on command line - normal CD (or USB) boot
  if [ "$SKIPCD" != "yes" ]; then
    echo "searching for CD .... "
    rootdev=0
    cd /dev

    # first try the IDE/ATAPI cdroms
    install_ide_cd_mods
    echo -n "trying to locate cdrom: "
    get_cdromlist
    if [ "$cdroms" ] ; then
      echo "found: $cdroms"
    else
      echo "no ATAPI CD-ROM drives found"
    fi
    locate_cdrom
    if [ $FOUND_ROOT -eq 0 ] ; then
      install_scsi_cd_mods
      check_scsi
      echo -n "trying to locate cdrom: "
      get_cdromlist
      if [ "$cdroms" ] ; then
        echo "found: $cdroms"
      else
        echo "no CD-ROM drives found on already detected adapters"
      fi
      locate_cdrom
    fi
    if [ $FOUND_ROOT -eq 0 ] ; then
      probe_scsi_mods
      echo -n "trying to locate cdrom: "
      get_cdromlist
      if [ "$cdroms" ] ; then
        echo "found: $cdroms"
      fi
    fi
  fi
  if [ "$SKIPCD" = "yes" ] || [ ! "$cdroms" ] || [ $FOUND_ROOT -eq 0 ]; then
    # no CD-ROM drive found, but we could still use an USB device
    locate_usb
  fi
  if [ ! "$cdroms" ] && [ $FOUND_ROOT -eq 0 ]; then
    error "no CD-ROM drives found and no USB media present"
  fi

  # do this until the cdrom has been found, because going on without
  # this step is senseless
  while [ $FOUND_ROOT -eq 0 ] ; do
    locate_cdrom
    if [ $FOUND_ROOT -eq 0 ]; then
      echo -e "${COLOR_RED}Error:${COLOR_NORMAL} CD or USB media not found, please insert"\
              "and press Enter to continue"
      echo "To enter an interactive shell, press 's'."
      read key
      if [ "$key" = "s" ]; then
      	/bin/sh
      fi
    fi
  done
elif [ $boot_uml_hostfs -eq 0 ]; then
  if [ $boot_image -eq 0 ]; then
      echo -e "booting an installed system on"\
              "${COLOR_GREEN}${cmdlineroot}${COLOR_NORMAL}"
  else
      echo -e "booting image ${COLOR_GREEN}${image_file}${COLOR_NORMAL} on device "\
          "${COLOR_GREEN}${cmdlineroot}${COLOR_NORMAL}"
  fi

  if [ $devfs_support -eq 0 ]; then
    parse_old_dev_name
  else
    # when there is devfs support, it may happen that people use the old
    # naming scheme for giving the root partition - we don't handle this here
    load_support="`expr \"$cmdlineroot\" : \"/dev/\(\w*\)/.*\"`"
    if [ -z $load_support ]; then
      # this means that devfs support is compiled into the kernel, but the
      # user has given an old-style device name on the command line - do the
      # best to cope with this
      parse_old_dev_name
      case "$load_support" in
        ide|scsi)
          add_link $cmdlineroot
          ;;
        *)
          echo "Unable to parse the given root device name."
      esac
    fi
  fi

  case $load_support in
    ide)  if [ "$VERBOSE" = "yes" ]; then
            echo "Loading IDE modules."
          fi
          install_ide_hd_mods
          ;;
    scsi) if [ "$VERBOSE" = "yes" ]; then
            echo "Loading SCSI modules."
          fi
          install_scsi_hd_mods
          check_scsi
          ;;
    md)   if [ "$VERBOSE" = "yes" ]; then
            echo "Loading Raid1 Software Support (+ide +scsi)"
          fi
          insmod_helper raid1
          install_ide_hd_mods
          install_scsi_hd_mods
          check_scsi
          ;;
    rd)   if [ "$VERBOSE" = "yes" ]; then
            echo "Loading Mylex Support"
          fi
          ### ATTENTION: there is still a problem w/ lacking devices (
          insmod_helper DAC960
          ;;
    cciss) if [ "$VERBOSE" = "yes" ]; then
             echo "Loading Compaq CCISS Support"
	   fi
	   insmod_helper cciss
	   ;;
    # This support is for User-Mode-Linux with virtual block devices.
    # See below for support of hostfs.
    ubd)  if [ "$VERBOSE" = "yes" ]; then
            echo "Not loading support for User-Mode-Linux block devices."
            echo "Assuming that the kernel already supports it if booted this way."
          fi
    	  ;;
    *)    echo -e "${COLOR_RED}Error:${COLOR_NORMAL} Unkown root device type: $load_support"
          ;;
  esac

  if [ $devfs_support -eq 0 ]; then
    # without devfs, we will have to create the device file
    cd /dev
    makedevfile="`expr \"$cmdlineroot\" : \"/dev/\(\w\w\w\)\d*\"`"
    MAKEDEV $makedevfile >/dev/null 2>/dev/null
  else
    # with devfs support the device file must exist now - if not, the kernel
    # will not be able to find the device anyway
    if [ ! -e $cmdlineroot ]; then
      echo -e "${COLOR_RED}Error:${COLOR_NORMAL} The given root device does not exist !"
      # it's the best to stop here
      stopit
    fi
  fi

  # when will these 3 lines go away ??
  ##cmdlinedev="`expr \"$cmdlineroot\" : \"/dev/\(.*\)\"`"
  ##calc_rootdev $cmdlinedev
  ##echo "$rootdev" > $ROOTDEVVAR

  # at last, check if the filesystem of the partition is supported by this
  # kernel
  for fs in $HDBOOT_FILESYSTEMS ""; do
    echo -n "Trying to mount root filesystem ... "
    if trymountrootdev; then
      # perfect - the root filesystem could be mounted
      echo -e "${COLOR_GREEN}done${COLOR_NORMAL}"
      break
    else
      # oops, mounting failed - try the next filesystem
      echo "failed"
      if [ -n "$fs" ]; then
        if [ "$VERBOSE" = "yes" ]; then
          echo -n "Trying to load additional support for filesystem $fs ... "
        fi
        if insmod_helper $fs; then
          if [ "$VERBOSE" = "yes" ]; then
            echo -e "${COLOR_GREEN}done${COLOR_NORMAL}"
          fi
        else
          if [ "$VERBOSE" = "yes" ]; then
            echo "failed"
          fi
        fi
      fi
    fi
  done

  if [ $FOUND_ROOT -eq 0 ]; then
    echo -e "${COLOR_RED}Error:${COLOR_NORMAL} Unsupported root filesystem !"
    echo "Please check that you specified the correct root device name" \
         "and that it"
    echo "contains a valid, supported filesystem."
    # it's the best to stop here
    stopit
  fi

  # in this case, we have one more mount to do
  if [ $boot_image -eq 1 ]; then
      insmod_helper loop
      if ! mount -o loop,ro $MEDIA_MNTPOINT/$image_file $ROOT_MNTPOINT 2>/dev/null; then
        echo -e "${COLOR_RED}Error:${COLOR_NORMAL} Unable to mount Gibraltar image !"
        # it's the best to stop here
        stopit
      fi
  fi
else
  echo -e "booting an installed system on"\
          "${COLOR_GREEN}${cmdlineroot}${COLOR_NORMAL} inside UML host"
  # here we simply assume that hostfs is compiled into the kernel - we don't
  # want to have a module inside the initrd image that has to be compiled for
  # another (the user-mode-linux) kernel.
  mount -t hostfs -o ro -o $cmdlineroot none $MEDIA_MNTPOINT
fi

# cleanup
killallbyname "klogd"
killallbyname "syslogd"
#dmesg -n 3
echo $old_printk > /proc/sys/kernel/printk

# now the new root change mechanism for kernels >= 2.4.x
# do we have two mounts or only one (the first is always to MEDIA_MNTPOINT) ?
if grep -q $ROOT_MNTPOINT /proc/mounts; then
  cd $ROOT_MNTPOINT
else
  cd $MEDIA_MNTPOINT
fi

umount /proc 2> /dev/null || error "unmounting proc filesystem failed"

pivot_root . initrd || error "changing to new root failed"
# OK, we now use the binaries from the new root
PATH=/bin:/sbin:/usr/bin:/usr/sbin

# also mount devfs on the new root, since pivot_root does not change that
# automatically
if test $devfs_support -eq 1; then
  mount --bind initrd/dev dev 2> /dev/null || error "remounting devfs filesystem failed"
fi

echo "Initial ram disk setup completed. Initiating normal boot procedure."
echo

# first prepare the environment for init - mainly runlevel.conf
test -x /sbin/prepareroot && /sbin/prepareroot

# init doesn't seem to like having PATH already set....
# (no need to unset others, PATH is the only one that is exported)
unset PATH
exec /usr/sbin/chroot . /sbin/init -i

exit 0
