about summary refs log tree commit diff
path: root/src/nm-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-manager.c')
-rw-r--r--src/nm-manager.c520
1 files changed, 310 insertions, 210 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 8277f6b0..5794bb9a 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -21,13 +21,14 @@
 
 #include "nm-default.h"
 
+#include "nm-manager.h"
+
 #include <stdlib.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
 
-#include "nm-manager.h"
 #include "nm-common-macros.h"
 #include "nm-bus-manager.h"
 #include "nm-vpn-manager.h"
@@ -46,13 +47,14 @@
 #include "nm-sleep-monitor.h"
 #include "nm-connectivity.h"
 #include "nm-policy.h"
-#include "nm-connection-provider.h"
 #include "nm-session-monitor.h"
 #include "nm-activation-request.h"
 #include "nm-core-internal.h"
 #include "nm-config.h"
 #include "nm-audit-manager.h"
 #include "nm-dbus-compat.h"
+#include "nm-checkpoint.h"
+#include "nm-checkpoint-manager.h"
 #include "NetworkManagerUtils.h"
 
 #include "nmdbus-manager.h"
@@ -91,15 +93,13 @@ typedef struct {
 	gboolean sw_enabled;
 	gboolean hw_enabled;
 	RfKillType rtype;
+	NMConfigRunStatePropertyType key;
 	const char *desc;
-	const char *key;
 	const char *prop;
 	const char *hw_prop;
 } RadioState;
 
 typedef struct {
-	char *state_file;
-
 	GSList *active_connections;
 	GSList *authorizing_connections;
 	guint ac_cleanup_id;
@@ -121,6 +121,8 @@ typedef struct {
 	} prop_filter;
 	NMRfkillManager *rfkill_mgr;
 
+	NMCheckpointManager *checkpoint_mgr;
+
 	NMSettings *settings;
 	char *hostname;
 
@@ -132,6 +134,8 @@ typedef struct {
 
 	NMSleepMonitor *sleep_monitor;
 
+	NMAuthManager *auth_mgr;
+
 	GSList *auth_chains;
 	GHashTable *sleep_devices;
 
@@ -145,7 +149,27 @@ typedef struct {
 	gboolean devices_inited;
 } NMManagerPrivate;
 
-#define NM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MANAGER, NMManagerPrivate))
+struct _NMManager {
+	NMExportedObject parent;
+	NMManagerPrivate _priv;
+};
+
+typedef struct {
+	NMExportedObjectClass parent;
+} NMManagerClass;
+
+#define NM_MANAGER_GET_PRIVATE(self) \
+	({ \
+		/* preserve the const-ness of self. Unfortunately, that
+		 * way, @self cannot be a void pointer */ \
+		typeof (self) _self = (self); \
+		\
+		/* Get compiler error if variable is of wrong type */ \
+		_nm_unused const NMManager *_self2 = (_self); \
+		\
+		nm_assert (NM_IS_MANAGER (_self)); \
+		&_self->_priv; \
+	})
 
 G_DEFINE_TYPE (NMManager, nm_manager, NM_TYPE_EXPORTED_OBJECT)
 
@@ -168,7 +192,6 @@ static guint signals[LAST_SIGNAL] = { 0 };
 NM_GOBJECT_PROPERTIES_DEFINE (NMManager,
 	PROP_VERSION,
 	PROP_STATE,
-	PROP_STATE_FILE,
 	PROP_STARTUP,
 	PROP_NETWORKING_ENABLED,
 	PROP_WIRELESS_ENABLED,
@@ -402,7 +425,7 @@ find_ac_for_connection (NMManager *manager, NMConnection *connection)
 }
 
 /* Filter out connections that are already active.
- * nm_settings_get_connections() returns sorted list. We need to preserve the
+ * nm_settings_get_connections_sorted() returns sorted list. We need to preserve the
  * order so that we didn't change auto-activation order (recent timestamps
  * are first).
  * Caller is responsible for freeing the returned list with g_slist_free().
@@ -411,7 +434,7 @@ GSList *
 nm_manager_get_activatable_connections (NMManager *manager)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	GSList *all_connections = nm_settings_get_connections (priv->settings);
+	GSList *all_connections = nm_settings_get_connections_sorted (priv->settings);
 	GSList *connections = NULL, *iter;
 	NMSettingsConnection *connection;
 
@@ -559,7 +582,7 @@ impl_manager_reload (NMManager *self,
 
 /************************************************************************/
 
-static NMDevice *
+NMDevice *
 nm_manager_get_device_by_path (NMManager *manager, const char *path)
 {
 	GSList *iter;
@@ -589,7 +612,7 @@ nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex)
 }
 
 static NMDevice *
-find_device_by_hw_addr (NMManager *manager, const char *hwaddr)
+find_device_by_permanent_hw_addr (NMManager *manager, const char *hwaddr)
 {
 	GSList *iter;
 	const char *device_addr;
@@ -598,7 +621,7 @@ find_device_by_hw_addr (NMManager *manager, const char *hwaddr)
 
 	if (nm_utils_hwaddr_valid (hwaddr, -1)) {
 		for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
-			device_addr = nm_device_get_hw_address (NM_DEVICE (iter->data));
+			device_addr = nm_device_get_permanent_hw_address (NM_DEVICE (iter->data), FALSE);
 			if (device_addr && nm_utils_hwaddr_matches (hwaddr, -1, device_addr, -1))
 				return NM_DEVICE (iter->data);
 		}
@@ -906,7 +929,7 @@ check_if_startup_complete (NMManager *self)
 		g_signal_handlers_disconnect_by_func (dev, G_CALLBACK (device_has_pending_action_changed), self);
 	}
 
-	if (nm_config_get_configure_and_quit (nm_config_get ()))
+	if (nm_config_get_configure_and_quit (priv->config))
 		g_signal_emit (self, signals[CONFIGURE_QUIT], 0);
 }
 
@@ -939,28 +962,12 @@ remove_device (NMManager *self,
 	       nm_device_get_iface (device), allow_unmanage, nm_device_get_managed (device, FALSE));
 
 	if (allow_unmanage && nm_device_get_managed (device, FALSE)) {
-		unmanage = TRUE;
 
-		if (!quitting) {
+		if (quitting)
+			unmanage = nm_device_unmanage_on_quit (device);
+		else {
 			/* the device is already gone. Unmanage it. */
-		} else {
-			/* Leave certain devices alone when quitting so their configuration
-			 * can be taken over when NM restarts.  This ensures connectivity while
-			 * NM is stopped.
-			 */
-			if (nm_device_uses_assumed_connection (device)) {
-				/* An assume connection must be left alone */
-				unmanage = FALSE;
-			} else if (!nm_device_get_act_request (device)) {
-				/* a device without any active connection is either UNAVAILABLE or DISCONNECTED
-				 * state. Since we don't know whether the device was upped by NetworkManager,
-				 * we must leave it up on exit. */
-				unmanage = FALSE;
-			} else if (!nm_platform_link_can_assume (NM_PLATFORM_GET, nm_device_get_ifindex (device))) {
-				/* The device has no layer 3 configuration. Leave it up. */
-				unmanage = FALSE;
-			} else if (nm_device_can_assume_active_connection (device))
-				unmanage = FALSE;
+			unmanage = TRUE;
 		}
 
 		if (unmanage) {
@@ -968,7 +975,7 @@ remove_device (NMManager *self,
 				nm_device_set_unmanaged_by_quitting (device);
 			else
 				nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_PLATFORM_INIT, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
-		} else if (quitting && nm_config_get_configure_and_quit (nm_config_get ())) {
+		} else if (quitting && nm_config_get_configure_and_quit (priv->config)) {
 			nm_device_spawn_iface_helper (device);
 		}
 	}
@@ -1048,7 +1055,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM
 		return parent;
 
 	/* Maybe a hardware address */
-	parent = find_device_by_hw_addr (self, parent_name);
+	parent = find_device_by_permanent_hw_addr (self, parent_name);
 	if (parent)
 		return parent;
 
@@ -1224,7 +1231,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 	}
 
 	/* Create backing resources if the device has any autoconnect connections */
-	connections = nm_settings_get_connections (priv->settings);
+	connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMConnection *candidate = iter->data;
 		NMSettingConnection *s_con;
@@ -1259,7 +1266,7 @@ retry_connections_for_parent_device (NMManager *self, NMDevice *device)
 
 	g_return_if_fail (device);
 
-	connections = nm_settings_get_connections (priv->settings);
+	connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = connections; iter; iter = g_slist_next (iter)) {
 		NMConnection *candidate = iter->data;
 		gs_free_error GError *error = NULL;
@@ -1328,7 +1335,7 @@ system_unmanaged_devices_changed_cb (NMSettings *settings,
 
 	unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
 	for (iter = priv->devices; iter; iter = g_slist_next (iter))
-		nm_device_set_unmanaged_by_user_config (NM_DEVICE (iter->data), unmanaged_specs);
+		nm_device_set_unmanaged_by_user_settings (NM_DEVICE (iter->data), unmanaged_specs);
 }
 
 static void
@@ -1367,54 +1374,6 @@ system_hostname_changed_cb (NMSettings *settings,
 /* General NMManager stuff                                         */
 /*******************************************************************/
 
-/* Store value into key-file; supported types: boolean, int, string */
-static gboolean
-write_value_to_state_file (const char *filename,
-                           const char *group,
-                           const char *key,
-                           GType value_type,
-                           gpointer value,
-                           GError **error)
-{
-	GKeyFile *key_file;
-	char *data;
-	gsize len = 0;
-	gboolean ret = FALSE;
-
-	g_return_val_if_fail (filename != NULL, FALSE);
-	g_return_val_if_fail (group != NULL, FALSE);
-	g_return_val_if_fail (key != NULL, FALSE);
-	g_return_val_if_fail (value_type == G_TYPE_BOOLEAN ||
-	                      value_type == G_TYPE_INT ||
-	                      value_type == G_TYPE_STRING,
-	                      FALSE);
-
-	key_file = g_key_file_new ();
-
-	g_key_file_set_list_separator (key_file, ',');
-	g_key_file_load_from_file (key_file, filename, G_KEY_FILE_KEEP_COMMENTS, NULL);
-	switch (value_type) {
-	case G_TYPE_BOOLEAN:
-		g_key_file_set_boolean (key_file, group, key, *((gboolean *) value));
-		break;
-	case G_TYPE_INT:
-		g_key_file_set_integer (key_file, group, key, *((gint *) value));
-		break;
-	case G_TYPE_STRING:
-		g_key_file_set_string (key_file, group, key, *((const gchar **) value));
-		break;
-	}
-
-	data = g_key_file_to_data (key_file, &len, NULL);
-	if (data) {
-		ret = g_file_set_contents (filename, data, len, error);
-		g_free (data);
-	}
-	g_key_file_free (key_file);
-
-	return ret;
-}
-
 static gboolean
 radio_enabled_for_rstate (RadioState *rstate, gboolean check_changeable)
 {
@@ -2043,7 +2002,7 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	type_desc = nm_device_get_type_desc (device);
 	g_assert (type_desc);
 
-	nm_device_set_unmanaged_by_user_config (device, nm_settings_get_unmanaged_specs (priv->settings));
+	nm_device_set_unmanaged_by_user_settings (device, nm_settings_get_unmanaged_specs (priv->settings));
 
 	nm_device_set_unmanaged_flags (device,
 	                               NM_UNMANAGED_SLEEPING,
@@ -2098,11 +2057,12 @@ factory_component_added_cb (NMDeviceFactory *factory,
                             GObject *component,
                             gpointer user_data)
 {
+	NMManager *self = user_data;
 	GSList *iter;
 
-	g_return_val_if_fail (NM_IS_MANAGER (user_data), FALSE);
+	g_return_val_if_fail (self, FALSE);
 
-	for (iter = NM_MANAGER_GET_PRIVATE (user_data)->devices; iter; iter = iter->next) {
+	for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) {
 		if (nm_device_notify_component_added ((NMDevice *) iter->data, component))
 			return TRUE;
 	}
@@ -2178,6 +2138,9 @@ platform_link_added (NMManager *self,
 			if (!ignore) {
 				_LOGW (LOGD_HW, "%s: factory failed to create device: %s",
 				       plink->name, error->message);
+			} else {
+				_LOGD (LOGD_HW, "%s: factory failed to create device: %s",
+				       plink->name, error->message);
 			}
 			return;
 		}
@@ -2185,7 +2148,7 @@ platform_link_added (NMManager *self,
 
 	if (device == NULL) {
 		switch (plink->type) {
-		case NM_LINK_TYPE_WWAN_ETHERNET:
+		case NM_LINK_TYPE_WWAN_NET:
 		case NM_LINK_TYPE_BNEP:
 		case NM_LINK_TYPE_OLPC_MESH:
 		case NM_LINK_TYPE_TEAM:
@@ -2740,7 +2703,7 @@ find_slaves (NMManager *manager,
 	 * even if a slave was already active, it might be deactivated during
 	 * master reactivation.
 	 */
-	all_connections = nm_settings_get_connections (priv->settings);
+	all_connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = all_connections; iter; iter = iter->next) {
 		NMSettingsConnection *master_connection = NULL;
 		NMDevice *master_device = NULL;
@@ -3474,7 +3437,7 @@ _activation_auth_done (NMActiveConnection *active,
 			g_dbus_method_invocation_return_value (context,
 			                                       g_variant_new ("(o)",
 			                                       nm_exported_object_get_path (NM_EXPORTED_OBJECT (active))));
-			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, TRUE,
+			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, TRUE, NULL,
 			                            subject, NULL);
 			g_object_unref (active);
 			return;
@@ -3486,7 +3449,7 @@ _activation_auth_done (NMActiveConnection *active,
 	}
 
 	g_assert (error);
-	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE,
+	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE, NULL,
 	                            subject, error->message);
 	_internal_activation_failed (self, active, error->message);
 
@@ -3574,7 +3537,7 @@ impl_manager_activate_connection (NMManager *self,
 
 error:
 	if (connection) {
-		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE,
+		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ACTIVATE, connection, FALSE, NULL,
 		                            subject, error->message);
 	}
 	g_clear_object (&active);
@@ -3623,6 +3586,7 @@ activation_add_done (NMSettings *settings,
 			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
 			                            nm_active_connection_get_settings_connection (active),
 			                            TRUE,
+			                            NULL,
 			                            nm_active_connection_get_subject (active),
 			                            NULL);
 			return;
@@ -3637,6 +3601,7 @@ activation_add_done (NMSettings *settings,
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
 	                            NULL,
 	                            FALSE,
+	                            NULL,
 	                            nm_active_connection_get_subject (active),
 	                            error->message);
 	g_clear_error (&local);
@@ -3681,6 +3646,7 @@ _add_and_activate_auth_done (NMActiveConnection *active,
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE,
 		                            NULL,
 		                            FALSE,
+		                            NULL,
 		                            nm_active_connection_get_subject (active),
 		                            error->message);
 		g_dbus_method_invocation_take_error (context, error);
@@ -3732,7 +3698,7 @@ impl_manager_add_and_activate_connection (NMManager *self,
 	if (!subject)
 		goto error;
 
-	all_connections = nm_settings_get_connections (priv->settings);
+	all_connections = nm_settings_get_connections_sorted (priv->settings);
 	if (vpn) {
 		/* Try to fill the VPN's connection setting and name at least */
 		if (!nm_connection_get_setting_vpn (connection)) {
@@ -3782,7 +3748,7 @@ impl_manager_add_and_activate_connection (NMManager *self,
 	return;
 
 error:
-	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, NULL, FALSE, subject, error->message);
+	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, NULL, FALSE, NULL, subject, error->message);
 	g_clear_object (&connection);
 	g_slist_free (all_connections);
 	g_clear_object (&subject);
@@ -3878,6 +3844,7 @@ deactivate_net_auth_done_cb (NMAuthChain *chain,
 		nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE,
 		                            nm_active_connection_get_settings_connection (active),
 		                            !error,
+		                            NULL,
 		                            nm_auth_chain_get_subject (chain),
 		                            error ? error->message : NULL);
 	}
@@ -3951,7 +3918,7 @@ impl_manager_deactivate_connection (NMManager *self,
 done:
 	if (error) {
 		if (connection) {
-			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE, connection, FALSE,
+			nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DEACTIVATE, connection, FALSE, NULL,
 			                            subject, error->message);
 		}
 		g_dbus_method_invocation_take_error (context, error);
@@ -4291,21 +4258,9 @@ static void
 _internal_enable (NMManager *self, gboolean enable)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GError *error = NULL;
 
-	/* Update "NetworkingEnabled" key in state file */
-	if (priv->state_file) {
-		if (!write_value_to_state_file (priv->state_file,
-		                                "main", "NetworkingEnabled",
-		                                G_TYPE_BOOLEAN, (gpointer) &enable,
-		                                &error)) {
-			/* Not a hard error */
-			_LOGW (LOGD_SUSPEND, "writing to state file %s failed: %s",
-			       priv->state_file,
-			       error->message);
-			g_clear_error (&error);
-		}
-	}
+	nm_config_state_set (priv->config, TRUE, FALSE,
+	                     NM_CONFIG_STATE_PROPERTY_NETWORKING_ENABLED, enable);
 
 	_LOGI (LOGD_SUSPEND, "%s requested (sleeping: %s  enabled: %s)",
 	       enable ? "enable" : "disable",
@@ -4457,7 +4412,10 @@ get_permissions_done_cb (NMAuthChain *chain,
 		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM);
 		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN);
 		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME);
+		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS);
 		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_RELOAD);
+		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK);
+		get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS);
 
 		g_dbus_method_invocation_return_value (context,
 		                                       g_variant_new ("(a{ss})", &results));
@@ -4495,7 +4453,10 @@ impl_manager_get_permissions (NMManager *self,
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM, FALSE);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN, FALSE);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, FALSE);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS, FALSE);
 	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_RELOAD, FALSE);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, FALSE);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS, FALSE);
 }
 
 static void
@@ -4513,30 +4474,24 @@ impl_manager_set_logging (NMManager *self,
                           const char *level,
                           const char *domains)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
-	gulong caller_uid = G_MAXULONG;
 
-	if (!nm_bus_manager_get_caller_info (priv->dbus_mgr, context, NULL, &caller_uid, NULL)) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             "Failed to get request UID.");
-		goto done;
-	}
-
-	if (0 != caller_uid) {
-		error = g_error_new_literal (NM_MANAGER_ERROR,
-		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
-		                             "Permission denied");
-		goto done;
-	}
+	/* The permission is already enforced by the D-Bus daemon, but we ensure
+	 * that the caller is still alive so that clients are forced to wait and
+	 * we'll be able to switch to polkit without breaking behavior.
+	 */
+	if (!nm_bus_manager_ensure_uid (nm_bus_manager_get (),
+	                                context,
+	                                G_MAXULONG,
+	                                NM_MANAGER_ERROR,
+	                                NM_MANAGER_ERROR_PERMISSION_DENIED))
+		return;
 
 	if (nm_logging_setup (level, domains, NULL, &error)) {
 		_LOGI (LOGD_CORE, "logging: level '%s' domains '%s'",
 		       nm_logging_level_to_string (), nm_logging_domains_to_string ());
 	}
 
-done:
 	if (error)
 		g_dbus_method_invocation_take_error (context, error);
 	else
@@ -4693,7 +4648,7 @@ nm_manager_start (NMManager *self, GError **error)
 	 * connection-added signals thus devices have to be created manually.
 	 */
 	_LOGD (LOGD_CORE, "creating virtual devices...");
-	connections = nm_settings_get_connections (priv->settings);
+	connections = nm_settings_get_connections_sorted (priv->settings);
 	for (iter = connections; iter; iter = iter->next)
 		connection_changed (self, NM_CONNECTION (iter->data));
 	g_slist_free (connections);
@@ -4956,6 +4911,10 @@ prop_set_auth_done_cb (NMAuthChain *chain,
 		/* ... but set the property on the @object itself. It would be correct to set the property
 		 * on the skeleton interface, but as it is now, the result is the same. */
 		g_object_set (object, pfd->glib_propname, value, NULL);
+	} else if (!strcmp (pfd->glib_propname, NM_DEVICE_STATISTICS_REFRESH_RATE_MS)) {
+		g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32));
+		/* the same here */
+		g_object_set (object, pfd->glib_propname, (guint) g_variant_get_uint32 (value), NULL);
 	} else {
 		g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN));
 		/* the same here */
@@ -5090,6 +5049,15 @@ prop_filter (GDBusConnection *connection,
 		} else
 			return message;
 		interface_type = NMDBUS_TYPE_DEVICE_SKELETON;
+	} else if (!strcmp (propiface, NM_DBUS_INTERFACE_DEVICE_STATISTICS)) {
+		if (!strcmp (propname, "RefreshRateMs")) {
+			glib_propname = NM_DEVICE_STATISTICS_REFRESH_RATE_MS;
+			permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS;
+			audit_op = NM_AUDIT_OP_STATISTICS;
+			expected_type = G_VARIANT_TYPE ("u");
+		} else
+			return message;
+		interface_type = NMDBUS_TYPE_DEVICE_SKELETON;
 	} else
 		return message;
 
@@ -5169,8 +5137,176 @@ _set_prop_filter (NMManager *self, GDBusConnection *connection)
 
 /******************************************************************************/
 
+static NMCheckpointManager *
+_checkpoint_mgr_get (NMManager *self, gboolean create_as_needed)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	if (G_UNLIKELY (!priv->checkpoint_mgr) && create_as_needed)
+		priv->checkpoint_mgr = nm_checkpoint_manager_new (self);
+	return priv->checkpoint_mgr;
+}
+
+static void
+checkpoint_auth_done_cb (NMAuthChain *chain,
+                         GError *auth_error,
+                         GDBusMethodInvocation *context,
+                         gpointer user_data)
+{
+	NMManager *self = NM_MANAGER (user_data);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	char *op, *checkpoint_path = NULL, **devices;
+	NMCheckpoint *checkpoint;
+	NMAuthCallResult result;
+	guint32 timeout, flags;
+	GVariant *variant = NULL;
+	GError *error = NULL;
+	const char *arg = NULL;
+
+	op = nm_auth_chain_get_data (chain, "audit-op");
+	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK);
+
+	if (   nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_DESTROY)
+	    || nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK))
+		arg = checkpoint_path = nm_auth_chain_get_data (chain, "checkpoint_path");
+
+	if (auth_error) {
+		error = g_error_new (NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                     "checkpoint check request failed: %s",
+		                     auth_error->message);
+	} else if (result != NM_AUTH_CALL_RESULT_YES) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Not authorized to checkpoint/rollback");
+	} else {
+		if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_CREATE)) {
+			timeout = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "timeout"));
+			flags = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "flags"));
+			devices = nm_auth_chain_get_data (chain, "devices");
+
+			checkpoint = nm_checkpoint_manager_create (_checkpoint_mgr_get (self, TRUE),
+			                                           (const char *const *) devices,
+			                                           timeout,
+			                                           (NMCheckpointCreateFlags) flags,
+			                                           &error);
+			if (checkpoint) {
+				arg = nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint));
+				variant = g_variant_new ("(o)", arg);
+			}
+		} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_DESTROY)) {
+			nm_checkpoint_manager_destroy (_checkpoint_mgr_get (self, TRUE),
+			                               checkpoint_path, &error);
+		} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK)) {
+			nm_checkpoint_manager_rollback (_checkpoint_mgr_get (self, TRUE),
+			                                checkpoint_path, &variant, &error);
+		} else
+			g_return_if_reached ();
+	}
+
+	nm_audit_log_checkpoint_op (op, arg ?: "", !error, nm_auth_chain_get_subject (chain),
+	                            error ? error->message : NULL);
+
+	if (error)
+		g_dbus_method_invocation_take_error (context, error);
+	else
+		g_dbus_method_invocation_return_value (context, variant);
+
+
+	nm_auth_chain_unref (chain);
+}
+
 static void
-authority_changed_cb (NMAuthManager *auth_manager, gpointer user_data)
+impl_manager_checkpoint_create (NMManager *self,
+                                GDBusMethodInvocation *context,
+                                const char *const *devices,
+                                guint32 rollback_timeout,
+                                guint32 flags)
+{
+	NMManagerPrivate *priv;
+	NMAuthChain *chain;
+	GError *error = NULL;
+
+	G_STATIC_ASSERT_EXPR (sizeof (flags) <= sizeof (NMCheckpointCreateFlags));
+	g_return_if_fail (NM_IS_MANAGER (self));
+	priv = NM_MANAGER_GET_PRIVATE (self);
+
+	chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self);
+	if (!chain) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request.");
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_CREATE, NULL);
+	nm_auth_chain_set_data (chain, "devices", g_strdupv ((char **) devices), (GDestroyNotify) g_strfreev);
+	nm_auth_chain_set_data (chain, "flags",  GUINT_TO_POINTER (flags), NULL);
+	nm_auth_chain_set_data (chain, "timeout", GUINT_TO_POINTER (rollback_timeout), NULL);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
+}
+
+static void
+impl_manager_checkpoint_destroy (NMManager *self,
+                                 GDBusMethodInvocation *context,
+                                 const char *checkpoint_path)
+{
+	NMManagerPrivate *priv;
+	GError *error = NULL;
+	NMAuthChain *chain;
+
+	g_return_if_fail (NM_IS_MANAGER (self));
+	priv = NM_MANAGER_GET_PRIVATE (self);
+
+	chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self);
+	if (!chain) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request.");
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_DESTROY, NULL);
+	nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
+}
+
+static void
+impl_manager_checkpoint_rollback (NMManager *self,
+                                  GDBusMethodInvocation *context,
+                                  const char *checkpoint_path)
+{
+	NMManagerPrivate *priv;
+	GError *error = NULL;
+	NMAuthChain *chain;
+
+	g_return_if_fail (NM_IS_MANAGER (self));
+	priv = NM_MANAGER_GET_PRIVATE (self);
+
+	chain = nm_auth_chain_new_context (context, checkpoint_auth_done_cb, self);
+	if (!chain) {
+		error = g_error_new_literal (NM_MANAGER_ERROR,
+		                             NM_MANAGER_ERROR_PERMISSION_DENIED,
+		                             "Unable to authenticate request.");
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+	nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ROLLBACK, NULL);
+	nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
+}
+
+/******************************************************************************/
+
+static void
+auth_mgr_changed (NMAuthManager *auth_manager, gpointer user_data)
 {
 	/* Let clients know they should re-check their authorization */
 	g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0);
@@ -5245,7 +5381,6 @@ manager_radio_user_toggled (NMManager *self,
                             gboolean enabled)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GError *error = NULL;
 	gboolean old_enabled, new_enabled;
 
 	/* Don't touch devices if asleep/networking disabled */
@@ -5259,17 +5394,8 @@ manager_radio_user_toggled (NMManager *self,
 	}
 
 	/* Update enabled key in state file */
-	if (priv->state_file) {
-		if (!write_value_to_state_file (priv->state_file,
-		                                "main", rstate->key,
-		                                G_TYPE_BOOLEAN, (gpointer) &enabled,
-		                                &error)) {
-			_LOGW (LOGD_CORE, "writing to state file %s failed: %s",
-			       priv->state_file,
-			       error->message);
-			g_clear_error (&error);
-		}
-	}
+	nm_config_state_set (priv->config, TRUE, FALSE,
+	                     rstate->key, enabled);
 
 	/* When the user toggles the radio, their request should override any
 	 * daemon (like ModemManager) enabled state that can be changed.  For WWAN
@@ -5331,34 +5457,22 @@ nm_manager_get (void)
 	return singleton_instance;
 }
 
-NMConnectionProvider *
-nm_connection_provider_get (void)
+NMSettings *
+nm_settings_get (void)
 {
-	NMConnectionProvider *p;
-
 	g_return_val_if_fail (singleton_instance, NULL);
 
-	p = NM_CONNECTION_PROVIDER (NM_MANAGER_GET_PRIVATE (singleton_instance)->settings);
-	g_return_val_if_fail (p, NULL);
-	return p;
+	return NM_MANAGER_GET_PRIVATE (singleton_instance)->settings;
 }
 
 NMManager *
-nm_manager_setup (const char *state_file,
-                  gboolean initial_net_enabled,
-                  gboolean initial_wifi_enabled,
-                  gboolean initial_wwan_enabled)
+nm_manager_setup (void)
 {
 	NMManager *self;
 
 	g_return_val_if_fail (!singleton_instance, singleton_instance);
 
-	self = g_object_new (NM_TYPE_MANAGER,
-	                     NM_MANAGER_NETWORKING_ENABLED, initial_net_enabled,
-	                     NM_MANAGER_WIRELESS_ENABLED, initial_wifi_enabled,
-	                     NM_MANAGER_WWAN_ENABLED, initial_wwan_enabled,
-	                     NM_MANAGER_STATE_FILE, state_file,
-	                     NULL);
+	self = g_object_new (NM_TYPE_MANAGER, NULL);
 	nm_assert (NM_IS_MANAGER (self));
 	singleton_instance = self;
 
@@ -5376,6 +5490,7 @@ constructed (GObject *object)
 	NMManager *self = NM_MANAGER (object);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMConfigData *config_data;
+	const NMConfigState *state;
 
 	G_OBJECT_CLASS (nm_manager_parent_class)->constructed (object);
 
@@ -5421,9 +5536,16 @@ constructed (GObject *object)
 	g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE,
 	                  G_CALLBACK (connectivity_changed), self);
 
+	state = nm_config_state_get (priv->config);
+
+	priv->net_enabled = state->net_enabled;
+
+	priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = state->wifi_enabled;
+	priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = state->wwan_enabled;
+
 	priv->rfkill_mgr = nm_rfkill_manager_new ();
 	g_signal_connect (priv->rfkill_mgr,
-	                  "rfkill-changed",
+	                  NM_RFKILL_MANAGER_SIGNAL_RFKILL_CHANGED,
 	                  G_CALLBACK (rfkill_manager_rfkill_changed_cb),
 	                  self);
 
@@ -5447,14 +5569,14 @@ nm_manager_init (NMManager *self)
 	memset (priv->radio_states, 0, sizeof (priv->radio_states));
 
 	priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = TRUE;
-	priv->radio_states[RFKILL_TYPE_WLAN].key = "WirelessEnabled";
+	priv->radio_states[RFKILL_TYPE_WLAN].key = NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WLAN].desc = "WiFi";
 	priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN;
 
 	priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = TRUE;
-	priv->radio_states[RFKILL_TYPE_WWAN].key = "WWANEnabled";
+	priv->radio_states[RFKILL_TYPE_WWAN].key = NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WWAN].prop = NM_MANAGER_WWAN_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WWAN].hw_prop = NM_MANAGER_WWAN_HARDWARE_ENABLED;
 	priv->radio_states[RFKILL_TYPE_WWAN].desc = "WWAN";
@@ -5479,12 +5601,12 @@ nm_manager_init (NMManager *self)
 	                  G_CALLBACK (sleeping_cb), self);
 
 	/* Listen for authorization changes */
-	g_signal_connect (nm_auth_manager_get (),
+	priv->auth_mgr = g_object_ref (nm_auth_manager_get ());
+	g_signal_connect (priv->auth_mgr,
 	                  NM_AUTH_MANAGER_SIGNAL_CHANGED,
-	                  G_CALLBACK (authority_changed_cb),
+	                  G_CALLBACK (auth_mgr_changed),
 	                  self);
 
-
 	/* Monitor the firmware directory */
 	if (strlen (KERNEL_FIRMWARE_DIR)) {
 		file = g_file_new_for_path (KERNEL_FIRMWARE_DIR "/");
@@ -5617,33 +5739,15 @@ set_property (GObject *object, guint prop_id,
 	GError *error = NULL;
 
 	switch (prop_id) {
-	case PROP_STATE_FILE:
-		/* construct-only */
-		priv->state_file = g_value_dup_string (value);
-		break;
-	case PROP_NETWORKING_ENABLED:
-		/* construct-only */
-		priv->net_enabled = g_value_get_boolean (value);
-		break;
 	case PROP_WIRELESS_ENABLED:
-		if (!priv->rfkill_mgr) {
-			/* called during object construction. */
-			priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = g_value_get_boolean (value);
-		} else {
-			manager_radio_user_toggled (NM_MANAGER (object),
-			                            &priv->radio_states[RFKILL_TYPE_WLAN],
-			                            g_value_get_boolean (value));
-		}
+		manager_radio_user_toggled (NM_MANAGER (object),
+		                            &priv->radio_states[RFKILL_TYPE_WLAN],
+		                            g_value_get_boolean (value));
 		break;
 	case PROP_WWAN_ENABLED:
-		if (!priv->rfkill_mgr) {
-			/* called during object construction. */
-			priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = g_value_get_boolean (value);
-		} else {
-			manager_radio_user_toggled (NM_MANAGER (object),
-			                            &priv->radio_states[RFKILL_TYPE_WWAN],
-			                            g_value_get_boolean (value));
-		}
+		manager_radio_user_toggled (NM_MANAGER (object),
+		                            &priv->radio_states[RFKILL_TYPE_WWAN],
+		                            g_value_get_boolean (value));
 		break;
 	case PROP_WIMAX_ENABLED:
 		/* WIMAX is depreacted. This does nothing. */
@@ -5681,9 +5785,17 @@ dispose (GObject *object)
 	g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref);
 	priv->auth_chains = NULL;
 
-	g_signal_handlers_disconnect_by_func (nm_auth_manager_get (),
-	                                      G_CALLBACK (authority_changed_cb),
-	                                      manager);
+	if (priv->checkpoint_mgr) {
+		nm_checkpoint_manager_destroy_all (priv->checkpoint_mgr, NULL);
+		g_clear_pointer (&priv->checkpoint_mgr, nm_checkpoint_manager_unref);
+	}
+
+	if (priv->auth_mgr) {
+		g_signal_handlers_disconnect_by_func (priv->auth_mgr,
+		                                      G_CALLBACK (auth_mgr_changed),
+		                                      manager);
+		g_clear_object (&priv->auth_mgr);
+	}
 
 	g_assert (priv->devices == NULL);
 
@@ -5721,7 +5833,6 @@ dispose (GObject *object)
 		g_clear_object (&priv->settings);
 	}
 
-	g_clear_pointer (&priv->state_file, g_free);
 	g_clear_object (&priv->vpn_manager);
 
 	/* Unregister property filter */
@@ -5766,8 +5877,6 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
 	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (manager_class);
 
-	g_type_class_add_private (manager_class, sizeof (NMManagerPrivate));
-
 	exported_object_class->export_path = NM_DBUS_PATH;
 
 	/* virtual methods */
@@ -5783,13 +5892,6 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                         G_PARAM_READABLE |
 	                         G_PARAM_STATIC_STRINGS);
 
-	obj_properties[PROP_STATE_FILE] =
-	    g_param_spec_string (NM_MANAGER_STATE_FILE, "", "",
-	                         NULL,
-	                         G_PARAM_WRITABLE |
-	                         G_PARAM_CONSTRUCT_ONLY |
-	                         G_PARAM_STATIC_STRINGS);
-
 	obj_properties[PROP_STATE] =
 	    g_param_spec_uint (NM_MANAGER_STATE, "", "",
 	                       0, NM_STATE_DISCONNECTED, 0,
@@ -5805,14 +5907,13 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	obj_properties[PROP_NETWORKING_ENABLED] =
 	    g_param_spec_boolean (NM_MANAGER_NETWORKING_ENABLED, "", "",
 	                          TRUE,
-	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_READABLE |
 	                          G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_WIRELESS_ENABLED] =
 	    g_param_spec_boolean (NM_MANAGER_WIRELESS_ENABLED, "", "",
 	                          TRUE,
 	                          G_PARAM_READWRITE |
-	                          G_PARAM_CONSTRUCT |
 	                          G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_WIRELESS_HARDWARE_ENABLED] =
@@ -5825,7 +5926,6 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	    g_param_spec_boolean (NM_MANAGER_WWAN_ENABLED, "", "",
 	                          TRUE,
 	                          G_PARAM_READWRITE |
-	                          G_PARAM_CONSTRUCT |
 	                          G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_WWAN_HARDWARE_ENABLED] =
@@ -5945,8 +6045,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	    g_signal_new (NM_MANAGER_DEVICE_ADDED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
-	                  G_STRUCT_OFFSET (NMManagerClass, device_added),
-	                  NULL, NULL, NULL,
+	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, NM_TYPE_DEVICE);
 
 	/* Emitted for both realized devices and placeholder devices */
@@ -5962,8 +6061,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	    g_signal_new (NM_MANAGER_DEVICE_REMOVED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
-	                  G_STRUCT_OFFSET (NMManagerClass, device_removed),
-	                  NULL, NULL, NULL,
+	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, NM_TYPE_DEVICE);
 
 	/* Emitted for both realized devices and placeholder devices */
@@ -5978,8 +6076,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	    g_signal_new (NM_MANAGER_STATE_CHANGED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
-	                  G_STRUCT_OFFSET (NMManagerClass, state_changed),
-	                  NULL, NULL, NULL,
+	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, G_TYPE_UINT);
 
 	signals[CHECK_PERMISSIONS] =
@@ -6026,6 +6123,9 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	                                        "GetLogging", impl_manager_get_logging,
 	                                        "CheckConnectivity", impl_manager_check_connectivity,
 	                                        "state", impl_manager_get_state,
+	                                        "CheckpointCreate", impl_manager_checkpoint_create,
+	                                        "CheckpointDestroy", impl_manager_checkpoint_destroy,
+	                                        "CheckpointRollback", impl_manager_checkpoint_rollback,
 	                                        NULL);
 }