From e7e94904682f237c3c8700ec0f3d60c539164508 Mon Sep 17 00:00:00 2001 From: KadirJeager Date: Sun, 25 Oct 2026 22:41:52 +0300 Subject: [PATCH] network(networkmanager): refresh wired profiles on settings changes NewConnection, ConnectionRemoved and Updated refreshed the VPN, Wi-Fi, hotspot and cellular lists but not the wired profile list. Adding or deleting an inactive wired profile does not change any device state, so the list stayed stale until the shell restarted and deleted profiles kept showing in the network settings and control center. listEthernetConnections now also resolves the settings handle through networkManagerSettings(); the old code shadowed the new handle and asserted on a nil interface when the cached handle was unset. Co-Authored-By: Claude Opus 5.5 --- .../backend_networkmanager_ethernet.go | 11 ++-- .../network/backend_networkmanager_signals.go | 3 ++ .../backend_networkmanager_signals_test.go | 54 +++++++++++++++++++ 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/core/internal/server/network/backend_networkmanager_ethernet.go b/core/internal/server/network/backend_networkmanager_ethernet.go index 85f1e6261b..b11272f882 100644 --- a/core/internal/server/network/backend_networkmanager_ethernet.go +++ b/core/internal/server/network/backend_networkmanager_ethernet.go @@ -258,16 +258,11 @@ func (b *NetworkManagerBackend) listEthernetConnections() ([]WiredConnection, er return nil, fmt.Errorf("no ethernet device available") } - s := b.settings - if s == nil { - s, err := gonetworkmanager.NewSettings() - if err != nil { - return nil, fmt.Errorf("failed to get settings: %w", err) - } - b.settings = s + settingsMgr, err := b.networkManagerSettings() + if err != nil { + return nil, err } - settingsMgr := s.(gonetworkmanager.Settings) connections, err := settingsMgr.ListConnections() if err != nil { return nil, fmt.Errorf("failed to get connections: %w", err) diff --git a/core/internal/server/network/backend_networkmanager_signals.go b/core/internal/server/network/backend_networkmanager_signals.go index 684a03c20c..272ed4b6e7 100644 --- a/core/internal/server/network/backend_networkmanager_signals.go +++ b/core/internal/server/network/backend_networkmanager_signals.go @@ -305,6 +305,9 @@ func (b *NetworkManagerBackend) handleDBusSignal(sig *dbus.Signal) { } b.updateHotspotState() b.listCellularConnections() + // Device state does not change when an inactive wired profile is + // added or deleted, so the profile list must be refreshed here too. + b.listEthernetConnections() if b.onStateChange != nil { b.onStateChange() } diff --git a/core/internal/server/network/backend_networkmanager_signals_test.go b/core/internal/server/network/backend_networkmanager_signals_test.go index 1123acb64f..0f6c811ac8 100644 --- a/core/internal/server/network/backend_networkmanager_signals_test.go +++ b/core/internal/server/network/backend_networkmanager_signals_test.go @@ -130,3 +130,57 @@ func TestNetworkManagerBackend_HandleWiFiChange_CoalescesAccessPointBurst(t *tes return stateChangeCalls.Load() == 1 }, 4*wifiNetworksUpdateDelay, 10*time.Millisecond) } + +func TestNetworkManagerBackend_HandleDBusSignal_ConnectionRemovedRefreshesWiredConnections(t *testing.T) { + mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) + mockNM.EXPECT().GetPropertyActiveConnections().Return([]gonetworkmanager.ActiveConnection{}, nil).Maybe() + + remaining := mock_gonetworkmanager.NewMockConnection(t) + remaining.EXPECT().GetPath().Return(dbus.ObjectPath("/org/freedesktop/NetworkManager/Settings/1")).Maybe() + remaining.EXPECT().GetSettings().Return(gonetworkmanager.ConnectionSettings{ + "connection": { + "id": "Wired connection 1", + "uuid": "uuid-1", + "type": "802-3-ethernet", + }, + }, nil).Maybe() + + mockSettings := mock_gonetworkmanager.NewMockSettings(t) + mockSettings.EXPECT().ListConnections().Return([]gonetworkmanager.Connection{remaining}, nil).Maybe() + + backend, err := NewNetworkManagerBackend(mockNM) + assert.NoError(t, err) + backend.settings = mockSettings + backend.ethernetDevice = mock_gonetworkmanager.NewMockDevice(t) + + backend.stateMutex.Lock() + backend.state.WiredConnections = []WiredConnection{ + {ID: "Wired connection 1", UUID: "uuid-1", Type: "802-3-ethernet"}, + {ID: "Wired connection 2", UUID: "uuid-2", Type: "802-3-ethernet"}, + } + backend.stateMutex.Unlock() + + backend.handleDBusSignal(&dbus.Signal{ + Name: "org.freedesktop.NetworkManager.Settings.ConnectionRemoved", + Body: []any{dbus.ObjectPath("/org/freedesktop/NetworkManager/Settings/2")}, + }) + + backend.stateMutex.RLock() + defer backend.stateMutex.RUnlock() + assert.Len(t, backend.state.WiredConnections, 1) + assert.Equal(t, "uuid-1", backend.state.WiredConnections[0].UUID) +} + +func TestNetworkManagerBackend_ListEthernetConnections_InvalidSettingsHandle(t *testing.T) { + mockNM := mock_gonetworkmanager.NewMockNetworkManager(t) + + backend, err := NewNetworkManagerBackend(mockNM) + assert.NoError(t, err) + backend.settings = "not a settings handle" + backend.ethernetDevice = mock_gonetworkmanager.NewMockDevice(t) + + assert.NotPanics(t, func() { + _, err = backend.listEthernetConnections() + }) + assert.Error(t, err) +}