#!/bin/bash

# Copyright 2015 Patrick J. Verner, Bayside, WI, USA
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Written by Patrick Verner and Jason Vasquez.
# Code clean-ups by Gert Hulselmans.
# Amended by Dick Burggraaff (burdi01).

export PATH="/bin:/sbin:/usr/bin:/usr/sbin"



if [ "e$version" != "e" ]; then
    VERSION=$version
fi
#VERSION="2015_05_04"


ISO_VERSION="pmagic_$VERSION.iso"
MEM="$((975*1024))"

NORMAL="$(echo -e '\033[0;39m')"
BLUE="$(echo -e '\033[1;34m')"
CYAN="$(echo -e '\033[1;36m')"
GREEN="$(echo -e '\033[1;32m')"
MAGENTA="$(echo -e '\033[1;35m')"
RED="$(echo -e '\033[1;31m')"
YELLOW="$(echo -e '\033[1;33m')"

CURSOFF="$(echo -e '\033[?25l')"
CURSON="$(echo -e '\033[?25h')"

DONE () { echo -e "${GREEN}DONE${NORMAL}\n" ;}

FAILED () {
   echo ${NORMAL}
   echo "You are now on the command line."
   echo "Commands like \"ls\", \"cat\", and \"more\" are at your disposal."
   echo "Use <Shift+PageUp/Down> to scroll up and down."
   echo "When done type \"exit\" to reboot the system."
   echo ${CURSON} ; setsid cttyhack bash ; reboot -f
}

# Show progress
dots () {
   set +x

   while [ -d /proc/$1 ]; do
      echo -n '.' ; usleep 150000
   done

   if [ "$tracing" = "yes" ]; then
      set -x
   fi
}

# ==============================================================================

do_sqfs () {
   echo -n "${BLUE}Mounting PMAGIC_$VERSION.SQFS... "
   
   losetup /dev/loop254 /$1/PMAGIC_$VERSION.SQFS
   if ! mount -n -r -t squashfs /dev/loop254 /br_sqfs ; then
      echo -e "\n${RED}Mounting /$1/PMAGIC_$VERSION.SQFS failed." ; FAILED
   fi

   mount -t tmpfs tmpfs /br_ram

   if ! mount -t aufs -o br:/br_ram=rw:/br_fu=rr:/br_m=rr:/br_sqfs=rr aufs /union ; then
      echo -e "\n${RED}Mounting the aufs union failed." ; FAILED
   fi

   mount -t tmpfs tmpfs /union/tmp         # for chromium
   chmod 1777 /union/tmp/                  # for syslinux
   mkdir -p /union/tmp/scripts             # for scripts from pmodules

   DONE
}

# ==============================================================================

# List pmodules and scripts
pmods () {
   ls $1/*.{SQFM,sqfm,xz,t?z,deb} $1/scripts/* 2> /dev/null
}

do_default () {
   echo -n "${BLUE}Copying files to RAM ${YELLOW}"
   mount -t tmpfs tmpfs /sqfs_disk
   cp $1/PMAGIC_$VERSION.SQFS /sqfs_disk &
   dots $!
   echo -e "${NORMAL}\n"

   do_sqfs /sqfs_disk

   [ -n "$(pmods $1)$(pmods /dev_mnt/$directory/pmagic/pmodules)" ] \
      && touch /union/tmp/mount_media
}

do_live () {
   do_sqfs $1
   touch /union/tmp/livemedia
}

do_switch () {
   echo $1 > /union/tmp/pmagic_device
   echo $ISO_PATH > /union/tmp/pmagic_iso_path

   killall -q klogd syslogd udevd

   rm -Rf /union/etc/modprobe.d ; mv /etc/modprobe.d /union/etc
   mv /var/log/messages /union/var/log

   # This line resolves the mkfs.btrfs - error checking mount status
   rm -f /dev/loop25[2,3,4,5]
   
   mount --move /dev /union/dev
   mount --move /sys /union/sys
   mount --move /proc /union/proc
   mount --move /run /union/run

   # These lines resolve the bogus entries in PCManFM
   mkdir /union/run/{br_ram,br_fu,br_m,br_sqfs}
   mount --move /br_ram /union/run/br_ram
   mount --move /br_fu /union/run/br_fu
   mount --move /br_m /union/run/br_m
   mount --move /br_sqfs /union/run/br_sqfs

   if echo $- | grep -q x ; then
      echo -n "${MAGENTA}Tracing >>> ${NORMAL}" ; read a
   fi
 
   exec busybox switch_root /union "/sbin/init"

}

# ==============================================================================

_M () { echo "${YELLOW}$(($1/1024))${RED}" ;}

find_pmagic () {
   if [ ! -e $1/pmagic/pmodules/PMAGIC_$VERSION.SQFS ]; then
      umount /test_mnt &> /dev/null
      return
   fi

   DONE

   if [ "$boot" != "live" ]; then

      M=$(awk '/^MemTotal/ {print $2}' /proc/meminfo)
      if [ $M -lt $MEM ]; then
         echo "${RED}To run from RAM Parted Magic must have at least $(_M $MEM) MB available,"
         echo "but only $(_M $M) MB was detected."
         echo "Select a 'Live' option from the menu after rebooting the system."
         FAILED
      fi

      do_default $1/pmagic/pmodules

      umount -d /test_mnt &> /dev/null
      umount -d /dev_mnt &> /dev/null

   else

      # This cumbersome for ntfs[-3g] media to become rw :((
      if [ ${dev:0:2} != "sr" ]; then
         if [ -z "$ISO_PATH" ]; then
            umount /test_mnt
            mount /dev/$dev /test_mnt
         else
            umount /test_mnt
            losetup -d /dev/loop255
            umount /dev_mnt
            mount /dev/$dev /dev_mnt
            losetup /dev/loop255 /dev_mnt/$ISO_PATH
            mount -r /dev/loop255 /test_mnt
         fi
      fi   

      do_live $1/pmagic/pmodules

      mkdir /union/media/$dev
      if [ -z "$ISO_PATH" ]; then
         mount --move /test_mnt /union/media/$dev
      else
         mount --move /dev_mnt /union/media/$dev
         mkdir /union/media/iso
         mount --move /test_mnt /union/media/iso
      fi

   fi

   do_switch /dev/$dev
}

# Assemble file for split PXE
concat () {
   if [ -e ${1}00 ]; then
      for F in $(ls ${1}[0-9][0-9] | grep -v ${1}00) ; do
         cat $F >> ${1}00 ; rm $F
      done
      mkdir -p $(dirname "$2")
      mv -f ${1}00 "$2"
   fi
}

find_pxe () {
   # /p0000 contains the concat commands
   [ -r /p0000 ] && . /p0000 

   [ -e /pmagic/pmodules/PMAGIC_$VERSION.SQFS ] || return

   # The medium is in memory, copying makes no sense
   do_live /pmagic/pmodules

   # /modules and /scripts are "old" and kept for backward compatibility
   mkdir -p /union/media/pxe/${directory}/pmagic/pmodules/scripts
   mv /modules/* /union/media/pxe/${directory}/pmagic/pmodules &> /dev/null
   mv /scripts/* /union/media/pxe/${directory}/pmagic/pmodules/scripts &> /dev/null

   # /pmagic/pmodules/{optional,scripts} is "new" -- doing a deep move
   for F in $(find pmagic ! -path pmagic/pmodules/PMAGIC_$VERSION.SQFS) ; do
      if [ -d "$F" ]; then
         mkdir -p "/union/media/pxe/${directory}/$F"
      elif [ -f "$F" ]; then 
         mkdir -p "/union/media/pxe/${directory}/$(dirname "$F")"
         mv -f "$F" "/union/media/pxe/${directory}/$F"
      fi
   done   

   do_switch pxe
}

find_memdisk () {
   if ! MEMDISK_LOC=$(memdiskfind) ; then return ; fi

   # We found a MEMDISK mapped disk, set up phram
   modprobe phram phram=memdisk,${MEMDISK_LOC}
   # Load mtdblock, the memdisk will be /dev/mtdblock0
   modprobe mtdblock
   sleep 1

   dev=mtdblock0
   if ! mount -r /dev/$dev /test_mnt ; then
      echo "${RED}Booting the memdisk-mapped ISO failed."
      echo "Is the vmalloc value in the syslinux.cfg in the ISO correct?"
      echo "It should be at least (isosize + 32) MB."
      FAILED
   fi

   # The iso is in memory, copying makes no sense
   do_live /test_mnt/${directory}/pmagic/pmodules

   mkdir /union/media/$dev
   mount --move /test_mnt /union/media/$dev

   do_switch /dev/$dev
}

# ==============================================================================

# Make some needed directories
mkdir -p /union /br_ram /br_fu /br_m /br_sqfs \
   /sqfs_disk /test_mnt /dev_mnt \
   /dev /mnt /proc /sys /tmp /var/log

# Mount kernel file systems
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mkdir -p /dev/shm
mount -t tmpfs tmpfs /dev/shm
mount -t tmpfs tmpfs /run

. /proc/cmdline

if [ "$tracing" = "yes" ]; then
   set -x
fi

# Start logging
syslogd
klogd -c 1

echo "${CURSOFF}"

# Process "special" command line parameters
root="$(cat /proc/cmdline | awk -F root= '{print $2}' | awk '{print $1}')"
[ -n "$iso_filename" ] && ISO_VERSION="$iso_filename"

# Assemble {fu,m}.sqfs if split PXE is used
concat /fu /fu.sqfs
concat /m /m.sqfs

# The PM kernels have the squashfs and loop drivers built in
# If yours does not then provide the module(s) in /lib
insmod /lib/squashfs.ko &> /dev/null
insmod /lib/loop.ko &> /dev/null

losetup /dev/loop252 /fu.sqfs
if ! mount -n -r -t squashfs /dev/loop252 /br_fu ; then
   echo "${RED}Mounting the fu.sqfs failed." ; FAILED
fi
ln -fns /br_fu/lib/{firmware,udev} /lib

losetup /dev/loop253 /m.sqfs
if ! mount -n -r -t squashfs /dev/loop253 /br_m ; then
   echo "${RED}Mounting the m.sqfs failed." ; FAILED
fi
ln -fns /br_m/lib/modules /lib

if [ ! -d /lib/modules/$(uname -r) ]; then
   echo "${RED}The bzImage and initrd differ in their kernel versions."
   echo "${YELLOW}$(uname -r) ==> $( (cd /lib/modules ; ls -C) )"
   FAILED
fi

mkdir -p /dev/pts
mount -t devpts devpts /dev/pts &> /dev/null

echo -n "${BLUE}Setting up system devices... ${NORMAL}"

# Add blacklisted modules from /proc/cmdline
rm -f /etc/modprobe.d/cmdline.conf &> /dev/null
for i in $(echo $blacklist | tr ":" " ") ; do
   echo blacklist $i >> /etc/modprobe.d/cmdline.conf
done

# If ahci is not loaded before udev the internal disk may not become /dev/sda
if [ "$ahci" != "no" ]; then
   modprobe ahci &> /dev/null
fi

# Modprobe modules from /proc/cmdline
for i in $(echo $modprobe | tr " " "~" | tr ":" " ") ; do
   modprobe $(echo $i | tr "~" " ")
done

# Start udev
udevd --daemon 2> /dev/null
udevadm trigger --type=subsystems --action=add
udevadm trigger --type=devices --action=add
udevadm settle --timeout=120

# Start mdadm
mdadm --examine --scan > /etc/mdadm.conf
mdadm --stop --scan &> /dev/null
mdadm --assemble --scan &> /dev/null

DONE

# Load fuse
modprobe fuse

# Load efivars for Clonezilla
modprobe efivars &> /dev/null

# Dump to shell
if [ "$initrd_shell" = "yes" ]; then
   echo -n "${GREEN}" ; busybox ; echo -n "${NORMAL}"
   echo -n "${CURSON}" ; setsid cttyhack bash ; echo "${CURSOFF}"
fi

# PXE : everything in initrd.img, no mounts or loops necessary
find_pxe

# MEMDISK mapped disks in memory, no loops necessary
find_memdisk

echo -n "${BLUE}Searching for PMAGIC_$VERSION.SQFS...${YELLOW} "

# Mount an ISO
iso () {
   for D in "" pmagic/ images/ ; do
      if [ -e /test_mnt/${iso_location}/$D$ISO_VERSION ]; then
         mount --move /test_mnt /dev_mnt
         ISO_PATH=${iso_location}/$D$ISO_VERSION
         losetup /dev/loop255 /dev_mnt/$ISO_PATH
         mount -r /dev/loop255 /test_mnt
         find_pmagic /test_mnt
         ISO_PATH=
         mount --move /dev_mnt /test_mnt
      fi
   done
}

device_lister () {
   if [ "$device_list" = "yes" ]; then
      echo -en "\n$@ "
   fi
}

# Mount via a device
root () {
if [ -n "$root" ]; then
   root=$(basename $root)
   device_lister $root
   [ $PASSES -lt 10 ] && DIRECT=direct
   mount -r "/dev/$root" /test_mnt &> /dev/null
   dev=$(grep test_mnt /proc/mounts | awk '{print $1}' | awk -F /dev/ '{print $2}')
   iso ; find_pmagic /test_mnt/$directory
fi
}

# Mount via a label
label () {
if [ -n "$label" ]; then
   device_lister $label
   [ $PASSES -lt 10 ] && DIRECT=direct
   mount -r LABEL="$label" /test_mnt &> /dev/null
   dev=$(grep test_mnt /proc/mounts | awk '{print $1}' | awk -F /dev/ '{print $2}')
   iso ; find_pmagic /test_mnt/$directory
fi
}

# Mount via a (partial) UUID
uuid () {
if [ -n "$uuid" ]; then
   device_lister $uuid
   [ $PASSES -lt 10 ] && DIRECT=direct
   UUID=$(blkid | grep $uuid | awk -F 'UUID="' '{print $2}' | awk -F '"' '{print $1}')
   if [ -n "$UUID" ]; then
      mount -r UUID="$UUID" /test_mnt &> /dev/null
      dev=$(grep test_mnt /proc/mounts | awk '{print $1}' | awk -F /dev/ '{print $2}')
      iso ; find_pmagic /test_mnt/$directory
   fi
fi
}

# List removable devices
removable () {
   ( cd /sys/block
     for DD in "$@" ; do
        for D in $(ls -d $DD* 2> /dev/null) ; do
            if [ "$(cat $D/removable 2> /dev/null)" = "1" ]; then
               ( cd /dev ; ls $D* 2> /dev/null )
            fi
        done
     done
   )
}

# List devices to scan
# Booted from: 49 = syslinux = USB ; 51 = isolinux = CD
_device_list () {
   if [ $PASSES -lt 10 -o "$removableonly" = "yes" ]; then
      case $(cat /proc/sys/kernel/bootloader_type) in
         49)  removable sd ;;
         51)  removable sr ;;
         *)   removable sd sr vd ;;
      esac
   else
      ( cd /dev ; ls sd* ; ls sr* ; ls vd* ; ls md* ) 2> /dev/null
   fi
}

device_list () {
   ZZ=$(_device_list)
   for Z in $(echo $ZZ | rev) ; do
      echo $Z | rev
   done
}


#Patrick Masotta mod - start

# Lets start the net and get an IP
net_on () {
#check netpath
urlprot=${netpath:0:7}
if [[ "$urlprot" != "http://" ]]; then
 echo -e "\n\n${RED}ERROR: No HTTP netpath."
 FAILED 
fi


#let's see if BOOTIF (IPPAPEND 2) is present
if [ -z $BOOTIF ]; then
 echo -e "\n\n${RED}ERROR: IPAPPEND 2 not present."
 FAILED
fi

echo -e "\n${BLUE}Probe Ethernet... ${NORMAL}"
sleep 3
ifconfig lo up

# get mac address out of BOOTIF
bootif_mac=$(echo ${BOOTIF#*-} | sed -e 'y,-,:,')

# look for devices with matching mac address, and set DEVICE to
# appropriate value if match is found.
for device in /sys/class/net/* ; do
 if [ -f "$device/address" ]; then
#echo "device=$device"
   current_mac=$(cat "$device/address")
#echo "current_mac=$current_mac"
   if [ "$bootif_mac" = "$current_mac" ]; then
    DEVICE=${device##*/}
#echo "DEVICE=$DEVICE"
    break
   fi
 fi
done


if [ -z $DEVICE ]; then
 echo -e "\n\n${RED}Error locating booting ETH device."
 FAILED
fi

ifconfig $DEVICE up
ip_clnt=$(udhcpc -i $DEVICE | grep Lease | cut -f 3 -d ' ')
ifconfig $DEVICE $ip_clnt up

ifconfig "$DEVICE"
}

http () {
if [ "e$netpath" != "e" ]; then
    
    echo -e "${NORMAL}\n\n┌────────────────────────────────────────────────┐"
    echo -e "│        Serva 3.2.0 Automated PXE Server        │"
    echo -e "│   Copyright (c) 2010-2018 by Patrick Masotta   │"
    echo -e "│             www.vercot.com/~serva/             │"
    echo -e "└────────────────────────────────────────────────┘"


      M=$(awk '/^MemTotal/ {print $2}' /proc/meminfo)
      if [ $M -lt $MEM ]; then
         echo "${RED}To run from RAM Parted Magic must have at least $(_M $MEM) MB available,"
         echo "but only $(_M $M) MB was detected."
         echo "Select a 'Live' option from the menu after rebooting the system."
         FAILED
      fi



    if [ "e$version" != "e" ]; then
        VERSION=$version
    fi

    net_on
    device_lister $netpath

    echo -e "${BLUE}wget ${netpath}/pmagic/pmodules/PMAGIC_$VERSION.SQFS${NORMAL}"
    wget "${netpath}/pmagic/pmodules/PMAGIC_$VERSION.SQFS"  2>&1

    if [ $? -ne 0 ]; then
	echo -e "\n\n${RED}Wget failed. \nPlease check that the \"version=\" variable in ServaAsset.inf is correct."
	FAILED
    fi
    mkdir -p /test_mnt/$directory/pmagic/pmodules			2>&1
    mv /PMAGIC_$VERSION.SQFS /test_mnt/$directory/pmagic/pmodules/      2>&1
   iso ; find_pmagic /test_mnt/$directory
fi
}
# Patrick Masotta mod -  end


# The big loop
PASSES=0 ; while [ $PASSES -lt 30 ]; do
   sleep 1
#   DIRECT= ; root ; label ; uuid # Patrick Masota mod
   DIRECT= ; root ; label ; uuid ; http
   if [ -z "$DIRECT" ]; then
      DEVICE_LIST="$( device_list | tr "\n" " ")"
      device_lister $DEVICE_LIST

      for dev in $DEVICE_LIST ; do
         mount -r /dev/$dev /test_mnt &> /dev/null
         iso ; find_pmagic /test_mnt/$directory
      done
   fi
   let PASSES=PASSES+1
done

# Everything failed
echo -e "\n\n${RED}The PMAGIC_$VERSION.SQFS file could not be found."
echo "Report this incident to the forum and if possible attach"
echo "a directory listing of the media."
echo
echo "Example:"
echo "${YELLOW}mount /dev/sr0 /mnt"
echo "ls -lR /mnt${RED}"
echo
echo "The ${YELLOW}/dev/sr0${RED} above refers to a CD or DVD, for a USB stick"
echo "use e.g. ${YELLOW}/dev/sdb1${RED}."
FAILED
