diff --git a/Makefile.am b/Makefile.am
index 4da2c8f6dc..6599d939c0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -737,6 +737,7 @@ libnm_lib_h_pub_real = \
libnm/NetworkManager.h \
libnm/nm-access-point.h \
libnm/nm-active-connection.h \
+ libnm/nm-checkpoint.h \
libnm/nm-client.h \
libnm/nm-device-adsl.h \
libnm/nm-device-bond.h \
@@ -792,6 +793,7 @@ libnm_lib_h_priv = \
libnm_lib_c_real = \
libnm/nm-access-point.c \
libnm/nm-active-connection.c \
+ libnm/nm-checkpoint.c \
libnm/nm-client.c \
libnm/nm-dbus-helpers.c \
libnm/nm-device-adsl.c \
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index 7f75c84955..7752eecbc6 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -76,6 +76,7 @@
#define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre"
#define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL NM_DBUS_INTERFACE_DEVICE ".IPTunnel"
#define NM_DBUS_INTERFACE_DEVICE_STATISTICS NM_DBUS_INTERFACE_DEVICE ".Statistics"
+#define NM_DBUS_INTERFACE_CHECKPOINT NM_DBUS_INTERFACE ".Checkpoint"
#define NM_DBUS_INTERFACE_SETTINGS "org.freedesktop.NetworkManager.Settings"
#define NM_DBUS_PATH_SETTINGS "/org/freedesktop/NetworkManager/Settings"
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index 18ff993de4..7b234ca762 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -1221,3 +1221,18 @@ global:
nm_setting_wireless_security_pmf_get_type;
nm_setting_wireless_security_wps_method_get_type;
} libnm_1_8_0;
+
+libnm_1_12_0 {
+global:
+ nm_checkpoint_get_created;
+ nm_checkpoint_get_devices;
+ nm_checkpoint_get_rollback_timeout;
+ nm_checkpoint_get_type;
+ nm_client_checkpoint_create_async;
+ nm_client_checkpoint_create_finish;
+ nm_client_checkpoint_destroy_async;
+ nm_client_checkpoint_destroy_finish;
+ nm_client_checkpoint_rollback_async;
+ nm_client_checkpoint_rollback_finish;
+ nm_client_get_checkpoints;
+} libnm_1_10_0;
diff --git a/libnm/nm-checkpoint.c b/libnm/nm-checkpoint.c
new file mode 100644
index 0000000000..264c819538
--- /dev/null
+++ b/libnm/nm-checkpoint.c
@@ -0,0 +1,219 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * .
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-checkpoint.h"
+#include "nm-core-internal.h"
+#include "nm-dbus-interface.h"
+#include "nm-device.h"
+#include "nm-object-private.h"
+
+typedef struct {
+ GPtrArray *devices;
+ gint64 created;
+ guint32 rollback_timeout;
+} NMCheckpointPrivate;
+
+struct _NMCheckpoint {
+ NMObject parent;
+ NMCheckpointPrivate _priv;
+};
+
+struct _NMCheckpointClass {
+ NMObjectClass parent;
+};
+
+G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_OBJECT)
+
+#define NM_CHECKPOINT_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMCheckpoint, NM_IS_CHECKPOINT)
+
+enum {
+ PROP_0,
+ PROP_DEVICES,
+ PROP_CREATED,
+ PROP_ROLLBACK_TIMEOUT,
+
+ LAST_PROP
+};
+
+/**
+ * nm_checkpoint_get_devices:
+ * @checkpoint: a #NMCheckpoint
+ *
+ * The devices that are part of this checkpoint.
+ *
+ * Returns: (element-type NMDevice): the devices list.
+ *
+ * Since: 1.12
+ **/
+const GPtrArray *
+nm_checkpoint_get_devices (NMCheckpoint *checkpoint)
+{
+ g_return_val_if_fail (NM_IS_CHECKPOINT (checkpoint), NULL);
+
+ return NM_CHECKPOINT_GET_PRIVATE (checkpoint)->devices;
+}
+
+/**
+ * nm_checkpoint_get_created:
+ * @checkpoint: a #NMCheckpoint
+ *
+ * Gets the timestamp (in CLOCK_BOOTTIME milliseconds)
+ * of checkpoint creation.
+ *
+ * Returns: the timestamp of checkpoint creation.
+ *
+ * Since: 1.12
+ **/
+gint64
+nm_checkpoint_get_created (NMCheckpoint *checkpoint)
+{
+ g_return_val_if_fail (NM_IS_CHECKPOINT (checkpoint), 0);
+
+ return NM_CHECKPOINT_GET_PRIVATE (checkpoint)->created;
+}
+
+/**
+ * nm_checkpoint_get_rollback_timeout:
+ * @checkpoint: a #NMCheckpoint
+ *
+ * Gets the the timeout in seconds for automatic rollback.
+ *
+ * Returns: the rollback timeout.
+ *
+ * Since: 1.12
+ **/
+guint32
+nm_checkpoint_get_rollback_timeout (NMCheckpoint *checkpoint)
+{
+ g_return_val_if_fail (NM_IS_CHECKPOINT (checkpoint), 0);
+
+ return NM_CHECKPOINT_GET_PRIVATE (checkpoint)->rollback_timeout;
+}
+
+/*****************************************************************************/
+
+static void
+nm_checkpoint_init (NMCheckpoint *checkpoint)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (NM_CHECKPOINT (object));
+
+ g_ptr_array_unref (priv->devices);
+
+ G_OBJECT_CLASS (nm_checkpoint_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMCheckpoint *checkpoint = NM_CHECKPOINT (object);
+ NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (checkpoint);
+
+ switch (prop_id) {
+ case PROP_DEVICES:
+ g_value_take_boxed (value, _nm_utils_copy_object_array (priv->devices));
+ 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
+init_dbus (NMObject *object)
+{
+ NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (NM_CHECKPOINT (object));
+ const NMPropertiesInfo property_info[] = {
+ { NM_CHECKPOINT_DEVICES, &priv->devices, NULL, NM_TYPE_DEVICE },
+ { NM_CHECKPOINT_CREATED, &priv->created },
+ { NM_CHECKPOINT_ROLLBACK_TIMEOUT, &priv->rollback_timeout },
+ { NULL },
+ };
+
+ NM_OBJECT_CLASS (nm_checkpoint_parent_class)->init_dbus (object);
+
+ _nm_object_register_properties (object,
+ NM_DBUS_INTERFACE_CHECKPOINT,
+ property_info);
+}
+
+
+static void
+nm_checkpoint_class_init (NMCheckpointClass *checkpoint_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (checkpoint_class);
+ NMObjectClass *nm_object_class = NM_OBJECT_CLASS (checkpoint_class);
+
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ nm_object_class->init_dbus = init_dbus;
+
+ /**
+ * NMCheckpoint:devices:
+ *
+ * The devices that are part of this checkpoint.
+ *
+ * Since: 1.12
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DEVICES,
+ g_param_spec_boxed (NM_CHECKPOINT_DEVICES, "", "",
+ G_TYPE_PTR_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMCheckpoint:created:
+ *
+ * The timestamp (in CLOCK_BOOTTIME milliseconds) of checkpoint creation.
+ *
+ * Since: 1.12
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CREATED,
+ g_param_spec_int64 (NM_CHECKPOINT_CREATED, "", "",
+ G_MININT64, G_MAXINT64, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMCheckpoint:rollback-timeout:
+ *
+ * Timeout in seconds for automatic rollback, or zero.
+ *
+ * Since: 1.12
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ROLLBACK_TIMEOUT,
+ g_param_spec_uint (NM_CHECKPOINT_ROLLBACK_TIMEOUT, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-checkpoint.h b/libnm/nm-checkpoint.h
new file mode 100644
index 0000000000..a0c9d722c5
--- /dev/null
+++ b/libnm/nm-checkpoint.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * .
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_CHECKPOINT_H__
+#define __NM_CHECKPOINT_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include "nm-object.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_CHECKPOINT (nm_checkpoint_get_type ())
+#define NM_CHECKPOINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CHECKPOINT, NMCheckpoint))
+#define NM_CHECKPOINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CHECKPOINT, NMCheckpointClass))
+#define NM_IS_CHECKPOINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CHECKPOINT))
+#define NM_IS_CHECKPOINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CHECKPOINT))
+#define NM_CHECKPOINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CHECKPOINT, NMCheckpointClass))
+
+#define NM_CHECKPOINT_DEVICES "devices"
+#define NM_CHECKPOINT_CREATED "created"
+#define NM_CHECKPOINT_ROLLBACK_TIMEOUT "rollback-timeout"
+
+/**
+ * NMCheckpoint:
+ */
+typedef struct _NMCheckpointClass NMCheckpointClass;
+
+GType nm_checkpoint_get_type (void);
+
+NM_AVAILABLE_IN_1_12
+const GPtrArray *nm_checkpoint_get_devices (NMCheckpoint *checkpoint);
+NM_AVAILABLE_IN_1_12
+gint64 nm_checkpoint_get_created (NMCheckpoint *checkpoint);
+NM_AVAILABLE_IN_1_12
+guint32 nm_checkpoint_get_rollback_timeout (NMCheckpoint *checkpoint);
+
+G_END_DECLS
+
+#endif /* __NM_CHECKPOINT_H__ */
diff --git a/libnm/nm-client.c b/libnm/nm-client.c
index 8f9c50e9f7..0cf4d4361c 100644
--- a/libnm/nm-client.c
+++ b/libnm/nm-client.c
@@ -50,6 +50,7 @@
#include "nm-access-point.h"
#include "nm-active-connection.h"
+#include "nm-checkpoint.h"
#include "nm-device-adsl.h"
#include "nm-device-bond.h"
#include "nm-device-bridge.h"
@@ -139,6 +140,7 @@ enum {
PROP_DNS_MODE,
PROP_DNS_RC_MANAGER,
PROP_DNS_CONFIGURATION,
+ PROP_CHECKPOINTS,
LAST_PROP
};
@@ -2058,6 +2060,294 @@ dns_notify (GObject *object,
}
}
+static void
+checkpoint_create_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple = user_data;
+ NMCheckpoint *checkpoint;
+ GError *error = NULL;
+
+ checkpoint = nm_manager_checkpoint_create_finish (NM_MANAGER (object), result, &error);
+ if (checkpoint)
+ g_simple_async_result_set_op_res_gpointer (simple, checkpoint, g_object_unref);
+ else
+ g_simple_async_result_take_error (simple, error);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+/**
+ * nm_client_get_checkpoints:
+ * @client: a #NMClient
+ *
+ * Gets all the active checkpoints.
+ *
+ * Returns: (transfer none) (element-type NMCheckpoint): a #GPtrArray
+ * containing all the #NMCheckpoint. The returned array is owned by the
+ * #NMClient object and should not be modified.
+ *
+ * Since: 1.12
+ **/
+const GPtrArray *
+nm_client_get_checkpoints (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+
+ if (!nm_client_get_nm_running (client))
+ return ∅
+
+ return nm_manager_get_checkpoints (NM_CLIENT_GET_PRIVATE (client)->manager);
+}
+
+/**
+ * nm_client_checkpoint_create_async:
+ * @client: the %NMClient
+ * @devices: (element-type NMDevice): a list of devices for which a
+ * checkpoint should be created.
+ * @rollback_timeout: the rollback timeout in seconds
+ * @flags: creation flags
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: (scope async): callback to be called when the add operation completes
+ * @user_data: (closure): caller-specific data passed to @callback
+ *
+ * Creates a checkpoint of the current networking configuration
+ * for given interfaces. An empty @devices argument means all
+ * devices. If @rollback_timeout is not zero, a rollback is
+ * automatically performed after the given timeout.
+ *
+ * Since: 1.12
+ **/
+void
+nm_client_checkpoint_create_async (NMClient *client,
+ const GPtrArray *devices,
+ guint32 rollback_timeout,
+ NMCheckpointCreateFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ GError *error = NULL;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+
+ if (!_nm_client_check_nm_running (client, &error)) {
+ g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error);
+ return;
+ }
+
+ simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data,
+ nm_client_checkpoint_create_async);
+ nm_manager_checkpoint_create_async (NM_CLIENT_GET_PRIVATE (client)->manager,
+ devices, rollback_timeout, flags,
+ cancellable, checkpoint_create_cb, simple);
+}
+
+/**
+ * nm_client_checkpoint_create_finish:
+ * @client: the #NMClient
+ * @result: the result passed to the #GAsyncReadyCallback
+ * @error: location for a #GError, or %NULL
+ *
+ * Gets the result of a call to nm_client_checkpoint_create_async().
+ *
+ * Returns: (transfer full): the new #NMCheckpoint on success, %NULL on
+ * failure, in which case @error will be set.
+ *
+ * Since: 1.12
+ **/
+NMCheckpoint *
+nm_client_checkpoint_create_finish (NMClient *client,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), 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 (nm_manager_checkpoint_destroy_finish (NM_MANAGER (object), result, &error))
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ else
+ g_simple_async_result_take_error (simple, error);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+/**
+ * nm_client_checkpoint_destroy_async:
+ * @client: the %NMClient
+ * @checkpoint: a checkpoint
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: (scope async): callback to be called when the add operation completes
+ * @user_data: (closure): caller-specific data passed to @callback
+ *
+ * Destroys an existing checkpoint without performing a rollback.
+ *
+ * Since: 1.12
+ **/
+void
+nm_client_checkpoint_destroy_async (NMClient *client,
+ NMCheckpoint *checkpoint,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ GError *error = NULL;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+
+ if (!_nm_client_check_nm_running (client, &error)) {
+ g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error);
+ return;
+ }
+
+ simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data,
+ nm_client_checkpoint_destroy_async);
+ nm_manager_checkpoint_destroy_async (NM_CLIENT_GET_PRIVATE (client)->manager,
+ checkpoint,
+ cancellable, checkpoint_destroy_cb, simple);
+}
+
+/**
+ * nm_client_checkpoint_destroy_finish:
+ * @client: an #NMClient
+ * @result: the result passed to the #GAsyncReadyCallback
+ * @error: location for a #GError, or %NULL
+ *
+ * Gets the result of a call to nm_client_checkpoint_destroy_async().
+ *
+ * Returns: %TRUE on success or %FALSE on failure, in which case
+ * @error will be set.
+ *
+ * Since: 1.12
+ **/
+gboolean
+nm_client_checkpoint_destroy_finish (NMClient *client,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
+ g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), 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;
+ GHashTable *hash;
+ GError *error = NULL;
+
+ hash = nm_manager_checkpoint_rollback_finish (NM_MANAGER (object), result, &error);
+ if (hash)
+ g_simple_async_result_set_op_res_gpointer (simple, hash, (GDestroyNotify) g_hash_table_unref);
+ else
+ g_simple_async_result_take_error (simple, error);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+/**
+ * nm_client_checkpoint_rollback_async:
+ * @client: the %NMClient
+ * @checkpoint: a checkpoint
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: (scope async): callback to be called when the add operation completes
+ * @user_data: (closure): caller-specific data passed to @callback
+ *
+ * Performs the rollback of a checkpoint before the timeout is reached.
+ *
+ * Since: 1.12
+ **/
+void
+nm_client_checkpoint_rollback_async (NMClient *client,
+ NMCheckpoint *checkpoint,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ GError *error = NULL;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+
+ if (!_nm_client_check_nm_running (client, &error)) {
+ g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error);
+ return;
+ }
+
+ simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data,
+ nm_client_checkpoint_rollback_async);
+ nm_manager_checkpoint_rollback_async (NM_CLIENT_GET_PRIVATE (client)->manager,
+ checkpoint,
+ cancellable, checkpoint_rollback_cb, simple);
+}
+
+/**
+ * nm_client_checkpoint_rollback_finish:
+ * @client: an #NMClient
+ * @result: the result passed to the #GAsyncReadyCallback
+ * @error: location for a #GError, or %NULL
+ *
+ * Gets the result of a call to nm_client_checkpoint_rollback_async().
+ *
+ * Returns: (transfer full) (element-type utf8 guint32): an hash table of
+ * devices and results. Devices are represented by their original
+ * D-Bus path; each result is a #NMRollbackResult.
+ *
+ * Since: 1.12
+ **/
+GHashTable *
+nm_client_checkpoint_rollback_finish (NMClient *client,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ GHashTable *hash;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), 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 {
+ hash = g_simple_async_result_get_op_res_gpointer (simple);
+ return g_hash_table_ref (hash);
+ }
+}
+
/****************************************************************/
/* Object Initialization */
/****************************************************************/
@@ -2188,6 +2478,8 @@ obj_nm_for_gdbus_object (NMClient *self, GDBusObject *object, GDBusObjectManager
type = NM_TYPE_VPN_CONNECTION;
else if (strcmp (ifname, NM_DBUS_INTERFACE_WIMAX_NSP) == 0)
type = NM_TYPE_WIMAX_NSP;
+ else if (strcmp (ifname, NM_DBUS_INTERFACE_CHECKPOINT) == 0)
+ type = NM_TYPE_CHECKPOINT;
if (type != G_TYPE_INVALID)
break;
@@ -2763,6 +3055,12 @@ get_property (GObject *object, guint prop_id,
case PROP_ALL_DEVICES:
g_value_take_boxed (value, _nm_utils_copy_object_array (nm_client_get_all_devices (self)));
break;
+ case PROP_CHECKPOINTS:
+ if (priv->manager)
+ g_object_get_property (G_OBJECT (priv->manager), pspec->name, value);
+ else
+ g_value_take_boxed (value, g_ptr_array_new ());
+ break;
/* Settings properties. */
case PROP_CONNECTIONS:
@@ -3169,6 +3467,20 @@ nm_client_class_init (NMClientClass *client_class)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
+ /**
+ * NMClient::checkpoints
+ *
+ * The list of active checkpoints.
+ *
+ * Since: 1.12
+ */
+ 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));
+
/* signals */
/**
diff --git a/libnm/nm-client.h b/libnm/nm-client.h
index 3b37b55a10..9833f48294 100644
--- a/libnm/nm-client.h
+++ b/libnm/nm-client.h
@@ -403,6 +403,45 @@ const char *nm_client_get_dns_rc_manager (NMClient *client);
NM_AVAILABLE_IN_1_6
const GPtrArray *nm_client_get_dns_configuration (NMClient *client);
+
+NM_AVAILABLE_IN_1_12
+const GPtrArray *nm_client_get_checkpoints (NMClient *client);
+
+NM_AVAILABLE_IN_1_12
+void nm_client_checkpoint_create_async (NMClient *client,
+ const GPtrArray *devices,
+ guint32 rollback_timeout,
+ NMCheckpointCreateFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+NM_AVAILABLE_IN_1_12
+NMCheckpoint *nm_client_checkpoint_create_finish (NMClient *client,
+ GAsyncResult *result,
+ GError **error);
+
+NM_AVAILABLE_IN_1_12
+void nm_client_checkpoint_destroy_async (NMClient *client,
+ NMCheckpoint *checkpoint,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+NM_AVAILABLE_IN_1_12
+gboolean nm_client_checkpoint_destroy_finish (NMClient *client,
+ GAsyncResult *result,
+ GError **error);
+
+NM_AVAILABLE_IN_1_12
+void nm_client_checkpoint_rollback_async (NMClient *client,
+ NMCheckpoint *checkpoint,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+NM_AVAILABLE_IN_1_12
+GHashTable *nm_client_checkpoint_rollback_finish (NMClient *client,
+ GAsyncResult *result,
+ GError **error);
+
G_END_DECLS
#endif /* __NM_CLIENT_H__ */
diff --git a/libnm/nm-manager.c b/libnm/nm-manager.c
index e664b71072..aa749232be 100644
--- a/libnm/nm-manager.c
+++ b/libnm/nm-manager.c
@@ -26,6 +26,7 @@
#include
#include "nm-utils.h"
+#include "nm-checkpoint.h"
#include "nm-common-macros.h"
#include "nm-device-ethernet.h"
#include "nm-device-wifi.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_streq (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)
{
@@ -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)
{
@@ -1109,6 +1180,21 @@ 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);
+}
+
+static void
+checkpoint_removed (NMManager *manager, NMCheckpoint *checkpoint)
+{
+}
+
gboolean
nm_manager_deactivate_connection (NMManager *manager,
NMActiveConnection *active,
@@ -1208,6 +1294,226 @@ 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;
+ GError *error = NULL;
+
+ 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 (info->manager, info, NULL, error);
+ g_clear_error (&error);
+ }
+}
+
+void
+nm_manager_checkpoint_create_async (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_async);
+ 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_async (NMManager *manager,
+ NMCheckpoint *checkpoint,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ const char *path;
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (NM_IS_MANAGER (manager));
+ g_return_if_fail (NM_IS_CHECKPOINT (checkpoint));
+
+ simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
+ nm_manager_checkpoint_destroy_async);
+
+ path = nm_object_get_path (NM_OBJECT (checkpoint));
+ nmdbus_manager_call_checkpoint_destroy (NM_MANAGER_GET_PRIVATE (manager)->proxy,
+ 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_async),
+ 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 (g_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_async (NMManager *manager,
+ NMCheckpoint *checkpoint,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ const char *path;
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (NM_IS_MANAGER (manager));
+ g_return_if_fail (NM_IS_CHECKPOINT (checkpoint));
+
+ simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
+ nm_manager_checkpoint_rollback_async);
+
+ path = nm_object_get_path (NM_OBJECT (checkpoint));
+ nmdbus_manager_call_checkpoint_rollback (NM_MANAGER_GET_PRIVATE (manager)->proxy,
+ 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_async),
+ 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
constructed (GObject *object)
{
@@ -1472,6 +1778,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;
@@ -1500,11 +1809,14 @@ 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;
+ manager_class->checkpoint_removed = checkpoint_removed;
/* properties */
@@ -1612,6 +1924,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:
*
@@ -1683,6 +2001,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),
diff --git a/libnm/nm-manager.h b/libnm/nm-manager.h
index 209a99a00c..852a088972 100644
--- a/libnm/nm-manager.h
+++ b/libnm/nm-manager.h
@@ -51,6 +51,7 @@ G_BEGIN_DECLS
#define NM_MANAGER_PRIMARY_CONNECTION "primary-connection"
#define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection"
#define NM_MANAGER_DEVICES "devices"
+#define NM_MANAGER_CHECKPOINTS "checkpoints"
#define NM_MANAGER_METERED "metered"
#define NM_MANAGER_ALL_DEVICES "all-devices"
@@ -69,6 +70,8 @@ typedef struct {
void (*device_removed) (NMManager *manager, NMDevice *device);
void (*active_connection_added) (NMManager *manager, NMActiveConnection *ac);
void (*active_connection_removed) (NMManager *manager, NMActiveConnection *ac);
+ void (*checkpoint_added) (NMManager *manager, NMCheckpoint *checkpoint);
+ void (*checkpoint_removed) (NMManager *manager, NMCheckpoint *checkpoint);
void (*permission_changed) (NMManager *manager,
NMClientPermission permission,
NMClientPermissionResult result);
@@ -184,6 +187,34 @@ gboolean nm_manager_deactivate_connection_finish (NMManager *manager,
GAsyncResult *result,
GError **error);
+const GPtrArray *nm_manager_get_checkpoints (NMManager *manager);
+void nm_manager_checkpoint_create_async (NMManager *manager,
+ const GPtrArray *devices,
+ guint32 rollback_timeout,
+ NMCheckpointCreateFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+NMCheckpoint *nm_manager_checkpoint_create_finish (NMManager *manager,
+ GAsyncResult *result,
+ GError **error);
+void nm_manager_checkpoint_destroy_async (NMManager *manager,
+ NMCheckpoint *checkpoint,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean nm_manager_checkpoint_destroy_finish (NMManager *manager,
+ GAsyncResult *result,
+ GError **error);
+void nm_manager_checkpoint_rollback_async (NMManager *manager,
+ NMCheckpoint *checkpoint,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GHashTable *nm_manager_checkpoint_rollback_finish (NMManager *manager,
+ GAsyncResult *result,
+ GError **error);
+
G_END_DECLS
#endif /* __NM_MANAGER_H__ */
diff --git a/libnm/nm-types.h b/libnm/nm-types.h
index 4310120fec..dff8cb901f 100644
--- a/libnm/nm-types.h
+++ b/libnm/nm-types.h
@@ -28,6 +28,7 @@
typedef struct _NMAccessPoint NMAccessPoint;
typedef struct _NMActiveConnection NMActiveConnection;
+typedef struct _NMCheckpoint NMCheckpoint;
typedef struct _NMClient NMClient;
typedef struct _NMDevice NMDevice;
typedef struct _NMDeviceAdsl NMDeviceAdsl;