rework apply and revert patches to patch
This commit is contained in:
parent
5ce7fc928d
commit
d74e016f90
2
debian/control
vendored
2
debian/control
vendored
@ -8,7 +8,7 @@ Homepage: https://github.com/Meliox/PVE-mods
|
||||
|
||||
Package: pve-mod
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, perl, librrds-perl
|
||||
Depends: ${misc:Depends}, perl, librrds-perl, patch
|
||||
Recommends: lm-sensors, nut-client
|
||||
Suggests: intel-gpu-tools
|
||||
Description: Proxmox VE UI modifications and sensor monitoring
|
||||
|
||||
2
debian/pve-mod.conffiles
vendored
2
debian/pve-mod.conffiles
vendored
@ -1 +1,3 @@
|
||||
/etc/pve-mod/pve-mod.conf
|
||||
/etc/pve-mod/conf.d/node_info.conf
|
||||
/etc/pve-mod/conf.d/nag_screen.conf
|
||||
|
||||
50
debian/pve-mod.postinst
vendored
50
debian/pve-mod.postinst
vendored
@ -1,9 +1,15 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
NODES_PM="/usr/share/perl5/PVE/API2/Nodes.pm"
|
||||
DEFAULT_CONF="/usr/share/pve-mod/pve-mod.conf.default"
|
||||
USER_CONF="/etc/pve-mod/pve-mod.conf"
|
||||
DEFAULT_DIR="/usr/share/pve-mod"
|
||||
CONF_DIR="/etc/pve-mod"
|
||||
|
||||
# Config file pairs to compare on upgrade: "<default-file>|<user-file>"
|
||||
CONF_PAIRS="
|
||||
${DEFAULT_DIR}/pve-mod.conf.default|${CONF_DIR}/pve-mod.conf
|
||||
${DEFAULT_DIR}/conf.d/node_info.conf.default|${CONF_DIR}/conf.d/node_info.conf
|
||||
${DEFAULT_DIR}/conf.d/nag_screen.conf.default|${CONF_DIR}/conf.d/nag_screen.conf
|
||||
"
|
||||
|
||||
# Extracts "section.key" pairs from an INI file, one per line.
|
||||
_extract_conf_keys() {
|
||||
@ -17,30 +23,48 @@ _extract_conf_keys() {
|
||||
done < "$file"
|
||||
}
|
||||
|
||||
# Warns about keys present in the default config but missing from the user's config.
|
||||
_check_new_config_keys() {
|
||||
[ -f "$DEFAULT_CONF" ] || return 0
|
||||
[ -f "$USER_CONF" ] || return 0
|
||||
# Warns about keys present in a default config but missing from the user's copy.
|
||||
_check_one_pair() {
|
||||
local default_conf="$1" user_conf="$2"
|
||||
[ -f "$default_conf" ] || return 0
|
||||
[ -f "$user_conf" ] || return 0
|
||||
|
||||
local new_keys="" section="" key default_val
|
||||
local new_keys="" section="" key line
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
'#'*|'') continue ;;
|
||||
'['*']') section="${line#[}"; section="${section%]}"; continue ;;
|
||||
*'='*)
|
||||
key="${line%%=*}"
|
||||
if ! _extract_conf_keys "$USER_CONF" | grep -qF "${section}.${key}"; then
|
||||
new_keys="${new_keys} [${section}] ${line}\n"
|
||||
if ! _extract_conf_keys "$user_conf" | grep -qF "${section}.${key}"; then
|
||||
new_keys="${new_keys} [${section}] ${line} (${user_conf})\n"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done < "$DEFAULT_CONF"
|
||||
done < "$default_conf"
|
||||
|
||||
if [ -n "$new_keys" ]; then
|
||||
printf "%b" "$new_keys"
|
||||
fi
|
||||
}
|
||||
|
||||
# Warns about any new config keys across the main config and all per-mod configs.
|
||||
_check_new_config_keys() {
|
||||
local all_new="" pair default_conf user_conf
|
||||
while IFS= read -r pair; do
|
||||
[ -n "$pair" ] || continue
|
||||
default_conf="${pair%%|*}"
|
||||
user_conf="${pair#*|}"
|
||||
all_new="${all_new}$(_check_one_pair "$default_conf" "$user_conf")"
|
||||
done <<EOF
|
||||
$CONF_PAIRS
|
||||
EOF
|
||||
|
||||
if [ -n "$all_new" ]; then
|
||||
echo ""
|
||||
echo "pve-mod: New configuration options are available since your last install:"
|
||||
printf "%b" "$new_keys"
|
||||
echo "pve-mod: Run 'pve-mod-configure' to configure them, or add them manually to $USER_CONF"
|
||||
printf "%b" "$all_new"
|
||||
echo "pve-mod: Run 'pve-mod-configure' to configure them, or add them manually."
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
39
debian/rules
vendored
39
debian/rules
vendored
@ -31,17 +31,46 @@ override_dh_install:
|
||||
# JS module (rename to match loader reference)
|
||||
install -Dm644 src/PVENodeInfo/PveMod_pvemanagerlib.js \
|
||||
debian/pve-mod/usr/share/pve-manager/js/PveMod_PveNodeStatusView.js
|
||||
# Patch helpers
|
||||
install -Dm755 src/PVENodeInfo/apply-patches.sh \
|
||||
# Patch helpers (generic, manifest-driven)
|
||||
install -Dm755 src/Scripts/apply-patches.sh \
|
||||
debian/pve-mod/usr/lib/pve-mod/apply-patches.sh
|
||||
install -Dm755 src/PVENodeInfo/revert-patches.sh \
|
||||
install -Dm755 src/Scripts/revert-patches.sh \
|
||||
debian/pve-mod/usr/lib/pve-mod/revert-patches.sh
|
||||
# node_info mod patches + manifest + hooks
|
||||
install -Dm644 src/PVENodeInfo/patches/patches.list \
|
||||
debian/pve-mod/usr/lib/pve-mod/patches/node_info/patches.list
|
||||
install -Dm644 src/PVENodeInfo/patches/01-nodes-pm-sensors.patch \
|
||||
debian/pve-mod/usr/lib/pve-mod/patches/node_info/01-nodes-pm-sensors.patch
|
||||
install -Dm644 src/PVENodeInfo/patches/02-nodes-pm-GPU-RRD-history.patch \
|
||||
debian/pve-mod/usr/lib/pve-mod/patches/node_info/02-nodes-pm-GPU-RRD-history.patch
|
||||
install -Dm644 src/PVENodeInfo/patches/03-pvemanager-js-sensors.patch \
|
||||
debian/pve-mod/usr/lib/pve-mod/patches/node_info/03-pvemanager-js-sensors.patch
|
||||
install -Dm755 src/PVENodeInfo/patches/post-apply.sh \
|
||||
debian/pve-mod/usr/lib/pve-mod/patches/node_info/post-apply.sh
|
||||
# nag_screen mod patches + manifest + hooks
|
||||
install -Dm644 src/NagScreen/patches/patches.list \
|
||||
debian/pve-mod/usr/lib/pve-mod/patches/nag_screen/patches.list
|
||||
install -Dm644 src/NagScreen/patches/01-proxmoxlib-js-nagscreen.patch \
|
||||
debian/pve-mod/usr/lib/pve-mod/patches/nag_screen/01-proxmoxlib-js-nagscreen.patch
|
||||
install -Dm755 src/NagScreen/patches/post-apply.sh \
|
||||
debian/pve-mod/usr/lib/pve-mod/patches/nag_screen/post-apply.sh
|
||||
install -Dm755 src/NagScreen/patches/post-revert.sh \
|
||||
debian/pve-mod/usr/lib/pve-mod/patches/nag_screen/post-revert.sh
|
||||
# Configure tool
|
||||
install -Dm755 src/Scripts/pve-mod-configure \
|
||||
debian/pve-mod/usr/sbin/pve-mod-configure
|
||||
# Default config (conffile for user edits)
|
||||
# Main config (conffile for user edits)
|
||||
install -Dm644 src/pve-mod.conf \
|
||||
debian/pve-mod/etc/pve-mod/pve-mod.conf
|
||||
# Reference copy for upgrade key-diff (not a conffile — always updated)
|
||||
# Per-mod configs under conf.d (conffiles for user edits)
|
||||
install -Dm644 src/PVENodeInfo/node_info.conf \
|
||||
debian/pve-mod/etc/pve-mod/conf.d/node_info.conf
|
||||
install -Dm644 src/NagScreen/nag_screen.conf \
|
||||
debian/pve-mod/etc/pve-mod/conf.d/nag_screen.conf
|
||||
# Reference copies for upgrade key-diff (not conffiles — always updated)
|
||||
install -Dm644 src/pve-mod.conf \
|
||||
debian/pve-mod/usr/share/pve-mod/pve-mod.conf.default
|
||||
install -Dm644 src/PVENodeInfo/node_info.conf \
|
||||
debian/pve-mod/usr/share/pve-mod/conf.d/node_info.conf.default
|
||||
install -Dm644 src/NagScreen/nag_screen.conf \
|
||||
debian/pve-mod/usr/share/pve-mod/conf.d/nag_screen.conf.default
|
||||
|
||||
3
src/NagScreen/nag_screen.conf
Normal file
3
src/NagScreen/nag_screen.conf
Normal file
@ -0,0 +1,3 @@
|
||||
# pve-mod :: nag_screen mod configuration
|
||||
# The nag-screen mod has no tunable settings; this file is a placeholder
|
||||
# kept for consistency with the per-mod conf.d layout.
|
||||
@ -0,0 +1 @@
|
||||
gain
|
||||
4
src/NagScreen/patches/patches.list
Normal file
4
src/NagScreen/patches/patches.list
Normal file
@ -0,0 +1,4 @@
|
||||
# pve-mod :: nag_screen patch manifest
|
||||
# Format: <patch-file> [section.key=value]
|
||||
|
||||
01-proxmoxlib-js-nagscreen.patch
|
||||
26
src/NagScreen/patches/post-apply.sh
Normal file
26
src/NagScreen/patches/post-apply.sh
Normal file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
# post-apply hook for the nag_screen mod.
|
||||
# Runs after nag_screen patches are applied. Provided env: MOD_CONF, STASH_DIR,
|
||||
# CONFD_DIR. Replaces the minified proxmoxlib with a symlink to the patched
|
||||
# unminified copy so the browser serves the patched code. The original minified
|
||||
# file is stashed (it is a non-patch binary replacement, so `patch -R` cannot
|
||||
# restore it). Exit codes: 0 = no change, 100 = changed, other = error.
|
||||
|
||||
set -u
|
||||
|
||||
PROXMOXLIB_JS="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
|
||||
PROXMOXLIB_MIN_JS="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
|
||||
STASH_DIR="${STASH_DIR:-/var/lib/pve-mod/backup}"
|
||||
|
||||
if [[ ! -L "$PROXMOXLIB_MIN_JS" ]]; then
|
||||
mkdir -p "$STASH_DIR"
|
||||
if [[ -f "$PROXMOXLIB_MIN_JS" ]]; then
|
||||
mv "$PROXMOXLIB_MIN_JS" \
|
||||
"$STASH_DIR/proxmoxlib.min.js.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
|
||||
fi
|
||||
ln -sf "$PROXMOXLIB_JS" "$PROXMOXLIB_MIN_JS"
|
||||
echo "[pve-mod] Symlinked proxmoxlib.min.js -> proxmoxlib.js"
|
||||
exit 100
|
||||
fi
|
||||
|
||||
exit 0
|
||||
23
src/NagScreen/patches/post-revert.sh
Normal file
23
src/NagScreen/patches/post-revert.sh
Normal file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# post-revert hook for the nag_screen mod.
|
||||
# Runs before nag_screen patches are reverted. Provided env: MOD_CONF,
|
||||
# STASH_DIR, CONFD_DIR. Removes the min.js symlink and restores the original
|
||||
# minified file from the stash. Exit codes: 0 = no change, 100 = changed.
|
||||
|
||||
set -u
|
||||
|
||||
PROXMOXLIB_MIN_JS="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
|
||||
STASH_DIR="${STASH_DIR:-/var/lib/pve-mod/backup}"
|
||||
|
||||
if [[ -L "$PROXMOXLIB_MIN_JS" ]]; then
|
||||
rm -f "$PROXMOXLIB_MIN_JS"
|
||||
latest=$(find "$STASH_DIR" -name "proxmoxlib.min.js.*" -type f -printf '%T+ %p\n' 2>/dev/null \
|
||||
| sort -r | head -n1 | awk '{print $2}')
|
||||
if [[ -n "$latest" ]]; then
|
||||
cp "$latest" "$PROXMOXLIB_MIN_JS"
|
||||
echo "[pve-mod] Restored proxmoxlib.min.js from stash"
|
||||
fi
|
||||
exit 100
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@ -1,261 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# /usr/lib/pve-mod/apply-patches.sh
|
||||
#
|
||||
# Applies pve-mod patches to Proxmox VE system files.
|
||||
# Reads /etc/pve-mod/pve-mod.conf to determine which modules are enabled.
|
||||
# Idempotent: safe to call multiple times (e.g. from apt hook after PVE upgrade).
|
||||
|
||||
CONF_FILE="/etc/pve-mod/pve-mod.conf"
|
||||
BACKUP_DIR="/var/lib/pve-mod/backup"
|
||||
NODES_PM="/usr/share/perl5/PVE/API2/Nodes.pm"
|
||||
PVE_MANAGER_JS="/usr/share/pve-manager/js/pvemanagerlib.js"
|
||||
PVE_MOD_JS="/usr/share/pve-manager/js/PveMod_PveNodeStatusView.js"
|
||||
PROXMOXLIB_JS="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
|
||||
PROXMOXLIB_MIN_JS="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
|
||||
GPU_RRD_DIR="/var/lib/rrdcached/db/pve-mod-gpu"
|
||||
|
||||
info() { echo "[pve-mod] $*"; }
|
||||
warn() { echo "[pve-mod] WARNING: $*" >&2; }
|
||||
|
||||
# Read one value from the INI config file; prints $default if not found.
|
||||
read_conf() {
|
||||
local section="$1" key="$2" default="${3:-0}"
|
||||
if [[ ! -f "$CONF_FILE" ]]; then
|
||||
echo "$default"
|
||||
return
|
||||
fi
|
||||
local val
|
||||
val=$(awk -F= -v sec="[$section]" -v k="$key" '
|
||||
/^\[/ { in_sec = ($0 == sec) }
|
||||
in_sec && /^[^#=]+=/ {
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1)
|
||||
if ($1 == k) {
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2)
|
||||
print $2; exit
|
||||
}
|
||||
}
|
||||
' "$CONF_FILE")
|
||||
echo "${val:-$default}"
|
||||
}
|
||||
|
||||
backup_file() {
|
||||
local src="$1"
|
||||
[[ -f "$src" ]] || return 0
|
||||
local name ts
|
||||
name=$(basename "$src")
|
||||
ts=$(date +%Y%m%d_%H%M%S)
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
cp "$src" "$BACKUP_DIR/${name}.${ts}"
|
||||
info "Backed up $(basename "$src") → $BACKUP_DIR/${name}.${ts}"
|
||||
}
|
||||
|
||||
# ── Read enabled modules ──────────────────────────────────────────────────────
|
||||
NODE_INFO=$(read_conf modules node_info 0)
|
||||
NAG_SCREEN=$(read_conf modules nag_screen 0)
|
||||
GPU_HISTORY=$(read_conf gpu gpu_history 0)
|
||||
|
||||
CHANGED=false
|
||||
|
||||
# ── node-info: Nodes.pm ───────────────────────────────────────────────────────
|
||||
if [[ "$NODE_INFO" == "1" ]]; then
|
||||
if ! grep -qF "use PVE::API2::PVEMod_SensorInfo" "$NODES_PM" 2>/dev/null; then
|
||||
backup_file "$NODES_PM"
|
||||
python3 - "$NODES_PM" <<'PYEOF'
|
||||
import sys, re
|
||||
|
||||
path = sys.argv[1]
|
||||
content = open(path).read()
|
||||
|
||||
if 'use PVE::API2::PVEMod_SensorInfo' in content:
|
||||
sys.exit(0)
|
||||
|
||||
m = re.search(r'^([ \t]*)my \$dinfo = df\(\'\/\', 1\);', content, re.MULTILINE)
|
||||
if not m:
|
||||
print("ERROR: Anchor 'my $dinfo = df' not found in Nodes.pm", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
indent = m.group(1)
|
||||
insertion = (
|
||||
f"{indent}# Collect sensor data from PveMod_SensorInfo\n"
|
||||
f"{indent}use PVE::API2::PVEMod_SensorInfo;\n"
|
||||
f"{indent}$res->{{PveMod_JsonSensorInfo}} = PVE::API2::PVEMod_SensorInfo::get_sensors_info();\n"
|
||||
f"{indent}$res->{{PveMod_Version}} = PVE::API2::PVEMod_SensorInfo::get_pve_mod_version();\n"
|
||||
f"{indent}$res->{{PveMod_graphicsInfo}} = PVE::API2::PVEMod_SensorInfo::get_graphics_info();\n"
|
||||
f"{indent}$res->{{PveMod_upsInfo}} = PVE::API2::PVEMod_SensorInfo::get_ups_info();\n"
|
||||
f"{indent}$res->{{PveMod_systemInfo}} = PVE::API2::PVEMod_SensorInfo::get_system_information();\n"
|
||||
)
|
||||
content = content[:m.start()] + insertion + content[m.start():]
|
||||
open(path, 'w').write(content)
|
||||
PYEOF
|
||||
info "Patched Nodes.pm"
|
||||
CHANGED=true
|
||||
fi
|
||||
|
||||
# ── node-info: pvemanagerlib.js ───────────────────────────────────────────
|
||||
if ! grep -qF "PveMod_PveNodeStatusView.js" "$PVE_MANAGER_JS" 2>/dev/null; then
|
||||
backup_file "$PVE_MANAGER_JS"
|
||||
python3 - "$PVE_MANAGER_JS" <<'PYEOF'
|
||||
import sys, re
|
||||
|
||||
path = sys.argv[1]
|
||||
content = open(path).read()
|
||||
|
||||
if 'PveMod_PveNodeStatusView.js' in content:
|
||||
sys.exit(0)
|
||||
|
||||
# Comment out original StatusView definition
|
||||
content = re.sub(
|
||||
r"(?m)^(Ext\.define\('PVE\.node\.StatusView',.*?^}\);)",
|
||||
lambda m: '\n'.join('// ' + line for line in m.group(1).split('\n')),
|
||||
content, flags=re.DOTALL
|
||||
)
|
||||
|
||||
# Comment out original Summary definition
|
||||
content = re.sub(
|
||||
r"(?m)^(Ext\.define\('PVE\.node\.Summary',.*?^}\);)",
|
||||
lambda m: '\n'.join('// ' + line for line in m.group(1).split('\n')),
|
||||
content, flags=re.DOTALL
|
||||
)
|
||||
|
||||
# Insert dynamic loader before the now-commented StatusView block
|
||||
loader = (
|
||||
"// Load custom PVE.node.StatusView from external module\n"
|
||||
"Ext.Loader.loadScript({\n"
|
||||
" url: '/pve2/js/PveMod_PveNodeStatusView.js',\n"
|
||||
" onLoad: function() { },\n"
|
||||
" onError: function() { console.error('Failed to load PveMod_PveNodeStatusView.js'); }\n"
|
||||
"});\n"
|
||||
)
|
||||
content = re.sub(
|
||||
r"(// Ext\.define\('PVE\.node\.StatusView',)",
|
||||
loader + r'\1',
|
||||
content, count=1
|
||||
)
|
||||
open(path, 'w').write(content)
|
||||
PYEOF
|
||||
info "Patched pvemanagerlib.js"
|
||||
CHANGED=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── node-info: GPU RRD history ────────────────────────────────────────────────
|
||||
if [[ "$NODE_INFO" == "1" && "$GPU_HISTORY" == "1" ]]; then
|
||||
if ! grep -qF "gpurrddata" "$NODES_PM" 2>/dev/null; then
|
||||
# Register method in the node sub-path list
|
||||
sed -i "s/{ name => 'rrddata' },/{ name => 'rrddata' },\n { name => 'gpurrddata' },/" "$NODES_PM"
|
||||
|
||||
# Append gpurrddata method definition after the rrddata code block
|
||||
python3 - "$NODES_PM" <<'PYEOF'
|
||||
import sys, re
|
||||
|
||||
path = sys.argv[1]
|
||||
content = open(path).read()
|
||||
|
||||
if 'gpurrddata' in content:
|
||||
sys.exit(0)
|
||||
|
||||
method = r"""
|
||||
__PACKAGE__->register_method({
|
||||
name => 'gpurrddata',
|
||||
path => 'gpurrddata',
|
||||
method => 'GET',
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
permissions => {
|
||||
check => ['perm', '/nodes/{node}', ['Sys.Audit']],
|
||||
},
|
||||
description => "Read GPU RRD statistics",
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
node => get_standard_option('pve-node'),
|
||||
card => {
|
||||
description => "The GPU card identifier (e.g. card0, nvidia0).",
|
||||
type => 'string',
|
||||
pattern => '[a-zA-Z0-9]+',
|
||||
},
|
||||
timeframe => {
|
||||
description => "Specify the time frame you are interested in.",
|
||||
type => 'string',
|
||||
enum => ['hour', 'day', 'week', 'month', 'year', 'decade'],
|
||||
},
|
||||
cf => {
|
||||
description => "The RRD consolidation function",
|
||||
type => 'string',
|
||||
enum => ['AVERAGE', 'MAX'],
|
||||
optional => 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
returns => {
|
||||
type => "array",
|
||||
items => {
|
||||
type => "object",
|
||||
properties => {},
|
||||
},
|
||||
},
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
my $nodename = PVE::INotify::nodename();
|
||||
my $card = $param->{card};
|
||||
die "invalid card name\n" unless $card =~ /^[a-zA-Z0-9]+$/;
|
||||
return PVE::RRD::create_rrd_data(
|
||||
"pve-mod-gpu/$nodename/$card", $param->{timeframe}, $param->{cf},
|
||||
);
|
||||
},
|
||||
});
|
||||
"""
|
||||
|
||||
# Insert before the final 1; at end of file
|
||||
content = re.sub(r'\n1;\s*$', method + '\n1;\n', content)
|
||||
open(path, 'w').write(content)
|
||||
PYEOF
|
||||
|
||||
mkdir -p "$GPU_RRD_DIR"
|
||||
chown www-data:www-data "$GPU_RRD_DIR" 2>/dev/null || true
|
||||
info "Installed gpurrddata API endpoint"
|
||||
CHANGED=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── nag-screen: proxmoxlib.js ─────────────────────────────────────────────────
|
||||
if [[ "$NAG_SCREEN" == "1" ]]; then
|
||||
if ! grep -qF "// disable subscription nag screen" "$PROXMOXLIB_JS" 2>/dev/null; then
|
||||
backup_file "$PROXMOXLIB_JS"
|
||||
python3 - "$PROXMOXLIB_JS" <<'PYEOF'
|
||||
import sys, re
|
||||
|
||||
path = sys.argv[1]
|
||||
content = open(path).read()
|
||||
|
||||
if '// disable subscription nag screen' in content:
|
||||
sys.exit(0)
|
||||
|
||||
m = re.search(r'(checked_command:\s*function\s*\(orig_cmd\)\s*\{)', content)
|
||||
if not m:
|
||||
print("ERROR: checked_command pattern not found in proxmoxlib.js", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
insert = "\n\t\t\t// disable subscription nag screen\n\t\t\torig_cmd();\n\t\t\treturn;"
|
||||
pos = m.end()
|
||||
content = content[:pos] + insert + content[pos:]
|
||||
open(path, 'w').write(content)
|
||||
PYEOF
|
||||
info "Patched proxmoxlib.js (nag screen)"
|
||||
CHANGED=true
|
||||
fi
|
||||
|
||||
if [[ ! -L "$PROXMOXLIB_MIN_JS" ]]; then
|
||||
backup_file "$PROXMOXLIB_MIN_JS"
|
||||
mv "$PROXMOXLIB_MIN_JS" "$BACKUP_DIR/proxmoxlib.min.js.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
|
||||
ln -sf "$PROXMOXLIB_JS" "$PROXMOXLIB_MIN_JS"
|
||||
info "Symlinked proxmoxlib.min.js → proxmoxlib.js"
|
||||
CHANGED=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── restart pveproxy if anything changed ──────────────────────────────────────
|
||||
if [[ "$CHANGED" == "true" ]]; then
|
||||
info "Restarting pveproxy..."
|
||||
systemctl restart pveproxy 2>/dev/null || true
|
||||
fi
|
||||
45
src/PVENodeInfo/node_info.conf
Normal file
45
src/PVENodeInfo/node_info.conf
Normal file
@ -0,0 +1,45 @@
|
||||
# pve-mod :: node_info mod configuration
|
||||
# Settings for the node-info / sensor-monitoring mod.
|
||||
# Managed by pve-mod-configure. Re-run to update.
|
||||
|
||||
[gpu]
|
||||
intel_enabled=0
|
||||
nvidia_enabled=0
|
||||
amd_enabled=0
|
||||
gpu_history=0
|
||||
|
||||
[lm_sensors]
|
||||
enabled=0
|
||||
enable_cpu=0
|
||||
cpu_temp_target=Core
|
||||
enable_ram_temp=0
|
||||
enable_hdd_temp=0
|
||||
enable_nvme_temp=0
|
||||
enable_fan_speed=0
|
||||
display_zero_speed_fans=0
|
||||
temp_unit=C
|
||||
|
||||
[ups]
|
||||
enabled=0
|
||||
device_name=ups@localhost
|
||||
|
||||
[system_info]
|
||||
enabled=0
|
||||
type=1
|
||||
|
||||
# Debug mode: when a collector's mode is 1, the real tool is not required.
|
||||
# Data is read from the file path instead. Useful for development/testing.
|
||||
[debug]
|
||||
lm_sensors_mode=0
|
||||
lm_sensors_output_file=/tmp/sensors-output.json
|
||||
intel_mode=0
|
||||
intel_devices_file=/tmp/intel-gpu-devices.json
|
||||
nvidia_mode=0
|
||||
nvidia_devices_file=/tmp/nvidia-smi-devices.csv
|
||||
nvidia_output_file=/tmp/nvidia-smi-output.csv
|
||||
amd_mode=0
|
||||
amd_devices_file=/tmp/amd-gpu-devices.json
|
||||
ups_mode=0
|
||||
ups_output_file=/tmp/ups-output.json
|
||||
log_enabled=0
|
||||
log_file=/tmp/pve-mod-debug.log
|
||||
9
src/PVENodeInfo/patches/patches.list
Normal file
9
src/PVENodeInfo/patches/patches.list
Normal file
@ -0,0 +1,9 @@
|
||||
# pve-mod :: node_info patch manifest
|
||||
# Format: <patch-file> [section.key=value]
|
||||
# Patches are applied top-to-bottom. An optional condition (read from this mod's
|
||||
# conf.d file, /etc/pve-mod/conf.d/node_info.conf) gates a patch; it is applied
|
||||
# only when the key equals the given value.
|
||||
|
||||
01-nodes-pm-sensors.patch
|
||||
02-nodes-pm-GPU-RRD-history.patch gpu.gpu_history=1
|
||||
03-pvemanager-js-sensors.patch
|
||||
34
src/PVENodeInfo/patches/post-apply.sh
Normal file
34
src/PVENodeInfo/patches/post-apply.sh
Normal file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# post-apply hook for the node_info mod.
|
||||
# Runs after node_info patches are applied. Provided env: MOD_CONF, STASH_DIR,
|
||||
# CONFD_DIR. Creates the GPU RRD storage directory when GPU history is enabled.
|
||||
# Exit codes: 0 = no change, 100 = changed (restart pveproxy), other = error.
|
||||
|
||||
set -u
|
||||
|
||||
GPU_RRD_DIR="/var/lib/rrdcached/db/pve-mod-gpu"
|
||||
|
||||
read_conf() {
|
||||
local file="$1" section="$2" key="$3" default="${4:-0}"
|
||||
[[ -f "$file" ]] || { echo "$default"; return; }
|
||||
local val
|
||||
val=$(awk -F= -v sec="[$section]" -v k="$key" '
|
||||
/^\[/ { in_sec = ($0 == sec) }
|
||||
in_sec && /^[^#=]+=/ {
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1)
|
||||
if ($1 == k) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2); print $2; exit }
|
||||
}
|
||||
' "$file")
|
||||
echo "${val:-$default}"
|
||||
}
|
||||
|
||||
gpu_history="$(read_conf "${MOD_CONF:-/etc/pve-mod/conf.d/node_info.conf}" gpu gpu_history 0)"
|
||||
|
||||
if [[ "$gpu_history" == "1" && ! -d "$GPU_RRD_DIR" ]]; then
|
||||
mkdir -p "$GPU_RRD_DIR"
|
||||
chown www-data:www-data "$GPU_RRD_DIR" 2>/dev/null || true
|
||||
echo "[pve-mod] Created GPU RRD directory: $GPU_RRD_DIR"
|
||||
exit 100
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# /usr/lib/pve-mod/revert-patches.sh
|
||||
#
|
||||
# Reverts all patches applied by apply-patches.sh.
|
||||
# Restores PVE system files from backups in /var/lib/pve-mod/backup/.
|
||||
# Called by prerm before package files are removed.
|
||||
|
||||
BACKUP_DIR="/var/lib/pve-mod/backup"
|
||||
NODES_PM="/usr/share/perl5/PVE/API2/Nodes.pm"
|
||||
PVE_MANAGER_JS="/usr/share/pve-manager/js/pvemanagerlib.js"
|
||||
PVE_MOD_JS="/usr/share/pve-manager/js/PveMod_PveNodeStatusView.js"
|
||||
PROXMOXLIB_JS="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
|
||||
PROXMOXLIB_MIN_JS="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
|
||||
|
||||
info() { echo "[pve-mod] $*"; }
|
||||
warn() { echo "[pve-mod] WARNING: $*" >&2; }
|
||||
|
||||
restore_latest() {
|
||||
local name="$1" target="$2"
|
||||
local latest
|
||||
latest=$(find "$BACKUP_DIR" -name "${name}.*" -type f -printf '%T+ %p\n' 2>/dev/null \
|
||||
| sort -r | head -n1 | awk '{print $2}')
|
||||
if [[ -n "$latest" ]]; then
|
||||
cp "$latest" "$target"
|
||||
info "Restored $(basename "$target") from backup"
|
||||
else
|
||||
warn "No backup found for ${name}; $target not restored"
|
||||
fi
|
||||
}
|
||||
|
||||
CHANGED=false
|
||||
|
||||
# ── node-info: Nodes.pm ───────────────────────────────────────────────────────
|
||||
if grep -qF "use PVE::API2::PVEMod_SensorInfo" "$NODES_PM" 2>/dev/null; then
|
||||
restore_latest "Nodes.pm" "$NODES_PM"
|
||||
CHANGED=true
|
||||
fi
|
||||
|
||||
# ── node-info: pvemanagerlib.js ───────────────────────────────────────────────
|
||||
if grep -qF "PveMod_PveNodeStatusView.js" "$PVE_MANAGER_JS" 2>/dev/null; then
|
||||
restore_latest "pvemanagerlib.js" "$PVE_MANAGER_JS"
|
||||
CHANGED=true
|
||||
fi
|
||||
|
||||
# ── node-info: JS module file ─────────────────────────────────────────────────
|
||||
if [[ -f "$PVE_MOD_JS" ]]; then
|
||||
rm -f "$PVE_MOD_JS"
|
||||
info "Removed PveMod_PveNodeStatusView.js"
|
||||
CHANGED=true
|
||||
fi
|
||||
|
||||
# ── nag-screen: proxmoxlib.min.js symlink ────────────────────────────────────
|
||||
if [[ -L "$PROXMOXLIB_MIN_JS" ]]; then
|
||||
rm -f "$PROXMOXLIB_MIN_JS"
|
||||
restore_latest "proxmoxlib.min.js" "$PROXMOXLIB_MIN_JS"
|
||||
CHANGED=true
|
||||
fi
|
||||
|
||||
# ── nag-screen: proxmoxlib.js ────────────────────────────────────────────────
|
||||
if grep -qF "// disable subscription nag screen" "$PROXMOXLIB_JS" 2>/dev/null; then
|
||||
restore_latest "proxmoxlib.js" "$PROXMOXLIB_JS"
|
||||
CHANGED=true
|
||||
fi
|
||||
|
||||
# ── restart pveproxy if anything changed ─────────────────────────────────────
|
||||
if [[ "$CHANGED" == "true" ]]; then
|
||||
info "Restarting pveproxy..."
|
||||
systemctl restart pveproxy 2>/dev/null || true
|
||||
fi
|
||||
206
src/Scripts/apply-patches.sh
Normal file
206
src/Scripts/apply-patches.sh
Normal file
@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env bash
|
||||
# /usr/lib/pve-mod/apply-patches.sh
|
||||
#
|
||||
# Generic patch applier for pve-mod.
|
||||
#
|
||||
# Reads /etc/pve-mod/pve-mod.conf [modules] to learn which mods are enabled,
|
||||
# then applies each enabled mod's patches from /usr/lib/pve-mod/patches/<mod>/.
|
||||
#
|
||||
# Patch convention: every .patch uses a/<path> b/<path> headers and is applied
|
||||
# with `patch -p1 -F0 -d /` (zero fuzz: line offsets tolerated, fuzzy context
|
||||
# matching disabled). Each mod folder contains a 'patches.list' manifest and
|
||||
# optional 'post-apply.sh' / 'post-revert.sh' hooks.
|
||||
#
|
||||
# Per-mod atomicity: before applying, every active patch of a mod is dry-run.
|
||||
# If any one cannot be applied (or is already partially/broken-applied), the
|
||||
# whole mod is reverted to a clean state and reported as failed - a mod is
|
||||
# never left half-applied.
|
||||
#
|
||||
# Idempotent: a patch that is already applied is detected (reverse dry-run) and
|
||||
# skipped, so this script is safe to run repeatedly (e.g. from the dpkg trigger
|
||||
# after a pve-manager upgrade).
|
||||
#
|
||||
# Hook exit-code convention: a hook returns 0 (no change), 100 (made a change,
|
||||
# triggers a pveproxy restart), or any other code (error).
|
||||
|
||||
set -u
|
||||
|
||||
# Root paths (overridable via environment, mainly for testing).
|
||||
PVE_MOD_ROOT="${PVE_MOD_ROOT:-/}"
|
||||
MAIN_CONF="${PVE_MOD_MAIN_CONF:-/etc/pve-mod/pve-mod.conf}"
|
||||
CONFD_DIR="${PVE_MOD_CONFD_DIR:-/etc/pve-mod/conf.d}"
|
||||
PATCHES_DIR="${PVE_MOD_PATCHES_DIR:-/usr/lib/pve-mod/patches}"
|
||||
# Storage for non-patch replaced files (e.g. nag-screen's minified proxmoxlib).
|
||||
# Patched text files need no backups - `patch -R` reverts them.
|
||||
STASH_DIR="${PVE_MOD_STASH_DIR:-/var/lib/pve-mod/backup}"
|
||||
|
||||
info() { echo "[pve-mod] $*"; }
|
||||
warn() { echo "[pve-mod] WARNING: $*" >&2; }
|
||||
|
||||
# read_conf <file> <section> <key> [default]
|
||||
# Prints one value from an INI file, or the default if absent.
|
||||
read_conf() {
|
||||
local file="$1" section="$2" key="$3" default="${4:-0}"
|
||||
if [[ ! -f "$file" ]]; then
|
||||
echo "$default"
|
||||
return
|
||||
fi
|
||||
local val
|
||||
val=$(awk -F= -v sec="[$section]" -v k="$key" '
|
||||
/^\[/ { in_sec = ($0 == sec) }
|
||||
in_sec && /^[^#=]+=/ {
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1)
|
||||
if ($1 == k) {
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2)
|
||||
print $2; exit
|
||||
}
|
||||
}
|
||||
' "$file")
|
||||
echo "${val:-$default}"
|
||||
}
|
||||
|
||||
# List the keys of the [modules] section in the main config, one per line.
|
||||
list_modules() {
|
||||
[[ -f "$MAIN_CONF" ]] || return 0
|
||||
awk -F= '
|
||||
/^\[/ { in_sec = ($0 == "[modules]") }
|
||||
in_sec && /^[^#=]+=/ {
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1)
|
||||
print $1
|
||||
}
|
||||
' "$MAIN_CONF"
|
||||
}
|
||||
|
||||
# Patch-state helpers (all use zero fuzz for deterministic detection).
|
||||
_dry_forward() { patch -p1 -F0 -d "$PVE_MOD_ROOT" -f --dry-run -s < "$1" >/dev/null 2>&1; }
|
||||
_dry_reverse() { patch -R -p1 -F0 -d "$PVE_MOD_ROOT" -f --dry-run -s < "$1" >/dev/null 2>&1; }
|
||||
is_applied() { _dry_reverse "$1"; }
|
||||
can_apply() { _dry_forward "$1"; }
|
||||
do_apply() { patch -p1 -F0 -d "$PVE_MOD_ROOT" -f -s < "$1"; }
|
||||
do_revert() { patch -R -p1 -F0 -d "$PVE_MOD_ROOT" -f -s < "$1"; }
|
||||
|
||||
CHANGED=false
|
||||
FAILED=false
|
||||
|
||||
# Run a mod hook honouring the exit-code convention. Sets CHANGED / FAILED.
|
||||
run_hook() {
|
||||
local hook="$1" mod_conf="$2"
|
||||
[[ -x "$hook" ]] || return 0
|
||||
STASH_DIR="$STASH_DIR" MOD_CONF="$mod_conf" CONFD_DIR="$CONFD_DIR" "$hook"
|
||||
local rc=$?
|
||||
case "$rc" in
|
||||
0) ;;
|
||||
100) CHANGED=true ;;
|
||||
*) warn " hook $(basename "$hook") reported an error (exit $rc)"; FAILED=true ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ── main ──────────────────────────────────────────────────────────────────────
|
||||
if ! command -v patch >/dev/null 2>&1; then
|
||||
warn "'patch' command not found; cannot apply mods. Install the 'patch' package."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for mod in $(list_modules); do
|
||||
[[ "$(read_conf "$MAIN_CONF" modules "$mod" 0)" == "1" ]] || continue
|
||||
|
||||
mod_dir="$PATCHES_DIR/$mod"
|
||||
manifest="$mod_dir/patches.list"
|
||||
mod_conf="$CONFD_DIR/$mod.conf"
|
||||
|
||||
if [[ ! -f "$manifest" ]]; then
|
||||
warn "No patch manifest for enabled mod '$mod' ($manifest); skipping."
|
||||
FAILED=true
|
||||
continue
|
||||
fi
|
||||
|
||||
info "Checking mod: $mod"
|
||||
|
||||
# Build the list of active patch files (those whose condition is met).
|
||||
active=()
|
||||
preflight_ok=true
|
||||
while IFS= read -r line; do
|
||||
# Strip comments and surrounding whitespace; skip blanks.
|
||||
line="${line%%#*}"
|
||||
line="$(echo "$line" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')"
|
||||
[[ -z "$line" ]] && continue
|
||||
|
||||
# Format: <patch-file> [section.key=value]
|
||||
patch_name="${line%%[[:space:]]*}"
|
||||
condition=""
|
||||
if [[ "$line" == *[[:space:]]* ]]; then
|
||||
condition="$(echo "${line#"$patch_name"}" | sed -E 's/^[[:space:]]+//')"
|
||||
fi
|
||||
|
||||
# Evaluate optional condition against the mod's conf.d file.
|
||||
if [[ -n "$condition" ]]; then
|
||||
local_key="${condition%%=*}"
|
||||
want="${condition#*=}"
|
||||
sect="${local_key%%.*}"
|
||||
ckey="${local_key#*.}"
|
||||
if [[ "$(read_conf "$mod_conf" "$sect" "$ckey" 0)" != "$want" ]]; then
|
||||
info " skip $patch_name (condition $condition not met)"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
patch_file="$mod_dir/$patch_name"
|
||||
if [[ ! -f "$patch_file" ]]; then
|
||||
warn " patch file missing: $patch_file"
|
||||
preflight_ok=false
|
||||
continue
|
||||
fi
|
||||
active+=("$patch_file")
|
||||
done < "$manifest"
|
||||
|
||||
# Preflight dry-run: every active patch must be already applied or cleanly
|
||||
# appliable. Otherwise the mod cannot be installed atomically.
|
||||
to_apply=()
|
||||
if [[ "$preflight_ok" == "true" ]]; then
|
||||
for pf in "${active[@]}"; do
|
||||
if is_applied "$pf"; then
|
||||
continue
|
||||
elif can_apply "$pf"; then
|
||||
to_apply+=("$pf")
|
||||
else
|
||||
warn " $(basename "$pf") does not apply cleanly"
|
||||
preflight_ok=false
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# If preflight failed, revert the whole mod back to a clean state so it is
|
||||
# never left half-applied.
|
||||
if [[ "$preflight_ok" != "true" ]]; then
|
||||
warn "Mod '$mod': preflight failed - reverting mod to clean state."
|
||||
for (( i=${#active[@]}-1 ; i>=0 ; i-- )); do
|
||||
pf="${active[$i]}"
|
||||
if is_applied "$pf"; then
|
||||
do_revert "$pf" && { info " reverted $(basename "$pf")"; CHANGED=true; }
|
||||
fi
|
||||
done
|
||||
run_hook "$mod_dir/post-revert.sh" "$mod_conf"
|
||||
FAILED=true
|
||||
continue
|
||||
fi
|
||||
|
||||
# Apply the outstanding patches.
|
||||
if [[ ${#to_apply[@]} -gt 0 ]]; then
|
||||
for pf in "${to_apply[@]}"; do
|
||||
do_apply "$pf" && { info " applied $(basename "$pf")"; CHANGED=true; }
|
||||
done
|
||||
else
|
||||
info " already up to date"
|
||||
fi
|
||||
|
||||
run_hook "$mod_dir/post-apply.sh" "$mod_conf"
|
||||
done
|
||||
|
||||
if [[ "$CHANGED" == "true" ]]; then
|
||||
info "Restarting pveproxy..."
|
||||
systemctl restart pveproxy 2>/dev/null || true
|
||||
fi
|
||||
|
||||
[[ "$FAILED" == "true" ]] && exit 1
|
||||
exit 0
|
||||
@ -7,6 +7,9 @@
|
||||
set -euo pipefail
|
||||
|
||||
CONF_FILE="/etc/pve-mod/pve-mod.conf"
|
||||
CONFD_DIR="/etc/pve-mod/conf.d"
|
||||
NODE_INFO_CONF="${CONFD_DIR}/node_info.conf"
|
||||
NAG_SCREEN_CONF="${CONFD_DIR}/nag_screen.conf"
|
||||
APPLY_PATCHES="/usr/lib/pve-mod/apply-patches.sh"
|
||||
NODES_PM="/usr/share/perl5/PVE/API2/Nodes.pm"
|
||||
|
||||
@ -24,11 +27,9 @@ ask() {
|
||||
}
|
||||
bool() { [[ "$1" == true ]] && echo 1 || echo 0; }
|
||||
|
||||
# Loads every value from an existing config file into the wizard's variables,
|
||||
# so that re-running the tool preserves settings the user does not change.
|
||||
_load_conf() {
|
||||
[[ -f "$CONF_FILE" ]] || return 0
|
||||
local section="" line key val
|
||||
_load_conf_debug() {
|
||||
[[ -f "$NODE_INFO_CONF" ]] || return 0
|
||||
local in_debug=0 line key val
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
'#'*|'') continue ;;
|
||||
@ -78,7 +79,7 @@ _load_conf() {
|
||||
debug.log_enabled) DEBUG_LOG="$val" ;;
|
||||
debug.log_file) DEBUG_LOG_FILE="$val" ;;
|
||||
esac
|
||||
done < "$CONF_FILE"
|
||||
done < "$NODE_INFO_CONF"
|
||||
}
|
||||
#endregion helpers
|
||||
|
||||
@ -390,15 +391,33 @@ configure_node_info() {
|
||||
|
||||
#region write config
|
||||
write_config() {
|
||||
mkdir -p "$(dirname "$CONF_FILE")"
|
||||
mkdir -p "$(dirname "$CONF_FILE")" "$CONFD_DIR"
|
||||
|
||||
# Main config: which mods are enabled + global trigger/service settings.
|
||||
cat > "$CONF_FILE" <<EOF
|
||||
# pve-mod configuration file
|
||||
# pve-mod main configuration file
|
||||
# Managed by pve-mod-configure. Re-run to update.
|
||||
#
|
||||
# This file only declares which mods are enabled. Each mod keeps its own
|
||||
# settings in ${CONFD_DIR}/<mod>.conf
|
||||
|
||||
[modules]
|
||||
node_info=${MOD_NODE_INFO}
|
||||
nag_screen=${MOD_NAG_SCREEN}
|
||||
|
||||
[pve_trigger]
|
||||
enabled=${PVE_TRIGGER_ENABLED}
|
||||
|
||||
[service]
|
||||
mode=embedded
|
||||
EOF
|
||||
info "Main configuration saved to $CONF_FILE"
|
||||
|
||||
# node_info mod config.
|
||||
cat > "$NODE_INFO_CONF" <<EOF
|
||||
# pve-mod :: node_info mod configuration
|
||||
# Managed by pve-mod-configure. Re-run to update.
|
||||
|
||||
[gpu]
|
||||
intel_enabled=${ENABLE_INTEL_GPU_INFO}
|
||||
nvidia_enabled=${ENABLE_NVIDIA_GPU_INFO}
|
||||
@ -424,12 +443,6 @@ device_name=${UPS_DEVICE_NAME}
|
||||
enabled=${ENABLE_SYSTEM_INFO}
|
||||
type=${SYSTEM_INFO_TYPE}
|
||||
|
||||
[pve_trigger]
|
||||
enabled=${PVE_TRIGGER_ENABLED}
|
||||
|
||||
[service]
|
||||
mode=embedded
|
||||
|
||||
# Debug mode: when a collector's mode is 1, the real tool is not required.
|
||||
# Data is read from the file path instead. Useful for development/testing.
|
||||
[debug]
|
||||
@ -447,7 +460,16 @@ ups_output_file=${DEBUG_UPS_FILE}
|
||||
log_enabled=${DEBUG_LOG}
|
||||
log_file=${DEBUG_LOG_FILE}
|
||||
EOF
|
||||
info "Configuration saved to $CONF_FILE"
|
||||
info "node_info configuration saved to $NODE_INFO_CONF"
|
||||
|
||||
# nag_screen mod config (placeholder; no tunable settings).
|
||||
if [[ ! -f "$NAG_SCREEN_CONF" ]]; then
|
||||
cat > "$NAG_SCREEN_CONF" <<EOF
|
||||
# pve-mod :: nag_screen mod configuration
|
||||
# The nag-screen mod has no tunable settings; this file is a placeholder
|
||||
# kept for consistency with the per-mod conf.d layout.
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
#endregion write config
|
||||
|
||||
|
||||
101
src/Scripts/revert-patches.sh
Normal file
101
src/Scripts/revert-patches.sh
Normal file
@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env bash
|
||||
# /usr/lib/pve-mod/revert-patches.sh
|
||||
#
|
||||
# Generic patch reverter for pve-mod.
|
||||
#
|
||||
# Reverses every patch found under /usr/lib/pve-mod/patches/<mod>/ (regardless
|
||||
# of whether the mod is currently enabled), using `patch -R -p1 -F0 -d /`.
|
||||
# Patches are reverted in reverse manifest order. Patched text files need no
|
||||
# backups - `patch -R` restores them exactly. Non-patch actions (e.g. the
|
||||
# nag-screen min.js symlink) are undone by each mod's 'post-revert.sh' hook.
|
||||
#
|
||||
# Called by prerm before the package's files are removed.
|
||||
#
|
||||
# Hook exit-code convention: a hook returns 0 (no change), 100 (made a change,
|
||||
# triggers a pveproxy restart), or any other code (error).
|
||||
|
||||
set -u
|
||||
|
||||
# Root paths (overridable via environment, mainly for testing).
|
||||
PVE_MOD_ROOT="${PVE_MOD_ROOT:-/}"
|
||||
CONFD_DIR="${PVE_MOD_CONFD_DIR:-/etc/pve-mod/conf.d}"
|
||||
PATCHES_DIR="${PVE_MOD_PATCHES_DIR:-/usr/lib/pve-mod/patches}"
|
||||
# Storage for non-patch replaced files (e.g. nag-screen's minified proxmoxlib).
|
||||
STASH_DIR="${PVE_MOD_STASH_DIR:-/var/lib/pve-mod/backup}"
|
||||
|
||||
info() { echo "[pve-mod] $*"; }
|
||||
warn() { echo "[pve-mod] WARNING: $*" >&2; }
|
||||
|
||||
# Revert a single patch. Returns 0 if a change was made, 1 otherwise.
|
||||
revert_one_patch() {
|
||||
local patch="$1"
|
||||
# Not applied? (a clean forward apply means the change is absent)
|
||||
if patch -p1 -F0 -d "$PVE_MOD_ROOT" -f --dry-run -s < "$patch" >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
if patch -R -p1 -F0 -d "$PVE_MOD_ROOT" -f --dry-run -s < "$patch" >/dev/null 2>&1; then
|
||||
patch -R -p1 -F0 -d "$PVE_MOD_ROOT" -f -s < "$patch"
|
||||
return 0
|
||||
fi
|
||||
warn " $(basename "$patch") could not be reverted cleanly; manual cleanup may be needed"
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── main ──────────────────────────────────────────────────────────────────────
|
||||
if ! command -v patch >/dev/null 2>&1; then
|
||||
warn "'patch' command not found; cannot revert mods."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[[ -d "$PATCHES_DIR" ]] || exit 0
|
||||
|
||||
CHANGED=false
|
||||
|
||||
for mod_dir in "$PATCHES_DIR"/*/; do
|
||||
[[ -d "$mod_dir" ]] || continue
|
||||
mod="$(basename "$mod_dir")"
|
||||
manifest="$mod_dir/patches.list"
|
||||
mod_conf="$CONFD_DIR/$mod.conf"
|
||||
|
||||
# Run the post-revert hook first (undo non-patch actions such as symlinks).
|
||||
hook="$mod_dir/post-revert.sh"
|
||||
if [[ -x "$hook" ]]; then
|
||||
info "Running post-revert hook for $mod"
|
||||
STASH_DIR="$STASH_DIR" MOD_CONF="$mod_conf" CONFD_DIR="$CONFD_DIR" "$hook"
|
||||
rc=$?
|
||||
case "$rc" in
|
||||
0) ;;
|
||||
100) CHANGED=true ;;
|
||||
*) warn "post-revert hook for $mod reported an error (exit $rc)" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
[[ -f "$manifest" ]] || continue
|
||||
info "Reverting mod: $mod"
|
||||
|
||||
# Collect patch names (ignore conditions and comments), then reverse order.
|
||||
mapfile -t patches < <(
|
||||
while IFS= read -r line; do
|
||||
line="${line%%#*}"
|
||||
line="$(echo "$line" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')"
|
||||
[[ -z "$line" ]] && continue
|
||||
echo "${line%%[[:space:]]*}"
|
||||
done < "$manifest"
|
||||
)
|
||||
|
||||
for (( i=${#patches[@]}-1 ; i>=0 ; i-- )); do
|
||||
patch_name="${patches[$i]}"
|
||||
patch_file="$mod_dir/$patch_name"
|
||||
[[ -f "$patch_file" ]] || continue
|
||||
if revert_one_patch "$patch_file"; then
|
||||
info " reverted $patch_name"
|
||||
CHANGED=true
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [[ "$CHANGED" == "true" ]]; then
|
||||
info "Restarting pveproxy..."
|
||||
systemctl restart pveproxy 2>/dev/null || true
|
||||
fi
|
||||
exit 0
|
||||
@ -1,55 +1,19 @@
|
||||
# pve-mod configuration file
|
||||
# pve-mod main configuration file
|
||||
# Run 'pve-mod-configure' to set values interactively.
|
||||
# All flags are 0 (disabled) by default; pve-mod-configure enables them.
|
||||
#
|
||||
# This file only declares which mods are enabled. Each mod keeps its own
|
||||
# settings in /etc/pve-mod/conf.d/<mod>.conf
|
||||
#
|
||||
# When a mod flag below is 1, its patches are (re)applied on install and,
|
||||
# if [pve_trigger] enabled=1, after every pve-manager upgrade.
|
||||
|
||||
[modules]
|
||||
node_info=0
|
||||
nag_screen=0
|
||||
|
||||
[gpu]
|
||||
intel_enabled=0
|
||||
nvidia_enabled=0
|
||||
amd_enabled=0
|
||||
gpu_history=0
|
||||
|
||||
[lm_sensors]
|
||||
enabled=0
|
||||
enable_cpu=0
|
||||
cpu_temp_target=Core
|
||||
enable_ram_temp=0
|
||||
enable_hdd_temp=0
|
||||
enable_nvme_temp=0
|
||||
enable_fan_speed=0
|
||||
display_zero_speed_fans=0
|
||||
temp_unit=C
|
||||
|
||||
[ups]
|
||||
enabled=0
|
||||
device_name=ups@localhost
|
||||
|
||||
[system_info]
|
||||
enabled=0
|
||||
type=1
|
||||
|
||||
# Re-apply patches automatically after a pve-manager upgrade (dpkg trigger).
|
||||
[pve_trigger]
|
||||
enabled=0
|
||||
|
||||
[service]
|
||||
mode=embedded
|
||||
|
||||
# Debug mode: when a mode is 1, the real tool is not required.
|
||||
# Data is read from the file path instead. Useful for development/testing.
|
||||
[debug]
|
||||
lm_sensors_mode=0
|
||||
lm_sensors_output_file=/tmp/sensors-output.json
|
||||
intel_mode=0
|
||||
intel_devices_file=/tmp/intel-gpu-devices.json
|
||||
nvidia_mode=0
|
||||
nvidia_devices_file=/tmp/nvidia-smi-devices.csv
|
||||
nvidia_output_file=/tmp/nvidia-smi-output.csv
|
||||
amd_mode=0
|
||||
amd_devices_file=/tmp/amd-gpu-devices.json
|
||||
ups_mode=0
|
||||
ups_output_file=/tmp/ups-output.json
|
||||
log_enabled=0
|
||||
log_file=/tmp/pve-mod-debug.log
|
||||
|
||||
Loading…
Reference in New Issue
Block a user