2025-03-23 17:50:33 +01:00
#!/usr/bin/env bash
# ==========================================================
# ProxMenuX - Synology DSM VM Creator Script
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
2026-01-19 17:15:00 +01:00
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
2025-03-23 17:50:33 +01:00
# Version : 1.0
# Last Updated: 13/03/2025
# ==========================================================
# Description:
# This script automates the creation and configuration of a Synology DSM
# (DiskStation Manager) virtual machine (VM) in Proxmox VE. It simplifies the
# setup process by allowing both default and advanced configuration options.
#
# The script automates the complete VM creation process, including loader
# download, disk configuration, and VM boot setup.
#
# **Credits**
# This script is an original idea but incorporates ideas and elements from
# a similar script by user **tim104979** from the ProxmoxVE branch:
# (https://raw.githubusercontent.com/tim104979/ProxmoxVE/refs/heads/main/vm/synology-vm.sh)
#
# Copyright (c) Proxmox VE Helper-Scripts Community
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
#
# ==========================================================
# Configuration ============================================
2026-04-05 11:24:08 +02:00
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 "
2025-03-23 17:50:33 +01:00
BASE_DIR = "/usr/local/share/proxmenux"
2026-04-05 11:24:08 +02:00
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
2025-03-23 17:50:33 +01:00
VENV_PATH = "/opt/googletrans-env"
if [ [ -f " $UTILS_FILE " ] ] ; then
source " $UTILS_FILE "
fi
2026-04-05 11:24:08 +02:00
if [ [ -f " $LOCAL_SCRIPTS_LOCAL /global/vm_storage_helpers.sh " ] ] ; then
source " $LOCAL_SCRIPTS_LOCAL /global/vm_storage_helpers.sh "
elif [ [ -f " $LOCAL_SCRIPTS_DEFAULT /global/vm_storage_helpers.sh " ] ] ; then
source " $LOCAL_SCRIPTS_DEFAULT /global/vm_storage_helpers.sh "
fi
2026-04-12 20:32:34 +02:00
if [ [ -f " $LOCAL_SCRIPTS_LOCAL /vm/disk_selector.sh " ] ] ; then
source " $LOCAL_SCRIPTS_LOCAL /vm/disk_selector.sh "
elif [ [ -f " $LOCAL_SCRIPTS_DEFAULT /vm/disk_selector.sh " ] ] ; then
source " $LOCAL_SCRIPTS_DEFAULT /vm/disk_selector.sh "
fi
2026-04-05 11:24:08 +02:00
if [ [ -f " $LOCAL_SCRIPTS_LOCAL /global/pci_passthrough_helpers.sh " ] ] ; then
source " $LOCAL_SCRIPTS_LOCAL /global/pci_passthrough_helpers.sh "
elif [ [ -f " $LOCAL_SCRIPTS_DEFAULT /global/pci_passthrough_helpers.sh " ] ] ; then
source " $LOCAL_SCRIPTS_DEFAULT /global/pci_passthrough_helpers.sh "
fi
2026-04-06 13:39:07 +02:00
if [ [ -f " $LOCAL_SCRIPTS_LOCAL /global/gpu_hook_guard_helpers.sh " ] ] ; then
source " $LOCAL_SCRIPTS_LOCAL /global/gpu_hook_guard_helpers.sh "
elif [ [ -f " $LOCAL_SCRIPTS_DEFAULT /global/gpu_hook_guard_helpers.sh " ] ] ; then
source " $LOCAL_SCRIPTS_DEFAULT /global/gpu_hook_guard_helpers.sh "
fi
2025-03-23 17:50:33 +01:00
load_language
initialize_cache
# ==========================================================
GEN_MAC = "02"
for i in { 1..5} ; do
BYTE = $( printf "%02X" $(( RANDOM % 256 )) )
GEN_MAC = " ${ GEN_MAC } : ${ BYTE } "
done
NEXTID = $( pvesh get /cluster/nextid 2>/dev/null || echo "100" )
NAME = "Synology VM"
IMAGES_DIR = "/var/lib/vz/template/iso"
ERROR_FLAG = false
2026-04-05 11:24:08 +02:00
WIZARD_ADD_GPU = "no"
WIZARD_GPU_RESULT = "not_requested"
VM_WIZARD_CAPTURE_FILE = ""
VM_WIZARD_CAPTURE_ACTIVE = 0
2026-04-06 17:16:26 +02:00
VM_STORAGE_IOMMU_PENDING_REBOOT = 0
2025-03-23 17:50:33 +01:00
function exit_script( ) {
clear
if whiptail --backtitle "ProxMenuX" --title " $NAME " --yesno " $( translate " This will create a New $NAME . Proceed? " ) " 10 58; then
start_script
else
clear
exit
fi
}
# Define the header_info function at the beginning of the script
function header_info( ) {
clear
show_proxmenux_logo
2026-04-05 11:24:08 +02:00
msg_title "Synology VM Creator"
2025-03-23 17:50:33 +01:00
}
# ==========================================================
2026-04-05 11:24:08 +02:00
function start_vm_wizard_capture( ) {
[ [ " ${ VM_WIZARD_CAPTURE_ACTIVE :- 0 } " -eq 1 ] ] && return 0
VM_WIZARD_CAPTURE_FILE = " /tmp/proxmenux_synology_vm_wizard_capture_ $$ .txt "
: >" $VM_WIZARD_CAPTURE_FILE "
exec 8>& 1
exec > >( tee -a " $VM_WIZARD_CAPTURE_FILE " )
VM_WIZARD_CAPTURE_ACTIVE = 1
}
function stop_vm_wizard_capture( ) {
if [ [ " ${ VM_WIZARD_CAPTURE_ACTIVE :- 0 } " -eq 1 ] ] ; then
exec 1>& 8
exec 8>& -
VM_WIZARD_CAPTURE_ACTIVE = 0
fi
if [ [ -n " ${ VM_WIZARD_CAPTURE_FILE :- } " && -f " $VM_WIZARD_CAPTURE_FILE " ] ] ; then
rm -f " $VM_WIZARD_CAPTURE_FILE "
fi
VM_WIZARD_CAPTURE_FILE = ""
}
function replay_vm_wizard_capture( ) {
if [ [ " ${ VM_WIZARD_CAPTURE_ACTIVE :- 0 } " -eq 1 ] ] ; then
stop_spinner
exec 1>& 8
exec 8>& -
VM_WIZARD_CAPTURE_ACTIVE = 0
fi
if [ [ -n " ${ VM_WIZARD_CAPTURE_FILE :- } " && -f " $VM_WIZARD_CAPTURE_FILE " ] ] ; then
show_proxmenux_logo
cat " $VM_WIZARD_CAPTURE_FILE "
rm -f " $VM_WIZARD_CAPTURE_FILE "
fi
VM_WIZARD_CAPTURE_FILE = ""
}
function has_usable_gpu_for_vm_passthrough( ) {
lspci -nn 2>/dev/null \
| grep -iE "VGA compatible controller|3D controller|Display controller" \
| grep -ivE "Ethernet|Network|Audio" \
| grep -ivE "ASPEED|AST[0-9]{3,4}|Matrox|G200e|BMC" \
| grep -q .
}
function prompt_optional_gpu_passthrough( ) {
WIZARD_ADD_GPU = "no"
if has_usable_gpu_for_vm_passthrough; then
if whiptail --backtitle "ProxMenuX" --title " $( translate "Optional GPU Passthrough" ) " \
--yesno " $( translate "Do you want to configure GPU passthrough for this VM now?" ) \n\n $( translate "This will launch the GPU assistant after VM creation and may require a host reboot." ) " 12 78 --defaultno; then
WIZARD_ADD_GPU = "yes"
fi
else
msg_warn " $( translate "No compatible GPU detected for VM passthrough. Skipping GPU wizard option." ) "
fi
export WIZARD_ADD_GPU
}
function run_gpu_passthrough_wizard( ) {
[ [ " ${ WIZARD_ADD_GPU :- no } " != "yes" ] ] && return 0
local gpu_script = " $LOCAL_SCRIPTS /gpu_tpu/add_gpu_vm.sh "
local local_gpu_script
local wizard_result_file = ""
if [ [ ! -f " $gpu_script " ] ] ; then
local_gpu_script = " $( cd " $( dirname " ${ BASH_SOURCE [0] } " ) " /.. && pwd ) /gpu_tpu/add_gpu_vm.sh "
[ [ -f " $local_gpu_script " ] ] && gpu_script = " $local_gpu_script "
fi
if [ [ ! -f " $gpu_script " ] ] ; then
msg_warn " $( translate "GPU passthrough assistant not found. You can run it later from Hardware Graphics." ) "
WIZARD_GPU_RESULT = "cancelled"
return 0
fi
msg_info2 " $( translate "Launching GPU passthrough assistant for VM" ) ${ VMID } ... "
wizard_result_file = " /tmp/proxmenux_gpu_wizard_result_ ${ VMID } _ $$ .txt "
: >" $wizard_result_file "
bash " $gpu_script " --vmid " $VMID " --wizard --result-file " $wizard_result_file "
if [ [ -s " $wizard_result_file " ] ] ; then
WIZARD_GPU_RESULT = $( head -n1 " $wizard_result_file " | tr -d '\r\n' )
else
WIZARD_GPU_RESULT = "cancelled"
fi
rm -f " $wizard_result_file "
}
2025-03-23 17:50:33 +01:00
# ==========================================================
# start Script
# ==========================================================
function start_script( ) {
if ( whiptail --backtitle "ProxMenuX" --title "SETTINGS" --yesno " $( translate "Use Default Settings?" ) " --no-button Advanced 10 58) ; then
header_info
echo -e " ${ DEF } Using Default Settings ${ CL } "
2026-04-05 11:24:08 +02:00
default_settings || return 1
2025-03-23 17:50:33 +01:00
else
header_info
echo -e " ${ CUS } Using Advanced Settings ${ CL } "
2026-04-05 11:24:08 +02:00
advanced_settings || return 1
2025-03-23 17:50:33 +01:00
fi
2026-04-05 11:24:08 +02:00
return 0
2025-03-23 17:50:33 +01:00
}
# ==========================================================
# ==========================================================
# Default Settings
# ==========================================================
function default_settings( ) {
VMID = " $NEXTID "
FORMAT = ""
MACHINE = " -machine q35"
BIOS_TYPE = " -bios ovmf"
DISK_CACHE = ""
HN = "Synology-DSM"
CPU_TYPE = " -cpu host"
CORE_COUNT = "2"
RAM_SIZE = "4096"
BRG = "vmbr0"
MAC = " $GEN_MAC "
VLAN = ""
MTU = ""
SERIAL_PORT = "socket"
START_VM = "no"
2025-05-09 18:46:37 +02:00
echo -e " ${ TAB } ${ DGN } Using Virtual Machine ID: ${ BGN } ${ VMID } ${ CL } "
echo -e " ${ TAB } ${ DGN } Using Machine Type: ${ BGN } q35 ${ CL } "
echo -e " ${ TAB } ${ DGN } Using BIOS Type: ${ BGN } OVMF (UEFI) ${ CL } "
echo -e " ${ TAB } ${ DGN } Using Hostname: ${ BGN } ${ HN } ${ CL } "
echo -e " ${ TAB } ${ DGN } Using CPU Model: ${ BGN } Host ${ CL } "
echo -e " ${ TAB } ${ DGN } Allocated Cores: ${ BGN } ${ CORE_COUNT } ${ CL } "
echo -e " ${ TAB } ${ DGN } Allocated RAM: ${ BGN } ${ RAM_SIZE } ${ CL } "
echo -e " ${ TAB } ${ DGN } Using Bridge: ${ BGN } ${ BRG } ${ CL } "
echo -e " ${ TAB } ${ DGN } Using MAC Address: ${ BGN } ${ MAC } ${ CL } "
echo -e " ${ TAB } ${ DGN } Using VLAN: ${ BGN } Default ${ CL } "
echo -e " ${ TAB } ${ DGN } Using Interface MTU Size: ${ BGN } Default ${ CL } "
echo -e " ${ TAB } ${ DGN } Configuring Serial Port: ${ BGN } ${ SERIAL_PORT } ${ CL } "
echo -e " ${ TAB } ${ DGN } Start VM when completed: ${ BGN } ${ START_VM } ${ CL } "
2025-03-23 17:50:33 +01:00
echo -e
echo -e " ${ DEF } Creating a $NAME using the above default settings ${ CL } "
sleep 1
2026-04-05 11:24:08 +02:00
select_disk_type || return 1
2025-03-23 17:50:33 +01:00
}
# ==========================================================
# ==========================================================
# advanced Settings
# ==========================================================
function advanced_settings( ) {
# VM ID Selection
while true; do
if VMID = $( whiptail --backtitle "ProxMenuX" --inputbox " $( translate "Set Virtual Machine ID" ) " 8 58 $NEXTID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>& 1 1>& 2 2>& 3) ; then
if [ -z " $VMID " ] ; then
VMID = " $NEXTID "
fi
if pct status " $VMID " & >/dev/null || qm status " $VMID " & >/dev/null; then
echo -e " ${ CROSS } ${ RD } ID $VMID is already in use ${ CL } "
sleep 1
continue
fi
echo -e " ${ DGN } Virtual Machine ID: ${ BGN } $VMID ${ CL } "
break
else
exit_script
fi
done
# Machine Type Selection
if MACH = $( whiptail --backtitle "ProxMenuX" --title " $( translate "MACHINE TYPE" ) " --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
"q35" "Machine q35" ON \
"i440fx" "Machine i440fx" OFF \
3>& 1 1>& 2 2>& 3) ; then
if [ $MACH = q35 ] ; then
echo -e " ${ DGN } Using Machine Type: ${ BGN } $MACH ${ CL } "
FORMAT = ""
MACHINE = " -machine q35"
else
echo -e " ${ DGN } Using Machine Type: ${ BGN } $MACH ${ CL } "
FORMAT = ",efitype=4m"
MACHINE = ""
fi
else
exit_script
fi
# BIOS Type Selection
if BIOS = $( whiptail --backtitle "ProxMenuX" --title " $( translate "BIOS TYPE" ) " --radiolist --cancel-button Exit-Script "Choose BIOS Type" 10 58 2 \
"ovmf" "UEFI (OVMF)" ON \
"seabios" "SeaBIOS (Legacy)" OFF \
3>& 1 1>& 2 2>& 3) ; then
if [ " $BIOS " = "seabios" ] ; then
echo -e " ${ DGN } Using BIOS Type: ${ BGN } SeaBIOS ${ CL } "
BIOS_TYPE = " -bios seabios"
else
echo -e " ${ DGN } Using BIOS Type: ${ BGN } OVMF (UEFI) ${ CL } "
BIOS_TYPE = " -bios ovmf"
fi
else
exit_script
fi
# Hostname Selection
if VM_NAME = $( whiptail --backtitle "ProxMenuX" --inputbox " $( translate "Set Hostname" ) " 8 58 Synology-DSM --title "HOSTNAME" --cancel-button Exit-Script 3>& 1 1>& 2 2>& 3) ; then
if [ -z $VM_NAME ] ; then
HN = "Synology-DSM"
echo -e " ${ DGN } Using Hostname: ${ BGN } $HN ${ CL } "
else
HN = $( echo ${ VM_NAME ,, } | tr -d ' ' )
echo -e " ${ DGN } Using Hostname: ${ BGN } $HN ${ CL } "
fi
else
exit_script
fi
# CPU Type Selection
if CPU_TYPE1 = $( whiptail --backtitle "ProxMenuX" --title " $( translate "CPU MODEL" ) " --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
"1" "Host" ON \
"0" "KVM64" OFF \
3>& 1 1>& 2 2>& 3) ; then
if [ $CPU_TYPE1 = "1" ] ; then
echo -e " ${ DGN } Using CPU Model: ${ BGN } Host ${ CL } "
CPU_TYPE = " -cpu host"
else
echo -e " ${ DGN } Using CPU Model: ${ BGN } KVM64 ${ CL } "
CPU_TYPE = ""
fi
else
exit_script
fi
# Core Count Selection
if CORE_COUNT = $( whiptail --backtitle "ProxMenuX" --inputbox " $( translate "Allocate CPU Cores" ) " 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>& 1 1>& 2 2>& 3) ; then
if [ -z $CORE_COUNT ] ; then
CORE_COUNT = "2"
echo -e " ${ DGN } Allocated Cores: ${ BGN } $CORE_COUNT ${ CL } "
else
echo -e " ${ DGN } Allocated Cores: ${ BGN } $CORE_COUNT ${ CL } "
fi
else
exit_script
fi
# RAM Size Selection
if RAM_SIZE = $( whiptail --backtitle "ProxMenuX" --inputbox " $( translate "Allocate RAM in MiB" ) " 8 58 4096 --title "RAM" --cancel-button Exit-Script 3>& 1 1>& 2 2>& 3) ; then
if [ -z $RAM_SIZE ] ; then
RAM_SIZE = "4096"
echo -e " ${ DGN } Allocated RAM: ${ BGN } $RAM_SIZE ${ CL } "
else
echo -e " ${ DGN } Allocated RAM: ${ BGN } $RAM_SIZE ${ CL } "
fi
else
exit_script
fi
# Bridge Selection
if BRG = $( whiptail --backtitle "ProxMenuX" --inputbox " $( translate "Set a Bridge" ) " 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>& 1 1>& 2 2>& 3) ; then
if [ -z $BRG ] ; then
BRG = "vmbr0"
echo -e " ${ DGN } Using Bridge: ${ BGN } $BRG ${ CL } "
else
echo -e " ${ DGN } Using Bridge: ${ BGN } $BRG ${ CL } "
fi
else
exit_script
fi
# MAC Address Selection
if MAC1 = $( whiptail --backtitle "ProxMenuX" --inputbox " $( translate "Set a MAC Address" ) " 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>& 1 1>& 2 2>& 3) ; then
if [ -z $MAC1 ] ; then
MAC = " $GEN_MAC "
echo -e " ${ DGN } Using MAC Address: ${ BGN } $MAC ${ CL } "
else
MAC = " $MAC1 "
echo -e " ${ DGN } Using MAC Address: ${ BGN } $MAC1 ${ CL } "
fi
else
exit_script
fi
# VLAN Selection
if VLAN1 = $( whiptail --backtitle "ProxMenuX" --inputbox " $( translate "Set a Vlan(leave blank for default)" ) " 8 58 --title "VLAN" --cancel-button Exit-Script 3>& 1 1>& 2 2>& 3) ; then
if [ -z $VLAN1 ] ; then
VLAN1 = "Default"
VLAN = ""
echo -e " ${ DGN } Using Vlan: ${ BGN } $VLAN1 ${ CL } "
else
VLAN = " ,tag= $VLAN1 "
echo -e " ${ DGN } Using Vlan: ${ BGN } $VLAN1 ${ CL } "
fi
else
exit_script
fi
# MTU Selection
if MTU1 = $( whiptail --backtitle "ProxMenuX" --inputbox " $( translate "Set Interface MTU Size (leave blank for default)" ) " 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>& 1 1>& 2 2>& 3) ; then
if [ -z $MTU1 ] ; then
MTU1 = "Default"
MTU = ""
echo -e " ${ DGN } Using Interface MTU Size: ${ BGN } $MTU1 ${ CL } "
else
MTU = " ,mtu= $MTU1 "
echo -e " ${ DGN } Using Interface MTU Size: ${ BGN } $MTU1 ${ CL } "
fi
else
exit_script
fi
# Confirmation
if ( whiptail --backtitle "ProxMenuX" --title " $( translate "ADVANCED SETTINGS COMPLETE" ) " --yesno " Ready to create a $NAME ? " --no-button Do-Over 10 58) ; then
echo -e
2025-03-26 20:10:23 +01:00
echo -e " ${ CUS } Creating a $NAME using the above advanced settings ${ CL } "
2025-03-23 17:50:33 +01:00
sleep 1
2026-04-05 11:24:08 +02:00
select_disk_type || return 1
2025-03-23 17:50:33 +01:00
else
header_info
sleep 1
2025-03-26 20:10:23 +01:00
echo -e " ${ CUS } Using Advanced Settings ${ CL } "
2025-03-23 17:50:33 +01:00
advanced_settings
fi
}
# ==========================================================
# ==========================================================
# Select Disk
# ==========================================================
2026-04-05 11:24:08 +02:00
VIRTUAL_DISKS = ( )
IMPORT_DISKS = ( )
CONTROLLER_NVME_PCIS = ( )
PASSTHROUGH_DISKS = ( )
function _build_storage_plan_summary( ) {
local separator
local summary
separator = " $( printf '%*s' 70 '' | tr ' ' '-' ) "
summary = " $( translate "Current selection:" ) \n "
summary += " - $( translate "Virtual disks" ) : ${# VIRTUAL_DISKS [@] } \n "
summary += " - $( translate "Import disks" ) : ${# IMPORT_DISKS [@] } \n "
summary += " - $( translate "Controllers + NVMe" ) : ${# CONTROLLER_NVME_PCIS [@] } \n "
summary += " ${ separator } \n\n "
echo -e " $summary "
2025-03-23 17:50:33 +01:00
}
2026-04-05 11:24:08 +02:00
function select_disk_type( ) {
VIRTUAL_DISKS = ( )
IMPORT_DISKS = ( )
CONTROLLER_NVME_PCIS = ( )
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
while true; do
local choice
choice = $( whiptail --backtitle "ProxMenuX" --title "STORAGE PLAN" --menu " $( _build_storage_plan_summary) " 18 78 5 \
2026-04-12 20:32:34 +02:00
"a" " $( translate "Add virtual disk" ) " \
"b" " $( translate "Add import disk" ) " \
"c" " $( translate "Add Controller or NVMe (PCI passthrough)" ) " \
2026-04-05 11:24:08 +02:00
"r" " $( translate "Reset current storage selection" ) " \
2026-04-12 20:32:34 +02:00
"d" " $( translate "──── [ Finish and continue ] ────" ) " \
2026-04-05 11:24:08 +02:00
--ok-button "Select" --cancel-button "Cancel" 3>& 1 1>& 2 2>& 3) || {
msg_warn " $( translate "Storage plan selection cancelled." ) "
return 1
}
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
case " $choice " in
2026-04-12 20:32:34 +02:00
a)
2026-04-05 11:24:08 +02:00
select_virtual_disk
; ;
2026-04-12 20:32:34 +02:00
b)
2026-04-05 11:24:08 +02:00
select_import_disk
; ;
2026-04-12 20:32:34 +02:00
c)
2026-04-05 11:24:08 +02:00
select_controller_nvme
; ;
r)
VIRTUAL_DISKS = ( )
IMPORT_DISKS = ( )
CONTROLLER_NVME_PCIS = ( )
; ;
d| done )
if [ [ ${# VIRTUAL_DISKS [@] } -eq 0 && ${# IMPORT_DISKS [@] } -eq 0 && ${# CONTROLLER_NVME_PCIS [@] } -eq 0 ] ] ; then
2025-03-23 17:50:33 +01:00
continue
fi
2026-04-05 11:24:08 +02:00
if [ [ ${# VIRTUAL_DISKS [@] } -gt 0 ] ] ; then
msg_ok " $( translate "Virtual Disks Created:" ) "
for i in " ${ !VIRTUAL_DISKS[@] } " ; do
echo -e " ${ TAB } ${ BL } - $( translate "Disk" ) $(( i+1)) : ${ VIRTUAL_DISKS [ $i ] } GB ${ CL } "
done
fi
if [ [ ${# IMPORT_DISKS [@] } -gt 0 ] ] ; then
msg_ok " $( translate "Import Disks Selected:" ) "
for i in " ${ !IMPORT_DISKS[@] } " ; do
local disk_info
disk_info = $( lsblk -ndo MODEL,SIZE " ${ IMPORT_DISKS [ $i ] } " 2>/dev/null | xargs)
echo -e " ${ TAB } ${ BL } - $( translate "Disk" ) $(( i+1)) : ${ IMPORT_DISKS [ $i ] } ${ disk_info : + ( $disk_info ) } ${ CL } "
done
fi
if [ [ ${# CONTROLLER_NVME_PCIS [@] } -gt 0 ] ] ; then
msg_ok " $( translate "Controllers + NVMe Selected:" ) "
for i in " ${ !CONTROLLER_NVME_PCIS[@] } " ; do
local pci_info
pci_info = $( lspci -nn -s " ${ CONTROLLER_NVME_PCIS [ $i ]#0000 : } " 2>/dev/null | sed 's/^[^ ]* //' )
echo -e " ${ TAB } ${ BL } - $( translate "Controller" ) $(( i+1)) : ${ CONTROLLER_NVME_PCIS [ $i ] } ${ pci_info : + ( $pci_info ) } ${ CL } "
done
fi
PASSTHROUGH_DISKS = ( " ${ IMPORT_DISKS [@] } " )
DISK_TYPE = "mixed"
export DISK_TYPE VIRTUAL_DISKS IMPORT_DISKS CONTROLLER_NVME_PCIS PASSTHROUGH_DISKS
select_loader || return 1
return 0
; ;
esac
2025-03-23 17:50:33 +01:00
done
}
2026-04-05 11:24:08 +02:00
function select_virtual_disk( ) {
msg_info "Detecting available storage volumes..."
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
local STORAGE_MENU = ( )
local TAG TYPE FREE ITEM
while read -r line; do
TAG = $( echo $line | awk '{print $1}' )
TYPE = $( echo $line | awk '{print $2}' )
FREE = $( echo $line | numfmt --field 4-6 --from-unit= K --to= iec --format "%.2f" | awk '{printf( "%9sB", $6)}' )
ITEM = $( printf "%-15s %-10s %-15s" " $TAG " " $TYPE " " $FREE " )
STORAGE_MENU += ( " $TAG " " $ITEM " "OFF" )
done < <( pvesm status -content images | awk 'NR>1' )
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
local VALID
VALID = $( pvesm status -content images | awk 'NR>1' )
if [ [ -z " $VALID " ] ] ; then
msg_error "Unable to detect a valid storage location."
return 1
fi
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
local STORAGE
if [ [ $(( ${# STORAGE_MENU [@] } / 3 )) -eq 1 ] ] ; then
STORAGE = ${ STORAGE_MENU [0] }
else
[ [ -n " ${ SPINNER_PID :- } " ] ] && kill " $SPINNER_PID " >/dev/null 2>& 1
STORAGE = $( whiptail --backtitle "ProxMenuX" --title " $( translate "Select Storage Volume" ) " --radiolist \
" $( translate "Choose the storage volume for the virtual disk:\n" ) " 20 78 10 \
" ${ STORAGE_MENU [@] } " 3>& 1 1>& 2 2>& 3) || return 0
[ [ -z " $STORAGE " ] ] && return 0
fi
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
local DISK_SIZE
stop_spinner
DISK_SIZE = $( whiptail --backtitle "ProxMenuX" --inputbox " $( translate "System Disk Size (GB)" ) " 8 58 32 --title "VIRTUAL DISK" --cancel-button Cancel 3>& 1 1>& 2 2>& 3) || return 0
[ [ -z " $DISK_SIZE " ] ] && DISK_SIZE = "32"
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
VIRTUAL_DISKS += ( " ${ STORAGE } : ${ DISK_SIZE } " )
}
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
function select_controller_nvme( ) {
2026-04-06 17:16:26 +02:00
local VM_STORAGE_IOMMU_REBOOT_POLICY = "defer"
if declare -F _vm_storage_ensure_iommu_or_offer >/dev/null 2>& 1; then
if ! _vm_storage_ensure_iommu_or_offer; then
return 1
fi
elif declare -F _pci_is_iommu_active >/dev/null 2>& 1; then
if ! _pci_is_iommu_active; then
whiptail --title "Controller + NVMe" --msgbox \
" $( translate "IOMMU is not active on this host." ) \n\n $( translate "Controller/NVMe passthrough requires IOMMU enabled in BIOS/UEFI and kernel." ) \n\n $( translate "Enable IOMMU, reboot the host, and try again." ) " \
14 90
return 1
fi
fi
2026-04-05 11:24:08 +02:00
msg_info " $( translate "Detecting PCI storage controllers and NVMe devices..." ) "
2026-04-06 17:16:26 +02:00
2026-04-05 11:24:08 +02:00
_refresh_host_storage_cache
local menu_items = ( )
local blocked_report = ""
2026-04-06 13:39:07 +02:00
local safe_count = 0 blocked_count = 0 hidden_target_count = 0
local pci_path pci_full class_hex name controller_disks disk state controller_desc slot_base
local target_vmid = " ${ VMID :- } "
2026-04-05 11:24:08 +02:00
while IFS = read -r pci_path; do
pci_full = $( basename " $pci_path " )
class_hex = $( cat " $pci_path /class " 2>/dev/null | sed 's/^0x//' )
[ [ -z " $class_hex " || " ${ class_hex : 0 : 2 } " != "01" ] ] && continue
2026-04-06 13:39:07 +02:00
slot_base = $( _pci_slot_base " $pci_full " )
if [ [ -n " $target_vmid " ] ] && _vm_has_pci_slot " $target_vmid " " $slot_base " ; then
hidden_target_count = $(( hidden_target_count + 1 ))
continue
fi
2026-04-05 11:24:08 +02:00
name = $( lspci -nn -s " ${ pci_full #0000 : } " 2>/dev/null | sed 's/^[^ ]* //' )
[ [ -z " $name " ] ] && name = " $( translate "Unknown storage controller" ) "
controller_disks = ( )
while IFS = read -r disk; do
[ [ -z " $disk " ] ] && continue
_array_contains " $disk " " ${ controller_disks [@] } " || controller_disks += ( " $disk " )
done < <( _controller_block_devices " $pci_full " )
2026-04-06 13:39:07 +02:00
local -a blocked_reasons = ( )
2026-04-05 11:24:08 +02:00
for disk in " ${ controller_disks [@] } " ; do
if _disk_is_host_system_used " $disk " ; then
2026-04-06 13:39:07 +02:00
blocked_reasons += ( " ${ disk } ( ${ DISK_USAGE_REASON } ) " )
2026-04-05 11:24:08 +02:00
elif _disk_used_in_guest_configs " $disk " ; then
2026-04-06 13:39:07 +02:00
blocked_reasons += ( " ${ disk } ( $( translate "In use by VM/LXC config" ) ) " )
2025-04-08 18:00:35 +02:00
fi
2026-04-05 11:24:08 +02:00
done
2025-04-08 18:00:35 +02:00
2026-04-06 13:39:07 +02:00
if [ [ ${# blocked_reasons [@] } -gt 0 ] ] ; then
2026-04-05 11:24:08 +02:00
blocked_count = $(( blocked_count + 1 ))
2026-04-06 21:12:13 +02:00
blocked_report += " • ${ pci_full } — $( _shorten_text " $name " 56) \n "
2026-04-05 11:24:08 +02:00
continue
fi
2025-03-23 17:50:33 +01:00
2026-04-06 13:39:07 +02:00
local short_name
short_name = $( _shorten_text " $name " 42)
local assigned_suffix = ""
if [ [ -n " $( _pci_assigned_vm_ids " $pci_full " " $target_vmid " 2>/dev/null | head -1) " ] ] ; then
assigned_suffix = " | $( translate "Assigned to VM" ) "
fi
2026-04-06 21:12:13 +02:00
controller_desc = " ${ short_name } ${ assigned_suffix } "
2025-04-05 16:50:31 +02:00
2026-04-05 11:24:08 +02:00
if _array_contains " $pci_full " " ${ CONTROLLER_NVME_PCIS [@] } " ; then
state = "ON"
else
state = "OFF"
fi
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
menu_items += ( " $pci_full " " $controller_desc " " $state " )
safe_count = $(( safe_count + 1 ))
done < <( ls -d /sys/bus/pci/devices/* 2>/dev/null | sort)
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
stop_spinner
if [ [ $safe_count -eq 0 ] ] ; then
2026-04-06 13:39:07 +02:00
local msg
if [ [ " $hidden_target_count " -gt 0 && " $blocked_count " -eq 0 ] ] ; then
msg = " $( translate "All detected controllers/NVMe are already present in the selected VM." ) \n\n $( translate "No additional device needs to be added." ) "
else
2026-04-06 21:12:13 +02:00
msg = " $( translate "No available Controllers/NVMe devices were found." ) \n\n "
if [ [ $blocked_count -gt 0 ] ] ; then
msg += " $( translate "Hidden for safety" ) :\n ${ blocked_report } "
fi
2026-04-06 13:39:07 +02:00
fi
2026-04-06 22:04:38 +02:00
whiptail --title "Controller + NVMe" --msgbox " $msg " 18 84
2026-04-05 11:24:08 +02:00
return 1
2025-03-23 17:50:33 +01:00
fi
2026-04-05 11:24:08 +02:00
local selected
selected = $( whiptail --title "Controller + NVMe" --checklist \
2026-04-06 21:12:13 +02:00
" $( translate "Select available Controllers/NVMe to add:" ) " 20 96 10 \
2026-04-05 11:24:08 +02:00
" ${ menu_items [@] } " 3>& 1 1>& 2 2>& 3) || return 1
2025-04-05 16:50:31 +02:00
2026-04-05 11:24:08 +02:00
CONTROLLER_NVME_PCIS = ( )
local pci
for pci in $( echo " $selected " | tr -d '"' ) ; do
CONTROLLER_NVME_PCIS += ( " $pci " )
2025-03-23 17:50:33 +01:00
done
2026-04-06 17:16:26 +02:00
if [ [ ${# CONTROLLER_NVME_PCIS [@] } -gt 0 ] ] && declare -F _vm_storage_confirm_controller_passthrough_risk >/dev/null 2>& 1; then
local vm_name_for_notice = " ${ HN :- $NAME } "
if ! _vm_storage_confirm_controller_passthrough_risk " ${ VMID :- } " " $vm_name_for_notice " "Controller + NVMe" ; then
CONTROLLER_NVME_PCIS = ( )
return 1
fi
fi
2026-04-05 11:24:08 +02:00
export CONTROLLER_NVME_PCIS
}
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
function select_passthrough_disk( ) {
select_import_disk
2025-03-23 17:50:33 +01:00
}
# ==========================================================
2026-04-06 13:39:07 +02:00
function prompt_controller_conflict_policy( ) {
local pci = " $1 "
shift
local -a source_vms = ( " $@ " )
local msg vmid vm_name st ob
2026-04-12 20:32:34 +02:00
msg = " \n $( translate "Selected controller/NVMe is already assigned to other VM(s):" ) \n\n "
2026-04-06 13:39:07 +02:00
for vmid in " ${ source_vms [@] } " ; do
vm_name = $( _vm_name_by_id " $vmid " )
st = "stopped" ; _vm_status_is_running " $vmid " && st = "running"
ob = "0" ; _vm_onboot_is_enabled " $vmid " && ob = "1"
msg += " - VM ${ vmid } ( ${ vm_name } ) [ ${ st } , onboot= ${ ob } ]\n "
done
msg += " \n $( translate "Choose action for this controller/NVMe:" ) "
local choice
2026-04-12 20:32:34 +02:00
choice = $( whiptail --title " $( translate "Controller/NVMe Conflict Policy" ) " --menu " $msg " 20 80 10 \
2026-04-06 13:39:07 +02:00
"1" " $( translate "Keep in source VM(s) + disable onboot + add to target VM" ) " \
"2" " $( translate "Move to target VM (remove from source VM config)" ) " \
"3" " $( translate "Skip this device" ) " \
3>& 1 1>& 2 2>& 3) || { echo "skip" ; return ; }
case " $choice " in
1) echo "keep_disable_onboot" ; ;
2) echo "move_remove_source" ; ;
*) echo "skip" ; ;
esac
}
2025-03-23 17:50:33 +01:00
# ==========================================================
# Select Loader
# ==========================================================
function select_loader( ) {
# Ensure the images directory exists
if [ ! -d " $IMAGES_DIR " ] ; then
msg_info "Creating images directory"
mkdir -p " $IMAGES_DIR "
chmod 755 " $IMAGES_DIR "
msg_ok " Images directory created: $IMAGES_DIR "
fi
# Create the loader selection menu
LOADER_OPTION = $( whiptail --backtitle "ProxMenuX" --title "SELECT LOADER" --menu " $( translate "Choose a loader for Synology DSM:" ) " 15 70 4 \
"1" "AuxXxilium Arc Loader" \
"2" "RedPill Loader (RROrg - RR)" \
"3" "TinyCore RedPill Loader (PeterSuh-Q3 M-shell)" \
"4" " Custom Loader Image (from $IMAGES_DIR ) " \
3>& 1 1>& 2 2>& 3)
if [ -z " $LOADER_OPTION " ] ; then
exit_script
fi
case $LOADER_OPTION in
1)
LOADER_TYPE = "arc"
LOADER_NAME = "AuxXxilium Arc"
LOADER_URL = "https://github.com/AuxXxilium/arc/"
echo -e " ${ DGN } ${ TAB } Selected Loader: ${ BGN } $LOADER_NAME ${ CL } "
download_loader
; ;
2)
LOADER_TYPE = "redpill"
LOADER_NAME = "RedPill RR"
LOADER_URL = "https://github.com/RROrg/rr/"
echo -e " ${ DGN } ${ TAB } Selected Loader: ${ BGN } $LOADER_NAME ${ CL } "
download_loader
; ;
3)
LOADER_TYPE = "tinycore"
LOADER_NAME = "TinyCore RedPill M-shell"
LOADER_URL = "https://github.com/PeterSuh-Q3/tinycore-redpill/"
echo -e " ${ DGN } ${ TAB } Selected Loader: ${ BGN } $LOADER_NAME ${ CL } "
download_loader
; ;
4)
LOADER_TYPE = "custom"
LOADER_NAME = "Custom Image"
LOADER_URL = "https://xpenology.com/forum/"
echo -e " ${ DGN } ${ TAB } Selected Loader: ${ BGN } $LOADER_NAME ${ CL } "
select_custom_image
; ;
esac
}
function select_custom_image( ) {
# Check if there are any images in the directory
IMAGES = $( find " $IMAGES_DIR " -type f -name "*.img" -o -name "*.iso" -o -name "*.qcow2" -o -name "*.vmdk" | sort)
if [ -z " $IMAGES " ] ; then
whiptail --title " $( translate "No Images Found" ) " --msgbox " No compatible images found in $IMAGES_DIR \n\nSupported formats: .img, .iso, .qcow2, .vmdk\n\nPlease add some images and try again. " 15 70
select_loader
fi
# Create an array of image options for whiptail
IMAGE_OPTIONS = ( )
while read -r img; do
filename = $( basename " $img " )
filesize = $( du -h " $img " | cut -f1)
IMAGE_OPTIONS += ( " $img " " $filesize " )
done <<< " $IMAGES "
# Let the user select an image
LOADER_FILE = $( whiptail --backtitle "ProxMenuX" --title "SELECT CUSTOM IMAGE" --menu " $( translate "Choose a custom image:" ) " 20 70 10 " ${ IMAGE_OPTIONS [@] } " 3>& 1 1>& 2 2>& 3)
if [ -z " $LOADER_FILE " ] ; then
msg_error "No custom image selected"
exit_script
fi
echo -e " ${ DGN } ${ TAB } Using Custom Image: ${ BGN } $( basename " $LOADER_FILE " ) ${ CL } "
FILE = $( basename " $LOADER_FILE " )
}
# ==========================================================
# ==========================================================
# Download Loader
# ==========================================================
function download_loader( ) {
echo -e " ${ DGN } ${ TAB } Retrieving the URL for the ${ BGN } $LOADER_NAME loader ${ CL } "
2025-04-01 19:39:30 +02:00
if [ [ " $LOADER_TYPE " = = "arc" || " $LOADER_TYPE " = = "redpill" ] ] && ! command -v unzip & > /dev/null; then
msg_info "Installing unzip..."
2025-04-01 19:45:16 +02:00
apt-get update -qq && apt-get install -y unzip -qq >/dev/null 2>& 1
2025-04-01 19:39:30 +02:00
if ! command -v unzip & > /dev/null; then
msg_error "Failed to install unzip"
sleep 2
return 1
fi
msg_ok "Installed unzip successfully."
fi
2025-03-23 17:50:33 +01:00
case $LOADER_TYPE in
arc)
curl -s https://api.github.com/repos/AuxXxilium/arc/releases/latest \
2025-04-01 19:25:19 +02:00
| grep "browser_download_url.*\.img\.zip" \
2025-03-23 17:50:33 +01:00
| cut -d '"' -f 4 \
2025-03-30 23:16:35 +02:00
| xargs wget -q --show-progress -O " $IMAGES_DIR /arc.img.zip "
2025-03-23 17:50:33 +01:00
2025-03-30 23:16:35 +02:00
if [ -f " $IMAGES_DIR /arc.img.zip " ] ; then
2025-03-23 17:50:33 +01:00
cd " $IMAGES_DIR "
2025-03-30 23:16:35 +02:00
unzip -q arc.img.zip
rm arc.img.zip
2025-03-23 17:50:33 +01:00
FILE = "arc.img"
LOADER_FILE = " $IMAGES_DIR / $FILE "
cd - > /dev/null
else
msg_error " Failed to download $LOADER_NAME loader "
sleep 1
select_loader
fi
; ;
redpill)
curl -s https://api.github.com/repos/RROrg/rr/releases/latest \
| grep "browser_download_url.*\.img\.zip" \
| cut -d '"' -f 4 \
| xargs wget -q --show-progress -O " $IMAGES_DIR /rr.img.zip "
if [ -f " $IMAGES_DIR /rr.img.zip " ] ; then
cd " $IMAGES_DIR "
msg_info " Unzipping $LOADER_NAME loader. Please wait... "
unzip -qo rr.img.zip
msg_ok " Unzipped $LOADER_NAME loader successfully. "
rm -f rr.img.zip
FILE = "rr.img"
LOADER_FILE = " $IMAGES_DIR / $FILE "
cd - > /dev/null
fi
; ;
tinycore)
curl -s https://api.github.com/repos/PeterSuh-Q3/tinycore-redpill/releases/latest \
| grep "browser_download_url.*tinycore-redpill.v.*img.gz" \
| cut -d '"' -f 4 \
| xargs wget -q --show-progress -O " $IMAGES_DIR /tinycore.img.gz "
if [ -f " $IMAGES_DIR /tinycore.img.gz " ] ; then
cd " $IMAGES_DIR "
msg_info " Unzipping $LOADER_NAME loader. Please wait... "
gunzip -f tinycore.img.gz 2> /dev/null
msg_ok " Unzipped $LOADER_NAME loader successfully. "
FILE = "tinycore.img"
LOADER_FILE = " $IMAGES_DIR / $FILE "
cd - > /dev/null
else
msg_error " Failed to download $LOADER_NAME loader "
sleep 1
select_loader
fi
; ;
esac
msg_ok " Downloaded ${ CL } ${ BL } ${ FILE } ${ CL } to ${ IMAGES_DIR } "
}
# =======================================================
# ==========================================================
# Select UEFI Storage
# ==========================================================
function select_efi_storage( ) {
local vmid = $1
local STORAGE = ""
STORAGE_MENU = ( )
while read -r line; do
TAG = $( echo $line | awk '{print $1}' )
TYPE = $( echo $line | awk '{printf "%-10s", $2}' )
FREE = $( echo $line | numfmt --field 4-6 --from-unit= K --to= iec --format "%.2f" | awk '{printf( "%9sB", $6)}' )
ITEM = " Type: $TYPE Free: $FREE "
OFFSET = 2
if [ [ $(( ${# ITEM } + $OFFSET )) -gt ${ MSG_MAX_LENGTH :- } ] ] ; then
MSG_MAX_LENGTH = $(( ${# ITEM } + $OFFSET ))
fi
STORAGE_MENU += ( " $TAG " " $ITEM " "OFF" )
done < <( pvesm status -content images | awk 'NR>1' )
VALID = $( pvesm status -content images | awk 'NR>1' )
if [ -z " $VALID " ] ; then
2026-04-05 11:24:08 +02:00
msg_error "Unable to detect a valid storage location for EFI disk." >& 2
return 1
2025-03-23 17:50:33 +01:00
elif [ $(( ${# STORAGE_MENU [@] } / 3 )) -eq 1 ] ; then
STORAGE = ${ STORAGE_MENU [0] }
else
2026-04-05 11:24:08 +02:00
[ [ -n " ${ SPINNER_PID :- } " ] ] && kill " $SPINNER_PID " > /dev/null 2>& 1
2025-03-23 17:50:33 +01:00
while [ -z " ${ STORAGE : +x } " ] ; do
STORAGE = $( whiptail --backtitle "ProxMenuX" --title "EFI Disk Storage" --radiolist \
" $( translate "Choose the storage volume for the EFI disk (4MB):\n\nUse Spacebar to select." ) " \
16 $(( $MSG_MAX_LENGTH + 23 )) 6 \
2026-04-05 11:24:08 +02:00
" ${ STORAGE_MENU [@] } " 3>& 1 1>& 2 2>& 3) || {
msg_warn " $( translate "EFI storage selection cancelled." ) " >& 2
return 1
}
2025-03-23 17:50:33 +01:00
done
fi
2026-04-05 11:24:08 +02:00
[ [ -z " $STORAGE " ] ] && return 1
2025-03-23 17:50:33 +01:00
echo " $STORAGE "
}
# ==========================================================
# ==========================================================
# Select Storage Loader
# ==========================================================
function select_storage_volume( ) {
local vmid = $1
local purpose = $2
local STORAGE = ""
STORAGE_MENU = ( )
while read -r line; do
TAG = $( echo $line | awk '{print $1}' )
TYPE = $( echo $line | awk '{printf "%-10s", $2}' )
FREE = $( echo $line | numfmt --field 4-6 --from-unit= K --to= iec --format "%.2f" | awk '{printf( "%9sB", $6)}' )
ITEM = " Type: $TYPE Free: $FREE "
OFFSET = 2
if [ [ $(( ${# ITEM } + $OFFSET )) -gt ${ MSG_MAX_LENGTH :- } ] ] ; then
MSG_MAX_LENGTH = $(( ${# ITEM } + $OFFSET ))
fi
STORAGE_MENU += ( " $TAG " " $ITEM " "OFF" )
done < <( pvesm status -content images | awk 'NR>1' )
VALID = $( pvesm status -content images | awk 'NR>1' )
if [ -z " $VALID " ] ; then
2026-04-05 11:24:08 +02:00
msg_error "Unable to detect a valid storage location." >& 2
return 1
2025-03-23 17:50:33 +01:00
elif [ $(( ${# STORAGE_MENU [@] } / 3 )) -eq 1 ] ; then
STORAGE = ${ STORAGE_MENU [0] }
else
while [ -z " ${ STORAGE : +x } " ] ; do
STORAGE = $( whiptail --backtitle "ProxMenuX" --title "Storage Pools" --radiolist \
" $( translate " Choose the storage volume for $purpose :\n\nUse Spacebar to select. " ) " \
16 $(( $MSG_MAX_LENGTH + 23 )) 6 \
2026-04-05 11:24:08 +02:00
" ${ STORAGE_MENU [@] } " 3>& 1 1>& 2 2>& 3) || {
msg_warn " $( translate " Storage selection cancelled for $purpose . " ) " >& 2
return 1
}
2025-03-23 17:50:33 +01:00
done
fi
2026-04-05 11:24:08 +02:00
[ [ -z " $STORAGE " ] ] && return 1
2025-03-23 17:50:33 +01:00
echo " $STORAGE "
}
# ==========================================================
# Create VM
# ==========================================================
function create_vm( ) {
# Create the VM
2026-04-06 17:16:26 +02:00
if ! qm create $VMID -agent 1${ MACHINE } -tablet 0 -localtime 1${ BIOS_TYPE } ${ CPU_TYPE } -cores $CORE_COUNT -memory $RAM_SIZE \
2026-04-05 11:24:08 +02:00
-name $HN -tags proxmenux,nas -net0 virtio,bridge= $BRG ,macaddr= $MAC $VLAN $MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci \
2026-04-06 17:16:26 +02:00
-serial0 socket; then
msg_error "Failed to create base VM. Check VM ID and host configuration."
return 1
fi
2025-03-23 17:50:33 +01:00
msg_ok " Create a $NAME "
# Check if UEFI (OVMF) is being used ===================
if [ [ " $BIOS_TYPE " = = *"ovmf" * ] ] ; then
msg_info "Configuring EFI disk"
2026-04-05 11:24:08 +02:00
if ! EFI_STORAGE = $( select_efi_storage $VMID ) ; then
msg_error "EFI storage selection failed or was cancelled."
return 1
fi
2025-03-23 17:50:33 +01:00
EFI_DISK_NAME = " vm- ${ VMID } -disk-efivars "
# Determine storage type and extension
STORAGE_TYPE = $( pvesm status -storage $EFI_STORAGE | awk 'NR>1 {print $2}' )
case $STORAGE_TYPE in
nfs | dir)
EFI_DISK_EXT = ".raw"
EFI_DISK_REF = " $VMID / "
; ;
*)
EFI_DISK_EXT = ""
EFI_DISK_REF = ""
; ;
esac
2025-05-09 18:46:37 +02:00
STORAGE_TYPE = $( pvesm status -storage " $EFI_STORAGE " | awk 'NR>1 {print $2}' )
EFI_DISK_ID = "efidisk0"
if [ [ " $STORAGE_TYPE " = = "btrfs" || " $STORAGE_TYPE " = = "dir" || " $STORAGE_TYPE " = = "nfs" ] ] ; then
if qm set " $VMID " -$EFI_DISK_ID " $EFI_STORAGE :4,efitype=4m,format=raw,pre-enrolled-keys=0 " >/dev/null 2>& 1; then
msg_ok " EFI disk created (raw) and configured on ${ CL } ${ BL } $EFI_STORAGE ${ GN } ${ CL } "
2025-03-23 17:50:33 +01:00
else
msg_error "Failed to configure EFI disk"
ERROR_FLAG = true
fi
else
2025-05-09 18:46:37 +02:00
EFI_DISK_NAME = " vm- ${ VMID } -disk-efivars "
EFI_DISK_EXT = ""
EFI_DISK_REF = ""
if pvesm alloc " $EFI_STORAGE " " $VMID " " $EFI_DISK_NAME " 4M >/dev/null 2>& 1; then
if qm set " $VMID " -$EFI_DISK_ID " $EFI_STORAGE : ${ EFI_DISK_NAME } ,pre-enrolled-keys=0 " >/dev/null 2>& 1; then
msg_ok " EFI disk created and configured on ${ CL } ${ BL } $EFI_STORAGE ${ GN } ${ CL } "
else
msg_error "Failed to configure EFI disk"
ERROR_FLAG = true
fi
else
msg_error "Failed to create EFI disk"
ERROR_FLAG = true
fi
2025-03-23 17:50:33 +01:00
fi
2025-05-09 18:46:37 +02:00
2025-03-23 17:50:33 +01:00
fi
# ==========================================================
# Select storage volume for loader =======================
2026-04-05 11:24:08 +02:00
if ! LOADER_STORAGE = $( select_storage_volume $VMID "loader disk" ) ; then
msg_error "Loader storage selection failed or was cancelled."
return 1
fi
2025-03-23 17:50:33 +01:00
#Run the command in the background and capture its PID
qm importdisk $VMID ${ LOADER_FILE } $LOADER_STORAGE > /tmp/import_log_$VMID .txt 2>& 1 &
import_pid = $!
# Show a simple progress indicator
echo -n "Importing loader disk: "
while kill -0 $import_pid 2>/dev/null; do
echo -n "."
sleep 2.5
done
wait $import_pid
rm -f /tmp/import_log_$VMID .txt
IMPORTED_DISK = $( qm config $VMID | grep -E 'unused[0-9]+' | tail -1 | cut -d: -f1)
# If the disk was not imported correctly, show an error message but continue
if [ -z " $IMPORTED_DISK " ] ; then
msg_error "Loader import failed. No disk detected."
ERROR_FLAG = true
else
msg_ok " Loader imported successfully to ${ CL } ${ BL } $LOADER_STORAGE ${ GN } ${ CL } "
fi
2025-05-09 18:46:37 +02:00
STORAGE_TYPE = $( pvesm status -storage " $LOADER_STORAGE " | awk 'NR>1 {print $2}' )
if [ [ " $STORAGE_TYPE " = = "btrfs" || " $STORAGE_TYPE " = = "dir" || " $STORAGE_TYPE " = = "nfs" ] ] ; then
UNUSED_LINE = $( qm config " $VMID " | grep -E '^unused[0-9]+:' )
IMPORTED_ID = $( echo " $UNUSED_LINE " | cut -d: -f1)
IMPORTED_REF = $( echo " $UNUSED_LINE " | cut -d: -f2- | xargs)
if [ [ -n " $IMPORTED_REF " && -n " $IMPORTED_ID " ] ] ; then
if qm set " $VMID " -ide0 " $IMPORTED_REF " >/dev/null 2>& 1; then
msg_ok "Configured loader disk as ide0"
qm set " $VMID " -delete " $IMPORTED_ID " >/dev/null 2>& 1
else
msg_error "Failed to assign loader disk to ide0"
ERROR_FLAG = true
fi
else
msg_error "Loader import failed. No disk detected in config."
ERROR_FLAG = true
fi
else
DISK_NAME = " vm- ${ VMID } -disk-0 "
if qm set " $VMID " -ide0 " $LOADER_STORAGE : ${ DISK_NAME } " >/dev/null 2>& 1; then
msg_ok "Configured loader disk as ide0"
else
msg_error "Failed to assign loader disk"
ERROR_FLAG = true
fi
2025-03-23 17:50:33 +01:00
fi
2025-05-09 18:46:37 +02:00
2025-03-23 17:50:33 +01:00
result = $( qm set " $VMID " -boot order = ide0 2>& 1)
if [ [ $? -eq 0 ] ] ; then
msg_ok "Loader configured as boot device."
else
ERROR_FLAG = true
fi
# ==========================================================
2026-04-05 11:24:08 +02:00
DISK_INFO = ""
CONSOLE_DISK_INFO = ""
DISK_SLOT_INDEX = 0
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
if [ [ ${# VIRTUAL_DISKS [@] } -gt 0 ] ] ; then
2025-03-23 17:50:33 +01:00
for i in " ${ !VIRTUAL_DISKS[@] } " ; do
IFS = ':' read -r STORAGE SIZE <<< " ${ VIRTUAL_DISKS [ $i ] } "
2026-04-05 11:24:08 +02:00
2025-03-23 17:50:33 +01:00
STORAGE_TYPE = $( pvesm status -storage $STORAGE | awk 'NR>1 {print $2}' )
case $STORAGE_TYPE in
nfs | dir)
DISK_EXT = ".raw"
DISK_REF = " $VMID / "
; ;
*)
DISK_EXT = ""
DISK_REF = ""
; ;
esac
2026-04-05 11:24:08 +02:00
DISK_NUM = $(( DISK_SLOT_INDEX+1))
2025-03-23 17:50:33 +01:00
DISK_NAME = " vm- ${ VMID } -disk- ${ DISK_NUM } ${ DISK_EXT } "
2026-04-05 11:24:08 +02:00
SATA_ID = " sata ${ DISK_SLOT_INDEX } "
2025-05-09 18:46:37 +02:00
if [ [ " $STORAGE_TYPE " = = "btrfs" || " $STORAGE_TYPE " = = "dir" || " $STORAGE_TYPE " = = "nfs" ] ] ; then
2026-04-05 11:24:08 +02:00
msg_info " Creating virtual disk (format=raw) for $STORAGE_TYPE ... "
if ! qm set " $VMID " -$SATA_ID " $STORAGE : $SIZE ,format=raw " >/dev/null 2>& 1; then
msg_error " Failed to assign disk $DISK_NUM ( $SATA_ID ) on $STORAGE "
ERROR_FLAG = true
continue
fi
2025-05-09 18:46:37 +02:00
else
2026-04-05 11:24:08 +02:00
msg_info " Allocating virtual disk for $STORAGE_TYPE ... "
if ! pvesm alloc " $STORAGE " " $VMID " " $DISK_NAME " " $SIZE " G >/dev/null 2>& 1; then
msg_error " Failed to allocate virtual disk $DISK_NUM "
ERROR_FLAG = true
continue
fi
if ! qm set " $VMID " -$SATA_ID " $STORAGE : ${ DISK_REF } $DISK_NAME " >/dev/null 2>& 1; then
msg_error " Failed to configure virtual disk as $SATA_ID "
ERROR_FLAG = true
continue
fi
2025-03-23 17:50:33 +01:00
fi
2025-05-09 18:46:37 +02:00
2025-03-23 17:50:33 +01:00
msg_ok " Configured virtual disk as $SATA_ID , ${ SIZE } GB on ${ CL } ${ BL } $STORAGE ${ CL } ${ GN } "
DISK_INFO = " ${ DISK_INFO } <p>Virtual Disk $DISK_NUM : ${ SIZE } GB on ${ STORAGE } </p> "
CONSOLE_DISK_INFO = " ${ CONSOLE_DISK_INFO } - Virtual Disk $DISK_NUM : ${ SIZE } GB on ${ STORAGE } ( $SATA_ID )\n "
2026-04-05 11:24:08 +02:00
DISK_SLOT_INDEX = $(( DISK_SLOT_INDEX + 1 ))
2025-03-23 17:50:33 +01:00
done
2026-04-05 11:24:08 +02:00
fi
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
EFFECTIVE_IMPORT_DISKS = ( )
if [ [ ${# IMPORT_DISKS [@] } -gt 0 ] ] ; then
EFFECTIVE_IMPORT_DISKS = ( " ${ IMPORT_DISKS [@] } " )
elif [ [ ${# PASSTHROUGH_DISKS [@] } -gt 0 ] ] ; then
EFFECTIVE_IMPORT_DISKS = ( " ${ PASSTHROUGH_DISKS [@] } " )
fi
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
if [ [ ${# EFFECTIVE_IMPORT_DISKS [@] } -gt 0 ] ] ; then
for DISK in " ${ EFFECTIVE_IMPORT_DISKS [@] } " ; do
DISK_MODEL = $( lsblk -ndo MODEL " $DISK " | xargs)
DISK_SIZE = $( lsblk -ndo SIZE " $DISK " | xargs)
DISK_NUM = $(( DISK_SLOT_INDEX+1))
DISK_ID = " sata ${ DISK_SLOT_INDEX } "
if qm set " $VMID " -${ DISK_ID } ${ DISK } >/dev/null 2>& 1; then
msg_ok " Configured import disk ${ CL } ${ BL } ( $DISK_MODEL $DISK_SIZE ) ${ CL } ${ GN } as $DISK_ID "
DISK_INFO = " ${ DISK_INFO } <p>Import Disk ${ DISK_NUM } : $DISK ( $DISK_MODEL $DISK_SIZE )</p> "
CONSOLE_DISK_INFO = " ${ CONSOLE_DISK_INFO } - Import Disk ${ DISK_NUM } : $DISK ( $DISK_MODEL $DISK_SIZE ) ( ${ DISK_ID } )\n "
DISK_SLOT_INDEX = $(( DISK_SLOT_INDEX + 1 ))
else
msg_error " Failed to configure import disk $DISK as $DISK_ID "
ERROR_FLAG = true
fi
done
fi
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
if [ [ ${# CONTROLLER_NVME_PCIS [@] } -gt 0 ] ] ; then
2026-04-06 19:13:31 +02:00
local CONTROLLER_CAN_STAGE = true
2026-04-06 17:16:26 +02:00
if declare -F _pci_is_iommu_active >/dev/null 2>& 1 && ! _pci_is_iommu_active; then
if [ [ " ${ VM_STORAGE_IOMMU_PENDING_REBOOT :- 0 } " = = "1" ] ] ; then
msg_warn " $( translate "IOMMU was configured during this wizard and a reboot is pending." ) "
2026-04-06 19:13:31 +02:00
msg_warn " $( translate "Controller + NVMe assignment will be written now and become active after host reboot." ) "
2026-04-06 17:16:26 +02:00
else
msg_error " $( translate "IOMMU is not active. Skipping Controller + NVMe assignment." ) "
ERROR_FLAG = true
2026-04-06 19:13:31 +02:00
CONTROLLER_CAN_STAGE = false
2026-04-06 17:16:26 +02:00
fi
2026-04-06 19:13:31 +02:00
fi
if [ [ " $CONTROLLER_CAN_STAGE " != "true" ] ] ; then
:
2026-04-06 17:16:26 +02:00
elif ! _vm_is_q35 " $VMID " ; then
2026-04-05 11:24:08 +02:00
msg_error " $( translate "Controller + NVMe passthrough requires machine type q35. Skipping controller assignment." ) "
ERROR_FLAG = true
else
2026-04-06 13:39:07 +02:00
NEED_HOOK_SYNC = false
2026-04-05 11:24:08 +02:00
HOSTPCI_INDEX = 0
if declare -F _pci_next_hostpci_index >/dev/null 2>& 1; then
HOSTPCI_INDEX = $( _pci_next_hostpci_index " $VMID " 2>/dev/null || echo 0)
else
while qm config " $VMID " | grep -q " ^hostpci ${ HOSTPCI_INDEX } : " ; do
HOSTPCI_INDEX = $(( HOSTPCI_INDEX + 1 ))
done
fi
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
for PCI_DEV in " ${ CONTROLLER_NVME_PCIS [@] } " ; do
if declare -F _pci_function_assigned_to_vm >/dev/null 2>& 1; then
if _pci_function_assigned_to_vm " $PCI_DEV " " $VMID " ; then
msg_warn " Controller/NVMe already present in VM config ( ${ PCI_DEV } ) "
continue
fi
else
local PCI_BDF
PCI_BDF = " ${ PCI_DEV #0000 : } "
if qm config " $VMID " 2>/dev/null | grep -qE " ^hostpci[0-9]+:.*(0000:)? ${ PCI_BDF } ([,[:space:]]| $) " ; then
msg_warn " Controller/NVMe already present in VM config ( ${ PCI_DEV } ) "
continue
fi
fi
2025-03-23 17:50:33 +01:00
2026-04-06 13:39:07 +02:00
SOURCE_VMS = ( )
mapfile -t SOURCE_VMS < <( _pci_assigned_vm_ids " $PCI_DEV " " $VMID " 2>/dev/null)
if [ [ ${# SOURCE_VMS [@] } -gt 0 ] ] ; then
HAS_RUNNING = false
for SRC_VMID in " ${ SOURCE_VMS [@] } " ; do
if _vm_status_is_running " $SRC_VMID " ; then
HAS_RUNNING = true
msg_warn " $( translate "Controller/NVMe is in use by running VM" ) ${ SRC_VMID } ( $( translate "stop source VM first" ) ) "
fi
done
if [ [ " $HAS_RUNNING " = = "true" ] ] ; then
continue
fi
CONFLICT_ACTION = $( prompt_controller_conflict_policy " $PCI_DEV " " ${ SOURCE_VMS [@] } " )
case " $CONFLICT_ACTION " in
keep_disable_onboot)
for SRC_VMID in " ${ SOURCE_VMS [@] } " ; do
if _vm_onboot_is_enabled " $SRC_VMID " ; then
if qm set " $SRC_VMID " -onboot 0 >/dev/null 2>& 1; then
msg_warn " $( translate "Start on boot disabled for VM" ) ${ SRC_VMID } "
fi
fi
done
NEED_HOOK_SYNC = true
; ;
move_remove_source)
SLOT_BASE = $( _pci_slot_base " $PCI_DEV " )
for SRC_VMID in " ${ SOURCE_VMS [@] } " ; do
if _remove_pci_slot_from_vm_config " $SRC_VMID " " $SLOT_BASE " ; then
msg_ok " $( translate "Controller/NVMe removed from source VM" ) ${ SRC_VMID } ( ${ PCI_DEV } ) "
fi
done
; ;
*)
msg_info2 " $( translate "Skipped device" ) : ${ PCI_DEV } "
continue
; ;
esac
fi
2026-04-05 11:24:08 +02:00
if qm set " $VMID " --hostpci${ HOSTPCI_INDEX } " ${ PCI_DEV } ,pcie=1 " >/dev/null 2>& 1; then
msg_ok " Configured controller/NVMe as hostpci ${ HOSTPCI_INDEX } : ${ PCI_DEV } "
DISK_INFO = " ${ DISK_INFO } <p>Controller/NVMe: ${ PCI_DEV } </p> "
CONSOLE_DISK_INFO = " ${ CONSOLE_DISK_INFO } - Controller/NVMe: ${ PCI_DEV } (hostpci ${ HOSTPCI_INDEX } )\n "
HOSTPCI_INDEX = $(( HOSTPCI_INDEX + 1 ))
else
msg_error " Failed to configure controller/NVMe: ${ PCI_DEV } "
ERROR_FLAG = true
fi
done
2026-04-06 13:39:07 +02:00
if [ [ " $NEED_HOOK_SYNC " = = "true" ] ] && declare -F sync_proxmenux_gpu_guard_hooks >/dev/null 2>& 1; then
ensure_proxmenux_gpu_guard_hookscript
sync_proxmenux_gpu_guard_hooks
msg_ok " $( translate "VM hook guard synced for shared controller/NVMe protection" ) "
fi
2026-04-05 11:24:08 +02:00
fi
fi
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
if [ [ ${# VIRTUAL_DISKS [@] } -eq 0 && ${# EFFECTIVE_IMPORT_DISKS [@] } -eq 0 && ${# CONTROLLER_NVME_PCIS [@] } -eq 0 ] ] ; then
msg_error "No disks/controllers configured."
exit_script
fi
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
# HTML description
2025-03-23 17:50:33 +01:00
HTML_DESC = " <div align='center'>
<table style = 'width: 100%; border-collapse: collapse;' >
<tr>
<td style = 'width: 100px; vertical-align: middle;' >
<img src = 'https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logo_desc.png' alt = 'ProxMenux Logo' style = 'height: 100px;' >
</td>
<td style = 'vertical-align: middle;' >
<h1 style = 'margin: 0;' >Synology DSM VM</h1>
<p style = 'margin: 0;' >Created with ProxMenuX</p>
<p style = 'margin: 0;' >Loader: $LOADER_NAME </p>
</td>
</tr>
</table>
<p>
<a href = 'https://macrimi.github.io/ProxMenux/docs/create-vm/synology' target = '_blank' ><img src = 'https://img.shields.io/badge/📚_Docs-blue' alt = 'Docs' ></a>
2025-03-23 19:46:51 +01:00
<a href = 'https://raw.githubusercontent.com/MacRimi/ProxMenux/refs/heads/main/scripts/vm/synology.sh' target = '_blank' ><img src = 'https://img.shields.io/badge/💻_Code-green' alt = 'Code' ></a>
2025-03-23 17:50:33 +01:00
<a href = '$LOADER_URL' target = '_blank' ><img src = 'https://img.shields.io/badge/📦_Loader-orange' alt = 'Loader' ></a>
<a href = 'https://ko-fi.com/macrimi' target = '_blank' ><img src = 'https://img.shields.io/badge/☕_Ko--fi-red' alt = 'Ko-fi' ></a>
</p>
<div>
${ DISK_INFO }
</div>
</div>"
2026-04-05 11:24:08 +02:00
msg_info "Setting VM description"
if ! qm set " $VMID " -description " $HTML_DESC " >/dev/null 2>& 1; then
msg_error "Failed to set VM description"
exit_script
2025-03-23 17:50:33 +01:00
fi
2026-04-05 11:24:08 +02:00
msg_ok "Configured VM description"
2025-03-23 17:50:33 +01:00
if [ " $ERROR_FLAG " = true ] ; then
msg_error "VM created with errors. Check configuration."
else
2026-04-05 11:24:08 +02:00
if [ [ " ${ WIZARD_ADD_GPU :- no } " = = "yes" ] ] ; then
WIZARD_GPU_RESULT = "cancelled"
run_gpu_passthrough_wizard
replay_vm_wizard_capture
fi
2026-04-06 18:40:57 +02:00
local GPU_WIZARD_APPLIED = "no"
local GPU_WIZARD_REBOOT_REQUIRED = "no"
case " ${ WIZARD_GPU_RESULT :- } " in
applied)
GPU_WIZARD_APPLIED = "yes"
; ;
applied_reboot_required)
GPU_WIZARD_APPLIED = "yes"
GPU_WIZARD_REBOOT_REQUIRED = "yes"
; ;
esac
if [ [ " ${ WIZARD_ADD_GPU :- no } " = = "yes" && " $GPU_WIZARD_APPLIED " = = "yes" ] ] ; then
2026-04-05 11:24:08 +02:00
msg_success " $( translate "Completed Successfully with GPU passthrough configured!" ) "
2026-04-06 18:40:57 +02:00
if [ [ " $GPU_WIZARD_REBOOT_REQUIRED " = = "yes" ] ] ; then
msg_warn " $( translate "Host VFIO configuration changed (initramfs updated). Reboot required before starting the VM." ) "
fi
2026-04-05 11:24:08 +02:00
else
msg_success " $( translate "Completed Successfully!" ) "
if [ [ " ${ WIZARD_ADD_GPU :- no } " = = "yes" && " $WIZARD_GPU_RESULT " = = "no_gpu" ] ] ; then
msg_warn " $( translate "GPU passthrough was skipped (no compatible GPU detected)." ) "
elif [ [ " ${ WIZARD_ADD_GPU :- no } " = = "yes" && " $WIZARD_GPU_RESULT " != "applied" ] ] ; then
msg_warn " $( translate "GPU passthrough was not applied." ) "
fi
fi
2025-03-23 17:50:33 +01:00
echo -e " ${ TAB } ${ GN } $( translate "Next Steps:" ) ${ CL } "
echo -e " ${ TAB } 1. $( translate "Start the VM" ) "
echo -e " ${ TAB } 2. $( translate "Open the VM console and wait for the loader to boot" ) "
echo -e " ${ TAB } 3. $( translate "In the loader interface, follow the instructions to select your Synology model" ) "
echo -e " ${ TAB } 4. $( translate "Complete the DSM installation wizard" ) "
echo -e " ${ TAB } 5. $( translate "Find your device using https://finds.synology.com" ) "
2026-04-06 18:40:57 +02:00
if [ [ " $GPU_WIZARD_APPLIED " = = "yes" ] ] ; then
2026-04-05 11:24:08 +02:00
echo -e " ${ TAB } - $( translate "If you want to use a physical monitor on the passthrough GPU:" ) "
echo -e " ${ TAB } • $( translate "First complete DSM setup and verify Web UI/SSH access." ) "
echo -e " ${ TAB } • $( translate "Then change the VM display to none (vga: none) when the system is stable." ) "
fi
2026-04-06 18:40:57 +02:00
local HOST_REBOOT_REQUIRED = "no"
2026-04-12 20:32:34 +02:00
local REBOOT_REASONS = ""
2026-04-06 17:16:26 +02:00
if [ [ " ${ VM_STORAGE_IOMMU_PENDING_REBOOT :- 0 } " = = "1" ] ] ; then
2026-04-06 18:40:57 +02:00
HOST_REBOOT_REQUIRED = "yes"
2026-04-12 20:32:34 +02:00
msg_ok " $( translate "IOMMU has been enabled — a system reboot is required" ) "
REBOOT_REASONS += " $( translate "IOMMU has been enabled on this system." ) \n "
2026-04-06 17:16:26 +02:00
fi
2026-04-06 18:40:57 +02:00
if [ [ " $GPU_WIZARD_REBOOT_REQUIRED " = = "yes" ] ] ; then
HOST_REBOOT_REQUIRED = "yes"
2026-04-12 20:32:34 +02:00
REBOOT_REASONS += " $( translate "GPU passthrough changes require a host reboot." ) \n "
2026-04-06 18:40:57 +02:00
fi
if [ [ " $HOST_REBOOT_REQUIRED " = = "yes" ] ] ; then
2026-04-12 20:32:34 +02:00
echo ""
if whiptail --title " $( translate "Reboot Required" ) " --yesno \
" \n ${ REBOOT_REASONS } \n $( translate "A host reboot is required before starting the VM. Reboot now?" ) " 13 78; then
2026-04-06 18:40:57 +02:00
msg_warn " $( translate "Rebooting the system..." ) "
reboot
2026-04-12 20:32:34 +02:00
else
echo ""
msg_info2 " $( translate "To use the VM without issues, the host must be restarted before starting it." ) "
msg_info2 " $( translate "Do not start the VM until the system has been rebooted." ) "
2026-04-06 18:40:57 +02:00
fi
fi
2025-03-23 17:50:33 +01:00
echo -e
2025-05-09 18:48:52 +02:00
#msg_success "$(translate "Press Enter to return to the main menu...")"
#read -r
2025-03-23 17:58:12 +01:00
2025-03-23 17:50:33 +01:00
fi
}
# ==========================================================
# ==========================================================
# Main execution
# ==========================================================
header_info
2025-05-07 23:36:02 +02:00
#echo -e "\n Loading..."
2025-03-23 17:50:33 +01:00
sleep 1
# Start script
if whiptail --backtitle "ProxMenuX" --title " $NAME " --yesno " $( translate " This will create a New $NAME . Proceed? " ) " 10 58; then
2026-04-05 11:24:08 +02:00
start_vm_wizard_capture
if ! start_script; then
stop_vm_wizard_capture
msg_warn " $( translate "VM creation cancelled before disk planning." ) "
exit 0
fi
prompt_optional_gpu_passthrough
if [ [ " ${ WIZARD_ADD_GPU :- no } " != "yes" ] ] ; then
stop_vm_wizard_capture
fi
2025-03-23 17:50:33 +01:00
else
clear
exit
fi
# Create VM
create_vm
2026-04-05 11:24:08 +02:00
stop_vm_wizard_capture
2025-03-23 17:50:33 +01:00
2026-04-05 11:24:08 +02:00
# ==========================================================