diff --git a/scripts/backup_restore/apply_pending_restore.sh b/scripts/backup_restore/apply_pending_restore.sh index 4ee3800d..f22f97c8 100644 --- a/scripts/backup_restore/apply_pending_restore.sh +++ b/scripts/backup_restore/apply_pending_restore.sh @@ -228,12 +228,11 @@ ConditionPathExists=/var/lib/proxmenux/cluster-apply-pending [Service] Type=oneshot ExecStart=/usr/local/share/proxmenux/scripts/backup_restore/apply_cluster_postboot.sh -# 15-min cap to fit update-initramfs -u -k all (5-10 min for -# 3 kernels) + update-grub (~30s) on top of the (fast) cluster -# config apply. The unit runs AFTER pve-cluster is up so the -# user is already at the login prompt and using the system — -# this just chugs in the background. -TimeoutStartSec=900 +# Cap sized for the worst case: update-initramfs across all +# kernels + update-grub + cluster apply + component auto-reinstall +# (NVIDIA driver download + kernel-module build is the long pole). +# Service runs after pve-cluster is up, so this is purely background. +TimeoutStartSec=3600 [Install] WantedBy=multi-user.target diff --git a/scripts/backup_restore/backup_host.sh b/scripts/backup_restore/backup_host.sh index 99a7bd87..515d1314 100644 --- a/scripts/backup_restore/backup_host.sh +++ b/scripts/backup_restore/backup_host.sh @@ -1431,11 +1431,8 @@ _rs_show_plan_summary() { local staging_root="$1" local meta="$staging_root/metadata" - # dialog --colors only fires inside --msgbox / --yesno / --infobox, not - # --textbox, so we build the body as a string. Color codes match the - # complete-restore confirm dialog for visual consistency. Leading blank - # line matches the convention used in info-style dialogs (compat report, - # etc.) — keeps the title from feeling glued to the first text line. + # dialog --colors needs --msgbox/--yesno/--infobox (not --textbox), + # so we build the body as a string. local body body=$'\n'"\Zb═══ $(translate "Restore plan summary") ═══\ZB"$'\n\n' @@ -1793,15 +1790,9 @@ _rs_run_complete_guided() { plan_body="\Zb$(translate "Smart restore plan — hardware compatibility check")\ZB"$'\n\n' plan_body+="$(translate "The backup metadata was compared against this host. The following items will be SKIPPED to keep the boot safe:")"$'\n\n' - # Identifier-based skips (ZFS pool GUID, boot EFI UUID, fstab - # UUID) ALWAYS fire on a cross-host restore — those identifiers - # are unique per disk/pool. Showing them in the dialog is noise - # the operator can't act on. We still apply the skips (that's - # what protects against, e.g., restoring zpool.cache pointing at - # a ghost pool), just without the dialog when only this noise - # would be shown. The signal-bearing skips (component drift — - # NVIDIA in backup, no NVIDIA card here) still trigger the dialog - # so the operator can decide. + # Identifier-based skips (ZFS pool GUID, boot EFI UUID, fstab UUID) + # always fire on cross-host restores. The skip itself still applies + # — only the dialog is suppressed when nothing else is signal-bearing. local -A _IDENTIFIER_PATHS=( ["/etc/zfs/zpool.cache"]=1 ["/etc/kernel/proxmox-boot-uuids"]=1 @@ -1820,10 +1811,6 @@ _rs_run_complete_guided() { dialog_signal=1 else skip_paths+="${key}"$'\n' - # Identifier-based paths on cross-host: skip silently - # (the path goes into RS_SKIP_PATHS but stays out of - # the dialog body, and dialog_signal stays 0 if every - # skip is of this kind). if [[ "${HB_COMPAT_SAME_HOST:-1}" == "0" ]] \ && [[ -n "${_IDENTIFIER_PATHS[$key]:-}" ]]; then continue @@ -2252,12 +2239,8 @@ _rs_run_complete_extras() { rm -f "$cur_pkgs_file" if [[ ${#missing[@]} -gt 0 ]]; then echo - # Pre-filter to packages apt actually knows about — otherwise a - # single typo or repo-renamed pkg in packages.manual.list (e.g. - # `lifnet-subnet-perl` from a hand-typo'd apt-mark) makes - # `apt-get install` exit with E_UNRESOLVABLE and the entire - # batch is skipped. Do this BEFORE the "installing N packages" - # message so the count is honest about what we'll actually try. + # Split into apt-known vs unknown so the install count + # announced below matches what apt-get will actually attempt. local -a installable=() unknown=() local pkg for pkg in "${missing[@]}"; do @@ -2269,9 +2252,6 @@ _rs_run_complete_extras() { done if (( ${#installable[@]} > 0 )); then - # Preview so the operator can see (and ^C if surprised by) - # what's about to land. First six is enough — anyone needing - # the full list goes to the apt log we write below. local _preview="${installable[*]:0:6}" (( ${#installable[@]} > 6 )) && _preview+=" … (+ $((${#installable[@]} - 6)) more)" echo -e "${TAB}${BGN}$(translate "Packages from backup to install:")${CL} ${BL}${_preview}${CL}" @@ -2285,20 +2265,10 @@ _rs_run_complete_extras() { msg_ok "$(translate "apt cache refreshed.")" msg_info "$(translate "Installing") ${#installable[@]} $(translate "packages (this may take a few minutes)...")" - # Silent install — full output goes to $apt_log so the - # spinner keeps turning and the operator sees ongoing - # activity. Without this redirect, dpkg's "Setting up..." - # spew would buffer and dump at the very end after a long - # silent stall, looking like a hang followed by a wall of - # text appearing from nowhere. - # DEBIAN_FRONTEND=noninteractive + --force-conf prevents - # apt from blocking on `*** log2ram.conf (Y/I/N/O/D/Z) ?` - # type prompts (which would leave the package in - # half-installed `iU` state and ultimately produce the - # same boot-hang problem we're trying to FIX with this - # restore). Confnew/confold both work; we pick confold - # so the keepers from the BACKUP's restored configs win, - # matching what the operator implicitly asked for. + # Full output to $apt_log so the spinner keeps turning + # instead of stalling silently then dumping at the end. + # --force-confold keeps the restored configs over any + # ucf prompts that would otherwise block dpkg. DEBIAN_FRONTEND=noninteractive \ apt-get install -y \ -o Dpkg::Options::="--force-confdef" \ diff --git a/scripts/backup_restore/lib_host_backup_common.sh b/scripts/backup_restore/lib_host_backup_common.sh index 8e0526d7..752076c4 100644 --- a/scripts/backup_restore/lib_host_backup_common.sh +++ b/scripts/backup_restore/lib_host_backup_common.sh @@ -1165,10 +1165,8 @@ hb_ensure_borg() { hb_borg_init_if_needed() { local borg_bin="$1" repo="$2" encrypt_mode="$3" - # Borg reads passphrase prompts from /dev/tty even when stdout/stderr - # are redirected — so `>/dev/null 2>&1` is not enough to silence it. - # Close stdin (`/dev/null 2>&1` does not suppress. if "$borg_bin" list "$repo" /dev/null 2>&1; then return 0 fi @@ -1193,11 +1191,6 @@ hb_prepare_borg_passphrase() { return 0 fi # Saved target, no pw yet — ask once and persist next to its config. - # The title is explicit about which passphrase: operators were typing - # their SSH/server password here, which silently persisted as the - # repokey passphrase and made every subsequent `borg list` fail with - # "passphrase ... is incorrect", dropping them back to the menu with - # only a one-second red flash for feedback. local sel_pass sel_pass=$(dialog --backtitle "ProxMenux" --colors --insecure \ --title "$(hb_translate "Borg repository passphrase")" \ @@ -1802,11 +1795,8 @@ hb_select_borg_repo() { if [[ "$choice" == "$add_idx" ]]; then hb_configure_borg_manual _borg_repo_ref || return 1 - # The new target is saved under HB_BORG_LAST_SAVED_NAME. Promote - # it to SELECTED so hb_prepare_borg_passphrase enters the saved- - # target branch (one passphrase prompt, then persist), instead - # of the brand-new branch (the "¿Cifrar?" yes/no flow that also - # races with borg picking up the repokey prompt on the TTY). + # Promote the freshly-saved target so hb_prepare_borg_passphrase + # takes the saved-target branch (single prompt + persist). if [[ -n "${HB_BORG_LAST_SAVED_NAME:-}" ]]; then HB_BORG_SELECTED_NAME="$HB_BORG_LAST_SAVED_NAME" HB_BORG_SELECTED_PASS="" @@ -1848,11 +1838,8 @@ hb_select_borg_repo() { else unset BORG_RSH fi - # Saved URL may differ from the repo's recorded location by one - # trailing slash (e.g. ssh://host//path vs ssh://host/path). Borg - # prompts y/N to confirm — without a TTY the answer is empty and - # borg aborts with a red one-liner, dropping the user back to the - # menu. Trust the saved target. + # Trust the saved URL/key — skip borg's interactive y/N confirmations + # when the recorded repo location or encryption status drifts. export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes HB_BORG_SELECTED_NAME="${HB_BORG_NAMES[$sel]}" @@ -2597,9 +2584,6 @@ hb_show_compat_report() { local tmpfile tmpfile=$(mktemp) - # Leading blank line before the summary — dialog's --textbox draws the - # title flush to the top border, and stacking the summary directly under - # it looks cramped. One empty line gives the eye a break before the data. { printf '\n%s\n' "$summary" printf '%s\n\n' "────────────────────────────────────────────────────────────" diff --git a/scripts/gpu_tpu/nvidia_installer.sh b/scripts/gpu_tpu/nvidia_installer.sh index f0e77df9..0b0fdfb7 100644 --- a/scripts/gpu_tpu/nvidia_installer.sh +++ b/scripts/gpu_tpu/nvidia_installer.sh @@ -1150,12 +1150,8 @@ EOF } apply_nvidia_patch_if_needed() { - # NVIDIA_PATCH_AUTO lets non-interactive callers (the post-restore - # auto-reinstall path in particular) skip the yes/no: - # yes → apply the patch silently (the operator decided "true" was - # the recorded state in the backup we just restored from) - # no → don't apply, mark as not patched (recorded state was false) - # unset → ask, as before (interactive use from the menu) + # NVIDIA_PATCH_AUTO=yes|no skips the yes/no prompt for non-interactive + # callers; unset preserves the interactive menu behavior. case "${NVIDIA_PATCH_AUTO:-}" in yes) : ;; no) @@ -1663,13 +1659,8 @@ auto_reinstall_from_state() { fi install_udev_rules_and_persistenced >>"$LOG_FILE" 2>&1 - # If the backup said the driver was patched, re-apply the keylase - # patch in non-interactive mode (NVIDIA_PATCH_AUTO=yes bypasses the - # yes/no prompt and goes straight to clone+apply). The patch helper - # writes its own update_component_status with {"patched":true} on - # success, so we don't overwrite it below. If the backup said the - # driver was NOT patched, write {"patched":false} explicitly so the - # operator's recorded preference doesn't silently flip. + # Preserve the recorded patched state across the reinstall. The patch + # helper writes its own update_component_status on success. CURRENT_DRIVER_VERSION="$DRIVER_VERSION" if [[ "$recorded_patched" == "true" ]]; then echo "Recorded state had patched=true — re-applying NVIDIA patch..." | tee -a "$LOG_FILE"