Wiring view: every machine and what connects it (Companion, MIDI, Dante, watchdog, web, Art-Net), hide types or single links, scripts per computer
RESI LIVE NOT REC follows the Resi auto-record service (custom autorec_status, not during its REC hold; stale heartbeat = no verdict); INGEST watches AUTO REC (running, heartbeat, Resi reachable), hold restarts it. Probes list each computer's scripts by file name only. HyperDeck addresses read from bonjourHost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+54
-8
@@ -27,7 +27,7 @@ MAC_PROBE = (
|
||||
'echo SP_AGENTS; launchctl list 2>/dev/null; echo SP_PROCS; ps -axo comm= 2>/dev/null; '
|
||||
'echo SP_APPS; ls /Applications 2>/dev/null; echo SP_HELPER; ls -l {helper} 2>&1; sudo -n -l {helper} 2>&1 | tail -1; '
|
||||
'echo SP_SOLO; cat {solo}/solo-mode.json 2>/dev/null; echo; stat -f %m {solo}/solo-mode.json 2>/dev/null; '
|
||||
'echo SP_NOW; date +%s; echo SP_END'
|
||||
'echo SP_NOW; date +%s; echo SP_SCRIPTS; ps -axo pid=,args= 2>/dev/null | grep -E \'[.](py|js|mjs|sh|zsh|command|swift|rb|pl|scpt)( |$)\' | grep -v SP_SCRIPTS; echo SP_END'
|
||||
)
|
||||
WIN_PROBE = (
|
||||
'echo SP_NAME=%COMPUTERNAME% & echo SP_TASKS & schtasks /Query /FO CSV /NH 2>nul & echo SP_PROCS & tasklist /FO CSV /NH 2>nul & '
|
||||
@@ -35,8 +35,15 @@ WIN_PROBE = (
|
||||
'"(Get-NetConnectionProfile | Select -First 1 -ExpandProperty NetworkCategory); '
|
||||
'(Get-NetAdapterPowerManagement | Select -First 1 -ExpandProperty WakeOnMagicPacket); '
|
||||
'((Get-NetFirewallRule -Name OpenSSH-Server-In-TCP -ErrorAction SilentlyContinue | Get-NetFirewallAddressFilter | '
|
||||
'Select -ExpandProperty RemoteAddress) -join \',\')" 2>nul & echo SP_END'
|
||||
'Select -ExpandProperty RemoteAddress) -join \',\')" 2>nul & echo SP_SCRIPTS & powershell -NoProfile -Command '
|
||||
'"Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match \'[.](ps1|py|js|bat|cmd|vbs)\' } | '
|
||||
'ForEach-Object { [string]$_.ProcessId + \' \' + $_.CommandLine }" 2>nul & echo SP_END'
|
||||
)
|
||||
# A script: a file run by an interpreter (its name only is kept, never the rest of the command line).
|
||||
SCRIPT_RE = r"[^ /\\]+[.](py|js|mjs|sh|zsh|command|swift|rb|pl|scpt|ps1|bat|cmd|vbs)( |\"|$)"
|
||||
# Not the user's scripts: programs' own insides (Companion's modules, app bundles, the system).
|
||||
NOT_SCRIPTS = (".app/Contents/", "/companion/modules/", "/System/", "/usr/libexec/", "/Library/Apple/", "\\Windows\\",
|
||||
"/node_modules/", "Program Files")
|
||||
|
||||
|
||||
def shell_path(path):
|
||||
@@ -113,6 +120,38 @@ def parse_sc(lines):
|
||||
return services
|
||||
|
||||
|
||||
def parse_scripts(lines, agents=None, tasks=None):
|
||||
"""The scripts a computer runs: [{"name", "pid", "by", "running"}]. From `ps` (pid, command line) lines,
|
||||
our own launchd jobs (org.waterschurch.*, linked to their script by pid) and the \\StatusPanel\\ tasks
|
||||
({path: state}). Only a script's file name is kept, never the rest of its command line."""
|
||||
agents, out, seen_pids = agents or {}, [], set()
|
||||
by_pid = {pid: label for label, pid in agents.items() if pid}
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
m = re.match(r"^(\d+)\s+(.*)$", line)
|
||||
if not m or "SP_SCRIPTS" in line:
|
||||
continue
|
||||
found = re.search(SCRIPT_RE, m.group(2))
|
||||
if not found:
|
||||
continue
|
||||
# The script's own path (from the last " /", ' "' or " C:\" before its name): the interpreter may sit
|
||||
# inside an app (Apple's python3 is in Python.app) without the script being a program's insides.
|
||||
head = m.group(2)[:found.end()]
|
||||
cut = max([head.rfind(" /"), head.rfind(' "')] + [x.start() for x in re.finditer(r" [A-Za-z]:\\", head)])
|
||||
if any(x in head[cut + 1:] for x in NOT_SCRIPTS):
|
||||
continue
|
||||
name = re.split(r"[\\/]", found.group(0).strip().strip('"'))[-1]
|
||||
pid = int(m.group(1))
|
||||
out.append({"name": name, "pid": pid, "by": by_pid.get(pid, ""), "running": True})
|
||||
seen_pids.add(pid)
|
||||
for label, pid in sorted(agents.items()):
|
||||
if label.startswith("org.waterschurch.") and pid not in seen_pids:
|
||||
out.append({"name": label[len("org.waterschurch."):], "pid": pid, "by": label, "running": pid is not None})
|
||||
for path, st in sorted((tasks or {}).items()):
|
||||
out.append({"name": path.split("\\")[-1], "pid": None, "by": path, "running": st.lower() == "running", "state": st})
|
||||
return sorted(out, key=lambda x: (not x["running"], x["name"].lower()))
|
||||
|
||||
|
||||
def light(ok, reason=""):
|
||||
return {"light": "ok" if ok else "problem", "reason": reason if not ok else ""}
|
||||
|
||||
@@ -192,7 +231,7 @@ def mac_items(host, parsed, cfg):
|
||||
item.update({"light": "unknown", "reason": ""})
|
||||
items.append(item)
|
||||
return items, {"agents": sorted(a for a in agents if not a.startswith(("com.apple.", "application."))),
|
||||
"processes": sorted(proc_names)[:200]}
|
||||
"processes": sorted(proc_names)[:200], "scripts": parse_scripts(parsed.get("SP_SCRIPTS", []), agents)}
|
||||
|
||||
|
||||
def windows_items(host, parsed):
|
||||
@@ -228,8 +267,13 @@ def windows_items(host, parsed):
|
||||
item.update({"light": "unknown", "reason": ""})
|
||||
items.append(item)
|
||||
setup = {"profile": profile, "wake_on_lan": wol, "firewall_allow": allow}
|
||||
return items, {"tasks": sorted(t for t in tasks if "StatusPanel" in t), "services": sorted(k for k, v in services.items()
|
||||
if v == "RUNNING" and not k.lower().startswith(("w", "s", "d", "e", "n", "p", "t", "u", "c")))[:60]}, setup
|
||||
# A task that starts a program is "Ready" once it did: it counts as running while that program runs.
|
||||
up = {"\\StatusPanel\\" + run["name"] for run, item in zip(host.get("runs", []), items)
|
||||
if run.get("kind") == "task" and run.get("exe") and item.get("light") == "ok"}
|
||||
ours = {t: ("Running" if t in up else row[2] if len(row) > 2 else "") for t, row in tasks.items() if "StatusPanel" in t}
|
||||
return items, {"tasks": sorted(ours), "services": sorted(k for k, v in services.items()
|
||||
if v == "RUNNING" and not k.lower().startswith(("w", "s", "d", "e", "n", "p", "t", "u", "c")))[:60],
|
||||
"scripts": parse_scripts(parsed.get("SP_SCRIPTS", []), tasks=ours)}, setup
|
||||
|
||||
|
||||
def probe_host(cfg, alias, host):
|
||||
@@ -245,7 +289,7 @@ def probe_host(cfg, alias, host):
|
||||
rec["error"] = err.strip() or "no answer"
|
||||
return rec
|
||||
rec["reachable"] = True
|
||||
parsed = sections(out, ("SP_TASKS", "SP_PROCS", "SP_SVC", "SP_PS", "SP_END"))
|
||||
parsed = sections(out, ("SP_TASKS", "SP_PROCS", "SP_SVC", "SP_PS", "SP_SCRIPTS", "SP_END"))
|
||||
rec["computer_name"] = values(out).get("NAME", "").strip()
|
||||
rec["items"], rec["found"], rec["setup"] = windows_items(host, parsed)
|
||||
rec["setup"]["key_ok"] = True
|
||||
@@ -257,7 +301,7 @@ def probe_host(cfg, alias, host):
|
||||
return rec
|
||||
rec["reachable"] = True
|
||||
v = values(out)
|
||||
parsed = sections(out, ("SP_AGENTS", "SP_PROCS", "SP_APPS", "SP_HELPER", "SP_SOLO", "SP_NOW", "SP_END"))
|
||||
parsed = sections(out, ("SP_AGENTS", "SP_PROCS", "SP_APPS", "SP_HELPER", "SP_SOLO", "SP_NOW", "SP_SCRIPTS", "SP_END"))
|
||||
rec["computer_name"], rec["os_version"], rec["python"] = v.get("NAME", ""), v.get("OSV", ""), v.get("PY", "")
|
||||
rec["items"], rec["found"] = mac_items(host, parsed, cfg)
|
||||
rec["setup"] = {"key_ok": (v.get("KEY") or "0").strip() not in ("", "0"), "python_ok": "3." in v.get("PY", "")}
|
||||
@@ -301,6 +345,7 @@ def probe_self(cfg, alias, host):
|
||||
try:
|
||||
agents_out = subprocess.run(["/bin/launchctl", "list"], capture_output=True, text=True, timeout=5).stdout
|
||||
procs_out = subprocess.run(["/bin/ps", "-axo", "comm="], capture_output=True, text=True, timeout=5).stdout
|
||||
args_out = subprocess.run(["/bin/ps", "-axo", "pid=,args="], capture_output=True, text=True, timeout=5).stdout
|
||||
except (OSError, subprocess.TimeoutExpired) as e:
|
||||
rec["error"] = str(e)
|
||||
return rec
|
||||
@@ -308,7 +353,8 @@ def probe_self(cfg, alias, host):
|
||||
helper = subprocess.run(["/bin/zsh", "-c", "ls -l %s 2>&1; sudo -n -l %s 2>&1 | tail -1" % (paths["helper"], paths["helper"])],
|
||||
capture_output=True, text=True, timeout=6).stdout
|
||||
parsed = {"SP_AGENTS": agents_out.splitlines(), "SP_PROCS": procs_out.splitlines(), "SP_APPS": [],
|
||||
"SP_HELPER": helper.splitlines(), "SP_SOLO": [], "SP_NOW": [str(int(time.time()))]}
|
||||
"SP_HELPER": helper.splitlines(), "SP_SOLO": [], "SP_NOW": [str(int(time.time()))],
|
||||
"SP_SCRIPTS": [l for l in args_out.splitlines() if re.search(SCRIPT_RE, l)]}
|
||||
rec["items"], rec["found"] = mac_items(host, parsed, cfg)
|
||||
for item, run in zip(rec["items"], host.get("runs", [])):
|
||||
if run.get("kind") == "folder":
|
||||
|
||||
Reference in New Issue
Block a user