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.c24
-rw-r--r--clients/cli/common.c108
-rw-r--r--clients/cli/common.h15
-rw-r--r--clients/cli/connections.c477
-rw-r--r--clients/cli/connections.h2
-rw-r--r--clients/cli/devices.c463
-rw-r--r--clients/cli/general.c49
-rw-r--r--clients/cli/meson.build8
-rw-r--r--clients/cli/nmcli.c49
-rw-r--r--clients/cli/nmcli.h6
-rw-r--r--clients/cli/polkit-agent.c16
-rw-r--r--clients/cli/settings.c17
-rw-r--r--clients/cli/settings.h3
-rw-r--r--clients/cli/utils.c65
-rw-r--r--clients/cli/utils.h6
15 files changed, 769 insertions, 539 deletions
diff --git a/clients/cli/agent.c b/clients/cli/agent.c
index bbfe47fb..6c116153 100644
--- a/clients/cli/agent.c
+++ b/clients/cli/agent.c
@@ -25,7 +25,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <errno.h>
 #include <readline/readline.h>
 #include <readline/history.h>
 
@@ -85,7 +84,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 +104,10 @@ 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);
+		if (secret->no_prompt_entry_id)
+			pwd = nmc_readline (nmc_config, "%s: ", secret->pretty_name);
+		else
+			pwd = nmc_readline (nmc_config, "%s (%s): ", secret->pretty_name, secret->entry_id);
 
 		/* No password provided, cancel the secrets. */
 		if (!pwd)
@@ -123,17 +126,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
@@ -149,7 +151,7 @@ do_agent_secret (NmCli *nmc, int argc, char **argv)
 		/* We keep running */
 		nmc->should_wait++;
 
-		nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent), NULL);
+		nm_secret_agent_simple_enable (nmc->secret_agent, NULL);
 		g_signal_connect (nmc->secret_agent,
 		                  NM_SECRET_AGENT_SIMPLE_REQUEST_SECRETS,
 		                  G_CALLBACK (secrets_requested),
diff --git a/clients/cli/common.c b/clients/cli/common.c
index d3f5f865..3c1c315d 100644
--- a/clients/cli/common.c
+++ b/clients/cli/common.c
@@ -25,7 +25,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <errno.h>
 #include <sys/ioctl.h>
 #include <readline/readline.h>
 #include <readline/history.h>
@@ -661,12 +660,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)
 {
@@ -686,6 +685,8 @@ get_secrets_from_user (const char *request_id,
 			pwd = g_strdup (pwd);
 		} else {
 			if (ask) {
+				gboolean echo_on;
+
 				if (secret->value) {
 					if (!g_strcmp0 (secret->vpn_type, NM_DBUS_INTERFACE ".openconnect")) {
 						/* Do not present and ask user for openconnect secrets, we already have them */
@@ -698,10 +699,16 @@ get_secrets_from_user (const char *request_id,
 				}
 				if (msg)
 					g_print ("%s\n", msg);
-				pwd = nmc_readline_echo (secret->is_secret
-				                         ? echo_on
-				                         : TRUE,
-				                         "%s (%s): ", secret->pretty_name, secret->entry_id);
+
+				echo_on = secret->is_secret
+				          ? nmc_config->show_secrets
+				          : TRUE;
+
+				if (secret->no_prompt_entry_id)
+					pwd = nmc_readline_echo (nmc_config, echo_on, "%s: ", secret->pretty_name);
+				else
+					pwd = nmc_readline_echo (nmc_config, echo_on, "%s (%s): ", secret->pretty_name, secret->entry_id);
+
 				if (!pwd)
 					pwd = g_strdup ("");
 			} else {
@@ -763,15 +770,21 @@ 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 {
 		/* Unregister our secret agent on failure, so that another agent
 		 * may be tried */
 		if (nmc->secret_agent) {
-			nm_secret_agent_old_unregister (nmc->secret_agent, NULL, NULL);
+			nm_secret_agent_old_unregister (NM_SECRET_AGENT_OLD (nmc->secret_agent), NULL, NULL);
 			g_clear_object (&nmc->secret_agent);
 		}
 	}
@@ -847,7 +860,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 +898,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 +940,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 +987,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 +1011,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) {
@@ -1336,23 +1349,39 @@ nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char
 /**
  * nmc_complete_strings:
  * @prefix: a string to match
- * @...: a %NULL-terminated list of candidate strings
+ * @nargs: the number of elements in @args. Or -1 if @args is a NULL terminated
+ *   strv array.
+ * @args: the argument list. If @nargs is not -1, then some elements may
+ *   be %NULL to indicate to silently skip the values.
  *
  * Prints all the matching candidates for completion. Useful when there's
  * no better way to suggest completion other than a hardcoded string list.
  */
 void
-nmc_complete_strings (const char *prefix, ...)
+nmc_complete_strv (const char *prefix, gssize nargs, const char *const*args)
 {
-	va_list args;
-	const char *candidate;
+	gsize i, n;
 
-	va_start (args, prefix);
-	while ((candidate = va_arg (args, const char *))) {
-		if (!*prefix || matches (prefix, candidate))
-			g_print ("%s\n", candidate);
+	if (prefix && !prefix[0])
+		prefix = NULL;
+
+	if (nargs < 0) {
+		nm_assert (nargs == -1);
+		n = NM_PTRARRAY_LEN (args);
+	} else
+		n = (gsize) nargs;
+
+	for (i = 0; i < n; i++) {
+		const char *candidate = args[i];
+
+		if (!candidate)
+			continue;
+		if (   prefix
+		    && !matches (prefix, candidate))
+			continue;
+
+		g_print ("%s\n", candidate);
 	}
-	va_end (args);
 }
 
 /**
@@ -1384,3 +1413,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..688f2819 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);
@@ -83,7 +88,9 @@ typedef struct {
 
 void nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char **argv);
 
-void nmc_complete_strings (const char *prefix, ...) G_GNUC_NULL_TERMINATED;
+void nmc_complete_strv (const char *prefix, gssize nargs, const char *const*args);
+
+#define nmc_complete_strings(prefix, ...) nmc_complete_strv ((prefix), NM_NARG (__VA_ARGS__), (const char *const[]) { __VA_ARGS__ })
 
 void nmc_complete_bool (const char *prefix);
 
@@ -93,4 +100,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 8a10691e..6db44f87 100644
--- a/clients/cli/connections.c
+++ b/clients/cli/connections.c
@@ -22,14 +22,13 @@
 #include "connections.h"
 
 #include <stdio.h>
-#include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <errno.h>
 #include <signal.h>
 #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 +139,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;
 }
 
@@ -742,6 +794,7 @@ const NmcMetaGenericInfo *const metagen_con_active_vpn[_NMC_GENERIC_INFO_TYPE_CO
                                          NM_SETTING_IP4_CONFIG_SETTING_NAME","\
                                          NM_SETTING_IP6_CONFIG_SETTING_NAME","\
                                          NM_SETTING_SERIAL_SETTING_NAME","\
+                                         NM_SETTING_WIFI_P2P_SETTING_NAME","\
                                          NM_SETTING_PPP_SETTING_NAME","\
                                          NM_SETTING_PPPOE_SETTING_NAME","\
                                          NM_SETTING_ADSL_SETTING_NAME","\
@@ -769,6 +822,7 @@ const NmcMetaGenericInfo *const metagen_con_active_vpn[_NMC_GENERIC_INFO_TYPE_CO
                                          NM_SETTING_VXLAN_SETTING_NAME"," \
                                          NM_SETTING_WPAN_SETTING_NAME","\
                                          NM_SETTING_6LOWPAN_SETTING_NAME","\
+                                         NM_SETTING_WIREGUARD_SETTING_NAME","\
                                          NM_SETTING_PROXY_SETTING_NAME"," \
                                          NM_SETTING_TC_CONFIG_SETTING_NAME"," \
                                          NM_SETTING_SRIOV_SETTING_NAME"," \
@@ -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;
@@ -2375,7 +2473,7 @@ check_activated (ActivateConnectionInfo *info)
 		if (nmc->secret_agent) {
 			NMRemoteConnection *connection = nm_active_connection_get_connection (info->active);
 
-			nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent),
+			nm_secret_agent_simple_enable (nmc->secret_agent,
 			                               nm_connection_get_path (NM_CONNECTION (connection)));
 		}
 		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 {
@@ -2676,7 +2775,6 @@ nmc_activate_connection (NmCli *nmc,
 		g_hash_table_destroy (nmc->pwds_hash);
 	nmc->pwds_hash = pwds_hash;
 
-	/* Create secret agent */
 	nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-connect");
 	if (nmc->secret_agent) {
 		g_signal_connect (nmc->secret_agent,
@@ -2726,14 +2824,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 +3004,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 +3079,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) {
@@ -3204,7 +3305,7 @@ get_valid_properties_string (const NMMetaSettingValidPartItem *const*array,
 	str = g_string_sized_new (1024);
 
 	for (i = 0; i < 2; i++, iter = array_slv) {
-		for(; !full_match && iter && *iter; iter++) {
+		for (; !full_match && iter && *iter; iter++) {
 			const NMMetaSettingInfoEditor *setting_info = (*iter)->setting_info;
 
 			if (   !(g_str_has_prefix (setting_info->general->setting_name, prefix))
@@ -3850,9 +3951,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 +3990,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 +4042,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 +4090,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 +4164,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 +4181,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 +4210,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 +4233,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 +4353,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 +4372,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 +4392,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 +4678,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 +4958,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 +5050,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 +5138,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 +5170,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;
 
@@ -6615,7 +6726,7 @@ progress_activation_editor_cb (gpointer user_data)
 		NMRemoteConnection *connection;
 
 		connection = nm_active_connection_get_connection (ac);
-		nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (info->nmc->secret_agent),
+		nm_secret_agent_simple_enable (info->nmc->secret_agent,
 		                               nm_object_get_path (NM_OBJECT (connection)));
 	}
 
@@ -6656,6 +6767,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);
 }
@@ -6749,20 +6864,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));
 }
 
 /*
@@ -6797,7 +6908,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. "
@@ -6810,10 +6921,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:
@@ -6834,7 +6948,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);
 
@@ -6846,7 +6962,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);
@@ -6862,12 +6978,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);
@@ -6879,19 +6997,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,
@@ -6934,7 +7053,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;
@@ -6995,7 +7114,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)
@@ -7006,12 +7126,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,
@@ -7025,7 +7145,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)
 {
@@ -7035,9 +7156,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);
 
@@ -7064,7 +7185,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;
@@ -7082,16 +7205,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;
 }
@@ -7196,9 +7318,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. "
@@ -7208,7 +7332,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) {
@@ -7222,7 +7348,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)
@@ -7236,10 +7363,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);
 					}
@@ -7294,11 +7423,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);
@@ -7317,7 +7448,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);
@@ -7363,7 +7495,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)
@@ -7386,7 +7519,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)
@@ -7480,7 +7614,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)
@@ -7682,9 +7817,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 */
@@ -7823,7 +7961,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) {
@@ -7897,7 +8036,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 */
@@ -8085,7 +8224,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 = ". ";
 
@@ -8173,8 +8312,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);
@@ -8367,14 +8507,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;
 	}
@@ -8390,9 +8530,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);
 	}
@@ -8477,14 +8618,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) {
@@ -8733,10 +8874,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);
@@ -8744,8 +8888,11 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 	}
 
 	while (argc > 0) {
-		if (argc == 1 && nmc->complete)
-			nmc_complete_strings (*argv, "type", "file", NULL);
+		if (argc == 1 && nmc->complete) {
+			nmc_complete_strings (*argv,
+			                      type ? NULL : "type",
+			                      filename ? NULL : "file");
+		}
 
 		if (strcmp (*argv, "type") == 0) {
 			argc--;
@@ -8755,8 +8902,13 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 				NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 			}
 
-			if (argc == 1 && nmc->complete)
-				complete_option ((const NMMetaAbstractInfo *) nm_meta_property_info_vpn_service_type, *argv, NULL);
+			if (   argc == 1
+			    && nmc->complete) {
+				nmc_complete_strings (*argv, "wireguard");
+				complete_option ((const NMMetaAbstractInfo *) nm_meta_property_info_vpn_service_type,
+				                 *argv,
+				                 NULL);
+			}
 
 			if (!type)
 				type = *argv;
@@ -8796,21 +8948,26 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 		NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT);
 	}
 
-	service_type = nm_vpn_plugin_info_list_find_service_type (nm_vpn_get_plugin_infos (), type);
-	if (!service_type) {
-		g_string_printf (nmc->return_text, _("Error: failed to find VPN plugin for %s."), type);
-		NMC_RETURN (nmc, NMC_RESULT_ERROR_UNKNOWN);
-	}
+	if (nm_streq (type, "wireguard"))
+		connection = nm_vpn_wireguard_import (filename, &error);
+	else {
+		service_type = nm_vpn_plugin_info_list_find_service_type (nm_vpn_get_plugin_infos (), type);
+		if (!service_type) {
+			g_string_printf (nmc->return_text, _("Error: failed to find VPN plugin for %s."), type);
+			NMC_RETURN (nmc, NMC_RESULT_ERROR_UNKNOWN);
+		}
 
-	/* Import VPN configuration */
-	plugin = nm_vpn_get_editor_plugin (service_type, &error);
-	if (!plugin) {
-		g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
-		                 error->message);
-		NMC_RETURN (nmc, NMC_RESULT_ERROR_UNKNOWN);
+		/* Import VPN configuration */
+		plugin = nm_vpn_get_editor_plugin (service_type, &error);
+		if (!plugin) {
+			g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
+			                 error->message);
+			NMC_RETURN (nmc, NMC_RESULT_ERROR_UNKNOWN);
+		}
+
+		connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
 	}
 
-	connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
 	if (!connection) {
 		g_string_printf (nmc->return_text, _("Error: failed to import '%s': %s."),
 		                 filename, error->message);
@@ -8853,14 +9010,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;
 	}
@@ -8883,8 +9040,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) {
@@ -8908,7 +9067,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;
@@ -9060,7 +9219,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 5aaf4ef8..04a8c988 100644
--- a/clients/cli/devices.c
+++ b/clients/cli/devices.c
@@ -22,9 +22,7 @@
 #include "devices.h"
 
 #include <stdio.h>
-#include <string.h>
 #include <stdlib.h>
-#include <errno.h>
 #include <readline/readline.h>
 
 #include "nm-secret-agent-simple.h"
@@ -44,43 +42,39 @@
 static char *
 ap_wpa_rsn_flags_to_string (NM80211ApSecurityFlags flags)
 {
-	char *flags_str[16]; /* Enough space for flags and terminating NULL */
-	char *ret_str;
+	char *flags_str[13];
 	int i = 0;
 
 	if (flags & NM_802_11_AP_SEC_PAIR_WEP40)
-		flags_str[i++] = g_strdup ("pair_wpe40");
+		flags_str[i++] = "pair_wpe40";
 	if (flags & NM_802_11_AP_SEC_PAIR_WEP104)
-		flags_str[i++] = g_strdup ("pair_wpe104");
+		flags_str[i++] = "pair_wpe104";
 	if (flags & NM_802_11_AP_SEC_PAIR_TKIP)
-		flags_str[i++] = g_strdup ("pair_tkip");
+		flags_str[i++] = "pair_tkip";
 	if (flags & NM_802_11_AP_SEC_PAIR_CCMP)
-		flags_str[i++] = g_strdup ("pair_ccmp");
+		flags_str[i++] = "pair_ccmp";
 	if (flags & NM_802_11_AP_SEC_GROUP_WEP40)
-		flags_str[i++] = g_strdup ("group_wpe40");
+		flags_str[i++] = "group_wpe40";
 	if (flags & NM_802_11_AP_SEC_GROUP_WEP104)
-		flags_str[i++] = g_strdup ("group_wpe104");
+		flags_str[i++] = "group_wpe104";
 	if (flags & NM_802_11_AP_SEC_GROUP_TKIP)
-		flags_str[i++] = g_strdup ("group_tkip");
+		flags_str[i++] = "group_tkip";
 	if (flags & NM_802_11_AP_SEC_GROUP_CCMP)
-		flags_str[i++] = g_strdup ("group_ccmp");
+		flags_str[i++] = "group_ccmp";
 	if (flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
-		flags_str[i++] = g_strdup ("psk");
+		flags_str[i++] = "psk";
 	if (flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
-		flags_str[i++] = g_strdup ("802.1X");
+		flags_str[i++] = "802.1X";
+	if (flags & NM_802_11_AP_SEC_KEY_MGMT_SAE)
+		flags_str[i++] = "sae";
+	/* Make sure you grow flags_str when adding items here. */
 
 	if (i == 0)
-		flags_str[i++] = g_strdup (_("(none)"));
+		flags_str[i++] = _("(none)");
 
 	flags_str[i] = NULL;
 
-	ret_str = g_strjoinv (" ", flags_str);
-
-	i = 0;
-	while (flags_str[i])
-		g_free (flags_str[i++]);
-
-	return ret_str;
+	return g_strjoinv (" ", flags_str);
 }
 
 static NMMetaColor
@@ -116,6 +110,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 +137,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 +157,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 +197,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 +259,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 +697,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 +832,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 +896,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 +965,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 +1024,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,
@@ -1195,21 +1158,21 @@ fill_output_access_point (gpointer data, gpointer user_data)
 	if (   (flags & NM_802_11_AP_FLAGS_PRIVACY)
 	    && (wpa_flags == NM_802_11_AP_SEC_NONE)
 	    && (rsn_flags == NM_802_11_AP_SEC_NONE)) {
-		g_string_append (security_str, _("WEP"));
-		g_string_append_c (security_str, ' ');
+		g_string_append (security_str, "WEP ");
 	}
 	if (wpa_flags != NM_802_11_AP_SEC_NONE) {
-		g_string_append (security_str, _("WPA1"));
-		g_string_append_c (security_str, ' ');
+		g_string_append (security_str, "WPA1 ");
+	}
+	if (   (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
+	    || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) {
+		g_string_append (security_str, "WPA2 ");
 	}
-	if (rsn_flags != NM_802_11_AP_SEC_NONE) {
-		g_string_append (security_str, _("WPA2"));
-		g_string_append_c (security_str, ' ');
+	if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE) {
+		g_string_append (security_str, "WPA3 ");
 	}
 	if (   (wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
 	    || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) {
-		g_string_append   (security_str, _("802.1X"));
-		g_string_append_c (security_str, ' ');
+		g_string_append   (security_str, "802.1X ");
 	}
 
 	if (security_str->len > 0)
@@ -1869,6 +1832,7 @@ typedef struct {
 	NmCli *nmc;
 	NMDevice *device;
 	gboolean hotspot;
+	gboolean create;
 } AddAndActivateInfo;
 
 static void
@@ -1879,47 +1843,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 +1873,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 +1918,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,27 +1947,24 @@ 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 {
 			if (nmc->secret_agent) {
 				NMRemoteConnection *connection = nm_active_connection_get_connection (active);
 
-				nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent),
+				nm_secret_agent_simple_enable (nmc->secret_agent,
 				                               nm_connection_get_path (NM_CONNECTION (connection)));
 			}
 
 			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 +2463,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 +2662,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 +2679,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 +2708,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;
@@ -3173,7 +3123,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;
@@ -3189,6 +3138,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)
@@ -3211,7 +3164,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);
 		}
@@ -3393,14 +3346,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;
@@ -3418,45 +3371,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;
+		}
 
-		/* 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 (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;
+		}
 	}
-	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 (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 (!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);
+			}
 		}
 	}
 
@@ -3469,15 +3452,38 @@ 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)
+				           || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE)) {
+					/* 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 */
@@ -3487,13 +3493,14 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
 				              wep_passphrase ? NM_WEP_KEY_TYPE_PASSPHRASE: NM_WEP_KEY_TYPE_KEY,
 				              NULL);
 			} else if (   (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
-			           || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) {
+			           || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
+			           || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE)) {
 				/* WPA PSK */
 				g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_PSK, password, NULL);
 			}
 		}
 	}
-	// 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
@@ -3511,14 +3518,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..2c22bdc9 100644
--- a/clients/cli/general.c
+++ b/clients/cli/general.c
@@ -21,7 +21,6 @@
 
 #include "general.h"
 
-#include <string.h>
 #include <stdlib.h>
 
 #include "nm-common-macros.h"
@@ -37,7 +36,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 +48,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 +71,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)
 {
@@ -146,12 +124,14 @@ permission_to_string (NMClientPermission perm)
 		return NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS;
 	case NM_CLIENT_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK:
 		return NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK;
+	case NM_CLIENT_PERMISSION_WIFI_SCAN:
+		return NM_AUTH_PERMISSION_WIFI_SCAN;
 	default:
 		return _("unknown");
 	}
 }
 
-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 +172,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 +182,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 +201,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 +266,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 +541,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 +977,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 +1093,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 +1108,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 +1265,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..d7bc2a46 100644
--- a/clients/cli/nmcli.c
+++ b/clients/cli/nmcli.c
@@ -24,7 +24,6 @@
 #include "nmcli.h"
 
 #include <stdio.h>
-#include <string.h>
 #include <stdlib.h>
 #include <signal.h>
 #include <termios.h>
@@ -245,19 +244,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                            show 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"
@@ -504,7 +503,7 @@ resolve_color_alias (const char *color)
 	static const struct {
 		const char *name;
 		const char *alias;
-	} const aliases[] = {
+	} aliases[] = {
 		{ "reset",        "0" },
 		{ "bold",         "1" },
 		{ "white",        "1;37" },
@@ -998,6 +997,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)
 {
@@ -1005,15 +1012,15 @@ nmc_cleanup (NmCli *nmc)
 
 	g_clear_object (&nmc->client);
 
-	g_string_free (nmc->return_text, TRUE);
+	if (nmc->return_text)
+		g_string_free (g_steal_pointer (&nmc->return_text), TRUE);
 
 	if (nmc->secret_agent) {
-		/* Destroy secret agent if we have one. */
-		nm_secret_agent_old_unregister (nmc->secret_agent, NULL, NULL);
-		g_object_unref (nmc->secret_agent);
+		nm_secret_agent_old_unregister (NM_SECRET_AGENT_OLD (nmc->secret_agent), NULL, NULL);
+		g_clear_object (&nmc->secret_agent);
 	}
-	if (nmc->pwds_hash)
-		g_hash_table_destroy (nmc->pwds_hash);
+
+	nm_clear_pointer (&nmc->pwds_hash, g_hash_table_destroy);
 
 	nm_clear_g_free (&nmc->required_fields);
 
diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h
index 28616855..cd50333a 100644
--- a/clients/cli/nmcli.h
+++ b/clients/cli/nmcli.h
@@ -20,7 +20,7 @@
 #ifndef NMC_NMCLI_H
 #define NMC_NMCLI_H
 
-#include "nm-secret-agent-old.h"
+#include "nm-secret-agent-simple.h"
 #include "nm-meta-setting-desc.h"
 
 struct _NMPolkitListener;
@@ -129,7 +129,7 @@ typedef struct _NmCli {
 
 	int timeout;                                      /* Operation timeout */
 
-	NMSecretAgentOld *secret_agent;                   /* Secret agent */
+	NMSecretAgentSimple *secret_agent;                /* Secret agent */
 	GHashTable *pwds_hash;                            /* Hash table with passwords in passwd-file */
 	struct _NMPolkitListener *pk_listener;            /* polkit agent listener */
 
@@ -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..accb567f 100644
--- a/clients/cli/polkit-agent.c
+++ b/clients/cli/polkit-agent.c
@@ -22,7 +22,6 @@
 #include "polkit-agent.h"
 
 #include <stdio.h>
-#include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -40,24 +39,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 feb67221..0e74ba88 100644
--- a/clients/cli/utils.c
+++ b/clients/cli/utils.c
@@ -23,9 +23,7 @@
 #include "utils.h"
 
 #include <stdio.h>
-#include <string.h>
 #include <stdlib.h>
-#include <errno.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -36,6 +34,7 @@
 #include "nm-meta-setting-access.h"
 
 #include "common.h"
+#include "nmcli.h"
 #include "settings.h"
 
 #define ML_HEADER_WIDTH 79
@@ -1100,7 +1099,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
@@ -1275,7 +1274,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;
@@ -1357,7 +1356,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;
@@ -1427,19 +1426,20 @@ pager_fallback (void)
 {
 	char buf[64];
 	int rb;
+	int errsv;
 
 	do {
 		rb = read (STDIN_FILENO, buf, sizeof (buf));
 		if (rb == -1) {
-			if (errno == EINTR) {
+			errsv = errno;
+			if (errsv == EINTR)
 				continue;
-			} else {
-				g_printerr (_("Error reading nmcli output: %s\n"), strerror (errno));
-				_exit(EXIT_FAILURE);
-			}
+			g_printerr (_("Error reading nmcli output: %s\n"), nm_strerror_native (errsv));
+			_exit(EXIT_FAILURE);
 		}
 		if (write (STDOUT_FILENO, buf, rb) == -1) {
-			g_printerr (_("Error writing nmcli output: %s\n"), strerror (errno));
+			errsv = errno;
+			g_printerr (_("Error writing nmcli output: %s\n"), nm_strerror_native (errsv));
 			_exit(EXIT_FAILURE);
 		}
 	} while (rb > 0);
@@ -1447,38 +1447,41 @@ 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];
+	int errsv;
 
-	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;
+		errsv = errno;
+		g_printerr (_("Failed to create pager pipe: %s\n"), nm_strerror_native (errsv));
+		return 0;
 	}
 
 	parent_pid = getpid ();
 
-	nm_cli.pager_pid = fork ();
-	if (nm_cli.pager_pid == -1) {
-		g_printerr (_("Failed to fork pager: %s\n"), strerror (errno));
+	pager_pid = fork ();
+	if (pager_pid == -1) {
+		errsv = errno;
+		g_printerr (_("Failed to fork pager: %s\n"), nm_strerror_native (errsv));
 		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]);
@@ -1516,13 +1519,18 @@ nmc_terminal_spawn_pager (const NmcConfig *nmc_config)
 	}
 
 	/* Return in the parent */
-	if (dup2 (fd[1], STDOUT_FILENO) < 0)
-		g_printerr (_("Failed to duplicate pager pipe: %s\n"), strerror (errno));
-	if (dup2 (fd[1], STDERR_FILENO) < 0)
-		g_printerr (_("Failed to duplicate pager pipe: %s\n"), strerror (errno));
+	if (dup2 (fd[1], STDOUT_FILENO) < 0) {
+		errsv = errno;
+		g_printerr (_("Failed to duplicate pager pipe: %s\n"), nm_strerror_native (errsv));
+	}
+	if (dup2 (fd[1], STDERR_FILENO) < 0) {
+		errsv = errno;
+		g_printerr (_("Failed to duplicate pager pipe: %s\n"), nm_strerror_native (errsv));
+	}
 
 	nm_close (fd[0]);
 	nm_close (fd[1]);
+	return pager_pid;
 }
 
 /*****************************************************************************/
@@ -1589,8 +1597,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
@@ -1730,7 +1737,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,