When the configured Ethernet interface holds a DHCP-assigned IPv4 at
startup, the app now skips wifi.Initialize / StartEventMonitoring and
guards every wifi.* wrapper against a nil backend. This prevents D-Bus
calls to fi.w1.wpa_supplicant1 from re-activating the daemon via
dbus-activation, honoring the "do nothing" intent of the Ethernet path.
The probed state is exposed in SystemStatus and rendered in the header
as a third pill ("Ethernet · <IP>"); a new "disabled" connectionState
covers the WiFi pill in this mode.
68 lines
2.3 KiB
Go
68 lines
2.3 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/nemunaire/repeater/internal/models"
|
|
)
|
|
|
|
// probeEthernet reports the wired uplink state of iface by parsing
|
|
// `ip -4 -o addr show dev <iface>`. An IPv4 line carrying the "dynamic" flag
|
|
// (set by ip(8) on addresses with a finite valid_lft) is treated as a
|
|
// DHCP-assigned lease — that's the signal we use to decide whether the box
|
|
// already has connectivity through Ethernet.
|
|
func probeEthernet(iface string) (*models.EthernetStatus, error) {
|
|
out, err := exec.Command("ip", "-4", "-o", "addr", "show", "dev", iface).Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ip addr show %s: %w", iface, err)
|
|
}
|
|
for _, line := range strings.Split(string(out), "\n") {
|
|
fields := strings.Fields(line)
|
|
var addr string
|
|
hasInet := false
|
|
hasDynamic := false
|
|
for i, f := range fields {
|
|
switch f {
|
|
case "inet":
|
|
hasInet = true
|
|
if i+1 < len(fields) {
|
|
addr = fields[i+1]
|
|
}
|
|
case "dynamic":
|
|
hasDynamic = true
|
|
}
|
|
}
|
|
if hasInet && hasDynamic {
|
|
if slash := strings.IndexByte(addr, '/'); slash >= 0 {
|
|
addr = addr[:slash]
|
|
}
|
|
return &models.EthernetStatus{Active: true, Interface: iface, IPv4: addr}, nil
|
|
}
|
|
}
|
|
return &models.EthernetStatus{Active: false, Interface: iface}, nil
|
|
}
|
|
|
|
// ensureUplink returns the current Ethernet status, and starts wpa_supplicant
|
|
// when no DHCP-assigned address is present. When Ethernet is already
|
|
// providing connectivity the WiFi backend is left alone — and crucially the
|
|
// caller must avoid any D-Bus call to fi.w1.wpa_supplicant1, which would
|
|
// otherwise re-activate the daemon via dbus-activation.
|
|
func ensureUplink(iface string) *models.EthernetStatus {
|
|
eth, err := probeEthernet(iface)
|
|
if err != nil {
|
|
log.Printf("Could not probe %s (%v); starting wpa_supplicant as fallback", iface, err)
|
|
eth = &models.EthernetStatus{Active: false, Interface: iface}
|
|
}
|
|
if eth.Active {
|
|
log.Printf("DHCP address %s on %s, leaving wpa_supplicant alone", eth.IPv4, iface)
|
|
return eth
|
|
}
|
|
log.Printf("No DHCP address on %s, starting wpa_supplicant", iface)
|
|
if out, err := exec.Command("service", "wpa_supplicant", "start").CombinedOutput(); err != nil {
|
|
log.Printf("Failed to start wpa_supplicant: %v: %s", err, strings.TrimSpace(string(out)))
|
|
}
|
|
return eth
|
|
}
|