summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2017-11-09 10:14:21 +0100
committerBeniamino Galvani <bgalvani@redhat.com>2017-11-09 10:14:21 +0100
commitf02df2c2ca0a57696840e80c647b4d8176ea449b (patch)
tree02ad6d1245f6a3ed585ec7081f0c91fd00a64de9
parenta5b4850682d6eb34be65583d86ebb989f65a89cc (diff)
parent77d3b1555e83116d849c0ad9bca5ae8322e6c936 (diff)
checkpoint: add libnm support
Add support for checkpoint/rollback functionality to libnm. https://bugzilla.redhat.com/show_bug.cgi?id=1496739
-rw-r--r--Makefile.am2
-rw-r--r--examples/python/gi/checkpoint.py130
-rw-r--r--introspection/org.freedesktop.NetworkManager.xml7
-rw-r--r--libnm-core/nm-dbus-interface.h1
-rw-r--r--libnm/libnm.ver15
-rw-r--r--libnm/nm-checkpoint.c219
-rw-r--r--libnm/nm-checkpoint.h53
-rw-r--r--libnm/nm-client.c312
-rw-r--r--libnm/nm-client.h39
-rw-r--r--libnm/nm-manager.c335
-rw-r--r--libnm/nm-manager.h31
-rw-r--r--libnm/nm-object.c2
-rw-r--r--libnm/nm-types.h1
-rw-r--r--src/nm-checkpoint-manager.c138
-rw-r--r--src/nm-checkpoint-manager.h4
-rw-r--r--src/nm-manager.c38
-rw-r--r--src/nm-manager.h2
17 files changed, 1270 insertions, 59 deletions
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/examples/python/gi/checkpoint.py b/examples/python/gi/checkpoint.py
new file mode 100644
index 0000000000..513e6c3a0a
--- /dev/null
+++ b/examples/python/gi/checkpoint.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+# -*- Mode: Python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+# vim: ft=python ts=4 sts=4 sw=4 et ai
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU 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 General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Copyright 2017 Red Hat, Inc.
+
+import sys
+
+import gi
+gi.require_version('NM', '1.0')
+from gi.repository import GLib, NM
+
+def usage():
+ print "Usage: %s [COMMAND [ARG]...]" % sys.argv[0]
+ print ""
+ print " COMMANDS: show"
+ print " create TIMEOUT [DEV]..."
+ print " destroy PATH|NUMBER"
+ print " rollback PATH|NUMBER"
+ print
+ sys.exit(1)
+
+def create_cb(client, result, data):
+ try:
+ checkpoint = client.checkpoint_create_finish(result)
+ print("%s" % checkpoint.get_path())
+ except Exception, e:
+ sys.stderr.write("Failed: %s\n" % e.message)
+ main_loop.quit()
+
+def do_create(client):
+ if len(sys.argv) < 3:
+ sys.exit("Failed: %s\n" % e.message)
+
+ timeout = int(sys.argv[2])
+ devices = []
+ for arg in sys.argv[3:]:
+ d = client.get_device_by_iface(arg)
+ if d is None:
+ sys.exit("Unknown device %s" % arg)
+ devices.append(d)
+
+ client.checkpoint_create_async(devices, timeout, 0, None, create_cb, None)
+
+def destroy_cb(client, result, data):
+ try:
+ if client.checkpoint_destroy_finish(result) == True:
+ print "Success"
+ except Exception, e:
+ sys.stderr.write("Failed: %s\n" % e.message)
+ main_loop.quit()
+
+def find_checkpoint(client, arg):
+ try:
+ num = int(arg)
+ path = "/org/freedesktop/NetworkManager/Checkpoint/%u" % num
+ except Exception, e:
+ path = arg
+
+ for c in client.get_checkpoints():
+ if c.get_path() == path:
+ return c
+ return None
+
+def do_destroy(client):
+ if len(sys.argv) < 3:
+ sys.exit("Missing checkpoint path")
+
+ checkpoint = find_checkpoint(client, sys.argv[2])
+ if checkpoint is None:
+ sys.exit("Uknown checkpoint %s" % sys.argv[2])
+
+ client.checkpoint_destroy_async(checkpoint, None, destroy_cb, None)
+
+def rollback_cb(client, result, data):
+ try:
+ res = client.checkpoint_rollback_finish(result)
+ for path in res:
+ d = client.get_device_by_path(path)
+ if d is None:
+ iface = path
+ else:
+ iface = d.get_iface()
+ print "%s => %s" % (iface, "OK" if res[path] == 0 else "ERROR")
+ except Exception, e:
+ sys.stderr.write("Failed: %s\n" % e.message)
+ main_loop.quit()
+
+def do_rollback(client):
+ if len(sys.argv) < 3:
+ sys.exit("Missing checkpoint path")
+
+ checkpoint = find_checkpoint(client, sys.argv[2])
+ if checkpoint is None:
+ sys.exit("Uknown checkpoint %s" % sys.argv[2])
+
+ client.checkpoint_rollback_async(checkpoint, None, rollback_cb, None)
+
+def do_show(client):
+ for c in client.get_checkpoints():
+ print "%s:" % c.get_path()
+ print " created: %u" % c.get_created()
+ print " timeout: %u seconds" % c.get_rollback_timeout()
+ print " devices:", ' '.join(sorted(map(lambda x: x.get_iface(), c.get_devices())))
+
+if __name__ == '__main__':
+ nm_client = NM.Client.new(None)
+ main_loop = GLib.MainLoop()
+
+ if len(sys.argv) < 2 or sys.argv[1] == 'show':
+ do_show(nm_client)
+ sys.exit(0)
+ elif sys.argv[1] == 'create':
+ do_create(nm_client)
+ elif sys.argv[1] == 'destroy':
+ do_destroy(nm_client)
+ elif sys.argv[1] == 'rollback':
+ do_rollback(nm_client)
+ else:
+ usage()
+
+ main_loop.run()
diff --git a/introspection/org.freedesktop.NetworkManager.xml b/introspection/org.freedesktop.NetworkManager.xml
index dceea7f21f..26a618c1bf 100644
--- a/introspection/org.freedesktop.NetworkManager.xml
+++ b/introspection/org.freedesktop.NetworkManager.xml
@@ -270,6 +270,13 @@
<property name="AllDevices" type="ao" access="read"/>
<!--
+ Checkpoints:
+
+ The list of active checkpoints.
+ -->
+ <property name="Checkpoints" type="ao" access="read"/>
+
+ <!--
NetworkingEnabled:
Indicates if overall networking is currently enabled or not. See the
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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_CHECKPOINT_H__
+#define __NM_CHECKPOINT_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> 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 &empty;
+
+ 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 <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"
@@ -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-object.c b/libnm/nm-object.c
index ef3358627e..c344a08dca 100644
--- a/libnm/nm-object.c
+++ b/libnm/nm-object.c
@@ -572,7 +572,7 @@ object_created (GObject *obj, const char *path, gpointer user_data)
object_class->object_creation_failed (odata->self, path);
}
- odata->objects[--odata->remaining] = obj ? g_object_ref (obj) : NULL;
+ odata->objects[odata->length - odata->remaining--] = obj ? g_object_ref (obj) : NULL;
object_property_maybe_complete (odata->self);
}
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;
diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c
index 033c11cc43..a205854be4 100644
--- a/src/nm-checkpoint-manager.c
+++ b/src/nm-checkpoint-manager.c
@@ -29,12 +29,15 @@
#include "nm-exported-object.h"
#include "nm-manager.h"
#include "nm-utils.h"
+#include "nm-utils/c-list.h"
/*****************************************************************************/
struct _NMCheckpointManager {
NMManager *_manager;
+ GParamSpec *property_spec;
GHashTable *checkpoints;
+ CList list;
guint rollback_timeout_id;
};
@@ -56,52 +59,71 @@ struct _NMCheckpointManager {
/*****************************************************************************/
+typedef struct {
+ CList list;
+ NMCheckpoint *checkpoint;
+} CheckpointItem;
+
static void update_rollback_timeout (NMCheckpointManager *self);
static void
-checkpoint_destroy (gpointer checkpoint)
+notify_checkpoints (NMCheckpointManager *self) {
+ g_object_notify_by_pspec ((GObject *) GET_MANAGER (self),
+ self->property_spec);
+}
+
+static void
+item_destroy (gpointer data)
{
- nm_exported_object_unexport (NM_EXPORTED_OBJECT (checkpoint));
- g_object_unref (G_OBJECT (checkpoint));
+ CheckpointItem *item = data;
+
+ c_list_unlink (&item->list);
+ nm_exported_object_unexport (NM_EXPORTED_OBJECT (item->checkpoint));
+ g_object_unref (G_OBJECT (item->checkpoint));
+ g_slice_free (CheckpointItem, item);
}
static gboolean
rollback_timeout_cb (NMCheckpointManager *self)
{
- NMCheckpoint *checkpoint;
- GHashTableIter iter;
+ CheckpointItem *item, *safe;
GVariant *result;
gint64 ts, now;
+ const char *path;
+ gboolean removed = FALSE;
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);
+ c_list_for_each_entry_safe (item, safe, &self->list, list) {
+ ts = nm_checkpoint_get_rollback_ts (item->checkpoint);
if (ts && ts <= now) {
- result = nm_checkpoint_rollback (checkpoint);
+ result = nm_checkpoint_rollback (item->checkpoint);
if (result)
g_variant_unref (result);
- g_hash_table_iter_remove (&iter);
+ path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (item->checkpoint));
+ if (!g_hash_table_remove (self->checkpoints, path))
+ nm_assert_not_reached();
+ removed = TRUE;
}
}
self->rollback_timeout_id = 0;
update_rollback_timeout (self);
+ if (removed)
+ notify_checkpoints (self);
+
return G_SOURCE_REMOVE;
}
static void
update_rollback_timeout (NMCheckpointManager *self)
{
- NMCheckpoint *checkpoint;
- GHashTableIter iter;
+ CheckpointItem *item;
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);
+ c_list_for_each_entry (item, &self->list, list) {
+ ts = nm_checkpoint_get_rollback_ts (item->checkpoint);
if (ts && ts < next)
next = ts;
}
@@ -120,13 +142,11 @@ update_rollback_timeout (NMCheckpointManager *self)
static NMCheckpoint *
find_checkpoint_for_device (NMCheckpointManager *self, NMDevice *device)
{
- GHashTableIter iter;
- NMCheckpoint *checkpoint;
+ CheckpointItem *item;
- 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;
+ c_list_for_each_entry (item, &self->list, list) {
+ if (nm_checkpoint_includes_device (item->checkpoint, device))
+ return item->checkpoint;
}
return NULL;
@@ -141,6 +161,7 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
{
NMManager *manager;
NMCheckpoint *checkpoint;
+ CheckpointItem *item;
const char * const *path;
gs_unref_ptrarray GPtrArray *devices = NULL;
NMDevice *device;
@@ -153,7 +174,23 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
manager = GET_MANAGER (self);
if (!device_paths || !device_paths[0]) {
- device_paths_free = nm_manager_get_device_paths (manager);
+ 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;
+ 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,
@@ -175,10 +212,12 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL)) {
for (i = 0; i < devices->len; i++) {
device = devices->pdata[i];
- if (find_checkpoint_for_device (self, device)) {
+ checkpoint = find_checkpoint_for_device (self, device);
+ if (checkpoint) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
- "a checkpoint for device '%s' already exists",
- nm_device_get_iface (device));
+ "device '%s' is already included in checkpoint %s",
+ nm_device_get_iface (device),
+ nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint)));
return NULL;
}
}
@@ -194,11 +233,16 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
nm_exported_object_export (NM_EXPORTED_OBJECT (checkpoint));
checkpoint_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint));
+ item = g_slice_new0 (CheckpointItem);
+ item->checkpoint = checkpoint;
+ c_list_link_tail (&self->list, &item->list);
+
if (!nm_g_hash_table_insert (self->checkpoints,
(gpointer) checkpoint_path,
- checkpoint))
+ item))
g_return_val_if_reached (NULL);
+ notify_checkpoints (self);
update_rollback_timeout (self);
return checkpoint;
@@ -211,6 +255,7 @@ nm_checkpoint_manager_destroy_all (NMCheckpointManager *self,
g_return_val_if_fail (self, FALSE);
g_hash_table_remove_all (self->checkpoints);
+ notify_checkpoints (self);
return TRUE;
}
@@ -228,7 +273,9 @@ nm_checkpoint_manager_destroy (NMCheckpointManager *self,
if (!nm_streq (checkpoint_path, "/")) {
ret = g_hash_table_remove (self->checkpoints, checkpoint_path);
- if (!ret) {
+ if (ret) {
+ notify_checkpoints (self);
+ } else {
g_set_error (error,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_INVALID_ARGUMENTS,
@@ -245,30 +292,53 @@ nm_checkpoint_manager_rollback (NMCheckpointManager *self,
GVariant **results,
GError **error)
{
- NMCheckpoint *cp;
+ CheckpointItem *item;
g_return_val_if_fail (self, 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);
- cp = g_hash_table_lookup (self->checkpoints, checkpoint_path);
- if (!cp) {
+ item = g_hash_table_lookup (self->checkpoints, checkpoint_path);
+ if (!item) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
"checkpoint %s does not exist", checkpoint_path);
return FALSE;
}
- *results = nm_checkpoint_rollback (cp);
+ *results = nm_checkpoint_rollback (item->checkpoint);
g_hash_table_remove (self->checkpoints, checkpoint_path);
+ notify_checkpoints (self);
return TRUE;
}
+char **
+nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self)
+{
+ CheckpointItem *item;
+ char **strv;
+ guint num, i = 0;
+
+ num = g_hash_table_size (self->checkpoints);
+ if (!num) {
+ nm_assert (c_list_is_empty (&self->list));
+ return NULL;
+ }
+
+ strv = g_new (char *, num + 1);
+ c_list_for_each_entry (item, &self->list, list)
+ strv[i++] = g_strdup (nm_exported_object_get_path (NM_EXPORTED_OBJECT (item->checkpoint)));
+ nm_assert (i == num);
+ strv[i] = NULL;
+
+ return strv;
+}
+
/*****************************************************************************/
NMCheckpointManager *
-nm_checkpoint_manager_new (NMManager *manager)
+nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec)
{
NMCheckpointManager *self;
@@ -284,7 +354,9 @@ nm_checkpoint_manager_new (NMManager *manager)
* instance. */
self->_manager = manager;
self->checkpoints = g_hash_table_new_full (nm_str_hash, g_str_equal,
- NULL, checkpoint_destroy);
+ NULL, item_destroy);
+ self->property_spec = spec;
+ c_list_init (&self->list);
return self;
}
diff --git a/src/nm-checkpoint-manager.h b/src/nm-checkpoint-manager.h
index 30e490417e..4ff75303ed 100644
--- a/src/nm-checkpoint-manager.h
+++ b/src/nm-checkpoint-manager.h
@@ -27,7 +27,7 @@
typedef struct _NMCheckpointManager NMCheckpointManager;
-NMCheckpointManager *nm_checkpoint_manager_new (NMManager *manager);
+NMCheckpointManager *nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec);
void nm_checkpoint_manager_unref (NMCheckpointManager *self);
NMCheckpoint *nm_checkpoint_manager_create (NMCheckpointManager *self,
@@ -47,4 +47,6 @@ gboolean nm_checkpoint_manager_rollback (NMCheckpointManager *self,
GVariant **results,
GError **error);
+char **nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self);
+
#endif /* __NM_CHECKPOINT_MANAGER_H__ */
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 45afc7f131..5e6dedcf82 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -220,6 +220,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMManager,
PROP_METERED,
PROP_GLOBAL_DNS_CONFIGURATION,
PROP_ALL_DEVICES,
+ PROP_CHECKPOINTS,
/* Not exported */
PROP_SLEEPING,
@@ -2589,28 +2590,6 @@ nm_manager_get_devices (NMManager *manager)
return NM_MANAGER_GET_PRIVATE (manager)->devices;
}
-const char **
-nm_manager_get_device_paths (NMManager *self)
-{
- const GSList *devices, *iter;
- GPtrArray *paths;
- const char *path;
-
- g_return_val_if_fail (NM_IS_MANAGER (self), NULL);
- devices = NM_MANAGER_GET_PRIVATE (self)->devices;
- paths = g_ptr_array_new ();
-
- for (iter = devices; iter; iter = g_slist_next (iter)) {
- path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (iter->data));
- if (path)
- g_ptr_array_add (paths, (gpointer) path);
- }
-
- g_ptr_array_add (paths, NULL);
-
- return (const char **) g_ptr_array_free (paths, FALSE);
-}
-
static NMDevice *
nm_manager_get_best_device_for_connection (NMManager *self,
NMConnection *connection,
@@ -5731,7 +5710,7 @@ _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);
+ priv->checkpoint_mgr = nm_checkpoint_manager_new (self, obj_properties[PROP_CHECKPOINTS]);
return priv->checkpoint_mgr;
}
@@ -6273,6 +6252,7 @@ get_property (GObject *object, guint prop_id,
const NMGlobalDnsConfig *dns_config;
const char *type;
NMConnectivity *connectivity;
+ char **strv;
switch (prop_id) {
case PROP_VERSION:
@@ -6359,6 +6339,12 @@ get_property (GObject *object, guint prop_id,
case PROP_ALL_DEVICES:
nm_utils_g_value_set_object_path_array (value, priv->devices, NULL, NULL);
break;
+ case PROP_CHECKPOINTS:
+ strv = NULL;
+ if (priv->checkpoint_mgr)
+ strv = nm_checkpoint_manager_get_checkpoint_paths (priv->checkpoint_mgr);
+ g_value_take_boxed (value, strv);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -6713,6 +6699,12 @@ nm_manager_class_init (NMManagerClass *manager_class)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_CHECKPOINTS] =
+ g_param_spec_boxed (NM_MANAGER_CHECKPOINTS, "", "",
+ G_TYPE_STRV,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
/* signals */
diff --git a/src/nm-manager.h b/src/nm-manager.h
index 622edb5b54..b4ec7b2688 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -54,6 +54,7 @@
#define NM_MANAGER_METERED "metered"
#define NM_MANAGER_GLOBAL_DNS_CONFIGURATION "global-dns-configuration"
#define NM_MANAGER_ALL_DEVICES "all-devices"
+#define NM_MANAGER_CHECKPOINTS "checkpoints"
/* Not exported */
#define NM_MANAGER_SLEEPING "sleeping"
@@ -95,7 +96,6 @@ void nm_manager_write_device_state (NMManager *manager);
/* Device handling */
const GSList * nm_manager_get_devices (NMManager *manager);
-const char ** nm_manager_get_device_paths (NMManager *self);
NMDevice * nm_manager_get_device_by_ifindex (NMManager *manager,
int ifindex);