wpa_supplicant: Reuse existing networks and preserve saved ones on connect
Connect() called AddNetwork unconditionally, creating duplicate entries for the same SSID, and SelectNetwork's side-effect of disabling all other networks was being persisted by SaveConfig — making previously saved networks appear erased. Disconnect() also removed the current network from the config. Now reuse an existing network entry when the SSID matches, re-enable other networks after SelectNetwork, and keep entries on disconnect. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
08cbe4f9ff
commit
77370eff19
3 changed files with 102 additions and 23 deletions
|
|
@ -152,35 +152,69 @@ func (b *WPABackend) IsScanning() (bool, error) {
|
|||
|
||||
// Connect connects to a WiFi network
|
||||
func (b *WPABackend) Connect(ssid, password string) error {
|
||||
// Create network configuration
|
||||
config := make(map[string]interface{})
|
||||
config["ssid"] = ssid // Raw SSID string, no quotes
|
||||
|
||||
if password != "" {
|
||||
// For WPA/WPA2-PSK networks
|
||||
config["psk"] = password // Raw password string, no quotes
|
||||
} else {
|
||||
// For open networks
|
||||
config["key_mgmt"] = "NONE"
|
||||
}
|
||||
|
||||
// Add network
|
||||
networkPath, err := b.iface.AddNetwork(config)
|
||||
// Look up existing network with the same SSID to avoid creating duplicates
|
||||
existingNetworks, err := b.iface.GetNetworks()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add network: %v", err)
|
||||
return fmt.Errorf("failed to list configured networks: %v", err)
|
||||
}
|
||||
|
||||
// Store current network path for cleanup
|
||||
var networkPath dbus.ObjectPath
|
||||
createdNew := false
|
||||
|
||||
for _, p := range existingNetworks {
|
||||
net := NewNetwork(b.conn, p)
|
||||
netSSID, err := net.GetSSID()
|
||||
if err != nil || netSSID != ssid {
|
||||
continue
|
||||
}
|
||||
networkPath = p
|
||||
break
|
||||
}
|
||||
|
||||
if networkPath == "" {
|
||||
// Create network configuration
|
||||
config := make(map[string]interface{})
|
||||
config["ssid"] = ssid // Raw SSID string, no quotes
|
||||
|
||||
if password != "" {
|
||||
// For WPA/WPA2-PSK networks
|
||||
config["psk"] = password // Raw password string, no quotes
|
||||
} else {
|
||||
// For open networks
|
||||
config["key_mgmt"] = "NONE"
|
||||
}
|
||||
|
||||
networkPath, err = b.iface.AddNetwork(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add network: %v", err)
|
||||
}
|
||||
createdNew = true
|
||||
}
|
||||
|
||||
// Store current network path
|
||||
b.currentNetwork = networkPath
|
||||
|
||||
// Select (connect to) the network
|
||||
// Select (connect to) the network. Note: SelectNetwork disables every
|
||||
// other configured network as a side-effect.
|
||||
err = b.iface.SelectNetwork(networkPath)
|
||||
if err != nil {
|
||||
// Clean up network on failure
|
||||
b.iface.RemoveNetwork(networkPath)
|
||||
if createdNew {
|
||||
b.iface.RemoveNetwork(networkPath)
|
||||
}
|
||||
return fmt.Errorf("failed to select network: %v", err)
|
||||
}
|
||||
|
||||
// Re-enable previously configured networks so SelectNetwork's side-effect
|
||||
// doesn't mark them all as disabled in the persisted config.
|
||||
for _, p := range existingNetworks {
|
||||
if p == networkPath {
|
||||
continue
|
||||
}
|
||||
if err := b.iface.EnableNetwork(p); err != nil {
|
||||
fmt.Printf("Warning: failed to re-enable network %s: %v\n", p, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Save the configuration to persist it across reboots
|
||||
if err := b.iface.SaveConfig(); err != nil {
|
||||
// Log warning but don't fail - connection still works
|
||||
|
|
@ -197,11 +231,7 @@ func (b *WPABackend) Disconnect() error {
|
|||
return fmt.Errorf("failed to disconnect: %v", err)
|
||||
}
|
||||
|
||||
// Remove the network configuration if we have one
|
||||
if b.currentNetwork != "" && b.currentNetwork != "/" {
|
||||
b.iface.RemoveNetwork(b.currentNetwork)
|
||||
b.currentNetwork = ""
|
||||
}
|
||||
b.currentNetwork = ""
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,6 +107,16 @@ func (i *WPAInterface) SelectNetwork(networkPath dbus.ObjectPath) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// EnableNetwork marks a network configuration as enabled (eligible for auto-connect)
|
||||
func (i *WPAInterface) EnableNetwork(networkPath dbus.ObjectPath) error {
|
||||
netObj := i.conn.Object(Service, networkPath)
|
||||
err := netObj.SetProperty(NetworkInterface+".Enabled", dbus.MakeVariant(true))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to enable network: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveNetwork removes a network configuration
|
||||
func (i *WPAInterface) RemoveNetwork(networkPath dbus.ObjectPath) error {
|
||||
err := i.obj.Call(InterfaceInterface+".RemoveNetwork", 0, networkPath).Err
|
||||
|
|
@ -116,6 +126,21 @@ func (i *WPAInterface) RemoveNetwork(networkPath dbus.ObjectPath) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetNetworks returns the object paths of all configured networks
|
||||
func (i *WPAInterface) GetNetworks() ([]dbus.ObjectPath, error) {
|
||||
prop, err := i.obj.GetProperty(InterfaceInterface + ".Networks")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get Networks property: %v", err)
|
||||
}
|
||||
|
||||
networks, ok := prop.Value().([]dbus.ObjectPath)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Networks property is not an array of ObjectPath")
|
||||
}
|
||||
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
// Disconnect disconnects from the current network
|
||||
func (i *WPAInterface) Disconnect() error {
|
||||
err := i.obj.Call(InterfaceInterface+".Disconnect", 0).Err
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package wpasupplicant
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
|
|
@ -39,3 +41,25 @@ func (n *Network) GetProperties() (map[string]dbus.Variant, error) {
|
|||
|
||||
return props, nil
|
||||
}
|
||||
|
||||
// GetSSID returns the configured SSID, stripping the wrapping quotes
|
||||
// that wpa_supplicant stores around string-form SSIDs.
|
||||
func (n *Network) GetSSID() (string, error) {
|
||||
props, err := n.GetProperties()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ssidVariant, ok := props["ssid"]
|
||||
if !ok {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
ssid, ok := ssidVariant.Value().(string)
|
||||
if !ok {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// wpa_supplicant returns quoted SSIDs like "MyNetwork"
|
||||
return strings.Trim(ssid, `"`), nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue