Update repository and clean up

This commit is contained in:
MacRimi
2026-03-26 19:03:01 +01:00
parent 12c088c10b
commit d497763e38
19 changed files with 2 additions and 7088 deletions

View File

@@ -1,720 +0,0 @@
# base-packages.txt - Generated on 2025-05-15 21:15:29
# Proxmox Version: pve-manager/8.4.1/ (running kernel: 6.8.12-9-pve)
adduser
apparmor
apt
apt-listchanges
apt-utils
attr
base-files
base-passwd
bash
bash-completion
bc
bind9-dnsutils
bind9-host
bind9-libs
binutils
binutils-common
binutils-x86-64-linux-gnu
bridge-utils
bsdextrautils
bsd-mailx
bsdutils
btrfs-progs
busybox
bzip2
ca-certificates
ceph-common
ceph-fuse
chrony
cifs-utils
console-setup
console-setup-linux
coreutils
corosync
cpio
criu
cron
cron-daemon-common
cstream
curl
dash
dbus
dbus-bin
dbus-daemon
dbus-session-bus-common
dbus-system-bus-common
debconf
debconf-i18n
debian-archive-keyring
debian-faq
debianutils
dialog
diffutils
dirmngr
distro-info-data
dmeventd
dmidecode
dmsetup
doc-debian
dosfstools
dpkg
dtach
e2fsprogs
ebtables
efibootmgr
eject
ethtool
faketime
fdisk
fdutils
file
findutils
fontconfig
fontconfig-config
fonts-dejavu-core
fonts-font-awesome
fonts-font-logos
fonts-glyphicons-halflings
frr
frr-pythontools
fuse
gcc-12-base
gdisk
genisoimage
gettext-base
glusterfs-client
glusterfs-common
gnupg
gnupg-l10n
gnupg-utils
gnutls-bin
gpg
gpg-agent
gpgconf
gpgsm
gpgv
gpg-wks-client
gpg-wks-server
grep
groff-base
grub2-common
grub-common
grub-efi-amd64
grub-efi-amd64-bin
grub-efi-amd64-signed
grub-pc-bin
gzip
hdparm
hostname
ifupdown2
inetutils-telnet
init
initramfs-tools
initramfs-tools-core
init-system-helpers
iproute2
ipset
iptables
iputils-ping
isc-dhcp-client
isc-dhcp-common
iso-codes
jq
kbd
keyboard-configuration
keyutils
klibc-utils
kmod
krb5-locales
ksm-control-daemon
less
libacl1
libaio1
libanyevent-http-perl
libanyevent-perl
libapparmor1
libappconfig-perl
libapt-pkg6.0
libapt-pkg-perl
libarchive13
libargon2-1
libasound2
libasound2-data
libassuan0
libasyncns0
libattr1
libaudit1
libaudit-common
libauthen-pam-perl
libavahi-client3
libavahi-common3
libavahi-common-data
libbabeltrace1
libbinutils
libblas3
libblkid1
libbpf1
libbrotli1
libbsd0
libbytes-random-secure-perl
libbz2-1.0
libc6
libcairo2
libcap2
libcap2-bin
libcap-ng0
libc-ares2
libc-bin
libcbor0.8
libcephfs2
libcfg7
libc-l10n
libclone-perl
libcmap4
libcom-err2
libcommon-sense-perl
libconvert-asn1-perl
libcorosync-common4
libcpg4
libcrypt1
libcrypt-openssl-bignum-perl
libcrypt-openssl-random-perl
libcrypt-openssl-rsa-perl
libcrypt-random-seed-perl
libcryptsetup12
libcrypt-ssleay-perl
libctf0
libctf-nobfd0
libcurl3-gnutls
libcurl4
libdatrie1
libdb5.3
libdbi1
libdbus-1-3
libdebconfclient0
libdevel-cycle-perl
libdevmapper1.02.1
libdevmapper-event1.02.1
libdigest-hmac-perl
libdouble-conversion3
libdrm2
libdrm-common
libdw1
libedit2
libefiboot1
libefivar1
libelf1
libencode-locale-perl
libepoxy0
libevent-2.1-7
libevent-core-2.1-7
libexpat1
libext2fs2
libfaketime
libfdisk1
libfdt1
libffi8
libfido2-1
libfile-chdir-perl
libfile-find-rule-perl
libfile-listing-perl
libfile-readbackwards-perl
libfilesys-df-perl
libflac12
libfmt9
libfontconfig1
libfreetype6
libfribidi0
libfstrm0
libfuse2
libfuse3-3
libgbm1
libgcc-s1
libgcrypt20
libgdbm6
libgdbm-compat4
libgfapi0
libgfchangelog0
libgfrpc0
libgfxdr0
libglib2.0-0
libglusterd0
libglusterfs0
libgmp10
libgnutls30
libgnutls-dane0
libgnutlsxx30
libgoogle-perftools4
libgpg-error0
libgprofng0
libgraphite2-3
libgssapi-krb5-2
libgstreamer1.0-0
libgstreamer-plugins-base1.0-0
libharfbuzz0b
libhogweed6
libhtml-parser-perl
libhtml-tagset-perl
libhtml-tree-perl
libhttp-cookies-perl
libhttp-daemon-perl
libhttp-date-perl
libhttp-message-perl
libhttp-negotiate-perl
libibverbs1
libicu72
libidn2-0
libinih1
libio-html-perl
libio-multiplex-perl
libio-socket-ssl-perl
libio-stringy-perl
libip4tc2
libip6tc2
libipset13
libiscsi7
libisns0
libjansson4
libjemalloc2
libjpeg62-turbo
libjq1
libjs-bootstrap
libjs-extjs
libjs-jquery
libjson-c5
libjson-glib-1.0-0
libjson-glib-1.0-common
libjson-perl
libjson-xs-perl
libjs-qrcodejs
libjs-sencha-touch
libk5crypto3
libkeyutils1
libklibc
libkmod2
libknet1
libkrb5-3
libkrb5support0
libksba8
libldap-2.5-0
libldb2
liblinear4
liblinux-inotify2-perl
liblmdb0
liblocale-gettext-perl
liblockfile1
liblockfile-bin
liblttng-ust1
liblttng-ust-common1
liblttng-ust-ctl5
liblua5.3-0
liblvm2cmd2.03
liblwp-mediatypes-perl
liblwp-protocol-https-perl
liblz4-1
liblzma5
liblzo2-2
libmagic1
libmagic-mgc
libmath-random-isaac-perl
libmaxminddb0
libmd0
libmime-base32-perl
libmnl0
libmount1
libmp3lame0
libmpg123-0
libncurses6
libncursesw6
libnet1
libnetaddr-ip-perl
libnet-dbus-perl
libnet-dns-perl
libnetfilter-conntrack3
libnetfilter-log1
libnet-http-perl
libnet-ip-perl
libnet-ldap-perl
libnet-ssleay-perl
libnet-subnet-perl
libnettle8
libnewt0.52
libnfnetlink0
libnfsidmap1
libnftables1
libnftnl11
libnghttp2-14
libnl-3-200
libnl-route-3-200
libnozzle1
libnpth0
libnsl2
libnspr4
libnss3
libnss-systemd
libnuma1
libnumber-compare-perl
libnvpair3linux
liboath0
libogg0
libonig5
libopeniscsiusr
libopus0
liborc-0.4-0
libp11-kit0
libpam0g
libpam-modules
libpam-modules-bin
libpam-runtime
libpam-systemd
libpango-1.0-0
libpangocairo-1.0-0
libpangoft2-1.0-0
libpcap0.8
libpci3
libpcre2-16-0
libpcre2-8-0
libpcre3
libperl5.36
libpipeline1
libpixman-1-0
libpng16-16
libpopt0
libposix-strptime-perl
libproc2-0
libprotobuf32
libprotobuf-c1
libproxmox-acme-perl
libproxmox-acme-plugins
libproxmox-backup-qemu0
libproxmox-rs-perl
libpsl5
libpulse0
libpve-access-control
libpve-apiclient-perl
libpve-cluster-api-perl
libpve-cluster-perl
libpve-common-perl
libpve-guest-common-perl
libpve-http-server-perl
libpve-network-api-perl
libpve-network-perl
libpve-notify-perl
libpve-rs-perl
libpve-storage-perl
libpve-u2f-server-perl
libpython3.11-minimal
libpython3.11-stdlib
libpython3-stdlib
libqb100
libqrencode4
libqt5core5a
libqt5dbus5
libqt5network5
libquorum5
librabbitmq4
librados2
librados2-perl
libradosstriper1
librbd1
librdkafka1
librdmacm1
libreadline8
libregexp-ipv6-perl
librgw2
librrd8
librrds-perl
librtmp1
libsasl2-2
libsasl2-modules-db
libseccomp2
libselinux1
libsemanage2
libsemanage-common
libsepol2
libslang2
libslirp0
libsmartcols1
libsmbclient
libsnappy1v5
libsndfile1
libsocket6-perl
libspice-server1
libsqlite3-0
libss2
libssh2-1
libssl3
libstatgrab10
libstdc++6
libstring-shellquote-perl
libsubid4
libsystemd0
libsystemd-shared
libtalloc2
libtasn1-6
libtcmalloc-minimal4
libtdb1
libtemplate-perl
libterm-readline-gnu-perl
libtevent0
libtext-charwidth-perl
libtext-glob-perl
libtext-iconv-perl
libtext-wrapi18n-perl
libthai0
libthai-data
libthrift-0.17.0
libtimedate-perl
libtinfo6
libtirpc3
libtirpc-common
libtpms0
libtry-tiny-perl
libtypes-serialiser-perl
libu2f-server0
libuchardet0
libudev1
libunbound8
libunistring2
libunwind8
liburcu8
liburing2
liburi-perl
libusb-1.0-0
libusbredirparser1
libuuid1
libuuid-perl
libuutil3linux
libuv1
libva2
libva-drm2
libvirglrenderer1
libvorbis0a
libvorbisenc2
libvotequorum8
libvulkan1
libwayland-server0
libwbclient0
libwrap0
libwww-perl
libwww-robotrules-perl
libx11-6
libx11-data
libx11-xcb1
libxau6
libxcb1
libxcb-render0
libxcb-shm0
libxdmcp6
libxext6
libxml2
libxml-libxml-perl
libxml-namespacesupport-perl
libxml-parser-perl
libxml-sax-base-perl
libxml-sax-perl
libxml-twig-perl
libxrender1
libxslt1.1
libxtables12
libxxhash0
libyaml-0-2
libyaml-libyaml-perl
libyang3
libzfs4linux
libzpool5linux
libzstd1
linux-base
locales
login
logrotate
logsave
lsof
lua-lpeg
lvm2
lxcfs
lxc-pve
lzop
mailcap
man-db
manpages
mawk
media-types
memtest86+
mime-support
mokutil
mount
nano
ncurses-base
ncurses-bin
ncurses-term
netbase
netcat-traditional
nfs-common
nftables
nmap
nmap-common
novnc-pve
open-iscsi
openssh-client
openssh-server
openssh-sftp-server
openssl
passwd
pci.ids
pciutils
perl
perl-base
perl-modules-5.36
perl-openssl-defaults
pinentry-curses
postfix
procmail
procps
proxmox-archive-keyring
proxmox-backup-client
proxmox-backup-file-restore
proxmox-backup-restore-image
proxmox-default-kernel
proxmox-firewall
proxmox-grub
proxmox-kernel-6.8
proxmox-kernel-6.8.12-10-pve-signed
proxmox-kernel-6.8.12-9-pve-signed
proxmox-kernel-helper
proxmox-mail-forward
proxmox-mini-journalreader
proxmox-offline-mirror-docs
proxmox-offline-mirror-helper
proxmox-termproxy
proxmox-ve
proxmox-websocket-tunnel
proxmox-widget-toolkit
psmisc
pv
pve-cluster
pve-container
pve-docs
pve-edk2-firmware
pve-edk2-firmware-legacy
pve-edk2-firmware-ovmf
pve-esxi-import-tools
pve-firewall
pve-firmware
pve-ha-manager
pve-i18n
pve-lxc-syscalld
pve-manager
pve-qemu-kvm
pve-xtermjs
python3
python3.11
python3.11-minimal
python3.11-venv
python3-apt
python3-ceph-argparse
python3-ceph-common
python3-cephfs
python3-certifi
python3-chardet
python3-charset-normalizer
python3-debconf
python3-debian
python3-debianbts
python3-distutils
python3-httplib2
python3-idna
python3-jwt
python3-lib2to3
python3-minimal
python3-pip-whl
python3-pkg-resources
python3-prettytable
python3-protobuf
python3-pycurl
python3-pyparsing
python3-pysimplesoap
python3-pyvmomi
python3-rados
python3-rbd
python3-reportbug
python3-requests
python3-rgw
python3-setuptools
python3-setuptools-whl
python3-six
python3-systemd
python3-urllib3
python3-venv
python3-wcwidth
python3-yaml
python-apt-common
qemu-server
qrencode
readline-common
reportbug
rpcbind
rrdcached
rsync
runit-helper
samba-common
samba-libs
sed
sensible-utils
sgml-base
shared-mime-info
shim-helpers-amd64-signed
shim-signed
shim-signed-common
shim-unsigned
smartmontools
smbclient
socat
spiceterm
spl
sqlite3
ssh
ssl-cert
strace
swtpm
swtpm-libs
swtpm-tools
systemd
systemd-boot
systemd-boot-efi
systemd-sysv
sysvinit-utils
tar
tasksel
tasksel-data
tcpdump
thin-provisioning-tools
time
traceroute
tzdata
ucf
udev
uidmap
usbutils
usrmerge
util-linux
util-linux-extra
vim-common
vim-tiny
virtiofsd
vncterm
wamerican
wget
whiptail
xfsprogs
xkb-data
xsltproc
xz-utils
zfs-initramfs
zfsutils-linux
zfs-zed
zlib1g
zstd

View File

@@ -1,368 +0,0 @@
#!/bin/bash
# ==========================================================
# ProxMenux - A menu-driven script for Proxmox VE management
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.0
# Last Updated: 28/01/2025
# ==========================================================
# Description:
# This script allows users to assign physical disks to existing
# Proxmox virtual machines (VMs) through an interactive menu.
# - Detects the system disk and excludes it from selection.
# - Lists all available VMs for the user to choose from.
# - Identifies and displays unassigned physical disks.
# - Allows the user to select multiple disks and attach them to a VM.
# - Supports interface types: SATA, SCSI, VirtIO, and IDE.
# - Ensures that disks are not already assigned to active VMs.
# - Warns about disk sharing between multiple VMs to avoid data corruption.
# - Configures the selected disks for the VM and verifies the assignment.
#
# The goal of this script is to simplify the process of assigning
# physical disks to Proxmox VMs, reducing manual configurations
# and preventing potential errors.
# ==========================================================
# Configuration ============================================
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
show_proxmenux_logo
# ==========================================================
get_disk_info() {
local disk=$1
MODEL=$(lsblk -dn -o MODEL "$disk" | xargs)
SIZE=$(lsblk -dn -o SIZE "$disk" | xargs)
echo "$MODEL" "$SIZE"
}
VM_LIST=$(qm list | awk 'NR>1 {print $1, $2}')
if [ -z "$VM_LIST" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No VMs available in the system.")" 8 40
exit 1
fi
VMID=$(whiptail --title "$(translate "Select VM")" --menu "$(translate "Select the VM to which you want to add disks:")" 15 60 8 $VM_LIST 3>&1 1>&2 2>&3)
if [ -z "$VMID" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No VM was selected.")" 8 40
exit 1
fi
VMID=$(echo "$VMID" | tr -d '"')
msg_ok "$(translate "VM selected successfully.")"
VM_STATUS=$(qm status "$VMID" | awk '{print $2}')
if [ "$VM_STATUS" == "running" ]; then
whiptail --title "$(translate "Warning")" --msgbox "$(translate "The VM is powered on. Turn it off before adding disks.")" 12 60
exit 1
fi
##########################################
msg_info "$(translate "Detecting available disks...")"
USED_DISKS=$(lsblk -n -o PKNAME,TYPE | grep 'lvm' | awk '{print "/dev/" $1}')
MOUNTED_DISKS=$(lsblk -ln -o NAME,MOUNTPOINT | awk '$2!="" {print "/dev/" $1}')
ZFS_DISKS=""
ZFS_RAW=$(zpool list -v -H 2>/dev/null | awk '{print $1}' | grep -v '^NAME$' | grep -v '^-' | grep -v '^mirror')
for entry in $ZFS_RAW; do
path=""
if [[ "$entry" == wwn-* || "$entry" == ata-* ]]; then
if [ -e "/dev/disk/by-id/$entry" ]; then
path=$(readlink -f "/dev/disk/by-id/$entry")
fi
elif [[ "$entry" == /dev/* ]]; then
path="$entry"
fi
if [ -n "$path" ]; then
base_disk=$(lsblk -no PKNAME "$path" 2>/dev/null)
if [ -n "$base_disk" ]; then
ZFS_DISKS+="/dev/$base_disk"$'\n'
fi
fi
done
ZFS_DISKS=$(echo "$ZFS_DISKS" | sort -u)
is_disk_in_use() {
local disk="$1"
while read -r part fstype; do
case "$fstype" in
zfs_member|linux_raid_member)
return 0 ;;
esac
if echo "$MOUNTED_DISKS" | grep -q "/dev/$part"; then
return 0
fi
done < <(lsblk -ln -o NAME,FSTYPE "$disk" | tail -n +2)
if echo "$USED_DISKS" | grep -q "$disk" || echo "$ZFS_DISKS" | grep -q "$disk"; then
return 0
fi
return 1
}
FREE_DISKS=()
LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -n1 readlink -f | sort -u)
RAID_ACTIVE=$(grep -Po 'md\d+\s*:\s*active\s+raid[0-9]+' /proc/mdstat | awk '{print $1}' | sort -u)
while read -r DISK; do
[[ "$DISK" =~ /dev/zd ]] && continue
INFO=($(get_disk_info "$DISK"))
MODEL="${INFO[@]::${#INFO[@]}-1}"
SIZE="${INFO[-1]}"
LABEL=""
SHOW_DISK=true
IS_MOUNTED=false
IS_RAID=false
IS_ZFS=false
IS_LVM=false
while read -r part fstype; do
[[ "$fstype" == "zfs_member" ]] && IS_ZFS=true
[[ "$fstype" == "linux_raid_member" ]] && IS_RAID=true
[[ "$fstype" == "LVM2_member" ]] && IS_LVM=true
if grep -q "/dev/$part" <<< "$MOUNTED_DISKS"; then
IS_MOUNTED=true
fi
done < <(lsblk -ln -o NAME,FSTYPE "$DISK" | tail -n +2)
REAL_PATH=$(readlink -f "$DISK")
if echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then
IS_MOUNTED=true
fi
USED_BY=""
REAL_PATH=$(readlink -f "$DISK")
CONFIG_DATA=$(grep -vE '^\s*#' /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf 2>/dev/null)
if grep -Fq "$REAL_PATH" <<< "$CONFIG_DATA"; then
USED_BY="$(translate "In use")"
else
for SYMLINK in /dev/disk/by-id/*; do
if [[ "$(readlink -f "$SYMLINK")" == "$REAL_PATH" ]]; then
if grep -Fq "$SYMLINK" <<< "$CONFIG_DATA"; then
USED_BY="$(translate "In use")"
break
fi
fi
done
fi
if $IS_RAID && grep -q "$DISK" <<< "$(cat /proc/mdstat)"; then
if grep -q "active raid" /proc/mdstat; then
SHOW_DISK=false
fi
fi
if $IS_ZFS; then
SHOW_DISK=false
fi
if $IS_MOUNTED; then
SHOW_DISK=false
fi
if qm config "$VMID" | grep -vE '^\s*#|^description:' | grep -q "$DISK"; then
SHOW_DISK=false
fi
if $SHOW_DISK; then
[[ -n "$USED_BY" ]] && LABEL+=" [$USED_BY]"
[[ "$IS_RAID" == true ]] && LABEL+=" ⚠ RAID"
[[ "$IS_LVM" == true ]] && LABEL+=" ⚠ LVM"
[[ "$IS_ZFS" == true ]] && LABEL+=" ⚠ ZFS"
DESCRIPTION=$(printf "%-30s %10s%s" "$MODEL" "$SIZE" "$LABEL")
FREE_DISKS+=("$DISK" "$DESCRIPTION" "OFF")
fi
done < <(lsblk -dn -e 7,11 -o PATH)
if [ "${#FREE_DISKS[@]}" -eq 0 ]; then
cleanup
whiptail --title "$(translate "Error")" --msgbox "$(translate "No disks available for this VM.")" 8 40
clear
exit 1
fi
msg_ok "$(translate "Available disks detected.")"
######################################################
MAX_WIDTH=$(printf "%s\n" "${FREE_DISKS[@]}" | awk '{print length}' | sort -nr | head -n1)
TOTAL_WIDTH=$((MAX_WIDTH + 20))
if [ $TOTAL_WIDTH -lt 50 ]; then
TOTAL_WIDTH=50
fi
SELECTED=$(whiptail --title "$(translate "Select Disks")" --checklist \
"$(translate "Select the disks you want to add:")" 20 $TOTAL_WIDTH 10 "${FREE_DISKS[@]}" 3>&1 1>&2 2>&3)
if [ -z "$SELECTED" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No disks were selected.")" 10 64
clear
exit 1
fi
msg_ok "$(translate "Disks selected successfully.")"
INTERFACE=$(whiptail --title "$(translate "Interface Type")" --menu "$(translate "Select the interface type for all disks:")" 15 40 4 \
"sata" "$(translate "Add as SATA")" \
"scsi" "$(translate "Add as SCSI")" \
"virtio" "$(translate "Add as VirtIO")" \
"ide" "$(translate "Add as IDE")" 3>&1 1>&2 2>&3)
if [ -z "$INTERFACE" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No interface type was selected for the disks.")" 8 40
clear
exit 1
fi
msg_ok "$(translate "Interface type selected: $INTERFACE")"
DISKS_ADDED=0
ERROR_MESSAGES=""
SUCCESS_MESSAGES=""
msg_info "$(translate "Processing selected disks...")"
for DISK in $SELECTED; do
DISK=$(echo "$DISK" | tr -d '"')
DISK_INFO=$(get_disk_info "$DISK")
ASSIGNED_TO=""
RUNNING_VMS=""
RUNNING_CTS=""
while read -r VM_ID VM_NAME; do
if [[ "$VM_ID" =~ ^[0-9]+$ ]] && qm config "$VM_ID" | grep -q "$DISK"; then
ASSIGNED_TO+="VM $VM_ID $VM_NAME\n"
VM_STATUS=$(qm status "$VM_ID" | awk '{print $2}')
if [ "$VM_STATUS" == "running" ]; then
RUNNING_VMS+="VM $VM_ID $VM_NAME\n"
fi
fi
done < <(qm list | awk 'NR>1 {print $1, $2}')
while read -r CT_ID CT_NAME; do
if [[ "$CT_ID" =~ ^[0-9]+$ ]] && pct config "$CT_ID" | grep -q "$DISK"; then
ASSIGNED_TO+="CT $CT_ID $CT_NAME\n"
CT_STATUS=$(pct status "$CT_ID" | awk '{print $2}')
if [ "$CT_STATUS" == "running" ]; then
RUNNING_CTS+="CT $CT_ID $CT_NAME\n"
fi
fi
done < <(pct list | awk 'NR>1 {print $1, $2}')
if [ -n "$RUNNING_VMS" ] || [ -n "$RUNNING_CTS" ]; then
ERROR_MESSAGES+="$(translate "The disk") $DISK_INFO $(translate "is currently in use by the following running VM(s) or CT(s):")\\n$RUNNING_VMS$RUNNING_CTS\\n\\n$(translate "You cannot add this disk while the VM or CT is running.")\\n$(translate "Please shut it down first and run this script again to add the disk.")\\n\\n"
continue
fi
if [ -n "$ASSIGNED_TO" ]; then
cleanup
whiptail --title "$(translate "Disk Already Assigned")" --yesno "$(translate "The disk") $DISK_INFO $(translate "is already assigned to the following VM(s) or CT(s):")\\n$ASSIGNED_TO\\n\\n$(translate "Do you want to continue anyway?")" 15 70
if [ $? -ne 0 ]; then
sleep 1
exec "$0"
fi
fi
INDEX=0
while qm config "$VMID" | grep -q "${INTERFACE}${INDEX}"; do
((INDEX++))
done
RESULT=$(qm set "$VMID" -${INTERFACE}${INDEX} "$DISK" 2>&1)
if [ $? -eq 0 ]; then
MESSAGE="$(translate "The disk") $DISK_INFO $(translate "has been successfully added to VM") $VMID."
if [ -n "$ASSIGNED_TO" ]; then
MESSAGE+="\\n\\n$(translate "WARNING: This disk is also assigned to the following VM(s):")\\n$ASSIGNED_TO"
MESSAGE+="\\n$(translate "Make sure not to start VMs that share this disk at the same time to avoid data corruption.")"
fi
SUCCESS_MESSAGES+="$MESSAGE\\n\\n"
((DISKS_ADDED++))
else
ERROR_MESSAGES+="$(translate "Could not add disk") $DISK_INFO $(translate "to VM") $VMID.\\n$(translate "Error:") $RESULT\\n\\n"
fi
done
msg_ok "$(translate "Disk processing completed.")"
if [ -n "$SUCCESS_MESSAGES" ]; then
MSG_LINES=$(echo "$SUCCESS_MESSAGES" | wc -l)
whiptail --title "$(translate "Successful Operations")" --msgbox "$SUCCESS_MESSAGES" 16 70
fi
if [ -n "$ERROR_MESSAGES" ]; then
whiptail --title "$(translate "Warnings and Errors")" --msgbox "$ERROR_MESSAGES" 16 70
fi
exit 0

View File

@@ -1,537 +0,0 @@
#!/bin/bash
# ==========================================================
# ProxMenux - A menu-driven script for Proxmox VE management
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.0
# Last Updated: 28/01/2025
# ==========================================================
# Description:
# This script allows users to assign physical disks to existing
# Proxmox containers (CTs) through an interactive menu.
# - Detects the system disk and excludes it from selection.
# - Lists all available CTs for the user to choose from.
# - Identifies and displays unassigned physical disks.
# - Allows the user to select multiple disks and attach them to a CT.
# - Configures the selected disks for the CT and verifies the assignment.
# ==========================================================
# Configuration ============================================
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
get_disk_info() {
local disk=$1
MODEL=$(lsblk -dn -o MODEL "$disk" | xargs)
SIZE=$(lsblk -dn -o SIZE "$disk" | xargs)
echo "$MODEL" "$SIZE"
}
CT_LIST=$(pct list | awk 'NR>1 {print $1, $3}')
if [ -z "$CT_LIST" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No CTs available in the system.")" 8 40
exit 1
fi
CTID=$(whiptail --title "$(translate "Select CT")" --menu "$(translate "Select the CT to which you want to add disks:")" 15 60 8 $CT_LIST 3>&1 1>&2 2>&3)
if [ -z "$CTID" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No CT was selected.")" 8 40
exit 1
fi
CTID=$(echo "$CTID" | tr -d '"')
msg_ok "$(translate "CT selected successfully.")"
CT_STATUS=$(pct status "$CTID" | awk '{print $2}')
if [ "$CT_STATUS" != "running" ]; then
msg_info "$(translate "Starting CT") $CTID..."
pct start "$CTID"
sleep 2
if [ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]; then
msg_error "$(translate "Failed to start the CT.")"
exit 1
fi
msg_ok "$(translate "CT started successfully.")"
fi
CONF_FILE="/etc/pve/lxc/$CTID.conf"
if grep -q '^unprivileged: 1' "$CONF_FILE"; then
if whiptail --title "$(translate "Privileged Container")" \
--yesno "$(translate "The selected container is unprivileged. A privileged container is required for direct device passthrough.")\\n\\n$(translate "Do you want to convert it to a privileged container now?")" 12 70; then
msg_info "$(translate "Stopping container") $CTID..."
pct shutdown "$CTID" &
for i in {1..10}; do
sleep 1
if [ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]; then
break
fi
done
if [ "$(pct status "$CTID" | awk '{print $2}')" == "running" ]; then
msg_error "$(translate "Failed to stop the container.")"
exit 1
fi
msg_ok "$(translate "Container stopped.")"
cp "$CONF_FILE" "$CONF_FILE.bak"
sed -i '/^unprivileged: 1/d' "$CONF_FILE"
echo "unprivileged: 0" >> "$CONF_FILE"
msg_ok "$(translate "Container successfully converted to privileged.")"
msg_info "$(translate "Starting container") $CTID..."
pct start "$CTID"
sleep 2
if [ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]; then
msg_error "$(translate "Failed to start the container.")"
exit 1
fi
msg_ok "$(translate "Container started successfully.")"
else
whiptail --title "$(translate "Aborted")" \
--msgbox "$(translate "Operation cancelled. Cannot continue with an unprivileged container.")" 10 60
exit 1
fi
fi
##########################################
msg_info "$(translate "Detecting available disks...")"
USED_DISKS=$(lsblk -n -o PKNAME,TYPE | grep 'lvm' | awk '{print "/dev/" $1}')
MOUNTED_DISKS=$(lsblk -ln -o NAME,MOUNTPOINT | awk '$2!="" {print "/dev/" $1}')
ZFS_DISKS=""
ZFS_RAW=$(zpool list -v -H 2>/dev/null | awk '{print $1}' | grep -v '^NAME$' | grep -v '^-' | grep -v '^mirror')
for entry in $ZFS_RAW; do
path=""
if [[ "$entry" == wwn-* || "$entry" == ata-* ]]; then
if [ -e "/dev/disk/by-id/$entry" ]; then
path=$(readlink -f "/dev/disk/by-id/$entry")
fi
elif [[ "$entry" == /dev/* ]]; then
path="$entry"
fi
if [ -n "$path" ]; then
base_disk=$(lsblk -no PKNAME "$path" 2>/dev/null)
if [ -n "$base_disk" ]; then
ZFS_DISKS+="/dev/$base_disk"$'\n'
fi
fi
done
ZFS_DISKS=$(echo "$ZFS_DISKS" | sort -u)
is_disk_in_use() {
local disk="$1"
while read -r part fstype; do
case "$fstype" in
zfs_member|linux_raid_member)
return 0 ;;
esac
if echo "$MOUNTED_DISKS" | grep -q "/dev/$part"; then
return 0
fi
done < <(lsblk -ln -o NAME,FSTYPE "$disk" | tail -n +2)
if echo "$USED_DISKS" | grep -q "$disk" || echo "$ZFS_DISKS" | grep -q "$disk"; then
return 0
fi
return 1
}
FREE_DISKS=()
LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -r -n1 readlink -f | sort -u)
if [[ -n "$LVM_DEVICES" ]] && echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then
IS_MOUNTED=true
fi
RAID_ACTIVE=$(grep -Po 'md\d+\s*:\s*active\s+raid[0-9]+' /proc/mdstat | awk '{print $1}' | sort -u)
while read -r DISK; do
[[ "$DISK" =~ /dev/zd ]] && continue
INFO=($(get_disk_info "$DISK"))
MODEL="${INFO[@]::${#INFO[@]}-1}"
SIZE="${INFO[-1]}"
LABEL=""
SHOW_DISK=true
IS_MOUNTED=false
IS_RAID=false
IS_ZFS=false
IS_LVM=false
while read -r part fstype; do
[[ "$fstype" == "zfs_member" ]] && IS_ZFS=true
[[ "$fstype" == "linux_raid_member" ]] && IS_RAID=true
[[ "$fstype" == "LVM2_member" ]] && IS_LVM=true
if grep -q "/dev/$part" <<< "$MOUNTED_DISKS"; then
IS_MOUNTED=true
fi
done < <(lsblk -ln -o NAME,FSTYPE "$DISK" | tail -n +2)
REAL_PATH=$(readlink -f "$DISK")
if echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then
IS_MOUNTED=true
fi
USED_BY=""
REAL_PATH=$(readlink -f "$DISK")
CONFIG_DATA=$(grep -vE '^\s*#' /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf 2>/dev/null)
if grep -Fq "$REAL_PATH" <<< "$CONFIG_DATA"; then
USED_BY="$(translate "In use")"
else
for SYMLINK in /dev/disk/by-id/*; do
if [[ "$(readlink -f "$SYMLINK")" == "$REAL_PATH" ]]; then
if grep -Fq "$SYMLINK" <<< "$CONFIG_DATA"; then
USED_BY="$(translate "In use")"
break
fi
fi
done
fi
if $IS_RAID && grep -q "$DISK" <<< "$(cat /proc/mdstat)"; then
if grep -q "active raid" /proc/mdstat; then
SHOW_DISK=false
fi
fi
if $IS_ZFS; then
SHOW_DISK=false
fi
if $IS_MOUNTED; then
SHOW_DISK=false
fi
if pct config "$CTID" | grep -vE '^\s*#|^description:' | grep -q "$DISK"; then
SHOW_DISK=false
fi
if $SHOW_DISK; then
[[ -n "$USED_BY" ]] && LABEL+=" [$USED_BY]"
[[ "$IS_RAID" == true ]] && LABEL+=" ⚠ RAID"
[[ "$IS_LVM" == true ]] && LABEL+=" ⚠ LVM"
[[ "$IS_ZFS" == true ]] && LABEL+=" ⚠ ZFS"
DESCRIPTION=$(printf "%-30s %10s%s" "$MODEL" "$SIZE" "$LABEL")
FREE_DISKS+=("$DISK" "$DESCRIPTION" "OFF")
fi
done < <(lsblk -dn -e 7,11 -o PATH)
if [ "${#FREE_DISKS[@]}" -eq 0 ]; then
cleanup
whiptail --title "$(translate "Error")" --msgbox "$(translate "No disks available for this CT.")" 8 40
clear
exit 1
fi
msg_ok "$(translate "Available disks detected.")"
######################################################
MAX_WIDTH=$(printf "%s\n" "${FREE_DISKS[@]}" | awk '{print length}' | sort -nr | head -n1)
TOTAL_WIDTH=$((MAX_WIDTH + 20))
if [ $TOTAL_WIDTH -lt 50 ]; then
TOTAL_WIDTH=50
fi
SELECTED=$(whiptail --title "$(translate "Select Disks")" --radiolist \
"$(translate "Select the disks you want to add:")" 20 $TOTAL_WIDTH 10 "${FREE_DISKS[@]}" 3>&1 1>&2 2>&3)
if [ -z "$SELECTED" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No disks were selected.")" 10 64
clear
exit 1
fi
msg_ok "$(translate "Disks selected successfully.")"
DISKS_ADDED=0
ERROR_MESSAGES=""
SUCCESS_MESSAGES=""
msg_info "$(translate "Processing selected disks...")"
for DISK in $SELECTED; do
DISK=$(echo "$DISK" | tr -d '"')
DISK_INFO=$(get_disk_info "$DISK")
ASSIGNED_TO=""
RUNNING_CTS=""
RUNNING_VMS=""
# Comprobar CTs
while read -r CT_ID CT_NAME; do
if [[ "$CT_ID" =~ ^[0-9]+$ ]] && pct config "$CT_ID" | grep -q "$DISK"; then
ASSIGNED_TO+="CT $CT_ID $CT_NAME\n"
CT_STATUS=$(pct status "$CT_ID" | awk '{print $2}')
if [ "$CT_STATUS" == "running" ]; then
RUNNING_CTS+="CT $CT_ID $CT_NAME\n"
fi
fi
done < <(pct list | awk 'NR>1 {print $1, $3}')
# Comprobar VMs
while read -r VM_ID VM_NAME; do
if [[ "$VM_ID" =~ ^[0-9]+$ ]] && qm config "$VM_ID" | grep -q "$DISK"; then
ASSIGNED_TO+="VM $VM_ID $VM_NAME\n"
VM_STATUS=$(qm status "$VM_ID" | awk '{print $2}')
if [ "$VM_STATUS" == "running" ]; then
RUNNING_VMS+="VM $VM_ID $VM_NAME\n"
fi
fi
done < <(qm list | awk 'NR>1 {print $1, $2}')
if [ -n "$RUNNING_CTS" ] || [ -n "$RUNNING_VMS" ]; then
ERROR_MESSAGES+="$(translate "The disk") $DISK_INFO $(translate "is in use by the following running VM(s) or CT(s):")\\n$RUNNING_CTS$RUNNING_VMS\\n\\n"
continue
fi
if [ -n "$ASSIGNED_TO" ]; then
cleanup
whiptail --title "$(translate "Disk Already Assigned")" --yesno "$(translate "The disk") $DISK_INFO $(translate "is already assigned to the following VM(s) or CT(s):")\\n$ASSIGNED_TO\\n\\n$(translate "Do you want to continue anyway?")" 15 70
if [ $? -ne 0 ]; then
sleep 1
exec "$0"
fi
fi
cleanup
if lsblk "$DISK" | grep -q "raid" || grep -q "${DISK##*/}" /proc/mdstat; then
whiptail --title "$(translate "RAID Detected")" --msgbox "$(translate "The disk") $DISK_INFO $(translate "appears to be part of a") RAID. $(translate "For security reasons, the system cannot format it.")\\n\\n$(translate "If you are sure you want to use it, please remove the") RAID metadata $(translate "or format it manually using external tools.")\\n\\n$(translate "After that, run this script again to add it.")" 18 70
exit
fi
MOUNT_POINT=$(whiptail --title "$(translate "Mount Point")" --inputbox "$(translate "Enter the mount point for the disk (e.g., /mnt/disk_passthrough):")" 10 60 "/mnt/disk_passthrough" 3>&1 1>&2 2>&3)
if [ -z "$MOUNT_POINT" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No mount point was specified.")" 8 40
continue
fi
msg_ok "$(translate "Mount point specified: $MOUNT_POINT")"
PARTITION=$(lsblk -rno NAME "$DISK" | awk -v disk="$(basename "$DISK")" '$1 != disk {print $1; exit}')
SKIP_FORMAT=false
if [ -n "$PARTITION" ]; then
PARTITION="/dev/$PARTITION"
CURRENT_FS=$(lsblk -no FSTYPE "$PARTITION" | xargs)
if [[ "$CURRENT_FS" == "ext4" || "$CURRENT_FS" == "xfs" || "$CURRENT_FS" == "btrfs" ]]; then
SKIP_FORMAT=true
msg_ok "$(translate "Detected existing filesystem") $CURRENT_FS $(translate "on") $PARTITION."
else
whiptail --title "$(translate "Unsupported Filesystem")" --yesno "$(translate "The partition") $PARTITION $(translate "has an unsupported filesystem ($CURRENT_FS).\\nDo you want to format it?")" 10 70
if [ $? -ne 0 ]; then
continue
fi
fi
else
CURRENT_FS=$(lsblk -no FSTYPE "$DISK" | xargs)
if [[ "$CURRENT_FS" == "ext4" || "$CURRENT_FS" == "xfs" || "$CURRENT_FS" == "btrfs" ]]; then
SKIP_FORMAT=true
PARTITION="$DISK"
msg_ok "$(translate "Detected filesystem") $CURRENT_FS $(translate "directly on disk") $DISK.)"
else
whiptail --title "$(translate "No Valid Partitions")" --yesno "$(translate "The disk has no partitions and no valid filesystem. Do you want to create a new partition and format it?")" 10 70
if [ $? -ne 0 ]; then
continue
fi
echo -e "$(translate "Creating partition table and partition...")"
parted -s "$DISK" mklabel gpt
parted -s "$DISK" mkpart primary 0% 100%
sleep 2
partprobe "$DISK"
sleep 2
PARTITION=$(lsblk -rno NAME "$DISK" | awk -v disk="$(basename "$DISK")" '$1 != disk {print $1; exit}')
if [ -n "$PARTITION" ]; then
PARTITION="/dev/$PARTITION"
else
whiptail --title "$(translate "Partition Error")" --msgbox "$(translate "Failed to create partition on disk") $DISK_INFO." 8 70
continue
fi
fi
fi
if [ "$SKIP_FORMAT" != true ]; then
CURRENT_FS=$(lsblk -no FSTYPE "$PARTITION" | xargs)
if [[ "$CURRENT_FS" == "ext4" || "$CURRENT_FS" == "xfs" || "$CURRENT_FS" == "btrfs" ]]; then
SKIP_FORMAT=true
msg_ok "$(translate "Detected existing filesystem") $CURRENT_FS $(translate "on") $PARTITION. $(translate "Skipping format.")"
else
FORMAT_TYPE=$(whiptail --title "$(translate "Select Format Type")" --menu "$(translate "Select the filesystem type for") $DISK_INFO:" 15 60 6 \
"ext4" "$(translate "Extended Filesystem 4 (recommended)")" \
"xfs" "$(translate "XFS Filesystem")" \
"btrfs" "$(translate "Btrfs Filesystem")" 3>&1 1>&2 2>&3)
if [ -z "$FORMAT_TYPE" ]; then
whiptail --title "$(translate "Format Cancelled")" --msgbox "$(translate "Format operation cancelled. The disk will not be added.")" 8 60
continue
fi
whiptail --title "$(translate "WARNING")" --yesno "$(translate "WARNING: This operation will FORMAT the disk") $DISK_INFO $(translate "with") $FORMAT_TYPE.\\n\\n$(translate "ALL DATA ON THIS DISK WILL BE PERMANENTLY LOST!")\\n\\n$(translate "Are you sure you want to continue")" 15 70
if [ $? -ne 0 ]; then
whiptail --title "$(translate "Format Cancelled")" --msgbox "$(translate "Format operation cancelled. The disk will not be added.")" 8 60
continue
fi
fi
fi
if [ "$SKIP_FORMAT" != true ]; then
echo -e "$(translate "Formatting partition") $PARTITION $(translate "with") $FORMAT_TYPE..."
case "$FORMAT_TYPE" in
"ext4") mkfs.ext4 -F "$PARTITION" ;;
"xfs") mkfs.xfs -f "$PARTITION" ;;
"btrfs") mkfs.btrfs -f "$PARTITION" ;;
esac
if [ $? -ne 0 ]; then
whiptail --title "$(translate "Format Failed")" --msgbox "$(translate "Failed to format partition") $PARTITION $(translate "with") $FORMAT_TYPE.\\n\\n$(translate "The disk may be in use by the system or have hardware issues.")" 12 70
continue
else
msg_ok "$(translate "Partition") $PARTITION $(translate "successfully formatted with") $FORMAT_TYPE."
partprobe "$DISK"
sleep 2
fi
fi
INDEX=0
while pct config "$CTID" | grep -q "mp${INDEX}:"; do
((INDEX++))
done
##############################################################################
RESULT=$(pct set "$CTID" -mp${INDEX} "$PARTITION,mp=$MOUNT_POINT,backup=0,ro=0,acl=1" 2>&1)
pct exec "$CTID" -- chmod -R 775 "$MOUNT_POINT"
##############################################################################
if [ $? -eq 0 ]; then
MESSAGE="$(translate "The disk") $DISK_INFO $(translate "has been successfully added to CT") $CTID $(translate "as a mount point at") $MOUNT_POINT."
if [ -n "$ASSIGNED_TO" ]; then
MESSAGE+="\\n\\n$(translate "WARNING: This disk is also assigned to the following CT(s):")\\n$ASSIGNED_TO"
MESSAGE+="\\n$(translate "Make sure not to start CTs that share this disk at the same time to avoid data corruption.")"
fi
SUCCESS_MESSAGES+="$MESSAGE\\n\\n"
((DISKS_ADDED++))
else
ERROR_MESSAGES+="$(translate "Could not add disk") $DISK_INFO $(translate "to CT") $CTID.\\n$(translate "Error:") $RESULT\\n\\n"
fi
done
msg_ok "$(translate "Disk processing completed.")"
if [ -n "$SUCCESS_MESSAGES" ]; then
MSG_LINES=$(echo "$SUCCESS_MESSAGES" | wc -l)
whiptail --title "$(translate "Successful Operations")" --msgbox "$SUCCESS_MESSAGES" 16 70
fi
if [ -n "$ERROR_MESSAGES" ]; then
whiptail --title "$(translate "Warnings and Errors")" --msgbox "$ERROR_MESSAGES" 16 70
fi
exit 0

View File

@@ -1,277 +0,0 @@
#!/bin/bash
# ==========================================================
# Remove Subscription Banner - Proxmox VE (v3 - Minimal Intrusive)
# ==========================================================
# This version makes a surgical change to the checked_command function
# by changing the condition to 'if (false)' and commenting out the banner logic.
# Also patches the mobile UI to remove the subscription dialog.
# ==========================================================
set -euo pipefail
# Source utilities if available
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# File paths
JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
MOBILE_UI_FILE="/usr/share/pve-yew-mobile-gui/index.html.tpl"
BACKUP_DIR="$BASE_DIR/backups"
APT_HOOK="/etc/apt/apt.conf.d/no-nag-script"
PATCH_BIN="/usr/local/bin/pve-remove-nag-v3.sh"
MARK="/* PROXMENUX_NAG_PATCH_V3 */"
MOBILE_MARK="<!-- PROXMENUX_MOBILE_NAG_PATCH -->"
# Ensure tools JSON exists
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
# Register tool in JSON
register_tool() {
command -v jq >/dev/null 2>&1 || return 0
local tool="$1" state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" \
> "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
# Verify JS file integrity
verify_js_integrity() {
local file="$1"
[ -f "$file" ] || return 1
[ -s "$file" ] || return 1
grep -Eq 'Ext|function|var|const|let' "$file" || return 1
if LC_ALL=C grep -qP '\x00' "$file" 2>/dev/null; then
return 1
fi
return 0
}
# Create timestamped backup
create_backup() {
local file="$1"
local timestamp
timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="$BACKUP_DIR/$(basename "$file").backup.$timestamp"
mkdir -p "$BACKUP_DIR"
if [ -f "$file" ]; then
rm -f "$BACKUP_DIR"/"$(basename "$file")".backup.* 2>/dev/null || true
cp -a "$file" "$backup_file"
echo "$backup_file"
fi
}
# Create the patch script that will be called by APT hook
create_patch_script() {
cat > "$PATCH_BIN" <<'EOFPATCH'
#!/usr/bin/env bash
# ==========================================================
# Proxmox Subscription Banner Patch (v3 - Minimal)
# ==========================================================
set -euo pipefail
JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
MOBILE_UI_FILE="/usr/share/pve-yew-mobile-gui/index.html.tpl"
BACKUP_DIR="/usr/local/share/proxmenux/backups"
MARK="/* PROXMENUX_NAG_PATCH_V3 */"
MOBILE_MARK="<!-- PROXMENUX_MOBILE_NAG_PATCH -->"
verify_js_integrity() {
local file="$1"
[ -f "$file" ] && [ -s "$file" ] && grep -Eq 'Ext|function' "$file" && ! LC_ALL=C grep -qP '\x00' "$file" 2>/dev/null
}
patch_checked_command() {
[ -f "$JS_FILE" ] || return 0
# Check if already patched
grep -q "$MARK" "$JS_FILE" && return 0
# Create backup
mkdir -p "$BACKUP_DIR"
local backup="$BACKUP_DIR/$(basename "$JS_FILE").backup.$(date +%Y%m%d_%H%M%S)"
cp -a "$JS_FILE" "$backup"
# Set trap to restore on error
trap "cp -a '$backup' '$JS_FILE' 2>/dev/null || true" ERR
# Add patch marker at the beginning
sed -i "1s|^|$MARK\n|" "$JS_FILE"
# Surgical patch: Change the condition in checked_command function
# This changes the if condition to 'if (false)' making the banner never show
if grep -q "res\.data\.status\.toLowerCase() !== 'active'" "$JS_FILE"; then
# Pattern for newer versions (8.4.5+)
sed -i "/checked_command: function/,/},$/s/res === null || res === undefined || !res || res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE"
elif grep -q "res\.data\.status !== 'Active'" "$JS_FILE"; then
# Pattern for older versions
sed -i "/checked_command: function/,/},$/s/res === null || res === undefined || !res || res\.data\.status !== 'Active'/false/g" "$JS_FILE"
fi
# Also handle the NoMoreNagging pattern if present
if grep -q "res\.data\.status\.toLowerCase() !== 'NoMoreNagging'" "$JS_FILE"; then
sed -i "/checked_command: function/,/},$/s/res === null || res === undefined || !res || res\.data\.status\.toLowerCase() !== 'NoMoreNagging'/false/g" "$JS_FILE"
fi
# Verify integrity after patch
if ! verify_js_integrity "$JS_FILE"; then
cp -a "$backup" "$JS_FILE"
return 1
fi
# Clean up generated files
rm -f "$MIN_JS_FILE" "$GZ_FILE" 2>/dev/null || true
find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/cache/nginx/ -type f -delete 2>/dev/null || true
trap - ERR
return 0
}
patch_mobile_ui() {
[ -f "$MOBILE_UI_FILE" ] || return 0
# Check if already patched
grep -q "$MOBILE_MARK" "$MOBILE_UI_FILE" && return 0
# Create backup
mkdir -p "$BACKUP_DIR"
local backup="$BACKUP_DIR/$(basename "$MOBILE_UI_FILE").backup.$(date +%Y%m%d_%H%M%S)"
cp -a "$MOBILE_UI_FILE" "$backup"
# Set trap to restore on error
trap "cp -a '$backup' '$MOBILE_UI_FILE' 2>/dev/null || true" ERR
# Insert the script before </head> tag
sed -i "/<\/head>/i\\
$MOBILE_MARK\\
<!-- Script to remove subscription banner from mobile UI -->\\
<script>\\
function removeNoSubDialog() {\\
const observer = new MutationObserver(() => {\\
const diag = document.querySelector('dialog[aria-label=\"No valid subscription\"]');\\
if (diag) {\\
diag.remove();\\
}\\
});\\
observer.observe(document.body, { childList: true, subtree: true });\\
}\\
window.addEventListener('load', () => {\\
setTimeout(removeNoSubDialog, 200);\\
});\\
</script>" "$MOBILE_UI_FILE"
trap - ERR
return 0
}
reload_services() {
systemctl is-active --quiet pveproxy 2>/dev/null && {
systemctl reload pveproxy 2>/dev/null || systemctl restart pveproxy 2>/dev/null || true
}
systemctl is-active --quiet nginx 2>/dev/null && {
systemctl reload nginx 2>/dev/null || true
}
systemctl is-active --quiet pvedaemon 2>/dev/null && {
systemctl reload pvedaemon 2>/dev/null || true
}
}
main() {
patch_checked_command || return 1
patch_mobile_ui || true
reload_services
}
main
EOFPATCH
chmod 755 "$PATCH_BIN"
}
# Create APT hook to reapply patch after updates
create_apt_hook() {
cat > "$APT_HOOK" <<'EOFAPT'
/* ProxMenux: reapply minimal nag patch after upgrades */
DPkg::Post-Invoke { "/usr/local/bin/pve-remove-nag-v3.sh || true"; };
EOFAPT
chmod 644 "$APT_HOOK"
# Verify APT hook syntax
apt-config dump >/dev/null 2>&1 || {
rm -f "$APT_HOOK"
}
}
# Main function to remove subscription banner
remove_subscription_banner_v3() {
local pve_version
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1 || echo "unknown")
msg_info "$(translate "Detected Proxmox VE") ${pve_version} - $(translate "applying banner patch")"
# Remove old APT hooks
for f in /etc/apt/apt.conf.d/*nag*; do
[[ -e "$f" ]] && rm -f "$f"
done
# Create backup for desktop UI
local backup_file
backup_file=$(create_backup "$JS_FILE")
if [ -n "$backup_file" ]; then
# msg_ok "$(translate "Desktop UI backup created"): $backup_file"
:
fi
if [ -f "$MOBILE_UI_FILE" ]; then
local mobile_backup
mobile_backup=$(create_backup "$MOBILE_UI_FILE")
if [ -n "$mobile_backup" ]; then
# msg_ok "$(translate "Mobile UI backup created"): $mobile_backup"
:
fi
fi
# Create patch script and APT hook
create_patch_script
create_apt_hook
# Apply the patch
if ! "$PATCH_BIN"; then
msg_error "$(translate "Error applying patch. Backups preserved at"): $BACKUP_DIR"
return 1
fi
# Register tool as applied
register_tool "subscription_banner" true
msg_ok "$(translate "Subscription banner removed successfully")"
msg_ok "$(translate "Desktop and Mobile UI patched")"
msg_ok "$(translate "Refresh your browser (Ctrl+Shift+R) to see changes")"
}
# Run if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
remove_subscription_banner_v3
fi

View File

@@ -1,124 +0,0 @@
#!/bin/bash
# ==========================================================
# Remove Subscription Banner - Proxmox VE 9.x
# ==========================================================
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
register_tool() {
local tool="$1"
local state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
remove_subscription_banner_pve9() {
local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
local MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script"
local pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1)
local pve_major=$(echo "$pve_version" | cut -d. -f1)
if [ "$pve_major" -lt 9 ] 2>/dev/null; then
msg_error "This script is for PVE 9.x only. Detected PVE $pve_version"
return 1
fi
msg_info "Detected Proxmox VE $pve_version - Applying PVE 9.x patches"
if [ ! -f "$JS_FILE" ]; then
msg_error "JavaScript file not found: $JS_FILE"
return 1
fi
local backup_file="${JS_FILE}.backup.pve9.$(date +%Y%m%d_%H%M%S)"
cp "$JS_FILE" "$backup_file"
for f in /etc/apt/apt.conf.d/*nag*; do
[[ -e "$f" ]] && rm -f "$f"
done
[[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE"
[[ -f "$MIN_JS_FILE" ]] && rm -f "$MIN_JS_FILE"
find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/cache/nginx/ -type f -delete 2>/dev/null || true
sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE"
sed -i "s/subscriptionActive: ''/subscriptionActive: true/g" "$JS_FILE"
sed -i "s/title: gettext('No valid subscription')/title: gettext('Community Edition')/g" "$JS_FILE"
sed -i "s/You do not have a valid subscription for this server/Community Edition - No subscription required/g" "$JS_FILE"
sed -i "s/Enterprise repository needs valid subscription/Enterprise repository configured/g" "$JS_FILE"
sed -i "s/icon: Ext\.Msg\.WARNING/icon: Ext.Msg.INFO/g" "$JS_FILE"
sed -i "s/subscription = !(/subscription = false \&\& (/g" "$JS_FILE"
if grep -q "res\.data\.status\.toLowerCase() !== 'active'" "$JS_FILE"; then
msg_warn "Some patches may not have applied correctly, retrying..."
sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE"
fi
[[ -f "$APT_HOOK" ]] && rm -f "$APT_HOOK"
cat > "$APT_HOOK" << 'EOF'
DPkg::Post-Invoke {
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/res\\.data\\.status\\.toLowerCase() !== '\''active'\''/false/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscriptionActive: '\'\'\''/subscriptionActive: true/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/title: gettext('\''No valid subscription'\'')/title: gettext('\''Community Edition'\'')/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscription = !(/subscription = false \\&\\& (/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz || true";
};
EOF
chmod 644 "$APT_HOOK"
if ! apt-config dump >/dev/null 2>&1; then
msg_warn "APT hook has syntax issues, removing..."
rm -f "$APT_HOOK"
else
msg_ok "APT hook created successfully"
fi
systemctl reload nginx 2>/dev/null || true
msg_ok "Subscription banner removed successfully for Proxmox VE $pve_version"
msg_ok "Banner removal process completed - refresh your browser to see changes"
register_tool "subscription_banner" true
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
remove_subscription_banner_pve9
fi

View File

@@ -1,119 +0,0 @@
#!/bin/bash
# ==========================================================
# Remove Subscription Banner - Proxmox VE 9.x ONLY
# ==========================================================
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# Tool registration system
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
register_tool() {
local tool="$1"
local state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
remove_subscription_banner_pve9() {
local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
local MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script"
# Verify PVE 9.x
local pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1)
local pve_major=$(echo "$pve_version" | cut -d. -f1)
if [ "$pve_major" -lt 9 ] 2>/dev/null; then
msg_error "This script is for PVE 9.x only. Detected PVE $pve_version"
return 1
fi
msg_info "Detected Proxmox VE $pve_version - Applying PVE 9.x patches"
# Verify that the file exists
if [ ! -f "$JS_FILE" ]; then
msg_error "JavaScript file not found: $JS_FILE"
return 1
fi
# Create backup of original file
local backup_file="${JS_FILE}.backup.pve9.$(date +%Y%m%d_%H%M%S)"
cp "$JS_FILE" "$backup_file"
# Clean any existing problematic APT hooks
for f in /etc/apt/apt.conf.d/*nag*; do
[[ -e "$f" ]] && rm -f "$f"
done
# Main subscription check patches for PVE 9
sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE"
sed -i "s/subscriptionActive: ''/subscriptionActive: true/g" "$JS_FILE"
sed -i "s/title: gettext('No valid subscription')/title: gettext('Community Edition')/g" "$JS_FILE"
# Additional UX improvements for PVE 9
sed -i "s/You do not have a valid subscription for this server/Community Edition - No subscription required/g" "$JS_FILE"
sed -i "s/Enterprise repository needs valid subscription/Enterprise repository configured/g" "$JS_FILE"
sed -i "s/icon: Ext\.Msg\.WARNING/icon: Ext.Msg.INFO/g" "$JS_FILE"
# Additional subscription patterns that may exist in PVE 9
sed -i "s/subscription = !(/subscription = false \&\& (/g" "$JS_FILE"
# Remove compressed/minified files to force regeneration
[[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE"
[[ -f "$MIN_JS_FILE" ]] && rm -f "$MIN_JS_FILE"
# Clear various caches
find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
# Create PVE 9.x specific APT hook
[[ -f "$APT_HOOK" ]] && rm -f "$APT_HOOK"
cat > "$APT_HOOK" << 'EOF'
DPkg::Post-Invoke {
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/res\\.data\\.status\\.toLowerCase() !== '\''active'\''/false/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscriptionActive: '\'\'\''/subscriptionActive: true/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/title: gettext('\''No valid subscription'\'')/title: gettext('\''Community Edition'\'')/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscription = !(/subscription = false \\&\\& (/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz || true";
};
EOF
chmod 644 "$APT_HOOK"
# Verify APT hook syntax
if ! apt-config dump >/dev/null 2>&1; then
msg_warn "APT hook has syntax issues, removing..."
rm -f "$APT_HOOK"
else
msg_ok "APT hook created successfully"
fi
msg_ok "Subscription banner removed successfully for Proxmox VE $pve_version"
msg_ok "Banner removal process completed"
register_tool "subscription_banner" true
}
# Execute function if called directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
remove_subscription_banner_pve9
fi

View File

@@ -1,257 +0,0 @@
#!/bin/bash
# ==========================================================
# Remove Subscription Banner - Proxmox VE 9.x (Clean Version)
# ==========================================================
set -euo pipefail
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
register_tool() {
command -v jq >/dev/null 2>&1 || return 0
local tool="$1" state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" \
> "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
MOBILE_TPL="/usr/share/pve-yew-mobile-gui/index.html.tpl"
APT_HOOK="/etc/apt/apt.conf.d/no-nag-script"
PATCH_BIN="/usr/local/bin/pve-remove-nag.sh"
MARK_JS="PROXMENUX_NAG_REMOVED_v2"
MARK_MOBILE="<!-- PROXMENUX: MOBILE NAG PATCH v2 -->"
verify_js_integrity() {
local file="$1"
[ -f "$file" ] || return 1
[ -s "$file" ] || return 1
grep -Eq 'Ext|function|var|const|let' "$file" || return 1
if LC_ALL=C grep -qP '\x00' "$file" 2>/dev/null; then
return 1
fi
return 0
}
create_backup() {
local file="$1"
local backup_dir="$BASE_DIR/backups"
local timestamp
timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="$backup_dir/$(basename "$file").backup.$timestamp"
mkdir -p "$backup_dir"
if [ -f "$file" ]; then
cp -a "$file" "$backup_file"
ls -t "$backup_dir"/"$(basename "$file")".backup.* 2>/dev/null | tail -n +6 | xargs -r rm -f 2>/dev/null || true
echo "$backup_file"
fi
}
# ----------------------------------------------------
create_patch_script() {
cat > "$PATCH_BIN" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
MOBILE_TPL="/usr/share/pve-yew-mobile-gui/index.html.tpl"
MARK_JS="PROXMENUX_NAG_REMOVED_v2"
MARK_MOBILE="<!-- PROXMENUX: MOBILE NAG PATCH v2 -->"
BASE_DIR="/usr/local/share/proxmenux"
verify_js_integrity() {
local file="$1"
[ -f "$file" ] && [ -s "$file" ] && grep -Eq 'Ext|function' "$file" && ! LC_ALL=C grep -qP '\x00' "$file" 2>/dev/null
}
patch_web() {
[ -f "$JS_FILE" ] || return 0
grep -q "$MARK_JS" "$JS_FILE" && return 0
local backup_dir="$BASE_DIR/backups"
mkdir -p "$backup_dir"
local backup="$backup_dir/$(basename "$JS_FILE").backup.$(date +%Y%m%d_%H%M%S)"
cp -a "$JS_FILE" "$backup"
trap "cp -a '$backup' '$JS_FILE' 2>/dev/null || true" ERR
sed -i '1s|^|/* '"$MARK_JS"' */\n|' "$JS_FILE"
local patterns_found=0
if grep -q "res\.data\.status\.toLowerCase() !== 'active'" "$JS_FILE"; then
sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE"
patterns_found=$((patterns_found + 1))
fi
if grep -q "subscriptionActive: ''" "$JS_FILE"; then
sed -i "s/subscriptionActive: ''/subscriptionActive: true/g" "$JS_FILE"
patterns_found=$((patterns_found + 1))
fi
if grep -q "title: gettext('No valid subscription')" "$JS_FILE"; then
sed -i "s/title: gettext('No valid subscription')/title: gettext('Community Edition')/g" "$JS_FILE"
patterns_found=$((patterns_found + 1))
fi
if grep -q "icon: Ext\.Msg\.WARNING" "$JS_FILE"; then
sed -i "s/icon: Ext\.Msg\.WARNING/icon: Ext.Msg.INFO/g" "$JS_FILE"
patterns_found=$((patterns_found + 1))
fi
if grep -q "subscription = !(" "$JS_FILE"; then
sed -i "s/subscription = !(/subscription = false \&\& (/g" "$JS_FILE"
patterns_found=$((patterns_found + 1))
fi
# Si nada coincidió (cambio upstream), restaura y sal limpio
if [ "${patterns_found:-0}" -eq 0 ]; then
cp -a "$backup" "$JS_FILE"
return 0
fi
# Verificación final
if ! verify_js_integrity "$JS_FILE"; then
cp -a "$backup" "$JS_FILE"
return 1
fi
# Limpiar artefactos/cachés
rm -f "$MIN_JS_FILE" "$GZ_FILE" 2>/dev/null || true
find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/cache/nginx/ -type f -delete 2>/dev/null || true
trap - ERR
}
patch_mobile() {
[ -f "$MOBILE_TPL" ] || return 0
grep -q "$MARK_MOBILE" "$MOBILE_TPL" && return 0
local backup_dir="$BASE_DIR/backups"
mkdir -p "$backup_dir"
cp -a "$MOBILE_TPL" "$backup_dir/$(basename "$MOBILE_TPL").backup.$(date +%Y%m%d_%H%M%S)"
cat >> "$MOBILE_TPL" <<EOM
$MARK_MOBILE
<script>
(function() {
'use strict';
function removeSubscriptionElements() {
try {
const dialogs = document.querySelectorAll('dialog.pwt-outer-dialog');
dialogs.forEach(d => {
const text = (d.textContent || '').toLowerCase();
if (text.includes('subscription') || text.includes('no valid')) { d.remove(); }
});
const cards = document.querySelectorAll('.pwt-card.pwt-p-2.pwt-d-flex.pwt-interactive.pwt-justify-content-center');
cards.forEach(c => {
const text = (c.textContent || '').toLowerCase();
const hasButton = c.querySelector('button');
if (!hasButton && (text.includes('subscription') || text.includes('no valid'))) { c.remove(); }
});
const alerts = document.querySelectorAll('[class*="alert"], [class*="warning"], [class*="notice"]');
alerts.forEach(a => {
const text = (a.textContent || '').toLowerCase();
if (text.includes('subscription') || text.includes('no valid')) { a.remove(); }
});
} catch (e) { console.warn('Error removing subscription elements:', e); }
}
if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', removeSubscriptionElements); }
else { removeSubscriptionElements(); }
const observer = new MutationObserver(removeSubscriptionElements);
if (document.body) {
observer.observe(document.body, { childList: true, subtree: true });
const interval = setInterval(removeSubscriptionElements, 500);
setTimeout(() => { try { observer.disconnect(); clearInterval(interval); } catch(e){} }, 30000);
}
})();
</script>
EOM
}
reload_services() {
systemctl is-active --quiet pveproxy 2>/dev/null && {
systemctl reload pveproxy 2>/dev/null || systemctl restart pveproxy 2>/dev/null || true
}
systemctl is-active --quiet nginx 2>/dev/null && {
systemctl reload nginx 2>/dev/null || true
}
systemctl is-active --quiet pvedaemon 2>/dev/null && {
systemctl reload pvedaemon 2>/dev/null || true
}
find /var/cache/pve-manager/ -type f -delete 2>/dev/null || true
find /var/lib/pve-manager/ -type f -delete 2>/dev/null || true
}
main() {
patch_web || return 1
patch_mobile
reload_services
}
main
EOF
chmod 755 "$PATCH_BIN"
}
# ----------------------------------------------------
create_apt_hook() {
cat > "$APT_HOOK" <<'EOF'
/* ProxMenux: reapply nag patch after upgrades */
DPkg::Post-Invoke { "/usr/local/bin/pve-remove-nag.sh || true"; };
EOF
chmod 644 "$APT_HOOK"
apt-config dump >/dev/null 2>&1 || { msg_warn "APT hook syntax issue"; rm -f "$APT_HOOK"; }
}
remove_subscription_banner_pve9() {
local pve_version
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1 || true)
local pve_major="${pve_version%%.*}"
msg_info "$(translate "Detected Proxmox VE ${pve_version:-9.x} removing subscription banner")"
create_patch_script
create_apt_hook
if ! "$PATCH_BIN"; then
msg_error "$(translate "Error applying patches")"
return 1
fi
register_tool "subscription_banner" true
msg_ok "$(translate "Subscription banner removed successfully.")"
msg_ok "$(translate "Refresh your browser to see changes.")"
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
remove_subscription_banner_pve9
fi

View File

@@ -1,339 +0,0 @@
#!/bin/bash
# ==========================================================
# Proxmox VE Update Script
# ==========================================================
# Configuration
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
register_tool() {
local tool="$1"
local state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
download_common_functions() {
if ! source "$LOCAL_SCRIPTS/global/common-functions.sh"; then
return 1
fi
}
update_pve9() {
local pve_version=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1)
local start_time=$(date +%s)
local log_file="/var/log/proxmox-update-$(date +%Y%m%d-%H%M%S).log"
local changes_made=false
local OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs)"
local TARGET_CODENAME="trixie"
if [ -z "$OS_CODENAME" ]; then
OS_CODENAME=$(lsb_release -cs 2>/dev/null || echo "trixie")
fi
download_common_functions
msg_info2 "$(translate "Detected: Proxmox VE $pve_version (Current: $OS_CODENAME, Target: $TARGET_CODENAME)")"
echo -e
local available_space=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}')
if [ "$available_space" -lt 1024 ]; then
msg_error "$(translate "Insufficient disk space. Available: ${available_space}MB")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
if ! ping -c 1 download.proxmox.com >/dev/null 2>&1; then
msg_error "$(translate "Cannot reach Proxmox repositories")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
disable_sources_repo() {
local file="$1"
if [[ -f "$file" ]]; then
sed -i ':a;/^\n*$/{$d;N;ba}' "$file"
if grep -q "^Enabled:" "$file"; then
sed -i 's/^Enabled:.*$/Enabled: false/' "$file"
else
echo "Enabled: false" >> "$file"
fi
if ! grep -q "^Types: " "$file"; then
msg_warn "$(translate "Malformed .sources file detected, removing: $(basename "$file")")"
rm -f "$file"
fi
return 0
fi
return 1
}
if disable_sources_repo "/etc/apt/sources.list.d/pve-enterprise.sources"; then
msg_ok "$(translate "Enterprise Proxmox repository disabled")"
changes_made=true
fi
if disable_sources_repo "/etc/apt/sources.list.d/ceph.sources"; then
msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")"
changes_made=true
fi
for legacy_file in /etc/apt/sources.list.d/pve-public-repo.list \
/etc/apt/sources.list.d/pve-install-repo.list \
/etc/apt/sources.list.d/debian.list; do
if [[ -f "$legacy_file" ]]; then
rm -f "$legacy_file"
msg_ok "$(translate "Removed legacy repository: $(basename "$legacy_file")")"
fi
done
if [[ -f /etc/apt/sources.list.d/debian.sources ]]; then
rm -f /etc/apt/sources.list.d/debian.sources
msg_ok "$(translate "Old debian.sources file removed to prevent duplication")"
fi
msg_info "$(translate "Creating Proxmox VE 9.x no-subscription repository...")"
cat > /etc/apt/sources.list.d/proxmox.sources << EOF
Enabled: true
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: ${TARGET_CODENAME}
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
msg_ok "$(translate "Proxmox VE 9.x no-subscription repository created")"
changes_made=true
msg_info "$(translate "Creating Debian ${TARGET_CODENAME} sources file...")"
cat > /etc/apt/sources.list.d/debian.sources << EOF
Types: deb
URIs: http://deb.debian.org/debian/
Suites: ${TARGET_CODENAME} ${TARGET_CODENAME}-updates
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security/
Suites: ${TARGET_CODENAME}-security
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
EOF
msg_ok "$(translate "Debian repositories configured for $TARGET_CODENAME")"
local firmware_conf="/etc/apt/apt.conf.d/no-firmware-warnings.conf"
if [ ! -f "$firmware_conf" ]; then
msg_info "$(translate "Disabling non-free firmware warnings...")"
echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > "$firmware_conf"
msg_ok "$(translate "Non-free firmware warnings disabled")"
fi
update_output=$(apt-get update 2>&1)
update_exit_code=$?
if [ $update_exit_code -eq 0 ]; then
msg_ok "$(translate "Package lists updated successfully")"
else
if echo "$update_output" | grep -q "NO_PUBKEY\|GPG error"; then
msg_info "$(translate "Fixing GPG key issues...")"
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $(echo "$update_output" | grep "NO_PUBKEY" | sed 's/.*NO_PUBKEY //' | head -1) 2>/dev/null
if apt-get update > "$log_file" 2>&1; then
msg_ok "$(translate "Package lists updated after GPG fix")"
else
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
return 1
fi
elif echo "$update_output" | grep -q "404\|Failed to fetch"; then
msg_warn "$(translate "Some repositories are not available, continuing with available ones...")"
else
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
echo "Error details: $update_output"
return 1
fi
fi
if apt policy 2>/dev/null | grep -q "${TARGET_CODENAME}.*pve-no-subscription"; then
msg_ok "$(translate "Proxmox VE 9.x repositories verified")"
else
msg_warn "$(translate "Proxmox VE 9.x repositories verification inconclusive, continuing...")"
fi
local current_pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
local available_pve_version=$(apt-cache policy pve-manager 2>/dev/null | grep -oP 'Candidate: \K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
local upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable")
local security_updates=$(apt list --upgradable 2>/dev/null | grep -c "security")
show_update_menu() {
local current_version="$1"
local target_version="$2"
local upgradable_count="$3"
local security_count="$4"
local menu_text="$(translate "System Update Information")\n\n"
menu_text+="$(translate "Current PVE Version"): $current_version\n"
if [ -n "$target_version" ] && [ "$target_version" != "$current_version" ]; then
menu_text+="$(translate "Available PVE Version"): $target_version\n"
fi
menu_text+="\n$(translate "Package Updates Available"): $upgradable_count\n"
menu_text+="$(translate "Security Updates"): $security_count\n\n"
if [ "$upgradable_count" -eq 0 ]; then
menu_text+="$(translate "System is already up to date")"
whiptail --title "$(translate "Update Status")" --msgbox "$menu_text" 15 70
return 2
else
menu_text+="$(translate "Do you want to proceed with the system update?")"
if whiptail --title "$(translate "Proxmox Update")" --yesno "$menu_text" 18 70; then
return 0
else
return 1
fi
fi
}
show_update_menu "$current_pve_version" "$available_pve_version" "$upgradable" "$security_updates"
MENU_RESULT=$?
if [[ $MENU_RESULT -eq 1 ]]; then
msg_info2 "$(translate "Update cancelled by user")"
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
return 0
elif [[ $MENU_RESULT -eq 2 ]]; then
msg_ok "$(translate "System is already up to date. No update needed.")"
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
return 0
fi
msg_info "$(translate "Cleaning up unused time synchronization services...")"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' purge ntp openntpd systemd-timesyncd > /dev/null 2>&1; then
msg_ok "$(translate "Old time services removed successfully")"
else
msg_warn "$(translate "Some old time services could not be removed (not installed)")"
fi
msg_info "$(translate "Updating packages...")"
apt-get install pv -y > /dev/null 2>&1
msg_ok "$(translate "Packages updated successfully")"
tput sc
DEBIAN_FRONTEND=noninteractive apt-get -y \
-o Dpkg::Options::='--force-confdef' \
-o Dpkg::Options::='--force-confold' \
dist-upgrade 2>&1 | while IFS= read -r line; do
echo "$line" >> "$log_file"
if [[ "$line" =~ \[[#=\-]+\]\ *[0-9]{1,3}% ]]; then
continue
fi
if [[ "$line" =~ ^(Setting\ up|Unpacking|Preparing\ to\ unpack|Processing\ triggers\ for) ]]; then
package_name=$(echo "$line" | sed -E 's/.*(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ([^ :]+).*/\2/')
[ -z "$package_name" ] && package_name="$(translate "Unknown")"
row=$(( $(tput lines) - 6 ))
tput cup $row 0; printf "%s\n" "$(translate "Installing packages...")"
tput cup $((row + 1)) 0; printf "%s\n" "──────────────────────────────────────────────"
tput cup $((row + 2)) 0; printf "%s %s\n" "$(translate "Package:")" "$package_name"
tput cup $((row + 3)) 0; printf "%s\n" "Progress: [ ] 0%"
tput cup $((row + 4)) 0; printf "%s\n" "──────────────────────────────────────────────"
for i in $(seq 1 10); do
sleep 0.1
progress=$((i * 10))
tput cup $((row + 3)) 9
printf "[%-50s] %3d%%" "$(printf "#%.0s" $(seq 1 $((progress/2))))" "$progress"
done
fi
done
tput rc
tput ed
upgrade_exit_code=${PIPESTATUS[0]}
if [ $upgrade_exit_code -eq 0 ]; then
msg_ok "$(translate "System upgrade completed successfully")"
else
msg_error "$(translate "System upgrade failed. Check log: $log_file")"
return 1
fi
msg_info "$(translate "Installing essential Proxmox packages...")"
local additional_packages="zfsutils-linux proxmox-backup-restore-image chrony"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install $additional_packages >> "$log_file" 2>&1; then
msg_ok "$(translate "Essential Proxmox packages installed")"
else
msg_warn "$(translate "Some essential Proxmox packages may not have been installed")"
fi
lvm_repair_check
cleanup_duplicate_repos
#msg_info "$(translate "Performing system cleanup...")"
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
msg_ok "$(translate "Cleanup finished")"
local end_time=$(date +%s)
local duration=$((end_time - start_time))
local minutes=$((duration / 60))
local seconds=$((duration % 60))
echo -e "${TAB}${BGN}$(translate "====== PVE UPDATE COMPLETED ======")${CL}"
echo -e "${TAB}${GN}⏱️ $(translate "Duration")${CL}: ${BL}${minutes}m ${seconds}s${CL}"
echo -e "${TAB}${GN}📄 $(translate "Log file")${CL}: ${BL}$log_file${CL}"
echo -e "${TAB}${GN}📦 $(translate "Packages upgraded")${CL}: ${BL}$upgradable${CL}"
echo -e "${TAB}${GN}🖥️ $(translate "Proxmox VE")${CL}: ${BL}$target_version (Debian $OS_CODENAME)${CL}"
msg_ok "$(translate "Proxmox VE 9.x configuration completed.")"
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
update_pve9
fi

View File

@@ -1,304 +0,0 @@
#!/bin/bash
# ==========================================================
# Proxmox VE Update Script - Improved Version
# ==========================================================
# Configuration
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
register_tool() {
local tool="$1"
local state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
download_common_functions() {
if ! source <(curl -s "$REPO_URL/scripts/global/common-functions.sh"); then
return 1
fi
}
update_pve9() {
local pve_version=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1)
local start_time=$(date +%s)
local log_file="/var/log/proxmox-update-$(date +%Y%m%d-%H%M%S).log"
local changes_made=false
local OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs)"
local TARGET_CODENAME="trixie"
local screen_capture="/tmp/proxmenux_screen_capture_$$.txt"
if [ -z "$OS_CODENAME" ]; then
OS_CODENAME=$(lsb_release -cs 2>/dev/null || echo "trixie")
fi
download_common_functions
{
msg_info2 "$(translate "Detected: Proxmox VE $pve_version (Current: $OS_CODENAME, Target: $TARGET_CODENAME)")"
} | tee -a "$screen_capture"
local available_space=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}')
if [ "$available_space" -lt 1024 ]; then
msg_error "$(translate "Insufficient disk space. Available: ${available_space}MB")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
if ! ping -c 1 download.proxmox.com >/dev/null 2>&1; then
msg_error "$(translate "Cannot reach Proxmox repositories")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
disable_sources_repo() {
local file="$1"
if [[ -f "$file" ]]; then
sed -i ':a;/^\n*$/{$d;N;ba}' "$file"
if grep -q "^Enabled:" "$file"; then
sed -i 's/^Enabled:.*$/Enabled: false/' "$file"
else
echo "Enabled: false" >> "$file"
fi
if ! grep -q "^Types: " "$file"; then
msg_warn "$(translate "Malformed .sources file detected, removing: $(basename "$file")")"
rm -f "$file"
fi
return 0
fi
return 1
}
if disable_sources_repo "/etc/apt/sources.list.d/pve-enterprise.sources"; then
msg_ok "$(translate "Enterprise Proxmox repository disabled")" | tee -a "$screen_capture"
changes_made=true
fi
if disable_sources_repo "/etc/apt/sources.list.d/ceph.sources"; then
msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")" | tee -a "$screen_capture"
changes_made=true
fi
for legacy_file in /etc/apt/sources.list.d/pve-public-repo.list \
/etc/apt/sources.list.d/pve-install-repo.list \
/etc/apt/sources.list.d/debian.list; do
if [[ -f "$legacy_file" ]]; then
rm -f "$legacy_file"
msg_ok "$(translate "Removed legacy repository: $(basename "$legacy_file")")" | tee -a "$screen_capture"
fi
done
if [[ -f /etc/apt/sources.list.d/debian.sources ]]; then
rm -f /etc/apt/sources.list.d/debian.sources
msg_ok "$(translate "Old debian.sources file removed to prevent duplication")" | tee -a "$screen_capture"
fi
msg_info "$(translate "Creating Proxmox VE 9.x no-subscription repository...")"
cat > /etc/apt/sources.list.d/proxmox.sources << EOF
Enabled: true
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: ${TARGET_CODENAME}
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
msg_ok "$(translate "Proxmox VE 9.x no-subscription repository created")" | tee -a "$screen_capture"
changes_made=true
msg_info "$(translate "Creating Debian ${TARGET_CODENAME} sources file...")"
cat > /etc/apt/sources.list.d/debian.sources << EOF
Types: deb
URIs: http://deb.debian.org/debian/
Suites: ${TARGET_CODENAME} ${TARGET_CODENAME}-updates
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security/
Suites: ${TARGET_CODENAME}-security
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
EOF
msg_ok "$(translate "Debian repositories configured for $TARGET_CODENAME")"
local firmware_conf="/etc/apt/apt.conf.d/no-firmware-warnings.conf"
if [ ! -f "$firmware_conf" ]; then
msg_info "$(translate "Disabling non-free firmware warnings...")"
echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > "$firmware_conf"
msg_ok "$(translate "Non-free firmware warnings disabled")"
fi
#update_output=$(apt-get update 2>&1)
update_output=$(apt-get -o Dpkg::Progress-Fancy=1 update 2>&1)
update_exit_code=$?
if [ $update_exit_code -eq 0 ]; then
msg_ok "$(translate "Package lists updated successfully")" | tee -a "$screen_capture"
else
if echo "$update_output" | grep -q "NO_PUBKEY\|GPG error"; then
msg_info "$(translate "Fixing GPG key issues...")"
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $(echo "$update_output" | grep "NO_PUBKEY" | sed 's/.*NO_PUBKEY //' | head -1) 2>/dev/null
if apt-get update > "$log_file" 2>&1; then
msg_ok "$(translate "Package lists updated after GPG fix")" | tee -a "$screen_capture"
else
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
return 1
fi
elif echo "$update_output" | grep -q "404\|Failed to fetch"; then
msg_warn "$(translate "Some repositories are not available, continuing with available ones...")"
else
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
echo "Error details: $update_output"
return 1
fi
fi
if apt policy 2>/dev/null | grep -q "${TARGET_CODENAME}.*pve-no-subscription"; then
msg_ok "$(translate "Proxmox VE 9.x repositories verified")" | tee -a "$screen_capture"
else
msg_warn "$(translate "Proxmox VE 9.x repositories verification inconclusive, continuing...")"
fi
local current_pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
local available_pve_version=$(apt-cache policy pve-manager 2>/dev/null | grep -oP 'Candidate: \K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
local upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable")
local security_updates=$(apt list --upgradable 2>/dev/null | grep -c "security")
show_update_menu() {
local current_version="$1"
local target_version="$2"
local upgradable_count="$3"
local security_count="$4"
local menu_text="$(translate "System Update Information")\n\n"
menu_text+="$(translate "Current PVE Version"): $current_version\n"
if [ -n "$target_version" ] && [ "$target_version" != "$current_version" ]; then
menu_text+="$(translate "Available PVE Version"): $target_version\n"
fi
menu_text+="\n$(translate "Package Updates Available"): $upgradable_count\n"
menu_text+="$(translate "Security Updates"): $security_count\n\n"
if [ "$upgradable_count" -eq 0 ]; then
menu_text+="$(translate "System is already up to date")"
whiptail --title "$(translate "Update Status")" --msgbox "$menu_text" 15 70
return 2
else
menu_text+="$(translate "Do you want to proceed with the system update?")"
if whiptail --title "$(translate "Proxmox Update")" --yesno "$menu_text" 18 70; then
return 0
else
return 1
fi
fi
}
show_update_menu "$current_pve_version" "$available_pve_version" "$upgradable" "$security_updates"
MENU_RESULT=$?
clear
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
cat "$screen_capture"
if [[ $MENU_RESULT -eq 1 ]]; then
msg_info2 "$(translate "Update cancelled by user")"
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
rm -f "$screen_capture"
return 0
elif [[ $MENU_RESULT -eq 2 ]]; then
msg_ok "$(translate "System is already up to date. No update needed.")"
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
rm -f "$screen_capture"
return 0
fi
msg_info "$(translate "Cleaning up unused time synchronization services...")"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' purge ntp openntpd systemd-timesyncd > /dev/null 2>&1; then
msg_ok "$(translate "Old time services removed successfully")"
else
msg_warn "$(translate "Some old time services could not be removed (not installed)")"
fi
echo -e
DEBIAN_FRONTEND=noninteractive apt-get -y \
-o Dpkg::Options::='--force-confdef' \
-o Dpkg::Options::='--force-confold' \
dist-upgrade 2>&1 | tee -a "$log_file"
upgrade_exit_code=${PIPESTATUS[0]}
echo -e
clear
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
cat "$screen_capture"
if [ $upgrade_exit_code -ne 0 ]; then
msg_error "$(translate "System upgrade failed. Check log: $log_file")"
rm -f "$screen_capture"
return 1
fi
msg_info "$(translate "Installing essential Proxmox packages...")"
local additional_packages="zfsutils-linux proxmox-backup-restore-image chrony"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install $additional_packages >> "$log_file" 2>&1; then
msg_ok "$(translate "Essential Proxmox packages installed")"
else
msg_warn "$(translate "Some essential Proxmox packages may not have been installed")"
fi
lvm_repair_check
cleanup_duplicate_repos
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
msg_ok "$(translate "Cleanup finished")"
local end_time=$(date +%s)
local duration=$((end_time - start_time))
local minutes=$((duration / 60))
local seconds=$((duration % 60))
echo -e "${TAB}${BGN}$(translate "====== PVE UPDATE COMPLETED ======")${CL}"
echo -e "${TAB}${GN}⏱️ $(translate "Duration")${CL}: ${BL}${minutes}m ${seconds}s${CL}"
echo -e "${TAB}${GN}📄 $(translate "Log file")${CL}: ${BL}$log_file${CL}"
echo -e "${TAB}${GN}📦 $(translate "Packages upgraded")${CL}: ${BL}$upgradable${CL}"
echo -e "${TAB}${GN}🖥️ $(translate "Proxmox VE")${CL}: ${BL}$available_pve_version (Debian $OS_CODENAME)${CL}"
msg_ok "$(translate "Proxmox VE 9.x configuration completed.")"
rm -f "$screen_capture"
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
update_pve9
fi

View File

@@ -1,916 +0,0 @@
#!/bin/bash
# ProxMenux - NVIDIA Driver Installer (PVE 9.x)
# ============================================
# Author : MacRimi
# License : MIT
# Version : 0.9 (PVE9, fixed download issues)
# Last Updated: 29/11/2025
# ============================================
SCRIPT_TITLE="NVIDIA GPU Driver Installer for Proxmox VE"
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
COMPONENTS_STATUS_FILE="$BASE_DIR/components_status.json"
LOG_FILE="/tmp/nvidia_install.log"
screen_capture="/tmp/proxmenux_nvidia_screen_capture_$$.txt"
NVIDIA_BASE_URL="https://download.nvidia.com/XFree86/Linux-x86_64"
NVIDIA_WORKDIR="/opt/nvidia"
export BASE_DIR
export COMPONENTS_STATUS_FILE
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ ! -f "$COMPONENTS_STATUS_FILE" ]]; then
echo "{}" > "$COMPONENTS_STATUS_FILE"
fi
load_language
initialize_cache
# ==========================================================
# GPU detection and current status
# ==========================================================
detect_nvidia_gpus() {
# Only video controllers (not audio)
local lspci_output
lspci_output=$(lspci | grep -i "NVIDIA" \
| grep -Ei "VGA compatible controller|3D controller|Display controller" || true)
if [[ -z "$lspci_output" ]]; then
NVIDIA_GPU_PRESENT=false
DETECTED_GPUS_TEXT="$(translate 'No NVIDIA GPU detected on this system.')"
else
NVIDIA_GPU_PRESENT=true
DETECTED_GPUS_TEXT=""
local i=1
while IFS= read -r line; do
DETECTED_GPUS_TEXT+=" ${i}. ${line}\n"
((i++))
done <<< "$lspci_output"
fi
}
detect_driver_status() {
CURRENT_DRIVER_INSTALLED=false
CURRENT_DRIVER_VERSION=""
# First check if nvidia kernel module is actually loaded
if lsmod | grep -q "^nvidia "; then
modprobe nvidia-uvm 2>/dev/null || true
sleep 1
if command -v nvidia-smi >/dev/null 2>&1; then
CURRENT_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1)
if [[ -n "$CURRENT_DRIVER_VERSION" ]]; then
CURRENT_DRIVER_INSTALLED=true
# Register the installed driver version in components_status.json
update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":false}'
fi
fi
fi
if $CURRENT_DRIVER_INSTALLED; then
CURRENT_STATUS_TEXT="$(printf '%s %s' "$(translate 'NVIDIA driver installed:')" "$CURRENT_DRIVER_VERSION")"
else
CURRENT_STATUS_TEXT="$(translate 'No NVIDIA driver installed.')"
fi
if $CURRENT_DRIVER_INSTALLED; then
CURRENT_STATUS_COLORED="\Z2${CURRENT_STATUS_TEXT}\Zn"
else
CURRENT_STATUS_COLORED="\Z3${CURRENT_STATUS_TEXT}\Zn"
fi
}
# ==========================================================
# System preparation (repos, headers, etc.)
# ==========================================================
ensure_repos_and_headers() {
msg_info "$(translate 'Checking kernel headers and build tools...')"
local kver
kver=$(uname -r)
apt-get update -qq >>"$LOG_FILE" 2>&1
if ! dpkg -s "pve-headers-$kver" >/dev/null 2>&1 && \
! dpkg -s "proxmox-headers-$kver" >/dev/null 2>&1; then
apt-get install -y "pve-headers-$kver" "proxmox-headers-$kver" build-essential dkms >>"$LOG_FILE" 2>&1 || true
else
apt-get install -y build-essential dkms >>"$LOG_FILE" 2>&1 || true
fi
msg_ok "$(translate 'Kernel headers and build tools verified.')" | tee -a "$screen_capture"
}
blacklist_nouveau() {
msg_info "$(translate 'Blacklisting nouveau driver...')"
if ! grep -q '^blacklist nouveau' /etc/modprobe.d/blacklist.conf 2>/dev/null; then
echo "blacklist nouveau" >> /etc/modprobe.d/blacklist.conf
fi
msg_ok "$(translate 'nouveau driver has been blacklisted.')" | tee -a "$screen_capture"
}
ensure_modules_config() {
msg_info "$(translate 'Configuring NVIDIA and VFIO modules...')"
cat > /etc/modules-load.d/nvidia-vfio.conf <<'EOF'
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
nvidia
nvidia_uvm
EOF
msg_ok "$(translate 'Modules configuration updated.')" | tee -a "$screen_capture"
}
stop_and_disable_nvidia_services() {
local services=(
"nvidia-persistenced.service"
"nvidia-persistenced"
"nvidia-powerd.service"
)
local services_detected=0
for service in "${services[@]}"; do
if systemctl is-active --quiet "$service" 2>/dev/null || \
systemctl is-enabled --quiet "$service" 2>/dev/null; then
services_detected=1
break
fi
done
if [ "$services_detected" -eq 1 ]; then
msg_info "$(translate 'Stopping and disabling NVIDIA services...')"
for service in "${services[@]}"; do
if systemctl is-active --quiet "$service" 2>/dev/null; then
systemctl stop "$service" >/dev/null 2>&1 || true
fi
if systemctl is-enabled --quiet "$service" 2>/dev/null; then
systemctl disable "$service" >/dev/null 2>&1 || true
fi
done
sleep 2
msg_ok "$(translate 'NVIDIA services stopped and disabled.')" | tee -a "$screen_capture"
fi
}
unload_nvidia_modules() {
msg_info "$(translate 'Unloading NVIDIA kernel modules...')"
for mod in nvidia_uvm nvidia_drm nvidia_modeset nvidia; do
modprobe -r "$mod" >/dev/null 2>&1 || true
done
if lsmod | grep -qi '\bnvidia'; then
for mod in nvidia_uvm nvidia_drm nvidia_modeset nvidia; do
modprobe -r --force "$mod" >/dev/null 2>&1 || true
done
fi
if lsmod | grep -qi '\bnvidia'; then
msg_warn "$(translate 'Some NVIDIA modules could not be unloaded. Installation may fail. Ensure no processes are using the GPU.')"
if command -v lsof >/dev/null 2>&1; then
echo "$(translate 'Processes using NVIDIA:'):" >> "$LOG_FILE"
lsof /dev/nvidia* 2>/dev/null >> "$LOG_FILE" || true
fi
else
msg_ok "$(translate 'NVIDIA kernel modules unloaded successfully.')" | tee -a "$screen_capture"
fi
}
complete_nvidia_uninstall() {
stop_and_disable_nvidia_services
unload_nvidia_modules
if command -v nvidia-uninstall >/dev/null 2>&1; then
msg_info "$(translate 'Running NVIDIA uninstaller...')"
nvidia-uninstall --silent >>"$LOG_FILE" 2>&1 || true
msg_ok "$(translate 'NVIDIA uninstaller completed.')"
fi
cleanup_nvidia_dkms
msg_info "$(translate 'Removing NVIDIA packages...')"
apt-get -y purge 'nvidia-*' 'libnvidia-*' 'cuda-*' 'libcudnn*' >>"$LOG_FILE" 2>&1 || true
apt-get -y autoremove --purge >>"$LOG_FILE" 2>&1 || true
apt-get -y autoclean >>"$LOG_FILE" 2>&1 || true
rm -f /etc/modules-load.d/nvidia-vfio.conf
rm -f /etc/udev/rules.d/70-nvidia.rules
rm -rf /usr/lib/modprobe.d/nvidia*.conf
rm -rf /etc/modprobe.d/nvidia*.conf
if [[ -d "$NVIDIA_WORKDIR" ]]; then
find "$NVIDIA_WORKDIR" -type d -name "nvidia-persistenced" -exec rm -rf {} + 2>/dev/null || true
find "$NVIDIA_WORKDIR" -type d -name "nvidia-patch" -exec rm -rf {} + 2>/dev/null || true
fi
update_component_status "nvidia_driver" "removed" "" "gpu" '{}'
msg_ok "$(translate 'Complete NVIDIA uninstallation finished.')" | tee -a "$screen_capture"
}
cleanup_nvidia_dkms() {
local versions
versions=$(dkms status 2>/dev/null | awk -F, '/nvidia/ {gsub(/ /,"",$2); print $2}' || true)
[[ -z "$versions" ]] && return 0
msg_info "$(translate 'Removing NVIDIA DKMS entries...')"
while IFS= read -r ver; do
[[ -z "$ver" ]] && continue
dkms remove -m nvidia -v "$ver" --all >/dev/null 2>&1 || true
done <<< "$versions"
msg_ok "$(translate 'NVIDIA DKMS entries removed.')"
}
ensure_workdir() {
mkdir -p "$NVIDIA_WORKDIR"
}
# ==========================================================
# Kernel compatibility detection
# ==========================================================
get_kernel_compatibility_info() {
local kernel_version
kernel_version=$(uname -r)
# Determine Proxmox and kernel version
if [[ -f /etc/pve/.version ]]; then
PVE_VERSION=$(cat /etc/pve/.version)
else
PVE_VERSION="unknown"
fi
# Extract kernel major version (6.x, 5.x, etc)
KERNEL_MAJOR=$(echo "$kernel_version" | cut -d. -f1)
KERNEL_MINOR=$(echo "$kernel_version" | cut -d. -f2)
# Define minimum compatible versions based on kernel
# Based on https://docs.nvidia.com/datacenter/tesla/drivers/index.html
if [[ "$KERNEL_MAJOR" -ge 6 ]] && [[ "$KERNEL_MINOR" -ge 17 ]]; then
# Kernel 6.17+ (Proxmox 9.x) - Requires 580.82.07 or higher
MIN_DRIVER_VERSION="580.82.07"
RECOMMENDED_BRANCH="580"
COMPATIBILITY_NOTE="Kernel $kernel_version requires NVIDIA driver 580.82.07 or newer"
elif [[ "$KERNEL_MAJOR" -ge 6 ]] && [[ "$KERNEL_MINOR" -ge 8 ]]; then
# Kernel 6.8-6.16 (Proxmox 8.2+) - Works with 550.x or higher
MIN_DRIVER_VERSION="550"
RECOMMENDED_BRANCH="580"
COMPATIBILITY_NOTE="Kernel $kernel_version works best with NVIDIA driver 550.x or newer"
elif [[ "$KERNEL_MAJOR" -ge 6 ]]; then
# Kernel 6.2-6.7 (Proxmox 8.x initial) - Works with 535.x or higher
MIN_DRIVER_VERSION="535"
RECOMMENDED_BRANCH="550"
COMPATIBILITY_NOTE="Kernel $kernel_version works with NVIDIA driver 535.x or newer"
elif [[ "$KERNEL_MAJOR" -eq 5 ]] && [[ "$KERNEL_MINOR" -ge 15 ]]; then
# Kernel 5.15+ (Proxmox 7.x, 8.x legacy) - Works with 470.x or higher
MIN_DRIVER_VERSION="470"
RECOMMENDED_BRANCH="535"
COMPATIBILITY_NOTE="Kernel $kernel_version works with NVIDIA driver 470.x or newer"
else
# Old kernels
MIN_DRIVER_VERSION="450"
RECOMMENDED_BRANCH="470"
COMPATIBILITY_NOTE="For older kernels, compatibility may vary"
fi
}
is_version_compatible() {
local version="$1"
local ver_major ver_minor ver_patch
# Extract version components (major.minor.patch)
ver_major=$(echo "$version" | cut -d. -f1)
ver_minor=$(echo "$version" | cut -d. -f2)
ver_patch=$(echo "$version" | cut -d. -f3)
if [[ "$MIN_DRIVER_VERSION" == "580.82.07" ]]; then
# Compare full version: must be >= 580.82.07
if [[ ${ver_major} -gt 580 ]]; then
return 0
elif [[ ${ver_major} -eq 580 ]]; then
if [[ $((10#${ver_minor})) -gt 82 ]]; then
return 0
elif [[ $((10#${ver_minor})) -eq 82 ]]; then
if [[ $((10#${ver_patch:-0})) -ge 7 ]]; then
return 0
fi
fi
fi
return 1
fi
if [[ ${ver_major} -ge ${MIN_DRIVER_VERSION} ]]; then
return 0
else
return 1
fi
}
# ==========================================================
# NVIDIA version management - FIXED VERSION
# ==========================================================
download_latest_version() {
local latest_line version
latest_line=$(curl -fsSL "${NVIDIA_BASE_URL}/latest.txt" 2>&1)
if [[ -z "$latest_line" ]]; then
echo "" >&2
return 1
fi
version=$(echo "$latest_line" | awk '{print $1}' | tr -d '[:space:]')
if [[ -z "$version" ]]; then
echo "" >&2
return 1
fi
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
echo "" >&2
return 1
fi
echo "$version"
return 0
}
list_available_versions() {
local html_content versions
html_content=$(curl -s "$NVIDIA_BASE_URL/" 2>&1)
if [[ -z "$html_content" ]]; then
echo "" >&2
return 1
fi
versions=$(echo "$html_content" \
| grep -o 'href=[^ >]*' \
| awk -F"'" '{print $2}' \
| grep -E '^[0-9]' \
| sed 's/\/$//' \
| sed 's/^[[:space:]]*//;s/[[:space:]]*$//' \
| sort -Vr \
| uniq)
if [[ -z "$versions" ]]; then
echo "" >&2
return 1
fi
echo "$versions"
return 0
}
verify_version_exists() {
local version="$1"
local url="${NVIDIA_BASE_URL}/${version}/"
if curl -fsSL --head "$url" >/dev/null 2>&1; then
return 0
else
return 1
fi
}
download_nvidia_installer() {
ensure_workdir
local version="$1"
version=$(echo "$version" | tr -d '[:space:]' | tr -d '\n' | tr -d '\r')
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
msg_error "Invalid version format: $version" >&2
echo "ERROR: Invalid version format: '$version'" >> "$LOG_FILE"
return 1
fi
local run_file="$NVIDIA_WORKDIR/NVIDIA-Linux-x86_64-${version}.run"
if [[ -f "$run_file" ]]; then
echo "Found existing file: $run_file" >> "$LOG_FILE"
local existing_size file_type
existing_size=$(stat -c%s "$run_file" 2>/dev/null || stat -f%z "$run_file" 2>/dev/null || echo "0")
file_type=$(file "$run_file" 2>/dev/null || echo "unknown")
echo "Existing file size: $existing_size bytes" >> "$LOG_FILE"
echo "Existing file type: $file_type" >> "$LOG_FILE"
if [[ $existing_size -gt 40000000 ]] && echo "$file_type" | grep -q "executable"; then
if sh "$run_file" --check 2>&1 | tee -a "$LOG_FILE" | grep -q "OK"; then
echo "Existing file passed integrity check" >> "$LOG_FILE"
msg_ok "$(translate 'Installer already downloaded and verified.')" >&2
printf '%s\n' "$run_file"
return 0
else
echo "Existing file FAILED integrity check, removing..." >> "$LOG_FILE"
msg_warn "$(translate 'Existing file failed verification, re-downloading...')" >&2
rm -f "$run_file"
fi
else
echo "Existing file invalid (size or type), removing..." >> "$LOG_FILE"
msg_warn "$(translate 'Removing invalid existing file...')" >&2
rm -f "$run_file"
fi
fi
if ! verify_version_exists "$version"; then
msg_error "Version $version does not exist on NVIDIA servers" >&2
echo "ERROR: Version $version not found on server" >> "$LOG_FILE"
return 1
fi
local urls=(
"${NVIDIA_BASE_URL}/${version}/NVIDIA-Linux-x86_64-${version}.run"
"${NVIDIA_BASE_URL}/${version}/NVIDIA-Linux-x86_64-${version}-no-compat32.run"
)
local success=false
local url_index=0
for url in "${urls[@]}"; do
((url_index++))
echo "Attempting download from: $url" >> "$LOG_FILE"
rm -f "$run_file"
if curl -fL --connect-timeout 30 --max-time 600 "$url" -o "$run_file" >> "$LOG_FILE" 2>&1; then
echo "Download completed, verifying file..." >> "$LOG_FILE"
if [[ ! -f "$run_file" ]]; then
echo "ERROR: File not created after download" >> "$LOG_FILE"
continue
fi
local file_size
file_size=$(stat -c%s "$run_file" 2>/dev/null || stat -f%z "$run_file" 2>/dev/null || echo "0")
echo "Downloaded file size: $file_size bytes" >> "$LOG_FILE"
if [[ $file_size -lt 40000000 ]]; then
echo "ERROR: File too small ($file_size bytes, expected >40MB)" >> "$LOG_FILE"
head -c 200 "$run_file" >> "$LOG_FILE" 2>&1
rm -f "$run_file"
continue
fi
local file_type
file_type=$(file "$run_file" 2>/dev/null)
echo "File type: $file_type" >> "$LOG_FILE"
if echo "$file_type" | grep -q "executable"; then
echo "SUCCESS: Valid executable downloaded" >> "$LOG_FILE"
success=true
break
else
echo "ERROR: Not a valid executable" >> "$LOG_FILE"
head -c 200 "$run_file" | od -c >> "$LOG_FILE" 2>&1
rm -f "$run_file"
fi
else
echo "ERROR: curl failed for $url (exit code: $?)" >> "$LOG_FILE"
rm -f "$run_file"
fi
done
if ! $success; then
msg_error "$(translate 'Download failed for all attempted URLs')" >&2
msg_error "Version $version may not be available for your architecture" >&2
echo "ERROR: All download attempts failed" >> "$LOG_FILE"
return 1
fi
chmod +x "$run_file"
echo "Installation file ready: $run_file" >> "$LOG_FILE"
printf '%s\n' "$run_file"
}
# ==========================================================
# Installation / uninstallation
# ==========================================================
run_nvidia_installer() {
local installer="$1"
msg_info2 "$(translate 'Starting NVIDIA installer. This may take several minutes...')"
echo "" >>"$LOG_FILE"
echo "=== Running NVIDIA installer: $installer ===" >>"$LOG_FILE"
local tmp_extract_dir="$NVIDIA_WORKDIR/tmp_extract"
mkdir -p "$tmp_extract_dir"
sh "$installer" --tmpdir="$tmp_extract_dir" --no-questions --ui=none --disable-nouveau --dkms 2>&1 | tee -a "$LOG_FILE"
local rc=${PIPESTATUS[0]}
echo "" >>"$LOG_FILE"
rm -rf "$tmp_extract_dir"
if [[ $rc -ne 0 ]]; then
msg_error "$(translate 'NVIDIA installer reported an error. Check /tmp/nvidia_install.log')"
update_component_status "nvidia_driver" "failed" "" "gpu" '{"patched":false}'
return 1
fi
msg_ok "$(translate 'NVIDIA driver installed successfully.')" | tee -a "$screen_capture"
return 0
}
remove_nvidia_driver() {
complete_nvidia_uninstall
}
install_udev_rules_and_persistenced() {
msg_info "$(translate 'Installing NVIDIA udev rules and persistence service...')"
cat >/etc/udev/rules.d/70-nvidia.rules <<'EOF'
# /etc/udev/rules.d/70-nvidia.rules
KERNEL=="nvidia", RUN+="/bin/bash -c '/usr/bin/nvidia-smi -L'"
KERNEL=="nvidia_uvm", RUN+="/bin/bash -c '/usr/bin/nvidia-modprobe -c0 -u'"
EOF
udevadm control --reload-rules
udevadm trigger --subsystem-match=drm --subsystem-match=pci || true
ensure_workdir
cd "$NVIDIA_WORKDIR" || return 1
if [[ ! -d nvidia-persistenced ]]; then
git clone https://github.com/NVIDIA/nvidia-persistenced.git >>"$LOG_FILE" 2>&1 || true
fi
if [[ -d nvidia-persistenced/init ]]; then
cd nvidia-persistenced/init || return 1
./install.sh >>"$LOG_FILE" 2>&1 || true
fi
msg_ok "$(translate 'NVIDIA udev rules and persistence service installed.')" | tee -a "$screen_capture"
}
apply_nvidia_patch_if_needed() {
if ! whiptail --title "$(translate 'NVIDIA Patch')" --yesno \
"\n$(translate 'Do you want to apply the optional NVIDIA patch to remove some GPU limitations?')" 10 70; then
msg_info2 "$(translate 'NVIDIA patch not applied.')"
update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":false}'
return 0
fi
msg_info "$(translate 'Cloning and applying NVIDIA patch (keylase/nvidia-patch)...')"
ensure_workdir
cd "$NVIDIA_WORKDIR" || return 1
if [[ ! -d nvidia-patch ]]; then
git clone https://github.com/keylase/nvidia-patch.git >>"$LOG_FILE" 2>&1 || true
fi
if [[ -x nvidia-patch/patch.sh ]]; then
cd nvidia-patch || return 1
./patch.sh >>"$LOG_FILE" 2>&1 || true
msg_ok "$(translate 'NVIDIA patch applied - check README for supported versions.')"
update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":true}'
else
msg_warn "$(translate 'Could not run NVIDIA patch script. Please verify repository and driver version.')"
update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":false}'
fi
}
restart_prompt() {
if whiptail --title "$(translate 'NVIDIA Drivers')" --yesno \
"\n$(translate 'The installation/changes require a server restart to apply correctly. Do you want to reboot now?')" 10 70; then
msg_success "$(translate 'Installation completed. Press Enter to continue...')"
read -r
msg_warn "$(translate 'Restarting the server...')"
rm -f "$screen_capture"
reboot
else
msg_success "$(translate 'Installation completed. Please reboot the server manually as soon as possible.')"
msg_success "$(translate 'Completed. Press Enter to return to menu...')"
read -r
rm -f "$screen_capture"
fi
}
# ==========================================================
# Dialog menus
# ==========================================================
show_action_menu_if_installed() {
if ! $CURRENT_DRIVER_INSTALLED; then
ACTION="install"
return 0
fi
local menu_choices=(
"install" "$(translate 'Reinstall/Update NVIDIA drivers')"
"remove" "$(translate 'Uninstall NVIDIA drivers and configuration')"
)
ACTION=$(dialog --clear --stdout \
--backtitle "ProxMenux" \
--title "$(translate 'NVIDIA GPU Driver Management')" \
--menu "$(translate 'Choose an action:')" 14 80 8 \
"${menu_choices[@]}") || ACTION="cancel"
}
show_install_overview() {
local overview
overview="\n$(translate 'This installation will:')\n\n"
overview+="$(translate 'Install NVIDIA proprietary drivers')\n"
overview+="$(translate 'Configure GPU passthrough with VFIO')\n"
overview+="$(translate 'Blacklist nouveau driver')\n"
overview+="$(translate 'Enable IOMMU support if not enabled')\n\n"
overview+="$(translate 'Detected GPU(s):')\n"
overview+="\Zb\Z4$DETECTED_GPUS_TEXT\Zn\n"
overview+="\n\Zn$(translate 'Current status: ') "
overview+="\Zb${CURRENT_STATUS_TEXT}\Zn\n\n"
overview+="$(translate 'After confirming, you will be asked to choose the NVIDIA driver version to install.')\n\n"
overview+="$(translate 'Do you want to continue?')"
dialog --colors --backtitle "ProxMenux" \
--title "$(translate 'NVIDIA GPU Driver Installation')" \
--yesno "$overview" 22 90
}
show_version_menu() {
local latest versions_list
local kernel_version
kernel_version=$(uname -r)
latest=$(download_latest_version 2>/dev/null)
versions_list=$(list_available_versions 2>/dev/null)
if [[ -z "$latest" ]] && [[ -z "$versions_list" ]]; then
dialog --backtitle "ProxMenux" --title "$(translate 'Error')" --msgbox \
"$(translate 'Could not retrieve versions list from NVIDIA. Please check your internet connection.')\n\nURL: ${NVIDIA_BASE_URL}" 10 80
DRIVER_VERSION="cancel"
return 1
fi
if [[ -z "$latest" ]] && [[ -n "$versions_list" ]]; then
latest=$(echo "$versions_list" | head -n1)
fi
if [[ -n "$latest" ]] && [[ -z "$versions_list" ]]; then
versions_list="$latest"
fi
# Clean latest version
latest=$(echo "$latest" | tr -d '[:space:]')
local filter=""
local selection
local choices
local current_list
local menu_text
while true; do
current_list="$versions_list"
if [[ -n "$MIN_DRIVER_VERSION" ]]; then
local filtered_list=""
while IFS= read -r ver; do
[[ -z "$ver" ]] && continue
if is_version_compatible "$ver"; then
filtered_list+="$ver"$'\n'
fi
done <<< "$current_list"
current_list="$filtered_list"
fi
if [[ -n "$filter" ]]; then
current_list=$(echo "$current_list" | grep "$filter" || true)
fi
menu_text="$(translate 'Select the NVIDIA driver version to install:')\n\n"
menu_text+="$(translate 'Use the filter entry to narrow the list. Latest available (recommended in most cases), or choose a specific version from the list.')"
choices=()
choices+=("latest" "$(translate 'Latest available') (${latest:-unknown})")
choices+=("" "")
choices+=("filter" "$(translate 'Filter versions')${filter:+: $filter}")
if [[ -n "$current_list" ]]; then
while IFS= read -r ver; do
[[ -z "$ver" ]] && continue
ver=$(echo "$ver" | tr -d '[:space:]')
[[ -z "$ver" ]] && continue
choices+=("$ver" "$ver")
done <<< "$current_list"
else
choices+=("" "$(translate 'No versions match the current filter')")
fi
selection=$(dialog --clear --stdout \
--backtitle "ProxMenux" \
--title "$(translate 'NVIDIA Driver Version')" \
--menu "$menu_text" 26 90 16 \
"${choices[@]}") || { DRIVER_VERSION="cancel"; return 1; }
case "$selection" in
"")
continue
;;
filter)
filter=$(dialog --clear --stdout \
--backtitle "ProxMenux" \
--title "$(translate 'Filter NVIDIA versions')" \
--inputbox "$(translate 'Enter a filter (e.g., 560, 570, 580). Leave empty to show all.')" 10 80 "$filter") || true
;;
latest)
DRIVER_VERSION="$latest"
DRIVER_VERSION=$(echo "$DRIVER_VERSION" | tr -d '[:space:]')
return 0
;;
*)
DRIVER_VERSION="$selection"
DRIVER_VERSION=$(echo "$DRIVER_VERSION" | tr -d '[:space:]')
return 0
;;
esac
done
}
# ==========================================================
# Main flow
# ==========================================================
main() {
: >"$LOG_FILE"
: >"$screen_capture"
detect_nvidia_gpus
detect_driver_status
if ! $NVIDIA_GPU_PRESENT; then
dialog --backtitle "ProxMenux" --title "$(translate 'NVIDIA GPU Driver Installation')" --msgbox \
"\n$(translate 'No NVIDIA GPU has been detected on this system. The installer will now exit.')" 20 70
exit 1
fi
show_action_menu_if_installed
case "$ACTION" in
install)
if ! show_install_overview; then
exit 0
fi
get_kernel_compatibility_info
show_version_menu
if [[ "$DRIVER_VERSION" == "cancel" || -z "$DRIVER_VERSION" ]]; then
exit 0
fi
if $CURRENT_DRIVER_INSTALLED; then
if [[ "$CURRENT_DRIVER_VERSION" == "$DRIVER_VERSION" ]]; then
if ! dialog --colors --backtitle "ProxMenux" --title "$(translate 'Same Version Detected')" --yesno \
"$(printf '\n\n\n%s \Zb%s\Zn\n\n%s' \
"$(translate 'Version')" "$DRIVER_VERSION" \
"$(translate 'is already installed. Do you want to reinstall it? This will perform a clean uninstall first.')")" 14 70; then
exit 0
fi
else
if ! dialog --colors --backtitle "ProxMenux" --title "$(translate 'Version Change Detected')" --yesno \
"$(printf '\n\n%s \Zb%s\Zn\n%s \Zb\Z4%s\Zn\n\n%s' \
"$(translate 'Current version:')" "$CURRENT_DRIVER_VERSION" \
"$(translate 'New version:')" "$DRIVER_VERSION" \
"$(translate 'The current driver will be completely uninstalled before installing the new version. Continue?')")" 20 70; then
exit 0
fi
fi
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
msg_info2 "$(translate 'Uninstalling current NVIDIA driver before installing new version...')"
complete_nvidia_uninstall
sleep 2
CURRENT_DRIVER_INSTALLED=false
CURRENT_DRIVER_VERSION=""
fi
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
ensure_repos_and_headers
blacklist_nouveau
ensure_modules_config
stop_and_disable_nvidia_services
unload_nvidia_modules
msg_info "$(translate 'Downloading NVIDIA driver version:') $DRIVER_VERSION"
local installer
installer=$(download_nvidia_installer "$DRIVER_VERSION" 2>>"$LOG_FILE")
local download_result=$?
if [[ $download_result -ne 0 ]]; then
msg_error "$(translate 'Failed to download NVIDIA installer')"
exit 1
fi
msg_ok "$(translate 'NVIDIA installer downloaded successfully')"
if [[ -z "$installer" || ! -f "$installer" ]]; then
msg_error "$(translate 'Internal error: NVIDIA installer path is empty or file not found.')"
rm -f "$screen_capture"
exit 1
fi
if ! run_nvidia_installer "$installer"; then
rm -f "$screen_capture"
exit 1
fi
sleep 2
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
cat "$screen_capture"
echo -e "${TAB}${GN}📄 $(translate "Log file")${CL}: ${BL}$LOG_FILE${CL}"
install_udev_rules_and_persistenced
msg_info "$(translate 'Updating initramfs for all kernels...')"
update-initramfs -u -k all >>"$LOG_FILE" 2>&1 || true
msg_ok "$(translate 'initramfs updated.')"
msg_info2 "$(translate 'Checking NVIDIA driver status with nvidia-smi')"
if command -v nvidia-smi >/dev/null 2>&1; then
nvidia-smi || true
CURRENT_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1)
CURRENT_DRIVER_INSTALLED=true
else
msg_warn "$(translate 'nvidia-smi not found in PATH. Please verify the driver installation.')"
fi
if [[ -n "$CURRENT_DRIVER_VERSION" ]]; then
msg_ok "$(translate 'NVIDIA driver') $CURRENT_DRIVER_VERSION $(translate 'installed successfully.')"
update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":false}'
msg_success "$(translate 'Driver installed successfully. Press Enter to continue...')"
read -r
else
msg_error "$(translate 'Failed to detect installed NVIDIA driver version.')"
update_component_status "nvidia_driver" "failed" "" "gpu" '{"patched":false}'
fi
apply_nvidia_patch_if_needed
restart_prompt
;;
remove)
if dialog --backtitle "ProxMenux" --title "$(translate 'NVIDIA Driver Uninstall')" --yesno \
"\n\n\n$(translate 'This will remove NVIDIA drivers and related configuration. Do you want to continue?')" 14 70; then
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
remove_nvidia_driver
msg_info "$(translate 'Updating initramfs for all kernels...')"
update-initramfs -u -k all >>"$LOG_FILE" 2>&1 || true
msg_ok "$(translate 'initramfs updated.')"
restart_prompt
fi
;;
cancel|*)
exit 0
;;
esac
}
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
main
fi

View File

@@ -1,194 +0,0 @@
#!/bin/bash
# ==========================================================
# ProxMenux - A menu-driven script for Proxmox VE management
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.1
# Last Updated: 29/05/2025
# ==========================================================
# Description:
# This script automates the process of importing disk images into Proxmox VE virtual machines (VMs),
# making it easy to attach pre-existing disk files without manual configuration.
#
# Before running the script, ensure that disk images are available in /var/lib/vz/template/images/.
# The script scans this directory for compatible formats (.img, .qcow2, .vmdk, .raw) and lists the available files.
#
# Using an interactive menu, you can:
# - Select a VM to attach the imported disk.
# - Choose one or multiple disk images for import.
# - Pick a storage volume in Proxmox for disk placement.
# - Assign a suitable interface (SATA, SCSI, VirtIO, or IDE).
# - Enable optional settings like SSD emulation or bootable disk configuration.
#
# Once completed, the script ensures the selected images are correctly attached and ready to use.
# ==========================================================
# Configuration ============================================
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
[[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE"
load_language
initialize_cache
# Configuration ============================================
detect_image_dir() {
for store in $(pvesm status -content images | awk 'NR>1 {print $1}'); do
path=$(pvesm path "${store}:template" 2>/dev/null)
if [[ -d "$path" ]]; then
for ext in raw img qcow2 vmdk; do
if compgen -G "$path/*.$ext" > /dev/null; then
echo "$path"
return 0
fi
done
for sub in images iso; do
dir="$path/$sub"
if [[ -d "$dir" ]]; then
for ext in raw img qcow2 vmdk; do
if compgen -G "$dir/*.$ext" > /dev/null; then
echo "$dir"
return 0
fi
done
fi
done
fi
done
for fallback in /var/lib/vz/template/images /var/lib/vz/template/iso; do
if [[ -d "$fallback" ]]; then
for ext in raw img qcow2 vmdk; do
if compgen -G "$fallback/*.$ext" > /dev/null; then
echo "$fallback"
return 0
fi
done
fi
done
return 1
}
IMAGES_DIR=$(detect_image_dir)
if [[ -z "$IMAGES_DIR" ]]; then
dialog --title "$(translate 'No Images Found')" \
--msgbox "$(translate 'Could not find any directory containing disk images')\n\n$(translate 'Make sure there is at least one file with extension .img, .qcow2, .vmdk or .raw')" 15 60
exit 1
fi
IMAGES=$(ls -A "$IMAGES_DIR" | grep -E "\.(img|qcow2|vmdk|raw)$")
if [ -z "$IMAGES" ]; then
dialog --title "$(translate 'No Disk Images Found')" \
--msgbox "$(translate 'No compatible disk images found in:')\n\n$IMAGES_DIR\n\n$(translate 'Supported formats: .img, .qcow2, .vmdk, .raw')" 15 60
exit 1
fi
# === Select VM
msg_info "$(translate 'Getting VM list')"
VM_LIST=$(qm list | awk 'NR>1 {print $1" "$2}')
[[ -z "$VM_LIST" ]] && { msg_error "$(translate 'No VMs available in the system')"; exit 1; }
msg_ok "$(translate 'VM list obtained')"
VMID=$(whiptail --title "$(translate 'Select VM')" \
--menu "$(translate 'Select the VM where you want to import the disk image:')" 20 70 10 $VM_LIST 3>&1 1>&2 2>&3)
[[ -z "$VMID" ]] && exit 1
# === Select storage
msg_info "$(translate 'Getting storage volumes')"
STORAGE_LIST=$(pvesm status -content images | awk 'NR>1 {print $1}')
[[ -z "$STORAGE_LIST" ]] && { msg_error "$(translate 'No storage volumes available')"; exit 1; }
msg_ok "$(translate 'Storage volumes obtained')"
STORAGE_OPTIONS=()
while read -r storage; do STORAGE_OPTIONS+=("$storage" ""); done <<< "$STORAGE_LIST"
STORAGE=$(whiptail --title "$(translate 'Select Storage')" \
--menu "$(translate 'Select the storage volume for disk import:')" 20 70 10 "${STORAGE_OPTIONS[@]}" 3>&1 1>&2 2>&3)
[[ -z "$STORAGE" ]] && exit 1
# === Select images
IMAGE_OPTIONS=()
while read -r img; do IMAGE_OPTIONS+=("$img" "" "OFF"); done <<< "$IMAGES"
SELECTED_IMAGES=$(whiptail --title "$(translate 'Select Disk Images')" \
--checklist "$(translate 'Select the disk images to import:')" 20 70 12 "${IMAGE_OPTIONS[@]}" 3>&1 1>&2 2>&3)
[[ -z "$SELECTED_IMAGES" ]] && exit 1
# === Import each selected image
for IMAGE in $SELECTED_IMAGES; do
IMAGE=$(echo "$IMAGE" | tr -d '"')
INTERFACE=$(whiptail --title "$(translate 'Interface Type')" --menu "$(translate 'Select the interface type for the image:') $IMAGE" 15 40 4 \
"sata" "SATA" "scsi" "SCSI" "virtio" "VirtIO" "ide" "IDE" 3>&1 1>&2 2>&3)
[[ -z "$INTERFACE" ]] && { msg_error "$(translate 'No interface type selected for') $IMAGE"; continue; }
FULL_PATH="$IMAGES_DIR/$IMAGE"
msg_info "$(translate 'Importing image:') $IMAGE"
TEMP_DISK_FILE=$(mktemp)
qm importdisk "$VMID" "$FULL_PATH" "$STORAGE" 2>&1 | while read -r line; do
if [[ "$line" =~ transferred ]]; then
PERCENT=$(echo "$line" | grep -oP "\(\d+\.\d+%\)" | tr -d '()%')
echo -ne "\r${TAB}${BL}-$(translate 'Importing image:') $IMAGE-${CL} ${PERCENT}%"
elif [[ "$line" =~ successfully\ imported\ disk ]]; then
echo "$line" | grep -oP "(?<=successfully imported disk ').*(?=')" > "$TEMP_DISK_FILE"
fi
done
echo -ne "\n"
IMPORT_STATUS=${PIPESTATUS[0]}
if [ "$IMPORT_STATUS" -eq 0 ]; then
msg_ok "$(translate 'Image imported successfully')"
IMPORTED_DISK=$(cat "$TEMP_DISK_FILE")
rm -f "$TEMP_DISK_FILE"
if [ -n "$IMPORTED_DISK" ]; then
EXISTING_DISKS=$(qm config "$VMID" | grep -oP "${INTERFACE}\d+" | sort -n)
NEXT_SLOT=0
[[ -n "$EXISTING_DISKS" ]] && NEXT_SLOT=$(( $(echo "$EXISTING_DISKS" | tail -n1 | sed "s/${INTERFACE}//") + 1 ))
SSD_OPTION=""
if [ "$INTERFACE" != "virtio" ]; then
whiptail --yesno "$(translate 'Do you want to use SSD emulation for this disk?')" 10 60 && SSD_OPTION=",ssd=1"
fi
msg_info "$(translate 'Configuring disk')"
if qm set "$VMID" --${INTERFACE}${NEXT_SLOT} "$IMPORTED_DISK${SSD_OPTION}" &>/dev/null; then
msg_ok "$(translate 'Image') $IMAGE $(translate 'configured as') ${INTERFACE}${NEXT_SLOT}"
whiptail --yesno "$(translate 'Do you want to make this disk bootable?')" 10 60 && {
msg_info "$(translate 'Configuring disk as bootable')"
if qm set "$VMID" --boot c --bootdisk ${INTERFACE}${NEXT_SLOT} &>/dev/null; then
msg_ok "$(translate 'Disk configured as bootable')"
else
msg_error "$(translate 'Could not configure the disk as bootable')"
fi
}
else
msg_error "$(translate 'Could not configure disk') ${INTERFACE}${NEXT_SLOT} $(translate 'for VM') $VMID"
fi
else
msg_error "$(translate 'Could not find the imported disk')"
fi
else
msg_error "$(translate 'Could not import') $IMAGE"
fi
done
msg_ok "$(translate 'All selected images have been processed')"
msg_success "$(translate "Press Enter to return to menu...")"
read -r

View File

@@ -1,256 +0,0 @@
#!/bin/bash
# ==========================================================
# ProxMenux - A menu-driven script for Proxmox VE management
# ==========================================================
# Author : MacRimi
# Revision : @Blaspt (USB passthrough via udev rule with persistent /dev/coral)
# Copyright : (c) 2024 MacRimi
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.1
# Last Updated: 16/05/2025
# ==========================================================
# Description:
# This script automates the configuration and installation of
# Coral TPU and iGPU support in Proxmox VE containers. It:
# - Configures a selected LXC container for hardware acceleration
# - Installs and sets up Coral TPU drivers on the Proxmox host
# - Installs necessary drivers inside the container
# - Manages required system and container restarts
#
# Supports Coral USB and Coral M.2 (PCIe) devices.
# Includes USB passthrough enhancement using persistent udev alias (/dev/coral).
# ==========================================================
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
select_container() {
CONTAINERS=$(pct list | awk 'NR>1 {print $1, $3}' | xargs -n2)
if [ -z "$CONTAINERS" ]; then
msg_error "$(translate 'No containers available in Proxmox.')"
exit 1
fi
CONTAINER_ID=$(whiptail --title "$(translate 'Select Container')" \
--menu "$(translate 'Select the LXC container:')" 20 70 10 $CONTAINERS 3>&1 1>&2 2>&3)
if [ -z "$CONTAINER_ID" ]; then
msg_error "$(translate 'No container selected. Exiting.')"
exit 1
fi
if ! pct list | awk 'NR>1 {print $1}' | grep -qw "$CONTAINER_ID"; then
msg_error "$(translate 'Container with ID') $CONTAINER_ID $(translate 'does not exist. Exiting.')"
exit 1
fi
msg_ok "$(translate 'Container selected:') $CONTAINER_ID"
}
validate_container_id() {
if [ -z "$CONTAINER_ID" ]; then
msg_error "$(translate 'Container ID not defined. Make sure to select a container first.')"
exit 1
fi
if pct status "$CONTAINER_ID" | grep -q "running"; then
msg_info "$(translate 'Stopping the container before applying configuration...')"
pct stop "$CONTAINER_ID"
msg_ok "$(translate 'Container stopped.')"
fi
}
add_udev_rule_for_coral_usb_() {
RULE_FILE="/etc/udev/rules.d/99-coral-usb.rules"
RULE_CONTENT='SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666", TAG+="uaccess"'
if [[ ! -f "$RULE_FILE" ]] || ! grep -qF "$RULE_CONTENT" "$RULE_FILE"; then
echo "$RULE_CONTENT" > "$RULE_FILE"
udevadm control --reload-rules && udevadm trigger
msg_ok "$(translate 'Udev rule for Coral USB added and rules reloaded.')"
else
msg_ok "$(translate 'Udev rule for Coral USB already exists.')"
fi
}
add_udev_rule_for_coral_usb() {
RULE_FILE="/etc/udev/rules.d/99-coral-usb.rules"
RULE_CONTENT='# Coral USB Accelerator
SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666", TAG+="uaccess", SYMLINK+="coral"
# Coral Dev Board / Mini PCIe
SUBSYSTEM=="usb", ATTRS{idVendor}=="1a6e", ATTRS{idProduct}=="089a", MODE="0666", TAG+="uaccess", SYMLINK+="coral"'
if [[ ! -f "$RULE_FILE" ]] || ! grep -q "18d1.*9302\|1a6e.*089a" "$RULE_FILE"; then
echo "$RULE_CONTENT" > "$RULE_FILE"
udevadm control --reload-rules && udevadm trigger
msg_ok "$(translate 'Udev rules for Coral USB devices added and rules reloaded.')"
else
msg_ok "$(translate 'Udev rules for Coral USB devices already exist.')"
fi
}
add_mount_if_needed() {
local DEVICE="$1"
local DEST="$2"
local CONFIG_FILE="$3"
if [ -e "$DEVICE" ] && ! grep -q "lxc.mount.entry: $DEVICE" "$CONFIG_FILE"; then
echo "lxc.mount.entry: $DEVICE $DEST none bind,optional,create=$( [ -c "$DEVICE" ] && echo file || echo dir )" >> "$CONFIG_FILE"
fi
}
configure_lxc_hardware() {
validate_container_id
CONFIG_FILE="/etc/pve/lxc/${CONTAINER_ID}.conf"
if [ ! -f "$CONFIG_FILE" ]; then
msg_error "$(translate 'Configuration file for container') $CONTAINER_ID $(translate 'not found.')"
exit 1
fi
# Privileged container
if grep -q "^unprivileged: 1" "$CONFIG_FILE"; then
msg_info "$(translate 'The container is unprivileged. Changing to privileged...')"
sed -i "s/^unprivileged: 1/unprivileged: 0/" "$CONFIG_FILE"
STORAGE_TYPE=$(pct config "$CONTAINER_ID" | grep "^rootfs:" | awk -F, '{print $2}' | cut -d'=' -f2)
if [[ "$STORAGE_TYPE" == "dir" ]]; then
STORAGE_PATH=$(pct config "$CONTAINER_ID" | grep "^rootfs:" | awk '{print $2}' | cut -d',' -f1)
chown -R root:root "$STORAGE_PATH"
fi
msg_ok "$(translate 'Container changed to privileged.')"
else
msg_ok "$(translate 'The container is already privileged.')"
fi
sed -i '/^dev[0-9]\+:/d' "$CONFIG_FILE"
# Enable nesting feature
if ! grep -q "features: nesting=1" "$CONFIG_FILE"; then
echo "features: nesting=1" >> "$CONFIG_FILE"
fi
# iGPU support
if ! grep -q "c 226:0 rwm" "$CONFIG_FILE"; then
echo "lxc.cgroup2.devices.allow: c 226:0 rwm # iGPU" >> "$CONFIG_FILE"
echo "lxc.cgroup2.devices.allow: c 226:128 rwm # iGPU" >> "$CONFIG_FILE"
fi
add_mount_if_needed "/dev/dri" "dev/dri" "$CONFIG_FILE"
add_mount_if_needed "/dev/dri/renderD128" "dev/dri/renderD128" "$CONFIG_FILE"
add_mount_if_needed "/dev/dri/card0" "dev/dri/card0" "$CONFIG_FILE"
# Framebuffer support
if ! grep -q "c 29:0 rwm # Framebuffer" "$CONFIG_FILE"; then
echo "lxc.cgroup2.devices.allow: c 29:0 rwm # Framebuffer" >> "$CONFIG_FILE"
fi
add_mount_if_needed "/dev/fb0" "dev/fb0" "$CONFIG_FILE"
# ----------------------------------------------------------
# Coral USB passthrough (via udev + /dev/coral)
# ----------------------------------------------------------
add_udev_rule_for_coral_usb
if ! grep -Pq "^lxc.cgroup2.devices.allow: c 189:\* rwm # Coral USB$" "$CONFIG_FILE"; then
echo "lxc.cgroup2.devices.allow: c 189:* rwm # Coral USB" >> "$CONFIG_FILE"
fi
add_mount_if_needed "/dev/coral" "dev/coral" "$CONFIG_FILE"
# ----------------------------------------------------------
# Coral M.2 (PCIe) support
# ----------------------------------------------------------
if lspci | grep -iq "Global Unichip"; then
if ! grep -Pq "^lxc.cgroup2.devices.allow: c 245:0 rwm # Coral M2 Apex$" "$CONFIG_FILE"; then
echo "lxc.cgroup2.devices.allow: c 245:0 rwm # Coral M2 Apex" >> "$CONFIG_FILE"
fi
add_mount_if_needed "/dev/apex_0" "dev/apex_0" "$CONFIG_FILE"
fi
msg_ok "$(translate 'Coral TPU and iGPU configuration added to container') $CONTAINER_ID."
}
install_coral_in_container() {
msg_info2 "$(translate 'Installing iGPU and Coral TPU drivers inside the container...')"
tput sc
LOG_FILE=$(mktemp)
pct start "$CONTAINER_ID"
CORAL_M2=$(lspci | grep -i "Global Unichip")
if [[ -n "$CORAL_M2" ]]; then
DRIVER_OPTION=$(whiptail --title "$(translate 'Select driver version')" \
--menu "$(translate 'Choose the driver version for Coral M.2:\n\nCaution: Maximum mode generates more heat.')" 15 60 2 \
1 "libedgetpu1-std ($(translate 'standard performance'))" \
2 "libedgetpu1-max ($(translate 'maximum performance'))" 3>&1 1>&2 2>&3)
case "$DRIVER_OPTION" in
1) DRIVER_PACKAGE="libedgetpu1-std" ;;
2) DRIVER_PACKAGE="libedgetpu1-max" ;;
*) DRIVER_PACKAGE="libedgetpu1-std" ;;
esac
else
DRIVER_PACKAGE="libedgetpu1-std"
fi
script -q -c "pct exec \"$CONTAINER_ID\" -- bash -c '
set -e
echo \"- Updating package lists...\"
apt-get update
echo \"- Installing iGPU drivers...\"
apt-get install -y va-driver-all ocl-icd-libopencl1 intel-opencl-icd vainfo intel-gpu-tools
chgrp video /dev/dri && chmod 755 /dev/dri
adduser root video && adduser root render
echo \"- Installing Coral TPU dependencies...\"
apt-get install -y gnupg python3 python3-pip python3-venv
echo \"- Adding Coral TPU repository...\"
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/coral-edgetpu.gpg
echo \"deb [signed-by=/usr/share/keyrings/coral-edgetpu.gpg] https://packages.cloud.google.com/apt coral-edgetpu-stable main\" | tee /etc/apt/sources.list.d/coral-edgetpu.list
echo \"- Updating package lists again...\"
apt-get update
echo \"- Installing Coral TPU driver ($DRIVER_PACKAGE)...\"
apt-get install -y $DRIVER_PACKAGE
'" "$LOG_FILE"
if [ $? -eq 0 ]; then
tput rc
tput ed
rm -f "$LOG_FILE"
msg_ok "$(translate 'iGPU and Coral TPU drivers installed inside the container.')"
else
msg_error "$(translate 'Failed to install iGPU and Coral TPU drivers inside the container.')"
cat "$LOG_FILE"
rm -f "$LOG_FILE"
exit 1
fi
}
select_container
show_proxmenux_logo
configure_lxc_hardware
install_coral_in_container
msg_ok "$(translate 'Configuration completed.')"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r

View File

@@ -1,116 +0,0 @@
#!/bin/bash
# ProxMenux - A menu-driven script for Proxmox VE management
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.0
# Last Updated: 28/01/2025
# ==========================================================
# Description:
# This script installs the Coral TPU drivers on the Proxmox VE host.
# It ensures that necessary packages are installed and compiles the
# Coral TPU drivers for proper functionality.
# ==========================================================
# Configuration ============================================
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
# Prompt before installation
pre_install_prompt() {
if ! whiptail --title "$(translate 'Coral TPU Installation')" --yesno "$(translate 'Installing Coral TPU drivers requires rebooting the server after installation. Do you want to proceed?')" 10 70; then
msg_warn "$(translate 'Installation cancelled by user.')"
exit 0
fi
}
# Verify and configure repositories on the host
verify_and_add_repos() {
msg_info "$(translate 'Configuring necessary repositories on the host...')"
sleep 2
if ! grep -q "pve-no-subscription" /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null; then
echo "deb http://download.proxmox.com/debian/pve $(lsb_release -sc) pve-no-subscription" | tee /etc/apt/sources.list.d/pve-no-subscription.list
msg_ok "$(translate 'pve-no-subscription repository added.')"
fi
if ! grep -q "non-free-firmware" /etc/apt/sources.list; then
echo "deb http://deb.debian.org/debian $(lsb_release -sc) main contrib non-free-firmware
deb http://deb.debian.org/debian $(lsb_release -sc)-updates main contrib non-free-firmware
deb http://security.debian.org/debian-security $(lsb_release -sc)-security main contrib non-free-firmware" | tee -a /etc/apt/sources.list
msg_ok "$(translate 'non-free-firmware repositories added.')"
fi
msg_ok "$(translate 'Added repositories')"
sleep 2
msg_info "$(translate 'Verifying repositories...')"
apt-get update &>/dev/null
msg_ok "$(translate 'Verified and updated repositories.')"
}
# Function to install Coral TPU drivers on the host
install_coral_host() {
show_proxmenux_logo
verify_and_add_repos
apt-get install -y git devscripts dh-dkms dkms pve-headers-$(uname -r) >/dev/null 2>&1
cd /tmp
rm -rf gasket-driver
git clone https://github.com/google/gasket-driver.git
if [ $? -ne 0 ]; then
msg_error "$(translate 'Error: Could not clone the repository.')"
exit 1
fi
cd gasket-driver/
debuild -us -uc -tc -b
if [ $? -ne 0 ]; then
msg_error "$(translate 'Error: Failed to build driver packages.')"
exit 1
fi
dpkg -i ../gasket-dkms_*.deb
if [ $? -ne 0 ]; then
msg_error "$(translate 'Error: Failed to install the driver packages.')"
exit 1
fi
msg_success "$(translate 'Coral TPU drivers installed successfully on the host.')"
echo -e
}
# Prompt for reboot after installation
restart_prompt() {
if whiptail --title "$(translate 'Coral TPU Installation')" --yesno "$(translate 'The installation requires a server restart to apply changes. Do you want to restart now?')" 10 70; then
msg_warn "$(translate 'Restarting the server...')"
reboot
else
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
fi
}
pre_install_prompt
install_coral_host
restart_prompt

View File

@@ -35,13 +35,13 @@ initialize_cache
case $OPTION in
1)
bash "$LOCAL_SCRIPTS/configure_igpu_lxc.sh"
bash "$LOCAL_SCRIPTS/gpu_tpu/configure_igpu_lxc.sh"
if [ $? -ne 0 ]; then
return
fi
;;
2)
bash "$LOCAL_SCRIPTS/install_coral_lxc.sh"
bash "$LOCAL_SCRIPTS/gpu_tpu/install_coral_lxc.sh"
if [ $? -ne 0 ]; then
return
fi

View File

@@ -1,75 +0,0 @@
#!/bin/bash
# ==========================================================
# ProxMenux - A menu-driven script for Proxmox VE management
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.0
# Last Updated: 28/01/2025
# ==========================================================
# Configuration ============================================
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
while true; do
OPTION=$(whiptail --title "$(translate "Disk and Storage Manager Menu")" --menu "$(translate "Select an option:")" 20 70 10 \
"1" "$(translate "Add Disk Passthrough to a VM")" \
"2" "$(translate "Add Disk") Passthrough $(translate "to a CT")" \
"3" "$(translate "Import Disk Image to a VM")" \
"4" "$(translate "Mount point to CT")" \
"5" "$(translate "Mount disk on HOST")" \
"6" "$(translate "Unmount disk from HOST")" \
"7" "$(translate "Format disk")" \
"8" "$(translate "Return to Main Menu")" 3>&1 1>&2 2>&3)
case $OPTION in
1)
msg_info2 "$(translate "Running script: Add Disk Passthrough to a VM")..."
bash "$LOCAL_SCRIPTS/storage/disk-passthrough.sh"
;;
2)
msg_info2 "$(translate "Running script: Add Disk Passthrough to a CT")..."
bash "$LOCAL_SCRIPTS/storage/disk-passthrough_ct.sh"
;;
3)
msg_info2 "$(translate "Running script: Import Disk Image to a VM")..."
bash "$LOCAL_SCRIPTS/storage/import-disk-image.sh"
;;
4)
msg_info2 "$(translate "Running script: Mount point to CT")..."
bash "$LOCAL_SCRIPTS/storage/mount-point-to-ct.sh"
;;
5)
msg_info2 "$(translate "Running script: Mount disk on HOST")..."
bash "$LOCAL_SCRIPTS/storage/mount-disk-on-host.sh"
;;
6)
msg_info2 "$(translate "Running script: Unmount disk from HOST")..."
bash "$LOCAL_SCRIPTS/storage/unmount-disk-from-host.sh"
;;
7)
msg_info2 "$(translate "Running script: Format disk")..."
bash "$LOCAL_SCRIPTS/storage/format-disk.sh"
;;
8)
exec bash "$LOCAL_SCRIPTS/menus/main_menu.sh"
;;
*)
exec bash "$LOCAL_SCRIPTS/menus/main_menu.sh"
;;
esac
done

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff