app: Start wpa_supplicant only when Ethernet has no DHCP lease

Probe the configured Ethernet interface (default eth0, overridable via
-ethernet-interface) at startup. If no DHCP-assigned IPv4 is present,
start the wpa_supplicant service so the WiFi backend has something to
talk to; otherwise leave it alone and rely on the wired uplink.
This commit is contained in:
nemunaire 2026-05-01 22:35:07 +08:00
commit d57c08a6c4
4 changed files with 82 additions and 20 deletions

View file

@ -57,6 +57,11 @@ func (a *App) Initialize(cfg *config.Config) error {
// Store config reference
a.Config = cfg
// If Ethernet uplink is not already providing connectivity (no DHCP lease
// on the configured interface), bring up wpa_supplicant so the WiFi
// backend has something to talk to.
ensureUplink(cfg.EthernetInterface)
// Initialize WiFi backend
if err := wifi.Initialize(cfg.WifiInterface, cfg.WifiBackend); err != nil {
return err

54
internal/app/network.go Normal file
View file

@ -0,0 +1,54 @@
package app
import (
"fmt"
"log"
"os/exec"
"strings"
)
// hasDHCPAddress reports whether iface holds an IPv4 address that ip(8) marks
// as "dynamic" — the flag set on addresses with a finite valid_lft, which is
// how DHCP-assigned leases appear (static addresses are "permanent").
func hasDHCPAddress(iface string) (bool, error) {
out, err := exec.Command("ip", "-4", "-o", "addr", "show", "dev", iface).Output()
if err != nil {
return false, fmt.Errorf("ip addr show %s: %w", iface, err)
}
for _, line := range strings.Split(string(out), "\n") {
fields := strings.Fields(line)
hasInet := false
hasDynamic := false
for _, f := range fields {
switch f {
case "inet":
hasInet = true
case "dynamic":
hasDynamic = true
}
}
if hasInet && hasDynamic {
return true, nil
}
}
return false, nil
}
// ensureUplink boots the WiFi uplink only if Ethernet is not already providing
// connectivity: when iface has no DHCP-assigned IPv4, start wpa_supplicant so
// the WiFi backend can take over.
func ensureUplink(iface string) {
has, err := hasDHCPAddress(iface)
if err != nil {
log.Printf("Could not probe %s for DHCP address (%v); starting wpa_supplicant as fallback", iface, err)
} else if has {
log.Printf("DHCP address present on %s, leaving wpa_supplicant alone", iface)
return
} else {
log.Printf("No DHCP address on %s, starting wpa_supplicant", iface)
}
if out, err := exec.Command("systemctl", "start", "wpa_supplicant").CombinedOutput(); err != nil {
log.Printf("Failed to start wpa_supplicant: %v: %s", err, strings.TrimSpace(string(out)))
}
}