#! /bin/bash
#########################################################################
#									#
#	lxcu-create is automatically generated,				#
#		please do not modify!					#
#									#
#########################################################################

#########################################################################
#									#
# Author: Copyright (C) 2020-2025  Mark Grant				#
#									#
# Released under the GPLv3 only.					#
# SPDX-License-Identifier: GPL-3.0-only					#
#									#
# Purpose:								#
# A script to create linux containers.					#
#									#
# Syntax:	See usage()						#
#									#
# Exit codes used:-							#
# Bash standard Exit Codes:	0 - success				#
#				1 - general failure			#
# User-defined exit code range is 64 - 113				#
#	C/C++ Semi-standard exit codes from sysexits.h range is 64 - 78	#
#		EX_USAGE	64	command line usage error	#
#		EX_DATAERR	65	data format error		#
#		EX_NOINPUT	66	cannot open input		#
#		EX_NOUSER	67	addressee unknown		#
#		EX_NOHOST	68	host name unknown		#
#		EX_UNAVAILABLE	69	service unavailable		#
#		EX_SOFTWARE	70	internal software error		#
#		EX_OSERR	71	system error (e.g., can't fork)	#
#		EX_OSFILE	72	critical OS file missing	#
#		EX_CANTCREAT	73	can't create (user) output file	#
#		EX_IOERR	74	input/output error		#
#		EX_TEMPFAIL	75	temp failure; user is invited	#
#					to retry			#
#		EX_PROTOCOL	76	remote error in protocol	#
#		EX_NOPERM	77	permission denied		#
#		EX_CONFIG	78	configuration error		#
#	User-defined (here) exit codes range 79 - 113:			#
#		None							#
#									#
# Further Info:								#
#									#
#########################################################################


set -o pipefail


##################
# Init variables #
##################
readonly version=1.4.0				# Script version
readonly packageversion=1.3.4		# Package version

readonly etclocation=/etc/lxcu		# Path to lxcu config directory

obs_rel=""	# OBS Release number (Debian)

# Ensure environment variables are set.
export XDG_RUNTIME_DIR="/run/user/$UID"
export DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/bus"


#############
# Functions #
#############

# -h --help output.
# No parameters
# No return value
usage()
{
cat << EOF
Usage is:-
$(basename "$0") {-h|-V}
$(basename "$0") [-d DISTRIBUTION] [-r RELEASE] -t CONTAINER_TYPE CONTAINER_NAME
Usage is:-
$(basename "$0") [options]
	-d or --dist 'distribution-name'
	-h or --help Display this help information
	-r or --rel 'release-name'
	-t or --type 'type-name' e.g. basic or dev.....
	-V or --version Print script version information
EOF
}

# Standard function to emit messages depending on various parameters.
# Parameters -	$1 What:-	The message to emit.
#		$2 Where:-	stdout == 0
#				stderr == 1
# No return value.
output()
{
	if (( !$2 )); then
		printf "%s\n" "$1"
	else
		printf "%s\n" "$1" 1>&2
	fi
}

# Standard function to tidy up and return exit code.
# Parameters - 	$1 is the exit code.
# No return value.
script_exit()
{
	exit "$1"
}

# Standard function to test command error and exit if non-zero.
# Parameters - 	$1 is the exit code, (normally $? from the preceeding command).
# No return value.
std_cmd_err_handler()
{
	if (( $1 )); then
		if [[ -n "$name" ]]; then
			if (( $(lxc-ls -1 --running --filter="^$name$" \
				| wc -l) )); then
				lxc-stop -n "$name"
			fi
			if (( $(lxc-ls -1 --filter="^$name$" | wc -l) )); then
				lxc-destroy -n "$name"
			fi
		fi
		script_exit "$1"
	fi
}

# Standard trap exit function.
# No parameters.
# No return value.
# shellcheck disable=SC2317  # Do not warn about unreachable commands in trap
# functions, they are legitimate.
trap_exit()
{
	local -i exit_code=$?
	local msg

	msg="Script terminating with exit code $exit_code due to trap received."
	output "$msg" 1
	std_cmd_err_handler "$exit_code"
}

# Setup trap
trap trap_exit SIGHUP SIGINT SIGQUIT SIGTERM

# Debian / Raspbian only. Convert the usual release string into an OBS release
# number using debian-releases.conf.
# Parameters - None.
# No return value.
determine_obs_release()
{
	local input=()
	local msg
	local oldIFS=$IFS

	IFS="="

	if [[ ! -e "$etclocation/debian-releases.conf" \
		|| ! -f "$etclocation/debian-releases.conf" \
		|| ! -r "$etclocation/debian-releases.conf" ]]; then
		msg="$etclocation/debian-releases.conf must exist, be a file"
		msg+=" and be readable."
		output "$msg" 1
		std_cmd_err_handler 66
	fi

	exec 4<"$etclocation/debian-releases.conf"
	std_cmd_err_handler $?
	while read -u4 -ra input; do
		if [[ "${input[0]}" == "$rel" ]]; then
			obs_rel=${input[1]}
			break
		fi
	done
	exec 4<&-

	IFS=$oldIFS

	if [[ -z $obs_rel ]]; then
		std_cmd_err_handler 78
	fi
}

# Process the config file just for the parameters of interest.
# Parameters - None
# No return value.
proc_config_file()
{
	local input=()
	local msg
	local oldIFS=$IFS

	IFS="="

	if [[ ! -e "$etclocation/lxcu.conf" || ! -f "$etclocation/lxcu.conf" \
		|| ! -r "$etclocation/lxcu.conf" ]]; then
		msg="$etclocation/lxcu.conf must exist, be a file and be"
		msg+=" readable."
		output "$msg" 1
		std_cmd_err_handler 66
	fi

	exec 3<"$etclocation/lxcu.conf"
	std_cmd_err_handler $?
	while read -u3 -ra input; do
		case ${input[0]} in
		dist)
			dist=${input[1]}
			;;
		rel)
			rel=${input[1]}
			determine_obs_release
			;;
		contlocation)
			contlocation=$(echo "${input[1]}" | envsubst)
			;;
		bridge)
			bridge=${input[1]}
			;;
		vni)
			vni=${input[1]}
			;;
		gitemail)
			gitemail=${input[1]}
			;;
		gitname)
			gitname=${input[1]}
			;;
		localelang)
			localelang=${input[1]}
			;;
		localex11keymap)
			localex11keymap=${input[1]}
			;;
		esac
	done
	exec 3<&-

	IFS=$oldIFS
}

# Process command line arguments with GNU getopt.
# Parameters -	$1 is the command line.
# No return value.
proc_CL()
{
	local GETOPTTEMP
	local msg
	local tmp

	tmp="getopt -o d:hr:t:V --long dist:,help,rel:,type:,version"
	GETOPTTEMP=$($tmp -n "$0" -- "$@")
	std_cmd_err_handler $?

	eval set -- "$GETOPTTEMP"
	std_cmd_err_handler $?

	while true; do
		case "$1" in
		-d|--dist)
			dist=$2
			shift 2
			;;
		-h|--help)
			usage
			shift
			script_exit 0
			;;
		-r|--rel)
			rel=$2
			determine_obs_release
			shift 2
			;;
		-t|--type)
			type=$2
			shift 2
			;;
		-V|--version)
			printf "%s Script version %s\n" "$0" $version
			printf "%s Package version %s\n" "$0" $packageversion
			shift
			script_exit 0
			;;
		--)	shift
			break
			;;
		*)	output "Internal error." 1
			std_cmd_err_handler 64
			;;
		esac
	done

	# The mandatory and only non-option argument must be the container name
	if (( $# == 1 )); then
		name=$1
	else
		output "One argument must be specified, the container name." 1
		std_cmd_err_handler 64
	fi

	# Target container must not exist.
	if (( $(lxc-ls -1 --filter="^$name$" | wc -l) )); then
		msg="Target container $name must not exist."
		output "$msg" 1
		std_cmd_err_handler 64
	fi

	# The type must also be specified.
	if [[ -z $type ]]; then
		output "The type must be specified." 1
		std_cmd_err_handler 64
	fi
}

# Setup network configuration.
# No parameters.
# No return value.
network_config()
{
	if [[ -n $bridge ]]; then
		cp -v "$contlocation/$name/config" \
			"$contlocation/$name/config-$bridge"
		std_cmd_err_handler $?
		tee -a "$contlocation/$name/config-$bridge" \
			< "$etclocation/$bridge.conf"
		std_cmd_err_handler $?
	fi
	tee -a "$contlocation/$name/config" < "$etclocation/$vni.conf"
	std_cmd_err_handler $?
}

# Setup some git defaults
# No parameters.
# No return value.
setup_git()
{
	/usr/bin/lxcu-attach "$name" \
		-- git -- config --system user.email "$gitemail"
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" \
		-- git -- config --system user.name "$gitname"
	std_cmd_err_handler $?
}


# Copy common files from host
# No parameters
# No return value
copy_common_files_from_host()
{
	# journald config
	sudo cp -rv "/etc/systemd/journald.conf.d" \
		"$contlocation/$name/rootfs/etc/systemd"
	sudo chown -Rv 100000:100000 \
		"$contlocation/$name/rootfs/etc/systemd/journald.conf.d"
}


# Create the specified type of APT container.
# Parameters - $1 is the type to create.
# No return value.
create_deb_type()
{
	if [[ -e $etclocation/$dist/$rel/$1/install.list ]]; then
		grep -v '^[[:blank:]]*#' \
			"$etclocation/$dist/$rel/$1/install.list" \
			| xargs /usr/bin/lxcu-attach "$name" \
			-- sudo apt-get -- -y install
		std_cmd_err_handler $?
	fi
	if [[ -e $etclocation/$dist/$rel/$1/cache.list ]]; then
		grep -v '^[[:blank:]]*#' \
			"$etclocation/$dist/$rel/$1/cache.list" \
			| xargs /usr/bin/lxcu-attach "$name" \
			-- sudo apt-get -- -yd install
		std_cmd_err_handler $?
	fi
	# Setup git.
	if [[ $1 == "dev" ]]; then
		setup_git
	fi

}

# Create an APT container.
# No parameters.
# No return value.
create_deb()
{
	# Set up the initial apt files, (just sources.list).
	# shellcheck disable=SC2046 # Word splitting required.
	sudo cp -v $(find "$etclocation/$dist/$rel/apt" -maxdepth 1 -type f) \
		"$contlocation/$name/rootfs/etc/apt"
	std_cmd_err_handler $?
	sudo chown -v 100000:100000 "$contlocation/$name/rootfs/etc/apt"
	std_cmd_err_handler $?
	# shellcheck disable=SC2046 # Word splitting required.
	sudo sed -i -e "s|@rel@|$rel|g" \
		$(find "$contlocation/$name/rootfs/etc/apt" -maxdepth 1 -type f)
	std_cmd_err_handler $?
	# shellcheck disable=SC2046 # Word splitting required.
	sudo sed -i -e "s|@obs_rel@|$obs_rel|g" \
		$(find "$contlocation/$name/rootfs/etc/apt" -maxdepth 1 -type f)
	std_cmd_err_handler $?

	# Initially install apt https transport, apt utils, certificates, curl,
	# gnupg and sudo.
	/usr/bin/lxcu-start "$name"
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- apt-get update
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- -vPATH="$PATH":/sbin:/usr/sbin \
		apt-get -- -y install apt-transport-https apt-utils \
		ca-certificates curl gnupg sudo
	std_cmd_err_handler $?

	# Set the locale.
	/usr/bin/lxcu-attach "$name" \
		-- sudo sed -- -i -e "s/\# $localelang/$localelang/" \
			/etc/locale.gen
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" \
		-- sudo locale-gen "$localelang"
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" \
		-- sudo update-locale LANG="$localelang"
	std_cmd_err_handler $?

	# Get the OBS repo key.
	if [[ $dist == "debian" ]]; then
		curl -fsSL "https://download.opensuse.org/repositories/home:m-grant-prg/Debian_$obs_rel/Release.key" \
			| gpg --dearmor \
			| sudo tee "$contlocation/$name/rootfs/etc/apt/trusted.gpg.d/home_m-grant-prg.gpg" > /dev/null
		std_cmd_err_handler $?
	fi

	# Set up the other initial apt files.
	sudo cp -rv "$etclocation/$dist/$rel"/apt/* \
		"$contlocation/$name/rootfs/etc/apt"
	std_cmd_err_handler $?
	sudo chown -Rv 100000:100000 "$contlocation/$name/rootfs/etc/apt"
	std_cmd_err_handler $?
	# shellcheck disable=SC2046 # Word splitting required.
	sudo sed -i -e "s|@rel@|$rel|g" \
		$(find "$contlocation/$name/rootfs/etc/apt" -maxdepth 1 -type f)
	std_cmd_err_handler $?
	# shellcheck disable=SC2046 # Word splitting required.
	sudo sed -i -e "s|@obs_rel@|$obs_rel|g" \
		$(find "$contlocation/$name/rootfs/etc/apt" -maxdepth 1 -type f)
	std_cmd_err_handler $?
	# shellcheck disable=SC2046 # Word splitting required.
	sudo sed -i -e "s|@rel@|$rel|g" \
		$(find "$contlocation/$name/rootfs/etc/apt/sources.list.d" \
			-maxdepth 1 -type f)
	std_cmd_err_handler $?
	# shellcheck disable=SC2046 # Word splitting required.
	sudo sed -i -e "s|@obs_rel@|$obs_rel|g" \
		$(find "$contlocation/$name/rootfs/etc/apt/sources.list.d" \
			-maxdepth 1 -type f)

	# Set up my debian keyring
	/usr/bin/lxcu-attach "$name" -- sudo apt-get update
	std_cmd_err_handler $?
	if [[ $dist != "ubuntu" ]]; then
		/usr/bin/lxcu-attach "$name" \
			-- sudo apt-get -- -y -o \
			Dpkg::Options::="--force-confnew" \
			install mgrant-obs-deb-keyring
		std_cmd_err_handler $?
	fi

	# Set up the dosab files.
	/usr/bin/lxcu-attach "$name" \
		-- sudo apt-get -- -y install server-dependency
	std_cmd_err_handler $?
	sudo cp -v "$etclocation/$dist/$rel"/dosab/* \
		"$contlocation/$name/rootfs/etc/dosab"
	std_cmd_err_handler $?
	sudo chown -Rv 100000:100000 "$contlocation/$name/rootfs/etc/dosab"
	std_cmd_err_handler $?
	# shellcheck disable=SC2046 # Word splitting required.
	sudo sed -i -e "s|@rel@|$rel|g" \
		$(find "$contlocation/$name/rootfs/etc/dosab" -maxdepth 1 \
			-type f)
	std_cmd_err_handler $?
	# shellcheck disable=SC2046 # Word splitting required.
	sudo sed -i -e "s|@obs_rel@|$obs_rel|g" \
		$(find "$contlocation/$name/rootfs/etc/dosab" -maxdepth 1 \
			-type f)
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- sudo dosab
	std_cmd_err_handler $?

	# Final fundamental installs.
	/usr/bin/lxcu-attach "$name" -- sudo apt-get update
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- sudo apt-get -- -y install apt
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- sudo apt-get -- -y install agmaint
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- sudo agmaint -- -- -y
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" \
		-- sudo apt-key-scripts -- --install \
		/usr/share/agmaint/trusted.sh.d/add-hermes.sh
	std_cmd_err_handler $?
	if [[ $dist == "ubuntu" ]]; then
		/usr/bin/lxcu-attach "$name" \
			-- sudo apt-key-scripts -- --install \
			/usr/share/agmaint/trusted.sh.d/add-mgrant-utils-ppa.sh
		std_cmd_err_handler $?
	fi

	create_deb_type basic
	if [[ $type != "basic" ]]; then
		create_deb_type "$type"
	fi

	lxc-stop -n "$name"
	std_cmd_err_handler $?
}

# Create the specified type of Fedora container.
# Parameters - $1 is the type to create.
# No return value.
create_fedora_type()
{
	# Install the development tools group.
	if [[ $1 == "dev" ]]; then
		/usr/bin/lxcu-attach "$name" -- sudo dnf -- -y \
			group install "development-tools"
		std_cmd_err_handler $?
	fi

	if [[ -e $etclocation/$dist/$rel/$1/install.list ]]; then
		grep -v '^[[:blank:]]*#' \
			"$etclocation/$dist/$rel/$1/install.list" \
			| xargs /usr/bin/lxcu-attach "$name" \
			-- sudo dnf -- -y install
		std_cmd_err_handler $?
	fi
	if [[ -e $etclocation/$dist/$rel/$1/cache.list ]]; then
		grep -v '^[[:blank:]]*#' \
			"$etclocation/$dist/$rel/$1/cache.list" \
			| xargs /usr/bin/lxcu-attach "$name" \
			-- sudo dnf -- -y install --downloadonly
		std_cmd_err_handler $?
	fi
	# Setup git.
	if [[ $1 == "dev" ]]; then
		setup_git
	fi
}

# Create a Fedora container.
# No parameters.
# No return value.
create_fedora()
{
	/usr/bin/lxcu-start "$name"
	std_cmd_err_handler $?

	# Set dnf to keep download only packagfes in the cache.
	echo "keepcache=True" | \
		sudo tee -a "$contlocation/$name/rootfs/etc/dnf/dnf.conf"
	std_cmd_err_handler $?

	# Set locale.
	/usr/bin/lxcu-attach "$name" -- sudo dnf -- -y update --refresh
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" \
		-- sudo localectl set-locale LANG="$localelang"
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" \
		-- sudo localectl set-x11-keymap "$localex11keymap"
	std_cmd_err_handler $?

	# Initial installs.
	/usr/bin/lxcu-attach "$name" \
		-- sudo dnf -- -y install 'dnf-command(config-manager)'
	std_cmd_err_handler $?

	# Add the opeSUSE Build Service repository.
	/usr/bin/lxcu-attach "$name" \
		-- sudo dnf config-manager addrepo -- --from-repofile \
		https://download.opensuse.org/repositories/home:m-grant-prg/Fedora_"$rel"/home:m-grant-prg.repo
	std_cmd_err_handler $?

	# Set up the dosab files.
	/usr/bin/lxcu-attach "$name" -- sudo dnf -- -y update --refresh
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" \
		-- sudo dnf -- -y install server-dependency
	std_cmd_err_handler $?
	sudo cp -v "$etclocation/$dist/$rel"/dosab/* \
		"$contlocation/$name/rootfs/etc/dosab"
	std_cmd_err_handler $?
	sudo chown -Rv 100000:100000 "$contlocation/$name/rootfs/etc/dosab"
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- sudo dosab
	std_cmd_err_handler $?

	# Final tidy.
	/usr/bin/lxcu-attach "$name" -- sudo dnf -- -y update --refresh
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- sudo dnf -- -y autoremove
	std_cmd_err_handler $?

	create_fedora_type basic
	if [[ $type != "basic" ]]; then
		create_fedora_type "$type"
	fi

	lxc-stop -n "$name"
	std_cmd_err_handler $?
}

# Create the specified type of openSUSE container.
# Parameters - $1 is the type to create.
# No return value.
create_opensuse_type()
{
	# Install the development tools.
	if [[ $1 == "dev" ]]; then
		/usr/bin/lxcu-attach "$name" \
			-- sudo zypper -- -n install -t pattern devel_basis
		std_cmd_err_handler $?
	fi

	if [[ -e $etclocation/$dist/$rel/$1/install.list ]]; then
		grep -v '^[[:blank:]]*#' \
			"$etclocation/$dist/$rel/$1/install.list" \
			| xargs /usr/bin/lxcu-attach "$name" \
			-- sudo zypper -- -n install
		std_cmd_err_handler $?
	fi
	if [[ -e $etclocation/$dist/$rel/$1/cache.list ]]; then
		grep -v '^[[:blank:]]*#' \
			"$etclocation/$dist/$rel/$1/cache.list" \
			| xargs /usr/bin/lxcu-attach "$name" \
			-- sudo zypper -- -n install --download-only
		std_cmd_err_handler $?
	fi
	# Setup git.
	if [[ $1 == "dev" ]]; then
		setup_git
	fi
}

# Create an openSUSE container.
# No parameters.
# No return value.
create_opensuse()
{
	/usr/bin/lxcu-start "$name"
	std_cmd_err_handler $?

	# Set locale.
	/usr/bin/lxcu-attach "$name" -- sudo zypper -- refresh
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- sudo zypper -- -n update
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" \
		-- sudo zypper -- -n install glibc-locale
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" \
		-- sudo localectl set-locale LANG="$localelang"
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" \
		-- sudo localectl set-x11-keymap "$localex11keymap"
	std_cmd_err_handler $?

	# Add the opeSUSE Build Service repository.
	/usr/bin/lxcu-attach "$name" \
		-- sudo zypper addrepo https://download.opensuse.org/repositories/home:m-grant-prg/"$rel"/home:m-grant-prg.repo
	std_cmd_err_handler $?

	# Set up the dosab files.
	/usr/bin/lxcu-attach "$name" -- sudo zypper -- refresh
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- sudo zypper -- -n update
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" \
		-- sudo zypper -- -n install server-dependency
	std_cmd_err_handler $?
	sudo cp -v "$etclocation/$dist/$rel"/dosab/* \
		"$contlocation/$name/rootfs/etc/dosab"
	std_cmd_err_handler $?
	sudo chown -Rv 100000:100000 "$contlocation/$name/rootfs/etc/dosab"
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- sudo dosab
	std_cmd_err_handler $?

	# Final tidy.
	/usr/bin/lxcu-attach "$name" -- sudo zypper -- refresh
	std_cmd_err_handler $?
	/usr/bin/lxcu-attach "$name" -- sudo zypper -- -n update
	std_cmd_err_handler $?

	create_opensuse_type basic
	if [[ $type != "basic" ]]; then
		create_opensuse_type "$type"
	fi

	lxc-stop -n "$name"
	std_cmd_err_handler $?
}


########
# Main #
########

proc_config_file

proc_CL "$@"

systemd-run --quiet --user --scope --property "Delegate=yes" \
	-- lxc-create -t download -n "$name"
std_cmd_err_handler $?

network_config

copy_common_files_from_host

case $dist in
debian)
	create_deb
	;;
fedora)
	create_fedora
	;;
opensuse)
	create_opensuse
	;;
ubuntu)
	create_deb
	;;
*)
	output "Unknown distribution." 1
	std_cmd_err_handler 64
	;;
esac

# And exit.
script_exit 0

