|
46 | 46 | os.environ.setdefault("SSL_CERT_FILE", certifi.where()) |
47 | 47 | os.environ.setdefault("REQUESTS_CA_BUNDLE", certifi.where()) |
48 | 48 |
|
| 49 | +# Ignore SIGHUP at import time. Python's default disposition is SIG_DFL |
| 50 | +# (terminate), so an ES game-end SIGHUP arriving before daemon_loop()'s |
| 51 | +# signal.signal(SIGHUP, _on_wake) call kills the process. daemon_loop |
| 52 | +# replaces this with the real handler once it's ready. |
| 53 | +signal.signal(signal.SIGHUP, signal.SIG_IGN) |
| 54 | + |
49 | 55 | # ---------------------------------------------------------------------- |
50 | 56 | # Paths |
51 | 57 | # ---------------------------------------------------------------------- |
@@ -361,12 +367,30 @@ def format_message(outdated: Iterable[str], titles: dict[str, str]) -> str: |
361 | 367 | return f"[PHAROS] {len(items)} updates available" |
362 | 368 |
|
363 | 369 | # ---------------------------------------------------------------------- |
364 | | -# Notify-state dedup (process-local; daemon restart wipes it) |
| 370 | +# Notify-state dedup (tmpfs-backed; survives in-session daemon restarts |
| 371 | +# but cleared on reboot — so the "reboot re-shows the toast" semantic is |
| 372 | +# preserved while spurious mid-session respawns stay silent) |
365 | 373 | # ---------------------------------------------------------------------- |
366 | 374 | def _outdated_hash(items: Iterable[str]) -> str: |
367 | 375 | return hashlib.sha256(",".join(sorted(items)).encode("utf-8")).hexdigest() |
368 | 376 |
|
369 | | -_state_cache: dict = {} |
| 377 | +STATE_FILE = Path("/tmp/pharos-daemon.state") |
| 378 | + |
| 379 | +def _load_state() -> dict: |
| 380 | + try: |
| 381 | + return json.loads(STATE_FILE.read_text("utf-8")) |
| 382 | + except (OSError, json.JSONDecodeError): |
| 383 | + return {} |
| 384 | + |
| 385 | +def _save_state(state: dict) -> None: |
| 386 | + tmp = STATE_FILE.with_suffix(".tmp") |
| 387 | + try: |
| 388 | + tmp.write_text(json.dumps(state), encoding="utf-8") |
| 389 | + os.replace(tmp, STATE_FILE) |
| 390 | + except OSError as e: |
| 391 | + log("WARN", f"state save failed: {e}") |
| 392 | + |
| 393 | +_state_cache: dict = _load_state() |
370 | 394 |
|
371 | 395 | # ---------------------------------------------------------------------- |
372 | 396 | # Main check |
@@ -421,6 +445,7 @@ def run_check() -> tuple[bool, bool]: |
421 | 445 | for attempt in range(1, 7): |
422 | 446 | if notify(cfw, msg): |
423 | 447 | _state_cache["last_outdated_hash"] = h |
| 448 | + _save_state(_state_cache) |
424 | 449 | return True, network_failed |
425 | 450 | time.sleep(backoff) |
426 | 451 | backoff = min(backoff * 2, RETRY_BACKOFF_MAX) |
|
0 commit comments