Update scripts

This commit is contained in:
MacRimi
2026-04-12 20:32:34 +02:00
parent 4fa4bbb08b
commit 4843fae0eb
47 changed files with 8313 additions and 3014 deletions

View File

@@ -28,6 +28,7 @@ LOCAL_SCRIPTS_LOCAL="$(cd "$SCRIPT_DIR/.." && pwd)"
LOCAL_SCRIPTS_DEFAULT="/usr/local/share/proxmenux/scripts"
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_DEFAULT"
BASE_DIR="/usr/local/share/proxmenux"
TOOLS_JSON="$BASE_DIR/installed_tools.json"
UTILS_FILE="$LOCAL_SCRIPTS/utils.sh"
if [[ -f "$LOCAL_SCRIPTS_LOCAL/utils.sh" ]]; then
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_LOCAL"
@@ -190,12 +191,80 @@ _vm_switch_action_label() {
esac
}
_gpu_register_vfio_iommu_tool() {
command -v jq >/dev/null 2>&1 || return 0
[[ -f "$TOOLS_JSON" ]] || echo "{}" > "$TOOLS_JSON"
jq '.vfio_iommu=true' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" \
&& mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" || true
}
_set_wizard_result() {
local result="$1"
[[ -z "${GPU_WIZARD_RESULT_FILE:-}" ]] && return 0
printf '%s\n' "$result" >"$GPU_WIZARD_RESULT_FILE" 2>/dev/null || true
}
# ==========================================================
# UI wrapper helpers — dialog in standalone, whiptail in wizard
# ==========================================================
# Strips dialog color sequences (\Zb, \Z1, \Zn, etc.) from a string
_strip_colors() {
printf '%s' "$1" | sed 's/\\Z[0-9a-zA-Z]//g'
}
# Msgbox: dialog in standalone mode, whiptail in wizard mode
_pmx_msgbox() {
local title="$1" msg="$2" h="${3:-10}" w="${4:-72}"
if [[ "$WIZARD_CALL" == "true" ]]; then
whiptail --backtitle "ProxMenux" --title "$title" \
--msgbox "$(_strip_colors "$msg")" "$h" "$w"
else
dialog --backtitle "ProxMenux" --colors \
--title "$title" --msgbox "$msg" "$h" "$w"
fi
}
# Yesno: dialog in standalone mode, whiptail in wizard mode
# Returns 0 for yes, 1 for no (same as dialog/whiptail)
_pmx_yesno() {
local title="$1" msg="$2" h="${3:-10}" w="${4:-72}"
if [[ "$WIZARD_CALL" == "true" ]]; then
whiptail --backtitle "ProxMenux" --title "$title" \
--yesno "$(_strip_colors "$msg")" "$h" "$w"
else
dialog --backtitle "ProxMenux" --colors \
--title "$title" --yesno "$msg" "$h" "$w"
fi
return $?
}
# Menu: dialog in standalone mode, whiptail in wizard mode
# Accepts optional --default-item VALUE before title
# Usage: _pmx_menu [--default-item VAL] title msg h w list_h item desc ...
_pmx_menu() {
local -a extra_opts=()
while [[ "${1:-}" == --* ]]; do
case "$1" in
--default-item) extra_opts+=("--default-item" "$2"); shift 2 ;;
*) shift ;;
esac
done
local title="$1" msg="$2" h="$3" w="$4" lh="$5"
shift 5
if [[ "$WIZARD_CALL" == "true" ]]; then
whiptail --backtitle "ProxMenux" "${extra_opts[@]}" \
--title "$title" \
--menu "$(_strip_colors "$msg")" "$h" "$w" "$lh" \
"$@" 3>&1 1>&2 2>&3
else
dialog --backtitle "ProxMenux" --colors "${extra_opts[@]}" \
--title "$title" \
--menu "$msg" "$h" "$w" "$lh" \
"$@" 2>&1 >/dev/tty
fi
return $?
}
_file_has_exact_line() {
local line="$1"
local file="$2"
@@ -398,18 +467,16 @@ ensure_selected_gpu_not_already_in_target_vm() {
TARGET_VM_ALREADY_HAS_GPU=true
local popup_title
popup_title=$(_get_vm_run_title)
dialog --backtitle "ProxMenux" \
--title "${popup_title}" \
--msgbox "\n$(translate 'The selected GPU is already assigned to this VM, but the host is not currently using vfio-pci for this device.')\n\n$(translate 'Current driver'): ${current_driver}\n\n$(translate 'The script will continue to restore VM passthrough mode on the host and reuse existing hostpci entries.')" \
_pmx_msgbox "${popup_title}" \
"\n$(translate 'The selected GPU is already assigned to this VM, but the host is not currently using vfio-pci for this device.')\n\n$(translate 'Current driver'): ${current_driver}\n\n$(translate 'The script will continue to restore VM passthrough mode on the host and reuse existing hostpci entries.')" \
13 78
return 0
fi
# Single GPU system: nothing else to choose
if [[ $GPU_COUNT -le 1 ]]; then
dialog --backtitle "ProxMenux" \
--title "$(translate 'GPU Already Added')" \
--msgbox "\n$(translate 'The selected GPU is already assigned to this VM.')\n\n$(translate 'No changes are required.')" \
_pmx_msgbox "$(translate 'GPU Already Added')" \
"\n$(translate 'The selected GPU is already assigned to this VM.')\n\n$(translate 'No changes are required.')" \
9 66
exit 0
fi
@@ -428,22 +495,18 @@ ensure_selected_gpu_not_already_in_target_vm() {
done
if [[ $available -eq 0 ]]; then
dialog --backtitle "ProxMenux" \
--title "$(translate 'All GPUs Already Assigned')" \
--msgbox "\n$(translate 'All detected GPUs are already assigned to this VM.')\n\n$(translate 'No additional GPU can be added.')" \
_pmx_msgbox "$(translate 'All GPUs Already Assigned')" \
"\n$(translate 'All detected GPUs are already assigned to this VM.')\n\n$(translate 'No additional GPU can be added.')" \
10 70
exit 0
fi
local choice
local -a clear_opt=()
[[ "$WIZARD_CALL" != "true" ]] && clear_opt+=(--clear)
choice=$(dialog "${clear_opt[@]}" --backtitle "ProxMenux" --colors \
--title "$(translate 'GPU Already Assigned to This VM')" \
--menu "\n$(translate 'The selected GPU is already present in this VM. Select another GPU to continue:')" \
choice=$(_pmx_menu \
"$(translate 'GPU Already Assigned to This VM')" \
"\n$(translate 'The selected GPU is already present in this VM. Select another GPU to continue:')" \
18 82 10 \
"${menu_items[@]}" \
2>&1 >/dev/tty) || exit 0
"${menu_items[@]}") || exit 0
SELECTED_GPU="${ALL_GPU_TYPES[$choice]}"
SELECTED_GPU_PCI="${ALL_GPU_PCIS[$choice]}"
@@ -492,9 +555,8 @@ detect_host_gpus() {
if [[ $GPU_COUNT -eq 0 ]]; then
_set_wizard_result "no_gpu"
dialog --backtitle "ProxMenux" \
--title "$(translate 'No GPU Detected')" \
--msgbox "\n$(translate 'No compatible GPU was detected on this host.')" 8 60
_pmx_msgbox "$(translate 'No GPU Detected')" \
"\n$(translate 'No compatible GPU was detected on this host.')" 8 60
exit 0
fi
@@ -506,7 +568,16 @@ detect_host_gpus() {
# Phase 1 — Step 2: Check IOMMU, offer to enable it
# ==========================================================
check_iommu_enabled() {
# Dedup: if IOMMU was already configured by another script in this wizard run, skip prompt
if [[ "${VM_STORAGE_IOMMU_PENDING_REBOOT:-0}" == "1" ]]; then
IOMMU_PENDING_REBOOT=true
HOST_CONFIG_CHANGED=true
_gpu_register_vfio_iommu_tool
return 0
fi
if declare -F _pci_is_iommu_active >/dev/null 2>&1 && _pci_is_iommu_active; then
_gpu_register_vfio_iommu_tool
return 0
fi
@@ -519,9 +590,11 @@ check_iommu_enabled() {
if [[ "$configured_next_boot" == "true" ]]; then
IOMMU_PENDING_REBOOT=true
HOST_CONFIG_CHANGED=true
dialog --backtitle "ProxMenux" \
--title "$(translate 'IOMMU Pending Reboot')" \
--msgbox "\n$(translate 'IOMMU is already configured for next boot, but it is not active yet.')\n\n$(translate 'GPU passthrough configuration will continue now and will become effective after host reboot.')" \
_gpu_register_vfio_iommu_tool
VM_STORAGE_IOMMU_PENDING_REBOOT=1
export VM_STORAGE_IOMMU_PENDING_REBOOT
_pmx_msgbox "$(translate 'IOMMU Pending Reboot')" \
"\n$(translate 'IOMMU is already configured for next boot, but it is not active yet.')\n\n$(translate 'GPU passthrough configuration will continue now and will become effective after host reboot.')" \
11 78
return 0
fi
@@ -533,9 +606,7 @@ check_iommu_enabled() {
msg+="$(translate 'Note: A system reboot will be required after enabling IOMMU.')\n"
msg+="$(translate 'Configuration will continue now and be effective after reboot.')"
dialog --backtitle "ProxMenux" \
--title "$(translate 'IOMMU Required')" \
--yesno "$msg" 15 72
_pmx_yesno "$(translate 'IOMMU Required')" "$msg" 15 72
local response=$?
[[ "$WIZARD_CALL" != "true" ]] && clear
@@ -553,6 +624,9 @@ check_iommu_enabled() {
fi
IOMMU_PENDING_REBOOT=true
HOST_CONFIG_CHANGED=true
_gpu_register_vfio_iommu_tool
VM_STORAGE_IOMMU_PENDING_REBOOT=1
export VM_STORAGE_IOMMU_PENDING_REBOOT
echo
msg_success "$(translate 'IOMMU configured. GPU passthrough setup will continue now and will be effective after reboot.')"
echo
@@ -632,14 +706,11 @@ select_gpu() {
done
local choice
local -a clear_opt=()
[[ "$WIZARD_CALL" != "true" ]] && clear_opt+=(--clear)
choice=$(dialog "${clear_opt[@]}" --backtitle "ProxMenux" --colors \
--title "$(translate 'Select GPU for VM Passthrough')" \
--menu "\n$(translate 'Select the GPU to pass through to the VM:')" \
choice=$(_pmx_menu \
"$(translate 'Select GPU for VM Passthrough')" \
"\n$(translate 'Select the GPU to pass through to the VM:')" \
18 82 10 \
"${menu_items[@]}" \
2>&1 >/dev/tty) || exit 0
"${menu_items[@]}") || exit 0
SELECTED_GPU="${ALL_GPU_TYPES[$choice]}"
SELECTED_GPU_PCI="${ALL_GPU_PCIS[$choice]}"
@@ -665,9 +736,7 @@ warn_single_gpu() {
msg+="$(translate 'Make sure you have SSH or Web UI access before rebooting.')\n\n"
msg+="$(translate 'Do you want to continue?')"
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'Single GPU Warning')" \
--yesno "$msg" 22 76
_pmx_yesno "$(translate 'Single GPU Warning')" "$msg" 22 76
[[ $? -ne 0 ]] && exit 0
}
@@ -765,9 +834,7 @@ check_intel_vm_compatibility() {
msg+="$(translate 'This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.')\n\n"
msg+="$(translate 'Recommended: use GPU with LXC workloads instead of VM passthrough on this hardware.')"
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'Blocked GPU ID')" \
--msgbox "$msg" 20 84
_pmx_msgbox "$(translate 'Blocked GPU ID')" "$msg" 20 84
exit 0
fi
@@ -782,9 +849,7 @@ check_intel_vm_compatibility() {
msg+="$(translate 'This state has a high probability of VM startup/reset failures.')\n\n"
msg+="\Zb$(translate 'Configuration has been stopped to prevent an unusable VM state.')\Zn"
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'High-Risk GPU Power State')" \
--msgbox "$msg" 20 80
_pmx_msgbox "$(translate 'High-Risk GPU Power State')" "$msg" 20 80
exit 0
fi
@@ -800,9 +865,7 @@ check_intel_vm_compatibility() {
msg+="$(translate 'startup/restart errors are likely.')\n\n"
msg+="\Zb$(translate 'Configuration has been stopped due to high reset risk.')\Zn"
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'Reset Capability Blocked')" \
--msgbox "$msg" 20 80
_pmx_msgbox "$(translate 'Reset Capability Blocked')" "$msg" 20 80
exit 0
fi
@@ -818,9 +881,7 @@ check_intel_vm_compatibility() {
msg+="$(translate 'start/restart failures and reset instability.')\n\n"
msg+="\Zb$(translate 'Configuration has been stopped due to high reset risk.')\Zn"
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'Reset Capability Blocked')" \
--msgbox "$msg" 20 80
_pmx_msgbox "$(translate 'Reset Capability Blocked')" "$msg" 20 80
exit 0
fi
@@ -834,9 +895,7 @@ check_intel_vm_compatibility() {
msg+="$(translate 'Passthrough may work, but startup/restart reliability is not guaranteed.')\n\n"
msg+="$(translate 'Do you want to continue anyway?')"
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'Reset Capability Warning')" \
--yesno "$msg" 18 78
_pmx_yesno "$(translate 'Reset Capability Warning')" "$msg" 18 78
[[ $? -ne 0 ]] && exit 0
fi
}
@@ -872,9 +931,7 @@ check_gpu_vm_compatibility() {
msg+="$(translate 'Potential QEMU startup/assertion failures')\n\n"
msg+="\Zb$(translate 'Configuration has been stopped to prevent an unusable VM state.')\Zn"
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'High-Risk GPU Power State')" \
--msgbox "$msg" 22 80
_pmx_msgbox "$(translate 'High-Risk GPU Power State')" "$msg" 22 80
exit 0
fi
@@ -903,9 +960,7 @@ check_gpu_vm_compatibility() {
msg+=" — QEMU IRQ assertion failure → VM does not start\n\n"
msg+="\Zb$(translate 'Configuration has been stopped to prevent leaving the VM in an unusable state.')\Zn"
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'Incompatible GPU for VM Passthrough')" \
--msgbox "$msg" 26 80
_pmx_msgbox "$(translate 'Incompatible GPU for VM Passthrough')" "$msg" 26 80
exit 0
fi
@@ -922,9 +977,7 @@ check_gpu_vm_compatibility() {
msg+="$(translate 'for this policy and may fail after first use or on subsequent VM starts.')\n\n"
msg+="\Zb$(translate 'Configuration has been stopped due to high reset risk.')\Zn"
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'Reset Capability Blocked')" \
--msgbox "$msg" 20 80
_pmx_msgbox "$(translate 'Reset Capability Blocked')" "$msg" 20 80
exit 0
fi
@@ -939,9 +992,7 @@ check_gpu_vm_compatibility() {
msg+="$(translate 'Passthrough may fail depending on hardware/firmware implementation.')\n\n"
msg+="$(translate 'Do you want to continue anyway?')"
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'Reset Capability Warning')" \
--yesno "$msg" 18 78
_pmx_yesno "$(translate 'Reset Capability Warning')" "$msg" 18 78
[[ $? -ne 0 ]] && exit 0
fi
}
@@ -965,16 +1016,14 @@ analyze_iommu_group() {
did=$(cat "/sys/bus/pci/devices/${pci_full}/device" 2>/dev/null | sed 's/0x//')
[[ -n "$vid" && -n "$did" ]] && IOMMU_VFIO_IDS+=("${vid}:${did}")
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'IOMMU Group Pending')" \
--msgbox "\n$(translate 'IOMMU groups are not available yet because reboot is pending.')\n\n$(translate 'The script will preconfigure the selected GPU now and finalize hardware binding after reboot.')\n\n$(translate 'Selected GPU function'):\n • ${pci_full}" \
_pmx_msgbox "$(translate 'IOMMU Group Pending')" \
"\n$(translate 'IOMMU groups are not available yet because reboot is pending.')\n\n$(translate 'The script will preconfigure the selected GPU now and finalize hardware binding after reboot.')\n\n$(translate 'Selected GPU function'):\n • ${pci_full}" \
14 82
return 0
fi
dialog --backtitle "ProxMenux" \
--title "$(translate 'IOMMU Group Error')" \
--msgbox "\n$(translate 'Could not determine the IOMMU group for the selected GPU.')\n\n$(translate 'Make sure IOMMU is properly enabled and the system has been rebooted after activation.')" \
_pmx_msgbox "$(translate 'IOMMU Group Error')" \
"\n$(translate 'Could not determine the IOMMU group for the selected GPU.')\n\n$(translate 'Make sure IOMMU is properly enabled and the system has been rebooted after activation.')" \
10 72
exit 1
fi
@@ -1016,19 +1065,6 @@ analyze_iommu_group() {
[[ "$dev" != "$pci_full" ]] && extra_devices=$((extra_devices + 1))
done
local msg
msg="$(translate 'IOMMU Group'): ${IOMMU_GROUP}\n\n"
msg+="$(translate 'The following devices will all be passed to the VM') "
msg+="($(translate 'IOMMU isolation rule')):\n\n"
msg+="${display_lines}"
if [[ $extra_devices -gt 0 ]]; then
msg+="\n\Z1$(translate 'All devices in the same IOMMU group must be passed together.')\Zn"
fi
dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'IOMMU Group') ${IOMMU_GROUP}" \
--msgbox "\n${msg}" 22 82
}
detect_optional_gpu_audio() {
@@ -1078,9 +1114,8 @@ select_vm() {
VM_NAME=$(qm config "$SELECTED_VMID" 2>/dev/null | grep "^name:" | awk '{print $2}')
return 0
fi
dialog --backtitle "ProxMenux" \
--title "$(translate 'Invalid VMID')" \
--msgbox "\n$(translate 'The preselected VMID does not exist on this host:') ${PRESELECT_VMID}" 9 72
_pmx_msgbox "$(translate 'Invalid VMID')" \
"\n$(translate 'The preselected VMID does not exist on this host:') ${PRESELECT_VMID}" 9 72
exit 1
fi
@@ -1097,19 +1132,17 @@ select_vm() {
done < <(qm list 2>/dev/null)
if [[ ${#menu_items[@]} -eq 0 ]]; then
dialog --backtitle "ProxMenux" \
--title "$(translate 'No VMs Found')" \
--msgbox "\n$(translate 'No Virtual Machines found on this system.')\n\n$(translate 'Create a VM first (machine type q35 + UEFI BIOS), then run this option again.')" \
_pmx_msgbox "$(translate 'No VMs Found')" \
"\n$(translate 'No Virtual Machines found on this system.')\n\n$(translate 'Create a VM first (machine type q35 + UEFI BIOS), then run this option again.')" \
10 68
exit 0
fi
SELECTED_VMID=$(dialog --backtitle "ProxMenux" \
--title "$(translate 'Select Virtual Machine')" \
--menu "\n$(translate 'Select the VM to add the GPU to:')" \
SELECTED_VMID=$(_pmx_menu \
"$(translate 'Select Virtual Machine')" \
"\n$(translate 'Select the VM to add the GPU to:')" \
20 72 12 \
"${menu_items[@]}" \
2>&1 >/dev/tty) || exit 0
"${menu_items[@]}") || exit 0
VM_NAME=$(qm config "$SELECTED_VMID" 2>/dev/null | grep "^name:" | awk '{print $2}')
}
@@ -1138,9 +1171,7 @@ check_vm_machine_type() {
msg+="$(translate 'BIOS: OVMF (UEFI)')\n"
msg+="$(translate 'Storage controller: VirtIO SCSI')"
dialog --backtitle "ProxMenux" \
--title "$(translate 'Incompatible Machine Type')" \
--msgbox "$msg" 20 78
_pmx_msgbox "$(translate 'Incompatible Machine Type')" "$msg" 20 78
exit 0
}
@@ -1210,13 +1241,11 @@ check_switch_mode() {
msg+="\Z1\Zb$(translate 'Start on boot enabled (onboot=1)'): ${onboot_count}\Zn\n"
msg+="\n\Z1$(translate 'After this LXC → VM switch, reboot the host so the new binding state is applied cleanly.')\Zn"
action_choice=$(dialog --backtitle "ProxMenux" --colors \
--title "$(translate 'GPU Used in LXC Containers')" \
--default-item "2" \
--menu "$msg" 25 96 8 \
action_choice=$(_pmx_menu --default-item "2" \
"$(translate 'GPU Used in LXC Containers')" \
"$msg" 25 96 8 \
"1" "$(translate 'Keep GPU in LXC config (disable Start on boot)')" \
"2" "$(translate 'Remove GPU from LXC config (keep Start on boot)')" \
2>&1 >/dev/tty) || exit 0
"2" "$(translate 'Remove GPU from LXC config (keep Start on boot)')") || exit 0
case "$action_choice" in
1) LXC_SWITCH_ACTION="keep_gpu_disable_onboot" ;;
@@ -1254,9 +1283,7 @@ check_switch_mode() {
msg+=" Hardware Graphics → Add GPU to VM\n"
msg+="$(translate 'to move the GPU safely.')"
dialog --backtitle "ProxMenux" \
--title "$(translate 'GPU Busy in Running VM')" \
--msgbox "$msg" 16 78
_pmx_msgbox "$(translate 'GPU Busy in Running VM')" "$msg" 16 78
exit 0
fi
@@ -1292,13 +1319,11 @@ check_switch_mode() {
msg+="$(translate 'Choose conflict policy for the source VM:')"
local vm_action_choice
vm_action_choice=$(dialog --clear --backtitle "ProxMenux" --colors \
--title "$(translate 'GPU Already Assigned to Another VM')" \
--default-item "1" \
--menu "$msg" 24 98 8 \
vm_action_choice=$(_pmx_menu --default-item "1" \
"$(translate 'GPU Already Assigned to Another VM')" \
"$msg" 24 84 8 \
"1" "$(translate 'Keep GPU in source VM config (disable Start on boot if enabled)')" \
"2" "$(translate 'Remove GPU from source VM config (keep Start on boot)')" \
2>&1 >/dev/tty) || exit 0
"2" "$(translate 'Remove GPU from source VM config (keep Start on boot)')") || exit 0
case "$vm_action_choice" in
1) SWITCH_VM_ACTION="keep_gpu_disable_onboot" ;;
@@ -1376,9 +1401,7 @@ confirm_summary() {
local run_title
run_title=$(_get_vm_run_title)
dialog --clear --backtitle "ProxMenux" --colors \
--title "${run_title}" \
--yesno "$msg" 28 78
_pmx_yesno "${run_title}" "$msg" 28 78
[[ $? -ne 0 ]] && exit 0
}
@@ -1724,7 +1747,7 @@ cleanup_vm_config() {
local pci_slot="${SELECTED_GPU_PCI#0000:}"
pci_slot="${pci_slot%.*}" # 01:00
if [[ "$VM_SWITCH_ACTION" == "keep_gpu_disable_onboot" ]]; then
if [[ "$SWITCH_VM_ACTION" == "keep_gpu_disable_onboot" ]]; then
msg_info "$(translate 'Keeping GPU in source VM config') ${SWITCH_VM_SRC}..."
if _vm_onboot_enabled "$SWITCH_VM_SRC"; then
if qm set "$SWITCH_VM_SRC" -onboot 0 >>"$LOG_FILE" 2>&1; then
@@ -1916,7 +1939,6 @@ main() {
if [[ "$WIZARD_CALL" == "true" ]]; then
echo
else
clear
show_proxmenux_logo
msg_title "${run_title}"
fi

View File

@@ -0,0 +1,160 @@
#!/bin/bash
# ==========================================================
# ProxMenux - GPU/TPU Manual CLI Guide
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : GPL-3.0
# Version : 1.0
# Last Updated: 07/04/2026
# ==========================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOCAL_SCRIPTS_LOCAL="$(cd "$SCRIPT_DIR/.." && pwd)"
LOCAL_SCRIPTS_DEFAULT="/usr/local/share/proxmenux/scripts"
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_DEFAULT"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$LOCAL_SCRIPTS/utils.sh"
if [[ -f "$LOCAL_SCRIPTS_LOCAL/utils.sh" ]]; then
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_LOCAL"
UTILS_FILE="$LOCAL_SCRIPTS/utils.sh"
elif [[ ! -f "$UTILS_FILE" ]]; then
UTILS_FILE="$BASE_DIR/utils.sh"
fi
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
GREEN=$'\033[0;32m'
NC=$'\033[0m'
_cl() {
# _cl <num> <display_cmd> <description>
# Prints a numbered command line with fixed-column alignment (separator at col 52).
local num="$1" disp="$2" desc="$3"
local pad=$((47 - ${#disp}))
[[ $pad -lt 1 ]] && pad=1
local spaces
spaces=$(printf '%*s' "$pad" '')
printf " %2d) %s%s%s%s - %s\n" "$num" "$GREEN" "$disp" "$NC" "$spaces" "$desc"
}
while true; do
clear
show_proxmenux_logo
msg_title "$(translate "GPU/TPU - Manual CLI Guide")"
echo -e "${TAB}${YW}$(translate 'Inspection commands run directly. Template commands [T] require parameter substitution.')${CL}"
echo
_cl 1 "lspci -nn | grep -iE 'VGA|3D|Display'" "$(translate 'Detect GPUs in host')"
_cl 2 "lspci -nnk | grep -A3 -Ei 'VGA|3D'" "$(translate 'Show GPU kernel driver in use')"
_cl 3 "cat /proc/cmdline" "$(translate 'Check kernel params (IOMMU flags)')"
_cl 4 "dmesg -T | grep -Ei 'DMAR|IOMMU|vfio|pcie'" "$(translate 'Inspect passthrough/kernel events')"
_cl 5 "find /sys/kernel/iommu_groups -type l" "$(translate 'List IOMMU group mapping')"
_cl 6 "lsmod | grep -E 'vfio|nvidia|amdgpu|apex'" "$(translate 'Check loaded GPU/TPU modules')"
_cl 7 "grep -R \"vfio-pci|blacklist\" /etc/modprobe.d" "$(translate 'Review passthrough config files')"
_cl 8 "nvidia-smi" "$(translate 'Check NVIDIA driver and devices')"
_cl 9 "qm config <vmid> | grep 'hostpci|bios'" "$(translate 'Check VM passthrough settings')"
_cl 10 "pct config <ctid> | grep 'dev|lxc.cgroup2'" "$(translate 'Check LXC GPU/TPU mapping')"
_cl 11 "ls -l /dev/dri /dev/kfd /dev/nvidia*" "$(translate 'Inspect host device nodes')"
_cl 12 "qm set <vmid> --hostpci<slot> <BDF>,pcie=1" "[T] $(translate 'Assign GPU PCI function to VM')"
_cl 13 "qm set <vmid> -delete hostpci<slot>" "[T] $(translate 'Remove passthrough device from VM')"
_cl 14 "qm set <vmid> -onboot 0" "[T] $(translate 'Disable autostart on conflicting VM')"
_cl 15 "sed -i '/GRUB_CMDLINE_LINUX_DEFAULT/ s|...|'" "[T] $(translate 'Enable IOMMU in GRUB or ZFS boot')"
_cl 16 "update-initramfs -u && proxmox-boot-tool" "[T] $(translate 'Apply boot/initramfs changes')"
_cl 17 "lsusb | grep Coral ; lspci | grep Unichip" "$(translate 'Check Coral USB/M.2 detection')"
echo -e " ${DEF} 0) $(translate 'Back to previous menu or Esc + Enter')${CL}"
echo
echo -en "${TAB}${BOLD}${YW}${HOLD}$(translate 'Enter a number, or write or paste a command: ') ${CL}"
read -r user_input
if [[ "$user_input" == $'\x1b' ]]; then
break
fi
mode="exec"
case "$user_input" in
1) cmd="lspci -nn | grep -iE 'VGA compatible|3D controller|Display controller'" ;;
2) cmd="lspci -nnk | grep -A3 -Ei 'VGA compatible|3D controller|Display controller'" ;;
3) cmd="cat /proc/cmdline" ;;
4) cmd="dmesg -T | grep -Ei 'DMAR|IOMMU|vfio|pcie|AER|reset'" ;;
5) cmd="find /sys/kernel/iommu_groups -type l" ;;
6) cmd="lsmod | grep -E 'vfio|nvidia|amdgpu|i915|apex|gasket'" ;;
7) cmd="grep -R \"vfio-pci\\|blacklist .*nvidia\\|blacklist .*amdgpu\\|blacklist .*radeon\" /etc/modprobe.d /etc/modules /etc/default/grub /etc/kernel/cmdline 2>/dev/null" ;;
8) cmd="nvidia-smi" ;;
9)
echo -en "${TAB}${BOLD}${YW}${HOLD}$(translate 'Enter VM ID: ')${CL}"
read -r vmid
cmd="qm config $vmid | grep -E '^(hostpci|cpu:|machine:|bios:|args:|boot:)'"
;;
10)
echo -en "${TAB}${BOLD}${YW}${HOLD}$(translate 'Enter CT ID: ')${CL}"
read -r ctid
cmd="pct config $ctid | grep -E '^(dev[0-9]+:|lxc\\.cgroup2\\.devices\\.allow:|lxc\\.mount\\.entry:|features:)'"
;;
11) cmd="ls -l /dev/dri /dev/kfd /dev/nvidia* /dev/apex* 2>/dev/null" ;;
12)
echo -en "${TAB}${BOLD}${YW}${HOLD}$(translate 'Enter VM ID: ')${CL}"; read -r vmid
echo -en "${TAB}${BOLD}${YW}${HOLD}$(translate 'Enter hostpci slot (e.g. 0): ')${CL}"; read -r slot
echo -en "${TAB}${BOLD}${YW}${HOLD}$(translate 'Enter PCI BDF (e.g. 0000:01:00.0): ')${CL}"; read -r bdf
cmd="qm set $vmid --hostpci${slot} ${bdf},pcie=1"
mode="template"
;;
13)
echo -en "${TAB}${BOLD}${YW}${HOLD}$(translate 'Enter VM ID: ')${CL}"; read -r vmid
echo -en "${TAB}${BOLD}${YW}${HOLD}$(translate 'Enter hostpci slot (e.g. 0): ')${CL}"; read -r slot
cmd="qm set $vmid -delete hostpci${slot}"
mode="template"
;;
14)
echo -en "${TAB}${BOLD}${YW}${HOLD}$(translate 'Enter VM ID: ')${CL}"; read -r vmid
cmd="qm set $vmid -onboot 0"
mode="template"
;;
15)
echo -en "${TAB}${BOLD}${YW}${HOLD}$(translate 'Boot type (grub/zfs): ')${CL}"; read -r boot_type
echo -en "${TAB}${BOLD}${YW}${HOLD}$(translate 'CPU vendor (intel/amd): ')${CL}"; read -r cpu_vendor
case "$cpu_vendor" in
amd|AMD) iommu_param="amd_iommu=on iommu=pt" ;;
*) iommu_param="intel_iommu=on iommu=pt" ;;
esac
case "$boot_type" in
zfs|ZFS) cmd="sed -i 's/\$/ ${iommu_param}/' /etc/kernel/cmdline" ;;
*) cmd="sed -i '/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param}\"|' /etc/default/grub" ;;
esac
mode="template"
;;
16)
cmd="update-initramfs -u -k all && (proxmox-boot-tool refresh || update-grub)"
mode="template"
;;
17) cmd="lsusb | grep -Ei '18d1:9302|1a6e:089a' ; lspci | grep -i 'Global Unichip'" ;;
0) break ;;
*)
if [[ -n "$user_input" ]]; then
cmd="$user_input"
else
continue
fi
;;
esac
if [[ "$mode" == "template" ]]; then
echo -e "\n${GREEN}$(translate 'Manual command template (copy/paste):')${NC}\n"
echo "$cmd"
echo
msg_success "$(translate 'Press ENTER to continue...')"
read -r tmp
continue
fi
echo -e "\n${GREEN}> $cmd${NC}\n"
bash -c "$cmd"
echo
msg_success "$(translate 'Press ENTER to continue...')"
read -r tmp
done

View File

@@ -57,6 +57,33 @@ detect_nvidia_gpus() {
fi
}
check_gpu_not_in_vm_passthrough() {
local dev vendor driver vfio_list=""
for dev in /sys/bus/pci/devices/*; do
vendor=$(cat "$dev/vendor" 2>/dev/null)
[[ "$vendor" != "0x10de" ]] && continue
if [[ -L "$dev/driver" ]]; then
driver=$(basename "$(readlink "$dev/driver")")
if [[ "$driver" == "vfio-pci" ]]; then
vfio_list+="$(basename "$dev")\n"
fi
fi
done
[[ -z "$vfio_list" ]] && return 0
local msg
msg="\n$(translate "One or more NVIDIA GPUs are currently configured for VM passthrough (vfio-pci):")\n\n"
msg+="${vfio_list}\n"
msg+="$(translate "Installing host drivers while the GPU is assigned to a VM could break passthrough and destabilize the system.")\n\n"
msg+="$(translate "To install host drivers, first remove the GPU from VM passthrough configuration and reboot.")"
dialog --backtitle "ProxMenux" \
--title "$(translate "GPU in VM Passthrough Mode")" \
--msgbox "$msg" 16 78
exit 0
}
detect_driver_status() {
CURRENT_DRIVER_INSTALLED=false
CURRENT_DRIVER_VERSION=""
@@ -842,6 +869,7 @@ main() {
detect_nvidia_gpus
detect_driver_status
check_gpu_not_in_vm_passthrough
if ! $NVIDIA_GPU_PRESENT; then
dialog --backtitle "ProxMenux" --title "$(translate 'NVIDIA GPU Driver Installation')" --msgbox \

View File

@@ -23,6 +23,37 @@ load_language
initialize_cache
# ============================================================
# GPU passthrough guard — block update when GPU is in VM passthrough mode
# ============================================================
check_gpu_not_in_vm_passthrough() {
local dev vendor driver vfio_list=""
for dev in /sys/bus/pci/devices/*; do
vendor=$(cat "$dev/vendor" 2>/dev/null)
[[ "$vendor" != "0x10de" ]] && continue
if [[ -L "$dev/driver" ]]; then
driver=$(basename "$(readlink "$dev/driver")")
if [[ "$driver" == "vfio-pci" ]]; then
vfio_list+="$(basename "$dev")\n"
fi
fi
done
[[ -z "$vfio_list" ]] && return 0
local msg
msg="\n$(translate "One or more NVIDIA GPUs are currently configured for VM passthrough (vfio-pci):")\n\n"
msg+="${vfio_list}\n"
msg+="$(translate "Updating host drivers while the GPU is assigned to a VM could break passthrough and destabilize the system.")\n\n"
msg+="$(translate "To update host drivers, first remove the GPU from VM passthrough configuration and reboot.")"
dialog --backtitle "ProxMenux" \
--title "$(translate "GPU in VM Passthrough Mode")" \
--msgbox "$msg" 16 78
exit 0
}
# ============================================================
# Host NVIDIA state detection
# ============================================================
@@ -436,13 +467,25 @@ show_current_state_dialog() {
# Restart prompt
# ============================================================
restart_prompt() {
if whiptail --title "$(translate 'NVIDIA Update')" --yesno \
"$(translate 'The host driver update requires a reboot to take effect. Reboot now?')" 10 70; then
msg_warn "$(translate 'Restarting the server...')"
echo
msg_success "$(translate 'NVIDIA driver update completed.')"
echo
msg_info "$(translate 'Removing no longer required packages and purging old cached updates...')"
apt-get -y autoremove >/dev/null 2>&1
apt-get -y autoclean >/dev/null 2>&1
msg_ok "$(translate 'Cleanup finished.')"
echo -e "${TAB}${BL}Log: ${LOG_FILE}${CL}"
echo
if whiptail --title "$(translate 'Reboot Required')" \
--yesno "$(translate 'The host driver update requires a reboot to take effect. Do you want to restart now?')" 10 70; then
msg_success "$(translate 'Press Enter to continue...')"
read -r
msg_warn "$(translate 'Rebooting the system...')"
reboot
else
msg_success "$(translate 'Update complete. Please reboot the server manually.')"
msg_success "$(translate 'Completed. Press Enter to return to menu...')"
msg_info2 "$(translate 'You can reboot later manually.')"
msg_success "$(translate 'Press Enter to continue...')"
read -r
fi
}
@@ -455,6 +498,7 @@ main() {
: >"$LOG_FILE"
# ---- Phase 1: dialogs ----
check_gpu_not_in_vm_passthrough
detect_host_nvidia
show_current_state_dialog
select_target_version

View File

@@ -888,6 +888,48 @@ apply_vm_action_for_lxc_mode() {
done
}
_register_iommu_tool() {
local tools_json="${BASE_DIR:-/usr/local/share/proxmenux}/installed_tools.json"
command -v jq >/dev/null 2>&1 || return 0
[[ -f "$tools_json" ]] || echo "{}" > "$tools_json"
jq '.vfio_iommu=true' "$tools_json" > "$tools_json.tmp" \
&& mv "$tools_json.tmp" "$tools_json" || true
}
_enable_iommu_cmdline() {
local cpu_vendor
cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}')
local iommu_param
if [[ "$cpu_vendor" == "GenuineIntel" ]]; then
iommu_param="intel_iommu=on"
elif [[ "$cpu_vendor" == "AuthenticAMD" ]]; then
iommu_param="amd_iommu=on"
else
return 1
fi
local cmdline_file="/etc/kernel/cmdline"
local grub_file="/etc/default/grub"
if [[ -f "$cmdline_file" ]] && grep -qE 'root=ZFS=|root=ZFS/' "$cmdline_file" 2>/dev/null; then
if ! grep -q "$iommu_param" "$cmdline_file"; then
cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)"
sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file"
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
fi
elif [[ -f "$grub_file" ]]; then
if ! grep -q "$iommu_param" "$grub_file"; then
cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)"
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file"
update-grub >>"$LOG_FILE" 2>&1 || true
fi
else
return 1
fi
return 0
}
switch_to_vm_mode() {
detect_affected_lxc_for_selected
prompt_lxc_action_for_vm_mode
@@ -897,6 +939,25 @@ switch_to_vm_mode() {
apply_lxc_action_for_vm_mode
msg_info "$(translate 'Configuring host for GPU -> VM mode...')"
if declare -F _pci_is_iommu_active >/dev/null 2>&1 && _pci_is_iommu_active; then
_register_iommu_tool
msg_ok "$(translate 'IOMMU is already active on this system')" | tee -a "$screen_capture"
elif grep -qE 'intel_iommu=on|amd_iommu=on' /etc/kernel/cmdline 2>/dev/null || \
grep -qE 'intel_iommu=on|amd_iommu=on' /etc/default/grub 2>/dev/null; then
_register_iommu_tool
HOST_CONFIG_CHANGED=true
msg_ok "$(translate 'IOMMU already configured in kernel parameters')" | tee -a "$screen_capture"
else
if _enable_iommu_cmdline; then
_register_iommu_tool
HOST_CONFIG_CHANGED=true
msg_ok "$(translate 'IOMMU kernel parameters configured')" | tee -a "$screen_capture"
else
msg_warn "$(translate 'Could not configure IOMMU kernel parameters automatically. Configure manually and reboot.')" | tee -a "$screen_capture"
fi
fi
_add_vfio_modules
msg_ok "$(translate 'VFIO modules configured in /etc/modules')" | tee -a "$screen_capture"
_configure_iommu_options
@@ -1011,6 +1072,45 @@ switch_to_lxc_mode() {
fi
}
# ==========================================================
# Send notification when GPU mode switch completes
# ==========================================================
_send_gpu_mode_notification() {
local new_mode="$1"
local old_mode="$2"
local notify_script="/usr/bin/notification_manager.py"
[[ ! -f "$notify_script" ]] && return 0
local hostname_short
hostname_short=$(hostname -s)
# Build GPU list for notification
local gpu_list=""
local idx
for idx in "${SELECTED_GPU_IDX[@]}"; do
gpu_list+="${ALL_GPU_NAMES[$idx]} (${ALL_GPU_PCIS[$idx]}), "
done
gpu_list="${gpu_list%, }"
local mode_label details
if [[ "$new_mode" == "vm" ]]; then
mode_label="GPU -> VM (VFIO passthrough)"
details="GPU(s) ready for VM passthrough. A host reboot may be required."
else
mode_label="GPU -> LXC (native driver)"
details="GPU(s) available for LXC containers with native drivers."
fi
python3 "$notify_script" --action send-raw --severity INFO \
--title "${hostname_short}: GPU mode changed to ${mode_label}" \
--message "GPU passthrough mode switched.
GPU(s): ${gpu_list}
Previous: ${old_mode}
New: ${mode_label}
${details}" 2>/dev/null || true
}
confirm_plan() {
local msg mode_line
if [[ "$TARGET_MODE" == "vm" ]]; then
@@ -1079,12 +1179,22 @@ main() {
_set_title
echo
# Determine old mode before switch for notification
local old_mode_label
if [[ "$CURRENT_MODE" == "vm" ]]; then
old_mode_label="GPU -> VM (VFIO)"
else
old_mode_label="GPU -> LXC (native)"
fi
if [[ "$TARGET_MODE" == "vm" ]]; then
switch_to_vm_mode
msg_success "$(translate 'GPU switch complete: VM mode prepared.')"
_send_gpu_mode_notification "vm" "$old_mode_label"
else
switch_to_lxc_mode
msg_success "$(translate 'GPU switch complete: LXC mode prepared.')"
_send_gpu_mode_notification "lxc" "$old_mode_label"
fi
final_summary

View File

@@ -816,6 +816,48 @@ apply_vm_action_for_lxc_mode() {
# ==========================================================
# Switch Mode Functions
# ==========================================================
_register_iommu_tool() {
local tools_json="${BASE_DIR:-/usr/local/share/proxmenux}/installed_tools.json"
command -v jq >/dev/null 2>&1 || return 0
[[ -f "$tools_json" ]] || echo "{}" > "$tools_json"
jq '.vfio_iommu=true' "$tools_json" > "$tools_json.tmp" \
&& mv "$tools_json.tmp" "$tools_json" || true
}
_enable_iommu_cmdline() {
local cpu_vendor
cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}')
local iommu_param
if [[ "$cpu_vendor" == "GenuineIntel" ]]; then
iommu_param="intel_iommu=on"
elif [[ "$cpu_vendor" == "AuthenticAMD" ]]; then
iommu_param="amd_iommu=on"
else
return 1
fi
local cmdline_file="/etc/kernel/cmdline"
local grub_file="/etc/default/grub"
if [[ -f "$cmdline_file" ]] && grep -qE 'root=ZFS=|root=ZFS/' "$cmdline_file" 2>/dev/null; then
if ! grep -q "$iommu_param" "$cmdline_file"; then
cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)"
sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file"
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
fi
elif [[ -f "$grub_file" ]]; then
if ! grep -q "$iommu_param" "$grub_file"; then
cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)"
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file"
update-grub >>"$LOG_FILE" 2>&1 || true
fi
else
return 1
fi
return 0
}
switch_to_vm_mode() {
detect_affected_lxc_for_selected
prompt_lxc_action_for_vm_mode
@@ -825,6 +867,25 @@ switch_to_vm_mode() {
apply_lxc_action_for_vm_mode
msg_info "$(translate 'Configuring host for GPU -> VM mode...')"
if declare -F _pci_is_iommu_active >/dev/null 2>&1 && _pci_is_iommu_active; then
_register_iommu_tool
msg_ok "$(translate 'IOMMU is already active on this system')" | tee -a "$screen_capture"
elif grep -qE 'intel_iommu=on|amd_iommu=on' /etc/kernel/cmdline 2>/dev/null || \
grep -qE 'intel_iommu=on|amd_iommu=on' /etc/default/grub 2>/dev/null; then
_register_iommu_tool
HOST_CONFIG_CHANGED=true
msg_ok "$(translate 'IOMMU already configured in kernel parameters')" | tee -a "$screen_capture"
else
if _enable_iommu_cmdline; then
_register_iommu_tool
HOST_CONFIG_CHANGED=true
msg_ok "$(translate 'IOMMU kernel parameters configured')" | tee -a "$screen_capture"
else
msg_warn "$(translate 'Could not configure IOMMU kernel parameters automatically. Configure manually and reboot.')" | tee -a "$screen_capture"
fi
fi
_add_vfio_modules
msg_ok "$(translate 'VFIO modules configured in /etc/modules')" | tee -a "$screen_capture"
_configure_iommu_options
@@ -986,6 +1047,39 @@ final_summary() {
# ==========================================================
# Parse Arguments (supports both CLI args and env vars)
# ==========================================================
# Send notification when GPU mode switch completes
# ==========================================================
_send_gpu_mode_notification() {
local new_mode="$1"
local gpu_name="$2"
local gpu_pci="$3"
local old_mode="$4"
local notify_script="/usr/bin/notification_manager.py"
[[ ! -f "$notify_script" ]] && return 0
local hostname_short
hostname_short=$(hostname -s)
local mode_label details
if [[ "$new_mode" == "vm" ]]; then
mode_label="GPU -> VM (VFIO passthrough)"
details="GPU is now ready for VM passthrough. A host reboot may be required."
else
mode_label="GPU -> LXC (native driver)"
details="GPU is now available for LXC containers with native drivers."
fi
python3 "$notify_script" --action send-raw --severity INFO \
--title "${hostname_short}: GPU mode changed to ${mode_label}" \
--message "GPU passthrough mode switched.
GPU: ${gpu_name} (${gpu_pci})
Previous: ${old_mode}
New: ${mode_label}
${details}" 2>/dev/null || true
}
# ==========================================================
parse_arguments() {
# First, check combined parameter (format: "SLOT|MODE")
@@ -1066,13 +1160,28 @@ main() {
_set_title
echo
# Determine old mode before switch for notification
local old_mode_label
if [[ "$CURRENT_MODE" == "vm" ]]; then
old_mode_label="GPU -> VM (VFIO)"
else
old_mode_label="GPU -> LXC (native)"
fi
# Get GPU info for notification
local gpu_idx="${SELECTED_GPU_IDX[0]}"
local gpu_name="${ALL_GPU_NAMES[$gpu_idx]}"
local gpu_pci="${ALL_GPU_PCIS[$gpu_idx]}"
# Execute the switch
if [[ "$TARGET_MODE" == "vm" ]]; then
switch_to_vm_mode
msg_success "$(translate 'GPU switch complete: VM mode prepared.')"
_send_gpu_mode_notification "vm" "$gpu_name" "$gpu_pci" "$old_mode_label"
else
switch_to_lxc_mode
msg_success "$(translate 'GPU switch complete: LXC mode prepared.')"
_send_gpu_mode_notification "lxc" "$gpu_name" "$gpu_pci" "$old_mode_label"
fi
final_summary