summaryrefslogtreecommitdiffstats
path: root/share/build-scripts
diff options
context:
space:
mode:
Diffstat (limited to 'share/build-scripts')
-rwxr-xr-xshare/build-scripts/veos1146
1 files changed, 1146 insertions, 0 deletions
diff --git a/share/build-scripts/veos b/share/build-scripts/veos
new file mode 100755
index 0000000..4a0540d
--- /dev/null
+++ b/share/build-scripts/veos
@@ -0,0 +1,1146 @@
+#!/bin/sh
+
+# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@open-infrastructure.net>
+# 2021 Simon Spöhel <simon.spoehel@open-infrastructure.net>
+#
+# SPDX-License-Identifier: GPL-3.0+
+#
+# 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 <https://www.gnu.org/licenses/>.
+
+set -e
+
+PROJECT="open-infrastructure"
+SOFTWARE="compute-tools"
+PROGRAM="container"
+
+CONFIG="/etc/${SOFTWARE}/config"
+HOOKS="/etc/${SOFTWARE}/hooks"
+MACHINES="/var/lib/machines"
+
+SCRIPT="${0}"
+export SCRIPT
+
+Parameters ()
+{
+ GETOPT_LONGOPTIONS="bind:,bind-ro:,script:,name:,preseed-file:,boot-method:,"
+ GETOPT_OPTIONS="b:,s:,n:,p:"
+
+ PARAMETERS="$(getopt --longoptions ${GETOPT_LONGOPTIONS} --name=${SCRIPT} --options ${GETOPT_OPTIONS} --shell sh -- "${@}")"
+
+ if [ "${?}" != "0" ]
+ then
+ echo "'${SCRIPT}': getopt exit" >&2
+ exit 1
+ fi
+
+ eval set -- "${PARAMETERS}"
+
+ while true
+ do
+ case "${1}" in
+ -b|--bind)
+ # ignore
+ shift 2
+ ;;
+
+ --bind-ro)
+ # ignore
+ shift 2
+ ;;
+
+ --cnt.auto)
+ # ignore
+ shift 2
+ ;;
+
+ --cnt.container-server)
+ # ignore
+ shift 2
+ ;;
+
+ -s|--script)
+ # ignore
+ shift 2
+ ;;
+
+ -n|--name)
+ NAME="${2}"
+ shift 2
+ ;;
+
+ --preseed-file)
+ PRESEED_FILE="${2}"
+ shift 2
+ ;;
+
+ --boot-method)
+ BOOT_METHOD="${2}"
+ shift 2
+ ;;
+
+ --)
+ shift 1
+ break
+ ;;
+
+ *)
+ echo "'${SCRIPT}': getopt error" >&2
+ exit 1
+ ;;
+ esac
+ done
+}
+
+Usage ()
+{
+ echo "Usage: container create -n|--name NAME -s|--script ${SCRIPT} -- [-p|--preseed-file FILE]" >&2
+ exit 1
+}
+
+Parameters "${@}"
+
+if [ -z "${NAME}" ]
+then
+ Usage
+fi
+
+if [ -e "${MACHINES}/${NAME}" ]
+then
+ echo "'${NAME}': container already exists" >&2
+ exit 1
+fi
+
+if [ ! -x /usr/sbin/debootstrap ]
+then
+ echo "'${NAME}': /usr/sbin/debootstrap - no such file." >&2
+ exit 1
+fi
+
+if [ "$(id -u)" -ne 0 ]
+then
+ echo "'${NAME}': need root privileges" >&2
+ exit 1
+fi
+
+case "${BOOT_METHOD}" in
+ "")
+ BOOT_METHOD="efi"
+ ;;
+
+ *)
+ ;;
+esac
+
+Create_Disk ()
+{
+ # FIXME image size as arg or preseed
+ IMG_SIZE=12000
+
+ dd if=/dev/zero of="${MACHINES}/${NAME}.img" bs=1024k count=0 seek="${IMG_SIZE}"
+ parted "${MACHINES}/${NAME}.img" mklabel gpt
+
+
+ case "${BOOT_METHOD}" in
+ bios)
+ # FIXME number partitions and size scrip and everything
+ parted -a optimal "${MACHINES}/${NAME}.img" mkpart BIOS-BOOT 1MB 2MB
+ parted "${MACHINES}/${NAME}.img" set 1 bios_grub on
+ parted -a optimal "${MACHINES}/${NAME}.img" mkpart SYSTEM 2MB 100%
+ SYSTEM_PARTITION="p2"
+ ;;
+
+ efi)
+ parted -a optimal "${MACHINES}/${NAME}.img" mkpart EFI fat32 1M 1000M
+ parted "${MACHINES}/${NAME}.img" set 1 esp on
+ parted -a optimal "${MACHINES}/${NAME}.img" mkpart SYSTEM 1000MB 100%
+ EFI_PARTITION="p1"
+ SYSTEM_PARTITION="p2"
+ ;;
+
+ *)
+ echo "Error, unrecognized BOOT_METHOD" >&2
+ exit 1
+ esac
+
+ kpartx -av "${MACHINES}/${NAME}.img"
+ # FIXME loop number hardcoded
+ mkfs.ext4 "/dev/mapper/loop0${SYSTEM_PARTITION}"
+
+ if [ ! -z "${EFI_PARTITION}" ]
+ then
+ mkfs.vfat -F 32 "/dev/mapper/loop0${EFI_PARTITION}"
+ fi
+
+ # FIXME swap, more discs
+}
+
+Mount_Disk ()
+{
+ # FIXME probably not necessary
+ mkdir -p "${MACHINES}/${NAME}"
+
+ # FIXME partition number hardcoded
+ mount "/dev/mapper/loop0${SYSTEM_PARTITION}" "${MACHINES}/${NAME}"
+}
+
+Umount ()
+{
+ # Unmounting pseudo-filesystems
+ umount -f "${DIRECTORY}/dev/pts" > /dev/null 2>&1 || true
+ umount -f "${DIRECTORY}/proc" > /dev/null 2>&1 || true
+ umount -f "${DIRECTORY}/sys" > /dev/null 2>&1 || true
+ umount -f "${DIRECTORY}/dev" > /dev/null 2>&1 || true
+ umount -f "${DIRECTORY}/boot/efi" > /dev/null 2>&1 || true
+
+ # Unmounting disk images
+ # FIXME unmount additinal images
+ umount -f "${DIRECTORY}" > /dev/null 2>&1 || true
+ kpartx -d "${MACHINES}/${NAME}.img"
+}
+
+Chroot ()
+{
+ CHROOT="${1}"
+ shift
+
+ chroot "${CHROOT}" /usr/bin/env -i \
+ LC_ALL="C" PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games" 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}" \
+ ${@}
+}
+
+Upgrade_system ()
+{
+ DIRECTORY="${1}"
+
+ # Mount pseudo-filesystems
+ mount -o bind /dev/pts "${DIRECTORY}/dev/pts"
+ mount -o bind /proc "${DIRECTORY}/proc"
+ mount -o bind /sys "${DIRECTORY}/sys"
+
+ # also mount dev
+ mount -o bind /dev "${DIRECTORY}/dev"
+
+ if [ ! -z "${EFI_PARTITION}" ]
+ then
+ mkdir -p "${DIRECTORY}/boot/efi"
+ mount "/dev/mapper/loop0${EFI_PARTITION}" "${DIRECTORY}/boot/efi"
+ fi
+ # Disable dpkg syncing
+
+cat > "${DIRECTORY}/etc/dpkg/dpkg.cfg.d/${SOFTWARE}" << EOF
+force-unsafe-io
+EOF
+
+ # Create policy-rc.d file
+
+cat > "${DIRECTORY}/usr/sbin/policy-rc.d" << EOF
+#!/bin/sh
+echo "All runlevel operations denied by policy" >&2
+exit 101
+EOF
+
+ chmod 0755 "${DIRECTORY}/usr/sbin/policy-rc.d"
+
+ # Upgrade system
+ Chroot "${DIRECTORY}" "apt update"
+ Chroot "${DIRECTORY}" "apt --yes --option Dpkg::Options::=--force-confnew upgrade"
+ Chroot "${DIRECTORY}" "apt --yes --option Dpkg::Options::=--force-confnew dist-upgrade"
+
+ # Install systemd support packages
+ Chroot "${DIRECTORY}" "apt --yes install dbus libpam-systemd systemd-sysv"
+
+ Chroot "${DIRECTORY}" "apt clean"
+}
+
+Cleanup_system ()
+{
+ DIRECTORY="${1}"
+
+ Chroot "${DIRECTORY}" "apt --yes --purge autoremove"
+ Chroot "${DIRECTORY}" "apt clean"
+
+ # Cleanup
+ rm -f "${DIRECTORY}/etc/dpkg/dpkg.cfg.d/${SOFTWARE}"
+ rm -f "${DIRECTORY}/usr/sbin/policy-rc.d"
+
+ # Unmount pseudo-filesystems
+ umount -f "${DIRECTORY}/dev/pts" > /dev/null 2>&1 || true
+ # WARNING: dev/pts is still mounted!
+ umount -f "${DIRECTORY}/proc" > /dev/null 2>&1 || true
+ umount -f "${DIRECTORY}/sys" > /dev/null 2>&1 || true
+ umount -f "${DIRECTORY}/dev" > /dev/null 2>&1 || true
+ umount -f "${DIRECTORY}/boot/efi" > /dev/null 2>&1 || true
+}
+
+Debconf ()
+{
+ # Configure local debconf
+ mkdir -p "/tmp/${SOFTWARE}"
+ DEBCONF_TMPDIR="$(mktemp -d -p "/tmp/${SOFTWARE}" -t $(basename ${0}).XXXX)"
+ export DEBCONF_TMPDIR
+
+ mkdir -p "${DEBCONF_TMPDIR}/debconf"
+
+cat > "${DEBCONF_TMPDIR}/debconf.systemrc" << EOF
+Config: configdb
+Templates: templatedb
+
+Name: config
+Driver: File
+Mode: 644
+Reject-Type: password
+Filename: ${DEBCONF_TMPDIR}/debconf/config.dat
+
+Name: passwords
+Driver: File
+Mode: 600
+Backup: false
+Required: false
+Accept-Type: password
+Filename: ${DEBCONF_TMPDIR}/debconf/passwords.dat
+
+Name: configdb
+Driver: Stack
+Stack: config, passwords
+
+Name: templatedb
+Driver: File
+Mode: 644
+Filename: ${DEBCONF_TMPDIR}/debconf/templates.dat
+EOF
+
+ DEBCONF_SYSTEMRC="${DEBCONF_TMPDIR}/debconf.systemrc"
+ export DEBCONF_SYSTEMRC
+}
+
+Debootstrap ()
+{
+ DIRECTORY="${1}"
+
+ EXCLUDE="ifupdown"
+ INCLUDE="dbus,linux-image-amd64,grub-pc"
+
+ # apt repositories
+ INCLUDE="${INCLUDE},gnupg"
+
+ if ( echo "${MIRROR}" | grep -qs '^https' ) || \
+ ( echo "${PARENT_MIRROR}" | grep -qs '^https' )
+ then
+ INCLUDE="${INCLUDE},apt-transport-https,ca-certificates"
+ fi
+
+ case "${MODE}" in
+ progress-linux)
+ INCLUDE="${INCLUDE},progress-linux,gnupg"
+ ;;
+ esac
+
+ mkdir -p "$(dirname ${DIRECTORY})"
+ debootstrap --verbose --arch=${ARCHITECTURE} --components=${PARENT_ARCHIVE_AREAS} \
+ --exclude=${EXCLUDE} --include=${INCLUDE} ${PARENT_DISTRIBUTION} "${DIRECTORY}" ${PARENT_MIRROR}
+}
+
+Configure_apt ()
+{
+ DIRECTORY="${1}"
+
+ # Configure apt
+ rm -f "${DIRECTORY}/etc/apt/sources.list"
+
+ PARENT_AREA="$(echo ${PARENT_ARCHIVE_AREAS} | sed -e 's|,| |g')"
+ PARENT_DIST="$(echo ${PARENT_DISTRIBUTION} | sed -e 's|-backports||')"
+
+cat > "${DIRECTORY}/etc/apt/sources.list.d/debian.list" << EOF
+# /etc/apt/sources.list.d/debian.list
+
+deb ${PARENT_MIRROR} ${PARENT_DIST} ${PARENT_AREA}
+EOF
+
+ case "${MODE}" in
+ progress-linux)
+
+cat > "${DIRECTORY}/progress-linux.cfg" << EOF
+progress-linux progress-linux/archives multiselect $(echo ${ARCHIVES} | sed -e 's| |, |g')
+progress-linux progress-linux/archive-areas multiselect $(echo ${ARCHIVE_AREAS} | sed -e 's|,| |g')
+EOF
+
+ Chroot "${DIRECTORY}" "debconf-set-selections progress-linux.cfg"
+ Chroot "${DIRECTORY}" "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=criticial dpkg-reconfigure progress-linux"
+
+ rm -f "${DIRECTORY}/progress-linux.cfg"
+
+ Chroot "${DIRECTORY}" "apt update"
+ ;;
+ esac
+}
+
+Deconfigure_system ()
+{
+ DIRECTORY="${1}"
+
+ # Configure fstab
+
+cat > "${DIRECTORY}/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
+
+ # Fix /etc/mtab
+ rm -f "${DIRECTORY}/etc/mtab"
+ ln -s /proc/self/mounts "${DIRECTORY}/etc/mtab"
+
+ # Removing resolv.conf
+ rm -f "${DIRECTORY}/etc/resolv.conf"
+ cp /etc/resolv.conf "${DIRECTORY}/etc"
+
+ # Removing hosts/hostname
+ rm -f "${DIRECTORY}"/etc/hosts
+ rm -f "${DIRECTORY}"/etc/hostname
+
+ # Removing openssh-server host keys
+ rm -f "${DIRECTORY}"/etc/ssh/ssh_host_*_key
+ rm -f "${DIRECTORY}"/etc/ssh/ssh_host_*_key.pub
+}
+
+Configure_system ()
+{
+ DIRECTORY="${1}"
+
+ echo "${NAME}" > "${DIRECTORY}/etc/hostname"
+
+ # Configure apt
+ rm -f "${DIRECTORY}/etc/apt/sources.list"
+
+ PARENT_AREA="$(echo ${PARENT_ARCHIVE_AREAS} | sed -e 's|,| |g')"
+ PARENT_DIST="$(echo ${PARENT_DISTRIBUTION} | sed -e 's|-backports||')"
+
+cat > "${DIRECTORY}/etc/apt/sources.list.d/debian.list" << EOF
+# /etc/apt/sources.list.d/debian.list
+
+deb ${PARENT_MIRROR} ${PARENT_DIST} ${PARENT_AREA}
+EOF
+
+ for PARENT_REPO in ${PARENT_ARCHIVES}
+ do
+ case "${PARENT_REPO}" in
+ buster-security)
+ echo "deb ${PARENT_MIRROR_SECURITY} ${PARENT_DIST}/updates ${PARENT_AREA}" >> "${DIRECTORY}/etc/apt/sources.list.d/debian.list"
+ ;;
+
+ ${PARENT_DIST}-security)
+ echo "deb ${PARENT_MIRROR_SECURITY} ${PARENT_DIST}-security ${PARENT_AREA}" >> "${DIRECTORY}/etc/apt/sources.list.d/debian.list"
+ ;;
+
+ ${PARENT_DIST}-updates)
+ echo "deb ${PARENT_MIRROR} ${PARENT_DIST}-updates ${PARENT_AREA}" >> "${DIRECTORY}/etc/apt/sources.list.d/debian.list"
+ ;;
+
+ ${PARENT_DIST}-backports)
+ echo "deb ${PARENT_MIRROR} ${PARENT_DIST}-backports ${PARENT_AREA}" >> "${DIRECTORY}/etc/apt/sources.list.d/debian.list"
+ ;;
+
+ ${PARENT_DIST}-proposed-updates)
+ echo "deb ${PARENT_MIRROR} ${PARENT_DIST}-proposed-updates ${PARENT_AREA}" >> "${DIRECTORY}/etc/apt/sources.list.d/debian.list"
+ ;;
+
+ experimental)
+ echo "deb ${PARENT_MIRROR} experimental ${PARENT_AREA}" >> "${DIRECTORY}/etc/apt/sources.list.d/debian.list"
+ ;;
+ esac
+ done
+
+ case "${MODE}" in
+ progress-linux)
+
+cat > "${DIRECTORY}/progress-linux.cfg" << EOF
+progress-linux progress-linux/archives multiselect $(echo ${ARCHIVES} | sed -e 's| |, |g')
+progress-linux progress-linux/archive-areas multiselect $(echo ${ARCHIVE_AREAS} | sed -e 's|,| |g')
+EOF
+
+ Chroot "${DIRECTORY}" "debconf-set-selections progress-linux.cfg"
+ Chroot "${DIRECTORY}" "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=criticial dpkg-reconfigure progress-linux"
+
+ rm -f "${DIRECTORY}/progress-linux.cfg"
+ ;;
+ esac
+
+ if [ "${APT_RECOMMENDS}" = "false" ]
+ then
+
+cat > "${DIRECTORY}/etc/apt/apt.conf.d/recommends.conf" << EOF
+APT::Install-Recommends "false";
+EOF
+
+ fi
+
+ # Add local archives configured from preseed file
+ if ls "${DEBCONF_TMPDIR}/apt"/*.list > /dev/null 2>&1
+ then
+ cp "${DEBCONF_TMPDIR}/apt"/*.list "${DIRECTORY}/etc/apt/sources.list.d"
+
+ if ls "${DEBCONF_TMPDIR}/apt"/*.key > /dev/null 2>&1
+ then
+ for KEY in "${DEBCONF_TMPDIR}/apt"/*.key
+ do
+ cp "${KEY}" "${DIRECTORY}"
+ Chroot "${DIRECTORY}" "apt-key add $(basename ${KEY})"
+ rm -f "${DIRECTORY}/$(basename ${KEY})"
+ done
+ fi
+
+ if ls "${DEBCONF_TMPDIR}/apt"/*.pref > /dev/null 2>&1
+ then
+ cp "${DEBCONF_TMPDIR}/apt"/*.pref "${DIRECTORY}/etc/apt/preferences.d"
+ fi
+ fi
+
+ Upgrade_system "${DIRECTORY}"
+
+ # Preseed system
+ if [ -n "${PRESEED_FILE}" ]
+ then
+ for FILE in ${PRESEED_FILE}
+ do
+ if [ -e /usr/bin/kdig ]
+ then
+ DIG="/usr/bin/kdig"
+ elif [ -e /usr/bin/dig ]
+ then
+ DIG="/usr/bin/dig"
+ fi
+
+ if [ -n "${DIG}" ]
+ then
+ IPV4_ADDRESS1="$(${DIG} A +short ${NAME} | tail -n1)"
+ IPV4_ADDRESS1_PART1="$(echo ${IPV4_ADDRESS1} | cut -d. -f1)"
+ IPV4_ADDRESS1_PART2="$(echo ${IPV4_ADDRESS1} | cut -d. -f2)"
+ IPV4_ADDRESS1_PART3="$(echo ${IPV4_ADDRESS1} | cut -d. -f3)"
+ IPV4_ADDRESS1_PART4="$(echo ${IPV4_ADDRESS1} | cut -d. -f4)"
+
+ IPV6_ADDRESS1="$(${DIG} AAAA +short ${NAME} | tail -n1)"
+ # TODO: address parts
+ fi
+
+ sed -e "s|@NAME@|${NAME}|g" \
+ -e "s|@IPV4_ADDRESS1@|${IPV4_ADDRESS1}|g" \
+ -e "s|@IPV4_ADDRESS1_PART1@|${IPV4_ADDRESS1_PART1}|g" \
+ -e "s|@IPV4_ADDRESS1_PART2@|${IPV4_ADDRESS1_PART2}|g" \
+ -e "s|@IPV4_ADDRESS1_PART3@|${IPV4_ADDRESS1_PART3}|g" \
+ -e "s|@IPV4_ADDRESS1_PART4@|${IPV4_ADDRESS1_PART4}|g" \
+ -e "s|@IPV6_ADDRESS1@|${IPV6_ADDRESS1}|g" \
+ "${FILE}" >> "${DIRECTORY}/preseed.cfg"
+ done
+
+ Chroot "${DIRECTORY}" "debconf-set-selections preseed.cfg"
+
+ rm -f "${DIRECTORY}/preseed.cfg"
+ fi
+
+ # Manual hack to workaround broken preseeding in locales package
+ if [ -n "${PRESEED_FILE}" ]
+ then
+ for FILE in ${PRESEED_FILE}
+ do
+ if grep -qs locales "${FILE}"
+ then
+ if Chroot "${DIRECTORY}" dpkg --get-selections | awk '{ print $1 }' | grep -qs '^locales$'
+ then
+ rm -f "${DIRECTORY}/etc/default/locale" "${DIRECTORY}/etc/locale.gen"
+ Chroot "${DIRECTORY}" "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=criticial dpkg-reconfigure locales"
+
+ break
+ fi
+ fi
+ done
+ fi
+
+ # Manual hack to create conffiles when using locales-all instead of locales
+ if [ ! -e "${DIRECTORY}/etc/environment" ]
+ then
+ echo "LANG=C.UTF-8" >> "${DIRECTORY}/etc/environment"
+ fi
+
+ if [ ! -e "${DIRECTORY}/etc/default/locale" ]
+ then
+ echo "LANG=C.UTF-8" >> "${DIRECTORY}/etc/default/locale"
+ fi
+
+ # 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 "${DIRECTORY}/etc/localtime" "${DIRECTORY}/etc/timezone"
+ Chroot "${DIRECTORY}" "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=criticial dpkg-reconfigure tzdata"
+
+ break
+ fi
+ done
+ fi
+
+ if [ -n "${PACKAGES}" ]
+ then
+ Chroot "${DIRECTORY}" "apt --option Dpkg::Options::=--force-confnew --yes install ${PACKAGES}"
+ fi
+
+ # Manual hack to regenerate ssh keys
+ if Chroot "${DIRECTORY}" dpkg --get-selections | awk '{ print $1 }' | grep -qs '^openssh-server$' && \
+ ! ls "${DIRECTORY}"/etc/ssh/ssh_host_*_key > /dev/null 2>&1
+ then
+ Chroot "${DIRECTORY}" "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=criticial dpkg-reconfigure openssh-server"
+ fi
+
+ # container command
+ if [ -n "${CONTAINER_COMMAND}" ]
+ then
+ echo "${CONTAINER_COMMAND}" > "${DIRECTORY}/.container-command"
+
+ chmod 0755 "${DIRECTORY}/.container-command"
+ Chroot "${DIRECTORY}" "sh /.container-command"
+
+ rm -f "${DIRECTORY}/.container-command"
+ fi
+
+ for NUMBER in $(seq 1 ${CONTAINER_COMMAND_NUMBER})
+ do
+ eval COMMAND="$`echo CONTAINER_COMMAND${NUMBER}`"
+
+ echo "${COMMAND}" > "${DIRECTORY}/.container-command"
+
+ chmod 0755 "${DIRECTORY}/.container-command"
+ Chroot "${DIRECTORY}" "sh /.container-command"
+
+ rm -f "${DIRECTORY}/.container-command"
+ done
+}
+
+Configure_network ()
+{
+ DIRECTORY="${1}"
+
+ # Create /etc/resolv.conf
+ rm -f "${DIRECTORY}/etc/resolv.conf.tmp"
+
+ if [ -n "${NAMESERVER_DOMAIN}" ]
+ then
+ echo "domain ${NAMESERVER_DOMAIN}" >> "${DIRECTORY}/etc/resolv.conf.tmp"
+ fi
+
+ if [ -n "${NAMESERVER_SEARCH}" ]
+ then
+ echo "search ${NAMESERVER_SEARCH}" >> "${DIRECTORY}/etc/resolv.conf.tmp"
+ fi
+
+ if [ -n "${NAMESERVER_SERVER}" ]
+ then
+ if [ -e "${DIRECTORY}/etc/resolv.conf.tmp" ]
+ then
+ echo "" >> "${DIRECTORY}/etc/resolv.conf.tmp"
+ fi
+
+ for NAMESERVER in $(echo ${NAMESERVER_SERVER} | sed -e 's|,| |g')
+ do
+ echo "nameserver ${NAMESERVER}" >> "${DIRECTORY}/etc/resolv.conf.tmp"
+ done
+ fi
+
+ if [ -n "${NAMESERVER_OPTIONS}" ]
+ then
+ if [ -e "${DIRECTORY}/etc/resolv.conf.tmp" ]
+ then
+ echo "" >> "${DIRECTORY}/etc/resolv.conf.tmp"
+ fi
+
+ echo "options ${NAMESERVER_OPTIONS}" >> "${DIRECTORY}/etc/resolv.conf.tmp"
+ fi
+
+ if [ -e "${DIRECTORY}/etc/resolv.conf.tmp" ]
+ then
+ mv "${DIRECTORY}/etc/resolv.conf.tmp" "${DIRECTORY}/etc/resolv.conf"
+ fi
+
+ # Create /etc/hosts
+ rm -f "${DIRECTORY}/etc/hosts.tmp"
+
+ if [ -n "${NETWORK1_IPV4_ADDRESS}" ]
+ then
+
+cat >> "${DIRECTORY}/etc/hosts.tmp" << EOF
+${NETWORK1_IPV4_ADDRESS} ${NAME}
+EOF
+
+ fi
+
+ if [ -n "${NETWORK1_IPV6_ADDRESS}" ]
+ then
+
+cat >> "${DIRECTORY}/etc/hosts.tmp" << EOF
+${NETWORK1_IPV6_ADDRESS} ${NAME}
+EOF
+
+ fi
+
+ if [ -n "${NETWORK1_IPV4_ADDRESS}" ] || [ -n "${NETWORK1_IPV6_ADDRESS}" ]
+ then
+ echo >> "${DIRECTORY}/etc/hosts.tmp"
+ fi
+
+ if [ -z "${NETWORK1_IPV4_ADDRESS}" ] && [ -z "${NETWORK1_IPV6_ADDRESS}" ]
+ then
+ # localhost only
+
+cat > "${DIRECTORY}/etc/hosts.tmp" << EOF
+127.0.0.1 localhost ${NAME}
+::1 localhost ${NAME}
+EOF
+
+ else
+
+cat > "${DIRECTORY}/etc/hosts.tmp" << EOF
+127.0.0.1 localhost
+::1 localhost
+EOF
+
+ fi
+
+cat >> "${DIRECTORY}/etc/hosts.tmp" << EOF
+
+# The following lines are desirable for IPv6 capable hosts
+::1 ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+EOF
+
+ mv "${DIRECTORY}/etc/hosts.tmp" "${DIRECTORY}/etc/hosts"
+}
+
+Configure_systemd_networkd ()
+{
+ DIRECTORY="${1}"
+
+ # Enable systemd-networkd
+ chroot "${DIRECTORY}" systemctl enable systemd-networkd
+
+ for NUMBER in $(seq 1 ${NETWORK_NUMBER})
+ do
+ eval IPV4_COMMENT="$`echo NETWORK${NUMBER}_IPV4_COMMENT`"
+ eval IPV4_METHOD="$`echo NETWORK${NUMBER}_IPV4_METHOD`"
+ eval IPV4_ADDRESS="$`echo NETWORK${NUMBER}_IPV4_ADDRESS`"
+ eval IPV4_GATEWAY="$`echo NETWORK${NUMBER}_IPV4_GATEWAY`"
+ eval IPV4_NETMASK="$`echo NETWORK${NUMBER}_IPV4_NETMASK`"
+ eval IPV4_POST_UP="$`echo NETWORK${NUMBER}_IPV4_POST_UP`"
+ eval IPV4_POST_DOWN="$`echo NETWORK${NUMBER}_IPV4_POST_DOWN`"
+
+ eval IPV6_COMMENT="$`echo NETWORK${NUMBER}_IPV6_COMMENT`"
+ eval IPV6_METHOD="$`echo NETWORK${NUMBER}_IPV6_METHOD`"
+ eval IPV6_ADDRESS="$`echo NETWORK${NUMBER}_IPV6_ADDRESS`"
+ eval IPV6_GATEWAY="$`echo NETWORK${NUMBER}_IPV6_GATEWAY`"
+ eval IPV6_NETMASK="$`echo NETWORK${NUMBER}_IPV6_NETMASK`"
+ eval IPV6_POST_UP="$`echo NETWORK${NUMBER}_IPV6_POST_UP`"
+ eval IPV6_POST_DOWN="$`echo NETWORK${NUMBER}_IPV6_POST_DOWN`"
+
+ if [ "${IPV4_METHOD}" != "none" ] || [ "${IPV6_METHOD}" != "none" ]
+ then
+
+cat > "${DIRECTORY}/etc/systemd/network/eno${NUMBER}.network" << EOF
+[Match]
+Name=eno${NUMBER}
+EOF
+
+ fi
+
+ if [ -n "${IPV4_METHOD}" ] && [ "${IPV4_METHOD}" != "none" ]
+ then
+
+cat >> "${DIRECTORY}/etc/systemd/network/eno${NUMBER}.network" << EOF
+
+[Network]
+EOF
+
+ if [ -n "${IPV4_COMMENT}" ]
+ then
+ echo "Description=${IPV4_COMMENT}" >> "${DIRECTORY}/etc/systemd/network/eno${NUMBER}.network"
+ fi
+
+ case "${IPV4_METHOD}" in
+ dhcp)
+
+cat >> "${DIRECTORY}/etc/systemd/network/eno${NUMBER}.network" << EOF
+DHCP=ipv4
+EOF
+
+ ;;
+
+ static)
+
+cat >> "${DIRECTORY}/etc/systemd/network/eno${NUMBER}.network" << EOF
+DHCP=no
+Address=${IPV4_ADDRESS}/${IPV4_NETMASK}
+EOF
+
+ if [ -n "${IPV4_GATEWAY}" ]
+ then
+
+cat >> "${DIRECTORY}/etc/systemd/network/eno${NUMBER}.network" << EOF
+Gateway=${IPV4_GATEWAY}
+EOF
+
+ fi
+ ;;
+ esac
+
+ if [ -n "${IPV4_POST_UP}" ]
+ then
+
+cat > "${DIRECTORY}/etc/systemd/system/cnt-ipv4-post-up-eno${NUMBER}.service" << EOF
+[Unit]
+Description=${SOFTWARE} IPV4_POST_UP
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sh -c "${IPV4_POST_UP}"
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+ chroot "${DIRECTORY}" systemctl enable cnt-ipv4-post-up-eno${NUMBER}.service
+ fi
+
+ if [ -n "${IPV4_POST_DOWN}" ]
+ then
+
+cat > "${DIRECTORY}/etc/systemd/system/cnt-ipv4-post-down-eno${NUMBER}.service" << EOF
+[Unit]
+Description=${SOFTWARE} IPV4_POST_DOWN
+After=network.target
+Wants=network.target
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sh -c "${IPV4_POST_DOWN}"
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+ chroot "${DIRECTORY}" systemctl enable cnt-ipv4-post-down-eno${NUMBER}.service
+ fi
+ fi
+
+ if [ -n "${IPV6_METHOD}" ] && [ "${IPV6_METHOD}" != "none" ]
+ then
+
+cat >> "${DIRECTORY}/etc/systemd/network/eno${NUMBER}.network" << EOF
+
+[Network]
+EOF
+
+ if [ -n "${IPV6_COMMENT}" ]
+ then
+ echo "Description=${IPV6_COMMENT}" >> "${DIRECTORY}/etc/systemd/network/eno${NUMBER}.network"
+ fi
+
+ case "${IPV6_METHOD}" in
+ dhcp)
+
+cat >> "${DIRECTORY}/etc/systemd/network/eno${NUMBER}.network" << EOF
+DHCP=ipv6
+EOF
+
+ ;;
+
+ static)
+
+cat >> "${DIRECTORY}/etc/systemd/network/eno${NUMBER}.network" << EOF
+DHCP=no
+IPv6AcceptRA=no
+Address=${IPV6_ADDRESS}/${IPV6_NETMASK}
+EOF
+
+ if [ -n "${IPV6_GATEWAY}" ]
+ then
+
+cat >> "${DIRECTORY}/etc/systemd/network/eno${NUMBER}.network" << EOF
+Gateway=${IPV6_GATEWAY}
+EOF
+
+ fi
+ ;;
+ esac
+
+ if [ -n "${IPV6_POST_UP}" ]
+ then
+
+cat > "${DIRECTORY}/etc/systemd/system/cnt-ipv6-post-up-eno${NUMBER}.service" << EOF
+[Unit]
+Description=${SOFTWARE} IPV6_POST_UP
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sh -c "${IPV6_POST_UP}"
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+ chroot "${DIRECTORY}" systemctl enable cnt-ipv6-post-up-eno${NUMBER}.service
+ fi
+
+ if [ -n "${IPV6_POST_DOWN}" ]
+ then
+
+cat > "${DIRECTORY}/etc/systemd/system/cnt-ipv6-post-down-eno${NUMBER}.service" << EOF
+[Unit]
+Description=${SOFTWARE} IPV6_POST_DOWN
+After=network.target
+Wants=network.target
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sh -c "${IPV6_POST_DOWN}"
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+ chroot "${DIRECTORY}" systemctl enable cnt-ipv6-post-down-eno${NUMBER}.service
+ fi
+ fi
+
+ NUMBER="$((${NUMBER} + 1))"
+ done
+}
+
+Commands ()
+{
+ DIRECTORY="${1}"
+
+ # maximum of 15 characters, prefix is 'veth-'
+ HOSTNAME_SHORT="$(echo ${NAME} | cut -c-8)"
+ HOST_INTERFACE_NAME="$(echo ${NETWORK1_VETH:-veth-${HOSTNAME_SHORT}-0})"
+
+ sed -i -e "s|^cnt.auto=.*|cnt.auto=${CNT_AUTO}|g" \
+ -e "s|^cnt.container-server=.*|cnt.container-server=${CNT_CONTAINER_SERVER}|g" \
+ -e "s|^cnt.network-bridge=.*|cnt.network-bridge=${HOST_INTERFACE_NAME}:${NETWORK1_BRIDGE:-bridge0}|g" \
+ -e "s|^cnt.overlay=.*|cnt.overlay=${CNT_OVERLAY}|g" \
+ -e "s|^cnt.overlay-options=.*|cnt.overlay-options=${CNT_OVERLAY_OPTIONS}|g" \
+ -e "s|^bind=.*|bind=${BIND}|g" \
+ -e "s|^bind-ro=.*|bind-ro=${BIND_RO}|g" \
+ -e "s|^network-veth-extra=.*|network-veth-extra=${HOST_INTERFACE_NAME}:eno1|g" \
+ "${CONFIG}/${NAME}.conf"
+
+ if [ "${NETWORK_NUMBER}" -ge 2 ]
+ then
+ for NUMBER in $(seq 2 ${NETWORK_NUMBER})
+ do
+ eval IPV4_METHOD="$`echo NETWORK${NUMBER}_IPV4_METHOD`"
+ eval IPV6_METHOD="$`echo NETWORK${NUMBER}_IPV6_METHOD`"
+
+ if [ -z "${IPV4_METHOD}" ] && [ -z "${IPV6_METHOD}" ]
+ then
+ continue
+ fi
+
+ eval HOST_INTERFACE_NAME="$`echo NETWORK${NUMBER}_VETH`"
+
+ HOST_INTERFACE_NAME="$(echo ${HOST_INTERFACE_NAME:-veth-${HOSTNAME_SHORT}-${NUMBER}})"
+ CONTAINER_INTERFACE_NAME="eno${NUMBER}"
+
+ sed -i -e "/^register=.*/ a network-veth-extra=${HOST_INTERFACE_NAME}:${CONTAINER_INTERFACE_NAME}" "${CONFIG}/${NAME}.conf"
+
+ eval BRIDGE="$`echo NETWORK${NUMBER}_BRIDGE`"
+ sed -i -e "/^register=.*/ a cnt.network-bridge=${HOST_INTERFACE_NAME}:${BRIDGE:-bridge${NUMBER}}" "${CONFIG}/${NAME}.conf"
+ done
+ fi
+
+ # Setting root password
+ echo root:${ROOT_PASSWORD} | chroot "${DIRECTORY}" chpasswd
+
+ # Host command
+ if [ -n "${HOST_COMMAND}" ]
+ then
+ echo "${HOST_COMMAND}" > "${DIRECTORY}/.host-command"
+
+ cd "${DIRECTORY}"
+ sh "${DIRECTORY}/.host-command"
+ cd "${OLDPWD}"
+
+ rm -f "${DIRECTORY}/.host-command"
+ fi
+
+ for NUMBER in $(seq 1 ${HOST_COMMAND_NUMBER})
+ do
+ eval COMMAND="$`echo HOST_COMMAND${NUMBER}`"
+
+ echo "${COMMAND}" > "${DIRECTORY}/.host-command"
+
+ cd "${DIRECTORY}"
+ sh "${DIRECTORY}/.host-command"
+ cd "${OLDPWD}"
+
+ rm -f "${DIRECTORY}/.host-command"
+ done
+
+ # Show root password in case its automatically set
+ case "${ROOT_RANDOM_PASSWORD}" in
+ true)
+ echo "${NAME}: root password set to '${ROOT_PASSWORD}'."
+ ;;
+ esac
+}
+
+# Trap function
+trap 'Umount' EXIT HUP INT QUIT TERM
+
+umask 0022
+
+export NAME
+
+Debconf
+
+# Pre hooks
+for FILE in "${HOOKS}/pre-${SCRIPT}".* "${HOOKS}/${NAME}.pre-${SCRIPT}"
+do
+ if [ -x "${FILE}" ]
+ then
+ "${FILE}"
+ fi
+done
+
+# Run debconf parts
+for DEBCONF_SCRIPT in "/usr/share/${SOFTWARE}/scripts/debconf.d"/*
+do
+ if [ -x "${DEBCONF_SCRIPT}" ]
+ then
+ "${DEBCONF_SCRIPT}"
+ fi
+done
+
+# Read-in configuration from debconf
+. "${DEBCONF_TMPDIR}/debconf.default"
+
+CACHE="/var/cache/${PROGRAM}/${MODE}"
+SYSTEM="${MACHINES}/${NAME}"
+
+## Generic parts
+if [ ! -e "${CACHE}/${DISTRIBUTION}_${ARCHITECTURE}" ]
+then
+ Debootstrap "${CACHE}/${DISTRIBUTION}_${ARCHITECTURE}.tmp"
+ Configure_apt "${CACHE}/${DISTRIBUTION}_${ARCHITECTURE}.tmp"
+ Deconfigure_system "${CACHE}/${DISTRIBUTION}_${ARCHITECTURE}.tmp"
+
+ mv "${CACHE}/${DISTRIBUTION}_${ARCHITECTURE}.tmp" "${CACHE}/${DISTRIBUTION}_${ARCHITECTURE}"
+fi
+
+Upgrade_system "${CACHE}/${DISTRIBUTION}_${ARCHITECTURE}" || echo "W: If upgrading the system failed, try removing the cache for your distribution in /var/cache/${PROGRAM}"
+Cleanup_system "${CACHE}/${DISTRIBUTION}_${ARCHITECTURE}"
+
+# FIXME workaround: /dev/pts in CACHE is still mounted without the following line
+umount -f "${DIRECTORY}/dev/pts" > /dev/null 2>&1 || true
+
+## Specific parts
+Create_Disk
+Mount_Disk
+
+# FIXME this is evil
+cp -a "${CACHE}/${DISTRIBUTION}_${ARCHITECTURE}/"* "${MACHINES}/${NAME}/"
+
+Configure_system "${MACHINES}/${NAME}"
+Configure_network "${MACHINES}/${NAME}"
+Configure_systemd_networkd "${MACHINES}/${NAME}"
+
+# Configure fstab
+
+# FIXME DIRECTORY
+cat > "${DIRECTORY}/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>
+
+UUID=$(lsblk --fs --noheadings --output UUID /dev/mapper/loop0${SYSTEM_PARTITION}) / ext4 errors=remount-ro 0 1
+EOF
+
+
+# configure grub
+cat > "${MACHINES}/${NAME}/tmp/device.map" << EOF
+(hd0) /dev/loop0
+(hd0,gpt1) /dev/mapper/loop0p1
+(hd0,gpt2) /dev/mapper/loop0p2
+EOF
+
+case "${BOOT_METHOD}" in
+ bios)
+ grub-install \
+ --no-floppy \
+ --grub-mkdevicemap="${MACHINES}/${NAME}/tmp/device.map" \
+ --modules="biosdisk part_gpt ext2 configfile normal multiboot" \
+ --root-directory="${MACHINES}/${NAME}" \
+ /dev/loop0
+ ;;
+
+ efi)
+ # FIXME
+ chroot "${MACHINES}/${NAME}" /bin/sh -c 'DEBIAN_FRONTEND=noninteractive; apt-get install grub-efi -y'
+ chroot "${MACHINES}/${NAME}" grub-install --efi-directory=/boot/efi --no-bootsector --target x86_64-efi --no-nvram --no-uefi-secure-boot --force-extra-removable
+
+ # FIXME DIRECTORY
+ echo "UUID=$(lsblk --fs --noheadings --output UUID /dev/mapper/loop0${EFI_PARTITION}) /boot/efi vfat umask=0077 0 0" >> "${DIRECTORY}/etc/fstab"
+ ;;
+
+ *)
+ echo "Error, unrecognized BOOT_METHOD" >&2
+ exit 1
+esac
+
+chroot "${MACHINES}/${NAME}" update-grub
+
+
+Cleanup_system "${MACHINES}/${NAME}"
+
+Commands "${MACHINES}/${NAME}"
+
+# remove debconf temporary files
+rm --preserve-root --one-file-system -rf "${DEBCONF_TMPDIR}"
+rmdir --ignore-fail-on-non-empty "/tmp/${SOFTWARE}" 2>&1 || true
+
+# Post hooks
+for FILE in "${HOOKS}/post-${SCRIPT}".* "${HOOKS}/${NAME}.post-${SCRIPT}"
+do
+ if [ -x "${FILE}" ]
+ then
+ "${FILE}"
+ fi
+done