#!/bin/sh # container-tools - Manage systemd-nspawn containers # Copyright (C) 2014-2017 Daniel Baumann # # 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 . set -e SCRIPT="${0}" HOOKS="/etc/container-tools/hooks" MACHINES="/var/lib/machines" CACHE="/var/cache/container-tools/images" Parameters () { GETOPT_LONGOPTIONS="bind:,bind-ro:,script:,name:,clean,password:,server:,system:," 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 ;; --clean) CLEAN="true" shift 1 ;; -p|--password) PASSWORD="${2}" shift 2 ;; --server) SERVER="${2}" shift 2 ;; --system) SYSTEM="${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} -- [--clean] [-p|--password PASSWORD] [--server SERVER] [--system SYSTEM]" >&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/bin/curl ] then echo "'${NAME}': /usr/bin/curl - no such file." >&2 exit 1 fi if [ "$(id -u)" -ne 0 ] then echo "'${NAME}': need root privileges" >&2 exit 1 fi if [ -x /usr/bin/lzip ] then COMPRESSION="lz" elif [ -x /usr/bin/xz ] then COMPRESSION="xz" elif [ -x /bin/gzip ] then COMPRESSION="gz" else echo "'${NAME}': no supported compressor available (lz, xz, gz)." exit 1 fi ARCHITECTURE="${ARCHITECTURE:-$(dpkg --print-architecture)}" SYSTEM="${SYSTEM:-debian-stretch-${ARCHITECTURE}.system.tar.${COMPRESSION}}" SETUP="$(echo ${SYSTEM} | sed -e 's|.system.tar.|.setup.tar.|')" SERVER="${SERVER:-https://files.open-infrastructure.net/system/container/debian}" PASSWORD="${PASSWORD:-$(dd if=/dev/urandom bs=12 count=1 2> /dev/null | base64)}" VERSION="$(container version)" # Pre hooks for FILE in "${HOOKS}/pre-${SCRIPT}".* "${HOOKS}/${NAME}.pre-${SCRIPT}" do if [ -x "${FILE}" ] then "${FILE}" fi done # Run # FIXME: Show available image directories on server # FIXME: Fetch debconf-choices.txt to show debconf select # FIXME: gpg verification against debian-keyring or local keyring # FIXME: default server via configuration file mkdir -p "${CACHE}" CURL_OPTIONS="" if curl -V | grep -qs http2 then CURL_OPTIONS="${CURL_OPTIONS} --http2" fi for FILE in "${SYSTEM}" "${SYSTEM}.sha512" \ "${SETUP}" "${SETUP}.sha512" do if curl --fail --head --output /dev/null --silent "${SERVER}/${FILE}" then if [ -e "${CACHE}/${FILE}" ] then CURL_TIME_COND="--time-cond ${CACHE}/${FILE}" else CURL_TIME_COND="" fi echo "Downloading ${FILE}" curl --fail --location --progress-bar --user-agent container-tools/${VERSION} ${CURL_OPTIONS} ${CURL_TIME_COND} \ "${SERVER}/${FILE}" -o "${CACHE}/${FILE}" fi done for FILE in "${SYSTEM}" "${SETUP}" do cd "${CACHE}" if [ ! -e "${FILE}" ] then continue fi if [ -e "${FILE}.sha512" ] then echo -n "Verifying ${FILE}:" set +e sha512sum --check "${FILE}.sha512" --status SHA512SUM="${?}" set -e case "${SHA512SUM}" in 0) echo " ok." ;; *) echo " failed." exit 1 ;; esac fi cd "${OLDPWD}" done case "${SYSTEM}" in *.gz) TAR_OPTIONS="--gzip" if [ ! -e /bin/gzip ] then echo -en "\n" echo "'${NAME}': /bin/lzip - no such file." >&2 exit 1 fi ;; *.lz) TAR_OPTIONS="--lzip" if [ ! -e /usr/bin/lzip ] then echo -en "\n" echo "'${NAME}': /usr/bin/lzip - no such file." >&2 exit 1 fi ;; *.xz) TAR_OPTIONS="--xz" if [ ! -e /usr/bin/xz ] then echo -en "\n" echo "'${NAME}': /usr/bin/xz - no such file." >&2 exit 1 fi ;; *) TAR_OPTIONS="" ;; esac for FILE in "${SYSTEM}" "${SETUP}" do if [ ! -e "${CACHE}/${FILE}" ] then continue fi case "${FILE}" in *.system.tar.*) DIRECTORY="${MACHINES}/${NAME}" ;; *.setup.tar.*) DIRECTORY="${MACHINES}/${NAME}/setup" ;; esac mkdir -p "${DIRECTORY}" if [ -e /usr/bin/pv ] then echo "Unpacking ${FILE}" pv --format '%p' --width 77 "${CACHE}/${FILE}" | tar xf - ${TAR_OPTIONS} -C "${DIRECTORY}" --strip 1 else echo -n "Unpacking ${FILE}:" tar xf "${CACHE}/${FILE}" ${TAR_OPTIONS} -C "${DIRECTORY}" --strip 1 echo " ok." fi done if [ -x "${MACHINES}/${NAME}/setup/container" ] then chroot "${MACHINES}/${NAME}" /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="dialog" DEBIAN_PRIORITY="low" \ DEBCONF_NONINTERACTIVE_SEEN="true" DEBCONF_NOWARNINGS="true" \ /setup/container rm -rf "${MACHINES}/${NAME}/setup" fi # Creating machine-id chroot "${MACHINES}/${NAME}" systemd-machine-id-setup > /dev/null 2>&1 # Setting hostname echo "${NAME}" > "${MACHINES}/${NAME}/etc/hostname" # Copying resolv.conf cp -L /etc/resolv.conf "${MACHINES}/${NAME}/etc/resolv.conf" # Setting root password echo root:${PASSWORD} | chroot "${MACHINES}/${NAME}" chpasswd echo "${NAME}: root password set to '${PASSWORD}'." # Remove cache case "${CLEAN}" in true) rm -f "${CACHE}/${SYSTEM}" "${CACHE}/${SYSTEM}.sha512" rm -f "${CACHE}/${SETUP}" "${CACHE}/${SETUP}.sha512" ;; esac # Post hooks for FILE in "${HOOKS}/post-${SCRIPT}".* "${HOOKS}/${NAME}.post-${SCRIPT}" do if [ -x "${FILE}" ] then "${FILE}" fi done