about summary refs log tree commit diff
path: root/libnm/nm-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnm/nm-manager.c')
-rw-r--r--libnm/nm-manager.c433
1 files changed, 424 insertions, 9 deletions
diff --git a/libnm/nm-manager.c b/libnm/nm-manager.c
index 5eeed668..aac120c6 100644
--- a/libnm/nm-manager.c
+++ b/libnm/nm-manager.c
@@ -26,6 +26,7 @@
 #include <string.h>
 
 #include "nm-utils.h"
+#include "nm-checkpoint.h"
 #include "nm-common-macros.h"
 #include "nm-device-ethernet.h"
 #include "nm-device-wifi.h"
@@ -34,7 +35,7 @@
 #include "nm-active-connection.h"
 #include "nm-vpn-connection.h"
 #include "nm-dbus-helpers.h"
-#include "nm-utils/c-list.h"
+#include "c-list/src/c-list.h"
 
 #include "introspection/org.freedesktop.NetworkManager.h"
 
@@ -61,6 +62,8 @@ typedef struct {
 	GPtrArray *devices;
 	GPtrArray *all_devices;
 	GPtrArray *active_connections;
+	GPtrArray *checkpoints;
+	GSList *added_checkpoints;
 	NMConnectivityState connectivity;
 	NMActiveConnection *primary_connection;
 	NMActiveConnection *activating_connection;
@@ -107,6 +110,7 @@ enum {
 	PROP_PRIMARY_CONNECTION,
 	PROP_ACTIVATING_CONNECTION,
 	PROP_DEVICES,
+	PROP_CHECKPOINTS,
 	PROP_METERED,
 	PROP_ALL_DEVICES,
 
@@ -120,6 +124,8 @@ enum {
 	ANY_DEVICE_REMOVED,
 	ACTIVE_CONNECTION_ADDED,
 	ACTIVE_CONNECTION_REMOVED,
+	CHECKPOINT_ADDED,
+	CHECKPOINT_REMOVED,
 	PERMISSION_CHANGED,
 
 	LAST_SIGNAL
@@ -129,6 +135,53 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 /*****************************************************************************/
 
+typedef struct {
+	NMManager *manager;
+	GSimpleAsyncResult *simple;
+	char *path;
+} CheckpointInfo;
+
+static CheckpointInfo *
+find_checkpoint_info (NMManager *manager, const char *path)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	CheckpointInfo *info;
+	GSList *iter;
+
+	for (iter = priv->added_checkpoints; iter; iter = g_slist_next (iter)) {
+		info = iter->data;
+		if (nm_streq0 (path, info->path))
+			return info;
+	}
+
+	return NULL;
+}
+
+static void
+checkpoint_info_complete (NMManager *self,
+                          CheckpointInfo *info,
+                          NMCheckpoint *checkpoint,
+                          GError *error)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	g_return_if_fail (info);
+
+	if (checkpoint) {
+		g_simple_async_result_set_op_res_gpointer (info->simple,
+		                                           g_object_ref (checkpoint),
+		                                           g_object_unref);
+	} else
+		g_simple_async_result_set_from_error (info->simple, error);
+	g_simple_async_result_complete (info->simple);
+
+	g_object_unref (info->simple);
+	priv->added_checkpoints = g_slist_remove (priv->added_checkpoints, info);
+
+	g_free (info->path);
+	g_slice_free (CheckpointInfo, info);
+}
+
 static void
 nm_manager_init (NMManager *manager)
 {
@@ -139,7 +192,7 @@ nm_manager_init (NMManager *manager)
 	priv->state = NM_STATE_UNKNOWN;
 	priv->connectivity = NM_CONNECTIVITY_UNKNOWN;
 
-	priv->permissions = g_hash_table_new (g_direct_hash, g_direct_equal);
+	priv->permissions = g_hash_table_new (nm_direct_hash, NULL);
 	priv->devices = g_ptr_array_new ();
 	priv->all_devices = g_ptr_array_new ();
 	priv->active_connections = g_ptr_array_new ();
@@ -189,6 +242,7 @@ init_dbus (NMObject *object)
 		{ NM_MANAGER_PRIMARY_CONNECTION,        &priv->primary_connection, NULL, NM_TYPE_ACTIVE_CONNECTION },
 		{ NM_MANAGER_ACTIVATING_CONNECTION,     &priv->activating_connection, NULL, NM_TYPE_ACTIVE_CONNECTION },
 		{ NM_MANAGER_DEVICES,                   &priv->devices, NULL, NM_TYPE_DEVICE, "device" },
+		{ NM_MANAGER_CHECKPOINTS,               &priv->checkpoints, NULL, NM_TYPE_CHECKPOINT, "checkpoint" },
 		{ NM_MANAGER_METERED,                   &priv->metered },
 		{ NM_MANAGER_ALL_DEVICES,               &priv->all_devices, NULL, NM_TYPE_DEVICE, "any-device" },
 		{ NULL },
@@ -206,6 +260,23 @@ init_dbus (NMObject *object)
 				 G_CALLBACK (manager_recheck_permissions), object, 0);
 }
 
+static void
+object_creation_failed (NMObject *object, const char *failed_path)
+{
+	NMManager *self = NM_MANAGER (object);
+	CheckpointInfo *info;
+	GError *add_error;
+
+	info = find_checkpoint_info (self, failed_path);
+	if (info) {
+		add_error = g_error_new_literal (NM_CLIENT_ERROR,
+		                                 NM_CLIENT_ERROR_OBJECT_CREATION_FAILED,
+		                                 _("Checkpoint was removed before it was initialized"));
+		checkpoint_info_complete (self, info, NULL, add_error);
+		g_error_free (add_error);
+	}
+}
+
 static NMClientPermission
 nm_permission_to_client (const char *nm)
 {
@@ -719,8 +790,7 @@ NMDevice *
 nm_manager_get_device_by_path (NMManager *manager, const char *object_path)
 {
 	const GPtrArray *devices;
-	int i;
-	NMDevice *device = NULL;
+	guint i;
 
 	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
 	g_return_val_if_fail (object_path, NULL);
@@ -729,12 +799,25 @@ nm_manager_get_device_by_path (NMManager *manager, const char *object_path)
 	for (i = 0; i < devices->len; i++) {
 		NMDevice *candidate = g_ptr_array_index (devices, i);
 		if (!strcmp (nm_object_get_path (NM_OBJECT (candidate)), object_path)) {
-			device = candidate;
-			break;
+			return candidate;
 		}
 	}
+	return NULL;
+}
 
-	return device;
+static NMCheckpoint *
+get_checkpoint_by_path (NMManager *manager, const char *object_path)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMCheckpoint *candidate;
+	guint i;
+
+	for (i = 0; i < priv->checkpoints->len; i++) {
+		candidate = priv->checkpoints->pdata[i];
+		if (nm_streq (nm_object_get_path (NM_OBJECT (candidate)), object_path))
+			return candidate;
+	}
+	return NULL;
 }
 
 NMDevice *
@@ -966,7 +1049,7 @@ nm_manager_activate_connection_async (NMManager *manager,
 	nmdbus_manager_call_activate_connection (priv->proxy,
 	                                         connection ? nm_connection_get_path (connection) : "/",
 	                                         device ? nm_object_get_path (NM_OBJECT (device)) : "/",
-	                                         specific_object ? specific_object : "/",
+	                                         specific_object ?: "/",
 	                                         cancellable,
 	                                         activate_cb, info);
 }
@@ -1050,7 +1133,7 @@ nm_manager_add_and_activate_connection_async (NMManager *manager,
 	nmdbus_manager_call_add_and_activate_connection (priv->proxy,
 	                                                 dict,
 	                                                 nm_object_get_path (NM_OBJECT (device)),
-	                                                 specific_object ? specific_object : "/",
+	                                                 specific_object ?: "/",
 	                                                 cancellable,
 	                                                 add_activate_cb, info);
 }
@@ -1115,6 +1198,16 @@ active_connection_removed (NMManager *self, NMActiveConnection *ac)
 	recheck_pending_activations (self);
 }
 
+static void
+checkpoint_added (NMManager *manager, NMCheckpoint *checkpoint)
+{
+	CheckpointInfo *info;
+
+	info = find_checkpoint_info (manager, nm_object_get_path (NM_OBJECT (checkpoint)));
+	if (info)
+		checkpoint_info_complete (manager, info, checkpoint, NULL);
+}
+
 gboolean
 nm_manager_deactivate_connection (NMManager *manager,
                                   NMActiveConnection *active,
@@ -1216,6 +1309,300 @@ free_active_connections (NMManager *manager)
 
 /*****************************************************************************/
 
+static const char **
+get_device_paths (const GPtrArray *devices)
+{
+	const char **array;
+	guint i;
+
+	array = g_new (const char *, devices->len + 1);
+	for (i = 0; i < devices->len; i++)
+		array[i] = nm_object_get_path (NM_OBJECT (devices->pdata[i]));
+
+	array[i] = NULL;
+
+	return array;
+}
+
+const GPtrArray *
+nm_manager_get_checkpoints (NMManager *manager)
+{
+	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
+
+	return NM_MANAGER_GET_PRIVATE (manager)->checkpoints;
+}
+
+static void
+checkpoint_created_cb (GObject *object,
+                       GAsyncResult *result,
+                       gpointer user_data)
+{
+	CheckpointInfo *info = user_data;
+	NMManager *self = info->manager;
+	gs_free_error GError *error = NULL;
+	NMCheckpoint *checkpoint;
+
+	nmdbus_manager_call_checkpoint_create_finish (NMDBUS_MANAGER (object),
+	                                              &info->path, result, &error);
+	if (error) {
+		g_dbus_error_strip_remote_error (error);
+		checkpoint_info_complete (self, info, NULL, error);
+		return;
+	}
+
+	checkpoint = get_checkpoint_by_path (self, info->path);
+	if (!checkpoint) {
+		/* this is really problematic. The async request returned, but
+		 * we don't yet have a visible (fully initialized) NMCheckpoint instance
+		 * to return. Wait longer for it to appear. However, it's ugly. */
+		return;
+	}
+
+	checkpoint_info_complete (self, info, checkpoint, NULL);
+}
+
+void
+nm_manager_checkpoint_create (NMManager *manager,
+                              const GPtrArray *devices,
+                              guint32 rollback_timeout,
+                              NMCheckpointCreateFlags flags,
+                              GCancellable *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	gs_free const char **paths = NULL;
+	CheckpointInfo *info;
+
+	g_return_if_fail (NM_IS_MANAGER (manager));
+
+	info = g_slice_new0 (CheckpointInfo);
+	info->manager = manager;
+	info->simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
+	                                          nm_manager_checkpoint_create);
+	if (cancellable)
+		g_simple_async_result_set_check_cancellable (info->simple, cancellable);
+	paths = get_device_paths (devices);
+	nmdbus_manager_call_checkpoint_create (NM_MANAGER_GET_PRIVATE (manager)->proxy,
+	                                       paths,
+	                                       rollback_timeout,
+	                                       flags,
+	                                       cancellable,
+	                                       checkpoint_created_cb,
+	                                       info);
+	priv->added_checkpoints = g_slist_append (priv->added_checkpoints, info);
+}
+
+NMCheckpoint *
+nm_manager_checkpoint_create_finish (NMManager *manager,
+                                     GAsyncResult *result,
+                                     GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
+	g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+	else
+		return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+static void
+checkpoint_destroy_cb (GObject *object,
+                       GAsyncResult *result,
+                       gpointer user_data)
+{
+	GSimpleAsyncResult *simple = user_data;
+	GError *error = NULL;
+
+	if (nmdbus_manager_call_checkpoint_destroy_finish (NMDBUS_MANAGER (object),
+	                                                   result, &error))
+		g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+	else {
+		g_dbus_error_strip_remote_error (error);
+		g_simple_async_result_take_error (simple, error);
+	}
+	g_simple_async_result_complete (simple);
+	g_object_unref (simple);
+}
+
+void
+nm_manager_checkpoint_destroy (NMManager *manager,
+                               const char *checkpoint_path,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_if_fail (NM_IS_MANAGER (manager));
+	g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/');
+
+	simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
+	                                    nm_manager_checkpoint_destroy);
+	if (cancellable)
+		g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	nmdbus_manager_call_checkpoint_destroy (NM_MANAGER_GET_PRIVATE (manager)->proxy,
+	                                        checkpoint_path,
+	                                        cancellable,
+	                                        checkpoint_destroy_cb, simple);
+}
+
+gboolean
+nm_manager_checkpoint_destroy_finish (NMManager *manager,
+                                      GAsyncResult *result,
+                                      GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (manager),
+	                                                      nm_manager_checkpoint_destroy),
+	                      FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (simple, error))
+		return FALSE;
+	else
+		return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+static void
+checkpoint_rollback_cb (GObject *object,
+                        GAsyncResult *result,
+                        gpointer user_data)
+{
+	GSimpleAsyncResult *simple = user_data;
+	GError *error = NULL;
+	GVariant *variant = NULL;
+	GVariantIter iter;
+	GHashTable *hash;
+	const char *path;
+	guint r;
+
+	if (nmdbus_manager_call_checkpoint_rollback_finish (NMDBUS_MANAGER (object),
+	                                                    &variant,
+	                                                    result,
+	                                                    &error)) {
+		hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
+		g_variant_iter_init (&iter, variant);
+		while (g_variant_iter_next (&iter, "{&su}", &path, &r))
+			g_hash_table_insert (hash, g_strdup (path), GUINT_TO_POINTER (r));
+		g_simple_async_result_set_op_res_gpointer (simple, hash, (GDestroyNotify) g_hash_table_unref);
+	} else {
+		g_dbus_error_strip_remote_error (error);
+		g_simple_async_result_take_error (simple, error);
+	}
+	g_simple_async_result_complete (simple);
+	g_object_unref (simple);
+}
+
+void
+nm_manager_checkpoint_rollback (NMManager *manager,
+                                const char *checkpoint_path,
+                                GCancellable *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_if_fail (NM_IS_MANAGER (manager));
+	g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/');
+
+	simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
+	                                    nm_manager_checkpoint_rollback);
+	if (cancellable)
+		g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	nmdbus_manager_call_checkpoint_rollback (NM_MANAGER_GET_PRIVATE (manager)->proxy,
+	                                         checkpoint_path,
+	                                         cancellable,
+	                                         checkpoint_rollback_cb, simple);
+}
+
+GHashTable *
+nm_manager_checkpoint_rollback_finish (NMManager *manager,
+                                       GAsyncResult *result,
+                                       GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (manager),
+	                                                      nm_manager_checkpoint_rollback),
+	                      NULL);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	if (g_simple_async_result_propagate_error (simple, error))
+		return NULL;
+	else
+		return g_simple_async_result_get_op_res_gpointer (simple);
+}
+
+static void
+checkpoint_adjust_rollback_timeout_cb (GObject *object,
+                                       GAsyncResult *result,
+                                       gpointer user_data)
+{
+	gs_unref_object GSimpleAsyncResult *simple = user_data;
+	GError *error = NULL;
+
+	if (nmdbus_manager_call_checkpoint_adjust_rollback_timeout_finish (NMDBUS_MANAGER (object),
+	                                                                   result,
+	                                                                   &error))
+		g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+	else {
+		g_dbus_error_strip_remote_error (error);
+		g_simple_async_result_take_error (simple, error);
+	}
+	g_simple_async_result_complete (simple);
+}
+
+void
+nm_manager_checkpoint_adjust_rollback_timeout (NMManager *manager,
+                                               const char *checkpoint_path,
+                                               guint32 add_timeout,
+                                               GCancellable *cancellable,
+                                               GAsyncReadyCallback callback,
+                                               gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_if_fail (NM_IS_MANAGER (manager));
+	g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/');
+
+	simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
+	                                    nm_manager_checkpoint_adjust_rollback_timeout);
+	if (cancellable)
+		g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+	nmdbus_manager_call_checkpoint_adjust_rollback_timeout (NM_MANAGER_GET_PRIVATE (manager)->proxy,
+	                                                        checkpoint_path,
+	                                                        add_timeout,
+	                                                        cancellable,
+	                                                        checkpoint_adjust_rollback_timeout_cb,
+	                                                        simple);
+}
+
+gboolean
+nm_manager_checkpoint_adjust_rollback_timeout_finish (NMManager *manager,
+                                                      GAsyncResult *result,
+                                                      GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (manager),
+	                                                      nm_manager_checkpoint_adjust_rollback_timeout),
+	                      FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/*****************************************************************************/
+
 static void
 constructed (GObject *object)
 {
@@ -1482,6 +1869,9 @@ get_property (GObject *object,
 	case PROP_DEVICES:
 		g_value_take_boxed (value, _nm_utils_copy_object_array (nm_manager_get_devices (self)));
 		break;
+	case PROP_CHECKPOINTS:
+		g_value_take_boxed (value, _nm_utils_copy_object_array (nm_manager_get_checkpoints (self)));
+		break;
 	case PROP_METERED:
 		g_value_set_uint (value, priv->metered);
 		break;
@@ -1510,11 +1900,13 @@ nm_manager_class_init (NMManagerClass *manager_class)
 	object_class->finalize = finalize;
 
 	nm_object_class->init_dbus = init_dbus;
+	nm_object_class->object_creation_failed = object_creation_failed;
 
 	manager_class->device_added = device_added;
 	manager_class->device_removed = device_removed;
 	manager_class->active_connection_added = active_connection_added;
 	manager_class->active_connection_removed = active_connection_removed;
+	manager_class->checkpoint_added = checkpoint_added;
 
 	/* properties */
 
@@ -1622,6 +2014,12 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		                     G_TYPE_PTR_ARRAY,
 		                     G_PARAM_READABLE |
 		                     G_PARAM_STATIC_STRINGS));
+	g_object_class_install_property
+		(object_class, PROP_CHECKPOINTS,
+		 g_param_spec_boxed (NM_MANAGER_CHECKPOINTS, "", "",
+		                     G_TYPE_PTR_ARRAY,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
 	/**
 	 * NMManager:metered:
 	 *
@@ -1693,6 +2091,23 @@ nm_manager_class_init (NMManagerClass *manager_class)
 		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 1,
 		              G_TYPE_OBJECT);
+	signals[CHECKPOINT_ADDED] =
+		g_signal_new ("checkpoint-added",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMManagerClass, checkpoint_added),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 1,
+		              G_TYPE_OBJECT);
+	signals[CHECKPOINT_REMOVED] =
+		g_signal_new ("checkpoint-removed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMManagerClass, checkpoint_removed),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 1,
+		              G_TYPE_OBJECT);
+
 	signals[PERMISSION_CHANGED] =
 		g_signal_new ("permission-changed",
 		              G_OBJECT_CLASS_TYPE (object_class),