feat: reload also scrubs expired leases from lease file before reloading dnsmasq

This commit is contained in:
Claus Lohmar 2026-07-25 18:22:34 +00:00
parent efeb993d20
commit da01893aa3

View file

@ -197,6 +197,11 @@ func unpinLease(mac string) error {
// reloadDnsmasq runs the reload command (e.g. sudo systemctl reload dnsmasq). // reloadDnsmasq runs the reload command (e.g. sudo systemctl reload dnsmasq).
func reloadDnsmasq() error { func reloadDnsmasq() error {
// Scrub expired leases before reloading so the UI stays clean.
if err := cleanLeaseFile(); err != nil {
log.Printf("WARNING: failed to clean lease file: %v", err)
// Continue — reload is still useful even if cleaning fails.
}
cmd := exec.Command(reloadCmd, reloadArgs...) cmd := exec.Command(reloadCmd, reloadArgs...)
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
if err != nil { if err != nil {
@ -205,6 +210,52 @@ func reloadDnsmasq() error {
return nil return nil
} }
// cleanLeaseFile reads the lease file, removes expired entries, writes back atomically.
func cleanLeaseFile() error {
leases, err := parseLeases()
if err != nil {
return err
}
now := time.Now()
var kept []string
for _, l := range leases {
if l.Expiry.After(now) {
kept = append(kept, fmt.Sprintf("%d %s %s %s %s",
l.Expiry.Unix(), l.MAC, l.IP, l.Hostname, l.ClientID))
}
}
// If nothing changed, skip the write.
if len(kept) == len(leases) {
return nil
}
tmpFile := leasesFile + ".tmp"
f, err := os.Create(tmpFile)
if err != nil {
return fmt.Errorf("create temp lease file: %w", err)
}
writeErr := func() error {
defer f.Close()
for _, line := range kept {
if _, err := fmt.Fprintln(f, line); err != nil {
return err
}
}
return nil
}()
if writeErr != nil {
os.Remove(tmpFile)
return writeErr
}
if err := os.Rename(tmpFile, leasesFile); err != nil {
os.Remove(tmpFile)
return fmt.Errorf("rename lease file: %w", err)
}
return nil
}
// getAllLeases returns merged active (non-expired) and pinned lease info. // getAllLeases returns merged active (non-expired) and pinned lease info.
func getAllLeases() ([]LeaseEntry, error) { func getAllLeases() ([]LeaseEntry, error) {
activeLeases, err := parseLeases() activeLeases, err := parseLeases()