checkpoint: add libnm support

Add support for checkpoint/rollback functionality to libnm.

https://bugzilla.redhat.com/show_bug.cgi?id=1496739
This commit is contained in:
Beniamino Galvani 2017-11-09 10:14:21 +01:00
commit f02df2c2ca
17 changed files with 1270 additions and 59 deletions

View file

@ -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 \

View file

@ -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()

View file

@ -269,6 +269,13 @@
-->
<property name="AllDevices" type="ao" access="read"/>
<!--
Checkpoints:
The list of active checkpoints.
-->
<property name="Checkpoints" type="ao" access="read"/>
<!--
NetworkingEnabled:

View file

@ -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"

View file

@ -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;

219
libnm/nm-checkpoint.c Normal file
View file

@ -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));
}

53
libnm/nm-checkpoint.h Normal file
View file

@ -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__ */

View file

@ -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 */
/**

View file

@ -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__ */

View file

@ -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),

View file

@ -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__ */

View file

@ -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);
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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 */

View file

@ -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);