#!/bin/sh

usage() (
	cat <<-EOF
	Template specific options can be passed to lxc-create after a '--' like this:
	    
	  lxc-create --name=NAME [-lxc-create-options] -- [-template-options]
	
	Usage: ${1} -h|--help --path=<path> --name=<name> --rootfs=<rootfs>
	                         --uidmap=<uidmap> --gidmap=<gidmap>
	                         --mirror=<mirror> [--security-mirror=<security mirror>]
	                         [--release=<release>]
	                         [--auth-key=<keyfile>]
	
	Options :
	        
	  -h, --help             print this help text
	  --path=PATH            directory where config of this VM will be kept
	  --name=NAME            hostname name of the container
	  --rootfs=ROOTFS        directory where the root filesystem can be accessed
	  --uidmap=UIDMAP        Map of user ids for the userns. Format u:0:1000000:65535
	                         See lxc-usernsexec (1) for format details.
	  --gidmap=GIDMAP        Map of group ids for the userns. Format g:0:1000000:65535
	                         See lxc-usernsexec (1) for format details.
	  --auth-key=KEYFILE     SSH public key to inject into the container as the root user.
	  --release=RELEASE      Debian release. Defaults to "stable"
	  --mirror=MIRROR        Debian mirror to use during installation.
	  --security-mirror=SECURITY_MIRROR
	                         Debian mirror to use for security updates.

	This template uses mmdebstrap to install Debian in a userns into a directory as provided by lxc-create via the
	--rootfs option. It's intended use is to create a filesystem for unprivileged containers started as root.
	EOF
	return 0
)


check_required_binary() (
	prog="${1}"
	bin="${2}"
	if ! which ${bin} > /dev/null; then
		printf "error: Need '%s' executable in PATH to execute this template.\n\n" ${bin}
		usage "${prog}"
		return 1
	fi
	return 0
)


check_required_arg() (
	prog="${1}"
	value="${2}"
	opt="${3}"
	if [ "${value}" = "" ]; then
		printf "error: %s is a required option!\n\n" "${opt}"
		usage "${prog}"
		return 1
	fi
	return 0
)


parse_args() {
	prog="${0}"
	shift

	options=$(getopt -o h -l help,path:,name:,rootfs:,mirror:,security-mirror:,auth-key:,release:,uidmap:,gidmap:,mapped-uid:,mapped-gid: -- "${@}")
	if [ $? -ne 0 ]; then
		usage "${prog}"
		exit 1
	fi
	# printf "%s\n" "${options}"

	eval set -- "${options}"

	while true; do
		case "${1}" in
			-h|--help)			usage "${prog}" && exit 1;;
			--)					shift 1; break;;
			--path)				path=${2}; shift 2;;
			--name)				name=${2}; shift 2;;
			--rootfs)			rootfs=${2}; shift 2;;
			--release)			release=${2}; shift 2;;
			--mirror)			mirror=${2}; shift 2;;
			--security-mirror)	security_mirror=${2}; shift 2;;
			--auth-key)			auth_key=${2}; shift 2;;
			--uidmap)           uidmap=${2}; shift 2;;
			--gidmap)           gidmap=${2}; shift 2;;
			--mapped-uid)		echo "Cannot run with lxc.idmap set in config. Use --uidmap/--gidmap template options instead."; exit 1; shift 2;;
			--mapped-gid)		echo "Cannot run with lxc.idmap set in config. Use --uidmap/--gidmap template options instead."; exit 1; shift 2;;
			*)					echo "programming error: found unknown opt ${1}"; exit 1; break;;
		esac
	done

	# check required args
	check_required_arg "${prog}" "${path}" "--path" || exit 1
	check_required_arg "${prog}" "${name}" "--name" || exit 1
	check_required_arg "${prog}" "${rootfs}" "--rootfs" || exit 1
	check_required_arg "${prog}" "${mirror}" "--mirror" || exit 1
	check_required_arg "${prog}" "${uidmap}" "--uidmap" || exit 1
	check_required_arg "${prog}" "${gidmap}" "--gidmap" || exit 1

	# set defaults where necessary
	release=${release:-stable}

	if ! [ -d "${rootfs}" ]; then
		printf "error: --rootfs must point to a valid directory. Got '%s'\n\n" "${rootfs}"
		usage "${prog}"
		exit 1
	fi

	if [ "${auth_key}" != "" ] && ! [ -f "${auth_key}" ]; then
		printf "error: --auth-key must point to a valid file. Got '%s'\n\n" "${auth_key}"
		usage "${prog}"
		exit 1
	fi
}


chown_mountpoint() (
	# uses $rootfs, $uidmap, $gidmap
	root_user_in_ns=$(echo $uidmap | cut -d : -f 3)
	root_group_in_ns=$(echo $gidmap | cut -d : -f 3)
	echo "Found root user ${root_user_in_ns} and group ${root_group_in_ns}"
	chown ${root_user_in_ns}:${root_group_in_ns} "${rootfs}"
	return 0
)


install_debian() (
	# uses $rootfs, $name, $release, $uidmap, $gidmap, $mirror, $security_mirror, $auth_key
	set -x

    lxc-usernsexec -m ${uidmap} -m ${gidmap} -- \
    	lxc-unshare -s 'MOUNT|PID|UTSNAME|IPC' -- \
    	mmdebstrap \
    	--variant minbase \
    	--mode unshare \
    	--include 'init ifupdown locales dialog isc-dhcp-client netbase net-tools iproute2 openssh-server python3 vim' \
    	--customize-hook 'echo '${name}' > $1/etc/hostname' \
        --customize-hook 'cp /etc/timezone $1/etc/timezone' \
        ${auth_key:+--customize-hook 'mkdir -p $1/root/.ssh' --customize-hook 'cat '${auth_key}' > $1/root/.ssh/authorized_keys'} \
        $release "${rootfs}" \
        "${mirror}" ${security_mirror:+"deb ${security_mirror} ${release}-security main contrib non-free"}
)


write_userns_to_config() (
	# uses $path, $uidmap, $gidmap
	printf "lxc.idmap = %s\n" "$(printf "%s" "${uidmap}" | tr ':' ' ')" >> "${path}/config"
	printf "lxc.idmap = %s\n" "$(printf "%s" "${gidmap}" | tr ':' ' ')" >> "${path}/config"
)


parse_args "${0}" "${@}"

check_required_binary "${0}" mmdebstrap || exit 1
check_required_binary "${0}" lxc-usernsexec || exit 1

chown_mountpoint || exit 1

install_debian

write_userns_to_config