#!/bin/bash

## lxc-debconfig - LXC template for Debian and Debian Derivatives
## Copyright (C) 2006-2013 Daniel Baumann <mail@daniel-baumann.ch>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program 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 General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
## The complete text of the GNU General Public License
## can be found in /usr/share/common-licenses/GPL-3 file.


#set -e

umask 0022

Warnings ()
{
	case "${LXC_DEBCONFIG_WITH_LIVE_DEBCONFIG}" in
		true)
			case "${_DISTRIBUTION}" in
				squeeze*|wheezy*)
					if ! ls /usr/share/lxc/packages/live-debconfig_*.deb > /dev/null 2>&1
					then

cat << EOF
WARNING: This version of the lxc-debconfig template requires the live-debconfig
package to configure the container.

live-debconfig is available as of Debian 8.0 (jessie).

Attempting to build a container based on any older Debian release *without*
having a local copy of live-debconfig in /usr/share/lxc/packages/ on the host
system will fail.

Press enter to continue nevertheless, or Ctrl-c to abort..
EOF

						read WAITING
					fi
					;;
			esac
			;;
	esac
}

Chroot ()
{
	_CHROOT="${1}"
	shift

	# Workaround: until the whole template can be executed with 'set -e',
	# let's at least run all chrooted commands with 'set -e'.
	set -e

	chroot "${_CHROOT}" /usr/bin/env -i \
		LC_ALL="C" PATH="/usr/sbin:/usr/bin:/sbin:/bin" TERM="${TERM}" \
		DEBIAN_FRONTEND="${_DEBCONF_FRONTEND}" DEBIAN_PRIORITY="${_DEBCONF_PRIORITY}" \
		DEBCONF_NONINTERACTIVE_SEEN="true" DEBCONF_NOWARNINGS="true" \
		ftp_proxy="${ftp_proxy}" http_proxy="${http_proxy}" \
		${@}

	set +e
}

Upgrade_system ()
{
	_ROOT="${1}"

	# Mount pseudo-filesystems
	mount -o bind /dev/pts "${_ROOT}/dev/pts"
	mount -o bind /proc "${_ROOT}/proc"
	mount -o bind /sys "${_ROOT}/sys"

	# Bind mount systems apt cache
	mount -o bind /var/cache/apt/archives "${_ROOT}/var/cache/apt/archives"

	# Disable dpkg syncing

cat > "${_ROOT}/etc/dpkg/dpkg.cfg.d/lxc" << EOF
force-unsafe-io
EOF

	# Create policy-rc.d file

cat > "${_ROOT}/usr/sbin/policy-rc.d" << EOF
#!/bin/sh
echo "All runlevel operations denied by policy" >&2
exit 101
EOF

	chmod 0755 "${_ROOT}/usr/sbin/policy-rc.d"

	# Upgrade system
	Chroot "${_ROOT}" "apt-get update"
	Chroot "${_ROOT}" "apt-get --yes upgrade"
	Chroot "${_ROOT}" "apt-get --yes dist-upgrade"

	# Unmount systems apt cache
	umount "${_ROOT}/var/cache/apt/archives"

	Chroot "${_ROOT}" "apt-get clean"
}

Cleanup_system ()
{
	_ROOT="${1}"

	Chroot "${_ROOT}" "apt-get --yes autoremove"
	Chroot "${_ROOT}" "apt-get clean"

	# Cleanup
	rm -f "${_ROOT}/etc/dpkg/dpkg.cfg.d/lxc"
	rm -f "${_ROOT}/usr/sbin/policy-rc.d"

	# Unmount pseudo-filesystems
	umount "${_ROOT}/dev/pts"
	umount "${_ROOT}/proc"
	umount "${_ROOT}/sys"
}

Configure_system ()
{
	_ROOTFS="${1}"

	# Configure apt
	rm -f "${_ROOTFS}/etc/apt/sources.list"

	_PARENT_AREA="$(echo ${_PARENT_ARCHIVE_AREAS} | sed -e 's|,| |g')"
	_PARENT_DIST="$(echo ${_PARENT_DISTRIBUTION} | sed -e 's|-backports||')"

	echo "deb ${_PARENT_MIRROR} ${_PARENT_DIST} ${_PARENT_AREA}" > "${_ROOTFS}/etc/apt/sources.list.d/debian.list"

	for _PARENT_REPO in ${_PARENT_ARCHIVES}
	do
		case "${_PARENT_REPO}" in
			${_PARENT_DIST}-security)
				echo "deb ${_PARENT_MIRROR_SECURITY} ${_PARENT_DIST}/updates ${_PARENT_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/debian.list"
				;;

			${_PARENT_DIST}-updates)
				echo "deb ${_PARENT_MIRROR} ${_PARENT_DIST}-updates ${_PARENT_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/debian.list"
				;;

			${_PARENT_DIST}-backports)
				echo "deb ${_PARENT_MIRROR_BACKPORTS} ${_PARENT_DIST}-backports ${_PARENT_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/debian.list"
				;;

			${_PARENT_DIST}-proposed-updates)
				echo "deb ${_PARENT_MIRROR} ${_PARENT_DIST}-proposed-updates ${_PARENT_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/debian.list"
				;;

			experimental)
				echo "deb ${_PARENT_MIRROR} experimental ${_PARENT_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/debian.list"
				;;
		esac
	done

	case "${_MODE}" in
		progress-linux)
			_AREA="$(echo ${_ARCHIVE_AREAS} | sed -e 's|,| |g')"
			_DIST="$(echo ${_DISTRIBUTION} | sed -e 's|-backports||')"

			echo "deb ${_MIRROR} ${_DIST} ${_AREA}" > "${_ROOTFS}/etc/apt/sources.list.d/progress-linux.list"

			for _REPO in ${_ARCHIVES}
			do
				case "${_REPO}" in
					${_DIST}-staging)
						echo "deb ${_MIRROR} ${_DIST}-staging ${_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/progress-linux.list"
						;;

					${_DIST}-security)
						echo "deb ${_MIRROR_SECURITY} ${_DIST}-security ${_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/progress-linux.list"
						;;

					${_DIST}-security-staging)
						echo "deb ${_MIRROR_SECURITY} ${_DIST}-security-staging ${_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/progress-linux.list"
						;;

					${_DIST}-updates)
						echo "deb ${_MIRROR} ${_DIST}-updates ${_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/progress-linux.list"
						;;

					${_DIST}-updates-staging)
						echo "deb ${_MIRROR} ${_DIST}-updates-staging ${_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/progress-linux.list"
						;;

					${_DIST}-backports)
						echo "deb ${_MIRROR_BACKPORTS} ${_DIST}-backports ${_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/progress-linux.list"
						;;

					${_DIST}-backports-staging)
						echo "deb ${_MIRROR_BACKPORTS} ${_DIST}-backports-staging ${_AREA}" >> "${_ROOTFS}/etc/apt/sources.list.d/progress-linux.list"
						;;
				esac
			done
			;;

		*)
			if [ "${_APT_RECOMMENDS}" = "false" ]
			then

cat > "${_ROOTFS}/etc/apt/apt.conf.d/recommends.conf" << EOF
APT::Install-Recommends "false";
EOF

			fi
			;;
	esac

	# Configure fstab

cat > "${_ROOTFS}/etc/fstab" << EOF
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>

EOF

	Upgrade_system "${_ROOTFS}"

	# Temporary hack for base-files
	for _FILE in motd.tail motd profile
	do
		if [ -e "${_ROOTFS}/usr/share/base-files/${_FILE}" ]
		then
			rm -f "${_ROOTFS}/etc/${_FILE}"
			cp "${_ROOTFS}/usr/share/base-files/${_FILE}" "${_ROOTFS}/etc"
		fi
	done

	# Temporary hack for dpkg
	if [ -e "${_ROOTFS}/etc/dpkg/origins/default" ]
	then
		rm -f "${_ROOTFS}/etc/dpkg/origins/default"
		Chroot "${_ROOTFS}" "dpkg-reconfigure base-files"
	fi

	# Temporary hack for readline
	if [ -e "${_ROOTFS}/etc/inputrc" ]
	then
		rm -f "${_ROOTFS}/etc/inputrc"
		cp "${_ROOTFS}/usr/share/readline/inputrc" "${_ROOTFS}/etc"
	fi

	# Temporary hack for sysvinit
	if [ -e "${_ROOTFS}/etc/inittab" ]
	then
		rm -f "${_ROOTFS}/etc/inittab"
		cp "${_ROOTFS}/usr/share/sysvinit/inittab" "${_ROOTFS}/etc/inittab"
	fi

	# Removing hostname
	rm -f "${_ROOTFS}"/etc/hostname

	# Removing openssh-server host keys
	rm -f "${_ROOTFS}"/etc/ssh/ssh_host_*_key
	rm -f "${_ROOTFS}"/etc/ssh/ssh_host_*_key.pub

	# Preseed system
	if [ -n "${_PRESEED_FILE}" ]
	then
		for _FILE in ${_PRESEED_FILE}
		do
			cat "${_FILE}" >> "${_ROOTFS}/preseed.cfg"
		done

		Chroot "${_ROOTFS}" "debconf-set-selections preseed.cfg"

		rm -f "${_ROOTFS}/preseed.cfg"
	fi

	# FIXME: All packages of priority of essential need to be reconfigured to reflect choices from preseeding

	# Manual hack to workaround broken preseeding in tzdata package
	if [ -n "${_PRESEED_FILE}" ]
	then
		for _FILE in ${_PRESEED_FILE}
		do
			if grep -qs tzdata "${_FILE}"
			then
				rm -f "${_ROOTFS}/etc/timezone"
				Chroot "${_ROOTFS}" "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=criticial dpkg-reconfigure tzdata"

				break
			fi
		done
	fi

	case "${LXC_DEBCONFIG_WITH_LIVE_DEBCONFIG}" in
		true)
			# Install live-debconfig
			if ls /usr/share/lxc/packages/live-debconfig_*.deb > /dev/null 2>&1
			then
				# Install local live-debconfig package
				cp -L /usr/share/lxc/packages/live-debconfig_*.deb "${_ROOTFS}"
				Chroot "${_ROOTFS}" "dpkg -i $(basename ${_ROOTFS}/live-debconfig_*.deb)"
				rm -f "${_ROOTFS}"/live-debconfig_*.deb
			else
				# Install remote live-debconfig package
				Chroot "${_ROOTFS}" "apt-get install --yes live-debconfig"
			fi

			# Preseed live-debconfig
cat > "${_ROOTFS}/preseed.cfg" << EOF
# multiselect
live-debconfig live-debconfig/components multiselect hostname, ifupdown, ${_INITSYSTEM}, util-linux, rsyslog, selinux, openssh-server

# passwd
live-debconfig live-debconfig/passwd/root-password string ${_ROOT_PASSWORD}
live-debconfig live-debconfig/passwd/root-password-again string ${_ROOT_PASSWORD}

# ifupdown
live-debconfig live-debconfig/ifupdown/lo-enable boolean false

# ${_INITSYSTEM}
live-debconfig live-debconfig/${_INITSYSTEM}/lxc-enable boolean true

# util-linux
live-debconfig live-debconfig/util-linux/hwclockaccess boolean false

# rsyslog
live-debconfig live-debconfig/rsyslog/imklog boolean false

# selinux
live-debconfig live-debconfig/selinux/enable boolean false
EOF

			if [ -n "${_PRESEED_FILE}" ]
			then
				for _FILE in ${_PRESEED_FILE}
				do
					grep -E '^ *live-debconfig' "${_FILE}" >> "${_ROOTFS}/preseed.cfg"
				done
			fi

			# Apply preseed information
			Chroot "${_ROOTFS}" "live-debconfig-set-selections preseed.cfg"

			rm -f "${_ROOTFS}/preseed.cfg"

			# Run live-debconfig (first pass)
			Chroot "${_ROOTFS}" "DEBIAN_FRONTEND=${_DEBCONF_FRONTEND:-dialog} DEBIAN_PRIORITY=${_DEBCONF_PRIORITY:-high} live-debconfig"
			;;
	esac

	# FIXME: Install additional packages after live-debconfig has been run.
	# This is suboptimal, ideally we should install all packages but not run the maintainer scripts,
	# then run live-debconfig, and run the maintainer scripts. This way, live-debconfig would see
	# all the installed packages and could skip those scripts entirely when a certain package is not
	# installed. Unfortunately, that is not possible in any reasonable way with apt-get.
	# FTR: The only known workaround for now would be to first apt-get install --download-only all
	# packages, then unpack them with dpkg, run live-debconfig, and dpkg --configure them.
	# For the time being, it's better to have live-debconfig see no packages at all and be run before
	# packages are installed, than the other way around.
	# Workaround: We're running live-debconfig at the end of the template again.
	if [ -n "${_PACKAGES}" ]
	then
		case "${_DEBCONF_FRONTEND}" in
			noninteractive)
				Chroot "${_ROOTFS}" apt-get --option Dpkg::Options::="--force-overwrite" --option Dpkg::Options::="--force-confold" --yes install ${_PACKAGES}
				;;

			*)
				Chroot "${_ROOTFS}" "apt-get --yes install ${_PACKAGES}"
				;;
		esac
	fi

	case "${_ROOT_RANDOM_PASSWORD}" in
		true)
			if [ -n "${_PRESEED_FILE}" ]
			then
				for _FILE in ${_PRESEED_FILE}
				do
					if grep -qs -E '^ *live-debconfig live-debconfig/passwd/root-password' "${_PRESEED_FILE}"
					then
						continue
					fi

					echo "P: Setting root password to ${_ROOT_PASSWORD}"
				done
			fi
			;;
	esac

	if ls /usr/share/lxc/includes/* > /dev/null 2>&1
	then
		cd /usr/share/lxc/includes

		find . | cpio -dmpu --no-preserve-owner "${_ROOTFS}"

		cd "${OLDPWD}"
	fi

	# Run live-debconfig (second pass)
	if [ -x "${_ROOTFS}/bin/live-debconfig" ]
	then
		Chroot "${_ROOTFS}" "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=criticial live-debconfig"
	fi

	if [ -n "${_LATE_COMMAND}" ]
	then
		echo "${_LATE_COMMAND}" > "${_ROOTFS}/.late-command"

		Chroot "${_ROOTFS}" "sh /.late-command"

		rm -f "${_ROOTFS}/.late-command"
	fi

	if [ -n "${_LATE_HOST_COMMAND}" ]
	then
		echo "${_LATE_HOST_COMMAND}" > "${_ROOTFS}/.late-host-command"

		cd "${_ROOTFS}"
		sh "${_ROOTFS}/.late-host-command"
		cd "${OLDPWD}"

		rm -f "${_ROOTFS}/.late-host-command"
	fi

	Cleanup_system "${_ROOTFS}"

	return 0
}

Download_system ()
{
	_CACHE="${1}"
	_ARCHITECTURE="${2}"

	case "${_MODE}" in
		debian)
			_INCLUDE="ifupdown,locales,libui-dialog-perl,dialog,isc-dhcp-client,netbase,net-tools,iproute,openssh-server,wget"
			_EXCLUDE=""
			;;

		progress-linux)
			_INCLUDE="apt-utils,ifupdown,locales-all,libui-dialog-perl,dialog,isc-dhcp-client,netbase,net-tools,iproute,wget"
			_EXCLUDE=""
			;;
	esac

	case "${_INITSYSTEM}" in
		systemd)
			_INCLUDE="--include=systemd,${_INCLUDE}"
			_EXCLUDE="--exclude=sysvinit,${_EXCLUDE}"
			;;

		*)
			_INCLUDE="--include=${_INCLUDE}"
			_EXCLUDE=""
			;;
	esac

	# check the minimal system was not already downloaded
	mkdir -p "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial"

	if [ "${?}" -ne 0 ]
	then
		echo "Failed to create '${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial' directory"
		return 1
	fi

	# download a minimal system into a cache
	echo "Downloading minimal system..."

	if [ -x "$(which cdebootstrap 2>/dev/null)" ]
	then
		cdebootstrap --arch=${_ARCHITECTURE} \
			--flavour=minimal ${_INCLUDE} ${_EXCLUDE} \
			${_PARENT_DISTRIBUTION} "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial" ${_PARENT_MIRROR}

		Chroot "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial" "dpkg --purge cdebootstrap-helper-rc.d"
	elif [ -x "$(which debootstrap 2>/dev/null)" ]
	then
		debootstrap --verbose --arch=${_ARCHITECTURE} --components=${_PARENT_ARCHIVE_AREAS} \
			--variant=minbase ${_INCLUDE} ${_EXCLUDE} \
			${_PARENT_DISTRIBUTION} "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial" ${_PARENT_MIRROR}
	fi

	if [ "${?}" -ne 0 ]
	then
		echo "Failed to download the rootfs, aborting."
		return 1
	fi

	# Configure apt
	rm -f "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/etc/apt/sources.list"

	_PARENT_AREA="$(echo ${_PARENT_ARCHIVE_AREAS} | sed -e 's|,| |g')"
	_PARENT_DIST="$(echo ${_PARENT_DISTRIBUTION} | sed -e 's|-backports||')"

	echo "deb ${_PARENT_MIRROR} ${_PARENT_DIST} ${_PARENT_AREA}" > "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/etc/apt/sources.list.d/debian.list"

	case "${_MODE}" in
		progress-linux)
			_AREA="$(echo ${_ARCHIVE_AREAS} | sed -e 's|,| |g')"
			_DIST="$(echo ${_DISTRIBUTION} | sed -e 's|-backports||')"

			echo "deb ${_MIRROR} ${_DIST} ${_AREA}" > "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/etc/apt/sources.list.d/progress-linux.list"

			for _REPO in ${_ARCHIVES}
			do
				case "${_REPO}" in
					${_DIST}-staging)
						echo "deb ${_MIRROR} ${_DIST}-staging ${_AREA}" >> "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/etc/apt/sources.list.d/progress-linux.list"
						;;
				esac
			done

			case "${_DISTRIBUTION}" in
				*-backports)
					for _REPO in ${_ARCHIVES}
					do
						case "${_REPO}" in
							${_DIST}-backports)
								echo "deb ${_MIRROR_BACKPORTS} ${_DIST}-backports ${_AREA}" >> "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/etc/apt/sources.list.d/progress-linux.list"
								;;

							${_DIST}-backports-staging)
								echo "deb ${_MIRROR_BACKPORTS} ${_DIST}-backports-staging ${_AREA}" >> "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/etc/apt/sources.list.d/progress-linux.list"
								;;
						esac
					done
					;;
			esac

cat > "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/etc/apt/preferences.d/progress-linux.pref" << EOF
Package: *
Pin: release n=${_DIST}
Pin-Priority: 999

Package: *
Pin: release n=${_DIST}-security
Pin-Priority: 999

Package: *
Pin: release n=${_DIST}-updates
Pin-Priority: 999
EOF
			case "${_DISTRIBUTION}" in
				*-backports)

cat >> "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/etc/apt/preferences.d/progress-linux.pref" << EOF

Package: *
Pin: release n=${_DIST}-backports
Pin-Priority: 999
EOF

					;;

				*)

cat >> "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/etc/apt/preferences.d/progress-linux.pref" << EOF

#Package: *
#Pin: release n=${_DIST}-backports
#Pin-Priority: 999
EOF

					;;
			esac

			# Import archive keys
			case "${_DISTRIBUTION}" in
				artax)
					_KEYS="1-artax"
					;;

				artax-backports)
					_KEYS="1-artax 1+-artax-backports"
					;;

				baureo)
					_KEYS="2-baureo"
					;;

				baureo-backports)
					_KEYS="2-baureo 2+-baureo-backports"
					;;

				chairon)
					_KEYS="3-chairon"
					;;

				chairon-backports)
					_KEYS="3-chairon 3+-chairon-backports"
					;;
			esac

			for _KEY in ${_KEYS}
			do
				echo "P: Fetching archive-key ${_KEY}..."

				wget -q "${_MIRROR}/project/keys/archive-key-${_KEY}.asc" -O "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/key.asc"
				wget -q "${_MIRROR}/project/keys/archive-key-${_KEY}.asc.sig" -O "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/key.asc.sig"

				if [ -e /usr/bin/gpgv ]
				then
					if [ -e /usr/share/keyrings/debian-keyring.gpg ] || [ -e /usr/share/keyrings/debian-maintainers.gpg ]
					then
						_KEY_VALID=""

						for _KEYRING in /usr/share/keyrings/debian-keyring.gpg /usr/share/keyrings/debian-maintainers.gpg
						do
							if [ -e "${_KEYRING}" ]
							then
								echo -n "I: Verifying archive-key ${_KEY} against $(basename ${_KEYRING} .gpg | sed -e 's|-keyring||') keyring..."

								# FIXME: doesn't work anymore when the template eventually be run with 'set -e'
								/usr/bin/gpgv --quiet --keyring ${_KEYRING} "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/key.asc.sig" "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/key.asc" > /dev/null 2>&1 && _KEY_VALID="true" && break
							fi
						done

						case "${_KEY_VALID}" in
							true)
								echo " successful."
								;;

							*)
								echo " failed."
								return 1
								;;
						esac
					else
						echo "W: Skipping archive-key ${_KEY} verification, debian-keyring not available..."
					fi
				else
					echo "W: Skipping archive-key ${_KEY} verification, gpgv not available..."
				fi

				echo "P: Importing archive-key ${_KEY}..."

				Chroot "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial" "apt-key add key.asc"
				rm -f "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/key.asc"
				rm -f "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial/key.asc.sig"
			done

			Chroot "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial" "apt-get update"
			;;
	esac

	# Fixup bootstrapping deficiencies with skipping essential packages.. (FIXME)
	case "${_INITSYSTEM}" in
		systemd)
			Chroot "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial" dpkg --force-remove-essential --purge sysvinit
			Chroot "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial" apt-get install --yes systemd-sysv
			Chroot "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial" apt-get clean
			;;
	esac

	mv "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}.partial" "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}"
	echo "Download complete."

	return 0
}

Copy_system ()
{
	_CACHE="${1}"
	_ARCHITECTURE="${2}"
	_ROOTFS="${3}"

	if [ -e "/usr/share/lxc/cache/${_MODE}/${_DISTRIBUTION}_${_ARCHITECTURE}" ]
	then
		# system cache in the form of a directory
		echo -n "Copying system cache to ${_ROOTFS}..."
		mkdir -p "${_ROOTFS}"
		(tar -C "/usr/share/lxc/cache/${_MODE}/${_DISTRIBUTION}_${_ARCHITECTURE}" -c . | tar -C "${_ROOTFS}" -x) || return 1
	elif ls "/usr/share/lxc/cache/${_MODE}/${_DISTRIBUTION}_${_ARCHITECTURE}".* > /dev/null 2>&1
	then
		# system cache in the form of a tarball
		for _FORMAT in tar.lz tar.xz tar.lzma tar.bz2 tar.gz tar
		do
			if [ -e "/usr/share/lxc/cache/${_MODE}/${_DISTRIBUTION}_${_ARCHITECTURE}.${_FORMAT}" ]
			then
				echo -n "Extracting system cache to ${_ROOTFS}..."

				mkdir -p "${_ROOTFS}"
				tar xf "/usr/share/lxc/cache/${_MODE}/${_DISTRIBUTION}_${_ARCHITECTURE}.${_FORMAT}" -C "${_ROOTFS}" || return 1
				break
			fi
		done
	else
		# local cache from bootstrap

		# Upgrade cache before copying it
		Upgrade_system "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}" || echo "W: If upgrading the system failed, try removing the cache for your distribution in /var/cache/lxc"
		Cleanup_system "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}"

		# make a local copy of the minimal system
		echo -n "Copying local cache to ${_ROOTFS}..."
		mkdir -p "${_ROOTFS}"
		(tar -C "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}" -c . | tar -C "${_ROOTFS}" -x) || return 1
	fi

	# Add local archives configured from preseed file
	if ls "${_TMPDIR}/sources.list.d"/*.list > /dev/null 2>&1
	then
		cp "${_TMPDIR}/sources.list.d"/*.list "${_ROOTFS}/etc/apt/sources.list.d"

		if ls "${_TMPDIR}/sources.list.d"/*.key > /dev/null 2>&1
		then
			for _KEY in "${_TMPDIR}/sources.list.d"/*.key
			do
				cp "${_KEY}" "${_ROOTFS}"
				Chroot "${_ROOTFS}" "apt-key add $(basename ${_KEY})"
				rm -f "${_ROOTFS}/$(basename ${_KEY})"
			done
		fi

		if ls "${_TMPDIR}/sources.list.d"/*.pref > /dev/null 2>&1
		then
			cp "${_TMPDIR}/sources.list.d"/*.pref "${_ROOTFS}/etc/apt/preferences.d"
		fi

		Upgrade_system "${_ROOTFS}"
		Cleanup_system "${_ROOTFS}"
	fi

	return 0
}

Install_system ()
{
	_CACHE="/var/cache/lxc/${_MODE}"
	_ROOTFS="${1}"

	mkdir -p /var/lock/subsys/
	(
		flock -x 200

		if [ "${?}" -ne 0 ]
		then
			echo "Cache repository is busy."
			return 1
		fi

		_ARCHITECTURE="${_ARCHITECTURE:-$(dpkg --print-architecture)}"

		if ! ls "/usr/share/lxc/cache/${_MODE}/${_DISTRIBUTION}_${_ARCHITECTURE}"* > /dev/null 2>&1 && \
		   [ ! -e "${_CACHE}/${_DISTRIBUTION}_${_ARCHITECTURE}" ]
		then
			Download_system "${_CACHE}" "${_ARCHITECTURE}"

			if [ "${?}" -ne 0 ]
			then
				echo "Failed to download base system"
				return 1
			fi
		fi

		Copy_system "${_CACHE}" "${_ARCHITECTURE}" "${_ROOTFS}"

		if [ "${?}" -ne 0 ]
		then
			echo "Failed to copy rootfs"
			return 1
		fi

		return 0
	) 200>/var/lock/subsys/lxc

	return "${?}"
}

Copy_configuration()
{
	_PATH="${1}"
	_ROOTFS="${2}"
	_NAME="${3}"

cat >> "${_PATH}/config" << EOF
# ${_PATH}/config

## Container
lxc.utsname = ${_NAME}
lxc.rootfs = ${_PATH}/rootfs
EOF

	case "${_ARCHITECTURE}" in
		amd64)
			echo "lxc.arch = x86_64" >> "${_PATH}/config"
			;;

		i386)
			echo "lxc.arch = x86" >> "${_PATH}/config"
			;;
	esac

cat >> "${_PATH}/config" << EOF
#lxc.console = /var/log/lxc/${_NAME}.console
lxc.tty = 6
lxc.pts = 1024

## Capabilities
EOF

	_CAPABILITIES="${_CAPABILITIES:-audit_control audit_write linux_immutable mac_admin mac_override setpcap sys_admin sys_module sys_pacct sys_rawio sys_time}"

	case "${_CAPABILITIES}" in
		none)
			_COMMENTED="#"
			_CAPABILITIES="audit_control audit_write linux_immutable mac_admin mac_override setpcap sys_admin sys_module sys_pacct sys_rawio sys_time"
			;;
		*)
			_COMMENTED=""
			;;
	esac

	for _CAPABILITY in ${_CAPABILITIES}
	do
		echo "${_COMMENTED}lxc.cap.drop = ${_CAPABILITY}" >> "${_PATH}/config"
	done

cat >> "${_PATH}/config" << EOF
## Devices
lxc.kmsg = 0
EOF

	case "${_INITSYSTEM}" in
		systemd)
			echo "lxc.autodev = 1" >> "${_PATH}/config"
			;;
		*)
			echo "lxc.autodev = 0" >> "${_PATH}/config"
			;;
	esac

cat >> "${_PATH}/config" << EOF
# Allow all devices
#lxc.cgroup.devices.allow = a
# Deny all devices
lxc.cgroup.devices.deny = a
# Allow to mknod all devices (but not using them)
lxc.cgroup.devices.allow = c *:* m
lxc.cgroup.devices.allow = b *:* m

# /dev/console
lxc.cgroup.devices.allow = c 5:1 rwm
# /dev/full
lxc.cgroup.devices.allow = c 1:7 rwm
# /dev/fuse
#lxc.cgroup.devices.allow = c 10:229 rwm
# /dev/hpet
#lxc.cgroup.devices.allow = c 10:228 rwm
# /dev/kvm
#lxc.cgroup.devices.allow = c 10:232 rwm
# /dev/loop*
#lxc.cgroup.devices.allow = b 7:* rwm
# /dev/loop-control
#lxc.cgroup.devices.allow = c 10:237 rwm
# /dev/null
lxc.cgroup.devices.allow = c 1:3 rwm
# /dev/ptmx
lxc.cgroup.devices.allow = c 5:2 rwm
# /dev/pts/*
lxc.cgroup.devices.allow = c 136:* rwm
# /dev/random
lxc.cgroup.devices.allow = c 1:8 rwm
# /dev/rtc
lxc.cgroup.devices.allow = c 254:0 rm
# /dev/tty
lxc.cgroup.devices.allow = c 5:0 rwm
# /dev/urandom
lxc.cgroup.devices.allow = c 1:9 rwm
# /dev/zero
lxc.cgroup.devices.allow = c 1:5 rwm

## Limits
#lxc.cgroup.cpu.shares = 1024
#lxc.cgroup.cpuset.cpus = 0
#lxc.cgroup.memory.limit_in_bytes = 256M
#lxc.cgroup.memory.memsw.limit_in_bytes = 1G

## Filesystem
EOF

	case "${LXC_DEBCONFIG_MOUNT_PROC_ENTRY}" in
		none)
			;;

		*)
			echo "lxc.mount.entry = ${LXC_DEBCONFIG_MOUNT_PROC_ENTRY}" >> "${_PATH}/config"
			;;
	esac

	case "${_INITSYSTEM}" in
		systemd)
			LXC_DEBCONFIG_MOUNT_SYSFS_ENTRY="none"
			;;
		*)
			;;
	esac

	case "${LXC_DEBCONFIG_MOUNT_SYSFS_ENTRY}" in
		none)
			;;

		*)
			echo "lxc.mount.entry = ${LXC_DEBCONFIG_MOUNT_SYSFS_ENTRY}" >> "${_PATH}/config"
			;;
	esac

	# Adding preseeded mount entries
	for _NUMBER in $(seq 0 ${LXC_DEBCONFIG_MOUNT_NUMBER})
	do
		eval _COMMENT="$`echo LXC_DEBCONFIG_MOUNT${_NUMBER}_COMMENT`"
		eval _ENTRY="$`echo LXC_DEBCONFIG_MOUNT${_NUMBER}_ENTRY`"

		if [ -n "${_COMMENT}" ]
		then
			echo "# ${_COMMENT}" >> "${_PATH}/config"
		fi

		if [ -n "${_ENTRY}" ]
		then
			echo "lxc.mount.entry = ${_ENTRY}" >> "${_PATH}/config"

			# Create directories
			_SOURCE="$(echo ${_ENTRY} | awk '{ print $1 }')"
			_TARGET="$(echo ${_ENTRY} | awk '{ print $2 }')"

			if [ -f "${_SOURCE}" ]
			then
				mkdir -p "${_ROOTFS}/$(dirname ${_TARGET})"
			else
				mkdir -p "${_ROOTFS}/${_TARGET}"
			fi
		fi
	done

	if [ -z "${_ENTRY}" ]
	then
		# Adding shared data directory if existing
		if [ -d "/srv/share/${_NAME}" ]
		then
			echo "lxc.mount.entry = /srv/share/${_NAME} ${_PATH}/rootfs/srv/${_NAME} none defaults,bind 0 0" >> "${_PATH}/config"
			mkdir -p "${_PATH}/rootfs/srv/${_NAME}"
		elif [ -d "/srv/${_NAME}" ]
		then
			echo "lxc.mount.entry = /srv/${_NAME} ${_PATH}/rootfs/srv/${_NAME} none defaults,bind 0 0" >> "${_PATH}/config"
			mkdir -p "${_PATH}/rootfs/srv/${_NAME}"
		elif [ -d "/srv/share" ]
		then
			echo "#lxc.mount.entry = /srv/share/${_NAME} ${_PATH}/rootfs/srv/${_NAME} none defaults,bind 0 0" >> "${_PATH}/config"
		else
			echo "#lxc.mount.entry = /srv/${_NAME} ${_PATH}/rootfs/srv/${_NAME} none defaults,bind 0 0" >> "${_PATH}/config"
		fi
	fi

	case "${_INITSYSTEM}" in
		systemd)
			echo "lxc.hook.pre-start = /usr/share/lxc/hooks/systemd-host" >> "${_PATH}/config"
			echo "lxc.hook.mount = /usr/share/lxc/hooks/systemd-container" >> "${_PATH}/config"
			;;
		*)
			;;
	esac

	# Adding network configuration
	for _NUMBER in $(seq 0 ${LXC_DEBCONFIG_ETH_NUMBER})
	do
		eval _METHOD="$`echo LXC_DEBCONFIG_ETH${_NUMBER}_METHOD`"
		eval _COMMENT="$`echo LXC_DEBCONFIG_ETH${_NUMBER}_COMMENT`"
		eval _BRIDGE="$`echo LXC_DEBCONFIG_ETH${_NUMBER}_BRIDGE`"
		eval _MAC="$`echo LXC_DEBCONFIG_ETH${_NUMBER}_MAC`"
		eval _VETH="$`echo LXC_DEBCONFIG_ETH${_NUMBER}_VETH`"

		if [ -n "${_COMMENT}" ]
		then

cat >> "${_PATH}/config" << EOF

## Network: ${_COMMENT}
EOF

		else

cat >> "${_PATH}/config" << EOF

## Network
EOF

		fi

		if [ "${_METHOD}" = "none" ]
		then
			echo "lxc.network.type = empty" >> "${_PATH}/config"

			_NUMBER="$((${_NUMBER} + 1))"
			continue
		fi

cat >> "${_PATH}/config" << EOF
lxc.network.type = veth
lxc.network.flags = up
lxc.network.hwaddr = ${_MAC}
lxc.network.link = ${_BRIDGE}
EOF

		echo "lxc.network.name = eth${_NUMBER}" >> "${_PATH}/config"

		if [ -n "${_VETH}" ]
		then
			if [ $(echo ${_VETH} | wc -c) -gt 16 ]
			then
				_VETH_OLD="${_VETH}"
				_VETH="$(echo ${_VETH} | cut -b 1-15)"

				echo "W: veth names must be maximum 15 characters long (IFNAMSIZ limit on linux)."
				echo "I: Automatically shortening ${_VETH_OLD} to ${_VETH}."
			fi

			echo "lxc.network.veth.pair = ${_VETH}" >> "${_PATH}/config"
		fi

		_NUMBER="$((${_NUMBER} + 1))"
	done

	# Remove configuration symlinks
	rm -f /etc/lxc/auto/${_NAME} /etc/lxc/auto/${_NAME}.conf

	case "${_AUTO}" in
		true)
			mkdir -p /etc/lxc/auto
			ln -s ${_PATH}/config /etc/lxc/auto/${_NAME}
			;;
	esac

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

	return 0
}

Clean ()
{
	_CACHE="/var/cache/lxc/${_MODE}"

	if [ ! -e "${_CACHE}" ]
	then
		exit 0
	fi

	# lock, so we won't purge while someone is creating a repository
	(
		flock -x 200

		if [ "${?}" != 0 ]
		then
			echo "Cache repository is busy."
			exit 1
		fi

		echo -n "Unmounting bind mounts..."
		umount -f "${cache}/var/cache/apt/archives" > /dev/null 1>&1 || true
		umount -f "${cache}/dev/pts" > /dev/null 1>&1 || true
		umount -f "${cache}/proc" > /dev/null 1>&1 || true
		umount -f "${cache}/sys" > /dev/null 2>&1 || true

		echo -n "Purging the download cache..."
		rm --preserve-root --one-file-system -rf $cache && echo "done." || exit 1

		exit 0
	) 200>/var/lock/subsys/lxc
}

Usage ()
{
	echo "Usage: $(basename ${0}) --apt-recommends=<true,false>|--debconf-frontend=<debconf-frontend>|--debconf-priority=<debconf-priority>|--preseed-file=<preseed-file>|-h|--help -p|--path=<path> --clean"

	return 0
}

for _CONFFILE in /etc/default/lxc /etc/default/lxc.d/*
do
	if [ -e "${_CONFFILE}" ]
	then
		. "${_CONFFILE}" || true
	fi
done

_OPTIONS="$(getopt -o d:hp:n:c -l apt-recommends:,debconf-frontend:,debconf-priority:,distribution:,preseed-file:,help,rootfs:,path:,name:,clean -- "$@")"

if [ "${?}" -ne 0 ]
then
	Usage
	exit 1
fi

eval set -- "${_OPTIONS}"

while true
do
	case "${1}" in
		--apt-recommends)
			_APT_RECOMMENDS="${2}"
			export _APT_RECOMMENDS
			shift 2
			;;

		--debconf-frontend)
			_DEBCONF_FRONTEND="${2}"
			export _DEBCONF_FRONTEND
			shift 2
			;;

		--debconf-priority)
			_DEBCONF_PRIORITY="${2}"
			export _DEBCONF_PRIORITY
			shift 2
			;;

		--preseed-file)
			_PRESEED_FILE="${2}"
			export _PRESEED_FILE
			shift 2
			;;

		-h|--help)
			Usage
			exit 0
			;;

		--rootfs)
			LXC_ROOTFS="${2}"
			shift 2
			;;

		-p|--path)
			_PATH="${2}"
			shift 2
			;;

		-n|--name)
			_NAME="${2}"
			shift 2
			;;

		-c|--clean)
			_CLEAN="${2}"
			shift 2
			;;

		--)
			shift 1
			break
			;;

		*)
			break
			;;
	esac
done

if [ -n "${_CLEAN}" ] && [ -z "${_PATH}" ]
then
	Clean || exit 1
	exit 0
fi

if ! type debootstrap > /dev/null 2>&1 \
&& ! type cdebootstrap > /dev/null 2>&1
then
	echo "E: neither debootstrap nor cdebootstrap found." >&2
	exit 1
fi

if [ -z "${_PATH}" ]
then
	echo "'path' parameter is required"
	exit 1
fi

if [ "$(id -u)" != "0" ]
then
	echo "$(basename ${0}) should be run as 'root'"
	exit 1
fi

_ROOTFS="${LXC_ROOTFS}"

if [ -z "${_ROOTFS}" ]
then
	if grep -Eqs '^ *lxc.rootfs' "${_PATH}/rootfs"
	then
		_ROOTFS="$(grep -E '* ^lxc.rootfs' ${_PATH}/rootfs | awk -F= '{ print $2 }')"
	else
		_ROOTFS="${_PATH}/rootfs"
	fi
fi

mkdir -p "${_ROOTFS}"

# Get distributor from template filename
_MODE="$(basename ${0} | sed -e 's|^lxc-||' -e 's|debconf||')"
export _MODE

# Configure local debconf
_TMPDIR="$(mktemp -d -t $(basename ${0}).XXXXXXXX)"
export _TMPDIR

# FIXME: check umask, nobody should be able to read these files
mkdir -p "${_TMPDIR}/debconf"

cat > "${_TMPDIR}/debconf.systemrc" << EOF
Config: configdb
Templates: templatedb

Name: config
Driver: File
Mode: 644
Reject-Type: password
Filename: ${_TMPDIR}/debconf/config.dat

Name: passwords
Driver: File
Mode: 600
Backup: false
Required: false
Accept-Type: password
Filename: ${_TMPDIR}/debconf/passwords.dat

Name: configdb
Driver: Stack
Stack: config, passwords

Name: templatedb
Driver: File
Mode: 644
Filename: ${_TMPDIR}/debconf/templates.dat
EOF

DEBCONF_SYSTEMRC="${_TMPDIR}/debconf.systemrc"
export DEBCONF_SYSTEMRC

# Run debconf parts
for _SCRIPT in /usr/share/lxc/templates/lxc-debconfig.d/*
do
	if [ -x "${_SCRIPT}" ]
	then
		# FIXME
		#debconf -olxc-debconfig "${_SCRIPT}"
		"${_SCRIPT}"
	fi
done

# Read-in configuration from debconf
. "${_TMPDIR}/debconf.default"

Warnings

Install_system "${_ROOTFS}"

if [ "${?}" -ne 0 ]
then
	echo "failed to install system"
	exit 1
fi

Configure_system "${_ROOTFS}" "${_NAME}"

if [ "${?}" -ne 0 ]
then
	echo "failed to configure system for a container"
	exit 1
fi

Copy_configuration "${_PATH}" "${_ROOTFS}" "${_NAME}"

if [ "${?}" -ne 0 ]
then
	echo "failed write configuration file"
	exit 1
fi

# remove debconf temporary files
rm --preserve-root --one-file-system -rf "${_TMPDIR}"

if [ -n "${_CLEAN}" ]
then
	Clean || exit 1
	exit 0
fi
