summary refs log tree commit diff
path: root/src/nm-checkpoint.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-checkpoint.c
parent45cb5bb3c0e6edb887cf69b417fcaf7053814a9b (diff)
New upstream version 1.5.90 upstream/1.5.90
Diffstat (limited to 'src/nm-checkpoint.c')
-rw-r--r--src/nm-checkpoint.c325
1 files changed, 195 insertions, 130 deletions
diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c
index 605e0700..7d89e61e 100644
--- a/src/nm-checkpoint.c
+++ b/src/nm-checkpoint.c
@@ -19,6 +19,7 @@
  */
 
 #include "nm-default.h"
+
 #include "nm-checkpoint.h"
 
 #include <string.h>
@@ -26,38 +27,32 @@
 #include "nm-auth-subject.h"
 #include "nm-core-utils.h"
 #include "nm-dbus-interface.h"
-#include "nm-device.h"
+#include "devices/nm-device.h"
 #include "nm-manager.h"
-#include "nm-settings.h"
-#include "nm-settings-connection.h"
+#include "settings/nm-settings.h"
+#include "settings/nm-settings-connection.h"
 #include "nm-simple-connection.h"
 #include "nm-utils.h"
-#include "nmdbus-checkpoint.h"
+#include "introspection/org.freedesktop.NetworkManager.Checkpoint.h"
 
-#define _NMLOG_PREFIX_NAME                "checkpoint"
-#define _NMLOG_DOMAIN                     LOGD_CORE
-
-#define _NMLOG(level, ...) \
-	G_STMT_START { \
-		if (nm_logging_enabled (level, _NMLOG_DOMAIN)) { \
-			char __prefix[32]; \
-			\
-			if (self) \
-				g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", ""_NMLOG_PREFIX_NAME"", (self)); \
-			else \
-				g_strlcpy (__prefix, _NMLOG_PREFIX_NAME, sizeof (__prefix)); \
-			_nm_log ((level), (_NMLOG_DOMAIN), 0, \
-			          "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
-			          __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
-		} \
-	} G_STMT_END
+/*****************************************************************************/
 
 typedef struct {
 	char *original_dev_path;
 	NMDevice *device;
-	NMConnection *connection;
+	NMConnection *applied_connection;
+	NMConnection *settings_connection;
+	NMDeviceState state;
+	bool realized:1;
+	bool unmanaged_explicit:1;
 } DeviceCheckpoint;
 
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_DEVICES,
+	PROP_CREATED,
+	PROP_ROLLBACK_TIMEOUT,
+);
+
 typedef struct {
 	/* properties */
 	GHashTable *devices;
@@ -66,6 +61,8 @@ typedef struct {
 	/* private members */
 	NMManager *manager;
 	gint64 rollback_ts;
+	NMCheckpointCreateFlags flags;
+	GHashTable *connection_uuids;
 } NMCheckpointPrivate;
 
 struct _NMCheckpoint {
@@ -73,19 +70,35 @@ struct _NMCheckpoint {
 	NMCheckpointPrivate _priv;
 };
 
-typedef struct {
+struct _NMCheckpointClass {
 	NMExportedObjectClass parent;
-} NMCheckpointClass;
+};
 
 G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_EXPORTED_OBJECT)
 
-#define NM_CHECKPOINT_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMCheckpoint, NM_IS_CHECKPOINT)
+#define NM_CHECKPOINT_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMCheckpoint, NM_IS_CHECKPOINT)
 
-NM_GOBJECT_PROPERTIES_DEFINE_BASE (
-	PROP_DEVICES,
-	PROP_CREATED,
-	PROP_ROLLBACK_TIMEOUT,
-);
+/*****************************************************************************/
+
+#define _NMLOG_PREFIX_NAME                "checkpoint"
+#define _NMLOG_DOMAIN                     LOGD_CORE
+
+#define _NMLOG(level, ...) \
+	G_STMT_START { \
+		if (nm_logging_enabled (level, _NMLOG_DOMAIN)) { \
+			char __prefix[32]; \
+			\
+			if (self) \
+				g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", ""_NMLOG_PREFIX_NAME"", (self)); \
+			else \
+				g_strlcpy (__prefix, _NMLOG_PREFIX_NAME, sizeof (__prefix)); \
+			_nm_log ((level), (_NMLOG_DOMAIN), 0, \
+			          "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+			          __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+		} \
+	} G_STMT_END
+
+/*****************************************************************************/
 
 guint64
 nm_checkpoint_get_rollback_ts (NMCheckpoint *self)
@@ -122,47 +135,75 @@ nm_checkpoint_rollback (NMCheckpoint *self)
 	while (g_hash_table_iter_next (&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) {
 		gs_unref_object NMAuthSubject *subject = NULL;
 		guint32 result = NM_ROLLBACK_RESULT_OK;
-		const char *con_path;
-
-		_LOGD ("rollback: restoring state of device %s", nm_device_get_iface (device));
-
-		if (!nm_device_is_real (device)) {
-			result = NM_ROLLBACK_RESULT_ERR_NO_DEVICE;
-			_LOGD ("rollback: device is not realized");
+		const char *con_uuid;
+
+		_LOGD ("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d)",
+		       nm_device_get_iface (device),
+		       (int) dev_checkpoint->state,
+		       dev_checkpoint->realized,
+		       dev_checkpoint->unmanaged_explicit);
+
+		if (nm_device_is_real (device)) {
+			if (!dev_checkpoint->realized) {
+				_LOGD ("rollback: device was not realized, unmanage it");
+				nm_device_set_unmanaged_by_flags_queue (device,
+				                                        NM_UNMANAGED_USER_EXPLICIT,
+				                                        TRUE,
+				                                        NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
+				goto next_dev;
+			}
+		} else {
+			if (dev_checkpoint->realized) {
+				if (nm_device_is_software (device)) {
+					/* try to recreate software device */
+					_LOGD ("rollback: software device not realized, will re-activate");
+					goto activate;
+				} else {
+					_LOGD ("rollback: device is not realized");
+					result = NM_ROLLBACK_RESULT_ERR_FAILED;
+				}
+			}
 			goto next_dev;
 		}
 
-		if (nm_device_get_state (device) <= NM_DEVICE_STATE_UNMANAGED) {
-			result = NM_ROLLBACK_RESULT_ERR_DEVICE_UNMANAGED;
-			_LOGD ("rollback: device is unmanaged");
+activate:
+		if (dev_checkpoint->state == NM_DEVICE_STATE_UNMANAGED) {
+			if (   nm_device_get_state (device) != NM_DEVICE_STATE_UNMANAGED
+			    || dev_checkpoint->unmanaged_explicit) {
+				_LOGD ("rollback: explicitly unmanage device");
+				nm_device_set_unmanaged_by_flags_queue (device,
+				                                        NM_UNMANAGED_USER_EXPLICIT,
+				                                        TRUE,
+				                                        NM_DEVICE_STATE_REASON_NOW_UNMANAGED);
+			}
 			goto next_dev;
 		}
 
-		if (dev_checkpoint->connection) {
+		if (dev_checkpoint->applied_connection) {
 			/* The device had an active connection, check if the
 			 * connection still exists
 			 * */
-			con_path = nm_connection_get_path (dev_checkpoint->connection);
-			connection = nm_settings_get_connection_by_path (nm_settings_get(), con_path);
+			con_uuid = nm_connection_get_uuid (dev_checkpoint->settings_connection);
+			connection = nm_settings_get_connection_by_uuid (nm_settings_get (), con_uuid);
 
 			if (connection) {
 				/* If the connection is still there, restore its content
 				 * and save it
 				 * */
-				_LOGD ("rollback: connection %s still exists", con_path);
+				_LOGD ("rollback: connection %s still exists", con_uuid);
 
 				nm_connection_replace_settings_from_connection (NM_CONNECTION (connection),
-				                                                dev_checkpoint->connection);
+				                                                dev_checkpoint->settings_connection);
 				nm_settings_connection_commit_changes (connection,
 				                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
 				                                       NULL,
 				                                       NULL);
 			} else {
 				/* The connection was deleted, recreate it */
-				_LOGD ("rollback: adding connection %s again", con_path);
+				_LOGD ("rollback: adding connection %s again", con_uuid);
 
 				connection = nm_settings_add_connection (nm_settings_get (),
-				                                         dev_checkpoint->connection,
+				                                         dev_checkpoint->settings_connection,
 				                                         TRUE,
 				                                         &local_error);
 				if (!connection) {
@@ -177,6 +218,7 @@ nm_checkpoint_rollback (NMCheckpoint *self)
 			subject = nm_auth_subject_new_internal ();
 			if (!nm_manager_activate_connection (priv->manager,
 			                                     connection,
+			                                     dev_checkpoint->applied_connection,
 			                                     NULL,
 			                                     device,
 			                                     subject,
@@ -205,6 +247,47 @@ next_dev:
 		g_variant_builder_add (&builder, "{su}", dev_checkpoint->original_dev_path, result);
 	}
 
+	if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) {
+		NMSettingsConnection *con;
+		gs_free_slist GSList *list = NULL;
+		GSList *item;
+
+		g_return_val_if_fail (priv->connection_uuids, NULL);
+		list = nm_settings_get_connections_sorted (nm_settings_get ());
+
+		for (item = list; item; item = g_slist_next (item)) {
+			con = item->data;
+			if (!g_hash_table_contains (priv->connection_uuids,
+			                            nm_settings_connection_get_uuid (con))) {
+				_LOGD ("rollback: deleting new connection %s (%s)",
+				       nm_settings_connection_get_uuid (con),
+				       nm_settings_connection_get_id (con));
+				nm_settings_connection_delete (con, NULL, NULL);
+			}
+		}
+	}
+
+	if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) {
+		const GSList *list = nm_manager_get_devices (priv->manager);
+		NMDeviceState state;
+		NMDevice *dev;
+
+		for (list = nm_manager_get_devices (priv->manager); list ; list = g_slist_next (list)) {
+			dev = list->data;
+			if (!g_hash_table_contains (priv->devices, dev)) {
+				state = nm_device_get_state (dev);
+				if (   state > NM_DEVICE_STATE_DISCONNECTED
+				    && state < NM_DEVICE_STATE_DEACTIVATING) {
+					_LOGD ("rollback: disconnecting new device %s", nm_device_get_iface (dev));
+					nm_device_state_changed (dev,
+					                         NM_DEVICE_STATE_DEACTIVATING,
+					                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
+				}
+			}
+		}
+
+	}
+
 	return g_variant_new ("(a{su})", &builder);
 }
 
@@ -213,36 +296,32 @@ device_checkpoint_create (NMDevice *device,
                           GError **error)
 {
 	DeviceCheckpoint *dev_checkpoint;
-	NMConnection *connection;
+	NMConnection *applied_connection;
+	NMSettingsConnection *settings_connection;
 	const char *path;
-
-	if (!nm_device_is_real (device)) {
-		g_set_error (error,
-		             NM_MANAGER_ERROR,
-		             NM_MANAGER_ERROR_INVALID_ARGUMENTS,
-		             "device '%s' is not realized",
-		             nm_device_get_iface (device));
-		return NULL;
-	}
-
-	if (nm_device_get_state (device) <= NM_DEVICE_STATE_UNMANAGED) {
-		g_set_error (error,
-		             NM_MANAGER_ERROR,
-		             NM_MANAGER_ERROR_INVALID_ARGUMENTS,
-		             "device '%s' is unmanaged",
-		             nm_device_get_iface (device));
-		return NULL;
-	}
+	gboolean unmanaged_explicit;
 
 	path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
+	unmanaged_explicit = !!nm_device_get_unmanaged_flags (device,
+	                                                      NM_UNMANAGED_USER_EXPLICIT);
 
 	dev_checkpoint = g_slice_new0 (DeviceCheckpoint);
 	dev_checkpoint->device = g_object_ref (device);
 	dev_checkpoint->original_dev_path = g_strdup (path);
-
-	connection = nm_device_get_applied_connection (device);
-	if (connection)
-		dev_checkpoint->connection = nm_simple_connection_new_clone (connection);
+	dev_checkpoint->state = nm_device_get_state (device);
+	dev_checkpoint->realized = nm_device_is_real (device);
+	dev_checkpoint->unmanaged_explicit = unmanaged_explicit;
+
+	applied_connection = nm_device_get_applied_connection (device);
+	if (applied_connection) {
+		dev_checkpoint->applied_connection =
+			nm_simple_connection_new_clone (applied_connection);
+
+		settings_connection = nm_device_get_settings_connection (device);
+		g_return_val_if_fail (settings_connection, NULL);
+		dev_checkpoint->settings_connection =
+			nm_simple_connection_new_clone (NM_CONNECTION (settings_connection));
+	}
 
 	return dev_checkpoint;
 }
@@ -252,51 +331,63 @@ device_checkpoint_destroy (gpointer data)
 {
 	DeviceCheckpoint *dev_checkpoint = data;
 
-	g_clear_object (&dev_checkpoint->connection);
+	g_clear_object (&dev_checkpoint->applied_connection);
+	g_clear_object (&dev_checkpoint->settings_connection);
 	g_clear_object (&dev_checkpoint->device);
 	g_free (dev_checkpoint->original_dev_path);
 
 	g_slice_free (DeviceCheckpoint, dev_checkpoint);
 }
 
+/*****************************************************************************/
+
 static void
-nm_checkpoint_init (NMCheckpoint *self)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
+	NMCheckpoint *self = NM_CHECKPOINT (object);
 	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
+	gs_free_slist GSList *devices = NULL;
+	GHashTableIter iter;
+	NMDevice *device;
 
-	priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal,
-	                                       NULL, device_checkpoint_destroy);
+	switch (prop_id) {
+	case PROP_DEVICES:
+		g_hash_table_iter_init (&iter, priv->devices);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &device, NULL))
+			devices = g_slist_append (devices, device);
+		nm_utils_g_value_set_object_path_array (value, devices, NULL, NULL);
+		break;
+	case PROP_CREATED:
+		g_value_set_int64 (value, priv->created);
+		break;
+	case PROP_ROLLBACK_TIMEOUT:
+		g_value_set_uint (value, priv->rollback_timeout);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
+/*****************************************************************************/
+
 static void
-get_all_devices (NMManager *manager, GPtrArray *devices)
+nm_checkpoint_init (NMCheckpoint *self)
 {
-	const GSList *list, *iter;
-	NMDevice *dev;
-
-	list = nm_manager_get_devices (manager);
-
-	for (iter = list; iter; iter = g_slist_next (iter)) {
-		dev = iter->data;
-
-		if (!nm_device_is_real (dev))
-			continue;
-		if (nm_device_get_state (dev) <= NM_DEVICE_STATE_UNMANAGED)
-			continue;
-		/* We never touch assumed connections, unless told explicitly */
-		if (nm_device_uses_assumed_connection (dev))
-			continue;
+	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
 
-		g_ptr_array_add (devices, dev);
-	}
+	priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+	                                       NULL, device_checkpoint_destroy);
 }
 
 NMCheckpoint *
 nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
-                   GError **error)
+                   NMCheckpointCreateFlags flags, GError **error)
 {
 	NMCheckpoint *self;
 	NMCheckpointPrivate *priv;
+	NMSettingsConnection *const *con;
 	DeviceCheckpoint *dev_checkpoint;
 	NMDevice *device;
 	guint i;
@@ -305,9 +396,6 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time
 	g_return_val_if_fail (devices, NULL);
 	g_return_val_if_fail (!error || !*error, NULL);
 
-	if (!devices->len)
-		get_all_devices (manager, devices);
-
 	if (!devices->len) {
 		g_set_error_literal (error,
 		                     NM_MANAGER_ERROR,
@@ -326,6 +414,15 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time
 	priv->rollback_ts = rollback_timeout ?
 	    (nm_utils_get_monotonic_timestamp_ms () + ((gint64) rollback_timeout * 1000)) :
 	    0;
+	priv->flags = flags;
+
+	if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) {
+		priv->connection_uuids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+		for (con = nm_settings_get_connections (nm_settings_get (), NULL); *con; con++) {
+			g_hash_table_add (priv->connection_uuids,
+			                  g_strdup (nm_settings_connection_get_uuid (*con)));
+		}
+	}
 
 	for (i = 0; i < devices->len; i++) {
 		device = (NMDevice *) devices->pdata[i];
@@ -347,55 +444,23 @@ dispose (GObject *object)
 	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
 
 	g_clear_pointer (&priv->devices, g_hash_table_unref);
+	g_clear_pointer (&priv->connection_uuids, g_hash_table_unref);
 
 	G_OBJECT_CLASS (nm_checkpoint_parent_class)->dispose (object);
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	NMCheckpoint *self = NM_CHECKPOINT (object);
-	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
-	gs_free_slist GSList *devices = NULL;
-	GHashTableIter iter;
-	NMDevice *device;
-
-	switch (prop_id) {
-	case PROP_DEVICES:
-		g_hash_table_iter_init (&iter, priv->devices);
-		while (g_hash_table_iter_next (&iter, (gpointer *) &device, NULL))
-			devices = g_slist_append (devices, device);
-		nm_utils_g_value_set_object_path_array (value, devices, NULL, NULL);
-		break;
-	case PROP_CREATED:
-		g_value_set_int64 (value, priv->created);
-		break;
-	case PROP_ROLLBACK_TIMEOUT:
-		g_value_set_uint (value, priv->rollback_timeout);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
 nm_checkpoint_class_init (NMCheckpointClass *checkpoint_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (checkpoint_class);
 	NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (checkpoint_class);
 
-	g_type_class_add_private (checkpoint_class, sizeof (NMCheckpointPrivate));
-
-	exported_object_class->export_path = NM_DBUS_PATH "/Checkpoint/%u";
+	exported_object_class->export_path = NM_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/Checkpoint");
 	exported_object_class->export_on_construction = FALSE;
 
-	/* virtual methods */
 	object_class->dispose = dispose;
 	object_class->get_property = get_property;
 
-	/* properties */
 	obj_properties[PROP_DEVICES] =
 	     g_param_spec_boxed (NM_CHECKPOINT_DEVICES, "", "",
 	                         G_TYPE_STRV,