#!/bin/sh
#(c) Copyright Barry Kauler, 30 January 2017. Licence: GPL v3 (/usr/share/doc/legal).
#simple script in initramfs to boot Easy Linux.
#deloyed as a drive image, with one 519MB fat32 partition, containing vmlinuz, initrd.q, q.sfs
#text colors: 34=blue, 33=yellow, 32=green, 31=red, 35=purple, 36=aquablue, 38=black

export TEXTDOMAIN=easyinitrd
export OUTPUT_CHARSET=UTF-8
mount -t proc none /proc
mount -t sysfs none /sys
mount -t rootfs -o remount,rw rootfs /
ln -s /proc/mounts /etc/mtab 2> /dev/null
export PATH="/bin:/sbin"

err_exit() {
 echo -e "\\033[1;31mERROR: ${1}\\033[0;39m" #red
 echo 'Have now dropped into a shell in the initramfs.'
 echo 'Note: On some PCs the keyboard does not work at this stage of bootup,'
 echo 'in which case hold down the power button to shutdown.'
 /bin/sh
}

#170206 reintroducing aufs in the kernel:
if grep -qw aufs /proc/filesystems; then
 LAYERFS='aufs'
 RO='=ro'
else
 LAYERFS='overlay'
 RO=''
fi

###find drives###
#find the drive we are booting on (has vmlinuz, initrd.q, q.sfs), and working drv...
. /BOOT_SPECS #has BOOT_DISKID, BOOT_PARTNUM, BOOT_FS, BOOT_DIR, WKG_DISKID, WKG_PARTNUM, WKG_FS, WKG_DIR, Q_DISTRO_VERSION
echo -n -e "\\033[1;35mSearching drives:\\033[0;39m\n " #purple
CNT=0; Pb=''; Pw=''; BOOT_DRV=''; WKG_DRV=''
while [ $CNT -lt 8 ];do #drives may take couple seconds to become available.
 sleep 1
 CNT=$(($CNT+1))
 for aDRV in /sys/block/sd[a-z] /sys/block/mmcblk[0-9]
 do
  DRV=${aDRV:11:7} #extract drv from /sys/block/<drv>
  echo -n " ${DRV}"
  fdisk -l /dev/${DRV} > diskinfo-${DRV} 2>/dev/null
  if grep "$BOOT_DISKID" diskinfo-${DRV} >/dev/null;then BOOT_DRV="$DRV"; fi
  if grep "$WKG_DISKID" diskinfo-${DRV} >/dev/null;then WKG_DRV="$DRV"; fi
  [ "$BOOT_DRV" -a "$WKG_DRV" ] && break 2
 done
done
[ ! "$BOOT_DRV" ] && err_exit 'Boot drive not found'
[ ! "$WKG_DRV" ] && err_exit 'Working drive not found'
[ "${BOOT_DRV:0:3}" == "mmc" ] && Pb='p'
[ "${WKG_DRV:0:3}" == "mmc" ] && Pw='p'
BOOT_DEV="${BOOT_DRV}${Pb}${BOOT_PARTNUM}"
WKG_DEV="${WKG_DRV}${Pw}${WKG_PARTNUM}"
echo -e "\n  Boot-partition: ${BOOT_DEV}, working-partition: ${WKG_DEV}"
mkdir -p /mnt/${BOOT_DEV}
mkdir -p /mnt/${WKG_DEV}

###performance measurements###
FREEK=`grep '^MemFree:' /proc/meminfo | tr -s ' ' | cut -f 2 -d ' '` #free RAM
#echo 3 > /proc/sys/vm/drop_caches #clear memory caches. note, could use "hdparm -t ..." but it is slower.
TIMEs="$(dd if=/dev/${WKG_DRV} of=/dev/null skip=524288 bs=1024 count=1 2>&1 | grep -o '[0-9.]* seconds,' | cut -f 1 -d ' ')" #read speed.
TIME10k=$(dc ${TIMEs} 10000 mul p | cut -f 1 -d '.') #cheap usb3 flash stick: 15, internal eMMC: 47

###[create and] mount working partition###
if ! grep " ${WKG_DEV}$" /proc/partitions >/dev/null;then
 echo -e "\\033[1;35mCreating ${WKG_FS} partition ${WKG_DEV} to fill drive\\033[0;39m" #purple
 echo -e "n\n${WKG_PARTNUM}\n\n\nw" | fdisk -u /dev/$WKG_DRV > err.log 2>&1
 sync
 if grep -i 'failed to write disklabel' err.log;then #confused by invalid backup gpt
  echo -e "\\033[1;31mFixing secondary GPT, will take awhile...\\033[0;39m" #red
  echo -e 'x\ne\nw\nY' | gdisk /dev/${WKG_DRV}
  sync
  echo -e "n\n${WKG_PARTNUM}\n\n\nw" | fdisk -u /dev/${WKG_DRV}
  sync
 fi
 mke2fs -q -t ${WKG_FS} -O ^has_journal -L easy2 -m 0 -b 4096 /dev/${WKG_DEV} #only supporting ext2/3/4
fi
mount -t ${WKG_FS} /dev/${WKG_DEV} /mnt/${WKG_DEV}
[ $? -ne 0 ] && err_exit "Unable to mount working-partition ${WKG_DEV}"
if [ ! -d /mnt/${WKG_DEV}/${WKG_DIR}repository ];then #populate with skeleton directory hierarchy.
 cp -a -f /skeleton/* /mnt/${WKG_DEV}/${WKG_DIR}
 cp -a -f /skeleton/.[a-z]* /mnt/${WKG_DEV}/${WKG_DIR}
fi

###set date and time###
#could read .session/etc/clock and run hwclock, but for now this probably good enough (refer: rc.shutdown)...
[ -s /mnt/${WKG_DEV}/${WKG_DIR}.session/root/.var/local/shutdown_date_saved ] && date -s "`cat /mnt/${WKG_DEV}/${WKG_DIR}.session/root/.var/local/shutdown_date_saved`" > /dev/null

###mount boot partition###
[ "$BOOT_DEV" != "$WKG_DEV" ] && mount -t ${BOOT_FS} /dev/$BOOT_DEV /mnt/$BOOT_DEV || err_exit "Unable to mount boot-partition ${BOOT_DEV}"
[ ! -f /mnt/$BOOT_DEV/${BOOT_DIR}q.sfs ] && err_exit "$(LANG=${wkgLANG} gettext 'Boot-partition does not have file q.sfs')"

###recovery, maintenance###
[ "$qfix" ] && QFIX=$qfix #kernel boot param
if [ "$QFIX" ];then
 for ONEFIX in `echo -n "$QFIX" | tr ',' ' '`
 do
  case $ONEFIX in
   fsck|FSCK) echo -n "${WKG_DEV},${WKG_FS},REQUEST" > /mnt/${WKG_DEV}/${WKG_DIR}.session/.fsckme.flg ;;
   back|bak|BACK|BAK) echo -n ",erase" > /mnt/${WKG_DEV}/${WKG_DIR}.session/.rollback.flg ;;
  esac
 done
fi
export wkgLANG="$(grep '^LANG=' /mnt/${WKG_DEV}/${WKG_DIR}.session/etc/profile 2>/dev/null | cut -f 2 -d '=')"
[ ! "$wkgLANG" ] && export wkgLANG=C
[ -s /mnt/${WKG_DEV}/${WKG_DIR}.session/.fsckme.flg ] && fscheck ${WKG_DRV} ${WKG_DEV} #ex: improper shutdown
[ -s /mnt/${WKG_DEV}/${WKG_DIR}.session/.rollback.flg ] && rollback ${WKG_DRV} ${WKG_DEV}

###version control###
if [ ! -d /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION} ];then
 echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'One-time only operation, creating a snapshot of Easy Linux')\\033[0;39m"
 echo "  $(LANG=${wkgLANG} gettext 'This will allow future rollback with the Easy Version Control Manager')"
 echo "  $(LANG=${wkgLANG} gettext 'Creating:') /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}"
 if [ -f /mnt/${WKG_DEV}/${WKG_DIR}.session/etc/DISTRO_SPECS ];then
  prevVER="$(grep '^DISTRO_VERSION=' /mnt/${WKG_DEV}/${WKG_DIR}.session/etc/DISTRO_SPECS | cut -f 2 -d '=')"
 else
  prevVER=""
 fi
 mkdir /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}
 echo -n '  initrd.q'; cp -f /mnt/${BOOT_DEV}/${BOOT_DIR}initrd.q /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/
 echo -n ' vmlinuz'; cp -f /mnt/${BOOT_DEV}/${BOOT_DIR}vmlinuz /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/
 echo -n ' q.sfs'; cp -f /mnt/${BOOT_DEV}/${BOOT_DIR}q.sfs /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/
 if [ ! "$prevVER" ];then
  #.session folder (rw layer) is empty, but need a session.sfs anyway...
  echo -n ' session'; mksquashfs /mnt/${WKG_DEV}/${WKG_DIR}.session /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/session.sfs -comp gzip >/dev/null
 else
  rm -f /mnt/${WKG_DEV}/${WKG_DIR}.session/.[a-z]* #precaution.
  #need to merge .session (rw layer) with easy-$prevVER/session.sfs, create new session.sfs...
  mkdir sro snew /mnt/${WKG_DEV}/${WKG_DIR}swork #170320
  mount -t squashfs -o loop,noatime /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${prevVER}/session.sfs sro
  if [ "$LAYERFS" == "aufs" ];then
   mount -t aufs -o br=/mnt/${WKG_DEV}/${WKG_DIR}.session=rw:sro=ro aufs snew
  else
   mount -t overlay -o lowerdir=sro,upperdir=/mnt/${WKG_DEV}/${WKG_DIR}.session,workdir=/mnt/${WKG_DEV}/${WKG_DIR}swork overlay snew
  fi
  echo -n ' session'; mksquashfs snew /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/session.sfs -comp gzip >/dev/null
  sync
  umount snew
  umount sro
  rm -rf /mnt/${WKG_DEV}/${WKG_DIR}swork #170320
  rm -rf /mnt/${WKG_DEV}/${WKG_DIR}.session/*
  touch /mnt/${WKG_DEV}/${WKG_DIR}.session/.delayedrun_version_upgrade #see /usr/sbin/delayedrun
 fi
 mkdir -p /mnt/${WKG_DEV}/${WKG_DIR}.session/etc
 #also save any container sessions:
 for EXE in `ls -1 /mnt/${WKG_DEV}/${WKG_DIR}containers | tr '\n' ' '`
 do
  [ ! -d /mnt/${WKG_DEV}/${WKG_DIR}containers/${EXE}/.session ] && continue
  #echo -n " ec-${EXE}"; mksquashfs /mnt/${WKG_DEV}/${WKG_DIR}containers/${EXE}/.session /mnt/${WKG_DEV}/${WKG_DIR}containers/${EXE}/rw-${prevVER}.sfs -comp gzip >/dev/null
  cp -f /mnt/${WKG_DEV}/${WKG_DIR}containers/${EXE}/configuration /mnt/${WKG_DEV}/${WKG_DIR}containers/${EXE}/configuration-${prevVER}
 done
 echo
 touch /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/configuration
 touch /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/configuration-${Q_DISTRO_VERSION}
fi

###recompress q.sfs### improve operating speed, recompress q.sfs (refer quicksetup)
if [ -s /mnt/${WKG_DEV}/${WKG_DIR}.session/.qsfs.flg ];then
 echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'Recompressing q.sfs, xz to gz, please wait')\\033[0;39m" #purple
 mkdir /mntsfs
 mount -r -t squashfs -o loop,noatime /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/q.sfs /mntsfs
 mksquashfs /mntsfs /mnt/${WKG_DEV}/${WKG_DIR}q-gz.sfs -comp gzip #>/dev/null
 sync
 umount /mntsfs
 mv -f /mnt/${WKG_DEV}/${WKG_DIR}q-gz.sfs /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/q.sfs
 sync
 rm -f /mnt/${WKG_DEV}/${WKG_DIR}.session/.qsfs.flg
fi

###setup bottom ro layer, with q.sfs###
echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'Mounting read-only layer of layered filesystem')\\033[0;39m" #purple
QSFSbytes0=`stat -c %s /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/q.sfs`
QSFSbytes1=$(($QSFSbytes0+500000))
mount -t tmpfs -o size=${QSFSbytes1} tmpfs /q_ro
mkdir /q_ro/q_sfs
#decide whether to copy q.sfs to RAM, or not...
CPYflg=0
[ $FREEK -gt 1100000 ] && CPYflg=1 #>1GB ram then copy
[ $TIME10k -lt 16 ] && CPYflg=0    #but fast drive so don't copy.
[ $FREEK -gt 3100000 ] && CPYflg=1 #but heaps of ram, so copy.
if [ $CPYflg -eq 0 ];then
 #do not copy q.sfs into ram, mount where it is...
 echo "  $(LANG=${wkgLANG} gettext 'Mounting squashfs file q.sfs')"
 QSFS_PATH="/mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/" #need this for setting up containers.
 mount -r -t squashfs -o noatime,loop /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/q.sfs /q_ro/q_sfs
else
 echo "  $(LANG=${wkgLANG} gettext 'Copying q.sfs to RAM, then mounting')"
 [ ! -f /q_ro/q.sfs ] && cp -a /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/q.sfs /q_ro/
 QSFS_PATH="/mnt/q_ro/" #need this for setting up containers.
 mount -r -t squashfs -o noatime,loop /q_ro/q.sfs /q_ro/q_sfs
fi
[ $? -ne 0 ] && err_exit "$(LANG=${wkgLANG} gettext 'Failed to mount q.sfs')"
cp -f /q_ro/q_sfs/etc/DISTRO_SPECS /mnt/${WKG_DEV}/${WKG_DIR}.session/etc/ #need to be sure correct one is on top.
#also mount session.sfs...
mkdir /q_ro/session
mount -r -t squashfs -o loop,noatime /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/session.sfs /q_ro/session
#precaution...
. /q_ro/q_sfs/etc/DISTRO_SPECS
[ "$DISTRO_VERSION" != "$Q_DISTRO_VERSION" ] && echo -e "\\033[1;31m$(gettext 'WARNING, versions do not match.') initrd.q: ${Q_DISTRO_VERSION}, q.sfs: ${DISTRO_VERSION}\\033[0;39m" #red

###load extra sfs on ro layer###
#the configuration file defines any more sfs files to load. q.sfs is always loaded
# (on bottom), and session.sfs on top of ro layers.
. /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/configuration
for NUM in 4 3 2 1
do
 eval "ROsfs=\$LAYER_RO${NUM}" #implements indirection. LAYER_RO1='devx.sfs' in configuration file, will assign 'devx.sfs' to ROsfs. 170320
 if [ "$ROsfs" ];then
  ANAME="$(basename $ROsfs .sfs)"
  mkdir /q_ro/$ANAME
  echo "  $(LANG=${wkgLANG} gettext 'Mounting extra squashfs file:') ${ANAME}.sfs"
  mount -r -t squashfs -o noatime,loop /mnt/${WKG_DEV}/${WKG_DIR}repository/easy-${Q_DISTRO_VERSION}/${ROsfs} /q_ro/${ANAME}
  EXTRASFS="/q_ro/${ANAME}${RO}:${EXTRASFS}" #170206
 fi
done

sync
[ "$BOOT_DEV" != "$WKG_DEV" ] && umount /dev/${BOOT_DEV}

###the big moment, create layered f.s.###
#echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'Creating layered filesystem, with read-write folder:') /mnt/${WKG_DEV}/.session\\033[0;39m"
echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'Creating layered filesystem, type:') ${LAYERFS}\\033[0;39m"
if [ "$LAYERFS" == "aufs" ];then
 mount -t aufs -o br=/mnt/${WKG_DEV}/${WKG_DIR}.session=rw:/q_ro/session=ro:${EXTRASFS}/q_ro/q_sfs=ro aufs /q_new
else
 mount -t overlay -o lowerdir=/q_ro/session:${EXTRASFS}/q_ro/q_sfs,upperdir=/mnt/${WKG_DEV}/${WKG_DIR}.session,workdir=/mnt/${WKG_DEV}/${WKG_DIR}.tempwork overlay /q_new
fi
[ $? -ne 0 ] && err_exit "$(LANG=${wkgLANG} gettext 'Failed to create layered filesystem')"

#/etc/rc.d/rc.sysinit will append to PUPSTATE, get the ball rolling here...
#PUPMODE, bit-1 (partition has session), bit-2 (bottom layer is sfs) --rough equiv to puppy
echo -e "PUPMODE=6\nBOOT_DEV='${BOOT_DEV}'\nBOOT_FS='${BOOT_FS}'\nBOOT_DIR='${BOOT_DIR}'\nWKG_DEV='${WKG_DEV}'\nWKG_FS='${WKG_FS}'\nWKG_DIR='${WKG_DIR}'\nQSFS_PATH='${QSFS_PATH}'" > /q_new/etc/rc.d/PUPSTATE

###relocate mount-points prior to switch_root###
echo -e "\\033[1;35m$(LANG=${wkgLANG} gettext 'Performing a switch_root onto the layered filesystem')\\033[0;39m" #purple
#move the mount points...
[ ! -d /q_new/mnt/q_ro ] && mkdir /q_new/mnt/q_ro
[ ! -d /q_new/mnt/${WKG_DEV} ] && mkdir /q_new/mnt/${WKG_DEV}
[ ! -d /q_new/mnt/${BOOT_DEV} ] && mkdir /q_new/mnt/${BOOT_DEV}
mount -o move /q_ro /q_new/mnt/q_ro
mount -o move /mnt/${WKG_DEV} /q_new/mnt/${WKG_DEV}

mount -t devtmpfs devtmpfs /q_new/dev #need to do this before switch_root.
sync
umount /sys
umount /proc
exec switch_root /q_new /sbin/init

###END###
