Compare commits

..

4 Commits

Author SHA1 Message Date
MickLesk 493c3244fd Use community header for firmware-update instead of inline banner
Move the ASCII art into tools/headers/firmware-update and load it via
APP/Firmware-Update + APP_TYPE=tools + core.func header_info, matching
pve-privilege-converter and add-iptag instead of a local header_info
override.
2026-06-26 22:43:20 +02:00
MickLesk 2bd2833c07 Fix firmware-update interactive prompt leak and source core.func
Runtime fix:
- fwupd's own "metadata is 30 days old, update now?" prompt was captured
  into the command output and garbled the report. Pass --no-metadata-check
  (and -y) to get-devices/get-updates so the checks run non-interactively.
- Detect the common Proxmox case of no mounted EFI System Partition and
  state clearly that UEFI/BIOS capsule updates cannot be staged, instead of
  burying the warning; also treat "No updatable devices" as nothing-to-do.

Consistency:
- Source core.func + load_functions for the shared color/msg_* helpers
  instead of duplicating them locally (api.func still provides telemetry),
  matching update-apps and pve-privilege-converter.
2026-06-26 22:01:45 +02:00
MickLesk ab14266389 Fix bare-metal detection in firmware-update
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:49:35 +02:00
MickLesk 6f08f3dede Add firmware-update tool (fwupd / LVFS)
New PVE host tool to check for and optionally apply firmware updates
(UEFI/BIOS and supported devices) via fwupd and the Linux Vendor Firmware
Service. Complements microcode.sh, which only handles volatile CPU
microcode.

- Guards for root, supported PVE 8.x/9.x and bare metal (firmware flashing
  inside a VM is refused).
- Installs fwupd on demand, refreshes LVFS metadata, lists devices and
  available updates, and only applies them after explicit confirmation.
- Clear warnings about flashing risk and reboot requirements.
2026-06-26 21:39:21 +02:00
15 changed files with 220 additions and 169 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"
+7 -2
View File
@@ -33,12 +33,17 @@ function update_script() {
systemctl stop tunarr systemctl stop tunarr
msg_ok "Stopped Service" msg_ok "Stopped Service"
create_backup /root/.local/share/tunarr msg_info "Creating Backup"
if [ -d "/root/.local/share/tunarr" ]; then
tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /root/.local/share/tunarr $STD
msg_ok "Backup Created"
else
msg_error "Backup failed: /root/.local/share/tunarr does not exist"
fi
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "prebuild" "latest" "/opt/tunarr" "*linux-$(arch_resolve "x64" "arm64").tar.gz" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "prebuild" "latest" "/opt/tunarr" "*linux-$(arch_resolve "x64" "arm64").tar.gz"
cd /opt/tunarr cd /opt/tunarr
mv tunarr* tunarr mv tunarr* tunarr
restore_backup
msg_info "Starting Service" msg_info "Starting Service"
systemctl start tunarr systemctl start tunarr
+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"
+6
View File
@@ -0,0 +1,6 @@
_______ __ __ __ __
/ ____(_)________ _ ______ _________ / / / /___ ____/ /___ _/ /____
/ /_ / / ___/ __ `/ | /| / / __ `/ ___/ _ \ / / / / __ \/ __ / __ `/ __/ _ \
/ __/ / / / / /_/ /| |/ |/ / /_/ / / / __/ / /_/ / /_/ / /_/ / /_/ / /_/ __/
/_/ /_/_/ \__,_/ |__/|__/\__,_/_/ \___/ \____/ .___/\__,_/\__,_/\__/\___/
/_/
+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
+116
View File
@@ -0,0 +1,116 @@
#!/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 "firmware-update" "pve"
APP="Firmware-Update"
APP_TYPE="tools"
header_info "$APP"
# Must run as root
if [ "$(id -u)" -ne 0 ]; then
msg_error "This script must be run as root."
exit 1
fi
# Must run on Proxmox VE 8.x or 9.x
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
# Firmware updates only make sense on bare metal. systemd-detect-virt prints
# "none" but exits non-zero on bare metal, so a `|| echo none` fallback would
# duplicate the value; capture the output as-is instead.
virt=$(systemd-detect-virt 2>/dev/null)
if [ -n "$virt" ] && [ "$virt" != "none" ]; then
msg_error "Firmware updates can only be applied on bare metal. Detected: $virt"
exit 1
fi
# Only x86_64/arm64 with UEFI/LVFS support; fwupd itself handles capability checks
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Firmware Update (fwupd / LVFS)" \
--yesno "This tool uses fwupd to check for and optionally install firmware updates (UEFI/BIOS and supported devices) from the Linux Vendor Firmware Service (LVFS).\n\nWARNING: Flashing firmware carries risk. Ensure stable power and do not interrupt the process. Some updates require a reboot to be applied.\n\nProceed?" 16 70 || exit 0
# Install fwupd if missing
if ! command -v fwupdmgr >/dev/null 2>&1; then
msg_info "Installing fwupd"
apt-get update &>/dev/null
if ! apt-get install -y fwupd &>/dev/null; then
msg_error "Failed to install fwupd"
exit 1
fi
msg_ok "Installed fwupd"
else
msg_ok "fwupd is already installed"
fi
# Refresh metadata from LVFS
msg_info "Refreshing firmware metadata from LVFS"
if fwupdmgr refresh --force &>/dev/null; then
msg_ok "Refreshed firmware metadata"
else
# A failed refresh is not fatal (cached metadata may still be usable)
msg_error "Could not refresh metadata (continuing with cached data)"
fi
# Show detected, updatable devices. --no-metadata-check / -y keep fwupd from
# prompting interactively (e.g. the "metadata is 30 days old, update now?"
# question) which would otherwise block and garble the output.
echo -e "\n${YW}Detected devices with firmware management support:${CL}\n"
fwupdmgr get-devices --no-unreported-check --no-metadata-check 2>/dev/null || true
echo
# Check for available updates (non-interactive)
msg_info "Checking for available firmware updates"
updates_output=$(fwupdmgr get-updates --no-unreported-check --no-metadata-check -y 2>&1)
updates_rc=$?
msg_ok "Checked for firmware updates"
# Many Proxmox hosts have no permanently mounted EFI System Partition (e.g.
# ZFS root managed by proxmox-boot-tool). Without it, fwupd cannot stage
# UEFI/BIOS capsule updates, so make that explicit instead of burying it.
if echo "$updates_output" | grep -qi "ESP partition not detected"; then
echo -e "${YW}Note:${CL} No mounted EFI System Partition (ESP) was detected."
echo -e " UEFI/BIOS capsule firmware updates cannot be staged on this host."
echo -e " Device firmware (e.g. NVMe/SSD) can still be updated if offered.\n"
fi
if [ "$updates_rc" -ne 0 ] || echo "$updates_output" | grep -qiE "No (updates|upgrades) available|Devices with no available firmware updates|No updatable devices"; then
whiptail --backtitle "Proxmox VE Helper Scripts" --title "No Firmware Updates" \
--msgbox "No applicable firmware updates were found for this system." 10 68
echo -e "${GN}Nothing to do.${CL}"
exit 0
fi
echo -e "\n${YW}Available firmware updates:${CL}\n"
echo "$updates_output"
echo
whiptail --backtitle "Proxmox VE Helper Scripts" --title "Apply Firmware Updates" \
--yesno "Firmware updates are available (see terminal output).\n\nDo you want to apply them now?\n\nNOTE: Some updates schedule a flash on the next reboot. Do NOT power off during the process." 14 70 || {
echo -e "${YW}Skipped applying updates.${CL}"
exit 0
}
msg_info "Applying firmware updates (this may take a while)"
echo
if fwupdmgr update -y; then
msg_ok "Firmware update process completed"
echo -e "\n${YW}A reboot may be required to finalize some firmware updates.${CL}\n"
else
msg_error "Firmware update reported an error. Review the output above."
exit 1
fi
+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