Compare commits

..

3 Commits

Author SHA1 Message Date
MickLesk 71ecb99c62 Source core.func for shared messaging in iommu-setup
Replace locally duplicated color variables and msg_* helpers with
core.func + load_functions, matching the pattern used by update-apps
and pve-privilege-converter. Telemetry remains via api.func only.
2026-06-26 22:02:49 +02:00
MickLesk a9cc6234e8 Fix bare-metal detection in iommu-setup
systemd-detect-virt prints "none" on bare metal but exits non-zero, so the
`|| echo "none"` fallback appended a second "none" and the check wrongly
treated a physical Proxmox host as virtualized. Capture the command output
directly and only block when a real virtualization type is reported.
2026-06-26 21:50:48 +02:00
MickLesk 9a4c8325cc Add iommu-setup tool for PCI(e) passthrough preparation
New PVE host tool that enables IOMMU for PCI(e) passthrough:

- Detects the CPU vendor and applies the matching kernel parameters
  (intel_iommu=on / amd_iommu=on plus iommu=pt).
- Supports both boot configurations: GRUB (/etc/default/grub + update-grub)
  and proxmox-boot-tool managed systemd-boot (/etc/kernel/cmdline +
  proxmox-boot-tool refresh).
- Idempotent: only missing parameters are appended and a timestamped backup
  of the boot config is created before editing.
- Loads the vfio modules at boot (vfio_virqfd omitted, merged into the core
  since kernel 6.2).
- Guards for root, supported PVE 8.x/9.x and bare metal; reports current
  IOMMU state and prints verification commands.
2026-06-26 21:43:56 +02:00
13 changed files with 249 additions and 167 deletions
-19
View File
@@ -486,12 +486,6 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details> </details>
## 2026-06-27
### ❔ Uncategorized
- fix(endurain): replace Poetry/uv-pip backend setup with uv sync --frozen --no-dev [@Copilot](https://github.com/Copilot) ([#15429](https://github.com/community-scripts/ProxmoxVE/pull/15429))
## 2026-06-26 ## 2026-06-26
### 🚀 Updated Scripts ### 🚀 Updated Scripts
@@ -500,28 +494,15 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- #### 🐞 Bug Fixes - #### 🐞 Bug Fixes
- Docuseal: use real SECRET_KEY_BASE for db:migrate on update [@MickLesk](https://github.com/MickLesk) ([#15411](https://github.com/community-scripts/ProxmoxVE/pull/15411))
- bun: correct install for degoog [@MickLesk](https://github.com/MickLesk) ([#15412](https://github.com/community-scripts/ProxmoxVE/pull/15412))
- fix databasus update/install errors [@asylumexp](https://github.com/asylumexp) ([#15403](https://github.com/community-scripts/ProxmoxVE/pull/15403)) - fix databasus update/install errors [@asylumexp](https://github.com/asylumexp) ([#15403](https://github.com/community-scripts/ProxmoxVE/pull/15403))
### 💾 Core ### 💾 Core
- #### 🐞 Bug Fixes - #### 🐞 Bug Fixes
- tools.func: fix setup_docker - don't abort update on docker pull failure [@MickLesk](https://github.com/MickLesk) ([#15410](https://github.com/community-scripts/ProxmoxVE/pull/15410))
- fix(build.func): set /dev/kfd GID in fix_gpu_gids for AMD ROCm [@jamiej](https://github.com/jamiej) ([#15401](https://github.com/community-scripts/ProxmoxVE/pull/15401)) - fix(build.func): set /dev/kfd GID in fix_gpu_gids for AMD ROCm [@jamiej](https://github.com/jamiej) ([#15401](https://github.com/community-scripts/ProxmoxVE/pull/15401))
- fix alpine mktmp error [@asylumexp](https://github.com/asylumexp) ([#15398](https://github.com/community-scripts/ProxmoxVE/pull/15398)) - fix alpine mktmp error [@asylumexp](https://github.com/asylumexp) ([#15398](https://github.com/community-scripts/ProxmoxVE/pull/15398))
### 🧰 Tools
- #### 🔧 Refactor
- Refactor: reduce IP-Tag resource usage and clean up ShellCheck findings [@MickLesk](https://github.com/MickLesk) ([#15418](https://github.com/community-scripts/ProxmoxVE/pull/15418))
- QoL: kernel-clean: Validate kernel selection input [@MickLesk](https://github.com/MickLesk) ([#15414](https://github.com/community-scripts/ProxmoxVE/pull/15414))
- QoL: clean-lxcs exclude matching and set -e cancel handling [@MickLesk](https://github.com/MickLesk) ([#15413](https://github.com/community-scripts/ProxmoxVE/pull/15413))
- QoL: Harden microcode download/install in microcode and pbs-microcode [@MickLesk](https://github.com/MickLesk) ([#15415](https://github.com/community-scripts/ProxmoxVE/pull/15415))
- QoL: scaling-governor extend selection and guard missing cpufreq [@MickLesk](https://github.com/MickLesk) ([#15416](https://github.com/community-scripts/ProxmoxVE/pull/15416))
## 2026-06-25 ## 2026-06-25
### 🆕 New Scripts ### 🆕 New Scripts
+2 -2
View File
@@ -38,7 +38,7 @@ function update_script() {
create_backup /opt/degoog/.env \ create_backup /opt/degoog/.env \
/opt/degoog/data /opt/degoog/data
if [[ ! -x /root/.bun/bin/bun ]]; then if ! command -v bun >/dev/null 2>&1; then
msg_info "Installing Bun" msg_info "Installing Bun"
export BUN_INSTALL="/root/.bun" export BUN_INSTALL="/root/.bun"
curl -fsSL https://bun.sh/install | $STD bash curl -fsSL https://bun.sh/install | $STD bash
@@ -52,7 +52,7 @@ function update_script() {
msg_ok "Updated Valkey" msg_ok "Updated Valkey"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "degoog" "fccview/degoog" "prebuild" "latest" "/opt/degoog" "degoog_*_prebuild.tar.gz" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "degoog" "fccview/degoog" "prebuild" "latest" "/opt/degoog" "degoog_*_prebuild.tar.gz"
fetch_and_deploy_gh_release "curl-impersonate" "lexiforest/curl-impersonate" "prebuild" "latest" "/usr/local/bin" "curl-impersonate-v*.$(uname -m)-linux-gnu.tar.gz" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "curl-impersonate" "lexiforest/curl-impersonate" "prebuild" "latest" "/usr/local/bin" "curl-impersonate-v*.$(uname -m)-linux-gnu.tar.gz"
restore_backup restore_backup
+1 -1
View File
@@ -55,7 +55,7 @@ function update_script() {
eval "$(rbenv init - bash)" 2>/dev/null || true eval "$(rbenv init - bash)" 2>/dev/null || true
export RAILS_ENV=production export RAILS_ENV=production
export NODE_ENV=production export NODE_ENV=production
mkdir -p /opt/docuseal/tmp export SECRET_KEY_BASE_DUMMY=1
set -a set -a
source /opt/docuseal/.env source /opt/docuseal/.env
set +a set +a
+4 -1
View File
@@ -63,7 +63,10 @@ function update_script() {
cd /opt/endurain/backend cd /opt/endurain/backend
UV_VERSION=$(grep -Po 'required-version\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null || echo "0.11.18") UV_VERSION=$(grep -Po 'required-version\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null || echo "0.11.18")
UV_VERSION="$UV_VERSION" setup_uv UV_VERSION="$UV_VERSION" setup_uv
$STD uv sync --frozen --no-dev $STD poetry export -f requirements.txt --output requirements.txt --without-hashes
$STD uv venv --clear
$STD uv pip install -r requirements.txt
$STD uv pip install pytz
msg_ok "Backend Updated" msg_ok "Backend Updated"
msg_info "Starting Service" msg_info "Starting Service"
+8 -1
View File
@@ -83,7 +83,14 @@ msg_info "Setting up Backend"
cd /opt/endurain/backend cd /opt/endurain/backend
UV_VERSION=$(grep -Po 'required-version\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null || echo "0.11.18") UV_VERSION=$(grep -Po 'required-version\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null || echo "0.11.18")
UV_VERSION="$UV_VERSION" setup_uv UV_VERSION="$UV_VERSION" setup_uv
$STD uv sync --frozen --no-dev $STD uv tool install poetry
$STD uv tool update-shell
export PATH="/root/.local/bin:$PATH"
$STD poetry self add poetry-plugin-export
$STD poetry export -f requirements.txt --output requirements.txt --without-hashes
$STD uv venv --clear
$STD uv pip install -r requirements.txt
$STD uv pip install pytz
msg_ok "Setup Backend" msg_ok "Setup Backend"
msg_info "Creating Service" msg_info "Creating Service"
+7 -7
View File
@@ -4614,11 +4614,11 @@ EOF
local image=$(echo "$container" | awk '{print $2}') local image=$(echo "$container" | awk '{print $2}')
local current_digest=$(docker inspect "$name" --format='{{.Image}}' 2>/dev/null | cut -d':' -f2 | cut -c1-12) local current_digest=$(docker inspect "$name" --format='{{.Image}}' 2>/dev/null | cut -d':' -f2 | cut -c1-12)
# Pull latest image digest (ignore failures, e.g. local-only images or registry/permission issues) # Pull latest image digest
docker pull "$image" >/dev/null 2>&1 || true docker pull "$image" >/dev/null 2>&1
local latest_digest=$(docker inspect "$image" --format='{{.Id}}' 2>/dev/null | cut -d':' -f2 | cut -c1-12) local latest_digest=$(docker inspect "$image" --format='{{.Id}}' 2>/dev/null | cut -d':' -f2 | cut -c1-12)
if [ -n "$latest_digest" ] && [ "$current_digest" != "$latest_digest" ]; then if [ "$current_digest" != "$latest_digest" ]; then
containers_with_updates+=("$name") containers_with_updates+=("$name")
container_info+=("${index}) ${name} (${image})") container_info+=("${index}) ${name} (${image})")
((index++)) ((index++))
@@ -7561,8 +7561,8 @@ setup_nodejs() {
} }
# Install global Node modules # Install global Node modules
if [[ -n "$NODE_MODULE" ]] || ((node_major >= 25)); then if [[ -n "$NODE_MODULE" ]] || (( node_major >= 25 )); then
if ((node_major >= 25)) && [[ ",${NODE_MODULE}," != *",corepack,"* ]] && [[ "$NODE_MODULE" != corepack* ]]; then if (( node_major >= 25 )) && [[ ",${NODE_MODULE}," != *",corepack,"* ]] && [[ "$NODE_MODULE" != corepack* ]]; then
NODE_MODULE="${NODE_MODULE:+$NODE_MODULE,}corepack" NODE_MODULE="${NODE_MODULE:+$NODE_MODULE,}corepack"
fi fi
@@ -7624,12 +7624,12 @@ setup_nodejs() {
fi fi
fi fi
done done
if ((failed_modules > 0)); then if (( failed_modules > 0 )); then
msg_warn "$failed_modules Node.js module(s) failed: $NODE_MODULE" msg_warn "$failed_modules Node.js module(s) failed: $NODE_MODULE"
fi fi
fi fi
if [[ "$NODE_COREPACK_ENABLE" == "1" ]] && ((wants_corepack)) && command -v corepack >/dev/null 2>&1; then if [[ "$NODE_COREPACK_ENABLE" == "1" ]] && (( wants_corepack )) && command -v corepack >/dev/null 2>&1; then
msg_info "Enabling corepack" msg_info "Enabling corepack"
if $STD corepack enable 2>/dev/null; then if $STD corepack enable 2>/dev/null; then
msg_ok "Enabled corepack" msg_ok "Enabled corepack"
+32 -72
View File
@@ -22,10 +22,10 @@ APP="IP-Tag"
hostname=$(hostname) hostname=$(hostname)
# Color variables # Color variables
YW="\033[33m" YW=$(echo "\033[33m")
GN="\033[1;92m" GN=$(echo "\033[1;92m")
RD="\033[01;31m" RD=$(echo "\033[01;31m")
CL="\033[m" CL=$(echo "\033[m")
BFR="\\r\\033[K" BFR="\\r\\033[K"
HOLD=" " HOLD=" "
CM="${GN}${CL} " CM="${GN}${CL} "
@@ -127,7 +127,7 @@ update_installation() {
echo -e "\n${YW}Configuration file already exists.${CL}" echo -e "\n${YW}Configuration file already exists.${CL}"
echo -e "${YW}Note: No critical changes were made in this version.${CL}" echo -e "${YW}Note: No critical changes were made in this version.${CL}"
while true; do while true; do
read -rp "Do you want to replace it with defaults? (y/n): " yn read -p "Do you want to replace it with defaults? (y/n): " yn
case $yn in case $yn in
[Yy]*) [Yy]*)
interactive_config_setup interactive_config_setup
@@ -176,7 +176,7 @@ export FORCE_SINGLE_RUN=true
exec "$SCRIPT_FILE" exec "$SCRIPT_FILE"
EOF EOF
chmod +x /usr/local/bin/iptag-run chmod +x /usr/local/bin/iptag-run
msg_ok "Created iptag-run executable - You can execute this manually by entering 'iptag-run' in the Proxmox host, so the script is executed by hand." msg_ok "Created iptag-run executable - You can execute this manually by entering iptag-run in the Proxmox host, so the script is executed by hand."
msg_info "Restarting service" msg_info "Restarting service"
systemctl daemon-reload &>/dev/null systemctl daemon-reload &>/dev/null
@@ -208,7 +208,7 @@ install_command_only() {
else else
stop_spinner stop_spinner
echo -e "\n${YW}Configuration file already exists.${CL}" echo -e "\n${YW}Configuration file already exists.${CL}"
read -rp "Do you want to reconfigure tag format? (y/n): " reconfigure read -p "Do you want to reconfigure tag format? (y/n): " reconfigure
case $reconfigure in case $reconfigure in
[Yy]*) [Yy]*)
interactive_config_setup_command interactive_config_setup_command
@@ -285,7 +285,7 @@ interactive_config_setup_command() {
echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)" echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)"
while true; do while true; do
read -rp "Enter your choice (1-3) [1]: " tag_choice read -p "Enter your choice (1-3) [1]: " tag_choice
case ${tag_choice:-1} in case ${tag_choice:-1} in
1) 1)
TAG_FORMAT="last_two_octets" TAG_FORMAT="last_two_octets"
@@ -323,7 +323,7 @@ interactive_config_setup() {
echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)" echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)"
while true; do while true; do
read -rp "Enter your choice (1-3) [1]: " tag_choice read -p "Enter your choice (1-3) [1]: " tag_choice
case ${tag_choice:-1} in case ${tag_choice:-1} in
1) 1)
TAG_FORMAT="last_two_octets" TAG_FORMAT="last_two_octets"
@@ -352,7 +352,7 @@ interactive_config_setup() {
echo -e "${YW}Recommended range: 300-3600 seconds${CL}" echo -e "${YW}Recommended range: 300-3600 seconds${CL}"
while true; do while true; do
read -rp "Enter interval in seconds [300]: " interval_input read -p "Enter interval in seconds [300]: " interval_input
interval_input=${interval_input:-300} interval_input=${interval_input:-300}
if [[ $interval_input =~ ^[0-9]+$ ]] && [ $interval_input -ge 300 ] && [ $interval_input -le 7200 ]; then if [[ $interval_input =~ ^[0-9]+$ ]] && [ $interval_input -ge 300 ] && [ $interval_input -le 7200 ]; then
@@ -563,10 +563,9 @@ get_vm_ips() {
debug_log "vm $vmid: starting IP detection" debug_log "vm $vmid: starting IP detection"
# Check if VM is running first (status comes from the cached `qm list`, # Check if VM is running first
# falling back to `qm status` only when called outside the normal cycle). local vm_status=""
local vm_status="${STATUS_CACHE[vm_${vmid}]:-}" if command -v qm >/dev/null 2>&1; then
if [[ -z "$vm_status" ]] && command -v qm >/dev/null 2>&1; then
vm_status=$(qm status "$vmid" 2>/dev/null | awk '{print $2}') vm_status=$(qm status "$vmid" 2>/dev/null | awk '{print $2}')
fi fi
@@ -579,43 +578,33 @@ get_vm_ips() {
local mac_addresses=$(grep -E "^net[0-9]+:" "$vm_config" | grep -oE "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}" | head -3) local mac_addresses=$(grep -E "^net[0-9]+:" "$vm_config" | grep -oE "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}" | head -3)
debug_log "vm $vmid: found MACs: $mac_addresses" debug_log "vm $vmid: found MACs: $mac_addresses"
# Method 1: QEMU guest agent (most reliable for current IP). Only query it # Method 1: QM guest agent (most reliable for current IP)
# when the agent is actually enabled in the VM config, otherwise the call if command -v qm >/dev/null 2>&1; then
# blocks until the timeout on every VM without an agent. debug_log "vm $vmid: trying qm guest agent first"
local agent_enabled=0 local qm_ips=$(timeout 8 qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v "127.0.0.1" | head -3)
if [[ "$(grep -E '^agent:' "$vm_config" 2>/dev/null)" =~ (^agent:[[:space:]]*1|enabled=1) ]]; then
agent_enabled=1
fi
if [[ "$agent_enabled" == "1" ]] && command -v qm >/dev/null 2>&1; then
debug_log "vm $vmid: querying guest agent"
local qm_ips=$(timeout 5 qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v "127.0.0.1" | head -3)
for qm_ip in $qm_ips; do for qm_ip in $qm_ips; do
if [[ "$qm_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then if [[ "$qm_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
debug_log "vm $vmid: found IP $qm_ip via qm guest cmd" debug_log "vm $vmid: found IP $qm_ip via qm guest cmd"
ips+="$qm_ip " ips+="$qm_ip "
fi fi
done done
else
debug_log "vm $vmid: guest agent not enabled, skipping qm guest cmd"
fi fi
# Method 2: ARP table lookup (only if the guest agent gave us nothing). # Method 2: Fresh ARP table lookup (force refresh)
if [[ -z "$ips" && -n "$mac_addresses" ]]; then if [[ -n "$mac_addresses" ]]; then
debug_log "vm $vmid: checking ARP table" debug_log "vm $vmid: refreshing ARP table and checking"
# Snapshot the neighbor table once instead of per MAC # Try to refresh ARP table by pinging network ranges
local neigh_table
neigh_table=$(ip neighbor show 2>/dev/null)
for mac in $mac_addresses; do for mac in $mac_addresses; do
local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]') local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]')
# Check current ARP table # First check current ARP table
local current_ip=$(echo "$neigh_table" | grep "$mac_lower" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) local current_ip=$(ip neighbor show | grep "$mac_lower" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
# If found in ARP, verify it's still valid by trying to ping # If found in ARP, verify it's still valid by trying to ping
if [[ -n "$current_ip" && "$current_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then if [[ -n "$current_ip" && "$current_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
debug_log "vm $vmid: found IP $current_ip in ARP table for MAC $mac_lower, verifying..." debug_log "vm $vmid: found IP $current_ip in ARP table for MAC $mac_lower, verifying..."
# Quick ping test to verify IP is still active # Quick ping test to verify IP is still active
if timeout 1 ping -c 1 -W 1 "$current_ip" >/dev/null 2>&1; then if timeout 2 ping -c 1 "$current_ip" >/dev/null 2>&1; then
debug_log "vm $vmid: verified IP $current_ip is active via ping" debug_log "vm $vmid: verified IP $current_ip is active via ping"
ips+="$current_ip " ips+="$current_ip "
else else
@@ -639,7 +628,7 @@ get_vm_ips() {
if [[ -n "$dhcp_ip" && "$dhcp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then if [[ -n "$dhcp_ip" && "$dhcp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
debug_log "vm $vmid: found IP $dhcp_ip via DHCP leases" debug_log "vm $vmid: found IP $dhcp_ip via DHCP leases"
# Verify this IP responds # Verify this IP responds
if timeout 1 ping -c 1 -W 1 "$dhcp_ip" >/dev/null 2>&1; then if timeout 2 ping -c 1 "$dhcp_ip" >/dev/null 2>&1; then
debug_log "vm $vmid: verified DHCP IP $dhcp_ip is active" debug_log "vm $vmid: verified DHCP IP $dhcp_ip is active"
ips+="$dhcp_ip " ips+="$dhcp_ip "
break 2 break 2
@@ -663,9 +652,6 @@ get_vm_ips() {
# Cache for configs to avoid repeated reads # Cache for configs to avoid repeated reads
declare -A CONFIG_CACHE declare -A CONFIG_CACHE
declare -A IP_CACHE declare -A IP_CACHE
# Status cache populated once per check from `pct list` / `qm list` to avoid
# spawning an expensive `pct status` / `qm status` (Perl) per guest each cycle.
declare -A STATUS_CACHE
# Update tags for container or VM # Update tags for container or VM
update_tags() { update_tags() {
@@ -850,16 +836,7 @@ update_all_tags() {
# Get list of all containers/VMs # Get list of all containers/VMs
if [[ "$type" == "lxc" ]]; then if [[ "$type" == "lxc" ]]; then
# A single `pct list` call yields both the VMID list and the running vmids=($(pct list 2>/dev/null | grep -v VMID | awk '{print $1}'))
# status, so we never need a per-container `pct status` afterwards.
local pct_list_output
pct_list_output=$(pct list 2>/dev/null)
vmids=($(echo "$pct_list_output" | awk 'NR>1 {print $1}'))
local _vmid _status _rest
while read -r _vmid _status _rest; do
[[ "$_vmid" == "VMID" || -z "$_vmid" ]] && continue
STATUS_CACHE["lxc_${_vmid}"]="$_status"
done <<<"$pct_list_output"
else else
# More efficient: direct file listing instead of ls+sed # More efficient: direct file listing instead of ls+sed
vmids=() vmids=()
@@ -868,15 +845,6 @@ update_all_tags() {
local basename="${conf##*/}" local basename="${conf##*/}"
vmids+=("${basename%.conf}") vmids+=("${basename%.conf}")
done done
# A single `qm list` call yields the status for all VMs, avoiding a
# per-VM `qm status`.
if command -v qm >/dev/null 2>&1; then
local _vmid _name _status _rest
while read -r _vmid _name _status _rest; do
[[ "$_vmid" == "VMID" || -z "$_vmid" ]] && continue
STATUS_CACHE["vm_${_vmid}"]="$_status"
done <<<"$(qm list 2>/dev/null)"
fi
fi fi
count=${#vmids[@]} count=${#vmids[@]}
@@ -913,7 +881,6 @@ check() {
# Clear caches before each run # Clear caches before each run
CONFIG_CACHE=() CONFIG_CACHE=()
IP_CACHE=() IP_CACHE=()
STATUS_CACHE=()
# Update LXC containers # Update LXC containers
update_all_tags "lxc" update_all_tags "lxc"
@@ -958,12 +925,8 @@ get_lxc_ips() {
debug_log "lxc $vmid: starting IP detection" debug_log "lxc $vmid: starting IP detection"
# Check if LXC is running (status comes from the cached `pct list`, # Check if LXC is running
# falling back to `pct status` only when called outside the normal cycle). local lxc_status=$(pct status "${vmid}" 2>/dev/null | awk '{print $2}')
local lxc_status="${STATUS_CACHE[lxc_${vmid}]:-}"
if [[ -z "$lxc_status" ]]; then
lxc_status=$(pct status "${vmid}" 2>/dev/null | awk '{print $2}')
fi
if [[ "$lxc_status" != "running" ]]; then if [[ "$lxc_status" != "running" ]]; then
debug_log "lxc $vmid: not running (status: $lxc_status)" debug_log "lxc $vmid: not running (status: $lxc_status)"
return return
@@ -989,12 +952,9 @@ get_lxc_ips() {
if [[ -z "$ips" && -f "$pve_lxc_config" ]]; then if [[ -z "$ips" && -f "$pve_lxc_config" ]]; then
local mac_addrs=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | cut -d'=' -f2) local mac_addrs=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | cut -d'=' -f2)
if [[ -n "$mac_addrs" ]]; then if [[ -n "$mac_addrs" ]]; then
# Snapshot the neighbor table once instead of per MAC
local neigh_table
neigh_table=$(ip neighbor show 2>/dev/null)
while IFS= read -r mac_addr; do while IFS= read -r mac_addr; do
[[ -z "$mac_addr" ]] && continue [[ -z "$mac_addr" ]] && continue
local arp_ip=$(echo "$neigh_table" | grep -i "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) local arp_ip=$(ip neighbor show | grep -i "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
if [[ -n "$arp_ip" && "$arp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then if [[ -n "$arp_ip" && "$arp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
debug_log "lxc $vmid: found IP $arp_ip via ARP table for MAC $mac_addr" debug_log "lxc $vmid: found IP $arp_ip via ARP table for MAC $mac_addr"
ips="${ips}${ips:+ }${arp_ip}" ips="${ips}${ips:+ }${arp_ip}"
@@ -1036,7 +996,7 @@ echo -e "${GN}3)${CL} Update existing installation"
echo -e "${RD}4)${CL} Cancel" echo -e "${RD}4)${CL} Cancel"
while true; do while true; do
read -rp "Enter your choice (1-4): " choice read -p "Enter your choice (1-4): " choice
case $choice in case $choice in
1) 1)
INSTALL_MODE="service" INSTALL_MODE="service"
@@ -1065,7 +1025,7 @@ done
echo -e "\n${YW}This will install ${APP} on ${hostname} in $INSTALL_MODE mode.${CL}" echo -e "\n${YW}This will install ${APP} on ${hostname} in $INSTALL_MODE mode.${CL}"
while true; do while true; do
read -rp "Proceed? (y/n): " yn read -p "Proceed? (y/n): " yn
case $yn in case $yn in
[Yy]*) [Yy]*)
break break
@@ -1112,7 +1072,7 @@ if [[ "$INSTALL_MODE" == "service" ]]; then
else else
stop_spinner stop_spinner
echo -e "\n${YW}Configuration file already exists.${CL}" echo -e "\n${YW}Configuration file already exists.${CL}"
read -rp "Do you want to reconfigure tag format and loop interval? (y/n): " reconfigure read -p "Do you want to reconfigure tag format and loop interval? (y/n): " reconfigure
case $reconfigure in case $reconfigure in
[Yy]*) [Yy]*)
interactive_config_setup interactive_config_setup
+8 -15
View File
@@ -30,7 +30,7 @@ declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "clean-lxcs" "
header_info header_info
echo "Loading..." echo "Loading..."
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This will clean logs, cache and update package lists on selected LXC Containers. Proceed?" 10 58 || exit 0 whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This will clean logs, cache and update package lists on selected LXC Containers. Proceed?" 10 58
NODE=$(hostname) NODE=$(hostname)
EXCLUDE_MENU=() EXCLUDE_MENU=()
@@ -42,17 +42,17 @@ while read -r TAG ITEM; do
EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF")
done < <(pct list | awk 'NR>1') done < <(pct list | awk 'NR>1')
# Capture the selection; abort cleanly if the user cancels the dialog excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from cleaning:\n" \
# (set -e would otherwise terminate on the failing command substitution). 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
if ! excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from cleaning:\n" \
16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"'); then if [ $? -ne 0 ]; then
exit 0 exit
fi fi
function run_lxc_clean() { function run_lxc_clean() {
local container=$1 local container=$1
header_info header_info
name=$(pct exec "$container" -- hostname) name=$(pct exec "$container" hostname)
pct exec "$container" -- bash -c ' pct exec "$container" -- bash -c '
BL="\033[36m"; GN="\033[1;92m"; CL="\033[m" BL="\033[36m"; GN="\033[1;92m"; CL="\033[m"
@@ -84,14 +84,7 @@ function run_lxc_clean() {
} }
for container in $(pct list | awk '{if(NR>1) print $1}'); do for container in $(pct list | awk '{if(NR>1) print $1}'); do
excluded=0 if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then
for ex in $excluded_containers; do
if [[ "$ex" == "$container" ]]; then
excluded=1
break
fi
done
if [[ "$excluded" -eq 1 ]]; then
header_info header_info
echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}"
sleep 1 sleep 1
+158
View File
@@ -0,0 +1,158 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/refs/heads/main/misc/core.func)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) 2>/dev/null || true
load_functions
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "iommu-setup" "pve"
function header_info {
clear
cat <<"EOF"
____ ____ __ _____ __ ____ _____ __
/ _/ / __ \/ |/ / |/ / / / / / ___/___ / /___ ______
/ / / / / / /|_/ / /|_/ / / / / \__ \/ _ \/ __/ / / / __ \
_/ / / /_/ / / / / / / / /_/ / ___/ / __/ /_/ /_/ / /_/ /
/___/ \____/_/ /_/_/ /_/\____/ /____/\___/\__/\__,_/ .___/
/_/
EOF
}
header_info
# Guards
if [ "$(id -u)" -ne 0 ]; then
msg_error "This script must be run as root."
exit 1
fi
if ! command -v pveversion >/dev/null 2>&1; then
msg_error "No Proxmox VE detected!"
exit 1
fi
if ! pveversion | grep -Eq "pve-manager/(8\.[0-4]|9\.[0-9]+)(\.[0-9]+)*"; then
msg_error "This version of Proxmox Virtual Environment is not supported."
msg_error "Requires Proxmox Virtual Environment Version 8.0-8.4 or 9.x."
exit 1
fi
# systemd-detect-virt prints "none" but exits non-zero on bare metal, so a
# `|| echo none` fallback would duplicate the value; capture output as-is.
virt=$(systemd-detect-virt 2>/dev/null)
if [ -n "$virt" ] && [ "$virt" != "none" ]; then
msg_error "IOMMU/PCI passthrough must be configured on bare metal. Detected: $virt"
exit 1
fi
# Whether a kernel parameter is already present in a cmdline string
has_token() {
case " $1 " in
*" $2 "*) return 0 ;;
*) return 1 ;;
esac
}
# Detect CPU vendor and the matching kernel parameters
cpu_vendor=$(lscpu | grep -oP 'Vendor ID:\s*\K\S+' | head -n 1)
case "$cpu_vendor" in
GenuineIntel) IOMMU_PARAMS=("intel_iommu=on" "iommu=pt") ;;
AuthenticAMD) IOMMU_PARAMS=("amd_iommu=on" "iommu=pt") ;;
*)
msg_error "Unsupported CPU vendor: ${cpu_vendor:-unknown}"
exit 1
;;
esac
# Report current IOMMU state
iommu_active="no"
if [ -d /sys/kernel/iommu_groups ] && [ -n "$(ls -A /sys/kernel/iommu_groups 2>/dev/null)" ]; then
iommu_active="yes"
fi
echo -e "${BL}CPU vendor:${CL} ${cpu_vendor}"
echo -e "${BL}IOMMU active:${CL} $([ "$iommu_active" = "yes" ] && echo -e "${GN}yes${CL}" || echo -e "${RD}no${CL}")"
echo -e "${BL}Kernel params:${CL} ${IOMMU_PARAMS[*]}"
echo
if [ "$iommu_active" = "yes" ]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "IOMMU Already Active" \
--yesno "IOMMU already appears to be active on this host.\n\nDo you still want to (re)apply the kernel parameters and vfio modules?" 12 70 || {
echo -e "${GN}Nothing to do.${CL}"
exit 0
}
else
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Enable IOMMU / PCI(e) Passthrough" \
--yesno "This will enable IOMMU for PCI(e) passthrough by:\n\n - adding '${IOMMU_PARAMS[*]}' to the kernel command line\n - loading the vfio kernel modules\n\nA reboot is required afterwards. A backup of the modified boot config is created.\n\nProceed?" 16 74 || exit 0
fi
# Determine the boot configuration in use
# proxmox-boot-tool managed systems (ZFS / UEFI) use /etc/kernel/cmdline,
# everything else uses GRUB via /etc/default/grub.
if command -v proxmox-boot-tool >/dev/null 2>&1 && [ -f /etc/kernel/cmdline ]; then
BOOT_MODE="systemd-boot"
else
BOOT_MODE="grub"
fi
apply_grub() {
local file="/etc/default/grub" current merged
cp -a "$file" "${file}.bak.$(date +%Y%m%d%H%M%S)"
if grep -q '^GRUB_CMDLINE_LINUX_DEFAULT=' "$file"; then
current=$(sed -n 's/^GRUB_CMDLINE_LINUX_DEFAULT=//p' "$file" | tail -1)
current="${current%\"}"
current="${current#\"}"
else
current=""
fi
merged="$current"
for tok in "${IOMMU_PARAMS[@]}"; do
has_token "$merged" "$tok" || merged="${merged:+$merged }$tok"
done
if grep -q '^GRUB_CMDLINE_LINUX_DEFAULT=' "$file"; then
sed -i "s|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"${merged}\"|" "$file"
else
echo "GRUB_CMDLINE_LINUX_DEFAULT=\"${merged}\"" >>"$file"
fi
update-grub &>/dev/null
}
apply_systemd_boot() {
local file="/etc/kernel/cmdline" current merged
cp -a "$file" "${file}.bak.$(date +%Y%m%d%H%M%S)"
current=$(tr -d '\n' <"$file")
merged="$current"
for tok in "${IOMMU_PARAMS[@]}"; do
has_token "$merged" "$tok" || merged="${merged:+$merged }$tok"
done
echo "$merged" >"$file"
proxmox-boot-tool refresh &>/dev/null
}
msg_info "Applying kernel parameters via ${BOOT_MODE}"
if [ "$BOOT_MODE" = "systemd-boot" ]; then
apply_systemd_boot
else
apply_grub
fi
msg_ok "Applied kernel parameters (${BOOT_MODE})"
# Load vfio modules at boot (vfio_virqfd was merged into the core in
# kernel 6.2+, so it is intentionally not added here)
msg_info "Configuring vfio modules"
for m in vfio vfio_iommu_type1 vfio_pci; do
grep -qxF "$m" /etc/modules 2>/dev/null || echo "$m" >>/etc/modules
done
msg_ok "Configured vfio modules"
echo -e "\n${GN}IOMMU configuration written.${CL}"
echo -e "${YW}A reboot is required to activate IOMMU.${CL}"
echo -e "After rebooting, verify with: ${BL}dmesg | grep -e DMAR -e IOMMU${CL}"
echo -e "and list groups with: ${BL}find /sys/kernel/iommu_groups/ -type l${CL}\n"
+4 -14
View File
@@ -55,23 +55,12 @@ read -r selected
selected_indices=() selected_indices=()
IFS=',' read -r -a tokens <<<"$selected" IFS=',' read -r -a tokens <<<"$selected"
for token in "${tokens[@]}"; do for token in "${tokens[@]}"; do
# Strip surrounding whitespace and skip empty tokens
token="${token//[[:space:]]/}"
[ -z "$token" ] && continue
if [[ "$token" =~ ^([0-9]+)-([0-9]+)$ ]]; then if [[ "$token" =~ ^([0-9]+)-([0-9]+)$ ]]; then
start=${BASH_REMATCH[1]} for ((i = BASH_REMATCH[1]; i <= BASH_REMATCH[2]; i++)); do
end=${BASH_REMATCH[2]}
if ((start > end)); then
echo -e "${RD}Ignoring invalid range '${token}' (start greater than end).${CL}"
continue
fi
for ((i = start; i <= end; i++)); do
selected_indices+=("$i") selected_indices+=("$i")
done done
elif [[ "$token" =~ ^[0-9]+$ ]]; then
selected_indices+=("$token")
else else
echo -e "${RD}Ignoring invalid selection '${token}'.${CL}" selected_indices+=("$token")
fi fi
done done
@@ -112,7 +101,8 @@ for kernel in "${kernels_to_remove[@]}"; do
remaining=$(dpkg --list | remaining=$(dpkg --list |
awk '/^ii/ {print $2}' | awk '/^ii/ {print $2}' |
grep -E "^proxmox-kernel-${minor_version}\." | grep -E "^proxmox-kernel-${minor_version}\." |
grep -cv "^${kernel}$") grep -v "^${kernel}$" |
wc -l)
if [ "$remaining" -eq 0 ]; then if [ "$remaining" -eq 0 ]; then
pkgs_to_remove+=("$meta") pkgs_to_remove+=("$meta")
fi fi
+11 -11
View File
@@ -16,10 +16,10 @@ function header_info {
EOF EOF
} }
RD="\033[01;31m" RD=$(echo "\033[01;31m")
YW="\033[33m" YW=$(echo "\033[33m")
GN="\033[1;92m" GN=$(echo "\033[1;92m")
CL="\033[m" CL=$(echo "\033[m")
BFR="\\r\\033[K" BFR="\\r\\033[K"
HOLD="-" HOLD="-"
CM="${GN}${CL}" CM="${GN}${CL}"
@@ -47,7 +47,7 @@ intel() {
sleep 1 sleep 1
fi fi
intel_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode/" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//') intel_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/i/intel-microcode//" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//')
[ -z "$intel_microcode" ] && { [ -z "$intel_microcode" ] && {
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Microcode Found" --msgbox "It appears there were no microcode packages found\n Try again later." 10 68 whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Microcode Found" --msgbox "It appears there were no microcode packages found\n Try again later." 10 68
msg_info "Exiting" msg_info "Exiting"
@@ -80,17 +80,17 @@ intel() {
msg_ok "Downloaded the Intel Processor Microcode Package $microcode" msg_ok "Downloaded the Intel Processor Microcode Package $microcode"
msg_info "Installing $microcode (Patience)" msg_info "Installing $microcode (Patience)"
dpkg -i "$microcode" &>/dev/null dpkg -i $microcode &>/dev/null
msg_ok "Installed $microcode" msg_ok "Installed $microcode"
msg_info "Cleaning up" msg_info "Cleaning up"
rm -f "$microcode" rm $microcode
msg_ok "Cleaned" msg_ok "Cleaned"
echo -e "\nIn order to apply the changes, a system reboot will be necessary.\n" echo -e "\nIn order to apply the changes, a system reboot will be necessary.\n"
} }
amd() { amd() {
amd_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//') amd_microcode=$(curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode///" | grep -o 'href="[^"]*amd64.deb"' | sed 's/href="//;s/"//')
[ -z "$amd_microcode" ] && { [ -z "$amd_microcode" ] && {
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Microcode Found" --msgbox "It appears there were no microcode packages found\n Try again later." 10 68 whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Microcode Found" --msgbox "It appears there were no microcode packages found\n Try again later." 10 68
@@ -120,15 +120,15 @@ amd() {
} }
msg_info "Downloading the AMD Processor Microcode Package $microcode" msg_info "Downloading the AMD Processor Microcode Package $microcode"
curl -fsSL --proto '=https' "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode" -o "$microcode" curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode" -o $(basename "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode")
msg_ok "Downloaded the AMD Processor Microcode Package $microcode" msg_ok "Downloaded the AMD Processor Microcode Package $microcode"
msg_info "Installing $microcode (Patience)" msg_info "Installing $microcode (Patience)"
dpkg -i "$microcode" &>/dev/null dpkg -i $microcode &>/dev/null
msg_ok "Installed $microcode" msg_ok "Installed $microcode"
msg_info "Cleaning up" msg_info "Cleaning up"
rm -f "$microcode" rm $microcode
msg_ok "Cleaned" msg_ok "Cleaned"
echo -e "\nIn order to apply the changes, a system reboot will be necessary.\n" echo -e "\nIn order to apply the changes, a system reboot will be necessary.\n"
} }
+9 -9
View File
@@ -18,10 +18,10 @@ EOF
} }
# Color definitions # Color definitions
RD="\033[01;31m" RD=$(echo "\033[01;31m")
YW="\033[33m" YW=$(echo "\033[33m")
GN="\033[1;92m" GN=$(echo "\033[1;92m")
CL="\033[m" CL=$(echo "\033[m")
BFR="\\r\\033[K" BFR="\\r\\033[K"
HOLD="-" HOLD="-"
CM="${GN}${CL}" CM="${GN}${CL}"
@@ -94,11 +94,11 @@ intel() {
msg_ok "Downloaded Intel processor microcode package $microcode" msg_ok "Downloaded Intel processor microcode package $microcode"
msg_info "Installing $microcode (this might take a while)" msg_info "Installing $microcode (this might take a while)"
dpkg -i "$microcode" &>/dev/null dpkg -i $microcode &>/dev/null
msg_ok "Installed $microcode" msg_ok "Installed $microcode"
msg_info "Cleaning up" msg_info "Cleaning up"
rm -f "$microcode" rm $microcode
msg_ok "Clean up complete" msg_ok "Clean up complete"
echo -e "\nA system reboot is required to apply the changes.\n" echo -e "\nA system reboot is required to apply the changes.\n"
} }
@@ -137,15 +137,15 @@ amd() {
} }
msg_info "Downloading AMD processor microcode package $microcode" msg_info "Downloading AMD processor microcode package $microcode"
curl -fsSL --proto '=https' "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode" -o "$microcode" curl -fsSL "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode" -o $(basename "https://ftp.debian.org/debian/pool/non-free-firmware/a/amd64-microcode/$microcode")
msg_ok "Downloaded AMD processor microcode package $microcode" msg_ok "Downloaded AMD processor microcode package $microcode"
msg_info "Installing $microcode (this might take a while)" msg_info "Installing $microcode (this might take a while)"
dpkg -i "$microcode" &>/dev/null dpkg -i $microcode &>/dev/null
msg_ok "Installed $microcode" msg_ok "Installed $microcode"
msg_info "Cleaning up" msg_info "Cleaning up"
rm -f "$microcode" rm $microcode
msg_ok "Clean up complete" msg_ok "Clean up complete"
echo -e "\nA system reboot is required to apply the changes.\n" echo -e "\nA system reboot is required to apply the changes.\n"
} }
+5 -15
View File
@@ -20,26 +20,16 @@ header_info() {
EOF EOF
} }
header_info header_info
whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Governors" --yesno "View/Change CPU Scaling Governors. Proceed?" 10 58 || exit 0 whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Governors" --yesno "View/Change CPU Scaling Governors. Proceed?" 10 58
current_governor=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor)
GOV_BASE="/sys/devices/system/cpu/cpu0/cpufreq"
if [[ ! -r "$GOV_BASE/scaling_governor" || ! -r "$GOV_BASE/scaling_available_governors" ]]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU Scaling Not Available" \
--msgbox "CPU frequency scaling is not available on this system.\n\nThis is normal when no cpufreq driver is active (e.g. CPU power management handled by the BIOS, or certain virtualized hosts)." 12 70
clear
exit 0
fi
current_governor=$(cat "$GOV_BASE/scaling_governor")
GOVERNORS_MENU=() GOVERNORS_MENU=()
MSG_MAX_LENGTH=0 MSG_MAX_LENGTH=0
while read -r TAG ITEM; do while read -r TAG ITEM; do
OFFSET=2 OFFSET=2
((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET
GOVERNORS_MENU+=("$TAG" "$ITEM " "OFF") GOVERNORS_MENU+=("$TAG" "$ITEM " "OFF")
done < <(tr ' ' '\n' <"$GOV_BASE/scaling_available_governors" | sed '/^$/d' | grep -vxF "$current_governor") done < <(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors | tr ' ' '\n' | grep -v "$current_governor")
# A radiolist is used on purpose: only a single governor can be active at a time. scaling_governor=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current CPU Scaling Governor is set to $current_governor" --checklist "\nSelect the Scaling Governor to use:\n" 16 $((MSG_MAX_LENGTH + 58)) 6 "${GOVERNORS_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
scaling_governor=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Current CPU Scaling Governor is set to $current_governor" --radiolist "\nSelect the Scaling Governor to use:\n" 16 $((MSG_MAX_LENGTH + 58)) 6 "${GOVERNORS_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"')
[ -z "$scaling_governor" ] && { [ -z "$scaling_governor" ] && {
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No CPU Scaling Governor Selected" --msgbox "It appears that no CPU Scaling Governor was selected" 10 68 whiptail --backtitle "Proxmox VE Helper Scripts" --title "No CPU Scaling Governor Selected" --msgbox "It appears that no CPU Scaling Governor was selected" 10 68
clear clear
@@ -59,7 +49,7 @@ yes)
EXISTING_CRONTAB=$(crontab -l 2>/dev/null) EXISTING_CRONTAB=$(crontab -l 2>/dev/null)
if [[ -n "$EXISTING_CRONTAB" ]]; then if [[ -n "$EXISTING_CRONTAB" ]]; then
TEMP_CRONTAB_FILE=$(mktemp) TEMP_CRONTAB_FILE=$(mktemp)
echo "$EXISTING_CRONTAB" | grep -vF "@reboot (sleep 60 && echo" >"$TEMP_CRONTAB_FILE" echo "$EXISTING_CRONTAB" | grep -v "@reboot (sleep 60 && echo*" >"$TEMP_CRONTAB_FILE"
crontab "$TEMP_CRONTAB_FILE" crontab "$TEMP_CRONTAB_FILE"
rm "$TEMP_CRONTAB_FILE" rm "$TEMP_CRONTAB_FILE"
fi fi