checkpoint: merge branch 'th/checkpoint'

https://github.com/NetworkManager/NetworkManager/pull/83
This commit is contained in:
Thomas Haller 2018-04-04 14:02:31 +02:00
commit aefb66d547
23 changed files with 1021 additions and 519 deletions

View file

@ -1466,10 +1466,10 @@ src_libNetworkManager_la_CPPFLAGS = $(src_cppflags)
src_libNetworkManager_la_SOURCES = \
\
src/nm-checkpoint-manager.c \
src/nm-checkpoint-manager.h \
src/nm-checkpoint.c \
src/nm-checkpoint.h \
src/nm-checkpoint-manager.c \
src/nm-checkpoint-manager.h \
\
src/devices/nm-device.c \
src/devices/nm-device.h \

View file

@ -150,6 +150,8 @@ EXTRA_DIST += \
examples/nm-conf.d/30-anon.conf \
examples/nm-conf.d/31-mac-addr-change.conf \
\
examples/python/nmex.py \
\
examples/python/dbus/nm-state.py \
examples/python/dbus/add-connection.py \
examples/python/dbus/add-connection-compat.py \

193
examples/python/gi/checkpoint.py Normal file → Executable file
View file

@ -18,98 +18,153 @@ import gi
gi.require_version('NM', '1.0')
from gi.repository import GLib, NM
import os
os.sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import nmex
###############################################################################
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
print("Usage: %s [COMMAND [ARG]...]" % sys.argv[0])
print("")
print(" COMMANDS: [show]")
print(" create TIMEOUT [--destroy-all|--delete-new-connections|--disconnect-new-devices|--allow-overlapping|DEV]...")
print(" destroy PATH|NUMBER")
print(" rollback PATH|NUMBER")
print(" adjust-rollback-timeout PATH|NUMBER TIMEOUT")
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
def show(c, ts = None):
cr = c.get_created()
rt = c.get_rollback_timeout()
print("%s:" % c.get_path())
print(" created: %u%s" % (cr, "" if ts is None else (" (%s sec ago)" % ((ts - cr) / 1000.0))))
if rt == 0:
print(" timeout: infinity")
else:
print(" timeout: %u seconds%s" % (rt, "" if ts is None else (" (circa %s sec left)" % ((cr + (rt * 1000) - ts) / 1000.0))))
print(" devices: %s" % (' '.join(sorted(map(lambda x: x.get_iface(), c.get_devices())))))
def find_checkpoint(client, path):
for c in client.get_checkpoints():
if c.get_path() == path:
return c
return None
def validate_path(path, client):
try:
num = int(path)
path = "/org/freedesktop/NetworkManager/Checkpoint/%u" % (num)
except Exception as e:
pass
if not path or path[0] != '/':
sys.exit('Invalid checkpoint path \"%s\"' % (path))
if client is not None:
checkpoint = find_checkpoint(client, path)
if checkpoint is None:
print('WARNING: no checkpoint with path "%s" found' % (path))
return path
def do_create(client):
flags = NM.CheckpointCreateFlags.NONE
if len(sys.argv) < 3:
sys.exit("Failed: missing argument timeout")
timeout = int(sys.argv[2])
devices = []
for arg in sys.argv[3:]:
if arg == '--destroy-all':
flags |= NM.CheckpointCreateFlags.DESTROY_ALL
elif arg == '--delete-new-connections':
flags |= NM.CheckpointCreateFlags.DELETE_NEW_CONNECTIONS
elif arg == '--disconnect-new-devices':
flags |= NM.CheckpointCreateFlags.DISCONNECT_NEW_DEVICES
elif arg == '--allow-overlapping':
flags |= NM.CheckpointCreateFlags.ALLOW_OVERLAPPING
else:
d = client.get_device_by_iface(arg)
if d is None:
sys.exit("Unknown device %s" % arg)
devices.append(d)
def create_cb(client, result, data):
try:
checkpoint = client.checkpoint_create_finish(result)
print("%s" % checkpoint.get_path())
except Exception as e:
sys.stderr.write("Failed: %s\n" % e.message)
main_loop.quit()
client.checkpoint_create(devices, timeout, flags, None, create_cb, 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])
path = validate_path(sys.argv[2], client)
client.checkpoint_destroy_async(checkpoint, None, destroy_cb, None)
def destroy_cb(client, result, data):
try:
if client.checkpoint_destroy_finish(result) == True:
print("Success")
except Exception as e:
sys.stderr.write("Failed: %s\n" % e.message)
main_loop.quit()
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()
client.checkpoint_destroy(path, None, destroy_cb, None)
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])
path = validate_path(sys.argv[2], client)
client.checkpoint_rollback_async(checkpoint, None, rollback_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 as e:
sys.stderr.write("Failed: %s\n" % e.message)
main_loop.quit()
client.checkpoint_rollback(path, None, rollback_cb, None)
def do_adjust_rollback_timeout(client):
if len(sys.argv) < 3:
sys.exit("Missing checkpoint path")
if len(sys.argv) < 4:
sys.exit("Missing timeout")
try:
add_timeout = int(sys.argv[3])
except:
sys.exit("Invalid timeout")
path = validate_path(sys.argv[2], client)
def adjust_rollback_timeout_cb(client, result, data):
try:
client.checkpoint_adjust_rollback_timeout_finish(result)
print("Success")
except Exception as e:
sys.stderr.write("Failed: %s\n" % e.message)
main_loop.quit()
client.checkpoint_adjust_rollback_timeout(path, add_timeout, None, adjust_rollback_timeout_cb, None)
def do_show(client):
ts = nmex.nm_boot_time_ms()
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())))
show(c, ts)
if __name__ == '__main__':
nm_client = NM.Client.new(None)
@ -124,6 +179,8 @@ if __name__ == '__main__':
do_destroy(nm_client)
elif sys.argv[1] == 'rollback':
do_rollback(nm_client)
elif sys.argv[1] == 'adjust-rollback-timeout':
do_adjust_rollback_timeout(nm_client)
else:
usage()

72
examples/python/nmex.py Normal file
View file

@ -0,0 +1,72 @@
#!/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 2018 Red Hat, Inc.
###############################################################################
# nmex.py contains helper functions used by some examples. The helper functions
# should be simple and independent, so that the user can extract them easily
# when modifying the example to his needs.
###############################################################################
def _sys_clock_gettime_ns_lazy():
import ctypes
class timespec(ctypes.Structure):
_fields_ = [
('tv_sec', ctypes.c_long),
('tv_nsec', ctypes.c_long)
]
librt = ctypes.CDLL('librt.so.1', use_errno=True)
clock_gettime = librt.clock_gettime
clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]
t = timespec()
def f(clock_id):
if clock_gettime(clock_id, ctypes.pointer(t)) != 0:
import os
errno_ = ctypes.get_errno()
raise OSError(errno_, os.strerror(errno_))
return (t.tv_sec * 1000000000) + t.tv_nsec
return f
_sys_clock_gettime_ns = None
# call POSIX clock_gettime() and return it as integer (in nanoseconds)
def sys_clock_gettime_ns(clock_id):
global _sys_clock_gettime_ns
if _sys_clock_gettime_ns is None:
_sys_clock_gettime_ns = _sys_clock_gettime_ns_lazy()
return _sys_clock_gettime_ns(clock_id)
def nm_boot_time_ns():
# NetworkManager exposes some timestamps as CLOCK_BOOTTIME.
# Try that first (number 7).
try:
return sys_clock_gettime_ns(7)
except OSError as e:
# On systems, where this is not available, fallback to
# CLOCK_MONOTONIC (numeric 1).
# That is what NetworkManager does as well.
import errno
if e.errno == errno.EINVAL:
return sys_clock_gettime_ns(1)
raise
def nm_boot_time_us():
return nm_boot_time_ns() / 1000
def nm_boot_time_ms():
return nm_boot_time_ns() / 1000000
def nm_boot_time_s():
return nm_boot_time_ns() / 1000000000
###############################################################################

View file

@ -251,6 +251,27 @@
<arg name="result" type="a{su}" direction="out" />
</method>
<!--
CheckpointAdjustRollbackTimeout:
@add_timeout: number of seconds from ~now~ in which the
timeout will expire. Set to 0 to disable the timeout.
Note that the added seconds start counting from now,
not "Created" timestamp or the previous expiration
time. Note that the "Created" property of the checkpoint
will stay unchanged by this call. However, the "RollbackTimeout"
will be recalculated to give the approximate new expiration time.
The new "RollbackTimeout" property will be approximate up to
one second precision, which is the accuracy of the property.
Reset the timeout for rollback for the checkpoint.
Since: 1.12
-->
<method name="CheckpointAdjustRollbackTimeout">
<arg name="checkpoint" type="o" direction="in"/>
<arg name="add_timeout" type="u" direction="in"/>
</method>
<!--
Devices:

View file

@ -842,16 +842,29 @@ typedef enum {
* delete any new connection added after the checkpoint (Since: 1.6)
* @NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES: upon rollback,
* disconnect any new device appeared after the checkpoint (Since: 1.6)
* @NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING: by default, creating
* a checkpoint fails if there are already existing checkoints that
* reference the same devices. With this flag, creation of such
* checkpoints is allowed, however, if an older checkpoint
* that references overlapping devices gets rolled back, it will
* automatically destroy this checkpoint during rollback. This
* allows to create several overlapping checkpoints in parallel,
* and rollback to them at will. With the special case that
* rolling back to an older checkpoint will invalidate all
* overlapping younger checkpoints. This opts-in that the
* checkpoint can be automatically destroyed by the rollback
* of an older checkpoint. (Since: 1.12)
*
* The flags for CheckpointCreate call
*
* Since: 1.4
* Since: 1.4 (gi flags generated since 1.12)
*/
typedef enum { /*< skip >*/
typedef enum { /*< flags >*/
NM_CHECKPOINT_CREATE_FLAG_NONE = 0,
NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL = 0x01,
NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS = 0x02,
NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES = 0x04,
NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING = 0x08,
} NMCheckpointCreateFlags;
/**

View file

@ -1334,15 +1334,18 @@ global:
libnm_1_12_0 {
global:
nm_checkpoint_create_flags_get_type;
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_adjust_rollback_timeout;
nm_client_checkpoint_adjust_rollback_timeout_finish;
nm_client_checkpoint_create;
nm_client_checkpoint_create_finish;
nm_client_checkpoint_destroy_async;
nm_client_checkpoint_destroy;
nm_client_checkpoint_destroy_finish;
nm_client_checkpoint_rollback_async;
nm_client_checkpoint_rollback;
nm_client_checkpoint_rollback_finish;
nm_client_get_checkpoints;
nm_device_ip_tunnel_get_flags;

View file

@ -2121,7 +2121,7 @@ nm_client_get_checkpoints (NMClient *client)
}
/**
* nm_client_checkpoint_create_async:
* nm_client_checkpoint_create:
* @client: the %NMClient
* @devices: (element-type NMDevice): a list of devices for which a
* checkpoint should be created.
@ -2139,13 +2139,13 @@ nm_client_get_checkpoints (NMClient *client)
* 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)
nm_client_checkpoint_create (NMClient *client,
const GPtrArray *devices,
guint32 rollback_timeout,
NMCheckpointCreateFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
GError *error = NULL;
@ -2158,12 +2158,12 @@ nm_client_checkpoint_create_async (NMClient *client,
}
simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data,
nm_client_checkpoint_create_async);
nm_client_checkpoint_create);
if (cancellable)
g_simple_async_result_set_check_cancellable (simple, cancellable);
nm_manager_checkpoint_create_async (NM_CLIENT_GET_PRIVATE (client)->manager,
devices, rollback_timeout, flags,
cancellable, checkpoint_create_cb, simple);
nm_manager_checkpoint_create (NM_CLIENT_GET_PRIVATE (client)->manager,
devices, rollback_timeout, flags,
cancellable, checkpoint_create_cb, simple);
}
/**
@ -2172,7 +2172,7 @@ nm_client_checkpoint_create_async (NMClient *client,
* @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().
* Gets the result of a call to nm_client_checkpoint_create().
*
* Returns: (transfer full): the new #NMCheckpoint on success, %NULL on
* failure, in which case @error will be set.
@ -2214,9 +2214,9 @@ checkpoint_destroy_cb (GObject *object,
}
/**
* nm_client_checkpoint_destroy_async:
* nm_client_checkpoint_destroy:
* @client: the %NMClient
* @checkpoint: a checkpoint
* @checkpoint_path: the D-Bus path for the 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
@ -2226,16 +2226,17 @@ checkpoint_destroy_cb (GObject *object,
* Since: 1.12
**/
void
nm_client_checkpoint_destroy_async (NMClient *client,
NMCheckpoint *checkpoint,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
nm_client_checkpoint_destroy (NMClient *client,
const char *checkpoint_path,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
GError *error = NULL;
g_return_if_fail (NM_IS_CLIENT (client));
g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/');
if (!_nm_client_check_nm_running (client, &error)) {
g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error);
@ -2243,12 +2244,12 @@ nm_client_checkpoint_destroy_async (NMClient *client,
}
simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data,
nm_client_checkpoint_destroy_async);
nm_client_checkpoint_destroy);
if (cancellable)
g_simple_async_result_set_check_cancellable (simple, cancellable);
nm_manager_checkpoint_destroy_async (NM_CLIENT_GET_PRIVATE (client)->manager,
checkpoint,
cancellable, checkpoint_destroy_cb, simple);
nm_manager_checkpoint_destroy (NM_CLIENT_GET_PRIVATE (client)->manager,
checkpoint_path,
cancellable, checkpoint_destroy_cb, simple);
}
/**
@ -2257,7 +2258,7 @@ nm_client_checkpoint_destroy_async (NMClient *client,
* @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().
* Gets the result of a call to nm_client_checkpoint_destroy().
*
* Returns: %TRUE on success or %FALSE on failure, in which case
* @error will be set.
@ -2301,9 +2302,9 @@ checkpoint_rollback_cb (GObject *object,
}
/**
* nm_client_checkpoint_rollback_async:
* nm_client_checkpoint_rollback:
* @client: the %NMClient
* @checkpoint: a checkpoint
* @checkpoint_path: the D-Bus path to the 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
@ -2313,16 +2314,17 @@ checkpoint_rollback_cb (GObject *object,
* Since: 1.12
**/
void
nm_client_checkpoint_rollback_async (NMClient *client,
NMCheckpoint *checkpoint,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
nm_client_checkpoint_rollback (NMClient *client,
const char *checkpoint_path,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
GError *error = NULL;
g_return_if_fail (NM_IS_CLIENT (client));
g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/');
if (!_nm_client_check_nm_running (client, &error)) {
g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error);
@ -2330,12 +2332,12 @@ nm_client_checkpoint_rollback_async (NMClient *client,
}
simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data,
nm_client_checkpoint_rollback_async);
nm_client_checkpoint_rollback);
if (cancellable)
g_simple_async_result_set_check_cancellable (simple, cancellable);
nm_manager_checkpoint_rollback_async (NM_CLIENT_GET_PRIVATE (client)->manager,
checkpoint,
cancellable, checkpoint_rollback_cb, simple);
nm_manager_checkpoint_rollback (NM_CLIENT_GET_PRIVATE (client)->manager,
checkpoint_path,
cancellable, checkpoint_rollback_cb, simple);
}
/**
@ -2344,7 +2346,7 @@ nm_client_checkpoint_rollback_async (NMClient *client,
* @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().
* Gets the result of a call to nm_client_checkpoint_rollback().
*
* Returns: (transfer full) (element-type utf8 guint32): an hash table of
* devices and results. Devices are represented by their original
@ -2372,6 +2374,89 @@ nm_client_checkpoint_rollback_finish (NMClient *client,
}
}
static void
checkpoint_adjust_rollback_timeout_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
gs_unref_object GSimpleAsyncResult *simple = user_data;
GError *error = NULL;
if (nm_manager_checkpoint_adjust_rollback_timeout_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);
}
/**
* nm_client_checkpoint_adjust_rollback_timeout:
* @client: the %NMClient
* @checkpoint_path: a D-Bus path to a checkpoint
* @add_timeout: the timeout in seconds counting from now.
* Set to zero, to disable the timeout.
* @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
*
* Resets the timeout for the checkpoint with path @checkpoint_path
* to @timeout_add.
*
* Since: 1.12
**/
void
nm_client_checkpoint_adjust_rollback_timeout (NMClient *client,
const char *checkpoint_path,
guint32 add_timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
GError *error = NULL;
g_return_if_fail (NM_IS_CLIENT (client));
g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/');
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);
if (cancellable)
g_simple_async_result_set_check_cancellable (simple, cancellable);
nm_manager_checkpoint_adjust_rollback_timeout (NM_CLIENT_GET_PRIVATE (client)->manager,
checkpoint_path, add_timeout,
cancellable, checkpoint_adjust_rollback_timeout_cb, simple);
}
/**
* nm_client_checkpoint_adjust_rollback_timeout_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_adjust_rollback_timeout().
*
* Returns: %TRUE on success or %FALSE on failure.
*
* Since: 1.12
**/
gboolean
nm_client_checkpoint_adjust_rollback_timeout_finish (NMClient *client,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
error);
}
/****************************************************************/
/* Object Initialization */
/****************************************************************/

View file

@ -408,40 +408,53 @@ 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);
void nm_client_checkpoint_create (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);
void nm_client_checkpoint_destroy (NMClient *client,
const char *checkpoint_path,
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);
void nm_client_checkpoint_rollback (NMClient *client,
const char *checkpoint_path,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
NM_AVAILABLE_IN_1_12
GHashTable *nm_client_checkpoint_rollback_finish (NMClient *client,
GAsyncResult *result,
GError **error);
NM_AVAILABLE_IN_1_12
void nm_client_checkpoint_adjust_rollback_timeout (NMClient *client,
const char *checkpoint_path,
guint32 add_timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
NM_AVAILABLE_IN_1_12
gboolean nm_client_checkpoint_adjust_rollback_timeout_finish (NMClient *client,
GAsyncResult *result,
GError **error);
G_END_DECLS
#endif /* __NM_CLIENT_H__ */

View file

@ -150,7 +150,7 @@ find_checkpoint_info (NMManager *manager, const char *path)
for (iter = priv->added_checkpoints; iter; iter = g_slist_next (iter)) {
info = iter->data;
if (nm_streq (path, info->path))
if (nm_streq0 (path, info->path))
return info;
}
@ -790,8 +790,7 @@ NMDevice *
nm_manager_get_device_by_path (NMManager *manager, const char *object_path)
{
const GPtrArray *devices;
int i;
NMDevice *device = NULL;
guint i;
g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
g_return_val_if_fail (object_path, NULL);
@ -800,12 +799,25 @@ nm_manager_get_device_by_path (NMManager *manager, const char *object_path)
for (i = 0; i < devices->len; i++) {
NMDevice *candidate = g_ptr_array_index (devices, i);
if (!strcmp (nm_object_get_path (NM_OBJECT (candidate)), object_path)) {
device = candidate;
break;
return candidate;
}
}
return NULL;
}
return device;
static NMCheckpoint *
get_checkpoint_by_path (NMManager *manager, const char *object_path)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
NMCheckpoint *candidate;
guint i;
for (i = 0; i < priv->checkpoints->len; i++) {
candidate = priv->checkpoints->pdata[i];
if (nm_streq (nm_object_get_path (NM_OBJECT (candidate)), object_path))
return candidate;
}
return NULL;
}
NMDevice *
@ -1196,11 +1208,6 @@ checkpoint_added (NMManager *manager, NMCheckpoint *checkpoint)
checkpoint_info_complete (manager, info, checkpoint, NULL);
}
static void
checkpoint_removed (NMManager *manager, NMCheckpoint *checkpoint)
{
}
gboolean
nm_manager_deactivate_connection (NMManager *manager,
NMActiveConnection *active,
@ -1331,25 +1338,37 @@ checkpoint_created_cb (GObject *object,
gpointer user_data)
{
CheckpointInfo *info = user_data;
GError *error = NULL;
NMManager *self = info->manager;
gs_free_error GError *error = NULL;
NMCheckpoint *checkpoint;
nmdbus_manager_call_checkpoint_create_finish (NMDBUS_MANAGER (object),
&info->path, result, &error);
if (error) {
g_dbus_error_strip_remote_error (error);
checkpoint_info_complete (info->manager, info, NULL, error);
g_clear_error (&error);
checkpoint_info_complete (self, info, NULL, error);
return;
}
checkpoint = get_checkpoint_by_path (self, info->path);
if (!checkpoint) {
/* this is really problematic. The async request returned, but
* we don't yet have a visible (fully initialized) NMCheckpoint instance
* to return. Wait longer for it to appear. However, it's ugly. */
return;
}
checkpoint_info_complete (self, info, checkpoint, NULL);
}
void
nm_manager_checkpoint_create_async (NMManager *manager,
const GPtrArray *devices,
guint32 rollback_timeout,
NMCheckpointCreateFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
nm_manager_checkpoint_create (NMManager *manager,
const GPtrArray *devices,
guint32 rollback_timeout,
NMCheckpointCreateFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
gs_free const char **paths = NULL;
@ -1360,7 +1379,7 @@ nm_manager_checkpoint_create_async (NMManager *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);
nm_manager_checkpoint_create);
if (cancellable)
g_simple_async_result_set_check_cancellable (info->simple, cancellable);
paths = get_device_paths (devices);
@ -1411,26 +1430,24 @@ checkpoint_destroy_cb (GObject *object,
}
void
nm_manager_checkpoint_destroy_async (NMManager *manager,
NMCheckpoint *checkpoint,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
nm_manager_checkpoint_destroy (NMManager *manager,
const char *checkpoint_path,
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));
g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/');
simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
nm_manager_checkpoint_destroy_async);
nm_manager_checkpoint_destroy);
if (cancellable)
g_simple_async_result_set_check_cancellable (simple, cancellable);
path = nm_object_get_path (NM_OBJECT (checkpoint));
nmdbus_manager_call_checkpoint_destroy (NM_MANAGER_GET_PRIVATE (manager)->proxy,
path,
checkpoint_path,
cancellable,
checkpoint_destroy_cb, simple);
}
@ -1443,7 +1460,7 @@ nm_manager_checkpoint_destroy_finish (NMManager *manager,
GSimpleAsyncResult *simple;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (manager),
nm_manager_checkpoint_destroy_async),
nm_manager_checkpoint_destroy),
FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
@ -1484,26 +1501,24 @@ checkpoint_rollback_cb (GObject *object,
}
void
nm_manager_checkpoint_rollback_async (NMManager *manager,
NMCheckpoint *checkpoint,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
nm_manager_checkpoint_rollback (NMManager *manager,
const char *checkpoint_path,
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));
g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/');
simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
nm_manager_checkpoint_rollback_async);
nm_manager_checkpoint_rollback);
if (cancellable)
g_simple_async_result_set_check_cancellable (simple, cancellable);
path = nm_object_get_path (NM_OBJECT (checkpoint));
nmdbus_manager_call_checkpoint_rollback (NM_MANAGER_GET_PRIVATE (manager)->proxy,
path,
checkpoint_path,
cancellable,
checkpoint_rollback_cb, simple);
}
@ -1516,7 +1531,7 @@ nm_manager_checkpoint_rollback_finish (NMManager *manager,
GSimpleAsyncResult *simple;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (manager),
nm_manager_checkpoint_rollback_async),
nm_manager_checkpoint_rollback),
NULL);
simple = G_SIMPLE_ASYNC_RESULT (result);
@ -1526,6 +1541,66 @@ nm_manager_checkpoint_rollback_finish (NMManager *manager,
return g_simple_async_result_get_op_res_gpointer (simple);
}
static void
checkpoint_adjust_rollback_timeout_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
gs_unref_object GSimpleAsyncResult *simple = user_data;
GError *error = NULL;
if (nmdbus_manager_call_checkpoint_adjust_rollback_timeout_finish (NMDBUS_MANAGER (object),
result,
&error))
g_simple_async_result_set_op_res_gboolean (simple, TRUE);
else {
g_dbus_error_strip_remote_error (error);
g_simple_async_result_take_error (simple, error);
}
g_simple_async_result_complete (simple);
}
void
nm_manager_checkpoint_adjust_rollback_timeout (NMManager *manager,
const char *checkpoint_path,
guint32 add_timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
g_return_if_fail (NM_IS_MANAGER (manager));
g_return_if_fail (checkpoint_path && checkpoint_path[0] == '/');
simple = g_simple_async_result_new (G_OBJECT (manager), callback, user_data,
nm_manager_checkpoint_adjust_rollback_timeout);
if (cancellable)
g_simple_async_result_set_check_cancellable (simple, cancellable);
nmdbus_manager_call_checkpoint_adjust_rollback_timeout (NM_MANAGER_GET_PRIVATE (manager)->proxy,
checkpoint_path,
add_timeout,
cancellable,
checkpoint_adjust_rollback_timeout_cb,
simple);
}
gboolean
nm_manager_checkpoint_adjust_rollback_timeout_finish (NMManager *manager,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (manager),
nm_manager_checkpoint_adjust_rollback_timeout),
FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
return !g_simple_async_result_propagate_error (simple, error);
}
/*****************************************************************************/
static void
@ -1832,7 +1907,6 @@ nm_manager_class_init (NMManagerClass *manager_class)
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 */

View file

@ -184,31 +184,40 @@ gboolean nm_manager_deactivate_connection_finish (NMManager *manager,
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);
void nm_manager_checkpoint_create (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);
void nm_manager_checkpoint_destroy (NMManager *manager,
const char *checkpoint_path,
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);
void nm_manager_checkpoint_rollback (NMManager *manager,
const char *checkpoint_path,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GHashTable *nm_manager_checkpoint_rollback_finish (NMManager *manager,
GAsyncResult *result,
GError **error);
void nm_manager_checkpoint_adjust_rollback_timeout (NMManager *manager,
const char *checkpoint_path,
guint32 add_timeout,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean nm_manager_checkpoint_adjust_rollback_timeout_finish (NMManager *manager,
GAsyncResult *result,
GError **error);
#endif /* __NM_MANAGER_H__ */

View file

@ -387,7 +387,7 @@ static void
find_companion (NMDeviceOlpcMesh *self)
{
NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
const CList *all_devices;
const CList *tmp_lst;
NMDevice *candidate;
if (priv->companion)
@ -396,8 +396,7 @@ find_companion (NMDeviceOlpcMesh *self)
nm_device_add_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_COMPANION, TRUE);
/* Try to find the companion if it's already known to the NMManager */
all_devices = nm_manager_get_devices (priv->manager);
c_list_for_each_entry (candidate, all_devices, devices_lst) {
nm_manager_for_each_device (priv->manager, candidate, tmp_lst) {
if (check_companion (self, candidate)) {
nm_device_queue_recheck_available (NM_DEVICE (self),
NM_DEVICE_STATE_REASON_NONE,

View file

@ -39,7 +39,7 @@ typedef struct {
} KnownNetworkData;
typedef struct {
NMManager *nm_manager;
NMManager *manager;
GCancellable *cancellable;
gboolean running;
GDBusObjectManager *object_manager;
@ -138,7 +138,7 @@ psk_agent_dbus_method_cb (GDBusConnection *connection,
goto return_error;
}
device = nm_manager_get_device_by_ifindex (priv->nm_manager, ifindex);
device = nm_manager_get_device_by_ifindex (priv->manager, ifindex);
if (!NM_IS_DEVICE_IWD (device)) {
_LOGE ("IWD device named %s is not a Wifi device in IWD Agent request",
ifname);
@ -291,7 +291,7 @@ set_device_dbus_object (NMIwdManager *self, GDBusInterface *interface,
return;
}
device = nm_manager_get_device_by_ifindex (priv->nm_manager, ifindex);
device = nm_manager_get_device_by_ifindex (priv->manager, ifindex);
if (!NM_IS_DEVICE_IWD (device)) {
_LOGE ("IWD device named %s is not a Wifi device", ifname);
return;
@ -460,7 +460,7 @@ name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
g_clear_object (&priv->object_manager);
prepare_object_manager (self);
} else {
const CList *all_devices;
const CList *tmp_lst;
NMDevice *device;
if (!priv->running)
@ -468,13 +468,11 @@ name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
priv->running = false;
all_devices = nm_manager_get_devices (priv->nm_manager);
c_list_for_each_entry (device, all_devices, devices_lst) {
if (!NM_IS_DEVICE_IWD (device))
continue;
nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device),
NULL);
nm_manager_for_each_device (priv->manager, device, tmp_lst) {
if (NM_IS_DEVICE_IWD (device)) {
nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device),
NULL);
}
}
}
}
@ -639,8 +637,8 @@ nm_iwd_manager_init (NMIwdManager *self)
{
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
priv->nm_manager = g_object_ref (nm_manager_get ());
g_signal_connect (priv->nm_manager, NM_MANAGER_DEVICE_ADDED,
priv->manager = g_object_ref (nm_manager_get ());
g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED,
G_CALLBACK (device_added), self);
priv->cancellable = g_cancellable_new ();
@ -679,9 +677,9 @@ dispose (GObject *object)
g_slist_free_full (priv->known_networks, (GDestroyNotify) known_network_free);
priv->known_networks = NULL;
if (priv->nm_manager) {
g_signal_handlers_disconnect_by_data (priv->nm_manager, self);
g_clear_object (&priv->nm_manager);
if (priv->manager) {
g_signal_handlers_disconnect_by_data (priv->manager, self);
g_clear_object (&priv->manager);
}
G_OBJECT_CLASS (nm_iwd_manager_parent_class)->dispose (object);

View file

@ -57,6 +57,7 @@ typedef struct _NMAuditManagerClass NMAuditManagerClass;
#define NM_AUDIT_OP_CHECKPOINT_CREATE "checkpoint-create"
#define NM_AUDIT_OP_CHECKPOINT_ROLLBACK "checkpoint-rollback"
#define NM_AUDIT_OP_CHECKPOINT_DESTROY "checkpoint-destroy"
#define NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT "checkpoint-adjust-rollback-timeout"
GType nm_audit_manager_get_type (void);
NMAuditManager *nm_audit_manager_get (void);

View file

@ -35,9 +35,7 @@
struct _NMCheckpointManager {
NMManager *_manager;
GParamSpec *property_spec;
GHashTable *checkpoints;
CList list;
guint rollback_timeout_id;
CList checkpoints_lst_head;
};
#define GET_MANAGER(self) \
@ -58,13 +56,6 @@ struct _NMCheckpointManager {
/*****************************************************************************/
typedef struct {
CList list;
NMCheckpoint *checkpoint;
} CheckpointItem;
static void update_rollback_timeout (NMCheckpointManager *self);
static void
notify_checkpoints (NMCheckpointManager *self) {
g_object_notify_by_pspec ((GObject *) GET_MANAGER (self),
@ -72,83 +63,60 @@ notify_checkpoints (NMCheckpointManager *self) {
}
static void
item_destroy (gpointer data)
destroy_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint, gboolean log_destroy)
{
CheckpointItem *item = data;
nm_assert (NM_IS_CHECKPOINT (checkpoint));
nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (checkpoint)));
nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
c_list_unlink_stale (&item->list);
nm_dbus_object_unexport (NM_DBUS_OBJECT (item->checkpoint));
g_object_unref (G_OBJECT (item->checkpoint));
g_slice_free (CheckpointItem, item);
nm_checkpoint_set_timeout_callback (checkpoint, NULL, NULL);
c_list_unlink (&checkpoint->checkpoints_lst);
if (log_destroy)
nm_checkpoint_log_destroy (checkpoint);
notify_checkpoints (self);
nm_dbus_object_unexport (NM_DBUS_OBJECT (checkpoint));
g_object_unref (checkpoint);
}
static gboolean
rollback_timeout_cb (NMCheckpointManager *self)
static GVariant *
rollback_checkpoint (NMCheckpointManager *self, NMCheckpoint *checkpoint)
{
CheckpointItem *item, *safe;
GVariant *result;
gint64 ts, now;
const char *path;
gboolean removed = FALSE;
const CList *iter;
now = nm_utils_get_monotonic_timestamp_ms ();
nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
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 (item->checkpoint);
if (result)
g_variant_unref (result);
path = nm_dbus_object_get_path (NM_DBUS_OBJECT (item->checkpoint));
if (!g_hash_table_remove (self->checkpoints, path))
nm_assert_not_reached();
removed = TRUE;
/* we destroy first all overlapping checkpoints that are younger/newer. */
for (iter = checkpoint->checkpoints_lst.next;
iter != &self->checkpoints_lst_head;
) {
NMCheckpoint *cp = c_list_entry (iter, NMCheckpoint, checkpoints_lst);
iter = iter->next;
if (nm_checkpoint_includes_devices_of (cp, checkpoint)) {
/* the younger checkpoint has overlapping devices and gets obsoleted.
* Destroy it. */
destroy_checkpoint (self, cp, TRUE);
}
}
self->rollback_timeout_id = 0;
update_rollback_timeout (self);
if (removed)
notify_checkpoints (self);
return G_SOURCE_REMOVE;
result = nm_checkpoint_rollback (checkpoint);
destroy_checkpoint (self, checkpoint, FALSE);
return result;
}
static void
update_rollback_timeout (NMCheckpointManager *self)
rollback_timeout_cb (NMCheckpoint *checkpoint,
gpointer user_data)
{
CheckpointItem *item;
gint64 ts, delta, next = G_MAXINT64;
NMCheckpointManager *self = user_data;
gs_unref_variant GVariant *result = NULL;
c_list_for_each_entry (item, &self->list, list) {
ts = nm_checkpoint_get_rollback_ts (item->checkpoint);
if (ts && ts < next)
next = ts;
}
nm_clear_g_source (&self->rollback_timeout_id);
if (next != G_MAXINT64) {
delta = MAX (next - nm_utils_get_monotonic_timestamp_ms (), 0);
self->rollback_timeout_id = g_timeout_add (delta,
(GSourceFunc) rollback_timeout_cb,
self);
_LOGT ("update timeout: next check in %" G_GINT64_FORMAT " ms", delta);
}
}
static NMCheckpoint *
find_checkpoint_for_device (NMCheckpointManager *self, NMDevice *device)
{
CheckpointItem *item;
c_list_for_each_entry (item, &self->list, list) {
if (nm_checkpoint_includes_device (item->checkpoint, device))
return item->checkpoint;
}
return NULL;
result = rollback_checkpoint (self, checkpoint);
}
NMCheckpoint *
@ -160,57 +128,61 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
{
NMManager *manager;
NMCheckpoint *checkpoint;
CheckpointItem *item;
const char * const *path;
gs_unref_ptrarray GPtrArray *devices = NULL;
NMDevice *device;
const char *checkpoint_path;
gs_free const char **device_paths_free = NULL;
guint i;
g_return_val_if_fail (self, FALSE);
g_return_val_if_fail (!error || !*error, FALSE);
manager = GET_MANAGER (self);
if (!device_paths || !device_paths[0]) {
const char *device_path;
const CList *all_devices;
GPtrArray *paths;
devices = g_ptr_array_new ();
paths = g_ptr_array_new ();
all_devices = nm_manager_get_devices (manager);
c_list_for_each_entry (device, all_devices, devices_lst) {
if (!device_paths || !device_paths[0]) {
const CList *tmp_lst;
nm_manager_for_each_device (manager, device, tmp_lst) {
/* FIXME: there is no strong reason to skip over unrealized devices.
* Also, NMCheckpoint anticipates to handle them (in parts). */
if (!nm_device_is_real (device))
continue;
device_path = nm_dbus_object_get_path (NM_DBUS_OBJECT (device));
if (device_path)
g_ptr_array_add (paths, (gpointer) device_path);
nm_assert (nm_dbus_object_get_path (NM_DBUS_OBJECT (device)));
g_ptr_array_add (devices, device);
}
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,
"the DISCONNECT_NEW_DEVICES flag can only be used with an empty device list");
return NULL;
}
devices = g_ptr_array_new ();
for (path = device_paths; *path; path++) {
device = nm_manager_get_device_by_path (manager, *path);
if (!device) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
"device %s does not exist", *path);
return NULL;
} else {
for (; *device_paths; device_paths++) {
device = nm_manager_get_device_by_path (manager, *device_paths);
if (!device) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
"device %s does not exist", *device_paths);
return NULL;
}
if (!nm_device_is_real (device)) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE,
"device %s is not realized", *device_paths);
return NULL;
}
g_ptr_array_add (devices, device);
}
g_ptr_array_add (devices, device);
}
if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL)) {
for (i = 0; i < devices->len; i++) {
device = devices->pdata[i];
checkpoint = find_checkpoint_for_device (self, device);
if (checkpoint) {
if (!devices->len) {
g_set_error_literal (error,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_INVALID_ARGUMENTS,
"no device available");
return NULL;
}
if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL))
nm_checkpoint_manager_destroy_all (self);
else if (!NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING)) {
c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst) {
device = nm_checkpoint_includes_devices (checkpoint, (NMDevice *const*) devices->pdata, devices->len);
if (device) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
"device '%s' is already included in checkpoint %s",
nm_device_get_iface (device),
@ -220,115 +192,132 @@ nm_checkpoint_manager_create (NMCheckpointManager *self,
}
}
checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags, error);
if (!checkpoint)
return NULL;
checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags);
if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL))
g_hash_table_remove_all (self->checkpoints);
checkpoint_path = nm_dbus_object_export (NM_DBUS_OBJECT (checkpoint));
item = g_slice_new0 (CheckpointItem);
item->checkpoint = checkpoint;
c_list_link_tail (&self->list, &item->list);
if (!g_hash_table_insert (self->checkpoints, (gpointer) checkpoint_path, item))
g_return_val_if_reached (NULL);
nm_dbus_object_export (NM_DBUS_OBJECT (checkpoint));
nm_checkpoint_set_timeout_callback (checkpoint, rollback_timeout_cb, self);
c_list_link_tail (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst);
notify_checkpoints (self);
update_rollback_timeout (self);
return checkpoint;
}
gboolean
nm_checkpoint_manager_destroy_all (NMCheckpointManager *self,
GError **error)
void
nm_checkpoint_manager_destroy_all (NMCheckpointManager *self)
{
g_return_val_if_fail (self, FALSE);
NMCheckpoint *checkpoint;
g_hash_table_remove_all (self->checkpoints);
notify_checkpoints (self);
g_return_if_fail (self);
return TRUE;
while ((checkpoint = c_list_first_entry (&self->checkpoints_lst_head, NMCheckpoint, checkpoints_lst)))
destroy_checkpoint (self, checkpoint, TRUE);
}
gboolean
nm_checkpoint_manager_destroy (NMCheckpointManager *self,
const char *checkpoint_path,
const char *path,
GError **error)
{
gboolean ret;
NMCheckpoint *checkpoint;
g_return_val_if_fail (self, FALSE);
g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE);
g_return_val_if_fail (path && path[0] == '/', FALSE);
g_return_val_if_fail (!error || !*error, FALSE);
if (!nm_streq (checkpoint_path, "/")) {
ret = g_hash_table_remove (self->checkpoints, checkpoint_path);
if (ret) {
notify_checkpoints (self);
} else {
g_set_error (error,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_INVALID_ARGUMENTS,
"checkpoint %s does not exist", checkpoint_path);
}
return ret;
} else
return nm_checkpoint_manager_destroy_all (self, error);
if (!nm_streq (path, "/")) {
nm_checkpoint_manager_destroy_all (self);
return TRUE;
}
checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error);
if (!checkpoint)
return FALSE;
destroy_checkpoint (self, checkpoint, TRUE);
return TRUE;
}
gboolean
nm_checkpoint_manager_rollback (NMCheckpointManager *self,
const char *checkpoint_path,
const char *path,
GVariant **results,
GError **error)
{
CheckpointItem *item;
NMCheckpoint *checkpoint;
g_return_val_if_fail (self, FALSE);
g_return_val_if_fail (checkpoint_path && checkpoint_path[0] == '/', FALSE);
g_return_val_if_fail (path && path[0] == '/', FALSE);
g_return_val_if_fail (results, FALSE);
g_return_val_if_fail (!error || !*error, FALSE);
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);
checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error);
if (!checkpoint)
return FALSE;
}
*results = nm_checkpoint_rollback (item->checkpoint);
g_hash_table_remove (self->checkpoints, checkpoint_path);
notify_checkpoints (self);
*results = rollback_checkpoint (self, checkpoint);
return TRUE;
}
char **
nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self)
NMCheckpoint *
nm_checkpoint_manager_lookup_by_path (NMCheckpointManager *self, const char *path, GError **error)
{
CheckpointItem *item;
char **strv;
guint num, i = 0;
NMCheckpoint *checkpoint;
num = g_hash_table_size (self->checkpoints);
if (!num) {
nm_assert (c_list_is_empty (&self->list));
g_return_val_if_fail (self, NULL);
checkpoint = (NMCheckpoint *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (GET_MANAGER (self))),
path);
if ( !checkpoint
|| !NM_IS_CHECKPOINT (checkpoint)) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
"checkpoint %s does not exist", path);
return NULL;
}
strv = g_new (char *, num + 1);
c_list_for_each_entry (item, &self->list, list)
strv[i++] = g_strdup (nm_dbus_object_get_path (NM_DBUS_OBJECT (item->checkpoint)));
nm_assert (c_list_contains (&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
return checkpoint;
}
const char **
nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self, guint *out_length)
{
NMCheckpoint *checkpoint;
const char **strv;
guint num, i = 0;
num = c_list_length (&self->checkpoints_lst_head);
NM_SET_OUT (out_length, num);
if (!num)
return NULL;
strv = g_new (const char *, num + 1);
c_list_for_each_entry (checkpoint, &self->checkpoints_lst_head, checkpoints_lst)
strv[i++] = nm_dbus_object_get_path (NM_DBUS_OBJECT (checkpoint));
nm_assert (i == num);
strv[i] = NULL;
return strv;
}
gboolean
nm_checkpoint_manager_adjust_rollback_timeout (NMCheckpointManager *self,
const char *path,
guint32 add_timeout,
GError **error)
{
NMCheckpoint *checkpoint;
g_return_val_if_fail (self, FALSE);
g_return_val_if_fail (path && path[0] == '/', FALSE);
g_return_val_if_fail (!error || !*error, FALSE);
checkpoint = nm_checkpoint_manager_lookup_by_path (self, path, error);
if (!checkpoint)
return FALSE;
nm_checkpoint_adjust_rollback_timeout (checkpoint, add_timeout);
return TRUE;
}
/*****************************************************************************/
NMCheckpointManager *
@ -347,22 +336,17 @@ nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec)
* of NMManager shall surpass the lifetime of the NMCheckpointManager
* instance. */
self->_manager = manager;
self->checkpoints = g_hash_table_new_full (nm_str_hash, g_str_equal,
NULL, item_destroy);
self->property_spec = spec;
c_list_init (&self->list);
c_list_init (&self->checkpoints_lst_head);
return self;
}
void
nm_checkpoint_manager_unref (NMCheckpointManager *self)
nm_checkpoint_manager_free (NMCheckpointManager *self)
{
if (!self)
return;
nm_clear_g_source (&self->rollback_timeout_id);
g_hash_table_destroy (self->checkpoints);
nm_checkpoint_manager_destroy_all (self);
g_slice_free (NMCheckpointManager, self);
}

View file

@ -28,7 +28,12 @@
typedef struct _NMCheckpointManager NMCheckpointManager;
NMCheckpointManager *nm_checkpoint_manager_new (NMManager *manager, GParamSpec *spec);
void nm_checkpoint_manager_unref (NMCheckpointManager *self);
void nm_checkpoint_manager_free (NMCheckpointManager *self);
NMCheckpoint *nm_checkpoint_manager_lookup_by_path (NMCheckpointManager *self,
const char *path,
GError **error);
NMCheckpoint *nm_checkpoint_manager_create (NMCheckpointManager *self,
const char *const*device_names,
@ -36,17 +41,22 @@ NMCheckpoint *nm_checkpoint_manager_create (NMCheckpointManager *self,
NMCheckpointCreateFlags flags,
GError **error);
gboolean nm_checkpoint_manager_destroy_all (NMCheckpointManager *self,
GError **error);
void nm_checkpoint_manager_destroy_all (NMCheckpointManager *self);
gboolean nm_checkpoint_manager_destroy (NMCheckpointManager *self,
const char *checkpoint_path,
const char *path,
GError **error);
gboolean nm_checkpoint_manager_rollback (NMCheckpointManager *self,
const char *checkpoint_path,
const char *path,
GVariant **results,
GError **error);
char **nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self);
gboolean nm_checkpoint_manager_adjust_rollback_timeout (NMCheckpointManager *self,
const char *path,
guint32 add_timeout,
GError **error);
const char **nm_checkpoint_manager_get_checkpoint_paths (NMCheckpointManager *self,
guint *out_length);
#endif /* __NM_CHECKPOINT_MANAGER_H__ */

View file

@ -25,6 +25,7 @@
#include <string.h>
#include "nm-active-connection.h"
#include "nm-act-request.h"
#include "nm-auth-subject.h"
#include "nm-core-utils.h"
#include "nm-dbus-interface.h"
@ -48,27 +49,26 @@ typedef struct {
NMUnmanFlagOp unmanaged_explicit;
} DeviceCheckpoint;
NM_GOBJECT_PROPERTIES_DEFINE_BASE (
NM_GOBJECT_PROPERTIES_DEFINE (NMCheckpoint,
PROP_DEVICES,
PROP_CREATED,
PROP_ROLLBACK_TIMEOUT,
);
typedef struct {
struct _NMCheckpointPrivate {
/* properties */
GHashTable *devices;
gint64 created;
guint32 rollback_timeout;
gint64 created_at_ms;
guint32 rollback_timeout_s;
guint timeout_id;
/* private members */
/* private members */
NMManager *manager;
gint64 rollback_ts;
NMCheckpointCreateFlags flags;
GHashTable *connection_uuids;
} NMCheckpointPrivate;
struct _NMCheckpoint {
NMDBusObject parent;
NMCheckpointPrivate _priv;
NMCheckpointTimeoutCallback timeout_cb;
gpointer timeout_data;
};
struct _NMCheckpointClass {
@ -77,7 +77,7 @@ struct _NMCheckpointClass {
G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_DBUS_OBJECT)
#define NM_CHECKPOINT_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMCheckpoint, NM_IS_CHECKPOINT)
#define NM_CHECKPOINT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMCheckpoint, NM_IS_CHECKPOINT)
/*****************************************************************************/
@ -101,20 +101,53 @@ G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_DBUS_OBJECT)
/*****************************************************************************/
guint64
nm_checkpoint_get_rollback_ts (NMCheckpoint *self)
void
nm_checkpoint_log_destroy (NMCheckpoint *self)
{
g_return_val_if_fail (NM_IS_CHECKPOINT (self), 0);
return NM_CHECKPOINT_GET_PRIVATE (self)->rollback_ts;
_LOGI ("destroy %s", nm_dbus_object_get_path (NM_DBUS_OBJECT (self)));
}
gboolean
nm_checkpoint_includes_device (NMCheckpoint *self, NMDevice *device)
void
nm_checkpoint_set_timeout_callback (NMCheckpoint *self,
NMCheckpointTimeoutCallback callback,
gpointer user_data)
{
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
return g_hash_table_contains (priv->devices, device);
/* in glib world, we would have a GSignal for this. But as there
* is only one subscriber, it's simpler to just set and unset(!)
* the callback this way. */
priv->timeout_cb = callback;
priv->timeout_data = user_data;
}
NMDevice *
nm_checkpoint_includes_devices (NMCheckpoint *self, NMDevice *const*devices, guint n_devices)
{
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
guint i;
for (i = 0; i < n_devices; i++) {
if (g_hash_table_contains (priv->devices, devices[i]))
return devices[i];
}
return NULL;
}
NMDevice *
nm_checkpoint_includes_devices_of (NMCheckpoint *self, NMCheckpoint *cp_for_devices)
{
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
NMCheckpointPrivate *priv2 = NM_CHECKPOINT_GET_PRIVATE (cp_for_devices);
GHashTableIter iter;
NMDevice *device;
g_hash_table_iter_init (&iter, priv2->devices);
while (g_hash_table_iter_next (&iter, (gpointer *) &device, NULL)) {
if (g_hash_table_contains (priv->devices, device))
return device;
}
return NULL;
}
static NMSettingsConnection *
@ -348,11 +381,10 @@ next_dev:
}
if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) {
const CList *all_devices;
const CList *tmp_lst;
NMDeviceState state;
all_devices = nm_manager_get_devices (priv->manager);
c_list_for_each_entry (device, all_devices, devices_lst) {
nm_manager_for_each_device (priv->manager, device, tmp_lst) {
if (g_hash_table_contains (priv->devices, device))
continue;
state = nm_device_get_state (device);
@ -371,8 +403,7 @@ next_dev:
}
static DeviceCheckpoint *
device_checkpoint_create (NMDevice *device,
GError **error)
device_checkpoint_create (NMDevice *device)
{
DeviceCheckpoint *dev_checkpoint;
NMConnection *applied_connection;
@ -380,6 +411,9 @@ device_checkpoint_create (NMDevice *device,
const char *path;
NMActRequest *act_request;
nm_assert (NM_IS_DEVICE (device));
nm_assert (nm_device_is_real (device));
path = nm_dbus_object_get_path (NM_DBUS_OBJECT (device));
dev_checkpoint = g_slice_new0 (DeviceCheckpoint);
@ -389,25 +423,21 @@ device_checkpoint_create (NMDevice *device,
dev_checkpoint->realized = nm_device_is_real (device);
if (nm_device_get_unmanaged_mask (device, NM_UNMANAGED_USER_EXPLICIT)) {
dev_checkpoint->unmanaged_explicit =
!!nm_device_get_unmanaged_flags (device, NM_UNMANAGED_USER_EXPLICIT);
dev_checkpoint->unmanaged_explicit = !!nm_device_get_unmanaged_flags (device,
NM_UNMANAGED_USER_EXPLICIT);
} else
dev_checkpoint->unmanaged_explicit = NM_UNMAN_FLAG_OP_FORGET;
applied_connection = nm_device_get_applied_connection (device);
if (applied_connection) {
dev_checkpoint->applied_connection =
nm_simple_connection_new_clone (applied_connection);
act_request = nm_device_get_act_request (device);
if (act_request) {
settings_connection = nm_act_request_get_settings_connection (act_request);
applied_connection = nm_act_request_get_applied_connection (act_request);
settings_connection = nm_device_get_settings_connection (device);
g_return_val_if_fail (settings_connection, NULL);
dev_checkpoint->applied_connection = nm_simple_connection_new_clone (applied_connection);
dev_checkpoint->settings_connection =
nm_simple_connection_new_clone (NM_CONNECTION (settings_connection));
act_request = nm_device_get_act_request (device);
g_return_val_if_fail (act_request, NULL);
nm_simple_connection_new_clone (NM_CONNECTION (settings_connection));
dev_checkpoint->ac_version_id =
nm_active_connection_version_id_get (NM_ACTIVE_CONNECTION (act_request));
nm_active_connection_version_id_get (NM_ACTIVE_CONNECTION (act_request));
}
return dev_checkpoint;
@ -426,6 +456,57 @@ device_checkpoint_destroy (gpointer data)
g_slice_free (DeviceCheckpoint, dev_checkpoint);
}
static gboolean
_timeout_cb (gpointer user_data)
{
NMCheckpoint *self = user_data;
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
priv->timeout_id = 0;
if (priv->timeout_cb)
priv->timeout_cb (self, priv->timeout_data);
/* beware, @self likely got destroyed! */
return G_SOURCE_REMOVE;
}
void
nm_checkpoint_adjust_rollback_timeout (NMCheckpoint *self, guint32 add_timeout)
{
guint32 rollback_timeout_s;
gint64 now_ms, add_timeout_ms, rollback_timeout_ms;
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
nm_clear_g_source (&priv->timeout_id);
if (add_timeout == 0)
rollback_timeout_s = 0;
else {
now_ms = nm_utils_get_monotonic_timestamp_ms ();
add_timeout_ms = ((gint64) add_timeout) * 1000;
rollback_timeout_ms = (now_ms - priv->created_at_ms) + add_timeout_ms;
/* round to nearest integer second. Since NM_CHECKPOINT_ROLLBACK_TIMEOUT is
* in units seconds, it will be able to exactly express the timeout. */
rollback_timeout_s = NM_MIN ((rollback_timeout_ms + 500) / 1000, (gint64) G_MAXUINT32);
/* we expect the timeout to be positive, because add_timeout_ms is positive.
* We cannot accept a zero, because it means "infinity". */
nm_assert (rollback_timeout_s > 0);
priv->timeout_id = g_timeout_add (NM_MIN (add_timeout_ms, (gint64) G_MAXUINT32),
_timeout_cb,
self);
}
if (rollback_timeout_s != priv->rollback_timeout_s) {
priv->rollback_timeout_s = rollback_timeout_s;
_notify (self, PROP_ROLLBACK_TIMEOUT);
}
}
/*****************************************************************************/
static void
@ -434,22 +515,20 @@ get_property (GObject *object, guint prop_id,
{
NMCheckpoint *self = NM_CHECKPOINT (object);
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
gs_free_slist GSList *devices = NULL;
GHashTableIter iter;
NMDevice *device;
switch (prop_id) {
case PROP_DEVICES:
g_hash_table_iter_init (&iter, priv->devices);
while (g_hash_table_iter_next (&iter, (gpointer *) &device, NULL))
devices = g_slist_append (devices, device);
nm_dbus_utils_g_value_set_object_path_array (value, devices, NULL, NULL);
nm_dbus_utils_g_value_set_object_path_from_hash (value,
priv->devices,
FALSE);
break;
case PROP_CREATED:
g_value_set_int64 (value, priv->created);
g_value_set_int64 (value,
nm_utils_monotonic_timestamp_as_boottime (priv->created_at_ms,
NM_UTILS_NS_PER_MSEC));
break;
case PROP_ROLLBACK_TIMEOUT:
g_value_set_uint (value, priv->rollback_timeout);
g_value_set_uint (value, priv->rollback_timeout_s);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -462,47 +541,47 @@ get_property (GObject *object, guint prop_id,
static void
nm_checkpoint_init (NMCheckpoint *self)
{
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
NMCheckpointPrivate *priv;
priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_CHECKPOINT, NMCheckpointPrivate);
self->_priv = priv;
c_list_init (&self->checkpoints_lst);
priv->devices = g_hash_table_new_full (nm_direct_hash, NULL,
NULL, device_checkpoint_destroy);
}
NMCheckpoint *
nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
NMCheckpointCreateFlags flags, GError **error)
nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout_s,
NMCheckpointCreateFlags flags)
{
NMCheckpoint *self;
NMCheckpointPrivate *priv;
NMSettingsConnection *const *con;
DeviceCheckpoint *dev_checkpoint;
NMDevice *device;
gint64 rollback_timeout_ms;
guint i;
g_return_val_if_fail (manager, NULL);
g_return_val_if_fail (devices, NULL);
g_return_val_if_fail (!error || !*error, NULL);
if (!devices->len) {
g_set_error_literal (error,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_INVALID_ARGUMENTS,
"no device available");
return NULL;
}
g_return_val_if_fail (devices->len > 0, NULL);
self = g_object_new (NM_TYPE_CHECKPOINT, NULL);
priv = NM_CHECKPOINT_GET_PRIVATE (self);
priv->manager = manager;
priv->created = nm_utils_monotonic_timestamp_as_boottime (nm_utils_get_monotonic_timestamp_ms (),
NM_UTILS_NS_PER_MSEC);
priv->rollback_timeout = rollback_timeout;
priv->rollback_ts = rollback_timeout ?
(nm_utils_get_monotonic_timestamp_ms () + ((gint64) rollback_timeout * 1000)) :
0;
priv->rollback_timeout_s = rollback_timeout_s;
priv->created_at_ms = nm_utils_get_monotonic_timestamp_ms ();
priv->flags = flags;
if (rollback_timeout_s != 0) {
rollback_timeout_ms = ((gint64) rollback_timeout_s) * 1000;
priv->timeout_id = g_timeout_add (NM_MIN (rollback_timeout_ms, (gint64) G_MAXUINT32),
_timeout_cb,
self);
}
if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) {
priv->connection_uuids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
for (con = nm_settings_get_connections (nm_settings_get (), NULL); *con; con++) {
@ -512,13 +591,15 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time
}
for (i = 0; i < devices->len; i++) {
device = (NMDevice *) devices->pdata[i];
dev_checkpoint = device_checkpoint_create (device, error);
if (!dev_checkpoint) {
g_object_unref (self);
return NULL;
}
g_hash_table_insert (priv->devices, device, dev_checkpoint);
NMDevice *device = devices->pdata[i];
/* FIXME: as long as the check point instance exists, it won't let go
* of the device. That is a bug, for example, if you have a ethernet
* device that gets removed (rmmod), the checkpoint will reference
* a non-existing D-Bus path of a device. */
g_hash_table_insert (priv->devices,
device,
device_checkpoint_create (device));
}
return self;
@ -530,9 +611,13 @@ dispose (GObject *object)
NMCheckpoint *self = NM_CHECKPOINT (object);
NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
nm_assert (c_list_is_empty (&self->checkpoints_lst));
g_clear_pointer (&priv->devices, g_hash_table_unref);
g_clear_pointer (&priv->connection_uuids, g_hash_table_unref);
nm_clear_g_source (&priv->timeout_id);
G_OBJECT_CLASS (nm_checkpoint_parent_class)->dispose (object);
}
@ -557,6 +642,8 @@ nm_checkpoint_class_init (NMCheckpointClass *checkpoint_class)
GObjectClass *object_class = G_OBJECT_CLASS (checkpoint_class);
NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (checkpoint_class);
g_type_class_add_private (object_class, sizeof (NMCheckpointPrivate));
dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH"/Checkpoint");
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_checkpoint);

View file

@ -35,16 +35,35 @@
#define NM_CHECKPOINT_CREATED "created"
#define NM_CHECKPOINT_ROLLBACK_TIMEOUT "rollback-timeout"
typedef struct _NMCheckpoint NMCheckpoint;
typedef struct _NMCheckpointPrivate NMCheckpointPrivate;
typedef struct {
NMDBusObject parent;
NMCheckpointPrivate *_priv;
CList checkpoints_lst;
} NMCheckpoint;
typedef struct _NMCheckpointClass NMCheckpointClass;
GType nm_checkpoint_get_type (void);
NMCheckpoint *nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout,
NMCheckpointCreateFlags flags, GError **error);
NMCheckpointCreateFlags flags);
typedef void (*NMCheckpointTimeoutCallback) (NMCheckpoint *self,
gpointer user_data);
void nm_checkpoint_log_destroy (NMCheckpoint *self);
void nm_checkpoint_set_timeout_callback (NMCheckpoint *self,
NMCheckpointTimeoutCallback callback,
gpointer user_data);
guint64 nm_checkpoint_get_rollback_ts (NMCheckpoint *checkpoint);
gboolean nm_checkpoint_includes_device (NMCheckpoint *checkpoint, NMDevice *device);
GVariant *nm_checkpoint_rollback (NMCheckpoint *self);
void nm_checkpoint_adjust_rollback_timeout (NMCheckpoint *self, guint32 add_timeout);
NMDevice *nm_checkpoint_includes_devices (NMCheckpoint *self, NMDevice *const*devices, guint n_devices);
NMDevice *nm_checkpoint_includes_devices_of (NMCheckpoint *self, NMCheckpoint *cp_for_devices);
#endif /* __NETWORKMANAGER_CHECKPOINT_H__ */

View file

@ -121,32 +121,35 @@ nm_dbus_utils_g_value_set_object_path (GValue *value, gpointer object)
}
void
nm_dbus_utils_g_value_set_object_path_array (GValue *value,
GSList *objects,
gboolean (*filter_func) (GObject *object, gpointer user_data),
gpointer user_data)
nm_dbus_utils_g_value_set_object_path_from_hash (GValue *value,
GHashTable *hash /* has keys of NMDBusObject type. */,
gboolean expect_all_exported)
{
char **paths;
guint i;
GSList *iter;
NMDBusObject *obj;
char **strv;
guint i, n;
GHashTableIter iter;
paths = g_new (char *, g_slist_length (objects) + 1);
for (i = 0, iter = objects; iter; iter = iter->next) {
NMDBusObject *object = iter->data;
nm_assert (value);
nm_assert (hash);
n = g_hash_table_size (hash);
strv = g_new (char *, n + 1);
i = 0;
g_hash_table_iter_init (&iter, hash);
while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) {
const char *path;
path = nm_dbus_object_get_path (object);
if (!path)
path = nm_dbus_object_get_path (obj);
if (!path) {
nm_assert (!expect_all_exported);
continue;
if ( filter_func
&& !filter_func ((GObject *) object, user_data))
continue;
paths[i++] = g_strdup (path);
}
strv[i++] = g_strdup (path);
}
paths[i] = NULL;
g_value_take_boxed (value, paths);
nm_assert (i <= n);
strv[i] = NULL;
g_value_take_boxed (value, strv);
}
/*****************************************************************************/

View file

@ -166,9 +166,8 @@ GVariant *nm_dbus_utils_get_property (GObject *obj,
void nm_dbus_utils_g_value_set_object_path (GValue *value, gpointer object);
void nm_dbus_utils_g_value_set_object_path_array (GValue *value,
GSList *objects,
gboolean (*filter_func) (GObject *object, gpointer user_data),
gpointer user_data);
void nm_dbus_utils_g_value_set_object_path_from_hash (GValue *value,
GHashTable *hash,
gboolean expect_all_exported);
#endif /* __NM_DBUS_UTILS_H__ */

View file

@ -5956,13 +5956,15 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
GVariant *variant = NULL;
GError *error = NULL;
const char *arg = NULL;
guint32 add_timeout;
op = nm_auth_chain_get_data (chain, "audit-op");
priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK);
if ( nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_DESTROY)
|| nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK))
if (NM_IN_STRSET (op, NM_AUDIT_OP_CHECKPOINT_DESTROY,
NM_AUDIT_OP_CHECKPOINT_ROLLBACK,
NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT))
arg = checkpoint_path = nm_auth_chain_get_data (chain, "checkpoint_path");
if (auth_error) {
@ -5995,6 +5997,10 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK)) {
nm_checkpoint_manager_rollback (_checkpoint_mgr_get (self, TRUE),
checkpoint_path, &variant, &error);
} else if (nm_streq0 (op, NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT)) {
add_timeout = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "add_timeout"));
nm_checkpoint_manager_adjust_rollback_timeout (_checkpoint_mgr_get (self, TRUE),
checkpoint_path, add_timeout, &error);
} else
g_return_if_reached ();
}
@ -6007,7 +6013,6 @@ checkpoint_auth_done_cb (NMAuthChain *chain,
else
g_dbus_method_invocation_return_value (context, variant);
nm_auth_chain_unref (chain);
}
@ -6110,6 +6115,39 @@ impl_manager_checkpoint_rollback (NMDBusObject *obj,
nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
}
static void
impl_manager_checkpoint_adjust_rollback_timeout (NMDBusObject *obj,
const NMDBusInterfaceInfoExtended *interface_info,
const NMDBusMethodInfoExtended *method_info,
GDBusConnection *connection,
const char *sender,
GDBusMethodInvocation *invocation,
GVariant *parameters)
{
NMManager *self = NM_MANAGER (obj);
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
NMAuthChain *chain;
const char *checkpoint_path;
guint32 add_timeout;
chain = nm_auth_chain_new_context (invocation, checkpoint_auth_done_cb, self);
if (!chain) {
g_dbus_method_invocation_return_error_literal (invocation,
NM_MANAGER_ERROR,
NM_MANAGER_ERROR_PERMISSION_DENIED,
"Unable to authenticate request.");
return;
}
g_variant_get (parameters, "(&ou)", &checkpoint_path, &add_timeout);
priv->auth_chains = g_slist_append (priv->auth_chains, chain);
nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT, NULL);
nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free);
nm_auth_chain_set_data (chain, "add_timeout", GUINT_TO_POINTER (add_timeout), NULL);
nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE);
}
/*****************************************************************************/
static void
@ -6471,7 +6509,6 @@ get_property (GObject *object, guint prop_id,
NMConfigData *config_data;
const NMGlobalDnsConfig *dns_config;
const char *type;
char **strv;
const char *path;
NMActiveConnection *ac;
GPtrArray *ptrarr;
@ -6578,10 +6615,11 @@ get_property (GObject *object, guint prop_id,
TRUE)));
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);
g_value_take_boxed (value,
priv->checkpoint_mgr
? nm_utils_strv_make_deep_copied (nm_checkpoint_manager_get_checkpoint_paths (priv->checkpoint_mgr,
NULL))
: NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -6664,10 +6702,7 @@ dispose (GObject *object)
nm_clear_g_source (&priv->devices_inited_id);
if (priv->checkpoint_mgr) {
nm_checkpoint_manager_destroy_all (priv->checkpoint_mgr, NULL);
g_clear_pointer (&priv->checkpoint_mgr, nm_checkpoint_manager_unref);
}
g_clear_pointer (&priv->checkpoint_mgr, nm_checkpoint_manager_free);
if (priv->auth_mgr) {
g_signal_handlers_disconnect_by_func (priv->auth_mgr,
@ -6967,6 +7002,16 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = {
),
.handle = impl_manager_checkpoint_rollback,
),
NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
NM_DEFINE_GDBUS_METHOD_INFO_INIT (
"CheckpointAdjustRollbackTimeout",
.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
NM_DEFINE_GDBUS_ARG_INFO ("checkpoint", "o"),
NM_DEFINE_GDBUS_ARG_INFO ("add_timeout", "u"),
),
),
.handle = impl_manager_checkpoint_adjust_rollback_timeout,
),
),
.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
&nm_signal_info_property_changed_legacy,

View file

@ -83,13 +83,14 @@ gboolean nm_manager_start (NMManager *manager,
GError **error);
void nm_manager_stop (NMManager *manager);
NMState nm_manager_get_state (NMManager *manager);
const CList * nm_manager_get_active_connections (NMManager *manager);
#define nm_manager_for_each_active_connection(manager, iter, tmp_list) \
for (tmp_list = nm_manager_get_active_connections (manager), \
iter = c_list_entry (tmp_list->next, NMActiveConnection, active_connections_lst); \
({ \
gboolean _has_next = (&iter->active_connections_lst != tmp_list); \
const gboolean _has_next = (&iter->active_connections_lst != tmp_list); \
\
if (!_has_next) \
iter = NULL; \
@ -107,6 +108,18 @@ void nm_manager_write_device_state (NMManager *manager);
const CList * nm_manager_get_devices (NMManager *manager);
#define nm_manager_for_each_device(manager, iter, tmp_list) \
for (tmp_list = nm_manager_get_devices (manager), \
iter = c_list_entry (tmp_list->next, NMDevice, devices_lst); \
({ \
const gboolean _has_next = (&iter->devices_lst != tmp_list); \
\
if (!_has_next) \
iter = NULL; \
_has_next; \
}); \
iter = c_list_entry (iter->devices_lst.next, NMDevice, devices_lst))
NMDevice * nm_manager_get_device_by_ifindex (NMManager *manager,
int ifindex);
NMDevice * nm_manager_get_device_by_path (NMManager *manager,

View file

@ -385,7 +385,7 @@ get_best_ip_device (NMPolicy *self,
gboolean fully_activated)
{
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
const CList *all_devices;
const CList *tmp_lst;
NMDevice *device;
NMDevice *best_device;
NMDevice *prev_device;
@ -401,8 +401,7 @@ get_best_ip_device (NMPolicy *self,
? (fully_activated ? priv->default_device4 : priv->activating_device4)
: (fully_activated ? priv->default_device6 : priv->activating_device6);
all_devices = nm_manager_get_devices (priv->manager);
c_list_for_each_entry (device, all_devices, devices_lst) {
nm_manager_for_each_device (priv->manager, device, tmp_lst) {
NMDeviceState state;
const NMPObject *r;
NMConnection *connection;
@ -462,11 +461,10 @@ static gboolean
all_devices_not_active (NMPolicy *self)
{
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
const CList *all_devices;
const CList *tmp_lst;
NMDevice *device;
all_devices = nm_manager_get_devices (priv->manager);
c_list_for_each_entry (device, all_devices, devices_lst) {
nm_manager_for_each_device (priv->manager, device, tmp_lst) {
NMDeviceState state;
state = nm_device_get_state (device);
@ -2186,13 +2184,12 @@ schedule_activate_all_cb (gpointer user_data)
{
NMPolicy *self = user_data;
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
const CList *all_devices;
const CList *tmp_lst;
NMDevice *device;
priv->schedule_activate_all_id = 0;
all_devices = nm_manager_get_devices (priv->manager);
c_list_for_each_entry (device, all_devices, devices_lst)
nm_manager_for_each_device (priv->manager, device, tmp_lst)
schedule_activate_check (self, device);
return G_SOURCE_REMOVE;
@ -2227,7 +2224,7 @@ firewall_state_changed (NMFirewallManager *manager,
{
NMPolicy *self = (NMPolicy *) user_data;
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
const CList *all_devices;
const CList *tmp_lst;
NMDevice *device;
if (initialized_now) {
@ -2241,8 +2238,7 @@ firewall_state_changed (NMFirewallManager *manager,
return;
/* add interface of each device to correct zone */
all_devices = nm_manager_get_devices (priv->manager);
c_list_for_each_entry (device, all_devices, devices_lst)
nm_manager_for_each_device (priv->manager, device, tmp_lst)
nm_device_update_firewall_zone (device);
}
@ -2288,14 +2284,13 @@ connection_updated (NMSettings *settings,
{
NMPolicyPrivate *priv = user_data;
NMPolicy *self = _PRIV_TO_SELF (priv);
const CList *all_devices;
const CList *tmp_lst;
NMDevice *device = NULL;
NMDevice *dev;
if (by_user) {
/* find device with given connection */
all_devices = nm_manager_get_devices (priv->manager);
c_list_for_each_entry (dev, all_devices, devices_lst) {
nm_manager_for_each_device (priv->manager, dev, tmp_lst) {
if (nm_device_get_settings_connection (dev) == connection) {
device = dev;
break;