#!/bin/bash # postinst: freenas-proxmox install/upgrade/trigger script # Applies patches to Proxmox VE system files and installs the TrueNAS API plugin. # No internet access required — all files are bundled in the package. set -e INSTALL_DIR="/usr/share/freenas-proxmox" LIB_PATH="/usr/share" LOG_FILE="/var/log/freenas-proxmox-install.log" ZFSPLUGIN_PATH="/perl5/PVE/Storage/ZFSPlugin.pm" PVEMANAGER_PATH="/pve-manager/js/pvemanagerlib.js" APIDOC_PATH="/pve-docs/api-viewer/apidoc.js" FREENAS_PM_PATH="/perl5/PVE/Storage/LunCmd/FreeNAS.pm" REST_CLIENT_PATH="/perl5/REST/Client.pm" log() { echo "[freenas-proxmox] $*" | tee -a "$LOG_FILE" } # Detect installed Proxmox VE major version (e.g. "8" from "8.4.19") detect_pve_major() { local ver ver=$(dpkg-query --showformat='${Version}' --show proxmox-ve 2>/dev/null || echo "0") echo "${ver%%.*}" } # Detect pve-manager major.minor (e.g. "8.4" from "8.4.19"). # Used to select minor-version-specific patches within a major series. detect_pve_manager_minor() { local ver ver=$(dpkg-query -W -f '${Version}' pve-manager 2>/dev/null || echo "0.0.0") echo "${ver%.*}" } # Find the best bundled patch for a given component and PVE version. # Tries {minor}.patch (e.g. 8.4.patch) first, then walks down {major}.patch, # {major-1}.patch, ... until a match is found. find_patch() { local component="$1" local major="$2" local minor="$3" # e.g. "8.4" — may be empty local patch_dir="${INSTALL_DIR}/patches/${component}" # Minor-version-specific patch takes priority (e.g. pvemanagerlib/8.4.patch) if [ -n "$minor" ] && [ -f "${patch_dir}/${minor}.patch" ]; then echo "${patch_dir}/${minor}.patch" return 0 fi # Fall back to major-version patch, walking down until one is found local ver="$major" while [ "$ver" -ge 5 ]; do if [ -f "${patch_dir}/${ver}.patch" ]; then echo "${patch_dir}/${ver}.patch" return 0 fi ver=$(( ver - 1 )) done log "WARNING: No bundled patch found for ${component} on Proxmox VE ${major}.x" return 1 } # Apply a patch to a target file idempotently. # Creates a .orig backup (via patch --backup) on first application. apply_patch() { local target_rel="$1" local patch_file="$2" local label="$3" local target="${LIB_PATH}${target_rel}" [ -f "$target" ] || { log "ERROR: Target not found: ${target}"; return 1; } [ -f "$patch_file" ] || { log "Skipping ${label}: patch file missing"; return 0; } if grep -q "freenas" "$target" 2>/dev/null; then log "${label} is already patched — skipping" return 0 fi log "Patching ${target} ..." if patch --backup --ignore-whitespace "$target" < "$patch_file" >> "$LOG_FILE" 2>&1; then log "${label} patched successfully" else log "ERROR: Failed to patch ${label} — see ${LOG_FILE} for details" return 1 fi } install_files() { log "Installing ${LIB_PATH}${FREENAS_PM_PATH}" mkdir -p "$(dirname "${LIB_PATH}${FREENAS_PM_PATH}")" cp "${INSTALL_DIR}/FreeNAS.pm" "${LIB_PATH}${FREENAS_PM_PATH}" log "Installing ${LIB_PATH}${REST_CLIENT_PATH}" mkdir -p "$(dirname "${LIB_PATH}${REST_CLIENT_PATH}")" cp "${INSTALL_DIR}/REST-Client.pm" "${LIB_PATH}${REST_CLIENT_PATH}" } restart_pve_services() { log "Restarting Proxmox VE services ..." pvedaemon restart && log "pvedaemon restarted" pveproxy restart && log "pveproxy restarted" pvestatd restart && log "pvestatd restarted" systemctl restart pvescheduler.service && log "pvescheduler restarted" log "Done. Refresh your Proxmox browser tab to load the updated UI." } # Warn the user if their PVE version is outside the supported range for this # plugin version. Exits non-zero (aborting the install) only for versions so # old that no patches exist (< 7). check_pve_version() { local major="$1" if [ "$major" -lt 7 ]; then log "ERROR: Proxmox VE ${major}.x is not supported by this plugin." log " PVE 5 and 6 reached end-of-life in 2019 and 2022 respectively." log " Please upgrade your Proxmox VE installation before installing this plugin." exit 1 fi if [ "$major" -eq 7 ]; then log "WARNING: *** Proxmox VE 7 — Important Notice ***" log "WARNING: v2.x is the LAST release series that supports Proxmox VE 7." log "WARNING: PVE 7 support is best-effort only; no new patches will be" log "WARNING: developed for it." log "WARNING: When v3.0 is released, DO NOT upgrade to it — v3.0 requires" log "WARNING: Proxmox VE 8 or later. Stay on the latest v2.x release." fi if [ "$major" -eq 8 ]; then log "INFO: Proxmox VE 8 reaches end-of-life on 2026-08-31." log "INFO: Plan your upgrade to PVE 9 and migration to v3.0 before that date." fi if [ "$major" -ge 9 ]; then log "WARNING: *** Proxmox VE ${major}.x — Not Supported by v2.x ***" log "WARNING: This plugin (v2.x) is not supported on Proxmox VE 9 or later." log "WARNING: Patches may fail to apply or the UI may not work correctly." log "WARNING: Please use v3.0 of this plugin, which is designed for PVE 9+." log "WARNING: See: https://github.com/TheGrandWazoo/freenas-proxmox/releases" log "WARNING: Continuing anyway — but expect problems." fi } # ── Entry point ────────────────────────────────────────────────────────────── major=$(detect_pve_major) pve_mgr_minor=$(detect_pve_manager_minor) log "Proxmox VE major version: ${major}, pve-manager: ${pve_mgr_minor}" check_pve_version "$major" case "$1" in triggered) # dpkg fired us because one of the watched PVE files was updated. log "Triggered by package update — re-applying patches for: $2" for fullpath in $2; do filename=$(basename "$fullpath") filename="${filename%.*}" # strip extension case "$filename" in ZFSPlugin) patch_file=$(find_patch "ZFSPlugin" "$major" "$pve_mgr_minor" || true) apply_patch "$ZFSPLUGIN_PATH" "$patch_file" "ZFSPlugin.pm" ;; pvemanagerlib) patch_file=$(find_patch "pvemanagerlib" "$major" "$pve_mgr_minor" || true) apply_patch "$PVEMANAGER_PATH" "$patch_file" "pvemanagerlib.js" ;; apidoc) patch_file=$(find_patch "apidoc" "$major" "$pve_mgr_minor" || true) apply_patch "$APIDOC_PATH" "$patch_file" "apidoc.js" ;; esac done install_files exit 0 ;; configure) log "Configuring freenas-proxmox (previous version: ${2:-none})" CHANGED="no" patch_file=$(find_patch "ZFSPlugin" "$major" "$pve_mgr_minor" || true) apply_patch "$ZFSPLUGIN_PATH" "$patch_file" "ZFSPlugin.pm" && CHANGED="yes" patch_file=$(find_patch "pvemanagerlib" "$major" "$pve_mgr_minor" || true) apply_patch "$PVEMANAGER_PATH" "$patch_file" "pvemanagerlib.js" && CHANGED="yes" patch_file=$(find_patch "apidoc" "$major" "$pve_mgr_minor" || true) apply_patch "$APIDOC_PATH" "$patch_file" "apidoc.js" && CHANGED="yes" install_files CHANGED="yes" [ "$CHANGED" = "yes" ] && restart_pve_services exit 0 ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "$0: called with unknown argument '$1'" >&2 exit 0 ;; esac exit 0