package main // NetworkManager's word on each link's names: `nmcli device show`, read in its terse form. A link // NetworkManager does not manage — a tunnel a VPN client raised with `ip`, the mesh's own — is not in it, // and the links verb says the link without what the manager does not know. import ( "context" "strings" ) func managerDNS(ctx context.Context) (map[string]LinkDNS, error) { out, err := execRunner(ctx, "nmcli", "-t", "-f", "GENERAL.DEVICE,GENERAL.STATE,IP4.DNS,IP6.DNS,IP4.DOMAIN,IP6.DOMAIN", "device", "show") if err != nil { return nil, err } return parseNmcli(out), nil } // parseNmcli reads `nmcli -t device show`: blocks of KEY:value lines, one block per device. func parseNmcli(out string) map[string]LinkDNS { links := map[string]LinkDNS{} device := "" var cur LinkDNS flush := func() { if device != "" && (len(cur.Servers) > 0 || len(cur.Domains) > 0) { cur.Manager = "NetworkManager" links[device] = cur } device, cur = "", LinkDNS{} } for _, line := range strings.Split(out, "\n") { key, value, ok := strings.Cut(strings.TrimSpace(line), ":") if !ok { continue } switch { case key == "GENERAL.DEVICE": flush() device = value case key == "GENERAL.STATE": cur.State = value case strings.HasPrefix(key, "IP4.DNS"), strings.HasPrefix(key, "IP6.DNS"): cur.Servers = append(cur.Servers, value) case strings.HasPrefix(key, "IP4.DOMAIN"), strings.HasPrefix(key, "IP6.DOMAIN"): cur.Domains = append(cur.Domains, value) } } flush() return links }