mdns-termux/lib/mdns_tools/hosts.py

78 lines
2.3 KiB
Python
Raw Normal View History

feat: publisher-daemon (singleton + re-announce), sync-hosts, status/stop y tests Bloque de robustez operativa + integracion de sistema + calidad: Publisher como servicio (lib/mdns_tools/service.py): - `mdns publish --daemon`: daemon singleton con lock PID en ~/.cache/mdns/publisher.pid. Un segundo intento es rechazado -> evita el problema de "Termux SMB" duplicado (varios publishers compitiendo por el mismo nombre, que zeroconf renombra a "-2"). - Re-anuncio automatico al cambiar la IP local (WiFi<->datos, roaming): unregister + register con la nueva IP para que el registro no quede stale. - Apagado limpio: maneja SIGTERM ademas de SIGINT (loop en ticks de 1s) y envia goodbye packets para que los clientes borren la entrada al instante. - `mdns status` (running/host/ip/servicios/desde) y `mdns stop`. Integracion de sistema (lib/mdns_tools/hosts.py): - `mdns sync-hosts [--dry-run]`: escribe un bloque gestionado en $PREFIX/etc/hosts (o /etc/hosts) para que ping/curl/kubectl nativos resuelvan .local sin el wrapper `resolve`. Solo toca su propio bloque. Refactor publish.py: expone helpers reutilizables (local_ipv4, load_config, build_infos, register_all, unregister_all) que consume el daemon. Tests offline (tests/, pytest, 20 casos, sin red): - construccion/parseo de queries A, seleccion de IP ante multi-homing (rechazo de rangos virtuales), cache con TTL y familias, PTR reverse, render/strip del bloque de hosts. - pyproject.toml con metadata, deps, extras [test] y config de pytest. README: seccion de daemon, sync-hosts y tests; tabla RFC ampliada. bashrc.snippet: aliases mdns-up/status/down/hosts (consolidados). install.sh: fix de backticks; symlinks ya cubren mdns + lib via resolve(). Nota: publish.py (109) y service.py (110) exceden levemente las 100 lineas; se dejan como modulos cohesivos de responsabilidad unica, igual que el dispatcher CLI, para no fragmentar. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 17:59:10 +00:00
"""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