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/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/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/libnm-glib/libnm-glib.ver b/libnm-glib/libnm-glib.ver index fc0c0b95c2..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; @@ -158,6 +159,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-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/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-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 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 dbd95922e9..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; @@ -876,6 +877,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; @@ -896,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-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); 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/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index d84bf1bc56..92f0efe209 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -3440,25 +3440,33 @@ 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; + 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; - g_ptr_array_add (paths, g_strdup (nm_exported_object_get_path (object))); + if (filter_func && !filter_func ((GObject *) object, user_data)) + continue; + 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); } /** 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..033c57214f 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: @@ -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 db9f0c60e5..e152bb38b9 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); @@ -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: @@ -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 fe45536609..65f8716d8e 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); } @@ -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; @@ -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..0be55d54f0 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)); @@ -196,14 +196,14 @@ 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; 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..95f69c93c3 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); @@ -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 746fb170d0..84ab7cd69d 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,13 +127,40 @@ update_properties (NMDevice *device) guint32 flow_label = 0; char *key; + if (ifindex <= 0) { +clear: + 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; + goto clear; } parent_ifindex = lnk->parent_ifindex; @@ -175,10 +202,10 @@ 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; + goto clear; } parent_ifindex = lnk->parent_ifindex; @@ -190,10 +217,10 @@ 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; + goto clear; } parent_ifindex = lnk->parent_ifindex; @@ -206,10 +233,10 @@ 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; + goto clear; } parent_ifindex = lnk->parent_ifindex; @@ -261,6 +288,8 @@ update_properties (NMDevice *device) } } +out: + if (priv->ttl != ttl) { priv->ttl = ttl; g_object_notify (object, NM_DEVICE_IP_TUNNEL_TTL); @@ -287,6 +316,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) { @@ -335,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); @@ -470,43 +503,43 @@ 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) { - 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; @@ -720,13 +753,21 @@ 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); } +static void +unrealize (NMDevice *device, gboolean remove_resources) +{ + NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->unrealize (device, remove_resources); + + update_properties_from_ifindex (device, 0); +} + static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) @@ -808,9 +849,15 @@ 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; + 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 978c1bace3..af37c3184d 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" @@ -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,27 +249,47 @@ 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; } +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 @@ -339,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; @@ -349,6 +376,8 @@ 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; /* properties */ 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 55790a9273..734a483bdc 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)); @@ -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 @@ -290,36 +300,31 @@ 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; + return; - if (!NM_IS_DEVICE (component)) - return FALSE; - added_device = NM_DEVICE (component); + 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."); - 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); } /******************************************************************/ @@ -395,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 @@ -659,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)); @@ -670,14 +678,15 @@ 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; parent_class->act_stage1_prepare = act_stage1_prepare; 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-vxlan.c b/src/devices/nm-device-vxlan.c index 7fb55f2086..ad2f2ba44f 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" @@ -133,15 +133,32 @@ 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); } +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); +} /**************************************************************/ @@ -227,10 +244,13 @@ 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; - device_class->setup = setup; + device_class->setup_start = setup_start; + 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 3318e2ef4c..6cbf29065b 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; @@ -554,17 +556,38 @@ 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; } +/** + * 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) { @@ -1129,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; } /** @@ -1362,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)); + } } } @@ -1426,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); @@ -1448,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)) @@ -1539,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; } @@ -1606,26 +1634,76 @@ 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. + * 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)) return FALSE; } - NM_DEVICE_GET_CLASS (self)->setup (self, plink); + NM_DEVICE_GET_CLASS (self)->setup_start (self, plink); return TRUE; } @@ -1648,15 +1726,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; @@ -1719,7 +1802,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; @@ -1728,15 +1811,16 @@ 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) { - 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); } 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); @@ -1821,15 +1905,150 @@ 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); + priv->real = TRUE; +} - if (master > 0) - device_set_master (self, master); +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) +{ + 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; + 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)); +} + +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 + * @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); } /** @@ -1847,8 +2066,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; } @@ -2064,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 @@ -2418,7 +2655,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 */ @@ -2648,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 @@ -2660,7 +2925,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; } /** @@ -6397,14 +6663,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 * @@ -6418,16 +6676,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; } @@ -6462,7 +6725,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; @@ -6558,6 +6821,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); @@ -6565,9 +6830,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 @@ -6575,10 +6842,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; } @@ -8302,6 +8569,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) @@ -9896,6 +10164,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: @@ -9905,12 +10174,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: @@ -10103,7 +10379,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); @@ -10122,6 +10398,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; @@ -10162,7 +10441,9 @@ 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; klass->take_down = take_down; @@ -10412,6 +10693,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 6a5cd46f29..a92719cd8a 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 */ @@ -122,6 +123,7 @@ typedef struct { NMExportedObjectClass parent; const char *connection_type; + const NMLinkType *link_types; void (*state_changed) (NMDevice *device, NMDeviceState new_state, @@ -173,14 +175,39 @@ 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(): + * @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); @@ -288,6 +315,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 @@ -324,6 +353,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); @@ -358,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); @@ -391,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); @@ -451,17 +483,22 @@ 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); gboolean nm_device_realize (NMDevice *device, NMPlatformLink *plink, + gboolean *out_compatible, GError **error); 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); gboolean nm_device_get_autoconnect (NMDevice *device); @@ -496,6 +533,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/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index a6679e422e..fdc603364d 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: @@ -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 70bb6d628a..72540efed4 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); } @@ -3034,13 +3034,15 @@ 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; 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-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 aa892cc711..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); @@ -598,17 +603,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); } @@ -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 72d9144d84..2c0d2418f3 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, @@ -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, @@ -487,22 +490,62 @@ 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; } +/** + * 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 @@ -797,9 +840,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); @@ -840,6 +887,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; @@ -848,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_ip_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; @@ -964,10 +1013,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); @@ -977,19 +1025,17 @@ 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); - goto out; + return NULL; } } @@ -1003,79 +1049,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 +1618,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); @@ -1664,45 +1700,63 @@ 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; } } } -static gboolean -notify_component_added (NMManager *self, GObject *component) +static void +device_realized (NMDevice *device, + GParamSpec *pspec, + NMManager *self) { - GSList *iter; + int ifindex; + gboolean assumed = FALSE; - 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; + /* 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) + 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); } - return FALSE; } /** * 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; @@ -1710,8 +1764,6 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume) 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 @@ -1721,9 +1773,11 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume) * 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); @@ -1750,6 +1804,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, @@ -1764,62 +1821,39 @@ 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); + g_signal_emit (self, signals[INTERNAL_DEVICE_ADDED], 0, device); + g_object_notify (G_OBJECT (self), NM_MANAGER_ALL_DEVICES); - notify_component_added (self, G_OBJECT (device)); + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *d = iter->data; - /* New devices might be master interfaces for virtual interfaces; so we may - * need to create new virtual interfaces now. - */ - system_create_virtual_devices (self); + if (d != device) + nm_device_notify_new_device_added (d, device); + } } /*******************************************************************/ @@ -1831,9 +1865,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, 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); @@ -1845,7 +1880,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 @@ -1874,12 +1917,39 @@ 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; + /* 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 (strcmp (nm_device_get_iface (candidate), plink->name)) + continue; + + 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; + } + + 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 */ factory = nm_device_factory_manager_find_factory_for_link_type (plink->type); if (factory) { @@ -1916,10 +1986,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, NULL, &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); } @@ -1948,12 +2019,26 @@ _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) - remove_device (self, device, FALSE, TRUE); + 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); + 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); + } + } + 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; @@ -2054,8 +2139,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; @@ -2068,7 +2154,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 + && (all_devices || nm_device_is_real (iter->data))) paths[i++] = path; } paths[i++] = NULL; @@ -2077,6 +2164,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, @@ -2170,7 +2271,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); @@ -2180,7 +2281,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, NULL, connection); if (master_device) { if (master_device == device) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED, @@ -2212,23 +2313,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); } } @@ -2322,7 +2406,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); @@ -2383,9 +2467,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, @@ -2570,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); @@ -2585,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 */ @@ -2672,6 +2714,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, @@ -2716,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 */ @@ -2729,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); @@ -2981,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, @@ -3055,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, @@ -3075,11 +3159,19 @@ validate_activation_request (NMManager *self, if (!iface) goto error; - device = find_device_by_ip_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; @@ -3393,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 */ @@ -4198,6 +4282,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)) @@ -4245,11 +4330,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; @@ -5056,6 +5144,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) @@ -5099,7 +5193,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)); @@ -5128,7 +5222,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, device_is_real, NULL); break; case PROP_METERED: g_value_set_uint (value, priv->metered); @@ -5138,6 +5232,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; @@ -5465,7 +5562,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), @@ -5474,6 +5587,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), @@ -5482,6 +5604,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), @@ -5528,6 +5658,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); 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 { 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); diff --git a/tools/test-networkmanager-service.py b/tools/test-networkmanager-service.py index 260810019c..ae63eaf745 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 @@ -638,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' @@ -659,7 +659,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 +680,11 @@ 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='', 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): @@ -799,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') @@ -808,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 @@ -831,9 +838,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):