From 8a993673b08c87a9da2a59971bb803848001626d62f8053569bde6fce87ac589 Mon Sep 17 00:00:00 2001 From: Thomas Oettli Date: Fri, 21 Aug 2026 23:34:05 +0200 Subject: [PATCH] improve installation routine and refactor code --- pbc | 208 ++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 133 insertions(+), 75 deletions(-) diff --git a/pbc b/pbc index d27d535..d3f4bfd 100755 --- a/pbc +++ b/pbc @@ -1,8 +1,4 @@ #!/usr/bin/env bash -SCRIPT_PATH=$(realpath -s "$0") -SCRIPT_DIR=$(dirname "$SCRIPT_PATH") -SCRIPT=$(basename "$SCRIPT_PATH") -ETC_DIR="/etc/$SCRIPT" function usage() { cat <&2 && exit 1 - - tmpdir=$(mktemp -d) - trap cleanup_tmpdir EXIT - - echo "Downloading $PBC_DOWNLOAD_URL/$pkgfile ..." - ! wget -q -O "$tmpdir/$pkgfile" "$PBC_DOWNLOAD_URL/$pkgfile" && echo "Error downloading package file!" >&2 && exit 2 - - echo "Extracting $pkgfile ..." - ! dpkg-deb -x "$tmpdir/$pkgfile" "$tmpdir/" && echo "Error extracting package file!" >&2 && exit 3 - - echo "Installing proxmox-backup-client binary to $PBC ..." - ! cp "$tmpdir/usr/bin/proxmox-backup-client" "$PBC" && echo "Error copying proxmox-backup-client binary!" >&2 && exit 4 - - local version=$("$PBC" version | head -n 1) - (( $? != 0 )) && echo "Error running $PBC version ... exited with rc=$?" >&2 && exit 5 - echo "Successfully installed proxmox-backup-$version" - else - local version=$("$PBC" version | head -n 1) - (( $? != 0 )) && echo "Error running $PBC version ... exited with rc=$?" >&2 && exit 5 - echo "Binary proxmox-backup-client ($version) already installed" - fi - - local jq=$(which jq) - if [ -z "$jq" ]; then - echo "Installing package jq ..." - ! apt-get install jq && echo "Error installing jq package!" >&2 && exit 6 - else - echo "Package jq already installed" - fi - if [ ! -d "$ETC_DIR" ]; then - echo "Creating config directory $ETC_DIR ..." - mkdir -p "$ETC_DIR" - fi - - if [ -f "$SCRIPT_DIR/config.example" ]; then - if $update || [ ! -f "$ETC_DIR/config.example" ]; then - echo "Copy example config to $ETC_DIR/config.example ..." - cp "$SCRIPT_DIR/config.example" "$ETC_DIR/config.example" - fi - fi - echo "Installation successful!" +function pbc-version() { + local vers_info=$("$PBC" version) + local rc=$? + (( $rc != 0 )) && echo "Error running $PBC version ... exited with rc=$rc" >&2 && return $rc + echo "$vers_info" | grep client + rc=$? + (( $rc != 0 )) && echo "Error reading client version, output was:" >&2 && echo "$vers_info" >&2 + return $rc } function pbc-backup() { @@ -121,8 +73,72 @@ function get_snapshot_files() { pbc-files "$1" --output-format json | jq -r '.[] | select(.filename | endswith(".pxar.didx")) | .filename | rtrimstr(".didx")' | sort } +function apt_get_install() { + local package=$1 + echo "Installing $package using package manager ..." + apt-get install "$package" >/dev/null + local rc=$? + (( $rc != 0 )) && echo "Error installing package!" >&2 + return $rc +} + +TMPDIR='' +function cleanup_tmpdir() { + [ -n "$TMPDIR" -a -d "$TMPDIR" ] && rm -r "$TMPDIR" + trap - EXIT +} + +function install_proxmox_backup_client() { + local update=${1:-false} + + [ -f "$PBC" ] && local installed=true || local installed=false + ! $installed && update=false + + if $installed && ! $update; then + local version=$("$PBC" version | head -n 1) + (( $? != 0 )) && echo "Error running $PBC version: exited with rc=$?" >&2 && return 10 + echo "Skip installation of proxmox-backup-client, already installed ($version)" + return 0 + fi + + if $update && dpkg -S "$PBC" &>/dev/null; then + echo "Skip update of proxmox-backup-client, package is managed by the package manager" + return 0 + fi + + if apt-get search proxmox-backup-client &>/dev/null; then + apt_get_install proxmox-backup-client + return $? + fi + + $update && local action='Updating' || local action='Installing' + echo "$action proxmox-backup-client-static binary ..." + + local pkgfile=$(curl -s "$PBC_DOWNLOAD_URL/" | grep proxmox-backup-client-static_ | tail -n 1 | sed 's#.*href="##g;s#".*##g;') + [ -z "$pkgfile" ] && echo "Unable to determine current package file!" >&2 && return 30 + + TMPDIR=$(mktemp -d) + trap cleanup_tmpdir EXIT + + echo "Downloading $PBC_DOWNLOAD_URL/$pkgfile ..." + ! curl -s -o "$TMPDIR/$pkgfile" "$PBC_DOWNLOAD_URL/$pkgfile" && echo "Error downloading file!" >&2 && return 40 + + echo "Extracting $pkgfile ..." + ! dpkg-deb -x "$TMPDIR/$pkgfile" "$TMPDIR/" && echo "Error extracting file!" >&2 && return 50 + + echo "Copying proxmox-backup-client static binary to $PBC ..." + ! cp "$TMPDIR/usr/bin/proxmox-backup-client" "$PBC" && echo "Error copying binary!" >&2 && return 60 + + local version=$(pbc-version) || return $? + $update && action='updated' || action='installed' + echo "Successfully $action proxmox-backup-$version" + + cleanup_tmpdir +} + + function arr_contains() { - search=$1 + local search=$1 shift [ -z "$search" ] && return 1 while [ -n "$1" ]; do @@ -133,7 +149,7 @@ function arr_contains() { } function may_select() { - value=$1 + local value=$1 shift (( $# == 0 )) && echo "Error: no values to select" >&2 && return 1 @@ -142,7 +158,7 @@ function may_select() { if (( $# == 1 )); then value=$1 else - sorted=($(printf "%s\n" "$@" | sort)) + local sorted=($(printf "%s\n" "$@" | sort)) select value in "${sorted[@]}"; do [ -n "$value" ] && break done @@ -155,8 +171,17 @@ function may_select() { return 0 } + +SCRIPT_PATH=$(realpath -s "$0") +SCRIPT_DIR=$(dirname "$SCRIPT_PATH") +SCRIPT=$(basename "$SCRIPT_PATH") + +PBC=$(which proxmox-backup-client) +PBC_DOWNLOAD_URL='http://download.proxmox.com/debian/pbs-client/dists/trixie/main/binary-amd64' +ETC_DIR="/etc/$SCRIPT" + action='' -configfile='' +cfgfile="$ETC_DIR/config" args=() while [ -n "$1" ]; do @@ -168,9 +193,9 @@ while [ -n "$1" ]; do action=$opt ;; -c|--config) - configfile=$1 + cfgfile=$1 shift - [ -z "$configfile" ] && usage && exit 255 + [ -z "$cfgfile" ] && usage && exit 255 ;; -h|--help) usage @@ -196,19 +221,22 @@ while [ -n "$1" ]; do fi done -configfile=${configfile:-$ETC_DIR/config} -if [ "$action" == 'install' ]; then - if [ -f "$configfile" ]; then - source "$configfile" || exit $? - fi -else - source "$configfile" || exit $? - [ ! -f "$PBC" ] && echo "Error: $PBC: no such file" >&2 && exit 1 - [ ! -e "$PBC" ] && echo "Error: $PBC: not executable" >&2 && exit 1 -fi -PBC=${PBC:-/usr/bin/proxmox-backup-client} -PBC_DOWNLOAD_URL=${PBC_DOWNLOAD_URL:-http://download.proxmox.com/debian/pbs-client/dists/trixie/main/binary-amd64} +if [ "$action" == 'install' ]; then + if [ -f "$cfgfile" ]; then + source "$cfgfile" || exit $? + fi + PBC=${PBC:-/usr/local/bin/proxmox-backup-client} + for exe in apt-get dpkg dpkg-deb curl; do + ! which -s "$exe" && echo "Error: $exe executable not found!" >&2 && exit 200 + done +else + source "$cfgfile" || exit $? + PBC=${PBC:-/usr/local/bin/proxmox-backup-client} + [ ! -f "$PBC" ] && echo "Error: $PBC: no such file" >&2 && exit 200 + [ ! -e "$PBC" ] && echo "Error: $PBC: not executable" >&2 && exit 200 + ! which -s jq && echo "Error: jq executable not found!" >&2 && exit 200 +fi export PBS_REPOSITORY="$PBS_USER@${PBS_SERVER:-8007}:$PBS_DATASTORE" export PBS_PASSWORD="$PBS_PASSWORD" @@ -304,7 +332,37 @@ case "$action" in pbc-mount "$snapshot" "$archive" "$target" || exit $? ;; install) - pbc-install "${args[@]}" || exit $? + update=${args[0]:-false} + + [ "$(whoami)" != "root" ] && echo "Error: installation only works as root!" >&2 && exit 1 + + tmpdir=$(mktemp -d) + trap cleanup_tmpdir EXIT + + install_proxmox_backup_client "$update" || exit $? + + if dpkg -s jq &>/dev/null; then + $update && echo "Skip update of jq, package is managed by package manager" || echo "Skip installation of jq, already installed" + else + apt_get_install jq || exit $? + fi + + ETC_DIR="/etc/$SCRIPT" + + if [ ! -d "$ETC_DIR" ]; then + echo "Creating config directory $ETC_DIR ..." + ! mkdir -p "$ETC_DIR" && echo "Error creating directory!" >&2 && exit 3 + fi + + if [ -f "$SCRIPT_DIR/config.example" ]; then + if $update || [ ! -f "$ETC_DIR/config.example" ]; then + echo "Copy example config to $ETC_DIR/config.example ..." + ! cp "$SCRIPT_DIR/config.example" "$ETC_DIR/config.example" && echo "Error copying file!" >&2 && exit 4 + fi + fi + + $update && action='Update' || action='Installation' + echo "$action successful!" ;; *) usage