about summary refs log tree commit diff
path: root/clients/cli
diff options
context:
space:
mode:
Diffstat (limited to 'clients/cli')
-rw-r--r--clients/cli/agent.c18
-rw-r--r--clients/cli/common.c60
-rw-r--r--clients/cli/common.h11
-rw-r--r--clients/cli/connections.c479
-rw-r--r--clients/cli/connections.h2
-rw-r--r--clients/cli/devices.c543
-rw-r--r--clients/cli/general.c46
-rw-r--r--clients/cli/meson.build8
-rw-r--r--clients/cli/nmcli.c34
-rw-r--r--clients/cli/nmcli.h2
-rw-r--r--clients/cli/polkit-agent.c15
-rw-r--r--clients/cli/settings.c17
-rw-r--r--clients/cli/settings.h3
-rw-r--r--clients/cli/utils.c33
-rw-r--r--clients/cli/utils.h6
15 files changed, 765 insertions, 512 deletions
diff --git a/clients/cli/agent.c b/clients/cli/agent.c
index bbfe47fb..05cc5dca 100644
--- a/clients/cli/agent.c
+++ b/clients/cli/agent.c
@@ -85,7 +85,8 @@ set_deftext (void)
 }
 
 static gboolean
-get_secrets_from_user (const char *request_id,
+get_secrets_from_user (const NmcConfig *nmc_config,
+                       const char *request_id,
                        const char *title,
                        const char *msg,
                        GPtrArray *secrets)
@@ -104,7 +105,7 @@ get_secrets_from_user (const char *request_id,
 			rl_startup_hook = set_deftext;
 			pre_input_deftext = g_strdup (secret->value);
 		}
-		pwd = nmc_readline ("%s (%s): ", secret->pretty_name, secret->entry_id);
+		pwd = nmc_readline (nmc_config, "%s (%s): ", secret->pretty_name, secret->entry_id);
 
 		/* No password provided, cancel the secrets. */
 		if (!pwd)
@@ -123,17 +124,16 @@ secrets_requested (NMSecretAgentSimple *agent,
                    GPtrArray           *secrets,
                    gpointer             user_data)
 {
-	NmCli *nmc = (NmCli *) user_data;
-	gboolean success = FALSE;
+	NmCli *nmc = user_data;
+	gboolean success;
 
 	if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
 		nmc_terminal_erase_line ();
 
-	success = get_secrets_from_user (request_id, title, msg, secrets);
-	if (success)
-		nm_secret_agent_simple_response (agent, request_id, secrets);
-	else
-		nm_secret_agent_simple_response (agent, request_id, NULL);
+	success = get_secrets_from_user (&nmc->nmc_config, request_id, title, msg, secrets);
+	nm_secret_agent_simple_response (agent,
+	                                 request_id,
+	                                 success ? secrets : NULL);
 }
 
 static NMCResultCode
diff --git a/clients/cli/common.c b/clients/cli/common.c
index d3f5f865..85b5005e 100644
--- a/clients/cli/common.c
+++ b/clients/cli/common.c
@@ -661,12 +661,12 @@ vpn_openconnect_get_secrets (NMConnection *connection, GPtrArray *secrets)
 }
 
 static gboolean
-get_secrets_from_user (const char *request_id,
+get_secrets_from_user (const NmcConfig *nmc_config,
+                       const char *request_id,
                        const char *title,
                        const char *msg,
                        NMConnection *connection,
                        gboolean ask,
-                       gboolean echo_on,
                        GHashTable *pwds_hash,
                        GPtrArray *secrets)
 {
@@ -698,8 +698,9 @@ get_secrets_from_user (const char *request_id,
 				}
 				if (msg)
 					g_print ("%s\n", msg);
-				pwd = nmc_readline_echo (secret->is_secret
-				                         ? echo_on
+				pwd = nmc_readline_echo (nmc_config,
+				                         secret->is_secret
+				                         ? nmc_config->show_secrets
 				                         : TRUE,
 				                         "%s (%s): ", secret->pretty_name, secret->entry_id);
 				if (!pwd)
@@ -763,8 +764,14 @@ nmc_secrets_requested (NMSecretAgentSimple *agent,
 		g_free (path);
 	}
 
-	success = get_secrets_from_user (request_id, title, msg, connection, nmc->nmc_config.in_editor || nmc->ask,
-	                                 nmc->nmc_config.show_secrets, nmc->pwds_hash, secrets);
+	success = get_secrets_from_user (&nmc->nmc_config,
+	                                 request_id,
+	                                 title,
+	                                 msg,
+	                                 connection,
+	                                 nmc->nmc_config.in_editor || nmc->ask,
+	                                 nmc->pwds_hash,
+	                                 secrets);
 	if (success)
 		nm_secret_agent_simple_response (agent, request_id, secrets);
 	else {
@@ -847,7 +854,8 @@ stdin_ready_cb (GIOChannel * io, GIOCondition condition, gpointer data)
 }
 
 static char *
-nmc_readline_helper (const char *prompt)
+nmc_readline_helper (const NmcConfig *nmc_config,
+                     const char *prompt)
 {
 	GIOChannel *io = NULL;
 	guint io_watch_id;
@@ -884,7 +892,7 @@ read_again:
 	if (nmc_seen_sigint ()) {
 		/* Ctrl-C */
 		nmc_clear_sigint ();
-		if (   nm_cli.nmc_config.in_editor
+		if (   nmc_config->in_editor
 		    || (rl_string  && *rl_string)) {
 			/* In editor, or the line is not empty */
 			/* Call readline again to get new prompt (repeat) */
@@ -926,22 +934,19 @@ read_again:
  * this function returns NULL.
  */
 char *
-nmc_readline (const char *prompt_fmt, ...)
+nmc_readline (const NmcConfig *nmc_config,
+              const char *prompt_fmt,
+              ...)
 {
 	va_list args;
-	char *prompt, *str;
+	gs_free char *prompt = NULL;
 
 	rl_initialize ();
 
 	va_start (args, prompt_fmt);
 	prompt = g_strdup_vprintf (prompt_fmt, args);
 	va_end (args);
-
-	str = nmc_readline_helper (prompt);
-
-	g_free (prompt);
-
-	return str;
+	return nmc_readline_helper (nmc_config, prompt);
 }
 
 static void
@@ -976,10 +981,14 @@ nmc_secret_redisplay (void)
  * nmc_readline(TRUE, ...) == nmc_readline(...)
  */
 char *
-nmc_readline_echo (gboolean echo_on, const char *prompt_fmt, ...)
+nmc_readline_echo (const NmcConfig *nmc_config,
+                   gboolean echo_on,
+                   const char *prompt_fmt,
+                   ...)
 {
 	va_list args;
-	char *prompt, *str;
+	gs_free char *prompt = NULL;
+	char *str;
 	HISTORY_STATE *saved_history;
 	HISTORY_STATE passwd_history = { 0, };
 
@@ -996,9 +1005,7 @@ nmc_readline_echo (gboolean echo_on, const char *prompt_fmt, ...)
 		rl_redisplay_function = nmc_secret_redisplay;
 	}
 
-	str = nmc_readline_helper (prompt);
-
-	g_free (prompt);
+	str = nmc_readline_helper (nmc_config, prompt);
 
 	/* Restore the non-hiding behavior */
 	if (!echo_on) {
@@ -1384,3 +1391,14 @@ nmc_error_get_simple_message (GError *error)
 	else
 		return error->message;
 }
+
+/*****************************************************************************/
+
+NM_UTILS_LOOKUP_STR_DEFINE (nm_connectivity_to_string, NMConnectivityState,
+	NM_UTILS_LOOKUP_DEFAULT (N_("unknown")),
+	NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_NONE,    N_("none")),
+	NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_PORTAL,  N_("portal")),
+	NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_LIMITED, N_("limited")),
+	NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_FULL,    N_("full")),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (NM_CONNECTIVITY_UNKNOWN),
+);
diff --git a/clients/cli/common.h b/clients/cli/common.h
index c180dfb7..71734acc 100644
--- a/clients/cli/common.h
+++ b/clients/cli/common.h
@@ -59,8 +59,13 @@ char *nmc_unique_connection_name (const GPtrArray *connections,
                                   const char *try_name);
 
 void nmc_cleanup_readline (void);
-char *nmc_readline (const char *prompt_fmt, ...) G_GNUC_PRINTF (1, 2);
-char *nmc_readline_echo (gboolean echo_on, const char *prompt_fmt, ...) G_GNUC_PRINTF (2, 3);
+char *nmc_readline (const NmcConfig *nmc_config,
+                    const char *prompt_fmt,
+                    ...) G_GNUC_PRINTF (2, 3);
+char *nmc_readline_echo (const NmcConfig *nmc_config,
+                         gboolean echo_on,
+                         const char *prompt_fmt,
+                         ...) G_GNUC_PRINTF (3, 4);
 NmcCompEntryFunc nmc_rl_compentry_func_wrap (const char *const*values);
 char *nmc_rl_gen_func_basic (const char *text, int state, const char *const*words);
 char *nmc_rl_gen_func_ifnames (const char *text, int state);
@@ -93,4 +98,6 @@ extern const NmcMetaGenericInfo *const metagen_ip4_config[];
 extern const NmcMetaGenericInfo *const metagen_ip6_config[];
 extern const NmcMetaGenericInfo *const metagen_dhcp_config[];
 
+const char *nm_connectivity_to_string (NMConnectivityState connectivity);
+
 #endif /* NMC_COMMON_H */
diff --git a/clients/cli/connections.c b/clients/cli/connections.c
index bcd257ac..446424ba 100644
--- a/clients/cli/connections.c
+++ b/clients/cli/connections.c
@@ -30,6 +30,7 @@
 #include <netinet/ether.h>
 #include <readline/readline.h>
 #include <readline/history.h>
+#include <fcntl.h>
 
 #include "nm-client-utils.h"
 #include "nm-vpn-helpers.h"
@@ -140,15 +141,68 @@ active_connection_get_state_ord (NMActiveConnection *active)
 	return -1;
 }
 
-static int
-active_connection_cmp (NMActiveConnection *ac_a, NMActiveConnection *ac_b)
+int
+nmc_active_connection_cmp (NMActiveConnection *ac_a, NMActiveConnection *ac_b)
 {
+	NMSettingIPConfig *s_ip;
+	NMRemoteConnection *conn;
+	NMIPConfig *da_ip;
+	NMIPConfig *db_ip;
+	int da_num_addrs;
+	int db_num_addrs;
+	int cmp = 0;
+
+	/* Non-active sort last. */
 	NM_CMP_SELF (ac_a, ac_b);
 	NM_CMP_DIRECT (active_connection_get_state_ord (ac_b),
 	               active_connection_get_state_ord (ac_a));
-	NM_CMP_DIRECT_STRCMP0 (nm_active_connection_get_id (ac_a), nm_active_connection_get_id (ac_b));
-	NM_CMP_DIRECT_STRCMP0 (nm_active_connection_get_connection_type (ac_a), nm_active_connection_get_connection_type (ac_b));
-	NM_CMP_DIRECT_STRCMP0 (nm_object_get_path (NM_OBJECT (ac_a)), nm_object_get_path (NM_OBJECT (ac_b)));
+
+	/* Shared connections (likely hotspots) go on the top if possible */
+	conn = nm_active_connection_get_connection (ac_a);
+	s_ip = conn ? nm_connection_get_setting_ip6_config (NM_CONNECTION (conn)) : NULL;
+	if (s_ip && strcmp (nm_setting_ip_config_get_method (s_ip), NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0)
+		cmp++;
+	conn = nm_active_connection_get_connection (ac_b);
+	s_ip = conn ? nm_connection_get_setting_ip6_config (NM_CONNECTION (conn)) : NULL;
+	if (s_ip && strcmp (nm_setting_ip_config_get_method (s_ip), NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0)
+		cmp--;
+	NM_CMP_RETURN (cmp);
+
+	conn = nm_active_connection_get_connection (ac_a);
+	s_ip = conn ? nm_connection_get_setting_ip4_config (NM_CONNECTION (conn)) : NULL;
+	if (s_ip && strcmp (nm_setting_ip_config_get_method (s_ip), NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0)
+		cmp++;
+	conn = nm_active_connection_get_connection (ac_b);
+	s_ip = conn ? nm_connection_get_setting_ip4_config (NM_CONNECTION (conn)) : NULL;
+	if (s_ip && strcmp (nm_setting_ip_config_get_method (s_ip), NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0)
+		cmp--;
+	NM_CMP_RETURN (cmp);
+
+	/* VPNs go next */
+	NM_CMP_DIRECT (!!nm_active_connection_get_vpn (ac_a),
+	               !!nm_active_connection_get_vpn (ac_b));
+
+	/* Default devices are prioritized */
+	NM_CMP_DIRECT (nm_active_connection_get_default (ac_a),
+	               nm_active_connection_get_default (ac_b));
+
+	/* Default IPv6 devices are prioritized */
+	NM_CMP_DIRECT (nm_active_connection_get_default6 (ac_a),
+	               nm_active_connection_get_default6 (ac_b));
+
+	/* Sort by number of addresses. */
+	da_ip = nm_active_connection_get_ip4_config (ac_a);
+	da_num_addrs = da_ip ? nm_ip_config_get_addresses (da_ip)->len : 0;
+	db_ip = nm_active_connection_get_ip4_config (ac_b);
+	db_num_addrs = db_ip ? nm_ip_config_get_addresses (db_ip)->len : 0;
+
+	da_ip = nm_active_connection_get_ip6_config (ac_a);
+	da_num_addrs += da_ip ? nm_ip_config_get_addresses (da_ip)->len : 0;
+	db_ip = nm_active_connection_get_ip6_config (ac_b);
+	db_num_addrs += db_ip ? nm_ip_config_get_addresses (db_ip)->len : 0;
+
+	NM_CMP_DIRECT (da_num_addrs, db_num_addrs);
+
 	return 0;
 }
 
@@ -991,8 +1045,10 @@ usage_connection_add (void)
 	              "                  [source-port-min <0-65535>]\n"
 	              "                  [source-port-max <0-65535>]\n"
 	              "                  [destination-port <0-65535>]\n\n"
-	              "    wpan:         [short-addr <0x0000-0xffff>]\n\n"
-	              "                  [pan-id <0x0000-0xffff>]\n\n"
+	              "    wpan:         [short-addr <0x0000-0xffff>]\n"
+	              "                  [pan-id <0x0000-0xffff>]\n"
+	              "                  [page <default|0-31>]\n"
+	              "                  [channel <default|0-26>]\n"
 	              "                  [mac <MAC address>]\n\n"
 	              "    6lowpan:      dev <parent device (connection UUID, ifname, or MAC)>\n"
 	              "    dummy:\n\n"
@@ -1141,12 +1197,20 @@ construct_header_name (const char *base, const char *spec)
 }
 
 static int
-get_ac_for_connection_cmp (gconstpointer pa, gconstpointer pb, gpointer user_data)
+get_ac_for_connection_cmp (gconstpointer pa, gconstpointer pb)
 {
 	NMActiveConnection *ac_a = *((NMActiveConnection *const*) pa);
 	NMActiveConnection *ac_b = *((NMActiveConnection *const*) pb);
 
-	return active_connection_cmp (ac_a, ac_b);
+	NM_CMP_RETURN (nmc_active_connection_cmp (ac_a, ac_b));
+	NM_CMP_DIRECT_STRCMP0 (nm_active_connection_get_id (ac_a),
+	                       nm_active_connection_get_id (ac_b));
+	NM_CMP_DIRECT_STRCMP0 (nm_active_connection_get_connection_type (ac_a),
+	                       nm_active_connection_get_connection_type (ac_b));
+	NM_CMP_DIRECT_STRCMP0 (nm_object_get_path (NM_OBJECT (ac_a)),
+	                       nm_object_get_path (NM_OBJECT (ac_b)));
+
+	g_return_val_if_reached (0);
 }
 
 static NMActiveConnection *
@@ -1172,7 +1236,7 @@ get_ac_for_connection (const GPtrArray *active_cons, NMConnection *connection, G
 	}
 
 	if (result) {
-		g_ptr_array_sort_with_data (result, get_ac_for_connection_cmp, NULL);
+		g_ptr_array_sort (result, get_ac_for_connection_cmp);
 		best_candidate = result->pdata[0];
 	}
 
@@ -1629,12 +1693,10 @@ con_show_get_items_cmp (gconstpointer pa, gconstpointer pb, gpointer user_data)
 			switch (item) {
 
 			case NMC_SORT_ACTIVE:
-				NM_CMP_DIRECT (active_connection_get_state_ord (ac_b),
-				               active_connection_get_state_ord (ac_a));
+				NM_CMP_RETURN (nmc_active_connection_cmp (ac_b, ac_a));
 				break;
 			case NMC_SORT_ACTIVE_INV:
-				NM_CMP_DIRECT (active_connection_get_state_ord (ac_a),
-				               active_connection_get_state_ord (ac_b));
+				NM_CMP_RETURN (nmc_active_connection_cmp (ac_a, ac_b));
 				break;
 
 			case NMC_SORT_TYPE:
@@ -1673,13 +1735,12 @@ con_show_get_items_cmp (gconstpointer pa, gconstpointer pb, gpointer user_data)
 		                       nm_connection_get_uuid (c_b));
 		NM_CMP_DIRECT_STRCMP0 (nm_connection_get_path (c_a),
 		                       nm_connection_get_path (c_b));
-
-		/* This line is not expected to be reached, because there shouldn't be two
-		 * different connections with the same path. Anyway, fall-through and compare by
-		 * active connections... */
 	}
 
-	return active_connection_cmp (ac_a, ac_b);
+	NM_CMP_DIRECT_STRCMP0 (nm_object_get_path (NM_OBJECT (ac_a)),
+	                       nm_object_get_path (NM_OBJECT (ac_b)));
+
+	g_return_val_if_reached (0);
 }
 
 static GPtrArray *
@@ -1755,7 +1816,7 @@ con_show_get_items (NmCli *nmc, gboolean active_only, gboolean show_active_field
 		 * color (activated or not) based on primary_active. */
 		if (!row_data) {
 			/* this is unexpected. The active connection references a connection that
-			 * seemingly no longer exists. It's a bug in libnm. Add a row nontheless. */
+			 * seemingly no longer exists. It's a bug in libnm. Add a row nonetheless. */
 			row_data = _metagen_con_show_row_data_new_for_connection (c, show_active_fields);
 			g_hash_table_insert (row_hash, c, row_data);
 		}
@@ -1985,8 +2046,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 			}
 		}
 
-		/* Optionally start paging the output. */
-		nmc_terminal_spawn_pager (&nmc->nmc_config);
+		nm_cli_spawn_pager (nmc);
 
 		items = con_show_get_items (nmc, active_only, show_active_fields, order);
 		g_ptr_array_add (items, NULL);
@@ -2287,7 +2347,7 @@ find_device_for_connection (NmCli *nmc,
 					continue;
 
 				if (!nm_device_connection_compatible (dev, connection, error)) {
-					g_prefix_error (error, _("device '%s' not compatible with connection '%s':"),
+					g_prefix_error (error, _("device '%s' not compatible with connection '%s': "),
 					                iface, nm_setting_connection_get_id (s_con));
 					return FALSE;
 				}
@@ -2340,6 +2400,43 @@ typedef struct {
 	NMActiveConnection *active;
 } ActivateConnectionInfo;
 
+static void
+active_connection_hint (GString *return_text,
+                        NMActiveConnection *active,
+                        NMDevice *device)
+{
+	NMRemoteConnection *connection;
+	nm_auto_free_gstring GString *hint = NULL;
+	const GPtrArray *devices;
+	guint i;
+
+	if (!active)
+		return;
+
+	if (!nm_streq (NM_CONFIG_DEFAULT_LOGGING_BACKEND, "journal"))
+		return;
+
+	connection = nm_active_connection_get_connection (active);
+	g_return_if_fail (connection);
+
+	hint = g_string_new ("journalctl -xe ");
+	g_string_append_printf (hint, "NM_CONNECTION=%s",
+	                        nm_connection_get_uuid (NM_CONNECTION (connection)));
+
+	if (device)
+		g_string_append_printf (hint, " + NM_DEVICE=%s", nm_device_get_iface (device));
+	else {
+		devices = nm_active_connection_get_devices (active);
+		for (i = 0; i < devices->len; i++) {
+			g_string_append_printf (hint, " + NM_DEVICE=%s",
+			                        nm_device_get_iface (NM_DEVICE (g_ptr_array_index (devices, i))));
+		}
+	}
+
+	g_string_append (return_text, "\n");
+	g_string_append_printf (return_text, _("Hint: use '%s' to get more details."), hint->str);
+}
+
 static void activate_connection_info_finish (ActivateConnectionInfo *info);
 
 static void
@@ -2368,6 +2465,7 @@ check_activated (ActivateConnectionInfo *info)
 		nm_assert (reason);
 		g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"),
 		                 reason);
+		active_connection_hint (nmc->return_text, info->active, info->device);
 		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
 		activate_connection_info_finish (info);
 		break;
@@ -2490,6 +2588,7 @@ activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_dat
 		g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"),
 		                 error->message);
 		g_error_free (error);
+		active_connection_hint (nmc->return_text, info->active, info->device);
 		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
 		activate_connection_info_finish (info);
 	} else {
@@ -2726,14 +2825,14 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 	argc_ptr = &argc;
 
 	if (argc == 0 && nmc->ask) {
-		char *line;
+		gs_free char *line = NULL;
 
 		/* nmc_do_cmd() should not call this with argc=0. */
 		g_assert (!nmc->complete);
 
-		line = nmc_readline (PROMPT_CONNECTION);
+		line = nmc_readline (&nmc->nmc_config,
+		                     PROMPT_CONNECTION);
 		nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
-		g_free (line);
 		argv_ptr = &arg_arr;
 		argc_ptr = &arg_num;
 	}
@@ -2906,6 +3005,7 @@ connection_cb_info_finish (ConnectionCbInfo *info, gpointer obj)
 
 	nm_clear_g_source (&info->timeout_id);
 	nm_clear_g_cancellable (&info->cancellable);
+	g_ptr_array_free (info->obj_list, TRUE);
 
 	g_signal_handlers_disconnect_by_func (info->nmc->client, connection_removed_cb, info);
 
@@ -2980,9 +3080,11 @@ do_connection_down (NmCli *nmc, int argc, char **argv)
 		g_assert (!nmc->complete);
 
 		if (nmc->ask) {
-			char *line = nmc_readline (PROMPT_ACTIVE_CONNECTIONS);
+			gs_free char *line = NULL;
+
+			line = nmc_readline (&nmc->nmc_config,
+			                     PROMPT_ACTIVE_CONNECTIONS);
 			nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
-			g_free (line);
 			arg_ptr = arg_arr;
 		}
 		if (arg_num == 0) {
@@ -3850,9 +3952,13 @@ reset_options (void)
 }
 
 static gboolean
-set_property (NMConnection *connection,
-              const char *setting_name, const char *property, const char *value,
-              char modifier, GError **error)
+set_property (NMClient *client,
+              NMConnection *connection,
+              const char *setting_name,
+              const char *property,
+              const char *value,
+              char modifier,
+              GError **error)
 {
 	gs_free char *property_name = NULL, *value_free = NULL;
 	NMSetting *setting;
@@ -3885,7 +3991,7 @@ set_property (NMConnection *connection,
 			value = value_free = g_strdup (value);
 			nmc_setting_reset_property (setting, property_name, NULL);
 		}
-		if (!nmc_setting_set_property (setting, property_name, value, &local)) {
+		if (!nmc_setting_set_property (client, setting, property_name, value, &local)) {
 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
 			             _("Error: failed to modify %s.%s: %s."),
 			             setting_name, property, local->message);
@@ -3937,7 +4043,7 @@ set_option (NmCli *nmc, NMConnection *connection, const NMMetaAbstractInfo *abst
 	if (option && option->check_and_set) {
 		return option->check_and_set (nmc, connection, option, value, error);
 	} else if (value) {
-		return set_property (connection, setting_name, property_name,
+		return set_property (nmc->client, connection, setting_name, property_name,
 		                     value, inf_flags & NM_META_PROPERTY_INF_FLAG_MULTI ? '+' : '\0', error);
 	} else if (inf_flags & NM_META_PROPERTY_INF_FLAG_REQD) {
 		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
@@ -3985,7 +4091,7 @@ con_settings (NMConnection *connection, const NMMetaSettingValidPartItem *const*
 
 /*
  * Make sure all required settings are in place (should be called when
- * it's possible that a type is aready set).
+ * it's possible that a type is already set).
  */
 static void
 ensure_settings (NMConnection *connection, const NMMetaSettingValidPartItem *const*item)
@@ -4059,7 +4165,7 @@ set_connection_type (NmCli *nmc, NMConnection *con, const OptionInfo *option, co
 	}
 
 	if (slave_type) {
-		if (!set_property (con, NM_SETTING_CONNECTION_SETTING_NAME,
+		if (!set_property (nmc->client, con, NM_SETTING_CONNECTION_SETTING_NAME,
 		                   NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
 		                   '\0', error)) {
 			return FALSE;
@@ -4076,7 +4182,7 @@ set_connection_type (NmCli *nmc, NMConnection *con, const OptionInfo *option, co
 		                 NM_SETTING_CONNECTION_INTERFACE_NAME);
 	}
 
-	if (!set_property (con, option->setting_info->general->setting_name, option->property, value, '\0', error))
+	if (!set_property (nmc->client, con, option->setting_info->general->setting_name, option->property, value, '\0', error))
 		return FALSE;
 
 	if (!con_settings (con, &type_settings, &slv_settings, error))
@@ -4105,7 +4211,7 @@ set_connection_iface (NmCli *nmc, NMConnection *con, const OptionInfo *option, c
 		}
 	}
 
-	return set_property (con, option->setting_info->general->setting_name, option->property, value, '\0', error);
+	return set_property (nmc->client, con, option->setting_info->general->setting_name, option->property, value, '\0', error);
 }
 
 static gboolean
@@ -4128,13 +4234,13 @@ set_connection_master (NmCli *nmc, NMConnection *con, const OptionInfo *option,
 	connections = nm_client_get_connections (nmc->client);
 	value = normalized_master_for_slave (connections, value, slave_type, &slave_type);
 
-	if (!set_property (con, NM_SETTING_CONNECTION_SETTING_NAME,
+	if (!set_property (nmc->client, con, NM_SETTING_CONNECTION_SETTING_NAME,
 	                   NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
 	                   '\0', error)) {
 		return FALSE;
 	}
 
-	return set_property (con, option->setting_info->general->setting_name, option->property, value, '\0', error);
+	return set_property (nmc->client, con, option->setting_info->general->setting_name, option->property, value, '\0', error);
 }
 
 static gboolean
@@ -4248,7 +4354,7 @@ set_bluetooth_type (NmCli *nmc, NMConnection *con, const OptionInfo *option, con
 		return FALSE;
 	}
 
-	return set_property (con, option->setting_info->general->setting_name, option->property, value, '\0', error);
+	return set_property (nmc->client, con, option->setting_info->general->setting_name, option->property, value, '\0', error);
 }
 
 static gboolean
@@ -4267,7 +4373,7 @@ set_ip4_address (NmCli *nmc, NMConnection *con, const OptionInfo *option, const
 		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
 		              NULL);
 	}
-	return set_property (con, option->setting_info->general->setting_name, option->property, value,
+	return set_property (nmc->client, con, option->setting_info->general->setting_name, option->property, value,
 	                     '+', error);
 }
 
@@ -4287,7 +4393,7 @@ set_ip6_address (NmCli *nmc, NMConnection *con, const OptionInfo *option, const
 		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
 		              NULL);
 	}
-	return set_property (con, option->setting_info->general->setting_name, option->property, value,
+	return set_property (nmc->client, con, option->setting_info->general->setting_name, option->property, value,
 	                     '+', error);
 }
 
@@ -4573,7 +4679,7 @@ nmc_read_connection_properties (NmCli *nmc,
 			if (!*argc && nmc->complete)
 				complete_property (setting, strv[1], value ?: "", connection);
 
-			if (!set_property (connection, setting_name, strv[1], value, modifier, error))
+			if (!set_property (nmc->client, connection, setting_name, strv[1], value, modifier, error))
 				return FALSE;
 		} else {
 			NMMetaSettingType s;
@@ -4853,7 +4959,9 @@ ask_option (NmCli *nmc, NMConnection *connection, const NMMetaAbstractInfo *abst
 		g_print (_("You can specify this option more than once. Press <Enter> when you're done.\n"));
 
 again:
-	value = nmc_readline ("%s", prompt);
+	value = nmc_readline (&nmc->nmc_config,
+	                      "%s",
+	                      prompt);
 	if (multi && !value)
 		return;
 
@@ -4943,23 +5051,25 @@ questionnaire_mandatory (NmCli *nmc, NMConnection *connection)
 }
 
 static gboolean
-want_provide_opt_args (const char *type, int num)
+want_provide_opt_args (const NmcConfig *nmc_config,
+                       const char *type,
+                       guint num)
 {
-	char *answer;
-	gboolean ret = TRUE;
+	gs_free char *answer = NULL;
 
 	/* Ask for optional arguments. */
 	g_print (ngettext ("There is %d optional setting for %s.\n",
-	                   "There are %d optional settings for %s.\n", num),
-	         num, type);
-	answer = nmc_readline (ngettext ("Do you want to provide it? %s",
-	                                 "Do you want to provide them? %s", num),
+	                   "There are %d optional settings for %s.\n",
+	                   num),
+	         (int) num,
+	         type);
+	answer = nmc_readline (nmc_config,
+	                       ngettext ("Do you want to provide it? %s",
+	                                 "Do you want to provide them? %s",
+	                                 num),
 	                       prompt_yes_no (TRUE, NULL));
-	answer = answer ? g_strstrip (answer) : NULL;
-	if (answer && !matches (answer, WORD_YES))
-		ret = FALSE;
-	g_free (answer);
-	return ret;
+	nm_strstrip (answer);
+	return !answer || matches (answer, WORD_YES);
 }
 
 static gboolean
@@ -5029,7 +5139,9 @@ again:
 
 		/* Now ask for the settings. */
 		if (   already_confirmed
-			|| want_provide_opt_args (_(setting_info->pretty_name), infos->len)) {
+		    || want_provide_opt_args (&nmc->nmc_config,
+		                              _(setting_info->pretty_name),
+		                              infos->len)) {
 			ask_option (nmc, connection, infos->pdata[0]);
 			already_confirmed = TRUE;
 			/* asking for an option may enable other options. Create the list again. */
@@ -5059,7 +5171,7 @@ do_connection_add (NmCli *nmc, int argc, char **argv)
 
 	next_arg (nmc, &argc, &argv, NULL);
 
-	rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion;
+	rl_attempted_completion_function = nmcli_con_add_tab_completion;
 
 	nmc->return_value = NMC_RESULT_SUCCESS;
 
@@ -6534,6 +6646,12 @@ static gboolean nmc_editor_cb_called;
 static GError *nmc_editor_error;
 static MonitorACInfo *nmc_editor_monitor_ac;
 
+static void
+editor_connection_changed_cb (NMConnection *connection, gboolean *changed)
+{
+	*changed = TRUE;
+}
+
 /*
  * Store 'error' to shared 'nmc_editor_error' and monitoring info to
  * 'nmc_editor_monitor_ac' and signal the condition so that
@@ -6579,8 +6697,8 @@ static gboolean
 progress_activation_editor_cb (gpointer user_data)
 {
 	MonitorACInfo *info = (MonitorACInfo *) user_data;
-	gs_unref_object NMDevice *device = info->device;
-	gs_unref_object NMActiveConnection *ac = info->ac;
+	NMDevice *device = info->device;
+	NMActiveConnection *ac = info->ac;
 	NMActiveConnectionState ac_state;
 	NMDeviceState dev_state;
 
@@ -6613,11 +6731,13 @@ progress_activation_editor_cb (gpointer user_data)
 		                               nm_object_get_path (NM_OBJECT (connection)));
 	}
 
-	return TRUE;
+	return G_SOURCE_CONTINUE;
 
 finish:
+	nm_g_object_unref (device);
+	nm_g_object_unref (ac);
 	info->monitor_id = 0;
-	return FALSE;
+	return G_SOURCE_REMOVE;
 }
 
 static void
@@ -6648,6 +6768,10 @@ activate_connection_editor_cb (GObject *client,
 		} else
 			g_object_unref (active);
 	}
+
+	nm_g_object_unref (info->device);
+	g_free (info);
+
 	set_info_and_signal_editor_thread (error, monitor_ac_info);
 	g_clear_error (&error);
 }
@@ -6741,20 +6865,16 @@ is_connection_dirty (NMConnection *connection, NMRemoteConnection *remote)
 }
 
 static gboolean
-confirm_quit (void)
+confirm_quit (const NmcConfig *nmc_config)
 {
-	char *answer;
-	gboolean want_quit = FALSE;
+	gs_free char *answer = NULL;
 
-	answer = nmc_readline (_("The connection is not saved. "
+	answer = nmc_readline (nmc_config,
+	                       _("The connection is not saved. "
 	                         "Do you really want to quit? %s"),
 	                       prompt_yes_no (FALSE, NULL));
-	answer = answer ? g_strstrip (answer) : NULL;
-	if (answer && matches (answer, WORD_YES))
-		want_quit = TRUE;
-
-	g_free (answer);
-	return want_quit;
+	nm_strstrip (answer);
+	return (answer && matches (answer, WORD_YES));
 }
 
 /*
@@ -6789,7 +6909,7 @@ property_edit_submenu (NmCli *nmc,
 		gboolean removed;
 		gboolean dirty;
 
-		/* Get the remote connection again, it may have disapeared */
+		/* Get the remote connection again, it may have disappeared */
 		removed = refresh_remote_connection (rem_con_weak, rem_con);
 		if (removed) {
 			g_print (_("The connection profile has been removed from another client. "
@@ -6802,10 +6922,13 @@ property_edit_submenu (NmCli *nmc,
 		if (nmc->editor_status_line)
 			editor_show_status_line (connection, dirty, temp_changes);
 
-		cmd_property_user = nmc_readline ("%s", prompt);
+		cmd_property_user = nmc_readline (&nmc->nmc_config,
+		                                  "%s",
+		                                  prompt);
 		if (!cmd_property_user || !*cmd_property_user)
 			continue;
-		cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg);
+		g_strstrip (cmd_property_user);
+		cmdsub = parse_editor_sub_cmd (cmd_property_user, &cmd_property_arg);
 
 		switch (cmdsub) {
 		case NMC_EDITOR_SUB_CMD_SET:
@@ -6826,7 +6949,9 @@ property_edit_submenu (NmCli *nmc,
 					g_print (_("Allowed values for '%s' property: %s\n"),
 					         prop_name, avals_str);
 				}
-				prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
+				prop_val_user = nmc_readline (&nmc->nmc_config,
+				                              _("Enter '%s' value: "),
+				                              prop_name);
 			} else
 				prop_val_user = g_strdup (cmd_property_arg);
 
@@ -6838,7 +6963,7 @@ property_edit_submenu (NmCli *nmc,
 				nmc_property_set_default_value (curr_setting, prop_name);
 			}
 
-			set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err);
+			set_result = nmc_setting_set_property (nmc->client, curr_setting, prop_name, prop_val_user, &tmp_err);
 			if (!set_result) {
 				g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
 				g_clear_error (&tmp_err);
@@ -6854,12 +6979,14 @@ property_edit_submenu (NmCli *nmc,
 		case NMC_EDITOR_SUB_CMD_CHANGE:
 			rl_startup_hook = nmc_rl_set_deftext;
 			nmc_rl_pre_input_deftext = nmc_setting_get_property_parsable (curr_setting, prop_name, NULL);
-			prop_val_user = nmc_readline (_("Edit '%s' value: "), prop_name);
+			prop_val_user = nmc_readline (&nmc->nmc_config,
+			                              _("Edit '%s' value: "),
+			                              prop_name);
 
 			nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
 			nmc_property_set_default_value (curr_setting, prop_name);
 
-			if (!nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err)) {
+			if (!nmc_setting_set_property (nmc->client, curr_setting, prop_name, prop_val_user, &tmp_err)) {
 				g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
 				g_clear_error (&tmp_err);
 				g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
@@ -6871,19 +6998,20 @@ property_edit_submenu (NmCli *nmc,
 		case NMC_EDITOR_SUB_CMD_REMOVE:
 			if (cmd_property_arg) {
 				unsigned long val_int = G_MAXUINT32;
-				char *option = NULL;
+				gs_free char *option = NULL;
 
-				if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int))
+				if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int)) {
 					option = g_strdup (cmd_property_arg);
+					g_strstrip (option);
+				}
 
 				if (!nmc_setting_remove_property_option (curr_setting, prop_name,
-				                                         option ? g_strstrip (option) : NULL,
+				                                         option,
 				                                         (guint32) val_int,
 				                                         &tmp_err)) {
 					g_print (_("Error: %s\n"), tmp_err->message);
 					g_clear_error (&tmp_err);
 				}
-				g_free (option);
 			} else {
 				if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) {
 					g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
@@ -6926,7 +7054,7 @@ property_edit_submenu (NmCli *nmc,
 
 		case NMC_EDITOR_SUB_CMD_QUIT:
 			if (is_connection_dirty (connection, *rem_con)) {
-				if (confirm_quit ())
+				if (confirm_quit (&nmc->nmc_config))
 					return FALSE;
 			} else
 				return FALSE;
@@ -6987,7 +7115,8 @@ create_setting_by_name (const char *name, const NMMetaSettingValidPartItem *cons
 }
 
 static const char *
-ask_check_setting (const char *arg,
+ask_check_setting (const NmcConfig *nmc_config,
+                   const char *arg,
                    const NMMetaSettingValidPartItem *const*valid_settings_main,
                    const NMMetaSettingValidPartItem *const*valid_settings_slave,
                    const char *valid_settings_str)
@@ -6998,12 +7127,12 @@ ask_check_setting (const char *arg,
 
 	if (!arg) {
 		g_print (_("Available settings: %s\n"), valid_settings_str);
-		setting_name_user = nmc_readline (EDITOR_PROMPT_SETTING);
+		setting_name_user = nmc_readline (nmc_config,
+		                                  EDITOR_PROMPT_SETTING);
 	} else
 		setting_name_user = g_strdup (arg);
 
-	if (setting_name_user)
-		g_strstrip (setting_name_user);
+	nm_strstrip (setting_name_user);
 
 	if (!(setting_name = check_valid_name (setting_name_user,
 	                                       valid_settings_main,
@@ -7017,7 +7146,8 @@ ask_check_setting (const char *arg,
 }
 
 static const char *
-ask_check_property (const char *arg,
+ask_check_property (const NmcConfig *nmc_config,
+                    const char *arg,
                     const char **valid_props,
                     const char *valid_props_str)
 {
@@ -7027,9 +7157,9 @@ ask_check_property (const char *arg,
 
 	if (!arg) {
 		g_print (_("Available properties: %s\n"), valid_props_str);
-		prop_name_user = nmc_readline (EDITOR_PROMPT_PROPERTY);
-		if (prop_name_user)
-			g_strstrip (prop_name_user);
+		prop_name_user = nmc_readline (nmc_config,
+		                               EDITOR_PROMPT_PROPERTY);
+		nm_strstrip (prop_name_user);
 	} else
 		prop_name_user = g_strdup (arg);
 
@@ -7056,7 +7186,9 @@ update_connection_timestamp (NMConnection *src, NMConnection *dst)
 }
 
 static gboolean
-confirm_connection_saving (NMConnection *local, NMConnection *remote)
+confirm_connection_saving (const NmcConfig *nmc_config,
+                           NMConnection *local,
+                           NMConnection *remote)
 {
 	NMSettingConnection *s_con_loc, *s_con_rem;
 	gboolean ac_local, ac_remote;
@@ -7074,16 +7206,15 @@ confirm_connection_saving (NMConnection *local, NMConnection *remote)
 		ac_remote = FALSE;
 
 	if (ac_local && !ac_remote) {
-		char *answer;
-		answer = nmc_readline (_("Saving the connection with 'autoconnect=yes'. "
+		gs_free char *answer = NULL;
+
+		answer = nmc_readline (nmc_config,
+		                       _("Saving the connection with 'autoconnect=yes'. "
 		                         "That might result in an immediate activation of the connection.\n"
-		                         "Do you still want to save? %s"), prompt_yes_no (TRUE, NULL));
-		answer = answer ? g_strstrip (answer) : NULL;
-		if (!answer || matches (answer, WORD_YES))
-			confirmed = TRUE;
-		else
-			confirmed = FALSE;
-		g_free (answer);
+		                         "Do you still want to save? %s"),
+		                       prompt_yes_no (TRUE, NULL));
+		nm_strstrip (answer);
+		confirmed = (!answer || matches (answer, WORD_YES));
 	}
 	return confirmed;
 }
@@ -7128,6 +7259,16 @@ menu_switch_to_level1 (const NmcConfig *nmc_config,
 }
 
 static gboolean
+editor_save_timeout (gpointer user_data)
+{
+	gboolean *timeout = user_data;
+
+	*timeout = TRUE;
+
+	return G_SOURCE_REMOVE;
+}
+
+static gboolean
 editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type)
 {
 	gs_unref_object NMRemoteConnection *rem_con = NULL;
@@ -7178,9 +7319,11 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 		if (nmc->editor_status_line)
 			editor_show_status_line (connection, dirty, temp_changes);
 
-		cmd_user = nmc_readline ("%s", menu_ctx.main_prompt);
+		cmd_user = nmc_readline (&nmc->nmc_config,
+		                         "%s",
+		                         menu_ctx.main_prompt);
 
-		/* Get the remote connection again, it may have disapeared */
+		/* Get the remote connection again, it may have disappeared */
 		removed = refresh_remote_connection (&weak, &rem_con);
 		if (removed) {
 			g_print (_("The connection profile has been removed from another client. "
@@ -7190,7 +7333,9 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 		if (!cmd_user || !*cmd_user)
 			continue;
 
-		cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg);
+		g_strstrip (cmd_user);
+
+		cmd = parse_editor_main_cmd (cmd_user, &cmd_arg);
 
 		split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v);
 		switch (cmd) {
@@ -7204,7 +7349,8 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 					const char *const*avals;
 					GError *tmp_err = NULL;
 
-					prop_name = ask_check_property (cmd_arg,
+					prop_name = ask_check_property (&nmc->nmc_config,
+					                                cmd_arg,
 					                                (const char **) menu_ctx.valid_props,
 					                                menu_ctx.valid_props_str);
 					if (!prop_name)
@@ -7218,10 +7364,12 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 						g_print (_("Allowed values for '%s' property: %s\n"),
 						         prop_name, avals_str);
 					}
-					prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
+					prop_val_user = nmc_readline (&nmc->nmc_config,
+					                              _("Enter '%s' value: "),
+					                              prop_name);
 
 					/* Set property value */
-					if (!nmc_setting_set_property (menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) {
+					if (!nmc_setting_set_property (nmc->client, menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) {
 						g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
 						g_clear_error (&tmp_err);
 					}
@@ -7276,11 +7424,13 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 						g_print (_("Allowed values for '%s' property: %s\n"),
 						         prop_name, avals_str);
 					}
-					cmd_arg_v = nmc_readline (_("Enter '%s' value: "), prop_name);
+					cmd_arg_v = nmc_readline (&nmc->nmc_config,
+					                          _("Enter '%s' value: "),
+					                          prop_name);
 				}
 
 				/* Set property value */
-				if (!nmc_setting_set_property (ss, prop_name, cmd_arg_v, &tmp_err)) {
+				if (!nmc_setting_set_property (nmc->client, ss, prop_name, cmd_arg_v, &tmp_err)) {
 					g_print (_("Error: failed to set '%s' property: %s\n"),
 					         prop_name, tmp_err->message);
 					g_clear_error (&tmp_err);
@@ -7299,7 +7449,8 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 				NMSetting *setting;
 				const char *user_arg = cmd_arg_s ?: cmd_arg_p;
 
-				setting_name = ask_check_setting (user_arg,
+				setting_name = ask_check_setting (&nmc->nmc_config,
+				                                  user_arg,
 				                                  valid_settings_main,
 				                                  valid_settings_slave,
 				                                  valid_settings_str);
@@ -7345,7 +7496,8 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 				/* level 1 - setting selected */
 				const char *prop_name;
 
-				prop_name = ask_check_property (cmd_arg_p,
+				prop_name = ask_check_property (&nmc->nmc_config,
+				                                cmd_arg_p,
 				                                (const char **) menu_ctx.valid_props,
 				                                menu_ctx.valid_props_str);
 				if (!prop_name)
@@ -7368,7 +7520,8 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 					GError *tmp_err = NULL;
 					const char *prop_name;
 
-					prop_name = ask_check_property (cmd_arg,
+					prop_name = ask_check_property (&nmc->nmc_config,
+					                                cmd_arg,
 					                                (const char **) menu_ctx.valid_props,
 					                                menu_ctx.valid_props_str);
 					if (!prop_name)
@@ -7462,7 +7615,8 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 				if (menu_ctx.level == 1) {
 					const char *prop_name;
 
-					prop_name = ask_check_property (cmd_arg,
+					prop_name = ask_check_property (&nmc->nmc_config,
+					                                cmd_arg,
 					                                (const char **) menu_ctx.valid_props,
 					                                menu_ctx.valid_props_str);
 					if (!prop_name)
@@ -7646,6 +7800,10 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 			/* Save the connection */
 			if (nm_connection_verify (connection, &err1)) {
 				gboolean persistent = TRUE;
+				gboolean connection_changed;
+				nm_auto_unref_gsource GSource *source = NULL;
+				gboolean timeout = FALSE;
+				gulong handler_id = 0;
 
 				/* parse argument */
 				if (cmd_arg) {
@@ -7660,9 +7818,12 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 				}
 
 				/* Ask for save confirmation if the connection changes to autoconnect=yes */
-				if (nmc->editor_save_confirmation)
-					if (!confirm_connection_saving (connection, NM_CONNECTION (rem_con)))
+				if (nmc->editor_save_confirmation) {
+					if (!confirm_connection_saving (&nmc->nmc_config,
+					                                connection,
+					                                NM_CONNECTION (rem_con)))
 						break;
+				}
 
 				if (!rem_con) {
 					/* Tell the settings service to add the new connection */
@@ -7674,23 +7835,44 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 					                    connection,
 					                    add_connection_editor_cb,
 					                    info);
+					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);
+
+					handler_id = g_signal_connect (rem_con,
+					                               NM_CONNECTION_CHANGED,
+					                               G_CALLBACK (editor_connection_changed_cb),
+					                               &connection_changed);
+					connection_changed = FALSE;
 				}
 
-				//FIXME: add also a timeout for cases the callback is not called
-				while (!nmc_editor_cb_called)
+				source = g_timeout_source_new (10 * NM_UTILS_MSEC_PER_SECOND);
+				g_source_set_callback (source, editor_save_timeout, &timeout, NULL);
+				g_source_attach (source, g_main_loop_get_context (loop));
+
+				while (!nmc_editor_cb_called && !timeout)
+					g_main_context_iteration (NULL, TRUE);
+
+				while (!connection_changed && !timeout)
 					g_main_context_iteration (NULL, TRUE);
 
+				if (handler_id)
+					g_signal_handler_disconnect (rem_con, handler_id);
+				g_source_destroy (source);
+
 				if (nmc_editor_error) {
 					g_print (_("Error: Failed to save '%s' (%s) connection: %s\n"),
 					         nm_connection_get_id (connection),
 					         nm_connection_get_uuid (connection),
 					         nmc_editor_error->message);
 					g_error_free (nmc_editor_error);
+				} else if (timeout) {
+					g_print (_("Error: Timeout saving '%s' (%s) connection\n"),
+					         nm_connection_get_id (connection),
+					         nm_connection_get_uuid (connection));
 				} else {
 					g_print (!rem_con ?
 					         _("Connection '%s' (%s) successfully saved.\n") :
@@ -7713,9 +7895,10 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 						if (menu_ctx.curr_setting)
 							s_name = g_strdup (nm_setting_get_name (menu_ctx.curr_setting));
 
-						/* Update settings in the local connection */
+						/* Update settings and secrets in the local connection */
 						nm_connection_replace_settings_from_connection (connection,
 						                                                NM_CONNECTION (con_tmp));
+						update_secrets_in_connection (con_tmp, connection);
 
 						/* Also update setting for menu context and TAB-completion */
 						menu_ctx.curr_setting = s_name ? nm_connection_get_setting_by_name (connection, s_name) : NULL;
@@ -7779,7 +7962,8 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 				         nmc_editor_error->message);
 				g_error_free (nmc_editor_error);
 			} else {
-				nmc_readline (_("Monitoring connection activation (press any key to continue)\n"));
+				nmc_readline (&nmc->nmc_config,
+				              _("Monitoring connection activation (press any key to continue)\n"));
 			}
 
 			if (nmc_editor_monitor_ac) {
@@ -7853,7 +8037,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 
 		case NMC_EDITOR_MAIN_CMD_QUIT:
 			if (is_connection_dirty (connection, rem_con)) {
-				if (confirm_quit ())
+				if (confirm_quit (&nmc->nmc_config))
 					cmd_loop = FALSE;  /* quit command loop */
 			} else
 				cmd_loop = FALSE;  /* quit command loop */
@@ -8041,7 +8225,7 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 
 	/* Setup some readline completion stuff */
 	/* Set a pointer to an alternative function to create matches */
-	rl_attempted_completion_function = (rl_completion_func_t *) nmcli_editor_tab_completion;
+	rl_attempted_completion_function = nmcli_editor_tab_completion;
 	/* Use ' ' and '.' as word break characters */
 	rl_completer_word_break_characters = ". ";
 
@@ -8129,8 +8313,9 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 				g_print (_("Error: invalid connection type; %s\n"), err1->message);
 			g_clear_error (&err1);
 
-			type_ask = nmc_readline (EDITOR_PROMPT_CON_TYPE);
-			type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
+			type_ask = nmc_readline (&nmc->nmc_config,
+			                         EDITOR_PROMPT_CON_TYPE);
+			type = type_ask = nm_strstrip (type_ask);
 			connection_type = check_valid_name_toplevel (type_ask, &slave_type, &err1);
 		}
 		nm_clear_g_free (&tmp_str);
@@ -8323,14 +8508,14 @@ do_connection_clone (NmCli *nmc, int argc, char **argv)
 	argc_ptr = &argc;
 
 	if (argc == 0 && nmc->ask) {
-		char *line;
+		gs_free char *line = NULL;
 
 		/* nmc_do_cmd() should not call this with argc=0. */
 		g_assert (!nmc->complete);
 
-		line = nmc_readline (PROMPT_CONNECTION);
+		line = nmc_readline (&nmc->nmc_config,
+		                     PROMPT_CONNECTION);
 		nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
-		g_free (line);
 		argv_ptr = &arg_arr;
 		argc_ptr = &arg_num;
 	}
@@ -8346,9 +8531,10 @@ 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 (_("New connection name: "));
-	else {
+	else if (nmc->ask) {
+		new_name = new_name_ask = 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);
 	}
@@ -8433,14 +8619,14 @@ do_connection_delete (NmCli *nmc, int argc, char **argv)
 
 	if (argc == 0) {
 		if (nmc->ask) {
-			char *line;
+			gs_free char *line = NULL;
 
 			/* nmc_do_cmd() should not call this with argc=0. */
 			g_assert (!nmc->complete);
 
-			line = nmc_readline (PROMPT_CONNECTIONS);
+			line = nmc_readline (&nmc->nmc_config,
+			                     PROMPT_CONNECTIONS);
 			nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
-			g_free (line);
 			arg_ptr = arg_arr;
 		}
 		if (arg_num == 0) {
@@ -8689,10 +8875,13 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 		g_assert (!nmc->complete);
 
 		if (nmc->ask) {
-			type_ask = nmc_readline ("%s: ", gettext (NM_META_TEXT_PROMPT_VPN_TYPE));
-			filename_ask = nmc_readline (gettext (PROMPT_IMPORT_FILE));
-			type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
-			filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL;
+			type_ask = nmc_readline (&nmc->nmc_config,
+			                         "%s: ",
+			                         gettext (NM_META_TEXT_PROMPT_VPN_TYPE));
+			type = nm_strstrip (type_ask);
+			filename_ask = nmc_readline (&nmc->nmc_config,
+			                             gettext (PROMPT_IMPORT_FILE));
+			filename = nm_strstrip (filename_ask);
 		} else {
 			g_string_printf (nmc->return_text, _("Error: No arguments provided."));
 			NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
@@ -8809,14 +8998,14 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 	argc_ptr = &argc;
 
 	if (argc == 0 && nmc->ask) {
-		char *line;
+		gs_free char *line = NULL;
 
 		/* nmc_do_cmd() should not call this with argc=0. */
 		g_assert (!nmc->complete);
 
-		line = nmc_readline (PROMPT_VPN_CONNECTION);
+		line = nmc_readline (&nmc->nmc_config,
+		                     PROMPT_VPN_CONNECTION);
 		nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
-		g_free (line);
 		argv_ptr = &arg_arr;
 		argc_ptr = &arg_num;
 	}
@@ -8839,8 +9028,10 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 		goto finish;
 	}
 
-	if (out_name == NULL && nmc->ask)
-		out_name = out_name_ask = nmc_readline (_("Output file name: "));
+	if (!out_name && nmc->ask) {
+		out_name = out_name_ask = nmc_readline (&nmc->nmc_config,
+		                                        _("Output file name: "));
+	}
 
 	type = nm_connection_get_connection_type (connection);
 	if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) != 0) {
@@ -8864,7 +9055,7 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 	else {
 		nm_auto_close int fd = -1;
 
-		fd = g_mkstemp (tmpfile);
+		fd = g_mkstemp_full (tmpfile, O_RDWR | O_CLOEXEC, 0600);
 		if (fd == -1) {
 			g_string_printf (nmc->return_text, _("Error: failed to create temporary file %s."), tmpfile);
 			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
@@ -9016,7 +9207,7 @@ do_connections (NmCli *nmc, int argc, char **argv)
 	nmc_start_polkit_agent_start_try (nmc);
 
 	/* Set completion function for 'nmcli con' */
-	rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion;
+	rl_attempted_completion_function = nmcli_con_tab_completion;
 
 	nmc_do_cmd (nmc, connection_cmds, *argv, argc, argv);
 
diff --git a/clients/cli/connections.h b/clients/cli/connections.h
index 22bfa8ec..122a7e27 100644
--- a/clients/cli/connections.h
+++ b/clients/cli/connections.h
@@ -35,6 +35,8 @@ nmc_read_connection_properties (NmCli *nmc,
 
 NMMetaColor nmc_active_connection_state_to_color (NMActiveConnectionState state);
 
+int nmc_active_connection_cmp (NMActiveConnection *ac_a, NMActiveConnection *ac_b);
+
 extern const NmcMetaGenericInfo *const metagen_con_show[];
 extern const NmcMetaGenericInfo *const metagen_con_active_general[];
 extern const NmcMetaGenericInfo *const metagen_con_active_vpn[];
diff --git a/clients/cli/devices.c b/clients/cli/devices.c
index ff980342..44573fb6 100644
--- a/clients/cli/devices.c
+++ b/clients/cli/devices.c
@@ -116,6 +116,12 @@ _metagen_device_status_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 	case NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_STATE:
 		return nmc_meta_generic_get_str_i18n (nmc_device_state_to_string (nm_device_get_state (d)),
 		                                      get_type);
+	case NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP4_CONNECTIVITY:
+		return nmc_meta_generic_get_str_i18n (nm_connectivity_to_string (nm_device_get_connectivity (d, AF_INET)),
+		                                      get_type);
+	case NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP6_CONNECTIVITY:
+		return nmc_meta_generic_get_str_i18n (nm_connectivity_to_string (nm_device_get_connectivity (d, AF_INET6)),
+		                                      get_type);
 	case NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DBUS_PATH:
 		return nm_object_get_path (NM_OBJECT (d));
 	case NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CONNECTION:
@@ -137,13 +143,15 @@ _metagen_device_status_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 const NmcMetaGenericInfo *const metagen_device_status[_NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_NUM + 1] = {
 #define _METAGEN_DEVICE_STATUS(type, name) \
 	[type] = NMC_META_GENERIC(name, .info_type = type, .get_fcn = _metagen_device_status_get_fcn)
-	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DEVICE,     "DEVICE"),
-	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_TYPE,       "TYPE"),
-	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_STATE,      "STATE"),
-	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DBUS_PATH,  "DBUS-PATH"),
-	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CONNECTION, "CONNECTION"),
-	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CON_UUID,   "CON-UUID"),
-	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CON_PATH,   "CON-PATH"),
+	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DEVICE,             "DEVICE"),
+	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_TYPE,               "TYPE"),
+	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_STATE,              "STATE"),
+	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP4_CONNECTIVITY,   "IP4-CONNECTIVITY"),
+	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP6_CONNECTIVITY,   "IP6-CONNECTIVITY"),
+	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DBUS_PATH,          "DBUS-PATH"),
+	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CONNECTION,         "CONNECTION"),
+	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CON_UUID,           "CON-UUID"),
+	_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CON_PATH,           "CON-PATH"),
 };
 
 /*****************************************************************************/
@@ -155,6 +163,7 @@ _metagen_device_detail_general_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 	NMActiveConnection *ac;
 	NMDeviceState state;
 	NMDeviceStateReason state_reason;
+	NMConnectivityState connectivity;
 	const char *s;
 
 	NMC_HANDLE_COLOR (NM_META_COLOR_NONE);
@@ -194,6 +203,18 @@ _metagen_device_detail_general_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 		                                                              state_reason,
 		                                                              nmc_device_reason_to_string (state_reason),
 		                                                              get_type));
+	case NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP4_CONNECTIVITY:
+		connectivity = nm_device_get_connectivity (d, AF_INET);
+		return (*out_to_free = nmc_meta_generic_get_enum_with_detail (NMC_META_GENERIC_GET_ENUM_TYPE_PARENTHESES,
+		                                                              connectivity,
+		                                                              nm_connectivity_to_string (connectivity),
+		                                                              get_type));
+	case NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP6_CONNECTIVITY:
+		connectivity = nm_device_get_connectivity (d, AF_INET6);
+		return (*out_to_free = nmc_meta_generic_get_enum_with_detail (NMC_META_GENERIC_GET_ENUM_TYPE_PARENTHESES,
+		                                                              connectivity,
+		                                                              nm_connectivity_to_string (connectivity),
+		                                                              get_type));
 	case NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_UDI:
 		return nm_device_get_udi (d);
 	case NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP_IFACE:
@@ -244,6 +265,8 @@ const NmcMetaGenericInfo *const metagen_device_detail_general[_NMC_GENERIC_INFO_
 	_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_MTU,               "MTU"),
 	_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_STATE,             "STATE"),
 	_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_REASON,            "REASON"),
+	_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP4_CONNECTIVITY,  "IP4-CONNECTIVITY"),
+	_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP6_CONNECTIVITY,  "IP6-CONNECTIVITY"),
 	_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_UDI,               "UDI"),
 	_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP_IFACE,          "IP-IFACE"),
 	_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IS_SOFTWARE,       "IS-SOFTWARE"),
@@ -680,7 +703,7 @@ usage (void)
 	              "  disconnect <ifname> ...\n\n"
 	              "  delete <ifname> ...\n\n"
 	              "  monitor <ifname> ...\n\n"
-	              "  wifi [list [ifname <ifname>] [bssid <BSSID>]]\n\n"
+	              "  wifi [list [ifname <ifname>] [bssid <BSSID>] [--rescan yes|no|auto]]\n\n"
 	              "  wifi connect <(B)SSID> [password <password>] [wep-key-type key|phrase] [ifname <ifname>]\n"
 	              "                         [bssid <BSSID>] [name <name>] [private yes|no] [hidden yes|no]\n\n"
 	              "  wifi hotspot [ifname <ifname>] [con-name <name>] [ssid <SSID>] [band a|bg] [channel <channel>] [password <password>]\n\n"
@@ -815,22 +838,22 @@ usage_device_wifi (void)
 	              "\n"
 	              "Perform operation on Wi-Fi devices.\n"
 	              "\n"
-	              "ARGUMENTS := [list [ifname <ifname>] [bssid <BSSID>]]\n"
+	              "ARGUMENTS := [list [ifname <ifname>] [bssid <BSSID>] [--rescan yes|no|auto]]\n"
 	              "\n"
 	              "List available Wi-Fi access points. The 'ifname' and 'bssid' options can be\n"
-	              "used to list APs for a particular interface, or with a specific BSSID.\n"
+	              "used to list APs for a particular interface, or with a specific BSSID. The\n"
+	              "--rescan flag tells whether a new Wi-Fi scan should be triggered.\n"
 	              "\n"
 	              "ARGUMENTS := connect <(B)SSID> [password <password>] [wep-key-type key|phrase] [ifname <ifname>]\n"
 	              "                     [bssid <BSSID>] [name <name>] [private yes|no] [hidden yes|no]\n"
 	              "\n"
-	              "Connect to a Wi-Fi network specified by SSID or BSSID. The command creates\n"
-	              "a new connection and then activates it on a device. This is a command-line\n"
-	              "counterpart of clicking an SSID in a GUI client. The command always creates\n"
-	              "a new connection and thus it is mainly useful for connecting to new Wi-Fi\n"
-	              "networks. If a connection for the network already exists, it is better to\n"
-	              "bring up the existing profile as follows: nmcli con up id <name>. Note that\n"
-	              "only open, WEP and WPA-PSK networks are supported at the moment. It is also\n"
-	              "assumed that IP configuration is obtained via DHCP.\n"
+	              "Connect to a Wi-Fi network specified by SSID or BSSID. The command finds a\n"
+	              "matching connection or creates one and then activates it on a device. This\n"
+	              "is a command-line counterpart of clicking an SSID in a GUI client. If a\n"
+	              "connection for the network already exists, it is possible to bring up the\n"
+	              "existing profile as follows: nmcli con up id <name>. Note that only open,\n"
+	              "WEP and WPA-PSK networks are supported if no previous connection exists.\n"
+	              "It is also assumed that IP configuration is obtained via DHCP.\n"
 	              "\n"
 	              "ARGUMENTS := hotspot [ifname <ifname>] [con-name <name>] [ssid <SSID>]\n"
 	              "                     [band a|bg] [channel <channel>] [password <password>]\n"
@@ -879,77 +902,19 @@ compare_devices (const void *a, const void *b)
 {
 	NMDevice *da = *(NMDevice **)a;
 	NMDevice *db = *(NMDevice **)b;
-	NMActiveConnection *da_ac;
-	NMActiveConnection *db_ac;
-	NMIPConfig *da_ip;
-	NMIPConfig *db_ip;
-	int da_num_addrs;
-	int db_num_addrs;
-	int cmp;
-
-	/* Sort by later device states first */
-	cmp = nm_device_get_state (db) - nm_device_get_state (da);
-	if (cmp != 0)
-		return cmp;
-
-	da_ac = nm_device_get_active_connection (da);
-	db_ac = nm_device_get_active_connection (db);
-
-	/* Prioritize devices with active connections */
-	if (da_ac)
-		cmp++;
-	if (db_ac)
-		cmp--;
-	if (cmp != 0)
-		return cmp;
-
-	/* VPNs go on the top if possible */
-	if (da_ac && !nm_active_connection_get_vpn (da_ac))
-		cmp++;
-	if (db_ac && !nm_active_connection_get_vpn (db_ac))
-		cmp--;
-	if (cmp != 0)
-		return cmp;
-
-	/* Default devices are prioritized */
-	if (da_ac && !nm_active_connection_get_default (da_ac))
-		cmp++;
-	if (db_ac && !nm_active_connection_get_default (db_ac))
-		cmp--;
-	if (cmp != 0)
-		return cmp;
-
-	/* Default IPv6 devices are prioritized */
-	if (da_ac && !nm_active_connection_get_default6 (da_ac))
-		cmp++;
-	if (db_ac && !nm_active_connection_get_default6 (db_ac))
-		cmp--;
-	if (cmp != 0)
-		return cmp;
-
-	/* Sort by number of addresses. */
-	da_ip = da_ac ? nm_active_connection_get_ip4_config (da_ac) : NULL;
-	da_num_addrs = da_ip ? nm_ip_config_get_addresses (da_ip)->len : 0;
-	db_ip = db_ac ? nm_active_connection_get_ip4_config (db_ac) : NULL;
-	db_num_addrs = db_ip ? nm_ip_config_get_addresses (db_ip)->len : 0;
-
-	da_ip = da_ac ? nm_active_connection_get_ip6_config (da_ac) : NULL;
-	da_num_addrs += da_ip ? nm_ip_config_get_addresses (da_ip)->len : 0;
-	db_ip = db_ac ? nm_active_connection_get_ip6_config (db_ac) : NULL;
-	db_num_addrs += db_ip ? nm_ip_config_get_addresses (db_ip)->len : 0;
-
-	cmp = db_num_addrs - da_num_addrs;
-	if (cmp != 0)
-		return cmp;
-
-	/* Fall back to alphanumeric sort by description and interface. */
-	cmp = g_strcmp0 (nm_device_get_type_description (da),
-	                 nm_device_get_type_description (db));
-	if (cmp != 0)
-		return cmp;
-
-	return g_strcmp0 (nm_device_get_iface (da),
-	                  nm_device_get_iface (db));
+	NMActiveConnection *da_ac = nm_device_get_active_connection (da);
+	NMActiveConnection *db_ac = nm_device_get_active_connection (db);
+
+	NM_CMP_DIRECT (nm_device_get_state (db), nm_device_get_state (da));
+	NM_CMP_RETURN (nmc_active_connection_cmp (db_ac, da_ac));
+	NM_CMP_DIRECT_STRCMP0 (nm_device_get_type_description (da),
+	                       nm_device_get_type_description (db));
+	NM_CMP_DIRECT_STRCMP0 (nm_device_get_iface (da),
+	                       nm_device_get_iface (db));
+	NM_CMP_DIRECT_STRCMP0 (nm_object_get_path (NM_OBJECT (da)),
+	                       nm_object_get_path (NM_OBJECT (db)));
+
+	g_return_val_if_reached (0);
 }
 
 NMDevice **
@@ -1006,9 +971,11 @@ get_device_list (NmCli *nmc, int argc, char **argv)
 
 	if (argc == 0) {
 		if (nmc->ask) {
-			char *line = nmc_readline (PROMPT_INTERFACES);
+			gs_free char *line = NULL;
+
+			line = nmc_readline (&nmc->nmc_config,
+			                     PROMPT_INTERFACES);
 			nmc_string_to_arg_array (line, NULL, FALSE, &arg_arr, &arg_num);
-			g_free (line);
 			arg_ptr = arg_arr;
 		}
 		if (arg_num == 0) {
@@ -1063,8 +1030,10 @@ get_device (NmCli *nmc, int *argc, char ***argv, GError **error)
 	int i;
 
 	if (*argc == 0) {
-		if (nmc->ask)
-			ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE);
+		if (nmc->ask) {
+			ifname = ifname_ask = nmc_readline (&nmc->nmc_config,
+			                                    PROMPT_INTERFACE);
+		}
 
 		if (!ifname_ask) {
 			g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
@@ -1869,6 +1838,7 @@ typedef struct {
 	NmCli *nmc;
 	NMDevice *device;
 	gboolean hotspot;
+	gboolean create;
 } AddAndActivateInfo;
 
 static void
@@ -1879,47 +1849,29 @@ add_and_activate_cb (GObject *client,
 	AddAndActivateInfo *info = (AddAndActivateInfo *) user_data;
 	NmCli *nmc = info->nmc;
 	NMDevice *device = info->device;
-	NMActiveConnectionState state;
 	NMActiveConnection *active;
 	GError *error = NULL;
 
-	active = nm_client_add_and_activate_connection_finish (NM_CLIENT (client), result, &error);
+	if (info->create)
+		active = nm_client_add_and_activate_connection_finish (NM_CLIENT (client), result, &error);
+	else
+		active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
 
 	if (error) {
 		if (info->hotspot)
 			g_string_printf (nmc->return_text, _("Error: Failed to setup a Wi-Fi hotspot: %s"),
 			                 error->message);
-		else
+		else if (info->create)
 			g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: %s"),
 			                 error->message);
+		else
+			g_string_printf (nmc->return_text, _("Error: Failed to activate connection: %s"),
+			                 error->message);
 		g_error_free (error);
 		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
 		quit ();
 	} else {
-		state = nm_active_connection_get_state (active);
-
-		if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) {
-			if (info->hotspot)
-				g_string_printf (nmc->return_text, _("Error: Failed to setup a Wi-Fi hotspot"));
-			else
-				g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: Unknown error"));
-			nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
-			g_object_unref (active);
-			quit ();
-		}
-
-		if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-			/* User doesn't want to wait or already activated */
-			if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-				if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
-					nmc_terminal_erase_line ();
-				if (!info->hotspot)
-					g_print (_("Connection with UUID '%s' created and activated on device '%s'\n"),
-					         nm_active_connection_get_uuid (active), nm_device_get_iface (device));
-				else
-					g_print (_("Hotspot '%s' activated on device '%s'\n"),
-					         nm_active_connection_get_id (active), nm_device_get_iface (device));
-			}
+		if (nmc->nowait_flag) {
 			g_object_unref (active);
 			quit ();
 		} else {
@@ -1927,6 +1879,8 @@ add_and_activate_cb (GObject *client,
 			g_signal_connect (device, "notify::state", G_CALLBACK (device_state_cb), active);
 			g_signal_connect (active, "notify::state", G_CALLBACK (active_state_cb), device);
 
+			connected_state_cb (device, active);
+
 			g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc);  /* Exit if timeout expires */
 
 			if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
@@ -1970,13 +1924,13 @@ connect_device_cb (GObject *client, GAsyncResult *result, gpointer user_data)
 	GError *error = NULL;
 	const GPtrArray *devices;
 	NMDevice *device;
-	NMDeviceState state;
 
 	active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
 
 	if (error) {
 		/* If no connection existed for the device, create one and activate it */
 		if (g_error_matches (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION)) {
+			info->create = TRUE;
 			create_connect_connection_for_device (info);
 			return;
 		}
@@ -1999,14 +1953,8 @@ connect_device_cb (GObject *client, GAsyncResult *result, gpointer user_data)
 		}
 
 		device = g_ptr_array_index (devices, 0);
-		state = nm_device_get_state (device);
 
-		if (nmc->nowait_flag || state == NM_DEVICE_STATE_ACTIVATED) {
-			/* Don't want to wait or device already activated */
-			if (state == NM_DEVICE_STATE_ACTIVATED && nmc->nmc_config.print_output == NMC_PRINT_PRETTY) {
-				nmc_terminal_erase_line ();
-				g_print (_("Device '%s' has been connected.\n"), nm_device_get_iface (device));
-			}
+		if (nmc->nowait_flag) {
 			g_object_unref (active);
 			quit ();
 		} else {
@@ -2020,6 +1968,9 @@ connect_device_cb (GObject *client, GAsyncResult *result, gpointer user_data)
 			g_object_ref (device);
 			g_signal_connect (device, "notify::state", G_CALLBACK (device_state_cb), active);
 			g_signal_connect (active, "notify::state", G_CALLBACK (active_state_cb), device);
+
+			connected_state_cb (device, active);
+
 			/* Start timer not to loop forever if "notify::state" signal is not issued */
 			g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc);
 		}
@@ -2518,7 +2469,7 @@ do_device_set (NmCli *nmc, int argc, char **argv)
 		return error->code;
 	}
 
-        if (!argc) {
+	if (!argc) {
 		g_string_printf (nmc->return_text, _("Error: No property specified."));
 		return NMC_RESULT_ERROR_USER_INPUT;
 	}
@@ -2717,7 +2668,7 @@ find_wifi_device_by_iface (NMDevice **devices, const char *iface, int *idx)
 }
 
 /*
- * Find AP on 'device' according to 'bssid' or 'ssid' parameter.
+ * Find AP on 'device' according to 'bssid' and 'ssid' parameters.
  * Returns: found AP or NULL
  */
 static NMAccessPoint *
@@ -2734,17 +2685,17 @@ find_ap_on_device (NMDevice *device, const char *bssid, const char *ssid, gboole
 		NMAccessPoint *candidate_ap = g_ptr_array_index (aps, i);
 
 		if (bssid) {
-			/* Parameter is BSSID */
 			const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap);
 
+			if (!candidate_bssid)
+				continue;
+
 			/* Compare BSSIDs */
 			if (complete) {
 				if (g_str_has_prefix (candidate_bssid, bssid))
 					g_print ("%s\n", candidate_bssid);
-			} else if (strcmp (bssid, candidate_bssid) == 0) {
-				ap = candidate_ap;
-				break;
-			}
+			} else if (strcmp (bssid, candidate_bssid) != 0)
+				continue;
 		}
 
 		if (ssid) {
@@ -2763,13 +2714,18 @@ find_ap_on_device (NMDevice *device, const char *bssid, const char *ssid, gboole
 			if (complete) {
 				if (g_str_has_prefix (ssid_tmp, ssid))
 					g_print ("%s\n", ssid_tmp);
-			} else if (strcmp (ssid, ssid_tmp) == 0) {
-				ap = candidate_ap;
+			} else if (strcmp (ssid, ssid_tmp) != 0) {
 				g_free (ssid_tmp);
-				break;
+				continue;
 			}
 			g_free (ssid_tmp);
 		}
+
+		if (complete)
+			continue;
+
+		ap = candidate_ap;
+		break;
 	}
 
 	return ap;
@@ -2804,7 +2760,7 @@ show_access_point_info (NMDeviceWifi *wifi, NmCli *nmc, NmcOutputData *out)
 
 		aps = sort_access_points (nm_device_wifi_get_access_points (wifi));
 		g_ptr_array_foreach (aps, fill_output_access_point, &info);
-		g_ptr_array_free (aps, FALSE);
+		g_ptr_array_free (aps, TRUE);
 	}
 
 	print_data_prepare_width (out->output_data);
@@ -2879,28 +2835,38 @@ wifi_print_aps (NMDeviceWifi *wifi,
 
 typedef struct {
 	NmCli *nmc;
-	NMDeviceWifi *wifi;
-	const NMMetaAbstractInfo *const*tmpl;
-
+	NMDevice **devices;
+	const NMMetaAbstractInfo *const *tmpl;
 	const char *bssid_user;
+	GArray *out_indices;
+} ScanInfo;
+
+typedef struct {
+	ScanInfo *scan_info;
+	NMDeviceWifi *wifi;
 	gulong last_scan_id;
 	guint  timeout_id;
 	GCancellable *scan_cancellable;
-	GArray *out_indices;
 } WifiListData;
 
 static void
 wifi_list_finish (WifiListData *data)
 {
-	NmCli *nmc = data->nmc;
-
-	wifi_print_aps (data->wifi, data->nmc, data->out_indices,
-	                data->tmpl, data->bssid_user);
+	ScanInfo *info = data->scan_info;
+	NmCli *nmc = info->nmc;
+	guint i;
 
-	if (--nmc->should_wait == 0) {
+	if (--info->nmc->should_wait == 0) {
+		for (i = 0; info->devices[i]; i++) {
+			wifi_print_aps (NM_DEVICE_WIFI (info->devices[i]),
+			                info->nmc,
+			                info->out_indices,
+			                info->tmpl,
+			                info->bssid_user);
+		}
 		if (nmc->return_value == NMC_RESULT_ERROR_NOT_FOUND) {
 			g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."),
-			                 data->bssid_user);
+			                 data->scan_info->bssid_user);
 		}
 		g_main_loop_quit (loop);
 	}
@@ -2908,9 +2874,15 @@ wifi_list_finish (WifiListData *data)
 	g_signal_handler_disconnect (data->wifi, data->last_scan_id);
 	nm_clear_g_source (&data->timeout_id);
 	nm_clear_g_cancellable (&data->scan_cancellable);
-	g_array_unref (data->out_indices);
-	g_object_unref (data->wifi);
 	g_slice_free (WifiListData, data);
+
+	if (info->nmc->should_wait == 0) {
+		for (i = 0; info->devices[i]; i++)
+			g_object_unref (info->devices[i]);
+		g_free (info->devices);
+		g_array_unref (info->out_indices);
+		g_free (info);
+	}
 }
 
 static void
@@ -2955,49 +2927,6 @@ wifi_list_scan_timeout (gpointer user_data)
 }
 
 static void
-wifi_list_aps (NMDeviceWifi *wifi,
-               NmCli *nmc,
-               GArray *out_indices,
-               const NMMetaAbstractInfo *const*tmpl,
-               const char *bssid_user,
-               gint64 rescan_cutoff)
-{
-	gboolean needs_rescan;
-	WifiListData *data;
-
-	needs_rescan = rescan_cutoff < 0 || (rescan_cutoff > 0 && nm_device_wifi_get_last_scan (wifi) < rescan_cutoff);
-
-	/* FIXME: nmcli should either
-	 *  - don't request any new scan for any device and print the full AP list right
-	 *    away.
-	 *  - or, when requesting a scan on one or more devices, don't print the result
-	 *    before all requests complete.
-	 *
-	 *  Otherwise:
-	 *    - the printed output is not self consistent. E.g. it will print the result
-	 *      on one device at a certain time, while printing the result for another
-	 *      device at a later point in time.
-	 *    - the order in which we print the AP list per-device, is unstable. */
-	if (needs_rescan) {
-		data = g_slice_new0 (WifiListData);
-		data->nmc = nmc;
-		data->wifi = g_object_ref (wifi);
-		data->tmpl = tmpl;
-		data->out_indices = g_array_ref (out_indices);;
-		data->bssid_user = bssid_user;
-		data->last_scan_id = g_signal_connect (wifi, "notify::" NM_DEVICE_WIFI_LAST_SCAN,
-		                                       G_CALLBACK (wifi_last_scan_updated), data);
-		data->scan_cancellable = g_cancellable_new ();
-		data->timeout_id = g_timeout_add_seconds (15, wifi_list_scan_timeout, data);
-		nm_device_wifi_request_scan_async (wifi, data->scan_cancellable, wifi_list_rescan_cb, data);
-
-		nmc->should_wait++;
-	} else {
-		wifi_print_aps (wifi, nmc, out_indices, tmpl, bssid_user);
-	}
-}
-
-static void
 complete_aps (NMDevice **devices, const char *ifname,
               const char *bssid_prefix, const char *ssid_prefix)
 {
@@ -3026,12 +2955,15 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 	const char *bssid_user = NULL;
 	const char *rescan = NULL;
 	gs_free NMDevice **devices = NULL;
-	guint i;
 	const char *fields_str = NULL;
 	const NMMetaAbstractInfo *const*tmpl;
 	gs_unref_array GArray *out_indices = NULL;
 	int option;
 	guint64 rescan_cutoff;
+	NMDeviceWifi *wifi;
+	ScanInfo *scan_info = NULL;
+	WifiListData *data;
+	guint i, j;
 
 	devices = nmc_get_devices_sorted (nmc->client);
 
@@ -3119,7 +3051,8 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 		}
 
 		if (NM_IS_DEVICE_WIFI (device)) {
-			wifi_list_aps (NM_DEVICE_WIFI (device), nmc, out_indices, tmpl, bssid_user, rescan_cutoff);
+			devices[0] = device;
+			devices[1] = NULL;
 		} else {
 			if (   nm_device_get_device_type (device) == NM_DEVICE_TYPE_GENERIC
 			    && g_strcmp0 (nm_device_get_type_description (device), "wifi") == 0) {
@@ -3133,13 +3066,52 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 			}
 			return NMC_RESULT_ERROR_UNKNOWN;
 		}
+	}
+
+	/* Filter out non-wifi devices */
+	for (i = 0, j = 0; devices[i]; i++) {
+		if (NM_IS_DEVICE_WIFI (devices[i]))
+			devices[j++] = devices[i];
+	}
+	devices[j] = NULL;
+
+	/* Start a new scan for devices that need it */
+	for (i = 0; devices[i]; i++) {
+		wifi = (NMDeviceWifi *) devices[i];
+		g_object_ref (wifi);
+
+		if (   rescan_cutoff == 0
+		    || (rescan_cutoff > 0 && nm_device_wifi_get_last_scan (wifi) >= rescan_cutoff))
+			continue;
+
+		if (!scan_info) {
+			scan_info = g_new0 (ScanInfo, 1);
+			scan_info->out_indices = g_array_ref (out_indices);
+			scan_info->tmpl = tmpl;
+			scan_info->bssid_user = bssid_user;
+			scan_info->nmc = nmc;
+		}
+
+		nmc->should_wait++;
+		data = g_slice_new0 (WifiListData);
+		data->wifi = wifi;
+		data->scan_info = scan_info;
+		data->last_scan_id = g_signal_connect (wifi, "notify::" NM_DEVICE_WIFI_LAST_SCAN,
+		                                       G_CALLBACK (wifi_last_scan_updated), data);
+		data->scan_cancellable = g_cancellable_new ();
+		data->timeout_id = g_timeout_add_seconds (15, wifi_list_scan_timeout, data);
+		nm_device_wifi_request_scan_async (wifi, data->scan_cancellable, wifi_list_rescan_cb, data);
+	}
+
+	if (scan_info) {
+		scan_info->devices = g_steal_pointer (&devices);
 	} else {
+		/* Print results right away if no scan is pending */
 		for (i = 0; devices[i]; i++) {
-			NMDevice *dev = devices[i];
-
-			if (NM_IS_DEVICE_WIFI (dev)) {
-				wifi_list_aps (NM_DEVICE_WIFI (dev), nmc, out_indices, tmpl, bssid_user, rescan_cutoff);
-			}
+			wifi_print_aps (NM_DEVICE_WIFI (devices[i]),
+			                nmc, out_indices,
+			                tmpl, bssid_user);
+			g_object_unref (devices[i]);
 		}
 	}
 
@@ -3157,7 +3129,6 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 	NMConnection *connection = NULL;
 	NMSettingConnection *s_con;
 	NMSettingWireless *s_wifi;
-	NMSettingWirelessSecurity *s_wsec;
 	AddAndActivateInfo *info;
 	const char *param_user = NULL;
 	const char *ifname = NULL;
@@ -3173,6 +3144,10 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 	int devices_idx;
 	char *ssid_ask = NULL;
 	char *passwd_ask = NULL;
+	const GPtrArray *avail_cons;
+	gboolean name_match = FALSE;
+	gboolean existing_con = FALSE;
+	int i;
 
 	/* Set default timeout waiting for operation completion. */
 	if (nmc->timeout == -1)
@@ -3195,7 +3170,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 		g_assert (!nmc->complete);
 
 		if (nmc->ask) {
-			ssid_ask = nmc_readline (_("SSID or BSSID: "));
+			ssid_ask = nmc_readline (&nmc->nmc_config, _("SSID or BSSID: "));
 			param_user = ssid_ask ?: "";
 			bssid1_arr = nm_utils_hwaddr_atoba (param_user, ETH_ALEN);
 		}
@@ -3377,14 +3352,14 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 	}
 
 	/* Find an AP to connect to */
-	ap = find_ap_on_device (device, bssid1_arr ? param_user : NULL,
+	ap = find_ap_on_device (device, bssid1_arr ? param_user : bssid,
 	                                bssid1_arr ? NULL : param_user, FALSE);
 	if (!ap && !ifname) {
 		NMDevice *dev;
 
 		/* AP not found, ifname was not specified, so try finding the AP on another device. */
 		while ((dev = find_wifi_device_by_iface (devices, NULL, &devices_idx)) != NULL) {
-			ap = find_ap_on_device (dev, bssid1_arr ? param_user : NULL,
+			ap = find_ap_on_device (dev, bssid1_arr ? param_user : bssid,
 			                             bssid1_arr ? NULL : param_user, FALSE);
 			if (ap) {
 				device = dev;
@@ -3402,45 +3377,75 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 		goto finish;
 	}
 
-	/* If there are some connection data from user, create a connection and
-	 * fill them into proper settings. */
-	if (con_name || private || bssid2_arr || password || hidden)
-		connection = nm_simple_connection_new ();
+	avail_cons = nm_device_get_available_connections (device);
+	for (i = 0; i < avail_cons->len; i++) {
+		NMRemoteConnection *avail_con = g_ptr_array_index (avail_cons, i);
+		const char *id = nm_connection_get_id (NM_CONNECTION (avail_con));
 
-	if (con_name || private) {
-		s_con =  (NMSettingConnection *) nm_setting_connection_new ();
-		nm_connection_add_setting (connection, NM_SETTING (s_con));
+		if (con_name) {
+			if (!id || strcmp (id, con_name))
+				continue;
 
-		/* Set user provided connection name */
-		if (con_name)
-			g_object_set (s_con, NM_SETTING_CONNECTION_ID, con_name, NULL);
+			name_match = TRUE;
+		}
+
+		if (nm_access_point_connection_valid (ap, NM_CONNECTION (avail_con))) {
+			/* ap has been checked against bssid1, bssid2 and the ssid
+			 * and now avail_con has been checked against ap.
+			 */
+			connection = NM_CONNECTION (avail_con);
+			existing_con = TRUE;
+			break;
+		}
+	}
 
-		/* Connection will only be visible to this user when 'private' is specified */
-		if (private)
-			nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL);
+	if (name_match && !existing_con) {
+		g_string_printf (nmc->return_text, _("Error: Connection '%s' exists but properties don't match."), con_name);
+		nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
+		goto finish;
 	}
-	if (bssid2_arr || hidden) {
-		s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
-		nm_connection_add_setting (connection, NM_SETTING (s_wifi));
-
-		/* 'bssid' parameter is used to restrict the connection only to the BSSID */
-		if (bssid2_arr)
-			g_object_set (s_wifi, NM_SETTING_WIRELESS_BSSID, bssid2_arr, NULL);
-
-		/* 'hidden' parameter is used to indicate that SSID is not broadcasted */
-		if (hidden) {
-			GBytes *ssid = g_bytes_new (param_user, strlen (param_user));
-
-			g_object_set (s_wifi,
-			              NM_SETTING_WIRELESS_SSID, ssid,
-			              NM_SETTING_WIRELESS_HIDDEN, hidden,
-			              NULL);
-			g_bytes_unref (ssid);
-
-			/* Warn when the provided AP identifier looks like BSSID instead of SSID */
-			if (bssid1_arr)
-				g_printerr (_("Warning: '%s' should be SSID for hidden APs; but it looks like a BSSID.\n"),
-				               param_user);
+
+	if (!existing_con) {
+		/* If there are some connection data from user, create a connection and
+		 * fill them into proper settings. */
+		if (con_name || private || bssid2_arr || password || hidden)
+			connection = nm_simple_connection_new ();
+
+		if (con_name || private) {
+			s_con =  (NMSettingConnection *) nm_setting_connection_new ();
+			nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+			/* Set user provided connection name */
+			if (con_name)
+				g_object_set (s_con, NM_SETTING_CONNECTION_ID, con_name, NULL);
+
+			/* Connection will only be visible to this user when 'private' is specified */
+			if (private)
+				nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL);
+		}
+		if (bssid2_arr || hidden) {
+			s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+			nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+			/* 'bssid' parameter is used to restrict the connection only to the BSSID */
+			if (bssid2_arr)
+				g_object_set (s_wifi, NM_SETTING_WIRELESS_BSSID, bssid2_arr, NULL);
+
+			/* 'hidden' parameter is used to indicate that SSID is not broadcasted */
+			if (hidden) {
+				GBytes *ssid = g_bytes_new (param_user, strlen (param_user));
+
+				g_object_set (s_wifi,
+				              NM_SETTING_WIRELESS_SSID, ssid,
+				              NM_SETTING_WIRELESS_HIDDEN, hidden,
+				              NULL);
+				g_bytes_unref (ssid);
+
+				/* Warn when the provided AP identifier looks like BSSID instead of SSID */
+				if (bssid1_arr)
+					g_printerr (_("Warning: '%s' should be SSID for hidden APs; but it looks like a BSSID.\n"),
+					               param_user);
+			}
 		}
 	}
 
@@ -3453,15 +3458,37 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 	if (   (ap_flags & NM_802_11_AP_FLAGS_PRIVACY)
 	    || ap_wpa_flags != NM_802_11_AP_SEC_NONE
 	    || ap_rsn_flags != NM_802_11_AP_SEC_NONE) {
+		const char *con_password = NULL;
+		NMSettingWirelessSecurity *s_wsec = NULL;
+
+		if (connection) {
+			s_wsec = nm_connection_get_setting_wireless_security (connection);
+			if (s_wsec) {
+				if (ap_wpa_flags == NM_802_11_AP_SEC_NONE && ap_rsn_flags == NM_802_11_AP_SEC_NONE) {
+					/* WEP */
+					con_password = nm_setting_wireless_security_get_wep_key (s_wsec, 0);
+				} else if (   (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
+				           || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) {
+					/* WPA PSK */
+					con_password = nm_setting_wireless_security_get_psk (s_wsec);
+				}
+			}
+		}
+
 		/* Ask for missing password when one is expected and '--ask' is used */
-		if (!password && nmc->ask)
-			password = passwd_ask = nmc_readline_echo (nmc->nmc_config.show_secrets, _("Password: "));
+		if (!password && !con_password && nmc->ask) {
+			password = passwd_ask = nmc_readline_echo (&nmc->nmc_config,
+			                                           nmc->nmc_config.show_secrets,
+			                                           _("Password: "));
+		}
 
 		if (password) {
 			if (!connection)
 				connection = nm_simple_connection_new ();
-			s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
-			nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+			if (!s_wsec) {
+				s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+				nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+			}
 
 			if (ap_wpa_flags == NM_802_11_AP_SEC_NONE && ap_rsn_flags == NM_802_11_AP_SEC_NONE) {
 				/* WEP */
@@ -3477,7 +3504,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 			}
 		}
 	}
-	// FIXME: WPA-Enterprise is not supported yet.
+	// FIXME: Creating WPA-Enterprise connections is not supported yet.
 	// We are not able to determine and fill all the parameters for
 	// 802.1X authentication automatically without user providing
 	// the data. Adding nmcli options for the 8021x setting would
@@ -3495,14 +3522,24 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 	info->nmc = nmc;
 	info->device = device;
 	info->hotspot = FALSE;
-
-	nm_client_add_and_activate_connection_async (nmc->client,
-	                                             connection,
-	                                             device,
-	                                             nm_object_get_path (NM_OBJECT (ap)),
-	                                             NULL,
-	                                             add_and_activate_cb,
-	                                             info);
+	info->create = !existing_con;
+	if (existing_con) {
+		nm_client_activate_connection_async (nmc->client,
+		                                     connection,
+		                                     device,
+		                                     nm_object_get_path (NM_OBJECT (ap)),
+		                                     NULL,
+		                                     add_and_activate_cb,
+		                                     info);
+	} else {
+		nm_client_add_and_activate_connection_async (nmc->client,
+		                                             connection,
+		                                             device,
+		                                             nm_object_get_path (NM_OBJECT (ap)),
+		                                             NULL,
+		                                             add_and_activate_cb,
+		                                             info);
+	}
 
 finish:
 	if (bssid1_arr)
diff --git a/clients/cli/general.c b/clients/cli/general.c
index cb87c110..d9128c76 100644
--- a/clients/cli/general.c
+++ b/clients/cli/general.c
@@ -37,7 +37,7 @@
 
 /*****************************************************************************/
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (nm_state_to_string_no_l10n, NMState,
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (nm_state_to_string, NMState,
 	NM_UTILS_LOOKUP_DEFAULT (N_("unknown")),
 	NM_UTILS_LOOKUP_ITEM (NM_STATE_ASLEEP,           N_("asleep")),
 	NM_UTILS_LOOKUP_ITEM (NM_STATE_CONNECTING,       N_("connecting")),
@@ -49,12 +49,6 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (nm_state_to_string_no_l10n, NMState,
 	NM_UTILS_LOOKUP_ITEM_IGNORE (NM_STATE_UNKNOWN),
 );
 
-static const char *
-nm_state_to_string (NMState state)
-{
-	return _(nm_state_to_string_no_l10n (state));
-}
-
 static NMMetaColor
 state_to_color (NMState state)
 {
@@ -78,21 +72,6 @@ state_to_color (NMState state)
 	}
 }
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (nm_connectivity_to_string_no_l10n, NMConnectivityState,
-	NM_UTILS_LOOKUP_DEFAULT (N_("unknown")),
-	NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_NONE,    N_("none")),
-	NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_PORTAL,  N_("portal")),
-	NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_LIMITED, N_("limited")),
-	NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_FULL,    N_("full")),
-	NM_UTILS_LOOKUP_ITEM_IGNORE (NM_CONNECTIVITY_UNKNOWN),
-);
-
-static const char *
-nm_connectivity_to_string (NMConnectivityState connectivity)
-{
-	return _(nm_connectivity_to_string_no_l10n (connectivity));
-}
-
 static NMMetaColor
 connectivity_to_color (NMConnectivityState connectivity)
 {
@@ -151,7 +130,7 @@ permission_to_string (NMClientPermission perm)
 	}
 }
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (permission_result_to_string_no_l10n, NMClientPermissionResult,
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (permission_result_to_string, NMClientPermissionResult,
 	NM_UTILS_LOOKUP_DEFAULT (N_("unknown")),
 	NM_UTILS_LOOKUP_ITEM (NM_CLIENT_PERMISSION_RESULT_YES,  N_("yes")),
 	NM_UTILS_LOOKUP_ITEM (NM_CLIENT_PERMISSION_RESULT_NO,   N_("no")),
@@ -192,7 +171,7 @@ _metagen_general_status_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_STATE:
 		state = nm_client_get_state (nmc->client);
 		NMC_HANDLE_COLOR (state_to_color (state));
-		value = nm_state_to_string_no_l10n (state);
+		value = nm_state_to_string (state);
 		goto translate_and_out;
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_STARTUP:
 		v_bool = nm_client_get_startup (nmc->client);
@@ -202,7 +181,7 @@ _metagen_general_status_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_CONNECTIVITY:
 		connectivity = nm_client_get_connectivity (nmc->client);
 		NMC_HANDLE_COLOR (connectivity_to_color (connectivity));
-		value = nm_connectivity_to_string_no_l10n (connectivity);
+		value = nm_connectivity_to_string (connectivity);
 		goto translate_and_out;
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_NETWORKING:
 		v_bool = nm_client_networking_get_enabled (nmc->client);
@@ -221,7 +200,7 @@ _metagen_general_status_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 		goto enabled_out;
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WIMAX_HW:
 	case NMC_GENERIC_INFO_TYPE_GENERAL_STATUS_WIMAX:
-		/* deprected fields. Don't return anything. */
+		/* deprecated fields. Don't return anything. */
 		return NULL;
 	default:
 		break;
@@ -286,7 +265,7 @@ _metagen_general_permissions_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
 	case NMC_GENERIC_INFO_TYPE_GENERAL_PERMISSIONS_VALUE:
 		perm_result = nm_client_get_permission_result (nmc->client, perm);
 		NMC_HANDLE_COLOR (permission_result_to_color (perm_result));
-		s = permission_result_to_string_no_l10n (perm_result);
+		s = permission_result_to_string (perm_result);
 		if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
 			return _(s);
 		return s;
@@ -561,8 +540,7 @@ print_permissions (void *user_data)
 		permissions[i++] = GINT_TO_POINTER (perm);
 	permissions[i++] = NULL;
 
-	/* Optionally start paging the output. */
-	nmc_terminal_spawn_pager (&nmc->nmc_config);
+	nm_cli_spawn_pager (nmc);
 
 	if (!nmc_print (&nmc->nmc_config,
 	                permissions,
@@ -998,7 +976,7 @@ do_radio_wifi (NmCli *nmc, int argc, char **argv)
 		if (nmc->complete)
 			return nmc->return_value;
 
-		/* no argument, show current WiFi state */
+		/* no argument, show current Wi-Fi state */
 		nmc_switch_show (nmc, NMC_FIELDS_NM_WIFI, N_("Wi-Fi radio switch"));
 	} else {
 		if (nmc->complete) {
@@ -1114,7 +1092,8 @@ client_connectivity (NMClient *client, GParamSpec *param, NmCli *nmc)
 
 	g_object_get (client, NM_CLIENT_CONNECTIVITY, &connectivity, NULL);
 	str = nmc_colorize (&nmc->nmc_config, connectivity_to_color (connectivity),
-	                    _("Connectivity is now '%s'\n"), nm_connectivity_to_string (connectivity));
+	                    _("Connectivity is now '%s'\n"),
+	                    gettext (nm_connectivity_to_string (connectivity)));
 	g_print ("%s", str);
 	g_free (str);
 }
@@ -1128,7 +1107,7 @@ client_state (NMClient *client, GParamSpec *param, NmCli *nmc)
 	g_object_get (client, NM_CLIENT_STATE, &state, NULL);
 	str = nmc_colorize (&nmc->nmc_config, state_to_color (state),
 	                    _("Networkmanager is now in the '%s' state\n"),
-	                    nm_state_to_string (state));
+	                    gettext (nm_state_to_string (state)));
 	g_print ("%s", str);
 	g_free (str);
 }
@@ -1285,8 +1264,7 @@ do_overview (NmCli *nmc, int argc, char **argv)
 	/* Register polkit agent */
 	nmc_start_polkit_agent_start_try (nmc);
 
-	/* Optionally start paging the output. */
-	nmc_terminal_spawn_pager (&nmc->nmc_config);
+	nm_cli_spawn_pager (nmc);
 
 	/* The VPN connections don't have devices (yet?). */
 	p = nm_client_get_active_connections (nmc->client);
diff --git a/clients/cli/meson.build b/clients/cli/meson.build
index c5669c96..11fe1cd1 100644
--- a/clients/cli/meson.build
+++ b/clients/cli/meson.build
@@ -3,7 +3,7 @@ name = 'nmcli'
 # FIXME: nmcli-completion should be renamed to nmcli
 install_data(
   'nmcli-completion',
-  install_dir: join_paths(nm_datadir, 'bash-completion', 'completions')
+  install_dir: join_paths(nm_datadir, 'bash-completion', 'completions'),
 )
 
 sources = files(
@@ -15,7 +15,7 @@ sources = files(
   'nmcli.c',
   'polkit-agent.c',
   'settings.c',
-  'utils.c'
+  'utils.c',
 )
 
 deps = [
@@ -23,7 +23,7 @@ deps = [
   libnmc_base_dep,
   libnmc_dep,
   nm_core_dep,
-  readline_dep
+  readline_dep,
 ]
 
 cflags = clients_cflags + [
@@ -43,5 +43,5 @@ executable(
   c_args: cflags,
   link_args: ldflags_linker_script_binary,
   link_depends: linker_script_binary,
-  install: true
+  install: true,
 )
diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c
index bbfc310d..7e8477cc 100644
--- a/clients/cli/nmcli.c
+++ b/clients/cli/nmcli.c
@@ -245,19 +245,19 @@ usage (void)
 	g_printerr (_("Usage: nmcli [OPTIONS] OBJECT { COMMAND | help }\n"
 	              "\n"
 	              "OPTIONS\n"
-	              "  -o[verview]                                    overview mode (hide default values)\n"
-	              "  -t[erse]                                       terse output\n"
-	              "  -p[retty]                                      pretty output\n"
-	              "  -m[ode] tabular|multiline                      output mode\n"
-	              "  -c[olors] auto|yes|no                          whether to use colors in output\n"
-	              "  -f[ields] <field1,field2,...>|all|common       specify fields to output\n"
-	              "  -g[et-values] <field1,field2,...>|all|common   shortcut for -m tabular -t -f\n"
-	              "  -e[scape] yes|no                               escape columns separators in values\n"
-	              "  -a[sk]                                         ask for missing parameters\n"
-	              "  -s[how-secrets]                                allow displaying passwords\n"
-	              "  -w[ait] <seconds>                              set timeout waiting for finishing operations\n"
-	              "  -v[ersion]                                     show program version\n"
-	              "  -h[elp]                                        print this help\n"
+	              "  -a, --ask                                ask for missing parameters\n"
+	              "  -c, --colors auto|yes|no                 whether to use colors in output\n"
+	              "  -e, --escape yes|no                      escape columns separators in values\n"
+	              "  -f, --fields <field,...>|all|common      specify fields to output\n"
+	              "  -g, --get-values <field,...>|all|common  shortcut for -m tabular -t -f\n"
+	              "  -h, --help                               print this help\n"
+	              "  -m, --mode tabular|multiline             output mode\n"
+	              "  -o, --overview                           overview mode\n"
+	              "  -p, --pretty                             pretty output\n"
+	              "  -s, --show-secrets                       allow displaying passwords\n"
+	              "  -t, --terse                              terse output\n"
+	              "  -v, --version                            how program version\n"
+	              "  -w, --wait <seconds>                     set timeout waiting for finishing operations\n"
 	              "\n"
 	              "OBJECT\n"
 	              "  g[eneral]       NetworkManager's general status and operations\n"
@@ -998,6 +998,14 @@ nmc_value_transforms_register (void)
 	                                 nmc_convert_bytes_to_string);
 }
 
+void
+nm_cli_spawn_pager (NmCli *nmc)
+{
+	if (nmc->pager_pid > 0)
+		return;
+	nmc->pager_pid = nmc_terminal_spawn_pager (&nmc->nmc_config);
+}
+
 static void
 nmc_cleanup (NmCli *nmc)
 {
diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h
index 28616855..0ccf1653 100644
--- a/clients/cli/nmcli.h
+++ b/clients/cli/nmcli.h
@@ -167,6 +167,8 @@ void     nmc_clear_sigint (void);
 void     nmc_set_sigquit_internal (void);
 void     nmc_exit (void);
 
+void nm_cli_spawn_pager (NmCli *nmc);
+
 void nmc_empty_output_fields (NmcOutputData *output_data);
 
 #define NMC_OUTPUT_DATA_DEFINE_SCOPED(out) \
diff --git a/clients/cli/polkit-agent.c b/clients/cli/polkit-agent.c
index 338f0b15..b895599b 100644
--- a/clients/cli/polkit-agent.c
+++ b/clients/cli/polkit-agent.c
@@ -40,24 +40,25 @@ polkit_request (NMPolkitListener *listener,
                 gboolean echo_on,
                 gpointer user_data)
 {
-	char *response, *tmp, *p;
+	NmCli *nmc = user_data;
 
 	g_print ("%s\n", message);
 	g_print ("(action_id: %s)\n", action_id);
 
 	/* Ask user for polkit authorization password */
 	if (user) {
+		gs_free char *tmp = NULL;
+		char *p;
+
 		/* chop of ": " if present */
 		tmp = g_strdup (request);
 		p = strrchr (tmp, ':');
-		if (p && !strcmp (p, ": "))
+		if (p && nm_streq (p, ": "))
 			*p = '\0';
-		response = nmc_readline_echo (echo_on, "%s (%s): ", tmp, user);
-		g_free (tmp);
-	} else
-		response = nmc_readline_echo (echo_on, "%s", request);
+		return nmc_readline_echo (&nmc->nmc_config, echo_on, "%s (%s): ", tmp, user);
+	}
 
-	return response;
+	return nmc_readline_echo (&nmc->nmc_config, echo_on, "%s", request);
 }
 
 static void
diff --git a/clients/cli/settings.c b/clients/cli/settings.c
index 2d231e1c..a04c8eb6 100644
--- a/clients/cli/settings.c
+++ b/clients/cli/settings.c
@@ -307,7 +307,8 @@ nmc_setting_connection_connect_handlers (NMSettingConnection *setting, NMConnect
 /*****************************************************************************/
 
 static gboolean
-_set_fcn_precheck_connection_secondaries (const char *value,
+_set_fcn_precheck_connection_secondaries (NMClient *client,
+                                          const char *value,
                                           char **value_coerced,
                                           GError **error)
 {
@@ -322,7 +323,7 @@ _set_fcn_precheck_connection_secondaries (const char *value,
 	if (!strv0)
 		return TRUE;
 
-	connections = nm_client_get_connections (nm_cli.client);
+	connections = nm_client_get_connections (client);
 
 	strv = g_strdupv ((char **) strv0);
 	for (iter = strv; *iter; iter++) {
@@ -332,7 +333,7 @@ _set_fcn_precheck_connection_secondaries (const char *value,
 				g_print (_("Warning: %s is not an UUID of any existing connection profile\n"),
 				         *iter);
 			} else {
-				/* Currenly NM only supports VPN connections as secondaries */
+				/* Currently NM only supports VPN connections as secondaries */
 				if (!nm_connection_is_type (con, NM_SETTING_VPN_SETTING_NAME)) {
 					g_set_error (error, 1, 0, _("'%s' is not a VPN connection profile"), *iter);
 					return FALSE;
@@ -345,7 +346,7 @@ _set_fcn_precheck_connection_secondaries (const char *value,
 				return FALSE;
 			}
 
-			/* Currenly NM only supports VPN connections as secondaries */
+			/* Currently NM only supports VPN connections as secondaries */
 			if (!nm_connection_is_type (con, NM_SETTING_VPN_SETTING_NAME)) {
 				g_set_error (error, 1, 0, _("'%s' is not a VPN connection profile"), *iter);
 				return FALSE;
@@ -531,7 +532,7 @@ _set_fcn_call (const NMMetaPropertyInfo *property_info,
  * Returns: TRUE on success; FALSE on failure and sets error
  */
 gboolean
-nmc_setting_set_property (NMSetting *setting, const char *prop, const char *value, GError **error)
+nmc_setting_set_property (NMClient *client, NMSetting *setting, const char *prop, const char *value, GError **error)
 {
 	const NMMetaPropertyInfo *property_info;
 
@@ -552,7 +553,7 @@ nmc_setting_set_property (NMSetting *setting, const char *prop, const char *valu
 				if (nm_streq (property_info->property_name, NM_SETTING_CONNECTION_SECONDARIES)) {
 					gs_free char *value_coerced = NULL;
 
-					if (!_set_fcn_precheck_connection_secondaries (value, &value_coerced, error))
+					if (!_set_fcn_precheck_connection_secondaries (client, value, &value_coerced, error))
 						return FALSE;
 
 					return _set_fcn_call (property_info,
@@ -590,11 +591,11 @@ nmc_property_set_default_value (NMSetting *setting, const char *prop)
 }
 
 /*
- * Generic function for reseting (single value) properties.
+ * Generic function for resetting (single value) properties.
  *
  * The function resets the property value to the default one. It respects
  * nmcli restrictions for changing properties. So if 'set_func' is NULL,
- * reseting the value is denied.
+ * resetting the value is denied.
  *
  * Returns: TRUE on success; FALSE on failure and sets error
  */
diff --git a/clients/cli/settings.h b/clients/cli/settings.h
index 7dad622b..4e7e38df 100644
--- a/clients/cli/settings.h
+++ b/clients/cli/settings.h
@@ -42,7 +42,8 @@ char       *nmc_setting_get_property (NMSetting *setting,
 char       *nmc_setting_get_property_parsable (NMSetting *setting,
                                                const char *prop,
                                                GError **error);
-gboolean    nmc_setting_set_property (NMSetting *setting,
+gboolean    nmc_setting_set_property (NMClient *client,
+                                      NMSetting *setting,
                                       const char *prop,
                                       const char *val,
                                       GError **error);
diff --git a/clients/cli/utils.c b/clients/cli/utils.c
index e21c108d..1b940467 100644
--- a/clients/cli/utils.c
+++ b/clients/cli/utils.c
@@ -36,6 +36,7 @@
 #include "nm-meta-setting-access.h"
 
 #include "common.h"
+#include "nmcli.h"
 #include "settings.h"
 
 #define ML_HEADER_WIDTH 79
@@ -919,6 +920,8 @@ nmc_empty_output_fields (NmcOutputData *output_data)
 	/* Empty output_data array */
 	if (output_data->output_data->len > 0)
 		g_ptr_array_remove_range (output_data->output_data, 0, output_data->output_data->len);
+
+	g_ptr_array_unref (output_data->output_data);
 }
 
 /*****************************************************************************/
@@ -1098,7 +1101,7 @@ _print_fill (const NmcConfig *nmc_config,
 				/* don't mark the entry for display. This is to shorten the output in case
 				 * the property is the default value. But we only do that, if the user
 				 * opts in to this behavior (-overview), or of the property marks itself
-				 * elegible to be hidden.
+				 * eligible to be hidden.
 				 *
 				 * In general, only new API shall mark itself eligible to be hidden.
 				 * Long established properties cannot, because it would be a change
@@ -1273,7 +1276,7 @@ _print_do (const NmcConfig *nmc_config,
 			title = header_cell->title;
 
 			width1 = strlen (title);
-			width2 = nmc_string_screen_width (title, NULL);  /* Width of the string (in screen colums) */
+			width2 = nmc_string_screen_width (title, NULL);  /* Width of the string (in screen columns) */
 			g_string_append_printf (str, "%-*s", (int) (header_cell->width + width1 - width2), title);
 			g_string_append_c (str, ' ');  /* Column separator */
 			table_width += header_cell->width + width1 - width2 + 1;
@@ -1355,7 +1358,7 @@ _print_do (const NmcConfig *nmc_config,
 						const PrintDataHeaderCell *header_cell = &header_row[i_col];
 
 						width1 = strlen (text);
-						width2 = nmc_string_screen_width (text, NULL);  /* Width of the string (in screen colums) */
+						width2 = nmc_string_screen_width (text, NULL);  /* Width of the string (in screen columns) */
 						g_string_append_printf (str, "%-*s", (int) (header_cell->width + width1 - width2), text);
 						g_string_append_c (str, ' ');  /* Column separator */
 						table_width += header_cell->width + width1 - width2 + 1;
@@ -1445,38 +1448,38 @@ pager_fallback (void)
 	_exit(EXIT_SUCCESS);
 }
 
-void
+pid_t
 nmc_terminal_spawn_pager (const NmcConfig *nmc_config)
 {
 	const char *pager = getenv ("PAGER");
+	pid_t pager_pid;
 	pid_t parent_pid;
 	int fd[2];
 
-	if (   nm_cli.nmc_config.in_editor
-	    || nm_cli.pager_pid > 0
+	if (   nmc_config->in_editor
 	    || nmc_config->print_output == NMC_PRINT_TERSE
 	    || !nmc_config->use_colors
 	    || g_strcmp0 (pager, "") == 0
 	    || getauxval (AT_SECURE))
-		return;
+		return 0;
 
 	if (pipe (fd) == -1) {
 		g_printerr (_("Failed to create pager pipe: %s\n"), strerror (errno));
-		return;
+		return 0;
 	}
 
 	parent_pid = getpid ();
 
-	nm_cli.pager_pid = fork ();
-	if (nm_cli.pager_pid == -1) {
+	pager_pid = fork ();
+	if (pager_pid == -1) {
 		g_printerr (_("Failed to fork pager: %s\n"), strerror (errno));
 		nm_close (fd[0]);
 		nm_close (fd[1]);
-		return;
+		return 0;
 	}
 
 	/* In the child start the pager */
-	if (nm_cli.pager_pid == 0) {
+	if (pager_pid == 0) {
 		dup2 (fd[0], STDIN_FILENO);
 		nm_close (fd[0]);
 		nm_close (fd[1]);
@@ -1521,6 +1524,7 @@ nmc_terminal_spawn_pager (const NmcConfig *nmc_config)
 
 	nm_close (fd[0]);
 	nm_close (fd[1]);
+	return pager_pid;
 }
 
 /*****************************************************************************/
@@ -1587,8 +1591,7 @@ print_required_fields (const NmcConfig *nmc_config,
 	gboolean field_names = of_flags & NMC_OF_FLAG_FIELD_NAMES;
 	gboolean section_prefix = of_flags & NMC_OF_FLAG_SECTION_PREFIX;
 
-	/* Optionally start paging the output. */
-	nmc_terminal_spawn_pager (nmc_config);
+	nm_cli_spawn_pager (&nm_cli);
 
 	/* --- Main header --- */
 	if (   nmc_config->print_output == NMC_PRINT_PRETTY
@@ -1728,7 +1731,7 @@ print_required_fields (const NmcConfig *nmc_config,
 			g_string_append_c (str, ':');  /* Column separator */
 		} else {
 			width1 = strlen (value);
-			width2 = nmc_string_screen_width (value, NULL);  /* Width of the string (in screen colums) */
+			width2 = nmc_string_screen_width (value, NULL);  /* Width of the string (in screen columns) */
 			g_string_append_printf (str, "%-*s", field_values[idx].width + width1 - width2, strlen (value) > 0 ? value : not_set_str);
 			g_string_append_c (str, ' ');  /* Column separator */
 			table_width += field_values[idx].width + width1 - width2 + 1;
diff --git a/clients/cli/utils.h b/clients/cli/utils.h
index b2a3e5eb..b84b35bf 100644
--- a/clients/cli/utils.h
+++ b/clients/cli/utils.h
@@ -40,7 +40,7 @@ gboolean nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***a
 char *ssid_to_hex (const char *str, gsize len);
 void nmc_terminal_erase_line (void);
 void nmc_terminal_show_progress (const char *str);
-void nmc_terminal_spawn_pager (const NmcConfig *nmc_config);
+pid_t nmc_terminal_spawn_pager (const NmcConfig *nmc_config);
 char *nmc_colorize (const NmcConfig *nmc_config, NMMetaColor color, const char * fmt, ...)  _nm_printf (3, 4);
 void nmc_filter_out_colors_inplace (char *str);
 char *nmc_filter_out_colors (const char *str);
@@ -166,6 +166,8 @@ typedef enum {
 	NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DEVICE = 0,
 	NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_TYPE,
 	NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_STATE,
+	NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP4_CONNECTIVITY,
+	NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP6_CONNECTIVITY,
 	NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DBUS_PATH,
 	NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CONNECTION,
 	NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CON_UUID,
@@ -184,6 +186,8 @@ typedef enum {
 	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_MTU,
 	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_STATE,
 	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_REASON,
+	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP4_CONNECTIVITY,
+	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP6_CONNECTIVITY,
 	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_UDI,
 	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP_IFACE,
 	NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IS_SOFTWARE,