diff --git a/Makefile.examples b/Makefile.examples index 92923a8db6..dcd1ac9d18 100644 --- a/Makefile.examples +++ b/Makefile.examples @@ -176,6 +176,7 @@ EXTRA_DIST += \ examples/python/gi/get-active-connections.py \ examples/python/gi/get_ips.py \ examples/python/gi/list-connections.py \ + examples/python/gi/nm-add-connection2.py \ examples/python/gi/nm-connection-update-stable-id.py \ examples/python/gi/nm-wg-set \ examples/python/gi/setting-user-data.py \ diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 25622c7d5d..53105f957f 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -41,6 +41,8 @@ #include "devices.h" #include "polkit-agent.h" +/*****************************************************************************/ + typedef enum { PROPERTY_INF_FLAG_NONE = 0x0, PROPERTY_INF_FLAG_DISABLED = 0x1, /* Don't ask due to runtime decision. */ @@ -50,14 +52,13 @@ typedef enum { typedef char *(*CompEntryFunc) (const char *, int); -typedef struct _OptionInfo OptionInfo; -struct _OptionInfo { +typedef struct _OptionInfo { const NMMetaSettingInfoEditor *setting_info; const char *property; const char *option; - gboolean (*check_and_set)(NmCli *nmc, NMConnection *connection, const OptionInfo *option, const char *value, GError **error); + gboolean (*check_and_set)(NmCli *nmc, NMConnection *connection, const struct _OptionInfo *option, const char *value, GError **error); CompEntryFunc generator_func; -}; +} OptionInfo; /* define some prompts for connection editor */ #define EDITOR_PROMPT_SETTING _("Setting name? ") @@ -96,6 +97,47 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (vpn_connection_state_to_string, NMVpnConnecti NM_UTILS_LOOKUP_ITEM_IGNORE (NM_VPN_CONNECTION_STATE_UNKNOWN), ) +/*****************************************************************************/ + +typedef struct { + NmCli *nmc; + char *orig_id; + char *orig_uuid; + char *new_id; +} AddConnectionInfo; + +static AddConnectionInfo * +_add_connection_info_new (NmCli *nmc, + NMConnection *orig_connection, + NMConnection *new_connection) +{ + AddConnectionInfo *info; + + info = g_slice_new (AddConnectionInfo); + *info = (AddConnectionInfo) { + .nmc = nmc, + .orig_id = orig_connection ? g_strdup (nm_connection_get_id (orig_connection)) : NULL, + .orig_uuid = orig_connection ? g_strdup (nm_connection_get_uuid (orig_connection)) : NULL, + .new_id = g_strdup (nm_connection_get_id (new_connection)), + }; + return info; +} + +static void +_add_connection_info_free (AddConnectionInfo *info) +{ + g_free (info->orig_id); + g_free (info->orig_uuid); + g_free (info->new_id); + nm_g_slice_free (info); +} + +NM_AUTO_DEFINE_FCN (AddConnectionInfo *, _nm_auto_free_add_connection_info, _add_connection_info_free) + +#define nm_auto_free_add_connection_info nm_auto (_nm_auto_free_add_connection_info) + +/*****************************************************************************/ + /* Essentially a version of nm_setting_connection_get_connection_type() that * prefers an alias instead of the settings name when in pretty print mode. * That is so that we print "wifi" instead of "802-11-wireless" in "nmcli c". */ @@ -4755,28 +4797,23 @@ nmc_read_connection_properties (NmCli *nmc, return TRUE; } -typedef struct { - NmCli *nmc; - char *con_name; -} AddConnectionInfo; - static void add_connection_cb (GObject *client, GAsyncResult *result, gpointer user_data) { - AddConnectionInfo *info = (AddConnectionInfo *) user_data; + nm_auto_free_add_connection_info AddConnectionInfo *info = user_data; NmCli *nmc = info->nmc; NMRemoteConnection *connection; GError *error = NULL; const GPtrArray *connections; guint i, found; - connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error); + connection = nm_client_add_connection2_finish (NM_CLIENT (client), result, NULL, &error); if (error) { g_string_printf (nmc->return_text, _("Error: Failed to add '%s' connection: %s"), - info->con_name, error->message); + info->new_id, error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; } else { @@ -4788,7 +4825,7 @@ add_connection_cb (GObject *client, if ((NMConnection *) connection == candidate) continue; - if (nm_streq0 (nm_connection_get_id (candidate), info->con_name)) + if (nm_streq0 (nm_connection_get_id (candidate), info->new_id)) found++; } if (found > 0) { @@ -4796,7 +4833,7 @@ add_connection_cb (GObject *client, "Warning: There is another connection with the name '%1$s'. Reference the connection by its uuid '%2$s'\n", "Warning: There are %3$u other connections with the name '%1$s'. Reference the connection by its uuid '%2$s'\n", found), - info->con_name, + info->new_id, nm_connection_get_uuid (NM_CONNECTION (connection)), found); } @@ -4808,30 +4845,39 @@ add_connection_cb (GObject *client, g_object_unref (connection); } - g_free (info->con_name); - g_free (info); quit (); } static void -add_new_connection (gboolean persistent, - NMClient *client, - NMConnection *connection, - GAsyncReadyCallback callback, - gpointer user_data) +add_connection (NMClient *client, + NMConnection *connection, + gboolean temporary, + GAsyncReadyCallback callback, + gpointer user_data) { - nm_client_add_connection_async (client, connection, persistent, - NULL, callback, user_data); + nm_client_add_connection2 (client, + nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL), + temporary + ? NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY + : NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK, + NULL, + TRUE, + NULL, + callback, + user_data); } static void -update_connection (gboolean persistent, - NMRemoteConnection *connection, +update_connection (NMRemoteConnection *connection, + gboolean temporary, GAsyncReadyCallback callback, gpointer user_data) { - nm_remote_connection_commit_changes_async (connection, persistent, - NULL, callback, user_data); + nm_remote_connection_commit_changes_async (connection, + !temporary, + NULL, + callback, + user_data); } static gboolean @@ -5150,7 +5196,6 @@ do_connection_add (NmCli *nmc, int argc, char **argv) gs_unref_object NMConnection *connection = NULL; NMSettingConnection *s_con; gs_free_error GError *error = NULL; - AddConnectionInfo *info = NULL; gboolean save_bool = TRUE; gboolean seen_dash_dash = FALSE; NMMetaSettingType s; @@ -5295,19 +5340,13 @@ read_properties: } } + add_connection (nmc->client, + connection, + !save_bool, + add_connection_cb, + _add_connection_info_new (nmc, NULL, connection)); nmc->should_wait++; - info = g_malloc0 (sizeof (AddConnectionInfo)); - info->nmc = nmc; - info->con_name = g_strdup (nm_connection_get_id (connection)); - - /* Tell the settings service to add the new connection */ - add_new_connection (save_bool, - nmc->client, - connection, - add_connection_cb, - info); - finish: reset_options (); return nmc->return_value; @@ -6656,14 +6695,11 @@ add_connection_editor_cb (GObject *client, GAsyncResult *result, gpointer user_data) { - NMRemoteConnection *connection; - GError *error = NULL; + gs_unref_object NMRemoteConnection *connection = NULL; + gs_free_error GError *error = NULL; - connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error); + connection = nm_client_add_connection2_finish (NM_CLIENT (client), result, NULL, &error); set_info_and_signal_editor_thread (error, NULL); - - g_clear_object (&connection); - g_clear_error (&error); } static void @@ -7242,7 +7278,6 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t const NMMetaSettingValidPartItem *const*valid_settings_slave; gs_free char *valid_settings_str = NULL; const char *s_type = NULL; - AddConnectionInfo *info = NULL; gboolean temp_changes; GError *err1 = NULL; NmcEditorMenuContext menu_ctx = { 0 }; @@ -7761,7 +7796,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t case NMC_EDITOR_MAIN_CMD_SAVE: /* Save the connection */ if (nm_connection_verify (connection, &err1)) { - gboolean persistent = TRUE; + gboolean temporary = FALSE; gboolean connection_changed; nm_auto_unref_gsource GSource *source = NULL; gboolean timeout = FALSE; @@ -7770,9 +7805,9 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t /* parse argument */ if (cmd_arg) { if (matches (cmd_arg, "temporary")) - persistent = FALSE; + temporary = TRUE; else if (matches (cmd_arg, "persistent")) - persistent = TRUE; + temporary = FALSE; else { g_print (_("Error: invalid argument '%s'\n"), cmd_arg); break; @@ -7788,21 +7823,17 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t } if (!rem_con) { - /* Tell the settings service to add the new connection */ - info = g_malloc0 (sizeof (AddConnectionInfo)); - info->nmc = nmc; - info->con_name = g_strdup (nm_connection_get_id (connection)); - add_new_connection (persistent, - nmc->client, - connection, - add_connection_editor_cb, - info); + add_connection (nmc->client, + connection, + temporary, + add_connection_editor_cb, + NULL); connection_changed = TRUE; } else { /* Save/update already saved (existing) connection */ nm_connection_replace_settings_from_connection (NM_CONNECTION (rem_con), connection); - update_connection (persistent, rem_con, update_connection_editor_cb, NULL); + update_connection (rem_con, temporary, update_connection_editor_cb, NULL); handler_id = g_signal_connect (rem_con, NM_CONNECTION_CHANGED, @@ -8403,35 +8434,27 @@ do_connection_modify (NmCli *nmc, if (nmc->complete) return nmc->return_value; - update_connection (!temporary, rc, modify_connection_cb, nmc); + update_connection (rc, temporary, modify_connection_cb, nmc); nmc->should_wait++; return nmc->return_value; } -typedef struct { - NmCli *nmc; - char *orig_id; - char *orig_uuid; - char *con_id; -} CloneConnectionInfo; - static void clone_connection_cb (GObject *client, GAsyncResult *result, gpointer user_data) { - CloneConnectionInfo *info = (CloneConnectionInfo *) user_data; + nm_auto_free_add_connection_info AddConnectionInfo *info = user_data; NmCli *nmc = info->nmc; - NMRemoteConnection *connection; - GError *error = NULL; + gs_unref_object NMRemoteConnection *connection = NULL; + gs_free_error GError *error = NULL; - connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error); + connection = nm_client_add_connection2_finish (NM_CLIENT (client), result, NULL, &error); if (error) { g_string_printf (nmc->return_text, _("Error: Failed to add '%s' connection: %s"), - info->con_id, error->message); - g_error_free (error); + info->new_id, error->message); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; } else { g_print (_("%s (%s) cloned as %s (%s).\n"), @@ -8439,13 +8462,8 @@ clone_connection_cb (GObject *client, info->orig_uuid, nm_connection_get_id (NM_CONNECTION (connection)), nm_connection_get_uuid (NM_CONNECTION (connection))); - g_object_unref (connection); } - g_free (info->con_id); - g_free (info->orig_id); - g_free (info->orig_uuid); - g_slice_free (CloneConnectionInfo, info); quit (); } @@ -8454,11 +8472,9 @@ do_connection_clone (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; gs_unref_object NMConnection *new_connection = NULL; - NMSettingConnection *s_con; - CloneConnectionInfo *info; const char *new_name; - gs_free char *new_name_ask = NULL; - char *uuid; + gs_free char *new_name_free = NULL; + gs_free char *uuid = NULL; gboolean temporary = FALSE; char **arg_arr = NULL; int arg_num; @@ -8499,8 +8515,8 @@ do_connection_clone (NmCli *nmc, int argc, char **argv) if (argv[0]) new_name = *argv; else if (nmc->ask) { - new_name = new_name_ask = nmc_readline (&nmc->nmc_config, - _("New connection name: ")); + new_name = new_name_free = nmc_readline (&nmc->nmc_config, + _("New connection name: ")); } else { g_string_printf (nmc->return_text, _("Error: argument is missing.")); NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT); @@ -8511,35 +8527,23 @@ do_connection_clone (NmCli *nmc, int argc, char **argv) NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT); } - /* Copy the connection */ new_connection = nm_simple_connection_new_clone (connection); - s_con = nm_connection_get_setting_connection (new_connection); - g_assert (s_con); uuid = nm_utils_uuid_generate (); - g_object_set (s_con, + g_object_set (nm_connection_get_setting_connection (new_connection), NM_SETTING_CONNECTION_ID, new_name, NM_SETTING_CONNECTION_UUID, uuid, NULL); - g_free (uuid); - /* Merge secrets into the new connection */ update_secrets_in_connection (NM_REMOTE_CONNECTION (connection), new_connection); - info = g_slice_new0 (CloneConnectionInfo); - info->nmc = nmc; - info->orig_id = g_strdup (nm_connection_get_id (connection)); - info->orig_uuid = g_strdup (nm_connection_get_uuid (connection)); - info->con_id = g_strdup (nm_connection_get_id (new_connection)); - - /* Add the new cloned connection to NetworkManager */ - add_new_connection (!temporary, - nmc->client, - new_connection, - clone_connection_cb, - info); - + add_connection (nmc->client, + new_connection, + temporary, + clone_connection_cb, + _add_connection_info_new (nmc, connection, new_connection)); nmc->should_wait++; + return nmc->return_value; } @@ -8826,7 +8830,6 @@ do_connection_import (NmCli *nmc, int argc, char **argv) const char *type = NULL, *filename = NULL; gs_free char *type_ask = NULL; gs_free char *filename_ask = NULL; - AddConnectionInfo *info; gs_unref_object NMConnection *connection = NULL; NMVpnEditorPlugin *plugin; gs_free char *service_type = NULL; @@ -8943,18 +8946,13 @@ do_connection_import (NmCli *nmc, int argc, char **argv) NMC_RETURN (nmc, NMC_RESULT_ERROR_UNKNOWN); } - info = g_malloc0 (sizeof (AddConnectionInfo)); - info->nmc = nmc; - info->con_name = g_strdup (nm_connection_get_id (connection)); - - /* Add the new imported connection to NetworkManager */ - add_new_connection (!temporary, - nmc->client, - connection, - add_connection_cb, - info); - + add_connection (nmc->client, + connection, + temporary, + add_connection_cb, + _add_connection_info_new (nmc, NULL, connection)); nmc->should_wait++; + return nmc->return_value; } diff --git a/examples/python/gi/nm-add-connection2.py b/examples/python/gi/nm-add-connection2.py new file mode 100755 index 0000000000..b78016dba4 --- /dev/null +++ b/examples/python/gi/nm-add-connection2.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Copyright 2019 Red Hat, Inc. +# + +import sys +import re + +import gi +gi.require_version('NM', '1.0') +from gi.repository import GLib, NM + +def find_connections(nm_client, arg_type, arg_id): + for c in nm_client.get_connections(): + if arg_type in [None, 'id'] and c.get_id() == arg_id: + yield c + if arg_type in [None, 'uuid'] and c.get_uuid() == arg_id: + yield c + +def find_connection_first(nm_client, arg_type, arg_id): + for f in find_connections(nm_client, arg_type, arg_id): + return f + +def con_to_str(con): + s_con = con.get_setting_connection() + return '"%s" (%s)' % (s_con.get_id(), s_con.get_uuid()) + +def usage(): + print('Usage: %s --clone [[id] ]' % (sys.argv[0])) + print(' %s --clone [[uuid] ]' % (sys.argv[0])) + return 1 + +def die(msg, print_usage=False): + print(msg) + if print_usage: + usage() + sys.exit(1) + +def main(): + + main_loop = GLib.MainLoop() + + nm_client = NM.Client.new(None) + + arg_mode = None + arg_block_autoconnect = NM.SettingsAddConnection2Flags.NONE + arg_id = None + arg_uuid = None + + cons = [] + + argv = list(sys.argv[1:]) + while argv: + if argv[0] == '--clone': + match_type = None + if len(argv) < 2: + die('missing argument for --clone option') + if argv[0] in ['id', 'uuid']: + match_type = argv[0] + if len(argv) < 3: + die('missing argument for "--clone %s" option' % (match_type)) + argv = argv[1:] + if cons: + die('cannot specify --clone argument more than once') + cons.extend(find_connections(nm_client, match_type, argv[1])) + if len(cons) == 0: + die('could not find connection for "--clone %s%s"' % ((match_type or ''), argv[1])) + if len(cons) != 1: + die('could not find unique connection for "--clone %s%s"' % ((match_type or ''), argv[1])) + argv = argv[2:] + continue + if argv[0] in ['--block-autoconnect']: + arg_block_autoconnect = NM.SettingsAddConnection2Flags.BLOCK_AUTOCONNECT + argv = argv[1:] + continue + if argv[0] in ['--to-disk', '--in-memory']: + if argv[0] == '--to-disk': + v = NM.SettingsAddConnection2Flags.TO_DISK + elif argv[0] == '--in-memory': + v = NM.SettingsAddConnection2Flags.IN_MEMORY + else: + assert(False) + if arg_mode is not None: + die('duplicate storage modes ("%s")' % (argv[0])) + arg_mode = v + argv = argv[1:] + continue + if argv[0] in ['--id']: + if len(argv) < 2: + die('missing argument for --id option') + arg_id = argv[1] + argv = argv[2:] + continue + if argv[0] in ['--uuid']: + if len(argv) < 2: + die('missing argument for --uuid option') + arg_uuid = argv[1] + argv = argv[2:] + continue + die('unknown argument "%s"' % (argv[0])) + + if len(cons) != 1: + die('missing --clone argument', True) + + con = cons[0] + + con2 = NM.SimpleConnection.new_clone(con) + + s_con = con2.get_setting_connection() + if arg_id: + s_con.set_property(NM.SETTING_CONNECTION_ID, arg_id) + s_con.set_property(NM.SETTING_CONNECTION_UUID, arg_uuid or NM.utils_uuid_generate()) + + result = {} + def _add_connection2_cb(cl, async_result, user_data): + try: + c, r = nm_client.add_connection2_finish(async_result) + except Exception as e: + result['error'] = e + else: + result['result'] = r + result['connection'] = c + main_loop.quit() + + nm_client.add_connection2(con2.to_dbus(NM.ConnectionSerializationFlags.ALL), + (arg_mode if arg_mode is not None else NM.SettingsAddConnection2Flags.TO_DISK) + | arg_block_autoconnect, + None, + False, + None, + _add_connection2_cb, + None) + + main_loop.run() + + if 'error' in result: + die('update connection %s failed [%s]: %s' % (con_to_str(con2), ' '.join(sys.argv), result['error'])) + + print('update connection %s succeeded [%s]: %s, %s' % (con_to_str(con2), ' '.join(sys.argv), result['connection'].get_path(), result['result'])) + +if __name__ == '__main__': + main() diff --git a/introspection/org.freedesktop.NetworkManager.Settings.xml b/introspection/org.freedesktop.NetworkManager.Settings.xml index 73da345ceb..f7be2717b8 100644 --- a/introspection/org.freedesktop.NetworkManager.Settings.xml +++ b/introspection/org.freedesktop.NetworkManager.Settings.xml @@ -63,6 +63,41 @@ + + + + + + + + +