Files
Proxmox-Container-Template/root/usr/sbin/pct-template
Cantibra 286c907af4
All checks were successful
Proxmox LXC Template Tool (Noble) / Proxmox LXC Template Tool [arm64] (push) Successful in 11s
Proxmox LXC Template Tool (Noble) / Proxmox LXC Template Tool [amd64] (push) Successful in 12s
Proxmox LXC Template Tool (Trixie) / Proxmox LXC Template Tool [arm64] (push) Successful in 11s
Proxmox LXC Template Tool (Trixie) / Proxmox LXC Template Tool [amd64] (push) Successful in 11s
Initialize Git Repository: 'Proxmox-Container-Template'
2026-01-26 06:10:26 +01:00

395 lines
15 KiB
Bash

#!/usr/bin/env bash
###
#
# Options Section
#
###
set -e
set -u
set -o pipefail
###
#
# Function Section
#
###
function bind_chroot () {
message "Bind file system '/dev' ..."
if ! /usr/bin/grep --quiet "${CT_REALPATH}/dev" '/proc/mounts'; then
/usr/bin/mount --bind '/dev' --target "${CT_REALPATH}/dev" &> '/dev/null' || \
error "\r\rBind file system '/dev' [ERROR]\n" \
" => The file system '/dev' could not be mounted.\n"
fi
message "\r\rBind filesystem '/dev' [OK]\n"
message "Bind file system '/dev/pts' ..."
if ! /usr/bin/grep --quiet "${CT_REALPATH}/dev/pts" '/proc/mounts'; then
/usr/bin/mount --bind '/dev/pts' --target "${CT_REALPATH}/dev/pts" &> '/dev/null' || \
error "\r\rBind file system '/dev/pts' [ERROR]\n" \
" => The file system '/dev/pts' could not be mounted.\n"
fi
message "\r\rBind file system '/dev/pts' [OK]\n"
message "Bind file system '/dev/mqueue' ..."
if ! /usr/bin/grep --quiet "${CT_REALPATH}/dev/mqueue" '/proc/mounts'; then
/usr/bin/mount --bind '/dev/mqueue' --target "${CT_REALPATH}/dev/mqueue" &> '/dev/null' || \
error "\r\rBind files ystem '/dev/mqueue' [ERROR]\n" \
" => The file system '/dev/mqueue' could not be mounted.\n"
fi
message "\r\rBind files ystem '/dev/mqueue' [OK]\n"
message "Slave file system '/dev' ..."
if ! /usr/bin/grep --quiet "${CT_REALPATH}/dev" '/proc/mounts'; then
/usr/bin/mount --make-rslave --target "${CT_REALPATH}/dev" &> '/dev/null' || \
error "\r\rSlave file system '/dev' [ERROR]\n" \
" => The file system '/dev' could not be slaved.\n"
fi
message "\r\rSlave file system '/dev' [OK]\n"
message "Bind file system '/proc' ..."
if ! /usr/bin/grep --quiet "${CT_REALPATH}/proc" '/proc/mounts'; then
/usr/bin/mount --types 'proc' --source '/proc' --target "${CT_REALPATH}/proc" &> '/dev/null' || \
error "\r\rBind file system '/proc' [ERROR]\n" \
" => The file system '/proc' could not be mounted.\n"
fi
message "\r\rBind file system '/proc' [OK]\n"
message "Bind file system '/sys' ..."
if ! /usr/bin/grep --quiet "${CT_REALPATH}/sys" '/proc/mounts'; then
/usr/bin/mount --types 'sysfs' --source '/sys' --target "${CT_REALPATH}/sys" &> '/dev/null' || \
error "\r\rBind file system '/sys' [ERROR]\n" \
" => The file system '/sys' could not be mounted.\n"
fi
message "\r\rBind file system '/sys' [OK]\n"
}
function consistency () {
message 'Check container consistency ...'
/usr/bin/test -d "${CT_REALPATH}" || \
error '\r\rCheck container consistency [ERROR]\n' \
' => The selected container does not exist.\n'
/usr/bin/test -f "${CT_REALPATH}/etc/os-release" || \
error '\r\rCheck container consistency [ERROR]\n' \
' => The selected container does not contain any system files.\n'
message '\r\rCheck container consistency [OK]\n'
}
function error () {
if [ -n "${1:-}" ]; then
/usr/bin/echo -n -e "${1}"
fi
if [ -n "${2:-}" ]; then
/usr/bin/echo -n -e "${2}"
fi
exit 1
}
function help () {
local HELP
case "${1}" in
command)
read -r -d '' HELP << EOL
Usage: pct-template (OPTION) (CONTAINER)
Options:
[ -c ] [ CONTAINER ] | Chroot Container
[ -g ] [ CONTAINER ] | Generate Container Template
[ -h ] | Help
[ -p ] [ CONTAINER ] | Prepare Container
[ -r ] [ CONTAINER ] | Container Repository
EOL
;;
chroot)
read -r -d '' HELP << EOL
Usage: pct-template -c (CONTAINER)
Description:
Change the root environment into the
container.
EOL
;;
generate)
read -r -d '' HELP << EOL
Usage: pct-template -g (CONTAINER)
Description:
Compresses the container with the data
compression format 'xz' and generates a
hash sum of it.
EOL
;;
prepare)
read -r -d '' HELP << EOL
Usage: pct-template -p (CONTAINER)
Description:
Prepare and/or clean up the container.
EOL
;;
repository)
read -r -d '' HELP << EOL
Usage: pct-template -r (CONTAINER)
Description:
Download one of the following images from
the repository.
- Debian GNU/Linux
- arm64_debian_bookworm
- arm64_debian_trixie
- Ubuntu GNU/Linux
- arm64_ubuntu_noble-numbat
EOL
;;
esac
/usr/bin/echo -e "${HELP}"
}
function message () {
if [ -n "${1:-}" ]; then
/usr/bin/echo -n -e "${1}"
fi
}
function resolvconf () {
local OPTION="${1}"
case "${OPTION}" in
prepare)
message 'Prepare DNS-Resolver ...'
if [[ -L "${CT_REALPATH}/etc/resolv.conf" ]]; then
/usr/bin/unlink "${CT_REALPATH}/etc/resolv.conf" || \
error '\r\rPrepare DNS-Resolver [ERROR]\n' \
" => The 'resolv.conf' could not be unlinked.\n"
fi
/usr/bin/cp --force '/etc/resolv.conf' "${CT_REALPATH}/etc/resolv.conf" || \
error '\r\rPrepare DNS-Resolver [ERROR]\n' \
" => The 'resolv.conf' could not be copied into the container.\n"
message '\r\rPrepare DNS-Resolver [OK]\n'
;;
reset)
message 'Reset DNS-Resolver ...'
if [[ -L "${CT_REALPATH}/etc/resolv.conf" ]]; then
/usr/bin/unlink "${CT_REALPATH}/etc/resolv.conf" || \
error '\r\rReset DNS-Resolver [ERROR]\n' \
" => The 'resolv.conf' could not be unlinked.\n"
fi
/usr/bin/cp --force '/usr/share/pct-template/etc/resolv.conf' "${CT_REALPATH}/etc/resolv.conf" || \
error '\r\rReset DNS-Resolver [ERROR]\n' \
" => The file '${CT_REALPATH}/etc/resolv.conf' could not be replaced.\n"
message '\r\rReset DNS-Resolver [OK]\n'
;;
esac
}
function unbind () {
message "Unbind file system '/dev' ..."
if /usr/bin/grep --quiet "${CT_REALPATH}/dev" '/proc/mounts'; then
/usr/bin/umount --recursive "${CT_REALPATH}/dev" || \
error "\r\rUnbind file system '/dev' [ERROR]\n" \
" => The file system '/dev' could not be unmounted from the container.\n"
fi
message "\r\rUnbind file system '/dev' [OK]\n"
message "Unbind file system '/proc' ..."
if /usr/bin/grep --quiet "${CT_REALPATH}/proc" '/proc/mounts'; then
/usr/bin/umount --recursive "${CT_REALPATH}/proc" || \
error "\r\rUnbind file system '/proc' [ERROR]\n" \
" => The file system '/proc' could not be unmounted from the container.\n"
fi
message "\r\rUnbind file system '/proc' [OK]\n"
message "Unbind file system '/sys' ..."
if /usr/bin/grep --quiet "${CT_REALPATH}/sys" '/proc/mounts'; then
/usr/bin/umount --recursive "${CT_REALPATH}/sys" || \
error "\r\rUnbind file system '/sys' [ERROR]\n" \
" => The file system '/sys' could not be unmounted from the container.\n"
fi
message "\r\rUnbind file system '/sys' [OK]\n"
}
###
#
# Runtime Environment
#
###
/usr/bin/echo -e 'Proxmox LXC Template Tool\n'
if [[ "${EUID}" -ne '0' ]]; then
/usr/bin/echo -e 'Error: Permission Denied'
exit 1
fi
while getopts ':c:g:hp:r:' OPT; do
case "${OPT}" in
c)
CT_BASENAME=$(/usr/bin/basename "${OPTARG}")
CT_REALPATH=$(/usr/bin/realpath "${OPTARG}")
consistency
trap 'unbind' EXIT
bind_chroot
trap "resolvconf 'reset' && unbind" EXIT
resolvconf 'prepare'
message '\n ... Enter chroot environment ... \n\n'
/usr/bin/sleep '3s'
/usr/sbin/chroot "${CT_REALPATH}" || true
message '\n ... Exit chroot environment ... \n\n'
/usr/bin/sleep '3s'
exit 0
;;
g)
CT_BASENAME=$(/usr/bin/basename "${OPTARG}")
CT_REALPATH=$(/usr/bin/realpath "${OPTARG}")
CT_DIRNAME=$(/usr/bin/dirname "${CT_REALPATH}")
consistency
trap "/usr/bin/rm --force ${CT_DIRNAME}/${CT_BASENAME}.tar.xz"* EXIT
message 'Generate container template ...'
/usr/bin/tar --create --use-compress-program="/usr/bin/xz -9 --extreme" --file "${CT_DIRNAME}/${CT_BASENAME}.tar.xz" --directory="${CT_REALPATH}" '.' || \
error '\r\rGenerate container template [ERROR]\n' \
' => The container folder could not be compressed.\n'
message '\r\rGenerate container template [OK]\n'
message 'Generate container hashsum ...'
/usr/bin/sha512sum "${CT_DIRNAME}/${CT_BASENAME}.tar.xz" | /usr/bin/sed --expression='s, .*/, ,' >> "${CT_DIRNAME}/${CT_BASENAME}.tar.xz.sha512sum" || \
error '\r\rGenerate container hashsum [ERROR]\n' \
' => The container hashsum could not be generated.\n'
message '\r\rGenerate container hashsum [OK]\n'
trap '' EXIT
exit 0
;;
h)
set +e
help 'command'
exit 0
;;
p)
CT_BASENAME=$(/usr/bin/basename "${OPTARG}")
CT_REALPATH=$(/usr/bin/realpath "${OPTARG}")
CT_DIRNAME=$(/usr/bin/dirname "${CT_REALPATH}")
consistency
trap "/usr/bin/rm --force ${CT_DIRNAME}/.${CT_BASENAME}" EXIT
message 'Prepare the planned file remove list ...'
if [ -f "/usr/share/pct-template/prepare/${CT_BASENAME}" ]; then
/usr/bin/cp --update='none' "/usr/share/pct-template/prepare/${CT_BASENAME}" "${CT_DIRNAME}/.${CT_BASENAME}" || \
error '\r\rPrepare the planned file remove list [ERROR]\n' \
" => The remove list '${CT_BASENAME}' could not be copied.\n"
elif [[ ! -f "/usr/share/pct-template/prepare/${CT_BASENAME}" ]]; then
/usr/bin/cp --update='none' "/usr/share/pct-template/prepare/default" "${CT_DIRNAME}/.${CT_BASENAME}" || \
error '\r\rPrepare the planned file remove list [ERROR]\n' \
" => The remove list 'default' could not be copied.\n"
fi
/usr/bin/editor "${CT_DIRNAME}/.${CT_BASENAME}" || \
error '\r\rPrepare the planned file remove list [ERROR]\n' \
" => The remove list '.${CT_BASENAME}' could not be opened.\n"
message '\r\rPrepare the planned file remove list [OK]\n'
message 'Remove files from remove list ...'
mapfile -t CLEANUP_LIST < <(/usr/bin/cat "${CT_DIRNAME}/.${CT_BASENAME}" | /usr/bin/sed '/#.*/d' | while read -r CLEANUP_PATH; do /usr/bin/echo "${CT_REALPATH}${CLEANUP_PATH}"; done)
for CLEANUP_LIST in "${CLEANUP_LIST[@]}"; do
/usr/bin/sh -c "/usr/bin/ls --directory ${CLEANUP_LIST} | /usr/bin/xargs /usr/bin/rm --force --recursive" &> '/dev/null' || \
error '\r\rRemove files from remove list [ERROR]\n' \
" => The file or directory '${CLEANUP_LIST}' could not be removed.\n"
done
unset CLEANUP_LIST
message '\r\rRemove files from remove list [OK]\n'
message 'Cleaning up the log files ...'
mapfile -t CLEANUP_LIST < <(/usr/bin/find "${CT_REALPATH}/var/log" -type 'f')
for CLEANUP_LIST in "${CLEANUP_LIST[@]}"; do
/usr/bin/dd if=/dev/null of="${CLEANUP_LIST}" &> '/dev/null' || \
error '\r\rCleaning up the log files [ERROR]\n' \
" => The file '${CLEANUP_LIST}' could not be emptied.\n"
done
unset CLEANUP_LIST
mapfile -t CLEANUP_LIST < <(/usr/bin/find "${CT_REALPATH}/var/logrotate" -type 'f')
for CLEANUP_LIST in "${CLEANUP_LIST[@]}"; do
/usr/bin/sh -c "/usr/bin/ls --directory ${CLEANUP_LIST} | /usr/bin/xargs /usr/bin/rm --force --recursive" &> '/dev/null' || \
error '\r\rCleaning up the log files [ERROR]\n' \
" => The file '${CLEANUP_LIST}' could not be removed.\n"
done
message '\r\rCleaning up the log files [OK]\n'
message 'Load container information ...'
source "${CT_REALPATH}/etc/os-release" || \
error '\r\rcontainer information [ERROR]\n' \
" => The system information '/etc/os-release' could not be loaded.\n"
message '\r\rLoad container information [OK]\n'
message 'Prepare container hostname ...'
/usr/bin/echo 'LXC_NAME' > "${CT_REALPATH}/etc/hostname" || \
error '\r\rPrepare container hostname [ERROR]\n' \
" => The file '/etc/hostname' could not be written.\n"
message '\r\rPrepare container hostname [OK]\n'
message 'Prepare container hosts ...'
/usr/bin/cp --force '/usr/share/pct-template/etc/hosts' "${CT_REALPATH}/etc/hosts" || \
error '\r\rPrepare container hosts [ERROR]\n' \
" => The file '/etc/hosts' could not be replaced.\n"
message '\r\rPrepare container hosts [OK]\n'
case "${NAME}" in
'Debian GNU/Linux')
message 'Prepare container network interfaces ...'
/usr/bin/cp --force '/usr/share/pct-template/etc/interfaces' "${CT_REALPATH}/etc/network/interfaces" || \
error '\r\rPrepare container network interfaces [ERROR]\n' \
" => The file '/etc/network/interfaces' could not be replaced.\n"
message '\r\rPrepare container network interfaces [OK]\n'
;;
esac
resolvconf 'reset'
exit 0
;;
r)
message 'Download container template ...'
if [[ -d "./${OPTARG}" ]]; then
error '\r\rDownload container template [ERROR]\n' \
" => The container '${OPTARG}' already exists.\n"
fi
trap "/usr/bin/rm --force --recursive ./${OPTARG}.tar.xz"* EXIT
/usr/bin/wget --quiet --output-document="./${OPTARG}.tar.xz" --no-hsts "https://repository.privlab.it/pct-template/${OPTARG}.tar.xz" &> '/dev/null' || \
error '\r\rDownload container template [ERROR]\n' \
" => The file '${OPTARG}.tar.xz' could not be downloaded.\n"
/usr/bin/wget --quiet --output-document="./${OPTARG}.tar.xz.sha512sum" --no-hsts "https://repository.privlab.it/pct-template/${OPTARG}.tar.xz.sha512sum" &> '/dev/null' || \
error '\r\rDownload container template [ERROR]\n' \
" => The file '${OPTARG}.tar.xz.sha512sum' could not be downloaded.\n"
message '\r\rDownload container template [OK]\n'
message 'Security check of the container archive ...'
/usr/bin/sha512sum --check --strict "./${OPTARG}.tar.xz.sha512sum" &> '/dev/null' || \
error '\r\rSecurity check of the container archive [ERROR]\n' \
" => The SHA512 hash value for '${OPTARG}.tar.xz' could not be verified.\n"
message '\r\rSecurity check of the container archive [OK]\n'
message 'Decompress container archive ...'
/usr/bin/install --directory "./${OPTARG}" || \
error '\r\rDecompress container archive [ERROR]\n' \
" => The path '${OPTARG}' could not be installed.\n"
/usr/bin/tar --extract --file="./${OPTARG}.tar.xz" --directory="./${OPTARG}" || \
error '\r\rDecompress container archive [ERROR]\n' \
" => The container archive '${OPTARG}.tar.xz' could not be decompressed.\n"
/usr/bin/rm --force "./${OPTARG}.tar.xz" || \
error '\r\rDecompress container archive [ERROR]\n' \
" => The container archive '${OPTARG}.tar.xz' could not be removed.\n"
message '\r\rDecompress container archive [OK]\n'
trap "/usr/bin/rm --force --recursive ./${OPTARG}.tar.xz.sha512sum"* EXIT
exit 0
;;
-*|*)
set +e
case "${1}" in
-a)
help 'archive'
exit 0
;;
-c)
help 'chroot'
exit 0
;;
-p)
help 'prepare'
exit 0
;;
-r)
help 'repository'
exit 0
;;
esac
help 'command'
exit 0
;;
esac
done
set +e
help 'command'
shift $((OPTIND-1))