summary refs log tree commit diff
path: root/src/nm-manager.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2017-01-17 20:25:09 +0100
committerMichael Biebl <biebl@debian.org>2017-01-17 20:25:09 +0100
commit58f8be580039b0575b197b9573a1c92745d96d30 (patch)
tree2c226233f623a0dcb529be0eb8cdf97e4a2ae0c0 /src/nm-manager.c
parent45cb5bb3c0e6edb887cf69b417fcaf7053814a9b (diff)
New upstream version 1.5.90 upstream/1.5.90
Diffstat (limited to 'src/nm-manager.c')
-rw-r--r--src/nm-manager.c367
1 files changed, 299 insertions, 68 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c
index c3d65cd1..867e0db6 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -31,24 +31,23 @@
 
 #include "nm-common-macros.h"
 #include "nm-bus-manager.h"
-#include "nm-vpn-manager.h"
-#include "nm-device.h"
-#include "nm-device-generic.h"
-#include "nm-platform.h"
+#include "vpn/nm-vpn-manager.h"
+#include "devices/nm-device.h"
+#include "devices/nm-device-generic.h"
+#include "platform/nm-platform.h"
 #include "nm-rfkill-manager.h"
-#include "nm-dhcp-manager.h"
-#include "nm-settings.h"
-#include "nm-settings-connection.h"
+#include "dhcp/nm-dhcp-manager.h"
+#include "settings/nm-settings.h"
+#include "settings/nm-settings-connection.h"
 #include "nm-auth-utils.h"
 #include "nm-auth-manager.h"
 #include "NetworkManagerUtils.h"
-#include "nm-device-factory.h"
-#include "nm-enum-types.h"
+#include "devices/nm-device-factory.h"
 #include "nm-sleep-monitor.h"
 #include "nm-connectivity.h"
 #include "nm-policy.h"
 #include "nm-session-monitor.h"
-#include "nm-activation-request.h"
+#include "nm-act-request.h"
 #include "nm-core-internal.h"
 #include "nm-config.h"
 #include "nm-audit-manager.h"
@@ -57,13 +56,14 @@
 #include "nm-checkpoint-manager.h"
 #include "NetworkManagerUtils.h"
 
-#include "nmdbus-manager.h"
-#include "nmdbus-device.h"
+#include "introspection/org.freedesktop.NetworkManager.h"
+#include "introspection/org.freedesktop.NetworkManager.Device.h"
 
 static gboolean add_device (NMManager *self, NMDevice *device, GError **error);
 
 static NMActiveConnection *_new_active_connection (NMManager *self,
                                                    NMConnection *connection,
+                                                   NMConnection *applied,
                                                    const char *specific_object,
                                                    NMDevice *device,
                                                    NMAuthSubject *subject,
@@ -100,6 +100,8 @@ typedef struct {
 } RadioState;
 
 typedef struct {
+	GArray *capabilities;
+
 	GSList *active_connections;
 	GSList *authorizing_connections;
 	guint ac_cleanup_id;
@@ -180,6 +182,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMManager,
 	PROP_VERSION,
+	PROP_CAPABILITIES,
 	PROP_STATE,
 	PROP_STARTUP,
 	PROP_NETWORKING_ENABLED,
@@ -206,7 +209,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMManager,
 
 NM_DEFINE_SINGLETON_INSTANCE (NMManager);
 
-/************************************************************************/
+/*****************************************************************************/
 
 #define _NMLOG_PREFIX_NAME      "manager"
 #define _NMLOG(level, domain, ...) \
@@ -228,7 +231,10 @@ NM_DEFINE_SINGLETON_INSTANCE (NMManager);
         } \
     } G_STMT_END
 
-/************************************************************************/
+/*****************************************************************************/
+
+GQuark autoconnect_root_quark (void);
+G_DEFINE_QUARK (autoconnect-root, autoconnect_root);
 
 static void active_connection_state_changed (NMActiveConnection *active,
                                              GParamSpec *pspec,
@@ -312,6 +318,7 @@ active_connection_state_changed (NMActiveConnection *active,
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMActiveConnectionState state;
+	NMSettingsConnection *con;
 
 	state = nm_active_connection_get_state (active);
 	if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
@@ -322,6 +329,10 @@ active_connection_state_changed (NMActiveConnection *active,
 		 */
 		if (!priv->ac_cleanup_id)
 			priv->ac_cleanup_id = g_idle_add (_active_connection_cleanup, self);
+
+		con = nm_active_connection_get_settings_connection (active);
+		if (con)
+			g_object_set_qdata (G_OBJECT (con), autoconnect_root_quark (), NULL);
 	}
 
 	nm_manager_update_state (self);
@@ -456,7 +467,7 @@ active_connection_get_by_path (NMManager *manager, const char *path)
 	return NULL;
 }
 
-/************************************************************************/
+/*****************************************************************************/
 
 static void
 _config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeFlags changes, NMConfigData *old_data, NMManager *self)
@@ -569,7 +580,7 @@ impl_manager_reload (NMManager *self,
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_RELOAD, TRUE);
 }
 
-/************************************************************************/
+/*****************************************************************************/
 
 NMDevice *
 nm_manager_get_device_by_path (NMManager *manager, const char *path)
@@ -939,6 +950,26 @@ settings_startup_complete_changed (NMSettings *settings,
 }
 
 static void
+_parent_notify_changed (NMManager *self,
+                        NMDevice *device,
+                        gboolean device_removed)
+{
+	GSList *iter;
+
+	nm_assert (NM_IS_DEVICE (device));
+	nm_assert (NM_IS_MANAGER (self));
+
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; ) {
+		if (nm_device_parent_notify_changed (iter->data, device, device_removed)) {
+			/* in the unlikely event that this changes anything, we start iterating
+			 * again, to be sure that the device list is up-to-date. */
+			iter = NM_MANAGER_GET_PRIVATE (self)->devices;
+		} else
+			iter = iter->next;
+	}
+}
+
+static void
 remove_device (NMManager *self,
                NMDevice *device,
                gboolean quitting,
@@ -974,6 +1005,8 @@ remove_device (NMManager *self,
 	nm_settings_device_removed (priv->settings, device, quitting);
 	priv->devices = g_slist_remove (priv->devices, device);
 
+	_parent_notify_changed (self, device, TRUE);
+
 	if (nm_device_is_real (device)) {
 		gboolean unconfigure_ip_config = !quitting || unmanage;
 
@@ -1013,7 +1046,7 @@ nm_manager_get_state (NMManager *manager)
 	return NM_MANAGER_GET_PRIVATE (manager)->state;
 }
 
-/***************************/
+/*****************************************************************************/
 
 static NMDevice *
 find_parent_device_for_connection (NMManager *self, NMConnection *connection, NMDeviceFactory *cached_factory)
@@ -1108,7 +1141,7 @@ nm_manager_get_connection_iface (NMManager *self,
 	}
 
 	if (   !out_parent
-	    && !NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_connection_iface) {
+	    && !NM_DEVICE_FACTORY_GET_CLASS (factory)->get_connection_iface) {
 		/* optimization. Shortcut lookup of the partent device. */
 		iface = g_strdup (nm_connection_get_interface_name (connection));
 		if (!iface) {
@@ -1135,6 +1168,30 @@ nm_manager_get_connection_iface (NMManager *self,
 }
 
 /**
+ * nm_manager_iface_for_uuid:
+ * @self: the #NMManager
+ * @uuid: the connection uuid
+ *
+ * Gets a link name for the given UUID. Useful for the settings plugins that
+ * wish to write configuration files compatible with tooling that can't
+ * interpret our UUIDs.
+ *
+ * Returns: An interface name; %NULL if none matches
+ */
+const char *
+nm_manager_iface_for_uuid (NMManager *self, const char *uuid)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMSettingsConnection *connection;
+
+	connection = nm_settings_get_connection_by_uuid (priv->settings, uuid);
+	if (!connection)
+		return NULL;
+
+	return nm_connection_get_interface_name (NM_CONNECTION (connection));
+}
+
+/**
  * system_create_virtual_device:
  * @self: the #NMManager
  * @connection: the connection which might require a virtual device
@@ -1358,9 +1415,9 @@ system_hostname_changed_cb (NMSettings *settings,
 	nm_dhcp_manager_set_default_hostname (nm_dhcp_manager_get (), priv->hostname);
 }
 
-/*******************************************************************/
+/*****************************************************************************/
 /* General NMManager stuff                                         */
-/*******************************************************************/
+/*****************************************************************************/
 
 static gboolean
 radio_enabled_for_rstate (RadioState *rstate, gboolean check_changeable)
@@ -1741,7 +1798,7 @@ assume_connection (NMManager *self, NMDevice *device, NMSettingsConnection *conn
 	g_return_val_if_fail (nm_device_get_state (device) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
 
 	subject = nm_auth_subject_new_internal ();
-	active = _new_active_connection (self, NM_CONNECTION (connection), NULL, device, subject, &error);
+	active = _new_active_connection (self, NM_CONNECTION (connection), NULL, NULL, device, subject, &error);
 	g_object_unref (subject);
 
 	if (!active) {
@@ -1826,6 +1883,14 @@ recheck_assume_connection_cb (NMDevice *device, gpointer user_data)
 }
 
 static void
+device_ifindex_changed (NMDevice *device,
+                        GParamSpec *pspec,
+                        NMManager *self)
+{
+	_parent_notify_changed (self, device, FALSE);
+}
+
+static void
 device_ip_iface_changed (NMDevice *device,
                          GParamSpec *pspec,
                          NMManager *self)
@@ -1963,6 +2028,10 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	                  G_CALLBACK (device_ip_iface_changed),
 	                  self);
 
+	g_signal_connect (device, "notify::" NM_DEVICE_IFINDEX,
+	                  G_CALLBACK (device_ifindex_changed),
+	                  self);
+
 	g_signal_connect (device, "notify::" NM_DEVICE_IFACE,
 	                  G_CALLBACK (device_iface_changed),
 	                  self);
@@ -2005,12 +2074,7 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	g_signal_emit (self, signals[INTERNAL_DEVICE_ADDED], 0, device);
 	_notify (self, PROP_ALL_DEVICES);
 
-	for (iter = priv->devices; iter; iter = iter->next) {
-		NMDevice *d = iter->data;
-
-		if (d != device)
-			nm_device_notify_new_device_added (d, device);
-	}
+	_parent_notify_changed (self, device, FALSE);
 
 	/* Virtual connections may refer to the new device as
 	 * parent device, retry to activate them.
@@ -2020,7 +2084,7 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	return TRUE;
 }
 
-/*******************************************************************/
+/*****************************************************************************/
 
 static void
 factory_device_added_cb (NMDeviceFactory *factory,
@@ -2074,16 +2138,16 @@ _register_device_factory (NMDeviceFactory *factory, gpointer user_data)
 	                  self);
 }
 
-/*******************************************************************/
+/*****************************************************************************/
 
 static void
 platform_link_added (NMManager *self,
                      int ifindex,
-                     const NMPlatformLink *plink)
+                     const NMPlatformLink *plink,
+                     const NMConfigDeviceStateData *dev_state)
 {
 	NMDeviceFactory *factory;
 	NMDevice *device = NULL;
-	gboolean nm_plugin_missing = FALSE;
 	GSList *iter;
 
 	g_return_if_fail (ifindex > 0);
@@ -2137,6 +2201,8 @@ platform_link_added (NMManager *self,
 	}
 
 	if (device == NULL) {
+		gboolean nm_plugin_missing = FALSE;
+
 		switch (plink->type) {
 		case NM_LINK_TYPE_WWAN_NET:
 		case NM_LINK_TYPE_BNEP:
@@ -2148,7 +2214,7 @@ platform_link_added (NMManager *self,
 			nm_plugin_missing = TRUE;
 			/* fall through */
 		default:
-			device = nm_device_generic_new (plink);
+			device = nm_device_generic_new (plink, nm_plugin_missing);
 			break;
 		}
 	}
@@ -2156,8 +2222,6 @@ platform_link_added (NMManager *self,
 	if (device) {
 		gs_free_error GError *error = NULL;
 
-		if (nm_plugin_missing)
-			nm_device_set_nm_plugin_missing (device, TRUE);
 		if (nm_device_realize_start (device, plink, NULL, &error)) {
 			add_device (self, device, NULL);
 			_device_realize_finish (self, device, plink);
@@ -2190,7 +2254,7 @@ _platform_link_cb_idle (PlatformLinkCbData *data)
 		NMPlatformLink pllink;
 
 		pllink = *l; /* make a copy of the link instance */
-		platform_link_added (self, data->ifindex, &pllink);
+		platform_link_added (self, data->ifindex, &pllink, NULL);
 	} else {
 		NMDevice *device;
 		GError *error = NULL;
@@ -2220,12 +2284,13 @@ out:
 
 static void
 platform_link_cb (NMPlatform *platform,
-                  NMPObjectType obj_type,
+                  int obj_type_i,
                   int ifindex,
                   NMPlatformLink *plink,
-                  NMPlatformSignalChangeType change_type,
+                  int change_type_i,
                   gpointer user_data)
 {
+	const NMPlatformSignalChangeType change_type = change_type_i;
 	PlatformLinkCbData *data;
 
 	switch (change_type) {
@@ -2245,14 +2310,24 @@ platform_link_cb (NMPlatform *platform,
 static void
 platform_query_devices (NMManager *self)
 {
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GArray *links_array;
 	NMPlatformLink *links;
 	int i;
 
 	links_array = nm_platform_link_get_all (NM_PLATFORM_GET);
 	links = (NMPlatformLink *) links_array->data;
-	for (i = 0; i < links_array->len; i++)
-		platform_link_added (self, links[i].ifindex, &links[i]);
+	for (i = 0; i < links_array->len; i++) {
+		gs_free NMConfigDeviceStateData *dev_state = NULL;
+
+		dev_state = nm_config_device_state_load (priv->config,
+		                                         links[i].ifindex);
+
+		platform_link_added (self,
+		                     links[i].ifindex,
+		                     &links[i],
+		                     dev_state);
+	}
 
 	g_array_unref (links_array);
 }
@@ -2274,6 +2349,28 @@ nm_manager_get_devices (NMManager *manager)
 	return NM_MANAGER_GET_PRIVATE (manager)->devices;
 }
 
+const char **
+nm_manager_get_device_paths (NMManager *self)
+{
+	const GSList *devices, *iter;
+	GPtrArray *paths;
+	const char *path;
+
+	g_return_val_if_fail (NM_IS_MANAGER (self), NULL);
+	devices = NM_MANAGER_GET_PRIVATE (self)->devices;
+	paths = g_ptr_array_new ();
+
+	for (iter = devices; iter; iter = g_slist_next (iter)) {
+		path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data));
+		if (path)
+			g_ptr_array_add (paths, (gpointer) path);
+	}
+
+	g_ptr_array_add (paths, NULL);
+
+	return (const char **) g_ptr_array_free (paths, FALSE);
+}
+
 static NMDevice *
 nm_manager_get_connection_device (NMManager *self,
                                   NMConnection *connection)
@@ -2598,6 +2695,7 @@ ensure_master_active_connection (NMManager *self,
 					master_ac = nm_manager_activate_connection (self,
 					                                            candidate,
 					                                            NULL,
+					                                            NULL,
 					                                            master_device,
 					                                            subject,
 					                                            error);
@@ -2644,6 +2742,7 @@ ensure_master_active_connection (NMManager *self,
 			master_ac = nm_manager_activate_connection (self,
 			                                            master_connection,
 			                                            NULL,
+			                                            NULL,
 			                                            candidate,
 			                                            subject,
 			                                            error);
@@ -2680,14 +2779,9 @@ find_slaves (NMManager *manager,
 	GSList *all_connections, *iter;
 	GSList *slaves = NULL;
 	NMSettingConnection *s_con;
-	const char *master;
 
 	s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
 	g_assert (s_con);
-	master = nm_setting_connection_get_master (s_con);
-
-	if (master != NULL)
-		return NULL;  /* connection is not master */
 
 	/* Search through all connections, not only inactive ones, because
 	 * even if a slave was already active, it might be deactivated during
@@ -2756,8 +2850,34 @@ autoconnect_slaves (NMManager *self,
 
 		while (iter) {
 			NMSettingsConnection *slave_connection = iter->data;
+			const char *uuid;
 
 			iter = iter->next;
+
+			/* To avoid loops when autoconnecting slaves, we propagate
+			 * the UUID of the initial connection down to slaves until
+			 * the same connection is found.
+			 */
+			uuid = g_object_get_qdata (G_OBJECT (master_connection),
+			                           autoconnect_root_quark ());
+			if (nm_streq0 (nm_settings_connection_get_uuid (slave_connection), uuid)) {
+				_LOGI (LOGD_CORE,
+				       "will NOT activate slave connection '%s' (%s) as a dependency for master '%s' (%s): "
+				       "circular dependency detected",
+				       nm_settings_connection_get_id (slave_connection),
+				       nm_settings_connection_get_uuid (slave_connection),
+				       nm_settings_connection_get_id (master_connection),
+				       nm_settings_connection_get_uuid (master_connection));
+				continue;
+			}
+
+			if (!uuid)
+				uuid = nm_settings_connection_get_uuid (master_connection);
+			g_object_set_qdata_full (G_OBJECT (slave_connection),
+			                         autoconnect_root_quark (),
+			                         g_strdup (uuid),
+			                         g_free);
+
 			_LOGD (LOGD_CORE, "will activate slave connection '%s' (%s) as a dependency for master '%s' (%s)",
 			       nm_settings_connection_get_id (slave_connection),
 			       nm_settings_connection_get_uuid (slave_connection),
@@ -2768,6 +2888,7 @@ autoconnect_slaves (NMManager *self,
 			nm_manager_activate_connection (self,
 			                                slave_connection,
 			                                NULL,
+			                                NULL,
 			                                nm_manager_get_best_device_for_connection (self, NM_CONNECTION (slave_connection), FALSE),
 			                                subject,
 			                                &local_err);
@@ -2927,7 +3048,7 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 				return FALSE;
 			}
 
-			parent_ac = nm_manager_activate_connection (self, parent_con, NULL, parent, subject, error);
+			parent_ac = nm_manager_activate_connection (self, parent_con, NULL, NULL, parent, subject, error);
 			if (!parent_ac) {
 				g_prefix_error (error, "%s failed to activate parent: ", nm_device_get_iface (device));
 				return FALSE;
@@ -3109,6 +3230,7 @@ _new_vpn_active_connection (NMManager *self,
 static NMActiveConnection *
 _new_active_connection (NMManager *self,
                         NMConnection *connection,
+                        NMConnection *applied,
                         const char *specific_object,
                         NMDevice *device,
                         NMAuthSubject *subject,
@@ -3148,6 +3270,7 @@ _new_active_connection (NMManager *self,
 	}
 
 	return (NMActiveConnection *) nm_act_request_new (settings_connection,
+	                                                  applied,
 	                                                  specific_object,
 	                                                  subject,
 	                                                  device);
@@ -3198,6 +3321,7 @@ _internal_activation_auth_done (NMActiveConnection *active,
  * nm_manager_activate_connection():
  * @self: the #NMManager
  * @connection: the #NMSettingsConnection to activate on @device
+ * @applied: (allow-none): the applied connection to activate on @device
  * @specific_object: the specific object path, if any, for the activation
  * @device: the #NMDevice to activate @connection on
  * @subject: the subject which requested activation
@@ -3207,7 +3331,8 @@ _internal_activation_auth_done (NMActiveConnection *active,
  * @subject should be the subject of the activation that triggered this
  * one, or if this is an autoconnect request, a new internal subject.
  * The returned #NMActiveConnection is owned by the Manager and should be
- * referenced by the caller if the caller continues to use it.
+ * referenced by the caller if the caller continues to use it. If @applied
+ * is supplied, it shall not be modified by the caller afterwards.
  *
  * Returns: (transfer none): the new #NMActiveConnection that tracks
  * activation of @connection on @device
@@ -3215,6 +3340,7 @@ _internal_activation_auth_done (NMActiveConnection *active,
 NMActiveConnection *
 nm_manager_activate_connection (NMManager *self,
                                 NMSettingsConnection *connection,
+                                NMConnection *applied,
                                 const char *specific_object,
                                 NMDevice *device,
                                 NMAuthSubject *subject,
@@ -3260,6 +3386,7 @@ nm_manager_activate_connection (NMManager *self,
 
 	active = _new_active_connection (self,
 	                                 NM_CONNECTION (connection),
+	                                 applied,
 	                                 specific_object,
 	                                 device,
 	                                 subject,
@@ -3328,18 +3455,6 @@ validate_activation_request (NMManager *self,
 		goto error;
 	}
 
-	/* Not implemented yet, we want to fail early */
-	if (   nm_connection_get_setting_connection (connection)
-	    && nm_connection_get_setting_ip6_config (connection)
-	    && !strcmp (nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG),
-	                NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
-		g_set_error_literal (error,
-		                     NM_MANAGER_ERROR,
-		                     NM_MANAGER_ERROR_CONNECTION_NOT_AVAILABLE,
-		                     "Sharing IPv6 connections is not supported yet.");
-		goto error;
-	}
-
 	/* Check whether it's a VPN or not */
 	if (   nm_connection_get_setting_vpn (connection)
 	    || nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME))
@@ -3404,7 +3519,7 @@ error:
 	return NULL;
 }
 
-/***********************************************************************/
+/*****************************************************************************/
 
 static void
 _activation_auth_done (NMActiveConnection *active,
@@ -3514,6 +3629,7 @@ impl_manager_activate_connection (NMManager *self,
 
 	active = _new_active_connection (self,
 	                                 NM_CONNECTION (connection),
+	                                 NULL,
 	                                 specific_object_path,
 	                                 device,
 	                                 subject,
@@ -3537,7 +3653,7 @@ error:
 	g_dbus_method_invocation_take_error (context, error);
 }
 
-/***********************************************************************/
+/*****************************************************************************/
 
 typedef struct {
 	NMManager *manager;
@@ -3586,7 +3702,8 @@ activation_add_done (NMSettings *settings,
 
 	g_assert (error);
 	_internal_activation_failed (self, active, error->message);
-	nm_settings_connection_delete (new_connection, NULL, NULL);
+	if (new_connection)
+		nm_settings_connection_delete (new_connection, NULL, NULL);
 	g_dbus_method_invocation_return_gerror (context, error);
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
 	                            NULL,
@@ -3721,6 +3838,7 @@ impl_manager_add_and_activate_connection (NMManager *self,
 
 	active = _new_active_connection (self,
 	                                 connection,
+	                                 NULL,
 	                                 specific_object_path,
 	                                 device,
 	                                 subject,
@@ -3748,7 +3866,7 @@ error:
 	g_dbus_method_invocation_take_error (context, error);
 }
 
-/***********************************************************************/
+/*****************************************************************************/
 
 gboolean
 nm_manager_deactivate_connection (NMManager *manager,
@@ -4592,6 +4710,58 @@ start_factory (NMDeviceFactory *factory, gpointer user_data)
 	nm_device_factory_start (factory);
 }
 
+void
+nm_manager_write_device_state (NMManager *self)
+{
+	const GSList *devices;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	gs_unref_hashtable GHashTable *seen_ifindexes = NULL;
+
+	seen_ifindexes = g_hash_table_new (NULL, NULL);
+
+	for (devices = priv->devices; devices; devices = devices->next) {
+		NMDevice *device = NM_DEVICE (devices->data);
+		int ifindex;
+		gboolean managed;
+		NMConnection *settings_connection;
+		const char *uuid = NULL;
+		const char *perm_hw_addr_fake = NULL;
+		gboolean perm_hw_addr_is_fake;
+
+		ifindex = nm_device_get_ip_ifindex (device);
+		if (ifindex <= 0)
+			continue;
+		if (ifindex == 1) {
+			/* ignore loopback */
+			continue;
+		}
+
+		if (!nm_platform_link_get (NM_PLATFORM_GET, ifindex))
+			continue;
+
+		managed = nm_device_get_managed (device, FALSE);
+		if (managed) {
+			settings_connection = NM_CONNECTION (nm_device_get_settings_connection (device));
+			if (settings_connection)
+				uuid = nm_connection_get_uuid (settings_connection);
+		}
+
+		perm_hw_addr_fake = nm_device_get_permanent_hw_address_full (device, FALSE, &perm_hw_addr_is_fake);
+		if (perm_hw_addr_fake && !perm_hw_addr_is_fake)
+			perm_hw_addr_fake = NULL;
+
+		if (nm_config_device_state_write (priv->config,
+		                                  ifindex,
+		                                  managed,
+		                                  perm_hw_addr_fake,
+		                                  uuid))
+			g_hash_table_add (seen_ifindexes, GINT_TO_POINTER (ifindex));
+	}
+
+	nm_config_device_state_prune_unseen (priv->config,
+	                                     seen_ifindexes);
+}
+
 gboolean
 nm_manager_start (NMManager *self, GError **error)
 {
@@ -5088,7 +5258,7 @@ prop_filter (GDBusConnection *connection,
 	return NULL;
 }
 
-/******************************************************************************/
+/*****************************************************************************/
 
 static int
 _set_prop_filter_free2 (gpointer user_data)
@@ -5135,7 +5305,7 @@ _set_prop_filter (NMManager *self, GDBusConnection *connection)
 	}
 }
 
-/******************************************************************************/
+/*****************************************************************************/
 
 static NMCheckpointManager *
 _checkpoint_mgr_get (NMManager *self, gboolean create_as_needed)
@@ -5303,7 +5473,7 @@ impl_manager_checkpoint_rollback (NMManager *self,
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
 }
 
-/******************************************************************************/
+/*****************************************************************************/
 
 static void
 auth_mgr_changed (NMAuthManager *auth_manager, gpointer user_data)
@@ -5320,7 +5490,7 @@ struct rfkill_event {
 	__u8  type;
 	__u8  op;
 	__u8  soft, hard;
-} __attribute__((packed));
+} _nm_packed;
 
 static void
 rfkill_change (NMManager *self, const char *desc, RfKillType rtype, gboolean enabled)
@@ -5446,7 +5616,40 @@ dbus_connection_changed_cb (NMBusManager *dbus_mgr,
 	_set_prop_filter (NM_MANAGER (user_data), connection);
 }
 
-/**********************************************************************/
+/*****************************************************************************/
+
+void
+nm_manager_set_capability (NMManager *self,
+                           NMCapability cap)
+{
+	NMManagerPrivate *priv;
+	guint32 cap_i;
+	gssize idx;
+
+	g_return_if_fail (NM_IS_MANAGER (self));
+	if (cap < 1 || cap > NM_CAPABILITY_TEAM)
+		g_return_if_reached ();
+
+	cap_i = (guint32) cap;
+
+	priv = NM_MANAGER_GET_PRIVATE (self);
+
+	idx = _nm_utils_array_find_binary_search (&g_array_index (priv->capabilities, guint32, 0),
+	                                          sizeof (guint32),
+	                                          priv->capabilities->len,
+	                                          &cap_i,
+	                                          nm_cmp_uint32_p_with_data,
+	                                          NULL);
+	if (idx >= 0)
+		return;
+
+	nm_assert ((~idx) <= (gssize) priv->capabilities->len);
+
+	g_array_insert_val (priv->capabilities, ~idx, cap_i);
+	_notify (self, PROP_CAPABILITIES);
+}
+
+/*****************************************************************************/
 
 NM_DEFINE_SINGLETON_REGISTER (NMManager);
 
@@ -5497,6 +5700,9 @@ constructed (GObject *object)
 	_set_prop_filter (self, nm_bus_manager_get_connection (priv->dbus_mgr));
 
 	priv->settings = nm_settings_new ();
+
+	nm_exported_object_export (NM_EXPORTED_OBJECT (priv->settings));
+
 	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_STARTUP_COMPLETE,
 	                  G_CALLBACK (settings_startup_complete_changed), self);
 	g_signal_connect (priv->settings, "notify::" NM_SETTINGS_UNMANAGED_SPECS,
@@ -5565,6 +5771,8 @@ nm_manager_init (NMManager *self)
 	guint i;
 	GFile *file;
 
+	priv->capabilities = g_array_new (FALSE, FALSE, sizeof (guint32));
+
 	/* Initialize rfkill structures and states */
 	memset (priv->radio_states, 0, sizeof (priv->radio_states));
 
@@ -5652,6 +5860,12 @@ get_property (GObject *object, guint prop_id,
 	case PROP_VERSION:
 		g_value_set_string (value, VERSION);
 		break;
+	case PROP_CAPABILITIES:
+		g_value_set_variant (value, g_variant_new_fixed_array (G_VARIANT_TYPE ("u"),
+		                                                       priv->capabilities->data,
+		                                                       priv->capabilities->len,
+		                                                       sizeof (guint32)));
+		break;
 	case PROP_STATE:
 		g_value_set_uint (value, priv->state);
 		break;
@@ -5871,6 +6085,16 @@ dispose (GObject *object)
 }
 
 static void
+finalize (GObject *object)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE ((NMManager *) object);
+
+	g_array_free (priv->capabilities, TRUE);
+
+	G_OBJECT_CLASS (nm_manager_parent_class)->finalize (object);
+}
+
+static void
 nm_manager_class_init (NMManagerClass *manager_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
@@ -5883,6 +6107,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
+	object_class->finalize = finalize;
 
 	/* properties */
 	obj_properties[PROP_VERSION] =
@@ -5891,6 +6116,13 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                         G_PARAM_READABLE |
 	                         G_PARAM_STATIC_STRINGS);
 
+	obj_properties[PROP_CAPABILITIES] =
+		g_param_spec_variant (NM_MANAGER_CAPABILITIES, "", "",
+		                      G_VARIANT_TYPE ("au"),
+		                      NULL,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS);
+
 	obj_properties[PROP_STATE] =
 	    g_param_spec_uint (NM_MANAGER_STATE, "", "",
 	                       0, NM_STATE_DISCONNECTED, 0,
@@ -6128,4 +6360,3 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                                        "CheckpointRollback", impl_manager_checkpoint_rollback,
 	                                        NULL);
 }
-