must_queue = _carrier_wait_check_act_request_must_queue (self, req);
- if (!priv->act_request && !must_queue) {
+ if (!priv->act_request && !must_queue && nm_device_is_real (self)) {
/* Just activate immediately */
if (!_device_activate (self, req))
g_assert_not_reached ();
}
/**
- * nm_device_get_available_connections:
+ * nm_device_get_best_connection:
* @self: the #NMDevice
* @specific_object: a specific object path if any
+ * @error: reason why no connection was returned
*
- * Returns a list of connections available to activate on the device, taking
- * into account any device-specific details given by @specific_object (like
- * WiFi access point path).
+ * Returns a connection that's most suitable for user-initiated activation
+ * of a device, optionally with a given specific object.
*
- * Returns: caller-owned #GPtrArray of #NMConnections
+ * Returns: the #NMSettingsConnection or %NULL (setting an @error)
*/
-GPtrArray *
-nm_device_get_available_connections (NMDevice *self, const char *specific_object)
+NMSettingsConnection *
+nm_device_get_best_connection (NMDevice *self,
+ const char *specific_object,
+ GError **error)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ NMSettingsConnection *connection = NULL;
+ NMSettingsConnection *candidate;
+ guint64 best_timestamp = 0;
GHashTableIter iter;
- guint num_available;
- NMConnection *connection = NULL;
- GPtrArray *array = NULL;
- num_available = g_hash_table_size (priv->available_connections);
- if (num_available > 0) {
- array = g_ptr_array_sized_new (num_available);
- g_hash_table_iter_init (&iter, priv->available_connections);
- while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) {
- /* If a specific object is given, only include connections that are
- * compatible with it.
- */
- if ( !specific_object /* << Optimization: we know that the connection is available without @specific_object. */
- || nm_device_check_connection_available (self, connection, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, specific_object))
- g_ptr_array_add (array, connection);
+ g_hash_table_iter_init (&iter, priv->available_connections);
+ while (g_hash_table_iter_next (&iter, (gpointer) &candidate, NULL)) {
+ guint64 candidate_timestamp = 0;
+
+ /* If a specific object is given, only include connections that are
+ * compatible with it.
+ */
+ if ( specific_object /* << Optimization: we know that the connection is available without @specific_object. */
+ && !nm_device_check_connection_available (self,
+ NM_CONNECTION (candidate),
+ _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST,
+ specific_object))
+ continue;
+
+ nm_settings_connection_get_timestamp (candidate, &candidate_timestamp);
+ if (!connection || (candidate_timestamp > best_timestamp)) {
+ connection = candidate;
+ best_timestamp = candidate_timestamp;
}
}
- return array;
+
+ if (!connection) {
+ g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+ "The device '%s' has no connections available for activation.",
+ nm_device_get_iface (self));
+ }
+
+ return connection;
}
static void
if (state <= NM_DEVICE_STATE_UNAVAILABLE) {
if (available_connections_del_all (self))
available_connections_notify (self);
- _clear_queued_act_request (priv);
+ if (old_state > NM_DEVICE_STATE_UNAVAILABLE)
+ _clear_queued_act_request (priv);
}
/* Update the available connections list when a device first becomes available */
NMActiveConnection *master;
gboolean master_ready;
+ NMActiveConnection *parent;
+
gboolean assumed;
NMAuthChain *chain;
enum {
DEVICE_CHANGED,
DEVICE_METERED_CHANGED,
+ PARENT_ACTIVE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
/****************************************************************/
+static void unwatch_parent (NMActiveConnection *self);
+
+static void
+parent_destroyed (gpointer user_data, GObject *parent)
+{
+ NMActiveConnection *self = user_data;
+
+ unwatch_parent (self);
+ g_signal_emit (self, signals[PARENT_ACTIVE], 0, NULL);
+}
+
+static void
+parent_state_cb (NMActiveConnection *parent_ac,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NMActiveConnection *self = user_data;
+ NMActiveConnectionState parent_state = nm_active_connection_get_state (parent_ac);
+
+ if (parent_state < NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
+ return;
+
+ unwatch_parent (self);
+ g_signal_emit (self, signals[PARENT_ACTIVE], 0, parent_ac);
+}
+
+static void
+unwatch_parent (NMActiveConnection *self)
+{
+ NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
+
+ g_signal_handlers_disconnect_by_func (priv->parent,
+ (GCallback) parent_state_cb,
+ self);
+ g_object_weak_unref ((GObject *) priv->parent, parent_destroyed, self);
+ priv->parent = NULL;
+}
+
+/**
+ * nm_active_connection_set_parent:
+ * @self: the #NMActiveConnection
+ * @parent: The #NMActiveConnection that must be active before the manager
+ * can proceed progressing the device to disconnected state for us.
+ *
+ * Sets the parent connection of @self. A "parent-active" signal will be
+ * emitted when the parent connection becomes active.
+ */
+void
+nm_active_connection_set_parent (NMActiveConnection *self, NMActiveConnection *parent)
+{
+ NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->parent == NULL);
+ priv->parent = parent;
+ g_signal_connect (priv->parent,
+ "notify::" NM_ACTIVE_CONNECTION_STATE,
+ (GCallback) parent_state_cb,
+ self);
+ g_object_weak_ref ((GObject *) priv->parent, parent_destroyed, self);
+}
+
+/****************************************************************/
+
static void
auth_done (NMAuthChain *chain,
GError *error,
self);
}
g_clear_object (&priv->master);
+
+ if (priv->parent)
+ unwatch_parent (self);
+
g_clear_object (&priv->subject);
G_OBJECT_CLASS (nm_active_connection_parent_class)->dispose (object);
NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals[PARENT_ACTIVE] =
+ g_signal_new (NM_ACTIVE_CONNECTION_PARENT_ACTIVE,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMActiveConnectionClass, parent_active),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, NM_TYPE_ACTIVE_CONNECTION);
+
nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (ac_class),
NMDBUS_TYPE_ACTIVE_CONNECTION_SKELETON,
NULL);
static void active_connection_default_changed (NMActiveConnection *active,
GParamSpec *pspec,
NMManager *self);
+static void active_connection_parent_active (NMActiveConnection *active,
+ NMActiveConnection *parent_ac,
+ NMManager *self);
/* Returns: whether to notify D-Bus of the removal or not */
static gboolean
g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active);
g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self);
g_signal_handlers_disconnect_by_func (active, active_connection_default_changed, self);
+ g_signal_handlers_disconnect_by_func (active, active_connection_parent_active, self);
if ( nm_active_connection_get_assumed (active)
&& (connection = nm_active_connection_get_settings_connection (active))
return success;
}
+/* Traverse the device to disconnected state. This means that the device is ready
+ * for connection and will proceed activating if there's an activation request
+ * enqueued.
+ */
+static void
+unmanaged_to_disconnected (NMDevice *device)
+{
+ /* when creating the software device, it can happen that the device is
+ * still unmanaged by NM_UNMANAGED_PLATFORM_INIT because we didn't yet
+ * get the udev event. At this point, we can no longer delay the activation
+ * and force the device to be managed. */
+ nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_PLATFORM_INIT, FALSE, NM_DEVICE_STATE_REASON_USER_REQUESTED);
+
+ nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_USER_EXPLICIT, FALSE, NM_DEVICE_STATE_REASON_USER_REQUESTED);
+
+ g_return_if_fail (nm_device_get_managed (device, FALSE));
+
+ if (nm_device_get_state (device) == NM_DEVICE_STATE_UNMANAGED) {
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_UNAVAILABLE,
+ NM_DEVICE_STATE_REASON_USER_REQUESTED);
+ }
+
+ if ( nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST)
+ && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) {
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_DISCONNECTED,
+ NM_DEVICE_STATE_REASON_USER_REQUESTED);
+ }
+}
+
+/* The parent connection is ready; we can proceed realizing the device and
+ * progressing the device to disconencted state.
+ */
+static void
+active_connection_parent_active (NMActiveConnection *active,
+ NMActiveConnection *parent_ac,
+ NMManager *self)
+{
+ NMDevice *device = nm_active_connection_get_device (active);
+ GError *error = NULL;
+
+ g_signal_handlers_disconnect_by_func (active,
+ (GCallback) active_connection_parent_active,
+ self);
+
+ if (parent_ac) {
+ NMSettingsConnection *connection = nm_active_connection_get_settings_connection (active);
+ NMDevice *parent = nm_active_connection_get_device (parent_ac);
+
+ if (nm_device_create_and_realize (device, (NMConnection *) connection, parent, &error)) {
+ /* We can now proceed to disconnected state so that activation proceeds. */
+ unmanaged_to_disconnected (device);
+ } else {
+ nm_log_warn (LOGD_CORE, "Could not realize device '%s': %s",
+ nm_device_get_iface (device), error->message);
+ nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
+ }
+ } else {
+ nm_log_warn (LOGD_CORE, "The parent connection device '%s' depended on disappeared.",
+ nm_device_get_iface (device));
+ nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
+ }
+}
+
static gboolean
_internal_activate_device (NMManager *self, NMActiveConnection *active, GError **error)
{
NMDevice *parent;
parent = find_parent_device_for_connection (self, (NMConnection *) connection, NULL);
- if (!nm_device_create_and_realize (device, (NMConnection *) connection, parent, error)) {
- g_prefix_error (error, "%s failed to create resources: ", nm_device_get_iface (device));
- return FALSE;
+
+ if (parent && !nm_device_is_real (parent)) {
+ NMSettingsConnection *parent_con;
+ NMActiveConnection *parent_ac;
+
+ parent_con = nm_device_get_best_connection (parent, NULL, error);
+ if (!parent_con) {
+ g_prefix_error (error, "%s failed to create parent: ", nm_device_get_iface (device));
+ return FALSE;
+ }
+
+ parent_ac = nm_manager_activate_connection (self, parent_con, NULL, parent, subject, error);
+ if (!parent_ac) {
+ g_prefix_error (error, "%s failed to activate parent: ", nm_device_get_iface (device));
+ return FALSE;
+ }
+
+ /* We can't realize now; defer until the parent device is ready. */
+ g_signal_connect (active,
+ NM_ACTIVE_CONNECTION_PARENT_ACTIVE,
+ (GCallback) active_connection_parent_active,
+ self);
+ nm_active_connection_set_parent (active, parent_ac);
+ } else {
+ /* We can realize now; no need to wait for a parent device. */
+ if (!nm_device_create_and_realize (device, (NMConnection *) connection, parent, error)) {
+ g_prefix_error (error, "%s failed to create resources: ", nm_device_get_iface (device));
+ return FALSE;
+ }
}
}
if (existing)
nm_device_steal_connection (existing, connection);
- /* when creating the software device, it can happen that the device is
- * still unmanaged by NM_UNMANAGED_PLATFORM_INIT because we didn't yet
- * get the udev event. At this point, we can no longer delay the activation
- * and force the device to be managed. */
- nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_PLATFORM_INIT, FALSE, NM_DEVICE_STATE_REASON_USER_REQUESTED);
-
- nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_USER_EXPLICIT, FALSE, NM_DEVICE_STATE_REASON_USER_REQUESTED);
-
- g_return_val_if_fail (nm_device_get_managed (device, FALSE), FALSE);
-
- if (nm_device_get_state (device) == NM_DEVICE_STATE_UNMANAGED) {
- nm_device_state_changed (device,
- NM_DEVICE_STATE_UNAVAILABLE,
- NM_DEVICE_STATE_REASON_USER_REQUESTED);
- }
-
- if ( nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST)
- && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) {
- nm_device_state_changed (device,
- NM_DEVICE_STATE_DISCONNECTED,
- NM_DEVICE_STATE_REASON_USER_REQUESTED);
- }
+ /* If the device is there, we can ready it for the activation. */
+ if (nm_device_is_real (device))
+ unmanaged_to_disconnected (device);
/* Export the new ActiveConnection to clients and start it on the device */
nm_exported_object_export (NM_EXPORTED_OBJECT (active));
* regardless of whether that connection is autoconnect-enabled or not
* (since this is an explicit request, not an auto-activation request).
*/
- if (!connection_path) {
- GPtrArray *available;
- guint64 best_timestamp = 0;
- guint i;
-
+ if (connection_path) {
+ connection = nm_settings_get_connection_by_path (priv->settings, connection_path);
+ if (!connection) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+ "Connection could not be found.");
+ goto error;
+ }
+ } else {
/* If no connection is given, find a suitable connection for the given device path */
if (!device_path) {
error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
goto error;
}
- available = nm_device_get_available_connections (device, specific_object_path);
- for (i = 0; available && i < available->len; i++) {
- NMSettingsConnection *candidate = g_ptr_array_index (available, i);
- guint64 candidate_timestamp = 0;
-
- nm_settings_connection_get_timestamp (candidate, &candidate_timestamp);
- if (!connection_path || (candidate_timestamp > best_timestamp)) {
- connection_path = nm_connection_get_path (NM_CONNECTION (candidate));
- best_timestamp = candidate_timestamp;
- }
- }
-
- if (available)
- g_ptr_array_free (available, TRUE);
-
- if (!connection_path) {
- error = g_error_new_literal (NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
- "The device has no connections available.");
+ connection = nm_device_get_best_connection (device, specific_object_path, &error);
+ if (!connection)
goto error;
- }
- }
-
- g_assert (connection_path);
- connection = nm_settings_get_connection_by_path (priv->settings, connection_path);
- if (!connection) {
- error = g_error_new_literal (NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
- "Connection could not be found.");
- goto error;
}
subject = validate_activation_request (self,