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):