Every method call and property access to fi.w1.wpa_supplicant1 used flag 0, leaving D-Bus service activation enabled. Any stray request — a startup race before `service wpa_supplicant start` takes effect, or a leaked call while the Ethernet uplink is the chosen path — would make the bus daemon spawn `wpa_supplicant -u` from its .service file, bypassing repeater's own launch of the daemon. Route every interaction through callNoAutoStart/getPropNoAutoStart/ setPropNoAutoStart, which set dbus.FlagNoAutoStart. A leaked call now fails cleanly with ServiceUnknown instead of resurrecting the daemon, leaving repeater as the sole launcher. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
60 lines
2.4 KiB
Go
60 lines
2.4 KiB
Go
package wpasupplicant
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/godbus/dbus/v5"
|
|
)
|
|
|
|
// D-Bus interactions with the fi.w1.wpa_supplicant1 name must never trigger
|
|
// service activation. repeater is the sole launcher of the daemon (via
|
|
// `service wpa_supplicant start` in the app layer); if a call reached the bus
|
|
// name while the daemon was down — a startup race, or a stray call in
|
|
// Ethernet-uplink mode — the bus would auto-activate it from its .service
|
|
// file, spawning `wpa_supplicant -u` and bypassing repeater's own launch.
|
|
//
|
|
// godbus only suppresses activation when FlagNoAutoStart is set on the message
|
|
// (msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)); the default
|
|
// flag value of 0 leaves activation enabled. The helpers below set the flag on
|
|
// every method call and property access. A leaked call then fails cleanly with
|
|
// a ServiceUnknown error instead of resurrecting the daemon.
|
|
|
|
// callNoAutoStart performs a method call with D-Bus auto-activation disabled.
|
|
func callNoAutoStart(obj dbus.BusObject, method string, args ...interface{}) *dbus.Call {
|
|
return obj.Call(method, dbus.FlagNoAutoStart, args...)
|
|
}
|
|
|
|
// getPropNoAutoStart reads a property (interface.member notation) with
|
|
// auto-activation disabled. It mirrors dbus.Object.GetProperty, which
|
|
// otherwise issues Properties.Get with the activation-enabling flag 0.
|
|
func getPropNoAutoStart(obj dbus.BusObject, p string) (dbus.Variant, error) {
|
|
var result dbus.Variant
|
|
iface, prop, err := splitProperty(p)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
err = obj.Call("org.freedesktop.DBus.Properties.Get", dbus.FlagNoAutoStart, iface, prop).Store(&result)
|
|
return result, err
|
|
}
|
|
|
|
// setPropNoAutoStart writes a property with auto-activation disabled. The value
|
|
// is passed through as-is, so callers wrap it in a dbus.Variant just as they
|
|
// would for dbus.Object.SetProperty.
|
|
func setPropNoAutoStart(obj dbus.BusObject, p string, v interface{}) error {
|
|
iface, prop, err := splitProperty(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return obj.Call("org.freedesktop.DBus.Properties.Set", dbus.FlagNoAutoStart, iface, prop, v).Err
|
|
}
|
|
|
|
// splitProperty splits an "interface.member" property name into its interface
|
|
// and member parts, matching godbus's own parsing.
|
|
func splitProperty(p string) (iface, prop string, err error) {
|
|
idx := strings.LastIndex(p, ".")
|
|
if idx == -1 || idx+1 == len(p) {
|
|
return "", "", fmt.Errorf("dbus: invalid property %s", p)
|
|
}
|
|
return p[:idx], p[idx+1:], nil
|
|
}
|