From ddeef464af9a565e6b6ca7a6470a7ee588079739 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 23 Sep 2016 09:37:42 +0200 Subject: [PATCH] checkpoint: introduce new flags to better restore previous state When a global checkpoint is created (one with empty device list) we save the status of all devices to restore it later. After the checkpoint new interfaces and connections may appear and they can significantly influence the overall networking status, but we don't consider them at the moment. Introduce a new flag DELETE_NEW_CONNECTIONS to delete any connection added after the checkpoint and similarly a DISCONNECT_NEW_DEVICES to ensure that the connection active on newly appeared devices doesn't disrupt network connectivity. https://bugzilla.redhat.com/show_bug.cgi?id=1378393 --- libnm-core/nm-dbus-interface.h | 10 ++++-- src/nm-checkpoint-manager.c | 6 +++- src/nm-checkpoint.c | 56 +++++++++++++++++++++++++++++++++- src/nm-checkpoint.h | 2 +- 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index 98c9860eaf..ecb6e10620 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -712,14 +712,20 @@ typedef enum { * @NM_CHECKPOINT_CREATE_FLAG_NONE: no flags * @NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL: when creating * a new checkpoint, destroy all existing ones. + * @NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS: upon rollback, + * delete any new connection added after the checkpoint. + * @NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES: upon rollback, + * disconnect any new device appeared after the checkpoint. * * The flags for CheckpointCreate call * * Since: 1.4 */ typedef enum { /*< skip >*/ - NM_CHECKPOINT_CREATE_FLAG_NONE = 0, - NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL = 0x01, + 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, } NMCheckpointCreateFlags; /** diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c index 07a97c25c4..b5dac7ada7 100644 --- a/src/nm-checkpoint-manager.c +++ b/src/nm-checkpoint-manager.c @@ -155,6 +155,10 @@ nm_checkpoint_manager_create (NMCheckpointManager *self, if (!device_paths || !device_paths[0]) { device_paths_free = nm_manager_get_device_paths (manager); 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 (); @@ -180,7 +184,7 @@ nm_checkpoint_manager_create (NMCheckpointManager *self, } } - checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, error); + checkpoint = nm_checkpoint_new (manager, devices, rollback_timeout, flags, error); if (!checkpoint) return NULL; diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c index 1ee8026842..ad00b68797 100644 --- a/src/nm-checkpoint.c +++ b/src/nm-checkpoint.c @@ -61,6 +61,8 @@ typedef struct { /* private members */ NMManager *manager; gint64 rollback_ts; + NMCheckpointCreateFlags flags; + GHashTable *connection_uuids; } NMCheckpointPrivate; struct _NMCheckpoint { @@ -245,6 +247,47 @@ next_dev: g_variant_builder_add (&builder, "{su}", dev_checkpoint->original_dev_path, result); } + if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) { + NMSettingsConnection *con; + gs_free_slist GSList *list = NULL; + GSList *item; + + g_return_val_if_fail (priv->connection_uuids, NULL); + list = nm_settings_get_connections_sorted (nm_settings_get ()); + + for (item = list; item; item = g_slist_next (item)) { + con = item->data; + if (!g_hash_table_contains (priv->connection_uuids, + nm_settings_connection_get_uuid (con))) { + _LOGD ("rollback: deleting new connection %s (%s)", + nm_settings_connection_get_uuid (con), + nm_settings_connection_get_id (con)); + nm_settings_connection_delete (con, NULL, NULL); + } + } + } + + if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) { + const GSList *list = nm_manager_get_devices (priv->manager); + NMDeviceState state; + NMDevice *dev; + + for (list = nm_manager_get_devices (priv->manager); list ; list = g_slist_next (list)) { + dev = list->data; + if (!g_hash_table_contains (priv->devices, dev)) { + state = nm_device_get_state (dev); + if ( state > NM_DEVICE_STATE_DISCONNECTED + && state < NM_DEVICE_STATE_DEACTIVATING) { + _LOGD ("rollback: disconnecting new device %s", nm_device_get_iface (dev)); + nm_device_state_changed (dev, + NM_DEVICE_STATE_DEACTIVATING, + NM_DEVICE_STATE_REASON_USER_REQUESTED); + } + } + } + + } + return g_variant_new ("(a{su})", &builder); } @@ -340,10 +383,11 @@ nm_checkpoint_init (NMCheckpoint *self) NMCheckpoint * nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout, - GError **error) + NMCheckpointCreateFlags flags, GError **error) { NMCheckpoint *self; NMCheckpointPrivate *priv; + NMSettingsConnection *const *con; DeviceCheckpoint *dev_checkpoint; NMDevice *device; guint i; @@ -370,6 +414,15 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time priv->rollback_ts = rollback_timeout ? (nm_utils_get_monotonic_timestamp_ms () + ((gint64) rollback_timeout * 1000)) : 0; + priv->flags = flags; + + if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) { + priv->connection_uuids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + for (con = nm_settings_get_connections (nm_settings_get (), NULL); *con; con++) { + g_hash_table_add (priv->connection_uuids, + g_strdup (nm_settings_connection_get_uuid (*con))); + } + } for (i = 0; i < devices->len; i++) { device = (NMDevice *) devices->pdata[i]; @@ -391,6 +444,7 @@ dispose (GObject *object) NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self); g_clear_pointer (&priv->devices, g_hash_table_unref); + g_clear_pointer (&priv->connection_uuids, g_hash_table_unref); G_OBJECT_CLASS (nm_checkpoint_parent_class)->dispose (object); } diff --git a/src/nm-checkpoint.h b/src/nm-checkpoint.h index 815ae7be10..fccf8af3f6 100644 --- a/src/nm-checkpoint.h +++ b/src/nm-checkpoint.h @@ -41,7 +41,7 @@ typedef struct _NMCheckpointClass NMCheckpointClass; GType nm_checkpoint_get_type (void); NMCheckpoint *nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_timeout, - GError **error); + NMCheckpointCreateFlags flags, GError **error); guint64 nm_checkpoint_get_rollback_ts (NMCheckpoint *checkpoint); gboolean nm_checkpoint_includes_device (NMCheckpoint *checkpoint, NMDevice *device);