about summary refs log tree commit diff
path: root/src/nm-checkpoint-manager.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-05-11 22:08:45 +0200
committerMichael Biebl <biebl@debian.org>2018-05-11 22:08:45 +0200
commitee9c73a923909e23a649407be77e25235d769e25 (patch)
treee21c923621fa278e737da693df9eb60ea31a6067 /src/nm-checkpoint-manager.c
parentf60117b41d5433be1b4a96d82cd11d0c3dce9b63 (diff)
New upstream version 1.10.8 upstream/1.10.8
Diffstat (limited to 'src/nm-checkpoint-manager.c')
-rw-r--r--src/nm-checkpoint-manager.c326
1 files changed, 147 insertions, 179 deletions
diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c
index 8ba19db9..6da220c4 100644
--- a/src/nm-checkpoint-manager.c
+++ b/src/nm-checkpoint-manager.c
@@ -26,16 +26,16 @@
 #include "nm-connection.h"
 #include "nm-core-utils.h"
 #include "devices/nm-device.h"
+#include "nm-exported-object.h"
 #include "nm-manager.h"
 #include "nm-utils.h"
-#include "c-list/src/c-list.h"
 
 /*****************************************************************************/
 
 struct _NMCheckpointManager {
 	NMManager *_manager;
-	GParamSpec *property_spec;
-	CList checkpoints_lst_head;
+	GHashTable *checkpoints;
+	guint rollback_timeout_id;
 };
 
 #define GET_MANAGER(self) \
@@ -56,67 +56,80 @@ struct _NMCheckpointManager {
 
 /*****************************************************************************/
 
-static void
-notify_checkpoints (NMCheckpointManager *self) {
-	g_object_notify_by_pspec ((GObject *) GET_MANAGER (self),
-	                          self->property_spec);
-}
+static void update_rollback_timeout (NMCheckpointManager *self);
 
 static void
-destroy_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint, gboolean log_destroy)
+checkpoint_destroy (gpointer checkpoint)
 {
-	nm_assert (NM_IS_CHECKPOINT (checkpoint));
-	nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (checkpoint)));
-	nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
-
-	nm_checkpoint_set_timeout_callback (checkpoint, NULL, NULL);
-
-	c_list_unlink (&checkpoint->checkpoints_lst);
-
-	if (log_destroy)
-		nm_checkpoint_log_destroy (checkpoint);
-
-	notify_checkpoints (self);
-
-	nm_dbus_object_unexport (NM_DBUS_OBJECT (checkpoint));
-	g_object_unref (checkpoint);
+	nm_exported_object_unexport (NM_EXPORTED_OBJECT (checkpoint));
+	g_object_unref (G_OBJECT (checkpoint));
 }
 
-static GVariant *
-rollback_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint)
+static gboolean
+rollback_timeout_cb (NMCheckpointManager *self)
 {
+	NMCheckpoint *checkpoint;
+	GHashTableIter iter;
 	GVariant *result;
-	const CList *iter;
+	gint64 ts, now;
+
+	now = nm_utils_get_monotonic_timestamp_ms ();
+
+	g_hash_table_iter_init (&iter, self->checkpoints);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) {
+		ts = nm_checkpoint_get_rollback_ts (checkpoint);
+		if (ts && ts <= now) {
+			result = nm_checkpoint_rollback (checkpoint);
+			if (result)
+				g_variant_unref (result);
+			g_hash_table_iter_remove (&iter);
+		}
+	}
 
-	nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
+	self->rollback_timeout_id = 0;
+	update_rollback_timeout (self);
 
-	/* we destroy first all overlapping checkpoints that are younger/newer. */
-	for (iter = checkpoint->checkpoints_lst.next;
-	     iter != &self->checkpoints_lst_head;
-	     ) {
-		NMCheckpoint *cp = c_list_entry (iter, NMCheckpoint, checkpoints_lst);
+	return G_SOURCE_REMOVE;
+}
 
-		iter = iter->next;
-		if (nm_checkpoint_includes_devices_of (cp, checkpoint)) {
-			/* the younger checkpoint has overlapping devices and gets obsoleted.
-			 * Destroy it. */
-			destroy_checkpoint (self, cp, TRUE);
-		}
+static void
+update_rollback_timeout (NMCheckpointManager *self)
+{
+	NMCheckpoint *checkpoint;
+	GHashTableIter iter;
+	gint64 ts, delta, next = G_MAXINT64;
+
+	g_hash_table_iter_init (&iter, self->checkpoints);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) {
+		ts = nm_checkpoint_get_rollback_ts (checkpoint);
+		if (ts && ts < next)
+			next = ts;
 	}
 
-	result = nm_checkpoint_rollback (checkpoint);
-	destroy_checkpoint (self, checkpoint, FALSE);
-	return result;
+	nm_clear_g_source (&self->rollback_timeout_id);
+
+	if (next != G_MAXINT64) {
+		delta = MAX (next - nm_utils_get_monotonic_timestamp_ms (), 0);
+		self->rollback_timeout_id = g_timeout_add (delta,
+		                                           (GSourceFunc) rollback_timeout_cb,
+		                                           self);
+		_LOGT ("update timeout: next check in %" G_GINT64_FORMAT " ms", delta);
+	}
 }
 
-static void
-rollback_timeout_cb (NMCheckpoint *checkpoint,
-                     gpointer user_data)
+static NMCheckpoint *
+find_checkpoint_for_device (NMCheckpointManager *self, NMDevice *device)
 {
-	NMCheckpointManager *self = user_data;
-	gs_unref_variant GVariant *result = NULL;
+	GHashTableIter iter;
+	NMCheckpoint *checkpoint;
 
-	result = rollback_checkpoint (self, checkpoint);
+	g_hash_table_iter_init (&iter, self->checkpoints);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &checkpoint)) {
+		if (nm_checkpoint_includes_device (checkpoint, device))
+			return checkpoint;
+	}
+
+	return NULL;
 }
 
 NMCheckpoint *
@@ -128,200 +141,152 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
 {
 	NMManager *manager;
 	NMCheckpoint *checkpoint;
+	const char * const *path;
 	gs_unref_ptrarray GPtrArray *devices = NULL;
 	NMDevice *device;
+	const char *checkpoint_path;
+	gs_free const char **device_paths_free = NULL;
+	guint i;
 
 	g_return_val_if_fail (self, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 	manager = GET_MANAGER (self);
 
-	devices = g_ptr_array_new ();
-
 	if (!device_paths || !device_paths[0]) {
-		const CList *tmp_lst;
-
-		nm_manager_for_each_device (manager, device, tmp_lst) {
-			/* FIXME: there is no strong reason to skip over unrealized devices.
-			 *        Also, NMCheckpoint anticipates to handle them (in parts). */
+		const char *device_path;
+		const GSList *iter;
+		GPtrArray *paths;
+
+		paths = g_ptr_array_new ();
+		for (iter = nm_manager_get_devices (manager);
+		     iter;
+		     iter = g_slist_next (iter)) {
+			device = NM_DEVICE (iter->data);
 			if (!nm_device_is_real (device))
 				continue;
-			nm_assert (nm_dbus_object_get_path (NM_DBUS_OBJECT (device)));
-			g_ptr_array_add (devices, device);
+			device_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
+			if (device_path)
+				g_ptr_array_add (paths, (gpointer) device_path);
 		}
+		g_ptr_array_add (paths, NULL);
+		device_paths_free = (const char **) g_ptr_array_free (paths, FALSE);
+		device_paths = (const char *const *) device_paths_free;
 	} else if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) {
 		g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
 		                     "the DISCONNECT_NEW_DEVICES flag can only be used with an empty device list");
 		return NULL;
-	} else {
-		for (; *device_paths; device_paths++) {
-			device = nm_manager_get_device_by_path (manager, *device_paths);
-			if (!device) {
-				g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-				             "device %s does not exist", *device_paths);
-				return NULL;
-			}
-			if (!nm_device_is_real (device)) {
-				g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
-				             "device %s is not realized", *device_paths);
-				return NULL;
-			}
-			g_ptr_array_add (devices, device);
-		}
 	}
 
-	if (!devices->len) {
-		g_set_error_literal (error,
-		                     NM_MANAGER_ERROR,
-		                     NM_MANAGER_ERROR_INVALID_ARGUMENTS,
-		                     "no device available");
-		return NULL;
+	devices = g_ptr_array_new ();
+	for (path = device_paths; *path; path++) {
+		device = nm_manager_get_device_by_path (manager, *path);
+		if (!device) {
+			g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
+			             "device %s does not exist", *path);
+			return NULL;
+		}
+		g_ptr_array_add (devices, device);
 	}
 
-	if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL))
-		nm_checkpoint_manager_destroy_all (self);
-	else if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING)) {
-		c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst) {
-			device = nm_checkpoint_includes_devices (checkpoint, (NMDevice *const*) devices->pdata, devices->len);
-			if (device) {
+	if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL)) {
+		for (i = 0; i < devices->len; i++) {
+			device = devices->pdata[i];
+			checkpoint = find_checkpoint_for_device (self, device);
+			if (checkpoint) {
 				g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
 				             "device '%s' is already included in checkpoint %s",
 				             nm_device_get_iface (device),
-				             nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint)));
+				             nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint)));
 				return NULL;
 			}
 		}
 	}
 
-	checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags);
+	checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags, error);
+	if (!checkpoint)
+		return NULL;
+
+	if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL))
+		g_hash_table_remove_all (self->checkpoints);
+
+	nm_exported_object_export (NM_EXPORTED_OBJECT (checkpoint));
+	checkpoint_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint));
 
-	nm_dbus_object_export (NM_DBUS_OBJECT (checkpoint));
+	if (!nm_g_hash_table_insert (self->checkpoints,
+	                             (gpointer) checkpoint_path,
+	                             checkpoint))
+		g_return_val_if_reached (NULL);
+
+	update_rollback_timeout (self);
 
-	nm_checkpoint_set_timeout_callback (checkpoint, rollback_timeout_cb, self);
-	c_list_link_tail (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst);
-	notify_checkpoints (self);
 	return checkpoint;
 }
 
-void
-nm_checkpoint_manager_destroy_all (NMCheckpointManager *self)
+gboolean
+nm_checkpoint_manager_destroy_all (NMCheckpointManager *self,
+                                   GError **error)
 {
-	NMCheckpoint *checkpoint;
+	g_return_val_if_fail (self, FALSE);
 
-	g_return_if_fail (self);
+	g_hash_table_remove_all (self->checkpoints);
 
-	while ((checkpoint = c_list_first_entry (&self->checkpoints_lst_head, NMCheckpoint, checkpoints_lst)))
-		destroy_checkpoint (self, checkpoint, TRUE);
+	return TRUE;
 }
 
 gboolean
 nm_checkpoint_manager_destroy (NMCheckpointManager *self,
-                               const char *path,
+                               const char *checkpoint_path,
                                GError **error)
 {
-	NMCheckpoint *checkpoint;
+	gboolean ret;
 
 	g_return_val_if_fail (self, FALSE);
-	g_return_val_if_fail (path && path[0] == '/', FALSE);
+	g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
-	if (!nm_streq (path, "/")) {
-		nm_checkpoint_manager_destroy_all (self);
-		return TRUE;
-	}
-
-	checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error);
-	if (!checkpoint)
-		return FALSE;
-
-	destroy_checkpoint (self, checkpoint, TRUE);
-	return TRUE;
+	if (!nm_streq (checkpoint_path, "/")) {
+		ret = g_hash_table_remove (self->checkpoints, checkpoint_path);
+		if (!ret) {
+			g_set_error (error,
+			             NM_MANAGER_ERROR,
+			             NM_MANAGER_ERROR_INVALID_ARGUMENTS,
+			             "checkpoint %s does not exist", checkpoint_path);
+		}
+		return ret;
+	} else
+		return nm_checkpoint_manager_destroy_all (self, error);
 }
 
 gboolean
 nm_checkpoint_manager_rollback (NMCheckpointManager *self,
-                                const char *path,
+                                const char *checkpoint_path,
                                 GVariant **results,
                                 GError **error)
 {
-	NMCheckpoint *checkpoint;
+	NMCheckpoint *cp;
 
 	g_return_val_if_fail (self, FALSE);
-	g_return_val_if_fail (path && path[0] == '/', FALSE);
+	g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE);
 	g_return_val_if_fail (results, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
-	checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error);
-	if (!checkpoint)
+	cp = g_hash_table_lookup (self->checkpoints, checkpoint_path);
+	if (!cp) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+		             "checkpoint %s does not exist", checkpoint_path);
 		return FALSE;
-
-	*results = rollback_checkpoint (self, checkpoint);
-	return TRUE;
-}
-
-NMCheckpoint *
-nm_checkpoint_manager_lookup_by_path (NMCheckpointManager *self, const char *path, GError **error)
-{
-	NMCheckpoint *checkpoint;
-
-	g_return_val_if_fail (self, NULL);
-
-	checkpoint = (NMCheckpoint *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (GET_MANAGER (self))),
-	                                                             path);
-	if (   !checkpoint
-	    || !NM_IS_CHECKPOINT (checkpoint)) {
-		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
-		             "checkpoint %s does not exist", path);
-		return NULL;
 	}
 
-	nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
-	return checkpoint;
-}
-
-const char **
-nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self, guint *out_length)
-{
-	NMCheckpoint *checkpoint;
-	const char **strv;
-	guint num, i = 0;
+	*results = nm_checkpoint_rollback (cp);
+	g_hash_table_remove (self->checkpoints, checkpoint_path);
 
-	num = c_list_length (&self->checkpoints_lst_head);
-	NM_SET_OUT (out_length, num);
-	if (!num)
-		return NULL;
-
-	strv = g_new (const char *, num + 1);
-	c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst)
-		strv[i++] = nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint));
-	nm_assert (i == num);
-	strv[i] = NULL;
-	return strv;
-}
-
-gboolean
-nm_checkpoint_manager_adjust_rollback_timeout (NMCheckpointManager *self,
-                                               const char *path,
-                                               guint32 add_timeout,
-                                               GError **error)
-{
-	NMCheckpoint *checkpoint;
-
-	g_return_val_if_fail (self, FALSE);
-	g_return_val_if_fail (path && path[0] == '/', FALSE);
-	g_return_val_if_fail (!error || !*error, FALSE);
-
-	checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error);
-	if (!checkpoint)
-		return FALSE;
-
-	nm_checkpoint_adjust_rollback_timeout (checkpoint, add_timeout);
 	return TRUE;
 }
 
 /*****************************************************************************/
 
 NMCheckpointManager *
-nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec)
+nm_checkpoint_manager_new (NMManager *manager)
 {
 	NMCheckpointManager *self;
 
@@ -336,17 +301,20 @@ nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec)
 	 * of NMManager shall surpass the lifetime of the NMCheckpointManager
 	 * instance. */
 	self->_manager = manager;
-	self->property_spec = spec;
-	c_list_init (&self->checkpoints_lst_head);
+	self->checkpoints = g_hash_table_new_full (nm_str_hash, g_str_equal,
+	                                           NULL, checkpoint_destroy);
+
 	return self;
 }
 
 void
-nm_checkpoint_manager_free (NMCheckpointManager *self)
+nm_checkpoint_manager_unref (NMCheckpointManager *self)
 {
 	if (!self)
 		return;
 
-	nm_checkpoint_manager_destroy_all (self);
+	nm_clear_g_source (&self->rollback_timeout_id);
+	g_hash_table_destroy (self->checkpoints);
+
 	g_slice_free (NMCheckpointManager, self);
 }