#!/bin/sh # Copyright (C) 2014-2020 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 <http://www.gnu.org/licenses/>. set -e PROJECT="open-infrastructure" PROGRAM="container" COMMAND="$(basename ${0})" CONFIG="/etc/${PROJECT}/${PROGRAM}/config" HOOKS="/etc/${PROJECT}/${PROGRAM}/hooks" MACHINES="/var/lib/machines" SCRIPTS="/usr/share/${PROJECT}/${PROGRAM}/scripts" CONFIG_TEMPLATE="/usr/share/${PROJECT}/${PROGRAM}/config/container.conf.in" Parameters () { GETOPT_LONGOPTIONS="name:,cnt.container-server:,cnt.overlay:,bind:,bind-ro:,capability:,drop-capability:script:,verbose," GETOPT_OPTIONS="n:,b:,c:,d:,s:,v," PARAMETERS="$(getopt --longoptions ${GETOPT_LONGOPTIONS} --name=${COMMAND} --options ${GETOPT_OPTIONS} --shell sh -- ${@})" if [ "${?}" != "0" ] then echo "'${COMMAND}': getopt exit" >&2 exit 1 fi eval set -- "${PARAMETERS}" while true do case "${1}" in -n|--name) NAME="${2}" shift 2 ;; --cnt.auto) CNT_AUTO="${2}" shift 2 ;; --cnt.container-server) CNT_CONTAINER_SERVER="${2}" shift 2 ;; --cnt.overlay) CNT_OVERLAY="${2}" shift 2 ;; -b|--bind) BIND="${2}" shift 2 ;; --bind-ro) BIND_RO="${2}" shift 2 ;; -c|--capability) CAPABILITY="${2}" shift 2 ;; -d|--drop-capability) DROP_CAPABILITY="${2}" shift 2 ;; -s|--script) SCRIPT="${2}" shift 2 ;; -v|--verbose) VERBOSE="true" shift 1 ;; --) shift 1 break ;; *) echo "'${COMMAND}': getopt error" >&2 exit 1 ;; esac done } Usage () { echo "Usage: ${PROGRAM} ${COMMAND} -n|--name NAME [--cnt.container-server=true|false|FQDN] [--cnt.overlay=DIRECTORY_LOWER:DIRECTORY_UPPER:DIRECTORY_WORK:DIRECTORY_MERGED] [-b|--bind DIRECTORY:DIRECTORY[:OPTIONS]] [--bind-ro DIRECTORY:DIRECTORY[:OPTIONS]] [-c|--capability CAPABILITY[,CAPABILITY]] [-d|--drop-capability DROP_CAPABILITY[,DROP_CAPABILITY]] [-s|--script SCRIPT] [-v|--verbose] [-- SCRIPT_OPTIONS]" >&2 exit 1 } Parameters "${@}" if [ -z "${NAME}" ] then Usage fi case "${NAME}" in ALL) echo "'${NAME}': name 'ALL' is reserved to expand to all available container" >&2 exit 1 ;; esac if [ -e "${CONFIG}/${NAME}.conf" ] then echo "'${NAME}': container already exists or ${CONFIG}/${NAME}.conf has not been removed" >&2 exit 1 fi if [ -z "${SCRIPT}" ] then if [ -e "${SCRIPTS}/default" ] then TARGET="$(basename $(readlink ${SCRIPTS}/default))" case "${TARGET}" in container_script) TARGET="$(basename $(readlink /etc/alternatives/container_script))" ;; esac if [ -e "${SCRIPTS}/${TARGET}" ] then SCRIPT="${TARGET}" else echo "default -> '${TARGET}': no such script" >&2 exit 1 fi else SCRIPT="debian" fi else if [ ! -e "${SCRIPTS}/${SCRIPT}" ] then echo "'${SCRIPT}': no such script" >&2 exit 1 fi fi case "${VERBOSE}" in true) cat << EOF ################################################################################ Creating container: ${NAME} ################################################################################ EOF ;; esac CNT_CONTAINER_SERVER="${CNT_CONTAINER_SERVER:-$(hostname -f 2> /dev/null || hostname)}" # Pre hooks for FILE in "${HOOKS}/pre-${COMMAND}".* "${HOOKS}/${NAME}.pre-${COMMAND}" do if [ -x "${FILE}" ] then "${FILE}" fi done # Creating rw bind mounts if [ -n "${BIND}" ] then BINDS="$(echo ${BIND} | sed -e 's|;| |g')" for ENTRY in ${BINDS} do DIRECTORY="$(echo ${ENTRY} | awk -F: '{ print $1 }')" mkdir -p "${DIRECTORY}" done fi # Creating ro bind mounts if [ -n "${BIND_RO}" ] then BINDS_RO="$(echo ${BIND_RO} | sed -e 's|;| |g')" for ENTRY in ${BINDS_RO} do DIRECTORY="$(echo ${ENTRY} | awk -F: '{ print $1 }')" mkdir -p "${DIRECTORY}" done fi # Creating overlay mounts if [ -n "${CNT_OVERLAY}" ] then CNT_OVERLAYS="$(echo ${CNT_OVERLAY} | sed -e 's|;| |g')" for ENTRY in ${CNT_OVERLAYS} do DIRECTORY_LOWER="$(echo ${ENTRY} | awk -F: '{ print $1 }')" DIRECTORY_UPPER="$(echo ${ENTRY} | awk -F: '{ print $2 }')" DIRECTORY_WORK="$(echo ${ENTRY} | awk -F: '{ print $3 }')" DIRECTORY_MERGED="$(echo ${ENTRY} | awk -F: '{ print $4 }')" for DIRECTORY in "${DIRECTORY_LOWER}" "${DIRECTORY_UPPER}" "${DIRECTORY_WORK}" "${DIRECTORY_MERGED}" do mkdir -p "${DIRECTORY}" done done fi # config mkdir -p "${CONFIG}" sed -e "s|@CNT_AUTO@|${CNT_AUTO}|g" \ -e "s|@CNT_CONTAINER_SERVER@|${CNT_CONTAINER_SERVER}|g" \ -e "s|@CNT_NETWORK_BRIDGE@|${CNT_NETWORK_BRIDGE}|g" \ -e "s|@CNT_OVERLAY@|${CNT_OVERLAY}|g" \ -e "s|@NAME@|${NAME}|g" \ -e "s|@BIND@|${BIND}|g" \ -e "s|@BIND_RO@|${BIND_RO}|g" \ -e "s|@BOOT@|yes|g" \ -e "s|@CAPABILITY@|${CAPABILITY}|g" \ -e "s|@DIRECTORY@|${MACHINES}/${NAME}|g" \ -e "s|@DROP_CAPABILITY@|${DROP_CAPABILITY}|g" \ -e "s|@LINK_JOURNAL@|no|g" \ -e "s|@MACHINE@|${NAME}|g" \ -e "s|@NETWORK_VETH_EXTRA@|${NETWORK_VETH_EXTRA}|g" \ -e "s|@PRIVATE_USERS@|no|g" \ -e "s|@REGISTER@|yes|g" \ "${CONFIG_TEMPLATE}" > "${CONFIG}/${NAME}.conf" # Run "${SCRIPTS}/${SCRIPT}" $(echo "${@}" | sed -e 's| -- | |') # Post hooks for FILE in "${HOOKS}/post-${COMMAND}".* "${HOOKS}/${NAME}.post-${COMMAND}" do if [ -x "${FILE}" ] then "${FILE}" fi done # done echo "'${NAME}': container created."