From 78f943a71badb20834d553cebd4b208ab1783511 Mon Sep 17 00:00:00 2001 From: sudolulo Date: Mon, 15 Jun 2026 02:39:34 +0000 Subject: [PATCH] Fix kill switch not cleared on reinstall; guard midclt delete; tighten sitecustomize imports install.sh: running install.sh after a recover.sh left /data/truecloud-patch/disabled in place, so apply.sh silently skipped all patching and middlewared restarted without the patch. Clear the kill switch file before running apply.sh. uninstall.sh: midclt initshutdownscript.delete was unguarded under set -euo pipefail, so a delete failure (already-removed entry, transient API error) aborted the script before sitecustomize.py was cleaned up or /data/truecloud-patch/ was removed. Now guarded with an if/else that warns and continues. sitecustomize.py: move importlib.machinery/.util imports inside the if block in find_spec so they only execute when intercepting our two target modules, not on every module import across the whole process. In _install(), import os before importlib.util so the kill switch check (cheap) runs before the importlib import (slightly heavier on first use). Remove the unused orig variable in _patch_restic. patch_ui.py: add explicit encoding="utf-8" to both open() calls. errors="replace" on read so malformed bytes in a bundle don't silently skip a candidate file. --- install.sh | 8 ++++++++ patch/patch_ui.py | 4 ++-- patch/sitecustomize.py | 11 +++++------ uninstall.sh | 7 +++++-- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/install.sh b/install.sh index d3c4310..7db705e 100755 --- a/install.sh +++ b/install.sh @@ -73,6 +73,14 @@ else fi echo "" +# ── Clear kill switch if set ────────────────────────────────────────────────── + +if [ -f "$PATCH_DIR/disabled" ]; then + rm "$PATCH_DIR/disabled" + echo "Removed kill switch ($PATCH_DIR/disabled) left from a previous recovery." + echo "" +fi + # ── Apply now ───────────────────────────────────────────────────────────────── echo "Applying patches ..." diff --git a/patch/patch_ui.py b/patch/patch_ui.py index 3eaec6a..c0cafe7 100644 --- a/patch/patch_ui.py +++ b/patch/patch_ui.py @@ -60,7 +60,7 @@ def find_bundle(webui): continue path = os.path.join(root, name) try: - with open(path) as fh: + with open(path, encoding="utf-8", errors="replace") as fh: content = fh.read() if FIND.search(content): matches.append((path, content)) @@ -112,7 +112,7 @@ def main(): patched, count = FIND.subn(REPLACE, content) try: - with open(path, "w") as fh: + with open(path, "w", encoding="utf-8") as fh: fh.write(patched) except OSError as exc: print(f"[truecloud-patch] ERROR: Could not write {path}: {exc}") diff --git a/patch/sitecustomize.py b/patch/sitecustomize.py index b1f853d..8c8651d 100644 --- a/patch/sitecustomize.py +++ b/patch/sitecustomize.py @@ -41,14 +41,14 @@ class _Finder: self._done = set() # modules already patched def find_spec(self, fullname, path, target=None): # noqa: ARG002 - import importlib.machinery - import importlib.util - if ( fullname in self._targets and fullname not in self._done and fullname not in self._loading ): + import importlib.machinery + import importlib.util + # Find the real file spec HERE, before Python adds the module to # sys.modules. If we deferred this to exec_module, find_spec would # short-circuit via sys.modules[fullname].__spec__ (our own spec) and @@ -135,8 +135,7 @@ def _patch_b2(module): def _patch_restic(module): - orig = module.get_restic_config - if getattr(orig, "_truecloud_patched", False): + if getattr(module.get_restic_config, "_truecloud_patched", False): return # ResticConfig is safe to capture now (it's a dataclass defined in the module). @@ -182,10 +181,10 @@ _PATCHES = { # ── Entry point ─────────────────────────────────────────────────────────────── def _install(): - import importlib.util import os if os.path.exists("/data/truecloud-patch/disabled"): return # kill switch: touch /data/truecloud-patch/disabled to bypass this hook + import importlib.util if importlib.util.find_spec("middlewared") is None: return # not a middlewared Python process; nothing to do sys.meta_path.append(_Finder()) diff --git a/uninstall.sh b/uninstall.sh index 43599e5..a63a625 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -32,8 +32,11 @@ for s in json.load(sys.stdin): if [ -n "$IDS" ]; then for id in $IDS; do - midclt call initshutdownscript.delete "$id" > /dev/null - echo " Removed initshutdownscript id=$id" + if midclt call initshutdownscript.delete "$id" > /dev/null; then + echo " Removed initshutdownscript id=$id" + else + echo " WARNING: could not delete id=$id (already gone?)" + fi done else echo " No entry found (already removed or never installed)."