about summary refs log tree commit diff
path: root/clients/cli
diff options
context:
space:
mode:
authorSebastien Bacher <seb128@ubuntu.com>2019-08-09 11:09:27 +0200
committerSebastien Bacher <seb128@ubuntu.com>2019-08-09 11:18:39 +0200
commit820498b2158e0b05421ce0922f203a0699927fa8 (patch)
tree3bf6d1089b762535ec5e59738d47ae2d2f54750f /clients/cli
parent1bd9f376796563dfb5016bada66c78b203f41d33 (diff)
parent488dda3930545969f5644b57cfca81e21df031c3 (diff)
Merge remote-tracking branch 'salsa/master'
Diffstat (limited to 'clients/cli')
-rw-r--r--clients/cli/common.c102
-rw-r--r--clients/cli/connections.c378
-rw-r--r--clients/cli/devices.c22
-rw-r--r--clients/cli/general.c4
-rw-r--r--clients/cli/nmcli.c86
-rw-r--r--clients/cli/utils.c4
-rw-r--r--clients/cli/utils.h1
7 files changed, 275 insertions, 322 deletions
diff --git a/clients/cli/common.c b/clients/cli/common.c
index 3c1c315d..2fc8aaf6 100644
--- a/clients/cli/common.c
+++ b/clients/cli/common.c
@@ -425,36 +425,38 @@ nmc_find_connection (const GPtrArray *connections,
                      GPtrArray **out_result,
                      gboolean complete)
 {
-	NMConnection *connection;
+	NMConnection *best_candidate_uuid = NULL;
 	NMConnection *best_candidate = NULL;
+	gs_unref_ptrarray GPtrArray *result_allocated = NULL;
 	GPtrArray *result = out_result ? *out_result : NULL;
+	const guint result_inital_len = result ? result->len : 0u;
 	guint i, j;
 
 	nm_assert (connections);
 	nm_assert (filter_val);
 
 	for (i = 0; i < connections->len; i++) {
-		const char *v, *v_num;
+		gboolean match_by_uuid = FALSE;
+		NMConnection *connection;
+		const char *v;
+		const char *v_num;
 
 		connection = NM_CONNECTION (connections->pdata[i]);
 
-		/* When filter_type is NULL, compare connection ID (filter_val)
-		 * against all types. Otherwise, only compare against the specific
-		 * type. If 'path' filter type is specified, comparison against
-		 * numeric index (in addition to the whole path) is allowed.
-		 */
-		if (NM_IN_STRSET (filter_type, NULL, "id")) {
-			v = nm_connection_get_id (connection);
-			if (complete)
-				nmc_complete_strings (filter_val, v, NULL);
-			if (nm_streq0 (filter_val, v))
-				goto found;
-		}
-
 		if (NM_IN_STRSET (filter_type, NULL, "uuid")) {
 			v = nm_connection_get_uuid (connection);
 			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, v, NULL);
+				nmc_complete_strings (filter_val, v);
+			if (nm_streq0 (filter_val, v)) {
+				match_by_uuid = TRUE;
+				goto found;
+			}
+		}
+
+		if (NM_IN_STRSET (filter_type, NULL, "id")) {
+			v = nm_connection_get_id (connection);
+			if (complete)
+				nmc_complete_strings (filter_val, v);
 			if (nm_streq0 (filter_val, v))
 				goto found;
 		}
@@ -463,7 +465,7 @@ nmc_find_connection (const GPtrArray *connections,
 			v = nm_connection_get_path (connection);
 			v_num = nm_utils_dbus_path_get_last_component (v);
 			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, v, filter_type ? v_num : NULL, NULL);
+				nmc_complete_strings (filter_val, v, (*filter_val ? v_num : NULL));
 			if (   nm_streq0 (filter_val, v)
 			    || (filter_type && nm_streq0 (filter_val, v_num)))
 				goto found;
@@ -472,29 +474,51 @@ nmc_find_connection (const GPtrArray *connections,
 		if (NM_IN_STRSET (filter_type, NULL, "filename")) {
 			v = nm_remote_connection_get_filename (NM_REMOTE_CONNECTION (connections->pdata[i]));
 			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, v, NULL);
+				nmc_complete_strings (filter_val, v);
 			if (nm_streq0 (filter_val, v))
 				goto found;
 		}
 
 		continue;
+
 found:
-		if (!out_result)
-			return connection;
-		if (!best_candidate)
-			best_candidate = connection;
-		if (!result)
-			result = g_ptr_array_new_with_free_func (g_object_unref);
-		for (j = 0; j < result->len; j++) {
-			if (connection == result->pdata[j])
-				break;
+		if (match_by_uuid) {
+			if (   !complete
+			    && !out_result)
+				return connection;
+			best_candidate_uuid = connection;
+		} else {
+			if (!best_candidate)
+				best_candidate = connection;
+		}
+		if (out_result) {
+			gboolean already_tracked = FALSE;
+
+			if (!result) {
+				result_allocated = g_ptr_array_new_with_free_func (g_object_unref);
+				result = result_allocated;
+			} else {
+				for (j = 0; j < result->len; j++) {
+					if (connection == result->pdata[j]) {
+						already_tracked = TRUE;
+						break;
+					}
+				}
+			}
+			if (!already_tracked) {
+				if (match_by_uuid) {
+					/* the profile is matched exactly (by UUID). We prepend it
+					 * to the list of all found profiles. */
+					g_ptr_array_insert (result, result_inital_len, g_object_ref (connection));
+				} else
+					g_ptr_array_add (result, g_object_ref (connection));
+			}
 		}
-		if (j == result->len)
-			g_ptr_array_add (result, g_object_ref (connection));
 	}
 
-	NM_SET_OUT (out_result, result);
-	return best_candidate;
+	if (result_allocated)
+		*out_result = g_steal_pointer (&result_allocated);
+	return best_candidate_uuid ?: best_candidate;
 }
 
 NMActiveConnection *
@@ -525,7 +549,7 @@ nmc_find_active_connection (const GPtrArray *active_cons,
 		if (NM_IN_STRSET (filter_type, NULL, "id")) {
 			v = nm_active_connection_get_id (candidate);
 			if (complete)
-				nmc_complete_strings (filter_val, v, NULL);
+				nmc_complete_strings (filter_val, v);
 			if (nm_streq0 (filter_val, v))
 				goto found;
 		}
@@ -533,7 +557,7 @@ nmc_find_active_connection (const GPtrArray *active_cons,
 		if (NM_IN_STRSET (filter_type, NULL, "uuid")) {
 			v = nm_active_connection_get_uuid (candidate);
 			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, v, NULL);
+				nmc_complete_strings (filter_val, v);
 			if (nm_streq0 (filter_val, v))
 				goto found;
 		}
@@ -542,7 +566,7 @@ nmc_find_active_connection (const GPtrArray *active_cons,
 			v = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL;
 			v_num = nm_utils_dbus_path_get_last_component (v);
 			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, v, filter_type ? v_num : NULL, NULL);
+				nmc_complete_strings (filter_val, v, filter_type ? v_num : NULL);
 			if (   nm_streq0 (filter_val, v)
 			    || (filter_type && nm_streq0 (filter_val, v_num)))
 				goto found;
@@ -551,7 +575,7 @@ nmc_find_active_connection (const GPtrArray *active_cons,
 		if (NM_IN_STRSET (filter_type, NULL, "filename")) {
 			v = nm_remote_connection_get_filename (con);
 			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, v, NULL);
+				nmc_complete_strings (filter_val, v);
 			if (nm_streq0 (filter_val, v))
 				goto found;
 		}
@@ -560,7 +584,7 @@ nmc_find_active_connection (const GPtrArray *active_cons,
 			v = nm_object_get_path (NM_OBJECT (candidate));
 			v_num = nm_utils_dbus_path_get_last_component (v);
 			if (complete && (filter_type || *filter_val))
-				nmc_complete_strings (filter_val, v, filter_type ? v_num : NULL, NULL);
+				nmc_complete_strings (filter_val, v, filter_type ? v_num : NULL);
 			if (   nm_streq0 (filter_val, v)
 			    || (filter_type && nm_streq0 (filter_val, v_num)))
 				goto found;
@@ -1254,9 +1278,9 @@ call_cmd (NmCli *nmc, GSimpleAsyncResult *simple, const NMCCommand *cmd, int arg
 static void
 nmc_complete_help (const char *prefix)
 {
-	nmc_complete_strings (prefix, "help", NULL);
+	nmc_complete_strings (prefix, "help");
 	if (*prefix == '-')
-		nmc_complete_strings (prefix, "-help", "--help", NULL);
+		nmc_complete_strings (prefix, "-help", "--help");
 }
 
 /**
@@ -1395,7 +1419,7 @@ void
 nmc_complete_bool (const char *prefix)
 {
 	nmc_complete_strings (prefix, "true", "yes", "on",
-	                              "false", "no", "off", NULL);
+	                              "false", "no", "off");
 }
 
 /**
diff --git a/clients/cli/connections.c b/clients/cli/connections.c
index 6ee3b49f..e365980e 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". */
@@ -209,7 +251,7 @@ get_ac_device_string (NMActiveConnection *active)
 {
 	GString *dev_str;
 	const GPtrArray *devices;
-	int i;
+	guint i;
 
 	if (!active)
 		return NULL;
@@ -826,7 +868,8 @@ const NmcMetaGenericInfo *const metagen_con_active_vpn[_NMC_GENERIC_INFO_TYPE_CO
                                          NM_SETTING_PROXY_SETTING_NAME"," \
                                          NM_SETTING_TC_CONFIG_SETTING_NAME"," \
                                          NM_SETTING_SRIOV_SETTING_NAME"," \
-                                         NM_SETTING_ETHTOOL_SETTING_NAME
+                                         NM_SETTING_ETHTOOL_SETTING_NAME"," \
+                                         NM_SETTING_OVS_DPDK_SETTING_NAME \
                                          // NM_SETTING_DUMMY_SETTING_NAME
                                          // NM_SETTING_WIMAX_SETTING_NAME
 
@@ -1255,17 +1298,16 @@ got_secrets (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
 	NMRemoteConnection *remote = NM_REMOTE_CONNECTION (source_object);
 	GetSecretsData *data = user_data;
-	GVariant *secrets;
-	GError *error = NULL;
+	gs_unref_variant GVariant *secrets = NULL;
 
 	secrets = nm_remote_connection_get_secrets_finish (remote, res, NULL);
 	if (secrets) {
+		gs_free_error GError *error = NULL;
+
 		if (!nm_connection_update_secrets (data->local, NULL, secrets, &error) && error) {
 			g_printerr (_("Error updating secrets for %s: %s\n"),
 			            data->setting_name, error->message);
-			g_clear_error (&error);
 		}
-		g_variant_unref (secrets);
 	}
 
 	g_main_loop_quit (data->loop);
@@ -1306,7 +1348,7 @@ nmc_connection_profile_details (NMConnection *connection, NmCli *nmc)
 	GError *error = NULL;
 	GArray *print_settings_array;
 	GPtrArray *prop_array = NULL;
-	int i;
+	guint i;
 	char *fields_str;
 	char *fields_all =    NMC_FIELDS_SETTINGS_NAMES_ALL;
 	char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL;
@@ -1872,7 +1914,7 @@ parse_preferred_connection_order (const char *order, GError **error)
 	GArray *order_arr;
 	NmcSortOrder val;
 	gboolean inverse, unique;
-	int i;
+	guint i;
 
 	strv = nm_utils_strsplit_set (order, ":");
 	if (!strv) {
@@ -1945,7 +1987,7 @@ get_connection (NmCli *nmc,
 	}
 
 	if (*argc == 1 && nmc->complete)
-		nmc_complete_strings (**argv, "id", "uuid", "path", "filename", NULL);
+		nmc_complete_strings (**argv, "id", "uuid", "path", "filename");
 
 	if (NM_IN_STRSET (**argv, "id", "uuid", "path", "filename")) {
 		if (*argc == 1) {
@@ -2104,7 +2146,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 			guint i_found_cons;
 
 			if (argc == 1 && nmc->complete)
-				nmc_complete_strings (*argv, "id", "uuid", "path", "filename", "apath", NULL);
+				nmc_complete_strings (*argv, "id", "uuid", "path", "filename", "apath");
 
 			if (NM_IN_STRSET (*argv, "id", "uuid", "path", "filename", "apath")) {
 				selector = *argv;
@@ -2243,7 +2285,7 @@ get_default_active_connection (NmCli *nmc, NMDevice **device)
 	NMDevice *non_default_device = NULL;
 	NMActiveConnection *non_default_ac = NULL;
 	const GPtrArray *connections;
-	int i;
+	guint i;
 
 	g_return_val_if_fail (nmc, NULL);
 	g_return_val_if_fail (device, NULL);
@@ -2302,7 +2344,7 @@ find_device_for_connection (NmCli *nmc,
 {
 	NMSettingConnection *s_con;
 	const char *con_type;
-	int i, j;
+	guint i, j;
 
 	g_return_val_if_fail (nmc, FALSE);
 	g_return_val_if_fail (iface || ap || nsp, FALSE);
@@ -2669,7 +2711,6 @@ parse_passwords (const char *passwd_file, GError **error)
 	if (!passwd_file)
 		return g_steal_pointer (&pwds_hash);
 
-	/* Read the passwords file */
 	if (!g_file_get_contents (passwd_file, &contents, &len, &local_err)) {
 		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
 		             _("failed to read passwd-file '%s': %s"),
@@ -2679,7 +2720,7 @@ parse_passwords (const char *passwd_file, GError **error)
 	}
 
 	strv = nm_utils_strsplit_set (contents, "\r\n");
-	for (iter = strv; *iter; iter++) {
+	for (iter = strv; strv && *iter; iter++) {
 		gs_free char *iter_s = g_strdup (*iter);
 
 		pwd = strchr (iter_s, ':');
@@ -2721,6 +2762,7 @@ parse_passwords (const char *passwd_file, GError **error)
 
 		g_hash_table_insert (pwds_hash, pwd_spec, g_strdup (pwd));
 	}
+
 	return g_steal_pointer (&pwds_hash);
 }
 
@@ -2740,22 +2782,24 @@ nmc_activate_connection (NmCli *nmc,
 	NMDevice *device = NULL;
 	const char *spec_object = NULL;
 	gboolean device_found;
-	GError *local = NULL;
 
 	g_return_val_if_fail (nmc, FALSE);
 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-	if (connection && (ifname || ap || nsp)) {
+	if (   connection
+	    && (   ifname
+	        || ap
+	        || nsp)) {
+		gs_free_error GError *local = NULL;
+
 		device_found = find_device_for_connection (nmc, connection, ifname, ap, nsp, &device, &spec_object, &local);
 
 		/* Virtual connection may not have their interfaces created yet */
 		if (!device_found && !nm_connection_is_virtual (connection)) {
 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_CON_ACTIVATION,
 			             "%s", local->message);
-			g_clear_error (&local);
 			return FALSE;
 		}
-		g_clear_error (&local);
 	} else if (ifname) {
 		device = nm_client_get_device_by_iface (nmc->client, ifname);
 		if (!device) {
@@ -2770,11 +2814,10 @@ nmc_activate_connection (NmCli *nmc,
 	}
 
 	/* Parse passwords given in passwords file */
-	pwds_hash = parse_passwords (pwds, &local);
-	if (local) {
-		g_propagate_error (error, local);
+	pwds_hash = parse_passwords (pwds, error);
+	if (!pwds_hash)
 		return FALSE;
-	}
+
 	if (nmc->pwds_hash)
 		g_hash_table_destroy (nmc->pwds_hash);
 	nmc->pwds_hash = pwds_hash;
@@ -2850,7 +2893,7 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 
 	while (argc > 0) {
 		if (argc == 1 && nmc->complete)
-			nmc_complete_strings (*argv, "ifname", "ap", "passwd-file", NULL);
+			nmc_complete_strings (*argv, "ifname", "ap", "passwd-file");
 
 		if (strcmp (*argv, "ifname") == 0) {
 			argc--;
@@ -3102,7 +3145,7 @@ do_connection_down (NmCli *nmc, int argc, char **argv)
 		const char *selector = NULL;
 
 		if (arg_num == 1 && nmc->complete)
-			nmc_complete_strings (*arg_ptr, "id", "uuid", "path",  "filename", "apath", NULL);
+			nmc_complete_strings (*arg_ptr, "id", "uuid", "path",  "filename", "apath");
 
 		if (NM_IN_STRSET (*arg_ptr, "id", "uuid", "path",  "filename", "apath")) {
 			selector = *arg_ptr;
@@ -3301,7 +3344,7 @@ get_valid_properties_string (const NMMetaSettingValidPartItem *const*array,
 	const NMMetaSettingValidPartItem *const*iter = array;
 	const char *prop_name = NULL;
 	GString *str;
-	int i, j;
+	guint i, j;
 	gboolean full_match = FALSE;
 
 	g_return_val_if_fail (prefix, NULL);
@@ -3546,7 +3589,7 @@ _strip_master_prefix (const char *master, const char *(**func)(NMConnection *))
 	} else if (g_str_has_prefix (master, "id/")) {
 		master = master + strlen ("id/");
 		if (func)
-			 *func = nm_connection_get_id;
+			*func = nm_connection_get_id;
 	}
 	return master;
 }
@@ -3573,7 +3616,7 @@ normalized_master_for_slave (const GPtrArray *connections,
 	NMConnection *connection;
 	NMSettingConnection *s_con;
 	const char *con_type = NULL, *id, *uuid, *ifname;
-	int i;
+	guint i;
 	const char *found_by_id = NULL;
 	const char *out_type_by_id = NULL;
 	const char *out_master = NULL;
@@ -3785,6 +3828,7 @@ _meta_abstract_complete (const NMMetaAbstractInfo *abstract_info, const char *te
 	                                         nmc_meta_environment_arg,
 	                                         &ctx,
 	                                         text,
+	                                         NULL,
 	                                         &values_to_free);
 	if (values)
 		return values_to_free ?: g_strdupv ((char **) values);
@@ -4208,7 +4252,7 @@ set_connection_master (NmCli *nmc, NMConnection *con, const OptionInfo *option,
 
 	if (!value) {
 		g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
-			             _("Error: master is required"));
+		                     _("Error: master is required"));
 		return FALSE;
 	}
 
@@ -4525,11 +4569,12 @@ run_rl_generator (rl_compentry_func_t *generator_func, const char *prefix)
 }
 
 static gboolean
-complete_option (const NMMetaAbstractInfo *abstract_info, const char *prefix, NMConnection *context_connection)
+complete_option (NmCli *nmc, const NMMetaAbstractInfo *abstract_info, const char *prefix, NMConnection *context_connection)
 {
 	const OptionInfo *candidate;
 	const char *const*values;
 	gs_strfreev char **values_to_free = NULL;
+	gboolean complete_filename = FALSE;
 	const NMMetaOperationContext ctx = {
 		.connection = context_connection,
 	};
@@ -4539,7 +4584,12 @@ complete_option (const NMMetaAbstractInfo *abstract_info, const char *prefix, NM
 	                                         nmc_meta_environment_arg,
 	                                         &ctx,
 	                                         prefix,
+	                                         &complete_filename,
 	                                         &values_to_free);
+	if (complete_filename) {
+		nmc->return_value = NMC_RESULT_COMPLETE_FILE;
+		return TRUE;
+	}
 	if (values) {
 		for (; values[0]; values++)
 			g_print ("%s\n", values[0]);
@@ -4556,19 +4606,13 @@ complete_option (const NMMetaAbstractInfo *abstract_info, const char *prefix, NM
 }
 
 static void
-complete_property (const char *setting_name, const char *property, const char *prefix, NMConnection *connection)
+complete_property (NmCli *nmc, const char *setting_name, const char *property, const char *prefix, NMConnection *connection)
 {
 	const NMMetaPropertyInfo *property_info;
 
 	property_info = nm_meta_property_info_find_by_name (setting_name, property);
-	if (property_info) {
-		if (complete_option ((const NMMetaAbstractInfo *) property_info, prefix, connection))
-			return;
-	}
-
-	if (   strcmp (setting_name, NM_SETTING_BLUETOOTH_SETTING_NAME) == 0
-	         && strcmp (property, NM_SETTING_BLUETOOTH_TYPE) == 0)
-		run_rl_generator (gen_func_bt_type, prefix);
+	if (property_info)
+		complete_option (nmc, (const NMMetaAbstractInfo *) property_info, prefix, connection);
 }
 
 /*****************************************************************************/
@@ -4658,8 +4702,10 @@ nmc_read_connection_properties (NmCli *nmc,
 			if (!get_value (&value, argc, argv, option, error))
 				return FALSE;
 
-			if (!*argc && nmc->complete)
-				complete_property (setting, strv[1], value ?: "", connection);
+			if (!*argc && nmc->complete) {
+				complete_property (nmc, setting, strv[1], value ?: "", connection);
+				return TRUE;
+			}
 
 			if (!set_property (nmc->client, connection, setting_name, strv[1], value, modifier, error))
 				return FALSE;
@@ -4740,7 +4786,7 @@ nmc_read_connection_properties (NmCli *nmc,
 				return FALSE;
 
 			if (!*argc && nmc->complete)
-				complete_option (chosen, value ?: "", connection);
+				complete_option (nmc, chosen, value ?: "", connection);
 
 			if (!set_option (nmc, connection, chosen, value, error))
 				return FALSE;
@@ -4751,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 {
@@ -4784,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) {
@@ -4792,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);
 			}
@@ -4804,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)
-{
-	nm_client_add_connection_async (client, connection, persistent,
-	                                NULL, callback, user_data);
+add_connection (NMClient *client,
+                NMConnection *connection,
+                gboolean temporary,
+                GAsyncReadyCallback callback,
+                gpointer 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
@@ -5146,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;
@@ -5291,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;
@@ -5578,7 +5621,7 @@ gen_property_names (const char *text, int state)
 static char *
 gen_compat_devices (const char *text, int state)
 {
-	int i, j = 0;
+	guint i, j = 0;
 	const GPtrArray *devices;
 	const char **compatible_devices;
 	char *ret;
@@ -6084,7 +6127,7 @@ nmcli_editor_tab_completion (const char *text, int start, int end)
 							rl_completion_display_matches_hook = uuid_display_hook;
 							generator_func = gen_vpn_uuids;
 						} else if (   should_complete_property_values (NULL, line, &multi)
-							   && (num == 3 || multi)) {
+						           && (num == 3 || multi)) {
 							generator_func = gen_property_values;
 						} else if (should_complete_boolean (NULL, line) && num == 3)
 							generator_func = gen_func_bool_values;
@@ -6131,7 +6174,7 @@ nmcli_editor_tab_completion (const char *text, int start, int end)
 						rl_completion_display_matches_hook = uuid_display_hook;
 						generator_func = gen_vpn_uuids;
 					} else if (   should_complete_property_values (prompt_tmp, NULL, &multi)
-						   && (num <= 2 || multi)) {
+					           && (num <= 2 || multi)) {
 						generator_func = gen_property_values;
 					} else if (should_complete_boolean (prompt_tmp, NULL) && num <= 2)
 						generator_func = gen_func_bool_values;
@@ -6652,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
@@ -7238,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 };
@@ -7533,18 +7572,19 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 					}
 				} else {
 					gs_free char *prop_name = NULL;
-					GError *tmp_err = NULL;
+					gs_free GError *tmp_err = NULL;
 
 					prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
 					if (prop_name) {
 						if (!nmc_setting_set_property (nmc->client, ss, prop_name, '\0', NULL, &tmp_err)) {
-							g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
+							g_print (_("Error: failed to remove value of '%s': %s\n"),
+							         prop_name,
 							         tmp_err->message);
-							g_clear_error (&tmp_err);
 						}
 					} else {
-						/* If the string is not a property, try it as a setting */
 						NMSetting *s_tmp;
+
+						/* If the string is not a property, try it as a setting */
 						s_tmp = is_setting_valid (connection,
 						                          valid_settings_main,
 						                          valid_settings_slave,
@@ -7552,16 +7592,17 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 						if (s_tmp) {
 							/* Remove setting from the connection */
 							connection_remove_setting (connection, s_tmp);
+
 							/* coverity[copy_paste_error] - suppress Coverity COPY_PASTE_ERROR defect */
 							if (ss == menu_ctx.curr_setting) {
 								/* If we removed the setting we are in, go up */
 								menu_switch_to_level0 (&nmc->nmc_config, &menu_ctx, BASE_PROMPT);
 								nmc_tab_completion.setting = NULL;  /* for TAB completion */
 							}
-						} else
+						} else {
 							g_print (_("Error: %s properties, nor it is a setting name.\n"),
 							         tmp_err->message);
-						g_clear_error (&tmp_err);
+						}
 					}
 				}
 			}
@@ -7621,7 +7662,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 					/* Show description for all properties */
 					print_setting_description (ss);
 				} else {
-					GError *tmp_err = NULL;
+					gs_free_error GError *tmp_err = NULL;
 					gs_free char *prop_name = NULL;
 
 					prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
@@ -7638,11 +7679,11 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 						                          cmd_arg_p);
 						if (s_tmp)
 							print_setting_description (s_tmp);
-						else
+						else {
 							g_print (_("Error: invalid property: %s, "
 							           "neither a valid setting name.\n"),
 							         tmp_err->message);
-						g_clear_error (&tmp_err);
+						}
 					}
 				}
 			}
@@ -7729,19 +7770,21 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 
 			if (   menu_ctx.curr_setting
 			    && (!cmd_arg || strcmp (cmd_arg, "all") != 0)) {
-				GError *tmp_err = NULL;
-				(void) nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err);
+				gs_free_error GError *tmp_err = NULL;
+
+				nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err);
 				g_print (_("Verify setting '%s': %s\n"),
 				         nm_setting_get_name (menu_ctx.curr_setting),
 				         tmp_err ? tmp_err->message : "OK");
-				g_clear_error (&tmp_err);
 			} else {
-				GError *tmp_err = NULL;
-				gboolean valid, modified;
+				gs_free_error GError *tmp_err = NULL;
 				gboolean fixed = TRUE;
+				gboolean modified;
+				gboolean valid;
 
 				valid = nm_connection_verify (connection, &tmp_err);
-				if (!valid && (g_strcmp0 (cmd_arg, "fix") == 0)) {
+				if (   !valid
+				    && nm_streq0 (cmd_arg, "fix")) {
 					/* Try to fix normalizable errors */
 					g_clear_error (&tmp_err);
 					fixed = nm_connection_normalize (connection, NULL, &modified, &tmp_err);
@@ -7750,14 +7793,13 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 				         tmp_err ? tmp_err->message : "OK");
 				if (!fixed)
 					g_print (_("The error cannot be fixed automatically.\n"));
-				g_clear_error (&tmp_err);
 			}
 			break;
 
 		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;
@@ -7766,9 +7808,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;
@@ -7784,21 +7826,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,
@@ -7814,8 +7852,10 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 				while (!nmc_editor_cb_called && !timeout)
 					g_main_context_iteration (NULL, TRUE);
 
-				while (!connection_changed && !timeout)
-					g_main_context_iteration (NULL, TRUE);
+				if (!nmc_editor_error) {
+					while (!connection_changed && !timeout)
+						g_main_context_iteration (NULL, TRUE);
+				}
 
 				if (handler_id)
 					g_signal_handler_disconnect (rem_con, handler_id);
@@ -8025,7 +8065,7 @@ static const char *
 get_ethernet_device_name (NmCli *nmc)
 {
 	const GPtrArray *devices;
-	int i;
+	guint i;
 
 	devices = nm_client_get_devices (nmc->client);
 	for (i = 0; i < devices->len; i++) {
@@ -8171,7 +8211,7 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 
 	next_arg (nmc, &argc, &argv, NULL);
 	if (argc == 1 && nmc->complete)
-		nmc_complete_strings (*argv, "type", "con-name", "id", "uuid", "path",  "filename", NULL);
+		nmc_complete_strings (*argv, "type", "con-name", "id", "uuid", "path",  "filename");
 
 	nmc->return_value = NMC_RESULT_SUCCESS;
 
@@ -8397,35 +8437,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"),
@@ -8433,13 +8465,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 ();
 }
 
@@ -8448,11 +8475,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;
@@ -8493,8 +8518,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: <new name> argument is missing."));
 		NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
@@ -8505,35 +8530,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;
 }
 
@@ -8736,6 +8749,9 @@ do_connection_monitor (NmCli *nmc, int argc, char **argv)
 		}
 	}
 
+	if (nmc->complete)
+		return nmc->return_value;
+
 	for (i = 0; i < connections->len; i++)
 		connection_watch (nmc, connections->pdata[i]);
 
@@ -8745,8 +8761,6 @@ do_connection_monitor (NmCli *nmc, int argc, char **argv)
 		g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_ADDED, G_CALLBACK (connection_added), nmc);
 	}
 
-	if (nmc->complete)
-		return nmc->return_value;
 	g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED, G_CALLBACK (connection_removed), nmc);
 
 	return NMC_RESULT_SUCCESS;
@@ -8819,7 +8833,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;
@@ -8867,7 +8880,7 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 			if (   argc == 1
 			    && nmc->complete) {
 				nmc_complete_strings (*argv, "wireguard");
-				complete_option ((const NMMetaAbstractInfo *) nm_meta_property_info_vpn_service_type,
+				complete_option (nmc, (const NMMetaAbstractInfo *) nm_meta_property_info_vpn_service_type,
 				                 *argv,
 				                 NULL);
 			}
@@ -8936,18 +8949,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;
 }
 
@@ -9068,7 +9076,7 @@ finish:
 static char *
 gen_func_connection_names (const char *text, int state)
 {
-	int i;
+	guint i;
 	const GPtrArray *connections;
 	const char **connection_names;
 	char *ret;
@@ -9091,7 +9099,7 @@ gen_func_connection_names (const char *text, int state)
 static char *
 gen_func_active_connection_names (const char *text, int state)
 {
-	int i;
+	guint i;
 	const GPtrArray *acs;
 	const char **connections;
 	char *ret;
diff --git a/clients/cli/devices.c b/clients/cli/devices.c
index ad3a44c4..c00d3191 100644
--- a/clients/cli/devices.c
+++ b/clients/cli/devices.c
@@ -536,6 +536,9 @@ _metagen_device_detail_wifi_properties_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_AR
 		                                         : N_("no"))
 		                                      : N_("unknown"),
 		                                      get_type);
+	case NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_WIFI_PROPERTIES_MESH:
+		return nmc_meta_generic_get_bool (NM_FLAGS_HAS (wcaps, NM_WIFI_DEVICE_CAP_MESH),
+		                                  get_type);
 	default:
 		break;
 	}
@@ -555,6 +558,7 @@ const NmcMetaGenericInfo *const metagen_device_detail_wifi_properties[_NMC_GENER
 	_METAGEN_DEVICE_DETAIL_WIFI_PROPERTIES (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_WIFI_PROPERTIES_ADHOC, "ADHOC"),
 	_METAGEN_DEVICE_DETAIL_WIFI_PROPERTIES (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_WIFI_PROPERTIES_2GHZ,  "2GHZ"),
 	_METAGEN_DEVICE_DETAIL_WIFI_PROPERTIES (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_WIFI_PROPERTIES_5GHZ,  "5GHZ"),
+	_METAGEN_DEVICE_DETAIL_WIFI_PROPERTIES (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_WIFI_PROPERTIES_MESH,  "MESH"),
 };
 
 /*****************************************************************************/
@@ -1188,6 +1192,7 @@ fill_output_access_point (gpointer data, gpointer user_data)
 	set_val_strc (arr, 3, bssid);
 	set_val_strc (arr, 4, mode == NM_802_11_MODE_ADHOC ? _("Ad-Hoc")
 	                    : mode == NM_802_11_MODE_INFRA ? _("Infra")
+	                    : mode == NM_802_11_MODE_MESH ? _("Mesh")
 	                    : _("N/A"));
 	set_val_str  (arr, 5, channel_str);
 	set_val_str  (arr, 6, freq_str);
@@ -1897,7 +1902,6 @@ create_connect_connection_for_device (AddAndActivateInfo *info)
 	nm_connection_add_setting (connection, NM_SETTING (s_con));
 	g_object_set (s_con,
 	              NM_SETTING_CONNECTION_ID, nm_device_get_iface (info->device),
-	              NM_SETTING_CONNECTION_INTERFACE_NAME, nm_device_get_iface (info->device),
 	              NULL);
 
 	nm_client_add_and_activate_connection_async (info->nmc->client,
@@ -2473,7 +2477,7 @@ do_device_set (NmCli *nmc, int argc, char **argv)
 		gboolean flag;
 
 		if (argc == 1 && nmc->complete)
-			nmc_complete_strings (*argv, "managed", "autoconnect", NULL);
+			nmc_complete_strings (*argv, "managed", "autoconnect");
 
 		if (matches (*argv, "managed")) {
 			argc--;
@@ -2997,7 +3001,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 			}
 			rescan = *argv;
 			if (argc == 1 && nmc->complete)
-				nmc_complete_strings (rescan, "auto", "no", "yes", NULL);
+				nmc_complete_strings (rescan, "auto", "no", "yes");
 			break;
 		default:
 			g_assert_not_reached();
@@ -3179,7 +3183,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 	while (argc > 0) {
 		if (argc == 1 && nmc->complete) {
 			nmc_complete_strings (*argv, "ifname", "bssid", "password", "wep-key-type",
-			                      "name", "private", "hidden", NULL);
+			                      "name", "private", "hidden");
 		}
 
 		if (strcmp (*argv, "ifname") == 0) {
@@ -3229,7 +3233,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 				goto finish;
 			}
 			if (argc == 1 && nmc->complete)
-				nmc_complete_strings (*argv, "key", "phrase", NULL);
+				nmc_complete_strings (*argv, "key", "phrase");
 			if (strcmp (*argv, "key") == 0)
 				wep_passphrase = FALSE;
 			else if (strcmp (*argv, "phrase") == 0)
@@ -3711,7 +3715,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 	while (argc > 0) {
 		if (argc == 1 && nmc->complete) {
 			nmc_complete_strings (*argv, "ifname", "con-name", "ssid", "band",
-			                             "channel", "password", NULL);
+			                             "channel", "password");
 		}
 
 		if (strcmp (*argv, "ifname") == 0) {
@@ -3753,7 +3757,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 			}
 			band = *argv;
 			if (argc == 1 && nmc->complete)
-				nmc_complete_strings (band, "a", "bg", NULL);
+				nmc_complete_strings (band, "a", "bg");
 			if (strcmp (band, "a") && strcmp (band, "bg")) {
 				g_string_printf (nmc->return_text, _("Error: band argument value '%s' is invalid; use 'a' or 'bg'."),
 				                 band);
@@ -3935,7 +3939,7 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv)
 	/* Get the parameters */
 	while (argc > 0) {
 		if (argc == 1 && nmc->complete)
-			nmc_complete_strings (*argv, "ifname", "ssid", NULL);
+			nmc_complete_strings (*argv, "ifname", "ssid");
 
 		if (strcmp (*argv, "ifname") == 0) {
 			if (ifname) {
@@ -4122,7 +4126,7 @@ do_device_lldp_list (NmCli *nmc, int argc, char **argv)
 	next_arg (nmc, &argc, &argv, NULL);
 	while (argc > 0) {
 		if (argc == 1 && nmc->complete)
-			nmc_complete_strings (*argv, "ifname", NULL);
+			nmc_complete_strings (*argv, "ifname");
 
 		if (strcmp (*argv, "ifname") == 0) {
 			argc--;
diff --git a/clients/cli/general.c b/clients/cli/general.c
index d713426b..e2ce639e 100644
--- a/clients/cli/general.c
+++ b/clients/cli/general.c
@@ -683,7 +683,7 @@ do_general_logging (NmCli *nmc, int argc, char **argv)
 
 		do {
 			if (argc == 1 && nmc->complete)
-				nmc_complete_strings (*argv, "level", "domains", NULL);
+				nmc_complete_strings (*argv, "level", "domains");
 
 			if (matches (*argv, "level")) {
 				argc--;
@@ -878,7 +878,7 @@ do_networking_connectivity (NmCli *nmc, int argc, char **argv)
 	next_arg (nmc, &argc, &argv, NULL);
 	if (nmc->complete) {
 		if (argc == 1)
-			nmc_complete_strings (*argv, "check", NULL);
+			nmc_complete_strings (*argv, "check");
 		return nmc->return_value;
 	}
 
diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c
index d7bc2a46..ad45f179 100644
--- a/clients/cli/nmcli.c
+++ b/clients/cli/nmcli.c
@@ -732,7 +732,7 @@ process_command_line (NmCli *nmc, int argc, char **argv)
 			nmc_complete_strings (argv[0], "--terse", "--pretty", "--mode", "--overview",
 			                               "--colors", "--escape",
 			                               "--fields", "--nocheck", "--get-values",
-			                               "--wait", "--version", "--help", NULL);
+			                               "--wait", "--version", "--help");
 		}
 
 		if (argv[0][1] == '-' && argv[0][2] == '\0') {
@@ -915,88 +915,6 @@ signal_handler (gpointer user_data)
 	return G_SOURCE_CONTINUE;
 }
 
-static void
-nmc_convert_strv_to_string (const GValue *src_value, GValue *dest_value)
-{
-	char **strings;
-
-	strings = g_value_get_boxed (src_value);
-	if (strings)
-		g_value_take_string (dest_value, g_strjoinv (",", strings));
-	else
-		g_value_set_string (dest_value, "");
-}
-
-static void
-nmc_convert_string_hash_to_string (const GValue *src_value, GValue *dest_value)
-{
-	GHashTable *hash;
-	GHashTableIter iter;
-	const char *key, *value;
-	GString *string;
-
-	hash = (GHashTable *) g_value_get_boxed (src_value);
-
-	string = g_string_new (NULL);
-	if (hash) {
-		g_hash_table_iter_init (&iter, hash);
-		while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) {
-			if (string->len)
-				g_string_append_c (string, ',');
-			g_string_append_printf (string, "%s=%s", key, value);
-		}
-	}
-
-	g_value_take_string (dest_value, g_string_free (string, FALSE));
-}
-
-static void
-nmc_convert_bytes_to_string (const GValue *src_value, GValue *dest_value)
-{
-	GBytes *bytes;
-	const guint8 *array;
-	gsize length;
-	GString *printable;
-	guint i = 0;
-
-	bytes = g_value_get_boxed (src_value);
-
-	printable = g_string_new ("[");
-
-	if (bytes) {
-		array = g_bytes_get_data (bytes, &length);
-		while (i < MIN (length, 35)) {
-			if (i > 0)
-				g_string_append_c (printable, ' ');
-			g_string_append_printf (printable, "0x%02X", array[i++]);
-		}
-		if (i < length)
-			g_string_append (printable, " ... ");
-	}
-	g_string_append_c (printable, ']');
-
-	g_value_take_string (dest_value, g_string_free (printable, FALSE));
-}
-
-static void
-nmc_value_transforms_register (void)
-{
-	g_value_register_transform_func (G_TYPE_STRV,
-	                                 G_TYPE_STRING,
-	                                 nmc_convert_strv_to_string);
-
-	/* This depends on the fact that all of the hash-table-valued properties
-	 * in libnm-core are string->string.
-	 */
-	g_value_register_transform_func (G_TYPE_HASH_TABLE,
-	                                 G_TYPE_STRING,
-	                                 nmc_convert_string_hash_to_string);
-
-	g_value_register_transform_func (G_TYPE_BYTES,
-	                                 G_TYPE_STRING,
-	                                 nmc_convert_bytes_to_string);
-}
-
 void
 nm_cli_spawn_pager (NmCli *nmc)
 {
@@ -1054,8 +972,6 @@ main (int argc, char *argv[])
 	/* Save terminal settings */
 	tcgetattr (STDIN_FILENO, &termios_orig);
 
-	nmc_value_transforms_register ();
-
 	nm_cli.return_text = g_string_new (_("Success"));
 	loop = g_main_loop_new (NULL, FALSE);
 
diff --git a/clients/cli/utils.c b/clients/cli/utils.c
index a8b81279..a822dd89 100644
--- a/clients/cli/utils.c
+++ b/clients/cli/utils.c
@@ -189,10 +189,10 @@ next_arg (NmCli *nmc, int *argc, char ***argv, ...)
 
 		if (nmc && nmc->complete && *argc == 1) {
 			while ((cmd_option = va_arg (args, const char *)))
-				nmc_complete_strings (**argv, cmd_option, NULL);
+				nmc_complete_strings (**argv, cmd_option);
 
 			if (***argv == '-')
-				nmc_complete_strings (**argv, "--ask", "--show-secrets", NULL);
+				nmc_complete_strings (**argv, "--ask", "--show-secrets");
 
 			va_end (args);
 			return 0;
diff --git a/clients/cli/utils.h b/clients/cli/utils.h
index b84b35bf..82b186b7 100644
--- a/clients/cli/utils.h
+++ b/clients/cli/utils.h
@@ -225,6 +225,7 @@ typedef enum {
 	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_WIFI_PROPERTIES_ADHOC,
 	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_WIFI_PROPERTIES_2GHZ,
 	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_WIFI_PROPERTIES_5GHZ,
+	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_WIFI_PROPERTIES_MESH,
 	_NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_WIFI_PROPERTIES_NUM,
 
 } NmcGenericInfoType;