sync: auto-sync from HOWARD-HOME at 2026-06-25 12:53:21

Author: Howard Enos
Machine: HOWARD-HOME
Timestamp: 2026-06-25 12:53:21
This commit is contained in:
2026-06-25 12:54:04 -07:00
parent 51751e6473
commit d4fd71baab
14 changed files with 153 additions and 51 deletions

View File

@@ -123,9 +123,7 @@
- [pfSense 25.07 ops quirks](reference_pfsense_25_07_ops.md) — Cascades pfSense Plus 25.07: logs are PLAIN TEXT (use tail/grep, NOT clog → clog returns empty); clean dhcpd restart = `services_dhcpd_configure()` via slow pfSsh.php (needs 50s+ timeout); dirty boot can leave 2 dhcpd → DISCOVER/OFFER but no ACK; reboot the Cox modem after a config restore; ZFS survives power loss. From the 2026-06-17 power-outage incident.
- [feedback_ascii_only_api_payloads](feedback_ascii_only_api_payloads.md) -- On Windows/Git-bash, non-ASCII chars (em-dash, arrow, smart quotes) in JSON payload TEXT passed to curl get mangled and rejected — Discord bot-alert returns 400, the coord API returns "error parsing the body". Use ASCII-only in API payload text, or a single-quoted heredoc.
- [feedback_bitdefender_unattended_install](feedback_bitdefender_unattended_install.md) -- Bitdefender unattended RMM install must use the FULL KIT as SYSTEM (silent, no UAC) — the downloader stub fails headless and triggers UAC
- [Broken [[backlinks]] are write-me-later markers — flesh out from session history, don't delete](feedback_broken_backlinks_are_writeme_markers.md) -- A [[name]] link in a memory body whose target file doesn't exist is NOT an error to clean up — it's an intentional marker that that memory is worth writing. When you hit one (or memory-dream lists them), flesh the missing memory out from the session logs / session history, don't strip the link.
- [feedback_rmm_longops_fire_and_forget](feedback_rmm_longops_fire_and_forget.md) -- Long-running RMM endpoint ops (software installs, big downloads) must be fire-and-forget, not live-monitored
- [Broken [[backlinks]] are write-me-later markers — flesh out from session history, don't delete](feedback_broken_backlinks_are_writeme_markers.md) -- A [[name]] link in a memory body whose target file doesn't exist is NOT an error to clean up — it's an intentional marker that that memory is worth writing. When you hit one (or memory-dream lists them), flesh the missing memory out from the session logs / session history, don't strip the link.
## Machine
- [GURU-5070 Workstation Setup](reference_workstation_setup.md) — Mike's primary (owner confirmed 2026-05-26). Windows 11 Pro. Renamed from OC-5070 → ACG-5070/acg-guru-5070 → GURU-5070; all the same box, all Mike's.

View File

@@ -23,9 +23,11 @@ import os
import random
import subprocess
import sys
import tempfile
import time
import urllib.error
import urllib.request
from contextlib import contextmanager
from dataclasses import dataclass, field
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
@@ -111,6 +113,11 @@ CACHE_TTL_SECONDS = 86400
SKILL_DIR = Path(__file__).resolve().parent.parent
CACHE_DIR = SKILL_DIR / ".cache"
CACHE_FILE = CACHE_DIR / "inventory.json"
CACHE_LOCK_FILE = CACHE_DIR / "inventory.lock"
# Best-effort advisory lock for read-modify-write of the cache. Short timeout:
# losing a write-through update is acceptable; hanging the CLI is not.
CACHE_LOCK_TIMEOUT_SECONDS = 5.0
CACHE_LOCK_STALE_SECONDS = 30.0
class GravityZoneError(RuntimeError):
@@ -1136,10 +1143,60 @@ class GravityZoneClient:
return None
def _write_cache(self, cache: dict) -> None:
"""Atomically replace the cache file (temp write + os.replace) so a crash
mid-write or a concurrent reader can never see a truncated file."""
CACHE_DIR.mkdir(parents=True, exist_ok=True)
CACHE_FILE.write_text(
json.dumps(cache, indent=2, sort_keys=True), encoding="utf-8"
)
payload = json.dumps(cache, indent=2, sort_keys=True)
fd, tmp = tempfile.mkstemp(dir=str(CACHE_DIR), prefix=".inventory.",
suffix=".tmp")
try:
with os.fdopen(fd, "w", encoding="utf-8") as fh:
fh.write(payload)
fh.flush()
os.fsync(fh.fileno())
os.replace(tmp, CACHE_FILE) # atomic on the same filesystem
except BaseException:
try:
os.unlink(tmp)
except OSError:
pass
raise
@contextmanager
def _cache_lock(self):
"""Best-effort cross-platform advisory lock around a read-modify-write of
the cache, so two concurrent gz.py invocations don't lose each other's
write-through update. Steals a stale lock; on timeout proceeds unlocked
(a lost update is tolerable, a hang is not)."""
CACHE_DIR.mkdir(parents=True, exist_ok=True)
deadline = time.monotonic() + CACHE_LOCK_TIMEOUT_SECONDS
acquired = False
while True:
try:
fd = os.open(str(CACHE_LOCK_FILE),
os.O_CREAT | os.O_EXCL | os.O_WRONLY)
os.close(fd)
acquired = True
break
except FileExistsError:
try:
age = time.time() - os.path.getmtime(CACHE_LOCK_FILE)
if age > CACHE_LOCK_STALE_SECONDS:
os.unlink(CACHE_LOCK_FILE)
continue
except OSError:
pass
if time.monotonic() >= deadline:
break # give up the lock, proceed unlocked
time.sleep(0.1)
try:
yield
finally:
if acquired:
try:
os.unlink(CACHE_LOCK_FILE)
except OSError:
pass
def _cache_is_fresh(self, cache: dict) -> bool:
fetched = cache.get("fetched_at")
@@ -1226,27 +1283,29 @@ class GravityZoneClient:
return self.refresh_inventory()
def _cache_add_group(self, group_id: str, name: str) -> None:
cache = self._read_cache()
if cache is None:
return # no cache yet - next refresh picks it up
cache.setdefault("companies", {})
# Groups live in the inventory tree; store under a 'groups' map.
cache.setdefault("groups", {})[group_id] = name
self._write_cache(cache)
with self._cache_lock():
cache = self._read_cache()
if cache is None:
return # no cache yet - next refresh picks it up
# Groups live in the inventory tree; store under a 'groups' map.
cache.setdefault("groups", {})[group_id] = name
self._write_cache(cache)
def _cache_add_package(self, package_name: str, create_result: Any) -> None:
cache = self._read_cache()
if cache is None:
return
packages = cache.setdefault("packages", [])
pkg_id = create_result if isinstance(create_result, str) else None
if isinstance(create_result, dict):
pkg_id = create_result.get("id")
if not any(
(isinstance(p, dict) and p.get("name") == package_name) for p in packages
):
packages.append({"id": pkg_id, "name": package_name})
self._write_cache(cache)
with self._cache_lock():
cache = self._read_cache()
if cache is None:
return
packages = cache.setdefault("packages", [])
pkg_id = create_result if isinstance(create_result, str) else None
if isinstance(create_result, dict):
pkg_id = create_result.get("id")
if not any(
(isinstance(p, dict) and p.get("name") == package_name)
for p in packages
):
packages.append({"id": pkg_id, "name": package_name})
self._write_cache(cache)
def main() -> int:
@@ -1258,9 +1317,4 @@ def main() -> int:
"httpx" if _HAS_HTTPX else "urllib")
return 0
except GravityZoneError as exc:
print(f"[ERROR] {exc}", file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())
prin