diff options
Diffstat (limited to '')
-rwxr-xr-x | share/build-scripts/qemu | 1135 |
1 files changed, 1135 insertions, 0 deletions
diff --git a/share/build-scripts/qemu b/share/build-scripts/qemu new file mode 100755 index 0000000..28c5262 --- /dev/null +++ b/share/build-scripts/qemu @@ -0,0 +1,1135 @@ +#!/bin/sh + +# Copyright (C) 2014-2021 Daniel Baumann <daniel.baumann@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:" + 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 + ;; + + --) + 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 + +Mount () +{ + # Mounting rw bind mounts + if [ -n "${BIND}" ] + then + BINDS="$(echo ${BIND} | sed -e 's|;| |g')" + + for ENTRY in ${BINDS} + do + SOURCE="$(echo ${ENTRY} | awk -F: '{ print $1 }')" + TARGET="$(echo ${ENTRY} | awk -F: '{ print $2 }')" + + mkdir -p "${SOURCE}" + mkdir -p "${MACHINES}/${NAME}/${TARGET}" + + mount -o bind "${SOURCE}" "${MACHINES}/${NAME}/${TARGET}" + done + fi + + # Mounting ro bind mounts + if [ -n "${BIND_RO}" ] + then + BINDS_RO="$(echo ${BIND_RO} | sed -e 's|;| |g')" + + for ENTRY in ${BINDS_RO} + do + SOURCE="$(echo ${ENTRY} | awk -F: '{ print $1 }')" + TARGET="$(echo ${ENTRY} | awk -F: '{ print $2 }')" + + mkdir -p "${SOURCE}" + mkdir -p "${MACHINES}/${NAME}/${TARGET}" + + mount -o rbind "${SOURCE}" "${MACHINES}/${NAME}/${TARGET}" + done + fi + + # Mounting overlay mounts + if [ -n "${CNT_OVERLAY}" ] + then + CNT_OVERLAYS="$(echo ${CNT_OVERLAY} | sed -e 's|;| |g')" + + COUNT="0" + for CNT_OVERLAY in ${CNT_OVERLAYS} + do + DIRECTORY_LOWER="$(echo ${CNT_OVERLAY} | awk -F: '{ print $1 }')" + DIRECTORY_UPPER="$(echo ${CNT_OVERLAY} | awk -F: '{ print $2 }')" + DIRECTORY_WORK="$(echo ${CNT_OVERLAY} | awk -F: '{ print $3 }')" + DIRECTORY_MERGED="$(echo ${CNT_OVERLAY} | awk -F: '{ print $4 }')" + + COUNT="$((${COUNT} + 1))" + CNT_OVERLAY_OPTION="$(echo ${CNT_OVERLAY_OPTIONS} | awk -F ';' "{ print \$${COUNT} }")" + + for DIRECTORY in "${DIRECTORY_LOWER}" "${DIRECTORY_UPPER}" "${DIRECTORY_WORK}" "${DIRECTORY_MERGED}" + do + mkdir -p "${DIRECTORY}" + done + + if ! findmnt -n -o SOURCE "${DIRECTORY_MERGED}" | grep -qs '^overlay-' + then + if [ -n "${CNT_OVERLAY_OPTION}" ] + then + CNT_OVERLAY_OPTION="-o ${CNT_OVERLAY_OPTION}" + fi + + mount -t overlay overlay-${NAME} ${CNT_OVERLAY_OPTION} -olowerdir="${DIRECTORY_LOWER}",upperdir="${DIRECTORY_UPPER}",workdir="${DIRECTORY_WORK}" "${DIRECTORY_MERGED}" + fi + done + fi +} + +Umount () +{ + # Unmounting overlay mounts + if [ -n "${CNT_OVERLAY}" ] + then + CNT_OVERLAYS="$(echo ${CNT_OVERLAY} | sed -e 's|;| |g')" + + for CNT_OVERLAY in ${CNT_OVERLAYS} + do + DIRECTORY_LOWER="$(echo ${CNT_OVERLAY} | awk -F: '{ print $1 }')" + DIRECTORY_UPPER="$(echo ${CNT_OVERLAY} | awk -F: '{ print $2 }')" + DIRECTORY_WORK="$(echo ${CNT_OVERLAY} | awk -F: '{ print $3 }')" + DIRECTORY_MERGED="$(echo ${CNT_OVERLAY} | awk -F: '{ print $4 }')" + + umount -f "${DIRECTORY_MERGED}" > /dev/null 2>&1 || true + + for DIRECTORY in "${DIRECTORY_LOWER}" "${DIRECTORY_UPPER}" "${DIRECTORY_WORK}" "${DIRECTORY_MERGED}" + do + rmdir --ignore-fail-on-non-empty --parents ${DIRECTORY} > /dev/null 2>&1 || true + done + done + + # empty workdir otherwise there might happen stale file handles + if [ -d "${DIRECTORY_WORK}" ] + then + rm --preserve-root --one-file-system -rf "${DIRECTORY_WORK}"/* + fi + fi + + # Unmounting ro bind mounts + if [ -n "${BIND_RO}" ] + then + # unmount in reverse order to allow nested bind mounts + BINDS_RO="$(echo ${BIND_RO} | sed -e 's|;| |g' | awk '{ for (i=NF; i>=1; i--) printf "%s ", $i; print ""}')" + + for ENTRY in ${BINDS_RO} + do + TARGET="$(echo ${ENTRY} | awk -F: '{ print $2 }')" + + umount -f "${MACHINES}/${NAME}/${TARGET}" > /dev/null 2>&1 || true + done + fi + + # Unmounting rw bind mounts + if [ -n "${BIND}" ] + then + # unmount in reverse order to allow nested bind mounts + BINDS="$(echo ${BIND} | sed -e 's|;| |g' | awk '{ for (i=NF; i>=1; i--) printf "%s ", $i; print ""}')" + + for ENTRY in ${BINDS} + do + TARGET="$(echo ${ENTRY} | awk -F: '{ print $2 }')" + + umount -f "${MACHINES}/${NAME}/${TARGET}" > /dev/null 2>&1 || true + done + fi + + # 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 +} + +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" + + # 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 + umount -f "${DIRECTORY}/proc" > /dev/null 2>&1 || true + umount -f "${DIRECTORY}/sys" > /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" + + # 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}" + + # Overwrite resolv.conf from cache with hosts resolv.conf + rm -f "${DIRECTORY}/etc/resolv.conf" + cp /etc/resolv.conf "${DIRECTORY}/etc" + + 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}" + +## Specific parts +mkdir -p "${MACHINES}" +cp -a "${CACHE}/${DISTRIBUTION}_${ARCHITECTURE}" "${MACHINES}/${NAME}" + +Mount + +Configure_system "${MACHINES}/${NAME}" +Configure_network "${MACHINES}/${NAME}" +Configure_systemd_networkd "${MACHINES}/${NAME}" +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 |