Compare commits

..

4 Commits

Author SHA1 Message Date
MickLesk f8eb3b10c1 switch to /usr/local 2026-07-21 16:38:38 +02:00
MickLesk ca80fea8cd Add persistent OS mismatch bypass option
Enhances `check_container_os_guard` to support a persistent opt-out for OS version mismatch checks. The prompt now uses a 3-option whiptail menu (cancel, continue once, continue and remember), and stores ignored targets in `/root/.helper-scripts-ignoreOSupdate` so users are re-prompted when a newer recommended OS appears. Messaging was also tightened to explicitly warn that bypassing may break updates and is unsupported.
2026-07-21 16:11:03 +02:00
MickLesk 8249b2ebe6 Refine OS mismatch update guidance
Updates the OS version mismatch prompts in `check_container_os_guard()` to recommend upgrading the existing container OS to the target release before rerunning the update, instead of recreating the container. The skip/error path now uses the same guidance while still documenting `var_ignore_os_mismatch=1` as an override.
2026-07-21 15:19:17 +02:00
MickLesk c5d2726994 core: add OS mismatch guard for container updates
Introduces `check_container_os_guard()` to compare the container's `/etc/os-release` against the script's recommended `var_os`/`var_version` before running updates. On mismatch, interactive runs now prompt to continue (default no), while silent/headless runs abort to avoid partial breakage on unsupported bases. A bypass flag (`var_ignore_os_mismatch=1|yes|true|on`) was added, and the guard is wired into all update entry paths in `start()`.
2026-07-21 15:16:39 +02:00
9 changed files with 36 additions and 151 deletions
-20
View File
@@ -505,26 +505,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details> </details>
## 2026-07-22
### 🚀 Updated Scripts
- #### 🐞 Bug Fixes
- InvoiceNinja: preserve and Re-download snappdf Chromium [@MickLesk](https://github.com/MickLesk) ([#15956](https://github.com/community-scripts/ProxmoxVE/pull/15956))
- Anytype: preserve default mongod.conf when configuring Anytype replica set [@MickLesk](https://github.com/MickLesk) ([#15954](https://github.com/community-scripts/ProxmoxVE/pull/15954))
- Nametag: fix missing tailwindcss module [@MickLesk](https://github.com/MickLesk) ([#15955](https://github.com/community-scripts/ProxmoxVE/pull/15955))
- #### ✨ New Features
- OPNSense: Bump FreeBSD to 15 and OPNSense to 26.7 [@MickLesk](https://github.com/MickLesk) ([#15943](https://github.com/community-scripts/ProxmoxVE/pull/15943))
### 💾 Core
- #### ✨ New Features
- core: add OS mismatch guard for container updates [@MickLesk](https://github.com/MickLesk) ([#15948](https://github.com/community-scripts/ProxmoxVE/pull/15948))
## 2026-07-21 ## 2026-07-21
### 🚀 Updated Scripts ### 🚀 Updated Scripts
+1 -7
View File
@@ -35,18 +35,12 @@ function update_script() {
systemctl stop supervisor nginx php8.4-fpm systemctl stop supervisor nginx php8.4-fpm
msg_ok "Stopped Services" msg_ok "Stopped Services"
create_backup /opt/invoiceninja/.env /opt/invoiceninja/storage /opt/invoiceninja/public/storage /opt/invoiceninja/vendor/beganovich/snappdf/versions create_backup /opt/invoiceninja/.env /opt/invoiceninja/storage /opt/invoiceninja/public/storage
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "invoiceninja" "invoiceninja/invoiceninja" "prebuild" "latest" "/opt/invoiceninja" "invoiceninja.tar.gz" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "invoiceninja" "invoiceninja/invoiceninja" "prebuild" "latest" "/opt/invoiceninja" "invoiceninja.tar.gz"
restore_backup restore_backup
msg_info "Verifying Chromium for PDF Generation"
cd /opt/invoiceninja
$STD ./vendor/bin/snappdf download
chown -R www-data:www-data /opt/invoiceninja/vendor/beganovich/snappdf/versions
msg_ok "Verified Chromium for PDF Generation"
msg_info "Running Migrations" msg_info "Running Migrations"
cd /opt/invoiceninja cd /opt/invoiceninja
$STD php artisan migrate --force $STD php artisan migrate --force
+1 -1
View File
@@ -43,7 +43,7 @@ function update_script() {
msg_info "Rebuilding Application" msg_info "Rebuilding Application"
cd /opt/nametag cd /opt/nametag
$STD npm ci --include=dev $STD npm ci
set -a set -a
source /opt/nametag/.env source /opt/nametag/.env
set +a set +a
+1 -1
View File
@@ -16,7 +16,7 @@ update_os
setup_mongodb setup_mongodb
msg_info "Configuring MongoDB Replica Set" msg_info "Configuring MongoDB Replica Set"
cat <<EOF >>/etc/mongod.conf cat <<EOF >/etc/mongod.conf
replication: replication:
replSetName: "rs0" replSetName: "rs0"
+1 -1
View File
@@ -20,7 +20,7 @@ fetch_and_deploy_gh_release "nametag" "mattogodoy/nametag" "tarball" "latest" "/
msg_info "Setting up Application" msg_info "Setting up Application"
cd /opt/nametag cd /opt/nametag
$STD npm ci --include=dev $STD npm ci
DATABASE_URL="postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}" $STD npx prisma generate DATABASE_URL="postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}" $STD npx prisma generate
DATABASE_URL="postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}" $STD npx prisma migrate deploy DATABASE_URL="postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}" $STD npx prisma migrate deploy
msg_ok "Set up Application" msg_ok "Set up Application"
+2 -12
View File
@@ -5215,10 +5215,7 @@ EOF
echo -en "${YW}Select option [1-${max_option}] (default: 1, auto-remove in 60s): ${CL}" echo -en "${YW}Select option [1-${max_option}] (default: 1, auto-remove in 60s): ${CL}"
local response="" local response=""
local read_rc if read -t 60 -r response; then
read -t 60 -r response
read_rc=$?
if [[ $read_rc -eq 0 ]]; then
case "${response:-1}" in case "${response:-1}" in
1) 1)
# Remove container # Remove container
@@ -5437,20 +5434,13 @@ EOF
fi fi
;; ;;
esac esac
elif [[ $read_rc -gt 128 ]]; then else
# Timeout - auto-remove # Timeout - auto-remove
echo "" echo ""
msg_info "No response - removing container ${CTID}" msg_info "No response - removing container ${CTID}"
pct stop "$CTID" &>/dev/null || true pct stop "$CTID" &>/dev/null || true
pct destroy "$CTID" &>/dev/null || true pct destroy "$CTID" &>/dev/null || true
msg_ok "Container ${CTID} removed" msg_ok "Container ${CTID} removed"
else
# read itself failed (e.g. broken/closed stdin, I/O error) rather than
# timing out - don't guess and destroy the container on a read we
# couldn't actually capture; keep it since that's the reversible choice.
echo ""
msg_error "Could not read your response (stdin error) - keeping container ${CTID} for safety."
msg_error "Remove it manually if not needed: pct destroy ${CTID}"
fi fi
# Force one final status update attempt after cleanup # Force one final status update attempt after cleanup
+2 -17
View File
@@ -497,10 +497,7 @@ error_handler() {
fi fi
local response="" local response=""
local read_rc if read -t 60 -r response; then
read -t 60 -r response
read_rc=$?
if [[ $read_rc -eq 0 ]]; then
if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then
echo "" echo ""
if declare -f msg_info >/dev/null 2>&1; then if declare -f msg_info >/dev/null 2>&1; then
@@ -523,7 +520,7 @@ error_handler() {
echo -e "${YW}Container ${CTID} kept for debugging${CL}" echo -e "${YW}Container ${CTID} kept for debugging${CL}"
fi fi
fi fi
elif [[ $read_rc -gt 128 ]]; then else
# Timeout - auto-remove # Timeout - auto-remove
echo "" echo ""
if declare -f msg_info >/dev/null 2>&1; then if declare -f msg_info >/dev/null 2>&1; then
@@ -538,18 +535,6 @@ error_handler() {
else else
echo -e "${GN}${CL} Container ${CTID} removed" echo -e "${GN}${CL} Container ${CTID} removed"
fi fi
else
# read itself failed (e.g. broken/closed stdin, I/O error) rather than
# timing out - don't guess and destroy the container on a read we
# couldn't actually capture; keep it since that's the reversible choice.
echo ""
if declare -f msg_error >/dev/null 2>&1; then
msg_error "Could not read your response (stdin error) - keeping container ${CTID} for safety."
msg_error "Remove it manually if not needed: pct destroy ${CTID}"
else
echo -e "${YW}Could not read your response (stdin error) - keeping container ${CTID} for safety.${CL}"
echo -e "${YW}Remove it manually if not needed: pct destroy ${CTID}${CL}"
fi
fi fi
fi fi
fi fi
+9 -9
View File
@@ -2689,7 +2689,7 @@ fetch_and_deploy_gh_tag() {
local tmpdir local tmpdir
tmpdir=$(mktemp -d) || return 1 tmpdir=$(mktemp -d) || return 1
trap 'rm -rf "$tmpdir"; trap - RETURN' RETURN trap 'rm -rf "$tmpdir"' RETURN
local tarball_url="https://github.com/${repo}/archive/refs/tags/${version}.tar.gz" local tarball_url="https://github.com/${repo}/archive/refs/tags/${version}.tar.gz"
local filename="${app_lc}-${version}.tar.gz" local filename="${app_lc}-${version}.tar.gz"
@@ -2866,7 +2866,7 @@ fetch_and_deploy_gl_tag() {
local tmpdir local tmpdir
tmpdir=$(mktemp -d) || return 1 tmpdir=$(mktemp -d) || return 1
trap 'rm -rf "$tmpdir"; trap - RETURN' RETURN trap 'rm -rf "$tmpdir"' RETURN
local filename="${app_lc}-${version_safe}.tar.gz" local filename="${app_lc}-${version_safe}.tar.gz"
msg_info "Fetching GitLab tag: ${app} (${resolved_tag})" msg_info "Fetching GitLab tag: ${app} (${resolved_tag})"
@@ -2974,7 +2974,7 @@ check_for_gh_release() {
local gh_check_json="" local gh_check_json=""
gh_check_json=$(mktemp /tmp/tools-gh-check-XXXXXX) || return 73 gh_check_json=$(mktemp /tmp/tools-gh-check-XXXXXX) || return 73
trap 'rm -f "${gh_check_json:-}"; trap - RETURN' RETURN trap 'rm -f "${gh_check_json:-}"' RETURN
# Build auth header if token is available # Build auth header if token is available
local header_args=() local header_args=()
@@ -3574,7 +3574,7 @@ fetch_and_deploy_codeberg_release() {
local tmpdir local tmpdir
tmpdir=$(mktemp -d) || return 252 tmpdir=$(mktemp -d) || return 252
trap 'rm -rf "$tmpdir"; trap - RETURN' RETURN trap 'rm -rf "$tmpdir"' RETURN
msg_info "Fetching Codeberg tag: $app ($tag_name)" msg_info "Fetching Codeberg tag: $app ($tag_name)"
@@ -3616,7 +3616,7 @@ fetch_and_deploy_codeberg_release() {
local codeberg_rel_json local codeberg_rel_json
codeberg_rel_json=$(mktemp /tmp/tools-codeberg-rel-XXXXXX) || return 73 codeberg_rel_json=$(mktemp /tmp/tools-codeberg-rel-XXXXXX) || return 73
trap 'rm -f "$codeberg_rel_json"; rm -rf "${tmpdir:-}" "${unpack_tmp:-}"; trap - RETURN' RETURN trap 'rm -f "$codeberg_rel_json"; rm -rf "${tmpdir:-}" "${unpack_tmp:-}"' RETURN
local attempt=0 success=false resp http_code local attempt=0 success=false resp http_code
@@ -4113,7 +4113,7 @@ fetch_and_deploy_gh_release() {
local tmpdir local tmpdir
tmpdir=$(mktemp -d) || return 1 tmpdir=$(mktemp -d) || return 1
trap 'rm -rf "$tmpdir" "${unpack_tmp:-}"; trap - RETURN' RETURN trap 'rm -rf "$tmpdir" "${unpack_tmp:-}"' RETURN
local filename="" url="" local filename="" url=""
msg_info "Fetching GitHub release: $app ($version)" msg_info "Fetching GitHub release: $app ($version)"
@@ -9218,7 +9218,7 @@ fetch_and_deploy_from_url() {
msg_error "Failed to create temporary directory" msg_error "Failed to create temporary directory"
return 252 return 252
} }
trap 'rm -rf "$tmpdir" "${unpack_tmp:-}"; trap - RETURN' RETURN trap 'rm -rf "$tmpdir" "${unpack_tmp:-}"' RETURN
curl -fsSL -o "$tmpdir/$filename" "$url" || { curl -fsSL -o "$tmpdir/$filename" "$url" || {
msg_error "Download failed: $url" msg_error "Download failed: $url"
@@ -9434,7 +9434,7 @@ check_for_gl_release() {
local gl_check_json local gl_check_json
gl_check_json=$(mktemp /tmp/tools-gl-check-XXXXXX) || return 73 gl_check_json=$(mktemp /tmp/tools-gl-check-XXXXXX) || return 73
trap 'rm -f "$gl_check_json"; trap - RETURN' RETURN trap 'rm -f "$gl_check_json"' RETURN
local repo_encoded local repo_encoded
repo_encoded=$(printf '%s' "$source" | sed 's|/|%2F|g') repo_encoded=$(printf '%s' "$source" | sed 's|/|%2F|g')
@@ -9724,7 +9724,7 @@ fetch_and_deploy_gl_release() {
local gl_rel_json local gl_rel_json
gl_rel_json=$(mktemp /tmp/tools-gl-rel-XXXXXX) || return 73 gl_rel_json=$(mktemp /tmp/tools-gl-rel-XXXXXX) || return 73
trap 'rm -f "$gl_rel_json"; rm -rf "${tmpdir:-}" "${unpack_tmp:-}"; trap - RETURN' RETURN trap 'rm -f "$gl_rel_json"; rm -rf "${tmpdir:-}" "${unpack_tmp:-}"' RETURN
local repo_encoded local repo_encoded
repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g') repo_encoded=$(printf '%s' "$repo" | sed 's|/|%2F|g')
+15 -79
View File
@@ -25,7 +25,6 @@ METHOD=""
NSAPP="opnsense-vm" NSAPP="opnsense-vm"
var_os="opnsense" var_os="opnsense"
var_version="26.7" var_version="26.7"
FREEBSD_MAJOR="15"
# #
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
GEN_MAC_LAN=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') GEN_MAC_LAN=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
@@ -491,7 +490,7 @@ function advanced_settings() {
fi fi
echo -e "${DGN}Using LAN GATEWAY ADDRESS: ${BGN}$LAN_GW${CL}" echo -e "${DGN}Using LAN GATEWAY ADDRESS: ${BGN}$LAN_GW${CL}"
fi fi
if NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN netmask (24 for example)" 8 58 $NETMASK --title "LAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN netmmask (24 for example)" 8 58 $NETMASK --title "LAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $NETMASK ]; then if [ -z $NETMASK ]; then
echo -e "${DGN}Netmask needs to be set if ip is not dhcp${CL}" echo -e "${DGN}Netmask needs to be set if ip is not dhcp${CL}"
fi fi
@@ -559,7 +558,7 @@ function advanced_settings() {
else else
exit-script exit-script
fi fi
if WAN_NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN netmask (24 for example)" 8 58 $WAN_NETMASK --title "WAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if WAN_NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN netmmask (24 for example)" 8 58 $WAN_NETMASK --title "WAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $WAN_NETMASK ]; then if [ -z $WAN_NETMASK ]; then
echo -e "${DGN}WAN Netmask needs to be set if ip is not dhcp${CL}" echo -e "${DGN}WAN Netmask needs to be set if ip is not dhcp${CL}"
fi fi
@@ -575,7 +574,7 @@ function advanced_settings() {
else else
exit-script exit-script
fi fi
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN MAC Address" 8 58 $GEN_MAC --title "LAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN MAC Address" 8 58 $GEN_MAC --title "WAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $MAC1 ]; then if [ -z $MAC1 ]; then
MAC="$GEN_MAC" MAC="$GEN_MAC"
else else
@@ -586,7 +585,7 @@ function advanced_settings() {
exit-script exit-script
fi fi
if MAC2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN MAC Address" 8 58 $GEN_MAC_LAN --title "WAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if MAC2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN MAC Address" 8 58 $GEN_MAC_LAN --title "LAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
if [ -z $MAC2 ]; then if [ -z $MAC2 ]; then
WAN_MAC="$GEN_MAC_LAN" WAN_MAC="$GEN_MAC_LAN"
else else
@@ -653,26 +652,23 @@ fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
msg_info "Retrieving the URL for the OPNsense Qcow2 Disk Image" msg_info "Retrieving the URL for the OPNsense Qcow2 Disk Image"
# Use latest stable FreeBSD amd64 qcow2 VM image matching FREEBSD_MAJOR # Use latest stable FreeBSD amd64 qcow2 VM image (generic, not UFS/ZFS)
RELEASE_LIST="$(curl -s https://download.freebsd.org/releases/VM-IMAGES/ | RELEASE_LIST="$(curl -s https://download.freebsd.org/releases/VM-IMAGES/ |
grep -Eo "${FREEBSD_MAJOR}\.[0-9]+-RELEASE" | grep -Eo '[0-9]+\.[0-9]+-RELEASE' |
sort -Vr | sort -Vr |
uniq)" uniq)"
URL="" URL=""
FREEBSD_VER="" FREEBSD_VER=""
for ver in $RELEASE_LIST; do for ver in $RELEASE_LIST; do
# FreeBSD 15+ publishes separate -ufs/-zfs images instead of a generic one candidate="https://download.freebsd.org/releases/VM-IMAGES/${ver}/amd64/Latest/FreeBSD-${ver}-amd64.qcow2.xz"
for variant in "" "-ufs" "-zfs"; do
candidate="https://download.freebsd.org/releases/VM-IMAGES/${ver}/amd64/Latest/FreeBSD-${ver}-amd64${variant}.qcow2.xz"
if curl -fsI "$candidate" >/dev/null 2>&1; then if curl -fsI "$candidate" >/dev/null 2>&1; then
FREEBSD_VER="$ver" FREEBSD_VER="$ver"
URL="$candidate" URL="$candidate"
break 2 break
fi fi
done done
done
if [ -z "$URL" ]; then if [ -z "$URL" ]; then
msg_error "Could not find a FreeBSD ${FREEBSD_MAJOR}.x amd64 qcow2 image." msg_error "Could not find generic FreeBSD amd64 qcow2 image (non-UFS/ZFS)."
exit 115 exit 115
fi fi
msg_ok "Download URL: ${CL}${BL}${URL}${CL}" msg_ok "Download URL: ${CL}${BL}${URL}${CL}"
@@ -772,7 +768,7 @@ DESCRIPTION=$(
cat <<EOF cat <<EOF
<div align='center'> <div align='center'>
<a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'> <a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/> <img src='https://raw.githubusercontent.com/michelroegl-brunner/ProxmoxVE/refs/heads/develop/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
</a> </a>
<h2 style='font-size: 24px; margin: 20px 0;'>OPNsense VM</h2> <h2 style='font-size: 24px; margin: 20px 0;'>OPNsense VM</h2>
@@ -818,70 +814,10 @@ if [ -n "$WAN_BRG" ]; then
msg_ok "WAN interface added" msg_ok "WAN interface added"
sleep 5 # Brief pause after adding network interface sleep 5 # Brief pause after adding network interface
fi fi
# FreeBSD 15+ VM images ship the base system as pkgbase packages; the bootstrap's send_line_to_vm "sh ./opnsense-bootstrap.sh.in -y -f -r 26.7"
# "delete all packages" step would remove the running base system (/bin/rm etc.)
# and brick the VM. Deregister them from the pkg db first - the files stay in
# place and OPNsense replaces base and kernel with its own sets afterwards.
send_line_to_vm "echo \"PRAGMA foreign_keys=ON; DELETE FROM packages WHERE name LIKE 'FreeBSD-%';\" | pkg shell"
sleep 5
send_line_to_vm "sh ./opnsense-bootstrap.sh.in -y -f -r ${var_version}"
msg_ok "OPNsense VM is being installed, do not close the terminal, or the installation will fail." msg_ok "OPNsense VM is being installed, do not close the terminal, or the installation will fail."
# The bootstrap ends with an automatic reboot into OPNsense. While it runs the #We need to wait for the OPNsense build proccess to finish, this takes a few minutes
# console keeps changing (download progress, package installs); once the VM has sleep 1000
# settled at the login prompt the screen stays static. Poll a screendump hash
# and continue after 3 minutes without change, bounded by a floor (the build
# never finishes faster) and a ceiling for slow machines. If no screendump can
# be captured at all, fall back to a fixed wait.
SCREEN_PPM="${TEMP_DIR}/screen-${VMID}.ppm"
function screen_hash() {
# Remove the previous dump first: a stale file from an earlier successful
# dump must not simulate a static screen when later dumps start failing.
# Note: "qm monitor" is unusable here - its readline attaches to /dev/tty
# even with piped stdin and captures the terminal, so use the API instead.
rm -f "$SCREEN_PPM"
timeout 10 pvesh create /nodes/$(hostname -s)/qemu/$VMID/monitor --command "screendump ${SCREEN_PPM}" >/dev/null 2>&1 || true
md5sum "$SCREEN_PPM" 2>/dev/null | cut -d' ' -f1 || true
}
build_elapsed=300
build_stable=0
screen_ok=0
hash_a=""
hash_b=""
sleep 300
while [ $build_stable -lt 6 ] && [ $build_elapsed -lt 2400 ]; do
sleep 30
build_elapsed=$((build_elapsed + 30))
new_hash=$(screen_hash)
if [ -n "$new_hash" ]; then
screen_ok=1
# The login prompt cursor may blink: a screen alternating between the same
# two frames (A/B/A/B) counts as stable, anything new resets the counter
if [ "$new_hash" = "$hash_a" ] || [ "$new_hash" = "$hash_b" ]; then
build_stable=$((build_stable + 1))
else
build_stable=0
fi
else
build_stable=0
fi
hash_b="$hash_a"
hash_a="$new_hash"
if [ -n "$new_hash" ]; then
echo -e "${DGN}Waiting for OPNsense build: ${YW}$((build_elapsed / 60))min elapsed, screen ${new_hash:0:8}, stable ${build_stable}/6${CL}"
else
echo -e "${DGN}Waiting for OPNsense build: ${YW}$((build_elapsed / 60))min elapsed, screendump failed${CL}"
fi
# No working screendump after several attempts: fixed wait instead
if [ $screen_ok -eq 0 ] && [ $build_elapsed -ge 480 ]; then
msg_error "Console screendump not available on this system - falling back to a fixed wait (12 minutes)."
sleep 720
build_elapsed=$((build_elapsed + 720))
break
fi
done
msg_ok "OPNsense build finished after $((build_elapsed / 60)) minutes"
send_line_to_vm "root" send_line_to_vm "root"
send_line_to_vm "opnsense" send_line_to_vm "opnsense"
send_line_to_vm "2" send_line_to_vm "2"
@@ -919,8 +855,8 @@ if [ -n "$WAN_BRG" ] && [ "$WAN_IP_ADDR" != "" ]; then
send_line_to_vm "2" send_line_to_vm "2"
send_line_to_vm "n" send_line_to_vm "n"
send_line_to_vm "${WAN_IP_ADDR}" send_line_to_vm "${WAN_IP_ADDR}"
send_line_to_vm "${WAN_NETMASK}" send_line_to_vm "${NETMASK}"
send_line_to_vm "${WAN_GW}" send_line_to_vm "${LAN_GW}"
send_line_to_vm "n" send_line_to_vm "n"
send_line_to_vm " " send_line_to_vm " "
send_line_to_vm "n" send_line_to_vm "n"