Lubomir Rintel 2022-03-28 15:00:45 +02:00
commit 8dbe6fa0d5
13 changed files with 347 additions and 19 deletions

View file

@ -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 @@
</varlistentry>
</variablelist>
The %args argument accepts the following keys:
<variablelist>
<varlistentry>
<term><literal>plugin</literal>:</term>
<listitem><para>The settings plugin the connection will be migrated to
such as "keyfile" or "ifcfg-rh".</para>
<para role="since">Since 1.38</para></listitem>
</varlistentry>
</variablelist>
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.

View file

@ -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 @@
</varlistentry>
</variablelist>
The %args argument accepts the following keys:
<variablelist>
<varlistentry>
<term><literal>plugin</literal>:</term>
<listitem><para>The settings plugin the newly added connection will
use, such as "keyfile" or "ifcfg-rh".</para>
<para role="since">Since 1.38</para></listitem>
</varlistentry>
</variablelist>
Either the flags 0x1 (to-disk) or 0x2 (in-memory) must be specified.
The effect is whether to behave like
<link linkend="gdbus-method-org-freedesktop-NetworkManager-Settings.AddConnection">AddConnection</link> or

View file

@ -659,6 +659,7 @@
<arg choice='plain'><command>load</command></arg>
<arg choice='plain'><command>import</command></arg>
<arg choice='plain'><command>export</command></arg>
<arg choice='plain'><command>migrate</command></arg>
</group>
<arg rep='repeat'><replaceable>ARGUMENTS</replaceable></arg>
</cmdsynopsis>
@ -1312,6 +1313,40 @@
data will be printed to standard output.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<command>migrate</command>
<arg>
<option>--plugin</option>
<arg choice='plain' rep='repeat'><replaceable>plugin</replaceable></arg>
</arg>
<group>
<arg choice='plain'><option>id</option></arg>
<arg choice='plain'><option>uuid</option></arg>
<arg choice='plain'><option>path</option></arg>
</group>
<arg rep='repeat'><replaceable>ID</replaceable></arg>
</term>
<listitem>
<para>Migrate connection profiles to a different settings plugin, such
as <literal>keyfile</literal> (default) or <literal>ifcfg-rh</literal>.</para>
<para>The connection to be migrated is identified by its name, UUID or D-Bus path.
If <replaceable>ID</replaceable> is ambiguous, a keyword <option>id</option>,
<option>uuid</option> or <option>path</option> can be used. See <command>connection
show</command> above for the description of the
<replaceable>ID</replaceable>-specifying keywords.</para>
<para>If no connections are specified, the command acts on all available
connections. Therefore, with no arguments, the command migrates all connection
profiles to the <literal>keyfile</literal> plugin.</para>
<para>If <option>--wait</option> option is not specified, the default timeout will be 10
seconds.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -1231,10 +1231,10 @@ usage_connection_delete(void)
{
g_printerr(_("Usage: nmcli connection delete { ARGUMENTS | help }\n"
"\n"
"ARGUMENTS := [id | uuid | path] <ID>\n"
"ARGUMENTS := [id | uuid | path] <ID>, ...\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 <plugin>] [id | uuid | path] <ID>, ...\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},
};