mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-10-16 04:54:23 +00:00
4d37f7a1e9
- 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
305 lines
8.8 KiB
C
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);
|
|
}
|