Files
Proxmox-LXC-Tool/root/usr/sbin/pve-lxc
Cantibra 0d9d472779
All checks were successful
Proxmox LXC Tool / Proxmox LXC Tool (Bookworm) [arm64] (push) Successful in 11s
Proxmox LXC Tool / Proxmox LXC Tool (Bookworm) [amd64] (push) Successful in 10s
Inital Commit
2025-10-26 08:05:51 +01:00

363 lines
14 KiB
Bash

#!/usr/bin/env bash
###
#
# Options Section
#
###
set -e
set -u
set -o pipefail
###
#
# Function Section
#
###
function ct_check () {
message 'Check container consistency'
/usr/bin/test -d "${CT_REALPATH}" || \
error '\r\rCheck container consistency \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The selected folder can not be found.\n'
message '\r\rCheck container consistency \xE2\x9C\x94\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: pve-lxc (OPTION) (CONTAINER)
Options:
[ -a ] [ CONTAINER ] | Archive Container
[ -c ] [ CONTAINER ] | Chroot Container
[ -h ] | Help
[ -p ] [ CONTAINER ] | Prepare Container
[ -r ] [ CONTAINER ] | Container Repository
EOL
;;
archive)
read -r -d '' HELP << EOL
Usage: pve-lxc -a (CONTAINER)
Description:
Compresses the container with
the data compression format
'xz' and generates a hash sum
of it.
EOL
;;
chroot)
read -r -d '' HELP << EOL
Usage: pve-lxc -c (CONTAINER)
Description:
Change the root environment
into the container.
EOL
;;
prepare)
read -r -d '' HELP << EOL
Usage: pve-lxc -p (CONTAINER)
Description:
Prepare and/or clean up the
container.
EOL
;;
repository)
read -r -d '' HELP << EOL
Usage: pve-lxc -r (CONTAINER)
Description:
Download one of the following
images from the repository.
- Debian GNU/Linux [ARM64]
- arm64_debian_bookworm
- Ubuntu GNU/Linux [ARM64]
- arm64_ubuntu_noble-numbat
EOL
;;
esac
/usr/bin/echo -e "${HELP}"
}
function message () {
if [ -n "${1:-}" ]; then
/usr/bin/echo -n -e "${1}"
fi
}
###
#
# Runtime Environment
#
###
/usr/bin/echo -e 'Proxmox Virtual Environment Container Tool\n'
if [[ "${EUID}" -ne '0' ]]; then
/usr/bin/echo -e 'Error: Permission Denied'
exit 1
fi
while getopts ':a:c:hp:r:' OPT; do
case "${OPT}" in
a)
CT_BASENAME=$(/usr/bin/basename "${OPTARG}")
CT_REALPATH=$(/usr/bin/realpath "${OPTARG}")
CT_DIRNAME=$(/usr/bin/dirname "${CT_REALPATH}")
ct_check
message 'Compress container'
/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 \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The container path can not be compressed.\n'
message '\r\rGenerate container \xE2\x9C\x94\n'
message 'Generate hash'
/usr/bin/sha512sum "${CT_DIRNAME}/${CT_BASENAME}.tar.xz" | /usr/bin/sed --expression='s, .*/, ,' >> "${CT_DIRNAME}/${CT_BASENAME}.tar.xz.sha512sum" || \
error '\r\rGenerate hash \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The container hash can not be generated.\n'
message '\r\rGenerate hash \xE2\x9C\x94\n'
exit 0
;;
c)
CT_BASENAME=$(/usr/bin/basename "${OPTARG}")
CT_REALPATH=$(/usr/bin/realpath "${OPTARG}")
ct_check
message "Bind filesystem '/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 filesystem '/dev' \xE2\x9D\x8C\n" \
' \xE2\x86\x92 Error: The filesystem can not be binded.\n'
fi
message "\r\rBind filesystem '/dev' \xE2\x9C\x94\n"
message "Bind filesystem '/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 filesystem '/dev/pts' \xE2\x9D\x8C\n" \
' \xE2\x86\x92 Error: The filesystem can not be binded.\n'
fi
message "\r\rBind filesystem '/dev/pts' \xE2\x9C\x94\n"
message "Bind filesystem '/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 filesystem '/dev/mqueue' \xE2\x9D\x8C\n" \
' \xE2\x86\x92 Error: The filesystem can not be bounded.\n'
fi
message "\r\rBind filesystem '/dev/mqueue' \xE2\x9C\x94\n"
message "Slave filesystem '/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 filesystem '/dev' \xE2\x9D\x8C\n" \
' \xE2\x86\x92 Error: The filesystem can not be slaved.\n'
fi
message "\r\rSlave filesystem '/dev' \xE2\x9C\x94\n"
message "Bind filesystem '/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 filesystem '/proc' \xE2\x9D\x8C\n" \
' \xE2\x86\x92 Error: The filesystem can not be bounded.\n'
fi
message "\r\rBind filesystem '/proc' \xE2\x9C\x94\n"
message "Bind filesystem '/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 filesystem '/sys' \xE2\x9D\x8C\n" \
' \xE2\x86\x92 Error: The filesystem can not be bounded.\n'
fi
message "\r\rBind filesystem '/sys' \xE2\x9C\x94\n"
message 'Prepare System-Resolver'
RESOLVCONF='0'
if [[ -L "${CT_REALPATH}/etc/resolv.conf" ]]; then
RESOLVCONF='1'
/usr/bin/unlink "${CT_REALPATH}/etc/resolv.conf" || \
error '\r\rPrepare System-Resolver \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The resolv.conf can not be unlinked.\n'
fi
/usr/bin/cp --force '/etc/resolv.conf' "${CT_REALPATH}/etc/resolv.conf" || \
error '\r\rPrepare System-Resolver \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The resolv.conf can not be copied.\n'
message '\r\rPrepare System-Resolver \xE2\x9C\x94\n'
message '\n ... exit 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'
message 'Reset System-Resolver'
/usr/bin/rm --force "${CT_REALPATH}/etc/resolv.conf" || \
error '\r\rReset System-Resolver \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The resolv.conf can not be removed.\n'
if [[ "${RESOLVCONF}" == '1' ]]; then
/usr/bin/ln --force --symbolic '../run/systemd/resolve/stub-resolv.conf' "${CT_REALPATH}/etc/resolv.conf" || \
error '\r\rReset System-Resolver \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The resolv.conf can not be linked.\n'
fi
unset RESOLVCONF
message '\r\rReset System-Resolver \xE2\x9C\x94\n'
message "Unbind filesystem '/dev'"
if /usr/bin/grep --quiet "${CT_REALPATH}/dev" '/proc/mounts'; then
/usr/bin/umount --recursive "${CT_REALPATH}/dev" || \
error "\r\rUnbind filesystem '/dev' \xE2\x9D\x8C\n" \
' \xE2\x86\x92 Error: The filesystem can not be unbound.\n'
fi
message "\r\rUnbind filesystem '/dev' \xE2\x9C\x94\n"
message "Unbind filesystem '/proc'"
if /usr/bin/grep --quiet "${CT_REALPATH}/proc" '/proc/mounts'; then
/usr/bin/umount --recursive "${CT_REALPATH}/proc" || \
error "\r\rUnbind filesystem '/proc' \xE2\x9D\x8C\n" \
' \xE2\x86\x92 Error: The filesystem can not be unbound.\n'
fi
message "\r\rUnbind filesystem '/proc' \xE2\x9C\x94\n"
message "Unbind filesystem '/sys'"
if /usr/bin/grep --quiet "${CT_REALPATH}/sys" '/proc/mounts'; then
/usr/bin/umount --recursive "${CT_REALPATH}/sys" || \
error "\r\rUnbind filesystem '/sys' \xE2\x9D\x8C\n" \
' \xE2\x86\x92 Error: The filesystem can not be unbound.\n'
fi
message "\r\rUnbind filesystem '/sys' \xE2\x9C\x94\n"
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}")
ct_check
message 'Open filelist'
if [ -f "/usr/lib/pve-lxc/${CT_BASENAME}.txt" ]; then
/usr/bin/cp --update='none' "/usr/lib/pve-lxc/${CT_BASENAME}.txt" "${CT_DIRNAME}/${CT_BASENAME}.txt" || \
error '\r\rOpen filelist \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The filelist can not be copied.\n'
elif [[ ! -f "/usr/lib/pve-lxc/${CT_BASENAME}.txt" ]]; then
/usr/bin/cp --update='none' "/usr/lib/pve-lxc/default.txt" "${CT_DIRNAME}/${CT_BASENAME}.txt" || \
error '\r\rOpen filelist \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The filelist can not be copied.\n'
fi
/usr/bin/nano "${CT_DIRNAME}/${CT_BASENAME}.txt" || \
error '\r\rOpen filelist \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The filelist can not be opened.\n'
message '\r\rOpen filelist \xE2\x9C\x94\n'
message 'Remove files'
mapfile -t CLEANUP_LIST < <(/usr/bin/cat "${CT_DIRNAME}/${CT_BASENAME}.txt" | /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 \xE2\x9D\x8C\n' \
" \xE2\x86\x92 Error: The file or directory >${CLEANUP_LIST}< can not be removed.\n"
done
unset CLEANUP_LIST
message '\r\rRemove files \xE2\x9C\x94\n'
message 'Create configuration'
/usr/bin/test -f "${CT_REALPATH}/etc/hostname" && \
/usr/bin/cp --force '/usr/lib/pve-lxc/hostname' "${CT_REALPATH}/etc/hostname"
/usr/bin/test -f "${CT_REALPATH}/etc/hosts" && \
/usr/bin/cp --force '/usr/lib/pve-lxc/hosts' "${CT_REALPATH}/etc/hosts"
/usr/bin/test -f "${CT_REALPATH}/etc/network/interfaces" && \
/usr/bin/cp --force '/usr/lib/pve-lxc/interfaces' "${CT_REALPATH}/etc/network/interfaces"
/usr/bin/test -f "${CT_REALPATH}/etc/resolv.conf" && \
/usr/bin/cp --force '/usr/lib/pve-lxc/resolv.conf' "${CT_REALPATH}/etc/resolv.conf"
message '\r\rCreate configuration \xE2\x9C\x94\n'
message 'Clear logfiles'
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\rClear logfiles \xE2\x9D\x8C\n' \
" \xE2\x86\x92 Error: The file >${CLEANUP_LIST}< can not be cleared.\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\rClear logfiles \xE2\x9D\x8C\n' \
" \xE2\x86\x92 Error: The file >${CLEANUP_LIST}< can not be removed.\n"
done
message '\r\rClear logfiles \xE2\x9C\x94\n'
exit 0
;;
r)
message 'Download container'
if [[ -d "./${OPTARG}" ]]; then
error '\r\rDownload container \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The container already exists.\n'
fi
/usr/bin/wget --quiet --output-document="./${OPTARG}.tar.xz" --no-hsts "https://repository.privlab.it/pve-lxc/${OPTARG}.tar.xz" &> '/dev/null' || \
error '\r\rDownload container \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The container can not be downloaded.\n'
/usr/bin/wget --quiet --output-document="./${OPTARG}.tar.xz.sha512sum" --no-hsts "https://repository.privlab.it/pve-lxc/${OPTARG}.tar.xz.sha512sum" &> '/dev/null' || \
error '\r\rDownload container \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The container SHA512SUM can not be downloaded.\n'
message '\r\rDownload container \xE2\x9C\x94\n'
message 'Compare hash'
/usr/bin/sha512sum --check --strict "./${OPTARG}.tar.xz.sha512sum" &> '/dev/null' || \
error '\r\rCompare hash \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The hash can not be verified.\n'
/usr/bin/rm --force "./${OPTARG}.tar.xz.sha512sum" || \
error '\r\rCompare hash \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The hash can not be removed.\n'
message '\r\rCompare hash \xE2\x9C\x94\n'
message 'Decompress container'
/usr/bin/install --directory "./${OPTARG}" || \
error '\r\rDecompress container \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The container path can not be installed.\n'
/usr/bin/tar --extract --file="./${OPTARG}.tar.xz" --directory="./${OPTARG}" || \
error '\r\rDecompress container \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The container can not be decompressed.\n'
/usr/bin/rm --force "./${OPTARG}.tar.xz" || \
error '\r\rDecompress container \xE2\x9D\x8C\n' \
' \xE2\x86\x92 Error: The compressed container can not be removed.\n'
message '\r\rDecompress container \xE2\x9C\x94\n'
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))