OneWheelGeek revised this gist 1 month ago. Go to revision
1 file changed, 85 insertions, 193 deletions
kill-subscription.sh
| @@ -3,55 +3,41 @@ set -Eeuo pipefail | |||
| 3 | 3 | ||
| 4 | 4 | # proxmox-subscription-popup.sh | |
| 5 | 5 | # | |
| 6 | - | # Safe Proxmox VE 9 subscription popup suppressor. | |
| 7 | - | # | |
| 8 | - | # Defaults: | |
| 9 | - | # - patch only | |
| 10 | - | # - no repair unless --repair is passed | |
| 11 | - | # - refuses to patch unless file matches expected structure | |
| 6 | + | # Safe-ish Proxmox VE 9 subscription popup suppressor using sed. | |
| 12 | 7 | # | |
| 13 | 8 | # Usage: | |
| 14 | 9 | # sudo bash proxmox-subscription-popup.sh | |
| 15 | 10 | # sudo bash proxmox-subscription-popup.sh --status | |
| 16 | 11 | # sudo bash proxmox-subscription-popup.sh --undo | |
| 17 | 12 | # sudo bash proxmox-subscription-popup.sh --repair | |
| 18 | - | # | |
| 19 | - | # Notes: | |
| 20 | - | # - Uses `patch` with a very specific diff | |
| 21 | - | # - Creates a backup before patching | |
| 22 | - | # - Restarts pveproxy after changes | |
| 23 | - | # - Any package update may restore the original file | |
| 24 | 13 | ||
| 25 | - | SCRIPT_NAME="$(basename "$0")" | |
| 26 | 14 | JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" | |
| 27 | 15 | STATE_DIR="/root/.proxmox-subscription-popup" | |
| 28 | 16 | BACKUP_FILE="${STATE_DIR}/proxmoxlib.js.bak" | |
| 29 | - | PATCH_FILE="${STATE_DIR}/subscription-popup.patch" | |
| 30 | - | WORK_ORIG="${STATE_DIR}/proxmoxlib.js.orig" | |
| 31 | 17 | ||
| 32 | - | DO_REPAIR=0 | |
| 33 | - | DO_UNDO=0 | |
| 34 | 18 | DO_STATUS=0 | |
| 19 | + | DO_UNDO=0 | |
| 20 | + | DO_REPAIR=0 | |
| 35 | 21 | ||
| 36 | 22 | for arg in "$@"; do | |
| 37 | 23 | case "$arg" in | |
| 38 | - | --repair) DO_REPAIR=1 ;; | |
| 39 | - | --undo) DO_UNDO=1 ;; | |
| 40 | 24 | --status) DO_STATUS=1 ;; | |
| 25 | + | --undo) DO_UNDO=1 ;; | |
| 26 | + | --repair) DO_REPAIR=1 ;; | |
| 41 | 27 | -h|--help) | |
| 42 | 28 | cat <<'EOF' | |
| 43 | 29 | Usage: | |
| 44 | 30 | sudo bash proxmox-subscription-popup.sh | |
| 45 | - | Apply the popup suppression patch | |
| 31 | + | Apply popup suppression patch | |
| 46 | 32 | ||
| 47 | 33 | sudo bash proxmox-subscription-popup.sh --status | |
| 48 | - | Show current state | |
| 34 | + | Show status | |
| 49 | 35 | ||
| 50 | 36 | sudo bash proxmox-subscription-popup.sh --undo | |
| 51 | - | Restore the original file from backup | |
| 37 | + | Restore original file | |
| 52 | 38 | ||
| 53 | 39 | sudo bash proxmox-subscription-popup.sh --repair | |
| 54 | - | Reinstall the package that owns proxmoxlib.js and restart pveproxy | |
| 40 | + | Reinstall the owning package | |
| 55 | 41 | EOF | |
| 56 | 42 | exit 0 | |
| 57 | 43 | ;; | |
| @@ -72,146 +58,75 @@ mkdir -p "$STATE_DIR" | |||
| 72 | 58 | if [[ -t 1 ]]; then | |
| 73 | 59 | C_RESET=$'\033[0m' | |
| 74 | 60 | C_BOLD=$'\033[1m' | |
| 75 | - | C_DIM=$'\033[2m' | |
| 76 | 61 | C_RED=$'\033[1;31m' | |
| 77 | 62 | C_GREEN=$'\033[1;32m' | |
| 78 | 63 | C_YELLOW=$'\033[1;33m' | |
| 79 | - | C_BLUE=$'\033[1;34m' | |
| 80 | - | C_MAGENTA=$'\033[1;35m' | |
| 81 | 64 | C_CYAN=$'\033[1;36m' | |
| 65 | + | C_MAGENTA=$'\033[1;35m' | |
| 82 | 66 | else | |
| 83 | 67 | C_RESET="" | |
| 84 | 68 | C_BOLD="" | |
| 85 | - | C_DIM="" | |
| 86 | 69 | C_RED="" | |
| 87 | 70 | C_GREEN="" | |
| 88 | 71 | C_YELLOW="" | |
| 89 | - | C_BLUE="" | |
| 90 | - | C_MAGENTA="" | |
| 91 | 72 | C_CYAN="" | |
| 73 | + | C_MAGENTA="" | |
| 92 | 74 | fi | |
| 93 | 75 | ||
| 94 | - | say() { printf "%b\n" "$*"; } | |
| 95 | - | info() { say "${C_CYAN}▶${C_RESET} $*"; } | |
| 96 | - | ok() { say "${C_GREEN}✔${C_RESET} $*"; } | |
| 97 | - | warn() { say "${C_YELLOW}⚠${C_RESET} $*"; } | |
| 98 | - | fail() { say "${C_RED}✖${C_RESET} $*" >&2; } | |
| 99 | - | title() { say "${C_BOLD}${C_MAGENTA}$*${C_RESET}"; } | |
| 76 | + | say() { printf "%b\n" "$*"; } | |
| 77 | + | info() { say "${C_CYAN}▶${C_RESET} $*"; } | |
| 78 | + | ok() { say "${C_GREEN}✔${C_RESET} $*"; } | |
| 79 | + | warn() { say "${C_YELLOW}⚠${C_RESET} $*"; } | |
| 80 | + | fail() { say "${C_RED}✖${C_RESET} $*" >&2; } | |
| 81 | + | title() { say "${C_BOLD}${C_MAGENTA}$*${C_RESET}"; } | |
| 100 | 82 | ||
| 101 | - | trap 'fail "Script failed on line ${LINENO}. No further changes will be made."' ERR | |
| 83 | + | trap 'fail "Script failed on line ${LINENO}"' ERR | |
| 102 | 84 | ||
| 103 | - | restart_proxy() { | |
| 104 | - | info "Restarting pveproxy..." | |
| 105 | - | systemctl restart pveproxy | |
| 106 | - | ok "pveproxy restarted" | |
| 107 | - | } | |
| 108 | - | ||
| 109 | - | show_browser_hint() { | |
| 110 | - | say | |
| 111 | - | say "${C_BOLD}Browser refresh:${C_RESET} hard refresh the UI after this." | |
| 112 | - | say "Mac: ${C_DIM}Cmd+Shift+R${C_RESET}" | |
| 113 | - | say "Other: ${C_DIM}Ctrl+Shift+R${C_RESET}" | |
| 114 | - | say | |
| 85 | + | require_file() { | |
| 86 | + | [[ -f "$JS_FILE" ]] || { | |
| 87 | + | fail "Missing file: $JS_FILE" | |
| 88 | + | exit 1 | |
| 89 | + | } | |
| 115 | 90 | } | |
| 116 | 91 | ||
| 117 | 92 | detect_owner_pkg() { | |
| 118 | 93 | dpkg -S "$JS_FILE" 2>/dev/null | head -n1 | cut -d: -f1 | |
| 119 | 94 | } | |
| 120 | 95 | ||
| 121 | - | is_proxmox() { | |
| 122 | - | command -v pveversion >/dev/null 2>&1 | |
| 123 | - | } | |
| 124 | - | ||
| 125 | - | version_major() { | |
| 126 | - | if ! command -v pveversion >/dev/null 2>&1; then | |
| 127 | - | return 1 | |
| 128 | - | fi | |
| 129 | - | pveversion | sed -n 's/^pve-manager\/\([0-9]\+\)\..*/\1/p' | head -n1 | |
| 130 | - | } | |
| 131 | - | ||
| 132 | - | require_file() { | |
| 133 | - | [[ -f "$JS_FILE" ]] || { fail "Missing file: $JS_FILE"; exit 1; } | |
| 96 | + | restart_proxy() { | |
| 97 | + | info "Restarting pveproxy..." | |
| 98 | + | systemctl restart pveproxy | |
| 99 | + | ok "pveproxy restarted" | |
| 134 | 100 | } | |
| 135 | 101 | ||
| 136 | - | has_patch_tool() { | |
| 137 | - | command -v patch >/dev/null 2>&1 | |
| 102 | + | show_hint() { | |
| 103 | + | say | |
| 104 | + | say "${C_BOLD}Hard refresh the browser afterwards:${C_RESET}" | |
| 105 | + | say " Mac: Cmd+Shift+R" | |
| 106 | + | say " Other: Ctrl+Shift+R" | |
| 107 | + | say | |
| 138 | 108 | } | |
| 139 | 109 | ||
| 140 | - | ensure_patch_tool() { | |
| 141 | - | if has_patch_tool; then | |
| 142 | - | return 0 | |
| 143 | - | fi | |
| 144 | - | fail "'patch' command not found." | |
| 145 | - | fail "Install it with: apt-get update && apt-get install -y patch" | |
| 146 | - | exit 1 | |
| 110 | + | is_patched() { | |
| 111 | + | grep -Fq "orig_cmd();" "$JS_FILE" && | |
| 112 | + | grep -Fq "return;" "$JS_FILE" | |
| 147 | 113 | } | |
| 148 | 114 | ||
| 149 | - | # Very specific to the function structure from the uploaded file. | |
| 150 | 115 | has_expected_original() { | |
| 151 | 116 | grep -Fq "checked_command: function (orig_cmd) {" "$JS_FILE" && | |
| 152 | 117 | grep -Fq "url: '/nodes/localhost/subscription'" "$JS_FILE" && | |
| 153 | - | grep -Fq "res.data.status.toLowerCase() !== 'active'" "$JS_FILE" && | |
| 154 | - | grep -Fq "title: gettext('No valid subscription')" "$JS_FILE" | |
| 155 | - | } | |
| 156 | - | ||
| 157 | - | # Detects our injected short-circuit immediately after the function opens. | |
| 158 | - | is_patched() { | |
| 159 | - | python3 - <<'PY' "$JS_FILE" | |
| 160 | - | import re, sys | |
| 161 | - | path = sys.argv[1] | |
| 162 | - | text = open(path, 'r', encoding='utf-8').read() | |
| 163 | - | pat = re.compile( | |
| 164 | - | r"checked_command:\s*function\s*\(orig_cmd\)\s*\{\s*orig_cmd\(\);\s*return;\s*Proxmox\.Utils\.API2Request\(", | |
| 165 | - | re.S, | |
| 166 | - | ) | |
| 167 | - | sys.exit(0 if pat.search(text) else 1) | |
| 168 | - | PY | |
| 169 | - | } | |
| 170 | - | ||
| 171 | - | write_patch_file() { | |
| 172 | - | cat > "$PATCH_FILE" <<'EOF' | |
| 173 | - | --- proxmoxlib.js.orig | |
| 174 | - | +++ proxmoxlib.js | |
| 175 | - | @@ -1,5 +1,7 @@ | |
| 176 | - | checked_command: function (orig_cmd) { | |
| 177 | - | + orig_cmd(); | |
| 178 | - | + return; | |
| 179 | - | Proxmox.Utils.API2Request({ | |
| 180 | - | url: '/nodes/localhost/subscription', | |
| 181 | - | method: 'GET', | |
| 182 | - | EOF | |
| 118 | + | grep -Fq "res.data.status.toLowerCase() !== 'active'" "$JS_FILE" | |
| 183 | 119 | } | |
| 184 | 120 | ||
| 185 | 121 | status() { | |
| 186 | 122 | title "Proxmox subscription popup status" | |
| 187 | 123 | ||
| 188 | - | if ! is_proxmox; then | |
| 189 | - | fail "This does not look like a Proxmox host." | |
| 190 | - | exit 1 | |
| 191 | - | fi | |
| 192 | - | ||
| 193 | 124 | require_file | |
| 194 | 125 | ||
| 195 | - | local major owner | |
| 196 | - | major="$(version_major || true)" | |
| 197 | - | owner="$(detect_owner_pkg || true)" | |
| 198 | - | ||
| 199 | - | if [[ -n "$major" ]]; then | |
| 200 | - | info "Detected Proxmox major version: ${C_BOLD}${major}${C_RESET}" | |
| 201 | - | else | |
| 202 | - | warn "Could not determine Proxmox major version" | |
| 203 | - | fi | |
| 204 | - | ||
| 205 | - | if [[ -n "$owner" ]]; then | |
| 206 | - | info "Owning package: ${C_BOLD}${owner}${C_RESET}" | |
| 207 | - | else | |
| 208 | - | warn "Could not determine owning package" | |
| 209 | - | fi | |
| 210 | - | ||
| 211 | 126 | if has_expected_original; then | |
| 212 | - | ok "Expected original popup code pattern is present" | |
| 127 | + | ok "Expected original code pattern found" | |
| 213 | 128 | else | |
| 214 | - | warn "Expected original popup code pattern is NOT present" | |
| 129 | + | warn "Expected original code pattern NOT found" | |
| 215 | 130 | fi | |
| 216 | 131 | ||
| 217 | 132 | if is_patched; then | |
| @@ -221,117 +136,94 @@ status() { | |||
| 221 | 136 | fi | |
| 222 | 137 | ||
| 223 | 138 | if [[ -f "$BACKUP_FILE" ]]; then | |
| 224 | - | info "Backup present: ${C_BOLD}${BACKUP_FILE}${C_RESET}" | |
| 139 | + | ok "Backup exists at $BACKUP_FILE" | |
| 225 | 140 | else | |
| 226 | - | warn "No backup found at ${STATE_DIR}" | |
| 141 | + | warn "No backup exists" | |
| 227 | 142 | fi | |
| 228 | 143 | } | |
| 229 | 144 | ||
| 230 | - | repair() { | |
| 231 | - | title "Repairing Proxmox web UI file" | |
| 232 | - | ||
| 233 | - | require_file | |
| 145 | + | undo() { | |
| 146 | + | title "Restoring original file" | |
| 234 | 147 | ||
| 235 | - | local owner | |
| 236 | - | owner="$(detect_owner_pkg || true)" | |
| 237 | - | [[ -n "$owner" ]] || { fail "Could not determine owning package for $JS_FILE"; exit 1; } | |
| 148 | + | [[ -f "$BACKUP_FILE" ]] || { | |
| 149 | + | fail "No backup file found at $BACKUP_FILE" | |
| 150 | + | exit 1 | |
| 151 | + | } | |
| 238 | 152 | ||
| 239 | - | info "Reinstalling package: ${C_BOLD}${owner}${C_RESET}" | |
| 240 | - | apt-get update | |
| 241 | - | apt-get install --reinstall -y "$owner" | |
| 153 | + | cp -f "$BACKUP_FILE" "$JS_FILE" | |
| 154 | + | ok "Original file restored" | |
| 242 | 155 | ||
| 243 | - | require_file | |
| 244 | 156 | restart_proxy | |
| 245 | - | ok "Repair complete" | |
| 246 | - | show_browser_hint | |
| 157 | + | show_hint | |
| 247 | 158 | } | |
| 248 | 159 | ||
| 249 | - | undo() { | |
| 250 | - | title "Restoring original file" | |
| 160 | + | repair() { | |
| 161 | + | title "Repairing Proxmox UI" | |
| 251 | 162 | ||
| 252 | - | [[ -f "$BACKUP_FILE" ]] || { fail "No backup found at $BACKUP_FILE"; exit 1; } | |
| 253 | - | require_file | |
| 163 | + | local owner | |
| 164 | + | owner="$(detect_owner_pkg)" | |
| 254 | 165 | ||
| 255 | - | cp -f "$BACKUP_FILE" "$JS_FILE" | |
| 256 | - | ok "Original file restored from backup" | |
| 166 | + | [[ -n "$owner" ]] || { | |
| 167 | + | fail "Could not determine owning package" | |
| 168 | + | exit 1 | |
| 169 | + | } | |
| 170 | + | ||
| 171 | + | info "Reinstalling package: $owner" | |
| 172 | + | apt-get update | |
| 173 | + | apt-get install --reinstall -y "$owner" | |
| 257 | 174 | ||
| 258 | 175 | restart_proxy | |
| 259 | - | show_browser_hint | |
| 176 | + | ok "Repair complete" | |
| 177 | + | show_hint | |
| 260 | 178 | } | |
| 261 | 179 | ||
| 262 | 180 | apply_patch() { | |
| 263 | - | title "Applying safe Proxmox popup patch" | |
| 181 | + | title "Applying subscription popup suppression" | |
| 264 | 182 | ||
| 265 | 183 | require_file | |
| 266 | - | ensure_patch_tool | |
| 267 | - | ||
| 268 | - | if ! is_proxmox; then | |
| 269 | - | fail "This does not look like a Proxmox host." | |
| 270 | - | exit 1 | |
| 271 | - | fi | |
| 272 | - | ||
| 273 | - | local major | |
| 274 | - | major="$(version_major || true)" | |
| 275 | - | if [[ "$major" == "9" ]]; then | |
| 276 | - | ok "Detected Proxmox VE 9" | |
| 277 | - | else | |
| 278 | - | warn "Expected Proxmox VE 9, detected: ${major:-unknown}" | |
| 279 | - | warn "Proceeding only if the file matches the exact expected structure" | |
| 280 | - | fi | |
| 281 | 184 | ||
| 282 | 185 | if is_patched; then | |
| 283 | - | ok "Patch is already installed. Nothing to do." | |
| 284 | - | show_browser_hint | |
| 186 | + | ok "Patch already appears to be installed" | |
| 187 | + | show_hint | |
| 285 | 188 | return 0 | |
| 286 | 189 | fi | |
| 287 | 190 | ||
| 288 | 191 | if ! has_expected_original; then | |
| 289 | - | fail "The expected original code pattern was not found." | |
| 290 | - | fail "Refusing to patch, because the file layout is not what this script expects." | |
| 291 | - | fail "Use --repair first if the file is damaged, then try again." | |
| 192 | + | fail "Expected original code pattern not found." | |
| 193 | + | fail "Refusing to patch because the file does not look like the expected Proxmox VE 9 file." | |
| 194 | + | fail "Run with --repair first if the file is damaged." | |
| 292 | 195 | exit 1 | |
| 293 | 196 | fi | |
| 294 | - | ok "Expected original code pattern found" | |
| 295 | 197 | ||
| 296 | 198 | if [[ ! -f "$BACKUP_FILE" ]]; then | |
| 297 | - | info "Creating backup at ${C_BOLD}${BACKUP_FILE}${C_RESET}" | |
| 199 | + | info "Creating backup at $BACKUP_FILE" | |
| 298 | 200 | cp -a "$JS_FILE" "$BACKUP_FILE" | |
| 299 | 201 | ok "Backup created" | |
| 300 | 202 | else | |
| 301 | - | warn "Backup already exists, leaving it untouched" | |
| 203 | + | warn "Backup already exists; leaving it untouched" | |
| 302 | 204 | fi | |
| 303 | 205 | ||
| 304 | - | cp -a "$JS_FILE" "$WORK_ORIG" | |
| 305 | - | write_patch_file | |
| 206 | + | info "Inserting short-circuit into checked_command()" | |
| 306 | 207 | ||
| 307 | - | info "Testing patch with dry-run..." | |
| 308 | - | if ! (cd "$STATE_DIR" && patch --dry-run proxmoxlib.js.orig subscription-popup.patch >/dev/null 2>&1); then | |
| 309 | - | fail "Patch dry-run failed." | |
| 310 | - | fail "No changes were made." | |
| 311 | - | exit 1 | |
| 312 | - | fi | |
| 313 | - | ok "Patch dry-run succeeded" | |
| 314 | - | ||
| 315 | - | info "Applying patch..." | |
| 316 | - | cp -a "$JS_FILE" "$WORK_ORIG" | |
| 317 | - | if ! (cd "$STATE_DIR" && patch "$WORK_ORIG" subscription-popup.patch >/dev/null 2>&1); then | |
| 318 | - | fail "Patch command failed unexpectedly." | |
| 319 | - | exit 1 | |
| 320 | - | fi | |
| 321 | - | ||
| 322 | - | cp -f "$WORK_ORIG" "$JS_FILE" | |
| 208 | + | sed -i '/checked_command: function (orig_cmd) {/a\ | |
| 209 | + | orig_cmd();\ | |
| 210 | + | return;' "$JS_FILE" | |
| 323 | 211 | ||
| 324 | 212 | if ! is_patched; then | |
| 325 | - | warn "Verification failed after patch; restoring backup" | |
| 213 | + | warn "Patch verification failed; restoring backup" | |
| 326 | 214 | cp -f "$BACKUP_FILE" "$JS_FILE" | |
| 327 | - | fail "Patch did not verify cleanly. Original file restored." | |
| 215 | + | fail "Patch did not verify cleanly. Original restored." | |
| 328 | 216 | exit 1 | |
| 329 | 217 | fi | |
| 330 | 218 | ||
| 331 | - | ok "Patch verified successfully" | |
| 219 | + | ok "Patch successfully applied" | |
| 220 | + | ||
| 221 | + | info "Snippet after patch:" | |
| 222 | + | grep -A4 'checked_command: function (orig_cmd)' "$JS_FILE" || true | |
| 223 | + | ||
| 332 | 224 | restart_proxy | |
| 333 | - | ok "Subscription popup suppression is in place" | |
| 334 | - | show_browser_hint | |
| 225 | + | ok "Subscription popup suppression is active" | |
| 226 | + | show_hint | |
| 335 | 227 | } | |
| 336 | 228 | ||
| 337 | 229 | case 1 in | |
OneWheelGeek revised this gist 1 month ago. Go to revision
No changes
OneWheelGeek revised this gist 1 month ago. Go to revision
1 file changed, 110 insertions, 60 deletions
kill-subscription.sh
| @@ -3,24 +3,31 @@ set -Eeuo pipefail | |||
| 3 | 3 | ||
| 4 | 4 | # proxmox-subscription-popup.sh | |
| 5 | 5 | # | |
| 6 | - | # Safe Proxmox VE 9 popup suppressor with: | |
| 7 | - | # - specific patch only | |
| 8 | - | # - no repair unless --repair is passed | |
| 9 | - | # - colored output | |
| 10 | - | # - backup before patch | |
| 11 | - | # - automatic rollback if patch verification fails | |
| 6 | + | # Safe Proxmox VE 9 subscription popup suppressor. | |
| 7 | + | # | |
| 8 | + | # Defaults: | |
| 9 | + | # - patch only | |
| 10 | + | # - no repair unless --repair is passed | |
| 11 | + | # - refuses to patch unless file matches expected structure | |
| 12 | 12 | # | |
| 13 | 13 | # Usage: | |
| 14 | 14 | # sudo bash proxmox-subscription-popup.sh | |
| 15 | + | # sudo bash proxmox-subscription-popup.sh --status | |
| 15 | 16 | # sudo bash proxmox-subscription-popup.sh --undo | |
| 16 | 17 | # sudo bash proxmox-subscription-popup.sh --repair | |
| 17 | - | # sudo bash proxmox-subscription-popup.sh --status | |
| 18 | + | # | |
| 19 | + | # Notes: | |
| 20 | + | # - Uses `patch` with a very specific diff | |
| 21 | + | # - Creates a backup before patching | |
| 22 | + | # - Restarts pveproxy after changes | |
| 23 | + | # - Any package update may restore the original file | |
| 18 | 24 | ||
| 19 | 25 | SCRIPT_NAME="$(basename "$0")" | |
| 20 | 26 | JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" | |
| 21 | 27 | STATE_DIR="/root/.proxmox-subscription-popup" | |
| 22 | 28 | BACKUP_FILE="${STATE_DIR}/proxmoxlib.js.bak" | |
| 23 | 29 | PATCH_FILE="${STATE_DIR}/subscription-popup.patch" | |
| 30 | + | WORK_ORIG="${STATE_DIR}/proxmoxlib.js.orig" | |
| 24 | 31 | ||
| 25 | 32 | DO_REPAIR=0 | |
| 26 | 33 | DO_UNDO=0 | |
| @@ -34,10 +41,17 @@ for arg in "$@"; do | |||
| 34 | 41 | -h|--help) | |
| 35 | 42 | cat <<'EOF' | |
| 36 | 43 | Usage: | |
| 37 | - | sudo bash proxmox-subscription-popup.sh Apply the patch | |
| 38 | - | sudo bash proxmox-subscription-popup.sh --undo Restore backup if present | |
| 39 | - | sudo bash proxmox-subscription-popup.sh --repair Reinstall owning package, restart proxy | |
| 40 | - | sudo bash proxmox-subscription-popup.sh --status Show current state | |
| 44 | + | sudo bash proxmox-subscription-popup.sh | |
| 45 | + | Apply the popup suppression patch | |
| 46 | + | ||
| 47 | + | sudo bash proxmox-subscription-popup.sh --status | |
| 48 | + | Show current state | |
| 49 | + | ||
| 50 | + | sudo bash proxmox-subscription-popup.sh --undo | |
| 51 | + | Restore the original file from backup | |
| 52 | + | ||
| 53 | + | sudo bash proxmox-subscription-popup.sh --repair | |
| 54 | + | Reinstall the package that owns proxmoxlib.js and restart pveproxy | |
| 41 | 55 | EOF | |
| 42 | 56 | exit 0 | |
| 43 | 57 | ;; | |
| @@ -48,7 +62,7 @@ EOF | |||
| 48 | 62 | esac | |
| 49 | 63 | done | |
| 50 | 64 | ||
| 51 | - | if [[ "${EUID}" -ne 0 ]]; then | |
| 65 | + | if [ "$(id -u)" -ne 0 ]; then | |
| 52 | 66 | echo "Please run as root." >&2 | |
| 53 | 67 | exit 1 | |
| 54 | 68 | fi | |
| @@ -84,6 +98,8 @@ warn() { say "${C_YELLOW}⚠${C_RESET} $*"; } | |||
| 84 | 98 | fail() { say "${C_RED}✖${C_RESET} $*" >&2; } | |
| 85 | 99 | title() { say "${C_BOLD}${C_MAGENTA}$*${C_RESET}"; } | |
| 86 | 100 | ||
| 101 | + | trap 'fail "Script failed on line ${LINENO}. No further changes will be made."' ERR | |
| 102 | + | ||
| 87 | 103 | restart_proxy() { | |
| 88 | 104 | info "Restarting pveproxy..." | |
| 89 | 105 | systemctl restart pveproxy | |
| @@ -113,29 +129,56 @@ version_major() { | |||
| 113 | 129 | pveversion | sed -n 's/^pve-manager\/\([0-9]\+\)\..*/\1/p' | head -n1 | |
| 114 | 130 | } | |
| 115 | 131 | ||
| 116 | - | is_patched() { | |
| 117 | - | grep -Fq "orig_cmd(); return;" "$JS_FILE" | |
| 132 | + | require_file() { | |
| 133 | + | [[ -f "$JS_FILE" ]] || { fail "Missing file: $JS_FILE"; exit 1; } | |
| 134 | + | } | |
| 135 | + | ||
| 136 | + | has_patch_tool() { | |
| 137 | + | command -v patch >/dev/null 2>&1 | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | ensure_patch_tool() { | |
| 141 | + | if has_patch_tool; then | |
| 142 | + | return 0 | |
| 143 | + | fi | |
| 144 | + | fail "'patch' command not found." | |
| 145 | + | fail "Install it with: apt-get update && apt-get install -y patch" | |
| 146 | + | exit 1 | |
| 118 | 147 | } | |
| 119 | 148 | ||
| 149 | + | # Very specific to the function structure from the uploaded file. | |
| 120 | 150 | has_expected_original() { | |
| 121 | - | grep -Fq "if (data.status.toLowerCase() !== 'active')" "$JS_FILE" && | |
| 122 | - | grep -Fq "Ext.Msg.show({" "$JS_FILE" | |
| 151 | + | grep -Fq "checked_command: function (orig_cmd) {" "$JS_FILE" && | |
| 152 | + | grep -Fq "url: '/nodes/localhost/subscription'" "$JS_FILE" && | |
| 153 | + | grep -Fq "res.data.status.toLowerCase() !== 'active'" "$JS_FILE" && | |
| 154 | + | grep -Fq "title: gettext('No valid subscription')" "$JS_FILE" | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | # Detects our injected short-circuit immediately after the function opens. | |
| 158 | + | is_patched() { | |
| 159 | + | python3 - <<'PY' "$JS_FILE" | |
| 160 | + | import re, sys | |
| 161 | + | path = sys.argv[1] | |
| 162 | + | text = open(path, 'r', encoding='utf-8').read() | |
| 163 | + | pat = re.compile( | |
| 164 | + | r"checked_command:\s*function\s*\(orig_cmd\)\s*\{\s*orig_cmd\(\);\s*return;\s*Proxmox\.Utils\.API2Request\(", | |
| 165 | + | re.S, | |
| 166 | + | ) | |
| 167 | + | sys.exit(0 if pat.search(text) else 1) | |
| 168 | + | PY | |
| 123 | 169 | } | |
| 124 | 170 | ||
| 125 | 171 | write_patch_file() { | |
| 126 | 172 | cat > "$PATCH_FILE" <<'EOF' | |
| 127 | 173 | --- proxmoxlib.js.orig | |
| 128 | 174 | +++ proxmoxlib.js | |
| 129 | - | @@ -1,7 +1,8 @@ | |
| 130 | - | - checked_command: function(orig_cmd) { | |
| 131 | - | + checked_command: function(orig_cmd) { | |
| 132 | - | + orig_cmd(); return; | |
| 133 | - | Proxmox.Utils.API2Request({ | |
| 134 | - | url: '/nodes/localhost/subscription', | |
| 135 | - | method: 'GET', | |
| 136 | - | failure: function(response, opts) { | |
| 137 | - | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
| 138 | - | }, | |
| 175 | + | @@ -1,5 +1,7 @@ | |
| 176 | + | checked_command: function (orig_cmd) { | |
| 177 | + | + orig_cmd(); | |
| 178 | + | + return; | |
| 179 | + | Proxmox.Utils.API2Request({ | |
| 180 | + | url: '/nodes/localhost/subscription', | |
| 181 | + | method: 'GET', | |
| 139 | 182 | EOF | |
| 140 | 183 | } | |
| 141 | 184 | ||
| @@ -147,40 +190,47 @@ status() { | |||
| 147 | 190 | exit 1 | |
| 148 | 191 | fi | |
| 149 | 192 | ||
| 150 | - | local major | |
| 193 | + | require_file | |
| 194 | + | ||
| 195 | + | local major owner | |
| 151 | 196 | major="$(version_major || true)" | |
| 197 | + | owner="$(detect_owner_pkg || true)" | |
| 198 | + | ||
| 152 | 199 | if [[ -n "$major" ]]; then | |
| 153 | 200 | info "Detected Proxmox major version: ${C_BOLD}${major}${C_RESET}" | |
| 201 | + | else | |
| 202 | + | warn "Could not determine Proxmox major version" | |
| 154 | 203 | fi | |
| 155 | 204 | ||
| 156 | - | if [[ ! -f "$JS_FILE" ]]; then | |
| 157 | - | fail "JS file not found: $JS_FILE" | |
| 158 | - | exit 1 | |
| 159 | - | fi | |
| 160 | - | ||
| 161 | - | local owner | |
| 162 | - | owner="$(detect_owner_pkg || true)" | |
| 163 | 205 | if [[ -n "$owner" ]]; then | |
| 164 | 206 | info "Owning package: ${C_BOLD}${owner}${C_RESET}" | |
| 207 | + | else | |
| 208 | + | warn "Could not determine owning package" | |
| 209 | + | fi | |
| 210 | + | ||
| 211 | + | if has_expected_original; then | |
| 212 | + | ok "Expected original popup code pattern is present" | |
| 213 | + | else | |
| 214 | + | warn "Expected original popup code pattern is NOT present" | |
| 165 | 215 | fi | |
| 166 | 216 | ||
| 167 | 217 | if is_patched; then | |
| 168 | - | ok "Popup suppression patch appears to be installed." | |
| 218 | + | ok "Patch appears to be installed" | |
| 169 | 219 | else | |
| 170 | - | warn "Popup suppression patch does not appear to be installed." | |
| 220 | + | warn "Patch does not appear to be installed" | |
| 171 | 221 | fi | |
| 172 | 222 | ||
| 173 | 223 | if [[ -f "$BACKUP_FILE" ]]; then | |
| 174 | 224 | info "Backup present: ${C_BOLD}${BACKUP_FILE}${C_RESET}" | |
| 175 | 225 | else | |
| 176 | - | warn "No backup found in ${STATE_DIR}" | |
| 226 | + | warn "No backup found at ${STATE_DIR}" | |
| 177 | 227 | fi | |
| 178 | 228 | } | |
| 179 | 229 | ||
| 180 | 230 | repair() { | |
| 181 | 231 | title "Repairing Proxmox web UI file" | |
| 182 | 232 | ||
| 183 | - | [[ -f "$JS_FILE" ]] || { fail "Missing file: $JS_FILE"; exit 1; } | |
| 233 | + | require_file | |
| 184 | 234 | ||
| 185 | 235 | local owner | |
| 186 | 236 | owner="$(detect_owner_pkg || true)" | |
| @@ -190,8 +240,7 @@ repair() { | |||
| 190 | 240 | apt-get update | |
| 191 | 241 | apt-get install --reinstall -y "$owner" | |
| 192 | 242 | ||
| 193 | - | [[ -f "$JS_FILE" ]] || { fail "File still missing after reinstall"; exit 1; } | |
| 194 | - | ||
| 243 | + | require_file | |
| 195 | 244 | restart_proxy | |
| 196 | 245 | ok "Repair complete" | |
| 197 | 246 | show_browser_hint | |
| @@ -201,7 +250,7 @@ undo() { | |||
| 201 | 250 | title "Restoring original file" | |
| 202 | 251 | ||
| 203 | 252 | [[ -f "$BACKUP_FILE" ]] || { fail "No backup found at $BACKUP_FILE"; exit 1; } | |
| 204 | - | [[ -f "$JS_FILE" ]] || { fail "Target file missing: $JS_FILE"; exit 1; } | |
| 253 | + | require_file | |
| 205 | 254 | ||
| 206 | 255 | cp -f "$BACKUP_FILE" "$JS_FILE" | |
| 207 | 256 | ok "Original file restored from backup" | |
| @@ -213,7 +262,8 @@ undo() { | |||
| 213 | 262 | apply_patch() { | |
| 214 | 263 | title "Applying safe Proxmox popup patch" | |
| 215 | 264 | ||
| 216 | - | [[ -f "$JS_FILE" ]] || { fail "Missing file: $JS_FILE"; exit 1; } | |
| 265 | + | require_file | |
| 266 | + | ensure_patch_tool | |
| 217 | 267 | ||
| 218 | 268 | if ! is_proxmox; then | |
| 219 | 269 | fail "This does not look like a Proxmox host." | |
| @@ -222,11 +272,11 @@ apply_patch() { | |||
| 222 | 272 | ||
| 223 | 273 | local major | |
| 224 | 274 | major="$(version_major || true)" | |
| 225 | - | if [[ "$major" != "9" ]]; then | |
| 226 | - | warn "This script was requested for Proxmox 9. Detected major version: ${major:-unknown}" | |
| 227 | - | warn "Continuing only if the target code still matches exactly." | |
| 228 | - | else | |
| 275 | + | if [[ "$major" == "9" ]]; then | |
| 229 | 276 | ok "Detected Proxmox VE 9" | |
| 277 | + | else | |
| 278 | + | warn "Expected Proxmox VE 9, detected: ${major:-unknown}" | |
| 279 | + | warn "Proceeding only if the file matches the exact expected structure" | |
| 230 | 280 | fi | |
| 231 | 281 | ||
| 232 | 282 | if is_patched; then | |
| @@ -238,9 +288,10 @@ apply_patch() { | |||
| 238 | 288 | if ! has_expected_original; then | |
| 239 | 289 | fail "The expected original code pattern was not found." | |
| 240 | 290 | fail "Refusing to patch, because the file layout is not what this script expects." | |
| 241 | - | fail "Use --repair first if the file is damaged, or inspect the current file manually." | |
| 291 | + | fail "Use --repair first if the file is damaged, then try again." | |
| 242 | 292 | exit 1 | |
| 243 | 293 | fi | |
| 294 | + | ok "Expected original code pattern found" | |
| 244 | 295 | ||
| 245 | 296 | if [[ ! -f "$BACKUP_FILE" ]]; then | |
| 246 | 297 | info "Creating backup at ${C_BOLD}${BACKUP_FILE}${C_RESET}" | |
| @@ -250,35 +301,34 @@ apply_patch() { | |||
| 250 | 301 | warn "Backup already exists, leaving it untouched" | |
| 251 | 302 | fi | |
| 252 | 303 | ||
| 253 | - | local tmp | |
| 254 | - | tmp="$(mktemp)" | |
| 255 | - | cp -a "$JS_FILE" "$tmp" | |
| 256 | - | ||
| 304 | + | cp -a "$JS_FILE" "$WORK_ORIG" | |
| 257 | 305 | write_patch_file | |
| 258 | 306 | ||
| 259 | - | info "Checking whether patch applies cleanly..." | |
| 260 | - | if ! patch --dry-run "$tmp" "$PATCH_FILE" >/dev/null 2>&1; then | |
| 261 | - | rm -f "$tmp" | |
| 262 | - | fail "Patch dry-run failed. File is not an exact match for this patch." | |
| 307 | + | info "Testing patch with dry-run..." | |
| 308 | + | if ! (cd "$STATE_DIR" && patch --dry-run proxmoxlib.js.orig subscription-popup.patch >/dev/null 2>&1); then | |
| 309 | + | fail "Patch dry-run failed." | |
| 263 | 310 | fail "No changes were made." | |
| 264 | 311 | exit 1 | |
| 265 | 312 | fi | |
| 266 | 313 | ok "Patch dry-run succeeded" | |
| 267 | 314 | ||
| 268 | 315 | info "Applying patch..." | |
| 269 | - | patch "$JS_FILE" "$PATCH_FILE" >/dev/null | |
| 270 | - | ok "Patch applied" | |
| 316 | + | cp -a "$JS_FILE" "$WORK_ORIG" | |
| 317 | + | if ! (cd "$STATE_DIR" && patch "$WORK_ORIG" subscription-popup.patch >/dev/null 2>&1); then | |
| 318 | + | fail "Patch command failed unexpectedly." | |
| 319 | + | exit 1 | |
| 320 | + | fi | |
| 321 | + | ||
| 322 | + | cp -f "$WORK_ORIG" "$JS_FILE" | |
| 271 | 323 | ||
| 272 | 324 | if ! is_patched; then | |
| 273 | 325 | warn "Verification failed after patch; restoring backup" | |
| 274 | 326 | cp -f "$BACKUP_FILE" "$JS_FILE" | |
| 275 | - | rm -f "$tmp" | |
| 276 | 327 | fail "Patch did not verify cleanly. Original file restored." | |
| 277 | 328 | exit 1 | |
| 278 | 329 | fi | |
| 279 | 330 | ||
| 280 | - | rm -f "$tmp" | |
| 281 | - | ||
| 331 | + | ok "Patch verified successfully" | |
| 282 | 332 | restart_proxy | |
| 283 | 333 | ok "Subscription popup suppression is in place" | |
| 284 | 334 | show_browser_hint | |
OneWheelGeek revised this gist 1 month ago. Go to revision
1 file changed, 292 insertions, 4 deletions
kill-subscription.sh
| @@ -1,4 +1,292 @@ | |||
| 1 | - | #!/bin/bash | |
| 2 | - | sed -i.bak "/No valid subscription/,+20 s/^/\/\//" \ | |
| 3 | - | /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js | |
| 4 | - | systemctl restart pveproxy | |
| 1 | + | #!/usr/bin/env bash | |
| 2 | + | set -Eeuo pipefail | |
| 3 | + | ||
| 4 | + | # proxmox-subscription-popup.sh | |
| 5 | + | # | |
| 6 | + | # Safe Proxmox VE 9 popup suppressor with: | |
| 7 | + | # - specific patch only | |
| 8 | + | # - no repair unless --repair is passed | |
| 9 | + | # - colored output | |
| 10 | + | # - backup before patch | |
| 11 | + | # - automatic rollback if patch verification fails | |
| 12 | + | # | |
| 13 | + | # Usage: | |
| 14 | + | # sudo bash proxmox-subscription-popup.sh | |
| 15 | + | # sudo bash proxmox-subscription-popup.sh --undo | |
| 16 | + | # sudo bash proxmox-subscription-popup.sh --repair | |
| 17 | + | # sudo bash proxmox-subscription-popup.sh --status | |
| 18 | + | ||
| 19 | + | SCRIPT_NAME="$(basename "$0")" | |
| 20 | + | JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" | |
| 21 | + | STATE_DIR="/root/.proxmox-subscription-popup" | |
| 22 | + | BACKUP_FILE="${STATE_DIR}/proxmoxlib.js.bak" | |
| 23 | + | PATCH_FILE="${STATE_DIR}/subscription-popup.patch" | |
| 24 | + | ||
| 25 | + | DO_REPAIR=0 | |
| 26 | + | DO_UNDO=0 | |
| 27 | + | DO_STATUS=0 | |
| 28 | + | ||
| 29 | + | for arg in "$@"; do | |
| 30 | + | case "$arg" in | |
| 31 | + | --repair) DO_REPAIR=1 ;; | |
| 32 | + | --undo) DO_UNDO=1 ;; | |
| 33 | + | --status) DO_STATUS=1 ;; | |
| 34 | + | -h|--help) | |
| 35 | + | cat <<'EOF' | |
| 36 | + | Usage: | |
| 37 | + | sudo bash proxmox-subscription-popup.sh Apply the patch | |
| 38 | + | sudo bash proxmox-subscription-popup.sh --undo Restore backup if present | |
| 39 | + | sudo bash proxmox-subscription-popup.sh --repair Reinstall owning package, restart proxy | |
| 40 | + | sudo bash proxmox-subscription-popup.sh --status Show current state | |
| 41 | + | EOF | |
| 42 | + | exit 0 | |
| 43 | + | ;; | |
| 44 | + | *) | |
| 45 | + | echo "Unknown argument: $arg" >&2 | |
| 46 | + | exit 2 | |
| 47 | + | ;; | |
| 48 | + | esac | |
| 49 | + | done | |
| 50 | + | ||
| 51 | + | if [[ "${EUID}" -ne 0 ]]; then | |
| 52 | + | echo "Please run as root." >&2 | |
| 53 | + | exit 1 | |
| 54 | + | fi | |
| 55 | + | ||
| 56 | + | mkdir -p "$STATE_DIR" | |
| 57 | + | ||
| 58 | + | if [[ -t 1 ]]; then | |
| 59 | + | C_RESET=$'\033[0m' | |
| 60 | + | C_BOLD=$'\033[1m' | |
| 61 | + | C_DIM=$'\033[2m' | |
| 62 | + | C_RED=$'\033[1;31m' | |
| 63 | + | C_GREEN=$'\033[1;32m' | |
| 64 | + | C_YELLOW=$'\033[1;33m' | |
| 65 | + | C_BLUE=$'\033[1;34m' | |
| 66 | + | C_MAGENTA=$'\033[1;35m' | |
| 67 | + | C_CYAN=$'\033[1;36m' | |
| 68 | + | else | |
| 69 | + | C_RESET="" | |
| 70 | + | C_BOLD="" | |
| 71 | + | C_DIM="" | |
| 72 | + | C_RED="" | |
| 73 | + | C_GREEN="" | |
| 74 | + | C_YELLOW="" | |
| 75 | + | C_BLUE="" | |
| 76 | + | C_MAGENTA="" | |
| 77 | + | C_CYAN="" | |
| 78 | + | fi | |
| 79 | + | ||
| 80 | + | say() { printf "%b\n" "$*"; } | |
| 81 | + | info() { say "${C_CYAN}▶${C_RESET} $*"; } | |
| 82 | + | ok() { say "${C_GREEN}✔${C_RESET} $*"; } | |
| 83 | + | warn() { say "${C_YELLOW}⚠${C_RESET} $*"; } | |
| 84 | + | fail() { say "${C_RED}✖${C_RESET} $*" >&2; } | |
| 85 | + | title() { say "${C_BOLD}${C_MAGENTA}$*${C_RESET}"; } | |
| 86 | + | ||
| 87 | + | restart_proxy() { | |
| 88 | + | info "Restarting pveproxy..." | |
| 89 | + | systemctl restart pveproxy | |
| 90 | + | ok "pveproxy restarted" | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | show_browser_hint() { | |
| 94 | + | say | |
| 95 | + | say "${C_BOLD}Browser refresh:${C_RESET} hard refresh the UI after this." | |
| 96 | + | say "Mac: ${C_DIM}Cmd+Shift+R${C_RESET}" | |
| 97 | + | say "Other: ${C_DIM}Ctrl+Shift+R${C_RESET}" | |
| 98 | + | say | |
| 99 | + | } | |
| 100 | + | ||
| 101 | + | detect_owner_pkg() { | |
| 102 | + | dpkg -S "$JS_FILE" 2>/dev/null | head -n1 | cut -d: -f1 | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | is_proxmox() { | |
| 106 | + | command -v pveversion >/dev/null 2>&1 | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | version_major() { | |
| 110 | + | if ! command -v pveversion >/dev/null 2>&1; then | |
| 111 | + | return 1 | |
| 112 | + | fi | |
| 113 | + | pveversion | sed -n 's/^pve-manager\/\([0-9]\+\)\..*/\1/p' | head -n1 | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | is_patched() { | |
| 117 | + | grep -Fq "orig_cmd(); return;" "$JS_FILE" | |
| 118 | + | } | |
| 119 | + | ||
| 120 | + | has_expected_original() { | |
| 121 | + | grep -Fq "if (data.status.toLowerCase() !== 'active')" "$JS_FILE" && | |
| 122 | + | grep -Fq "Ext.Msg.show({" "$JS_FILE" | |
| 123 | + | } | |
| 124 | + | ||
| 125 | + | write_patch_file() { | |
| 126 | + | cat > "$PATCH_FILE" <<'EOF' | |
| 127 | + | --- proxmoxlib.js.orig | |
| 128 | + | +++ proxmoxlib.js | |
| 129 | + | @@ -1,7 +1,8 @@ | |
| 130 | + | - checked_command: function(orig_cmd) { | |
| 131 | + | + checked_command: function(orig_cmd) { | |
| 132 | + | + orig_cmd(); return; | |
| 133 | + | Proxmox.Utils.API2Request({ | |
| 134 | + | url: '/nodes/localhost/subscription', | |
| 135 | + | method: 'GET', | |
| 136 | + | failure: function(response, opts) { | |
| 137 | + | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
| 138 | + | }, | |
| 139 | + | EOF | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | status() { | |
| 143 | + | title "Proxmox subscription popup status" | |
| 144 | + | ||
| 145 | + | if ! is_proxmox; then | |
| 146 | + | fail "This does not look like a Proxmox host." | |
| 147 | + | exit 1 | |
| 148 | + | fi | |
| 149 | + | ||
| 150 | + | local major | |
| 151 | + | major="$(version_major || true)" | |
| 152 | + | if [[ -n "$major" ]]; then | |
| 153 | + | info "Detected Proxmox major version: ${C_BOLD}${major}${C_RESET}" | |
| 154 | + | fi | |
| 155 | + | ||
| 156 | + | if [[ ! -f "$JS_FILE" ]]; then | |
| 157 | + | fail "JS file not found: $JS_FILE" | |
| 158 | + | exit 1 | |
| 159 | + | fi | |
| 160 | + | ||
| 161 | + | local owner | |
| 162 | + | owner="$(detect_owner_pkg || true)" | |
| 163 | + | if [[ -n "$owner" ]]; then | |
| 164 | + | info "Owning package: ${C_BOLD}${owner}${C_RESET}" | |
| 165 | + | fi | |
| 166 | + | ||
| 167 | + | if is_patched; then | |
| 168 | + | ok "Popup suppression patch appears to be installed." | |
| 169 | + | else | |
| 170 | + | warn "Popup suppression patch does not appear to be installed." | |
| 171 | + | fi | |
| 172 | + | ||
| 173 | + | if [[ -f "$BACKUP_FILE" ]]; then | |
| 174 | + | info "Backup present: ${C_BOLD}${BACKUP_FILE}${C_RESET}" | |
| 175 | + | else | |
| 176 | + | warn "No backup found in ${STATE_DIR}" | |
| 177 | + | fi | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | repair() { | |
| 181 | + | title "Repairing Proxmox web UI file" | |
| 182 | + | ||
| 183 | + | [[ -f "$JS_FILE" ]] || { fail "Missing file: $JS_FILE"; exit 1; } | |
| 184 | + | ||
| 185 | + | local owner | |
| 186 | + | owner="$(detect_owner_pkg || true)" | |
| 187 | + | [[ -n "$owner" ]] || { fail "Could not determine owning package for $JS_FILE"; exit 1; } | |
| 188 | + | ||
| 189 | + | info "Reinstalling package: ${C_BOLD}${owner}${C_RESET}" | |
| 190 | + | apt-get update | |
| 191 | + | apt-get install --reinstall -y "$owner" | |
| 192 | + | ||
| 193 | + | [[ -f "$JS_FILE" ]] || { fail "File still missing after reinstall"; exit 1; } | |
| 194 | + | ||
| 195 | + | restart_proxy | |
| 196 | + | ok "Repair complete" | |
| 197 | + | show_browser_hint | |
| 198 | + | } | |
| 199 | + | ||
| 200 | + | undo() { | |
| 201 | + | title "Restoring original file" | |
| 202 | + | ||
| 203 | + | [[ -f "$BACKUP_FILE" ]] || { fail "No backup found at $BACKUP_FILE"; exit 1; } | |
| 204 | + | [[ -f "$JS_FILE" ]] || { fail "Target file missing: $JS_FILE"; exit 1; } | |
| 205 | + | ||
| 206 | + | cp -f "$BACKUP_FILE" "$JS_FILE" | |
| 207 | + | ok "Original file restored from backup" | |
| 208 | + | ||
| 209 | + | restart_proxy | |
| 210 | + | show_browser_hint | |
| 211 | + | } | |
| 212 | + | ||
| 213 | + | apply_patch() { | |
| 214 | + | title "Applying safe Proxmox popup patch" | |
| 215 | + | ||
| 216 | + | [[ -f "$JS_FILE" ]] || { fail "Missing file: $JS_FILE"; exit 1; } | |
| 217 | + | ||
| 218 | + | if ! is_proxmox; then | |
| 219 | + | fail "This does not look like a Proxmox host." | |
| 220 | + | exit 1 | |
| 221 | + | fi | |
| 222 | + | ||
| 223 | + | local major | |
| 224 | + | major="$(version_major || true)" | |
| 225 | + | if [[ "$major" != "9" ]]; then | |
| 226 | + | warn "This script was requested for Proxmox 9. Detected major version: ${major:-unknown}" | |
| 227 | + | warn "Continuing only if the target code still matches exactly." | |
| 228 | + | else | |
| 229 | + | ok "Detected Proxmox VE 9" | |
| 230 | + | fi | |
| 231 | + | ||
| 232 | + | if is_patched; then | |
| 233 | + | ok "Patch is already installed. Nothing to do." | |
| 234 | + | show_browser_hint | |
| 235 | + | return 0 | |
| 236 | + | fi | |
| 237 | + | ||
| 238 | + | if ! has_expected_original; then | |
| 239 | + | fail "The expected original code pattern was not found." | |
| 240 | + | fail "Refusing to patch, because the file layout is not what this script expects." | |
| 241 | + | fail "Use --repair first if the file is damaged, or inspect the current file manually." | |
| 242 | + | exit 1 | |
| 243 | + | fi | |
| 244 | + | ||
| 245 | + | if [[ ! -f "$BACKUP_FILE" ]]; then | |
| 246 | + | info "Creating backup at ${C_BOLD}${BACKUP_FILE}${C_RESET}" | |
| 247 | + | cp -a "$JS_FILE" "$BACKUP_FILE" | |
| 248 | + | ok "Backup created" | |
| 249 | + | else | |
| 250 | + | warn "Backup already exists, leaving it untouched" | |
| 251 | + | fi | |
| 252 | + | ||
| 253 | + | local tmp | |
| 254 | + | tmp="$(mktemp)" | |
| 255 | + | cp -a "$JS_FILE" "$tmp" | |
| 256 | + | ||
| 257 | + | write_patch_file | |
| 258 | + | ||
| 259 | + | info "Checking whether patch applies cleanly..." | |
| 260 | + | if ! patch --dry-run "$tmp" "$PATCH_FILE" >/dev/null 2>&1; then | |
| 261 | + | rm -f "$tmp" | |
| 262 | + | fail "Patch dry-run failed. File is not an exact match for this patch." | |
| 263 | + | fail "No changes were made." | |
| 264 | + | exit 1 | |
| 265 | + | fi | |
| 266 | + | ok "Patch dry-run succeeded" | |
| 267 | + | ||
| 268 | + | info "Applying patch..." | |
| 269 | + | patch "$JS_FILE" "$PATCH_FILE" >/dev/null | |
| 270 | + | ok "Patch applied" | |
| 271 | + | ||
| 272 | + | if ! is_patched; then | |
| 273 | + | warn "Verification failed after patch; restoring backup" | |
| 274 | + | cp -f "$BACKUP_FILE" "$JS_FILE" | |
| 275 | + | rm -f "$tmp" | |
| 276 | + | fail "Patch did not verify cleanly. Original file restored." | |
| 277 | + | exit 1 | |
| 278 | + | fi | |
| 279 | + | ||
| 280 | + | rm -f "$tmp" | |
| 281 | + | ||
| 282 | + | restart_proxy | |
| 283 | + | ok "Subscription popup suppression is in place" | |
| 284 | + | show_browser_hint | |
| 285 | + | } | |
| 286 | + | ||
| 287 | + | case 1 in | |
| 288 | + | $DO_STATUS) status ;; | |
| 289 | + | $DO_UNDO) undo ;; | |
| 290 | + | $DO_REPAIR) repair ;; | |
| 291 | + | *) apply_patch ;; | |
| 292 | + | esac | |
OneWheelGeek revised this gist 1 month ago. Go to revision
1 file changed, 4 insertions
kill-subscription.sh(file created)
| @@ -0,0 +1,4 @@ | |||
| 1 | + | #!/bin/bash | |
| 2 | + | sed -i.bak "/No valid subscription/,+20 s/^/\/\//" \ | |
| 3 | + | /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js | |
| 4 | + | systemctl restart pveproxy | |