From d681cbfd3daeec38934bf1e3b73a571b3fef5f2f Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 19 Nov 2015 16:08:18 +0100 Subject: [PATCH 01/17] libnm-glib: queue added/removed signals and suppress uninitialized notifications This is a straightforward copy of the changes done in libnm. It is done to cope with test failures due to redundant or misordered signals. See commit 52ae28f6e5bf68c575145f633f7c85c145aa20fe for a detailed explanation. --- libnm-glib/nm-object.c | 245 +++++++++++++++++++++++++++++++---------- 1 file changed, 188 insertions(+), 57 deletions(-) diff --git a/libnm-glib/nm-object.c b/libnm-glib/nm-object.c index 87c1237c8a..f2f46e0118 100644 --- a/libnm-glib/nm-object.c +++ b/libnm-glib/nm-object.c @@ -58,7 +58,7 @@ typedef struct { const char *signal_prefix; } PropertyInfo; -static void reload_complete (NMObject *object); +static void reload_complete (NMObject *object, gboolean emit_now); typedef struct { DBusGConnection *connection; @@ -72,7 +72,7 @@ typedef struct { NMObject *parent; gboolean suppress_property_updates; - GSList *notify_props; + GSList *notify_items; guint32 notify_id; gboolean inited; @@ -114,6 +114,27 @@ nm_object_error_quark (void) return quark; } +typedef enum { + NOTIFY_SIGNAL_PENDING_NONE, + NOTIFY_SIGNAL_PENDING_ADDED, + NOTIFY_SIGNAL_PENDING_REMOVED, + NOTIFY_SIGNAL_PENDING_ADDED_REMOVED, +} NotifySignalPending; + +typedef struct { + const char *property; + const char *signal_prefix; + NotifySignalPending pending; + NMObject *changed; +} NotifyItem; + +static void +notify_item_free (NotifyItem *item) +{ + g_clear_object (&item->changed); + g_slice_free (NotifyItem, item); +} + static void proxy_name_owner_changed (DBusGProxy *proxy, const char *name, @@ -297,8 +318,8 @@ dispose (GObject *object) priv->notify_id = 0; } - g_slist_free_full (priv->notify_props, g_free); - priv->notify_props = NULL; + g_slist_free_full (priv->notify_items, (GDestroyNotify) notify_item_free); + priv->notify_items = NULL; g_slist_free_full (priv->property_interfaces, g_free); priv->property_interfaces = NULL; @@ -484,52 +505,155 @@ deferred_notify_cb (gpointer data) { NMObject *object = NM_OBJECT (data); NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + NMObjectClass *object_class = NM_OBJECT_GET_CLASS (object); GSList *props, *iter; priv->notify_id = 0; - /* Clear priv->notify_props early so that an NMObject subclass that + /* Wait until all reloads are done before notifying */ + if (priv->reload_remaining) + return G_SOURCE_REMOVE; + + /* Clear priv->notify_items early so that an NMObject subclass that * listens to property changes can queue up other property changes * during the g_object_notify() call separately from the property * list we're iterating. */ - props = g_slist_reverse (priv->notify_props); - priv->notify_props = NULL; + props = g_slist_reverse (priv->notify_items); + priv->notify_items = NULL; g_object_ref (object); + + /* Emit property change notifications first */ for (iter = props; iter; iter = g_slist_next (iter)) { - g_object_notify (G_OBJECT (object), (const char *) iter->data); - g_free (iter->data); + NotifyItem *item = iter->data; + + if (item->property) + g_object_notify (G_OBJECT (object), item->property); + } + + /* And added/removed signals second */ + for (iter = props; iter; iter = g_slist_next (iter)) { + NotifyItem *item = iter->data; + char buf[50]; + gint ret = 0; + + switch (item->pending) { + case NOTIFY_SIGNAL_PENDING_ADDED: + ret = g_snprintf (buf, sizeof (buf), "%s-added", item->signal_prefix); + break; + case NOTIFY_SIGNAL_PENDING_REMOVED: + ret = g_snprintf (buf, sizeof (buf), "%s-removed", item->signal_prefix); + break; + case NOTIFY_SIGNAL_PENDING_ADDED_REMOVED: + // XXX + if (object_class->object_creation_failed) + object_class->object_creation_failed (object, NULL, g_strdup (nm_object_get_path (item->changed))); + break; + case NOTIFY_SIGNAL_PENDING_NONE: + default: + break; + } + if (ret > 0) { + g_assert (ret < sizeof (buf)); + g_signal_emit_by_name (object, buf, item->changed); + } } g_object_unref (object); - g_slist_free (props); - return FALSE; + g_slist_free_full (props, (GDestroyNotify) notify_item_free); + return G_SOURCE_REMOVE; +} + +static void +_nm_object_defer_notify (NMObject *object) +{ + NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); + + if (!priv->notify_id) + priv->notify_id = g_idle_add_full (G_PRIORITY_LOW, deferred_notify_cb, object, NULL); +} + +static void +_nm_object_queue_notify_full (NMObject *object, + const char *property, + const char *signal_prefix, + gboolean added, + NMObject *changed) +{ + NMObjectPrivate *priv; + NotifyItem *item; + GSList *iter; + + g_return_if_fail (NM_IS_OBJECT (object)); + g_return_if_fail (!signal_prefix != !property); + g_return_if_fail (!signal_prefix == !changed); + + priv = NM_OBJECT_GET_PRIVATE (object); + _nm_object_defer_notify (object); + + property = g_intern_string (property); + signal_prefix = g_intern_string (signal_prefix); + for (iter = priv->notify_items; iter; iter = g_slist_next (iter)) { + item = iter->data; + + if (property && (property == item->property)) + return; + + /* Collapse signals for the same object (such as "added->removed") to + * ensure we don't emit signals when their sum should have no effect. + * The "added->removed->removed" sequence requires special handling, + * hence the addition of the ADDED_REMOVED state to ensure that no + * signal is emitted in this case: + * + * Without the ADDED_REMOVED state: + * NONE + added -> ADDED + * ADDED + removed -> NONE + * NONE + removed -> REMOVED (would emit 'removed' signal) + * + * With the ADDED_REMOVED state: + * NONE | ADDED_REMOVED + added -> ADDED + * ADDED + removed -> ADDED_REMOVED + * ADDED_REMOVED + removed -> ADDED_REMOVED (emits no signal) + */ + if (signal_prefix && (changed == item->changed) && (item->signal_prefix == signal_prefix)) { + switch (item->pending) { + case NOTIFY_SIGNAL_PENDING_ADDED: + if (!added) + item->pending = NOTIFY_SIGNAL_PENDING_ADDED_REMOVED; + break; + case NOTIFY_SIGNAL_PENDING_REMOVED: + if (added) + item->pending = NOTIFY_SIGNAL_PENDING_NONE; + break; + case NOTIFY_SIGNAL_PENDING_ADDED_REMOVED: + if (added) + item->pending = NOTIFY_SIGNAL_PENDING_ADDED; + break; + case NOTIFY_SIGNAL_PENDING_NONE: + item->pending = added ? NOTIFY_SIGNAL_PENDING_ADDED : NOTIFY_SIGNAL_PENDING_REMOVED; + break; + default: + g_assert_not_reached (); + } + return; + } + } + + item = g_slice_new0 (NotifyItem); + item->property = property; + if (signal_prefix) { + item->signal_prefix = signal_prefix; + item->pending = added ? NOTIFY_SIGNAL_PENDING_ADDED : NOTIFY_SIGNAL_PENDING_REMOVED; + item->changed = changed ? g_object_ref (changed) : NULL; + } + priv->notify_items = g_slist_prepend (priv->notify_items, item); } void _nm_object_queue_notify (NMObject *object, const char *property) { - NMObjectPrivate *priv; - gboolean found = FALSE; - GSList *iter; - - g_return_if_fail (NM_IS_OBJECT (object)); - g_return_if_fail (property != NULL); - - priv = NM_OBJECT_GET_PRIVATE (object); - if (!priv->notify_id) - priv->notify_id = g_idle_add_full (G_PRIORITY_LOW, deferred_notify_cb, object, NULL); - - for (iter = priv->notify_props; iter; iter = g_slist_next (iter)) { - if (!strcmp ((char *) iter->data, property)) { - found = TRUE; - break; - } - } - - if (!found) - priv->notify_props = g_slist_prepend (priv->notify_props, g_strdup (property)); + _nm_object_queue_notify_full (object, property, NULL, FALSE, NULL); } void @@ -759,17 +883,12 @@ array_diff (GPtrArray *needles, GPtrArray *haystack, GPtrArray *diff) } static void -emit_added_removed_signal (NMObject *self, - const char *signal_prefix, - NMObject *changed, - gboolean added) +queue_added_removed_signal (NMObject *self, + const char *signal_prefix, + NMObject *changed, + gboolean added) { - char buf[50]; - int ret; - - ret = g_snprintf (buf, sizeof (buf), "%s-%s", signal_prefix, added ? "added" : "removed"); - g_assert (ret < sizeof (buf)); - g_signal_emit_by_name (self, buf, changed); + _nm_object_queue_notify_full (self, NULL, signal_prefix, added, changed); } static void @@ -809,17 +928,17 @@ object_property_complete (ObjectCreatedData *odata) /* Emit added & removed */ for (i = 0; i < removed->len; i++) { - emit_added_removed_signal (self, - pi->signal_prefix, - g_ptr_array_index (removed, i), - FALSE); + queue_added_removed_signal (self, + pi->signal_prefix, + g_ptr_array_index (removed, i), + FALSE); } for (i = 0; i < added->len; i++) { - emit_added_removed_signal (self, - pi->signal_prefix, - g_ptr_array_index (added, i), - TRUE); + queue_added_removed_signal (self, + pi->signal_prefix, + g_ptr_array_index (added, i), + TRUE); } different = removed->len || added->len; @@ -850,8 +969,8 @@ object_property_complete (ObjectCreatedData *odata) if (different && odata->property_name) _nm_object_queue_notify (self, odata->property_name); - if (priv->reload_results && --priv->reload_remaining == 0) - reload_complete (self); + if (--priv->reload_remaining == 0) + reload_complete (self, FALSE); g_object_unref (self); g_free (odata->objects); @@ -898,8 +1017,7 @@ handle_object_property (NMObject *self, const char *property_name, GValue *value odata->array = FALSE; odata->property_name = property_name; - if (priv->reload_results) - priv->reload_remaining++; + priv->reload_remaining++; path = g_value_get_boxed (value); @@ -946,8 +1064,7 @@ handle_object_array_property (NMObject *self, const char *property_name, GValue odata->array = TRUE; odata->property_name = property_name; - if (priv->reload_results) - priv->reload_remaining++; + priv->reload_remaining++; if (paths->len == 0) { object_property_complete (odata); @@ -1214,6 +1331,8 @@ _nm_object_reload_properties (NMObject *object, GError **error) if (!priv->property_interfaces || !priv->nm_running) return TRUE; + priv->reload_remaining++; + for (p = priv->property_interfaces; p; p = p->next) { if (!dbus_g_proxy_call (priv->properties_proxy, "GetAll", error, G_TYPE_STRING, p->data, @@ -1226,6 +1345,9 @@ _nm_object_reload_properties (NMObject *object, GError **error) g_hash_table_destroy (props); } + if (--priv->reload_remaining == 0) + reload_complete (object, TRUE); + return TRUE; } @@ -1322,13 +1444,22 @@ _nm_object_set_property (NMObject *object, } static void -reload_complete (NMObject *object) +reload_complete (NMObject *object, gboolean emit_now) { NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object); GSimpleAsyncResult *simple; GSList *results, *iter; GError *error; + if (emit_now) { + if (priv->notify_id) { + g_source_remove (priv->notify_id); + priv->notify_id = 0; + } + deferred_notify_cb (object); + } else + _nm_object_defer_notify (object); + results = priv->reload_results; priv->reload_results = NULL; error = priv->reload_error; @@ -1371,7 +1502,7 @@ reload_got_properties (DBusGProxy *proxy, DBusGProxyCall *call, } if (--priv->reload_remaining == 0) - reload_complete (object); + reload_complete (object, FALSE); } void From 9e36ccbe284aa4352296b21e5cd087edc6155820 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 24 Nov 2015 16:43:18 +0100 Subject: [PATCH 02/17] device: split new_device_added() out of component_added() Commit cd3df12c8f8ed6c868c12bc4e7fe6ba162dafc5b reused the virtual function component_added() to notify the vlan device about a possibly new parent. This reuse of the virtual function for another purpose is confusing. Clean that up by splitting the implementation and add a new virtual function nm_device_notify_new_device_added() which gets (only implemented by NMDeviceVlan). --- src/devices/nm-device-vlan.c | 24 ++++++++---------------- src/devices/nm-device.c | 31 +++++++++++++++++++++++++++++-- src/devices/nm-device.h | 3 +++ src/nm-manager.c | 29 +++++++++++++++-------------- 4 files changed, 55 insertions(+), 32 deletions(-) diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 55790a9273..1712a81385 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -290,36 +290,28 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) return NM_DEVICE_CLASS (nm_device_vlan_parent_class)->is_available (device, flags); } -static gboolean -component_added (NMDevice *device, GObject *component) +static void +notify_new_device_added (NMDevice *device, NMDevice *new_device) { NMDeviceVlan *self = NM_DEVICE_VLAN (device); NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self); - NMDevice *added_device; const NMPlatformLink *plink; const NMPlatformLnkVlan *plnk; if (priv->parent) - return FALSE; - - if (!NM_IS_DEVICE (component)) - return FALSE; - added_device = NM_DEVICE (component); + return; plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink); if (!plnk) { _LOGW (LOGD_VLAN, "failed to get VLAN interface info while checking added component."); - return FALSE; + return; } if ( plink->parent <= 0 - || nm_device_get_ifindex (added_device) != plink->parent) - return FALSE; + || nm_device_get_ifindex (new_device) != plink->parent) + return; - nm_device_vlan_set_parent (self, added_device); - - /* Don't claim parent exclusively */ - return FALSE; + nm_device_vlan_set_parent (self, new_device); } /******************************************************************/ @@ -677,7 +669,7 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) parent_class->ip4_config_pre_commit = ip4_config_pre_commit; parent_class->deactivate = deactivate; parent_class->is_available = is_available; - parent_class->component_added = component_added; + parent_class->notify_new_device_added = notify_new_device_added; parent_class->check_connection_compatible = check_connection_compatible; parent_class->complete_connection = complete_connection; diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 3318e2ef4c..77f3e2031b 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1832,6 +1832,27 @@ setup (NMDevice *self, NMPlatformLink *plink) g_object_thaw_notify (G_OBJECT (self)); } +/** + * nm_device_notify_new_device_added(): + * @self: the #NMDevice + * @device: the newly added device + * + * Called by the manager to notify the device that a new device has + * been found and added. + */ +void +nm_device_notify_new_device_added (NMDevice *self, NMDevice *device) +{ + NMDeviceClass *klass; + + g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (NM_IS_DEVICE (device)); + + klass = NM_DEVICE_GET_CLASS (self); + if (klass->notify_new_device_added) + klass->notify_new_device_added (self, device); +} + /** * nm_device_notify_component_added(): * @self: the #NMDevice @@ -1847,8 +1868,14 @@ setup (NMDevice *self, NMPlatformLink *plink) gboolean nm_device_notify_component_added (NMDevice *self, GObject *component) { - if (NM_DEVICE_GET_CLASS (self)->component_added) - return NM_DEVICE_GET_CLASS (self)->component_added (self, component); + NMDeviceClass *klass; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (G_IS_OBJECT (component), FALSE); + + klass = NM_DEVICE_GET_CLASS (self); + if (klass->component_added) + return klass->component_added (self, component); return FALSE; } diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 6a5cd46f29..704ef6e9a9 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -288,6 +288,8 @@ typedef struct { gboolean (* have_any_ready_slaves) (NMDevice *self, const GSList *slaves); + void (* notify_new_device_added) (NMDevice *self, NMDevice *new_device); + /** * component_added: * @self: the #NMDevice @@ -496,6 +498,7 @@ gboolean nm_device_check_connection_available (NMDevice *device, NMDeviceCheckConAvailableFlags flags, const char *specific_object); +void nm_device_notify_new_device_added (NMDevice *self, NMDevice *new_device); gboolean nm_device_notify_component_added (NMDevice *device, GObject *component); gboolean nm_device_owns_iface (NMDevice *device, const char *iface); diff --git a/src/nm-manager.c b/src/nm-manager.c index 72d9144d84..0ccd3f7c1f 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1671,18 +1671,6 @@ device_ip_iface_changed (NMDevice *device, } } -static gboolean -notify_component_added (NMManager *self, GObject *component) -{ - GSList *iter; - - for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) { - if (nm_device_notify_component_added (NM_DEVICE (iter->data), component)) - return TRUE; - } - return FALSE; -} - /** * add_device: * @self: the #NMManager @@ -1814,7 +1802,12 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume) g_signal_emit (self, signals[DEVICE_ADDED], 0, device); g_object_notify (G_OBJECT (self), NM_MANAGER_DEVICES); - notify_component_added (self, G_OBJECT (device)); + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *d = iter->data; + + if (d != device) + nm_device_notify_new_device_added (d, device); + } /* New devices might be master interfaces for virtual interfaces; so we may * need to create new virtual interfaces now. @@ -1845,7 +1838,15 @@ factory_component_added_cb (NMDeviceFactory *factory, GObject *component, gpointer user_data) { - return notify_component_added (NM_MANAGER (user_data), component); + GSList *iter; + + g_return_val_if_fail (NM_IS_MANAGER (user_data), FALSE); + + for (iter = NM_MANAGER_GET_PRIVATE (user_data)->devices; iter; iter = iter->next) { + if (nm_device_notify_component_added ((NMDevice *) iter->data, component)) + return TRUE; + } + return FALSE; } static void From 15d305c07f4248ec33413c5571e2728bdf7fe779 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 6 Aug 2015 10:29:29 +0200 Subject: [PATCH 03/17] utils: add @filter_func argument to nm_utils_g_value_set_object_path_array() --- src/NetworkManagerUtils.c | 9 ++++++++- src/NetworkManagerUtils.h | 15 ++++++++++++++- src/devices/nm-device-bond.c | 2 +- src/devices/nm-device-bridge.c | 2 +- src/devices/team/nm-device-team.c | 2 +- src/nm-manager.c | 4 ++-- 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index d84bf1bc56..5ff4f086bb 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -3440,11 +3440,16 @@ nm_utils_g_value_set_object_path (GValue *value, gpointer object) * nm_utils_g_value_set_object_path_array: * @value: a #GValue, initialized to store an object path * @objects: a #GSList of #NMExportedObjects + * @filter_func: (allow-none): function to call on each object in @objects + * @user_data: data to pass to @filter_func * * Sets @value to an array of object paths of the objects in @objects. */ void -nm_utils_g_value_set_object_path_array (GValue *value, GSList *objects) +nm_utils_g_value_set_object_path_array (GValue *value, + GSList *objects, + NMUtilsObjectFunc filter_func, + gpointer user_data) { GPtrArray *paths; GSList *iter; @@ -3455,6 +3460,8 @@ nm_utils_g_value_set_object_path_array (GValue *value, GSList *objects) if (!nm_exported_object_is_exported (object)) continue; + if (filter_func && !filter_func (G_OBJECT (object), user_data)) + continue; g_ptr_array_add (paths, g_strdup (nm_exported_object_get_path (object))); } g_ptr_array_add (paths, NULL); diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index fddeb57d8d..a88f3afa08 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -320,7 +320,20 @@ NMUtilsTestFlags nm_utils_get_testing (void); void _nm_utils_set_testing (NMUtilsTestFlags flags); void nm_utils_g_value_set_object_path (GValue *value, gpointer object); -void nm_utils_g_value_set_object_path_array (GValue *value, GSList *objects); void nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings); +/** + * NMUtilsObjectFunc: + * @object: the object to filter on + * @user_data: data passed to the function from the caller + * + * Returns: %TRUE if the object should be used, %FALSE if not + */ +typedef gboolean (*NMUtilsObjectFunc) (GObject *object, gpointer user_data); + +void nm_utils_g_value_set_object_path_array (GValue *value, + GSList *objects, + NMUtilsObjectFunc filter_func, + gpointer user_data); + #endif /* __NETWORKMANAGER_UTILS_H__ */ diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 98e772adff..359b3c4abf 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -501,7 +501,7 @@ get_property (GObject *object, guint prop_id, break; case PROP_SLAVES: list = nm_device_master_get_slaves (NM_DEVICE (object)); - nm_utils_g_value_set_object_path_array (value, list); + nm_utils_g_value_set_object_path_array (value, list, NULL, NULL); g_slist_free (list); break; default: diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index db9f0c60e5..b54b2e0a01 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -447,7 +447,7 @@ get_property (GObject *object, guint prop_id, break; case PROP_SLAVES: list = nm_device_master_get_slaves (NM_DEVICE (object)); - nm_utils_g_value_set_object_path_array (value, list); + nm_utils_g_value_set_object_path_array (value, list, NULL, NULL); g_slist_free (list); break; default: diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index a6679e422e..4bbe4c9ce7 100644 --- a/src/devices/team/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -758,7 +758,7 @@ get_property (GObject *object, guint prop_id, break; case PROP_SLAVES: list = nm_device_master_get_slaves (NM_DEVICE (object)); - nm_utils_g_value_set_object_path_array (value, list); + nm_utils_g_value_set_object_path_array (value, list, NULL, NULL); g_slist_free (list); break; default: diff --git a/src/nm-manager.c b/src/nm-manager.c index 0ccd3f7c1f..4bc0dc3413 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -5100,7 +5100,7 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, FALSE); break; case PROP_ACTIVE_CONNECTIONS: - nm_utils_g_value_set_object_path_array (value, priv->active_connections); + nm_utils_g_value_set_object_path_array (value, priv->active_connections, NULL, NULL); break; case PROP_CONNECTIVITY: g_value_set_uint (value, nm_connectivity_get_state (priv->connectivity)); @@ -5129,7 +5129,7 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, priv->sleeping); break; case PROP_DEVICES: - nm_utils_g_value_set_object_path_array (value, priv->devices); + nm_utils_g_value_set_object_path_array (value, priv->devices, NULL, NULL); break; case PROP_METERED: g_value_set_uint (value, priv->metered); From 7d9f819aa17c514fca7425b55b7d281984d5cbe5 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 18 Aug 2015 15:12:43 +0200 Subject: [PATCH 04/17] utils: construct paths array directly in nm_utils_g_value_set_object_path_array() nm_utils_g_value_set_object_path_array() is used at several places, so that this optimazation makes some sense. --- src/NetworkManagerUtils.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 5ff4f086bb..92f0efe209 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -3451,21 +3451,22 @@ nm_utils_g_value_set_object_path_array (GValue *value, NMUtilsObjectFunc filter_func, gpointer user_data) { - GPtrArray *paths; + char **paths; + guint i; GSList *iter; - paths = g_ptr_array_new (); - for (iter = objects; iter; iter = iter->next) { + paths = g_new (char *, g_slist_length (objects) + 1); + for (i = 0, iter = objects; iter; iter = iter->next) { NMExportedObject *object = iter->data; if (!nm_exported_object_is_exported (object)) continue; - if (filter_func && !filter_func (G_OBJECT (object), user_data)) + if (filter_func && !filter_func ((GObject *) object, user_data)) continue; - g_ptr_array_add (paths, g_strdup (nm_exported_object_get_path (object))); + paths[i++] = g_strdup (nm_exported_object_get_path (object)); } - g_ptr_array_add (paths, NULL); - g_value_take_boxed (value, (char **) g_ptr_array_free (paths, FALSE)); + paths[i] = NULL; + g_value_take_boxed (value, paths); } /** From 7c4b5a44127eb0d1f1669fa49b5e32dbcbb4bd38 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 6 Aug 2015 13:23:56 +0200 Subject: [PATCH 05/17] device: let NM_DEVICE_MASTER be equal to nm_device_get_master() There is only one user of the NM_DEVICE_MASTER property (NMActiveConnection). device_master_changed() uses the property for change notifications, but returns early if !nm_device_get_master(). We might emit the "notify::master" signal too often, even when nothing changed (because currently we always emit it when priv->master changes). If we would fix that to only emit the signal when nm_device_get_master() actually changes, it would still be correct, because device_master_changed() doesn't care about notifications when the master is unset. Hence, the only user doesn't care about the difference. So change it because it's unexpected that the property and function are not completely equal. --- src/devices/nm-device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 77f3e2031b..54b02534df 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -10130,7 +10130,7 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, priv->is_master); break; case PROP_MASTER: - g_value_set_object (value, priv->master); + g_value_set_object (value, nm_device_get_master (self)); break; case PROP_HW_ADDRESS: g_value_set_string (value, priv->hw_addr); From d6f9230beba146818112b36f9f31c9eb10bec888 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 5 Sep 2014 08:50:02 -0500 Subject: [PATCH 06/17] core: add "real" NMDevice property This property is TRUE for devices that exist either as a kernel device or are backed by some other resource (eg, ModemManager object, Bluez device, etc). It will eventually be FALSE for software devices that are not yet instantiated. --- introspection/nm-device.xml | 7 ++++++ src/devices/nm-device.c | 44 +++++++++++++++++++++++++++++++++---- src/devices/nm-device.h | 2 ++ src/settings/nm-settings.c | 27 +++++++++++++++++++++-- 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/introspection/nm-device.xml b/introspection/nm-device.xml index fd377a07eb..cf04ac82f8 100644 --- a/introspection/nm-device.xml +++ b/introspection/nm-device.xml @@ -161,6 +161,13 @@ LLDP TLV names to variant boxed values. + + + True if the device exists, or False for placeholder devices that + do not yet exist but could be automatically created by NetworkManager + if one of their AvailableConnections was activated. + + diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 54b02534df..d55493fff6 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -129,6 +129,7 @@ enum { PROP_HAS_PENDING_ACTION, PROP_METERED, PROP_LLDP_NEIGHBORS, + PROP_REAL, LAST_PROP }; @@ -207,6 +208,7 @@ typedef struct { char * udi; char * iface; /* may change, could be renamed by user */ int ifindex; + gboolean real; char * ip_iface; int ip_ifindex; NMDeviceType type; @@ -559,12 +561,33 @@ nm_device_get_ifindex (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->ifindex; } +/** + * nm_device_is_software: + * @self: the #NMDevice + * + * Indicates if the device is a software-based virtual device without + * backing hardware, which can be added and removed programmatically. + * + * Returns: %TRUE if the device is a software-based device + */ gboolean nm_device_is_software (NMDevice *self) { return NM_FLAGS_HAS (NM_DEVICE_GET_PRIVATE (self)->capabilities, NM_DEVICE_CAP_IS_SOFTWARE); } +/** + * nm_device_is_real: + * @self: the #NMDevice + * + * Returns: %TRUE if the device exists, %FALSE if the device is a placeholder + */ +gboolean +nm_device_is_real (NMDevice *self) +{ + return NM_DEVICE_GET_PRIVATE (self)->real; +} + const char * nm_device_get_ip_iface (NMDevice *self) { @@ -1829,6 +1852,9 @@ setup (NMDevice *self, NMPlatformLink *plink) device_set_master (self, master); } + priv->real = TRUE; + g_object_notify (G_OBJECT (self), NM_DEVICE_REAL); + g_object_thaw_notify (G_OBJECT (self)); } @@ -2445,7 +2471,7 @@ device_has_config (NMDevice *self) return TRUE; /* The existence of a software device is good enough. */ - if (nm_device_is_software (self)) + if (nm_device_is_software (self) && nm_device_is_real (self)) return TRUE; /* Slaves are also configured by definition */ @@ -6489,7 +6515,7 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) return; if (priv->queued_act_request) return; - if (!nm_device_is_software (self)) + if (!nm_device_is_software (self) || !nm_device_is_real (self)) return; if (nm_device_get_state (self) == NM_DEVICE_STATE_UNMANAGED) return; @@ -6602,10 +6628,10 @@ impl_device_delete (NMDevice *self, GDBusMethodInvocation *context) { GError *error = NULL; - if (!nm_device_is_software (self)) { + if (!nm_device_is_software (self) || !nm_device_is_real (self)) { error = g_error_new_literal (NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_SOFTWARE, - "This device is not a software device"); + "This device is not a software device or is not realized"); g_dbus_method_invocation_take_error (context, error); return; } @@ -10149,6 +10175,9 @@ get_property (GObject *object, guint prop_id, g_value_take_variant (value, g_variant_builder_end (&array_builder)); } break; + case PROP_REAL: + g_value_set_boolean (value, priv->real); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -10439,6 +10468,13 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_REAL, + g_param_spec_boolean (NM_DEVICE_REAL, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* Signals */ signals[STATE_CHANGED] = g_signal_new ("state-changed", diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 704ef6e9a9..8931b85609 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -59,6 +59,7 @@ #define NM_DEVICE_HW_ADDRESS "hw-address" #define NM_DEVICE_METERED "metered" #define NM_DEVICE_LLDP_NEIGHBORS "lldp-neighbors" +#define NM_DEVICE_REAL "real" #define NM_DEVICE_TYPE_DESC "type-desc" /* Internal only */ #define NM_DEVICE_RFKILL_TYPE "rfkill-type" /* Internal only */ @@ -326,6 +327,7 @@ const char * nm_device_get_udi (NMDevice *dev); const char * nm_device_get_iface (NMDevice *dev); int nm_device_get_ifindex (NMDevice *dev); gboolean nm_device_is_software (NMDevice *dev); +gboolean nm_device_is_real (NMDevice *dev); const char * nm_device_get_ip_iface (NMDevice *dev); int nm_device_get_ip_ifindex(NMDevice *dev); const char * nm_device_get_driver (NMDevice *dev); diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index d742333194..3ba5b5de64 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -1854,13 +1854,20 @@ default_wired_clear_tag (NMSettings *self, nm_config_set_no_auto_default_for_device (NM_SETTINGS_GET_PRIVATE (self)->config, device); } -void -nm_settings_device_added (NMSettings *self, NMDevice *device) +static void +device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self) { NMConnection *connection; NMSettingsConnection *added; GError *error = NULL; + if (!nm_device_is_real (device)) + return; + + g_signal_handlers_disconnect_by_func (device, + G_CALLBACK (device_realized), + self); + /* If the device isn't managed or it already has a default wired connection, * ignore it. */ @@ -1898,11 +1905,27 @@ nm_settings_device_added (NMSettings *self, NMDevice *device) nm_settings_connection_get_id (added)); } +void +nm_settings_device_added (NMSettings *self, NMDevice *device) +{ + if (nm_device_is_real (device)) + device_realized (device, NULL, self); + else { + g_signal_connect_after (device, "notify::" NM_DEVICE_REAL, + G_CALLBACK (device_realized), + self); + } +} + void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting) { NMSettingsConnection *connection; + g_signal_handlers_disconnect_by_func (device, + G_CALLBACK (device_realized), + self); + connection = g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG); if (connection) { default_wired_clear_tag (self, device, connection, FALSE); From deb6c5f7143f9f257b79a8c9db724310ef4c9a31 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 6 Oct 2014 11:34:02 -0500 Subject: [PATCH 07/17] libnm-glib/libnm: add support for "real" NMDevice property --- libnm-glib/libnm-glib.ver | 1 + libnm-glib/nm-device.c | 41 +++++++++++++++++++++++++++++++++++++++ libnm-glib/nm-device.h | 3 +++ libnm/libnm.ver | 1 + libnm/nm-device.c | 40 ++++++++++++++++++++++++++++++++++++++ libnm/nm-device.h | 3 +++ 6 files changed, 89 insertions(+) diff --git a/libnm-glib/libnm-glib.ver b/libnm-glib/libnm-glib.ver index fc0c0b95c2..53352587b5 100644 --- a/libnm-glib/libnm-glib.ver +++ b/libnm-glib/libnm-glib.ver @@ -158,6 +158,7 @@ global: nm_device_infiniband_get_hw_address; nm_device_infiniband_get_type; nm_device_infiniband_new; + nm_device_is_real; nm_device_is_software; nm_device_modem_error_get_type; nm_device_modem_error_quark; diff --git a/libnm-glib/nm-device.c b/libnm-glib/nm-device.c index ce76432107..ade17d579e 100644 --- a/libnm-glib/nm-device.c +++ b/libnm-glib/nm-device.c @@ -79,6 +79,7 @@ typedef struct { char *firmware_version; char *type_description; NMDeviceCapabilities capabilities; + gboolean real; gboolean managed; gboolean firmware_missing; gboolean autoconnect; @@ -110,6 +111,7 @@ enum { PROP_DRIVER_VERSION, PROP_FIRMWARE_VERSION, PROP_CAPABILITIES, + PROP_REAL, PROP_MANAGED, PROP_AUTOCONNECT, PROP_FIRMWARE_MISSING, @@ -196,6 +198,7 @@ register_properties (NMDevice *device) { NM_DEVICE_DRIVER_VERSION, &priv->driver_version }, { NM_DEVICE_FIRMWARE_VERSION, &priv->firmware_version }, { NM_DEVICE_CAPABILITIES, &priv->capabilities }, + { NM_DEVICE_REAL, &priv->real }, { NM_DEVICE_MANAGED, &priv->managed }, { NM_DEVICE_AUTOCONNECT, &priv->autoconnect }, { NM_DEVICE_FIRMWARE_MISSING, &priv->firmware_missing }, @@ -446,6 +449,9 @@ get_property (GObject *object, case PROP_CAPABILITIES: g_value_set_uint (value, nm_device_get_capabilities (device)); break; + case PROP_REAL: + g_value_set_boolean (value, nm_device_is_real (device)); + break; case PROP_MANAGED: g_value_set_boolean (value, nm_device_get_managed (device)); break; @@ -652,6 +658,22 @@ nm_device_class_init (NMDeviceClass *device_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMDevice:real: + * + * Whether the device is real or is a placeholder device that could + * be created automatically by NetworkManager if one of its + * #NMDevice:available-connections was activated. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_REAL, + g_param_spec_boolean (NM_DEVICE_REAL, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** * NMDevice:managed: * @@ -2105,6 +2127,25 @@ nm_device_get_mtu (NMDevice *device) return NM_DEVICE_GET_PRIVATE (device)->mtu; } +/** + * nm_device_is_real: + * @device: a #NMDevice + * + * Returns: %TRUE if the device exists, or %FALSE if it is a placeholder device + * that could be automatically created by NetworkManager if one of its + * #NMDevice:available-connections was activated. + * + * Since: 1.2 + **/ +gboolean +nm_device_is_real (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + _nm_object_ensure_inited (NM_OBJECT (device)); + return NM_DEVICE_GET_PRIVATE (device)->real; +} + /** * nm_device_is_software: * @device: a #NMDevice diff --git a/libnm-glib/nm-device.h b/libnm-glib/nm-device.h index 3d0abde1ca..d2d154dc1f 100644 --- a/libnm-glib/nm-device.h +++ b/libnm-glib/nm-device.h @@ -66,6 +66,7 @@ GQuark nm_device_error_quark (void); #define NM_DEVICE_DRIVER_VERSION "driver-version" #define NM_DEVICE_FIRMWARE_VERSION "firmware-version" #define NM_DEVICE_CAPABILITIES "capabilities" +#define NM_DEVICE_REAL "real" #define NM_DEVICE_MANAGED "managed" #define NM_DEVICE_AUTOCONNECT "autoconnect" #define NM_DEVICE_FIRMWARE_MISSING "firmware-missing" @@ -146,6 +147,8 @@ NM_AVAILABLE_IN_0_9_10 guint32 nm_device_get_mtu (NMDevice *device); NM_AVAILABLE_IN_1_0 gboolean nm_device_is_software (NMDevice *device); +NM_AVAILABLE_IN_1_2 +gboolean nm_device_is_real (NMDevice *device); const char * nm_device_get_product (NMDevice *device); const char * nm_device_get_vendor (NMDevice *device); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index dbd95922e9..ae287695bc 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -876,6 +876,7 @@ global: nm_device_ip_tunnel_get_ttl; nm_device_ip_tunnel_get_type; nm_device_get_nm_plugin_missing; + nm_device_is_real; nm_device_set_managed; nm_device_tun_get_group; nm_device_tun_get_hw_address; diff --git a/libnm/nm-device.c b/libnm/nm-device.c index 3ca08c55ce..7e48a53087 100644 --- a/libnm/nm-device.c +++ b/libnm/nm-device.c @@ -86,6 +86,7 @@ typedef struct { char *type_description; NMMetered metered; NMDeviceCapabilities capabilities; + gboolean real; gboolean managed; gboolean firmware_missing; gboolean nm_plugin_missing; @@ -119,6 +120,7 @@ enum { PROP_DRIVER_VERSION, PROP_FIRMWARE_VERSION, PROP_CAPABILITIES, + PROP_REAL, PROP_MANAGED, PROP_AUTOCONNECT, PROP_FIRMWARE_MISSING, @@ -229,6 +231,7 @@ init_dbus (NMObject *object) { NM_DEVICE_DRIVER_VERSION, &priv->driver_version }, { NM_DEVICE_FIRMWARE_VERSION, &priv->firmware_version }, { NM_DEVICE_CAPABILITIES, &priv->capabilities }, + { NM_DEVICE_REAL, &priv->real }, { NM_DEVICE_MANAGED, &priv->managed }, { NM_DEVICE_AUTOCONNECT, &priv->autoconnect }, { NM_DEVICE_FIRMWARE_MISSING, &priv->firmware_missing }, @@ -461,6 +464,9 @@ get_property (GObject *object, case PROP_CAPABILITIES: g_value_set_flags (value, nm_device_get_capabilities (device)); break; + case PROP_REAL: + g_value_set_boolean (value, nm_device_is_real (device)); + break; case PROP_MANAGED: g_value_set_boolean (value, nm_device_get_managed (device)); break; @@ -679,6 +685,22 @@ nm_device_class_init (NMDeviceClass *device_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMDevice:real: + * + * Whether the device is real or is a placeholder device that could + * be created automatically by NetworkManager if one of its + * #NMDevice:available-connections was activated. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_REAL, + g_param_spec_boolean (NM_DEVICE_REAL, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** * NMDevice:managed: * @@ -2097,6 +2119,24 @@ nm_device_get_lldp_neighbors (NMDevice *device) return NM_DEVICE_GET_PRIVATE (device)->lldp_neighbors; } +/** + * nm_device_is_real: + * @device: a #NMDevice + * + * Returns: %TRUE if the device exists, or %FALSE if it is a placeholder device + * that could be automatically created by NetworkManager if one of its + * #NMDevice:available-connections was activated. + * + * Since: 1.2 + **/ +gboolean +nm_device_is_real (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + return NM_DEVICE_GET_PRIVATE (device)->real; +} + /** * nm_device_is_software: * @device: a #NMDevice diff --git a/libnm/nm-device.h b/libnm/nm-device.h index 005d85dd4f..45ee3b2100 100644 --- a/libnm/nm-device.h +++ b/libnm/nm-device.h @@ -45,6 +45,7 @@ G_BEGIN_DECLS #define NM_DEVICE_DRIVER_VERSION "driver-version" #define NM_DEVICE_FIRMWARE_VERSION "firmware-version" #define NM_DEVICE_CAPABILITIES "capabilities" +#define NM_DEVICE_REAL "real" #define NM_DEVICE_MANAGED "managed" #define NM_DEVICE_AUTOCONNECT "autoconnect" #define NM_DEVICE_FIRMWARE_MISSING "firmware-missing" @@ -123,6 +124,8 @@ NMActiveConnection * nm_device_get_active_connection(NMDevice *device); const GPtrArray * nm_device_get_available_connections(NMDevice *device); const char * nm_device_get_physical_port_id (NMDevice *device); guint32 nm_device_get_mtu (NMDevice *device); +NM_AVAILABLE_IN_1_2 +gboolean nm_device_is_real (NMDevice *device); gboolean nm_device_is_software (NMDevice *device); const char * nm_device_get_product (NMDevice *device); From 0be66bb1ebb0f0ff197c38131bba42dda346df52 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 24 Sep 2014 15:13:19 -0500 Subject: [PATCH 08/17] core: add class function for device unrealization When a device is unrealized, its class-specific properties must also be cleared since the device is no longer valid. --- src/devices/nm-device-ip-tunnel.c | 29 ++++++++ src/devices/nm-device-tun.c | 21 +++++- src/devices/nm-device-vlan.c | 11 +++ src/devices/nm-device-vxlan.c | 20 ++++- src/devices/nm-device.c | 119 ++++++++++++++++++++++++++++-- src/devices/nm-device.h | 14 ++++ src/nm-manager.c | 12 ++- 7 files changed, 217 insertions(+), 9 deletions(-) diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 746fb170d0..922a3489c1 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -727,6 +727,34 @@ setup (NMDevice *device, NMPlatformLink *plink) update_properties (device); } +static void +unrealize (NMDevice *device, gboolean remove_resources) +{ + NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (device); + NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (self); + GParamSpec **properties; + guint n_properties, i; + + NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->unrealize (device, remove_resources); + + g_clear_object (&priv->parent); + priv->parent_ifindex = 0; + g_clear_pointer (&priv->local, g_free); + g_clear_pointer (&priv->remote, g_free); + priv->ttl = 0; + priv->tos = 0; + priv->path_mtu_discovery = FALSE; + g_clear_pointer (&priv->input_key, g_free); + g_clear_pointer (&priv->output_key, g_free); + priv->encap_limit = 0; + priv->flow_label = 0; + + properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (self), &n_properties); + for (i = 0; i < n_properties; i++) + g_object_notify_by_pspec (G_OBJECT (self), properties[i]); + g_free (properties); +} + static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) @@ -809,6 +837,7 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass) device_class->create_and_realize = create_and_realize; device_class->realize = realize; device_class->setup = setup; + device_class->unrealize = unrealize; device_class->connection_type = NM_SETTING_IP_TUNNEL_SETTING_NAME; diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 978c1bace3..214b227030 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -24,9 +24,9 @@ #include #include +#include "nm-default.h" #include "nm-device-tun.h" #include "nm-device-private.h" -#include "nm-default.h" #include "nm-platform.h" #include "nm-device-factory.h" #include "nm-setting-tun.h" @@ -265,6 +265,24 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) return TRUE; } +static void +unrealize (NMDevice *device, gboolean remove_resources) +{ + NMDeviceTun *self = NM_DEVICE_TUN (device); + NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); + GParamSpec **properties; + guint n_properties, i; + + NM_DEVICE_CLASS (nm_device_tun_parent_class)->unrealize (device, remove_resources); + + memset (&priv->props, 0, sizeof (NMPlatformTunProperties)); + + properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (self), &n_properties); + for (i = 0; i < n_properties; i++) + g_object_notify_by_pspec (G_OBJECT (self), properties[i]); + g_free (properties); +} + /**************************************************************/ static void @@ -349,6 +367,7 @@ nm_device_tun_class_init (NMDeviceTunClass *klass) device_class->check_connection_compatible = check_connection_compatible; device_class->create_and_realize = create_and_realize; device_class->realize = realize; + device_class->unrealize = unrealize; device_class->update_connection = update_connection; /* properties */ diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 1712a81385..0f7b0d9f80 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -256,6 +256,16 @@ create_and_realize (NMDevice *device, return TRUE; } +static void +unrealize (NMDevice *device, gboolean remove_resources) +{ + NM_DEVICE_CLASS (nm_device_vlan_parent_class)->unrealize (device, remove_resources); + + NM_DEVICE_VLAN_GET_PRIVATE (device)->vlan_id = 0; + g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_ID); + nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), NULL); +} + /******************************************************************/ static NMDeviceCapabilities @@ -663,6 +673,7 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) parent_class->create_and_realize = create_and_realize; parent_class->realize = realize; parent_class->setup = setup; + parent_class->unrealize = unrealize; parent_class->get_generic_capabilities = get_generic_capabilities; parent_class->bring_up = bring_up; parent_class->act_stage1_prepare = act_stage1_prepare; diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index 7fb55f2086..f52d81e0a7 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -22,9 +22,9 @@ #include +#include "nm-default.h" #include "nm-device-vxlan.h" #include "nm-device-private.h" -#include "nm-default.h" #include "nm-manager.h" #include "nm-platform.h" #include "nm-utils.h" @@ -142,6 +142,23 @@ setup (NMDevice *device, NMPlatformLink *plink) update_properties (device); } +static void +unrealize (NMDevice *device, gboolean remove_resources) +{ + NMDeviceVxlan *self = NM_DEVICE_VXLAN (device); + NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE (self); + GParamSpec **properties; + guint n_properties, i; + + NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->unrealize (device, remove_resources); + + memset (&priv->props, 0, sizeof (NMPlatformLnkVxlan)); + + properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (self), &n_properties); + for (i = 0; i < n_properties; i++) + g_object_notify_by_pspec (G_OBJECT (self), properties[i]); + g_free (properties); +} /**************************************************************/ @@ -231,6 +248,7 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass) device_class->link_changed = link_changed; device_class->setup = setup; + device_class->unrealize = unrealize; /* properties */ g_object_class_install_property diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index d55493fff6..f9607685fd 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1858,6 +1858,103 @@ setup (NMDevice *self, NMPlatformLink *plink) g_object_thaw_notify (G_OBJECT (self)); } +static void +unrealize (NMDevice *self, gboolean remove_resources) +{ + int ifindex; + + if (remove_resources) { + ifindex = nm_device_get_ifindex (self); + if ( ifindex > 0 + && nm_device_is_software (self)) + nm_platform_link_delete (NM_PLATFORM_GET, ifindex); + } +} + +/** + * nm_device_unrealize(): + * @self: the #NMDevice + * @remove_resources: if %TRUE, remove backing resources + * @error: location to store error, or %NULL + * + * Clears any properties that depend on backing resources (kernel devices, + * etc) and removes those resources if @remove_resources is %TRUE. + * + * Returns: %TRUE on success, %FALSE on error + */ +gboolean +nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + if (!nm_device_is_software (self) || !nm_device_is_real (self)) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_SOFTWARE, + "This device is not a software device or is not realized"); + return FALSE; + } + + priv = NM_DEVICE_GET_PRIVATE (self); + + g_return_val_if_fail (priv->iface != NULL, FALSE); + + g_object_freeze_notify (G_OBJECT (self)); + + if (NM_DEVICE_GET_CLASS (self)->unrealize) + NM_DEVICE_GET_CLASS (self)->unrealize (self, remove_resources); + + if (priv->ifindex > 0) { + priv->ifindex = 0; + g_object_notify (G_OBJECT (self), NM_DEVICE_IFINDEX); + } + priv->ip_ifindex = 0; + if (priv->ip_iface) { + g_clear_pointer (&priv->ip_iface, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE); + } + if (priv->driver_version) { + g_clear_pointer (&priv->driver_version, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER_VERSION); + } + if (priv->firmware_version) { + g_clear_pointer (&priv->firmware_version, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_FIRMWARE_VERSION); + } + if (priv->udi) { + g_clear_pointer (&priv->udi, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_UDI); + } + if (priv->hw_addr) { + g_clear_pointer (&priv->hw_addr, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS); + } + if (priv->physical_port_id) { + g_clear_pointer (&priv->physical_port_id, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_PHYSICAL_PORT_ID); + } + + g_clear_pointer (&priv->perm_hw_addr, g_free); + g_clear_pointer (&priv->initial_hw_addr, g_free); + + priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED; + g_object_notify (G_OBJECT (self), NM_DEVICE_CAPABILITIES); + + priv->real = FALSE; + g_object_notify (G_OBJECT (self), NM_DEVICE_REAL); + + g_object_thaw_notify (G_OBJECT (self)); + + nm_device_state_changed (self, + NM_DEVICE_STATE_UNMANAGED, + remove_resources ? + NM_DEVICE_STATE_REASON_USER_REQUESTED : NM_DEVICE_STATE_REASON_NOW_UNMANAGED); + + return TRUE; +} + /** * nm_device_notify_new_device_added(): * @self: the #NMDevice @@ -6471,16 +6568,21 @@ delete_on_deactivate_link_delete (gpointer user_data) DeleteOnDeactivateData *data = user_data; NMDevice *self = data->device; + _LOGD (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)", + data->ifindex, data->idle_add_id); + if (data->device) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (data->device); + gs_free_error GError *error = NULL; g_object_remove_weak_pointer (G_OBJECT (data->device), (void **) &data->device); priv->delete_on_deactivate_data = NULL; - } - _LOGD (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)", - data->ifindex, data->idle_add_id); - nm_platform_link_delete (NM_PLATFORM_GET, data->ifindex); + if (!nm_device_unrealize (data->device, TRUE, &error)) + _LOGD (LOGD_DEVICE, "delete_on_deactivate: unrealizing %d failed (%s)", data->ifindex, error->message); + } else + nm_platform_link_delete (NM_PLATFORM_GET, data->ifindex); + g_free (data); return FALSE; } @@ -6611,6 +6713,8 @@ delete_cb (NMDevice *self, GError *error, gpointer user_data) { + GError *local = NULL; + if (error) { g_dbus_method_invocation_return_gerror (context, error); nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DELETE, self, FALSE, subject, error->message); @@ -6618,9 +6722,11 @@ delete_cb (NMDevice *self, } /* Authorized */ - nm_platform_link_delete (NM_PLATFORM_GET, nm_device_get_ifindex (self)); - g_dbus_method_invocation_return_value (context, NULL); nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DELETE, self, TRUE, subject, NULL); + if (nm_device_unrealize (self, TRUE, &local)) + g_dbus_method_invocation_return_value (context, NULL); + else + g_dbus_method_invocation_take_error (context, local); } static void @@ -10219,6 +10325,7 @@ nm_device_class_init (NMDeviceClass *klass) klass->check_connection_available = check_connection_available; klass->can_unmanaged_external_down = can_unmanaged_external_down; klass->setup = setup; + klass->unrealize = unrealize; klass->is_up = is_up; klass->bring_up = bring_up; klass->take_down = take_down; diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 8931b85609..c185184a79 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -183,6 +183,17 @@ typedef struct { */ void (*setup) (NMDevice *self, NMPlatformLink *plink); + /** + * unrealize(): + * @self: the #NMDevice + * @remove_resources: if %TRUE remove backing resources + * @error: location to store error, or %NULL + * + * Clears any properties that depend on backing resources (kernel devices, + * etc) and removes those resources if @remove_resources is %TRUE. + */ + void (*unrealize) (NMDevice *self, gboolean remove_resources); + /* Hardware state (IFF_UP) */ gboolean (*can_unmanaged_external_down) (NMDevice *self); gboolean (*is_up) (NMDevice *self); @@ -466,6 +477,9 @@ gboolean nm_device_create_and_realize (NMDevice *self, NMConnection *connection, NMDevice *parent, GError **error); +gboolean nm_device_unrealize (NMDevice *device, + gboolean remove_resources, + GError **error); gboolean nm_device_get_autoconnect (NMDevice *device); diff --git a/src/nm-manager.c b/src/nm-manager.c index 4bc0dc3413..5c6bdb2a14 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1949,10 +1949,20 @@ _platform_link_cb_idle (PlatformLinkCbData *data) platform_link_added (self, data->ifindex, &pllink); } else { NMDevice *device; + GError *error = NULL; device = nm_manager_get_device_by_ifindex (self, data->ifindex); - if (device) + if (device) { + if (nm_device_is_software (device)) { + if (!nm_device_unrealize (device, FALSE, &error)) { + nm_log_warn (LOGD_DEVICE, "(%s): failed to unrealize: %s", + nm_device_get_iface (device), + error->message); + g_clear_error (&error); + } + } remove_device (self, device, FALSE, TRUE); + } } g_object_remove_weak_pointer (G_OBJECT (self), (gpointer *) &data->self); } From 8bbc6899b959079c559bf2c46540ec859b1821ca Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 1 Dec 2015 23:21:25 +0100 Subject: [PATCH 09/17] ip-tunnel: refactor update_properties in NMDeviceIPTunnel let update_properties_from_ifindex() also support clearing the properties to make it usable from unrealize(). --- src/devices/nm-device-ip-tunnel.c | 66 +++++++++++++++++++------------ 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 922a3489c1..b9b3d20e45 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -113,7 +113,7 @@ address_equal_pn (int family, const char *a, const void *b) } static void -update_properties (NMDevice *device) +update_properties_from_ifindex (NMDevice *device, int ifindex) { NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (device); NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (self); @@ -127,10 +127,36 @@ update_properties (NMDevice *device) guint32 flow_label = 0; char *key; + if (ifindex <= 0) { + if (priv->parent || priv->parent_ifindex) { + g_clear_object (&priv->parent); + priv->parent_ifindex = 0; + g_object_notify (object, NM_DEVICE_IP_TUNNEL_PARENT); + } + if (priv->local) { + g_clear_pointer (&priv->local, g_free); + g_object_notify (object, NM_DEVICE_IP_TUNNEL_LOCAL); + } + if (priv->remote) { + g_clear_pointer (&priv->remote, g_free); + g_object_notify (object, NM_DEVICE_IP_TUNNEL_REMOTE); + } + if (priv->input_key) { + g_clear_pointer (&priv->input_key, g_free); + g_object_notify (object, NM_DEVICE_IP_TUNNEL_INPUT_KEY); + } + if (priv->output_key) { + g_clear_pointer (&priv->output_key, g_free); + g_object_notify (object, NM_DEVICE_IP_TUNNEL_OUTPUT_KEY); + } + + goto out; + } + if (priv->mode == NM_IP_TUNNEL_MODE_GRE) { const NMPlatformLnkGre *lnk; - lnk = nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL); + lnk = nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL); if (!lnk) { _LOGW (LOGD_HW, "could not read %s properties", "gre"); return; @@ -175,7 +201,7 @@ update_properties (NMDevice *device) } else if (priv->mode == NM_IP_TUNNEL_MODE_SIT) { const NMPlatformLnkSit *lnk; - lnk = nm_platform_link_get_lnk_sit (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL); + lnk = nm_platform_link_get_lnk_sit (NM_PLATFORM_GET, ifindex, NULL); if (!lnk) { _LOGW (LOGD_HW, "could not read %s properties", "sit"); return; @@ -190,7 +216,7 @@ update_properties (NMDevice *device) } else if (priv->mode == NM_IP_TUNNEL_MODE_IPIP) { const NMPlatformLnkIpIp *lnk; - lnk = nm_platform_link_get_lnk_ipip (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL); + lnk = nm_platform_link_get_lnk_ipip (NM_PLATFORM_GET, ifindex, NULL); if (!lnk) { _LOGW (LOGD_HW, "could not read %s properties", "ipip"); return; @@ -206,7 +232,7 @@ update_properties (NMDevice *device) || priv->mode == NM_IP_TUNNEL_MODE_IP6IP6) { const NMPlatformLnkIp6Tnl *lnk; - lnk = nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL); + lnk = nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL); if (!lnk) { _LOGW (LOGD_HW, "could not read %s properties", "ip6tnl"); return; @@ -261,6 +287,8 @@ update_properties (NMDevice *device) } } +out: + if (priv->ttl != ttl) { priv->ttl = ttl; g_object_notify (object, NM_DEVICE_IP_TUNNEL_TTL); @@ -287,6 +315,12 @@ update_properties (NMDevice *device) } } +static void +update_properties (NMDevice *device) +{ + update_properties_from_ifindex (device, nm_device_get_ifindex (device)); +} + static void link_changed (NMDevice *device, NMPlatformLink *info) { @@ -730,29 +764,9 @@ setup (NMDevice *device, NMPlatformLink *plink) static void unrealize (NMDevice *device, gboolean remove_resources) { - NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (device); - NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (self); - GParamSpec **properties; - guint n_properties, i; - NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->unrealize (device, remove_resources); - g_clear_object (&priv->parent); - priv->parent_ifindex = 0; - g_clear_pointer (&priv->local, g_free); - g_clear_pointer (&priv->remote, g_free); - priv->ttl = 0; - priv->tos = 0; - priv->path_mtu_discovery = FALSE; - g_clear_pointer (&priv->input_key, g_free); - g_clear_pointer (&priv->output_key, g_free); - priv->encap_limit = 0; - priv->flow_label = 0; - - properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (self), &n_properties); - for (i = 0; i < n_properties; i++) - g_object_notify_by_pspec (G_OBJECT (self), properties[i]); - g_free (properties); + update_properties_from_ifindex (device, 0); } static void From 2b51ecc9e53b8de87644cc30d1f8873243b4aa7d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 1 Dec 2015 23:39:47 +0100 Subject: [PATCH 10/17] ip-tunnel: let update_properties_from_ifindex() clear properties on failure --- src/devices/nm-device-ip-tunnel.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index b9b3d20e45..056135fbfd 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -128,6 +128,7 @@ update_properties_from_ifindex (NMDevice *device, int ifindex) char *key; if (ifindex <= 0) { +clear: if (priv->parent || priv->parent_ifindex) { g_clear_object (&priv->parent); priv->parent_ifindex = 0; @@ -159,7 +160,7 @@ update_properties_from_ifindex (NMDevice *device, int ifindex) lnk = nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL); if (!lnk) { _LOGW (LOGD_HW, "could not read %s properties", "gre"); - return; + goto clear; } parent_ifindex = lnk->parent_ifindex; @@ -204,7 +205,7 @@ update_properties_from_ifindex (NMDevice *device, int ifindex) lnk = nm_platform_link_get_lnk_sit (NM_PLATFORM_GET, ifindex, NULL); if (!lnk) { _LOGW (LOGD_HW, "could not read %s properties", "sit"); - return; + goto clear; } parent_ifindex = lnk->parent_ifindex; @@ -219,7 +220,7 @@ update_properties_from_ifindex (NMDevice *device, int ifindex) lnk = nm_platform_link_get_lnk_ipip (NM_PLATFORM_GET, ifindex, NULL); if (!lnk) { _LOGW (LOGD_HW, "could not read %s properties", "ipip"); - return; + goto clear; } parent_ifindex = lnk->parent_ifindex; @@ -235,7 +236,7 @@ update_properties_from_ifindex (NMDevice *device, int ifindex) lnk = nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL); if (!lnk) { _LOGW (LOGD_HW, "could not read %s properties", "ip6tnl"); - return; + goto clear; } parent_ifindex = lnk->parent_ifindex; From b7eb622c247656595a04aa6a32ef17ba4cf15a2a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 1 Dec 2015 23:43:13 +0100 Subject: [PATCH 11/17] ip-tunnel: don't update_properties() during update_connection()/check_connection_compatible() The device properties are obtained via netlink, thus we get proper platform notifications whenever they change. So the device properties always reflect the platform state and there is no need to refresh it during update_connection()/check_connection_compatible(). --- src/devices/nm-device-ip-tunnel.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 056135fbfd..4cf2a689f6 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -370,8 +370,6 @@ update_connection (NMDevice *device, NMConnection *connection) nm_connection_add_setting (connection, (NMSetting *) s_ip_tunnel); } - update_properties (device); - if (nm_setting_ip_tunnel_get_mode (s_ip_tunnel) != priv->mode) g_object_set (G_OBJECT (s_ip_tunnel), NM_SETTING_IP_TUNNEL_MODE, priv->mode, NULL); @@ -505,8 +503,6 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (!s_ip_tunnel) return FALSE; - update_properties (device); - /* Check parent interface; could be an interface name or a UUID */ parent = nm_setting_ip_tunnel_get_parent (s_ip_tunnel); if (parent) { From 4dbaac4ba24ebc8b257fffe5197cc8e362804a58 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 24 Sep 2014 16:58:07 -0500 Subject: [PATCH 12/17] core: create devices first and realize them later Unrealized devices aren't backed by kernel resources and so won't know all of their attributes. That means three things: 1) they must update their attributes when they become realized 2) they must clear those attributes when unrealized 3) they must be looser in checking compatible connections until they are realized This requires that the setup() function be split into two parts, start & finish, because finish must be run after add_device() Also, we can simplify whether to pay attention to 'recheck-assume', which is now dependent on priv->is_nm_owned, because the only case where NM should *not* listen for the 'recheck-assume' signal is when the device is a software device created by NM itself. That logic was previously spread across the callers of add_device() but is now consolidated into nm-manager.c::device_realized() and nm-device.c::nm_device_create_and_realize(). --- src/devices/nm-device-bridge.c | 2 +- src/devices/nm-device-ethernet.c | 6 +- src/devices/nm-device-generic.c | 6 +- src/devices/nm-device-infiniband.c | 2 +- src/devices/nm-device-ip-tunnel.c | 64 +++--- src/devices/nm-device-tun.c | 42 ++-- src/devices/nm-device-vlan.c | 34 +-- src/devices/nm-device-vxlan.c | 6 +- src/devices/nm-device.c | 117 ++++++----- src/devices/nm-device.h | 23 +- src/devices/wifi/nm-device-wifi.c | 6 +- src/nm-active-connection.c | 8 +- src/nm-manager.c | 323 ++++++++++++++++------------- 13 files changed, 358 insertions(+), 281 deletions(-) diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index b54b2e0a01..2a2f234c16 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -97,7 +97,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) return FALSE; mac_address = nm_setting_bridge_get_mac_address (s_bridge); - if (mac_address) { + if (mac_address && nm_device_is_real (device)) { const char *hw_addr; hw_addr = nm_device_get_hw_address (device); diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index fe45536609..6f0c3e8881 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -305,9 +305,9 @@ nm_device_ethernet_init (NMDeviceEthernet *self) } static void -setup (NMDevice *device, NMPlatformLink *plink) +setup_start (NMDevice *device, NMPlatformLink *plink) { - NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->setup (device, plink); + NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->setup_start (device, plink); g_object_notify (G_OBJECT (device), NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS); } @@ -1716,7 +1716,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) object_class->set_property = set_property; parent_class->get_generic_capabilities = get_generic_capabilities; - parent_class->setup = setup; + parent_class->setup_start = setup_start; parent_class->check_connection_compatible = check_connection_compatible; parent_class->complete_connection = complete_connection; parent_class->new_default_connection = new_default_connection; diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c index 37eae35bdc..70ae48c678 100644 --- a/src/devices/nm-device-generic.c +++ b/src/devices/nm-device-generic.c @@ -61,13 +61,13 @@ get_type_description (NMDevice *device) } static void -setup (NMDevice *device, NMPlatformLink *plink) +setup_start (NMDevice *device, NMPlatformLink *plink) { NMDeviceGeneric *self = NM_DEVICE_GENERIC (device); NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (self); int ifindex; - NM_DEVICE_CLASS (nm_device_generic_parent_class)->setup (device, plink); + NM_DEVICE_CLASS (nm_device_generic_parent_class)->setup_start (device, plink); g_clear_pointer (&priv->type_description, g_free); ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self)); @@ -203,7 +203,7 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass) object_class->get_property = get_property; object_class->set_property = set_property; - parent_class->setup = setup; + parent_class->setup_start = setup_start; parent_class->get_generic_capabilities = get_generic_capabilities; parent_class->get_type_description = get_type_description; parent_class->check_connection_compatible = check_connection_compatible; diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index a647245f63..e0c3c62d76 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -149,7 +149,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (!s_infiniband) return FALSE; - if (s_infiniband) { + if (nm_device_is_real (device)) { const char *mac; mac = nm_setting_infiniband_get_mac_address (s_infiniband); diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 4cf2a689f6..64c3a52146 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -503,41 +503,43 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (!s_ip_tunnel) return FALSE; - /* Check parent interface; could be an interface name or a UUID */ - parent = nm_setting_ip_tunnel_get_parent (s_ip_tunnel); - if (parent) { - if (!match_parent (priv->parent, parent)) - return FALSE; - } - if (nm_setting_ip_tunnel_get_mode (s_ip_tunnel) != priv->mode) return FALSE; - if (!address_equal_pp (priv->addr_family, - nm_setting_ip_tunnel_get_local (s_ip_tunnel), - priv->local)) - return FALSE; + if (nm_device_is_real (device)) { + /* Check parent interface; could be an interface name or a UUID */ + parent = nm_setting_ip_tunnel_get_parent (s_ip_tunnel); + if (parent) { + if (!match_parent (priv->parent, parent)) + return FALSE; + } - if (!address_equal_pp (priv->addr_family, - nm_setting_ip_tunnel_get_remote (s_ip_tunnel), - priv->remote)) - return FALSE; - - if (nm_setting_ip_tunnel_get_ttl (s_ip_tunnel) != priv->ttl) - return FALSE; - - if (nm_setting_ip_tunnel_get_tos (s_ip_tunnel) != priv->tos) - return FALSE; - - if (priv->addr_family == AF_INET) { - if (nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel) != priv->path_mtu_discovery) - return FALSE; - } else { - if (nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel) != priv->encap_limit) + if (!address_equal_pp (priv->addr_family, + nm_setting_ip_tunnel_get_local (s_ip_tunnel), + priv->local)) return FALSE; - if (nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel) != priv->flow_label) + if (!address_equal_pp (priv->addr_family, + nm_setting_ip_tunnel_get_remote (s_ip_tunnel), + priv->remote)) return FALSE; + + if (nm_setting_ip_tunnel_get_ttl (s_ip_tunnel) != priv->ttl) + return FALSE; + + if (nm_setting_ip_tunnel_get_tos (s_ip_tunnel) != priv->tos) + return FALSE; + + if (priv->addr_family == AF_INET) { + if (nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel) != priv->path_mtu_discovery) + return FALSE; + } else { + if (nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel) != priv->encap_limit) + return FALSE; + + if (nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel) != priv->flow_label) + return FALSE; + } } return TRUE; @@ -751,9 +753,9 @@ create_and_realize (NMDevice *device, } static void -setup (NMDevice *device, NMPlatformLink *plink) +setup_start (NMDevice *device, NMPlatformLink *plink) { - NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->setup (device, plink); + NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->setup_start (device, plink); update_properties (device); } @@ -847,7 +849,7 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass) device_class->check_connection_compatible = check_connection_compatible; device_class->create_and_realize = create_and_realize; device_class->realize = realize; - device_class->setup = setup; + device_class->setup_start = setup_start; device_class->unrealize = unrealize; device_class->connection_type = NM_SETTING_IP_TUNNEL_SETTING_NAME; diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 214b227030..46d4d391e8 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -226,6 +226,13 @@ realize (NMDevice *device, NMPlatformLink *plink, GError **error) return TRUE; } +static void +setup_start (NMDevice *device, NMPlatformLink *plink) +{ + NM_DEVICE_CLASS (nm_device_tun_parent_class)->setup_start (device, plink); + reload_tun_properties (device); +} + static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection) { @@ -235,8 +242,6 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) NMSettingTun *s_tun; gint64 user, group; - reload_tun_properties (self); - if (!NM_DEVICE_CLASS (nm_device_tun_parent_class)->check_connection_compatible (device, connection)) return FALSE; @@ -244,23 +249,25 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (!s_tun) return FALSE; - mode = tun_mode_from_string (priv->mode); - if (mode != nm_setting_tun_get_mode (s_tun)) - return FALSE; + if (nm_device_is_real (device)) { + mode = tun_mode_from_string (priv->mode); + if (mode != nm_setting_tun_get_mode (s_tun)) + return FALSE; - user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1); - group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1); + user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1); + group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1); - if (user != priv->props.owner) - return FALSE; - if (group != priv->props.group) - return FALSE; - if (nm_setting_tun_get_pi (s_tun) == priv->props.no_pi) - return FALSE; - if (nm_setting_tun_get_vnet_hdr (s_tun) != priv->props.vnet_hdr) - return FALSE; - if (nm_setting_tun_get_multi_queue (s_tun) != priv->props.multi_queue) - return FALSE; + if (user != priv->props.owner) + return FALSE; + if (group != priv->props.group) + return FALSE; + if (nm_setting_tun_get_pi (s_tun) == priv->props.no_pi) + return FALSE; + if (nm_setting_tun_get_vnet_hdr (s_tun) != priv->props.vnet_hdr) + return FALSE; + if (nm_setting_tun_get_multi_queue (s_tun) != priv->props.multi_queue) + return FALSE; + } return TRUE; } @@ -367,6 +374,7 @@ nm_device_tun_class_init (NMDeviceTunClass *klass) device_class->check_connection_compatible = check_connection_compatible; device_class->create_and_realize = create_and_realize; device_class->realize = realize; + device_class->setup_start = setup_start; device_class->unrealize = unrealize; device_class->update_connection = update_connection; diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 0f7b0d9f80..58263c5d0c 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -148,12 +148,12 @@ nm_device_vlan_set_parent (NMDeviceVlan *self, NMDevice *parent) } static void -setup (NMDevice *device, NMPlatformLink *plink) +setup_start (NMDevice *device, NMPlatformLink *plink) { NMDeviceVlan *self = NM_DEVICE_VLAN (device); NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self); - NM_DEVICE_CLASS (nm_device_vlan_parent_class)->setup (device, plink); + NM_DEVICE_CLASS (nm_device_vlan_parent_class)->setup_start (device, plink); _LOGI (LOGD_HW | LOGD_VLAN, "VLAN ID %d with parent %s", priv->vlan_id, nm_device_get_iface (priv->parent)); @@ -311,6 +311,9 @@ notify_new_device_added (NMDevice *device, NMDevice *new_device) if (priv->parent) return; + if (!nm_device_is_real (device)) + return; + plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink); if (!plnk) { _LOGW (LOGD_VLAN, "failed to get VLAN interface info while checking added component."); @@ -397,18 +400,21 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (!s_vlan) return FALSE; - if (nm_setting_vlan_get_id (s_vlan) != priv->vlan_id) - return FALSE; + /* Before the device is realized some properties will not be set */ + if (nm_device_is_real (device)) { + if (nm_setting_vlan_get_id (s_vlan) != priv->vlan_id) + return FALSE; - /* Check parent interface; could be an interface name or a UUID */ - parent = nm_setting_vlan_get_parent (s_vlan); - if (parent) { - if (!match_parent (NM_DEVICE_VLAN (device), parent)) - return FALSE; - } else { - /* Parent could be a MAC address in an NMSettingWired */ - if (!match_hwaddr (device, connection, TRUE)) - return FALSE; + /* Check parent interface; could be an interface name or a UUID */ + parent = nm_setting_vlan_get_parent (s_vlan); + if (parent) { + if (!match_parent (NM_DEVICE_VLAN (device), parent)) + return FALSE; + } else { + /* Parent could be a MAC address in an NMSettingWired */ + if (!match_hwaddr (device, connection, TRUE)) + return FALSE; + } } /* Ensure the interface name matches. If not specified we assume a match @@ -672,7 +678,7 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) parent_class->create_and_realize = create_and_realize; parent_class->realize = realize; - parent_class->setup = setup; + parent_class->setup_start = setup_start; parent_class->unrealize = unrealize; parent_class->get_generic_capabilities = get_generic_capabilities; parent_class->bring_up = bring_up; diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index f52d81e0a7..65b3950dd4 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -133,11 +133,11 @@ link_changed (NMDevice *device, NMPlatformLink *info) } static void -setup (NMDevice *device, NMPlatformLink *plink) +setup_start (NMDevice *device, NMPlatformLink *plink) { g_assert (plink->type == NM_LINK_TYPE_VXLAN); - NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->setup (device, plink); + NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->setup_start (device, plink); update_properties (device); } @@ -247,7 +247,7 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass) object_class->get_property = get_property; device_class->link_changed = link_changed; - device_class->setup = setup; + device_class->setup_start = setup_start; device_class->unrealize = unrealize; /* properties */ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index f9607685fd..63df7b71aa 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -556,7 +556,7 @@ nm_device_get_iface (NMDevice *self) int nm_device_get_ifindex (NMDevice *self) { - g_return_val_if_fail (self != NULL, 0); + g_return_val_if_fail (NM_IS_DEVICE (self), 0); return NM_DEVICE_GET_PRIVATE (self)->ifindex; } @@ -1152,8 +1152,7 @@ nm_device_release_one_slave (NMDevice *self, NMDevice *slave, gboolean configure static gboolean can_unmanaged_external_down (NMDevice *self) { - return nm_device_is_software (self) - && !nm_device_get_is_nm_owned (self); + return nm_device_is_software (self) && !NM_DEVICE_GET_PRIVATE (self)->is_nm_owned; } /** @@ -1385,23 +1384,31 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier) } static void -device_set_master (NMDevice *self, int ifindex) +device_recheck_slave_status (NMDevice *self, NMPlatformLink *plink) { - NMDevice *master; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - master = nm_manager_get_device_by_ifindex (nm_manager_get (), ifindex); - if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) { - g_clear_object (&priv->master); - priv->master = g_object_ref (master); - nm_device_master_add_slave (master, self, FALSE); - } else if (master) { - _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring", - nm_device_get_iface (master)); - } else { - _LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s", - ifindex, - nm_platform_link_get_name (NM_PLATFORM_GET, ifindex)); + g_return_if_fail (plink != NULL); + + if (priv->enslaved && plink->master != nm_device_get_ifindex (priv->master)) + nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + + if (plink->master && !priv->enslaved) { + NMDevice *master; + + master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master); + if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) { + g_clear_object (&priv->master); + priv->master = g_object_ref (master); + nm_device_master_add_slave (master, self, FALSE); + } else if (master) { + _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring", + nm_device_get_iface (master)); + } else { + _LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s", + plink->master, + nm_platform_link_get_name (NM_PLATFORM_GET, plink->master)); + } } } @@ -1449,6 +1456,12 @@ device_link_changed (NMDevice *self) g_object_notify (G_OBJECT (self), NM_DEVICE_MTU); } + if (info.driver && g_strcmp0 (priv->driver, info.driver) != 0) { + g_free (priv->driver); + priv->driver = g_strdup (info.driver); + g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER); + } + if (info.name[0] && strcmp (priv->iface, info.name) != 0) { _LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'", priv->ifindex, priv->iface, info.name); @@ -1471,15 +1484,6 @@ device_link_changed (NMDevice *self) nm_device_emit_recheck_auto_activate (self); } - /* Update slave status for external changes */ - if (priv->enslaved && info.master != nm_device_get_ifindex (priv->master)) - nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); - if (info.master && !priv->enslaved) { - device_set_master (self, info.master); - if (priv->master) - nm_device_enslave_slave (priv->master, self, NULL); - } - if (priv->rdisc && nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &token_iid)) { _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); if (nm_rdisc_set_iid (priv->rdisc, token_iid)) @@ -1562,6 +1566,7 @@ device_link_changed (NMDevice *self) if (emit_link_initialized) g_signal_emit (self, signals[LINK_INITIALIZED], 0); + device_recheck_slave_status (self, &info); return G_SOURCE_REMOVE; } @@ -1635,7 +1640,8 @@ link_changed (NMDevice *self, NMPlatformLink *info) * @plink: an existing platform link or %NULL * @error: location to store error, or %NULL * - * Initializes and sets up the device using existing backing resources. + * Initializes and sets up the device using existing backing resources. Before + * the device is ready for use nm_device_setup_finish() must be called. * * Returns: %TRUE on success, %FALSE on error */ @@ -1648,7 +1654,7 @@ nm_device_realize (NMDevice *self, NMPlatformLink *plink, GError **error) return FALSE; } - NM_DEVICE_GET_CLASS (self)->setup (self, plink); + NM_DEVICE_GET_CLASS (self)->setup_start (self, plink); return TRUE; } @@ -1671,15 +1677,20 @@ nm_device_create_and_realize (NMDevice *self, NMDevice *parent, GError **error) { + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPlatformLink plink = { .type = NM_LINK_TYPE_UNKNOWN }; + /* Must be set before device is realized */ + priv->is_nm_owned = !nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface); + /* Create any resources the device needs */ if (NM_DEVICE_GET_CLASS (self)->create_and_realize) { if (!NM_DEVICE_GET_CLASS (self)->create_and_realize (self, connection, parent, &plink, error)) return FALSE; } - NM_DEVICE_GET_CLASS (self)->setup (self, (plink.type != NM_LINK_TYPE_UNKNOWN) ? &plink : NULL); + NM_DEVICE_GET_CLASS (self)->setup_start (self, (plink.type != NM_LINK_TYPE_UNKNOWN) ? &plink : NULL); + nm_device_setup_finish (self, (plink.type != NM_LINK_TYPE_UNKNOWN) ? &plink : NULL); g_return_val_if_fail (nm_device_check_connection_compatible (self, connection), TRUE); return TRUE; @@ -1742,7 +1753,7 @@ check_carrier (NMDevice *self) } static void -setup (NMDevice *self, NMPlatformLink *plink) +setup_start (NMDevice *self, NMPlatformLink *plink) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); static guint32 id = 0; @@ -1751,6 +1762,7 @@ setup (NMDevice *self, NMPlatformLink *plink) g_return_if_fail (priv->ip_ifindex <= 0); g_return_if_fail (priv->ip_iface == NULL); + /* Balanced by a thaw in nm_device_setup_finish() */ g_object_freeze_notify (G_OBJECT (self)); if (plink) { @@ -1759,7 +1771,7 @@ setup (NMDevice *self, NMPlatformLink *plink) } if (priv->ifindex > 0) { - _LOGD (LOGD_DEVICE, "setup(): %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), priv->ifindex); + _LOGD (LOGD_DEVICE, "setup_start(): %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), priv->ifindex); priv->physical_port_id = nm_platform_link_get_physical_port_id (NM_PLATFORM_GET, priv->ifindex); g_object_notify (G_OBJECT (self), NM_DEVICE_PHYSICAL_PORT_ID); @@ -1844,17 +1856,29 @@ setup (NMDevice *self, NMPlatformLink *plink) g_object_notify (G_OBJECT (self), NM_DEVICE_CAPABILITIES); - /* Enslave ourselves */ - if (priv->ifindex > 0) { - int master = nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex); - - if (master > 0) - device_set_master (self, master); - } - priv->real = TRUE; +} + +static void +setup_finish (NMDevice *self, NMPlatformLink *plink) +{ + if (plink) { + update_device_from_platform_link (self, plink); + device_recheck_slave_status (self, plink); + } +} + +void +nm_device_setup_finish (NMDevice *self, NMPlatformLink *plink) +{ + NM_DEVICE_GET_CLASS (self)->setup_finish (self, plink); + + NM_DEVICE_GET_PRIVATE (self)->real = TRUE; g_object_notify (G_OBJECT (self), NM_DEVICE_REAL); + nm_device_recheck_available_connections (self); + + /* Balanced by a freeze in setup_start() */ g_object_thaw_notify (G_OBJECT (self)); } @@ -2810,7 +2834,8 @@ nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection) static gboolean nm_device_can_assume_connections (NMDevice *self) { - return !!NM_DEVICE_GET_CLASS (self)->update_connection; + return !!NM_DEVICE_GET_CLASS (self)->update_connection + && !NM_DEVICE_GET_PRIVATE (self)->is_nm_owned; } /** @@ -6547,14 +6572,6 @@ nm_device_get_is_nm_owned (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->is_nm_owned; } -void -nm_device_set_nm_owned (NMDevice *self) -{ - g_return_if_fail (NM_IS_DEVICE (self)); - - NM_DEVICE_GET_PRIVATE (self)->is_nm_owned = TRUE; -} - /* * delete_on_deactivate_link_delete * @@ -8461,6 +8478,7 @@ _nm_device_check_connection_available (NMDevice *self, && nm_device_get_unmanaged (self, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT)) return FALSE; if ( state < NM_DEVICE_STATE_DISCONNECTED + && !nm_device_is_software (self) && ( ( !NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER) && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) || ( NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER) @@ -10324,7 +10342,8 @@ nm_device_class_init (NMDeviceClass *klass) klass->check_connection_compatible = check_connection_compatible; klass->check_connection_available = check_connection_available; klass->can_unmanaged_external_down = can_unmanaged_external_down; - klass->setup = setup; + klass->setup_start = setup_start; + klass->setup_finish = setup_finish; klass->unrealize = unrealize; klass->is_up = is_up; klass->bring_up = bring_up; diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index c185184a79..268ed06294 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -174,14 +174,28 @@ typedef struct { GError **error); /** - * setup(): + * setup_start(): * @self: the #NMDevice * @plink: the #NMPlatformLink if backed by a kernel netdevice * * Update the device from backing resource properties (like hardware - * addresses, carrier states, driver/firmware info, etc). + * addresses, carrier states, driver/firmware info, etc). This function + * should only change properties for this device, and should not perform + * any tasks that affect other interfaces (like master/slave or parent/child + * stuff). */ - void (*setup) (NMDevice *self, NMPlatformLink *plink); + void (*setup_start) (NMDevice *self, NMPlatformLink *plink); + + /** + * setup_finish(): + * @self: the #NMDevice + * @plink: the #NMPlatformLink if backed by a kernel netdevice + * + * Update the device's master/slave or parent/child relationships from + * backing resource properties. After this function finishes, the device + * is ready for network connectivity. + */ + void (*setup_finish) (NMDevice *self, NMPlatformLink *plink); /** * unrealize(): @@ -466,7 +480,6 @@ void nm_device_set_unmanaged_initial (NMDevice *device, gboolean unmanaged); gboolean nm_device_get_is_nm_owned (NMDevice *device); -void nm_device_set_nm_owned (NMDevice *device); gboolean nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps); @@ -477,6 +490,8 @@ gboolean nm_device_create_and_realize (NMDevice *self, NMConnection *connection, NMDevice *parent, GError **error); +void nm_device_setup_finish (NMDevice *self, + NMPlatformLink *plink); gboolean nm_device_unrealize (NMDevice *device, gboolean remove_resources, GError **error); diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 70bb6d628a..bb73e8c456 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -424,9 +424,9 @@ periodic_update_cb (gpointer user_data) } static void -setup (NMDevice *device, NMPlatformLink *plink) +setup_start (NMDevice *device, NMPlatformLink *plink) { - NM_DEVICE_CLASS (nm_device_wifi_parent_class)->setup (device, plink); + NM_DEVICE_CLASS (nm_device_wifi_parent_class)->setup_start (device, plink); g_object_notify (G_OBJECT (device), NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS); } @@ -3040,7 +3040,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) object_class->dispose = dispose; object_class->finalize = finalize; - parent_class->setup = setup; + parent_class->setup_start = setup_start; parent_class->bring_up = bring_up; parent_class->can_auto_connect = can_auto_connect; parent_class->is_available = is_available; diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index aa892cc711..609e645e0a 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -598,17 +598,17 @@ master_state_cb (NMActiveConnection *master, gpointer user_data) { NMActiveConnection *self = NM_ACTIVE_CONNECTION (user_data); - NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); NMActiveConnectionState master_state = nm_active_connection_get_state (master); + NMDevice *master_device = nm_active_connection_get_device (master); check_master_ready (self); _LOGD ("master ActiveConnection [%p] state now '%s' (%d)", master, state_to_string (master_state), master_state); - if ( master_state >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING - && !priv->master_ready) { - /* Master failed without ever creating its device */ + if ( master_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATING + && (!master_device || !nm_device_is_real (master_device))) { + /* Master failed without ever creating or realizing its device */ if (NM_ACTIVE_CONNECTION_GET_CLASS (self)->master_failed) NM_ACTIVE_CONNECTION_GET_CLASS (self)->master_failed (self); } diff --git a/src/nm-manager.c b/src/nm-manager.c index 5c6bdb2a14..eb0ec6fffc 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -58,7 +58,7 @@ #include "nmdbus-manager.h" #include "nmdbus-device.h" -static void add_device (NMManager *self, NMDevice *device, gboolean try_assume); +static void add_device (NMManager *self, NMDevice *device); static NMActiveConnection *_new_active_connection (NMManager *self, NMConnection *connection, @@ -487,8 +487,11 @@ find_device_by_ip_iface (NMManager *self, const gchar *iface) g_return_val_if_fail (iface != NULL, NULL); for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) { - if (g_strcmp0 (nm_device_get_ip_iface (NM_DEVICE (iter->data)), iface) == 0) - return NM_DEVICE (iter->data); + NMDevice *candidate = iter->data; + + if ( nm_device_is_real (candidate) + && g_strcmp0 (nm_device_get_ip_iface (candidate), iface) == 0) + return candidate; } return NULL; } @@ -840,6 +843,8 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection) NMDevice *parent, *first_compatible = NULL; GSList *iter; + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + factory = nm_device_factory_manager_find_factory_for_connection (connection); if (!factory) return NULL; @@ -849,7 +854,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection) return NULL; /* Try as an interface name */ - parent = find_device_by_ip_iface (self, parent_name); + parent = find_device_by_iface (self, parent_name); if (parent) return parent; @@ -964,10 +969,9 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMDeviceFactory *factory; - GSList *iter; - char *iface = NULL; + GSList *connections, *iter; + gs_free char *iface = NULL; NMDevice *device = NULL, *parent = NULL; - gboolean nm_owned = FALSE; g_return_val_if_fail (NM_IS_MANAGER (self), NULL); g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); @@ -989,7 +993,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, "interface name '%s' already created", iface); - goto out; + return NULL; } } @@ -1003,79 +1007,61 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError NM_MANAGER_ERROR_FAILED, "NetworkManager plugin for '%s' unavailable", nm_connection_get_connection_type (connection)); - goto out; + return NULL; } - nm_owned = !nm_platform_link_get_by_ifname (NM_PLATFORM_GET, iface); - device = nm_device_factory_create_device (factory, iface, NULL, connection, NULL, error); - if (device) { + if (!device) + return NULL; + + add_device (self, device); + + /* Add device takes a reference that NMManager still owns, so it's + * safe to unref here and still return @device. + */ + g_object_unref (device); + + /* Create backing resources if the device has any autoconnect connections */ + connections = nm_settings_get_connections (priv->settings); + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *candidate = iter->data; + NMSettingConnection *s_con; + + if (!nm_device_check_connection_compatible (device, candidate)) + continue; + + s_con = nm_connection_get_setting_connection (candidate); + g_assert (s_con); + if (!nm_setting_connection_get_autoconnect (s_con)) + continue; + + /* Create any backing resources the device needs */ if (!nm_device_create_and_realize (device, connection, parent, error)) { - g_clear_object (&device); - goto out; + remove_device (self, device, FALSE, TRUE); + device = NULL; } - - if (nm_owned) - nm_device_set_nm_owned (device); - - /* If it was created by NM there's no connection to assume, but if it - * previously existed there might be one. - */ - add_device (self, device, !nm_owned); - - /* Add device takes a reference that NMManager still owns, so it's - * safe to unref here and still return @device. - */ - g_object_unref (device); + break; } -out: - g_free (iface); return device; } -static void -system_create_virtual_devices (NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter, *connections; - - nm_log_dbg (LOGD_CORE, "creating virtual devices..."); - - connections = nm_settings_get_connections (priv->settings); - for (iter = connections; iter; iter = g_slist_next (iter)) { - NMConnection *connection = iter->data; - - /* We only create a virtual interface if the connection can autoconnect */ - if ( nm_connection_is_virtual (connection) - && nm_settings_connection_can_autoconnect (NM_SETTINGS_CONNECTION (connection))) - system_create_virtual_device (self, connection, NULL); - } - g_slist_free (connections); -} - static void connection_added (NMSettings *settings, - NMSettingsConnection *settings_connection, + NMConnection *connection, NMManager *manager) { - NMConnection *connection = NM_CONNECTION (settings_connection); - - if (nm_connection_is_virtual (connection)) { - NMSettingConnection *s_con = nm_connection_get_setting_connection (connection); - - g_assert (s_con); - if (nm_setting_connection_get_autoconnect (s_con)) - system_create_virtual_device (manager, connection, NULL); - } + if (nm_connection_is_virtual (connection)) + system_create_virtual_device (manager, connection, NULL); } static void connection_changed (NMSettings *settings, - NMSettingsConnection *connection, + NMConnection *connection, NMManager *manager) { - /* FIXME: Some virtual devices may need to be updated in the future. */ + if (nm_connection_is_virtual (connection)) + system_create_virtual_device (manager, connection, NULL); } static void @@ -1590,17 +1576,25 @@ assume_connection (NMManager *self, NMDevice *device, NMSettingsConnection *conn return TRUE; } +static gboolean +can_start_device (NMManager *self, NMDevice *device) +{ + return nm_device_is_real (device) + && !manager_sleeping (self) + && !nm_device_get_unmanaged (device, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT); +} + static gboolean recheck_assume_connection (NMDevice *device, gpointer user_data) { NMManager *self = NM_MANAGER (user_data); NMSettingsConnection *connection; - gboolean was_unmanaged = FALSE, success, generated; + gboolean was_unmanaged = FALSE, success, generated = FALSE; NMDeviceState state; - if (manager_sleeping (self)) - return FALSE; - if (nm_device_get_unmanaged (device, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT)) + g_return_val_if_fail (!nm_device_get_is_nm_owned (device), FALSE); + + if (!can_start_device (self, device)) return FALSE; state = nm_device_get_state (device); @@ -1671,26 +1665,51 @@ device_ip_iface_changed (NMDevice *device, } } +static void +device_realized (NMDevice *device, + GParamSpec *pspec, + NMManager *self) +{ + int ifindex; + gboolean assumed = FALSE; + + /* Loopback device never gets managed */ + ifindex = nm_device_get_ifindex (device); + if (ifindex > 0 && nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) == NM_LINK_TYPE_LOOPBACK) + return; + + if (!can_start_device (self, device)) + return; + + if (!nm_device_get_is_nm_owned (device)) { + assumed = recheck_assume_connection (device, self); + g_signal_connect (device, NM_DEVICE_RECHECK_ASSUME, + G_CALLBACK (recheck_assume_connection), self); + } + + if (!assumed && nm_device_get_managed (device)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_NOW_MANAGED); + } +} + /** * add_device: * @self: the #NMManager * @device: the #NMDevice to add - * @try_assume: %TRUE if existing connection (if any) should be assumed * * If successful, this function will increase the references count of @device. * Callers should decrease the reference count. */ static void -add_device (NMManager *self, NMDevice *device, gboolean try_assume) +add_device (NMManager *self, NMDevice *device) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - const char *iface, *driver, *type_desc; + const char *iface, *type_desc; const GSList *unmanaged_specs; - gboolean user_unmanaged, sleeping; - gboolean enabled = FALSE; RfKillType rtype; GSList *iter, *remove = NULL; - gboolean connection_assumed = FALSE; int ifindex; const char *dbus_path; @@ -1738,6 +1757,9 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume) g_signal_connect (device, "notify::" NM_DEVICE_IP_IFACE, G_CALLBACK (device_ip_iface_changed), self); + g_signal_connect (device, "notify::" NM_DEVICE_REAL, + G_CALLBACK (device_realized), + self); if (priv->startup) { g_signal_connect (device, "notify::" NM_DEVICE_HAS_PENDING_ACTION, @@ -1752,52 +1774,29 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume) rtype = nm_device_get_rfkill_type (device); if (rtype != RFKILL_TYPE_UNKNOWN) { nm_manager_rfkill_update (self, rtype); - enabled = radio_enabled_for_type (self, rtype, TRUE); - nm_device_set_enabled (device, enabled); + nm_device_set_enabled (device, radio_enabled_for_type (self, rtype, TRUE)); } iface = nm_device_get_iface (device); g_assert (iface); - type_desc = nm_device_get_type_desc (device); g_assert (type_desc); - driver = nm_device_get_driver (device); - if (!driver) - driver = "unknown"; - nm_log_info (LOGD_HW, "(%s): new %s device (carrier: %s, driver: '%s', ifindex: %d)", - iface, type_desc, - nm_device_has_capability (device, NM_DEVICE_CAP_CARRIER_DETECT) - ? (nm_device_has_carrier (device) ? "ON" : "OFF") - : "UNKNOWN", - driver, nm_device_get_ifindex (device)); + + nm_log_info (LOGD_HW, "(%s): new %s device", iface, type_desc); unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings); - user_unmanaged = nm_device_spec_match_list (device, unmanaged_specs); - nm_device_set_unmanaged_initial (device, NM_UNMANAGED_USER, user_unmanaged); - - sleeping = manager_sleeping (self); - nm_device_set_unmanaged_initial (device, NM_UNMANAGED_INTERNAL, sleeping); + nm_device_set_unmanaged_initial (device, + NM_UNMANAGED_USER, + nm_device_spec_match_list (device, unmanaged_specs)); + nm_device_set_unmanaged_initial (device, + NM_UNMANAGED_INTERNAL, + manager_sleeping (self)); dbus_path = nm_exported_object_export (NM_EXPORTED_OBJECT (device)); nm_log_dbg (LOGD_DEVICE, "(%s): exported as %s", nm_device_get_iface (device), dbus_path); nm_device_finish_init (device); - if (try_assume) { - connection_assumed = recheck_assume_connection (device, self); - g_signal_connect (device, NM_DEVICE_RECHECK_ASSUME, - G_CALLBACK (recheck_assume_connection), self); - } - - if (!connection_assumed && nm_device_get_managed (device)) { - nm_device_state_changed (device, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_NOW_MANAGED); - } - - /* Try to generate a default connection. If this fails because the link is - * not initialized, we will retry again in device_link_initialized_cb(). - */ nm_settings_device_added (priv->settings, device); g_signal_emit (self, signals[DEVICE_ADDED], 0, device); g_object_notify (G_OBJECT (self), NM_MANAGER_DEVICES); @@ -1808,11 +1807,6 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume) if (d != device) nm_device_notify_new_device_added (d, device); } - - /* New devices might be master interfaces for virtual interfaces; so we may - * need to create new virtual interfaces now. - */ - system_create_virtual_devices (self); } /*******************************************************************/ @@ -1824,9 +1818,10 @@ factory_device_added_cb (NMDeviceFactory *factory, { GError *error = NULL; - if (nm_device_realize (device, NULL, &error)) - add_device (NM_MANAGER (user_data), device, TRUE); - else { + if (nm_device_realize (device, NULL, &error)) { + add_device (NM_MANAGER (user_data), device); + nm_device_setup_finish (device, NULL); + } else { nm_log_warn (LOGD_DEVICE, "(%s): failed to realize device: %s", nm_device_get_iface (device), error->message); g_error_free (error); @@ -1881,6 +1876,26 @@ platform_link_added (NMManager *self, if (nm_manager_get_device_by_ifindex (self, ifindex)) return; + device = find_device_by_iface (self, plink->name); + if (device) { + if (!nm_device_is_real (device)) { + if (nm_device_realize (device, plink, &error)) + nm_device_setup_finish (device, plink); + else { + nm_log_warn (LOGD_DEVICE, "(%s): %s", plink->name, error->message); + g_clear_error (&error); + remove_device (self, device, FALSE, FALSE); + } + return; + } else if (!nm_device_realize (device, plink, &error)) { + nm_log_warn (LOGD_HW, "%s: factory failed to create device: %s", + plink->name, error->message); + g_clear_error (&error); + return; + } + return; + } + /* Try registered device factories */ factory = nm_device_factory_manager_find_factory_for_link_type (plink->type); if (factory) { @@ -1917,10 +1932,11 @@ platform_link_added (NMManager *self, if (device) { if (nm_plugin_missing) nm_device_set_nm_plugin_missing (device, TRUE); - if (nm_device_realize (device, plink, &error)) - add_device (self, device, plink->type != NM_LINK_TYPE_LOOPBACK); - else { - nm_log_warn (LOGD_HW, "%s: failed to realize device: %s", + if (nm_device_realize (device, plink, &error)) { + add_device (self, device); + nm_device_setup_finish (device, plink); + } else { + nm_log_warn (LOGD_DEVICE, "%s: failed to realize device: %s", plink->name, error->message); g_clear_error (&error); } @@ -1954,17 +1970,21 @@ _platform_link_cb_idle (PlatformLinkCbData *data) device = nm_manager_get_device_by_ifindex (self, data->ifindex); if (device) { if (nm_device_is_software (device)) { + /* Software devices stick around until their connection is removed */ if (!nm_device_unrealize (device, FALSE, &error)) { nm_log_warn (LOGD_DEVICE, "(%s): failed to unrealize: %s", - nm_device_get_iface (device), - error->message); + nm_device_get_iface (device), + error->message); g_clear_error (&error); + remove_device (self, device, FALSE, TRUE); } + } else { + /* Hardware devices always get removed when their kernel link is gone */ + remove_device (self, device, FALSE, TRUE); } - remove_device (self, device, FALSE, TRUE); } + g_object_remove_weak_pointer (G_OBJECT (self), (gpointer *) &data->self); } - g_object_remove_weak_pointer (G_OBJECT (self), (gpointer *) &data->self); } g_slice_free (PlatformLinkCbData, data); return G_SOURCE_REMOVE; @@ -2079,7 +2099,8 @@ impl_manager_get_devices (NMManager *self, const char *path; path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data)); - if (path) + if ( path + && nm_device_is_real (iter->data)) paths[i++] = path; } paths[i++] = NULL; @@ -2181,7 +2202,7 @@ find_master (NMManager *self, const char *master; NMDevice *master_device = NULL; NMSettingsConnection *master_connection = NULL; - GSList *iter, *connections = NULL; + GSList *iter; s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); @@ -2191,7 +2212,7 @@ find_master (NMManager *self, return TRUE; /* success, but no master */ /* Try as an interface name first */ - master_device = find_device_by_ip_iface (self, master); + master_device = find_device_by_iface (self, master); if (master_device) { if (master_device == device) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED, @@ -2223,23 +2244,6 @@ find_master (NMManager *self, break; } } - } else { - /* Might be a virtual interface that hasn't been created yet, so - * look through the interface names of connections that require - * virtual interfaces and see if one of their virtual interface - * names matches the master. - */ - connections = nm_manager_get_activatable_connections (self); - for (iter = connections; iter && !master_connection; iter = g_slist_next (iter)) { - NMSettingsConnection *candidate = iter->data; - char *vname; - - vname = get_virtual_iface_name (self, NM_CONNECTION (candidate), NULL, NULL); - if (g_strcmp0 (master, vname) == 0 && is_compatible_with_slave (NM_CONNECTION (candidate), connection)) - master_connection = candidate; - g_free (vname); - } - g_slist_free (connections); } } @@ -2333,7 +2337,7 @@ ensure_master_active_connection (NMManager *self, /* If the device is disconnected, find a compatible connection and * activate it on the device. */ - if (master_state == NM_DEVICE_STATE_DISCONNECTED) { + if (master_state == NM_DEVICE_STATE_DISCONNECTED || !nm_device_is_real (master_device)) { GSList *connections; g_assert (master_connection == NULL); @@ -2394,9 +2398,11 @@ ensure_master_active_connection (NMManager *self, continue; found_device = TRUE; - master_state = nm_device_get_state (candidate); - if (master_state != NM_DEVICE_STATE_DISCONNECTED) - continue; + if (!nm_device_is_software (candidate)) { + master_state = nm_device_get_state (candidate); + if (nm_device_is_real (candidate) && master_state != NM_DEVICE_STATE_DISCONNECTED) + continue; + } master_ac = nm_manager_activate_connection (self, master_connection, @@ -2683,6 +2689,17 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * return FALSE; } + /* Create any backing resources the device needs */ + if (!nm_device_is_real (device)) { + NMDevice *parent; + + parent = find_parent_device_for_connection (self, (NMConnection *) connection); + 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; + } + } + /* Try to find the master connection/device if the connection has a dependency */ if (!find_master (self, applied, device, &master_connection, &master_device, &master_ac, @@ -3086,7 +3103,7 @@ validate_activation_request (NMManager *self, if (!iface) goto error; - device = find_device_by_ip_iface (self, iface); + device = find_device_by_iface (self, iface); g_free (iface); } } @@ -4209,6 +4226,7 @@ gboolean nm_manager_start (NMManager *self, GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter, *connections; guint i; if (!nm_settings_start (priv->settings, error)) @@ -4256,11 +4274,14 @@ nm_manager_start (NMManager *self, GError **error) /* Load VPN plugins */ priv->vpn_manager = g_object_ref (nm_vpn_manager_get ()); - /* - * Connections added before the manager is started do not emit + /* Connections added before the manager is started do not emit * connection-added signals thus devices have to be created manually. */ - system_create_virtual_devices (self); + nm_log_dbg (LOGD_CORE, "creating virtual devices..."); + connections = nm_settings_get_connections (priv->settings); + for (iter = connections; iter; iter = iter->next) + connection_added (priv->settings, NM_CONNECTION (iter->data), self); + g_slist_free (connections); priv->devices_inited = TRUE; @@ -5067,6 +5088,12 @@ nm_manager_init (NMManager *manager) priv->metered = NM_METERED_UNKNOWN; } +static gboolean +device_is_real (GObject *device, gpointer user_data) +{ + return nm_device_is_real (NM_DEVICE (device)); +} + static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) @@ -5139,7 +5166,7 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, priv->sleeping); break; case PROP_DEVICES: - nm_utils_g_value_set_object_path_array (value, priv->devices, NULL, NULL); + nm_utils_g_value_set_object_path_array (value, priv->devices, device_is_real, NULL); break; case PROP_METERED: g_value_set_uint (value, priv->metered); From 20906ca7a338661776a0a8364e41df9fd0ee3467 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 9 Oct 2014 12:42:29 -0500 Subject: [PATCH 13/17] core: ensure platform links are compatible with the NMDevice Ensure the platform link with the same interface name as the NMDevice is actually compatible with it before using the link for initialization of device properties. If not, remove the NMDevice and create a new one since there are kernel resources with a different type. --- src/devices/nm-device-bond.c | 2 +- src/devices/nm-device-bridge.c | 2 +- src/devices/nm-device-ethernet.c | 2 +- src/devices/nm-device-generic.c | 2 +- src/devices/nm-device-infiniband.c | 2 + src/devices/nm-device-ip-tunnel.c | 7 ++- src/devices/nm-device-macvlan.c | 2 + src/devices/nm-device-private.h | 7 +++ src/devices/nm-device-tun.c | 2 + src/devices/nm-device-veth.c | 2 + src/devices/nm-device-vlan.c | 2 +- src/devices/nm-device-vxlan.c | 2 + src/devices/nm-device.c | 73 +++++++++++++++++++++++--- src/devices/nm-device.h | 2 + src/devices/team/nm-device-team.c | 2 +- src/devices/wifi/nm-device-olpc-mesh.c | 2 + src/devices/wifi/nm-device-wifi.c | 2 + src/nm-manager.c | 37 +++++++------ src/nm-types.h | 2 + 19 files changed, 125 insertions(+), 29 deletions(-) diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 359b3c4abf..033c57214f 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -529,7 +529,7 @@ nm_device_bond_class_init (NMDeviceBondClass *klass) g_type_class_add_private (object_class, sizeof (NMDeviceBondPrivate)); - parent_class->connection_type = NM_SETTING_BOND_SETTING_NAME; + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BOND_SETTING_NAME, NM_LINK_TYPE_BOND) /* virtual methods */ object_class->get_property = get_property; diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 2a2f234c16..e152bb38b9 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -475,7 +475,7 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass) g_type_class_add_private (object_class, sizeof (NMDeviceBridgePrivate)); - parent_class->connection_type = NM_SETTING_BRIDGE_SETTING_NAME; + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BRIDGE_SETTING_NAME, NM_LINK_TYPE_BRIDGE) /* virtual methods */ object_class->get_property = get_property; diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 6f0c3e8881..65f8716d8e 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -1706,7 +1706,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) g_type_class_add_private (object_class, sizeof (NMDeviceEthernetPrivate)); - parent_class->connection_type = NM_SETTING_WIRED_SETTING_NAME; + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRED_SETTING_NAME, NM_LINK_TYPE_ETHERNET) /* virtual methods */ object_class->constructor = constructor; diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c index 70ae48c678..0be55d54f0 100644 --- a/src/devices/nm-device-generic.c +++ b/src/devices/nm-device-generic.c @@ -196,7 +196,7 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass) g_type_class_add_private (klass, sizeof (NMDeviceGenericPrivate)); - parent_class->connection_type = NM_SETTING_GENERIC_SETTING_NAME; + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_GENERIC_SETTING_NAME, NM_LINK_TYPE_ANY) object_class->constructor = constructor; object_class->dispose = dispose; diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index e0c3c62d76..95f69c93c3 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -327,6 +327,8 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass) g_type_class_add_private (object_class, sizeof (NMDeviceInfinibandPrivate)); + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_INFINIBAND_SETTING_NAME, NM_LINK_TYPE_INFINIBAND) + /* virtual methods */ object_class->get_property = get_property; object_class->set_property = set_property; diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 64c3a52146..84ab7cd69d 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -852,7 +852,12 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass) device_class->setup_start = setup_start; device_class->unrealize = unrealize; - device_class->connection_type = NM_SETTING_IP_TUNNEL_SETTING_NAME; + NM_DEVICE_CLASS_DECLARE_TYPES (klass, + NM_SETTING_IP_TUNNEL_SETTING_NAME, + NM_LINK_TYPE_GRE, + NM_LINK_TYPE_IP6TNL, + NM_LINK_TYPE_IPIP, + NM_LINK_TYPE_SIT); /* properties */ g_object_class_install_property diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index e2dd891c1a..aed28816ff 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -143,6 +143,8 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass) g_type_class_add_private (klass, sizeof (NMDeviceMacvlanPrivate)); + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP) + object_class->constructed = constructed; object_class->get_property = get_property; diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 33f51bfb6e..5010e90655 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -106,4 +106,11 @@ void nm_device_set_wwan_ip6_config (NMDevice *device, NMIP6Config *config); gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value); +#define NM_DEVICE_CLASS_DECLARE_TYPES(klass, conn_type, ...) \ + NM_DEVICE_CLASS (klass)->connection_type = conn_type; \ + { \ + static const NMLinkType link_types[] = { __VA_ARGS__, NM_LINK_TYPE_NONE }; \ + NM_DEVICE_CLASS (klass)->link_types = link_types; \ + } + #endif /* NM_DEVICE_PRIVATE_H */ diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 46d4d391e8..af37c3184d 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -364,6 +364,8 @@ nm_device_tun_class_init (NMDeviceTunClass *klass) g_type_class_add_private (klass, sizeof (NMDeviceTunPrivate)); + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP) + object_class->get_property = get_property; object_class->set_property = set_property; diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c index 9c22ad1e5c..1fd2a5a834 100644 --- a/src/devices/nm-device-veth.c +++ b/src/devices/nm-device-veth.c @@ -153,6 +153,8 @@ nm_device_veth_class_init (NMDeviceVethClass *klass) g_type_class_add_private (klass, sizeof (NMDeviceVethPrivate)); + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_VETH) + object_class->get_property = get_property; object_class->dispose = dispose; diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 58263c5d0c..734a483bdc 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -667,7 +667,7 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); - parent_class->connection_type = NM_SETTING_VLAN_SETTING_NAME; + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_VLAN_SETTING_NAME, NM_LINK_TYPE_VLAN) g_type_class_add_private (object_class, sizeof (NMDeviceVlanPrivate)); diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index 65b3950dd4..ad2f2ba44f 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -244,6 +244,8 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass) g_type_class_add_private (klass, sizeof (NMDeviceVxlanPrivate)); + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_VXLAN) + object_class->get_property = get_property; device_class->link_changed = link_changed; diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 63df7b71aa..5435440e2d 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1634,20 +1634,69 @@ link_changed (NMDevice *self, NMPlatformLink *info) nm_device_set_carrier (self, info->connected); } +static gboolean +link_type_compatible (NMDevice *self, + NMLinkType link_type, + gboolean *out_compatible, + GError **error) +{ + NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); + guint i = 0; + + if (!klass->link_types) { + NM_SET_OUT (out_compatible, FALSE); + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + "Device does not support platform links"); + return FALSE; + } + + for (i = 0; klass->link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) { + if (klass->link_types[i] == link_type) + return TRUE; + if (klass->link_types[i] == NM_LINK_TYPE_ANY) + return TRUE; + } + + NM_SET_OUT (out_compatible, FALSE); + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + "Device does not support platform link type 0x%X", + link_type); + return FALSE; +} + /** * nm_device_realize(): * @self: the #NMDevice * @plink: an existing platform link or %NULL + * @out_compatible: %TRUE on return if @self is compatible with @plink * @error: location to store error, or %NULL * * Initializes and sets up the device using existing backing resources. Before * the device is ready for use nm_device_setup_finish() must be called. + * @out_compatible will only be set if @plink is not %NULL, and * * Returns: %TRUE on success, %FALSE on error */ gboolean -nm_device_realize (NMDevice *self, NMPlatformLink *plink, GError **error) +nm_device_realize (NMDevice *self, + NMPlatformLink *plink, + gboolean *out_compatible, + GError **error) { + NM_SET_OUT (out_compatible, TRUE); + + if (plink) { + if (g_strcmp0 (nm_device_get_iface (self), plink->name) != 0) { + NM_SET_OUT (out_compatible, FALSE); + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + "Device interface name does not match platform link"); + return FALSE; + } + + if (!link_type_compatible (self, plink->type, out_compatible, error)) + return FALSE; + } + /* Try to realize the device from existing resources */ if (NM_DEVICE_GET_CLASS (self)->realize) { if (!NM_DEVICE_GET_CLASS (self)->realize (self, plink, error)) @@ -1766,7 +1815,7 @@ setup_start (NMDevice *self, NMPlatformLink *plink) g_object_freeze_notify (G_OBJECT (self)); if (plink) { - g_return_if_fail (priv->iface == NULL || strcmp (plink->name, priv->iface) == 0); + g_return_if_fail (link_type_compatible (self, plink->type, NULL, NULL)); update_device_from_platform_link (self, plink); } @@ -1871,6 +1920,8 @@ setup_finish (NMDevice *self, NMPlatformLink *plink) void nm_device_setup_finish (NMDevice *self, NMPlatformLink *plink) { + g_return_if_fail (!plink || link_type_compatible (self, plink->type, NULL, NULL)); + NM_DEVICE_GET_CLASS (self)->setup_finish (self, plink); NM_DEVICE_GET_PRIVATE (self)->real = TRUE; @@ -10073,6 +10124,7 @@ set_property (GObject *object, guint prop_id, const char *hw_addr, *p; guint count; gboolean val_bool; + const NMPlatformLink *pllink; switch (prop_id) { case PROP_UDI: @@ -10082,12 +10134,19 @@ set_property (GObject *object, guint prop_id, } break; case PROP_IFACE: - if (g_value_get_string (value)) { + p = g_value_get_string (value); + if (p) { + g_free (priv->iface); - priv->iface = g_value_dup_string (value); - priv->ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->iface); - if (priv->ifindex > 0) - priv->up = nm_platform_link_is_up (NM_PLATFORM_GET, priv->ifindex); + priv->iface = g_strdup (p); + + pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface); + if (pllink) { + if (link_type_compatible (self, pllink->type, NULL, NULL)) { + priv->ifindex = pllink->ifindex; + priv->up = nm_platform_link_is_up (NM_PLATFORM_GET, priv->ifindex); + } + } } break; case PROP_DRIVER: diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 268ed06294..55f05c4fab 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -123,6 +123,7 @@ typedef struct { NMExportedObjectClass parent; const char *connection_type; + const NMLinkType *link_types; void (*state_changed) (NMDevice *device, NMDeviceState new_state, @@ -485,6 +486,7 @@ gboolean nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps); gboolean nm_device_realize (NMDevice *device, NMPlatformLink *plink, + gboolean *out_compatible, GError **error); gboolean nm_device_create_and_realize (NMDevice *self, NMConnection *connection, diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index 4bbe4c9ce7..fdc603364d 100644 --- a/src/devices/team/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -802,7 +802,7 @@ nm_device_team_class_init (NMDeviceTeamClass *klass) g_type_class_add_private (object_class, sizeof (NMDeviceTeamPrivate)); - parent_class->connection_type = NM_SETTING_TEAM_SETTING_NAME; + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_TEAM_SETTING_NAME, NM_LINK_TYPE_TEAM) /* virtual methods */ object_class->constructed = constructed; diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c index 38f1f88dd4..8898c59cd6 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.c +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -511,6 +511,8 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) g_type_class_add_private (object_class, sizeof (NMDeviceOlpcMeshPrivate)); + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_OLPC_MESH_SETTING_NAME, NM_LINK_TYPE_OLPC_MESH) + object_class->constructor = constructor; object_class->get_property = get_property; object_class->set_property = set_property; diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index bb73e8c456..72540efed4 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -3034,6 +3034,8 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) g_type_class_add_private (object_class, sizeof (NMDeviceWifiPrivate)); + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRELESS_SETTING_NAME, NM_LINK_TYPE_WIFI) + object_class->constructor = constructor; object_class->get_property = get_property; object_class->set_property = set_property; diff --git a/src/nm-manager.c b/src/nm-manager.c index eb0ec6fffc..ba926157f8 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1818,7 +1818,7 @@ factory_device_added_cb (NMDeviceFactory *factory, { GError *error = NULL; - if (nm_device_realize (device, NULL, &error)) { + if (nm_device_realize (device, NULL, NULL, &error)) { add_device (NM_MANAGER (user_data), device); nm_device_setup_finish (device, NULL); } else { @@ -1878,22 +1878,29 @@ platform_link_added (NMManager *self, device = find_device_by_iface (self, plink->name); if (device) { - if (!nm_device_is_real (device)) { - if (nm_device_realize (device, plink, &error)) - nm_device_setup_finish (device, plink); - else { - nm_log_warn (LOGD_DEVICE, "(%s): %s", plink->name, error->message); - g_clear_error (&error); - remove_device (self, device, FALSE, FALSE); - } + gboolean compatible = FALSE; + + if (nm_device_is_real (device)) return; - } else if (!nm_device_realize (device, plink, &error)) { - nm_log_warn (LOGD_HW, "%s: factory failed to create device: %s", - plink->name, error->message); - g_clear_error (&error); + + if (nm_device_realize (device, plink, &compatible, &error)) { + /* Success */ + nm_device_setup_finish (device, plink); return; } - return; + + nm_log_warn (LOGD_DEVICE, "(%s): %s", plink->name, error->message); + remove_device (self, device, FALSE, FALSE); + g_clear_error (&error); + + if (compatible) { + /* Device compatible with platform link, but some other fatal error + * happened during realization. + */ + return; + } + + /* Fall through and create new compatible device for the link */ } /* Try registered device factories */ @@ -1932,7 +1939,7 @@ platform_link_added (NMManager *self, if (device) { if (nm_plugin_missing) nm_device_set_nm_plugin_missing (device, TRUE); - if (nm_device_realize (device, plink, &error)) { + if (nm_device_realize (device, plink, NULL, &error)) { add_device (self, device); nm_device_setup_finish (device, plink); } else { diff --git a/src/nm-types.h b/src/nm-types.h index 16ae6fa7b4..65d6193be2 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -126,6 +126,8 @@ typedef enum { NM_LINK_TYPE_BRIDGE = 0x10000 | 0x20000, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM, + + NM_LINK_TYPE_ANY = G_MAXUINT32, } NMLinkType; typedef enum { From 5da37a129c25350e6f5041726a23d4d9f8318762 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 6 Oct 2014 11:21:54 -0500 Subject: [PATCH 14/17] api/manager: add GetAllDevices() method and AllDevices property Returns both realized and un-realized devices. --- introspection/nm-manager.xml | 33 +++++++++++++-- src/nm-manager.c | 79 ++++++++++++++++++++++++++++++++---- src/nm-manager.h | 1 + src/nm-policy.c | 4 +- 4 files changed, 104 insertions(+), 13 deletions(-) diff --git a/introspection/nm-manager.xml b/introspection/nm-manager.xml index 36cbee3011..dd6fc4bdd1 100644 --- a/introspection/nm-manager.xml +++ b/introspection/nm-manager.xml @@ -6,11 +6,27 @@ - Get the list of network devices. + Get the list of realized network devices. - List of object paths of network devices known to the system. + List of object paths of network devices known to the system. This + list does not include device placeholders (see GetAllDevices()). + + + + + + + Get the list of all network devices. + + + + + List of object paths of network devices and device placeholders + (eg, devices that do not yet exist but which can be automatically + created by NetworkManager if one of their AvailableConnections + was activated). @@ -249,7 +265,18 @@ - The list of network devices/interfaces NetworkManager knows about. + The list of realized network devices. Realized devices are those which + have backing resources (eg from the kernel or a management daemon like + ModemManager, teamd, etc). + + + + + + The list of both realized and un-realized network devices. Un-realized + devices are software devices which do not yet have backing resources, + but for which backing resources can be created if the device is + activated. diff --git a/src/nm-manager.c b/src/nm-manager.c index ba926157f8..daaca90366 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -149,7 +149,9 @@ G_DEFINE_TYPE (NMManager, nm_manager, NM_TYPE_EXPORTED_OBJECT) enum { DEVICE_ADDED, + INTERNAL_DEVICE_ADDED, DEVICE_REMOVED, + INTERNAL_DEVICE_REMOVED, STATE_CHANGED, CHECK_PERMISSIONS, USER_PERMISSIONS_CHANGED, @@ -182,6 +184,7 @@ enum { PROP_DEVICES, PROP_METERED, PROP_GLOBAL_DNS_CONFIGURATION, + PROP_ALL_DEVICES, /* Not exported */ PROP_HOSTNAME, @@ -800,9 +803,13 @@ remove_device (NMManager *manager, nm_settings_device_removed (priv->settings, device, quitting); priv->devices = g_slist_remove (priv->devices, device); - g_signal_emit (manager, signals[DEVICE_REMOVED], 0, device); - g_object_notify (G_OBJECT (manager), NM_MANAGER_DEVICES); - nm_device_removed (device); + if (nm_device_is_real (device)) { + g_signal_emit (manager, signals[DEVICE_REMOVED], 0, device); + g_object_notify (G_OBJECT (manager), NM_MANAGER_DEVICES); + nm_device_removed (device); + } + g_signal_emit (manager, signals[INTERNAL_DEVICE_REMOVED], 0, device); + g_object_notify (G_OBJECT (manager), NM_MANAGER_ALL_DEVICES); nm_exported_object_clear_and_unexport (&device); @@ -1673,6 +1680,10 @@ device_realized (NMDevice *device, int ifindex; gboolean assumed = FALSE; + /* Emit D-Bus signals */ + g_signal_emit (self, signals[DEVICE_ADDED], 0, device); + g_object_notify (G_OBJECT (self), NM_MANAGER_DEVICES); + /* Loopback device never gets managed */ ifindex = nm_device_get_ifindex (device); if (ifindex > 0 && nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) == NM_LINK_TYPE_LOOPBACK) @@ -1798,8 +1809,8 @@ add_device (NMManager *self, NMDevice *device) nm_device_finish_init (device); nm_settings_device_added (priv->settings, device); - g_signal_emit (self, signals[DEVICE_ADDED], 0, device); - g_object_notify (G_OBJECT (self), NM_MANAGER_DEVICES); + g_signal_emit (self, signals[INTERNAL_DEVICE_ADDED], 0, device); + g_object_notify (G_OBJECT (self), NM_MANAGER_ALL_DEVICES); for (iter = priv->devices; iter; iter = iter->next) { NMDevice *d = iter->data; @@ -2092,8 +2103,9 @@ nm_manager_get_best_device_for_connection (NMManager *self, } static void -impl_manager_get_devices (NMManager *self, - GDBusMethodInvocation *context) +_get_devices (NMManager *self, + GDBusMethodInvocation *context, + gboolean all_devices) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); gs_free const char **paths = NULL; @@ -2107,7 +2119,7 @@ impl_manager_get_devices (NMManager *self, path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data)); if ( path - && nm_device_is_real (iter->data)) + && (all_devices || nm_device_is_real (iter->data))) paths[i++] = path; } paths[i++] = NULL; @@ -2116,6 +2128,20 @@ impl_manager_get_devices (NMManager *self, g_variant_new ("(^ao)", (char **) paths)); } +static void +impl_manager_get_devices (NMManager *self, + GDBusMethodInvocation *context) +{ + _get_devices (self, context, FALSE); +} + +static void +impl_manager_get_all_devices (NMManager *self, + GDBusMethodInvocation *context) +{ + _get_devices (self, context, TRUE); +} + static void impl_manager_get_device_by_ip_iface (NMManager *self, GDBusMethodInvocation *context, @@ -5183,6 +5209,9 @@ get_property (GObject *object, guint prop_id, dns_config = nm_config_data_get_global_dns_config (config_data); nm_global_dns_config_to_dbus (dns_config, value); break; + case PROP_ALL_DEVICES: + nm_utils_g_value_set_object_path_array (value, priv->devices, NULL, NULL); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -5510,7 +5539,23 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NMManager:all-devices: + * + * All devices, including those that are not realized. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_ALL_DEVICES, + g_param_spec_boxed (NM_MANAGER_ALL_DEVICES, "", "", + G_TYPE_STRV, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* signals */ + + /* D-Bus exported; emitted only for realized devices */ signals[DEVICE_ADDED] = g_signal_new ("device-added", G_OBJECT_CLASS_TYPE (object_class), @@ -5519,6 +5564,15 @@ nm_manager_class_init (NMManagerClass *manager_class) NULL, NULL, NULL, G_TYPE_NONE, 1, NM_TYPE_DEVICE); + /* Emitted for both realized devices and placeholder devices */ + signals[INTERNAL_DEVICE_ADDED] = + g_signal_new ("internal-device-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + /* D-Bus exported; emitted only for realized devices */ signals[DEVICE_REMOVED] = g_signal_new ("device-removed", G_OBJECT_CLASS_TYPE (object_class), @@ -5527,6 +5581,14 @@ nm_manager_class_init (NMManagerClass *manager_class) NULL, NULL, NULL, G_TYPE_NONE, 1, NM_TYPE_DEVICE); + /* Emitted for both realized devices and placeholder devices */ + signals[INTERNAL_DEVICE_REMOVED] = + g_signal_new ("internal-device-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + signals[STATE_CHANGED] = g_signal_new ("state-changed", G_OBJECT_CLASS_TYPE (object_class), @@ -5573,6 +5635,7 @@ nm_manager_class_init (NMManagerClass *manager_class) nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (manager_class), NMDBUS_TYPE_MANAGER_SKELETON, "GetDevices", impl_manager_get_devices, + "GetAllDevices", impl_manager_get_all_devices, "GetDeviceByIpIface", impl_manager_get_device_by_ip_iface, "ActivateConnection", impl_manager_activate_connection, "AddAndActivateConnection", impl_manager_add_and_activate_connection, diff --git a/src/nm-manager.h b/src/nm-manager.h index 7807f450c8..7486c49958 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -50,6 +50,7 @@ #define NM_MANAGER_DEVICES "devices" #define NM_MANAGER_METERED "metered" #define NM_MANAGER_GLOBAL_DNS_CONFIGURATION "global-dns-configuration" +#define NM_MANAGER_ALL_DEVICES "all-devices" /* Not exported */ #define NM_MANAGER_HOSTNAME "hostname" diff --git a/src/nm-policy.c b/src/nm-policy.c index 3a4236faf5..3d67e7af01 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -1779,8 +1779,8 @@ nm_policy_new (NMManager *manager, NMSettings *settings) _connect_manager_signal (policy, "notify::" NM_MANAGER_HOSTNAME, hostname_changed); _connect_manager_signal (policy, "notify::" NM_MANAGER_SLEEPING, sleeping_changed); _connect_manager_signal (policy, "notify::" NM_MANAGER_NETWORKING_ENABLED, sleeping_changed); - _connect_manager_signal (policy, "device-added", device_added); - _connect_manager_signal (policy, "device-removed", device_removed); + _connect_manager_signal (policy, "internal-device-added", device_added); + _connect_manager_signal (policy, "internal-device-removed", device_removed); _connect_manager_signal (policy, NM_MANAGER_ACTIVE_CONNECTION_ADDED, active_connection_added); _connect_manager_signal (policy, NM_MANAGER_ACTIVE_CONNECTION_REMOVED, active_connection_removed); From e84587183bfa86d92f24deffeba23a4e928cbee7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 17 Nov 2015 13:56:52 +0100 Subject: [PATCH 15/17] test: some refactoring of test-networkmanager-service.py --- tools/test-networkmanager-service.py | 89 ++++++++++++++-------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/tools/test-networkmanager-service.py b/tools/test-networkmanager-service.py index 260810019c..94f8b28439 100755 --- a/tools/test-networkmanager-service.py +++ b/tools/test-networkmanager-service.py @@ -9,6 +9,7 @@ import dbus import dbus.service import dbus.mainloop.glib import random +import collections mainloop = GLib.MainLoop() @@ -83,32 +84,42 @@ def to_path(src): return dbus.ObjectPath("/") class ExportedObj(dbus.service.Object): + + DBusInterface = collections.namedtuple('DBusInterface', ['dbus_iface', 'get_props_func', 'prop_changed_func']) + def __init__(self, bus, object_path): dbus.service.Object.__init__(self, bus, object_path) self._bus = bus self.path = object_path self.__dbus_ifaces = {} - def add_dbus_interface(self, dbus_iface, get_props_func): - self.__dbus_ifaces[dbus_iface] = get_props_func + def add_dbus_interface(self, dbus_iface, get_props_func, prop_changed_func): + self.__dbus_ifaces[dbus_iface] = ExportedObj.DBusInterface(dbus_iface, get_props_func, prop_changed_func) - def _get_dbus_properties(self, iface): - return self.__dbus_ifaces[iface]() + def __dbus_interface_get(self, dbus_iface): + if dbus_iface not in self.__dbus_ifaces: + raise UnknownInterfaceException() + return self.__dbus_ifaces[dbus_iface] + + def _dbus_property_get(self, dbus_iface, propname = None): + props = self.__dbus_interface_get(dbus_iface).get_props_func() + if propname is None: + return props + if propname not in props: + raise UnknownPropertyException() + return props[propname] + + def _dbus_property_notify(self, dbus_iface, propname): + prop = self._dbus_property_get(dbus_iface, propname) + self.__dbus_interface_get(dbus_iface).prop_changed_func(self, { propname: prop }) @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}') - def GetAll(self, iface): - if iface not in self.__dbus_ifaces.keys(): - raise UnknownInterfaceException() - return self._get_dbus_properties(iface) + def GetAll(self, dbus_iface): + return self._dbus_property_get(dbus_iface) @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v') - def Get(self, iface, name): - if iface not in self.__dbus_ifaces.keys(): - raise UnknownInterfaceException() - props = self._get_dbus_properties(iface) - if not name in props.keys(): - raise UnknownPropertyException() - return props[name] + def Get(self, dbus_iface, name): + return self._dbus_property_get(dbus_iface, name) ################################################################### IFACE_DEVICE = 'org.freedesktop.NetworkManager.Device' @@ -137,7 +148,7 @@ class Device(ExportedObj): object_path = "/org/freedesktop/NetworkManager/Devices/%d" % Device.counter Device.counter = Device.counter + 1 ExportedObj.__init__(self, bus, object_path) - self.add_dbus_interface(IFACE_DEVICE, self.__get_props) + self.add_dbus_interface(IFACE_DEVICE, self.__get_props, Device.PropertiesChanged) self.iface = iface self.udi = "/sys/devices/virtual/%s" % iface @@ -180,9 +191,7 @@ class Device(ExportedObj): pass def __notify(self, propname): - props = self._get_dbus_properties(IFACE_DEVICE) - changed = { propname: props[propname] } - Device.PropertiesChanged(self, changed) + self._dbus_property_notify(IFACE_DEVICE, propname) @dbus.service.signal(IFACE_DEVICE, signature='a{sv}') def PropertiesChanged(self, changed): @@ -212,7 +221,7 @@ PE_S390_SUBCHANNELS = "S390Subchannels" class WiredDevice(Device): def __init__(self, bus, iface, mac, subchannels): Device.__init__(self, bus, iface, NM_DEVICE_TYPE_ETHERNET) - self.add_dbus_interface(IFACE_WIRED, self.__get_props) + self.add_dbus_interface(IFACE_WIRED, self.__get_props, WiredDevice.PropertiesChanged) if mac is None: self.mac = random_mac() @@ -232,9 +241,7 @@ class WiredDevice(Device): return props def __notify(self, propname): - props = self._get_dbus_properties(IFACE_WIRED) - changed = { propname: props[propname] } - WiredDevice.PropertiesChanged(self, changed) + self._dbus_property_notify(IFACE_WIRED, propname) @dbus.service.signal(IFACE_WIRED, signature='a{sv}') def PropertiesChanged(self, changed): @@ -250,7 +257,7 @@ PV_VLAN_ID = "VlanId" class VlanDevice(Device): def __init__(self, bus, iface): Device.__init__(self, bus, iface, NM_DEVICE_TYPE_VLAN) - self.add_dbus_interface(IFACE_VLAN, self.__get_props) + self.add_dbus_interface(IFACE_VLAN, self.__get_props, VlanDevice.PropertiesChanged) self.mac = random_mac() self.carrier = False @@ -288,7 +295,7 @@ class WifiAp(ExportedObj): path = "/org/freedesktop/NetworkManager/AccessPoint/%d" % WifiAp.counter WifiAp.counter = WifiAp.counter + 1 ExportedObj.__init__(self, bus, path) - self.add_dbus_interface(IFACE_WIFI_AP, self.__get_props) + self.add_dbus_interface(IFACE_WIFI_AP, self.__get_props, WifiAp.PropertiesChanged) self.ssid = ssid if mac: @@ -327,9 +334,7 @@ class WifiAp(ExportedObj): return props def __notify(self, propname): - props = self._get_dbus_properties(IFACE_WIFI_AP) - changed = { propname: props[propname] } - WifiAp.PropertiesChanged(self, changed) + self._dbus_property_notify(IFACE_WIFI_AP, propname) @dbus.service.signal(IFACE_WIFI_AP, signature='a{sv}') def PropertiesChanged(self, changed): @@ -352,7 +357,7 @@ PW_WIRELESS_CAPABILITIES = "WirelessCapabilities" class WifiDevice(Device): def __init__(self, bus, iface): Device.__init__(self, bus, iface, NM_DEVICE_TYPE_WIFI) - self.add_dbus_interface(IFACE_WIFI, self.__get_props) + self.add_dbus_interface(IFACE_WIFI, self.__get_props, WifiDevice.PropertiesChanged) self.mac = random_mac() self.aps = [] @@ -404,9 +409,7 @@ class WifiDevice(Device): return props def __notify(self, propname): - props = self._get_dbus_properties(IFACE_WIFI) - changed = { propname: props[propname] } - WifiDevice.PropertiesChanged(self, changed) + self._dbus_property_notify(IFACE_WIFI, propname) @dbus.service.signal(IFACE_WIFI, signature='a{sv}') def PropertiesChanged(self, changed): @@ -440,7 +443,7 @@ class WimaxNsp(ExportedObj): path = "/org/freedesktop/NetworkManager/Nsp/%d" % WimaxNsp.counter WimaxNsp.counter = WimaxNsp.counter + 1 ExportedObj.__init__(self, bus, path) - self.add_dbus_interface(IFACE_WIMAX_NSP, self.__get_props) + self.add_dbus_interface(IFACE_WIMAX_NSP, self.__get_props, WimaxNsp.PropertiesChanged) self.name = name self.strength = random.randint(0, 100) @@ -465,9 +468,7 @@ class WimaxNsp(ExportedObj): return props def __notify(self, propname): - props = self._get_dbus_properties(IFACE_WIMAX_NSP) - changed = { propname: props[propname] } - WimaxNsp.PropertiesChanged(self, changed) + self._dbus_property_notify(IFACE_WIMAX_NSP, propname) @dbus.service.signal(IFACE_WIMAX_NSP, signature='a{sv}') def PropertiesChanged(self, changed): @@ -491,7 +492,7 @@ PX_ACTIVE_NSP = "ActiveNsp" class WimaxDevice(Device): def __init__(self, bus, iface): Device.__init__(self, bus, iface, NM_DEVICE_TYPE_WIMAX) - self.add_dbus_interface(IFACE_WIMAX, self.__get_props) + self.add_dbus_interface(IFACE_WIMAX, self.__get_props, WimaxDevice.PropertiesChanged) self.mac = random_mac() self.bsid = random_mac() @@ -536,9 +537,7 @@ class WimaxDevice(Device): return props def __notify(self, propname): - props = self._get_dbus_properties(IFACE_WIMAX) - changed = { propname: props[propname] } - WimaxDevice.PropertiesChanged(self, changed) + self._dbus_property_notify(IFACE_WIMAX, propname) @dbus.service.signal(IFACE_WIMAX, signature='a{sv}') def PropertiesChanged(self, changed): @@ -583,7 +582,7 @@ class ActiveConnection(ExportedObj): object_path = "/org/freedesktop/NetworkManager/ActiveConnection/%d" % ActiveConnection.counter ActiveConnection.counter = ActiveConnection.counter + 1 ExportedObj.__init__(self, bus, object_path) - self.add_dbus_interface(IFACE_ACTIVE_CONNECTION, self.__get_props) + self.add_dbus_interface(IFACE_ACTIVE_CONNECTION, self.__get_props, ActiveConnection.PropertiesChanged) self.device = device self.conn = connection @@ -659,7 +658,7 @@ def set_device_ac_cb(device, ac): class NetworkManager(ExportedObj): def __init__(self, bus, object_path): ExportedObj.__init__(self, bus, object_path) - self.add_dbus_interface(IFACE_NM, self.__get_props) + self.add_dbus_interface(IFACE_NM, self.__get_props, NetworkManager.PropertiesChanged) self._bus = bus; self.devices = [] @@ -680,7 +679,7 @@ class NetworkManager(ExportedObj): @dbus.service.method(dbus_interface=IFACE_NM, in_signature='', out_signature='ao') def GetDevices(self): - return self._get_dbus_properties(IFACE_NM)[PM_DEVICES] + return to_path_array(self.devices) @dbus.service.method(dbus_interface=IFACE_NM, in_signature='s', out_signature='o') def GetDeviceByIpIface(self, ip_iface): @@ -831,9 +830,7 @@ class NetworkManager(ExportedObj): return props def __notify(self, propname): - props = self._get_dbus_properties(IFACE_NM) - changed = { propname: props[propname] } - NetworkManager.PropertiesChanged(self, changed) + self._dbus_property_notify(IFACE_NM, propname) @dbus.service.signal(IFACE_NM, signature='a{sv}') def PropertiesChanged(self, changed): From 4db851f852b73a85b9b4a5d617e66461836be210 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 10 Oct 2014 14:28:49 -0500 Subject: [PATCH 16/17] libnm/libnm-glib: add NMClient.get_all_devices() method and AllDevices property Mirror new NetworkManager API to return both real devices and device placeholders. --- examples/python/gi/get-devices.py | 41 +++++++ libnm-glib/libnm-glib.ver | 1 + libnm-glib/nm-client.c | 156 +++++++++++++++++++++++---- libnm-glib/nm-client.h | 3 + libnm-glib/tests/test-nm-client.c | 1 + libnm/libnm.ver | 2 + libnm/nm-client.c | 110 ++++++++++++++++++- libnm/nm-client.h | 9 +- libnm/nm-manager.c | 96 +++++++++++++---- libnm/nm-manager.h | 5 + tools/test-networkmanager-service.py | 8 ++ 11 files changed, 389 insertions(+), 43 deletions(-) create mode 100755 examples/python/gi/get-devices.py diff --git a/examples/python/gi/get-devices.py b/examples/python/gi/get-devices.py new file mode 100755 index 0000000000..5a387bd2d5 --- /dev/null +++ b/examples/python/gi/get-devices.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# vim: ft=python ts=4 sts=4 sw=4 et ai +# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright (C) 2014 Red Hat, Inc. +# + +# This example lists all devices, both real and placeholder ones + +from gi.repository import GLib, NM + +if __name__ == "__main__": + client = NM.Client.new(None) + devices = client.get_all_devices() + + print "Real devices" + print "------------" + for d in devices: + if d.is_real(): + print "%s (%s): %s" % (d.get_iface(), d.get_type_description(), d.get_state()) + + print "\nUnrealized/placeholder devices" + print "------------------------------" + for d in devices: + if not d.is_real(): + print "%s (%s): %s" % (d.get_iface(), d.get_type_description(), d.get_state()) + diff --git a/libnm-glib/libnm-glib.ver b/libnm-glib/libnm-glib.ver index 53352587b5..bce31b0b72 100644 --- a/libnm-glib/libnm-glib.ver +++ b/libnm-glib/libnm-glib.ver @@ -47,6 +47,7 @@ global: nm_client_error_quark; nm_client_get_activating_connection; nm_client_get_active_connections; + nm_client_get_all_devices; nm_client_get_connectivity; nm_client_get_device_by_iface; nm_client_get_device_by_path; diff --git a/libnm-glib/nm-client.c b/libnm-glib/nm-client.c index a30b096ed3..248b9d9762 100644 --- a/libnm-glib/nm-client.c +++ b/libnm-glib/nm-client.c @@ -59,6 +59,7 @@ typedef struct { NMState state; gboolean startup; GPtrArray *devices; + GPtrArray *all_devices; GPtrArray *active_connections; NMConnectivityState connectivity; NMActiveConnection *primary_connection; @@ -101,6 +102,7 @@ enum { PROP_PRIMARY_CONNECTION, PROP_ACTIVATING_CONNECTION, PROP_DEVICES, + PROP_ALL_DEVICES, LAST_PROP }; @@ -108,6 +110,8 @@ enum { enum { DEVICE_ADDED, DEVICE_REMOVED, + ANY_DEVICE_ADDED, + ANY_DEVICE_REMOVED, PERMISSION_CHANGED, LAST_SIGNAL @@ -160,8 +164,8 @@ poke_wireless_devices_with_rf_status (NMClient *client) NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client); int i; - for (i = 0; priv->devices && (i < priv->devices->len); i++) { - NMDevice *device = g_ptr_array_index (priv->devices, i); + for (i = 0; priv->all_devices && (i < priv->all_devices->len); i++) { + NMDevice *device = g_ptr_array_index (priv->all_devices, i); if (NM_IS_DEVICE_WIFI (device)) _nm_device_wifi_set_wireless_enabled (NM_DEVICE_WIFI (device), priv->wireless_enabled); @@ -194,6 +198,7 @@ register_properties (NMClient *client) { NM_CLIENT_PRIMARY_CONNECTION, &priv->primary_connection, NULL, NM_TYPE_ACTIVE_CONNECTION }, { NM_CLIENT_ACTIVATING_CONNECTION, &priv->activating_connection, NULL, NM_TYPE_ACTIVE_CONNECTION }, { NM_CLIENT_DEVICES, &priv->devices, NULL, NM_TYPE_DEVICE, "device" }, + { NM_CLIENT_ALL_DEVICES, &priv->all_devices, NULL, NM_TYPE_DEVICE, "any-device" }, { NULL }, }; @@ -378,6 +383,34 @@ nm_client_get_devices (NMClient *client) return handle_ptr_array_return (NM_CLIENT_GET_PRIVATE (client)->devices); } +/** + * nm_client_get_all_devices: + * @client: a #NMClient + * + * Gets both real devices and device placeholders (eg, software devices which + * do not currently exist, but could be created automatically by NetworkManager + * if one of their NMDevice::ActivatableConnections was activated). Use + * nm_device_is_real() to determine whether each device is a real device or + * a placeholder. + * + * Use nm_device_get_type() or the NM_IS_DEVICE_XXXX() functions to determine + * what kind of device each member of the returned array is, and then you may + * use device-specific methods such as nm_device_ethernet_get_hw_address(). + * + * Returns: (transfer none) (element-type NMDevice): a #GPtrArray + * containing all the #NMDevices. The returned array is owned by the + * #NMClient object and should not be modified. + * + * Since: 1.2 + **/ +const GPtrArray * +nm_client_get_all_devices (NMClient *client) +{ + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); + + return NM_CLIENT_GET_PRIVATE (client)->all_devices; +} + /** * nm_client_get_device_by_path: * @client: a #NMClient @@ -1291,25 +1324,55 @@ nm_client_get_activating_connection (NMClient *client) /****************************************************************/ static void -free_devices (NMClient *client, gboolean emit_signals) +free_devices (NMClient *client, gboolean in_dispose) { NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client); - GPtrArray *devices; - NMDevice *device; - int i; + gs_unref_ptrarray GPtrArray *real_devices = NULL; + gs_unref_ptrarray GPtrArray *all_devices = NULL; + GPtrArray *devices = NULL; + guint i, j; - if (!priv->devices) - return; + real_devices = priv->devices; + all_devices = priv->all_devices; - devices = priv->devices; - priv->devices = NULL; - for (i = 0; i < devices->len; i++) { - device = devices->pdata[i]; - if (emit_signals) - g_signal_emit (client, signals[DEVICE_REMOVED], 0, device); - g_object_unref (device); + if (in_dispose) { + priv->devices = NULL; + priv->all_devices = NULL; + } else { + priv->devices = g_ptr_array_new (); + priv->all_devices = g_ptr_array_new (); + } + + if (all_devices && all_devices->len > 0) + devices = all_devices; + else if (devices && devices->len > 0) + devices = real_devices; + + if (real_devices && devices != real_devices) { + for (i = 0; i < real_devices->len; i++) { + NMDevice *d = real_devices->pdata[i]; + + if (all_devices) { + for (j = 0; j < all_devices->len; j++) { + if (d == all_devices->pdata[j]) + goto next; + } + } + if (!in_dispose) + g_signal_emit (client, signals[DEVICE_REMOVED], 0, d); +next: + g_object_unref (d); + } + } + if (devices) { + for (i = 0; i < devices->len; i++) { + NMDevice *d = devices->pdata[i]; + + if (!in_dispose) + g_signal_emit (client, signals[DEVICE_REMOVED], 0, d); + g_object_unref (d); + } } - g_ptr_array_free (devices, TRUE); } static void @@ -1382,7 +1445,7 @@ proxy_name_owner_changed (DBusGProxy *proxy, _nm_object_queue_notify (NM_OBJECT (client), NM_CLIENT_MANAGER_RUNNING); _nm_object_suppress_property_updates (NM_OBJECT (client), TRUE); poke_wireless_devices_with_rf_status (client); - free_devices (client, TRUE); + free_devices (client, FALSE); free_active_connections (client, TRUE); update_permissions (client, NULL); priv->wireless_enabled = FALSE; @@ -1980,7 +2043,7 @@ dispose (GObject *object) g_clear_object (&priv->client_proxy); g_clear_object (&priv->bus_proxy); - free_devices (client, FALSE); + free_devices (client, TRUE); free_active_connections (client, FALSE); g_clear_object (&priv->primary_connection); g_clear_object (&priv->activating_connection); @@ -2106,6 +2169,9 @@ get_property (GObject *object, case PROP_DEVICES: g_value_set_boxed (value, nm_client_get_devices (self)); break; + case PROP_ALL_DEVICES: + g_value_set_boxed (value, nm_client_get_all_devices (self)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2323,7 +2389,7 @@ nm_client_class_init (NMClientClass *client_class) /** * NMClient:devices: * - * List of known network devices. + * List of real network devices. Does not include placeholder devices. * * Since: 0.9.10 **/ @@ -2334,6 +2400,20 @@ nm_client_class_init (NMClientClass *client_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMClient:all-devices: + * + * List of both real devices and device placeholders. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_ALL_DEVICES, + g_param_spec_boxed (NM_CLIENT_ALL_DEVICES, "", "", + NM_TYPE_OBJECT_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* signals */ /** @@ -2341,7 +2421,8 @@ nm_client_class_init (NMClientClass *client_class) * @client: the client that received the signal * @device: (type NMDevice): the new device * - * Notifies that a #NMDevice is added. + * Notifies that a #NMDevice is added. This signal is not emitted for + * placeholder devices. **/ signals[DEVICE_ADDED] = g_signal_new ("device-added", @@ -2357,7 +2438,8 @@ nm_client_class_init (NMClientClass *client_class) * @client: the client that received the signal * @device: (type NMDevice): the removed device * - * Notifies that a #NMDevice is removed. + * Notifies that a #NMDevice is removed. This signal is not emitted for + * placeholder devices. **/ signals[DEVICE_REMOVED] = g_signal_new ("device-removed", @@ -2368,6 +2450,38 @@ nm_client_class_init (NMClientClass *client_class) G_TYPE_NONE, 1, G_TYPE_OBJECT); + /** + * NMClient::any-device-added: + * @client: the client that received the signal + * @device: (type NMDevice): the new device + * + * Notifies that a #NMDevice is added. This signal is emitted for both + * regular devices and placeholder devices. + **/ + signals[ANY_DEVICE_ADDED] = + g_signal_new ("any-device-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + /** + * NMClient::any-device-removed: + * @client: the client that received the signal + * @device: (type NMDevice): the removed device + * + * Notifies that a #NMDevice is removed. This signal is emitted for both + * regular devices and placeholder devices. + **/ + signals[ANY_DEVICE_REMOVED] = + g_signal_new ("any-device-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + /** * NMClient::permission-changed: * @client: the client that received the signal diff --git a/libnm-glib/nm-client.h b/libnm-glib/nm-client.h index ffe513cb03..39af321ebe 100644 --- a/libnm-glib/nm-client.h +++ b/libnm-glib/nm-client.h @@ -57,6 +57,7 @@ G_BEGIN_DECLS #define NM_CLIENT_PRIMARY_CONNECTION "primary-connection" #define NM_CLIENT_ACTIVATING_CONNECTION "activating-connection" #define NM_CLIENT_DEVICES "devices" +#define NM_CLIENT_ALL_DEVICES "all-devices" /** * NMClientPermission: @@ -175,6 +176,8 @@ NMClient *nm_client_new_finish (GAsyncResult *result, GError **error); const GPtrArray *nm_client_get_devices (NMClient *client); +NM_AVAILABLE_IN_1_2 +const GPtrArray *nm_client_get_all_devices(NMClient *client); NMDevice *nm_client_get_device_by_path (NMClient *client, const char *object_path); NMDevice *nm_client_get_device_by_iface (NMClient *client, const char *iface); diff --git a/libnm-glib/tests/test-nm-client.c b/libnm-glib/tests/test-nm-client.c index 3b269e0fdb..2c721f94df 100644 --- a/libnm-glib/tests/test-nm-client.c +++ b/libnm-glib/tests/test-nm-client.c @@ -686,6 +686,7 @@ typedef struct { static void da_check_quit (DaInfo *info) { + g_assert (info->quit_count > 0); info->quit_count--; if (info->quit_count == 0) { g_source_remove (info->quit_id); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index ae287695bc..e092a5845a 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -861,6 +861,7 @@ global: nm_connection_get_setting_ip_tunnel; nm_connection_verify_secrets; nm_device_ethernet_get_s390_subchannels; + nm_client_get_all_devices; nm_device_get_lldp_neighbors; nm_device_get_metered; nm_device_ip_tunnel_get_encapsulation_limit; @@ -897,6 +898,7 @@ global: nm_lldp_neighbor_new; nm_lldp_neighbor_ref; nm_lldp_neighbor_unref; + nm_manager_get_all_devices; nm_metered_get_type; nm_setting_802_1x_check_cert_scheme; nm_setting_bridge_get_multicast_snooping; diff --git a/libnm/nm-client.c b/libnm/nm-client.c index e17042eb44..dc6db0aad9 100644 --- a/libnm/nm-client.c +++ b/libnm/nm-client.c @@ -73,6 +73,7 @@ enum { PROP_PRIMARY_CONNECTION, PROP_ACTIVATING_CONNECTION, PROP_DEVICES, + PROP_ALL_DEVICES, PROP_CONNECTIONS, PROP_HOSTNAME, PROP_CAN_MODIFY, @@ -84,6 +85,8 @@ enum { enum { DEVICE_ADDED, DEVICE_REMOVED, + ANY_DEVICE_ADDED, + ANY_DEVICE_REMOVED, PERMISSION_CHANGED, CONNECTION_ADDED, CONNECTION_REMOVED, @@ -719,6 +722,34 @@ nm_client_get_devices (NMClient *client) return nm_manager_get_devices (NM_CLIENT_GET_PRIVATE (client)->manager); } +/** + * nm_client_get_all_devices: + * @client: a #NMClient + * + * Gets both real devices and device placeholders (eg, software devices which + * do not currently exist, but could be created automatically by NetworkManager + * if one of their NMDevice::ActivatableConnections was activated). Use + * nm_device_is_real() to determine whether each device is a real device or + * a placeholder. + * + * Use nm_device_get_type() or the NM_IS_DEVICE_XXXX() functions to determine + * what kind of device each member of the returned array is, and then you may + * use device-specific methods such as nm_device_ethernet_get_hw_address(). + * + * Returns: (transfer none) (element-type NMDevice): a #GPtrArray + * containing all the #NMDevices. The returned array is owned by the + * #NMClient object and should not be modified. + * + * Since: 1.2 + **/ +const GPtrArray * +nm_client_get_all_devices (NMClient *client) +{ + g_return_val_if_fail (NM_IS_CLIENT (client), NULL); + + return nm_manager_get_all_devices (NM_CLIENT_GET_PRIVATE (client)->manager); +} + /** * nm_client_get_device_by_path: * @client: a #NMClient @@ -1660,6 +1691,7 @@ manager_device_added (NMManager *manager, { g_signal_emit (client, signals[DEVICE_ADDED], 0, device); } + static void manager_device_removed (NMManager *manager, NMDevice *device, @@ -1668,6 +1700,22 @@ manager_device_removed (NMManager *manager, g_signal_emit (client, signals[DEVICE_REMOVED], 0, device); } +static void +manager_any_device_added (NMManager *manager, + NMDevice *device, + gpointer client) +{ + g_signal_emit (client, signals[ANY_DEVICE_ADDED], 0, device); +} + +static void +manager_any_device_removed (NMManager *manager, + NMDevice *device, + gpointer client) +{ + g_signal_emit (client, signals[ANY_DEVICE_REMOVED], 0, device); +} + static void manager_permission_changed (NMManager *manager, NMClientPermission permission, @@ -1707,6 +1755,10 @@ constructed (GObject *object) G_CALLBACK (manager_device_added), client); g_signal_connect (priv->manager, "device-removed", G_CALLBACK (manager_device_removed), client); + g_signal_connect (priv->manager, "any-device-added", + G_CALLBACK (manager_any_device_added), client); + g_signal_connect (priv->manager, "any-device-removed", + G_CALLBACK (manager_any_device_removed), client); g_signal_connect (priv->manager, "permission-changed", G_CALLBACK (manager_permission_changed), client); @@ -1873,6 +1925,7 @@ get_property (GObject *object, guint prop_id, case PROP_ACTIVATING_CONNECTION: case PROP_DEVICES: case PROP_METERED: + case PROP_ALL_DEVICES: g_object_get_property (G_OBJECT (NM_CLIENT_GET_PRIVATE (object)->manager), pspec->name, value); break; @@ -2092,7 +2145,7 @@ nm_client_class_init (NMClientClass *client_class) /** * NMClient:devices: * - * List of known network devices. + * List of real network devices. Does not include placeholder devices. * * Element-type: NMDevice **/ @@ -2103,6 +2156,21 @@ nm_client_class_init (NMClientClass *client_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMClient:all-devices: + * + * List of both real devices and device placeholders. + * + * Element-type: NMDevice + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_ALL_DEVICES, + g_param_spec_boxed (NM_CLIENT_ALL_DEVICES, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** * NMClient:connections: * @@ -2166,7 +2234,8 @@ nm_client_class_init (NMClientClass *client_class) * @client: the client that received the signal * @device: (type NMDevice): the new device * - * Notifies that a #NMDevice is added. + * Notifies that a #NMDevice is added. This signal is not emitted for + * placeholder devices. **/ signals[DEVICE_ADDED] = g_signal_new (NM_CLIENT_DEVICE_ADDED, @@ -2182,7 +2251,8 @@ nm_client_class_init (NMClientClass *client_class) * @client: the client that received the signal * @device: (type NMDevice): the removed device * - * Notifies that a #NMDevice is removed. + * Notifies that a #NMDevice is removed. This signal is not emitted for + * placeholder devices. **/ signals[DEVICE_REMOVED] = g_signal_new (NM_CLIENT_DEVICE_REMOVED, @@ -2193,6 +2263,40 @@ nm_client_class_init (NMClientClass *client_class) G_TYPE_NONE, 1, G_TYPE_OBJECT); + /** + * NMClient::any-device-added: + * @client: the client that received the signal + * @device: (type NMDevice): the new device + * + * Notifies that a #NMDevice is added. This signal is emitted for both + * regular devices and placeholder devices. + **/ + signals[ANY_DEVICE_ADDED] = + g_signal_new (NM_CLIENT_ANY_DEVICE_ADDED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMClientClass, any_device_added), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + /** + * NMClient::any-device-removed: + * @client: the client that received the signal + * @device: (type NMDevice): the removed device + * + * Notifies that a #NMDevice is removed. This signal is emitted for both + * regular devices and placeholder devices. + **/ + signals[ANY_DEVICE_REMOVED] = + g_signal_new (NM_CLIENT_ANY_DEVICE_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMClientClass, any_device_removed), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + /** * NMClient::permission-changed: * @client: the client that received the signal diff --git a/libnm/nm-client.h b/libnm/nm-client.h index fc309fa1a0..54d6deeeeb 100644 --- a/libnm/nm-client.h +++ b/libnm/nm-client.h @@ -53,6 +53,7 @@ G_BEGIN_DECLS #define NM_CLIENT_PRIMARY_CONNECTION "primary-connection" #define NM_CLIENT_ACTIVATING_CONNECTION "activating-connection" #define NM_CLIENT_DEVICES "devices" +#define NM_CLIENT_ALL_DEVICES "all-devices" #define NM_CLIENT_CONNECTIONS "connections" #define NM_CLIENT_HOSTNAME "hostname" #define NM_CLIENT_CAN_MODIFY "can-modify" @@ -60,6 +61,8 @@ G_BEGIN_DECLS #define NM_CLIENT_DEVICE_ADDED "device-added" #define NM_CLIENT_DEVICE_REMOVED "device-removed" +#define NM_CLIENT_ANY_DEVICE_ADDED "any-device-added" +#define NM_CLIENT_ANY_DEVICE_REMOVED "any-device-removed" #define NM_CLIENT_PERMISSION_CHANGED "permission-changed" #define NM_CLIENT_CONNECTION_ADDED "connection-added" #define NM_CLIENT_CONNECTION_REMOVED "connection-removed" @@ -164,6 +167,8 @@ typedef struct { /* Signals */ void (*device_added) (NMClient *client, NMDevice *device); void (*device_removed) (NMClient *client, NMDevice *device); + void (*any_device_added) (NMClient *client, NMDevice *device); + void (*any_device_removed) (NMClient *client, NMDevice *device); void (*permission_changed) (NMClient *client, NMClientPermission permission, NMClientPermissionResult result); @@ -171,7 +176,7 @@ typedef struct { void (*connection_removed) (NMClient *client, NMRemoteConnection *connection); /*< private >*/ - gpointer padding[8]; + gpointer padding[6]; } NMClientClass; GType nm_client_get_type (void); @@ -248,6 +253,8 @@ gboolean nm_client_save_hostname_finish (NMClient *client, /* Devices */ const GPtrArray *nm_client_get_devices (NMClient *client); +NM_AVAILABLE_IN_1_2 +const GPtrArray *nm_client_get_all_devices(NMClient *client); NMDevice *nm_client_get_device_by_path (NMClient *client, const char *object_path); NMDevice *nm_client_get_device_by_iface (NMClient *client, const char *iface); diff --git a/libnm/nm-manager.c b/libnm/nm-manager.c index 7ed50e3ced..b4b7e0c7f2 100644 --- a/libnm/nm-manager.c +++ b/libnm/nm-manager.c @@ -60,6 +60,7 @@ typedef struct { NMState state; gboolean startup; GPtrArray *devices; + GPtrArray *all_devices; GPtrArray *active_connections; NMConnectivityState connectivity; NMActiveConnection *primary_connection; @@ -104,6 +105,7 @@ enum { PROP_ACTIVATING_CONNECTION, PROP_DEVICES, PROP_METERED, + PROP_ALL_DEVICES, LAST_PROP }; @@ -111,6 +113,8 @@ enum { enum { DEVICE_ADDED, DEVICE_REMOVED, + ANY_DEVICE_ADDED, + ANY_DEVICE_REMOVED, ACTIVE_CONNECTION_ADDED, ACTIVE_CONNECTION_REMOVED, PERMISSION_CHANGED, @@ -136,6 +140,7 @@ nm_manager_init (NMManager *manager) priv->permissions = g_hash_table_new (g_direct_hash, g_direct_equal); priv->devices = g_ptr_array_new (); + priv->all_devices = g_ptr_array_new (); priv->active_connections = g_ptr_array_new (); } @@ -145,8 +150,8 @@ poke_wireless_devices_with_rf_status (NMManager *manager) NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); int i; - for (i = 0; i < priv->devices->len; i++) { - NMDevice *device = g_ptr_array_index (priv->devices, i); + for (i = 0; i < priv->all_devices->len; i++) { + NMDevice *device = g_ptr_array_index (priv->all_devices, i); if (NM_IS_DEVICE_WIFI (device)) _nm_device_wifi_set_wireless_enabled (NM_DEVICE_WIFI (device), priv->wireless_enabled); @@ -182,6 +187,7 @@ init_dbus (NMObject *object) { NM_MANAGER_ACTIVATING_CONNECTION, &priv->activating_connection, NULL, NM_TYPE_ACTIVE_CONNECTION }, { NM_MANAGER_DEVICES, &priv->devices, NULL, NM_TYPE_DEVICE, "device" }, { NM_MANAGER_METERED, &priv->metered }, + { NM_MANAGER_ALL_DEVICES, &priv->all_devices, NULL, NM_TYPE_DEVICE, "any-device" }, { NULL }, }; @@ -667,6 +673,14 @@ nm_manager_get_devices (NMManager *manager) return NM_MANAGER_GET_PRIVATE (manager)->devices; } +const GPtrArray * +nm_manager_get_all_devices (NMManager *manager) +{ + g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); + + return NM_MANAGER_GET_PRIVATE (manager)->all_devices; +} + NMDevice * nm_manager_get_device_by_path (NMManager *manager, const char *object_path) { @@ -1180,27 +1194,47 @@ static void free_devices (NMManager *manager, gboolean in_dispose) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - GPtrArray *devices; - NMDevice *device; - int i; + gs_unref_ptrarray GPtrArray *real_devices = NULL; + gs_unref_ptrarray GPtrArray *all_devices = NULL; + GPtrArray *devices = NULL; + guint i, j; - if (!priv->devices) - return; + real_devices = priv->devices; + all_devices = priv->all_devices; - devices = priv->devices; - - if (in_dispose) + if (in_dispose) { priv->devices = NULL; - else { - priv->devices = g_ptr_array_new (); - - for (i = 0; i < devices->len; i++) { - device = devices->pdata[i]; - g_signal_emit (manager, signals[DEVICE_REMOVED], 0, device); - } + priv->all_devices = NULL; + return; } - g_ptr_array_unref (devices); + priv->devices = g_ptr_array_new_with_free_func (g_object_unref); + priv->all_devices = g_ptr_array_new_with_free_func (g_object_unref); + + if (all_devices && all_devices->len > 0) + devices = all_devices; + else if (devices && devices->len > 0) + devices = real_devices; + + if (real_devices && devices != real_devices) { + for (i = 0; i < real_devices->len; i++) { + NMDevice *d = real_devices->pdata[i]; + + if (all_devices) { + for (j = 0; j < all_devices->len; j++) { + if (d == all_devices->pdata[j]) + goto next; + } + } + g_signal_emit (manager, signals[DEVICE_REMOVED], 0, d); +next: + ; + } + } + if (devices) { + for (i = 0; i < devices->len; i++) + g_signal_emit (manager, signals[DEVICE_REMOVED], 0, devices->pdata[i]); + } } static void @@ -1543,6 +1577,9 @@ get_property (GObject *object, case PROP_METERED: g_value_set_uint (value, priv->metered); break; + case PROP_ALL_DEVICES: + g_value_take_boxed (value, _nm_utils_copy_object_array (nm_manager_get_all_devices (self))); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1689,6 +1726,13 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_ALL_DEVICES, + g_param_spec_boxed (NM_MANAGER_ALL_DEVICES, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* signals */ signals[DEVICE_ADDED] = @@ -1707,6 +1751,22 @@ nm_manager_class_init (NMManagerClass *manager_class) NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); + signals[ANY_DEVICE_ADDED] = + g_signal_new ("any-device-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + signals[ANY_DEVICE_REMOVED] = + g_signal_new ("any-device-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); signals[ACTIVE_CONNECTION_ADDED] = g_signal_new ("active-connection-added", G_OBJECT_CLASS_TYPE (object_class), diff --git a/libnm/nm-manager.h b/libnm/nm-manager.h index 8d04a060f6..f9e4cd0e28 100644 --- a/libnm/nm-manager.h +++ b/libnm/nm-manager.h @@ -51,6 +51,7 @@ G_BEGIN_DECLS #define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection" #define NM_MANAGER_DEVICES "devices" #define NM_MANAGER_METERED "metered" +#define NM_MANAGER_ALL_DEVICES "all-devices" typedef struct { NMObject parent; @@ -67,6 +68,8 @@ typedef struct { void (*permission_changed) (NMManager *manager, NMClientPermission permission, NMClientPermissionResult result); + + /* Beware: no more slots. Cannot extend struct without breaking ABI. */ } NMManagerClass; GType nm_manager_get_type (void); @@ -121,6 +124,8 @@ NMConnectivityState nm_manager_check_connectivity_finish (NMManager *manager, /* Devices */ const GPtrArray *nm_manager_get_devices (NMManager *manager); +NM_AVAILABLE_IN_1_2 +const GPtrArray *nm_manager_get_all_devices(NMManager *manager); NMDevice *nm_manager_get_device_by_path (NMManager *manager, const char *object_path); NMDevice *nm_manager_get_device_by_iface (NMManager *manager, const char *iface); diff --git a/tools/test-networkmanager-service.py b/tools/test-networkmanager-service.py index 94f8b28439..ae63eaf745 100755 --- a/tools/test-networkmanager-service.py +++ b/tools/test-networkmanager-service.py @@ -637,6 +637,7 @@ class UnknownConnectionException(dbus.DBusException): _dbus_error_name = IFACE_NM + '.UnknownConnection' PM_DEVICES = 'Devices' +PM_ALL_DEVICES = 'AllDevices' PM_NETWORKING_ENABLED = 'NetworkingEnabled' PM_WWAN_ENABLED = 'WwanEnabled' PM_WWAN_HARDWARE_ENABLED = 'WwanHardwareEnabled' @@ -681,6 +682,10 @@ class NetworkManager(ExportedObj): def GetDevices(self): return to_path_array(self.devices) + @dbus.service.method(dbus_interface=IFACE_NM, in_signature='', out_signature='ao') + def GetAllDevices(self): + return to_path_array(self.devices) + @dbus.service.method(dbus_interface=IFACE_NM, in_signature='s', out_signature='o') def GetDeviceByIpIface(self, ip_iface): for d in self.devices: @@ -798,6 +803,7 @@ class NetworkManager(ExportedObj): def add_device(self, device): self.devices.append(device) self.__notify(PM_DEVICES) + self.__notify(PM_ALL_DEVICES) self.DeviceAdded(to_path(device)) @dbus.service.signal(IFACE_NM, signature='o') @@ -807,12 +813,14 @@ class NetworkManager(ExportedObj): def remove_device(self, device): self.devices.remove(device) self.__notify(PM_DEVICES) + self.__notify(PM_ALL_DEVICES) self.DeviceRemoved(to_path(device)) ################# D-Bus Properties interface def __get_props(self): props = {} props[PM_DEVICES] = to_path_array(self.devices) + props[PM_ALL_DEVICES] = to_path_array(self.devices) props[PM_NETWORKING_ENABLED] = True props[PM_WWAN_ENABLED] = True props[PM_WWAN_HARDWARE_ENABLED] = True From f2256af5bc310240b0004709c1c06aa456740ef6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 15 Oct 2014 21:17:45 -0500 Subject: [PATCH 17/17] core: allow multiple devices with the same interface name But, of course, only one realized device can have the same interface name at a time. This commit effectively reverts most of: 1b37cd03409fa3c0fb26fb6e8093b54b2b7c5d8d core: allow ActiveConnections to be created without a device But it's not easy to do a separate revert of that code due to interdependencies with nm-manager.c. Creating devices when they are defined by a connection also makes makes it possible to require the NMDevice to be present when activating it, which means we can remove a bunch of code from NMManager that had to handle software devices not existing yet at the time of the activation request. But it also means we must be more careful when finding master interfaces during slave activation, since we cannot simply match by interface name alone. Instead we must find the master which matches both the interface name and can control slaves of the type which is being activated. --- src/devices/nm-device.c | 40 ++++++ src/devices/nm-device.h | 2 + src/nm-activation-request.c | 6 +- src/nm-active-connection.c | 10 +- src/nm-manager.c | 259 ++++++++++++++++++++---------------- 5 files changed, 193 insertions(+), 124 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 5435440e2d..6cbf29065b 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2289,6 +2289,18 @@ nm_device_master_release_slaves (NMDevice *self) } } +/** + * nm_device_is_master: + * @self: the device + * + * Returns: %TRUE if the device can have slaves + */ +gboolean +nm_device_is_master (NMDevice *self) +{ + return NM_DEVICE_GET_PRIVATE (self)->is_master; +} + /** * nm_device_get_master: * @self: the device @@ -2873,6 +2885,34 @@ nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection) return NM_DEVICE_GET_CLASS (self)->check_connection_compatible (self, connection); } +gboolean +nm_device_check_slave_connection_compatible (NMDevice *self, NMConnection *slave) +{ + NMDevicePrivate *priv; + NMSettingConnection *s_con; + const char *connection_type, *slave_type; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (slave), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (!priv->is_master) + return FALSE; + + /* All masters should have connection type set */ + connection_type = NM_DEVICE_GET_CLASS (self)->connection_type; + g_return_val_if_fail (connection_type, FALSE); + + s_con = nm_connection_get_setting_connection (slave); + g_assert (s_con); + slave_type = nm_setting_connection_get_slave_type (s_con); + if (!slave_type) + return FALSE; + + return strcmp (connection_type, slave_type) == 0; +} + /** * nm_device_can_assume_connections: * @self: #NMDevice instance diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 55f05c4fab..a92719cd8a 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -388,6 +388,7 @@ void nm_device_capture_initial_config (NMDevice *dev); /* Master */ GSList * nm_device_master_get_slaves (NMDevice *dev); +gboolean nm_device_is_master (NMDevice *dev); /* Slave */ NMDevice * nm_device_get_master (NMDevice *dev); @@ -421,6 +422,7 @@ gboolean nm_device_complete_connection (NMDevice *device, GError **error); gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection); +gboolean nm_device_check_slave_connection_compatible (NMDevice *device, NMConnection *connection); gboolean nm_device_uses_assumed_connection (NMDevice *device); diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c index e6645bdbc4..30e98fcacb 100644 --- a/src/nm-activation-request.c +++ b/src/nm-activation-request.c @@ -468,9 +468,7 @@ master_failed (NMActiveConnection *self) * @specific_object: the object path of the specific object (ie, WiFi access point, * etc) that will be used to activate @connection and @device * @subject: the #NMAuthSubject representing the requestor of the activation - * @device: the device/interface to configure according to @connection; or %NULL - * if the connection describes a software device which will be created during - * connection activation + * @device: the device/interface to configure according to @connection * * Creates a new device-based activation request. * @@ -483,7 +481,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection, NMDevice *device) { g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL); - g_return_val_if_fail (!device || NM_IS_DEVICE (device), NULL); + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); return (NMActRequest *) g_object_new (NM_TYPE_ACT_REQUEST, diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index 609e645e0a..604e6ec4ec 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -515,8 +515,13 @@ nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device) priv->pending_activation_id = g_strdup_printf ("activation::%p", (void *)self); nm_device_add_pending_action (device, priv->pending_activation_id, TRUE); } - } else + } else { + /* The ActiveConnection's device can only be cleared after the + * connection is activated. + */ + g_warn_if_fail (priv->state > NM_ACTIVE_CONNECTION_STATE_UNKNOWN); priv->device = NULL; + } g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_INT_DEVICE); g_signal_emit (self, signals[DEVICE_CHANGED], 0, priv->device, old_device); @@ -831,6 +836,7 @@ set_property (GObject *object, guint prop_id, } break; case PROP_INT_DEVICE: + /* construct-only */ nm_active_connection_set_device (NM_ACTIVE_CONNECTION (object), g_value_get_object (value)); break; case PROP_INT_SUBJECT: @@ -1129,7 +1135,7 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) (object_class, PROP_INT_DEVICE, g_param_spec_object (NM_ACTIVE_CONNECTION_INT_DEVICE, "", "", NM_TYPE_DEVICE, - G_PARAM_READWRITE | + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property diff --git a/src/nm-manager.c b/src/nm-manager.c index daaca90366..2c0d2418f3 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -499,16 +499,53 @@ find_device_by_ip_iface (NMManager *self, const gchar *iface) return NULL; } +/** + * find_device_by_iface: + * @self: the #NMManager + * @iface: the device interface to find + * @connection: a connection to ensure the returned device is compatible with + * @slave: a slave connection to ensure a master is compatible with + * + * Finds a device by interface name, preferring realized devices. If @slave + * is given, this function will only return master devices and will ensure + * @slave, when activated, can be a slave of the returned master device. If + * @connection is given, this function will only consider devices that are + * compatible with @connection. + * + * Returns: the matching #NMDevice + */ static NMDevice * -find_device_by_iface (NMManager *self, const gchar *iface) +find_device_by_iface (NMManager *self, + const char *iface, + NMConnection *connection, + NMConnection *slave) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *fallback = NULL; GSList *iter; - for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) { - if (g_strcmp0 (nm_device_get_iface (NM_DEVICE (iter->data)), iface) == 0) - return NM_DEVICE (iter->data); + g_return_val_if_fail (iface != NULL, NULL); + + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *candidate = iter->data; + + if (strcmp (nm_device_get_iface (candidate), iface)) + continue; + if (connection && !nm_device_check_connection_compatible (candidate, connection)) + continue; + if (slave) { + if (!nm_device_is_master (candidate)) + continue; + if (!nm_device_check_slave_connection_compatible (candidate, slave)) + continue; + } + + if (nm_device_is_real (candidate)) + return candidate; + else if (!fallback) + fallback = candidate; } - return NULL; + return fallback; } static gboolean @@ -860,8 +897,8 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection) if (!parent_name) return NULL; - /* Try as an interface name */ - parent = find_device_by_iface (self, parent_name); + /* Try as an interface name of a parent device */ + parent = find_device_by_iface (self, parent_name, NULL, NULL); if (parent) return parent; @@ -988,18 +1025,16 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError if (!iface) return NULL; - /* Make sure we didn't create a device for this connection already */ + /* If some other device is already compatible with this connection, + * don't create a new device for it. + */ for (iter = priv->devices; iter; iter = g_slist_next (iter)) { NMDevice *candidate = iter->data; if ( g_strcmp0 (nm_device_get_iface (candidate), iface) == 0 - || nm_device_check_connection_compatible (candidate, connection)) { + && nm_device_check_connection_compatible (candidate, connection)) { nm_log_dbg (LOGD_DEVICE, "(%s) already created virtual interface name %s", nm_connection_get_id (connection), iface); - g_set_error (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "interface name '%s' already created", iface); return NULL; } } @@ -1665,7 +1700,8 @@ device_ip_iface_changed (NMDevice *device, NMDevice *candidate = NM_DEVICE (iter->data); if ( candidate != device - && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0) { + && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0 + && nm_device_is_real (candidate)) { remove_device (self, candidate, FALSE, FALSE); break; } @@ -1728,8 +1764,6 @@ add_device (NMManager *self, NMDevice *device) ifindex = nm_device_get_ifindex (device); if (ifindex > 0 && nm_manager_get_device_by_ifindex (self, ifindex)) return; - if (find_device_by_iface (self, nm_device_get_iface (device))) - return; /* Remove existing devices owned by the new device; eg remove ethernet * ports that are owned by a WWAN modem, since udev may announce them @@ -1739,9 +1773,11 @@ add_device (NMManager *self, NMDevice *device) * the child NMDevice entirely */ for (iter = priv->devices; iter; iter = iter->next) { - iface = nm_device_get_ip_iface (iter->data); - if (nm_device_owns_iface (device, iface)) - remove = g_slist_prepend (remove, iter->data); + NMDevice *candidate = iter->data; + + iface = nm_device_get_ip_iface (candidate); + if (nm_device_is_real (candidate) && nm_device_owns_iface (device, iface)) + remove = g_slist_prepend (remove, candidate); } for (iter = remove; iter; iter = iter->next) remove_device (self, NM_DEVICE (iter->data), FALSE, FALSE); @@ -1881,37 +1917,37 @@ platform_link_added (NMManager *self, NMDevice *device = NULL; GError *error = NULL; gboolean nm_plugin_missing = FALSE; + GSList *iter; g_return_if_fail (ifindex > 0); if (nm_manager_get_device_by_ifindex (self, ifindex)) return; - device = find_device_by_iface (self, plink->name); - if (device) { - gboolean compatible = FALSE; + /* Let unrealized devices try to realize themselves with the link */ + for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) { + NMDevice *candidate = iter->data; + gboolean compatible = TRUE; - if (nm_device_is_real (device)) - return; + if (strcmp (nm_device_get_iface (candidate), plink->name)) + continue; - if (nm_device_realize (device, plink, &compatible, &error)) { - /* Success */ - nm_device_setup_finish (device, plink); - return; - } - - nm_log_warn (LOGD_DEVICE, "(%s): %s", plink->name, error->message); - remove_device (self, device, FALSE, FALSE); - g_clear_error (&error); - - if (compatible) { - /* Device compatible with platform link, but some other fatal error - * happened during realization. + if (nm_device_is_real (candidate)) { + /* Ignore the link added event since there's already a realized + * device with the link's name. */ return; + } else if (nm_device_realize (candidate, plink, &compatible, &error)) { + /* Success */ + nm_device_setup_finish (candidate, plink); + return; } - /* Fall through and create new compatible device for the link */ + nm_log_dbg (LOGD_DEVICE, "(%s): failed to realize from plink: '%s'", + plink->name, error->message); + g_clear_error (&error); + + /* Try next unrealized device */ } /* Try registered device factories */ @@ -2245,7 +2281,7 @@ find_master (NMManager *self, return TRUE; /* success, but no master */ /* Try as an interface name first */ - master_device = find_device_by_iface (self, master); + master_device = find_device_by_iface (self, master, NULL, connection); if (master_device) { if (master_device == device) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED, @@ -2620,8 +2656,10 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * NMConnection *applied; NMSettingsConnection *connection; NMSettingsConnection *master_connection = NULL; + NMConnection *existing_connection = NULL; NMActiveConnection *master_ac = NULL; - GError *local_err = NULL; + NMAuthSubject *subject; + char *error_desc = NULL; g_return_val_if_fail (NM_IS_MANAGER (self), FALSE); g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (active), FALSE); @@ -2635,72 +2673,26 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * applied = nm_active_connection_get_applied_connection (active); device = nm_active_connection_get_device (active); - if (!device) { - if (!nm_connection_is_virtual (applied)) { - NMSettingConnection *s_con = nm_connection_get_setting_connection (applied); + g_return_val_if_fail (device != NULL, FALSE); - g_assert (s_con); - g_set_error (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Unsupported virtual interface type '%s'", - nm_setting_connection_get_connection_type (s_con)); - return FALSE; - } - - device = system_create_virtual_device (self, applied, &local_err); - if (!device) { - g_set_error (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Failed to create virtual interface: %s", - local_err ? local_err->message : "(unknown)"); - g_clear_error (&local_err); - return FALSE; - } - - if (!nm_active_connection_set_device (active, device)) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "The device could not be activated with this connection"); - return FALSE; - } - - /* A newly created device, if allowed to be managed by NM, will be - * in the UNAVAILABLE state here. To ensure it can be activated - * immediately, we transition it to DISCONNECTED. - */ - if ( nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE) - && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) { - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_NONE); - } - } else { - NMConnection *existing_connection = NULL; - NMAuthSubject *subject; - char *error_desc = NULL; - - /* If the device is active and its connection is not visible to the - * user that's requesting this new activation, fail, since other users - * should not be allowed to implicitly deactivate private connections - * by activating a connection of their own. - */ - existing_connection = nm_device_get_applied_connection (device); - subject = nm_active_connection_get_subject (active); - if (existing_connection && - !nm_auth_is_subject_in_acl (existing_connection, - subject, - &error_desc)) { - g_set_error (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Private connection already active on the device: %s", - error_desc); - g_free (error_desc); - return FALSE; - } + /* If the device is active and its connection is not visible to the + * user that's requesting this new activation, fail, since other users + * should not be allowed to implicitly deactivate private connections + * by activating a connection of their own. + */ + existing_connection = nm_device_get_applied_connection (device); + subject = nm_active_connection_get_subject (active); + if (existing_connection && + !nm_auth_is_subject_in_acl (existing_connection, + subject, + &error_desc)) { + g_set_error (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Private connection already active on the device: %s", + error_desc); + g_free (error_desc); + return FALSE; } /* Final connection must be available on device */ @@ -2777,9 +2769,10 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * } nm_active_connection_set_master (active, master_ac); - nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %p", + nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %p %s", nm_settings_connection_get_id (connection), - master_ac); + master_ac, + str_if_set (nm_exported_object_get_path (NM_EXPORTED_OBJECT (master_ac)), "")); } /* Check slaves for master connection and possibly activate them */ @@ -2790,6 +2783,19 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * if (existing) nm_device_steal_connection (existing, connection); + 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_NONE) + && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_USER_REQUESTED); + } + /* Export the new ActiveConnection to clients and start it on the device */ nm_exported_object_export (NM_EXPORTED_OBJECT (active)); g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); @@ -3042,6 +3048,23 @@ nm_manager_activate_connection (NMManager *self, return active; } +/** + * validate_activation_request: + * @self: the #NMManager + * @context: the D-Bus context of the requestor + * @connection: the partial or complete #NMConnection to be activated + * @device_path: the object path of the device to be activated, or "/" + * @out_device: on successful reutrn, the #NMDevice to be activated with @connection + * @out_vpn: on successful return, %TRUE if @connection is a VPN connection + * @error: location to store an error on failure + * + * Performs basic validation on an activation request, including ensuring that + * the requestor is a valid Unix process, is not disallowed in @connection + * permissions, and that a device exists that can activate @connection. + * + * Returns: on success, the #NMAuthSubject representing the requestor, or + * %NULL on error + */ static NMAuthSubject * validate_activation_request (NMManager *self, GDBusMethodInvocation *context, @@ -3116,11 +3139,11 @@ validate_activation_request (NMManager *self, } else device = nm_manager_get_best_device_for_connection (self, connection, TRUE); - if (!device) { + if (!device && !vpn) { gboolean is_software = nm_connection_is_virtual (connection); /* VPN and software-device connections don't need a device yet */ - if (!vpn && !is_software) { + if (!is_software) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, @@ -3136,11 +3159,19 @@ validate_activation_request (NMManager *self, if (!iface) goto error; - device = find_device_by_iface (self, iface); + device = find_device_by_iface (self, iface, connection, NULL); g_free (iface); } } + if ((!vpn || device_path) && !device) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Failed to find a compatible device for this connection"); + goto error; + } + *out_device = device; *out_vpn = vpn; return subject; @@ -3454,14 +3485,6 @@ impl_manager_add_and_activate_connection (NMManager *self, if (!subject) goto error; - /* AddAndActivate() requires a device to complete the connection with */ - if (!device) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "This connection requires an existing device."); - goto error; - } - all_connections = nm_settings_get_connections (priv->settings); if (vpn) { /* Try to fill the VPN's connection setting and name at least */