#!/bin/bash

#
# template script for generating Void linux container for LXC
#

#
# lxc: linux Container library

# Authors:
# Juan RP <xtraeme@gmail.com>

# 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

# 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

# defaults
arch=$(uname -m)
default_path="/var/lib/lxc"
LXC_TEMPLATE_CONFIG="/usr/share/lxc/config"

# Install 'base-container' by default
base_packages=('base-container' 'dhcpcd' 'openssh' 'iproute2')
declare -a additional_packages

# split comma-separated string into an array
# ${1} - string to split
# ${2} - separator (default is ",")
# ${result} - result value on success
split_string() {
    local ifs=${IFS}
    IFS="${2:-,}"
    read -a result < <(echo "${1}")
    IFS=${ifs}
    return 0
}

# write container configuration files
copy_configuration() {
    path=$1
    rootfs=$2
    hostname=$3
    arch=$4

    # if there is exactly one veth network entry, make sure it has an
    # associated hwaddr.
    nics=`grep -e '^lxc\.net\.0\.type[ \t]*=[ \t]*veth' $path/config | wc -l`
    if [ $nics -eq 1 ]; then
        grep -q "^lxc.net.0.hwaddr" $path/config || sed -i -e "/^lxc\.net\.0\.type[ \t]*=[ \t]*veth/a lxc.net.0.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" $path/config
    fi

    ## Add all the includes
    echo "" >> $path/config
    echo "# Common configuration" >> $path/config
    if [ -e "${LXC_TEMPLATE_CONFIG}/void.common.conf" ]; then
        echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/void.common.conf" >> $path/config
    fi

    ## Add the container-specific config
    echo "" >> $path/config
    echo "# Container specific configuration" >> $path/config
    grep -q "^lxc.rootfs.path" $path/config 2> /dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config

    cat <<EOF >> $path/config
lxc.uts.name = $hostname
lxc.arch = $arch
EOF

    if [ $? -ne 0 ]; then
        echo "Failed to add configuration"
        return 1
    fi

    return 0
}

# install packages within container chroot
install_void() {
    path=$1
    rootfs=$2
    hostname=$3
    arch=$4

    [ "${arch}" != "$(uname -m)" ] && different_arch=1

    if [ -n "${different_arch}" ]; then
	    export XBPS_ARCH=${arch}
    fi

    # set the hostname
    mkdir -p $rootfs/etc
    echo $hostname > $rootfs/etc/hostname

    # missing device nodes
    mkdir -p $rootfs/dev
    mknod -m 666 "$rootfs/dev/null" c 1 3

    echo "Installing ${base_packages[@]}"
    mkdir -p ${rootfs}/var/db/xbps/keys

    # base64 encoded Void RSA public key
    vkb64=$(mktemp || return 1)
    cat > ${vkb64} << EOF
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBV
QkxJQyAiLS8vQXBwbGUgQ29tcHV0ZXIvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5h
cHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAi
Pgo8ZGljdD4KCTxrZXk+cHVibGljLWtleTwva2V5PgoJPGRhdGE+TFMwdExTMUNSVWRKVGlCUVZV
Sk1TVU1nUzBWWkxTMHRMUzBLVFVsSlEwbHFRVTVDWjJ0eGFHdHBSemwzTUVKQlVVVkdRVUZQUTBG
bk9FRk5TVWxEUTJkTFEwRm5SVUYyY2xONlFscE5kbWQyVDBOSk0wRllZazlxWVFveWNrdFNhMHBU
VkUwell5OUZhbFJKWjBOblJGaG5kVzA1TTBKUVEzUlpPRTFqUmxadlExVTBUMmxZU0VkbVZHMXhN
emxDVms1d1RIWk1TRXc1UzJzeENuQXlOemhUUW1oWVZrOTBZa0l5UlZadFJFdHVkbVpKUkVWVWJH
Uk1SM3BsTjNKYVRsSktaSFIxVGpKdFdpOVVWbkpWUWpsVE1IbFJZeXRKZFdZMGFIWUtNeXRFT1Rk
V1NXUlVTa2hCTjBGVGNqQTBNamh3Y0VWSFNrZDNVMU5vV1RKWVNtMDVSRFZKTUVWMVIxSlhZekUw
VFVWSE4yUkpTMHBwV1dsTk1HNUZOQXAwV1c4eUwzWklORWxHVkVoa2JsWkJNMmRaYVZwNVJHNWlk
VU5CVWk4NFJWTm1WVlJWTVROVFRrTlBaR0oxWkdZelJEVkNZM2tyVldsTlJFcEpNMWxsQ2pSTlJr
dENjbFE1V21oYUswZHpXRUphV1RRNE1teHhhVnBwTmtOTU5YQjBZemxKVVVabU9DOWxTMXBoT0dw
aGRHdHBWa1pXWjNKTFpVNVNhazlVZUU0S1psZFRkVEp1YTNoSFRsZ3JZbWhZV1hSb2FVZFhiVXBG
V1RoalEwRlFlVVpPSzB4Mk5WSmxkRXN5TlRablpHTmlNbk5yYlVWeFpXWjJNbnBRUXl0M1ZncFhR
bUprU0RWaVJEUmlXbXB1TUU0MldtdzRNWEoyTlZKNlJIWnVkbVlyZGtReE5HRkdWV0phT0ZGR2NY
VTNOVkJpVERSM05tMVpUVFJzWkUwdlp6QlNDalpPV0VVNFFYbzVRbmQ0TW5SRVpsbGxTM1YxZEhj
eFJYQlFiVEpaZGtaNVZGVmlNV052ZVVGMVZFZFNlVUZoY0RGVlZFaDJaemxzYUZCSlNtMW9SbEVL
U2pWclEyY3hjVVEzUVRNeFYyd3dVbXh1WlRab1owZHZNRnBhVGtvMVkwcE5MM1l2ZWxOVVMwcGpk
VVpuZDI4M1NEQm9UMGRwYkRaRVptODRPVUkwYWdwSE9UWkJRM2xRVXl0RVZrdFFSbGhTV1hkcUww
RnJZa2h3WVZFeVpqRkdUVUZ2VTNCQ2NYVkVjVWhvTTNWcmF6Y3hTMWcyYWpFNWREQnBSamhFVVV4
eUNuWjBSbE5UWkVscVJFRXdNbXgzWlZZNVRtRlJjRmR6UTBGM1JVRkJVVDA5Q2kwdExTMHRSVTVF
SUZCVlFreEpReUJMUlZrdExTMHRMUW89PC9kYXRhPgoJPGtleT5wdWJsaWMta2V5LXNpemU8L2tl
eT4KCTxpbnRlZ2VyPjQwOTY8L2ludGVnZXI+Cgk8a2V5PnNpZ25hdHVyZS1ieTwva2V5PgoJPHN0
cmluZz5Wb2lkIExpbnV4PC9zdHJpbmc+CjwvZGljdD4KPC9wbGlzdD4K
EOF
    base64 -d ${vkb64} > ${rootfs}/var/db/xbps/keys/60\:ae\:0c\:d6\:f0\:95\:17\:80\:bc\:93\:46\:7a\:89\:af\:a3\:2d.plist
    rm -f ${vkb64}

    mkdir -p ${rootfs}/usr/share/xbps.d
    echo "repository=http://repo-default.voidlinux.org/current" > ${rootfs}/usr/share/xbps.d/00-repository-main.conf

    if ! xbps-install ${xbps_cachedir:+ -c $xbps_cachedir} \
	    ${xbps_config:+-C $xbps_config} -r "${rootfs}" \
	    -Sy ${base_packages[@]}; then
        echo "Failed to install container packages"
        return 1
    fi

    grep nameserver /etc/resolv.conf > "${rootfs}/etc/resolv.conf"
    # Enable agetty on /dev/console
    ln -s /etc/sv/agetty-console $rootfs/etc/runit/runsvdir/default/
    # Enable dhcpcd and sshd.
    ln -s /etc/sv/dhcpcd-eth0 $rootfs/etc/runit/runsvdir/default/
    ln -s /etc/sv/sshd $rootfs/etc/runit/runsvdir/default/
    # Copy host /etc/localtime
    if [ -f /etc/localtime ]; then
        cat /etc/localtime > $rootfs/etc/localtime
    fi

    echo "root:root" | chroot ${rootfs} chpasswd -c SHA512
    echo
    echo "Root password is 'root', please change!"
    echo

    return 0
}

usage() {
    cat <<EOF
usage:
    ${1} -n|--name=<container_name>
        [-P|--packages=<pkg1,pkg2,...>] [-p|--path=<path>] [-h|--help]
Mandatory args:
  -n,--name         container name, used to as an identifier for that container from now on
Optional args:
  -p,--path         path to where the container rootfs will be created, defaults to ${default_path}/rootfs. The container config will go under ${default_path} in that case
  -P,--packages     preinstall additional packages, comma-separated list
  -c,--config       use specified xbps config when installing container packages
     --cachedir     XBPS cache directory to store downloaded packages
  -a,--arch         use specified architecture instead of host's architecture
  -r,--root_passwd  set container root password
  -h,--help         print this help
EOF
    return 0
}

options=$(getopt -o hp:P:e:n:c:a:l:t:r: -l help,rootfs:,path:,packages:,name:,config:,cachedir:,arch:,root_passwd: -- "${@}")
if [ ${?} -ne 0 ]; then
    usage $(basename ${0})
    exit 1
fi
eval set -- "${options}"

while true
do
    case "${1}" in
    -h|--help)          usage ${0} && exit 0;;
    -p|--path)          path=${2}; shift 2;;
    -n|--name)          name=${2}; shift 2;;
    --rootfs)           rootfs_path=${2}; shift 2;;
    -P|--packages)      additional_packages=${2}; shift 2;;
    -c|--config)        xbps_config=${2}; shift 2;;
    --cachedir)		xbps_cachedir=${2}; shift 2;;
    -a|--arch)          arch=${2}; shift 2;;
    -r|--root_passwd)   root_passwd=${2}; shift 2;;
    --)             shift 1; break ;;
    *)              break ;;
    esac
done

if [ -z "${name}" ]; then
    echo "missing required 'name' parameter"
    exit 1
fi

type xbps-install >/dev/null 2>&1
if [ ${?} -ne 0 ]; then
    echo "'xbps-install' command is missing, download xbps from http://repo-default.voidlinux.org/static/"
    exit 1
fi

if [ -z "${path}" ]; then
    path="${default_path}/${name}"
fi

if [ "${EUID}" != "0" ]; then
    echo "This script should be run as 'root'"
    exit 1
fi

if [ -z "${rootfs_path}_path" ]; then
    rootfs_path="${path}/rootfs"
fi
config_path="${default_path}/${name}"

revert() {
    echo "Interrupted, cleaning up"
    lxc-destroy -n "${name}"
    rm -rf "${path}/${name}"
    rm -rf "${default_path}/${name}"
    exit 1
}

trap revert SIGHUP SIGINT SIGTERM

copy_configuration $path $rootfs_path $name $arch
if [ ${?} -ne 0 ]; then
    echo "failed to write configuration file"
    rm -rf "${config_path}"
    exit 1
fi

if [ ${#additional_packages[@]} -gt 0 ]; then
    split_string ${additional_packages}
    base_packages+=(${result[@]})
fi

mkdir -p "${rootfs_path}"
install_void $path $rootfs_path $name $arch
if [ ${?} -ne 0 ]; then
    echo "failed to install Void Linux"
    rm -rf "${config_path}" "${path}"
    exit 1
fi

cat << EOF
Void container ${name} is successfully created! The configuration is
stored in ${config_path}/config. Please refer to https://docs.voidlinux.org
for information about configuring your Void installation.
EOF
