Add ipadm: Go-Tool zur Verwaltung statischer DHCP/DNS-Hosts
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// renderDnsmasqConfig builds the content of the generated dnsmasq include
|
||||
// file from the current host list. Every host gets a host-record (forward +
|
||||
// reverse DNS). Hosts with a MAC additionally get a dhcp-host static lease.
|
||||
func renderDnsmasqConfig(hosts []Host) string {
|
||||
sorted := make([]Host, len(hosts))
|
||||
copy(sorted, hosts)
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
return strings.ToLower(sorted[i].Name) < strings.ToLower(sorted[j].Name)
|
||||
})
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("# Generated by ipadm -u — DO NOT EDIT MANUALLY.\n")
|
||||
b.WriteString("# Edit hosts with `ipadm` and re-run `ipadm -u` to regenerate this file.\n\n")
|
||||
for _, h := range sorted {
|
||||
if h.Comment != "" {
|
||||
fmt.Fprintf(&b, "# %s: %s\n", h.Name, h.Comment)
|
||||
}
|
||||
fmt.Fprintf(&b, "host-record=%s,%s\n", h.Name, h.IP)
|
||||
if h.MAC != "" {
|
||||
fmt.Fprintf(&b, "dhcp-host=%s,%s,%s,infinite\n", h.MAC, h.IP, h.Name)
|
||||
}
|
||||
b.WriteString("\n")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// applyDnsmasqConfig writes the generated config to path, validates the full
|
||||
// dnsmasq configuration, and on success reloads the dnsmasq service. On
|
||||
// validation failure the previous file content is restored and the service
|
||||
// is left untouched.
|
||||
func applyDnsmasqConfig(path string, content string) error {
|
||||
var backup []byte
|
||||
hadFile := false
|
||||
if b, err := os.ReadFile(path); err == nil {
|
||||
backup = b
|
||||
hadFile = true
|
||||
} else if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("kann bestehende %s nicht lesen: %w", path, err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
||||
return fmt.Errorf("kann %s nicht schreiben: %w", path, err)
|
||||
}
|
||||
|
||||
if err := runDnsmasqTest(); err != nil {
|
||||
// roll back so a bad generated file never lingers
|
||||
if hadFile {
|
||||
os.WriteFile(path, backup, 0644)
|
||||
} else {
|
||||
os.Remove(path)
|
||||
}
|
||||
return fmt.Errorf("dnsmasq-Konfiguration ungültig, Änderung zurückgerollt: %w", err)
|
||||
}
|
||||
|
||||
if err := reloadDnsmasq(); err != nil {
|
||||
return fmt.Errorf("%s geschrieben, aber Reload fehlgeschlagen: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runDnsmasqTest() error {
|
||||
out, err := exec.Command("dnsmasq", "--test").CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s", strings.TrimSpace(string(out)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func reloadDnsmasq() error {
|
||||
out, err := exec.Command("systemctl", "reload", "dnsmasq").CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s", strings.TrimSpace(string(out)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user