#!/bin/bash

export PATH=$PATH:/sbin

failed() {
  if [ "$1" == "" ] ; then
    touch /tmp/failed
  else
    echo -n "$1" > /tmp/failed
  fi
}

is_failed() {
  [ -e /tmp/failed ]
  return $?
}

failed_reason() {
  if [ -e /tmp/failed ] ; then
    cat /tmp/failed
  else
    echo -n ""
  fi
}

stop_md_devices() {
  grep ^md /proc/mdstat | while read mddevice rest
   do
    mdadm --stop $mddevice
   done
}

mcmserror() {
  echo "$1" 1>&2
  echo "Starting bash restore rescue shell to allow manual recovery." 1>&2
  /bin/bash -i
  echo "Rescue shell terminated." 1>&2
  exit 1
}

nvme_nods() {
 if grep -q nvme /proc/partitions ; then
   #nvme devices exist, create /dev entries as needed
   grep nvme /proc/partitions | while read -r major minor blocks name ; do
     [ -e /dev/${name} ] && rm /dev/${name}
     mknod /dev/${name} b ${major} ${minor}
   done
 fi
}

virtio_nods() {
 if grep -qw vda /proc/partitions ; then
   #vda devices exist, create /dev entries as needed
   grep "\ vd[a-z]" /proc/partitions | while read -r major minor blocks name ; do
     [ -e /dev/${name} ] && rm /dev/${name}
     mknod /dev/${name} b ${major} ${minor}
   done
 fi
}

mkfs_param () {
   devname="$1"
   fstype="$2"

   devsr=${devname//\"/}      #strip out "
   devid=${devsr#*=}          #strip out <UUID, LABEL, whatever>=

   if [[ $devname =~ UUID ]] ; then
      case "$fstype" in
        xfs)
         echo "-m uuid=${devid}"
         ;;
        ext*|swap)
         echo "-U ${devid}"
         ;;
        vfat)
         echo "-i ${devid/-/}"
         ;;
        *)
         echo "Unknown filesystem $fstype for device $devname" 1>&2
         echo ""
         ;;
      esac
   else
      case "$fstype" in
        xfs|ext*|swap)
         echo "-L ${devid}"
         ;;
        vfat)
         echo "-n ${devid}"
         ;;
        *)
         echo "Unknown filesystem $fstype for device $devname" 1>&2
         echo ""
         ;;
      esac
   fi
}

uuid_to_devname () {
   devname="$1"

   devsr=${devname//\"/}      #strip out "
   devid=${devsr#*=}          #strip out <UUID, LABEL, whatever>=
   declare -i devct=`grep ${devid} ${ourblkid} | wc -l`
   if [ $devct -gt 1 ] ; then
      echo "Found $devct entries converting ${devname} to a device node from blkid info:" 1>&2
      cat ${ourblkid} 1>&2
      devnm=`grep ${devid} ${ourblkid} | sort | head -n1 | sed "s/: .*//g"`
      echo "Guessing ${devnm}, the first device of:" 1>&2
      grep ${devid} ${ourblkid} | sort | sed "s/: .*//g" 1>&2
      if grep -q DGX-2 /sys/firmware/dmi/tables/DMI ; then
         if [ ! -e /tmp/dgxcleared ] ; then
            touch /tmp/dgxcleared
            echo "DGX-2 systems use a custom replicated /boot/efi and need the mirror cleared to restore properly" 1>&2
            for old in `grep ${devid} ${ourblkid} | sort | sed -e 1d -e "s/: .*//g"` ; do
               echo "Zeroing out $old with dd" 1>&2
               dd if=/dev/zero of=$old bs=1M 1>&2
            done
         fi
      fi
      echo ${devnm}
   elif [ $devct -eq 0 ] ; then
      echo "Found $devct entries converting ${devname} to a device node from blkid info:" 1>&2
      cat ${ourblkid} 1>&2
      echo "Cannot continue creating filesystems"
      return 1
   else
      devnm=`grep ${devid} ${ourblkid} | sed "s/: .*//g"`
      if [ ! -b ${devnm} ] ; then
         echo "Device name ${devnm} (converted from ${devname})" 1>&2
         echo "is not a valid block device." 1>&2
         mcmserror "Cannot continue creating filesystems"
         return 1
      fi
      echo ${devnm}
      return 0
   fi
}

get_md_uuid () {
  if [ ! -e $1 ] ; then
    mkdir -p `dirname "${1}"`
    if ! cp -a /drive/"${1}" "${1}" ; then
      echo "Could not find md device ${1} to acquire uuid"
      return 1
    fi
  fi
    
  mdadm --detail -Y "${1}" > /tmp/mdadm-uuid.sh
  . /tmp/mdadm-uuid.sh
  echo "${MD_UUID}"
  return 0
}

#takes param of fs mountpoint, feeds out uuid
get_md_uuid_from_fs () {
   fs_device=`awk '/^[^\s]*\s+\'$1'\s/ {print $1}' /tmp/stripped-fstab`
   if [ "${fs_device}" == "" ] ; then return 1 ; fi
   #echo "Getting md uuid for $1 mount fs_device ${fs_device}" 1>&2
   md_device="" 
   if [[ ${fs_device} =~ /dev/md ]] ; then
      md_device=${fs_device}
   elif [[ ${fs_device^^} =~ UUID || ${fs_device^^} =~ LABEL ]] ; then
      md_device=`uuid_to_devname ${fs_device} ${ourblkid}`
      if [ $? -ne 0 ] ; then 
        echo "uuid_to_devname error: $md_device" 1>&2
        return 1
      fi
   elif lvs ${fs_device} >&/dev/null ; then
     #echo "Determining md device for LVM mount ${fs_device}" 1>&2
     #LVM, must determine underlying md "physical" volume
     vgroup=`lvs --noheadings ${fs_device} | awk '{print $2}'`
     pdev=`pvs --noheadings | grep -w ${vgroup} | awk '{print $1}'`
     if [[ ${pdev} =~ /dev/md ]] ; then
       md_device=${pdev}
       #echo "Found md device ${md_device}" 1>&2
     elif [[ ${pdev} =~ /dev/mapper/luks ]] ; then
       md_device=`cryptsetup status ${pdev} | grep ^..device: | awk '{print $2}'`
       #echo "Found md device ${md_device}" 1>&2
     fi
   fi
   if [ "$md_device" != "" ] ; then 
      get_md_uuid ${md_device}
      return $?
   else
      return 1
   fi
}

fix_grub_md_uuids() {
  echo Updating /etc/default/grub for correct md devices to assemble at boot
      
  #strip out old rd.md.uuid params
  sed -i /drive/etc/default/grub -e "s/rd.md.uuid=[^[:space:]]*[[:space:]]//g" -e "s/rd.md.uuid[^[:space:]]*\"/\"/g"
   
  newmduuids=""
  for fs in / /usr /boot ; do
    newuuid=`get_md_uuid_from_fs $fs`
    if [ $? -ne 0 ] ; then
      echo "No md uuid required for $fs"
    fi
    [ "$newuuid" != "" ] && newmduuids="$newmduuids rd.md.uuid=$newuuid"
  done

  setting="GRUB_CMDLINE_LINUX"
  sed -i /drive/etc/default/grub -e "/^${setting}=/s/=\"/=\"${newmduuids} /"    
  echo "Updated md.uuid values in /etc/default/grub"
  ble="/drive/boot/loader/entries"
  if [ -e ${ble} ] ; then
    if grep -q rd.md.uuid= ${ble}/* ; then
      for i in ${ble}/* ; do
        sed -i ${i} -e "s/rd.md.uuid=[^[:space:]]*[[:space:]]//g" -e "s/rd.md.uuid[^[:space:]]*\"/\"/g"
        sed -i ${i} -e "/^options/s/\(root=[^[:space:]]*\) /\1 ${newmduuids} /"
      done
    fi
  fi
  kcmd="/drive/etc/kernel/cmdline"
  if [ -e ${kcmd} ] ; then
    if grep -q rd.md.uuid ${kcmd} ; then
      sed -i ${kcmd} -e "s/rd.md.uuid=[^[:space:]]*[[:space:]]//g" -e "s/rd.md.uuid[^[:space:]]*\"/\"/g"
      sed -i ${kcmd} -e "/^options/s/\(root=[^[:space:]]*\) /\1 ${newmduuids} /"
    fi
  fi
}

fix_mdadm_conf_uuids() {
  if [ -f /drive/etc/mdadm/mdadm.conf ] ; then
    mymdconf=/drive/etc/mdadm/mdadm.conf
  else
    mymdconf=/drive/etc/mdadm.conf
  fi
  echo Replacing UUIDs in mdadm.conf file
  for md_dev in `grep ^ARRAY ${mymdconf} | awk '{print $2}'` ; do
    newuuid=`get_md_uuid $md_dev`
    if [ $? -ne 0 ] ; then mcmserror "fix_mdadm_conf_uuids error: $newuuid" ; fi
    simple_md_dev=`echo $md_dev | sed "s,.*/,,g"`
    sed -i ${mymdconf} -e "/${simple_md_dev}[[:space:]]/s/UUID=[^[:space:]]*/UUID=${newuuid}/"
  done
}

rm /tmp/failed >& /dev/null
. /config

if [ "$HEAD" == "" -a "$MASTER" != "" ] ; then
  HEAD=$MASTER
fi

if [ "$HEAD" == "" ] ; then
  mcmserror "This node restore image is missing required settings in its embedded config file"
fi

# mount proc
[ -d /proc/sys ] || mount -t proc none /proc
[ -d /sys/class ] || mount -t sysfs none /sys

efimode="no"
mount -t efivarfs none /sys/firmware/efi/efivars
if [ $? -eq 0 ] ; then
  efimode="yes"
fi


# devtmpfs
grep -q devtmpfs /proc/mounts || mount -t devtmpfs none /dev

/lib/systemd/systemd-udevd --daemon


# start access shells on other ttys
bash < /dev/tty2 >& /dev/tty2 &
bash < /dev/tty3 >& /dev/tty3 &
bash < /dev/tty4 >& /dev/tty4 &
bash < /dev/tty5 >& /dev/tty5 &
bash < /dev/tty6 >& /dev/tty6 &

if grep -q rescue /proc/cmdline ; then
  mcmserror "Exiting to rescue mode"
fi

# bring up lo
ifconfig lo 127.0.0.1
hostname localhost

#install usb drivers for non legacy usb keyboard support
/sbin/modprobe -q ohci-hcd
/sbin/modprobe -q uhci-hcd
/sbin/modprobe -q usbhid

#install network module
if [ -z "$ETHMODS" ] ; then    #old style config with single ETHMOD, create ETHMODS from it
  ETHMODS=$ETHMOD
fi

for ETHMOD in $ETHMODS ; do
  if [ $ETHMOD == "forcedeth" ] ; then
    /sbin/modprobe $ETHMOD optimization_mode=1 max_interrupt_work=20
  else
    /sbin/modprobe -q $ETHMOD
  fi
done

if [ -d /sys/class/net/eth0 ] ; then
  echo Found eth0, continuing install.
else
  mcmserror "None of the ethernet drivers created eth0."
fi

if [ -z "$STORAGEMODS" ] ; then
  STORAGEMODS="sata_nv ahci ata_piix sata_svw sata_sil24 sata_sil sata_promise arcmsr 3w-9xxx mpt2sas mpt3sas mptsas 3w-sas isci pata_atiixp megaraid_sas virtio_blk nvme smartpqi"
fi

for storagemod in $STORAGEMODS
 do
   /sbin/modprobe -q $storagemod
done

#NETDEV is typically undefined, so dhclient will try eth0 only with this
[ "x$NETDEVS" == "x" ] && NETDEVS="eth0"

if [[ $NETDEVS =~ bond0 ]] ; then
  if [ "x$BONDMODE" == "x" ] ; then BONDMODE=4 ; fi
  if [ "x$BONDDEVS" == "x" ] ; then BONDDEVS="eth0 eth1" ; fi
  modprobe bonding mode=$BONDMODE
  ifconfig bond0 up
  for BONDDEV in $BONDDEVS ; do
    ifconfig $BONDDEV up
    ifenslave bond0 $BONDDEV
  done 
fi

#poll head for ip for each NETDEV
for NETDEV in $NETDEVS ; do
  if [ ! -e /var/lib/dhclient/dhclient.$NETDEV.lease ] ; then
    if /sbin/dhclient -lf /var/lib/dhclient/dhclient.$NETDEV.lease $NETDEV ; then
      echo Brought up network devices.
    else
      echo "Failed to acquire DHCP lease on ${NETDEV}.  Sleeping 10s and trying again."
      sleep 10
      /sbin/dhclient -lf /var/lib/dhclient/dhclient.$NETDEV.lease $NETDEV || mcmserror "Unable to pull ip."
    fi
  else
    echo DHCP client already active.
  fi
done

#Initialize nfs mount
if ! pgrep rpcbind >/dev/null ; then
  /sbin/rpcbind || mcmserror "rpcbind failed"
  #sleep for 2 seconds to let rpcbind fully start
  /bin/sleep 2
fi


if [ -d /mnt/backups ] ; then
  echo NFS mounted already
else
  declare -i attemptsleft
  attemptsleft=5

  if [ "x$NODERESTORE" == "x" ] ; then
    NODERESTORE=/home/noderestore
  fi

  while [ $attemptsleft -gt 0 ]
   do
    mount -t nfs4  $HEAD:$NODERESTORE /mnt
    mountreturn=$?
    if [ $mountreturn -ne 0 ] ; then
    
      sleeptime=`expr $RANDOM % 10 + 5`
      attemptsleft=$attemptsleft-1
      echo "NFS mount failed with code $mountreturn.  $attemptsleft attempts left remaining. Sleeping $sleeptime before trying again."
      sleep $sleeptime
    else
      attemptsleft=0
    fi
   done   
  [ -d /mnt/backups ] || mcmserror "Nfs mount $HEAD:/noderestore failed."
fi

declare -a device_save
declare -i count
count=0

cd /mnt/backups/
ourslot=`readlink current`
cd -
ourfstab=/mnt/backups/${ourslot}/fstab
egrep -v "^\ *#|^\ *$" ${ourfstab} > /tmp/stripped-fstab
ourblkid=/mnt/backups/${ourslot}/blkid
ourlvs=/mnt/backups/${ourslot}/lvs
ourpvs=/mnt/backups/${ourslot}/pvs
ourpagesize=/mnt/backups/${ourslot}/pagesize

defarch=`cat /mnt/backups/${ourslot}/arch`
qarch=`uname -m`
if [ "$qarch" != "$defarch" ] ; then
  if [ "$defarch" == "" -a "$qarch" == "x86_64" ] ; then
    echo "No arch is defined for this backup image, but this is an x86_64 system."
    echo "Assuming the backup is correct but from a previous MCMS version.  Continuing."
  else
    mcmserror "You are attempting to restore an image for arch $defarch on arch $qarch"
  fi
fi

fstabefi="no"
if grep -q boot.efi ${ourfstab} ; then
  fstabefi="yes"
fi

if [ $fstabefi == "no" -a $efimode == "yes" ] ; then
  echo "****************************************************************************"
  echo "***                       Fatal error                                    ***"
  echo "*** You are attempting to restore a legacy mode backup onto a system     ***"
  echo "*** that is booted in EFI mode.  Either change to a slot with an EFI     ***"
  echo "*** backup or change the BIOS settings on this system to legacy mode     ***"
  echo "****************************************************************************"
  mcmserror "Boot mode mismatch."
fi

if [ $fstabefi == "yes" -a $efimode == "no" ] ; then
  echo "****************************************************************************"
  echo "***                       Fatal error                                    ***"
  echo "*** You are attempting to restore an EFI mode backup onto a system that  ***"
  echo "*** is booted in legacy mode.  Either change to a slot with a legacy     ***"
  echo "*** backup or change the BIOS settings on this system to EFI mode        ***"
  echo "****************************************************************************"
  mcmserror "Boot mode mismatch."
fi

stop_md_devices

/mnt/backups/${ourslot}/restorepartitions.sh

stop_md_devices

sleep 3

if [ "${DEVICES^^}" == "AUTO" ] ; then
   for DEVICE in `grep mklabel /mnt/backups/${ourslot}/restorepartitions.sh | awk '{print $3}'`
     do
      device_save[$count]=$DEVICE
      count=$count+1
   done
else
   for DEVICE in $DEVICES
     do
      device_save[$count]=$DEVICE
      count=$count+1
   done
fi

#new software raid handling
if [ -f /mnt/backups/${ourslot}/mdadm.conf ] ; then
  grep /dev/md /mnt/backups/${ourslot}/mdadm.conf | while read array mddevice restline
   do
    level=`echo $restline |      sed "s/\(.*level=\)\([^\ ]*\)\(.*\)/\2/"`
    devices=`echo $restline |    sed "s/\(.*[^-]devices=\)\([^\ ]*\)\(.*\)/\2/" | sed "s/,/\ /g"`
    if [[ $restline =~ metadata= ]] ; then
       metadata=`echo $restline | sed "s/\(.*metadata=\)\([^\ ]*\)\(.*\)/\2/"`
    else
       metadata=""
    fi
    numdevices=`echo $restline | sed "s/\(.*num-devices=\)\([^\ ]*\)\(.*\)/\2/"`
    if [[ $restline =~ spares= ]] ; then
       mdspares="-x `echo $restline | sed "s/\(.*spares=\)\([^\ ]*\)\(.*\)/\2/"`"
    else
       mdspares=""
    fi
    if [[ $restline =~ name= ]] ; then
       mdname="--name=`echo $restline | sed "s/\(.*name=\)\([^\ ]*\)\(.*\)/\2/"`"
    else
       mdname=""
    fi
    if [ "$metadata" != "" ] ; then metaoption="--metadata=$metadata" ; else metaoption="" ; fi
    for mdev in $devices ; do
      grep -q ${mddevice##/dev/} /proc/mdstat && mdadm --stop ${mddevice}
      echo "Wiping any previous fs/md signatures on $mdev"
      /sbin/wipefs -a -q $mdev
    done
    sleep 2
    echo mdadm -C -n$numdevices $mdspares --level=$level $mdname $metaoption $mddevice $devices
    mdadm -C -n$numdevices $mdspares --level=$level $mdname $metaoption $mddevice $devices >/dev/null
    sleep 2
   done
fi

# LUKs handling
if grep -q TYPE=.crypto_LUKS ${ourblkid} ; then
  echo "LUKS encryption enabled."
  echo
  pass1="foo"
  pass2="bar"
  while [ "${pass1}" != "${pass2}" ] ; do
    if [ "$pass1" != "foo" ] ; then
      echo "Passwords did not match!  Try again!"
      echo
    fi
    echo "Please provide password for encryption:"
    read -s pass1
    echo "Enter password again to verify:"
    read -s pass2
  done
  
  echo Passwords match.  Proceeding to make encrypted volumes.
  grep TYPE=.crypto_LUKS ${ourblkid} | while read cryptline ; do
    devn=${cryptline/:*/}
    uuidtmp1=`echo ${cryptline} | sed -e "s/PARTUUID[^ ]*//"`
    uuidtmp2=${uuidtmp1/*UUID=\"/}
    uuid=${uuidtmp2/\"*/}
    echo LUKS Formatting: ${devn}
    if (echo "$pass1" | cryptsetup -v --force-password --uuid=${uuid} --type luks2 luksFormat ${devn}) ; then
      echo LUKS Opening:    ${devn} luks-${uuid}
      echo "$pass1" | cryptsetup -v luksOpen ${devn} luks-${uuid}
      if [ $? -ne 0 ] ; then
        echo "Failed to luksOpen device ${devn}."
        failed
        break
      fi  
    else
      echo "Failed to luksFormat device ${devn} with uuid ${uuid}."
      failed
      break
    fi
  done
  if is_failed ; then
    mcmserror "Failed to create or open LUKS volume(s)"
  else
    echo "LUKS volumes created"
    echo
  fi  
fi

# Create LVM
if grep -q TYPE=.LVM2_member ${ourblkid} ; then
  #deactivate any potentially auto-activated pre-existing VGs, etc
  lvm vgchange -a n
  grep TYPE=.LVM2_member ${ourblkid} | while read lvmline ; do
    devn=${lvmline/:*/}
    uuidtmp1=`echo ${lvmline} | sed -e "s/PARTUUID[^ ]*//"`
    uuidtmp2=${uuidtmp1/*UUID=\"/}
    uuid=${uuidtmp2/\"*/}
    lvm pvcreate --norestorefile -ff -y --uuid=${uuid} ${devn}
    if [ $? -ne 0 ] ; then
      failed
      break
    fi
  done
  if is_failed ; then mcmserror "Failed to create LVM PVs" ; fi
  vgsmade=""
  cat ${ourpvs} | sed 1d | while read devn vgname junk ; do
    made="no"
    for vgsm in $vgsmade ; do
      if [ "$vgname" == "$vgsm" ] ; then
        made=yes
      fi
    done

    if [ $made == "yes" ] ; then 
      lvm vgextend $vgname $devn
      if [ $? -ne 0 ] ; then
        failed
        break
      fi
    else
      if lvm vgs $vgname > /dev/null ; then 
        #if we didn't make the vg, and we're supposed to, but it already exists, delete it
        lvm vgremove $vgname -f
      fi
      lvm vgcreate $vgname $devn
      vgsmade="$vgsmade $vgname"
      if [ $? -ne 0 ] ; then
        failed
        break
      fi
    fi
  done
  if is_failed ; then mcmserror "Failed to create LVM VGs" ; fi

  cat ${ourlvs} | sed 1d | while read lvname vgname attr size junk ; do
    if [[ $attr =~ ^t ]] ; then thin="-T" ; else thin="" ; fi
    lvm lvcreate -W y -y -n $lvname -L $size $thin $vgname
    if [ $? -ne 0 ] ; then
      failed
      echo Failed: lvm lvcreate -n $lvname -L $size $thin $vgname
      break
    fi
  done
  if is_failed ; then mcmserror "Failed to create LVM LVs" ; fi
fi

# Create filesystems

fslines=`egrep -v "^\s*#" ${ourfstab}`

while read devname mntpt fstype junk
  do
   if is_failed ; then continue ; fi
   mkfsparam=""
   case $fstype in
     ""|nfs|nfs4|bind|tmpfs|devpts|sysfs|panfs|cifs|proc|configfs|nfsd|lustre)
        continue
        ;;
     vfat|ext2|ext3|ext4|xfs|reiserfs|swap)
        ;;
     *)
       case ${mntpt} in
         /|/boot|/usr|/boot/efi|/var|/tmp|/home)
            failed "Unknown fs type ${fstype} for critical filesystem ${mntpt}."
            break
            ;;
         *)
            echo "******************************************************"
            echo "Skipping unknown fstype $fstype for $devname $mntpt"
            echo "******************************************************"
            continue
            ;;
       esac
       ;;
   esac
   #fi

   if [[ ${devname^^} =~ UUID || ${devname^^} =~ LABEL ]] ; then
      mkfsparam=`mkfs_param ${devname} ${fstype}`
      uuid=${devname//\"/}      #strip out "
      uuid=${uuid#*=}          #strip out <UUID, LABEL, whatever>=
      devnm=`uuid_to_devname ${devname} ${ourblkid}`
      if [ $? -ne 0 ] ; then
        failed "$devnm"
        continue
      fi
      echo "Using ${devnm} for ${devname}"
   else
      devnm=${devname}
      if [ ! -b ${devnm} ] ; then
         failed "Device ${devnm} for ${mntpt} does not exist"
         break
      fi
   fi

   case ${fstype} in
     reiserfs)
       fscom="echo y | /sbin/mkreiserfs ${mkfsparam} ${devnm}"
       ;;
     ext4)
       fscom="/sbin/mkfs.ext4 -F -F ${mkfsparam} ${devnm}"
       ;;
     ext2|ext3)
       fscom="/sbin/mke2fs -j ${mkfsparam} ${devnm}"
       ;;
     xfs)
       fscom="/sbin/mkfs.xfs -f ${mkfsparam} ${devnm}"
       ;;
     vfat)
       fscom="/usr/sbin/mkfs.fat -F 16 ${mkfsparam} ${devnm}"
       ;;
     swap)
       psparam=""
       if [ -e $ourpagesize ] ; then
          psparam="-p `cat $ourpagesize`"
       fi
       fscom="/sbin/mkswap -f ${mkfsparam} ${psparam} ${devnm}"
       ;;
   esac

   #actually create the filesystem
   fsout=`eval $fscom 2>&1`
   if [ $? -ne 0 ] ; then
      echo "A warning or error was encountered when creating ${mntpt} with the command"
      echo "${fscom}"
      echo "$fsout"
      echo
      if [ $? -ne 0 ] ; then
        failed "Unable to continue"
        continue
      fi
   else
      echo "Created ${fstype} on ${devnm}"
   fi
done <<< "$fslines"

if is_failed ; then
  mcmserror `failed_reason`
fi

#mount root
ROOT=`grep -v "^\s*#" ${ourfstab} | grep "[[:space:]]/[[:space:]]" | awk '{print $1}'`
if [[ ${ROOT^^} =~ UUID || ${ROOT^^} =~ LABEL ]] ; then
   ROOT=`uuid_to_devname ${ROOT} ${ourblkid}`
   if [ $? -ne 0 ] ; then mcmserror "$ROOT" ; fi
fi
mkdir -p /drive
mount $ROOT /drive
if [ $? -ne 0 ] ; then
   mcmserror "Unable to mount $ROOT at /drive"
else
   echo "Mounted $ROOT at /drive"
fi

#mount remaining linux partitions
grep -v "^\s*#" ${ourfstab} | egrep 'ext.|reiserfs|xfs' | grep -vw / | \
while read i
 do
   DEV=`echo $i | awk '{print $1}'`
   if [[ ${DEV^^} =~ UUID || ${DEV^^} =~ LABEL ]] ; then
      DEV=`uuid_to_devname ${DEV} ${ourblkid}`
      if [ $? -ne 0 ] ; then mcmserror "uuid_to_devname error: $DEV" ; fi
   fi
   MNTPT=/drive`echo $i | awk '{print $2}'`
   if [ ! -d $MNTPT ] ; then mkdir -p $MNTPT ; fi
   #making sure boot is mounted with notail if it is reiserfs
   echo $MNTPT | grep -q boot
   if [ $? -eq 0 ] ; then
      grep boot ${ourfstab} | grep -q reiserfs
      [ $? -eq 0 ] && mount -o notail $DEV $MNTPT || mount $DEV $MNTPT
   else
      mount $DEV $MNTPT
   fi

   if [ $? -ne 0 ] ; then
      mcmserror "Unable to mount $DEV at $MNTPT  i=$i"
   else
      echo "Mounted $DEV at $MNTPT"
   fi
done

#mount vfat partitions (includes /boot/efi)
grep -v "^\s*#" ${ourfstab} | grep vfat | grep -vw / | \
while read i
 do
   DEV=`echo $i | awk '{print $1}'`
   if [[ ${DEV^^} =~ UUID || ${DEV^^} =~ LABEL ]] ; then
      DEV=`uuid_to_devname ${DEV} ${ourblkid}`
      if [ $? -ne 0 ] ; then mcmserror "uuid_to_devname error: $DEV" ; fi
   fi
   MNTPT=/drive`echo $i | awk '{print $2}'`
   if [ ! -d $MNTPT ] ; then mkdir -p $MNTPT ; fi
   mount $DEV $MNTPT

   if [ $? -ne 0 ] ; then
      mcmserror "Unable to mount $DEV at $MNTPT  i=$i"
   else
      echo "Mounted $DEV at $MNTPT"
   fi
done

#actually restore drive
echo "Restoring drive...."
[ -f /mnt/backups/${ourslot}/backup.tgz ] || mcmserror "File /mnt/backups/${ourslot}/backup.tbz is not available."
tar --extract --sparse --preserve-permissions --xattrs --xattrs-include='*' --selinux \
    --file=/mnt/backups/${ourslot}/backup.tgz -C /drive 2>&1 | grep -v time\ stamp.*future &
PID=$!

#create /proc
mkdir /drive/proc
mkdir /drive/sys

#while waiting for tar to complete, lets print out a spinner
declare -a PROGCHAR
PROGCHAR[1]="|"
PROGCHAR[2]="/"
PROGCHAR[3]="-"
PROGCHAR[4]="\\" #"
declare -i COUNT=1

while [ -d /proc/$PID ]
 do
   echo -ne "\t\t\t\t\t\t\r${PROGCHAR[$COUNT]}"
   COUNT=$COUNT+1
   if [ $COUNT -ge 5 ] ; then COUNT=1 ; fi
   sleep 1s
 done
echo

#creating /drive/mcms since it is excluded from the backup process
mkdir /drive/mcms

#completed restoring files
echo File Restoration complete.

echo Ensuring /media/floppy and /media/cdrom have been created
mkdir -p /drive/media/floppy /drive/media/cdrom /drive/media/dvd

echo Ensuring correct permissions on tmp
chmod 1777 /drive/tmp

if [ -f /drive/etc/noderestore.exclude ] ; then
  echo Recreating manually and automatically excluded directories.
  while read dirname
   do
    if [ "x$dirname" != "x" -a "x$dirname" != "xvar/run" -a "x$dirname" != "x/var/run" ] ; then
      mkdir -p /drive/$dirname
    fi
   done < /drive/etc/noderestore.exclude
  rm -f /drive/etc/noderestore.exclude
fi

#recreate symlink from /var/run to /run
ln -s ../run /drive/var/run

mount -t proc none /drive/proc
mount -t sysfs none /drive/sys
if [ $efimode == "yes" ] ; then
  mount -t efivarfs none /drive/sys/firmware/efi/efivars
fi

if [ -e /drive/etc/selinux/config ] ; then
  if grep -q ^SELINUX=disable /drive/etc/selinux/config ; then
    echo SELinux is disabled, skipping relabel.
  else
    echo SELinux is enabled.  Marking node for auto relabeling on first boot
    touch /drive/.autorelabel
  fi
fi

#the dev nodes should exist, but this handles cases where a node image
#is being modified for a different layout
cp -a /dev/md[0-9]* /drive/dev/ >& /dev/null

if [ -f /mnt/backups/${ourslot}/mdadm.conf ] ; then
  if [ -f /drive/etc/redhat-release ] ; then
    if grep -qi release.[789] /drive/etc/redhat-release ; then 
      echo "Fixing grub md_uuid values for Redhat/CentOS 7+"
      fix_grub_md_uuids
      fix_mdadm_conf_uuids
    elif grep -qi release.6 /drive/etc/redhat-release ; then
      fix_mdadm_conf_uuids
    fi
  else
    fix_mdadm_conf_uuids
  fi
fi


#provide full dev to image
mount -o bind /dev /drive/dev


#fix resume if used
swapexport=`blkid -t TYPE=swap -o export | grep UUID`
eval $swapexport
if grep -q ^GRUB_CMDLINE.*=.*resume=UUID /drive/etc/default/grub ; then
  if [ "$swapexport" != "" ] ; then
    sed -i /drive/etc/default/grub -e "/^GRUB_CMDLINE.*=.*resume=UUID/s/resume=UUID=[^ \"]*/resume=UUID=${UUID}/g"
  else
    sed -i /drive/etc/default/grub -e "/^GRUB_CMDLINE.*=.*resume=UUID/s/resume=UUID=[^ \"]*//g"
  fi
fi

ble="/drive/boot/loader/entries"
if [ -e ${ble} ] ; then
  if grep -q resume=UUID ${ble}/* ; then
    for i in ${ble}/* ; do
      if [ "$swapexport" != "" ] ; then
        sed -i ${i} -e "/resume=UUID/s/resume=UUID=[^ \"]*/resume=UUID=${UUID}/g"
      else
        sed -i ${i} -e "/resume=UUID/s/resume=UUID=[^ \"]*//g"
      fi
    done
  fi
fi

kcmd="/drive/etc/kernel/cmdline"
if [ -e ${kcmd} ] ; then
  if grep -q resume=UUID ${kcmd} ; then
    if [ "$swapexport" != "" ] ; then
      sed -i ${kcmd} -e "/resume=UUID/s/resume=UUID=[^ \"]*/resume=UUID=${UUID}/g"
    else
      sed -i ${kcmd} -e "/resume=UUID/s/resume=UUID=[^ \"]*//g"
    fi
  fi
fi

echo "Installing boot loader (grub/grub2)."
if [ -f /drive/usr/sbin/update-grub2 -o -f /drive/sbin/grub2-mkconfig -o -f /drive/sbin/update-grub2 -o -f /drive/usr/sbin/grub2-mkconfig ] ; then #grub2
  if [ -f /drive/usr/sbin/update-grub2 -o -f /drive/sbin/update-grub2 ] ; then  #some distros provide update-grub2, others need grub2-mkconfig manually
    #This is the path used by Ubuntu and Debian - they have update-grub
    if [ $qarch == "x86_64" ] ; then
      [ -f /drive/usr/sbin/update-grub2 ] && chroot /drive /usr/sbin/update-grub2 || chroot /drive /sbin/update-grub2
      if [ -f /drive/usr/sbin/grub2-install ] ; then
        chroot /drive /usr/sbin/grub2-install ${device_save[0]} >& /dev/null
      elif [ -f /drive/usr/sbin/grub-install ] ; then
        chroot /drive /usr/sbin/grub-install ${device_save[0]} >& /dev/null
      elif [ -f /drive/sbin/grub2-install ] ; then
        chroot /drive /sbin/grub2-install ${device_save[0]} >& /dev/null
      elif [ -f /drive/sbin/grub-install ] ; then
        chroot /drive /sbin/grub-install ${device_save[0]} >& /dev/null
      else
        /bin/false
      fi
      if [ $? -ne 0 ] ; then
         if [ $efimode == "yes" ] ; then
            loader=`find /drive/boot/efi | grep grub.*efi$ | sed -e "s/.*EFI/EFI/" -e "s,/,\\\\\\,g"`
            if [ "$loader" == "" ] ; then
               mcmserror "Cannot determine EFI loader"
            fi
            devname=`echo ${device_save[0]} | sed s,.*/,,`
            for old in `chroot /drive /sbin/efibootmgr 2>&1 | grep "^Boot.*Microway_$devname" | awk '{print $1}'` ; do
               oldnum=${old##Boot}
               oldnum=${oldnum/\*/}
               echo "Removing old EFI boot entry for Microway_$devname with id ${oldnum}"
               chroot /drive /sbin/efibootmgr -b ${oldnum} -B
            done
            chroot /drive /sbin/efibootmgr -c -d ${device_save[0]} -p 1 -L Microway_$devname -l $loader
            echo "Added EFI entry Microway_$devname for ${device_save[0]} using loader $loader"
         else
            mcmserror "Unable to install grub2 on ${device_save[0]}"
         fi
      else
         echo "Grub2 installed on ${device_save[0]}"
      fi
    else
      [ -f /drive/usr/sbin/update-grub2 ] && chroot /drive /usr/sbin/update-grub2 || chroot /drive /sbin/update-grub2
      if [ $? -ne 0 ] ; then
        mcmserror "Unable to configure grub2 grub.cfg"
      else
        echo "Grub2 grub.cfg updated"
      fi
    fi
  else
    #This is the path used by Redhat and Rocky
    if [ $qarch == "x86_64" ] ; then
      if [ $efimode == "yes" ] ; then
        if [ -f /drive/boot/efi/EFI/redhat/grub.cfg ] ; then
          gfile=/drive/boot/efi/EFI/redhat/grub.cfg
        elif [ -f /drive/boot/efi/EFI/centos/grub.cfg ] ; then
          gfile=/drive/boot/efi/EFI/centos/grub.cfg
        elif [ -f /drive/boot/efi/EFI/rocky/grub.cfg ] ; then
          gfile=/drive/boot/efi/EFI/rocky/grub.cfg
        elif [ -f /drive/boot/efi/EFI/ubuntu/grub.cfg ] ; then
          gfile=/drive/boot/efi/EFI/ubuntu/grub.cfg
        fi
        #test if the EFI grub.cfg is a chainloader config to grub.cfg elsewhere
        if grep -q ^configfile.*cfg$ ${gfile} ; then
          #if using a grub.cfg file in the efi dir that opens grub.cfg in the real /boot-
          if grep -q prefix.*grub2 ${gfile} ; then
            gpfx=grub2
          else
            gpfx=grub
          fi
          if [ -f /drive/usr/sbin/grub-probe ] ; then
            gprobe=/usr/sbin/grub-probe
          elif [ -f /drive/usr/sbin/grub2-probe ] ; then
            gprobe=/usr/sbin/grub2-probe
          elif [ -f /drive/sbin/grub-probe ] ; then
            gprobe=/sbin/grub-probe
          elif [ -f /drive/sbin/grub2-probe ] ; then
            gprobe=/sbin/grub2-probe
          else
            mcmserror "Could not find grub2-probe or grub-probe"
          fi

          bootuuid=`chroot /drive $gprobe --target=fs_uuid /boot`
          if grep -qw "/boot" ${ourfstab} ; then
            bootpref=""
          else
            bootpref="/boot"
          fi
          echo "search --no-floppy --fs-uuid --set=dev ${bootuuid}" > ${gfile}
          echo 'set prefix=($dev)/'${gpfx} >> ${gfile}
          echo 'export $prefix' >> ${gfile}
          echo 'configfile $prefix'"${bootpref}/grub.cfg" >> ${gfile}
          #handle copying this to a redundant ESP if in use
          if mount | grep -q boot/efi2 ; then
            cp -a /drive/boot/efi/EFI /drive/boot/efi2/
          fi

          #set gfile to the actual file for grub mkconfig command below
          gfile=/drive/boot/${gpfx}/grub.cfg
        fi
      else
          gfile=/drive/boot/grub2/grub.cfg
      fi
      #handle boot= for FIPS
      if grep -q " boot=" /drive/etc/default/grub ; then
        boot=`mount | grep "/drive/boot " | awk '{print $1}'`
        uuid=`blkid $boot | sed -e "s/.* UUID=\"*//" -e "s/\".*$//"`
        echo Replacing boot uuid with $uuid
        sed -i /drive/etc/default/grub -e "s/ boot=[^ ]*/ boot=UUID=${uuid}/"
      fi

      if [ -f /drive/usr/sbin/grub2-mkconfig ] ; then
        chroot /drive /usr/sbin/grub2-mkconfig > $gfile
      else
        chroot /drive /sbin/grub2-mkconfig > $gfile
      fi
      if [ $efimode == "no" ] ; then
        if [ -f /drive/usr/sbin/grub2-install ] ; then
          chroot /drive /usr/sbin/grub2-install ${device_save[0]} >& /dev/null
        elif [ -f /drive/usr/sbin/grub-install ] ; then
          chroot /drive /usr/sbin/grub-install ${device_save[0]} >& /dev/null
        elif [ -f /drive/sbin/grub2-install ] ; then
          chroot /drive /sbin/grub2-install ${device_save[0]} >& /dev/null
        elif [ -f /drive/sbin/grub-install ] ; then
          chroot /drive /sbin/grub-install ${device_save[0]} >& /dev/null
        else
          mcmserror "Could not find grub install binary"
        fi
        if [ $? -eq 0 ] ; then
          echo "Grub2 installed on ${device_save[0]}"
        else
          mcmserror "Unable to install grub2 on ${device_save[0]}"
        fi
      else  #manual EFI entry creation
        loader=`find /drive/boot/efi | grep -i shimx64.efi$ | sed -e "s/.*EFI/EFI/" -e "s,/,\\\\\\,g"`
        if [ "$loader" == "" ] ; then
          mcmserror "Cannot determine EFI loader"
        fi
        devname=`echo ${device_save[0]} | sed s,.*/,,`
        for old in `chroot /drive /sbin/efibootmgr 2>&1 | grep "^Boot.*Microway_$devname" | awk '{print $1}'` ; do
          oldnum=${old##Boot}
          oldnum=${oldnum/\*/}
          echo "Removing old EFI boot entry for Microway_$devname with id ${oldnum}"
          chroot /drive /sbin/efibootmgr -b ${oldnum} -B
        done
        chroot /drive /sbin/efibootmgr -c -d ${device_save[0]} -p 1 -L Microway_$devname -l $loader
        if [ $? -eq 0 ] ; then
          echo "Added EFI entry Microway_$devname for ${device_save[0]} using loader $loader"
        else
          mcmserror "Unable to add EFI entry Microway_$devname for ${device_save[0]} using loader $loader"
        fi
      fi
    else
      [ -f /drive/usr/sbin/grub2-mkconfig ] && chroot /drive /usr/sbin/grub2-mkconfig > /drive/boot/grub2/grub.cfg || chroot /drive /sbin/grub2-mkconfig > /drive/boot/grub2/grub.cfg
      if [ $? -ne 0 ] ; then
        mcmserror "Unable to configure grub2 grub.cfg"
      else
        echo "Grub2 grub.cfg updated"
      fi
    fi
  fi
else  #grub1
  [ -f /drive/usr/sbin/grub ] && GRUB=/usr/sbin/grub
  [ -f /drive/sbin/grub ] && GRUB=/sbin/grub

  #make drive bootable with grub1
  chroot /drive $GRUB --batch --no-floppy << EOF &>/dev/null
device (hd0) ${device_save[0]}
root (hd0,0)
setup (hd0)
EOF
  if [ $? -ne 0 ] ; then
    mcmserror "Unable to run grub on ${device_save[0]}"
  else
    echo "Grub installed on ${device_save[0]}"
  fi
fi

if grep -qi ubuntu /drive/etc/issue ; then
  echo Rebuilding initrd images
  chroot /drive /usr/sbin/update-initramfs -u -k all
elif [ -f /drive/etc/SuSE-release ] ; then
  echo Rebuilding initrd images
  chroot /drive /sbin/mkinitrd
elif [ -f /drive/etc/redhat-release ] ; then
  if grep -qi release.[789] /drive/etc/redhat-release ; then
    echo Rebuilding initrd images

    if [ -e /drive/dev/kmsg ] ; then
      mv /drive/dev/kmsg /drive/dev/kmsg.orig
      ln -s null /drive/dev/kmsg
    fi
    waitlist=""
    for i in /drive/boot/initramfs*.img ; do
      if [[ $i =~ dump || $i =~ rescue ]] ; then continue ; fi
      initfile=`basename $i`
      version=`echo $initfile | sed -e "s/^initramfs-//" -e "s/\.img$//"`
      echo Recreating $initfile
      grep -q nvme /proc/partitions && nvmod="nvme" || nvmod=""

      grep -q TYPE=.LVM2_member ${ourblkid}
      haslvm=$?

      grep -qi release.9 /drive/etc/redhat-release
      if [ $? -eq 0 -a $haslvm -eq 0 ] ; then
        sed -i /drive/etc/lvm/devices/system.devices -e /PVID=/d      
        for vgname in `vgs | grep -v VSize | while read vgname junk ; do echo ${vgname} ; done` ; do
          chroot /drive vgimportdevices ${vgname}
        done
      fi
      if [ -e /drive/etc/mdadm.conf -a -s /drive/etc/crypttab -a ${haslvm} -eq 0 ] ; then
         echo Rebuilding initrd images with features: lvm mdraid crypt
         chroot /drive /bin/dracut -f -a " lvm mdraid crypt " --add-drivers " dm-crypt raid0 raid1 raid10 raid5 raid6 ahci megaraid_sas sd_mod ${nvmod}" \
                                   -L1 /boot/$initfile $version >& /dev/null &
         waitlist="$! $waitlist"
      elif [ -e /drive/etc/mdadm.conf -a ${haslvm} -eq 0 ] ; then
         echo Rebuilding initrd images with features: lvm mdraid
         chroot /drive /bin/dracut -f -a " lvm mdraid " --add-drivers " raid0 raid1 raid10 raid5 raid6 ahci megaraid_sas sd_mod ${nvmod}" \
                                   -L1 /boot/$initfile $version >& /dev/null &
         waitlist="$! $waitlist"
      elif [ -s /drive/etc/crypttab -a ${haslvm} -eq 0 ] ; then
         echo Rebuilding initrd images with features: lvm crypt
         chroot /drive /bin/dracut -f -a " lvm crypt " --add-drivers " dm-crypt ahci megaraid_sas sd_mod ${nvmod}" \
                                   -L1 /boot/$initfile $version >& /dev/null &
         waitlist="$! $waitlist"
      elif [ -e /drive/etc/mdadm.conf -a -s /drive/etc/crypttab ] ; then
         echo Rebuilding initrd images with features: mdraid crypt
         chroot /drive /bin/dracut -f -a "mdraid crypt" --add-drivers " dm-crypt raid0 raid1 raid10 raid5 raid6 ahci megaraid_sas sd_mod ${nvmod}" \
                                   -L1 /boot/$initfile $version >& /dev/null &
         waitlist="$! $waitlist"
      elif [ -e /drive/etc/mdadm.conf ] ; then
         echo Rebuilding initrd images with features: mdraid
         chroot /drive /bin/dracut -f -a mdraid --add-drivers " raid0 raid1 raid10 raid5 raid6 ahci megaraid_sas sd_mod ${nvmod}" -L1 /boot/$initfile $version >& /dev/null &
         waitlist="$! $waitlist"
      elif [ -s /drive/etc/crypttab ] ; then
         echo Rebuilding initrd images with features: crypt
         chroot /drive /bin/dracut -f -a crypt --add-drivers " dm-crypt ahci megaraid_sas sd_mod ${nvmod}" -L1 /boot/$initfile $version >& /dev/null &
         waitlist="$! $waitlist"
      elif [ ${haslvm} -eq 0 ] ; then
         echo Rebuilding initrd images with features: lvm
         chroot /drive /bin/dracut -f -a lvm --add-drivers " ahci megaraid_sas sd_mod ${nvmod}" -L1 /boot/$initfile $version >& /dev/null &
         waitlist="$! $waitlist"
      else
         echo Rebuilding initrd images 
         chroot /drive /bin/dracut -f --add-drivers " ahci megaraid_sas sd_mod ${nvmod}" -L1 /boot/$initfile $version >& /dev/null &
         waitlist="$! $waitlist"
      fi
    done
    echo Waiting for mkinitrd processes to finish
    wait $waitlist
    [ -e /drive/dev/kmsg.orig ] && mv /drive/dev/kmsg.orig /drive/dev/kmsg
  elif grep -qi release.6 /drive/etc/redhat-release ; then
    echo Rebuilding initrd images

    if [ -e /drive/dev/kmsg ] ; then
      mv /drive/dev/kmsg /drive/dev/kmsg.orig
      ln -s null /drive/dev/kmsg
    fi
    waitlist=""
    for i in /drive/boot/initramfs*.img ; do
      if [[ $i =~ dump || $i =~ rescue ]] ; then continue ; fi
      initfile=`basename $i`
      version=`echo $initfile | sed -e "s/^initramfs-//" -e "s/\.img$//"`
      echo Recreating $initfile
      chroot /drive /sbin/dracut -f /boot/$initfile $version >& /dev/null &
      waitlist="$! $waitlist"
    done
    echo Waiting for mkinitrd processes to finish
    wait $waitlist
    [ -e /drive/dev/kmsg.orig ] && mv /drive/dev/kmsg.orig /drive/dev/kmsg

  fi
fi

#Prompt user for node #
[ -f /drive/usr/local/sbin/change_ip.sh ] && CHANGEIP=/usr/local/sbin/change_ip.sh
[ -f /drive/usr/local/bin/change_ip.sh ] && CHANGEIP=/usr/local/bin/change_ip.sh
[ -f /drive/usr/bin/change_ip.sh ] && CHANGEIP=/usr/bin/change_ip.sh
[ -f /drive/usr/sbin/change_ip.sh ] && CHANGEIP=/usr/sbin/change_ip.sh
echo "Please enter the last octet of the IP address to set the node name/number"
echo "For example, typically for \"node3\", enter just \"3\". Refer to /etc/hosts"
echo "on your head node for name/ip pairings if using non-standard naming."
read i
while test "$i" == "" || echo $i | egrep -qi "[[:alpha:]]|[[:cntrl:]]|[[:punct:]]|[[:space:]]" ; do
  echo "Invalid input. Please try again:"
  read i
done
until chroot /drive $CHANGEIP $i ; do
  echo Setting the IP failed. Please try again:
  read i
  while test "$i" == "" || echo $i | egrep -qi "[[:alpha:]]|[[:cntrl:]]|[[:punct:]]|[[:space:]]" ; do
    echo "Invalid input. Please try again:"
    read i
  done
done

echo Syncing filesystems
sync

#unmount everything
umount /drive/boot/efi &>/dev/null
umount /drive/usr/local &>/dev/null
umount /drive/* &>/dev/null

#Ubuntu is retarded and expects these directories present in / even if you have a /var partition
mkdir -p /drive/var/run
mkdir -p /drive/var/lock

#any 2nd level remaining mounts must be unmounted now before you can umount /drive
while grep -q "/drive/" /proc/mounts ; do grep "/drive" /proc/mounts | awk '{print $2}' |
  while read mountpt ; do umount $mountpt ; done
done

umount /drive &>/dev/null
if [ $? -ne 0 ] ; then
  mcmserror "Unable to unmount newly created system."
else
  echo "Unmounted filesystems."
fi

echo Syncing one final time
sync

#complete
echo The drive should be standalone bootable now.
echo "The system should reboot at this point, if it doesn't, reboot manually."

echo "Sleeping 5s to allow RAID controllers to flush cache (if applicable)"
sleep 5
echo b > /proc/sysrq-trigger
