NetworkManager/src/core/nm-checkpoint-manager.c
Thomas Haller ac1a9e03e4
all: move "src/" directory to "src/core/"
Currently "src/" mostly contains the source code of the daemon.
I say mostly, because that is not true, there are also the device,
settings, wwan, ppp plugins, the initrd generator, the pppd and dhcp
helper, and probably more.

Also we have source code under libnm-core/, libnm/, clients/, and
shared/ directories. That is all confusing.

We should have one "src" directory, that contains subdirectories. Those
subdirectories should contain individual parts (libraries or
applications), that possibly have dependencies on other subdirectories.
There should be a flat hierarchy of directories under src/, which
contains individual modules.

As the name "src/" is already taken, that prevents any sensible
restructuring of the code.

As a first step, move "src/" to "src/core/". This gives space to
reorganize the code better by moving individual components into "src/".

For inspiration, look at systemd's "src/" directory.

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/743
2021-02-04 09:45:55 +01:00

349 lines
11 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* 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 "devices/nm-device.h"
#include "nm-manager.h"
#include "nm-utils.h"
#include "c-list/src/c-list.h"
/*****************************************************************************/
struct _NMCheckpointManager {
NMManager * _manager;
GParamSpec *property_spec;
CList checkpoints_lst_head;
};
#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_DOMAIN LOGD_CORE
#define _NMLOG(level, ...) __NMLOG_DEFAULT(level, _NMLOG_DOMAIN, "checkpoint", __VA_ARGS__)
/*****************************************************************************/
static void
notify_checkpoints(NMCheckpointManager *self)
{
g_object_notify_by_pspec((GObject *) GET_MANAGER(self), self->property_spec);
}
static void
destroy_checkpoint(NMCheckpointManager *self, NMCheckpoint *checkpoint, gboolean log_destroy)
{
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));
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 GVariant *
rollback_checkpoint(NMCheckpointManager *self, NMCheckpoint *checkpoint)
{
GVariant * result;
const CList *iter;
nm_assert(c_list_contains(&self->checkpoints_lst_head, &checkpoint->checkpoints_lst));
/* 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);
}
}
result = nm_checkpoint_rollback(checkpoint);
destroy_checkpoint(self, checkpoint, FALSE);
return result;
}
static void
rollback_timeout_cb(NMCheckpoint *checkpoint, gpointer user_data)
{
NMCheckpointManager *self = user_data;
gs_unref_variant GVariant *result = NULL;
result = rollback_checkpoint(self, checkpoint);
}
NMCheckpoint *
nm_checkpoint_manager_create(NMCheckpointManager * self,
const char *const * device_paths,
guint32 rollback_timeout,
NMCheckpointCreateFlags flags,
GError ** error)
{
NMManager * manager;
NMCheckpoint * checkpoint;
gs_unref_ptrarray GPtrArray *devices = NULL;
NMDevice * device;
g_return_val_if_fail(self, FALSE);
g_return_val_if_fail(!error || !*error, FALSE);
manager = GET_MANAGER(self);
devices = g_ptr_array_new();
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;
nm_assert(nm_dbus_object_get_path(NM_DBUS_OBJECT(device)));
g_ptr_array_add(devices, device);
}
} 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;
} 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);
}
}
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),
nm_dbus_object_get_path(NM_DBUS_OBJECT(checkpoint)));
return NULL;
}
}
}
checkpoint = nm_checkpoint_new(manager, devices, rollback_timeout, flags);
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);
return checkpoint;
}
void
nm_checkpoint_manager_destroy_all(NMCheckpointManager *self)
{
NMCheckpoint *checkpoint;
g_return_if_fail(self);
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 *path, 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);
if (!nm_dbus_path_not_empty(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 * path,
GVariant ** results,
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(results, FALSE);
g_return_val_if_fail(!error || !*error, FALSE);
checkpoint = nm_checkpoint_manager_lookup_by_path(self, path, error);
if (!checkpoint)
return FALSE;
*results = rollback_checkpoint(self, checkpoint);
return TRUE;
}
NMCheckpoint *
nm_checkpoint_manager_lookup_by_path(NMCheckpointManager *self, const char *path, GError **error)
{
NMCheckpoint *checkpoint;
g_return_val_if_fail(self, NULL);
checkpoint =
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;
}
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 *
nm_checkpoint_manager_new(NMManager *manager, GParamSpec *spec)
{
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->property_spec = spec;
c_list_init(&self->checkpoints_lst_head);
return self;
}
void
nm_checkpoint_manager_free(NMCheckpointManager *self)
{
if (!self)
return;
nm_checkpoint_manager_destroy_all(self);
g_slice_free(NMCheckpointManager, self);
}