NetworkManager/src/nm-checkpoint-manager.c
Thomas Haller 4d37f7a1e9 core: refactor private data in "src"
- use _NM_GET_PRIVATE() and _NM_GET_PRIVATE_PTR() everywhere.

- reorder statements, to have GObject related functions (init, dispose,
  constructed) at the bottom of each file and in a consistent order w.r.t.
  each other.

- unify whitespaces in signal and properties declarations.

- use NM_GOBJECT_PROPERTIES_DEFINE() and _notify()

- drop unused signal slots in class structures

- drop unused header files for device factories
2016-10-04 09:50:56 +02:00

305 lines
8.8 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2016 Red Hat, Inc.
*/
#include "nm-default.h"
#include "nm-checkpoint-manager.h"
#include "nm-checkpoint.h"
#include "nm-connection.h"
#include "nm-core-utils.h"
#include "nm-device.h"
#include "nm-exported-object.h"
#include "nm-manager.h"
#include "nm-utils.h"
/*****************************************************************************/
struct _NMCheckpointManager {
NMManager *_manager;
GHashTable *checkpoints;
guint rollback_timeout_id;
};
#define GET_MANAGER(self) \
({ \
typeof (self) _self = (self); \
\
_nm_unused NMCheckpointManager *_self2 = _self; \
\
nm_assert (_self); \
nm_assert (NM_IS_MANAGER (_self->_manager)); \
_self->_manager; \
})
/*****************************************************************************/
#define _NMLOG_PREFIX_NAME "checkpoint"
#define _NMLOG_DOMAIN LOGD_CORE
#define _NMLOG(level, ...) \
nm_log (level, _NMLOG_DOMAIN, \
"%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
_NMLOG_PREFIX_NAME \
_NM_UTILS_MACRO_REST(__VA_ARGS__))
/*****************************************************************************/
static void update_rollback_timeout (NMCheckpointManager *self);
static void
checkpoint_destroy (gpointer checkpoint)
{
nm_exported_object_unexport (NM_EXPORTED_OBJECT (checkpoint));
g_object_unref (G_OBJECT (checkpoint));
}
static gboolean
rollback_timeout_cb (NMCheckpointManager *self)
{
NMCheckpoint *checkpoint;
GHashTableIter iter;
GVariant *result;
gint64 ts, now;
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);
if (ts && ts <= now) {
result = nm_checkpoint_rollback (checkpoint);
if (result)
g_variant_unref (result);
g_hash_table_iter_remove (&iter);
}
}
self->rollback_timeout_id = 0;
update_rollback_timeout (self);
return G_SOURCE_REMOVE;
}
static void
update_rollback_timeout (NMCheckpointManager *self)
{
NMCheckpoint *checkpoint;
GHashTableIter iter;
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);
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)
{
GHashTableIter iter;
NMCheckpoint *checkpoint;
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;
}
return NULL;
}
NMCheckpoint *
nm_checkpoint_manager_create (NMCheckpointManager *self,
const char *const *device_paths,
guint32 rollback_timeout,
NMCheckpointCreateFlags flags,
GError **error)
{
NMManager *manager;
NMCheckpoint *checkpoint;
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]) {
device_paths_free = nm_manager_get_device_paths (manager);
device_paths = (const char *const *) device_paths_free;
}
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;
}
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];
if (find_checkpoint_for_device (self, device)) {
g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS,
"a checkpoint for device '%s' already exists",
nm_device_get_iface (device));
return NULL;
}
}
}
checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, error);
if (!checkpoint)
return NULL;
if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL))
g_hash_table_remove_all (self->checkpoints);
nm_exported_object_export (NM_EXPORTED_OBJECT (checkpoint));
checkpoint_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (checkpoint));
if (!nm_g_hash_table_insert (self->checkpoints,
(gpointer) checkpoint_path,
checkpoint))
g_return_val_if_reached (NULL);
update_rollback_timeout (self);
return checkpoint;
}
gboolean
nm_checkpoint_manager_destroy_all (NMCheckpointManager *self,
GError **error)
{
g_return_val_if_fail (self, FALSE);
g_hash_table_remove_all (self->checkpoints);
return TRUE;
}
gboolean
nm_checkpoint_manager_destroy (NMCheckpointManager *self,
const char *checkpoint_path,
GError **error)
{
gboolean ret;
g_return_val_if_fail (self, FALSE);
g_return_val_if_fail (checkpoint_path && checkpoint_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) {
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);
}
gboolean
nm_checkpoint_manager_rollback (NMCheckpointManager *self,
const char *checkpoint_path,
GVariant **results,
GError **error)
{
NMCheckpoint *cp;
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) {
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);
g_hash_table_remove (self->checkpoints, checkpoint_path);
return TRUE;
}
/*****************************************************************************/
NMCheckpointManager *
nm_checkpoint_manager_new (NMManager *manager)
{
NMCheckpointManager *self;
g_return_val_if_fail (NM_IS_MANAGER (manager), FALSE);
self = g_slice_new0 (NMCheckpointManager);
/* the NMCheckpointManager instance is actually owned by NMManager.
* Thus, we cannot take a reference to it, and we also don't bother
* taking a weak-reference. Instead let GET_MANAGER() assert that
* self->_manager is alive -- which we always expect as the lifetime
* of NMManager shall surpass the lifetime of the NMCheckpointManager
* instance. */
self->_manager = manager;
self->checkpoints = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, checkpoint_destroy);
return self;
}
void
nm_checkpoint_manager_unref (NMCheckpointManager *self)
{
if (!self)
return;
nm_clear_g_source (&self->rollback_timeout_id);
g_hash_table_destroy (self->checkpoints);
g_slice_free (NMCheckpointManager, self);
}