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}, };