On all four machines and owned by none. The module declares the package and the daemon. It leaves nsswitch.conf and nss-mdns as found — the hosts: line is one list every name source shares, and the host writes blocks, not line members — and opens nothing: the mesh's filter drops inbound UDP 5353 on every machine and `listens` has no local-link scope. avahi_status, _browse, _resolve and _services report both (to-be 42 Phase 1).
86 lines
3.3 KiB
Go
86 lines
3.3 KiB
Go
// avahi's tools bundle (novox/hq to-be 42 Phase 1, research 026/05): a process the node's runtime
|
|
// launches and speaks MCP over stdio to, through the Go SDK (ADR 0188, ADR 0193). It reads the
|
|
// daemon, the name service's wiring and the packet filter's view of mDNS, browses the local network
|
|
// for services, resolves a name, and lists what the machine publishes. It changes nothing.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
)
|
|
|
|
// binaryName is what the build names this bundle's executable: the manifest's `binary`.
|
|
const binaryName = "avahi-tools"
|
|
|
|
func bg() context.Context { return context.Background() }
|
|
|
|
func main() {
|
|
// An empty name serves as the module the runtime names (MESH_SERVED_MODULE): avahi.
|
|
if err := stdio.Serve("", tools(ThisMachine())); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func tools(m *Machine) []stdio.Tool {
|
|
return []stdio.Tool{
|
|
{
|
|
Name: "avahi_status",
|
|
Description: "The daemon's state and version, its configuration as set, the name service switch's hosts line and whether mdns is on it, " +
|
|
"whether nss-mdns is installed, whether the packet filter accepts inbound mDNS (UDP 5353), whether systemd-resolved runs beside it, " +
|
|
"and notes naming what keeps discovery from working.",
|
|
Input: schema(map[string]any{}),
|
|
Run: func(map[string]any) (any, error) { return m.GetStatus() },
|
|
},
|
|
{
|
|
Name: "avahi_browse",
|
|
Description: "Listen on the local network for a few seconds (avahi-browse -prt) and answer every service announced, with interface, " +
|
|
"protocol, name, type, host, address, port and TXT where it resolved; narrowed to one service type when given. Hearing nothing says why it may be.",
|
|
Input: schema(map[string]any{
|
|
"seconds": map[string]any{"type": "integer", "description": "how long to listen (default 5, at most 15)"},
|
|
"type": map[string]any{"type": "string", "description": "one service type, e.g. _ssh._tcp (optional)"},
|
|
}),
|
|
Run: func(args map[string]any) (any, error) {
|
|
n, err := whole(args, "seconds", 5, 1, 15)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
kind, err := text(args, "type", false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return m.Browse(n, kind)
|
|
},
|
|
},
|
|
{
|
|
Name: "avahi_resolve",
|
|
Description: "Resolve a .local name to its addresses through avahi, and through the name service (getent) beside it, or an address to its name. " +
|
|
"An answer from avahi that the name service lacks shows nsswitch unwired; no answer says why it may be.",
|
|
Input: schema(map[string]any{
|
|
"name": map[string]any{"type": "string", "description": "a host name; .local is added when missing"},
|
|
"address": map[string]any{"type": "string", "description": "an address to name instead"},
|
|
}),
|
|
Run: func(args map[string]any) (any, error) {
|
|
name, err := text(args, "name", false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
address, err := text(args, "address", false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return m.Resolve(name, address)
|
|
},
|
|
},
|
|
{
|
|
Name: "avahi_services",
|
|
Description: "What this machine publishes from /etc/avahi/services: each file with the service's name, types and ports.",
|
|
Input: schema(map[string]any{}),
|
|
Run: func(map[string]any) (any, error) { return m.Services() },
|
|
},
|
|
}
|
|
}
|