Last active 1 month ago

OneWheelGeek's Avatar 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's Avatar OneWheelGeek revised this gist 1 month ago. Go to revision

No changes

OneWheelGeek's Avatar 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's Avatar 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's Avatar 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
Newer Older