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.c96
-rw-r--r--clients/cli/agent.h13
-rw-r--r--clients/cli/common.c77
-rw-r--r--clients/cli/common.h10
-rw-r--r--clients/cli/connections.c508
-rw-r--r--clients/cli/connections.h13
-rw-r--r--clients/cli/devices.c784
-rw-r--r--clients/cli/devices.h2
-rw-r--r--clients/cli/general.c532
-rw-r--r--clients/cli/general.h17
-rw-r--r--clients/cli/meson.build33
-rw-r--r--clients/cli/nmcli.c120
-rw-r--r--clients/cli/nmcli.h35
-rw-r--r--clients/cli/polkit-agent.c97
-rw-r--r--clients/cli/settings.c6
-rw-r--r--clients/cli/utils.c77
-rw-r--r--clients/cli/utils.h8
17 files changed, 1322 insertions, 1106 deletions
diff --git a/clients/cli/agent.c b/clients/cli/agent.c
index f4057df9..f3707db7 100644
--- a/clients/cli/agent.c
+++ b/clients/cli/agent.c
@@ -5,8 +5,6 @@
 
 #include "nm-default.h"
 
-#include "agent.h"
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <readline/readline.h>
@@ -16,6 +14,7 @@
 #include "utils.h"
 #include "nm-secret-agent-simple.h"
 #include "polkit-agent.h"
+#include "nm-polkit-listener.h"
 
 static void
 usage (void)
@@ -122,12 +121,12 @@ secrets_requested (NMSecretAgentSimple *agent,
 	                                 success ? secrets : NULL);
 }
 
-static NMCResultCode
-do_agent_secret (NmCli *nmc, int argc, char **argv)
+static void
+do_agent_secret (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	next_arg (nmc, &argc, &argv, NULL);
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	/* Create secret agent */
 	nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-agent");
@@ -145,76 +144,93 @@ do_agent_secret (NmCli *nmc, int argc, char **argv)
 		g_string_printf (nmc->return_text, _("Error: secret agent initialization failed"));
 		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 	}
+}
 
-	return nmc->return_value;
+static void
+polkit_registered (gpointer instance,
+                   gpointer user_data)
+{
+	g_print (_("nmcli successfully registered as a polkit agent.\n"));
+}
+
+static void
+polkit_error (gpointer instance,
+              const char *error,
+              gpointer user_data)
+{
+	g_main_loop_quit (loop);
 }
 
-static NMCResultCode
-do_agent_polkit (NmCli *nmc, int argc, char **argv)
+static void
+do_agent_polkit (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
 
 	next_arg (nmc, &argc, &argv, NULL);
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
-	/* Initialize polkit agent */
 	if (!nmc_polkit_agent_init (nmc, TRUE, &error)) {
 		g_dbus_error_strip_remote_error (error);
-		g_string_printf (nmc->return_text, _("Error: polkit agent initialization failed: %s"),
+		g_string_printf (nmc->return_text,
+		                 _("Error: polkit agent initialization failed: %s"),
 		                 error->message);
 		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
-		g_error_free (error);
 	} else {
 		/* We keep running */
 		nmc->should_wait++;
-
-		g_print (_("nmcli successfully registered as a polkit agent.\n"));
+		g_signal_connect (nmc->pk_listener,
+		                  NM_POLKIT_LISTENER_SIGNAL_ERROR,
+		                  G_CALLBACK (polkit_error),
+		                  NULL);
+		g_signal_connect (nmc->pk_listener,
+		                  NM_POLKIT_LISTENER_SIGNAL_REGISTERED,
+		                  G_CALLBACK (polkit_registered),
+		                  NULL);
+
+		/* keep running */
+		nmc->should_wait++;
 	}
-
-	return nmc->return_value;
 }
 
-static NMCResultCode
-do_agent_all (NmCli *nmc, int argc, char **argv)
+static void
+do_agent_all (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
-	NMCResultCode secret_res;
+	NMCResultCode r;
 
 	next_arg (nmc, &argc, &argv, NULL);
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	/* Run both secret and polkit agent */
-	secret_res = do_agent_secret (nmc, argc, argv);
-	if (secret_res != NMC_RESULT_SUCCESS) {
+	do_agent_secret (cmd, nmc, argc, argv);
+	r = nmc->return_value;
+	if (r != NMC_RESULT_SUCCESS) {
 		g_printerr ("%s\n", nmc->return_text->str);
 		g_string_truncate (nmc->return_text, 0);
+		nmc->return_value = NMC_RESULT_SUCCESS;
 	}
 
-	nmc->return_value = do_agent_polkit (nmc, argc, argv);
+	do_agent_polkit (cmd, nmc, argc, argv);
 	if (nmc->return_value != NMC_RESULT_SUCCESS) {
 		g_printerr ("%s\n", nmc->return_text->str);
 		g_string_truncate (nmc->return_text, 0);
 	}
 
-	if (nmc->return_value == NMC_RESULT_SUCCESS && secret_res != NMC_RESULT_SUCCESS)
-		nmc->return_value = secret_res;
-
-	return nmc->return_value;
+	if (r != NMC_RESULT_SUCCESS)
+		nmc->return_value = r;
 }
 
-static const NMCCommand agent_cmds[] = {
-	{ "secret",  do_agent_secret,  usage_agent_secret,  TRUE,   TRUE },
-	{ "polkit",  do_agent_polkit,  usage_agent_polkit,  TRUE,   TRUE },
-	{ "all",     do_agent_all,     usage_agent_all,     TRUE,   TRUE },
-	{ NULL,      do_agent_all,     usage,               TRUE,   TRUE },
-};
-
-NMCResultCode
-do_agent (NmCli *nmc, int argc, char **argv)
+void
+nmc_command_func_agent (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
-	next_arg (nmc, &argc, &argv, NULL);
-	nmc_do_cmd (nmc, agent_cmds, *argv, argc, argv);
+	static const NMCCommand cmds[] = {
+		{ "secret", do_agent_secret, usage_agent_secret, TRUE, TRUE },
+		{ "polkit", do_agent_polkit, usage_agent_polkit, TRUE, TRUE },
+		{ "all",    do_agent_all,    usage_agent_all,    TRUE, TRUE },
+		{ NULL,     do_agent_all,    usage,              TRUE, TRUE },
+	};
 
-	return nmc->return_value;
+	next_arg (nmc, &argc, &argv, NULL);
+	nmc_do_cmd (nmc, cmds, *argv, argc, argv);
 }
diff --git a/clients/cli/agent.h b/clients/cli/agent.h
deleted file mode 100644
index aad56bba..00000000
--- a/clients/cli/agent.h
+++ /dev/null
@@ -1,13 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2014 Red Hat, Inc.
- */
-
-#ifndef __NMC_AGENT_H__
-#define __NMC_AGENT_H__
-
-#include "nmcli.h"
-
-NMCResultCode do_agent (NmCli *nmc, int argc, char **argv);
-
-#endif /* __NMC_AGENT_H__ */
diff --git a/clients/cli/common.c b/clients/cli/common.c
index 04ccf8dc..93defe65 100644
--- a/clients/cli/common.c
+++ b/clients/cli/common.c
@@ -13,8 +13,11 @@
 #include <readline/readline.h>
 #include <readline/history.h>
 
+#include "nm-libnm-aux/nm-libnm-aux.h"
+
 #include "nm-vpn-helpers.h"
 #include "nm-client-utils.h"
+#include "nm-glib-aux/nm-secret-utils.h"
 
 #include "utils.h"
 
@@ -730,7 +733,7 @@ get_secrets_from_user (const NmcConfig *nmc_config,
 		/* No password provided, cancel the secrets. */
 		if (!pwd)
 			return FALSE;
-		g_free (secret->value);
+		nm_free_secret (secret->value);
 		secret->value = pwd;
 	}
 	return TRUE;
@@ -861,7 +864,9 @@ readline_cb (char *line)
 }
 
 static gboolean
-stdin_ready_cb (GIOChannel * io, GIOCondition condition, gpointer data)
+stdin_ready_cb (int fd,
+                GIOCondition condition,
+                gpointer data)
 {
 	rl_callback_read_char ();
 	return TRUE;
@@ -871,14 +876,17 @@ static char *
 nmc_readline_helper (const NmcConfig *nmc_config,
                      const char *prompt)
 {
-	GIOChannel *io = NULL;
-	guint io_watch_id;
+	GSource *io_source;
 
 	nmc_set_in_readline (TRUE);
 
-	io = g_io_channel_unix_new (STDIN_FILENO);
-	io_watch_id = g_io_add_watch (io, G_IO_IN, stdin_ready_cb, NULL);
-	g_io_channel_unref (io);
+	io_source = nm_g_unix_fd_source_new (STDIN_FILENO,
+	                                     G_IO_IN,
+	                                     G_PRIORITY_DEFAULT,
+	                                     stdin_ready_cb,
+	                                     NULL,
+	                                     NULL);
+	g_source_attach (io_source, NULL);
 
 read_again:
 	rl_string = NULL;
@@ -927,7 +935,8 @@ read_again:
 		rl_string = NULL;
 	}
 
-	g_source_remove (io_watch_id);
+	nm_clear_g_source_inst (&io_source);
+
 	nmc_set_in_readline (FALSE);
 
 	return rl_string;
@@ -1003,7 +1012,7 @@ nmc_readline_echo (const NmcConfig *nmc_config,
 	va_list args;
 	gs_free char *prompt = NULL;
 	char *str;
-	HISTORY_STATE *saved_history;
+	nm_auto_free HISTORY_STATE *saved_history = NULL;
 	HISTORY_STATE passwd_history = { 0, };
 
 	va_start (args, prompt_fmt);
@@ -1016,6 +1025,10 @@ nmc_readline_echo (const NmcConfig *nmc_config,
 	if (!echo_on) {
 		saved_history = history_get_history_state ();
 		history_set_history_state (&passwd_history);
+		/* stifling history is important as it tells readline to
+		 * not store anything, otherwise sensitive data could be
+		 * leaked */
+		stifle_history (0);
 		rl_redisplay_function = nmc_secret_redisplay;
 	}
 
@@ -1105,7 +1118,7 @@ nmc_rl_gen_func_ifnames (const char *text, int state)
 	const char **ifnames;
 	char *ret;
 
-	devices = nm_client_get_devices (nm_cli.client);
+	devices = nm_client_get_devices (nm_cli_global_readline->client);
 	if (devices->len == 0)
 		return NULL;
 
@@ -1206,7 +1219,7 @@ typedef struct {
 } CmdCall;
 
 static void
-call_cmd (NmCli *nmc, GTask *task, const NMCCommand *cmd, int argc, char **argv);
+call_cmd (NmCli *nmc, GTask *task, const NMCCommand *cmd, int argc, const char *const*argv);
 
 static void
 got_client (GObject *source_object, GAsyncResult *res, gpointer user_data)
@@ -1216,25 +1229,31 @@ got_client (GObject *source_object, GAsyncResult *res, gpointer user_data)
 	CmdCall *call = user_data;
 	NmCli *nmc;
 
+	nm_assert (NM_IS_CLIENT (source_object));
+
 	task = g_steal_pointer (&call->task);
 	nmc = g_task_get_task_data (task);
 
 	nmc->should_wait--;
-	nmc->client = nm_client_new_finish (res, &error);
 
-	if (!nmc->client) {
+	if (!g_async_initable_init_finish (G_ASYNC_INITABLE (source_object),
+	                                   res,
+	                                   &error)) {
+		g_object_unref (source_object);
 		g_task_return_new_error (task, NMCLI_ERROR, NMC_RESULT_ERROR_UNKNOWN,
 		                         _("Error: Could not create NMClient object: %s."),
 		                         error->message);
 	} else {
-		call_cmd (nmc, g_steal_pointer (&task), call->cmd, call->argc, call->argv);
+		nmc->client = NM_CLIENT (source_object);
+		call_cmd (nmc, g_steal_pointer (&task), call->cmd, call->argc, (const char *const*) call->argv);
 	}
 
-	g_slice_free (CmdCall, call);
+	g_strfreev (call->argv);
+	nm_g_slice_free (call);
 }
 
 static void
-call_cmd (NmCli *nmc, GTask *task, const NMCCommand *cmd, int argc, char **argv)
+call_cmd (NmCli *nmc, GTask *task, const NMCCommand *cmd, int argc, const char *const*argv)
 {
 	CmdCall *call;
 
@@ -1245,7 +1264,7 @@ call_cmd (NmCli *nmc, GTask *task, const NMCCommand *cmd, int argc, char **argv)
 			g_task_return_new_error (task, NMCLI_ERROR, NMC_RESULT_ERROR_NM_NOT_RUNNING,
 			                         _("Error: NetworkManager is not running."));
 		} else {
-			nmc->return_value = cmd->func (nmc, argc, argv);
+			cmd->func (cmd, nmc, argc, argv);
 			g_task_return_boolean (task, TRUE);
 		}
 
@@ -1254,12 +1273,18 @@ call_cmd (NmCli *nmc, GTask *task, const NMCCommand *cmd, int argc, char **argv)
 		nm_assert (nmc->client == NULL);
 
 		nmc->should_wait++;
-		call = g_slice_new0 (CmdCall);
-		call->cmd = cmd;
-		call->argc = argc;
-		call->argv = argv;
-		call->task = task;
-		nm_client_new_async (NULL, got_client, call);
+		call = g_slice_new (CmdCall);
+		*call = (CmdCall) {
+			.cmd  = cmd,
+			.argc = argc,
+			.argv = nm_utils_strv_dup ((char **) argv, argc, TRUE),
+			.task = task,
+		};
+		nmc_client_new_async (NULL,
+		                      got_client,
+		                      call,
+		                      NM_CLIENT_INSTANCE_FLAGS, (guint) NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS,
+		                      NULL);
 	}
 }
 
@@ -1291,7 +1316,7 @@ nmc_complete_help (const char *prefix)
  * no callback to free the memory in (for simplicity).
  */
 void
-nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char **argv)
+nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, const char *const*argv)
 {
 	const NMCCommand *c;
 	gs_unref_object GTask *task = NULL;
@@ -1327,7 +1352,7 @@ nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char
 			c->usage ();
 			g_task_return_boolean (task, TRUE);
 		} else {
-			call_cmd (nmc, g_steal_pointer (&task), c, argc, argv);
+			call_cmd (nmc, g_steal_pointer (&task), c, argc, (const char *const*) argv);
 		}
 	} else if (cmd) {
 		/* Not a known command. */
@@ -1340,7 +1365,7 @@ nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char
 		}
 	} else if (c->func) {
 		/* No command, run the default handler. */
-		call_cmd (nmc, g_steal_pointer (&task), c, argc, argv);
+		call_cmd (nmc, g_steal_pointer (&task), c, argc, (const char *const*) argv);
 	} else {
 		/* No command and no default handler. */
 		g_task_return_new_error (task, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
diff --git a/clients/cli/common.h b/clients/cli/common.h
index f8fad8c6..243a2098 100644
--- a/clients/cli/common.h
+++ b/clients/cli/common.h
@@ -62,15 +62,7 @@ int nmc_rl_set_deftext (void);
 
 char *nmc_parse_lldp_capabilities (guint value);
 
-typedef struct {
-	const char *cmd;
-	NMCResultCode (*func) (NmCli *nmc, int argc, char **argv);
-	void (*usage) (void);
-	gboolean needs_client;
-	gboolean needs_nm_running;
-} NMCCommand;
-
-void nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char **argv);
+void nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, const char *const*argv);
 
 void nmc_complete_strv (const char *prefix, gssize nargs, const char *const*args);
 
diff --git a/clients/cli/connections.c b/clients/cli/connections.c
index e13ae9ac..0e4a95e6 100644
--- a/clients/cli/connections.c
+++ b/clients/cli/connections.c
@@ -62,7 +62,8 @@ typedef struct _OptionInfo {
 
 /*****************************************************************************/
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (active_connection_state_to_string, NMActiveConnectionState,
+static
+NM_UTILS_LOOKUP_STR_DEFINE (active_connection_state_to_string, NMActiveConnectionState,
 	NM_UTILS_LOOKUP_DEFAULT (N_("unknown")),
 	NM_UTILS_LOOKUP_ITEM (NM_ACTIVE_CONNECTION_STATE_ACTIVATING,   N_("activating")),
 	NM_UTILS_LOOKUP_ITEM (NM_ACTIVE_CONNECTION_STATE_ACTIVATED,    N_("activated")),
@@ -71,7 +72,8 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (active_connection_state_to_string, NMActiveCo
 	NM_UTILS_LOOKUP_ITEM_IGNORE (NM_ACTIVE_CONNECTION_STATE_UNKNOWN),
 )
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (vpn_connection_state_to_string, NMVpnConnectionState,
+static
+NM_UTILS_LOOKUP_STR_DEFINE (vpn_connection_state_to_string, NMVpnConnectionState,
 	NM_UTILS_LOOKUP_DEFAULT (N_("unknown")),
 	NM_UTILS_LOOKUP_ITEM (NM_VPN_CONNECTION_STATE_PREPARE,       N_("VPN connecting (prepare)")),
 	NM_UTILS_LOOKUP_ITEM (NM_VPN_CONNECTION_STATE_NEED_AUTH,     N_("VPN connecting (need authentication)")),
@@ -421,7 +423,7 @@ _metagen_con_show_row_data_init_primary_active (MetagenConShowRowData *row_data)
 		g_object_unref (row_data->primary_active);
 		row_data->primary_active = g_object_ref (best_ac);
 	}
-	g_clear_pointer (&row_data->all_active, g_ptr_array_unref);
+	nm_clear_pointer (&row_data->all_active, g_ptr_array_unref);
 }
 
 static void
@@ -434,7 +436,7 @@ _metagen_con_show_row_data_destroy (gpointer data)
 
 	g_clear_object (&row_data->connection);
 	g_clear_object (&row_data->primary_active);
-	g_clear_pointer (&row_data->all_active, g_ptr_array_unref);
+	nm_clear_pointer (&row_data->all_active, g_ptr_array_unref);
 	g_slice_free (MetagenConShowRowData, row_data);
 }
 
@@ -855,6 +857,7 @@ const NmcMetaGenericInfo *const metagen_con_active_vpn[_NMC_GENERIC_INFO_TYPE_CO
                                          NM_SETTING_MACSEC_SETTING_NAME"," \
                                          NM_SETTING_MACVLAN_SETTING_NAME"," \
                                          NM_SETTING_VXLAN_SETTING_NAME"," \
+                                         NM_SETTING_VRF_SETTING_NAME"," \
                                          NM_SETTING_WPAN_SETTING_NAME","\
                                          NM_SETTING_6LOWPAN_SETTING_NAME","\
                                          NM_SETTING_WIREGUARD_SETTING_NAME","\
@@ -1111,6 +1114,10 @@ usage_connection_modify (void)
 	              "The '+' sign allows appending items instead of overwriting the whole value.\n"
 	              "The '-' sign allows removing selected items instead of the whole value.\n"
 	              "\n"
+	              "ARGUMENTS := remove <setting>\n"
+	              "\n"
+	              "Remove a setting from the connection profile.\n"
+	              "\n"
 	              "Examples:\n"
 	              "nmcli con mod home-wifi wifi.ssid rakosnicek\n"
 	              "nmcli con mod em1-1 ipv4.method manual ipv4.addr \"192.168.1.2/24, 10.10.1.5/8\"\n"
@@ -1118,7 +1125,8 @@ usage_connection_modify (void)
 	              "nmcli con mod em1-1 -ipv4.dns 1\n"
 	              "nmcli con mod em1-1 -ipv6.addr \"abbe::cafe/56\"\n"
 	              "nmcli con mod bond0 +bond.options mii=500\n"
-	              "nmcli con mod bond0 -bond.options downdelay\n\n"));
+	              "nmcli con mod bond0 -bond.options downdelay\n"
+	              "nmcli con mod em1-1 remove sriov\n\n"));
 }
 
 static void
@@ -1348,9 +1356,9 @@ nmc_connection_profile_details (NMConnection *connection, NmCli *nmc)
 	const char *base_hdr = _("Connection profile details");
 	gboolean was_output = FALSE;
 
-	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
+	if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0)
 		fields_str = fields_common;
-	else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
+	else if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "all") == 0)
 		fields_str = fields_all;
 	else
 		fields_str = nmc->required_fields;
@@ -1380,9 +1388,13 @@ nmc_connection_profile_details (NMConnection *connection, NmCli *nmc)
 		for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++)
 			row[i].info = (const NMMetaAbstractInfo *) &nm_meta_setting_infos_editor[i];
 
-		print_required_fields (&nmc->nmc_config, NMC_OF_FLAG_MAIN_HEADER_ONLY,
-		                       out_indices, header_name,
-		                       0, row);
+		print_required_fields (&nmc->nmc_config,
+		                       &nmc->pager_data,
+		                       NMC_OF_FLAG_MAIN_HEADER_ONLY,
+		                       out_indices,
+		                       header_name,
+		                       0,
+		                       row);
 	}
 
 	/* Loop through the required settings and print them. */
@@ -1436,8 +1448,8 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 	const char *base_hdr = _("Activate connection details");
 	gboolean was_output = FALSE;
 
-	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) {
-	} else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) {
+	if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0) {
+	} else if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "all") == 0) {
 	} else
 		fields_str = nmc->required_fields;
 
@@ -1465,9 +1477,13 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 		for (i = 0; nmc_fields_con_active_details_groups[i]; i++)
 			row[i].info = (const NMMetaAbstractInfo *) nmc_fields_con_active_details_groups[i];
 
-		print_required_fields (&nmc->nmc_config, NMC_OF_FLAG_MAIN_HEADER_ONLY,
-		                       out_indices, header_name,
-		                       0, row);
+		print_required_fields (&nmc->nmc_config,
+		                       &nmc->pager_data,
+		                       NMC_OF_FLAG_MAIN_HEADER_ONLY,
+		                       out_indices,
+		                       header_name,
+		                       0,
+		                       row);
 	}
 
 	/* Loop through the groups and print them. */
@@ -1500,7 +1516,7 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 		}
 
 		/* IP4 */
-		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[1]->name) == 0) {
+		if (g_ascii_strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[1]->name) == 0) {
 			gboolean b1 = FALSE;
 			NMIPConfig *cfg4 = nm_active_connection_get_ip4_config (acon);
 
@@ -1509,7 +1525,7 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 		}
 
 		/* DHCP4 */
-		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[2]->name) == 0) {
+		if (g_ascii_strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[2]->name) == 0) {
 			gboolean b1 = FALSE;
 			NMDhcpConfig *dhcp4 = nm_active_connection_get_dhcp4_config (acon);
 
@@ -1518,7 +1534,7 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 		}
 
 		/* IP6 */
-		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[3]->name) == 0) {
+		if (g_ascii_strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[3]->name) == 0) {
 			gboolean b1 = FALSE;
 			NMIPConfig *cfg6 = nm_active_connection_get_ip6_config (acon);
 
@@ -1527,7 +1543,7 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
 		}
 
 		/* DHCP6 */
-		if (strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[4]->name) == 0) {
+		if (g_ascii_strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name,  nmc_fields_con_active_details_groups[4]->name) == 0) {
 			gboolean b1 = FALSE;
 			NMDhcpConfig *dhcp6 = nm_active_connection_get_dhcp6_config (acon);
 
@@ -1594,13 +1610,13 @@ split_required_fields_for_con_show (const char *input,
 		if (dot)
 			*dot = '\0';
 
-		is_all = !dot && strcasecmp (s_mutable, "all") == 0;
-		is_common = !dot && strcasecmp (s_mutable, "common") == 0;
+		is_all = !dot && g_ascii_strcasecmp (s_mutable, "all") == 0;
+		is_common = !dot && g_ascii_strcasecmp (s_mutable, "common") == 0;
 
 		found = FALSE;
 		for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
 			if (   is_all || is_common
-			    || !strcasecmp (s_mutable, nm_meta_setting_infos[i].setting_name)) {
+			    || !g_ascii_strcasecmp (s_mutable, nm_meta_setting_infos[i].setting_name)) {
 				if (dot)
 					*dot = '.';
 				g_string_append (str1, s_mutable);
@@ -1614,7 +1630,7 @@ split_required_fields_for_con_show (const char *input,
 
 		for (i = 0; nmc_fields_con_active_details_groups[i]; i++) {
 			if (   is_all || is_common
-			    || !strcasecmp (s_mutable, nmc_fields_con_active_details_groups[i]->name)) {
+			    || !g_ascii_strcasecmp (s_mutable, nmc_fields_con_active_details_groups[i]->name)) {
 				if (dot)
 					*dot = '.';
 				g_string_append (str2, s_mutable);
@@ -1626,9 +1642,9 @@ split_required_fields_for_con_show (const char *input,
 		if (!found) {
 			if (dot)
 				*dot = '.';
-			if (!strcasecmp (s_mutable, CON_SHOW_DETAIL_GROUP_PROFILE))
+			if (!g_ascii_strcasecmp (s_mutable, CON_SHOW_DETAIL_GROUP_PROFILE))
 				group_profile = TRUE;
-			else if (!strcasecmp (s_mutable, CON_SHOW_DETAIL_GROUP_ACTIVE))
+			else if (!g_ascii_strcasecmp (s_mutable, CON_SHOW_DETAIL_GROUP_ACTIVE))
 				group_active = TRUE;
 			else {
 				gs_free char *allowed1 = nm_meta_abstract_infos_get_names_str ((const NMMetaAbstractInfo *const*) nm_meta_setting_infos_editor_p (), NULL);
@@ -1960,7 +1976,7 @@ parse_preferred_connection_order (const char *order, GError **error)
 static NMConnection *
 get_connection (NmCli *nmc,
                 int *argc,
-                char ***argv,
+                const char *const**argv,
                 const char **out_selector,
                 const char **out_value,
                 GPtrArray **out_result,
@@ -2011,8 +2027,8 @@ get_connection (NmCli *nmc,
 	return connection;
 }
 
-static NMCResultCode
-do_connections_show (NmCli *nmc, int argc, char **argv)
+static void
+do_connections_show (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	gs_free_error GError *err = NULL;
 	gs_free char *profile_flds = NULL;
@@ -2055,9 +2071,9 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 		if (nmc->complete)
 			goto finish;
 
-		if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
+		if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0)
 			fields_str = NMC_FIELDS_CON_SHOW_COMMON;
-		else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) {
+		else if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "all") == 0) {
 		} else
 			fields_str = nmc->required_fields;
 
@@ -2085,7 +2101,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 			}
 		}
 
-		nm_cli_spawn_pager (nmc);
+		nm_cli_spawn_pager (&nmc->nmc_config, &nmc->pager_data);
 
 		items = con_show_get_items (nmc, active_only, show_active_fields, order);
 		g_ptr_array_add (items, NULL);
@@ -2118,7 +2134,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 		 * option after the connection ids */
 		if (!nmc->nmc_config.show_secrets && !nmc->complete) {
 			int argc_cp = argc;
-			char **argv_cp = argv;
+			const char *const*argv_cp = argv;
 
 			do {
 				if (NM_IN_STRSET (*argv_cp, "id", "uuid", "path", "filename", "apath")) {
@@ -2268,7 +2284,6 @@ finish:
 		g_string_printf (nmc->return_text, _("Error: %s."), err->message);
 		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
 	}
-	return nmc->return_value;
 }
 
 static NMActiveConnection *
@@ -2838,8 +2853,8 @@ nmc_activate_connection (NmCli *nmc,
 	return TRUE;
 }
 
-static NMCResultCode
-do_connection_up (NmCli *nmc, int argc, char **argv)
+static void
+do_connection_up (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMConnection *connection = NULL;
 	const char *ifname = NULL;
@@ -2847,9 +2862,9 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 	const char *nsp = NULL;
 	const char *pwds = NULL;
 	gs_free_error GError *error = NULL;
-	char **arg_arr = NULL;
+	gs_strfreev char **arg_arr = NULL;
 	int arg_num;
-	char ***argv_ptr;
+	const char *const**argv_ptr;
 	int *argc_ptr;
 
 	/*
@@ -2872,7 +2887,7 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 		line = nmc_readline (&nmc->nmc_config,
 		                     PROMPT_CONNECTION);
 		nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
-		argv_ptr = &arg_arr;
+		argv_ptr = (const char *const**) &arg_arr;
 		argc_ptr = &arg_num;
 	}
 
@@ -2880,7 +2895,8 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 		connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, NULL, NULL, &error);
 		if (!connection) {
 			g_string_printf (nmc->return_text, _("Error: %s."), error->message);
-			return error->code;
+			nmc->return_value = error->code;
+			return;
 		}
 	}
 
@@ -2893,7 +2909,8 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 
 			ifname = *argv;
@@ -2905,7 +2922,8 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 
 			ap = *argv;
@@ -2917,7 +2935,8 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 
 			if (argc == 1 && nmc->complete)
@@ -2927,14 +2946,15 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 		}
 		else if (!nmc->complete) {
 			g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv);
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 
 		next_arg (nmc, &argc, &argv, NULL);
 	}
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	/* Use nowait_flag instead of should_wait because exiting has to be postponed till
 	 * active_connection_state_cb() is called. That gives NM time to check our permissions
@@ -2947,14 +2967,13 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 		g_string_printf (nmc->return_text, _("Error: %s."),
 		                 error->message);
 		nmc->should_wait--;
-		return error->code;
+		nmc->return_value = error->code;
+		return;
 	}
 
 	/* Start progress indication */
 	if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
 		progress_id = g_timeout_add (120, progress_cb, _("preparing"));
-
-	return nmc->return_value;
 }
 
 /*****************************************************************************/
@@ -3096,14 +3115,14 @@ connection_op_timeout_cb (gpointer user_data)
 	return G_SOURCE_REMOVE;
 }
 
-static NMCResultCode
-do_connection_down (NmCli *nmc, int argc, char **argv)
+static void
+do_connection_down (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMActiveConnection *active;
 	ConnectionCbInfo *info = NULL;
 	const GPtrArray *active_cons;
 	gs_strfreev char **arg_arr = NULL;
-	char **arg_ptr;
+	const char *const*arg_ptr;
 	int arg_num;
 	guint i;
 	gs_unref_ptrarray GPtrArray *found_active_cons = NULL;
@@ -3125,11 +3144,12 @@ do_connection_down (NmCli *nmc, int argc, char **argv)
 			line = nmc_readline (&nmc->nmc_config,
 			                     PROMPT_ACTIVE_CONNECTIONS);
 			nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
-			arg_ptr = arg_arr;
+			arg_ptr = (const char *const*) arg_arr;
 		}
 		if (arg_num == 0) {
 			g_string_printf (nmc->return_text, _("Error: No connection specified."));
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 	}
 
@@ -3147,7 +3167,8 @@ do_connection_down (NmCli *nmc, int argc, char **argv)
 			arg_ptr++;
 			if (!arg_num) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 		}
 
@@ -3168,12 +3189,13 @@ do_connection_down (NmCli *nmc, int argc, char **argv)
 
 	if (!found_active_cons) {
 		g_string_printf (nmc->return_text, _("Error: no active connection provided."));
-		return NMC_RESULT_ERROR_NOT_FOUND;
+		nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
+		return;
 	}
 	nm_assert (found_active_cons->len > 0);
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	if (nmc->timeout > 0) {
 		nmc->should_wait++;
@@ -3210,8 +3232,6 @@ do_connection_down (NmCli *nmc, int argc, char **argv)
 			}
 		}
 	}
-
-	return nmc->return_value;
 }
 
 /*****************************************************************************/
@@ -3699,7 +3719,7 @@ prompt_yes_no (gboolean default_yes, char *delim)
 }
 
 static NMSetting *
-is_setting_valid (NMConnection *connection, const NMMetaSettingValidPartItem *const*valid_settings_main, const NMMetaSettingValidPartItem *const*valid_settings_slave, char *setting)
+is_setting_valid (NMConnection *connection, const NMMetaSettingValidPartItem *const*valid_settings_main, const NMMetaSettingValidPartItem *const*valid_settings_slave, const char *setting)
 {
 	const char *setting_name;
 
@@ -3834,7 +3854,7 @@ _meta_abstract_complete (const NMMetaAbstractInfo *abstract_info, const char *te
 
 	values = nm_meta_abstract_info_complete (abstract_info,
 	                                         nmc_meta_environment,
-	                                         nmc_meta_environment_arg,
+	                                         (gpointer) nmc_meta_environment_arg,
 	                                         &ctx,
 	                                         text,
 	                                         NULL,
@@ -4634,7 +4654,7 @@ complete_option (NmCli *nmc, const NMMetaAbstractInfo *abstract_info, const char
 
 	values = nm_meta_abstract_info_complete (abstract_info,
 	                                         nmc_meta_environment,
-	                                         nmc_meta_environment_arg,
+	                                         (gpointer) nmc_meta_environment_arg,
 	                                         &ctx,
 	                                         prefix,
 	                                         &complete_filename,
@@ -4659,6 +4679,27 @@ complete_option (NmCli *nmc, const NMMetaAbstractInfo *abstract_info, const char
 }
 
 static void
+complete_existing_setting (NmCli *nmc, NMConnection *connection, const char *prefix)
+{
+	gs_free NMSetting **settings = NULL;
+	const NMMetaSettingInfoEditor *editor;
+	guint i;
+
+	settings = nm_connection_get_settings (connection, NULL);
+	for (i = 0; settings && settings[i]; i++) {
+		editor = nm_meta_setting_info_editor_find_by_setting (settings[i]);
+
+		if (!prefix || g_str_has_prefix (editor->general->setting_name, prefix))
+			g_print ("%s\n", editor->general->setting_name);
+
+		if (editor->alias) {
+			if (!prefix || g_str_has_prefix (editor->alias, prefix))
+				g_print ("%s\n", editor->alias);
+		}
+	}
+}
+
+static void
 complete_property (NmCli *nmc, const char *setting_name, const char *property, const char *prefix, NMConnection *connection)
 {
 	const NMMetaPropertyInfo *property_info;
@@ -4671,7 +4712,25 @@ complete_property (NmCli *nmc, const char *setting_name, const char *property, c
 /*****************************************************************************/
 
 static gboolean
-get_value (const char **value, int *argc, char ***argv, const char *option, GError **error)
+connection_remove_setting (NMConnection *connection, NMSetting *setting, GError **error)
+{
+	gboolean mandatory;
+
+	g_return_val_if_fail (setting, FALSE);
+
+	mandatory = is_setting_mandatory (connection, setting);
+	if (!mandatory) {
+		nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting));
+		return TRUE;
+	}
+	g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+	             _("Error: setting '%s' is mandatory and cannot be removed."),
+	             nm_setting_get_name (setting));
+	return FALSE;
+}
+
+static gboolean
+get_value (const char **value, int *argc, const char *const**argv, const char *option, GError **error)
 {
 	if (!**argv) {
 		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
@@ -4691,11 +4750,12 @@ get_value (const char **value, int *argc, char ***argv, const char *option, GErr
 }
 
 gboolean
-nmc_read_connection_properties (NmCli *nmc,
-                                NMConnection *connection,
-                                int *argc,
-                                char ***argv,
-                                GError **error)
+nmc_process_connection_properties (NmCli *nmc,
+                                   NMConnection *connection,
+                                   int *argc,
+                                   const char *const**argv,
+                                   gboolean allow_setting_removal,
+                                   GError **error)
 {
 	/* First check if we have a slave-type, as this would mean we will not
 	 * have ip properties but possibly others, slave-type specific.
@@ -4729,7 +4789,53 @@ nmc_read_connection_properties (NmCli *nmc,
 		default:  modifier = NM_META_ACCESSOR_MODIFIER_SET; option = option_orig;     break;
 		}
 
-		if ((tmp = strchr (option, '.'))) {
+		if (   allow_setting_removal
+		    && modifier == NM_META_ACCESSOR_MODIFIER_SET
+		    && nm_streq (option, "remove")) {
+			NMSetting *ss;
+			const char *setting_name;
+
+			(*argc)--;
+			(*argv)++;
+
+			if (*argc == 1 && nmc->complete) {
+				complete_existing_setting (nmc, connection, value);
+				return TRUE;
+			}
+
+			if (!*argc) {
+				g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+				                     _("Error: missing setting."));
+				return FALSE;
+			}
+
+			setting_name = **argv;
+			(*argc)--;
+			(*argv)++;
+
+			ss = is_setting_valid (connection,
+			                       type_settings,
+			                       slv_settings,
+			                       setting_name);
+			if (!ss) {
+				if (check_valid_name (setting_name,
+				                      type_settings,
+				                      slv_settings,
+				                      NULL)) {
+					g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+					             _("Setting '%s' is not present in the connection."),
+					             setting_name);
+				} else {
+					g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+					             _("Error: invalid setting argument '%s'."),
+					             setting_name);
+				}
+				return FALSE;
+			}
+
+			if (!connection_remove_setting (connection, ss, error))
+				return FALSE;
+		} else if ((tmp = strchr (option, '.'))) {
 			gs_free char *option_sett = g_strndup (option, tmp - option);
 			const char *option_prop = &tmp[1];
 			const char *option_sett_expanded;
@@ -4823,8 +4929,12 @@ nmc_read_connection_properties (NmCli *nmc,
 			}
 
 			if (!chosen) {
-				if (*argc == 1 && nmc->complete)
+				if (*argc == 1 && nmc->complete) {
+					if (   allow_setting_removal
+					    && g_str_has_prefix ("remove", option))
+						g_print ("remove\n");
 					complete_property_name (nmc, connection, modifier, option, NULL);
+				}
 				g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
 				             _("Error: invalid <setting>.<property> '%s'."), option);
 				return FALSE;
@@ -4844,7 +4954,6 @@ nmc_read_connection_properties (NmCli *nmc,
 			if (!set_option (nmc, connection, chosen, value, error))
 				return FALSE;
 		}
-
 	} while (*argc);
 
 	return TRUE;
@@ -5243,8 +5352,8 @@ again:
 	return TRUE;
 }
 
-static NMCResultCode
-do_connection_add (NmCli *nmc, int argc, char **argv)
+static void
+do_connection_add (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingConnection *s_con;
@@ -5267,7 +5376,7 @@ do_connection_add (NmCli *nmc, int argc, char **argv)
 read_properties:
 	g_clear_error (&error);
 	/* Get the arguments from the command line if any */
-	if (argc && !nmc_read_connection_properties (nmc, connection, &argc, &argv, &error)) {
+	if (argc && !nmc_process_connection_properties (nmc, connection, &argc, &argv, FALSE, &error)) {
 		if (g_strcmp0 (*argv, "--") == 0 && !seen_dash_dash) {
 			/* This is for compatibility with older nmcli that required
 			 * options and properties to be separated with "--" */
@@ -5401,7 +5510,6 @@ read_properties:
 
 finish:
 	reset_options ();
-	return nmc->return_value;
 }
 
 /*****************************************************************************/
@@ -5730,7 +5838,7 @@ gen_vpn_uuids (const char *text, int state)
 	const char **uuids;
 	char *ret;
 
-	connections = nm_client_get_connections (nm_cli.client);
+	connections = nm_client_get_connections (nm_cli_global_readline->client);
 	if (connections->len < 1)
 		return NULL;
 
@@ -5747,7 +5855,7 @@ gen_vpn_ids (const char *text, int state)
 	const char **ids;
 	char *ret;
 
-	connections = nm_client_get_connections (nm_cli.client);
+	connections = nm_client_get_connections (nm_cli_global_readline->client);
 	if (connections->len < 1)
 		return NULL;
 
@@ -6878,23 +6986,6 @@ print_setting_description (NMSetting *setting)
 	g_strfreev (all_props);
 }
 
-static gboolean
-connection_remove_setting (NMConnection *connection, NMSetting *setting)
-{
-	gboolean mandatory;
-
-	g_return_val_if_fail (setting, FALSE);
-
-	mandatory = is_setting_mandatory (connection, setting);
-	if (!mandatory) {
-		nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting));
-		return TRUE;
-	}
-	g_print (_("Error: setting '%s' is mandatory and cannot be removed.\n"),
-	         nm_setting_get_name (setting));
-	return FALSE;
-}
-
 static void
 editor_show_status_line (NMConnection *connection, gboolean dirty, gboolean temp)
 {
@@ -7632,8 +7723,12 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 					ss = menu_ctx.curr_setting;
 
 				if (descr_all) {
+					gs_free_error GError *local = NULL;
+
 					/* Remove setting from the connection */
-					connection_remove_setting (connection, ss);
+					if (!connection_remove_setting (connection, ss, &local))
+						g_print ("%s\n", local->message);
+
 					if (ss == menu_ctx.curr_setting) {
 						/* If we removed the setting we are in, go up */
 						menu_switch_to_level0 (&nmc->nmc_config, &menu_ctx, BASE_PROMPT);
@@ -7664,8 +7759,11 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 						                          valid_settings_slave,
 						                          cmd_arg_p);
 						if (s_tmp) {
+							gs_free_error GError *local = NULL;
+
 							/* Remove setting from the connection */
-							connection_remove_setting (connection, s_tmp);
+							if (!connection_remove_setting (connection, s_tmp, &local))
+								g_print ("%s\n", local->message);
 
 							/* coverity[copy_paste_error] - suppress Coverity COPY_PASTE_ERROR defect */
 							if (ss == menu_ctx.curr_setting) {
@@ -7919,7 +8017,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 					connection_changed = FALSE;
 				}
 
-				source = g_timeout_source_new (10 * NM_UTILS_MSEC_PER_SECOND);
+				source = g_timeout_source_new (10 * NM_UTILS_MSEC_PER_SEC);
 				g_source_set_callback (source, editor_save_timeout, &timeout, NULL);
 				g_source_attach (source, g_main_loop_get_context (loop));
 
@@ -8259,8 +8357,8 @@ nmc_complete_connection_type (const char *prefix)
 	}
 }
 
-static NMCResultCode
-do_connection_edit (NmCli *nmc, int argc, char **argv)
+static void
+do_connection_edit (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	const GPtrArray *connections;
 	gs_unref_object NMConnection *connection = NULL;
@@ -8295,7 +8393,8 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 	else {
 		if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) {
 			g_string_assign (nmc->return_text, error->message);
-			return error->code;
+			nmc->return_value = error->code;
+			return;
 		}
 	}
 
@@ -8325,7 +8424,8 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 		} else {
 			g_string_printf (nmc->return_text,
 			                 _("Error: only one of 'id', 'filename', uuid, or 'path' can be provided."));
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 	}
 
@@ -8335,11 +8435,12 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 
 		found_con = nmc_find_connection (connections, selector, con, NULL, nmc->complete);
 		if (nmc->complete)
-			return nmc->return_value;
+			return;
 
 		if (!found_con) {
 			g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con);
-			return NMC_RESULT_ERROR_NOT_FOUND;
+			nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
+			return;
 		}
 
 		/* Duplicate the connection and use that so that we need not
@@ -8374,7 +8475,7 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 		if (nmc->complete) {
 			if (type && argc == 0)
 				nmc_complete_connection_type (type);
-			return nmc->return_value;
+			return;
 		}
 
 		connection_type = check_valid_name_toplevel (type, &slave_type, &err1);
@@ -8450,7 +8551,7 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 	nm_clear_g_free (&nmc_tab_completion.con_type);
 	nmc_tab_completion.connection = NULL;
 
-	return nmc->return_value;
+	return;
 }
 
 static void
@@ -8478,10 +8579,8 @@ modify_connection_cb (GObject *connection,
 	quit ();
 }
 
-static NMCResultCode
-do_connection_modify (NmCli *nmc,
-                      int argc,
-                      char **argv)
+static void
+do_connection_modify (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMConnection *connection = NULL;
 	NMRemoteConnection *rc = NULL;
@@ -8496,7 +8595,8 @@ do_connection_modify (NmCli *nmc,
 	connection = get_connection (nmc, &argc, &argv, NULL, NULL, NULL, &error);
 	if (!connection) {
 		g_string_printf (nmc->return_text, _("Error: %s."), error->message);
-		return error->code;
+		nmc->return_value = error->code;
+		return;
 	}
 
 	rc = nm_client_get_connection_by_uuid (nmc->client,
@@ -8504,21 +8604,21 @@ do_connection_modify (NmCli *nmc,
 	if (!rc) {
 		g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."),
 		                 nm_connection_get_uuid (connection));
-		return NMC_RESULT_ERROR_NOT_FOUND;
+		nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
+		return;
 	}
 
-	if (!nmc_read_connection_properties (nmc, NM_CONNECTION (rc), &argc, &argv, &error)) {
+	if (!nmc_process_connection_properties (nmc, NM_CONNECTION (rc), &argc, &argv, TRUE, &error)) {
 		g_string_assign (nmc->return_text, error->message);
-		return error->code;
+		nmc->return_value = error->code;
+		return;
 	}
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	update_connection (rc, temporary, modify_connection_cb, nmc);
 	nmc->should_wait++;
-
-	return nmc->return_value;
 }
 
 static void
@@ -8548,8 +8648,8 @@ clone_connection_cb (GObject *client,
 	quit ();
 }
 
-static NMCResultCode
-do_connection_clone (NmCli *nmc, int argc, char **argv)
+static void
+do_connection_clone (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMConnection *connection = NULL;
 	gs_unref_object NMConnection *new_connection = NULL;
@@ -8557,9 +8657,9 @@ do_connection_clone (NmCli *nmc, int argc, char **argv)
 	gs_free char *new_name_free = NULL;
 	gs_free char *uuid = NULL;
 	gboolean temporary = FALSE;
-	char **arg_arr = NULL;
+	gs_strfreev char **arg_arr = NULL;
 	int arg_num;
-	char ***argv_ptr;
+	const char *const**argv_ptr;
 	int *argc_ptr;
 	GError *error = NULL;
 
@@ -8580,18 +8680,19 @@ do_connection_clone (NmCli *nmc, int argc, char **argv)
 		line = nmc_readline (&nmc->nmc_config,
 		                     PROMPT_CONNECTION);
 		nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
-		argv_ptr = &arg_arr;
+		argv_ptr = (const char *const**) &arg_arr;
 		argc_ptr = &arg_num;
 	}
 
 	connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, NULL, NULL, &error);
 	if (!connection) {
 		g_string_printf (nmc->return_text, _("Error: %s."), error->message);
-		return error->code;
+		nmc->return_value = error->code;
+		return;
 	}
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	if (argv[0])
 		new_name = *argv;
@@ -8600,12 +8701,14 @@ do_connection_clone (NmCli *nmc, int argc, char **argv)
 		                                         _("New connection name: "));
 	} else {
 		g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
 	if (next_arg (nmc->ask ? NULL : nmc, argc_ptr, argv_ptr, NULL) == 0) {
 		g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv);
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
 	new_connection = nm_simple_connection_new_clone (connection);
@@ -8624,8 +8727,6 @@ do_connection_clone (NmCli *nmc, int argc, char **argv)
 	                clone_connection_cb,
 	                _add_connection_info_new (nmc, connection, new_connection));
 	nmc->should_wait++;
-
-	return nmc->return_value;
 }
 
 static void
@@ -8649,13 +8750,13 @@ delete_cb (GObject *con, GAsyncResult *result, gpointer user_data)
 	}
 }
 
-static NMCResultCode
-do_connection_delete (NmCli *nmc, int argc, char **argv)
+static void
+do_connection_delete (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMConnection *connection;
 	ConnectionCbInfo *info = NULL;
 	gs_strfreev char **arg_arr = NULL;
-	char **arg_ptr;
+	const char *const*arg_ptr;
 	guint i;
 	int arg_num;
 	nm_auto_free_gstring GString *invalid_cons = NULL;
@@ -8679,7 +8780,7 @@ do_connection_delete (NmCli *nmc, int argc, char **argv)
 			line = nmc_readline (&nmc->nmc_config,
 			                     PROMPT_CONNECTIONS);
 			nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
-			arg_ptr = arg_arr;
+			arg_ptr = (const char *const*) arg_arr;
 		}
 		if (arg_num == 0) {
 			g_string_printf (nmc->return_text, _("Error: No connection specified."));
@@ -8750,7 +8851,6 @@ finish:
 		                 invalid_cons->str);
 		nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
 	}
-	return nmc->return_value;
 }
 
 static void
@@ -8795,8 +8895,8 @@ connection_removed (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
 	connection_unwatch (nmc, connection);
 }
 
-static NMCResultCode
-do_connection_monitor (NmCli *nmc, int argc, char **argv)
+static void
+do_connection_monitor (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	GError *error = NULL;
 	guint i;
@@ -8817,7 +8917,8 @@ do_connection_monitor (NmCli *nmc, int argc, char **argv)
 				if (!nmc->complete)
 					g_printerr (_("Error: %s.\n"), error->message);
 				g_string_printf (nmc->return_text, _("Error: not all connections found."));
-				return error->code;
+				nmc->return_value = error->code;
+				return;
 			}
 
 			if (nmc->complete)
@@ -8828,7 +8929,7 @@ do_connection_monitor (NmCli *nmc, int argc, char **argv)
 	}
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	for (i = 0; i < connections->len; i++)
 		connection_watch (nmc, connections->pdata[i]);
@@ -8840,19 +8941,17 @@ do_connection_monitor (NmCli *nmc, int argc, char **argv)
 	}
 
 	g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED, G_CALLBACK (connection_removed), nmc);
-
-	return NMC_RESULT_SUCCESS;
 }
 
-static NMCResultCode
-do_connection_reload (NmCli *nmc, int argc, char **argv)
+static void
+do_connection_reload (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	gs_unref_variant GVariant *result = NULL;
 	gs_free_error GError *error = NULL;
 
 	next_arg (nmc, &argc, &argv, NULL);
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	result = nmc_dbus_call_sync (nmc,
 	                             "/org/freedesktop/NetworkManager/Settings",
@@ -8866,33 +8965,31 @@ do_connection_reload (NmCli *nmc, int argc, char **argv)
 		                 nmc_error_get_simple_message (error));
 		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 	}
-
-	return nmc->return_value;
 }
 
-static NMCResultCode
-do_connection_load (NmCli *nmc, int argc, char **argv)
+static void
+do_connection_load (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	GError *error = NULL;
-	char **filenames, **failures = NULL;
+	gs_free const char **filenames = NULL;
+	gs_strfreev char **failures = NULL;
 	int i;
 
 	next_arg (nmc, &argc, &argv, NULL);
 	if (argc == 0) {
 		g_string_printf (nmc->return_text, _("Error: No connection specified."));
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
-	if (nmc->complete)
-		return NMC_RESULT_COMPLETE_FILE;
+	if (nmc->complete) {
+		nmc->return_value = NMC_RESULT_COMPLETE_FILE;
+		return;
+	}
 
-	filenames = g_new (char *, argc + 1);
-	for (i = 0; i < argc; i++)
-		filenames[i] = argv[i];
-	filenames[i] = NULL;
+	filenames = (const char **) nm_utils_strv_dup ((char **) argv, argc, FALSE);
 
-	nm_client_load_connections (nmc->client, filenames, &failures, NULL, &error);
-	g_free (filenames);
+	nm_client_load_connections (nmc->client, (char **) filenames, &failures, NULL, &error);
 	if (error) {
 		g_string_printf (nmc->return_text, _("Error: failed to load connection: %s."),
 		                 nmc_error_get_simple_message (error));
@@ -8903,16 +9000,13 @@ do_connection_load (NmCli *nmc, int argc, char **argv)
 	if (failures) {
 		for (i = 0; failures[i]; i++)
 			g_printerr (_("Could not load file '%s'\n"), failures[i]);
-		g_strfreev (failures);
 	}
-
-	return nmc->return_value;
 }
 
 #define PROMPT_IMPORT_FILE N_("File to import: ")
 
-static NMCResultCode
-do_connection_import (NmCli *nmc, int argc, char **argv)
+static void
+do_connection_import (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	gs_free_error GError *error = NULL;
 	const char *type = NULL, *filename = NULL;
@@ -8943,7 +9037,8 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 			filename = nm_strstrip (filename_ask);
 		} else {
 			g_string_printf (nmc->return_text, _("Error: No arguments provided."));
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 	}
 
@@ -8959,7 +9054,8 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 
 			if (   argc == 1
@@ -8980,7 +9076,8 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			if (argc == 1 && nmc->complete)
 				nmc->return_value = NMC_RESULT_COMPLETE_FILE;
@@ -8990,22 +9087,25 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 				g_printerr (_("Warning: 'file' already specified, ignoring extra one.\n"));
 		} else {
 			g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv);
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 
 		next_arg (nmc, &argc, &argv, NULL);
 	}
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	if (!type) {
 		g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 	if (!filename) {
 		g_string_printf (nmc->return_text, _("Error: 'file' argument is required."));
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
 	if (nm_streq (type, "wireguard"))
@@ -9014,7 +9114,8 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 		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);
-			return NMC_RESULT_ERROR_UNKNOWN;
+			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+			return;
 		}
 
 		/* Import VPN configuration */
@@ -9022,7 +9123,8 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 		if (!plugin) {
 			g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
 			                 error->message);
-			return NMC_RESULT_ERROR_UNKNOWN;
+			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+			return;
 		}
 
 		connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
@@ -9031,7 +9133,8 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 	if (!connection) {
 		g_string_printf (nmc->return_text, _("Error: failed to import '%s': %s."),
 		                 filename, error->message);
-		return NMC_RESULT_ERROR_UNKNOWN;
+		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+		return;
 	}
 
 	add_connection (nmc->client,
@@ -9040,12 +9143,10 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 	                add_connection_cb,
 	                _add_connection_info_new (nmc, NULL, connection));
 	nmc->should_wait++;
-
-	return nmc->return_value;
 }
 
-static NMCResultCode
-do_connection_export (NmCli *nmc, int argc, char **argv)
+static void
+do_connection_export (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMConnection *connection = NULL;
 	const char *out_name = NULL;
@@ -9055,9 +9156,9 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 	NMVpnEditorPlugin *plugin;
 	gs_free_error GError *error = NULL;
 	char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX";
-	char **arg_arr = NULL;
+	gs_strfreev char **arg_arr = NULL;
 	int arg_num;
-	char ***argv_ptr;
+	const char *const**argv_ptr;
 	int *argc_ptr;
 
 	next_arg (nmc, &argc, &argv, NULL);
@@ -9073,7 +9174,7 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 		line = nmc_readline (&nmc->nmc_config,
 		                     PROMPT_VPN_CONNECTION);
 		nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
-		argv_ptr = &arg_arr;
+		argv_ptr = (const char *const**) &arg_arr;
 		argc_ptr = &arg_num;
 	}
 
@@ -9085,7 +9186,7 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 	}
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	out_name = *argv;
 
@@ -9155,7 +9256,6 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 finish:
 	if (!out_name && path)
 		unlink (path);
-	return nmc->return_value;
 }
 
 static char *
@@ -9166,7 +9266,7 @@ gen_func_connection_names (const char *text, int state)
 	const char **connection_names;
 	char *ret;
 
-	connections = nm_client_get_connections (nm_cli.client);
+	connections = nm_client_get_connections (nm_cli_global_readline->client);
 	if (connections->len == 0)
 		return NULL;
 
@@ -9189,10 +9289,10 @@ gen_func_active_connection_names (const char *text, int state)
 	const char **connections;
 	char *ret;
 
-	if (!nm_cli.client)
+	if (!nm_cli_global_readline->client)
 		return NULL;
 
-	acs = nm_client_get_active_connections (nm_cli.client);
+	acs = nm_client_get_active_connections (nm_cli_global_readline->client);
 	if (!acs || acs->len == 0)
 		return NULL;
 
@@ -9243,46 +9343,42 @@ nmcli_con_tab_completion (const char *text, int start, int end)
 	if (generator_func)
 		match_array = rl_completion_matches (text, generator_func);
 
-	g_clear_pointer (&nmc_tab_completion.words, g_strfreev);
+	nm_clear_pointer (&nmc_tab_completion.words, g_strfreev);
 	return match_array;
 }
 
-static const NMCCommand connection_cmds[] = {
-	{ "show",     do_connections_show,      usage_connection_show,     TRUE,   TRUE },
-	{ "up",       do_connection_up,         usage_connection_up,       TRUE,   TRUE },
-	{ "down",     do_connection_down,       usage_connection_down,     TRUE,   TRUE },
-	{ "add",      do_connection_add,        usage_connection_add,      TRUE,   TRUE },
-	{ "edit",     do_connection_edit,       usage_connection_edit,     TRUE,   TRUE },
-	{ "delete",   do_connection_delete,     usage_connection_delete,   TRUE,   TRUE },
-	{ "reload",   do_connection_reload,     usage_connection_reload,   FALSE,  FALSE },
-	{ "load",     do_connection_load,       usage_connection_load,     TRUE,   TRUE },
-	{ "modify",   do_connection_modify,     usage_connection_modify,   TRUE,   TRUE },
-	{ "clone",    do_connection_clone,      usage_connection_clone,    TRUE,   TRUE },
-	{ "import",   do_connection_import,     usage_connection_import,   TRUE,   TRUE },
-	{ "export",   do_connection_export,     usage_connection_export,   TRUE,   TRUE },
-	{ "monitor",  do_connection_monitor,    usage_connection_monitor,  TRUE,   TRUE },
-	{ NULL,       do_connections_show,      usage,                     TRUE,   TRUE },
-};
+void
+nmc_command_func_connection (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
+{
+	static const NMCCommand cmds[] = {
+		{ "show",   do_connections_show,   usage_connection_show,    TRUE,  TRUE  },
+		{ "up",     do_connection_up,      usage_connection_up,      TRUE,  TRUE  },
+		{ "down",   do_connection_down,    usage_connection_down,    TRUE,  TRUE  },
+		{ "add",    do_connection_add,     usage_connection_add,     TRUE,  TRUE  },
+		{ "edit",   do_connection_edit,    usage_connection_edit,    TRUE,  TRUE  },
+		{ "delete", do_connection_delete,  usage_connection_delete,  TRUE,  TRUE  },
+		{ "reload", do_connection_reload,  usage_connection_reload,  FALSE, FALSE },
+		{ "load",   do_connection_load,    usage_connection_load,    TRUE,  TRUE  },
+		{ "modify", do_connection_modify,  usage_connection_modify,  TRUE,  TRUE  },
+		{ "clone",  do_connection_clone,   usage_connection_clone,   TRUE,  TRUE  },
+		{ "import", do_connection_import,  usage_connection_import,  TRUE,  TRUE  },
+		{ "export", do_connection_export,  usage_connection_export,  TRUE,  TRUE  },
+		{ "monitor",do_connection_monitor, usage_connection_monitor, TRUE,  TRUE  },
+		{ NULL,     do_connections_show,   usage,                    TRUE,  TRUE  },
+	};
 
-/* Entry point function for connections-related commands: 'nmcli connection' */
-NMCResultCode
-do_connections (NmCli *nmc, int argc, char **argv)
-{
 	next_arg (nmc, &argc, &argv, NULL);
 
-	/* Register polkit agent */
 	nmc_start_polkit_agent_start_try (nmc);
 
 	/* Set completion function for 'nmcli con' */
 	rl_attempted_completion_function = nmcli_con_tab_completion;
 
-	nmc_do_cmd (nmc, connection_cmds, *argv, argc, argv);
-
-	return nmc->return_value;
+	nmc_do_cmd (nmc, cmds, *argv, argc, argv);
 }
 
 void
 monitor_connections (NmCli *nmc)
 {
-	do_connection_monitor (nmc, 0, NULL);
+	do_connection_monitor (NULL, nmc, 0, NULL);
 }
diff --git a/clients/cli/connections.h b/clients/cli/connections.h
index 7fa9d448..79359a1f 100644
--- a/clients/cli/connections.h
+++ b/clients/cli/connections.h
@@ -8,16 +8,15 @@
 
 #include "nmcli.h"
 
-NMCResultCode do_connections (NmCli *nmc, int argc, char **argv);
-
 void monitor_connections (NmCli *nmc);
 
 gboolean
-nmc_read_connection_properties (NmCli *nmc,
-                                NMConnection *connection,
-                                int *argc,
-                                char ***argv,
-                                GError **error);
+nmc_process_connection_properties (NmCli *nmc,
+                                   NMConnection *connection,
+                                   int *argc,
+                                   const char *const**argv,
+                                   gboolean allow_remove_setting,
+                                   GError **error);
 
 NMMetaColor nmc_active_connection_state_to_color (NMActiveConnectionState state);
 
diff --git a/clients/cli/devices.c b/clients/cli/devices.c
index c99efd0d..4ea4e772 100644
--- a/clients/cli/devices.c
+++ b/clients/cli/devices.c
@@ -11,13 +11,13 @@
 #include <stdlib.h>
 #include <readline/readline.h>
 
-#include "nm-secret-agent-simple.h"
+#include "nm-glib-aux/nm-secret-utils.h"
+#include "common.h"
+#include "connections.h"
 #include "nm-client-utils.h"
-
+#include "nm-secret-agent-simple.h"
 #include "polkit-agent.h"
 #include "utils.h"
-#include "common.h"
-#include "connections.h"
 
 /* define some prompts */
 #define PROMPT_INTERFACE  _("Interface: ")
@@ -26,9 +26,9 @@
 /*****************************************************************************/
 
 static char *
-ap_wpa_rsn_flags_to_string (NM80211ApSecurityFlags flags)
+ap_wpa_rsn_flags_to_string (NM80211ApSecurityFlags flags, NMMetaAccessorGetType get_type)
 {
-	char *flags_str[13];
+	char *flags_str[16];
 	int i = 0;
 
 	if (flags & NM_802_11_AP_SEC_PAIR_WEP40)
@@ -53,13 +53,19 @@ ap_wpa_rsn_flags_to_string (NM80211ApSecurityFlags flags)
 		flags_str[i++] = "802.1X";
 	if (flags & NM_802_11_AP_SEC_KEY_MGMT_SAE)
 		flags_str[i++] = "sae";
+	if (flags & NM_802_11_AP_SEC_KEY_MGMT_OWE)
+		flags_str[i++] = "owe";
+
 	/* Make sure you grow flags_str when adding items here. */
+	nm_assert (i < G_N_ELEMENTS (flags_str));
 
-	if (i == 0)
-		flags_str[i++] = _("(none)");
+	if (i == 0) {
+		if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY)
+			return g_strdup (_("(none)"));
+		return g_strdup ("(none)");
+	}
 
 	flags_str[i] = NULL;
-
 	return g_strjoinv (" ", flags_str);
 }
 
@@ -990,11 +996,11 @@ nmc_complete_device (NMClient *client, const char *prefix, gboolean wifi_only)
 }
 
 static GSList *
-get_device_list (NmCli *nmc, int argc, char **argv)
+get_device_list (NmCli *nmc, int argc, const char *const*argv)
 {
 	int arg_num = argc;
-	char **arg_arr = NULL;
-	char **arg_ptr = argv;
+	gs_strfreev char **arg_arr = NULL;
+	const char *const*arg_ptr = argv;
 	NMDevice **devices;
 	GSList *queue = NULL;
 	NMDevice *device;
@@ -1007,7 +1013,7 @@ get_device_list (NmCli *nmc, int argc, char **argv)
 			line = nmc_readline (&nmc->nmc_config,
 			                     PROMPT_INTERFACES);
 			nmc_string_to_arg_array (line, NULL, FALSE, &arg_arr, &arg_num);
-			arg_ptr = arg_arr;
+			arg_ptr = (const char *const*) arg_arr;
 		}
 		if (arg_num == 0) {
 			g_string_printf (nmc->return_text, _("Error: No interface specified."));
@@ -1053,7 +1059,7 @@ error:
 }
 
 static NMDevice *
-get_device (NmCli *nmc, int *argc, char ***argv, GError **error)
+get_device (NmCli *nmc, int *argc, const char *const**argv, GError **error)
 {
 	gs_free NMDevice **devices = NULL;
 	gs_free char *ifname_ask = NULL;
@@ -1098,30 +1104,30 @@ compare_aps (gconstpointer a, gconstpointer b, gpointer user_data)
 {
 	NMAccessPoint *apa = *(NMAccessPoint **)a;
 	NMAccessPoint *apb = *(NMAccessPoint **)b;
-	int cmp;
 
-	cmp = nm_access_point_get_strength (apb) - nm_access_point_get_strength (apa);
-	if (cmp != 0)
-		return cmp;
+	NM_CMP_DIRECT (nm_access_point_get_strength (apb), nm_access_point_get_strength (apa));
+	NM_CMP_DIRECT (nm_access_point_get_frequency (apa), nm_access_point_get_frequency (apb));
+	NM_CMP_DIRECT (nm_access_point_get_max_bitrate (apb), nm_access_point_get_max_bitrate (apa));
 
-	cmp = nm_access_point_get_frequency (apa) - nm_access_point_get_frequency (apb);
-	if (cmp != 0)
-		return cmp;
+	/* as fallback, just give it some stable order and use the D-Bus path (literally). */
+	NM_CMP_DIRECT_STRCMP0 (nm_object_get_path (NM_OBJECT (apa)),
+	                       nm_object_get_path (NM_OBJECT (apb)));
 
-	return nm_access_point_get_max_bitrate (apb) - nm_access_point_get_max_bitrate (apa);
+	return 0;
 }
 
 static GPtrArray *
 sort_access_points (const GPtrArray *aps)
 {
 	GPtrArray *sorted;
-	int i;
+	guint i;
 
 	g_return_val_if_fail (aps, NULL);
 
 	sorted = g_ptr_array_sized_new (aps->len);
+	g_ptr_array_set_free_func (sorted, nm_g_object_unref);
 	for (i = 0; i < aps->len; i++)
-		g_ptr_array_add (sorted, aps->pdata[i]);
+		g_ptr_array_add (sorted, g_object_ref (aps->pdata[i]));
 	g_ptr_array_sort_with_data (sorted, compare_aps, NULL);
 	return sorted;
 }
@@ -1186,8 +1192,8 @@ fill_output_access_point (gpointer data, gpointer user_data)
 	freq_str = g_strdup_printf (_("%u MHz"), freq);
 	bitrate_str = g_strdup_printf (_("%u Mbit/s"), bitrate/1000);
 	strength_str = g_strdup_printf ("%u", strength);
-	wpa_flags_str = ap_wpa_rsn_flags_to_string (wpa_flags);
-	rsn_flags_str = ap_wpa_rsn_flags_to_string (rsn_flags);
+	wpa_flags_str = ap_wpa_rsn_flags_to_string (wpa_flags, NM_META_ACCESSOR_GET_TYPE_PRETTY);
+	rsn_flags_str = ap_wpa_rsn_flags_to_string (rsn_flags, NM_META_ACCESSOR_GET_TYPE_PRETTY);
 	sig_bars = nmc_wifi_strength_bars (strength);
 
 	security_str = g_string_new (NULL);
@@ -1207,6 +1213,9 @@ fill_output_access_point (gpointer data, gpointer user_data)
 	if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE) {
 		g_string_append (security_str, "WPA3 ");
 	}
+	if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_OWE) {
+		g_string_append (security_str, "OWE ");
+	}
 	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 ");
@@ -1331,7 +1340,7 @@ print_bond_bridge_info (NMDevice *device,
 	g_ptr_array_add (out.output_data, arr);
 
 	print_data_prepare_width (out.output_data);
-	print_data (&nmc->nmc_config, out_indices, NULL, 0, &out);
+	print_data (&nmc->nmc_config, &nmc->pager_data, out_indices, NULL, 0, &out);
 
 	g_string_free (slaves_str, FALSE);
 
@@ -1401,7 +1410,7 @@ print_team_info (NMDevice *device,
 	g_ptr_array_add (out.output_data, arr);
 
 	print_data_prepare_width (out.output_data);
-	print_data (&nmc->nmc_config, out_indices, NULL, 0, &out);
+	print_data (&nmc->nmc_config, &nmc->pager_data, out_indices, NULL, 0, &out);
 
 	g_string_free (slaves_str, FALSE);
 
@@ -1424,9 +1433,9 @@ show_device_info (NMDevice *device, NmCli *nmc)
 	const char *base_hdr = _("Device details");
 	GPtrArray *fields_in_section = NULL;
 
-	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
+	if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0)
 		fields_str = NMC_FIELDS_DEV_SHOW_SECTIONS_COMMON;
-	else if (strcasecmp (nmc->required_fields, "all") == 0) {
+	else if (g_ascii_strcasecmp (nmc->required_fields, "all") == 0) {
 	} else
 		fields_str = nmc->required_fields;
 
@@ -1456,9 +1465,13 @@ show_device_info (NMDevice *device, NmCli *nmc)
 		for (i = 0; i < G_N_ELEMENTS (metagen_device_detail_general); i++)
 			row[i].info = (const NMMetaAbstractInfo *) &metagen_device_detail_general[i];
 
-		print_required_fields (&nmc->nmc_config, NMC_OF_FLAG_MAIN_HEADER_ONLY,
-		                       out_indices, header_name,
-		                       0, row);
+		print_required_fields (&nmc->nmc_config,
+		                       &nmc->pager_data,
+		                       NMC_OF_FLAG_MAIN_HEADER_ONLY,
+		                       out_indices,
+		                       header_name,
+		                       0,
+		                       row);
 	}
 
 	/* Loop through the required sections and print them. */
@@ -1537,10 +1550,9 @@ show_device_info (NMDevice *device, NmCli *nmc)
 		if ((NM_IS_DEVICE_WIFI (device))) {
 			NMAccessPoint *active_ap = NULL;
 			const char *active_bssid = NULL;
-			GPtrArray *aps;
 
 			/* section AP */
-			if (!strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[4]->name)) {
+			if (!g_ascii_strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[4]->name)) {
 				NMC_OUTPUT_DATA_DEFINE_SCOPED (out);
 
 				if (state == NM_DEVICE_STATE_ACTIVATED) {
@@ -1555,6 +1567,7 @@ show_device_info (NMDevice *device, NmCli *nmc)
 				g_ptr_array_add (out.output_data, arr);
 
 				{
+					gs_unref_ptrarray GPtrArray *aps = NULL;
 					APInfo info = {
 						.nmc = nmc,
 						.index = 1,
@@ -1566,11 +1579,10 @@ show_device_info (NMDevice *device, NmCli *nmc)
 
 					aps = sort_access_points (nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)));
 					g_ptr_array_foreach (aps, fill_output_access_point, &info);
-					g_ptr_array_free (aps, FALSE);
 				}
 
 				print_data_prepare_width (out.output_data);
-				print_data (&nmc->nmc_config, out_indices, NULL, 0, &out);
+				print_data (&nmc->nmc_config, &nmc->pager_data, out_indices, NULL, 0, &out);
 				was_output = TRUE;
 			}
 		}
@@ -1598,42 +1610,42 @@ show_device_info (NMDevice *device, NmCli *nmc)
 		dhcp6 = nm_device_get_dhcp6_config (device);
 
 		/* IP4 */
-		if (cfg4 && !strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[8]->name))
+		if (cfg4 && !g_ascii_strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[8]->name))
 			was_output = print_ip_config (cfg4, AF_INET, &nmc->nmc_config, section_fld);
 
 		/* DHCP4 */
-		if (dhcp4 && !strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[9]->name))
+		if (dhcp4 && !g_ascii_strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[9]->name))
 			was_output = print_dhcp_config (dhcp4, AF_INET, &nmc->nmc_config, section_fld);
 
 		/* IP6 */
-		if (cfg6 && !strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[10]->name))
+		if (cfg6 && !g_ascii_strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[10]->name))
 			was_output = print_ip_config (cfg6, AF_INET6, &nmc->nmc_config, section_fld);
 
 		/* DHCP6 */
-		if (dhcp6 && !strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[11]->name))
+		if (dhcp6 && !g_ascii_strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[11]->name))
 			was_output = print_dhcp_config (dhcp6, AF_INET6, &nmc->nmc_config, section_fld);
 
 		/* Bond specific information */
 		if (NM_IS_DEVICE_BOND (device)) {
-			if (!strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[12]->name))
+			if (!g_ascii_strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[12]->name))
 				was_output = print_bond_bridge_info (device, nmc, nmc_fields_dev_show_sections[12]->name, section_fld);
 		}
 
 		/* Team specific information */
 		if (NM_IS_DEVICE_TEAM (device)) {
-			if (!strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[13]->name))
+			if (!g_ascii_strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[13]->name))
 				was_output = print_team_info (device, nmc, nmc_fields_dev_show_sections[13]->name, section_fld);
 		}
 
 		/* Bridge specific information */
 		if (NM_IS_DEVICE_BRIDGE (device)) {
-			if (!strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[14]->name))
+			if (!g_ascii_strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[14]->name))
 				was_output = print_bond_bridge_info (device, nmc, nmc_fields_dev_show_sections[14]->name, section_fld);
 		}
 
 		/* VLAN-specific information */
 		if ((NM_IS_DEVICE_VLAN (device))) {
-			if (!strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[15]->name)) {
+			if (!g_ascii_strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[15]->name)) {
 				char * vlan_id_str = g_strdup_printf ("%u", nm_device_vlan_get_vlan_id (NM_DEVICE_VLAN (device)));
 				NMDevice *parent = nm_device_vlan_get_parent (NM_DEVICE_VLAN (device));
 				NMC_OUTPUT_DATA_DEFINE_SCOPED (out);
@@ -1651,14 +1663,14 @@ show_device_info (NMDevice *device, NmCli *nmc)
 				g_ptr_array_add (out.output_data, arr);
 
 				print_data_prepare_width (out.output_data);
-				print_data (&nmc->nmc_config, out_indices, NULL, 0, &out);
+				print_data (&nmc->nmc_config, &nmc->pager_data, out_indices, NULL, 0, &out);
 
 				was_output = TRUE;
 			}
 		}
 
 		if (NM_IS_DEVICE_BT (device)) {
-			if (!strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[16]->name)) {
+			if (!g_ascii_strcasecmp (nmc_fields_dev_show_sections[section_idx]->name, nmc_fields_dev_show_sections[16]->name)) {
 				NMC_OUTPUT_DATA_DEFINE_SCOPED (out);
 
 				tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_dev_show_bluetooth;
@@ -1673,7 +1685,7 @@ show_device_info (NMDevice *device, NmCli *nmc)
 				g_ptr_array_add (out.output_data, arr);
 
 				print_data_prepare_width (out.output_data);
-				print_data (&nmc->nmc_config, out_indices, NULL, 0, &out);
+				print_data (&nmc->nmc_config, &nmc->pager_data, out_indices, NULL, 0, &out);
 				was_output = TRUE;
 			}
 		}
@@ -1716,8 +1728,8 @@ nmc_device_state_to_color (NMDeviceState state)
 	return NM_META_COLOR_DEVICE_UNKNOWN;
 }
 
-static NMCResultCode
-do_devices_status (NmCli *nmc, int argc, char **argv)
+static void
+do_devices_status (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	GError *error = NULL;
 	gs_free NMDevice **devices = NULL;
@@ -1726,16 +1738,17 @@ do_devices_status (NmCli *nmc, int argc, char **argv)
 	next_arg (nmc, &argc, &argv, NULL);
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	if (argc) {
 		g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv);
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
-	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
+	if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0)
 		fields_str = "DEVICE,TYPE,STATE,CONNECTION";
-	else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) {
+	else if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "all") == 0) {
 	} else
 		fields_str = nmc->required_fields;
 
@@ -1750,14 +1763,13 @@ do_devices_status (NmCli *nmc, int argc, char **argv)
 	                &error)) {
 		g_string_printf (nmc->return_text, _("Error: 'device status': %s"), error->message);
 		g_error_free (error);
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
-
-	return NMC_RESULT_SUCCESS;
 }
 
-static NMCResultCode
-do_device_show (NmCli *nmc, int argc, char **argv)
+static void
+do_device_show (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	gs_free_error GError *error = NULL;
 
@@ -1771,16 +1783,18 @@ do_device_show (NmCli *nmc, int argc, char **argv)
 		device = get_device (nmc, &argc, &argv, &error);
 		if (!device) {
 			g_string_printf (nmc->return_text, _("Error: %s."), error->message);
-			return error->code;
+			nmc->return_value = error->code;
+			return;
 		}
 
 		if (argc) {
 			g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv);
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 
 		if (nmc->complete)
-			return nmc->return_value;
+			return;
 
 		show_device_info (device, nmc);
 	} else {
@@ -1800,8 +1814,6 @@ do_device_show (NmCli *nmc, int argc, char **argv)
 
 		g_free (devices);
 	}
-
-	return nmc->return_value;
 }
 
 static gboolean
@@ -2024,8 +2036,8 @@ connect_device_cb (GObject *client, GAsyncResult *result, gpointer user_data)
 	g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc);
 }
 
-static NMCResultCode
-do_device_connect (NmCli *nmc, int argc, char **argv)
+static void
+do_device_connect (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMDevice *device = NULL;
 	AddAndActivateInfo *info;
@@ -2039,16 +2051,18 @@ do_device_connect (NmCli *nmc, int argc, char **argv)
 	device = get_device (nmc, &argc, &argv, &error);
 	if (!device) {
 		g_string_printf (nmc->return_text, _("Error: %s."), error->message);
-		return error->code;
+		nmc->return_value = error->code;
+		return;
 	}
 
 	if (*argv) {
 		g_string_printf (nmc->return_text, _("Error: extra argument not allowed: '%s'."), *argv);
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	/*
 	 * Use nowait_flag instead of should_wait, because exiting has to be postponed
@@ -2079,8 +2093,6 @@ do_device_connect (NmCli *nmc, int argc, char **argv)
 	/* Start progress indication */
 	if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
 		progress_id = g_timeout_add (120, progress_cb, device);
-
-	return nmc->return_value;
 }
 
 typedef struct {
@@ -2194,8 +2206,8 @@ reapply_device_cb (GObject *object, GAsyncResult *result, gpointer user_data)
 	}
 }
 
-static NMCResultCode
-do_device_reapply (NmCli *nmc, int argc, char **argv)
+static void
+do_device_reapply (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMDevice *device;
 	DeviceCbInfo *info = NULL;
@@ -2209,16 +2221,18 @@ do_device_reapply (NmCli *nmc, int argc, char **argv)
 	device = get_device (nmc, &argc, &argv, &error);
 	if (!device) {
 		g_string_printf (nmc->return_text, _("Error: %s."), error->message);
-		return error->code;
+		nmc->return_value = error->code;
+		return;
 	}
 
 	if (argc) {
 		g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv);
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	nmc->nowait_flag = (nmc->timeout == 0);
 	nmc->should_wait++;
@@ -2229,8 +2243,6 @@ do_device_reapply (NmCli *nmc, int argc, char **argv)
 
 	/* Now reapply the connection to the device */
 	nm_device_reapply_async (device, NULL, 0, 0, NULL, reapply_device_cb, info);
-
-	return nmc->return_value;
 }
 
 typedef struct {
@@ -2276,6 +2288,8 @@ modify_get_applied_cb (GObject *object,
 	gs_free_error GError *error = NULL;
 	NMConnection *connection;
 	guint64 version_id;
+	int argc;
+	const char *const*argv;
 
 	connection = nm_device_get_applied_connection_finish (device,
 	                                                      result,
@@ -2292,7 +2306,10 @@ modify_get_applied_cb (GObject *object,
 		return;
 	}
 
-	if (!nmc_read_connection_properties (info->nmc, connection, &info->argc, &info->argv, &error)) {
+	argc = info->argc;
+	argv = (const char *const*) info->argv;
+
+	if (!nmc_process_connection_properties (info->nmc, connection, &argc, &argv, TRUE, &error)) {
 		g_string_assign (nmc->return_text, error->message);
 		nmc->return_value = error->code;
 		g_slice_free (ModifyInfo, info);
@@ -2306,8 +2323,8 @@ modify_get_applied_cb (GObject *object,
 		nm_device_reapply_async (device, connection, version_id, 0, NULL, modify_reapply_cb, info);
 }
 
-static NMCResultCode
-do_device_modify (NmCli *nmc, int argc, char **argv)
+static void
+do_device_modify (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMDevice *device = NULL;
 	ModifyInfo *info = NULL;
@@ -2317,7 +2334,8 @@ do_device_modify (NmCli *nmc, int argc, char **argv)
 	device = get_device (nmc, &argc, &argv, &error);
 	if (!device) {
 		g_string_printf (nmc->return_text, _("Error: %s."), error->message);
-		return error->code;
+		nmc->return_value = error->code;
+		return;
 	}
 
 	if (nmc->timeout == -1)
@@ -2329,11 +2347,9 @@ do_device_modify (NmCli *nmc, int argc, char **argv)
 	info = g_slice_new0 (ModifyInfo);
 	info->nmc = nmc;
 	info->argc = argc;
-	info->argv = argv;
+	info->argv = nm_utils_strv_dup ((char **) argv, argc, TRUE);
 
 	nm_device_get_applied_connection_async (device, 0, NULL, modify_get_applied_cb, info);
-
-	return nmc->return_value;
 }
 
 static void
@@ -2373,8 +2389,8 @@ disconnect_device_cb (GObject *object, GAsyncResult *result, gpointer user_data)
 	}
 }
 
-static NMCResultCode
-do_devices_disconnect (NmCli *nmc, int argc, char **argv)
+static void
+do_devices_disconnect (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMDevice *device;
 	DeviceCbInfo *info = NULL;
@@ -2387,7 +2403,7 @@ do_devices_disconnect (NmCli *nmc, int argc, char **argv)
 	next_arg (nmc, &argc, &argv, NULL);
 	queue = get_device_list (nmc, argc, argv);
 	if (!queue)
-		return nmc->return_value;
+		return;
 	if (nmc->complete)
 		goto out;
 	queue = g_slist_reverse (queue);
@@ -2418,7 +2434,6 @@ do_devices_disconnect (NmCli *nmc, int argc, char **argv)
 
 out:
 	g_slist_free (queue);
-	return nmc->return_value;
 }
 
 static void
@@ -2445,8 +2460,8 @@ delete_device_cb (GObject *object, GAsyncResult *result, gpointer user_data)
 	}
 }
 
-static NMCResultCode
-do_devices_delete (NmCli *nmc, int argc, char **argv)
+static void
+do_devices_delete (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMDevice *device;
 	DeviceCbInfo *info = NULL;
@@ -2459,7 +2474,7 @@ do_devices_delete (NmCli *nmc, int argc, char **argv)
 	next_arg (nmc, &argc, &argv, NULL);
 	queue = get_device_list (nmc, argc, argv);
 	if (!queue)
-		return nmc->return_value;
+		return;
 	if (nmc->complete)
 		goto out;
 	queue = g_slist_reverse (queue);
@@ -2483,11 +2498,10 @@ do_devices_delete (NmCli *nmc, int argc, char **argv)
 
 out:
 	g_slist_free (queue);
-	return nmc->return_value;
 }
 
-static NMCResultCode
-do_device_set (NmCli *nmc, int argc, char **argv)
+static void
+do_device_set (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 #define DEV_SET_AUTOCONNECT 0
 #define DEV_SET_MANAGED     1
@@ -2509,12 +2523,14 @@ do_device_set (NmCli *nmc, int argc, char **argv)
 	device = get_device (nmc, &argc, &argv, &error);
 	if (!device) {
 		g_string_printf (nmc->return_text, _("Error: %s."), error->message);
-		return error->code;
+		nmc->return_value = error->code;
+		return;
 	}
 
 	if (!argc) {
 		g_string_printf (nmc->return_text, _("Error: No property specified."));
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
 	i = 0;
@@ -2529,14 +2545,16 @@ do_device_set (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: '%s' argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			if (argc == 1 && nmc->complete)
 				nmc_complete_bool (*argv);
 			if (!nmc_string_to_bool (*argv, &flag, &error)) {
 				g_string_printf (nmc->return_text, _("Error: 'managed': %s."),
 				                 error->message);
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			values[DEV_SET_MANAGED].idx = ++i;
 			values[DEV_SET_MANAGED].value = flag;
@@ -2546,26 +2564,29 @@ do_device_set (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: '%s' argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			if (argc == 1 && nmc->complete)
 				nmc_complete_bool (*argv);
 			if (!nmc_string_to_bool (*argv, &flag, &error)) {
 				g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."),
 				                 error->message);
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			values[DEV_SET_AUTOCONNECT].idx = ++i;
 			values[DEV_SET_AUTOCONNECT].value = flag;
 		}
 		else {
 			g_string_printf (nmc->return_text, _("Error: property '%s' is not known."), *argv);
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 	} while (next_arg (nmc, &argc, &argv, NULL) == 0);
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	/* when multiple properties are specified, set them in the order as they
 	 * are specified on the command line. */
@@ -2579,8 +2600,6 @@ do_device_set (NmCli *nmc, int argc, char **argv)
 		nm_device_set_autoconnect (device, values[DEV_SET_AUTOCONNECT].value);
 	if (values[DEV_SET_MANAGED].idx >= 0)
 		nm_device_set_managed (device, values[DEV_SET_MANAGED].value);
-
-	return nmc->return_value;
 }
 
 static void
@@ -2645,11 +2664,11 @@ device_removed (NMClient *client, NMDevice *device, NmCli *nmc)
 	device_unwatch (nmc, device);
 }
 
-static NMCResultCode
-do_devices_monitor (NmCli *nmc, int argc, char **argv)
+static void
+do_devices_monitor (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	next_arg (nmc, &argc, &argv, NULL);
 	if (argc == 0) {
@@ -2674,7 +2693,6 @@ do_devices_monitor (NmCli *nmc, int argc, char **argv)
 	}
 
 	g_signal_connect (nmc->client, NM_CLIENT_DEVICE_REMOVED, G_CALLBACK (device_removed), nmc);
-	return nmc->return_value;
 }
 
 /*
@@ -2779,7 +2797,6 @@ show_access_point_info (NMDeviceWifi *wifi, NmCli *nmc, NmcOutputData *out)
 {
 	NMAccessPoint *active_ap = NULL;
 	const char *active_bssid = NULL;
-	GPtrArray *aps;
 	NmcOutputField *arr;
 
 	if (nm_device_get_state (NM_DEVICE (wifi)) == NM_DEVICE_STATE_ACTIVATED) {
@@ -2792,6 +2809,7 @@ show_access_point_info (NMDeviceWifi *wifi, NmCli *nmc, NmcOutputData *out)
 	g_ptr_array_add (out->output_data, arr);
 
 	{
+		gs_unref_ptrarray GPtrArray *aps = NULL;
 		APInfo info = {
 			.nmc = nmc,
 			.index = 1,
@@ -2803,7 +2821,6 @@ show_access_point_info (NMDeviceWifi *wifi, NmCli *nmc, NmcOutputData *out)
 
 		aps = sort_access_points (nm_device_wifi_get_access_points (wifi));
 		g_ptr_array_foreach (aps, fill_output_access_point, &info);
-		g_ptr_array_free (aps, TRUE);
 	}
 
 	print_data_prepare_width (out->output_data);
@@ -2814,7 +2831,8 @@ wifi_print_aps (NMDeviceWifi *wifi,
                 NmCli *nmc,
                 GArray *_out_indices,
                 const NMMetaAbstractInfo *const*tmpl,
-                const char *bssid_user)
+                const char *bssid_user,
+                gboolean *bssid_found)
 {
 	NMAccessPoint *ap = NULL;
 	const GPtrArray *aps;
@@ -2838,14 +2856,13 @@ wifi_print_aps (NMDeviceWifi *wifi,
 		/* Specific AP requested - list only that */
 		aps = nm_device_wifi_get_access_points (wifi);
 		for (i = 0; i < aps->len; i++) {
-			char *bssid_up;
 			NMAccessPoint *candidate_ap = g_ptr_array_index (aps, i);
-			const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap);
 
-			bssid_up = g_ascii_strup (bssid_user, -1);
-			if (!strcmp (bssid_up, candidate_bssid))
+			if (nm_utils_hwaddr_matches (bssid_user,
+			                             -1,
+			                             nm_access_point_get_bssid (candidate_ap),
+			                             -1))
 				ap = candidate_ap;
-			g_free (bssid_up);
 		}
 		if (ap) {
 			/* Add headers (field names) */
@@ -2863,25 +2880,38 @@ wifi_print_aps (NMDeviceWifi *wifi,
 			fill_output_access_point (ap, info);
 
 			print_data_prepare_width (out.output_data);
-			print_data (&nmc->nmc_config, out_indices, header_name, 0, &out);
+			print_data (&nmc->nmc_config, &nmc->pager_data, out_indices, header_name, 0, &out);
 			g_free (info);
 
-			nmc->return_value = NMC_RESULT_SUCCESS;
+			*bssid_found = TRUE;
 			empty_line = TRUE;
 		}
 	} else {
 		show_access_point_info (wifi, nmc, &out);
-		print_data (&nmc->nmc_config, out_indices, header_name, 0, &out);
+		print_data (&nmc->nmc_config, &nmc->pager_data, out_indices, header_name, 0, &out);
 		empty_line = TRUE;
 	}
 }
 
+static gint64
+_device_wifi_get_last_scan (NMDeviceWifi *wifi)
+{
+	gint64 timestamp;
+
+	timestamp = nm_device_wifi_get_last_scan (wifi);
+	if (timestamp == -1)
+		return G_MININT64;
+	return timestamp;
+}
+
 typedef struct {
 	NmCli *nmc;
 	NMDevice **devices;
 	const NMMetaAbstractInfo *const *tmpl;
-	const char *bssid_user;
+	char *bssid_user;
 	GArray *out_indices;
+	gint64 rescan_cutoff_msec;
+	guint pending;
 } ScanInfo;
 
 typedef struct {
@@ -2893,79 +2923,94 @@ typedef struct {
 } WifiListData;
 
 static void
-wifi_list_finish (WifiListData *data)
+wifi_list_finish (WifiListData *wifi_list_data,
+                  gboolean force_finished)
 {
-	ScanInfo *info = data->scan_info;
-	NmCli *nmc = info->nmc;
+	ScanInfo *scan_info = wifi_list_data->scan_info;
+	NmCli *nmc = scan_info->nmc;
+	gboolean bssid_found = FALSE;
 	guint i;
 
-	if (--info->nmc->should_wait == 0) {
-		for (i = 0; info->devices[i]; i++) {
-			wifi_print_aps (NM_DEVICE_WIFI (info->devices[i]),
-			                info->nmc,
-			                info->out_indices,
-			                info->tmpl,
-			                info->bssid_user);
-		}
-		if (nmc->return_value == NMC_RESULT_ERROR_NOT_FOUND) {
-			g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."),
-			                 data->scan_info->bssid_user);
-		}
-		g_main_loop_quit (loop);
+	if (   !force_finished
+	    && scan_info->rescan_cutoff_msec > _device_wifi_get_last_scan (wifi_list_data->wifi)) {
+		/* wait longer... */
+		return;
+	}
+
+	nm_clear_g_signal_handler (wifi_list_data->wifi, &wifi_list_data->last_scan_id);
+	nm_clear_g_source (&wifi_list_data->timeout_id);
+	nm_clear_g_cancellable (&wifi_list_data->scan_cancellable);
+	nm_g_slice_free (wifi_list_data);
+
+	if (--scan_info->pending > 0)
+		return;
+
+	for (i = 0; scan_info->devices[i]; i++) {
+		wifi_print_aps (NM_DEVICE_WIFI (scan_info->devices[i]),
+		                nmc,
+		                scan_info->out_indices,
+		                scan_info->tmpl,
+		                scan_info->bssid_user,
+		                &bssid_found);
 	}
 
-	g_signal_handler_disconnect (data->wifi, data->last_scan_id);
-	nm_clear_g_source (&data->timeout_id);
-	nm_clear_g_cancellable (&data->scan_cancellable);
-	g_slice_free (WifiListData, data);
-
-	if (info->nmc->should_wait == 0) {
-		for (i = 0; info->devices[i]; i++)
-			g_object_unref (info->devices[i]);
-		g_free (info->devices);
-		g_array_unref (info->out_indices);
-		g_free (info);
+	if (   scan_info->bssid_user
+	    && !bssid_found) {
+		nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
+		g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."),
+		                 scan_info->bssid_user);
 	}
+
+	for (i = 0; scan_info->devices[i]; i++)
+		g_object_unref (scan_info->devices[i]);
+	g_free (scan_info->devices);
+	g_array_unref (scan_info->out_indices);
+	g_free (scan_info->bssid_user);
+	nm_g_slice_free (scan_info);
+
+	nmc->should_wait--;
+	g_main_loop_quit (loop);
 }
 
 static void
 wifi_last_scan_updated (GObject *gobject, GParamSpec *pspec, gpointer user_data)
 {
-	WifiListData *data = user_data;
-
-	wifi_list_finish (data);
+	wifi_list_finish (user_data, FALSE);
 }
 
 static void
 wifi_list_rescan_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
 	NMDeviceWifi *wifi = NM_DEVICE_WIFI (source_object);
-	WifiListData *data = user_data;
 	gs_free_error GError *error = NULL;
+	WifiListData *wifi_list_data;
+	gboolean force_finished;
 
-	if (!nm_device_wifi_request_scan_finish (wifi, res, &error)) {
-		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-			return;
+	nm_device_wifi_request_scan_finish (wifi, res, &error);
+	if (nm_utils_error_is_cancelled (error))
+		return;
 
-		if (g_error_matches (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED)) {
-			/* This likely means that scanning is already in progress. There's
-			 * a good chance we'll get updated results soon; wait for them. */
-			return;
-		}
+	if (g_error_matches (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED)) {
+		/* This likely means that scanning is already in progress. There's
+		 * a good chance we'll get updated results soon; wait for them. */
+		force_finished = FALSE;
+	} else if (error)
+		force_finished = TRUE;
+	else
+		force_finished = FALSE;
 
-		/* Scanning could not be initiated for unknown reason,
-		 * no point in waiting for results. */
-		wifi_list_finish (data);
-	}
+	wifi_list_data = user_data;
+	g_clear_object (&wifi_list_data->scan_cancellable);
+	wifi_list_finish (wifi_list_data, force_finished);
 }
 
 static gboolean
 wifi_list_scan_timeout (gpointer user_data)
 {
-	WifiListData *data = user_data;
-
-	wifi_list_finish (data);
+	WifiListData *wifi_list_data = user_data;
 
+	wifi_list_data->timeout_id = 0;
+	wifi_list_finish (user_data, TRUE);
 	return G_SOURCE_REMOVE;
 }
 
@@ -2989,8 +3034,8 @@ nmc_complete_bssid (NMClient *client, const char *ifname, const char *bssid_pref
 	complete_aps (devices, ifname, bssid_prefix, NULL);
 }
 
-static NMCResultCode
-do_device_wifi_list (NmCli *nmc, int argc, char **argv)
+static void
+do_device_wifi_list (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	GError *error = NULL;
 	NMDevice *device = NULL;
@@ -3002,10 +3047,10 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 	const NMMetaAbstractInfo *const*tmpl;
 	gs_unref_array GArray *out_indices = NULL;
 	int option;
-	guint64 rescan_cutoff;
-	NMDeviceWifi *wifi;
+	gint64 rescan_cutoff_msec;
 	ScanInfo *scan_info = NULL;
-	WifiListData *data;
+	gboolean ifname_handled;
+	NMDevice *ifname_handled_candidate;
 	guint i, j;
 
 	devices = nmc_get_devices_sorted (nmc->client);
@@ -3017,7 +3062,8 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			ifname = *argv;
 			if (argc == 1 && nmc->complete)
@@ -3029,20 +3075,20 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			bssid_user = *argv;
 			if (argc == 1 && nmc->complete)
 				complete_aps (devices, NULL, bssid_user, NULL);
-			/* We'll switch this to NMC_RESULT_SUCCESS if we find an access point. */
-			nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
 			break;
 		case 4: /* --rescan */
 			argc--;
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			rescan = *argv;
 			if (argc == 1 && nmc->complete)
@@ -3054,9 +3100,12 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 		}
 	}
 
-	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
+	if (nmc->complete)
+		return;
+
+	if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0)
 		fields_str = NMC_FIELDS_DEV_WIFI_LIST_COMMON;
-	else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) {
+	else if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "all") == 0) {
 	} else
 		fields_str = nmc->required_fields;
 
@@ -3066,101 +3115,134 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv)
 	if (error) {
 		g_string_printf (nmc->return_text, _("Error: 'device wifi': %s"), error->message);
 		g_error_free (error);
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
-	if (nmc->complete)
-		return nmc->return_value;
-
 	if (argc) {
 		g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv);
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
-	if (rescan == NULL || strcmp (rescan, "auto") == 0) {
-		rescan_cutoff = NM_MAX (nm_utils_get_timestamp_msec () - 30 * NM_UTILS_MSEC_PER_SECOND, 0);
-	} else if (strcmp (rescan, "no") == 0) {
-		rescan_cutoff = 0;
-	} else if (strcmp (rescan, "yes") == 0) {
-		rescan_cutoff = -1;
-	} else {
+	if (NM_IN_STRSET (rescan, NULL, "auto"))
+		rescan_cutoff_msec = nm_utils_get_timestamp_msec () - (30 * NM_UTILS_MSEC_PER_SEC);
+	else if (nm_streq (rescan, "no"))
+		rescan_cutoff_msec = G_MININT64;
+	else if (nm_streq (rescan, "yes"))
+		rescan_cutoff_msec = nm_utils_get_timestamp_msec ();
+	else {
 		g_string_printf (nmc->return_text, _("Error: invalid rescan argument: '%s' not among [auto, no, yes]"), rescan);
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
-	if (ifname) {
-		device = find_wifi_device_by_iface (devices, ifname, NULL);
-		if (!device) {
-			g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname);
-			return NMC_RESULT_ERROR_NOT_FOUND;
+	ifname_handled = (ifname == NULL);
+	ifname_handled_candidate = NULL;
+
+	j = 0;
+	for (i = 0; devices[i]; i++) {
+		const char *dev_iface;
+
+		device = devices[i];
+		dev_iface = nm_device_get_iface (device);
+
+		if (ifname) {
+			if (!nm_streq0 (ifname, dev_iface))
+				continue;
+			if (!NM_IS_DEVICE_WIFI (device)) {
+				if (   nm_device_get_device_type (device) == NM_DEVICE_TYPE_GENERIC
+				    && nm_streq0 (nm_device_get_type_description (device), "wifi"))
+					ifname_handled_candidate = device;
+				else if (!ifname_handled_candidate)
+					ifname_handled_candidate = device;
+				continue;
+			}
+			ifname_handled = TRUE;
+		} else {
+			if (!NM_IS_DEVICE_WIFI (device))
+				continue;
 		}
 
-		if (NM_IS_DEVICE_WIFI (device)) {
-			devices[0] = device;
-			devices[1] = NULL;
+		devices[j++] = device;
+	}
+	devices[j] = NULL;
+
+	if (!ifname_handled) {
+		if (!ifname_handled_candidate) {
+			g_string_printf (nmc->return_text,
+			                 _("Error: Device '%s' not found."),
+			                 ifname);
+		} else if (   nm_device_get_device_type (ifname_handled_candidate) == NM_DEVICE_TYPE_GENERIC
+		           && nm_streq0 (nm_device_get_type_description (ifname_handled_candidate), "wifi")) {
+			g_string_printf (nmc->return_text,
+			                 _("Error: Device '%s' was not recognized as a Wi-Fi device, check NetworkManager Wi-Fi plugin."),
+			                 ifname);
 		} else {
-			if (   nm_device_get_device_type (device) == NM_DEVICE_TYPE_GENERIC
-			    && g_strcmp0 (nm_device_get_type_description (device), "wifi") == 0) {
-				g_string_printf (nmc->return_text,
-				                 _("Error: Device '%s' was not recognized as a Wi-Fi device, check NetworkManager Wi-Fi plugin."),
-				                 ifname);
-			} else {
-				g_string_printf (nmc->return_text,
-				                 _("Error: Device '%s' is not a Wi-Fi device."),
-				                 ifname);
-			}
-			return NMC_RESULT_ERROR_UNKNOWN;
+			g_string_printf (nmc->return_text,
+			                 _("Error: Device '%s' is not a Wi-Fi device."),
+			                 ifname);
 		}
+		nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
+		return;
 	}
 
-	/* Filter out non-wifi devices */
-	for (i = 0, j = 0; devices[i]; i++) {
-		if (NM_IS_DEVICE_WIFI (devices[i]))
-			devices[j++] = devices[i];
+	if (!devices[0]) {
+		if (bssid_user) {
+			nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
+			g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."),
+			                 bssid_user);
+			nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
+		}
+		return;
 	}
-	devices[j] = NULL;
 
-	/* Start a new scan for devices that need it */
-	for (i = 0; devices[i]; i++) {
-		wifi = (NMDeviceWifi *) devices[i];
-		g_object_ref (wifi);
+	scan_info = g_slice_new (ScanInfo);
+	*scan_info = (ScanInfo) {
+		.out_indices        = g_array_ref (out_indices),
+		.tmpl               = tmpl,
+		.bssid_user         = g_strdup (bssid_user),
+		.nmc                = nmc,
+		.rescan_cutoff_msec = rescan_cutoff_msec,
+	};
 
-		if (   rescan_cutoff == 0
-		    || (rescan_cutoff > 0 && nm_device_wifi_get_last_scan (wifi) >= rescan_cutoff))
-			continue;
+	for (i = 0; devices[i]; i++)
+		g_object_ref (devices[i]);
 
-		if (!scan_info) {
-			scan_info = g_new0 (ScanInfo, 1);
-			scan_info->out_indices = g_array_ref (out_indices);
-			scan_info->tmpl = tmpl;
-			scan_info->bssid_user = bssid_user;
-			scan_info->nmc = nmc;
-		}
+	for (i = 0; devices[i]; i++) {
+		NMDeviceWifi *wifi = NM_DEVICE_WIFI (devices[i]);
+		WifiListData *wifi_list_data;
+		int timeout_msec;
 
-		nmc->should_wait++;
-		data = g_slice_new0 (WifiListData);
-		data->wifi = wifi;
-		data->scan_info = scan_info;
-		data->last_scan_id = g_signal_connect (wifi, "notify::" NM_DEVICE_WIFI_LAST_SCAN,
-		                                       G_CALLBACK (wifi_last_scan_updated), data);
-		data->scan_cancellable = g_cancellable_new ();
-		data->timeout_id = g_timeout_add_seconds (15, wifi_list_scan_timeout, data);
-		nm_device_wifi_request_scan_async (wifi, data->scan_cancellable, wifi_list_rescan_cb, data);
-	}
+		if (rescan_cutoff_msec <= _device_wifi_get_last_scan (wifi))
+			timeout_msec = 0;
+		else
+			timeout_msec = 15000;
+
+		wifi_list_data = g_slice_new (WifiListData);
+		*wifi_list_data = (WifiListData) {
+			.wifi             = wifi,
+			.scan_info        = scan_info,
+			.timeout_id       = g_timeout_add (timeout_msec,
+			                                   wifi_list_scan_timeout,
+			                                   wifi_list_data),
+		};
 
-	if (scan_info) {
-		scan_info->devices = g_steal_pointer (&devices);
-	} else {
-		/* Print results right away if no scan is pending */
-		for (i = 0; devices[i]; i++) {
-			wifi_print_aps (NM_DEVICE_WIFI (devices[i]),
-			                nmc, out_indices,
-			                tmpl, bssid_user);
-			g_object_unref (devices[i]);
+		scan_info->pending++;
+
+		if (timeout_msec > 0) {
+			wifi_list_data->last_scan_id = g_signal_connect (wifi,
+			                                                 "notify::" NM_DEVICE_WIFI_LAST_SCAN,
+			                                                 G_CALLBACK (wifi_last_scan_updated),
+			                                                 wifi_list_data),
+			wifi_list_data->scan_cancellable = g_cancellable_new (),
+			nm_device_wifi_request_scan_async (wifi, wifi_list_data->scan_cancellable, wifi_list_rescan_cb, wifi_list_data);
 		}
 	}
 
-	return nmc->return_value;
+	scan_info->devices = g_steal_pointer (&devices);
+
+	nmc->should_wait++;
 }
 
 static void
@@ -3224,8 +3306,8 @@ save_and_activate_connection (NmCli *nmc,
 	}
 }
 
-static NMCResultCode
-do_device_wifi_connect (NmCli *nmc, int argc, char **argv)
+static void
+do_device_wifi_connect (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMDevice *device = NULL;
 	NMAccessPoint *ap = NULL;
@@ -3561,8 +3643,8 @@ do_device_wifi_connect (NmCli *nmc, int argc, char **argv)
 
 	/* Set password for WEP or WPA-PSK. */
 	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) {
+	    || (ap_wpa_flags != NM_802_11_AP_SEC_NONE && !(ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_OWE))
+	    || (ap_rsn_flags != NM_802_11_AP_SEC_NONE && !(ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_OWE))) {
 		const char *con_password = NULL;
 		NMSettingWirelessSecurity *s_wsec = NULL;
 
@@ -3634,9 +3716,7 @@ finish:
 	if (bssid2_arr)
 		g_byte_array_free (bssid2_arr, TRUE);
 	g_free (ssid_ask);
-	g_free (passwd_ask);
-
-	return nmc->return_value;
+	nm_free_secret (passwd_ask);
 }
 
 static GBytes *
@@ -3865,8 +3945,8 @@ create_hotspot_conn (const GPtrArray *connections,
 	return connection;
 }
 
-static NMCResultCode
-do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
+static void
+do_device_wifi_hotspot (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	const char *ifname = NULL;
 	const char *con_name = NULL;
@@ -3903,7 +3983,8 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			ifname = *argv;
 			if (argc == 1 && nmc->complete)
@@ -3913,7 +3994,8 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			con_name = *argv;
 		} else if (strcmp (*argv, "ssid") == 0) {
@@ -3921,11 +4003,13 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			if (strlen (*argv) > 32) {
 				g_string_printf (nmc->return_text, _("Error: ssid is too long."));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			ssid_bytes = g_bytes_new (*argv, strlen (*argv));
 		} else if (strcmp (*argv, "band") == 0) {
@@ -3933,7 +4017,8 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			band = *argv;
 			if (argc == 1 && nmc->complete)
@@ -3941,14 +4026,16 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 			if (strcmp (band, "a") && strcmp (band, "bg")) {
 				g_string_printf (nmc->return_text, _("Error: band argument value '%s' is invalid; use 'a' or 'bg'."),
 				                 band);
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 		} else if (strcmp (*argv, "channel") == 0) {
 			argc--;
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			channel = *argv;
 		} else if (strcmp (*argv, "password") == 0) {
@@ -3956,7 +4043,8 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			password = *argv;
 		/* --show-password is deprecated in favour of global --show-secrets option */
@@ -3965,7 +4053,8 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 			show_password = TRUE;
 		} else {
 			g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv);
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 
 		next_arg (nmc, &argc, &argv, NULL);
@@ -3973,7 +4062,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 	show_password = nmc->nmc_config.show_secrets || show_password;
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	/* Verify band and channel parameters */
 	if (!channel) {
@@ -3987,13 +4076,15 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 
 		if (!band) {
 			g_string_printf (nmc->return_text, _("Error: channel requires band too."));
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 		if (   !nmc_string_to_uint (channel, TRUE, 1, 5825, &value)
 		    || !nm_utils_wifi_is_channel_valid (value, band)) {
 			g_string_printf (nmc->return_text, _("Error: channel '%s' not valid for band '%s'."),
 			                 channel, band);
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 
 		channel_int = value;
@@ -4006,7 +4097,8 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 			g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), ifname);
 		else
 			g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found."));
-		return NMC_RESULT_ERROR_UNKNOWN;
+		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+		return;
 	}
 
 	/* Check device supported mode */
@@ -4018,7 +4110,8 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 	else {
 		g_string_printf (nmc->return_text, _("Error: Device '%s' supports neither AP nor Ad-Hoc mode."),
 		                 nm_device_get_iface (device));
-		return NMC_RESULT_ERROR_UNKNOWN;
+		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+		return;
 	}
 
 	connections = nm_client_get_connections (nmc->client);
@@ -4034,13 +4127,14 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 
 	if (password || !NM_IS_REMOTE_CONNECTION (connection)) {
 		s_wsec = nm_connection_get_setting_wireless_security (connection);
-		g_return_val_if_fail (s_wsec, NMC_RESULT_ERROR_UNKNOWN);
+		g_return_if_fail (s_wsec);
 
 		if (!set_wireless_security_for_hotspot (s_wsec, wifi_mode, caps, password, show_password, &error)) {
 			g_object_unref (connection);
 			g_string_printf (nmc->return_text, _("Error: Invalid 'password': %s."), error->message);
 			g_clear_error (&error);
-			return NMC_RESULT_ERROR_UNKNOWN;
+			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+			return;
 		}
 	}
 
@@ -4049,8 +4143,6 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
 	nmc->should_wait++;
 
 	save_and_activate_connection (nmc, device, connection, TRUE, NULL);
-
-	return nmc->return_value;
 }
 
 static void
@@ -4068,16 +4160,15 @@ request_rescan_cb (GObject *object, GAsyncResult *result, gpointer user_data)
 	quit ();
 }
 
-static NMCResultCode
-do_device_wifi_rescan (NmCli *nmc, int argc, char **argv)
+static void
+do_device_wifi_rescan (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMDevice *device;
 	const char *ifname = NULL;
-	GPtrArray *ssids;
+	gs_unref_ptrarray GPtrArray *ssids = NULL;
 	gs_free NMDevice **devices = NULL;
 	GVariantBuilder builder, array_builder;
 	GVariant *options;
-	const char *ssid;
 	int i;
 
 	ssids = g_ptr_array_new ();
@@ -4093,14 +4184,14 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv)
 			if (ifname) {
 				g_string_printf (nmc->return_text, _("Error: '%s' cannot repeat."), *(argv-1));
 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-				goto finish;
+				return;
 			}
 			argc--;
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-				goto finish;
+				return;
 			}
 			ifname = *argv;
 			if (argc == 1 && nmc->complete)
@@ -4111,20 +4202,20 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv)
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-				goto finish;
+				return;
 			}
-			g_ptr_array_add (ssids, *argv);
+			g_ptr_array_add (ssids, (gpointer) *argv);
 		} else if (!nmc->complete) {
 			g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv);
 			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-			goto finish;
+			return;
 		}
 
 		next_arg (nmc, &argc, &argv, NULL);
 	}
 
 	if (nmc->complete)
-		goto finish;
+		return;
 
 	/* Find Wi-Fi device to scan on. When no ifname is provided, the first Wi-Fi is used. */
 	device = find_wifi_device_by_iface (devices, ifname, NULL);
@@ -4135,7 +4226,7 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv)
 		else
 			g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found."));
 		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
-		goto finish;
+		return;
 	}
 
 	if (ssids->len) {
@@ -4143,7 +4234,8 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv)
 		g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aay"));
 
 		for (i = 0; i < ssids->len; i++) {
-			ssid = g_ptr_array_index (ssids, i);
+			const char *ssid = g_ptr_array_index (ssids, i);
+
 			g_variant_builder_add (&array_builder, "@ay",
 			                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, ssid, strlen (ssid), 1));
 		}
@@ -4158,9 +4250,6 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv)
 		                                   NULL, request_rescan_cb, nmc);
 
 	nmc->should_wait++;
-finish:
-	g_ptr_array_free (ssids, FALSE);
-	return nmc->return_value;
 }
 
 static void
@@ -4234,6 +4323,9 @@ print_wifi_connection (const NmcConfig *nmc_config, NMConnection *connection)
 	           || strcmp (key_mgmt, "sae") == 0) {
 		type = "WPA";
 		g_print ("%s: WPA\n", _("Security"));
+	} else if (   strcmp (key_mgmt, "owe") == 0) {
+		type = "nopass";
+		g_print ("%s: OWE\n", _("Security"));
 	}
 
 	if (psk)
@@ -4296,8 +4388,8 @@ wifi_show_device (const NmcConfig *nmc_config, NMDevice *device, GError **error)
 	return TRUE;
 }
 
-static NMCResultCode
-do_device_wifi_show_password (NmCli *nmc, int argc, char **argv)
+static void
+do_device_wifi_show_password (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	const char *ifname = NULL;
 	gs_free NMDevice **devices = NULL;
@@ -4317,7 +4409,8 @@ do_device_wifi_show_password (NmCli *nmc, int argc, char **argv)
 				g_string_printf (nmc->return_text,
 				                 _("Error: '%s' cannot repeat."),
 				                 *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			argc--;
 			argv++;
@@ -4325,7 +4418,8 @@ do_device_wifi_show_password (NmCli *nmc, int argc, char **argv)
 				g_string_printf (nmc->return_text,
 				                 _("Error: %s argument is missing."),
 				                 *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 			ifname = *argv;
 			if (argc == 1 && nmc->complete)
@@ -4334,14 +4428,15 @@ do_device_wifi_show_password (NmCli *nmc, int argc, char **argv)
 			g_string_printf (nmc->return_text,
 			                 _("Error: invalid extra argument '%s'."),
 			                 *argv);
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 
 		next_arg (nmc, &argc, &argv, NULL);
 	}
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	for (i = 0; devices[i]; i++) {
 		if (ifname && g_strcmp0 (nm_device_get_iface (devices[i]), ifname) != 0)
@@ -4353,7 +4448,8 @@ do_device_wifi_show_password (NmCli *nmc, int argc, char **argv)
 			if (ifname) {
 				g_string_printf (nmc->return_text,
 				                 _("%s"), error->message);
-				return NMC_RESULT_ERROR_UNKNOWN;
+				nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+				return;
 			}
 			g_clear_error (&error);
 		}
@@ -4365,10 +4461,9 @@ do_device_wifi_show_password (NmCli *nmc, int argc, char **argv)
 	if (!found) {
 		g_string_printf (nmc->return_text,
 		                 _("Error: No Wi-Fi device found."));
-		return NMC_RESULT_ERROR_UNKNOWN;
+		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+		return;
 	}
-
-	return nmc->return_value;
 }
 
 static NMCCommand device_wifi_cmds[] = {
@@ -4380,13 +4475,11 @@ static NMCCommand device_wifi_cmds[] = {
 	{ NULL,             do_device_wifi_list,           NULL,  TRUE,  TRUE },
 };
 
-static NMCResultCode
-do_device_wifi (NmCli *nmc, int argc, char **argv)
+static void
+do_device_wifi (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	next_arg (nmc, &argc, &argv, NULL);
 	nmc_do_cmd (nmc, device_wifi_cmds, *argv, argc, argv);
-
-	return nmc->return_value;
 }
 
 static int
@@ -4469,13 +4562,13 @@ show_device_lldp_list (NMDevice *device, NmCli *nmc, const char *fields_str, int
 	}
 
 	print_data_prepare_width (out.output_data);
-	print_data (&nmc->nmc_config, out_indices, header_name, 0, &out);
+	print_data (&nmc->nmc_config, &nmc->pager_data, out_indices, header_name, 0, &out);
 
 	return neighbors->len;
 }
 
-static NMCResultCode
-do_device_lldp_list (NmCli *nmc, int argc, char **argv)
+static void
+do_device_lldp_list (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMDevice *device = NULL;
 	gs_free_error GError *error = NULL;
@@ -4493,25 +4586,28 @@ do_device_lldp_list (NmCli *nmc, int argc, char **argv)
 			argv++;
 			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 
 			device = get_device (nmc, &argc, &argv, &error);
 			if (!device) {
 				g_string_printf (nmc->return_text, _("Error: %s."), error->message);
-				return error->code;
+				nmc->return_value = error->code;
+				return;
 			}
 		} else {
 			g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv);
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 
 		next_arg (nmc, &argc, &argv, NULL);
 	}
 
-	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
+	if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0)
 		fields_str = NMC_FIELDS_DEV_LLDP_LIST_COMMON;
-	else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) {
+	else if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "all") == 0) {
 	} else
 		fields_str = nmc->required_fields;
 
@@ -4519,25 +4615,22 @@ do_device_lldp_list (NmCli *nmc, int argc, char **argv)
 
 	if (error) {
 		g_string_printf (nmc->return_text, _("Error: 'device lldp list': %s"), error->message);
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	if (device) {
 		show_device_lldp_list (device, nmc, fields_str, &counter);
 	} else {
-		NMDevice **devices = nmc_get_devices_sorted (nmc->client);
-		int i;
+		gs_free NMDevice **devices = nmc_get_devices_sorted (nmc->client);
+		guint i;
 
 		for (i = 0; devices[i]; i++)
 			show_device_lldp_list (devices[i], nmc, fields_str, &counter);
-
-		g_free (devices);
 	}
-
-	return nmc->return_value;
 }
 
 static NMCCommand device_lldp_cmds[] = {
@@ -4545,16 +4638,14 @@ static NMCCommand device_lldp_cmds[] = {
 	{ NULL,    do_device_lldp_list,  NULL,             TRUE,   TRUE },
 };
 
-static NMCResultCode
-do_device_lldp (NmCli *nmc, int argc, char **argv)
+static void
+do_device_lldp (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	if (!nmc->mode_specified)
 		nmc->nmc_config_mutable.multiline_output = TRUE;  /* multiline mode is default for 'device lldp' */
 
 	next_arg (nmc, &argc, &argv, NULL);
 	nmc_do_cmd (nmc, device_lldp_cmds, *argv, argc, argv);
-
-	return nmc->return_value;
 }
 
 static gboolean
@@ -4599,38 +4690,35 @@ nmcli_device_tab_completion (const char *text, int start, int end)
 	return match_array;
 }
 
-static const NMCCommand device_cmds[] = {
-	{ "status",      do_devices_status,      usage_device_status,      TRUE,   TRUE },
-	{ "show",        do_device_show,         usage_device_show,        TRUE,   TRUE },
-	{ "connect",     do_device_connect,      usage_device_connect,     TRUE,   TRUE },
-	{ "reapply",     do_device_reapply,      usage_device_reapply,     TRUE,   TRUE },
-	{ "disconnect",  do_devices_disconnect,  usage_device_disconnect,  TRUE,   TRUE },
-	{ "delete",      do_devices_delete,      usage_device_delete,      TRUE,   TRUE },
-	{ "set",         do_device_set,          usage_device_set,         TRUE,   TRUE },
-	{ "monitor",     do_devices_monitor,     usage_device_monitor,     TRUE,   TRUE },
-	{ "wifi",        do_device_wifi,         usage_device_wifi,        FALSE,  FALSE },
-	{ "lldp",        do_device_lldp,         usage_device_lldp,        FALSE,  FALSE },
-	{ "modify",      do_device_modify,       usage_device_modify,      TRUE,   TRUE },
-	{ NULL,          do_devices_status,      usage,                    TRUE,   TRUE },
-};
-
-NMCResultCode
-do_devices (NmCli *nmc, int argc, char **argv)
+void
+nmc_command_func_device (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
+	static const NMCCommand cmds[] = {
+		{ "status",     do_devices_status,     usage_device_status,     TRUE,  TRUE  },
+		{ "show",       do_device_show,        usage_device_show,       TRUE,  TRUE  },
+		{ "connect",    do_device_connect,     usage_device_connect,    TRUE,  TRUE  },
+		{ "reapply",    do_device_reapply,     usage_device_reapply,    TRUE,  TRUE  },
+		{ "disconnect", do_devices_disconnect, usage_device_disconnect, TRUE,  TRUE  },
+		{ "delete",     do_devices_delete,     usage_device_delete,     TRUE,  TRUE  },
+		{ "set",        do_device_set,         usage_device_set,        TRUE,  TRUE  },
+		{ "monitor",    do_devices_monitor,    usage_device_monitor,    TRUE,  TRUE  },
+		{ "wifi",       do_device_wifi,        usage_device_wifi,       FALSE, FALSE },
+		{ "lldp",       do_device_lldp,        usage_device_lldp,       FALSE, FALSE },
+		{ "modify",     do_device_modify,      usage_device_modify,     TRUE,  TRUE  },
+		{ NULL,         do_devices_status,     usage,                   TRUE,  TRUE  },
+	};
+
 	next_arg (nmc, &argc, &argv, NULL);
 
-	/* Register polkit agent */
 	nmc_start_polkit_agent_start_try (nmc);
 
 	rl_attempted_completion_function = (rl_completion_func_t *) nmcli_device_tab_completion;
 
-	nmc_do_cmd (nmc, device_cmds, *argv, argc, argv);
-
-	return nmc->return_value;
+	nmc_do_cmd (nmc, cmds, *argv, argc, argv);
 }
 
 void
 monitor_devices (NmCli *nmc)
 {
-	do_devices_monitor (nmc, 0, NULL);
+	do_devices_monitor (NULL, nmc, 0, NULL);
 }
diff --git a/clients/cli/devices.h b/clients/cli/devices.h
index 055d56e7..8b7aa901 100644
--- a/clients/cli/devices.h
+++ b/clients/cli/devices.h
@@ -8,8 +8,6 @@
 
 #include "nmcli.h"
 
-NMCResultCode do_devices (NmCli *nmc, int argc, char **argv);
-
 void nmc_complete_device (NMClient *client, const char *prefix, gboolean wifi_only);
 
 void nmc_complete_bssid (NMClient *client, const char *ifname, const char *bssid_prefix);
diff --git a/clients/cli/general.c b/clients/cli/general.c
index 123cc5b4..747203bb 100644
--- a/clients/cli/general.c
+++ b/clients/cli/general.c
@@ -5,8 +5,6 @@
 
 #include "nm-default.h"
 
-#include "general.h"
-
 #include <stdlib.h>
 
 #include "nm-libnm-core-intern/nm-common-macros.h"
@@ -22,7 +20,14 @@
 
 /*****************************************************************************/
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (nm_state_to_string, NMState,
+static void permission_changed (GObject *gobject,
+                                GParamSpec *pspec,
+                                NmCli *nmc);
+
+/*****************************************************************************/
+
+static
+NM_UTILS_LOOKUP_STR_DEFINE (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")),
@@ -77,47 +82,12 @@ connectivity_to_color (NMConnectivityState connectivity)
 static const char *
 permission_to_string (NMClientPermission perm)
 {
-	switch (perm) {
-	case NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK:
-		return NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK;
-	case NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI:
-		return NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI;
-	case NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN:
-		return NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN;
-	case NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX:
-		return NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX;
-	case NM_CLIENT_PERMISSION_SLEEP_WAKE:
-		return NM_AUTH_PERMISSION_SLEEP_WAKE;
-	case NM_CLIENT_PERMISSION_NETWORK_CONTROL:
-		return NM_AUTH_PERMISSION_NETWORK_CONTROL;
-	case NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED:
-		return NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED;
-	case NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN:
-		return NM_AUTH_PERMISSION_WIFI_SHARE_OPEN;
-	case NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM:
-		return NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM;
-	case NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN:
-		return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
-	case NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME:
-		return NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME;
-	case NM_CLIENT_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS:
-		return NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS;
-	case NM_CLIENT_PERMISSION_RELOAD:
-		return NM_AUTH_PERMISSION_RELOAD;
-	case NM_CLIENT_PERMISSION_CHECKPOINT_ROLLBACK:
-		return NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK;
-	case NM_CLIENT_PERMISSION_ENABLE_DISABLE_STATISTICS:
-		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");
-	}
+	return    nm_auth_permission_to_string (perm)
+	       ?: _("unknown");
 }
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (permission_result_to_string, NMClientPermissionResult,
+static
+NM_UTILS_LOOKUP_STR_DEFINE (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")),
@@ -125,7 +95,8 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (permission_result_to_string, NMClientPermissi
 	NM_UTILS_LOOKUP_ITEM_IGNORE (NM_CLIENT_PERMISSION_RESULT_UNKNOWN),
 );
 
-_NM_UTILS_LOOKUP_DEFINE (static, permission_result_to_color, NMClientPermissionResult, NMMetaColor,
+static
+NM_UTILS_LOOKUP_DEFINE (permission_result_to_color, NMClientPermissionResult, NMMetaColor,
 	NM_UTILS_LOOKUP_DEFAULT (NM_META_COLOR_PERMISSION_UNKNOWN),
 	NM_UTILS_LOOKUP_ITEM (NM_CLIENT_PERMISSION_RESULT_YES,  NM_META_COLOR_PERMISSION_YES),
 	NM_UTILS_LOOKUP_ITEM (NM_CLIENT_PERMISSION_RESULT_NO,   NM_META_COLOR_PERMISSION_NO),
@@ -497,9 +468,9 @@ show_nm_status (NmCli *nmc, const char *pretty_header_name, const char *print_fl
 	const char *fields_all =    print_flds ?: NMC_FIELDS_NM_STATUS_ALL;
 	const char *fields_common = print_flds ?: NMC_FIELDS_NM_STATUS_COMMON;
 
-	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
+	if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0)
 		fields_str = fields_common;
-	else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
+	else if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "all") == 0)
 		fields_str = fields_all;
 	else
 		fields_str = nmc->required_fields;
@@ -518,15 +489,14 @@ show_nm_status (NmCli *nmc, const char *pretty_header_name, const char *print_fl
 	return TRUE;
 }
 
-static NMCResultCode
-do_general_status (NmCli *nmc, int argc, char **argv)
+static void
+do_general_status (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	next_arg (nmc, &argc, &argv, NULL);
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	show_nm_status (nmc, NULL, NULL);
-	return nmc->return_value;
 }
 
 static gboolean
@@ -534,32 +504,56 @@ timeout_cb (gpointer user_data)
 {
 	NmCli *nmc = (NmCli *) user_data;
 
+	g_signal_handlers_disconnect_by_func (nmc->client,
+	                                      G_CALLBACK (permission_changed),
+	                                      nmc);
+
 	g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout);
 	nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED;
 	quit ();
 	return FALSE;
 }
 
-static int
+static void
 print_permissions (void *user_data)
 {
 	NmCli *nmc = user_data;
 	gs_free_error GError *error = NULL;
 	const char *fields_str = NULL;
-	NMClientPermission perm;
-	guint i;
-	gpointer permissions[NM_CLIENT_PERMISSION_LAST + 1];
+	gpointer permissions[G_N_ELEMENTS (nm_auth_permission_sorted) + 1];
+	gboolean is_running;
+	int i;
+
+	is_running = nm_client_get_nm_running (nmc->client);
+
+	if (   is_running
+	    && nm_client_get_permissions_state (nmc->client) != NM_TERNARY_TRUE) {
+		/* wait longer. Permissions are not up to date. */
+		return;
+	}
+
+	g_signal_handlers_disconnect_by_func (nmc->client,
+	                                      G_CALLBACK (permission_changed),
+	                                      nmc);
 
-	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) {
-	} else if (strcasecmp (nmc->required_fields, "all") == 0) {
+	if (!is_running) {
+		/* NetworkManager quit while we were waiting. */
+		g_string_printf (nmc->return_text, _("NetworkManager is not running."));
+		nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
+		quit ();
+		return;
+	}
+
+	if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0) {
+	} else if (g_ascii_strcasecmp (nmc->required_fields, "all") == 0) {
 	} else
 		fields_str = nmc->required_fields;
 
-	for (i = 0, perm = NM_CLIENT_PERMISSION_NONE + 1; perm <= NM_CLIENT_PERMISSION_LAST; perm++)
-		permissions[i++] = GINT_TO_POINTER (perm);
-	permissions[i++] = NULL;
+	for (i = 0; i < (int) G_N_ELEMENTS (nm_auth_permission_sorted); i++)
+		permissions[i] = GINT_TO_POINTER (nm_auth_permission_sorted[i]);
+	permissions[i] = NULL;
 
-	nm_cli_spawn_pager (nmc);
+	nm_cli_spawn_pager (&nmc->nmc_config, &nmc->pager_data);
 
 	if (!nmc_print (&nmc->nmc_config,
 	                permissions,
@@ -573,64 +567,48 @@ print_permissions (void *user_data)
 	}
 
 	quit ();
-	return G_SOURCE_REMOVE;
-}
-
-static gboolean
-got_permissions (NmCli *nmc)
-{
-	NMClientPermission perm;
-
-	/* The server returns all the permissions at once, so if at least one is there
-	 * we already received the reply. */
-	for (perm = NM_CLIENT_PERMISSION_NONE + 1; perm <= NM_CLIENT_PERMISSION_LAST; perm++) {
-		if (nm_client_get_permission_result (nmc->client, perm) != NM_CLIENT_PERMISSION_RESULT_UNKNOWN)
-			return TRUE;
-	}
-
-	return FALSE;
 }
 
 static void
-permission_changed (NMClient *client,
-                    NMClientPermission permission,
-                    NMClientPermissionResult result,
+permission_changed (GObject *gobject,
+                    GParamSpec *pspec,
                     NmCli *nmc)
 {
-	if (got_permissions (nmc)) {
-		/* Defer the printing, so that we have a chance to process the other
-		 * permission-changed signals. */
-		g_signal_handlers_disconnect_by_func (nmc->client,
-		                                      G_CALLBACK (permission_changed),
-		                                      nmc);
-		g_idle_remove_by_data (nmc);
-		g_idle_add (print_permissions, nmc);
-	}
+	if (NM_IN_STRSET (pspec->name, NM_CLIENT_NM_RUNNING,
+	                               NM_CLIENT_PERMISSIONS_STATE))
+		print_permissions (nmc);
 }
 
 static gboolean
 show_nm_permissions (NmCli *nmc)
 {
-	/* The permissions are available now, just print them. */
-	if (got_permissions (nmc)) {
-		print_permissions (nmc);
-		return TRUE;
-	}
+	NMClientInstanceFlags instance_flags;
+
+	instance_flags = nm_client_get_instance_flags (nmc->client);
+	instance_flags &= ~NM_CLIENT_INSTANCE_FLAGS_NO_AUTO_FETCH_PERMISSIONS;
+
+	g_object_set (nmc->client,
+	              NM_CLIENT_INSTANCE_FLAGS, (guint) instance_flags,
+	              NULL);
 
-	/* The client didn't get the permissions reply yet. Subscribe to changes. */
-	g_signal_connect (nmc->client, NM_CLIENT_PERMISSION_CHANGED,
-	                  G_CALLBACK (permission_changed), nmc);
+	g_signal_connect (nmc->client,
+	                  "notify",
+	                  G_CALLBACK (permission_changed),
+	                  nmc);
 
 	if (nmc->timeout == -1)
 		nmc->timeout = 10;
 	g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc);
 
 	nmc->should_wait++;
+
+	print_permissions (nmc);
+
 	return TRUE;
 }
 
-static NMCResultCode
-do_general_reload (NmCli *nmc, int argc, char **argv)
+static void
+do_general_reload (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	gs_unref_variant GVariant *result = NULL;
 	gs_free_error GError *error = NULL;
@@ -643,7 +621,7 @@ do_general_reload (NmCli *nmc, int argc, char **argv)
 
 	if (nmc->complete) {
 		if (argc == 0)
-			return nmc->return_value;
+			return;
 
 		if (argc == 1) {
 			values = nm_utils_enum_get_values (nm_manager_reload_flags_get_type (),
@@ -651,7 +629,7 @@ do_general_reload (NmCli *nmc, int argc, char **argv)
 			                                   NM_MANAGER_RELOAD_FLAG_ALL);
 			nmc_complete_strv (*argv, -1, values);
 		}
-		return nmc->return_value;
+		return;
 	}
 
 	if (argc > 0) {
@@ -664,7 +642,8 @@ do_general_reload (NmCli *nmc, int argc, char **argv)
 			                 _("Error: invalid reload flag '%s'. Allowed flags are: %s"),
 			                 err_token,
 			                 joined);
-			return NMC_RESULT_ERROR_USER_INPUT;
+			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+			return;
 		}
 		argc--;
 		argv++;
@@ -672,7 +651,8 @@ do_general_reload (NmCli *nmc, int argc, char **argv)
 
 	if (argc > 0) {
 		g_string_printf (nmc->return_text, _("Error: extra argument '%s'"), *argv);
-		return NMC_RESULT_ERROR_USER_INPUT;
+		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+		return;
 	}
 
 	result = nmc_dbus_call_sync (nmc,
@@ -687,21 +667,18 @@ do_general_reload (NmCli *nmc, int argc, char **argv)
 		g_string_printf (nmc->return_text,
 		                 _("Error: failed to reload: %s"),
 		                 nmc_error_get_simple_message (error));
-		return NMC_RESULT_ERROR_UNKNOWN;
+		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
 	}
-
-	return nmc->return_value;
 }
 
-static NMCResultCode
-do_general_permissions (NmCli *nmc, int argc, char **argv)
+static void
+do_general_permissions (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	next_arg (nmc, &argc, &argv, NULL);
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	show_nm_permissions (nmc);
-	return nmc->return_value;
 }
 
 static void
@@ -716,8 +693,8 @@ show_general_logging (NmCli *nmc)
 		.domains = &domains_cache,
 	};
 
-	if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) {
-	} else if (strcasecmp (nmc->required_fields, "all") == 0) {
+	if (!nmc->required_fields || g_ascii_strcasecmp (nmc->required_fields, "common") == 0) {
+	} else if (g_ascii_strcasecmp (nmc->required_fields, "all") == 0) {
 	} else
 		fields_str = nmc->required_fields;
 
@@ -750,15 +727,30 @@ nmc_complete_strings_nocase (const char *prefix, ...)
 	va_end (args);
 }
 
-static NMCResultCode
-do_general_logging (NmCli *nmc, int argc, char **argv)
+static void
+_set_logging_cb (GObject *object, GAsyncResult *result, gpointer user_data)
 {
+	NmCli *nmc = user_data;
+	gs_unref_variant GVariant *res = NULL;
 	gs_free_error GError *error = NULL;
 
+	res = nm_client_dbus_call_finish (NM_CLIENT (object), result, &error);
+	if (!res) {
+		g_dbus_error_strip_remote_error (error);
+		g_string_printf (nmc->return_text, _("Error: failed to set logging: %s"),
+		                 nmc_error_get_simple_message (error));
+		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+	}
+	quit ();
+}
+
+static void
+do_general_logging (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
+{
 	next_arg (nmc, &argc, &argv, NULL);
 	if (argc == 0) {
 		if (nmc->complete)
-			return nmc->return_value;
+			return;
 
 		show_general_logging (nmc);
 	} else {
@@ -775,7 +767,8 @@ do_general_logging (NmCli *nmc, int argc, char **argv)
 				argv++;
 				if (!argc) {
 					g_string_printf (nmc->return_text, _("Error: '%s' argument is missing."), *(argv-1));
-					return NMC_RESULT_ERROR_USER_INPUT;
+					nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+					return;
 				}
 				if (argc == 1 && nmc->complete) {
 					nmc_complete_strings_nocase (*argv, "TRACE", "DEBUG", "INFO", "WARN",
@@ -787,7 +780,8 @@ do_general_logging (NmCli *nmc, int argc, char **argv)
 				argv++;
 				if (!argc) {
 					g_string_printf (nmc->return_text, _("Error: '%s' argument is missing."), *(argv-1));
-					return NMC_RESULT_ERROR_USER_INPUT;
+					nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+					return;
 				}
 				if (argc == 1 && nmc->complete) {
 					nmc_complete_strings_nocase (*argv, "PLATFORM", "RFKILL", "ETHER", "WIFI", "BT",
@@ -801,93 +795,90 @@ do_general_logging (NmCli *nmc, int argc, char **argv)
 				domains = *argv;
 			} else {
 				g_string_printf (nmc->return_text, _("Error: property '%s' is not known."), *argv);
-				return NMC_RESULT_ERROR_USER_INPUT;
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return;
 			}
 		} while (next_arg (nmc, &argc, &argv, NULL) == 0);
 
 		if (nmc->complete)
-			return nmc->return_value;
+			return;
 
-		nm_client_set_logging (nmc->client, level, domains, &error);
-		if (error) {
-			g_string_printf (nmc->return_text, _("Error: failed to set logging: %s"),
-			                 nmc_error_get_simple_message (error));
-			return NMC_RESULT_ERROR_UNKNOWN;
-		}
+		nmc->should_wait++;
+		nm_client_dbus_call (nmc->client,
+		                     NM_DBUS_PATH,
+		                     NM_DBUS_INTERFACE,
+		                     "SetLogging",
+		                     g_variant_new ("(ss)",
+		                                    level ?: "",
+		                                    domains ?: ""),
+		                     G_VARIANT_TYPE ("()"),
+		                     -1,
+		                     NULL,
+		                     _set_logging_cb,
+		                     nmc);
 	}
-
-	return nmc->return_value;
 }
 
 static void
 save_hostname_cb (GObject *object, GAsyncResult *result, gpointer user_data)
 {
-	NmCli *nmc = (NmCli *) user_data;
-	GError *error = NULL;
+	NmCli *nmc = user_data;
+	gs_free_error GError *error = NULL;
 
 	nm_client_save_hostname_finish (NM_CLIENT (object), result, &error);
 	if (error) {
 		g_string_printf (nmc->return_text, _("Error: failed to set hostname: %s"),
 		                 error->message);
 		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
-		g_error_free (error);
 	}
+
 	quit ();
 }
 
-static NMCResultCode
-do_general_hostname (NmCli *nmc, int argc, char **argv)
+static void
+do_general_hostname (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
+	const char *hostname;
+
 	next_arg (nmc, &argc, &argv, NULL);
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	if (argc == 0) {
 		/* no arguments -> get hostname */
-		char *hostname = NULL;
-
-		g_object_get (nmc->client, NM_CLIENT_HOSTNAME, &hostname, NULL);
-		if (hostname)
-			g_print ("%s\n", hostname);
-		g_free (hostname);
-	} else {
-		/* hostname provided -> set it */
-		const char *hostname = *argv;
-
-		if (next_arg (nmc, &argc, &argv, NULL) == 0)
-			g_print ("Warning: ignoring extra garbage after '%s' hostname\n", hostname);
+		gs_free char *s = NULL;
 
-		nmc->should_wait++;
-		nm_client_save_hostname_async (nmc->client, hostname, NULL, save_hostname_cb, nmc);
+		g_object_get (nmc->client, NM_CLIENT_HOSTNAME, &s, NULL);
+		if (s)
+			g_print ("%s\n", s);
+		return;
 	}
 
-	return nmc->return_value;
+	hostname = *argv;
+	if (next_arg (nmc, &argc, &argv, NULL) == 0)
+		g_print ("Warning: ignoring extra garbage after '%s' hostname\n", hostname);
 
+	nmc->should_wait++;
+	nm_client_save_hostname_async (nmc->client, hostname, NULL, save_hostname_cb, nmc);
 }
 
-static const NMCCommand general_cmds[] = {
-	{ "status",       do_general_status,       usage_general_status,       TRUE,   TRUE },
-	{ "hostname",     do_general_hostname,     usage_general_hostname,     TRUE,   TRUE },
-	{ "permissions",  do_general_permissions,  usage_general_permissions,  TRUE,   TRUE },
-	{ "logging",      do_general_logging,      usage_general_logging,      TRUE,   TRUE },
-	{ "reload",       do_general_reload,       usage_general_reload,       FALSE,  FALSE },
-	{ NULL,           do_general_status,       usage_general,              TRUE,   TRUE },
-};
-
-/*
- * Entry point function for general operations 'nmcli general'
- */
-NMCResultCode
-do_general (NmCli *nmc, int argc, char **argv)
+void
+nmc_command_func_general (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
+	static const NMCCommand cmds[] = {
+		{ "status",      do_general_status,      usage_general_status,      TRUE,  TRUE  },
+		{ "hostname",    do_general_hostname,    usage_general_hostname,    TRUE,  TRUE  },
+		{ "permissions", do_general_permissions, usage_general_permissions, TRUE,  TRUE  },
+		{ "logging",     do_general_logging,     usage_general_logging,     TRUE,  TRUE  },
+		{ "reload",      do_general_reload,      usage_general_reload,      FALSE, FALSE },
+		{ NULL,          do_general_status,      usage_general,             TRUE,  TRUE  },
+	};
+
 	next_arg (nmc, &argc, &argv, NULL);
 
-	/* Register polkit agent */
 	nmc_start_polkit_agent_start_try (nmc);
 
-	nmc_do_cmd (nmc, general_cmds, *argv, argc, argv);
-
-	return nmc->return_value;
+	nmc_do_cmd (nmc, cmds, *argv, argc, argv);
 }
 
 static gboolean
@@ -896,7 +887,7 @@ nmc_switch_show (NmCli *nmc, const char *switch_name, const char *header)
 	g_return_val_if_fail (nmc != NULL, FALSE);
 	g_return_val_if_fail (switch_name != NULL, FALSE);
 
-	if (nmc->required_fields && strcasecmp (nmc->required_fields, switch_name) != 0) {
+	if (nmc->required_fields && g_ascii_strcasecmp (nmc->required_fields, switch_name) != 0) {
 		g_string_printf (nmc->return_text, _("Error: '--fields' value '%s' is not valid here (allowed field: %s)"),
 		                 nmc->required_fields, switch_name);
 		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
@@ -930,42 +921,62 @@ nmc_switch_parse_on_off (NmCli *nmc, const char *arg1, const char *arg2, gboolea
 	return TRUE;
 }
 
-static NMCResultCode
-do_networking_on_off (NmCli *nmc, int argc, char **argv, gboolean enable)
+static void
+_do_networking_on_off_cb (GObject *object, GAsyncResult *result, gpointer user_data)
 {
-	if (nmc->complete)
-		return nmc->return_value;
-
-	/* Register polkit agent */
-	nmc_start_polkit_agent_start_try (nmc);
-
-	nm_client_networking_set_enabled (nmc->client, enable, NULL);
+	NmCli *nmc = user_data;
+	gs_unref_variant GVariant *ret = NULL;
+	gs_free_error GError *error = NULL;
 
-	return nmc->return_value;
+	ret = nm_client_dbus_call_finish (NM_CLIENT (object), result, &error);
+	if (!ret) {
+		if (g_error_matches (error,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED)) {
+			/* This is fine. Be quiet about it. */
+		} else {
+			g_dbus_error_strip_remote_error (error);
+			g_string_printf (nmc->return_text, _("Error: failed to set networking: %s"),
+			                 nmc_error_get_simple_message (error));
+			nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+		}
+	}
+	quit ();
 }
 
-static NMCResultCode
-do_networking_on (NmCli *nmc, int argc, char **argv)
+static void
+do_networking_on_off (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
-	next_arg (nmc, &argc, &argv, NULL);
-	return do_networking_on_off (nmc, argc, argv, TRUE);
-}
+	gboolean enable = nm_streq (cmd->cmd, "on");
 
-static NMCResultCode
-do_networking_off (NmCli *nmc, int argc, char **argv)
-{
 	next_arg (nmc, &argc, &argv, NULL);
-	return do_networking_on_off (nmc, argc, argv, FALSE);
+
+	if (nmc->complete)
+		return;
+
+	nmc_start_polkit_agent_start_try (nmc);
+
+	nmc->should_wait++;
+	nm_client_dbus_call (nmc->client,
+	                     NM_DBUS_PATH,
+	                     NM_DBUS_INTERFACE,
+	                     "Enable",
+	                     g_variant_new ("(b)", enable),
+	                     G_VARIANT_TYPE ("()"),
+	                     -1,
+	                     NULL,
+	                     _do_networking_on_off_cb,
+	                     nmc);
 }
 
-static NMCResultCode
-do_networking_connectivity (NmCli *nmc, int argc, char **argv)
+static void
+do_networking_connectivity (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	next_arg (nmc, &argc, &argv, NULL);
 	if (nmc->complete) {
 		if (argc == 1)
 			nmc_complete_strings (*argv, "check");
-		return nmc->return_value;
+		return;
 	}
 
 	if (!argc) {
@@ -988,50 +999,41 @@ do_networking_connectivity (NmCli *nmc, int argc, char **argv)
 		g_string_printf (nmc->return_text, _("Error: 'networking' command '%s' is not valid."), *argv);
 		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
 	}
-
-	return nmc->return_value;
 }
 
-static NMCResultCode
-do_networking_show (NmCli *nmc, int argc, char **argv)
+static void
+do_networking_show (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	next_arg (nmc, &argc, &argv, NULL);
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	nmc_switch_show (nmc, NMC_FIELDS_NM_NETWORKING, N_("Networking"));
-
-	return nmc->return_value;
 }
 
-static const NMCCommand networking_cmds[] = {
-	{ "on",           do_networking_on,           usage_networking_on,           TRUE,   TRUE },
-	{ "off",          do_networking_off,          usage_networking_off,          TRUE,   TRUE },
-	{ "connectivity", do_networking_connectivity, usage_networking_connectivity, TRUE,   TRUE },
-	{ NULL,           do_networking_show,         usage_networking,              TRUE,   TRUE },
-};
-
-/*
- * Entry point function for networking commands 'nmcli networking'
- */
-NMCResultCode
-do_networking (NmCli *nmc, int argc, char **argv)
+void
+nmc_command_func_networking (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
-	next_arg (nmc, &argc, &argv, NULL);
-	nmc_do_cmd (nmc, networking_cmds, *argv, argc, argv);
+	static const NMCCommand cmds[] = {
+		{ "on",           do_networking_on_off,       usage_networking_on,           TRUE, TRUE },
+		{ "off",          do_networking_on_off,       usage_networking_off,          TRUE, TRUE },
+		{ "connectivity", do_networking_connectivity, usage_networking_connectivity, TRUE, TRUE },
+		{ NULL,           do_networking_show,         usage_networking,              TRUE, TRUE },
+	};
 
-	return nmc->return_value;
+	next_arg (nmc, &argc, &argv, NULL);
+	nmc_do_cmd (nmc, cmds, *argv, argc, argv);
 }
 
-static NMCResultCode
-do_radio_all (NmCli *nmc, int argc, char **argv)
+static void
+do_radio_all (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	gboolean enable_flag;
 
 	next_arg (nmc, &argc, &argv, NULL);
 	if (argc == 0) {
 		if (nmc->complete)
-			return nmc->return_value;
+			return;
 
 		/* no argument, show all radio switches */
 		show_nm_status (nmc, N_("Radio switches"), NMC_FIELDS_NM_STATUS_RADIO);
@@ -1039,29 +1041,42 @@ do_radio_all (NmCli *nmc, int argc, char **argv)
 		if (nmc->complete) {
 			if (argc == 1)
 				nmc_complete_bool (*argv);
-			return nmc->return_value;
+			return;
 		}
 
 		if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag))
-			return nmc->return_value;
+			return;
 
 		nm_client_wireless_set_enabled (nmc->client, enable_flag);
 		nm_client_wimax_set_enabled (nmc->client, enable_flag);
 		nm_client_wwan_set_enabled (nmc->client, enable_flag);
 	}
+}
+
+static void
+_do_radio_wifi_cb (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+	NmCli *nmc = user_data;
+	gs_free_error GError *error = NULL;
 
-	return nmc->return_value;
+	if (!nm_client_dbus_set_property_finish (NM_CLIENT (object), result, &error)) {
+		g_dbus_error_strip_remote_error (error);
+		g_string_printf (nmc->return_text, _("Error: failed to set Wi-Fi radio: %s"),
+		                 nmc_error_get_simple_message (error));
+		nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+	}
+	quit ();
 }
 
-static NMCResultCode
-do_radio_wifi (NmCli *nmc, int argc, char **argv)
+static void
+do_radio_wifi (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	gboolean enable_flag;
 
 	next_arg (nmc, &argc, &argv, NULL);
 	if (argc == 0) {
 		if (nmc->complete)
-			return nmc->return_value;
+			return;
 
 		/* no argument, show current Wi-Fi state */
 		nmc_switch_show (nmc, NMC_FIELDS_NM_WIFI, N_("Wi-Fi radio switch"));
@@ -1069,26 +1084,35 @@ do_radio_wifi (NmCli *nmc, int argc, char **argv)
 		if (nmc->complete) {
 			if (argc == 1)
 				nmc_complete_bool (*argv);
-			return nmc->return_value;
+			return;
 		}
 		if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag))
-			return nmc->return_value;
+			return;
 
-		nm_client_wireless_set_enabled (nmc->client, enable_flag);
-	}
+		nmc_start_polkit_agent_start_try (nmc);
 
-	return nmc->return_value;
+		nmc->should_wait++;
+		nm_client_dbus_set_property (nmc->client,
+		                             NM_DBUS_PATH,
+		                             NM_DBUS_INTERFACE,
+		                             "WirelessEnabled",
+		                             g_variant_new_boolean (enable_flag),
+		                             -1,
+		                             NULL,
+		                             _do_radio_wifi_cb,
+		                             nmc);
+	}
 }
 
-static NMCResultCode
-do_radio_wwan (NmCli *nmc, int argc, char **argv)
+static void
+do_radio_wwan (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	gboolean enable_flag;
 
 	next_arg (nmc, &argc, &argv, NULL);
 	if (argc == 0) {
 		if (nmc->complete)
-			return nmc->return_value;
+			return;
 
 		/* no argument, show current WWAN (mobile broadband) state */
 		nmc_switch_show (nmc, NMC_FIELDS_NM_WWAN, N_("WWAN radio switch"));
@@ -1096,38 +1120,30 @@ do_radio_wwan (NmCli *nmc, int argc, char **argv)
 		if (nmc->complete) {
 			if (argc == 1)
 				nmc_complete_bool (*argv);
-			return nmc->return_value;
+			return;
 		}
 		if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag))
-			return nmc->return_value;
+			return;
 
 		nm_client_wwan_set_enabled (nmc->client, enable_flag);
 	}
-
-	return nmc->return_value;
 }
 
-static const NMCCommand radio_cmds[] = {
-	{ "all",   do_radio_all,   usage_radio_all,   TRUE,   TRUE },
-	{ "wifi",  do_radio_wifi,  usage_radio_wifi,  TRUE,   TRUE },
-	{ "wwan",  do_radio_wwan,  usage_radio_wwan,  TRUE,   TRUE },
-	{ NULL,    do_radio_all,   usage_radio,       TRUE,   TRUE },
-};
-
-/*
- * Entry point function for radio switch commands 'nmcli radio'
- */
-NMCResultCode
-do_radio (NmCli *nmc, int argc, char **argv)
+void
+nmc_command_func_radio (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
+	static const NMCCommand cmds[] = {
+		{ "all",  do_radio_all,  usage_radio_all,  TRUE, TRUE },
+		{ "wifi", do_radio_wifi, usage_radio_wifi, TRUE, TRUE },
+		{ "wwan", do_radio_wwan, usage_radio_wwan, TRUE, TRUE },
+		{ NULL,   do_radio_all,  usage_radio,      TRUE, TRUE },
+	};
+
 	next_arg (nmc, &argc, &argv, NULL);
 
-	/* Register polkit agent */
 	nmc_start_polkit_agent_start_try (nmc);
 
-	nmc_do_cmd (nmc, radio_cmds, *argv, argc, argv);
-
-	return nmc->return_value;
+	nmc_do_cmd (nmc, cmds, *argv, argc, argv);
 }
 
 static void
@@ -1367,11 +1383,8 @@ ac_overview (NmCli *nmc, NMActiveConnection *ac)
 	g_string_free (outbuf, TRUE);
 }
 
-/*
- * Entry point function for 'nmcli' without arguments.
- */
-NMCResultCode
-do_overview (NmCli *nmc, int argc, char **argv)
+void
+nmc_command_func_overview (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	NMDevice **devices;
 	const GPtrArray *p;
@@ -1386,7 +1399,7 @@ do_overview (NmCli *nmc, int argc, char **argv)
 	/* Register polkit agent */
 	nmc_start_polkit_agent_start_try (nmc);
 
-	nm_cli_spawn_pager (nmc);
+	nm_cli_spawn_pager (&nmc->nmc_config, &nmc->pager_data);
 
 	/* The VPN connections don't have devices (yet?). */
 	p = nm_client_get_active_connections (nmc->client);
@@ -1481,20 +1494,15 @@ do_overview (NmCli *nmc, int argc, char **argv)
 	           "\"nmcli connection show\" to get an overview on active connection profiles.\n"
 	           "\n"
 	           "Consult nmcli(1) and nmcli-examples(7) manual pages for complete usage details.\n"));
-
-	return NMC_RESULT_SUCCESS;
 }
 
-/*
- * Entry point function for 'nmcli monitor'
- */
-NMCResultCode
-do_monitor (NmCli *nmc, int argc, char **argv)
+void
+nmc_command_func_monitor (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv)
 {
 	next_arg (nmc, &argc, &argv, NULL);
 
 	if (nmc->complete)
-		return nmc->return_value;
+		return;
 
 	if (argc > 0) {
 		if (!nmc_arg_is_help (*argv)) {
@@ -1503,7 +1511,7 @@ do_monitor (NmCli *nmc, int argc, char **argv)
 		}
 
 		usage_monitor ();
-		return nmc->return_value;
+		return;
 	}
 
 	if (!nm_client_get_nm_running (nmc->client)) {
@@ -1530,6 +1538,4 @@ do_monitor (NmCli *nmc, int argc, char **argv)
 
 	monitor_devices (nmc);
 	monitor_connections (nmc);
-
-	return NMC_RESULT_SUCCESS;
 }
diff --git a/clients/cli/general.h b/clients/cli/general.h
deleted file mode 100644
index dd63e43f..00000000
--- a/clients/cli/general.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2010 - 2014 Red Hat, Inc.
- */
-
-#ifndef NMC_GENERAL_H
-#define NMC_GENERAL_H
-
-#include "nmcli.h"
-
-NMCResultCode do_general    (NmCli *nmc, int argc, char **argv);
-NMCResultCode do_networking (NmCli *nmc, int argc, char **argv);
-NMCResultCode do_radio      (NmCli *nmc, int argc, char **argv);
-NMCResultCode do_monitor    (NmCli *nmc, int argc, char **argv);
-NMCResultCode do_overview   (NmCli *nmc, int argc, char **argv);
-
-#endif /* NMC_GENERAL_H */
diff --git a/clients/cli/meson.build b/clients/cli/meson.build
index 56202063..8dd05aff 100644
--- a/clients/cli/meson.build
+++ b/clients/cli/meson.build
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
 name = 'nmcli'
 
 # FIXME: nmcli-completion should be renamed to nmcli
@@ -6,33 +8,26 @@ install_data(
   install_dir: join_paths(nm_datadir, 'bash-completion', 'completions'),
 )
 
-sources = files(
-  'agent.c',
-  'common.c',
-  'connections.c',
-  'devices.c',
-  'general.c',
-  'nmcli.c',
-  'polkit-agent.c',
-  'settings.c',
-  'utils.c',
-)
-
 deps = [
   libnmc_base_dep,
   libnmc_dep,
   readline_dep,
+  libnm_libnm_aux_dep,
 ]
 
-if enable_polkit_agent
-  sources += nm_polkit_listener
-
-  deps += polkit_agent_dep
-endif
-
 executable(
   name,
-  sources,
+  files(
+    'agent.c',
+    'common.c',
+    'connections.c',
+    'devices.c',
+    'general.c',
+    'nmcli.c',
+    'polkit-agent.c',
+    'settings.c',
+    'utils.c',
+  ),
   dependencies: deps,
   c_args: clients_c_flags + ['-DG_LOG_DOMAIN="@0@"'.format(name)],
   link_args: ldflags_linker_script_binary,
diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c
index 5fbd6d72..ee5ef015 100644
--- a/clients/cli/nmcli.c
+++ b/clients/cli/nmcli.c
@@ -25,8 +25,6 @@
 #include "common.h"
 #include "connections.h"
 #include "devices.h"
-#include "general.h"
-#include "agent.h"
 #include "settings.h"
 
 #if defined(NM_DIST_VERSION)
@@ -72,7 +70,7 @@
 	[NM_META_COLOR_ENABLED]                  = "32", \
 	[NM_META_COLOR_DISABLED]                 = "31", \
 
-NmCli nm_cli = {
+static NmCli nm_cli = {
 	.client = NULL,
 
 	.return_value = NMC_RESULT_SUCCESS,
@@ -101,6 +99,9 @@ NmCli nm_cli = {
 	.editor_save_confirmation = TRUE,
 };
 
+const NmCli *const nm_cli_global_readline = &nm_cli;
+const NmCli *const nmc_meta_environment_arg = &nm_cli;
+
 /*****************************************************************************/
 
 typedef struct {
@@ -255,21 +256,21 @@ usage (void)
 	              "\n"));
 }
 
-static const NMCCommand nmcli_cmds[] = {
-	{ "general",     do_general,      NULL,   FALSE,  FALSE },
-	{ "monitor",     do_monitor,      NULL,   TRUE,   FALSE },
-	{ "networking",  do_networking,   NULL,   FALSE,  FALSE },
-	{ "radio",       do_radio,        NULL,   FALSE,  FALSE },
-	{ "connection",  do_connections,  NULL,   FALSE,  FALSE },
-	{ "device",      do_devices,      NULL,   FALSE,  FALSE },
-	{ "agent",       do_agent,        NULL,   FALSE,  FALSE },
-	{ NULL,          do_overview,     usage,  TRUE,   TRUE },
-};
-
 static gboolean
-matches_arg (NmCli *nmc, int *argc, char ***argv, const char *pattern, char **arg)
+matches_arg (NmCli *nmc,
+             int *argc,
+             const char *const**argv,
+             const char *pattern,
+             char **arg)
 {
-	char *opt = *argv[0];
+	gs_free char *opt_free = NULL;
+	const char *opt = (*argv)[0];
+	gs_free char *arg_tmp = NULL;
+	const char *s;
+
+	nm_assert (opt);
+	nm_assert (opt[0] == '-');
+	nm_assert (!arg || !*arg);
 
 	if (nmc->return_value != NMC_RESULT_SUCCESS) {
 		/* Don't process further matches if there has been an error. */
@@ -285,33 +286,31 @@ matches_arg (NmCli *nmc, int *argc, char ***argv, const char *pattern, char **ar
 	if (arg) {
 		/* If there's a "=" separator, replace it with NUL so that matches()
 		 * works and consider the part after it to be the arguemnt's value. */
-		*arg = strchr (opt, '=');
-		if (*arg) {
-			**arg = '\0';
-			(*arg)++;
+		s = strchr (opt, '=');
+		if (s) {
+			opt = nm_strndup_a (300, opt, s - opt, &opt_free);
+			arg_tmp = g_strdup (&s[1]);
 		}
 	}
 
-	if (!matches (opt, pattern)) {
-		if (arg && *arg) {
-			/* Back off the replacement of "=". */
-			(*arg)--;
-			**arg = '=';
-		}
+	if (!matches (opt, pattern))
 		return FALSE;
-	}
 
-	if (arg && !*arg) {
-		/* We need a value, but the option didn't contain a "=<value>" part.
-		 * Proceed to the next argument. */
-		(*argc)--;
-		(*argv)++;
-		if (!*argc) {
-			g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt);
-			nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
-			return FALSE;
+	if (arg) {
+		if (arg_tmp)
+			*arg = g_steal_pointer (&arg_tmp);
+		else {
+			/* We need a value, but the option didn't contain a "=<value>" part.
+			 * Proceed to the next argument. */
+			if (*argc <= 1) {
+				g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt);
+				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
+				return FALSE;
+			}
+			(*argc)--;
+			(*argv)++;
+			*arg = g_strdup (*argv[0]);
 		}
-		*arg = *argv[0];
 	}
 
 	return TRUE;
@@ -697,26 +696,43 @@ set_colors (NmcColorOption color_option,
 /*************************************************************************************/
 
 static gboolean
-process_command_line (NmCli *nmc, int argc, char **argv)
+process_command_line (NmCli *nmc, int argc, char **argv_orig)
 {
+	static const NMCCommand nmcli_cmds[] = {
+		{ "general",    nmc_command_func_general,     NULL,  FALSE, FALSE },
+		{ "monitor",    nmc_command_func_monitor,     NULL,  TRUE,  FALSE },
+		{ "networking", nmc_command_func_networking,  NULL,  FALSE, FALSE },
+		{ "radio",      nmc_command_func_radio,       NULL,  FALSE, FALSE },
+		{ "connection", nmc_command_func_connection,  NULL,  FALSE, FALSE },
+		{ "device",     nmc_command_func_device,      NULL,  FALSE, FALSE },
+		{ "agent",      nmc_command_func_agent,       NULL,  FALSE, FALSE },
+		{ NULL,         nmc_command_func_overview,    usage, TRUE,  TRUE },
+	};
 	NmcColorOption colors = NMC_USE_COLOR_AUTO;
-	char *base;
+	const char *base;
+	const char *const*argv;
 
-	base = strrchr (argv[0], '/');
+	base = strrchr (argv_orig[0], '/');
 	if (base == NULL)
-		base = argv[0];
+		base = argv_orig[0];
 	else
 		base++;
-	if (argc > 1 && nm_streq (argv[1], "--complete-args")) {
+
+	if (   argc > 1
+	    && nm_streq (argv_orig[1], "--complete-args")) {
 		nmc->complete = TRUE;
-		argv[1] = argv[0];
-		next_arg (nmc, &argc, &argv, NULL);
+		argv_orig[1] = argv_orig[0];
+		argc--;
+		argv_orig++;
 	}
+
+	argv = (const char *const*) argv_orig;
+
 	next_arg (nmc, &argc, &argv, NULL);
 
 	/* parse options */
 	while (argc) {
-		char *value;
+		gs_free char *value = NULL;
 
 		if (argv[0][0] != '-')
 			break;
@@ -909,11 +925,12 @@ signal_handler (gpointer user_data)
 }
 
 void
-nm_cli_spawn_pager (NmCli *nmc)
+nm_cli_spawn_pager (const NmcConfig *nmc_config,
+                    NmcPagerData *pager_data)
 {
-	if (nmc->pager_pid > 0)
+	if (pager_data->pid != 0)
 		return;
-	nmc->pager_pid = nmc_terminal_spawn_pager (&nmc->nmc_config);
+	pager_data->pid = nmc_terminal_spawn_pager (nmc_config);
 }
 
 static void
@@ -935,13 +952,14 @@ nmc_cleanup (NmCli *nmc)
 
 	nm_clear_g_free (&nmc->required_fields);
 
-	if (nmc->pager_pid > 0) {
+	if (nmc->pager_data.pid != 0) {
+		pid_t pid = nm_steal_int (&nmc->pager_data.pid);
+
 		fclose (stdout);
 		fclose (stderr);
 		do {
-			ret = waitpid (nmc->pager_pid, NULL, 0);
+			ret = waitpid (pid, NULL, 0);
 		} while (ret == -1 && errno == EINTR);
-		nmc->pager_pid = 0;
 	}
 
 	nm_clear_g_free (&nmc->palette_buffer);
diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h
index 4615b046..c8d27b3d 100644
--- a/clients/cli/nmcli.h
+++ b/clients/cli/nmcli.h
@@ -101,6 +101,10 @@ typedef struct _NmcConfig {
 	const char *palette[_NM_META_COLOR_NUM];          /* Color palette */
 } NmcConfig;
 
+typedef struct {
+	pid_t pid;
+} NmcPagerData;
+
 typedef struct _NmcOutputData {
 	GPtrArray *output_data;                           /* GPtrArray of arrays of NmcOutputField structs - accumulates data for output */
 } NmcOutputData;
@@ -111,7 +115,8 @@ typedef struct _NmCli {
 
 	NMCResultCode return_value;                       /* Return code of nmcli */
 	GString *return_text;                             /* Reason text */
-	pid_t pager_pid;                                  /* PID of a pager, if one was spawned */
+
+	NmcPagerData pager_data;
 
 	int timeout;                                      /* Operation timeout */
 
@@ -135,7 +140,7 @@ typedef struct _NmCli {
 	char *palette_buffer;                             /* Buffer with sequences for terminal-colors.d(5)-based coloring. */
 } NmCli;
 
-extern NmCli nm_cli;
+extern const NmCli *const nm_cli_global_readline;
 
 /* Error quark for GError domain */
 #define NMCLI_ERROR (nmcli_error_quark ())
@@ -148,7 +153,8 @@ void     nmc_clear_sigint (void);
 void     nmc_set_sigquit_internal (void);
 void     nmc_exit (void);
 
-void nm_cli_spawn_pager (NmCli *nmc);
+void nm_cli_spawn_pager (const NmcConfig *nmc_config,
+                         NmcPagerData *pager_data);
 
 void nmc_empty_output_fields (NmcOutputData *output_data);
 
@@ -158,4 +164,27 @@ void nmc_empty_output_fields (NmcOutputData *output_data);
 		.output_data = g_ptr_array_new_full (20, g_free), \
 	}
 
+/*****************************************************************************/
+
+struct _NMCCommand;
+
+typedef struct _NMCCommand {
+	const char *cmd;
+	void (*func) (const struct _NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv);
+	void (*usage) (void);
+	bool needs_client;
+	bool needs_nm_running;
+} NMCCommand;
+
+void nmc_command_func_agent      (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv);
+void nmc_command_func_general    (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv);
+void nmc_command_func_networking (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv);
+void nmc_command_func_radio      (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv);
+void nmc_command_func_monitor    (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv);
+void nmc_command_func_overview   (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv);
+void nmc_command_func_connection (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv);
+void nmc_command_func_device     (const NMCCommand *cmd, NmCli *nmc, int argc, const char *const*argv);
+
+/*****************************************************************************/
+
 #endif /* NMC_NMCLI_H */
diff --git a/clients/cli/polkit-agent.c b/clients/cli/polkit-agent.c
index 69bfb68d..183562e7 100644
--- a/clients/cli/polkit-agent.c
+++ b/clients/cli/polkit-agent.c
@@ -14,16 +14,12 @@
 #include "nm-polkit-listener.h"
 #include "common.h"
 
-#if WITH_POLKIT_AGENT
 static char *
-polkit_request (NMPolkitListener *listener,
-                const char *request,
-                const char *action_id,
-                const char *message,
-                const char *icon_name,
-                const char *user,
-                gboolean echo_on,
-                gpointer user_data)
+polkit_read_passwd (gpointer instance,
+                    const char *action_id,
+                    const char *message,
+                    const char *user,
+                    gpointer user_data)
 {
 	NmCli *nmc = user_data;
 
@@ -32,87 +28,68 @@ polkit_request (NMPolkitListener *listener,
 
 	/* 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 && nm_streq (p, ": "))
-			*p = '\0';
-		return nmc_readline_echo (&nmc->nmc_config, echo_on, "%s (%s): ", tmp, user);
+		return nmc_readline_echo (&nmc->nmc_config, FALSE, "password (%s): ", user);
 	}
-
-	return nmc_readline_echo (&nmc->nmc_config, echo_on, "%s", request);
+	return nmc_readline_echo (&nmc->nmc_config, FALSE, "password: ");
 }
 
 static void
-polkit_show_info (NMPolkitListener *listener,
-                  const char *text,
-                  gpointer user_data)
+polkit_error (gpointer instance,
+              const char *error,
+              gpointer user_data)
 {
-	g_print (_("Authentication message: %s\n"), text);
+	g_printerr (_("Error: polkit agent failed: %s\n"), error);
 }
 
-static void
-polkit_show_error (NMPolkitListener *listener,
-                   const char *text,
-                   gpointer user_data)
-{
-	g_print (_("Authentication error: %s\n"), text);
-}
-
-static void
-polkit_completed (NMPolkitListener *listener,
-                  gboolean gained_authorization,
-                  gpointer user_data)
-{
-	/* We don't print anything here. The outcome will be evident from
-	 * the operation result anyway. */
-}
-#endif
-
 gboolean
 nmc_polkit_agent_init (NmCli* nmc, gboolean for_session, GError **error)
 {
-#if WITH_POLKIT_AGENT
-	static const NMPolkitListenVtable vtable = {
-		.on_request = polkit_request,
-		.on_show_info = polkit_show_info,
-		.on_show_error = polkit_show_error,
-		.on_completed = polkit_completed,
-	};
 	NMPolkitListener *listener;
+	GDBusConnection *dbus_connection = NULL;
 
 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-	listener = nm_polkit_listener_new (for_session, error);
-	if (!listener)
-		return FALSE;
+	if (nmc->client && nm_client_get_dbus_connection (nmc->client)) {
+		dbus_connection = nm_client_get_dbus_connection (nmc->client);
+		listener = nm_polkit_listener_new (dbus_connection, for_session);
+	} else {
+		dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
+		                                  NULL,
+		                                  error);
+
+		if (!dbus_connection) {
+			return FALSE;
+		}
+
+		listener = nm_polkit_listener_new (dbus_connection, for_session);
+		g_object_unref (dbus_connection);
+	}
 
-	nm_polkit_listener_set_vtable (listener, &vtable, nmc);
+	g_signal_connect (listener,
+	                  NM_POLKIT_LISTENER_SIGNAL_REQUEST_SYNC,
+	                  G_CALLBACK (polkit_read_passwd),
+	                  nmc);
+	g_signal_connect (listener,
+	                  NM_POLKIT_LISTENER_SIGNAL_ERROR,
+	                  G_CALLBACK (polkit_error),
+	                  NULL);
 
 	nmc->pk_listener = listener;
-#endif
 	return TRUE;
 }
 
 void
 nmc_polkit_agent_fini (NmCli* nmc)
 {
-#if WITH_POLKIT_AGENT
 	if (nmc->pk_listener) {
-		nm_polkit_listener_set_vtable (nmc->pk_listener, NULL, NULL);
 		g_clear_object (&nmc->pk_listener);
 	}
-#endif
 }
 
 gboolean
 nmc_start_polkit_agent_start_try (NmCli *nmc)
 {
-#if WITH_POLKIT_AGENT
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
 
 	/* We don't register polkit agent at all when running non-interactively */
 	if (!nmc->ask)
@@ -121,9 +98,7 @@ nmc_start_polkit_agent_start_try (NmCli *nmc)
 	if (!nmc_polkit_agent_init (nmc, FALSE, &error)) {
 		g_printerr (_("Warning: polkit agent initialization failed: %s\n"),
 		            error->message);
-		g_error_free (error);
 		return FALSE;
 	}
-#endif
 	return TRUE;
 }
diff --git a/clients/cli/settings.c b/clients/cli/settings.c
index 698471d7..0487fb96 100644
--- a/clients/cli/settings.c
+++ b/clients/cli/settings.c
@@ -456,8 +456,6 @@ const NMMetaEnvironment *const nmc_meta_environment = &((NMMetaEnvironment) {
 	.get_nm_connections = _env_get_nm_connections,
 });
 
-NmCli *const nmc_meta_environment_arg = &nm_cli;
-
 static char *
 get_property_val (NMSetting *setting, const char *prop, NMMetaAccessorGetType get_type, gboolean show_secrets, GError **error)
 {
@@ -475,7 +473,7 @@ get_property_val (NMSetting *setting, const char *prop, NMMetaAccessorGetType ge
 
 			value = property_info->property_type->get_fcn (property_info,
 			                                               nmc_meta_environment,
-			                                               nmc_meta_environment_arg,
+			                                               (gpointer) nmc_meta_environment_arg,
 			                                               setting,
 			                                               get_type,
 			                                               show_secrets ? NM_META_ACCESSOR_GET_FLAGS_SHOW_SECRETS : 0,
@@ -568,7 +566,7 @@ nmc_setting_set_property (NMClient *client,
 	g_object_freeze_notify (G_OBJECT (setting));
 	success = property_info->property_type->set_fcn (property_info,
 	                                                 nmc_meta_environment,
-	                                                 nmc_meta_environment_arg,
+	                                                 (gpointer) nmc_meta_environment_arg,
 	                                                 setting,
 	                                                 modifier,
 	                                                 value,
diff --git a/clients/cli/utils.c b/clients/cli/utils.c
index 2e3a38f7..d0d553b9 100644
--- a/clients/cli/utils.c
+++ b/clients/cli/utils.c
@@ -154,7 +154,7 @@ parse_global_arg (NmCli *nmc, const char *arg)
  * -1 otherwise (no more args).
  */
 int
-next_arg (NmCli *nmc, int *argc, char ***argv, ...)
+next_arg (NmCli *nmc, int *argc, const char *const**argv, ...)
 {
 	va_list args;
 	const char *cmd_option;
@@ -248,7 +248,7 @@ nmc_arg_is_option (const char *str, const char *opt_name)
  * Returns: TRUE on success, FALSE on an error and sets 'error'
  */
 gboolean
-nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error)
+nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, const char *const**argv, GError **error)
 {
 	nmc_arg_t *p;
 	gboolean found;
@@ -321,19 +321,14 @@ nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GErr
 char *
 ssid_to_hex (const char *str, gsize len)
 {
-	GString *printable;
-	char *printable_str;
-	int i;
-
-	if (str == NULL || len == 0)
+	if (len == 0)
 		return NULL;
 
-	printable = g_string_new (NULL);
-	for (i = 0; i < len; i++) {
-		g_string_append_printf (printable, "%02X", (unsigned char) str[i]);
-	}
-	printable_str = g_string_free (printable, FALSE);
-	return printable_str;
+	return nm_utils_bin2hexstr_full (str,
+	                                 len,
+	                                 '\0',
+	                                 TRUE,
+	                                 NULL);
 }
 
 /*
@@ -667,7 +662,6 @@ _output_selection_append (GArray *cols,
 	guint i;
 	const NMMetaAbstractInfo *const*nested;
 	NMMetaSelectionResultList *selection;
-	const NMMetaSelectionItem *si;
 
 	col_idx = cols->len;
 
@@ -688,6 +682,8 @@ _output_selection_append (GArray *cols,
 			gs_free char *allowed_fields = NULL;
 
 			if (parent_idx != PRINT_DATA_COL_PARENT_NIL) {
+				const NMMetaSelectionItem *si;
+
 				si = g_array_index (cols, PrintDataCol, parent_idx).selection_item;
 				allowed_fields = nm_meta_abstract_info_get_nested_names_str (si->info, si->self_selection);
 			}
@@ -719,10 +715,9 @@ _output_selection_append (GArray *cols,
 		g_ptr_array_add (gfree_keeper, selection);
 
 		for (i = 0; i < selection->num; i++) {
-			si = &selection->items[i];
 			if (!_output_selection_append (cols,
 			                               col_idx,
-			                               si,
+			                               &selection->items[i],
 			                               gfree_keeper,
 			                               error))
 				return FALSE;
@@ -778,7 +773,8 @@ _output_selection_complete (GArray *cols)
 static gboolean
 _output_selection_parse (const NMMetaAbstractInfo *const*fields,
                          const char *fields_str,
-                         GArray **out_cols,
+                         PrintDataCol **out_cols_data,
+                         guint *out_cols_len,
                          GPtrArray **out_gfree_keeper,
                          GError **error)
 {
@@ -803,16 +799,18 @@ _output_selection_parse (const NMMetaAbstractInfo *const*fields,
 	cols = g_array_new (FALSE, TRUE, sizeof (PrintDataCol));
 
 	for (i = 0; i < selection->num; i++) {
-		const NMMetaSelectionItem *si = &selection->items[i];
-
-		if (!_output_selection_append (cols, PRINT_DATA_COL_PARENT_NIL,
-		                               si, gfree_keeper, error))
+		if (!_output_selection_append (cols,
+		                               PRINT_DATA_COL_PARENT_NIL,
+		                               &selection->items[i],
+		                               gfree_keeper,
+		                               error))
 			return FALSE;
 	}
 
 	_output_selection_complete (cols);
 
-	*out_cols = g_steal_pointer (&cols);
+	*out_cols_len = cols->len;
+	*out_cols_data = (PrintDataCol *) g_array_free (g_steal_pointer (&cols), FALSE);
 	*out_gfree_keeper = g_steal_pointer (&gfree_keeper);
 	return TRUE;
 }
@@ -1069,7 +1067,7 @@ _print_fill (const NmcConfig *nmc_config,
 
 			value = nm_meta_abstract_info_get (info,
 			                                   nmc_meta_environment,
-			                                   nmc_meta_environment_arg,
+			                                   (gpointer) nmc_meta_environment_arg,
 			                                   target,
 			                                   targets_data,
 			                                   text_get_type,
@@ -1114,7 +1112,7 @@ _print_fill (const NmcConfig *nmc_config,
 
 			cell->color = GPOINTER_TO_INT (nm_meta_abstract_info_get (info,
 			                                                          nmc_meta_environment,
-			                                                          nmc_meta_environment_arg,
+			                                                          (gpointer) nmc_meta_environment_arg,
 			                                                          target,
 			                                                          targets_data,
 			                                                          NM_META_ACCESSOR_GET_TYPE_COLOR,
@@ -1142,7 +1140,8 @@ _print_fill (const NmcConfig *nmc_config,
 		header_cell->width = nmc_string_screen_width (header_cell->title, NULL);
 
 		for (i_row = 0; i_row < targets_len; i_row++) {
-			const PrintDataCell *cell = &g_array_index (cells, PrintDataCell, i_row * cols_len + i_col);
+			const PrintDataCell *cells_line = &g_array_index (cells, PrintDataCell, i_row * header_row->len);
+			const PrintDataCell *cell = &cells_line[i_col];
 			const char *const*i_strv;
 
 			switch (cell->text_format) {
@@ -1379,20 +1378,24 @@ nmc_print (const NmcConfig *nmc_config,
            GError **error)
 {
 	gs_unref_ptrarray GPtrArray *gfree_keeper = NULL;
-	gs_unref_array GArray *cols = NULL;
+	gs_free PrintDataCol *cols_data = NULL;
+	guint cols_len;
 	gs_unref_array GArray *header_row = NULL;
 	gs_unref_array GArray *cells = NULL;
 
-	if (!_output_selection_parse (fields, fields_str,
-	                              &cols, &gfree_keeper,
+	if (!_output_selection_parse (fields,
+	                              fields_str,
+	                              &cols_data,
+	                              &cols_len,
+	                              &gfree_keeper,
 	                              error))
 		return FALSE;
 
 	_print_fill (nmc_config,
 	             targets,
 	             targets_data,
-	             &g_array_index (cols, PrintDataCol, 0),
-	             cols->len,
+	             cols_data,
+	             cols_len,
 	             &header_row,
 	             &cells);
 
@@ -1568,6 +1571,7 @@ get_value_to_print (const NmcConfig *nmc_config,
  */
 void
 print_required_fields (const NmcConfig *nmc_config,
+                       NmcPagerData *pager_data,
                        NmcOfFlags of_flags,
                        const GArray *indices,
                        const char *header_name,
@@ -1584,7 +1588,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;
 
-	nm_cli_spawn_pager (&nm_cli);
+	nm_cli_spawn_pager (nmc_config, pager_data);
 
 	/* --- Main header --- */
 	if (   nmc_config->print_output == NMC_PRINT_PRETTY
@@ -1793,6 +1797,7 @@ print_data_prepare_width (GPtrArray *output_data)
 
 void
 print_data (const NmcConfig *nmc_config,
+            NmcPagerData *pager_data,
             const GArray *indices,
             const char *header_name,
             int indent,
@@ -1803,9 +1808,13 @@ print_data (const NmcConfig *nmc_config,
 	for (i = 0; i < out->output_data->len; i++) {
 		const NmcOutputField *field_values = g_ptr_array_index (out->output_data, i);
 
-		print_required_fields (nmc_config, field_values[0].flags,
-		                       indices, header_name,
-		                       indent, field_values);
+		print_required_fields (nmc_config,
+		                       pager_data,
+		                       field_values[0].flags,
+		                       indices,
+		                       header_name,
+		                       indent,
+		                       field_values);
 	}
 }
 
diff --git a/clients/cli/utils.h b/clients/cli/utils.h
index e699a641..76f5bae0 100644
--- a/clients/cli/utils.h
+++ b/clients/cli/utils.h
@@ -19,10 +19,10 @@ typedef struct {
 } nmc_arg_t;
 
 /* === Functions === */
-int next_arg (NmCli *nmc, int *argc, char ***argv, ...);
+int next_arg (NmCli *nmc, int *argc, const char *const**argv, ...);
 gboolean nmc_arg_is_help (const char *arg);
 gboolean nmc_arg_is_option (const char *arg, const char *opt_name);
-gboolean nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error);
+gboolean nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, const char *const**argv, GError **error);
 char *ssid_to_hex (const char *str, gsize len);
 void nmc_terminal_erase_line (void);
 void nmc_terminal_show_progress (const char *str);
@@ -51,6 +51,7 @@ GArray *parse_output_fields (const char *fields_str,
 NmcOutputField *nmc_dup_fields_array (const NMMetaAbstractInfo *const*fields, NmcOfFlags flags);
 void nmc_empty_output_fields (NmcOutputData *output_data);
 void print_required_fields (const NmcConfig *nmc_config,
+                            NmcPagerData *pager_data,
                             NmcOfFlags of_flags,
                             const GArray *indices,
                             const char *header_name,
@@ -58,6 +59,7 @@ void print_required_fields (const NmcConfig *nmc_config,
                             const NmcOutputField *field_values);
 void print_data_prepare_width (GPtrArray *output_data);
 void print_data (const NmcConfig *nmc_config,
+                 NmcPagerData *pager_data,
                  const GArray *indices,
                  const char *header_name,
                  int indent,
@@ -66,7 +68,7 @@ void print_data (const NmcConfig *nmc_config,
 /*****************************************************************************/
 
 extern const NMMetaEnvironment *const nmc_meta_environment;
-extern NmCli *const nmc_meta_environment_arg;
+extern const NmCli *const nmc_meta_environment_arg;
 
 typedef enum {