diff options
-rw-r--r-- | src/nm-policy.c | 73 |
1 files changed, 71 insertions, 2 deletions
diff --git a/src/nm-policy.c b/src/nm-policy.c index 8d392b2235..846c90de0d 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -68,6 +68,7 @@ typedef struct { GSList *pending_activation_checks; GHashTable *devices; + GHashTable *pending_active_connections; GSList *pending_secondaries; @@ -141,6 +142,7 @@ _PRIV_TO_SELF (NMPolicyPrivate *priv) /*****************************************************************************/ static void schedule_activate_all (NMPolicy *self); +static void schedule_activate_check (NMPolicy *self, NMDevice *device); /*****************************************************************************/ @@ -1165,6 +1167,44 @@ activate_data_free (ActivateData *data) } static void +pending_ac_gone (gpointer data, GObject *where_the_object_was) +{ + NMPolicy *self = NM_POLICY (data); + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + + /* Active connections should reach the DEACTIVATED state + * before disappearing. */ + nm_assert_not_reached(); + + g_hash_table_remove (priv->pending_active_connections, where_the_object_was); + g_object_unref (self); +} + +static void +pending_ac_state_changed (NMActiveConnection *ac, guint state, guint reason, NMPolicy *self) +{ + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + NMSettingsConnection *con; + + if (state >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) { + /* The AC is being deactivated before the device had a chance + * to move to PREPARE. Schedule a new auto-activation on the + * device, but block the current connection to avoid an activation + * loop. + */ + con = nm_active_connection_get_settings_connection (ac); + nm_settings_connection_set_autoconnect_blocked_reason (con, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED); + schedule_activate_check (self, nm_active_connection_get_device (ac)); + + /* Cleanup */ + g_signal_handlers_disconnect_by_func (ac, pending_ac_state_changed, self); + nm_assert (g_hash_table_remove (priv->pending_active_connections, ac)); + g_object_weak_unref (G_OBJECT (ac), pending_ac_gone, self); + g_object_unref (self); + } +} + +static void auto_activate_device (NMPolicy *self, NMDevice *device) { @@ -1206,24 +1246,41 @@ auto_activate_device (NMPolicy *self, if (best_connection) { GError *error = NULL; NMAuthSubject *subject; + NMActiveConnection *ac; _LOGI (LOGD_DEVICE, "auto-activating connection '%s'", nm_settings_connection_get_id (best_connection)); subject = nm_auth_subject_new_internal (); - if (!nm_manager_activate_connection (priv->manager, + ac = nm_manager_activate_connection (priv->manager, best_connection, NULL, specific_object, device, subject, NM_ACTIVATION_TYPE_MANAGED, - &error)) { + &error); + if (!ac) { _LOGI (LOGD_DEVICE, "connection '%s' auto-activation failed: (%d) %s", nm_settings_connection_get_id (best_connection), error->code, error->message); g_error_free (error); + nm_settings_connection_set_autoconnect_blocked_reason (best_connection, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED); + schedule_activate_check (self, device); + return; } + + /* Subscribe to AC state-changed signal to detect when the + * activation fails in early stages without changing device + * state. + */ + if (g_hash_table_add (priv->pending_active_connections, ac)) { + g_signal_connect (ac, NM_ACTIVE_CONNECTION_STATE_CHANGED, + G_CALLBACK (pending_ac_state_changed), g_object_ref (self)); + g_object_weak_ref (G_OBJECT (ac), (GWeakNotify) pending_ac_gone, self); + } + g_object_unref (subject); } } @@ -1660,6 +1717,7 @@ device_state_changed (NMDevice *device, { NMPolicyPrivate *priv = user_data; NMPolicy *self = _PRIV_TO_SELF (priv); + NMActiveConnection *ac; NMSettingsConnection *connection = nm_device_get_settings_connection (device); @@ -1772,6 +1830,15 @@ device_state_changed (NMDevice *device, /* Reset auto-connect retries of all slaves and schedule them for * activation. */ activate_slave_connections (self, device); + + /* Now that the device state is progressing, we don't care + * anymore for the AC state. */ + ac = (NMActiveConnection *) nm_device_get_act_request (device); + if (ac && g_hash_table_remove (priv->pending_active_connections, ac)) { + g_signal_handlers_disconnect_by_func (ac, pending_ac_state_changed, self); + g_object_weak_unref (G_OBJECT (ac), pending_ac_gone, self); + g_object_unref (self); + } break; case NM_DEVICE_STATE_IP_CONFIG: /* We must have secrets if we got here. */ @@ -2411,6 +2478,7 @@ nm_policy_init (NMPolicy *self) priv->hostname_mode = NM_POLICY_HOSTNAME_MODE_FULL; priv->devices = g_hash_table_new (NULL, NULL); + priv->pending_active_connections = g_hash_table_new (NULL, NULL); priv->ip6_prefix_delegations = g_array_new (FALSE, FALSE, sizeof (IP6PrefixDelegation)); g_array_set_clear_func (priv->ip6_prefix_delegations, clear_ip6_prefix_delegation); } @@ -2494,6 +2562,7 @@ dispose (GObject *object) nm_clear_g_object (&priv->default_device6); nm_clear_g_object (&priv->activating_device4); nm_clear_g_object (&priv->activating_device6); + g_clear_pointer (&priv->pending_active_connections, g_hash_table_unref); while (priv->pending_activation_checks) activate_data_free (priv->pending_activation_checks->data); |