#!/bin/bash -eu

#
# template script for generating Plamo Linux container for LXC
#

#
# lxc: linux Container library

# Authors:
# KATOH Yasufumi <karma@jazz.email.ne.jp>
# TAMUKI Shoichi <tamuki@linet.gr.jp>

# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.

# This library 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
# Lesser General Public License for more details.

# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

# ref. https://github.com/Ponce/lxc-slackware/blob/master/lxc-slackware
#      lxc-ubuntu script

# Detect use under userns (unsupported)
for arg in "$@"; do
    [ "$arg" = "--" ] && break
    if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then
        echo "This template can't be used for unprivileged containers." 1>&2
        echo "You may want to try the \"download\" template instead." 1>&2
        exit 1
    fi
done

# Make sure the usual locations are in PATH
export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin

[ -r /etc/default/lxc ] && . /etc/default/lxc

download_plamo() {
  # check the mini plamo was not already downloaded
  if ! mkdir -p $ptcache ; then
    echo "Failed to create '$ptcache' directory."
    return 1
  fi
  # download a mini plamo into a cache
  echo "Downloading Plamo-$release minimal..."
  cd $ptcache
  case $DLSCHEME in
      http|https) depth=2 ;;
      ftp) depth=3 ;;
  esac
  rej=${IGNOREPKGS%% *} ; [ -n "$rej" ] && rej="$rej-*"
  if [ `echo $IGNOREPKGS | wc -w` -gt 1 ] ; then
    for p in ${IGNOREPKGS#* } ; do rej="$rej,$p-*" ; done
  fi
  for i in $CATEGORIES ; do
    wget -nv -e robots=off -r -l $depth -nd -A .tgz,.txz,.tzst -R "$rej" \
        -I $MIRRORPATH/Plamo-$release/$arch/plamo/$i \
        -X $MIRRORPATH/Plamo-$release/$arch/plamo/$i/old \
        $DLSCHEME://$MIRRORSRV$MIRRORPATH/Plamo-$release/$arch/plamo/$i
    if [ $? -ne 0 ] ; then
      echo "Failed to download the rootfs, aborting."
      return 1
    fi
  done
  for i in $EXTRACTGRS ; do
    wget -nv -e robots=off -r -l $depth -nd -A .tgz,.txz,.tzst -R "$rej" \
        -I $MIRRORPATH/Plamo-$release/$arch/contrib/$i \
        -X $MIRRORPATH/Plamo-$release/$arch/contrib/$i/old \
        $DLSCHEME://$MIRRORSRV$MIRRORPATH/Plamo-$release/$arch/contrib/$i
    if [ $? -ne 0 ] ; then
      echo "Failed to download the rootfs, aborting."
      return 1
    fi
  done
  for p in $ADDONPKGS ; do
    wget -nv -e robots=off -r -l $depth -nd -A "`basename $p`-*" \
        -I $MIRRORPATH/Plamo-$release/$arch/`dirname $p` \
        -X $MIRRORPATH/Plamo-$release/$arch/`dirname $p`/old \
        $DLSCHEME://$MIRRORSRV$MIRRORPATH/Plamo-$release/$arch/`dirname $p`
    if [ $? -ne 0 ] ; then
      echo "Failed to download the rootfs, aborting."
      return 1
    fi
  done
  mv $ptcache $dlcache
  echo "Download complete."
  return 0
}

copy_plamo() {
  # make a local copy of the mini plamo
  echo "Copying $rtcache to $rootfs..."
  mkdir -p $rootfs
  find $rtcache -mindepth 1 -maxdepth 1 -exec cp -a {} $rootfs \; || return 1
  return 0
}

install_plamo() {
  mkdir -p @LOCALSTATEDIR@/lock/subsys
  (
    if ! flock -n 9 ; then
      echo "Cache repository is busy."
      return 1
    fi
    echo "Checking cache download in $dlcache..."
    if [ ! -d $dlcache ] ; then
      if ! download_plamo ; then
        echo "Failed to download plamo $release base packages."
        return 1
      fi
    fi
    # install "installpkg" command temporarily with static linked tar
    # command into the lxc cache directory to keep the original uid/
    # gid of files/directories.
    echo "Installing 'installpkg' command into $dlcache/sbin..."

    if [ $majorver -ge 7 ]; then
      pkgtool="pkgtools"
      pkgtool=$( cd $dlcache ; ls "$pkgtool"* )
      pkgtool=${pkgtool%%-*}
      pkgtool=${pkgtool:0:-1}
    else
      pkgtool="hdsetup"
      LANG=C
    fi

    ( cd $dlcache ; tar xpf "$pkgtool"*-*.t*z* ; rm -rf tmp usr var )
    if [ $pkgtool = "pkgtools" ]; then
      ( cd $dlcache/sbin
        mv installer_new installer
        pkg_scripts="installpkg installpkg2 installpkg2.mes makepkg updatepkg removepkg"
        for s in $pkg_scripts
        do
          mv new_"$s" $s
        done
        cd installer
        ln -sf zstd-* zstd
      )
    fi

    sed -i "/ldconfig/!s@/sbin@$dlcache&@g" $dlcache/sbin/installpkg*
    PATH=$dlcache/sbin:$PATH
    echo "Installing packages to $rtcache..."
    if [ ! -d $rtcache ] ; then
      mkdir -p $rtcache
      for p in `ls -cr $dlcache/*.t*z*` ; do
        installpkg -root $rtcache -priority ADD $p
      done
    fi
    echo "Copy $rtcache to $rootfs..."
    if ! copy_plamo ; then
      echo "Failed to copy rootfs."
      return 1
    fi
    return 0
  ) 9> @LOCALSTATEDIR@/lock/subsys/lxc-plamo
}

configure_plamo6() {
  # suppress log level output for udev
  sed -i 's/="err"/=0/' $rootfs/etc/udev/udev.conf
  # /etc/fstab
  cat <<- "EOF" > $rootfs/etc/fstab
	none            /proc   proc    defaults        0 0
	none            /sys    sysfs   defaults        0 0
	none            /dev    tmpfs   defaults        0 0
	none            /tmp    tmpfs   defaults        0 0
	none            /dev/pts        devpts  gid=5,mode=620  0 0
	none            /proc/bus/usb   usbfs   noauto  0 0
	none            /var/lib/nfs/rpc_pipefs rpc_pipefs  defaults   0 0
	EOF
  # /etc/inittab
  cat <<- "EOF" | patch $rootfs/etc/inittab
	32,33c32,33
	< # What to do when power fails (shutdown to single user).
	< pf::powerfail:/sbin/shutdown -f +5 "THE POWER IS FAILING"
	---
	> # What to do when power fails (shutdown).
	> pf::powerfail:/sbin/shutdown -h +0 "THE POWER IS FAILING"
	47a48
	> 1:1235:respawn:/sbin/agetty 38400 console
	52,53d52
	< c5:1235:respawn:/sbin/agetty 38400 tty5 linux
	< c6:12345:respawn:/sbin/agetty 38400 tty6 linux
	EOF
  # configure the network using the dhcp
  echo "DHCP" > $rootfs/var/run/inet1-scheme

  # delete unnecessary process from rc.S
  ed - $rootfs/etc/rc.d/rc.S <<- "EOF"
	/^mount -w -n -t proc/;/^mkdir \/dev\/shm/-1d
	/^mknod \/dev\/null/;/^# Clean \/etc\/mtab/-2d
	/^# copy the rules/;/^# Set the hostname/-1d
	/^# Check the integrity/;/^# Clean up temporary/-1d
	w
	EOF

  # delete unnecessary process from rc.M
  ed - $rootfs/etc/rc.d/rc.M <<- "EOF"
	/^# Screen blanks/;/^# Initialize ip6tables/-1d
	/^# Initialize sysctl/;/^echo "Starting services/-1d
	/^sync/;/^# All done/-1d
	w
	EOF

  # delete unnecessary process from rc.6
  ed - $rootfs/etc/rc.d/rc.6 <<- "EOF"
	/^# Save system time/;/^# Unmount any remote filesystems/-1d
	/^# Turn off swap/;/^# See if this is a powerfail situation/-1d
	w
	EOF

  # /etc/rc.d/rc.inet1.tradnet
  head -n-93 $rootfs/sbin/netconfig.tradnet > /tmp/netconfig.rconly
  cat <<- EOF >> /tmp/netconfig.rconly
	PCMCIA=n
	RC=$rootfs/etc/rc.d/rc.inet1.tradnet
	IFCONFIG=sbin/ifconfig
	ROUTE=sbin/route
	INET1SCHEME=var/run/inet1-scheme
	IPADDR=127.0.0.1
	NETWORK=127.0.0.0
	DHCPCD=usr/sbin/dhclient
	LOOPBACK=y
	make_config_file
	EOF
  rm -f $rootfs/etc/rc.d/rc.inet1.tradnet
  sh /tmp/netconfig.rconly
  rm -f /tmp/netconfig.rconly
  sed -i '/cmdline/s/if/& false \&\&/' $rootfs/etc/rc.d/rc.inet1.tradnet
  # /etc/rc.d/rc.inet2
  sed -i '/rpc.mountd/s/^/#/' $rootfs/etc/rc.d/rc.inet2
  sed -i '/modprobe/s/^/#/' $rootfs/etc/rc.d/rc.inet2
  # configure to start only the minimum of service
  chmod 644 $rootfs/etc/rc.d/init.d/saslauthd
  chmod 644 $rootfs/etc/rc.d/init.d/open-iscsi
  rm -f $rootfs/etc/rc.d/init.d/postfix
  rm -f $rootfs/var/log/initpkg/shadow
  return 0
}

configure_plamo7() {
    # fstab
    cat <<- "EOF" >> $rootfs/etc/fstab
	proc            /proc   proc    defaults        0 0
	tmpfs           /run    tmpfs   defaults        0 0
	sysfs           /sys    sysfs   defaults        0 0
	tmpfs           /tmp    tmpfs   defaults        0 0
	devpts          /dev/pts        devpts  gid=5,mode=620  0 0
	usbfs           /proc/bus/usb   usbfs   noauto  0 0
	EOF

    # inittab
    sed -i -e '/^1:2345/i c:1235:respawn:/sbin/agetty console 38400'\
        -e '/^ca:/a pf::powerfail:/sbin/shutdown -h +0 "THE POWER IS FAILING"' \
        -e '/^4:2345/d' \
        -e '/^5:2345/d' \
        -e '/^6:2345/d' $rootfs/etc/inittab

    # tweak init script on startup
    remove_init_S="S05modules S10eudev S20swap S30checkfs S35setclock S50eudev_retry S70console"
    for f in $remove_init_S
    do
      rm -f $rootfs/etc/rc.d/rcS.d/"$f"
    done

    # remove init script in runlevel3
    remove_init="rc3.d/S30sshd
                 rc6.d/K30sshd rc6.d/K35setclock rc6.d/S65swap rc6.d/S70mountfs
                 rc0.d/K30sshd rc0.d/K35setclock rc0.d/S65swap rc0.d/S70mountfs"
    for f in $remove_init
    do
      rm -f $rootfs/etc/rc.d/"$f"
    done

    # Tweak rc script
    sed -i -e '/wait_for_user/d' \
        -e '/Press Enter to/d' \
        $rootfs/etc/rc.d/init.d/rc

    # network
    cat <<- "EOF" > $rootfs/etc/sysconfig/ifconfig.eth0
	ONBOOT="yes"
	IFACE="eth0"
	SERVICE="dhclient"
	EOF

    # remove initpkg that do not execute on containers
    noexec="shadow netconfig7 eudev openssh"
    for f in $noexec
    do
      rm -f $rootfs/var/log/initpkg/"$f"
    done
}

configure_plamo() {
  # set the hostname
  echo "$name" > $rootfs/etc/HOSTNAME
  # set minimal hosts
  echo "127.0.0.1 localhost $name" > $rootfs/etc/hosts
  # localtime (JST)
  ln -s ../usr/share/zoneinfo/Asia/Tokyo $rootfs/etc/localtime
  # disable pam_loginuid.so in /etc/pam.d/login
  sed -i '/pam_loginuid/s/^/#/' $rootfs/etc/pam.d/login
  if [ $majorver -ge 7 ]; then
    configure_plamo7
  else
    configure_plamo6
  fi
}

copy_configuration() {
  ret=0
  cat <<- EOF >> $path/config || let ret++
	lxc.uts.name = $name
	lxc.arch = $arch
	EOF
  if [ -f "@LXCTEMPLATECONFIG@/plamo.common.conf" ] ; then
    cat <<- "EOF" >> $path/config || let ret++
	
	lxc.include = @LXCTEMPLATECONFIG@/plamo.common.conf
	EOF
  fi
  if [ $ret -ne 0 ] ; then
    echo "Failed to add configuration."
    return 1
  fi
  return 0
}

post_process() {
  # nothing do in Plamo Linux
  true
}

do_bindhome() {
  # bind-mount the user's path into the container's /home
  h=`getent passwd $bindhome | cut -d: -f6`
  mkdir -p $rootfs/$h
  echo "lxc.mount.entry = $h $rootfs/$h none bind 0 0" >> $path/config
  # copy /etc/passwd, /etc/shadow, and /etc/group entries into container
  if ! pwd=`getent passwd $bindhome` ; then
    echo "Warning: failed to copy password entry for $bindhome."
  else
    echo $pwd >> $rootfs/etc/passwd
  fi
  echo `getent shadow $bindhome` >> $rootfs/etc/shadow
}

cleanup() {
  [ -d $dlcache -a -d $rtcache ] || return 0
  # lock, so we won't purge while someone is creating a repository
  (
    if ! flock -n 9 ; then
      echo "Cache repository is busy."
      return 1
    fi
    echo "Purging the download cache..."
    rm -rf --one-file-system $dlcache $rtcache || return 1
    echo "Done."
    return 0
  ) 9> @LOCALSTATEDIR@/lock/subsys/lxc-plamo
}

usage() {
  cat <<- EOF
	$prog [-h|--help] -p|--path=<path> -n|--name=<name> --rootfs=<rootfs>
	          [-c|--clean] [-r|--release=<release>] [-a|--arch=<arch>]
	          [-b|--bindhome=<user>]
	release: $release
	arch: x86 or x86_64: defaults to host arch
	bindhome: bind <user>'s home into the container
	EOF
}

prog=`basename $0`
path="" ; name="" ; rootfs=""
clean=0
release=${release:-7.x}
arch=`uname -m | sed 's/i.86/x86/'` ; hostarch=$arch
bindhome=""
sopts=hp:n:cr:a:b:
lopts=help,path:,name:,rootfs:,clean,release:,arch:,bindhome:
if ! options=`getopt -o $sopts -l $lopts -- "$@"` ; then
  usage
  exit 1
fi
eval set -- "$options"
while true ; do
  case "$1" in
  -h|--help) usage && exit 0 ;;
  -p|--path) path=$2 ; shift 2 ;;
  -n|--name) name=$2 ; shift 2 ;;
  --rootfs) rootfs=$2 ; shift 2 ;;
  -c|--clean) clean=1 ; shift 1 ;;
  -r|--release) release=$2 ; shift 2 ;;
  -a|--arch) arch=$2 ; shift 2 ;;
  -b|--bindhome) bindhome=$2 ; shift 2 ;;
  --) shift 1 ; break ;;
  *) break ;;
  esac
done
if [ $clean -eq 1 -a -z "$path" ] ; then
  cleanup || exit 1
  exit 0
fi
if [ $hostarch == "x86" -a $arch == "x86_64" ] ; then
  echo "Can't create x86_64 container on x86."
  exit 1
fi
if [ -z "$path" ] ; then
  echo "'path' parameter is required."
  exit 1
fi
if [ -z "$name" ] ; then
  echo "'name' parameter is required."
  exit 1
fi
if [ `id -u` -ne 0 ] ; then
  echo "This script should be run as 'root'."
  exit 1
fi

majorver=${release%.*}
if [ $majorver -ge 7 ]; then
    CATEGORIES=${CATEGORIES-"00_base"}
    ADDONPKGS=${ADDONPKGS-""}
else
    CATEGORIES=${CATEGORIES-"00_base 01_minimum"}
    ADDONPKGS=${ADDONPKGS-"`echo contrib/Hamradio/{morse,qrq}`"}
fi

DLSCHEME=${DLSCHEME:-"https"}
MIRRORSRV=${MIRRORSRV:-"repository.plamolinux.org"}
MIRRORPATH=${MIRRORPATH:-"/pub/linux/Plamo"}
CATEGORIES=${CATEGORIES-"00_base 01_minimum"}
EXTRACTGRS=${EXTRACTGRS-""}
IGNOREPKGS=${IGNOREPKGS-"grub kernel lilo linux_firmware microcode_ctl
    linux_firmwares cpufreqd cpufrequtils gpm ntp kmod kmscon"}
ADDONPKGS=${ADDONPKGS-"`echo contrib/Hamradio/{morse,qrq}`"}

cache="${LXC_CACHE_PATH:-@LOCALSTATEDIR@/cache/lxc}"
ptcache=$cache/partial-${prog##*-}-$release-$arch
dlcache=$cache/cache-${prog##*-}-$release-$arch
rtcache=$cache/rootfs-${prog##*-}-$release-$arch
if [ -z "$rootfs" ] ; then
  if grep -q "^lxc.rootfs.path" $path/config ; then
    rootfs=`awk -F= '/^lxc.rootfs.path =/{ print $2 }' $path/config`
  else
    rootfs=$path/rootfs
  fi
fi
if ! install_plamo ; then
  echo "Failed to install plamo $release."
  exit 1
fi
if ! configure_plamo ; then
  echo "Failed to configure plamo $release for a container."
  exit 1
fi
if ! copy_configuration ; then
  echo "Failed to write configuration file."
  exit 1
fi
post_process
if [ -n "$bindhome" ] ; then
  do_bindhome
fi
if [ $clean -eq 1 ] ; then
  cleanup || exit 1
  exit 0
fi