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);