#!/bin/bash
##############################################################################
# This scrripts creates miniroot images currently for ARM and x86_64 machines
# and possible for any slackware port with minor editing.
#
# It is a rework from scratch of Suart's miniroot creation script for 
# slackwareARM 
# ftp.arm.slackware.com/slackwarearm/slackwarearm-devtools/minirootfs
#
# Although, in order to produce a bootable image, it is still necessary to run 
# the script from the same type of machine as the miniroot target it is 
# possible to do part of the work cross platform.
#
# Another fundamental difference is that this script will download the packages wheras 
# Stuart's script needs a local repo.
#
# Author:  louigi600 <louifi600@yahoo.it>
#
# 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.
###############################################################################

#configuration for this script is in the conf subflder
Name=$(basename $0)
Basedir=$(dirname $0)
[ "$Basedir" = "." ] && Basedir=$PWD
Cfgdir=${Basedir}/conf

#sourcing configuration file
source ${Cfgdir}/${Name}.conf

LARCH="$(uname -m)"

if $Debug
then
  cat << EOF!
  Script Name : $Name
  Base Directory : $Basedir
  Config Directory : $Cfgdir
  Supported Releases : ${SupportedReleases[*]}
EOF!
  sleep 1
fi

#functn for chsng the miniroot you wat to build from the supported list
Choose_Release ()
{ #echo "${!SupportedReleases[*]}"
  DialogString="$(awk '{ for (a=1;a<=NF;a++) {printf("%s %i ",$a,a);}  }' <<< ${!SupportedReleases[*]})"
  #echo "$DialogString"
  exec 3>&1;
  Release=$(dialog --title 'Release' --menu 'Choose Release:' 0 0 0 $DialogString  2>&1 1>&3)
  Res=$?
  exec 3>&-;
  [ $Res -ne 0 ] && exit 0
}

Get_Packages ()
{ mkdir -p $1
  cd $1
  
  for pkgspec in $Pkglist
  do
    pkg=$(basename ${pkgspec})
    FileName="$(links -dump $(dirname "${URL}/$pkgspec") | sed 's/^.*] *//' |awk -v pattern="^${pkg}-[0-9].*t.z\$" '$1 ~ pattern {print $1}')"
    [ "$FileName" = "" ] && continue
    PatchesFileName="$(links -dump $(dirname $URL)/patches/packages | awk -v pattern="^${pkg}-[0-9].*z\$" '$2 ~ pattern {print $2}')"
    if [ "$PatchesFileName" != "" ]
    then
      $Debug && echo "$pkgspec : $(dirname $URL)/patches/packages/$PatchesFileName"
      FileName="$PatchesFileName"
      FetchURL="$(dirname $URL)/patches/packages/$PatchesFileName"
    else
      $Debug && echo "$pkgspec : ${URL}/$(dirname $pkgspec)/$FileName"
      FetchURL="${URL}/$(dirname $pkgspec)/$FileName"
    fi
    LocalFlename="$(ls ${pkg}-[0-9]*.t*z 2>/dev/null)"
    if [ "$LocalFlename" != "" -a "$LocalFlename" != "$FileName" ]
    then
      $Debug && echo "There is a newer verson $LocalFlename -> $FileName"
      rm -f $LocalFlename
    fi
    echo "$FileName"
    wget -qN --show-progres $FetchURL
  done
}

Recreate_Miniroot ()
{ [ "$1" = "" ] && usage Recreate_Miniroot was caled wth no miniroot path
  #create a fresh miniroot
  echo "Recreating a fresh minroot in $1 ..."
  $Debug && sleep 1
  mkdir -p $1
  cd  $1
  rm -rf ./*

  #installing into miniroot with option --root $1
  echo "install the packages into $1"
  $Debug && sleep 1
  for pkgspec in $Pkglist
  do
    pkg=$(basename ${pkgspec})
    FileName="$(ls ${Pkgdir}/${Release}/${pkg}-[0-9]* 2>/dev/null)"
    [ "$FileName" = "" ] && continue
    installpkg --threads $( nproc ) --terse --root $1 $FileName
  done
  #patching update-ca-certificates not to use perl
  echo "patching update-ca-certificates so that it will not use perl"
  $Debug && sleep 1
  ( cd ${1}/usr/sbin
    patch -p0 < ${Basedir}/conf/update-ca-certificates_no-perl.patch
  )
  
  #setting up root password
  echo "setting root password on miniroot"
  $Debug && sleep 1
  chpasswd -R $1 <<< "root:$(get_myoptions_value p)" 2>/dev/null
}

Chroot_Setup ()
{ #configure dynamic linker run-time bindings
  #this does not work cross platform but is necessary to be able to boot the image
  echo "configuring dynamic linker run-time bindings and cache"
  ldconfig -r $1 2>/dev/null

  echo "updateing /etc/ssl/certs and ca-certificates.crt"
  $Debug && sleep 1
  chroot $1 usr/sbin/update-ca-certificates --fresh
}

Setup_Fstab ()
{ echo "generating a sample fstab"
  $Debug && sleep 1
  #creating the fstab
cat << EOF > ${1}/etc/fstab
proc             /proc            proc        defaults                   0   0
devpts           /dev/pts         devpts      gid=5,mode=620             0   0
tmpfs            /dev/shm         tmpfs       nosuid,nodev,noexec        0   0
#example of boot and root for normal ide/sata/scsi/sas/usb storage
#/dev/sda1        /boot            ext4        errors=remount-ro,noatime  0   1
#/dev/sda3        /                ext4        errors=remount-ro,noatime  0   1
#example of boot and root for mmc storage like the RPi
#/dev/mmcblk0p1   /boot            vfat        errors=remount-ro,noatime  0   1
#/dev/mmcblk0p2   /                ext4        errors=remount-ro,noatime  0   1
EOF
}

Setup_Basic_Networking ()
{ echo "setting u loopback networking on miniroot"
  $Debug && sleep 1
  #creating hosts and resolv.conf
  echo "miniroot" > ${1}/etc/HOSTNAME

  cat << EOF > ${1}/etc/hosts
# For loopbacking.
127.0.0.1       localhost
#::1             localhost #uncomment if you are going to use ipv6
# Same thing for the default hostname if nothing was set up by netconfig:
127.0.0.1       miniroot.example.net miniroot
#::1             miniroot.example.net miniroot

# End of hosts.
EOF

  cat << EOF > ${1}/etc/resolv.conf
search example.net
nameserver 192.168.0.1
EOF

  #making sure sshd is enabled
  chmod +x ${1}/etc/rc.d/rc.ssh*
}

Setup_Timezone ()
{ echo "setting up timezone"
  $Debug && sleep 1
  #setting up timezone
  ( cd ${1}/etc
    cat << EOF > hardwareclock
localtime
EOF

    rm -f localtime*
    ln -vfs ../usr/share/zoneinfo/Europe/Rome localtime-copied-from
    cp -favv ../usr/share/zoneinfo/Europe/Rome localtime
  )
}

Setup_Keymap ()
{ echo "setting up keymap"
  $Debug && sleep 1
  
  cat << EOF > ${1}/etc/rc.d/rc.keymap
#!/bin/sh
# Load the keyboard map.  More maps are in /usr/share/kbd/keymaps.
if [ -x /usr/bin/loadkeys ]; then
 /usr/bin/loadkeys us.map
fi
EOF
  chmod 755 ${1}/etc/rc.d/rc.keymap
}

##################
#main
##################
$Debug && dump_options
$Debug && echo -n "Composing optistring for getopt ... "
GetoptString=$( for I in ${!MyOptions[@]}
  do
    echo -en "${I}${MyOptions[$I]%%,*}"
  done 
)
$Debug && echo $GetoptString

getopt $GetoptString $* >/dev/null 2>/dev/null || \
  usage unknown option or insufficient parameters: $*

GPO=$(getopt $GetoptString $* 2>/dev/null |tr -d "'")
set -- $GPO

$Debug && echo "Getopt Parsed Options: $*"

$Debug && echo -n "Beginning to parse options internally ... "
while [ $# -ge 1 ]
do
  $Debug && echo "parsing $1 ..."
  case $1 in
    -r|-p) if $Debug ; then echo "$1 $2"; fi; set_myoptions_value ${1#-}  $2; shift 2;;
    -h|-D|-I|-R|-T) if $Debug ; then echo "$1"; fi; set_myoptions_value ${1#-} true; shift;; 
    --) shift ;;
    *) usage $1 unknown option ;;
  esac
done

#show usahe is -h was issued
$(get_myoptions_value h) && usage

#run chooser function if no release was specified 
if [ "$(get_myoptions_value r)" = "" ] 
then
  Choose_Release
else
  Release="$(get_myoptions_value r)"
fi
URL="${SupportedReleases[$Release]}"
[ "$URL" = "" ] && usage unknown release $Release

if $Debug
then
  echo "$Release $URL"
  sleep 1
fi

#disabling chroot post install if LARCH id different to target architecture
case $Release in
  slackwarearm-14.2) TARCH="armv7l";;
  slackwarearm-current) TARCH="armv7l";;
  slackwareaarch64-current) TARCH="aarch64";;
  slackware64-14.2|slackware64-current) TARCH="x86_64" ;;
  *) echo "Unsupported release : $Release"; exit 1 ;;
esac

if [ "$LARCH" != "$TARCH" ] 
then 
  set_myoptions_value R true
  echo "Disabling chroot post install setup bacause $LARCH != $TARCH"
fi

if $(get_myoptions_value D) && [ -d ${Minirootdir}/$Release ]
then
  echo "You have chosen to skip package download/update and work with wht's already inside ${Minirootdir}/$Release"
  $Debug && sleep 1
else
  #Download or update the required packages (${Cfgdir}/pkglist.txt)
  echo "Downloading/updating packges for $Release ... "
  $Debug && sleep 1
  Get_Packages ${Pkgdir}/$Release
fi

#will now create a fresh minirot for $Release
if $(get_myoptions_value I) && [ -d ${Minirootdir}/$Release ]
then
  echo "You have chosen to skip package and proceed with post install setup in ${Minirootdir}/$Release"
  $Debug && sleep 1 
else
  Recreate_Miniroot ${Minirootdir}/$Release
fi

if ! $(get_myoptions_value R)
then
  Chroot_Setup ${Minirootdir}/$Release
fi

#creating a sample fstab
Setup_Fstab ${Minirootdir}/$Release

Setup_Basic_Networking ${Minirootdir}/$Release

Setup_Timezone ${Minirootdir}/$Release

Setup_Keymap ${Minirootdir}/$Release

#############################
#doing the other trivial post install stuff

echo "doing the other trivial post install stuff"
$Debug && sleep 1
cd ${Minirootdir}/$Release

#setting up default window manager
if [ -d etc/X11/xinit/ ]; then
   ( cd etc/X11/xinit/
     ln -vfs xinitrc.fluxbox xinitrc )
fi

sed -i 's?^#ttyS0?ttyS0?' etc/securetty

cat << EOF > etc/e2fsck.conf
# These options stop e2fsck from erroring/requiring manual intervention
# when it encounters bad time stamps on filesystems -- which happens on
# the Versatile platform because QEMU does not have RTC (real time clock)
# support.
#
[options]
        accept_time_fudge = 1
        broken_system_clock = 1
EOF

#if we have a local copy of netconnect copy that into the miniroot
#this will attempt to get dhcp addres on wired first then from any known AP in range via wifi
if [ -r ${Cfgdir}/netconnect ]
then
  cp ${Cfgdir}/netconnect usr/local/bin/
  echo "/usr/local/bin/netconnect start" >> etc/rc.d/rc.local 
fi

#copying wpa_supplicant.conf into th miniroot
cp /etc/wpa_supplicant.conf etc/wpa_supplicant.conf

#by default sshd will not allow password auth
#copying over roots authorized_keys so that you have root access via ssh
mkdir root/.ssh
chmod 700 root/.ssh
[ -r /root/.ssh/authorized_keys ] && cp -p /root/.ssh/authorized_keys root/.ssh/authorized_keys
#put a copy of MANIFEST.bz2 in root's home
wget -qN --show-progres ${URL}/MANIFEST.bz2 -O root/MANIFEST.bz2

if ! $(get_myoptions_value T)
then
  #creating the tarball
  echo "creating the miniroot tarball"
  $Debug && sleep 1
  Date="$(date +%Y%m%d)"
  tar cpJf ../${Release}_${Date}.txz *

  #Creating the release txt
  echo "creating the miniroot txt"
  $Debug && sleep 1
  ( cd ${Minirootdir} 
    cat << EOF > ${Release}_${Date}.txt
Build date        : ${Date}
root password     : $(get_myoptions_value p)
Uncompressed size : $(du -ms ${Release})
Compressed size   : $(du -ms ${Release}_${Date}.txz)
md5sum            : $(md5sum ${Release}_${Date}.txz)
EOF
  )
fi

if ! $(get_myoptions_value R)
then
  #looking if any bins broke (not belonging to the known broken list)
  echo "checking if any more bins broke in this build"
  $Debug && sleep 1
  cp  ${Cfgdir}/chroot_checks tmp
  chroot . /tmp/chroot_checks
fi
