diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2017-11-09 10:14:21 +0100 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2017-11-09 10:14:21 +0100 |
commit | f02df2c2ca0a57696840e80c647b4d8176ea449b (patch) | |
tree | 02ad6d1245f6a3ed585ec7081f0c91fd00a64de9 | |
parent | a5b4850682d6eb34be65583d86ebb989f65a89cc (diff) | |
parent | 77d3b1555e83116d849c0ad9bca5ae8322e6c936 (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.am | 2 | ||||
-rw-r--r-- | examples/python/gi/checkpoint.py | 130 | ||||
-rw-r--r-- | introspection/org.freedesktop.NetworkManager.xml | 7 | ||||
-rw-r--r-- | libnm-core/nm-dbus-interface.h | 1 | ||||
-rw-r--r-- | libnm/libnm.ver | 15 | ||||
-rw-r--r-- | libnm/nm-checkpoint.c | 219 | ||||
-rw-r--r-- | libnm/nm-checkpoint.h | 53 | ||||
-rw-r--r-- | libnm/nm-client.c | 312 | ||||
-rw-r--r-- | libnm/nm-client.h | 39 | ||||
-rw-r--r-- | libnm/nm-manager.c | 335 | ||||
-rw-r--r-- | libnm/nm-manager.h | 31 | ||||
-rw-r--r-- | libnm/nm-object.c | 2 | ||||
-rw-r--r-- | libnm/nm-types.h | 1 | ||||
-rw-r--r-- | src/nm-checkpoint-manager.c | 138 | ||||
-rw-r--r-- | src/nm-checkpoint-manager.h | 4 | ||||
-rw-r--r-- | src/nm-manager.c | 38 | ||||
-rw-r--r-- | src/nm-manager.h | 2 |
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 ∅ + + 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); |