summary refs log tree commit diff
path: root/clients/cli/connections.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2017-05-11 14:55:55 +0200
committerMichael Biebl <biebl@debian.org>2017-05-11 14:55:55 +0200
commitc333f062ddcba9b35330647bf6cbd0a07f2d786e (patch)
tree257c3a0c74c09f4ad2328eab5b932806405f0c1c /clients/cli/connections.c
parenta222e56e103f949b148a6942e385ccca2c26d9f3 (diff)
New upstream version 1.8.0 upstream/1.8.0
Diffstat (limited to 'clients/cli/connections.c')
-rw-r--r--clients/cli/connections.c673
1 files changed, 376 insertions, 297 deletions
diff --git a/clients/cli/connections.c b/clients/cli/connections.c
index b40d517b..a93dc15f 100644
--- a/clients/cli/connections.c
+++ b/clients/cli/connections.c
@@ -183,6 +183,7 @@ NmcOutputField nmc_fields_settings_names[] = {
 	SETTING_FIELD (NM_SETTING_MACVLAN_SETTING_NAME,           nmc_fields_setting_macvlan + 1),           /* 28 */
 	SETTING_FIELD (NM_SETTING_VXLAN_SETTING_NAME,             nmc_fields_setting_vxlan + 1),             /* 29 */
 	SETTING_FIELD (NM_SETTING_PROXY_SETTING_NAME,             nmc_fields_setting_proxy + 1),             /* 30 */
+	SETTING_FIELD (NM_SETTING_DUMMY_SETTING_NAME,             nmc_fields_setting_dummy + 1),             /* 31 */
 	{NULL, NULL, 0, NULL, NULL, FALSE, FALSE, 0}
 };
 #define NMC_FIELDS_SETTINGS_NAMES_ALL_X  NM_SETTING_CONNECTION_SETTING_NAME","\
@@ -477,6 +478,7 @@ usage_connection_add (void)
 	              "                  [source-port-min <0-65535>]\n"
 	              "                  [source-port-max <0-65535>]\n"
 	              "                  [destination-port <0-65535>]\n\n"
+	              "    dummy:         \n\n"
 	              "  SLAVE_OPTIONS:\n"
 	              "    bridge:       [priority <0-63>]\n"
 	              "                  [path-cost <1-65535>]\n"
@@ -1658,13 +1660,13 @@ parse_preferred_connection_order (const char *order, GError **error)
 		if (str[0] == '+' || str[0] == '-')
 			str++;
 
-		if (matches (str, "active") == 0)
+		if (matches (str, "active"))
 			val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE;
-		else if (matches (str, "name") == 0)
+		else if (matches (str, "name"))
 			val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME;
-		else if (matches (str, "type") == 0)
+		else if (matches (str, "type"))
 			val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE;
-		else if (matches (str, "path") == 0)
+		else if (matches (str, "path"))
 			val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH;
 		else {
 			g_array_unref (order_arr);
@@ -1711,7 +1713,9 @@ get_connection (NmCli *nmc, int *argc, char ***argv, int *pos, GError **error)
 	    || strcmp (**argv, "uuid") == 0
 	    || strcmp (**argv, "path") == 0) {
 		selector = **argv;
-		if (next_arg (argc, argv) != 0) {
+		(*argc)--;
+		(*argv)++;
+		if (!*argc) {
 			g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
 			             _("%s argument is missing"), selector);
 			return NULL;
@@ -1730,7 +1734,7 @@ get_connection (NmCli *nmc, int *argc, char ***argv, int *pos, GError **error)
 	 * don't switch to next argument.
 	 */
 	if (!pos || !*pos)
-		next_arg (argc, argv);
+		next_arg (nmc, argc, argv, NULL);
 
 	return connection;
 }
@@ -1742,41 +1746,32 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 	char *profile_flds = NULL, *active_flds = NULL;
 	GPtrArray *invisibles, *sorted_cons;
 	gboolean active_only = FALSE;
-	gboolean show_secrets = FALSE;
 	GArray *order = NULL;
-	int i;
-
-	/* check connection show options [--active] [--show-secrets] */
-	for (i = 0; i < 3; i++) {
-		if (argc == 1 && nmc->complete) {
-			nmc_complete_strings (*argv, "--active", "--show-secrets",
-			                             "--order", NULL);
-		}
+	int i, option;
 
-		if (!active_only && nmc_arg_is_option (*argv, "active")) {
+	/* check connection show options [--active] [--order <order spec>] */
+	while ((option = next_arg (nmc, &argc, &argv, "--active", "--order", NULL)) > 0) {
+		switch (option) {
+		case 1: /* --active */
 			active_only = TRUE;
-			next_arg (&argc, &argv);
-		} else if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) {
-			/* --show-secrets is deprecated in favour of global --show-secrets */
-			/* Keep it here for backwards compatibility */
-			show_secrets = TRUE;
-			next_arg (&argc, &argv);
-		} else if (!order && nmc_arg_is_option (*argv, "order")) {
-			if (next_arg (&argc, &argv) != 0) {
+			break;
+		case 2: /* --order */
+			argc--;
+			argv++;
+			if (!argc) {
 				g_set_error_literal (&err, NMCLI_ERROR, 0,
 				                     _("'--order' argument is missing"));
 				goto finish;
 			}
-			/* TODO: complete --order */
 			order = parse_preferred_connection_order (*argv, &err);
 			if (err)
 				goto finish;
-			next_arg (&argc, &argv);
-		} else {
+			break;
+		default:
+			g_assert_not_reached();
 			break;
 		}
 	}
-	show_secrets = nmc->show_secrets || show_secrets;
 
 	if (argc == 0) {
 		const GPtrArray *connections;
@@ -1799,10 +1794,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 		tmpl = nmc_fields_con_show;
 		tmpl_len = sizeof (nmc_fields_con_show);
 		nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &err);
-		if (err) {
-			goto finish;
-		}
-		if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &err))
+		if (err)
 			goto finish;
 
 		/* Add headers */
@@ -1842,6 +1834,23 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 		g_free (nmc->required_fields);
 		nmc->required_fields = NULL;
 
+		/* Before printing the connections check if we have a "--show-secret"
+		 * option after the connection ids */
+		if (!nmc->show_secrets && !nmc->complete) {
+			int argc_cp = argc;
+			char **argv_cp = argv;
+
+			do {
+				if (   nm_streq (*argv_cp, "id")
+				    || nm_streq (*argv_cp, "uuid")
+				    || nm_streq (*argv_cp, "path")
+				    || nm_streq (*argv_cp, "apath")) {
+					argc_cp--;
+					argv_cp++;
+				}
+			} while (next_arg (nmc, &argc_cp, &argv_cp, NULL) != -1);
+		}
+
 		while (argc > 0) {
 			const GPtrArray *connections;
 			gboolean res;
@@ -1857,7 +1866,9 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 			    || strcmp (*argv, "path") == 0
 			    || strcmp (*argv, "apath") == 0) {
 				selector = *argv;
-				if (next_arg (&argc, &argv) != 0) {
+				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;
@@ -1892,12 +1903,12 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 			if (!acon)
 				acon = get_ac_for_connection (active_cons, con);
 			if (active_only && !acon) {
-				next_arg (&argc, &argv);
+				next_arg (nmc, &argc, &argv, NULL);
 				continue;
 			}
 
 			if (nmc->complete) {
-				next_arg (&argc, &argv);
+				next_arg (nmc, &argc, &argv, NULL);
 				continue;
 			}
 
@@ -1909,9 +1920,9 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 			if (without_fields || profile_flds) {
 				if (con) {
 					nmc->required_fields = profile_flds;
-					if (show_secrets)
+					if (nmc->show_secrets)
 						update_secrets_in_connection (NM_REMOTE_CONNECTION (con), con);
-					res = nmc_connection_profile_details (con, nmc, show_secrets);
+					res = nmc_connection_profile_details (con, nmc, nmc->show_secrets);
 					nmc->required_fields = NULL;
 					if (!res)
 						goto finish;
@@ -1935,7 +1946,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv)
 			 * so process the same argument again.
 			 */
 			if (!pos)
-				next_arg (&argc, &argv);
+				next_arg (nmc, &argc, &argv, NULL);
 		}
 	}
 
@@ -2122,104 +2133,90 @@ typedef struct {
 static void activate_connection_info_finish (ActivateConnectionInfo *info);
 
 static const char *
-vpn_connection_state_reason_to_string (NMVpnConnectionStateReason reason)
+active_connection_state_reason_to_string (NMActiveConnectionStateReason reason)
 {
 	switch (reason) {
-	case NM_VPN_CONNECTION_STATE_REASON_UNKNOWN:
-		return _("unknown reason");
-	case NM_VPN_CONNECTION_STATE_REASON_NONE:
-		return _("none");
-	case NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED:
-		return _("the user was disconnected");
-	case NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED:
-		return _("the base network connection was interrupted");
-	case NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED:
-		return _("the VPN service stopped unexpectedly");
-	case NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID:
-		return _("the VPN service returned invalid configuration");
-	case NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT:
-		return _("the connection attempt timed out");
-	case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT:
-		return _("the VPN service did not start in time");
-	case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED:
-		return _("the VPN service failed to start");
-	case NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS:
-		return _("no valid VPN secrets");
-	case NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED:
-		return _("invalid VPN secrets");
-	case NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED:
-		return _("the connection was removed");
-	default:
-		return _("unknown");
-	}
+	case NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN:
+		return _("Unknown reason");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_NONE:
+		return _("The connection was disconnected");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED:
+		return _("Disconnected by user");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED:
+		return _("The base network connection was interrupted");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_STOPPED:
+		return _("The VPN service stopped unexpectedly");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_IP_CONFIG_INVALID:
+		return _("The VPN service returned invalid configuration");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_CONNECT_TIMEOUT:
+		return _("The connection attempt timed out");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT:
+		return _("The VPN service did not start in time");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED:
+		return _("The VPN service failed to start");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS:
+		return _("No valid secrets");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_LOGIN_FAILED:
+		return _("Invalid secrets");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_CONNECTION_REMOVED:
+		return _("The connection was removed");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_DEPENDENCY_FAILED:
+		return _("Master connection failed");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REALIZE_FAILED:
+		return _("Could not create a software link");
+	case NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REMOVED:
+		return _("The device disappeared");
+	}
+
+	g_return_val_if_reached (_("Invalid reason"));
 }
 
 static void
-device_state_cb (NMDevice *device, GParamSpec *pspec, ActivateConnectionInfo *info)
+check_activated (ActivateConnectionInfo *info)
 {
 	NmCli *nmc = info->nmc;
-	NMActiveConnection *active;
-	NMDeviceState state;
+	NMDevice *device = info->device;
+	NMActiveConnection *active = info->active;
 	NMActiveConnectionState ac_state;
+	NMActiveConnectionStateReason ac_reason;
+	NMDeviceState dev_state;
+	NMDeviceStateReason dev_reason;
 
-	active = nm_device_get_active_connection (device);
-	state = nm_device_get_state (device);
-
-	ac_state = active ? nm_active_connection_get_state (active) : NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
-
-	if (ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-		if (nmc->print_output == NMC_PRINT_PRETTY)
-			nmc_terminal_erase_line ();
-		g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
-		         nm_object_get_path (NM_OBJECT (active)));
-		activate_connection_info_finish (info);
-	} else if (   ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING
-	           && state >= NM_DEVICE_STATE_IP_CONFIG
-	           && state <= NM_DEVICE_STATE_ACTIVATED) {
-		if (nmc->print_output == NMC_PRINT_PRETTY)
-			nmc_terminal_erase_line ();
-		g_print (_("Connection successfully activated (master waiting for slaves) (D-Bus active path: %s)\n"),
-		         nm_object_get_path (NM_OBJECT (active)));
-		activate_connection_info_finish (info);
-	}
-}
-
-static void
-active_connection_removed_cb (NMClient *client, NMActiveConnection *active, ActivateConnectionInfo *info)
-{
-	NmCli *nmc = info->nmc;
+	ac_state = nm_active_connection_get_state (active);
+	ac_reason = nm_active_connection_get_state_reason (active);
 
-	if (active == info->active) {
-		g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
-		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
-		activate_connection_info_finish (info);
+	if (device) {
+		dev_state = nm_device_get_state (device);
+		dev_reason = nm_device_get_state_reason (device);
 	}
-}
 
-static void
-active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, ActivateConnectionInfo *info)
-{
-	NmCli *nmc = info->nmc;
-	NMActiveConnectionState state;
-
-	state = nm_active_connection_get_state (active);
-
-	if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
+	if (ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
 		if (nmc->print_output == NMC_PRINT_PRETTY)
 			nmc_terminal_erase_line ();
 		g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
 		         nm_object_get_path (NM_OBJECT (active)));
 		activate_connection_info_finish (info);
-	} else if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
-		g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
-		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
-		activate_connection_info_finish (info);
-	} else if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
+	} else if (ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
+		if (device && ac_reason == NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED) {
+			if (dev_state == NM_DEVICE_STATE_FAILED || dev_state == NM_DEVICE_STATE_DISCONNECTED) {
+				g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"),
+				                 nmc_device_reason_to_string (dev_reason));
+				nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+				activate_connection_info_finish (info);
+			} else {
+				/* Just wait for the device to go failed. We'll get a better error message. */
+				return;
+			}
+		} else {
+			g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"),
+			                 active_connection_state_reason_to_string (ac_reason));
+			nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+			activate_connection_info_finish (info);
+		}
+	} else if (ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
 		/* activating master connection does not automatically activate any slaves, so their
 		 * active connection state will not progress beyond ACTIVATING state.
 		 * Monitor the device instead. */
-		const GPtrArray *devices;
-		NMDevice *device;
 
 		if (nmc->secret_agent) {
 			NMRemoteConnection *connection = nm_active_connection_get_connection (active);
@@ -2228,53 +2225,34 @@ active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, Activ
 			                               nm_connection_get_path (NM_CONNECTION (connection)));
 		}
 
-		devices = nm_active_connection_get_devices (active);
-		device = devices->len ? g_ptr_array_index (devices, 0) : NULL;
 		if (   device
 		    && (   NM_IS_DEVICE_BOND (device)
 		        || NM_IS_DEVICE_TEAM (device)
-		        || NM_IS_DEVICE_BRIDGE (device))) {
-			g_signal_connect (device, "notify::" NM_DEVICE_STATE, G_CALLBACK (device_state_cb), info);
-			device_state_cb (device, NULL, info);
+		        || NM_IS_DEVICE_BRIDGE (device))
+	            && dev_state >= NM_DEVICE_STATE_IP_CONFIG
+	            && dev_state <= NM_DEVICE_STATE_ACTIVATED) {
+			if (nmc->print_output == NMC_PRINT_PRETTY)
+				nmc_terminal_erase_line ();
+			g_print (_("Connection successfully activated (master waiting for slaves) (D-Bus active path: %s)\n"),
+			          nm_object_get_path (NM_OBJECT (active)));
+			activate_connection_info_finish (info);
 		}
 	}
 }
 
 static void
-vpn_connection_state_cb (NMVpnConnection *vpn,
-                         NMVpnConnectionState state,
-                         NMVpnConnectionStateReason reason,
-                         ActivateConnectionInfo *info)
+device_state_cb (NMDevice *device, GParamSpec *pspec, ActivateConnectionInfo *info)
 {
-	NmCli *nmc = info->nmc;
-
-	switch (state) {
-	case NM_VPN_CONNECTION_STATE_PREPARE:
-	case NM_VPN_CONNECTION_STATE_NEED_AUTH:
-	case NM_VPN_CONNECTION_STATE_CONNECT:
-	case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
-		/* no operation */
-		break;
-
-	case NM_VPN_CONNECTION_STATE_ACTIVATED:
-		if (nmc->print_output == NMC_PRINT_PRETTY)
-			nmc_terminal_erase_line ();
-		g_print (_("VPN connection successfully activated (D-Bus active path: %s)\n"),
-		         nm_object_get_path (NM_OBJECT (vpn)));
-		activate_connection_info_finish (info);
-		break;
-
-	case NM_VPN_CONNECTION_STATE_FAILED:
-	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
-		g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."),
-		                 vpn_connection_state_reason_to_string (reason));
-		nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
-		activate_connection_info_finish (info);
-		break;
+	check_activated (info);
+}
 
-	default:
-		break;
-	}
+static void
+active_connection_state_cb (NMActiveConnection *active,
+                            NMActiveConnectionState state,
+                            NMActiveConnectionStateReason reason,
+                            ActivateConnectionInfo *info)
+{
+	check_activated (info);
 }
 
 static void
@@ -2306,24 +2284,28 @@ progress_cb (gpointer user_data)
 }
 
 static gboolean
-progress_device_cb (gpointer user_data)
+progress_active_connection_cb (gpointer user_data)
 {
-	NMDevice *device = (NMDevice *) user_data;
-
-	nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : "");
+	NMActiveConnection *active = user_data;
+	const char *str;
+	NMDevice *device;
+	NMActiveConnectionState ac_state;
+	const GPtrArray *ac_devs;
 
-	return TRUE;
-}
+	ac_state = nm_active_connection_get_state (active);
 
-static gboolean
-progress_vpn_cb (gpointer user_data)
-{
-	NMVpnConnection *vpn = (NMVpnConnection *) user_data;
-	const char *str;
+	if (ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
+		/* If the connection is activating, the device state
+		 * is more interesting. */
+		ac_devs = nm_active_connection_get_devices (active);
+		device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
+	} else {
+		device = NULL;
+	}
 
-	str = NM_IS_VPN_CONNECTION (vpn) ?
-	        vpn_connection_state_to_string (nm_vpn_connection_get_vpn_state (vpn)) :
-	        "";
+	str =   device
+	      ? nmc_device_state_to_string (nm_device_get_state (device))
+	      : active_connection_state_to_string (ac_state);
 
 	nmc_terminal_show_progress (str);
 
@@ -2339,14 +2321,9 @@ activate_connection_info_finish (ActivateConnectionInfo *info)
 	}
 
 	if (info->active) {
-		if (NM_IS_VPN_CONNECTION (info->active))
-			g_signal_handlers_disconnect_by_func (info->active, G_CALLBACK (vpn_connection_state_cb), info);
-		else
-			g_signal_handlers_disconnect_by_func (info->active, G_CALLBACK (active_connection_state_cb), info);
+		g_signal_handlers_disconnect_by_func (info->active, G_CALLBACK (active_connection_state_cb), info);
 		g_object_unref (info->active);
-
 	}
-	g_signal_handlers_disconnect_by_func (info->nmc->client, G_CALLBACK (active_connection_removed_cb), info);
 
 	g_free (info);
 	quit ();
@@ -2391,34 +2368,26 @@ activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_dat
 			}
 			activate_connection_info_finish (info);
 		} else {
-			if (NM_IS_VPN_CONNECTION (active)) {
-				/* Monitor VPN state */
-				g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), info);
-
-				/* Start progress indication showing VPN states */
-				if (nmc->print_output == NMC_PRINT_PRETTY) {
-					if (progress_id)
-						g_source_remove (progress_id);
-					progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active));
-				}
-			} else {
-				g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), info);
-				active_connection_state_cb (active, NULL, info);
-
-				/* Start progress indication showing device states */
-				if (nmc->print_output == NMC_PRINT_PRETTY) {
-					if (progress_id)
-						g_source_remove (progress_id);
-					progress_id = g_timeout_add (120, progress_device_cb, device);
-				}
+			/* Monitor the active connection and device (if available) states */
+			g_signal_connect (active, "state-changed", G_CALLBACK (active_connection_state_cb), info);
+			if (device)
+				g_signal_connect (device, "notify::" NM_DEVICE_STATE, G_CALLBACK (device_state_cb), info);
+			/* Both active_connection_state_cb () and device_state_cb () will just
+			 * call check_activated (info). So, just call it once directly after
+			 * connecting on both the signals of the objects and skip the call to
+			 * the callbacks.
+			 */
+			check_activated (info);
+
+			/* Start progress indication showing VPN states */
+			if (nmc->print_output == NMC_PRINT_PRETTY) {
+				if (progress_id)
+					g_source_remove (progress_id);
+				progress_id = g_timeout_add (120, progress_active_connection_cb, active);
 			}
 
 			/* Start timer not to loop forever when signals are not emitted */
 			g_timeout_add_seconds (nmc->timeout, activate_connection_timeout_cb, info);
-
-			/* Fail when the active connection goes away. */
-			g_signal_connect (nmc->client, NM_CLIENT_ACTIVE_CONNECTION_REMOVED,
-			                  G_CALLBACK (active_connection_removed_cb), info);
 		}
 	}
 }
@@ -2602,8 +2571,8 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 	gs_free_error GError *error = NULL;
 	char **arg_arr = NULL;
 	int arg_num;
-	char ***argv_ptr = &argv;
-	int *argc_ptr = &argc;
+	char ***argv_ptr;
+	int *argc_ptr;
 
 	/*
 	 * Set default timeout for connection activation.
@@ -2612,6 +2581,10 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 	if (nmc->timeout == -1)
 		nmc->timeout = 90;
 
+	next_arg (nmc, &argc, &argv, NULL);
+	argv_ptr = &argv;
+	argc_ptr = &argc;
+
 	if (argc == 0 && nmc->ask) {
 		char *line;
 
@@ -2638,7 +2611,9 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 			nmc_complete_strings (*argv, "ifname", "ap", "passwd-file", NULL);
 
 		if (strcmp (*argv, "ifname") == 0) {
-			if (next_arg (&argc, &argv) != 0) {
+			argc--;
+			argv++;
+			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
 				return NMC_RESULT_ERROR_USER_INPUT;
 			}
@@ -2648,7 +2623,9 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 				nmc_complete_device (nmc->client, ifname, ap != NULL);
 		}
 		else if (strcmp (*argv, "ap") == 0) {
-			if (next_arg (&argc, &argv) != 0) {
+			argc--;
+			argv++;
+			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
 				return NMC_RESULT_ERROR_USER_INPUT;
 			}
@@ -2658,7 +2635,9 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 				nmc_complete_bssid (nmc->client, ifname, ap);
 		}
 		else if (strcmp (*argv, "passwd-file") == 0) {
-			if (next_arg (&argc, &argv) != 0) {
+			argc--;
+			argv++;
+			if (!argc) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
 				return NMC_RESULT_ERROR_USER_INPUT;
 			}
@@ -2672,8 +2651,7 @@ do_connection_up (NmCli *nmc, int argc, char **argv)
 			g_printerr (_("Unknown parameter: %s\n"), *argv);
 		}
 
-		argc--;
-		argv++;
+		next_arg (nmc, &argc, &argv, NULL);
 	}
 
 	if (nmc->complete)
@@ -2788,15 +2766,19 @@ do_connection_down (NmCli *nmc, int argc, char **argv)
 	NMActiveConnection *active;
 	ConnectionCbInfo *info = NULL;
 	const GPtrArray *active_cons;
-	GSList *queue = NULL, *iter;
+	GSList *queue = NULL, *iter, *next;
 	char **arg_arr = NULL;
-	char **arg_ptr = argv;
-	int arg_num = argc;
+	char **arg_ptr;
+	int arg_num;
 	int idx = 0;
 
 	if (nmc->timeout == -1)
 		nmc->timeout = 10;
 
+	next_arg (nmc, &argc, &argv, NULL);
+	arg_ptr = argv;
+	arg_num = argc;
+
 	if (argc == 0) {
 		/* nmc_do_cmd() should not call this with argc=0. */
 		g_assert (!nmc->complete);
@@ -2829,7 +2811,9 @@ do_connection_down (NmCli *nmc, int argc, char **argv)
 		    || strcmp (*arg_ptr, "apath") == 0) {
 
 			selector = *arg_ptr;
-			if (next_arg (&arg_num, &arg_ptr) != 0) {
+			arg_num--;
+			arg_ptr++;
+			if (!arg_num) {
 				g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
 				nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
 				goto finish;
@@ -2853,7 +2837,7 @@ do_connection_down (NmCli *nmc, int argc, char **argv)
 		}
 
 		if (idx == 0)
-			next_arg (&arg_num, &arg_ptr);
+			next_arg (nmc->ask ? NULL : nmc, &arg_num, &arg_ptr, NULL);
 	}
 
 	if (!queue) {
@@ -2875,17 +2859,36 @@ do_connection_down (NmCli *nmc, int argc, char **argv)
 		info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
 	}
 
-	for (iter = queue; iter; iter = g_slist_next (iter)) {
+	iter = queue;
+	while (iter) {
+		GError *error = NULL;
+
+		next = g_slist_next (iter);
 		active = iter->data;
 
-		if (info)
+		if (info) {
 			g_signal_connect (active,
 			                  "notify::" NM_ACTIVE_CONNECTION_STATE,
 			                  G_CALLBACK (down_active_connection_state_cb),
 			                  info);
+		}
 
 		/* Now deactivate the connection */
-		nm_client_deactivate_connection (nmc->client, active, NULL, NULL);
+		if (!nm_client_deactivate_connection (nmc->client, active, NULL, &error)) {
+			g_print (_("Connection '%s' deactivation failed: %s\n"),
+			         nm_active_connection_get_id (active), error->message);
+			g_error_free (error);
+
+			if (info) {
+				g_signal_handlers_disconnect_by_func (active,
+				                                      down_active_connection_state_cb,
+				                                      info);
+				/* Remove the active connection from @queue */
+				connection_cb_info_finish (info, active);
+			}
+		}
+
+		iter = next;
 	}
 
 finish:
@@ -3071,6 +3074,13 @@ static const NameItem nmc_vxlan_settings [] = {
 	{ NULL, NULL, NULL, FALSE }
 };
 
+static const NameItem nmc_dummy_settings [] = {
+	{ NM_SETTING_CONNECTION_SETTING_NAME,  NULL,       NULL, TRUE  },
+	{ NM_SETTING_DUMMY_SETTING_NAME,       NULL,       NULL, TRUE  },
+	{ NM_SETTING_WIRED_SETTING_NAME,       "ethernet", NULL, FALSE },
+	{ NULL, NULL, NULL, FALSE }
+};
+
 /* Available connection types */
 static const NameItem nmc_valid_connection_types[] = {
 	{ NM_SETTING_GENERIC_SETTING_NAME,    NULL,        nmc_generic_settings      }, /* Needs to be first. */
@@ -3098,6 +3108,7 @@ static const NameItem nmc_valid_connection_types[] = {
 	{ NM_SETTING_MACSEC_SETTING_NAME,     NULL,        nmc_macsec_settings       },
 	{ NM_SETTING_MACVLAN_SETTING_NAME,    NULL,        nmc_macvlan_settings      },
 	{ NM_SETTING_VXLAN_SETTING_NAME,      NULL,        nmc_vxlan_settings        },
+	{ NM_SETTING_DUMMY_SETTING_NAME,      NULL,        nmc_dummy_settings        },
 	{ NULL, NULL, NULL }
 };
 
@@ -3545,7 +3556,7 @@ unique_master_iface_ifname (const GPtrArray *connections,
 {
 	NMConnection *connection;
 	char *new_name;
-	unsigned int num = 1;
+	unsigned num = 1;
 	int i = 0;
 	const char *ifname = NULL;
 
@@ -4156,9 +4167,9 @@ set_bond_monitoring_mode (NmCli *nmc, NMConnection *con, OptionInfo *option, con
 		monitor_mode = g_strdup (WORD_MIIMON);
 	}
 
-	if (matches (monitor_mode, WORD_MIIMON) == 0)
+	if (matches (monitor_mode, WORD_MIIMON))
 		enable_options (NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, miimon_opts);
-	else if (matches (monitor_mode, WORD_ARP) == 0)
+	else if (matches (monitor_mode, WORD_ARP))
 		enable_options (NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS, arp_opts);
 	else {
 		g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
@@ -4535,7 +4546,8 @@ get_value (const char **value, int *argc, char ***argv, const char *option, GErr
 	else
 		*value = *argv[0];
 
-	next_arg (argc, argv);
+	(*argc)--;
+	(*argv)++;
 	return TRUE;
 }
 
@@ -4599,7 +4611,8 @@ nmc_read_connection_properties (NmCli *nmc,
 				return FALSE;
 			}
 
-			next_arg (argc, argv);
+			(*argc)--;
+			(*argv)++;
 			if (!get_value (&value, argc, argv, option, error))
 				return FALSE;
 
@@ -4638,7 +4651,8 @@ nmc_read_connection_properties (NmCli *nmc,
 			if (*argc == 1 && nmc->complete)
 				complete_property_name (nmc, connection, modifier, option, NULL);
 
-			next_arg (argc, argv);
+			(*argc)--;
+			(*argv)++;
 			if (!get_value (&value, argc, argv, option, error))
 				return FALSE;
 
@@ -4844,7 +4858,7 @@ want_provide_opt_args (const char *type, int num)
 	                                 "Do you want to provide them? %s", num),
 	                       prompt_yes_no (TRUE, NULL));
 	answer = answer ? g_strstrip (answer) : NULL;
-	if (answer && matches (answer, WORD_LOC_YES) != 0)
+	if (answer && !matches (answer, WORD_LOC_YES))
 		ret = FALSE;
 	g_free (answer);
 	return ret;
@@ -4956,6 +4970,8 @@ do_connection_add (NmCli *nmc, int argc, char **argv)
 	OptionInfo *candidate;
 	gboolean seen_dash_dash = FALSE;
 
+	next_arg (nmc, &argc, &argv, NULL);
+
 	rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion;
 
 	nmc->return_value = NMC_RESULT_SUCCESS;
@@ -4975,13 +4991,15 @@ read_properties:
 			 * options and properties to be separated with "--" */
 			g_clear_error (&error);
 			seen_dash_dash = TRUE;
-			next_arg (&argc, &argv);
+			next_arg (nmc, &argc, &argv, NULL);
 			goto read_properties;
 		} else if (g_strcmp0 (*argv, "save") == 0) {
 			/* It would be better if "save" was a separate argument and not
 			 * mixed with properties, but there's not much we can do about it now. */
 			g_clear_error (&error);
-			if (next_arg (&argc, &argv) != 0) {
+			argc--;
+			argv++;
+			if (!argc) {
 				g_string_printf (nmc->return_text,
 				                 _("Error: value for '%s' argument is required."),
 				                "save");
@@ -4995,7 +5013,7 @@ read_properties:
 				g_clear_error (&error);
 				goto finish;
 			}
-			next_arg (&argc, &argv);
+			next_arg (nmc, &argc, &argv, NULL);
 			goto read_properties;
 		}
 
@@ -5441,13 +5459,13 @@ get_gen_func_cmd_nmcli (const char *str)
 {
 	if (!str)
 		return NULL;
-	if (matches (str, "status-line") == 0)
+	if (matches (str, "status-line"))
 		return gen_func_bool_values;
-	if (matches (str, "save-confirmation") == 0)
+	if (matches (str, "save-confirmation"))
 		return gen_func_bool_values;
-	if (matches (str, "show-secrets") == 0)
+	if (matches (str, "show-secrets"))
 		return gen_func_bool_values;
-	if (matches (str, "prompt-color") == 0)
+	if (matches (str, "prompt-color"))
 		return gen_cmd_nmcli_prompt_color;
 	return NULL;
 }
@@ -5524,7 +5542,7 @@ should_complete_cmd (const char *line, int end, const char *cmd,
 			*prev_word = g_strdup (word3);
 	}
 
-	if (word1 && matches (word1, cmd) == 0)
+	if (word1 && matches (word1, cmd))
 		ret = TRUE;
 
 	g_free (tmp);
@@ -6075,29 +6093,29 @@ parse_editor_main_cmd (const char *cmd, char **cmd_arg)
 		return NMC_EDITOR_MAIN_CMD_UNKNOWN;
 	}
 
-	if (matches (vec[0], "goto") == 0)
+	if (matches (vec[0], "goto"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO;
-	else if (matches (vec[0], "remove") == 0)
+	else if (matches (vec[0], "remove"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE;
-	else if (matches (vec[0], "set") == 0)
+	else if (matches (vec[0], "set"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_SET;
-	else if (matches (vec[0], "describe") == 0)
+	else if (matches (vec[0], "describe"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE;
-	else if (matches (vec[0], "print") == 0)
+	else if (matches (vec[0], "print"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT;
-	else if (matches (vec[0], "verify") == 0)
+	else if (matches (vec[0], "verify"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY;
-	else if (matches (vec[0], "save") == 0)
+	else if (matches (vec[0], "save"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE;
-	else if (matches (vec[0], "activate") == 0)
+	else if (matches (vec[0], "activate"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_ACTIVATE;
-	else if (matches (vec[0], "back") == 0)
+	else if (matches (vec[0], "back"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_BACK;
-	else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
+	else if (matches (vec[0], "help") || strcmp (vec[0], "?") == 0)
 		editor_cmd = NMC_EDITOR_MAIN_CMD_HELP;
-	else if (matches (vec[0], "quit") == 0)
+	else if (matches (vec[0], "quit"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT;
-	else if (matches (vec[0], "nmcli") == 0)
+	else if (matches (vec[0], "nmcli"))
 		editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI;
 
 	/* set pointer to command argument */
@@ -6263,23 +6281,23 @@ parse_editor_sub_cmd (const char *cmd, char **cmd_arg)
 		return NMC_EDITOR_SUB_CMD_UNKNOWN;
 	}
 
-	if (matches (vec[0], "set") == 0)
+	if (matches (vec[0], "set"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_SET;
-	else if (matches (vec[0], "add") == 0)
+	else if (matches (vec[0], "add"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_ADD;
-	else if (matches (vec[0], "change") == 0)
+	else if (matches (vec[0], "change"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE;
-	else if (matches (vec[0], "remove") == 0)
+	else if (matches (vec[0], "remove"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE;
-	else if (matches (vec[0], "describe") == 0)
+	else if (matches (vec[0], "describe"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE;
-	else if (matches (vec[0], "print") == 0)
+	else if (matches (vec[0], "print"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_PRINT;
-	else if (matches (vec[0], "back") == 0)
+	else if (matches (vec[0], "back"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_BACK;
-	else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
+	else if (matches (vec[0], "help") || strcmp (vec[0], "?") == 0)
 		editor_cmd = NMC_EDITOR_SUB_CMD_HELP;
-	else if (matches (vec[0], "quit") == 0)
+	else if (matches (vec[0], "quit"))
 		editor_cmd = NMC_EDITOR_SUB_CMD_QUIT;
 
 	/* set pointer to command argument */
@@ -6595,7 +6613,7 @@ confirm_quit (void)
 	                         "Do you really want to quit? %s"),
 	                       prompt_yes_no (FALSE, NULL));
 	answer = answer ? g_strstrip (answer) : NULL;
-	if (answer && matches (answer, WORD_LOC_YES) == 0)
+	if (answer && matches (answer, WORD_LOC_YES))
 		want_quit = TRUE;
 
 	g_free (answer);
@@ -6750,10 +6768,10 @@ property_edit_submenu (NmCli *nmc,
 		case NMC_EDITOR_SUB_CMD_PRINT:
 			/* Print current connection settings/properties */
 			if (cmd_property_arg) {
-				if (matches (cmd_property_arg, "setting") == 0)
+				if (matches (cmd_property_arg, "setting"))
 					editor_show_setting (curr_setting, nmc);
-				else if (   matches (cmd_property_arg, "connection") == 0
-				         || matches (cmd_property_arg, "all") == 0)
+				else if (   matches (cmd_property_arg, "connection")
+				         || matches (cmd_property_arg, "all"))
 					editor_show_connection (connection, nmc);
 				else
 					g_print (_("Unknown command argument: '%s'\n"), cmd_property_arg);
@@ -6943,7 +6961,7 @@ confirm_connection_saving (NMConnection *local, NMConnection *remote)
 		                         "That might result in an immediate activation of the connection.\n"
 		                         "Do you still want to save? %s"), prompt_yes_no (TRUE, NULL));
 		answer = answer ? g_strstrip (answer) : NULL;
-		if (!answer || matches (answer, WORD_LOC_YES) == 0)
+		if (!answer || matches (answer, WORD_LOC_YES))
 			confirmed = TRUE;
 		else
 			confirmed = FALSE;
@@ -6995,7 +7013,7 @@ menu_switch_to_level1 (NmCli *nmc,
 }
 
 static gboolean
-editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type)
+editor_menu_main (NmCli *nmc, NMConnection *connection)
 {
 	NMSettingConnection *s_con;
 	NMRemoteConnection *rem_con;
@@ -7023,7 +7041,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 		s_type = nm_setting_connection_get_slave_type (s_con);
 	slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
 
-	valid_settings_main = get_valid_settings_array (connection_type);
+	valid_settings_main = get_valid_settings_array (nm_connection_get_connection_type (connection));
 	valid_settings_slave = get_valid_settings_array (slv_type);
 	g_free (slv_type);
 
@@ -7510,9 +7528,9 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 
 				/* parse argument */
 				if (cmd_arg) {
-					if (matches (cmd_arg, "temporary") == 0)
+					if (matches (cmd_arg, "temporary"))
 						persistent = FALSE;
-					else if (matches (cmd_arg, "persistent") == 0)
+					else if (matches (cmd_arg, "persistent"))
 						persistent = TRUE;
 					else {
 						g_print (_("Error: invalid argument '%s'\n"), cmd_arg);
@@ -7672,7 +7690,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 			break;
 
 		case NMC_EDITOR_MAIN_CMD_NMCLI:
-			if (cmd_arg_p && matches (cmd_arg_p, "status-line") == 0) {
+			if (cmd_arg_p && matches (cmd_arg_p, "status-line")) {
 				GError *tmp_err = NULL;
 				gboolean bb;
 				if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
@@ -7680,7 +7698,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 					g_clear_error (&tmp_err);
 				} else
 					nmc->editor_status_line = bb;
-			} else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation") == 0) {
+			} else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation")) {
 				GError *tmp_err = NULL;
 				gboolean bb;
 				if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
@@ -7688,7 +7706,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 					g_clear_error (&tmp_err);
 				} else
 					nmc->editor_save_confirmation = bb;
-			} else if (cmd_arg_p && matches (cmd_arg_p, "show-secrets") == 0) {
+			} else if (cmd_arg_p && matches (cmd_arg_p, "show-secrets")) {
 				GError *tmp_err = NULL;
 				gboolean bb;
 				if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
@@ -7696,7 +7714,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t
 					g_clear_error (&tmp_err);
 				} else
 					nmc->editor_show_secrets = bb;
-			} else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color") == 0) {
+			} else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color")) {
 				GError *tmp_err = NULL;
 				NmcTermColor color;
 				color = nmc_term_color_parse_string (cmd_arg_v ? g_strstrip (cmd_arg_v) : " ", &tmp_err);
@@ -7876,6 +7894,18 @@ editor_init_existing_connection (NMConnection *connection)
 		nmc_setting_connection_connect_handlers (s_con, connection);
 }
 
+static void
+nmc_complete_connection_type (const char *prefix, const NameItem *types)
+{
+	while (types->name) {
+		if (!*prefix || matches (prefix, types->name))
+			g_print ("%s\n", types->name);
+		if (types->alias && (!*prefix || matches (prefix, types->alias)))
+			g_print ("%s\n", types->alias);
+		types++;
+	}
+}
+
 static NMCResultCode
 do_connection_edit (NmCli *nmc, int argc, char **argv)
 {
@@ -7903,9 +7933,9 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 	                         {"path",     TRUE, &con_path, FALSE},
 	                         {NULL} };
 
-	/* TODO: complete uuid, path or id */
-	if (nmc->complete)
-		return nmc->return_value;
+	next_arg (nmc, &argc, &argv, NULL);
+	if (argc == 1 && nmc->complete)
+		nmc_complete_strings (*argv, "type", "con-name", "id", "uuid", "path", NULL);
 
 	nmc->return_value = NMC_RESULT_SUCCESS;
 
@@ -7952,7 +7982,10 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 		/* Existing connection */
 		NMConnection *found_con;
 
-		found_con = nmc_find_connection (connections, selector, con, NULL, FALSE);
+		found_con = nmc_find_connection (connections, selector, con, NULL, nmc->complete);
+		if (nmc->complete)
+			goto error;
+
 		if (!found_con) {
 			g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con);
 			nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
@@ -7984,6 +8017,12 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 		editor_init_existing_connection (connection);
 	} else {
 		/* New connection */
+		if (nmc->complete) {
+			if (type && argc == 0)
+				nmc_complete_connection_type (type, nmc_valid_connection_types);
+			goto error;
+		}
+
 		connection_type = check_valid_name (type, nmc_valid_connection_types, NULL, &err1);
 		tmp_str = get_valid_options_string (nmc_valid_connection_types, NULL);
 
@@ -8044,11 +8083,11 @@ do_connection_edit (NmCli *nmc, int argc, char **argv)
 
 	/* Set global variables for use in TAB completion */
 	nmc_tab_completion.nmc = nmc;
-	nmc_tab_completion.con_type = g_strdup (connection_type);
+	nmc_tab_completion.con_type = g_strdup (nm_connection_get_connection_type (connection));
 	nmc_tab_completion.connection = connection;
 
 	/* Run menu loop */
-	editor_menu_main (nmc, connection, connection_type);
+	editor_menu_main (nmc, connection);
 
 	if (connection)
 		g_object_unref (connection);
@@ -8098,11 +8137,10 @@ do_connection_modify (NmCli *nmc,
 	GError *error = NULL;
 	gboolean temporary = FALSE;
 
-	if (argc && nmc_arg_is_option (*argv, "temporary")) {
-		if (nmc->complete)
-			goto finish;
+	/* Check --temporary */
+	if (next_arg (nmc, &argc, &argv, "--temporary", NULL) > 0) {
 		temporary = TRUE;
-		next_arg (&argc, &argv);
+		next_arg (nmc, &argc, &argv, NULL);
 	}
 
 	connection = get_connection (nmc, &argc, &argv, NULL, &error);
@@ -8191,12 +8229,17 @@ do_connection_clone (NmCli *nmc, int argc, char **argv)
 	gboolean temporary = FALSE;
 	char **arg_arr = NULL;
 	int arg_num;
-	char ***argv_ptr = &argv;
-	int *argc_ptr = &argc;
+	char ***argv_ptr;
+	int *argc_ptr;
 	GError *error = NULL;
 
-	if (argc == 1 && nmc->complete)
-		nmc_complete_strings (*argv, "temporary", NULL);
+	if (next_arg (nmc, &argc, &argv, "--temporary", NULL) > 0) {
+		temporary = TRUE;
+		next_arg (nmc, &argc, &argv, NULL);
+	}
+
+	argv_ptr = &argv;
+	argc_ptr = &argc;
 
 	if (argc == 0 && nmc->ask) {
 		char *line;
@@ -8209,9 +8252,6 @@ do_connection_clone (NmCli *nmc, int argc, char **argv)
 		g_free (line);
 		argv_ptr = &arg_arr;
 		argc_ptr = &arg_num;
-	} else if (nmc_arg_is_option (*argv, "temporary")) {
-		temporary = TRUE;
-		next_arg (&argc, &argv);
 	}
 
 	connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, &error);
@@ -8234,7 +8274,7 @@ do_connection_clone (NmCli *nmc, int argc, char **argv)
 		goto finish;
 	}
 
-	if (next_arg (argc_ptr, argv_ptr) == 0) {
+	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);
 		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
 		goto finish;
@@ -8305,8 +8345,8 @@ do_connection_delete (NmCli *nmc, int argc, char **argv)
 	ConnectionCbInfo *info = NULL;
 	GSList *queue = NULL, *iter;
 	char **arg_arr = NULL, *old_arg;
-	char **arg_ptr = argv;
-	int arg_num = argc;
+	char **arg_ptr;
+	int arg_num;
 	GString *invalid_cons = NULL;
 	int pos = 0;
 	GError *error = NULL;
@@ -8314,6 +8354,10 @@ do_connection_delete (NmCli *nmc, int argc, char **argv)
 	if (nmc->timeout == -1)
 		nmc->timeout = 10;
 
+	next_arg (nmc, &argc, &argv, NULL);
+	arg_ptr = argv;
+	arg_num = argc;
+
 	if (argc == 0) {
 		if (nmc->ask) {
 			char *line;
@@ -8444,6 +8488,7 @@ do_connection_monitor (NmCli *nmc, int argc, char **argv)
 {
 	GError *error = NULL;
 
+	next_arg (nmc, &argc, &argv, NULL);
 	if (argc == 0) {
 		/* No connections specified. Monitor all. */
 		const GPtrArray *connections;
@@ -8492,6 +8537,7 @@ do_connection_reload (NmCli *nmc, int argc, char **argv)
 {
 	GError *error = NULL;
 
+	next_arg (nmc, &argc, &argv, NULL);
 	if (nmc->complete)
 		return nmc->return_value;
 
@@ -8512,6 +8558,7 @@ do_connection_load (NmCli *nmc, int argc, char **argv)
 	char **filenames, **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;
@@ -8547,6 +8594,22 @@ do_connection_load (NmCli *nmc, int argc, char **argv)
 #define PROMPT_IMPORT_TYPE PROMPT_VPN_TYPE
 #define PROMPT_IMPORT_FILE N_("File to import: ")
 
+static void
+nmc_complete_vpn_service (const char *prefix)
+{
+	char **services;
+	char **candidate;
+
+	services = nm_vpn_plugin_info_list_get_service_types (NULL, FALSE, TRUE);
+	for (candidate = services; *candidate; candidate++) {
+		if (!*prefix && g_str_has_prefix (*candidate, NM_DBUS_INTERFACE))
+			continue;
+		if (!*prefix || matches (prefix, *candidate))
+			g_print ("%s\n", *candidate);
+	}
+	g_strfreev (services);
+}
+
 static NMCResultCode
 do_connection_import (NmCli *nmc, int argc, char **argv)
 {
@@ -8559,6 +8622,13 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 	gs_free char *service_type = NULL;
 	gboolean temporary = FALSE;
 
+	/* Check --temporary */
+	if (next_arg (nmc, &argc, &argv, "--temporary", NULL) > 0) {
+		temporary = TRUE;
+		next_arg (nmc, &argc, &argv, NULL);
+	}
+
+
 	if (argc == 0) {
 		/* nmc_do_cmd() should not call this with argc=0. */
 		g_assert (!nmc->complete);
@@ -8577,25 +8647,29 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 
 	while (argc > 0) {
 		if (argc == 1 && nmc->complete)
-			nmc_complete_strings (*argv, "temporary", "type", "file", NULL);
-		if (nmc_arg_is_option (*argv, "temporary")) {
-			temporary = TRUE;
-			next_arg (&argc, &argv);
-		}
+			nmc_complete_strings (*argv, "type", "file", NULL);
 
 		if (strcmp (*argv, "type") == 0) {
-			if (next_arg (&argc, &argv) != 0) {
+			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;
 			}
+
+			if (argc == 1 && nmc->complete)
+				nmc_complete_vpn_service (*argv);
+
 			if (!type)
 				type = *argv;
 			else
 				g_printerr (_("Warning: 'type' already specified, ignoring extra one.\n"));
 
 		} else if (strcmp (*argv, "file") == 0) {
-			if (next_arg (&argc, &argv) != 0) {
+			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;
@@ -8612,8 +8686,7 @@ do_connection_import (NmCli *nmc, int argc, char **argv)
 			goto finish;
 		}
 
-		argc--;
-		argv++;
+		next_arg (nmc, &argc, &argv, NULL);
 	}
 
 	if (nmc->complete)
@@ -8689,8 +8762,12 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 	char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX";
 	char **arg_arr = NULL;
 	int arg_num;
-	char ***argv_ptr = &argv;
-	int *argc_ptr = &argc;
+	char ***argv_ptr;
+	int *argc_ptr;
+
+	next_arg (nmc, &argc, &argv, NULL);
+	argv_ptr = &argv;
+	argc_ptr = &argc;
 
 	if (argc == 0 && nmc->ask) {
 		char *line;
@@ -8715,17 +8792,17 @@ do_connection_export (NmCli *nmc, int argc, char **argv)
 	if (nmc->complete)
 		return nmc->return_value;
 
-	if (next_arg (&argc, &argv) == 0)
-		out_name = *argv;
-	else if (nmc->ask)
-		out_name = out_name_ask = nmc_readline (_("Output file name: "));
+	out_name = *argv;
 
-	if (next_arg (argc_ptr, argv_ptr) == 0) {
+	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);
 		nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
 		goto finish;
 	}
 
+	if (out_name == NULL && nmc->ask)
+		out_name = out_name_ask = nmc_readline (_("Output file name: "));
+
 	type = nm_connection_get_connection_type (connection);
 	if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) != 0) {
 		g_string_printf (nmc->return_text, _("Error: the connection is not VPN."));
@@ -8893,6 +8970,8 @@ static const NMCCommand connection_cmds[] = {
 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);