"""Sync discovered mDNS hosts into the system hosts file. Writes a managed block so native tools (ping, curl, kubectl) resolve `.local` names without the `resolve` wrapper. Only IPv4, only the managed block is touched — the rest of the file is preserved. """ import os from pathlib import Path from . import browse as _browse from ._pick import pick_best MARK_BEGIN = "# >>> mdns-termux managed >>>" MARK_END = "# <<< mdns-termux managed <<<" def hosts_path() -> Path: prefix = os.environ.get("PREFIX", "") if prefix and "com.termux" in prefix: return Path(prefix) / "etc" / "hosts" return Path("/etc/hosts") def collect(duration: float = 3.0) -> dict[str, str]: """Return {hostname.local: best_ipv4} from all advertised services.""" out: dict[str, str] = {} for it in _browse.browse(None, duration): server = (it.get("server") or "").rstrip(".") v4 = [a for a in (it.get("addresses") or []) if ":" not in a] if not server or not v4: continue best = pick_best(v4, family="v4") if best and server not in out: out[server] = best return out def _render_block(mapping: dict[str, str]) -> str: lines = [MARK_BEGIN] for host in sorted(mapping): short = host[:-6] if host.endswith(".local") else host lines.append(f"{mapping[host]}\t{host} {short}") lines.append(MARK_END) return "\n".join(lines) def _strip_block(text: str) -> str: out, skip = [], False for line in text.splitlines(): if line.strip() == MARK_BEGIN: skip = True continue if line.strip() == MARK_END: skip = False continue if not skip: out.append(line) return "\n".join(out).rstrip("\n") def sync(duration: float = 3.0, dry_run: bool = False) -> int: mapping = collect(duration) if not mapping: print("no hosts discovered on the LAN") return 1 block = _render_block(mapping) if dry_run: print(block) return 0 path = hosts_path() original = path.read_text() if path.exists() else "127.0.0.1\tlocalhost\n" body = _strip_block(original) path.write_text(f"{body}\n{block}\n") print(f"wrote {len(mapping)} host(s) to {path}") for host, ip in sorted(mapping.items()): print(f" {ip}\t{host}") return 0