"""Fixed Bluetooth privilege boundary. Never accepts serials, paths or SDK calls.""" import json import os import pwd import re import subprocess import sys import tempfile import threading import time from contextlib import suppress from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) from layout import CODE, trusted, write # noqa: E402 from runtime.http import Server # noqa: E402 from runtime.identity import read_binding # noqa: E402 from runtime.lifecycle import acquisition # noqa: E402 ROOT = Path("/var/lib/mission-core-x4-recovery") SOCKET = Path("/run/mission-core-x4-recovery/driver.sock") IDENTIFIER = re.compile(r"instax4_[0-9a-f]{32}") def connected(): values = [] for path in Path("/sys/bus/usb/devices").iterdir(): with suppress(OSError, ValueError): values.append(read_binding(path.name)) return values class WakeService: def __init__(self, root=ROOT): self.root = root self.lock = threading.RLock() self.advertiser = threading.Lock() self.running = {} def load(self, ident): path = self.root / (ident + ".json") if not path.exists(): return {"enabled": False, "attempts": []} return json.loads(trusted(path).read_text()) def save(self, ident, value): write(self.root / (ident + ".json"), json.dumps(value).encode(), 0o600) def control(self, ident, enabled): with self.lock: value = self.load(ident) if not enabled and "serial" not in value: return {"enabled": False} if enabled: matches = [x for x in connected() if x.device_id == ident] if len(matches) != 1 or not re.fullmatch(r"[A-Za-z0-9]{7,64}", matches[0].serial): raise ValueError("Connect the exact X4 before enabling recovery") if len(list(self.root.glob("instax4_*.json"))) >= 500 and "serial" not in value: raise ValueError("Recovery inventory is full") value["serial"] = matches[0].serial value["enabled"] = enabled self.save(ident, value) if not enabled and ident in self.running: self.running[ident].terminate() return {"enabled": enabled} def wake(self, ident, attempt): if not self.advertiser.acquire(blocking=False): return {"state": "busy"} try: # Installation cannot replace code/state while a wake is in flight. with acquisition(): return self.wake_locked(ident, attempt) finally: self.advertiser.release() def wake_locked(self, ident, attempt): with self.lock: value = self.load(ident) if not value["enabled"] or "serial" not in value: return {"state": "disabled"} serial = value["serial"] if any(x.device_id == ident for x in connected()): return {"state": "connected"} # A radio suffix is weaker than full USB identity. Refuse known collisions. for path in self.root.glob("instax4_*.json"): other = json.loads(trusted(path).read_text()).get("serial", "") if other and other != serial and other[-6:] == serial[-6:]: return {"state": "identity_conflict"} now = time.time() attempts = [x for x in value["attempts"] if now - x["at"] < 600] if any(x["id"] == attempt for x in attempts): return {"state": "already_attempted"} if len(attempts) >= 3: return {"state": "exhausted"} value["attempts"] = attempts + [{"id": attempt, "at": now}] # Persist before advertising; a crash cannot silently replay the attempt. self.save(ident, value) target = { "device_id": ident, "serial": serial, "bluetooth_address": None, "wakeup_enabled": True, } with tempfile.TemporaryFile(dir=self.root) as output: process = subprocess.Popen( ["/usr/bin/python3", "-I", str(CODE / "ble_wake.py")], stdin=subprocess.PIPE, stdout=output, stderr=subprocess.DEVNULL, ) self.running[ident] = process process.stdin.write(json.dumps(target).encode()) process.stdin.close() # Release state lock during hardware wait so disable can cancel. self.lock.release() try: try: process.wait(timeout=45) except subprocess.TimeoutExpired: process.kill() process.wait(timeout=5) output.seek(0) raw = output.read(1024 * 1024 + 1) finally: self.lock.acquire() self.running.pop(ident, None) state = "unavailable" try: if len(raw) <= 1024 * 1024: report = json.loads(raw) write(self.root / (ident + ".last-wake"), raw, 0o600) if process.returncode == 0 and report.get("sdk_usb_returned"): state = "connected" except (ValueError, OSError): pass return {"state": state if self.load(ident)["enabled"] else "disabled"} def dispatch(self, method, route, value, _headers): if method == "GET" and route == "/health": return {"ready": True} if method != "POST" or not isinstance(value, dict): raise ValueError("Unsupported recovery request") ident = value.get("device_id") if not isinstance(ident, str) or not IDENTIFIER.fullmatch(ident): raise ValueError("Invalid recovery identity") if route == "/control" and set(value) == {"device_id", "enabled"}: if type(value["enabled"]) is not bool: raise ValueError("Recovery requires an explicit boolean") return self.control(ident, value["enabled"]) if route == "/wake" and set(value) == {"device_id", "attempt"}: if not isinstance(value["attempt"], str) or not re.fullmatch( r"[0-9a-f]{32}", value["attempt"] ): raise ValueError("Invalid wake attempt") return self.wake(ident, value["attempt"]) raise ValueError("Unsupported recovery request") def main(): if os.geteuid() or sys.argv[1:]: raise RuntimeError("Use the installed fixed wake service") trusted(ROOT, True) if SOCKET.exists(): SOCKET.unlink() allowed = {0, pwd.getpwnam("mission-core-insta360").pw_uid} with Server(SOCKET, WakeService().dispatch, allowed) as server: SOCKET.chmod(0o660) server.serve_forever(poll_interval=0.5) if __name__ == "__main__": main()