diff --git a/introspection/org.freedesktop.NetworkManager.Settings.Connection.xml b/introspection/org.freedesktop.NetworkManager.Settings.Connection.xml
index 8bc6ebbd08..a9240ec107 100644
--- a/introspection/org.freedesktop.NetworkManager.Settings.Connection.xml
+++ b/introspection/org.freedesktop.NetworkManager.Settings.Connection.xml
@@ -105,7 +105,7 @@
Update2:
@settings: New connection settings, properties, and (optionally) secrets. Provide an empty array to use the current settings.
@flags: Optional flags. Unknown flags cause the call to fail.
- @args: Optional arguments dictionary, for extentibility. Currently, no arguments are accepted. Specifying unknown keys causes the call to fail.
+ @args: Optional arguments dictionary, for extentibility. Specifying unknown keys causes the call to fail.
@result: Currently no results are returned.
@since: 1.12
@@ -156,6 +156,17 @@
+ The %args argument accepts the following keys:
+
+
+
+ plugin:
+ The settings plugin the connection will be migrated to
+ such as "keyfile" or "ifcfg-rh".
+ Since 1.38
+
+
+
Secrets may be part of the update request, and will be either stored in persistent
storage or sent to a Secret Agent for storage, depending on the flags
associated with each secret.
diff --git a/introspection/org.freedesktop.NetworkManager.Settings.xml b/introspection/org.freedesktop.NetworkManager.Settings.xml
index efdd8294c6..ea271dfaa1 100644
--- a/introspection/org.freedesktop.NetworkManager.Settings.xml
+++ b/introspection/org.freedesktop.NetworkManager.Settings.xml
@@ -67,7 +67,7 @@
AddConnection2:
@settings: New connection settings, properties, and (optionally) secrets.
@flags: Flags. Unknown flags cause the call to fail.
- @args: Optional arguments dictionary, for extentibility. Currently, no arguments are accepted. Specifying unknown keys causes the call to fail.
+ @args: Optional arguments dictionary, for extentibility. Specifying unknown keys causes the call to fail.
@path: Object path of the new connection that was just added.
@result: Output argument, currently no additional results are returned.
@since: 1.20
@@ -100,6 +100,17 @@
+ The %args argument accepts the following keys:
+
+
+
+ plugin:
+ The settings plugin the newly added connection will
+ use, such as "keyfile" or "ifcfg-rh".
+ Since 1.38
+
+
+
Either the flags 0x1 (to-disk) or 0x2 (in-memory) must be specified.
The effect is whether to behave like
AddConnection or
diff --git a/man/nmcli.xml b/man/nmcli.xml
index fabb590f24..d4f27b0b37 100644
--- a/man/nmcli.xml
+++ b/man/nmcli.xml
@@ -659,6 +659,7 @@
load
import
export
+ migrate
ARGUMENTS
@@ -1312,6 +1313,40 @@
data will be printed to standard output.
+
+
+
+ migrate
+
+
+ plugin
+
+
+
+
+
+
+ ID
+
+
+ Migrate connection profiles to a different settings plugin, such
+ as keyfile (default) or ifcfg-rh.
+
+ The connection to be migrated is identified by its name, UUID or D-Bus path.
+ If ID is ambiguous, a keyword ,
+ or can be used. See connection
+ show above for the description of the
+ ID-specifying keywords.
+
+ If no connections are specified, the command acts on all available
+ connections. Therefore, with no arguments, the command migrates all connection
+ profiles to the keyfile plugin.
+
+ If option is not specified, the default timeout will be 10
+ seconds.
+
+
+
diff --git a/src/core/devices/bluetooth/nm-bluez-manager.c b/src/core/devices/bluetooth/nm-bluez-manager.c
index 05f852975a..6bc00faf39 100644
--- a/src/core/devices/bluetooth/nm-bluez-manager.c
+++ b/src/core/devices/bluetooth/nm-bluez-manager.c
@@ -1352,6 +1352,7 @@ _conn_create_panu_connection(NMBluezManager *self, BzDBusObj *bzobj)
bzobj->d_device.address);
nm_settings_add_connection(priv->settings,
+ NULL,
connection,
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index e9bf8afcf9..ea931c6a95 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -3859,6 +3859,7 @@ update_external_connection(NMDevice *self)
if (connection_new) {
nm_settings_connection_update(settings_connection,
+ NULL,
connection_new,
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
diff --git a/src/core/devices/wifi/nm-iwd-manager.c b/src/core/devices/wifi/nm-iwd-manager.c
index 5563ebf8fa..27222aaea8 100644
--- a/src/core/devices/wifi/nm-iwd-manager.c
+++ b/src/core/devices/wifi/nm-iwd-manager.c
@@ -948,6 +948,7 @@ mirror_connection(NMIwdManager *self,
if (!nm_settings_add_connection(
priv->settings,
+ NULL,
connection,
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
diff --git a/src/core/nm-checkpoint.c b/src/core/nm-checkpoint.c
index 5b48f91aa5..1566733289 100644
--- a/src/core/nm-checkpoint.c
+++ b/src/core/nm-checkpoint.c
@@ -231,6 +231,7 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp
persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP;
nm_settings_connection_update(
connection,
+ NULL,
dev_checkpoint->settings_connection,
persist_mode,
sett_flags,
@@ -247,6 +248,7 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp
persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
if (!nm_settings_add_connection(NM_SETTINGS_GET,
+ NULL,
dev_checkpoint->settings_connection,
persist_mode,
NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c
index 88488befa0..046fa819e9 100644
--- a/src/core/nm-manager.c
+++ b/src/core/nm-manager.c
@@ -2939,6 +2939,7 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat
nm_device_assume_state_reset(device);
if (!nm_settings_add_connection(priv->settings,
+ NULL,
connection,
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
@@ -3059,6 +3060,7 @@ recheck_assume_connection(NMManager *self, NMDevice *device)
nm_settings_connection_update(
sett_conn,
+ NULL,
con2,
NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
@@ -5915,6 +5917,7 @@ _add_and_activate_auth_done(NMManager *self,
* shutdown. */
nm_settings_add_connection_dbus(
priv->settings,
+ NULL,
connection,
persist_mode,
NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
diff --git a/src/core/settings/nm-settings-connection.c b/src/core/settings/nm-settings-connection.c
index 55efaebe9f..1638efcd7e 100644
--- a/src/core/settings/nm-settings-connection.c
+++ b/src/core/settings/nm-settings-connection.c
@@ -606,6 +606,7 @@ _secrets_update(NMConnection *connection,
gboolean
nm_settings_connection_update(NMSettingsConnection *self,
+ const char *plugin_name,
NMConnection *new_connection,
NMSettingsConnectionPersistMode persist_mode,
NMSettingsConnectionIntFlags sett_flags,
@@ -618,6 +619,7 @@ nm_settings_connection_update(NMSettingsConnection *self,
return nm_settings_update_connection(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->settings,
self,
+ plugin_name,
new_connection,
persist_mode,
sett_flags,
@@ -835,6 +837,7 @@ nm_settings_connection_new_secrets(NMSettingsConnection *self,
if (!nm_settings_connection_update(
self,
+ NULL,
new_connection ?: connection,
NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
@@ -980,6 +983,7 @@ get_secrets_done_cb(NMAgentManager *manager,
}
if (!nm_settings_connection_update(
self,
+ NULL,
new_connection,
agent_had_system ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
: NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
@@ -1409,6 +1413,7 @@ typedef struct {
NMConnection *new_settings;
NMSettingsUpdate2Flags flags;
char *audit_args;
+ char *plugin_name;
bool is_update2 : 1;
} UpdateInfo;
@@ -1436,6 +1441,7 @@ update_complete(NMSettingsConnection *self, UpdateInfo *info, GError *error)
g_clear_object(&info->agent_mgr);
g_clear_object(&info->new_settings);
g_free(info->audit_args);
+ g_free(info->plugin_name);
g_slice_free(UpdateInfo, info);
}
@@ -1572,6 +1578,7 @@ update_auth_cb(NMSettingsConnection *self,
nm_settings_connection_update(
self,
+ info->plugin_name,
info->new_settings,
persist_mode,
(NM_FLAGS_HAS(info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
@@ -1642,6 +1649,7 @@ settings_connection_update(NMSettingsConnection *self,
gboolean is_update2,
GDBusMethodInvocation *context,
GVariant *new_settings,
+ const char *plugin_name,
NMSettingsUpdate2Flags flags)
{
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
@@ -1696,6 +1704,7 @@ settings_connection_update(NMSettingsConnection *self,
info->subject = subject;
info->flags = flags;
info->new_settings = tmp;
+ info->plugin_name = g_strdup(plugin_name);
permission = get_update_modify_permission(nm_settings_connection_get_connection(self),
tmp ?: nm_settings_connection_get_connection(self));
@@ -1724,7 +1733,12 @@ impl_settings_connection_update(NMDBusObject *obj,
gs_unref_variant GVariant *settings = NULL;
g_variant_get(parameters, "(@a{sa{sv}})", &settings);
- settings_connection_update(self, FALSE, invocation, settings, NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
+ settings_connection_update(self,
+ FALSE,
+ invocation,
+ settings,
+ NULL,
+ NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
}
static void
@@ -1744,6 +1758,7 @@ impl_settings_connection_update_unsaved(NMDBusObject *obj,
FALSE,
invocation,
settings,
+ NULL,
NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY);
}
@@ -1758,7 +1773,12 @@ impl_settings_connection_save(NMDBusObject *obj,
{
NMSettingsConnection *self = NM_SETTINGS_CONNECTION(obj);
- settings_connection_update(self, FALSE, invocation, NULL, NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
+ settings_connection_update(self,
+ FALSE,
+ invocation,
+ NULL,
+ NULL,
+ NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
}
static void
@@ -1770,13 +1790,15 @@ impl_settings_connection_update2(NMDBusObject *obj,
GDBusMethodInvocation *invocation,
GVariant *parameters)
{
- NMSettingsConnection *self = NM_SETTINGS_CONNECTION(obj);
- gs_unref_variant GVariant *settings = NULL;
- gs_unref_variant GVariant *args = NULL;
+ NMSettingsConnection *self = NM_SETTINGS_CONNECTION(obj);
+ gs_unref_variant GVariant *settings = NULL;
+ gs_unref_variant GVariant *args = NULL;
+ gs_free char *plugin_name = NULL;
guint32 flags_u;
GError *error = NULL;
GVariantIter iter;
const char *args_name;
+ GVariant *args_value;
NMSettingsUpdate2Flags flags;
g_variant_get(parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args);
@@ -1812,7 +1834,13 @@ impl_settings_connection_update2(NMDBusObject *obj,
nm_assert(g_variant_is_of_type(args, G_VARIANT_TYPE("a{sv}")));
g_variant_iter_init(&iter, args);
- while (g_variant_iter_next(&iter, "{&sv}", &args_name, NULL)) {
+ while (g_variant_iter_next(&iter, "{&sv}", &args_name, &args_value)) {
+ if (plugin_name == NULL && nm_streq(args_name, "plugin")
+ && g_variant_is_of_type(args_value, G_VARIANT_TYPE_STRING)) {
+ plugin_name = g_variant_dup_string(args_value, NULL);
+ continue;
+ }
+
error = g_error_new(NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
"Unsupported argument '%s'",
@@ -1821,7 +1849,7 @@ impl_settings_connection_update2(NMDBusObject *obj,
return;
}
- settings_connection_update(self, TRUE, invocation, settings, flags);
+ settings_connection_update(self, TRUE, invocation, settings, plugin_name, flags);
}
static void
@@ -2010,6 +2038,7 @@ dbus_clear_secrets_auth_cb(NMSettingsConnection *self,
if (!nm_settings_connection_update(
self,
+ NULL,
connection_cloned,
NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
diff --git a/src/core/settings/nm-settings-connection.h b/src/core/settings/nm-settings-connection.h
index 5f8a22e8a5..893b0d7b74 100644
--- a/src/core/settings/nm-settings-connection.h
+++ b/src/core/settings/nm-settings-connection.h
@@ -240,6 +240,7 @@ nm_settings_connection_has_unmodified_applied_connection(NMSettingsConnection *s
NMSettingCompareFlags compare_flage);
gboolean nm_settings_connection_update(NMSettingsConnection *self,
+ const char *plugin_name,
NMConnection *new_connection,
NMSettingsConnectionPersistMode persist_mode,
NMSettingsConnectionIntFlags sett_flags,
diff --git a/src/core/settings/nm-settings.c b/src/core/settings/nm-settings.c
index 6589291131..4a27f1ef6b 100644
--- a/src/core/settings/nm-settings.c
+++ b/src/core/settings/nm-settings.c
@@ -236,12 +236,16 @@ _sett_conn_entry_get_conn(SettConnEntry *sett_conn_entry)
* update-connection. If this parameter is omitted, then it's about what happens
* when adding a new profile (add-connection).
*
+ * @storage_check_ignore is optional, and if given then it skips this particular
+ * storage.
+ *
* Returns: the conflicting storage or %NULL if there is none.
*/
static NMSettingsStorage *
_sett_conn_entry_storage_find_conflicting_storage(SettConnEntry *sett_conn_entry,
NMSettingsPlugin *target_plugin,
NMSettingsStorage *storage_check_including,
+ NMSettingsStorage *storage_check_ignore,
const GSList *plugins)
{
StorageData *sd;
@@ -269,6 +273,12 @@ _sett_conn_entry_storage_find_conflicting_storage(SettConnEntry *sett_conn_e
continue;
}
+ if (sd->storage == storage_check_ignore) {
+ /* We ignore this one, because we're in the process of
+ * replacing it. */
+ continue;
+ }
+
if (sd->storage == storage_check_including) {
/* ok, the storage is the one we are about to check. All other
* storages are lower priority, so there is no storage that hides
@@ -1463,6 +1473,7 @@ _plugin_connections_reload(NMSettings *self)
static gboolean
_add_connection_to_first_plugin(NMSettings *self,
+ const char *plugin_name,
SettConnEntry *sett_conn_entry,
NMConnection *new_connection,
gboolean in_memory,
@@ -1471,12 +1482,14 @@ _add_connection_to_first_plugin(NMSettings *self,
gboolean shadowed_owned,
NMSettingsStorage **out_new_storage,
NMConnection **out_new_connection,
+ NMSettingsStorage *drop_storage,
GError **error)
{
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self);
gs_free_error GError *first_error = NULL;
GSList *iter;
const char *uuid;
+ gboolean no_plugin = TRUE;
uuid = nm_connection_get_uuid(new_connection);
@@ -1493,12 +1506,18 @@ _add_connection_to_first_plugin(NMSettings *self,
gboolean success;
const char *filename;
+ if (plugin_name && strcmp(plugin_name, nm_settings_plugin_get_plugin_name(plugin))) {
+ /* Not the plugin we're confined to. Ignore. */
+ continue;
+ }
+
if (!in_memory) {
NMSettingsStorage *conflicting_storage;
conflicting_storage = _sett_conn_entry_storage_find_conflicting_storage(sett_conn_entry,
plugin,
NULL,
+ drop_storage,
priv->plugins);
if (conflicting_storage) {
/* we have a connection provided by a plugin with higher priority than the one
@@ -1545,6 +1564,8 @@ _add_connection_to_first_plugin(NMSettings *self,
&add_error);
}
+ no_plugin = FALSE;
+
if (!success) {
_LOGT("add-connection: failed to add %s/'%s': %s",
nm_connection_get_uuid(new_connection),
@@ -1588,8 +1609,18 @@ _add_connection_to_first_plugin(NMSettings *self,
return TRUE;
}
- nm_assert(first_error);
- g_propagate_error(error, g_steal_pointer(&first_error));
+ if (no_plugin) {
+ nm_assert(plugin_name);
+ nm_assert(!first_error);
+ g_set_error(error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
+ "a plugin by the name of '%s' is not available",
+ plugin_name);
+ } else {
+ nm_assert(first_error);
+ g_propagate_error(error, g_steal_pointer(&first_error));
+ }
return FALSE;
}
@@ -1704,6 +1735,7 @@ _set_nmmeta_tombstone(NMSettings *self,
*/
gboolean
nm_settings_add_connection(NMSettings *self,
+ const char *plugin,
NMConnection *connection,
NMSettingsConnectionPersistMode persist_mode,
NMSettingsConnectionAddReason add_reason,
@@ -1800,6 +1832,7 @@ nm_settings_add_connection(NMSettings *self,
sett_conn_entry,
nm_settings_storage_get_plugin(shadowed_storage),
shadowed_storage,
+ NULL,
priv->plugins);
if (conflicting_storage) {
/* We cannot add the profile as @shadowed_storage, because there is another, existing storage
@@ -1824,6 +1857,7 @@ again_add_connection:
if (!update_storage) {
success = _add_connection_to_first_plugin(self,
+ plugin,
sett_conn_entry,
connection,
new_in_memory,
@@ -1832,6 +1866,7 @@ again_add_connection:
FALSE,
&new_storage,
&new_connection,
+ NULL,
&local);
} else {
success = _update_connection_to_plugin(self,
@@ -1948,6 +1983,7 @@ again_delete_tombstone:
gboolean
nm_settings_update_connection(NMSettings *self,
NMSettingsConnection *sett_conn,
+ const char *plugin_name,
NMConnection *connection,
NMSettingsConnectionPersistMode persist_mode,
NMSettingsConnectionIntFlags sett_flags,
@@ -2154,8 +2190,9 @@ nm_settings_update_connection(NMSettings *self,
} else if (nm_settings_storage_is_keyfile_lib(cur_storage)) {
/* the profile is a keyfile in /usr/lib. It cannot be overwritten, we must migrate it
* from /usr/lib to /etc. */
- } else
+ } else {
update_storage = cur_storage;
+ }
if (new_in_memory) {
if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY) {
@@ -2174,8 +2211,22 @@ nm_settings_update_connection(NMSettings *self,
}
}
+ if (update_storage && plugin_name) {
+ NMSettingsPlugin *plugin = nm_settings_storage_get_plugin(update_storage);
+
+ if (strcmp(plugin_name, nm_settings_plugin_get_plugin_name(plugin))) {
+ /* We're updating a connection, we're confined to a particular
+ * plugin, but the connection is currently using a different one.
+ * We need to migrate. Drop the existing storage and look out for
+ * a new one. */
+ drop_storage = update_storage;
+ update_storage = NULL;
+ }
+ }
+
if (!update_storage) {
success = _add_connection_to_first_plugin(self,
+ plugin_name,
sett_conn_entry,
connection,
new_in_memory,
@@ -2184,6 +2235,7 @@ nm_settings_update_connection(NMSettings *self,
new_shadowed_owned,
&new_storage,
&new_connection,
+ drop_storage,
&local);
} else {
success = _update_connection_to_plugin(self,
@@ -2462,6 +2514,7 @@ pk_add_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data
nm_assert(NM_IS_CONNECTION(connection));
nm_settings_add_connection(self,
+ nm_auth_chain_get_data(chain, "plugin"),
connection,
GPOINTER_TO_UINT(nm_auth_chain_get_data(chain, "persist-mode")),
GPOINTER_TO_UINT(nm_auth_chain_get_data(chain, "add-reason")),
@@ -2489,6 +2542,7 @@ pk_add_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data
void
nm_settings_add_connection_dbus(NMSettings *self,
+ const char *plugin,
NMConnection *connection,
NMSettingsConnectionPersistMode persist_mode,
NMSettingsConnectionAddReason add_reason,
@@ -2549,6 +2603,7 @@ nm_settings_add_connection_dbus(NMSettings *self,
nm_auth_chain_set_data(chain, "persist-mode", GUINT_TO_POINTER(persist_mode), NULL);
nm_auth_chain_set_data(chain, "add-reason", GUINT_TO_POINTER(add_reason), NULL);
nm_auth_chain_set_data(chain, "sett-flags", GUINT_TO_POINTER(sett_flags), NULL);
+ nm_auth_chain_set_data(chain, "plugin", g_strdup(plugin), g_free);
nm_auth_chain_add_call_unsafe(chain, perm, TRUE);
return;
@@ -2601,6 +2656,7 @@ settings_add_connection_helper(NMSettings *self,
GDBusMethodInvocation *context,
gboolean is_add_connection_2,
GVariant *settings,
+ const char *plugin,
NMSettingsAddConnection2Flags flags)
{
gs_unref_object NMConnection *connection = NULL;
@@ -2636,6 +2692,7 @@ settings_add_connection_helper(NMSettings *self,
nm_settings_add_connection_dbus(
self,
+ plugin,
connection,
persist_mode,
NM_FLAGS_HAS(flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT)
@@ -2665,6 +2722,7 @@ impl_settings_add_connection(NMDBusObject *obj,
invocation,
FALSE,
settings,
+ NULL,
NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK);
}
@@ -2685,6 +2743,7 @@ impl_settings_add_connection_unsaved(NMDBusObject *obj,
invocation,
FALSE,
settings,
+ NULL,
NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY);
}
@@ -2700,8 +2759,10 @@ impl_settings_add_connection2(NMDBusObject *obj,
NMSettings *self = NM_SETTINGS(obj);
gs_unref_variant GVariant *settings = NULL;
gs_unref_variant GVariant *args = NULL;
+ gs_free char *plugin = NULL;
NMSettingsAddConnection2Flags flags;
const char *args_name;
+ GVariant *args_value;
GVariantIter iter;
guint32 flags_u;
@@ -2745,7 +2806,13 @@ impl_settings_add_connection2(NMDBusObject *obj,
nm_assert(g_variant_is_of_type(args, G_VARIANT_TYPE("a{sv}")));
g_variant_iter_init(&iter, args);
- while (g_variant_iter_next(&iter, "{&sv}", &args_name, NULL)) {
+ while (g_variant_iter_next(&iter, "{&sv}", &args_name, &args_value)) {
+ if (plugin == NULL && nm_streq(args_name, "plugin")
+ && g_variant_is_of_type(args_value, G_VARIANT_TYPE_STRING)) {
+ plugin = g_variant_dup_string(args_value, NULL);
+ continue;
+ }
+
g_dbus_method_invocation_take_error(invocation,
g_error_new(NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
@@ -2754,7 +2821,7 @@ impl_settings_add_connection2(NMDBusObject *obj,
return;
}
- settings_add_connection_helper(self, invocation, TRUE, settings, flags);
+ settings_add_connection_helper(self, invocation, TRUE, settings, plugin, flags);
}
/*****************************************************************************/
@@ -3606,6 +3673,7 @@ device_realized(NMDevice *device, GParamSpec *pspec, NMSettings *self)
nm_device_get_iface(device));
nm_settings_add_connection(self,
+ NULL,
connection,
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
diff --git a/src/core/settings/nm-settings.h b/src/core/settings/nm-settings.h
index 56cebed037..8747d1dcd7 100644
--- a/src/core/settings/nm-settings.h
+++ b/src/core/settings/nm-settings.h
@@ -68,6 +68,7 @@ typedef void (*NMSettingsAddCallback)(NMSettings *settings,
gpointer user_data);
void nm_settings_add_connection_dbus(NMSettings *self,
+ const char *plugin,
NMConnection *connection,
NMSettingsConnectionPersistMode persist_mode,
NMSettingsConnectionAddReason add_reason,
@@ -90,6 +91,7 @@ NMSettingsConnection **nm_settings_get_connections_clone(NMSettings
gpointer sort_data);
gboolean nm_settings_add_connection(NMSettings *settings,
+ const char *plugin,
NMConnection *connection,
NMSettingsConnectionPersistMode persist_mode,
NMSettingsConnectionAddReason add_reason,
@@ -99,6 +101,7 @@ gboolean nm_settings_add_connection(NMSettings *settings,
gboolean nm_settings_update_connection(NMSettings *self,
NMSettingsConnection *sett_conn,
+ const char *plugin_name,
NMConnection *new_connection,
NMSettingsConnectionPersistMode persist_mode,
NMSettingsConnectionIntFlags sett_flags,
diff --git a/src/nmcli/connections.c b/src/nmcli/connections.c
index 06665755be..234ec4d65a 100644
--- a/src/nmcli/connections.c
+++ b/src/nmcli/connections.c
@@ -1231,10 +1231,10 @@ usage_connection_delete(void)
{
g_printerr(_("Usage: nmcli connection delete { ARGUMENTS | help }\n"
"\n"
- "ARGUMENTS := [id | uuid | path] \n"
+ "ARGUMENTS := [id | uuid | path] , ...\n"
"\n"
- "Delete a connection profile.\n"
- "The profile is identified by its name, UUID or D-Bus path.\n\n"));
+ "Delete connection profiles.\n"
+ "The profiles are identified by their name, UUID or D-Bus path.\n\n"));
}
static void
@@ -1294,6 +1294,17 @@ usage_connection_export(void)
"The data are directed to standard output or to a file if a name is given.\n\n"));
}
+static void
+usage_connection_migrate(void)
+{
+ g_printerr(_("Usage: nmcli connection migrate { ARGUMENTS | help }\n"
+ "\n"
+ "ARGUMENTS := [--plugin ] [id | uuid | path] , ...\n"
+ "\n"
+ "Migrate connection profiles to a different settings plugin,\n"
+ "such as \"keyfile\" (default) or \"ifcfg-rh\".\n\n"));
+}
+
static void
quit(void)
{
@@ -9138,8 +9149,11 @@ do_connection_delete(const NMCCommand *cmd, NmCli *nmc, int argc, const char *co
nmc->return_value = error->code;
g_clear_error(&error);
- if (nmc->return_value != NMC_RESULT_ERROR_NOT_FOUND)
+ if (nmc->return_value != NMC_RESULT_ERROR_NOT_FOUND) {
+ g_string_free(invalid_cons, TRUE);
+ invalid_cons = NULL;
goto finish;
+ }
if (!invalid_cons)
invalid_cons = g_string_new(NULL);
@@ -9192,7 +9206,6 @@ finish:
g_string_printf(nmc->return_text,
_("Error: cannot delete unknown connection(s): %s."),
invalid_cons->str);
- nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
}
}
@@ -9645,6 +9658,154 @@ finish:
unlink(path);
}
+static void
+migrate_cb(GObject *obj, GAsyncResult *result, gpointer user_data)
+{
+ ConnectionCbInfo *info = (ConnectionCbInfo *) user_data;
+ NMConnection *connection = NM_CONNECTION(obj);
+ gs_unref_variant GVariant *res = NULL;
+ GError *error = NULL;
+
+ res = nm_remote_connection_update2_finish(NM_REMOTE_CONNECTION(obj), result, &error);
+ if (!res) {
+ g_string_printf(info->nmc->return_text, _("Error: not all connections migrated."));
+ g_printerr(_("Error: Connection migration failed: %s\n"), error->message);
+ g_error_free(error);
+ info->nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ } else {
+ g_print(_("Connection '%s' (%s) successfully migrated.\n"),
+ nm_connection_get_id(connection),
+ nm_connection_get_uuid(connection));
+ }
+ connection_cb_info_finish(info, obj);
+}
+
+static void
+do_connection_migrate(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *argv)
+{
+ NMConnection *connection;
+ ConnectionCbInfo *info = NULL;
+ gs_strfreev char **arg_arr = NULL;
+ const char *const *arg_ptr;
+ guint i;
+ int arg_num;
+ nm_auto_free_gstring GString *invalid_cons = NULL;
+ gs_unref_ptrarray GPtrArray *found_cons = NULL;
+ GError *error = NULL;
+ const char *plugin = "keyfile";
+ const GPtrArray *connections = NULL;
+ int option;
+
+ if (nmc->timeout == -1)
+ nmc->timeout = 10;
+
+ while ((option = next_arg(nmc, &argc, &argv, "--plugin", NULL)) > 0) {
+ switch (option) {
+ case 1: /* --plugin */
+ argc--;
+ argv++;
+ if (!argc) {
+ g_set_error_literal(&error, NMCLI_ERROR, 0, _("'--plugin' argument is missing"));
+ goto finish;
+ }
+ plugin = *argv;
+ break;
+ default:
+ g_return_if_reached();
+ break;
+ }
+ }
+
+ arg_ptr = argv;
+ arg_num = argc;
+ if (argc == 0) {
+ if (nmc->ask) {
+ gs_free char *line = NULL;
+
+ /* nmc_do_cmd() should not call this with argc=0. */
+ g_assert(!nmc->complete);
+
+ line = nmc_readline(&nmc->nmc_config, PROMPT_CONNECTIONS);
+ nmc_string_to_arg_array(line, NULL, TRUE, &arg_arr, &arg_num);
+ arg_ptr = (const char *const *) arg_arr;
+ }
+ }
+
+ while (arg_num > 0) {
+ const char *cur_selector, *cur_value;
+
+ connection =
+ get_connection(nmc, &arg_num, &arg_ptr, &cur_selector, &cur_value, &found_cons, &error);
+ if (!connection) {
+ if (!nmc->complete)
+ g_printerr(_("Error: %s.\n"), error->message);
+ g_string_printf(nmc->return_text, _("Error: not all connections found."));
+ nmc->return_value = error->code;
+ g_clear_error(&error);
+
+ if (nmc->return_value != NMC_RESULT_ERROR_NOT_FOUND) {
+ g_string_free(invalid_cons, TRUE);
+ invalid_cons = NULL;
+ goto finish;
+ }
+
+ if (!invalid_cons)
+ invalid_cons = g_string_new(NULL);
+ if (cur_selector)
+ g_string_append_printf(invalid_cons, "%s '%s', ", cur_selector, cur_value);
+ else
+ g_string_append_printf(invalid_cons, "'%s', ", cur_value);
+ }
+ }
+
+ if (nmc->complete)
+ goto finish;
+
+ if (invalid_cons)
+ goto finish;
+
+ if (!found_cons) {
+ /* No connections specified explicitly? Fine, add all. */
+ found_cons = g_ptr_array_new();
+ connections = nm_client_get_connections(nmc->client);
+ for (i = 0; i < connections->len; i++) {
+ connection = connections->pdata[i];
+ g_ptr_array_add(found_cons, connection);
+ }
+ }
+
+ info = g_slice_new0(ConnectionCbInfo);
+ info->nmc = nmc;
+ info->obj_list = g_ptr_array_sized_new(found_cons->len);
+ for (i = 0; i < found_cons->len; i++) {
+ connection = found_cons->pdata[i];
+ g_ptr_array_add(info->obj_list, g_object_ref(connection));
+ }
+ info->timeout_id = g_timeout_add_seconds(nmc->timeout, connection_op_timeout_cb, info);
+ info->cancellable = g_cancellable_new();
+
+ nmc->nowait_flag = (nmc->timeout == 0);
+ nmc->should_wait++;
+
+ for (i = 0; i < found_cons->len; i++) {
+ nm_remote_connection_update2(NM_REMOTE_CONNECTION(found_cons->pdata[i]),
+ NULL,
+ 0,
+ g_variant_new_parsed("{'plugin': <%s>}", plugin),
+ info->cancellable,
+ migrate_cb,
+ info);
+ }
+
+finish:
+ if (invalid_cons) {
+ g_string_truncate(invalid_cons, invalid_cons->len - 2); /* truncate trailing ", " */
+ g_string_printf(nmc->return_text,
+ _("Error: cannot migrate unknown connection(s): %s."),
+ invalid_cons->str);
+ }
+}
+
static char *
gen_func_connection_names(const char *text, int state)
{
@@ -9752,6 +9913,7 @@ nmc_command_func_connection(const NMCCommand *cmd, NmCli *nmc, int argc, const c
{"clone", do_connection_clone, usage_connection_clone, TRUE, TRUE},
{"import", do_connection_import, usage_connection_import, TRUE, TRUE},
{"export", do_connection_export, usage_connection_export, TRUE, TRUE},
+ {"migrate", do_connection_migrate, usage_connection_migrate, TRUE, TRUE},
{"monitor", do_connection_monitor, usage_connection_monitor, TRUE, TRUE},
{NULL, do_connections_show, usage, TRUE, TRUE},
};