diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 0c1720e033..6b404c4fe5 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -16670,6 +16670,14 @@ nm_device_get_state(NMDevice *self) return NM_DEVICE_GET_PRIVATE(self)->state; } +NMDeviceStateReason +nm_device_get_state_reason(NMDevice *self) +{ + g_return_val_if_fail(NM_IS_DEVICE(self), NM_DEVICE_STATE_REASON_NONE); + + return NM_DEVICE_GET_PRIVATE(self)->state_reason; +} + /*****************************************************************************/ /** diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index 7353a3f327..198ea14318 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -561,7 +561,8 @@ int nm_device_spec_match_list_full(NMDevice *self, const GSList *specs, int gboolean nm_device_is_activating(NMDevice *dev); gboolean nm_device_autoconnect_allowed(NMDevice *self); -NMDeviceState nm_device_get_state(NMDevice *device); +NMDeviceState nm_device_get_state(NMDevice *device); +NMDeviceStateReason nm_device_get_state_reason(NMDevice *device); gboolean nm_device_get_enabled(NMDevice *device); diff --git a/src/core/nm-active-connection.c b/src/core/nm-active-connection.c index 5feab966de..1badef8fb2 100644 --- a/src/core/nm-active-connection.c +++ b/src/core/nm-active-connection.c @@ -50,6 +50,7 @@ typedef struct _NMActiveConnectionPrivate { NMAuthSubject *subject; NMActiveConnection *master; + NMDevice *controller_dev; NMActiveConnection *parent; @@ -825,6 +826,31 @@ master_state_cb(NMActiveConnection *master, GParamSpec *pspec, gpointer user_dat } } +static void +controller_dev_state_cb(NMDevice *controller_dev, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMActiveConnection *self = NM_ACTIVE_CONNECTION(user_data); + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(self); + NMActRequest *controller_act_request; + NMActiveConnection *controller_ac; + + if (new_state >= NM_DEVICE_STATE_PREPARE && new_state <= NM_DEVICE_STATE_ACTIVATED) { + controller_act_request = nm_device_get_act_request(controller_dev); + if (controller_act_request) { + controller_ac = NM_ACTIVE_CONNECTION(controller_act_request); + g_signal_handlers_disconnect_by_func(controller_dev, + G_CALLBACK(controller_dev_state_cb), + self); + g_clear_object(&priv->controller_dev); + nm_active_connection_set_master(self, controller_ac); + } + } +} + /** * nm_active_connection_set_master: * @self: the #NMActiveConnection @@ -866,6 +892,36 @@ nm_active_connection_set_master(NMActiveConnection *self, NMActiveConnection *ma check_master_ready(self); } +void +nm_active_connection_set_controller_dev(NMActiveConnection *self, NMDevice *controller_dev) +{ + NMActiveConnectionPrivate *priv; + + g_return_if_fail(NM_IS_ACTIVE_CONNECTION(self)); + g_return_if_fail(NM_IS_DEVICE(controller_dev)); + + priv = NM_ACTIVE_CONNECTION_GET_PRIVATE(self); + + /* Controller device is write-once, and must be set before exporting the object */ + g_return_if_fail(priv->controller_dev == NULL); + g_return_if_fail(!nm_dbus_object_is_exported(NM_DBUS_OBJECT(self))); + if (priv->device) { + g_return_if_fail(priv->device != controller_dev); + } + + _LOGD("set controller device %p, %s(%s), state %s", + controller_dev, + nm_device_get_iface(controller_dev), + nm_device_get_type_desc(controller_dev), + nm_device_state_to_string(nm_device_get_state(controller_dev))); + + priv->controller_dev = g_object_ref(controller_dev); + g_signal_connect(priv->controller_dev, + NM_DEVICE_STATE_CHANGED, + G_CALLBACK(controller_dev_state_cb), + self); +} + NMActivationType nm_active_connection_get_activation_type(NMActiveConnection *self) { @@ -1532,7 +1588,14 @@ dispose(GObject *object) if (priv->master) { g_signal_handlers_disconnect_by_func(priv->master, G_CALLBACK(master_state_cb), self); } + + if (priv->controller_dev) { + g_signal_handlers_disconnect_by_func(priv->controller_dev, + G_CALLBACK(controller_dev_state_cb), + self); + } g_clear_object(&priv->master); + g_clear_object(&priv->controller_dev); if (priv->parent) unwatch_parent(self, TRUE); diff --git a/src/core/nm-active-connection.h b/src/core/nm-active-connection.h index 8032294f3c..7db90e542c 100644 --- a/src/core/nm-active-connection.h +++ b/src/core/nm-active-connection.h @@ -175,6 +175,7 @@ NMActiveConnection *nm_active_connection_get_master(NMActiveConnection *self); gboolean nm_active_connection_get_master_ready(NMActiveConnection *self); void nm_active_connection_set_master(NMActiveConnection *self, NMActiveConnection *master); +void nm_active_connection_set_controller_dev(NMActiveConnection *self, NMDevice *controller_dev); void nm_active_connection_set_parent(NMActiveConnection *self, NMActiveConnection *parent); diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index c69d879d26..c990c32687 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -5955,7 +5955,20 @@ _internal_activate_device(NMManager *self, NMActiveConnection *active, GError ** NM_DEVICE_STATE_REASON_USER_REQUESTED); } - nm_active_connection_set_master(active, master_ac); + /* If controller NMActiveConnection is deactivating, we should wait on + * controller's NMDevice to have new NMActiveConnection after + * controller device state change to between NM_DEVICE_STATE_PREPARE and + * NM_DEVICE_STATE_ACTIVATED. + */ + if ((nm_active_connection_get_state(master_ac) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) + && master_device + && (nm_device_get_state_reason(master_device) + == NM_DEVICE_STATE_REASON_NEW_ACTIVATION)) { + nm_active_connection_set_controller_dev(active, master_device); + } else { + nm_active_connection_set_master(active, master_ac); + } + _LOGD(LOGD_CORE, "Activation of '%s' depends on active connection %p %s", nm_settings_connection_get_id(sett_conn),